From 646edc7f8f112ea96c6706e230887fcddce5c983 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 31 Oct 2023 14:25:41 +0100 Subject: [PATCH 001/603] AtlasEngine: Minor bug fixes (#16219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes 4 minor bugs: * Forgot to set the maximum swap chain latency. Without it, it defaults to up to 3 frames of latency. We don't need this, because our renderer is simple and fast and is expected to draw frames within <1ms. * ClearType treats the alpha channel as ignored, whereas custom shaders can manipulate the alpha channel freely. This meant that using both simultaneously would produce weird effects, like text having black background. We now force grayscale AA instead. * The builtin retro shader should not be effected by the previous point. * When the cbuffer is entirely unused in a custom shader, it has so far resulted in constant redraws. This happened because the D3D reflection `GetDesc` call will then return `E_FAIL` in this situation. The new code on the other hand will now assume that a failure to get the description is equal to the variable being unused. Closes #15960 ## Validation Steps Performed * A custom passthrough shader works with grayscale and ClearType AA while also changing the opacity with Ctrl+Shift+Scroll ✅ * Same for the builtin retro shader, but ClearType works ✅ * The passthrough shader doesn't result in constant redrawing ✅ (cherry picked from commit 0289cb043c3aac0f9199f667550d8811022e9bed) Service-Card-Id: 90915277 Service-Version: 1.19 --- src/renderer/atlas/AtlasEngine.api.cpp | 14 +++++++------- src/renderer/atlas/AtlasEngine.r.cpp | 4 +++- src/renderer/atlas/BackendD3D.cpp | 20 ++++++++++++-------- src/renderer/atlas/common.h | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 4459c97a8e5..c810dd115f6 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -490,20 +490,20 @@ void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept void AtlasEngine::_resolveTransparencySettings() noexcept { + // An opaque background allows us to use true "independent" flips. See AtlasEngine::_createSwapChain(). + // We can't enable them if custom shaders are specified, because it's unknown, whether they support opaque inputs. + const bool useAlpha = _api.enableTransparentBackground || !_api.s->misc->customPixelShaderPath.empty(); // If the user asks for ClearType, but also for a transparent background // (which our ClearType shader doesn't simultaneously support) // then we need to sneakily force the renderer to grayscale AA. - const auto antialiasingMode = _api.enableTransparentBackground && _api.antialiasingMode == AntialiasingMode::ClearType ? AntialiasingMode::Grayscale : _api.antialiasingMode; - const bool enableTransparentBackground = _api.enableTransparentBackground || !_api.s->misc->customPixelShaderPath.empty() || _api.s->misc->useRetroTerminalEffect; + const auto antialiasingMode = useAlpha && _api.antialiasingMode == AntialiasingMode::ClearType ? AntialiasingMode::Grayscale : _api.antialiasingMode; - if (antialiasingMode != _api.s->font->antialiasingMode || enableTransparentBackground != _api.s->target->enableTransparentBackground) + if (antialiasingMode != _api.s->font->antialiasingMode || useAlpha != _api.s->target->useAlpha) { const auto s = _api.s.write(); s->font.write()->antialiasingMode = antialiasingMode; - // An opaque background allows us to use true "independent" flips. See AtlasEngine::_createSwapChain(). - // We can't enable them if custom shaders are specified, because it's unknown, whether they support opaque inputs. - s->target.write()->enableTransparentBackground = enableTransparentBackground; - _api.backgroundOpaqueMixin = enableTransparentBackground ? 0x00000000 : 0xff000000; + s->target.write()->useAlpha = useAlpha; + _api.backgroundOpaqueMixin = useAlpha ? 0x00000000 : 0xff000000; } } diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index e7070b708e6..1fac8f0e834 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -329,7 +329,7 @@ void AtlasEngine::_createSwapChain() .SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, // If our background is opaque we can enable "independent" flips by setting DXGI_ALPHA_MODE_IGNORE. // As our swap chain won't have to compose with DWM anymore it reduces the display latency dramatically. - .AlphaMode = _p.s->target->enableTransparentBackground ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE, + .AlphaMode = _p.s->target->useAlpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE, .Flags = swapChainFlags, }; @@ -360,6 +360,8 @@ void AtlasEngine::_createSwapChain() _p.swapChain.targetSize = _p.s->targetSize; _p.swapChain.waitForPresentation = true; + LOG_IF_FAILED(_p.swapChain.swapChain->SetMaximumFrameLatency(1)); + WaitUntilCanRender(); if (_p.swapChainChangedCallback) diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 846567ca613..19334b3a9d4 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -403,23 +403,24 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p) /* ppCode */ blob.addressof(), /* ppErrorMsgs */ error.addressof()); - // Unless we can determine otherwise, assume this shader requires evaluation every frame - _requiresContinuousRedraw = true; - if (SUCCEEDED(hr)) { - THROW_IF_FAILED(p.device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, _customPixelShader.put())); + THROW_IF_FAILED(p.device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, _customPixelShader.addressof())); // Try to determine whether the shader uses the Time variable wil::com_ptr reflector; - if (SUCCEEDED_LOG(D3DReflect(blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(reflector.put())))) + if (SUCCEEDED_LOG(D3DReflect(blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(reflector.addressof())))) { + // Depending on the version of the d3dcompiler_*.dll, the next two functions either return nullptr + // on failure or an instance of CInvalidSRConstantBuffer or CInvalidSRVariable respectively, + // which cause GetDesc() to return E_FAIL. In other words, we have to assume that any failure in the + // next few lines indicates that the cbuffer is entirely unused (--> _requiresContinuousRedraw=false). if (ID3D11ShaderReflectionConstantBuffer* constantBufferReflector = reflector->GetConstantBufferByIndex(0)) // shader buffer { if (ID3D11ShaderReflectionVariable* variableReflector = constantBufferReflector->GetVariableByIndex(0)) // time { D3D11_SHADER_VARIABLE_DESC variableDescriptor; - if (SUCCEEDED_LOG(variableReflector->GetDesc(&variableDescriptor))) + if (SUCCEEDED(variableReflector->GetDesc(&variableDescriptor))) { // only if time is used _requiresContinuousRedraw = WI_IsFlagSet(variableDescriptor.uFlags, D3D_SVF_USED); @@ -427,6 +428,11 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p) } } } + else + { + // Unless we can determine otherwise, assume this shader requires evaluation every frame + _requiresContinuousRedraw = true; + } } else { @@ -447,8 +453,6 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p) else if (p.s->misc->useRetroTerminalEffect) { THROW_IF_FAILED(p.device->CreatePixelShader(&custom_shader_ps[0], sizeof(custom_shader_ps), nullptr, _customPixelShader.put())); - // We know the built-in retro shader doesn't require continuous redraw. - _requiresContinuousRedraw = false; } if (_customPixelShader) diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index bb46ff61547..94434bffc9c 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -313,7 +313,7 @@ namespace Microsoft::Console::Render::Atlas struct TargetSettings { HWND hwnd = nullptr; - bool enableTransparentBackground = false; + bool useAlpha = false; bool useSoftwareRendering = false; }; From a608a9157160cf1b57c4d3eb486f5cb85e42bae9 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 6 Nov 2023 22:30:03 +0100 Subject: [PATCH 002/603] Fix the fix for the fix of nearby font loading (#16196) I still don't know how to reproduce it properly, but I'm slowly wrapping my head around how and why it happens. The issue isn't that `FindFamilyName` fails with `exists=FALSE`, but rather that any of the followup calls like `GetDesignGlyphMetrics` fails, which results in an exception and subsequently in an orderly fallback to Consolas. I've always thought that the issue is that even with the nearby font collection we get an `exists=FALSE`... I'm not sure why I thought that. This changeset also drops the fallback iteration for Lucida Console and Courier New, because I felt like the code looks neater that way and I think it's a reasonable expectation that Consolas is always installed. Closes #16058 (cherry picked from commit 9e86c9811f2cf0ad53c9c543b886274b1079dd00) Service-Card-Id: 90885607 Service-Version: 1.19 --- src/renderer/atlas/AtlasEngine.api.cpp | 65 ++++++++++++-------------- src/renderer/atlas/common.h | 1 - src/renderer/dx/DxFontInfo.cpp | 18 +++---- 3 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index c810dd115f6..0e2f7941688 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -455,30 +455,34 @@ void AtlasEngine::SetWarningCallback(std::function pfn) noexcept [[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept { - static constexpr std::array fallbackFaceNames{ static_cast(nullptr), L"Consolas", L"Lucida Console", L"Courier New" }; - auto it = fallbackFaceNames.begin(); - const auto end = fallbackFaceNames.end(); + try + { + _updateFont(fontInfoDesired.GetFaceName().c_str(), fontInfoDesired, fontInfo, features, axes); + return S_OK; + } + CATCH_LOG(); - for (;;) + if constexpr (Feature_NearbyFontLoading::IsEnabled()) { try { - _updateFont(*it, fontInfoDesired, fontInfo, features, axes); + // _resolveFontMetrics() checks `_api.s->font->fontCollection` for a pre-existing font collection, + // before falling back to using the system font collection. This way we can inject our custom one. See GH#9375. + // Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection + // instance across font changes, like when zooming the font size rapidly using the scroll wheel. + _api.s.write()->font.write()->fontCollection = FontCache::GetCached(); + _updateFont(fontInfoDesired.GetFaceName().c_str(), fontInfoDesired, fontInfo, features, axes); return S_OK; } - catch (...) - { - ++it; - if (it == end) - { - RETURN_CAUGHT_EXCEPTION(); - } - else - { - LOG_CAUGHT_EXCEPTION(); - } - } + CATCH_LOG(); } + + try + { + _updateFont(nullptr, fontInfoDesired, fontInfo, features, axes); + return S_OK; + } + CATCH_RETURN(); } void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept @@ -598,11 +602,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo if (!requestedFaceName) { - requestedFaceName = fontInfoDesired.GetFaceName().c_str(); - if (!requestedFaceName) - { - requestedFaceName = L"Consolas"; - } + requestedFaceName = L"Consolas"; } if (!requestedSize.height) { @@ -614,22 +614,19 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo requestedWeight = DWRITE_FONT_WEIGHT_NORMAL; } - wil::com_ptr fontCollection; - THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontCollection(fontCollection.addressof(), FALSE)); + // UpdateFont() (and its NearbyFontLoading feature path specifically) sets `_api.s->font->fontCollection` + // to a custom font collection that includes .ttf files that are bundled with our app package. See GH#9375. + // Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection + // instance across font changes, like when zooming the font size rapidly using the scroll wheel. + auto fontCollection = _api.s->font->fontCollection; + if (!fontCollection) + { + THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontCollection(fontCollection.addressof(), FALSE)); + } u32 index = 0; BOOL exists = false; THROW_IF_FAILED(fontCollection->FindFamilyName(requestedFaceName, &index, &exists)); - - if constexpr (Feature_NearbyFontLoading::IsEnabled()) - { - if (!exists) - { - fontCollection = FontCache::GetCached(); - THROW_IF_FAILED(fontCollection->FindFamilyName(requestedFaceName, &index, &exists)); - } - } - THROW_HR_IF(DWRITE_E_NOFONT, !exists); wil::com_ptr fontFamily; diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 94434bffc9c..b8fa3ac0d59 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -470,7 +470,6 @@ namespace Microsoft::Console::Render::Atlas wil::com_ptr systemFontFallback; wil::com_ptr systemFontFallback1; // optional, might be nullptr wil::com_ptr textAnalyzer; - wil::com_ptr renderingParams; std::function warningCallback; std::function swapChainChangedCallback; diff --git a/src/renderer/dx/DxFontInfo.cpp b/src/renderer/dx/DxFontInfo.cpp index 4945939fd86..b1d4844a601 100644 --- a/src/renderer/dx/DxFontInfo.cpp +++ b/src/renderer/dx/DxFontInfo.cpp @@ -127,15 +127,6 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, { face = _FindFontFace(localeName); - if constexpr (Feature_NearbyFontLoading::IsEnabled()) - { - if (!face) - { - _fontCollection = FontCache::GetCached(); - face = _FindFontFace(localeName); - } - } - if (!face) { // If we missed, try looking a little more by trimming the last word off the requested family name a few times. @@ -167,6 +158,15 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, } CATCH_LOG(); + if constexpr (Feature_NearbyFontLoading::IsEnabled()) + { + if (!face) + { + _fontCollection = FontCache::GetCached(); + face = _FindFontFace(localeName); + } + } + // Alright, if our quick shot at trimming didn't work either... // move onto looking up a font from our hard-coded list of fonts // that should really always be available. From 95e4d0bbe0c633859a0ba288d060ed22d917f64a Mon Sep 17 00:00:00 2001 From: Taha Haksal <91548254+TahaHaksal@users.noreply.github.com> Date: Tue, 7 Nov 2023 01:42:56 +0300 Subject: [PATCH 003/603] Added selectionBackground to light color schemes (#16243) Add a selectionBackground property which is set to the scheme's brightBlack too all 3 of the light color schemes. Related to #8716 It does not close the bug because as mentioned in the issue, when you input numbers, they seem to be invisible in the light color schemes and selecting them with the cursor doesn't reveal them. (cherry picked from commit a5c269b2801a42f833f7a611ab7c624648f37054) Service-Card-Id: 91033167 Service-Version: 1.19 --- src/cascadia/TerminalSettingsModel/defaults.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index cdd5d6e78dd..360f37afaad 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -174,6 +174,7 @@ "foreground": "#383A42", "background": "#FAFAFA", "cursorColor": "#4F525D", + "selectionBackground": "#4F525D", "black": "#383A42", "red": "#E45649", "green": "#50A14F", @@ -218,6 +219,7 @@ "foreground": "#657B83", "background": "#FDF6E3", "cursorColor": "#002B36", + "selectionBackground": "#073642", "black": "#002B36", "red": "#DC322F", "green": "#859900", @@ -262,6 +264,7 @@ "foreground": "#555753", "background": "#FFFFFF", "cursorColor": "#000000", + "selectionBackground": "#555753", "black": "#000000", "red": "#CC0000", "green": "#4E9A06", From 83a2bc181a154e744b8b2a76bc1d7301a8d07917 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Nov 2023 16:45:24 -0600 Subject: [PATCH 004/603] Another theoretical fix for a crash (#16267) For history: > This is MSFT:46763065 internally. Dumps show this repros on 1.19 too. > > This was previously #16061 which had a theoretical fix in #16065. Looks like you're on Terminal Stable v1.18.2822.0, and https://github.com/microsoft/terminal/releases/tag/v1.18.2822.0 is supposed to have had that fix in it. Dang. > well this is embarrassing ... I never actually checked if we _still had a `_window`_. We're alive, yay! But we're still in the middle of refrigerating. So, there's no HWND anymore Attempt to fix this by actually ensuring there's a `_window` in `AppHost::_WindowInitializedHandler` Closes #16235 (cherry picked from commit 59dcbbe0e94814b94a7bfc5ae189d36c4bb0436f) Service-Card-Id: 91041359 Service-Version: 1.19 --- src/cascadia/WindowsTerminal/AppHost.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 5af3a80d50a..7cae9959998 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -979,7 +979,8 @@ winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowL co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); const auto strongThis = weakThis.lock(); - if (!strongThis) + // GH #16235: If we don't have a window logic, we're already refrigerating, and won't have our _window either. + if (!strongThis || _windowLogic == nullptr) { co_return layoutJson; } @@ -1267,7 +1268,8 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation: co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); const auto strongThis = weakThis.lock(); - if (!strongThis) + // GH #16235: If we don't have a window logic, we're already refrigerating, and won't have our _window either. + if (!strongThis || _windowLogic == nullptr) { co_return; } @@ -1431,7 +1433,7 @@ winrt::fire_and_forget AppHost::_WindowInitializedHandler(const winrt::Windows:: // If we're gone on the other side of this co_await, well, that's fine. Just bail. const auto strongThis = weakThis.lock(); - if (!strongThis) + if (!strongThis || _window == nullptr) { co_return; } From db27348e27e9989eaae75c61517aa52921390c12 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 7 Nov 2023 18:51:13 +0100 Subject: [PATCH 005/603] Fix tabs being printed in cmd.exe prompts (#16273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A late change in #16105 wrapped `_buffer` into a class to better track its dirty state, but I failed to notice that in this one instance we intentionally manipulated `_buffer` without marking it as dirty. This fixes the issue by adding a call to `MarkAsClean()`. This changeset also adds the test instructions from #15783 as a document to this repository. I've extended the list with two bugs we've found in the implementation since then. ## Validation Steps Performed * In cmd.exe, with an empty prompt in an empty directory: Pressing tab produces an audible bing and prints no text ✅ (cherry picked from commit 7a8dd90294e53b9d5466ff755611d13f4249ce0b) Service-Card-Id: 91033502 Service-Version: 1.19 --- doc/COOKED_READ_DATA.md | 90 +++++++++++++++++++++++++++++++++++++ src/host/readDataCooked.cpp | 7 ++- 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 doc/COOKED_READ_DATA.md diff --git a/doc/COOKED_READ_DATA.md b/doc/COOKED_READ_DATA.md new file mode 100644 index 00000000000..113a51bb749 --- /dev/null +++ b/doc/COOKED_READ_DATA.md @@ -0,0 +1,90 @@ +# COOKED_READ_DATA, aka conhost's readline implementation + +## Test instructions + +All of the following ✅ marks must be fulfilled during manual testing: +* ASCII input +* Chinese input (中文維基百科) ❔ + * Resizing the window properly wraps/unwraps wide glyphs ❌ + Broken due to `TextBuffer::Reflow` bugs +* Surrogate pair input (🙂) ❔ + * Resizing the window properly wraps/unwraps surrogate pairs ❌ + Broken due to `TextBuffer::Reflow` bugs +* In cmd.exe + * Create 2 file: "a😊b.txt" and "a😟b.txt" + * Press tab: Autocomplete to "a😊b.txt" ✅ + * Navigate the cursor right past the "a" + * Press tab twice: Autocomplete to "a😟b.txt" ✅ +* Backspace deletes preceding glyphs ✅ +* Ctrl+Backspace deletes preceding words ✅ +* Escape clears input ✅ +* Home navigates to start ✅ +* Ctrl+Home deletes text between cursor and start ✅ +* End navigates to end ✅ +* Ctrl+End deletes text between cursor and end ✅ +* Left navigates over previous code points ✅ +* Ctrl+Left navigates to previous word-starts ✅ +* Right and F1 navigate over next code points ✅ + * Pressing right at the end of input copies characters + from the previous command ✅ +* Ctrl+Right navigates to next word-ends ✅ +* Insert toggles overwrite mode ✅ +* Delete deletes next code point ✅ +* Up and F5 cycle through history ✅ + * Doesn't crash with no history ✅ + * Stops at first entry ✅ +* Down cycles through history ✅ + * Doesn't crash with no history ✅ + * Stops at last entry ✅ +* PageUp retrieves the oldest command ✅ +* PageDown retrieves the newest command ✅ +* F2 starts "copy to char" prompt ✅ + * Escape dismisses prompt ✅ + * Typing a character copies text from the previous command up + until that character into the current buffer (acts identical + to F3, but with automatic character search) ✅ +* F3 copies the previous command into the current buffer, + starting at the current cursor position, + for as many characters as possible ✅ + * Doesn't erase trailing text if the current buffer + is longer than the previous command ✅ + * Puts the cursor at the end of the copied text ✅ +* F4 starts "copy from char" prompt ✅ + * Escape dismisses prompt ✅ + * Erases text between the current cursor position and the + first instance of a given char (but not including it) ✅ +* F6 inserts Ctrl+Z ✅ +* F7 without modifiers starts "command list" prompt ✅ + * Escape dismisses prompt ✅ + * Minimum size of 40x10 characters ✅ + * Width expands to fit the widest history command ✅ + * Height expands up to 20 rows with longer histories ✅ + * F9 starts "command number" prompt ✅ + * Left/Right paste replace the buffer with the given command ✅ + * And put cursor at the end of the buffer ✅ + * Up/Down navigate selection through history ✅ + * Stops at start/end with <10 entries ✅ + * Stops at start/end with >20 entries ✅ + * Wide text rendering during pagination with >20 entries ✅ + * Shift+Up/Down moves history items around ✅ + * Home navigates to first entry ✅ + * End navigates to last entry ✅ + * PageUp navigates by 20 items at a time or to first ✅ + * PageDown navigates by 20 items at a time or to last ✅ +* Alt+F7 clears command history ✅ +* F8 cycles through commands that start with the same text as + the current buffer up until the current cursor position ✅ + * Doesn't crash with no history ✅ +* F9 starts "command number" prompt ✅ + * Escape dismisses prompt ✅ + * Ignores non-ASCII-decimal characters ✅ + * Allows entering between 1 and 5 digits ✅ + * Pressing Enter fetches the given command from the history ✅ +* Alt+F10 clears doskey aliases ✅ +* In cmd.exe, with an empty prompt in an empty directory: + Pressing tab produces an audible bing and prints no text ✅ +* When Narrator is enabled, in cmd.exe: + * Typing individual characters announces only + exactly each character that is being typed ✅ + * Backspacing at the end of a prompt announces + only exactly each deleted character ✅ diff --git a/src/host/readDataCooked.cpp b/src/host/readDataCooked.cpp index 3644dfe78db..c842bab312d 100644 --- a/src/host/readDataCooked.cpp +++ b/src/host/readDataCooked.cpp @@ -436,14 +436,17 @@ void COOKED_READ_DATA::_handleChar(wchar_t wch, const DWORD modifiers) if (_ctrlWakeupMask != 0 && wch < L' ' && (_ctrlWakeupMask & (1 << wch))) { - _flushBuffer(); - // The old implementation (all the way since the 90s) overwrote the character at the current cursor position with the given wch. // But simultaneously it incremented the buffer length, which would have only worked if it was written at the end of the buffer. // Press tab past the "f" in the string "foo" and you'd get "f\to " (a trailing whitespace; the initial contents of the buffer back then). // It's unclear whether the original intention was to write at the end of the buffer at all times or to implement an insert mode. // I went with insert mode. + // + // It is important that we don't actually print that character out though, as it's only for the calling application to see. + // That's why we flush the contents before the insertion and then ensure that the _flushBuffer() call in Read() exits early. + _flushBuffer(); _buffer.Replace(_buffer.GetCursorPosition(), 0, &wch, 1); + _buffer.MarkAsClean(); _controlKeyState = modifiers; _transitionState(State::DoneWithWakeupMask); From 2ac5e8670a90b329dda9136b1fe94ef3a17b0a41 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Nov 2023 14:35:16 -0600 Subject: [PATCH 006/603] Defer package updates while the Terminal is running (#16250) Adds ```xml defer ``` to our `Package.Properties` for all our packages. This was added in the September 2023 OS release of Windows 11. Apparently, this just works now? I did update VS, but I don't _think_ that updated the SDK. I have no idea how it updated the manifest definitions. Closes #3915 Closes #6726 (cherry picked from commit 077d63e6a34e392bb497d59768c551f7c099baaf) Service-Card-Id: 91033136 Service-Version: 1.19 --- src/cascadia/CascadiaPackage/Package-Can.appxmanifest | 4 +++- src/cascadia/CascadiaPackage/Package-Dev.appxmanifest | 4 +++- src/cascadia/CascadiaPackage/Package-Pre.appxmanifest | 4 +++- src/cascadia/CascadiaPackage/Package.appxmanifest | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/cascadia/CascadiaPackage/Package-Can.appxmanifest b/src/cascadia/CascadiaPackage/Package-Can.appxmanifest index d72c96e1405..77bf2fc1a0d 100644 --- a/src/cascadia/CascadiaPackage/Package-Can.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Can.appxmanifest @@ -8,13 +8,14 @@ xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" + xmlns:uap17="http://schemas.microsoft.com/appx/manifest/uap/windows10/17" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5" xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10" - IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization"> + IgnorableNamespaces="uap mp rescap uap3 uap17 desktop6 virtualization"> HKEY_CURRENT_USER\Console\%%Startup + defer diff --git a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest index 4a0735bb206..d8ceefaeef6 100644 --- a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest @@ -7,6 +7,7 @@ xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" + xmlns:uap17="http://schemas.microsoft.com/appx/manifest/uap/windows10/17" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5" @@ -14,7 +15,7 @@ xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" - IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization"> + IgnorableNamespaces="uap mp rescap uap3 uap17 desktop6 virtualization"> HKEY_CURRENT_USER\Console\%%Startup + defer diff --git a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest index 98fb12b9455..3a7bf26e0f7 100644 --- a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest @@ -9,13 +9,14 @@ xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:uap7="http://schemas.microsoft.com/appx/manifest/uap/windows10/7" + xmlns:uap17="http://schemas.microsoft.com/appx/manifest/uap/windows10/17" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5" xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10" - IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization"> + IgnorableNamespaces="uap mp rescap uap3 uap17 desktop6 virtualization"> HKEY_CURRENT_USER\Console\%%Startup + defer diff --git a/src/cascadia/CascadiaPackage/Package.appxmanifest b/src/cascadia/CascadiaPackage/Package.appxmanifest index c123778d1e9..f04863da27c 100644 --- a/src/cascadia/CascadiaPackage/Package.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package.appxmanifest @@ -9,13 +9,14 @@ xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:uap7="http://schemas.microsoft.com/appx/manifest/uap/windows10/7" + xmlns:uap17="http://schemas.microsoft.com/appx/manifest/uap/windows10/17" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5" xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10" - IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization"> + IgnorableNamespaces="uap mp rescap uap3 uap17 desktop6 virtualization"> HKEY_CURRENT_USER\Console\%%Startup + defer From 4bbfa0570abf14427fdb1c0a9aecd27852c815bf Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 8 Nov 2023 17:28:07 +0100 Subject: [PATCH 007/603] Fix deadlocks due to holding locks across WriteFile calls (#16224) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a number of bugs introduced in 4370da9, all of which are of the same kind: Holding the terminal lock across `WriteFile` calls into the ConPTY pipe. This is problematic, because the pipe has a tiny buffer size of just 4KiB and ConPTY may respond on its output pipe, before the entire buffer given to `WriteFile` has been emptied. When the ConPTY output thread then tries to acquire the terminal lock to begin parsing the VT output, we get ourselves a proper deadlock (cross process too!). The solution is to tease `Terminal` further apart into code that is thread-safe and code that isn't. Functions like `SendKeyEvent` so far have mixed them into one, because when they get called by `ControlCore` they both, processed the data (not thread-safe as it accesses VT state) and also sent that data back into `ControlCore` through a callback which then indirectly called into the `ConptyConnection` which calls `WriteFile`. Instead, we now return the data that needs to be sent from these functions, and `ControlCore` is free to release the lock and then call into the connection, which may then block indefinitely. ## Validation Steps Performed * Start nvim in WSL * Press `i` to enter the regular Insert mode * Paste 1MB of text * Doesn't deadlock ✅ (cherry picked from commit 71a1a97a9aa7b23a0c0f21b2f9e437b051cb5238) Service-Card-Id: 91043521 Service-Version: 1.19 --- src/cascadia/TerminalControl/ControlCore.cpp | 162 ++++++++++++------ src/cascadia/TerminalControl/HwndTerminal.cpp | 69 +++++--- src/cascadia/TerminalCore/ITerminalInput.hpp | 9 +- src/cascadia/TerminalCore/Terminal.cpp | 81 ++++----- src/cascadia/TerminalCore/Terminal.hpp | 25 +-- .../ConptyRoundtripTests.cpp | 2 +- .../UnitTests_TerminalCore/InputTest.cpp | 43 ++--- .../ScreenSizeLimitsTest.cpp | 16 +- .../UnitTests_TerminalCore/ScrollTest.cpp | 2 +- .../UnitTests_TerminalCore/SelectionTest.cpp | 44 ++--- .../TerminalApiTest.cpp | 20 +-- .../TerminalBufferTests.cpp | 2 +- 12 files changed, 270 insertions(+), 205 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 7925fc16eb8..734e1b98d2a 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -10,11 +10,11 @@ #include #include +#include #include #include #include "EventArgs.h" -#include "../../types/inc/GlyphWidth.hpp" #include "../../buffer/out/search.h" #include "../../renderer/atlas/AtlasEngine.h" #include "../../renderer/dx/DxRenderer.hpp" @@ -443,6 +443,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void ControlCore::_sendInputToConnection(std::wstring_view wstr) { + if (wstr.empty()) + { + return; + } + + // The connection may call functions like WriteFile() which may block indefinitely. + // It's important we don't hold any mutexes across such calls. + _terminal->_assertUnlocked(); + if (_isReadOnly) { _raiseReadOnlyWarning(); @@ -492,8 +501,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation _handleControlC(); } - const auto lock = _terminal->LockForWriting(); - return _terminal->SendCharEvent(ch, scanCode, modifiers); + TerminalInput::OutputType out; + { + const auto lock = _terminal->LockForReading(); + out = _terminal->SendCharEvent(ch, scanCode, modifiers); + } + if (out) + { + _sendInputToConnection(*out); + return true; + } + return false; } void ControlCore::_handleControlC() @@ -602,46 +620,56 @@ namespace winrt::Microsoft::Terminal::Control::implementation const ControlKeyStates modifiers, const bool keyDown) { - const auto lock = _terminal->LockForWriting(); + if (!vkey) + { + return true; + } - // Update the selection, if it's present - // GH#8522, GH#3758 - Only modify the selection on key _down_. If we - // modify on key up, then there's chance that we'll immediately dismiss - // a selection created by an action bound to a keydown. - if (_shouldTryUpdateSelection(vkey) && keyDown) + TerminalInput::OutputType out; { - // try to update the selection - if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) }) - { - _terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers); - _updateSelectionUI(); - return true; - } + const auto lock = _terminal->LockForWriting(); - // GH#8791 - don't dismiss selection if Windows key was also pressed as a key-combination. - if (!modifiers.IsWinPressed()) + // Update the selection, if it's present + // GH#8522, GH#3758 - Only modify the selection on key _down_. If we + // modify on key up, then there's chance that we'll immediately dismiss + // a selection created by an action bound to a keydown. + if (_shouldTryUpdateSelection(vkey) && keyDown) { - _terminal->ClearSelection(); - _updateSelectionUI(); - } + // try to update the selection + if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) }) + { + _terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers); + _updateSelectionUI(); + return true; + } - // When there is a selection active, escape should clear it and NOT flow through - // to the terminal. With any other keypress, it should clear the selection AND - // flow through to the terminal. - if (vkey == VK_ESCAPE) - { - return true; + // GH#8791 - don't dismiss selection if Windows key was also pressed as a key-combination. + if (!modifiers.IsWinPressed()) + { + _terminal->ClearSelection(); + _updateSelectionUI(); + } + + // When there is a selection active, escape should clear it and NOT flow through + // to the terminal. With any other keypress, it should clear the selection AND + // flow through to the terminal. + if (vkey == VK_ESCAPE) + { + return true; + } } - } - // If the terminal translated the key, mark the event as handled. - // This will prevent the system from trying to get the character out - // of it and sending us a CharacterReceived event. - return vkey ? _terminal->SendKeyEvent(vkey, - scanCode, - modifiers, - keyDown) : - true; + // If the terminal translated the key, mark the event as handled. + // This will prevent the system from trying to get the character out + // of it and sending us a CharacterReceived event. + out = _terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown); + } + if (out) + { + _sendInputToConnection(*out); + return true; + } + return false; } bool ControlCore::SendMouseEvent(const til::point viewportPos, @@ -650,8 +678,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation const short wheelDelta, const TerminalInput::MouseButtonState state) { - const auto lock = _terminal->LockForWriting(); - return _terminal->SendMouseEvent(viewportPos, uiButton, states, wheelDelta, state); + TerminalInput::OutputType out; + { + const auto lock = _terminal->LockForReading(); + out = _terminal->SendMouseEvent(viewportPos, uiButton, states, wheelDelta, state); + } + if (out) + { + _sendInputToConnection(*out); + return true; + } + return false; } void ControlCore::UserScrollViewport(const int viewTop) @@ -1324,8 +1361,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation // before sending it over the terminal's connection. void ControlCore::PasteText(const winrt::hstring& hstr) { + using namespace ::Microsoft::Console::Utils; + + auto filtered = FilterStringForPaste(hstr, CarriageReturnNewline | ControlCodes); + if (BracketedPasteEnabled()) + { + filtered.insert(0, L"\x1b[200~"); + filtered.append(L"\x1b[201~"); + } + + // It's important to not hold the terminal lock while calling this function as sending the data may take a long time. + _sendInputToConnection(filtered); + const auto lock = _terminal->LockForWriting(); - _terminal->WritePastedText(hstr); _terminal->ClearSelection(); _updateSelectionUI(); _terminal->TrySnapOnInput(); @@ -1894,17 +1942,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto endPoint = goRight ? clampedClick : cursorPos; const auto delta = _terminal->GetTextBuffer().GetCellDistance(startPoint, endPoint); - const WORD key = goRight ? VK_RIGHT : VK_LEFT; + + std::wstring buffer; + const auto append = [&](TerminalInput::OutputType&& out) { + if (out) + { + buffer.append(std::move(*out)); + } + }; + // Send an up and a down once per cell. This won't // accurately handle wide characters, or continuation // prompts, or cases where a single escape character in the // command (e.g. ^[) takes up two cells. for (size_t i = 0u; i < delta; i++) { - _terminal->SendKeyEvent(key, 0, {}, true); - _terminal->SendKeyEvent(key, 0, {}, false); + append(_terminal->SendKeyEvent(key, 0, {}, true)); + append(_terminal->SendKeyEvent(key, 0, {}, false)); } + + _sendInputToConnection(buffer); } } } @@ -1915,7 +1973,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - Updates the renderer's representation of the selection as well as the selection marker overlay in TermControl void ControlCore::_updateSelectionUI() { - const auto lock = _terminal->LockForWriting(); _renderer->TriggerSelection(); // only show the markers if we're doing a keyboard selection or in mark mode const bool showMarkers{ _terminal->SelectionMode() >= ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Keyboard }; @@ -2247,14 +2304,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_focusChanged(bool focused) { - // GH#13461 - temporarily turn off read-only mode, send the focus event, - // then turn it back on. Even in focus mode, focus events are fine to - // send. We don't want to pop a warning every time the control is - // focused. - const auto previous = std::exchange(_isReadOnly, false); - const auto restore = wil::scope_exit([&]() { _isReadOnly = previous; }); - const auto lock = _terminal->LockForWriting(); - _terminal->FocusChanged(focused); + TerminalInput::OutputType out; + { + const auto lock = _terminal->LockForReading(); + out = _terminal->FocusChanged(focused); + } + if (out && !out->empty()) + { + // _sendInputToConnection() asserts that we aren't in focus mode, + // but window focus events are always fine to send. + _connection.WriteInput(*out); + } } bool ControlCore::_isBackgroundTransparent() diff --git a/src/cascadia/TerminalControl/HwndTerminal.cpp b/src/cascadia/TerminalControl/HwndTerminal.cpp index a13ac5b3eda..6bec410cda8 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.cpp +++ b/src/cascadia/TerminalControl/HwndTerminal.cpp @@ -272,7 +272,7 @@ void HwndTerminal::RegisterScrollCallback(std::function cal void HwndTerminal::_WriteTextToConnection(const std::wstring_view input) noexcept { - if (!_pfnWriteCallback) + if (input.empty() || !_pfnWriteCallback) { return; } @@ -758,8 +758,17 @@ try WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed) }; - const auto lock = _terminal->LockForWriting(); - return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state); + TerminalInput::OutputType out; + { + const auto lock = _terminal->LockForReading(); + out = _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state); + } + if (out) + { + _WriteTextToConnection(*out); + return true; + } + return false; } catch (...) { @@ -784,8 +793,16 @@ try { _uiaProvider->RecordKeyEvent(vkey); } - const auto lock = _terminal->LockForWriting(); - _terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown); + + TerminalInput::OutputType out; + { + const auto lock = _terminal->LockForReading(); + out = _terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown); + } + if (out) + { + _WriteTextToConnection(*out); + } } CATCH_LOG(); @@ -797,31 +814,39 @@ try return; } - const auto lock = _terminal->LockForWriting(); - - if (_terminal->IsSelectionActive()) + TerminalInput::OutputType out; { - _ClearSelection(); - if (ch == UNICODE_ESC) + const auto lock = _terminal->LockForWriting(); + + if (_terminal->IsSelectionActive()) { - // ESC should clear any selection before it triggers input. - // Other characters pass through. + _ClearSelection(); + if (ch == UNICODE_ESC) + { + // ESC should clear any selection before it triggers input. + // Other characters pass through. + return; + } + } + + if (ch == UNICODE_TAB) + { + // TAB was handled as a keydown event (cf. Terminal::SendKeyEvent) return; } - } - if (ch == UNICODE_TAB) - { - // TAB was handled as a keydown event (cf. Terminal::SendKeyEvent) - return; - } + auto modifiers = getControlKeyState(); + if (WI_IsFlagSet(flags, ENHANCED_KEY)) + { + modifiers |= ControlKeyStates::EnhancedKey; + } - auto modifiers = getControlKeyState(); - if (WI_IsFlagSet(flags, ENHANCED_KEY)) + out = _terminal->SendCharEvent(ch, scanCode, modifiers); + } + if (out) { - modifiers |= ControlKeyStates::EnhancedKey; + _WriteTextToConnection(*out); } - _terminal->SendCharEvent(ch, scanCode, modifiers); } CATCH_LOG(); diff --git a/src/cascadia/TerminalCore/ITerminalInput.hpp b/src/cascadia/TerminalCore/ITerminalInput.hpp index 480494de1f6..6adbb0374b5 100644 --- a/src/cascadia/TerminalCore/ITerminalInput.hpp +++ b/src/cascadia/TerminalCore/ITerminalInput.hpp @@ -16,9 +16,10 @@ namespace Microsoft::Terminal::Core ITerminalInput& operator=(const ITerminalInput&) = default; ITerminalInput& operator=(ITerminalInput&&) = default; - virtual bool SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states, const bool keyDown) = 0; - virtual bool SendMouseEvent(const til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) = 0; - virtual bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) = 0; + virtual [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states, const bool keyDown) = 0; + virtual [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendMouseEvent(const til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) = 0; + virtual [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) = 0; + virtual [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType FocusChanged(const bool focused) = 0; [[nodiscard]] virtual HRESULT UserResize(const til::size size) noexcept = 0; virtual void UserScrollViewport(const int viewTop) = 0; @@ -26,8 +27,6 @@ namespace Microsoft::Terminal::Core virtual void TrySnapOnInput() = 0; - virtual void FocusChanged(const bool focused) = 0; - protected: ITerminalInput() = default; }; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index fe2417b1f97..3b086e3c143 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -30,6 +30,15 @@ Terminal::Terminal() _renderSettings.SetColorAlias(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND, RGB(0, 0, 0)); } +#pragma warning(suppress : 26455) // default constructor is throwing, too much effort to rearrange at this time. +Terminal::Terminal(TestDummyMarker) : + Terminal{} +{ +#ifndef NDEBUG + _suppressLockChecks = true; +#endif +} + void Terminal::Create(til::size viewportSize, til::CoordType scrollbackLines, Renderer& renderer) { _mutableViewport = Viewport::FromDimensions({ 0, 0 }, viewportSize); @@ -425,24 +434,6 @@ void Terminal::Write(std::wstring_view stringView) } } -void Terminal::WritePastedText(std::wstring_view stringView) -{ - const auto option = ::Microsoft::Console::Utils::FilterOption::CarriageReturnNewline | - ::Microsoft::Console::Utils::FilterOption::ControlCodes; - - auto filtered = ::Microsoft::Console::Utils::FilterStringForPaste(stringView, option); - if (IsXtermBracketedPasteModeEnabled()) - { - filtered.insert(0, L"\x1b[200~"); - filtered.append(L"\x1b[201~"); - } - - if (_pfnWriteInput) - { - _pfnWriteInput(filtered); - } -} - // Method Description: // - Attempts to snap to the bottom of the buffer, if SnapOnInput is true. Does // nothing if SnapOnInput is set to false, or we're already at the bottom of @@ -606,10 +597,10 @@ std::optional Terminal::GetHyperlinkIntervalFromViewportPos // Return Value: // - true if we translated the key event, and it should not be processed any further. // - false if we did not translate the key, and it should be processed into a character. -bool Terminal::SendKeyEvent(const WORD vkey, - const WORD scanCode, - const ControlKeyStates states, - const bool keyDown) +TerminalInput::OutputType Terminal::SendKeyEvent(const WORD vkey, + const WORD scanCode, + const ControlKeyStates states, + const bool keyDown) { // GH#6423 - don't snap on this key if the key that was pressed was a // modifier key. We'll wait for a real keystroke to snap to the bottom. @@ -627,7 +618,7 @@ bool Terminal::SendKeyEvent(const WORD vkey, // GH#7064 if (vkey == 0 || vkey >= 0xff) { - return false; + return {}; } // While not explicitly permitted, a wide range of software, including Windows' own touch keyboard, @@ -637,7 +628,7 @@ bool Terminal::SendKeyEvent(const WORD vkey, const auto sc = scanCode ? scanCode : _ScanCodeFromVirtualKey(vkey); if (sc == 0) { - return false; + return {}; } const auto isAltOnlyPressed = states.IsAltPressed() && !states.IsCtrlPressed(); @@ -665,11 +656,11 @@ bool Terminal::SendKeyEvent(const WORD vkey, // See the method description for more information. if (keyDown && !isAltOnlyPressed && vkey != VK_TAB && ch != UNICODE_NULL) { - return false; + return {}; } const auto keyEv = SynthesizeKeyEvent(keyDown, 1, vkey, sc, ch, states.Value()); - return _handleTerminalInputResult(_getTerminalInput().HandleKey(keyEv)); + return _getTerminalInput().HandleKey(keyEv); } // Method Description: @@ -686,14 +677,14 @@ bool Terminal::SendKeyEvent(const WORD vkey, // Return Value: // - true if we translated the key event, and it should not be processed any further. // - false if we did not translate the key, and it should be processed into a character. -bool Terminal::SendMouseEvent(til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const TerminalInput::MouseButtonState state) +TerminalInput::OutputType Terminal::SendMouseEvent(til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const TerminalInput::MouseButtonState state) { // GH#6401: VT applications should be able to receive mouse events from outside the // terminal buffer. This is likely to happen when the user drags the cursor offscreen. // We shouldn't throw away perfectly good events when they're offscreen, so we just // clamp them to be within the range [(0, 0), (W, H)]. _GetMutableViewport().ToOrigin().Clamp(viewportPos); - return _handleTerminalInputResult(_getTerminalInput().HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state)); + return _getTerminalInput().HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state); } // Method Description: @@ -708,7 +699,7 @@ bool Terminal::SendMouseEvent(til::point viewportPos, const unsigned int uiButto // Return Value: // - true if we translated the character event, and it should not be processed any further. // - false otherwise. -bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) +TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) { auto vkey = _TakeVirtualKeyFromLastKeyEvent(scanCode); if (vkey == 0 && scanCode != 0) @@ -746,7 +737,7 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro } const auto keyDown = SynthesizeKeyEvent(true, 1, vkey, scanCode, ch, states.Value()); - return _handleTerminalInputResult(_getTerminalInput().HandleKey(keyDown)); + return _getTerminalInput().HandleKey(keyDown); } // Method Description: @@ -757,9 +748,9 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro // - focused: true if we're focused, false otherwise. // Return Value: // - none -void Terminal::FocusChanged(const bool focused) +TerminalInput::OutputType Terminal::FocusChanged(const bool focused) { - _handleTerminalInputResult(_getTerminalInput().HandleFocus(focused)); + return _getTerminalInput().HandleFocus(focused); } // Method Description: @@ -882,20 +873,6 @@ catch (...) return UNICODE_INVALID; } -[[maybe_unused]] bool Terminal::_handleTerminalInputResult(TerminalInput::OutputType&& out) const -{ - if (out) - { - const auto& str = *out; - if (_pfnWriteInput && !str.empty()) - { - _pfnWriteInput(str); - } - return true; - } - return false; -} - // Method Description: // - It's possible for a single scan code on a keyboard to // produce different key codes depending on the keyboard state. @@ -933,7 +910,7 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept void Terminal::_assertLocked() const noexcept { #ifndef NDEBUG - if (!_readWriteLock.is_locked()) + if (!_suppressLockChecks && !_readWriteLock.is_locked()) { // __debugbreak() has the benefit over assert() that the debugger jumps right here to this line. // That way there's no need to first click any dialogues, etc. The disadvantage of course is that the @@ -943,6 +920,16 @@ void Terminal::_assertLocked() const noexcept #endif } +void Terminal::_assertUnlocked() const noexcept +{ +#ifndef NDEBUG + if (!_suppressLockChecks && _readWriteLock.is_locked()) + { + __debugbreak(); + } +#endif +} + // Method Description: // - Acquire a read lock on the terminal. // Return Value: diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 72c34d2f629..3e2ffe4c083 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -60,6 +60,10 @@ class Microsoft::Terminal::Core::Terminal final : using RenderSettings = Microsoft::Console::Render::RenderSettings; public: + struct TestDummyMarker + { + }; + static constexpr bool IsInputKey(WORD vkey) { return vkey != VK_CONTROL && @@ -77,6 +81,7 @@ class Microsoft::Terminal::Core::Terminal final : } Terminal(); + Terminal(TestDummyMarker); void Create(til::size viewportSize, til::CoordType scrollbackLines, @@ -98,9 +103,8 @@ class Microsoft::Terminal::Core::Terminal final : // Write comes from the PTY and goes to our parser to be stored in the output buffer void Write(std::wstring_view stringView); - // WritePastedText comes from our input and goes back to the PTY's input channel - void WritePastedText(std::wstring_view stringView); - + void _assertLocked() const noexcept; + void _assertUnlocked() const noexcept; [[nodiscard]] std::unique_lock LockForReading() const noexcept; [[nodiscard]] std::unique_lock LockForWriting() noexcept; til::recursive_ticket_lock_suspension SuspendLock() noexcept; @@ -167,9 +171,10 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region ITerminalInput // These methods are defined in Terminal.cpp - bool SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states, const bool keyDown) override; - bool SendMouseEvent(const til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override; - bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override; + [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states, const bool keyDown) override; + [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendMouseEvent(const til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override; + [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override; + [[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType FocusChanged(const bool focused) override; [[nodiscard]] HRESULT UserResize(const til::size viewportSize) noexcept override; void UserScrollViewport(const int viewTop) override; @@ -179,8 +184,6 @@ class Microsoft::Terminal::Core::Terminal final : bool IsTrackingMouseInput() const noexcept; bool ShouldSendAlternateScroll(const unsigned int uiButton, const int32_t delta) const noexcept; - void FocusChanged(const bool focused) override; - std::wstring GetHyperlinkAtViewportPosition(const til::point viewportPos); std::wstring GetHyperlinkAtBufferPosition(const til::point bufferPos); uint16_t GetHyperlinkIdAtViewportPosition(const til::point viewportPos); @@ -309,6 +312,10 @@ class Microsoft::Terminal::Core::Terminal final : const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace); #pragma endregion +#ifndef NDEBUG + bool _suppressLockChecks = false; +#endif + private: std::function _pfnWriteInput; std::function _pfnWarningBell; @@ -431,11 +438,9 @@ class Microsoft::Terminal::Core::Terminal final : static WORD _VirtualKeyFromCharacter(const wchar_t ch) noexcept; static wchar_t _CharacterFromKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states) noexcept; - [[maybe_unused]] bool _handleTerminalInputResult(::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType&& out) const; void _StoreKeyEvent(const WORD vkey, const WORD scanCode) noexcept; WORD _TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept; - void _assertLocked() const noexcept; Console::VirtualTerminal::TerminalInput& _getTerminalInput() noexcept; const Console::VirtualTerminal::TerminalInput& _getTerminalInput() const noexcept; diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index 48eeacf53c6..cf15e3ba191 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -87,7 +87,7 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final TEST_METHOD_SETUP(MethodSetup) { // STEP 1: Set up the Terminal - term = std::make_unique(); + term = std::make_unique(Terminal::TestDummyMarker{}); emptyRenderer = std::make_unique(term.get()); term->Create({ TerminalViewWidth, TerminalViewHeight }, 100, *emptyRenderer); diff --git a/src/cascadia/UnitTests_TerminalCore/InputTest.cpp b/src/cascadia/UnitTests_TerminalCore/InputTest.cpp index 8c6d3245162..01436fcddf3 100644 --- a/src/cascadia/UnitTests_TerminalCore/InputTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/InputTest.cpp @@ -5,40 +5,33 @@ #include #include "../cascadia/TerminalCore/Terminal.hpp" -#include "../renderer/inc/DummyRenderer.hpp" -#include "consoletaeftemplates.hpp" using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace Microsoft::Terminal::Core; -using namespace Microsoft::Console::Render; + +constexpr Microsoft::Console::VirtualTerminal::TerminalInput::OutputType unhandled() +{ + return {}; +} + +constexpr Microsoft::Console::VirtualTerminal::TerminalInput::OutputType escChar(const wchar_t wch) +{ + const wchar_t buffer[2]{ L'\x1b', wch }; + return { { &buffer[0], 2 } }; +} namespace TerminalCoreUnitTests { class InputTest { TEST_CLASS(InputTest); - TEST_CLASS_SETUP(ClassSetup) - { - DummyRenderer renderer; - term.Create({ 100, 100 }, 0, renderer); - auto inputFn = std::bind(&InputTest::_VerifyExpectedInput, this, std::placeholders::_1); - term.SetWriteInputCallback(inputFn); - return true; - }; TEST_METHOD(AltShiftKey); TEST_METHOD(InvalidKeyEvent); - void _VerifyExpectedInput(std::wstring_view actualInput) - { - VERIFY_ARE_EQUAL(expectedinput.size(), actualInput.size()); - VERIFY_ARE_EQUAL(expectedinput, actualInput); - }; - - Terminal term{}; - std::wstring expectedinput{}; + Terminal term{ Terminal::TestDummyMarker{} }; }; void InputTest::AltShiftKey() @@ -46,21 +39,17 @@ namespace TerminalCoreUnitTests // Tests GH:637 // Verify that Alt+a generates a lowercase a on the input - expectedinput = L"\x1b" - "a"; - VERIFY_IS_TRUE(term.SendCharEvent(L'a', 0, ControlKeyStates::LeftAltPressed)); + VERIFY_ARE_EQUAL(escChar(L'a'), term.SendCharEvent(L'a', 0, ControlKeyStates::LeftAltPressed)); // Verify that Alt+shift+a generates a uppercase a on the input - expectedinput = L"\x1b" - "A"; - VERIFY_IS_TRUE(term.SendCharEvent(L'A', 0, ControlKeyStates::LeftAltPressed | ControlKeyStates::ShiftPressed)); + VERIFY_ARE_EQUAL(escChar(L'A'), term.SendCharEvent(L'A', 0, ControlKeyStates::LeftAltPressed | ControlKeyStates::ShiftPressed)); } void InputTest::InvalidKeyEvent() { // Certain applications like AutoHotKey and its keyboard remapping feature, // send us key events using SendInput() whose values are outside of the valid range. - VERIFY_IS_FALSE(term.SendKeyEvent(0, 123, {}, true)); - VERIFY_IS_FALSE(term.SendKeyEvent(255, 123, {}, true)); + VERIFY_ARE_EQUAL(unhandled(), term.SendKeyEvent(0, 123, {}, true)); + VERIFY_ARE_EQUAL(unhandled(), term.SendKeyEvent(255, 123, {}, true)); } } diff --git a/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp index 8e9971abdff..667de3ad469 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp @@ -38,7 +38,7 @@ void ScreenSizeLimitsTest::ScreenWidthAndHeightAreClampedToBounds() // Negative values for initial visible row count or column count // are clamped to 1. Too-large positive values are clamped to SHRT_MAX. auto negativeColumnsSettings = winrt::make(10000, 9999999, -1234); - Terminal negativeColumnsTerminal; + Terminal negativeColumnsTerminal{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &negativeColumnsTerminal }; negativeColumnsTerminal.CreateFromSettings(negativeColumnsSettings, renderer); auto actualDimensions = negativeColumnsTerminal.GetViewport().Dimensions(); @@ -47,7 +47,7 @@ void ScreenSizeLimitsTest::ScreenWidthAndHeightAreClampedToBounds() // Zero values are clamped to 1 as well. auto zeroRowsSettings = winrt::make(10000, 0, 9999999); - Terminal zeroRowsTerminal; + Terminal zeroRowsTerminal{ Terminal::TestDummyMarker{} }; zeroRowsTerminal.CreateFromSettings(zeroRowsSettings, renderer); actualDimensions = zeroRowsTerminal.GetViewport().Dimensions(); VERIFY_ARE_EQUAL(actualDimensions.height, 1, L"Row count clamped to 1"); @@ -64,32 +64,32 @@ void ScreenSizeLimitsTest::ScrollbackHistorySizeIsClampedToBounds() // Zero history size is acceptable. auto noHistorySettings = winrt::make(0, visibleRowCount, 100); - Terminal noHistoryTerminal; + Terminal noHistoryTerminal{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &noHistoryTerminal }; noHistoryTerminal.CreateFromSettings(noHistorySettings, renderer); VERIFY_ARE_EQUAL(noHistoryTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"History size of 0 is accepted"); // Negative history sizes are clamped to zero. auto negativeHistorySizeSettings = winrt::make(-100, visibleRowCount, 100); - Terminal negativeHistorySizeTerminal; + Terminal negativeHistorySizeTerminal{ Terminal::TestDummyMarker{} }; negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(negativeHistorySizeTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"Negative history size is clamped to 0"); // History size + initial visible rows == SHRT_MAX is acceptable. auto maxHistorySizeSettings = winrt::make(SHRT_MAX - visibleRowCount, visibleRowCount, 100); - Terminal maxHistorySizeTerminal; + Terminal maxHistorySizeTerminal{ Terminal::TestDummyMarker{} }; maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(maxHistorySizeTerminal.GetTextBuffer().TotalRowCount(), SHRT_MAX, L"History size == SHRT_MAX - initial row count is accepted"); // History size + initial visible rows == SHRT_MAX + 1 will be clamped slightly. auto justTooBigHistorySizeSettings = winrt::make(SHRT_MAX - visibleRowCount + 1, visibleRowCount, 100); - Terminal justTooBigHistorySizeTerminal; + Terminal justTooBigHistorySizeTerminal{ Terminal::TestDummyMarker{} }; justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(justTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), SHRT_MAX, L"History size == 1 + SHRT_MAX - initial row count is clamped to SHRT_MAX - initial row count"); // Ridiculously large history sizes are also clamped. auto farTooBigHistorySizeSettings = winrt::make(99999999, visibleRowCount, 100); - Terminal farTooBigHistorySizeTerminal; + Terminal farTooBigHistorySizeTerminal{ Terminal::TestDummyMarker{} }; farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(farTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), SHRT_MAX, L"History size that is far too large is clamped to SHRT_MAX - initial row count"); } @@ -111,7 +111,7 @@ void ScreenSizeLimitsTest::ResizeIsClampedToBounds() auto settings = winrt::make(historySize, initialVisibleRowCount, initialVisibleColCount); Log::Comment(L"First create a terminal with fewer than SHRT_MAX lines"); - Terminal terminal; + Terminal terminal{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &terminal }; terminal.CreateFromSettings(settings, renderer); VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), historySize + initialVisibleRowCount); diff --git a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp index 12223e94a63..4b456e7cd5b 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp @@ -107,7 +107,7 @@ class TerminalCoreUnitTests::ScrollTest final TEST_METHOD_SETUP(MethodSetup) { - _term = std::make_unique<::Microsoft::Terminal::Core::Terminal>(); + _term = std::make_unique<::Microsoft::Terminal::Core::Terminal>(Terminal::TestDummyMarker{}); _scrollBarNotification = std::make_shared>(); _term->SetScrollPositionChangedCallback([scrollBarNotification = _scrollBarNotification](const int top, const int height, const int bottom) { diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index 894c5b7764f..195eb17fcc3 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -46,7 +46,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectUnit) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -59,7 +59,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectArea) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -113,7 +113,7 @@ namespace TerminalCoreUnitTests // Test SetSelectionAnchor(til::point) and SetSelectionEnd(til::point) // Behavior: clamp coord to viewport. auto ValidateSingleClickSelection = [&](til::CoordType scrollback, const til::inclusive_rect& expected) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 10, 10 }, scrollback, renderer); @@ -126,7 +126,7 @@ namespace TerminalCoreUnitTests // Behavior: clamp coord to viewport. // Then, do double click selection. auto ValidateDoubleClickSelection = [&](til::CoordType scrollback, const til::inclusive_rect& expected) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 10, 10 }, scrollback, renderer); @@ -138,7 +138,7 @@ namespace TerminalCoreUnitTests // Behavior: clamp coord to viewport. // Then, do triple click selection. auto ValidateTripleClickSelection = [&](til::CoordType scrollback, const til::inclusive_rect& expected) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 10, 10 }, scrollback, renderer); @@ -171,7 +171,7 @@ namespace TerminalCoreUnitTests - All selection expansion functions will operate as if they were performed at the boundary */ - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 10, 10 }, 0, renderer); @@ -213,7 +213,7 @@ namespace TerminalCoreUnitTests - All selection expansion functions will operate as if they were performed at the boundary */ - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 10, 10 }, 0, renderer); @@ -299,7 +299,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectBoxArea) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -335,7 +335,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectAreaAfterScroll) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; til::CoordType scrollbackLines = 5; term.Create({ 100, 100 }, scrollbackLines, renderer); @@ -385,7 +385,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectWideGlyph_Trailing) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -408,7 +408,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectWideGlyph_Leading) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -431,7 +431,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectWideGlyphsInBoxSelection) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -486,7 +486,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClick_GeneralCase) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -509,7 +509,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClick_Delimiter) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -530,7 +530,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClick_DelimiterClass) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -558,7 +558,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClickDrag_Right) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -587,7 +587,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClickDrag_Left) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -616,7 +616,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(TripleClick_GeneralCase) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -630,7 +630,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(TripleClickDrag_Horizontal) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -647,7 +647,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(TripleClickDrag_Vertical) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -675,7 +675,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(ShiftClick) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -792,7 +792,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(Pivot) { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp index 683740c155b..9f002db5098 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp @@ -48,7 +48,7 @@ using namespace TerminalCoreUnitTests; void TerminalApiTest::SetColorTableEntry() { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -67,7 +67,7 @@ void TerminalApiTest::SetColorTableEntry() // PrintString() is called with more code units than the buffer width. void TerminalApiTest::PrintStringOfSurrogatePairs() { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 3, renderer); @@ -134,7 +134,7 @@ void TerminalApiTest::PrintStringOfSurrogatePairs() void TerminalApiTest::CursorVisibility() { // GH#3093 - Cursor Visibility and On states shouldn't affect each other - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -166,7 +166,7 @@ void TerminalApiTest::CursorVisibility() void TerminalApiTest::CursorVisibilityViaStateMachine() { // This is a nearly literal copy-paste of ScreenBufferTests::TestCursorIsOn, adapted for the Terminal - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -218,7 +218,7 @@ void TerminalApiTest::CursorVisibilityViaStateMachine() void TerminalApiTest::CheckDoubleWidthCursor() { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -262,7 +262,7 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlink() { // This is a nearly literal copy-paste of ScreenBufferTests::TestAddHyperlink, adapted for the Terminal - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -288,7 +288,7 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlinkCustomId() { // This is a nearly literal copy-paste of ScreenBufferTests::TestAddHyperlinkCustomId, adapted for the Terminal - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -316,7 +316,7 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlinkCustomIdDifferentUri() { // This is a nearly literal copy-paste of ScreenBufferTests::TestAddHyperlinkCustomId, adapted for the Terminal - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -344,7 +344,7 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlinkCustomIdDifferentUri() void TerminalCoreUnitTests::TerminalApiTest::SetTaskbarProgress() { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); @@ -415,7 +415,7 @@ void TerminalCoreUnitTests::TerminalApiTest::SetTaskbarProgress() void TerminalCoreUnitTests::TerminalApiTest::SetWorkingDirectory() { - Terminal term; + Terminal term{ Terminal::TestDummyMarker{} }; DummyRenderer renderer{ &term }; term.Create({ 100, 100 }, 0, renderer); diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp index 1ea9e0be168..d789bfc5583 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp @@ -56,7 +56,7 @@ class TerminalCoreUnitTests::TerminalBufferTests final TEST_METHOD_SETUP(MethodSetup) { // STEP 1: Set up the Terminal - term = std::make_unique(); + term = std::make_unique(Terminal::TestDummyMarker{}); emptyRenderer = std::make_unique(term.get()); term->Create({ TerminalViewWidth, TerminalViewHeight }, TerminalHistoryLength, *emptyRenderer); return true; From 28a1ecbdaadafbd0226ad1ff211433046aa6eba5 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 8 Nov 2023 10:29:01 -0600 Subject: [PATCH 008/603] releng: add --first-parent to the scripts that use git log (#16279) It makes the output less cluttered and more correct (for example: ServicingPipeline no longer tries to service two copies of each commit if there's a merge in the history...) (cherry picked from commit 18b0ecbb2a1b22cbbc02961a9166db0e3a865ab1) Service-Card-Id: 91042450 Service-Version: 1.19 --- tools/Get-OSSConhostLog.ps1 | 2 +- tools/ReleaseEngineering/New-TerminalStackedChangelog.ps1 | 2 +- tools/ReleaseEngineering/ServicingPipeline.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/Get-OSSConhostLog.ps1 b/tools/Get-OSSConhostLog.ps1 index 4c206effa8e..46cd8aca1f2 100644 --- a/tools/Get-OSSConhostLog.ps1 +++ b/tools/Get-OSSConhostLog.ps1 @@ -43,7 +43,7 @@ Function Get-Git2GitIgnoresAsExcludes() { $Excludes = Get-Git2GitIgnoresAsExcludes Write-Verbose "IGNORING: $Excludes" -$Entries = & git log $RevisionRange "--pretty=format:%an%x1C%ae%x1C%s" -- $Excludes | +$Entries = & git log $RevisionRange --first-parent "--pretty=format:%an%x1C%ae%x1C%s" -- $Excludes | ConvertFrom-CSV -Delimiter "`u{001C}" -Header Author,Email,Subject Write-Verbose ("{0} unfiltered log entries" -f $Entries.Count) diff --git a/tools/ReleaseEngineering/New-TerminalStackedChangelog.ps1 b/tools/ReleaseEngineering/New-TerminalStackedChangelog.ps1 index 7f442f2ccf7..4d48b18bbf0 100644 --- a/tools/ReleaseEngineering/New-TerminalStackedChangelog.ps1 +++ b/tools/ReleaseEngineering/New-TerminalStackedChangelog.ps1 @@ -62,7 +62,7 @@ ForEach ($RevisionRange in $RevisionRanges) { # - %ae: author email # - %x1C: another FS # - %s: subject, the title of the commit - $NewEntries = & git log $RevisionRange "--pretty=format:%an%x1C%ae%x1C%s" | + $NewEntries = & git log $RevisionRange --first-parent "--pretty=format:%an%x1C%ae%x1C%s" | ConvertFrom-CSV -Delimiter "`u{001C}" -Header Author,Email,Subject $Entries += $NewEntries | % { [PSCustomObject]@{ diff --git a/tools/ReleaseEngineering/ServicingPipeline.ps1 b/tools/ReleaseEngineering/ServicingPipeline.ps1 index d7eb2cfebc2..2adf3843a0a 100644 --- a/tools/ReleaseEngineering/ServicingPipeline.ps1 +++ b/tools/ReleaseEngineering/ServicingPipeline.ps1 @@ -89,7 +89,7 @@ $Cards = Get-GithubProjectCard -ColumnId $ToPickColumn.id & git fetch --all 2>&1 | Out-Null -$Entries = @(& git log $SourceBranch --grep "(#\($($Cards.Number -Join "\|")\))" "--pretty=format:%H%x1C%s" | +$Entries = @(& git log $SourceBranch --first-parent --grep "(#\($($Cards.Number -Join "\|")\))" "--pretty=format:%H%x1C%s" | ConvertFrom-CSV -Delimiter "`u{001C}" -Header CommitID,Subject) [Array]::Reverse($Entries) From a7409ea4d3f8523ae05201e0e95c087a381bb3a0 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Wed, 8 Nov 2023 09:12:13 -0800 Subject: [PATCH 009/603] Update Azure Cloud Shell for their new URI format (#16247) The Azure cloud shell team made some API changes that required us to format our requests a little differently. This PR makes those changes (more info in the comments in the code) Closes #16098 (cherry picked from commit 5a9f3529d79d2168021be4b52202f9ad230de203) Service-Card-Id: 90985893 Service-Version: 1.19 --- .github/actions/spelling/allow/allow.txt | 4 ++ .../TerminalConnection/AzureConnection.cpp | 53 +++++++++++++++++-- .../TerminalConnection/AzureConnection.h | 2 +- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 28c46840207..a5d5bfcab3e 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -1,3 +1,4 @@ +aci admins allcolors Apc @@ -8,6 +9,7 @@ breadcrumbs bsd calt ccmp +ccon changelog clickable clig @@ -91,6 +93,7 @@ reserialize reserializes rlig runtimes +servicebus shcha slnt Sos @@ -116,6 +119,7 @@ vsdevcmd walkthrough walkthroughs We'd +westus wildcards XBox YBox diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index 3844979b488..708518509b5 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -398,6 +398,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation switch (bufferType) { + case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE: case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE: case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE: { @@ -797,7 +798,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // - an optional HTTP method (defaults to POST if content is present, GET otherwise) // Return value: // - the response from the server as a json value - WDJ::JsonObject AzureConnection::_SendRequestReturningJson(std::wstring_view uri, const WWH::IHttpContent& content, WWH::HttpMethod method) + WDJ::JsonObject AzureConnection::_SendRequestReturningJson(std::wstring_view uri, const WWH::IHttpContent& content, WWH::HttpMethod method, const Windows::Foundation::Uri referer) { if (!method) { @@ -810,6 +811,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation auto headers{ request.Headers() }; headers.Accept().TryParseAdd(L"application/json"); + if (referer) + { + headers.Referer(referer); + } + const auto response{ _httpClient.SendRequestAsync(request).get() }; const auto string{ response.Content().ReadAsStringAsync().get() }; const auto jsonResult{ WDJ::JsonObject::Parse(string) }; @@ -974,17 +980,56 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation auto uri{ fmt::format(L"{}terminals?cols={}&rows={}&version=2019-01-01&shell={}", _cloudShellUri, _initialCols, _initialRows, shellType) }; WWH::HttpStringContent content{ - L"", + L"{}", WSS::UnicodeEncoding::Utf8, // LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'" L"application/json" }; - const auto terminalResponse = _SendRequestReturningJson(uri, content); + const auto terminalResponse = _SendRequestReturningJson(uri, content, WWH::HttpMethod::Post(), Windows::Foundation::Uri(_cloudShellUri)); _terminalID = terminalResponse.GetNamedString(L"id"); + // we have to do some post-handling to get the proper socket endpoint + // the logic here is based on the way the cloud shell team itself does it + winrt::hstring finalSocketUri; + const std::wstring_view wCloudShellUri{ _cloudShellUri }; + + if (wCloudShellUri.find(L"servicebus") == std::wstring::npos) + { + // wCloudShellUri does not contain the word "servicebus", we can just use it to make the final URI + + // remove the "https" from the cloud shell URI + const auto uriWithoutProtocol = wCloudShellUri.substr(5); + + finalSocketUri = fmt::format(FMT_COMPILE(L"wss{}terminals/{}"), uriWithoutProtocol, _terminalID); + } + else + { + // if wCloudShellUri contains the word "servicebus", that means the returned socketUri is of the form + // wss://ccon-prod-westus-aci-03.servicebus.windows.net/cc-AAAA-AAAAAAAA//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + // we need to change it to: + // wss://ccon-prod-westus-aci-03.servicebus.windows.net/$hc/cc-AAAA-AAAAAAAA/terminals/aaaaaaaaaaaaaaaaaaaaaa + + const auto socketUri = terminalResponse.GetNamedString(L"socketUri"); + const std::wstring_view wSocketUri{ socketUri }; + + // get the substring up until the ".net" + const auto dotNetStart = wSocketUri.find(L".net"); + THROW_HR_IF(E_UNEXPECTED, dotNetStart == std::wstring::npos); + const auto dotNetEnd = dotNetStart + 4; + const auto wSocketUriBody = wSocketUri.substr(0, dotNetEnd); + + // get the portion between the ".net" and the "//" (this is the cc-AAAA-AAAAAAAA part) + const auto lastDoubleSlashPos = wSocketUri.find_last_of(L"//"); + THROW_HR_IF(E_UNEXPECTED, lastDoubleSlashPos == std::wstring::npos); + const auto wSocketUriMiddle = wSocketUri.substr(dotNetEnd, lastDoubleSlashPos - (dotNetEnd)); + + // piece together the final uri, adding in the "$hc" and "terminals" where needed + finalSocketUri = fmt::format(FMT_COMPILE(L"{}/$hc{}terminals/{}"), wSocketUriBody, wSocketUriMiddle, _terminalID); + } + // Return the uri - return terminalResponse.GetNamedString(L"socketUri"); + return winrt::hstring{ finalSocketUri }; } // Method description: diff --git a/src/cascadia/TerminalConnection/AzureConnection.h b/src/cascadia/TerminalConnection/AzureConnection.h index cedd76757af..9d179073019 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.h +++ b/src/cascadia/TerminalConnection/AzureConnection.h @@ -68,7 +68,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation void _WriteStringWithNewline(const std::wstring_view str); void _WriteCaughtExceptionRecord(); - winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr); + winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr, const winrt::Windows::Foundation::Uri referer = nullptr); void _setAccessToken(std::wstring_view accessToken); winrt::Windows::Data::Json::JsonObject _GetDeviceCode(); winrt::Windows::Data::Json::JsonObject _WaitForUser(const winrt::hstring& deviceCode, int pollInterval, int expiresIn); From 55a9874d5ca6cb626de42c9c2756f1e3283e988f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Nov 2023 19:10:35 -0600 Subject: [PATCH 010/603] Fix leak in buffering text for UIA when unfocused (#16251) Notes in #16217 have the investigation. TL;DR: we'd always buffer text. Even if we're disabled (unfocused). When we're disabled, we'd _never_ clear the buffered text. Oops. Closes #16217 (cherry picked from commit d14524cd4cc4970bb1b6456f9667e2dd661b9854) Service-Card-Id: 91033138 Service-Version: 1.19 --- src/renderer/uia/UiaRenderer.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index e58c227c561..f39bcbe4fbf 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -47,6 +47,12 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) : [[nodiscard]] HRESULT UiaEngine::Disable() noexcept { _isEnabled = false; + + // If we had buffered any text from NotifyNewText, dump it. When we do come + // back around to actually paint, we will just no-op. No sense in keeping + // the data buffered. + _newOutput = std::wstring{}; + return S_OK; } @@ -171,6 +177,10 @@ CATCH_RETURN(); [[nodiscard]] HRESULT UiaEngine::NotifyNewText(const std::wstring_view newText) noexcept try { + // GH#16217 - don't even buffer this text if we're disabled. We may never + // come around to write it out. + RETURN_HR_IF(S_FALSE, !_isEnabled); + if (!newText.empty()) { _newOutput.append(newText); From 65e19ddaf0edab64b6361a17ecb0f6e67179aaa6 Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Wed, 15 Nov 2023 21:32:10 +0000 Subject: [PATCH 011/603] Merged PR 9880704: [Git2Git] conhost: remove all EOL velocity keys I've also removed all of the supporting code. Related work items: MSFT-47555635 Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_we_adept_e4d2 f8ad0110fd81d1b848224158c8f95724f34b1db2 --- src/host/exe/exemain.cpp | 2 +- src/host/srvinit.cpp | 4 ++-- src/inc/conint.h | 6 ------ src/internal/stubs.cpp | 15 --------------- src/propsheet/console.cpp | 19 +++---------------- 5 files changed, 6 insertions(+), 40 deletions(-) diff --git a/src/host/exe/exemain.cpp b/src/host/exe/exemain.cpp index 7aaa45271d9..a30680a40a5 100644 --- a/src/host/exe/exemain.cpp +++ b/src/host/exe/exemain.cpp @@ -276,7 +276,7 @@ int CALLBACK wWinMain( { // Only try to register as a handoff target if we are NOT a part of Windows. #if TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED - if (args.ShouldRunAsComServer() && Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy()) + if (args.ShouldRunAsComServer()) { try { diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index 284f4483820..afb505ad144 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -64,7 +64,7 @@ try // Check if this conhost is allowed to delegate its activities to another. // If so, look up the registered default console handler. - if (Globals.delegationPair.IsUndecided() && Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy()) + if (Globals.delegationPair.IsUndecided()) { Globals.delegationPair = DelegationConfig::s_GetDelegationPair(); @@ -82,7 +82,7 @@ try // If we looked up the registered defterm pair, and it was left as the default (missing or {0}), // AND velocity is enabled for DxD, then we switch the delegation pair to Terminal and // mark that we should check that class for the marker interface later. - if (Globals.delegationPair.IsDefault() && Microsoft::Console::Internal::DefaultApp::CheckShouldTerminalBeDefault()) + if (Globals.delegationPair.IsDefault()) { Globals.delegationPair = DelegationConfig::TerminalDelegationPair; Globals.defaultTerminalMarkerCheckRequired = true; diff --git a/src/inc/conint.h b/src/inc/conint.h index 2a3c8d0fd54..68d9745fcfb 100644 --- a/src/inc/conint.h +++ b/src/inc/conint.h @@ -44,10 +44,4 @@ namespace Microsoft::Console::Internal { [[nodiscard]] HRESULT TrySetDarkMode(HWND hwnd) noexcept; } - - namespace DefaultApp - { - [[nodiscard]] bool CheckDefaultAppPolicy() noexcept; - [[nodiscard]] bool CheckShouldTerminalBeDefault() noexcept; - } } diff --git a/src/internal/stubs.cpp b/src/internal/stubs.cpp index f0cf811b017..c7b5dcfaff5 100644 --- a/src/internal/stubs.cpp +++ b/src/internal/stubs.cpp @@ -29,18 +29,3 @@ void EdpPolicy::AuditClipboard(const std::wstring_view /*destinationName*/) noex { return S_FALSE; } - -[[nodiscard]] bool DefaultApp::CheckDefaultAppPolicy() noexcept -{ - // True so propsheet will show configuration options but be sure that - // the open one won't attempt handoff from double click of OpenConsole.exe - return true; -} - -[[nodiscard]] bool DefaultApp::CheckShouldTerminalBeDefault() noexcept -{ - // False since setting Terminal as the default app is an OS feature and probably - // should not be done in the open source conhost. We can always decide to turn it - // on in the future though. - return false; -} diff --git a/src/propsheet/console.cpp b/src/propsheet/console.cpp index 68b146c2e11..d6058440590 100644 --- a/src/propsheet/console.cpp +++ b/src/propsheet/console.cpp @@ -96,10 +96,7 @@ void SaveConsoleSettingsIfNeeded(const HWND hwnd) gpStateInfo->FaceName[0] = TEXT('\0'); } - if (Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy()) - { - LOG_IF_FAILED(DelegationConfig::s_SetDefaultByPackage(g_selectedPackage)); - } + LOG_IF_FAILED(DelegationConfig::s_SetDefaultByPackage(g_selectedPackage)); if (gpStateInfo->LinkTitle != nullptr) { @@ -552,14 +549,7 @@ BOOL PopulatePropSheetPageArray(_Out_writes_(cPsps) PROPSHEETPAGE* pPsp, const s { pTerminalPage->dwSize = sizeof(PROPSHEETPAGE); pTerminalPage->hInstance = ghInstance; - if (Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy()) - { - pTerminalPage->pszTemplate = MAKEINTRESOURCE(DID_TERMINAL_WITH_DEFTERM); - } - else - { - pTerminalPage->pszTemplate = MAKEINTRESOURCE(DID_TERMINAL); - } + pTerminalPage->pszTemplate = MAKEINTRESOURCE(DID_TERMINAL_WITH_DEFTERM); pTerminalPage->pfnDlgProc = TerminalDlgProc; pTerminalPage->lParam = TERMINAL_PAGE_INDEX; pTerminalPage->dwFlags = PSP_DEFAULT; @@ -629,10 +619,7 @@ INT_PTR ConsolePropertySheet(__in HWND hWnd, __in PCONSOLE_STATE_INFO pStateInfo // Find the available default console/terminal packages // - if (Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy()) - { - LOG_IF_FAILED(DelegationConfig::s_GetAvailablePackages(g_availablePackages, g_selectedPackage)); - } + LOG_IF_FAILED(DelegationConfig::s_GetAvailablePackages(g_availablePackages, g_selectedPackage)); // // Get the current page number From 654b755161f2635a16e2878e54941aee8d552075 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 4 Dec 2023 21:14:26 +0100 Subject: [PATCH 012/603] Fix backspacing over control visualizers (#16400) During `!measureOnly` the old code would increment `distance` twice. Now it doesn't. :) Closes #16356 ## Validation Steps Performed See updated test instructions in `doc/COOKED_READ_DATA.md` --- doc/COOKED_READ_DATA.md | 6 ++++++ src/host/readDataCooked.cpp | 19 ++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/doc/COOKED_READ_DATA.md b/doc/COOKED_READ_DATA.md index 113a51bb749..f4b463f7aa6 100644 --- a/doc/COOKED_READ_DATA.md +++ b/doc/COOKED_READ_DATA.md @@ -15,6 +15,12 @@ All of the following ✅ marks must be fulfilled during manual testing: * Press tab: Autocomplete to "a😊b.txt" ✅ * Navigate the cursor right past the "a" * Press tab twice: Autocomplete to "a😟b.txt" ✅ +* Execute `printf(" "); gets(buffer);` in C (or equivalent) + * Press Tab, A, Ctrl+V, Tab, A ✅ + * The prompt is " A^V A" ✅ + * Cursor navigation works ✅ + * Backspacing/Deleting random parts of it works ✅ + * It never deletes the initial 4 spaces ✅ * Backspace deletes preceding glyphs ✅ * Ctrl+Backspace deletes preceding words ✅ * Escape clears input ✅ diff --git a/src/host/readDataCooked.cpp b/src/host/readDataCooked.cpp index c842bab312d..4869e2ad91a 100644 --- a/src/host/readDataCooked.cpp +++ b/src/host/readDataCooked.cpp @@ -938,22 +938,31 @@ ptrdiff_t COOKED_READ_DATA::_writeCharsImpl(const std::wstring_view& text, const const auto wch = *it; if (wch == UNICODE_TAB) { - const auto col = _getColumnAtRelativeCursorPosition(distance + cursorOffset); - const auto remaining = width - col; - distance += std::min(remaining, 8 - (col & 7)); buf[0] = L'\t'; len = 1; } else { // In the interactive mode we replace C0 control characters (0x00-0x1f) with ASCII representations like ^C (= 0x03). - distance += 2; buf[0] = L'^'; buf[1] = gsl::narrow_cast(wch + L'@'); len = 2; } - if (!measureOnly) + if (measureOnly) + { + if (wch == UNICODE_TAB) + { + const auto col = _getColumnAtRelativeCursorPosition(distance + cursorOffset); + const auto remaining = width - col; + distance += std::min(remaining, 8 - (col & 7)); + } + else + { + distance += 2; + } + } + else { distance += _writeCharsUnprocessed({ &buf[0], len }); } From abab8705fea32854a6b55153b5736f9fd9dacb66 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 15 Nov 2023 19:13:03 -0600 Subject: [PATCH 013/603] Add a magic incantation to tell the Store we support Server (#16306) I find it somewhat silly that (1) this isn't documented anywhere and (2) installing the "desktop experience" packages for Server doesn't automatically add support for the `Windows.Desktop` platform... Oh well. I'm going to roll this one out via Preview first, because if the store blows up on it I would rather it not be during Stable roll-out. (cherry picked from commit 86fb9b44787accd09c5943a506e27eb4c8e573c0) Service-Card-Id: 91098597 Service-Version: 1.19 --- src/cascadia/CascadiaPackage/Package-Dev.appxmanifest | 1 + src/cascadia/CascadiaPackage/Package-Pre.appxmanifest | 1 + src/cascadia/CascadiaPackage/Package.appxmanifest | 1 + 3 files changed, 3 insertions(+) diff --git a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest index d8ceefaeef6..53fc49c7833 100644 --- a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest @@ -39,6 +39,7 @@ + diff --git a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest index 3a7bf26e0f7..c7956c34576 100644 --- a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest @@ -40,6 +40,7 @@ + diff --git a/src/cascadia/CascadiaPackage/Package.appxmanifest b/src/cascadia/CascadiaPackage/Package.appxmanifest index f04863da27c..98c10c860a0 100644 --- a/src/cascadia/CascadiaPackage/Package.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package.appxmanifest @@ -40,6 +40,7 @@ + From 0d353d8be5e2510d8edfec1238c9f4c6f25b3594 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 16 Nov 2023 22:27:33 +0100 Subject: [PATCH 014/603] Fix nearby fonts for DxEngine again (#16323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nearby font loading has to be outside of the try/catch of the `_FindFontFace` call, because it'll throw for broken font files. But in my previous PR I had overlooked that the font variant loop modifies the only copy of the face name that we got and was in the same try/catch. That's bad, because once we get to the nearby search code, the face name will be invalid. This commit fixes the issue by wrapping each individual `_FindFontFace` call in a try/catch block. Closes #16322 ## Validation Steps Performed * Remove every single copy of Windows Terminal from your system * Manually clean up Cascadia .ttf files because they aren't gone * Destroy your registry by manually removing appx references (fun!) * Put the 4 Cascadia .ttf files into the Dev app AppX directory * Launch * No warning ✅ (cherry picked from commit b780b445284597c6fe9d423126e2afd90c329265) Service-Card-Id: 91114951 Service-Version: 1.19 --- src/renderer/dx/DxFontInfo.cpp | 71 ++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/src/renderer/dx/DxFontInfo.cpp b/src/renderer/dx/DxFontInfo.cpp index b1d4844a601..b0f79cde39a 100644 --- a/src/renderer/dx/DxFontInfo.cpp +++ b/src/renderer/dx/DxFontInfo.cpp @@ -126,44 +126,52 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, try { face = _FindFontFace(localeName); + } + CATCH_LOG(); - if (!face) + if constexpr (Feature_NearbyFontLoading::IsEnabled()) + { + try { - // If we missed, try looking a little more by trimming the last word off the requested family name a few times. - // Quite often, folks are specifying weights or something in the familyName and it causes failed resolution and - // an unexpected error dialog. We theoretically could detect the weight words and convert them, but this - // is the quick fix for the majority scenario. - // The long/full fix is backlogged to GH#9744 - // Also this doesn't count as a fallback because we don't want to annoy folks with the warning dialog over - // this resolution. - while (!face && !_familyName.empty()) + if (!face) { - const auto lastSpace = _familyName.find_last_of(UNICODE_SPACE); - - // value is unsigned and npos will be greater than size. - // if we didn't find anything to trim, leave. - if (lastSpace >= _familyName.size()) - { - break; - } - - // trim string down to just before the found space - // (space found at 6... trim from 0 for 6 length will give us 0-5 as the new string) - _familyName = _familyName.substr(0, lastSpace); - - // Try to find it with the shortened family name + _fontCollection = FontCache::GetCached(); face = _FindFontFace(localeName); } } + CATCH_LOG(); } - CATCH_LOG(); - if constexpr (Feature_NearbyFontLoading::IsEnabled()) + if (!face) { - if (!face) + // If we missed, try looking a little more by trimming the last word off the requested family name a few times. + // Quite often, folks are specifying weights or something in the familyName and it causes failed resolution and + // an unexpected error dialog. We theoretically could detect the weight words and convert them, but this + // is the quick fix for the majority scenario. + // The long/full fix is backlogged to GH#9744 + // Also this doesn't count as a fallback because we don't want to annoy folks with the warning dialog over + // this resolution. + while (!face && !_familyName.empty()) { - _fontCollection = FontCache::GetCached(); - face = _FindFontFace(localeName); + const auto lastSpace = _familyName.find_last_of(UNICODE_SPACE); + + // value is unsigned and npos will be greater than size. + // if we didn't find anything to trim, leave. + if (lastSpace >= _familyName.size()) + { + break; + } + + // trim string down to just before the found space + // (space found at 6... trim from 0 for 6 length will give us 0-5 as the new string) + _familyName = _familyName.substr(0, lastSpace); + + try + { + // Try to find it with the shortened family name + face = _FindFontFace(localeName); + } + CATCH_LOG(); } } @@ -176,7 +184,12 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, { _familyName = fallbackFace; - face = _FindFontFace(localeName); + try + { + face = _FindFontFace(localeName); + } + CATCH_LOG(); + if (face) { _didFallback = true; From 5ed7dc2f63a4322f8ef8631f48f378985b049ad1 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 21 Nov 2023 21:50:46 +0100 Subject: [PATCH 015/603] ConPTY: Fix a shutdown deadlock with WSL (#16340) Under normal circumstances this bug should be rare as far as I can observe it on my system. However, it does occur randomly. In short, WSL doesn't pass us anonymous pipes, but rather WSA sockets and those signal their graceful shutdown first before being closed later by returning a `lpNumberOfBytesRead` of 0 in the meantime. Additionally, `VtIo` synchronously pumps the input pipe to get the initial cursor position, but fails to check `_exitRequested`. And so even with the pipe handling fixed, `VtIo` will also deadlock, because it will never realize that `VtInputThread` is done reading. ## Validation Steps Performed * Build commit 376737e with this change and replace conhost with it Coincidentally it contains a bug (of as of yet unknown origin) due to which the initial cursor position loop in `VtIo` never completes. Thanks to this, we can easily provoke this issue. * Launch WSL in conhost and run an .exe inside it * Close the conhost window * Task manager shows that all conhost instances exit immediately (cherry picked from commit adb04729bcce7151f6380eded79e9408df9d1e3b) Service-Card-Id: 91152102 Service-Version: 1.19 --- src/host/VtInputThread.cpp | 89 +++++++++++++++----------------------- src/host/VtInputThread.hpp | 5 +-- src/host/VtIo.cpp | 3 +- 3 files changed, 36 insertions(+), 61 deletions(-) diff --git a/src/host/VtInputThread.cpp b/src/host/VtInputThread.cpp index 495a6e51d8b..4a4b773cb02 100644 --- a/src/host/VtInputThread.cpp +++ b/src/host/VtInputThread.cpp @@ -29,7 +29,6 @@ VtInputThread::VtInputThread(_In_ wil::unique_hfile hPipe, _hThread{}, _u8State{}, _dwThreadId{ 0 }, - _exitRequested{ false }, _pfnSetLookingForDSR{} { THROW_HR_IF(E_HANDLE, _hFile.get() == INVALID_HANDLE_VALUE); @@ -50,40 +49,6 @@ VtInputThread::VtInputThread(_In_ wil::unique_hfile hPipe, _pfnSetLookingForDSR = std::bind(&InputStateMachineEngine::SetLookingForDSR, engineRef, std::placeholders::_1); } -// Method Description: -// - Processes a string of input characters. The characters should be UTF-8 -// encoded, and will get converted to wstring to be processed by the -// input state machine. -// Arguments: -// - u8Str - the UTF-8 string received. -// Return Value: -// - S_OK on success, otherwise an appropriate failure. -[[nodiscard]] HRESULT VtInputThread::_HandleRunInput(const std::string_view u8Str) -{ - // Make sure to call the GLOBAL Lock/Unlock, not the gci's lock/unlock. - // Only the global unlock attempts to dispatch ctrl events. If you use the - // gci's unlock, when you press C-c, it won't be dispatched until the - // next console API call. For something like `powershell sleep 60`, - // that won't happen for 60s - LockConsole(); - auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); - - try - { - std::wstring wstr{}; - auto hr = til::u8u16(u8Str, wstr, _u8State); - // If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it. - if (FAILED(hr)) - { - return S_FALSE; - } - _pInputStateMachine->ProcessString(wstr); - } - CATCH_RETURN(); - - return S_OK; -} - // Function Description: // - Static function used for initializing an instance's ThreadProc. // Arguments: @@ -100,35 +65,50 @@ DWORD WINAPI VtInputThread::StaticVtInputThreadProc(_In_ LPVOID lpParameter) // Method Description: // - Do a single ReadFile from our pipe, and try and handle it. If handling // failed, throw or log, depending on what the caller wants. -// Arguments: -// - throwOnFail: If true, throw an exception if there was an error processing -// the input received. Otherwise, log the error. // Return Value: -// - -void VtInputThread::DoReadInput(const bool throwOnFail) +// - true if you should continue reading +bool VtInputThread::DoReadInput() { char buffer[256]; DWORD dwRead = 0; - auto fSuccess = !!ReadFile(_hFile.get(), buffer, ARRAYSIZE(buffer), &dwRead, nullptr); - - if (!fSuccess) + const auto ok = ReadFile(_hFile.get(), buffer, ARRAYSIZE(buffer), &dwRead, nullptr); + + // The ReadFile() documentations calls out that: + // > If the lpNumberOfBytesRead parameter is zero when ReadFile returns TRUE on a pipe, the other + // > end of the pipe called the WriteFile function with nNumberOfBytesToWrite set to zero. + // But I was unable to replicate any such behavior. I'm not sure it's true anymore. + // + // However, what the documentations fails to mention is that winsock2 (WSA) handles of the \Device\Afd type are + // transparently compatible with ReadFile() and the WSARecv() documentations contains this important information: + // > For byte streams, zero bytes having been read [..] indicates graceful closure and that no more bytes will ever be read. + // In other words, for pipe HANDLE of unknown type you should consider `lpNumberOfBytesRead == 0` as an exit indicator. + // + // Here, `dwRead == 0` fixes a deadlock when exiting conhost while being in use by WSL whose hypervisor pipes are WSA. + if (!ok || dwRead == 0) { - _exitRequested = true; - return; + return false; } - auto hr = _HandleRunInput({ buffer, gsl::narrow_cast(dwRead) }); - if (FAILED(hr)) + try { - if (throwOnFail) - { - _exitRequested = true; - } - else + // Make sure to call the GLOBAL Lock/Unlock, not the gci's lock/unlock. + // Only the global unlock attempts to dispatch ctrl events. If you use the + // gci's unlock, when you press C-c, it won't be dispatched until the + // next console API call. For something like `powershell sleep 60`, + // that won't happen for 60s + LockConsole(); + const auto unlock = wil::scope_exit([&] { UnlockConsole(); }); + + std::wstring wstr; + // If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it. + if (SUCCEEDED_LOG(til::u8u16({ buffer, gsl::narrow_cast(dwRead) }, wstr, _u8State))) { - LOG_IF_FAILED(hr); + _pInputStateMachine->ProcessString(wstr); } } + CATCH_LOG(); + + return true; } void VtInputThread::SetLookingForDSR(const bool looking) noexcept @@ -145,9 +125,8 @@ void VtInputThread::SetLookingForDSR(const bool looking) noexcept // InputStateMachineEngine. void VtInputThread::_InputThread() { - while (!_exitRequested) + while (DoReadInput()) { - DoReadInput(true); } ServiceLocator::LocateGlobals().getConsoleInformation().GetVtIo()->CloseInput(); } diff --git a/src/host/VtInputThread.hpp b/src/host/VtInputThread.hpp index 7c075b70025..7652a53887f 100644 --- a/src/host/VtInputThread.hpp +++ b/src/host/VtInputThread.hpp @@ -25,19 +25,16 @@ namespace Microsoft::Console [[nodiscard]] HRESULT Start(); static DWORD WINAPI StaticVtInputThreadProc(_In_ LPVOID lpParameter); - void DoReadInput(const bool throwOnFail); + bool DoReadInput(); void SetLookingForDSR(const bool looking) noexcept; private: - [[nodiscard]] HRESULT _HandleRunInput(const std::string_view u8Str); void _InputThread(); wil::unique_hfile _hFile; wil::unique_handle _hThread; DWORD _dwThreadId; - bool _exitRequested; - std::function _pfnSetLookingForDSR; std::unique_ptr _pInputStateMachine; diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index dd8b635efd9..c286a5e8aca 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -282,9 +282,8 @@ bool VtIo::IsUsingVt() const if (_lookingForCursorPosition && _pVtRenderEngine && _pVtInputThread) { LOG_IF_FAILED(_pVtRenderEngine->RequestCursor()); - while (_lookingForCursorPosition) + while (_lookingForCursorPosition && _pVtInputThread->DoReadInput()) { - _pVtInputThread->DoReadInput(false); } } From 3538a9f72b77569b2c60e551fbdb23677b868c1e Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 21 Nov 2023 21:57:56 +0100 Subject: [PATCH 016/603] Fix input buffering for A APIs (#16313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes an issue where character-wise reading of an input like "abc" would return "a" to the caller, store "b" as a partial translation (= wrong) and return "c" for the caller to store it for the next call. Closes #16223 Closes #16299 ## Validation Steps Performed * `ReadFile` with a buffer size of 1 returns inputs character by character without dropping any inputs ✅ --------- Co-authored-by: Dustin L. Howett (cherry picked from commit 63b3820a18be096a4b1950e335c9605267440734) Service-Card-Id: 91122022 Service-Version: 1.19 --- src/host/inputBuffer.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index 3a4e556e440..5150a21427d 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -4,13 +4,13 @@ #include "precomp.h" #include "inputBuffer.hpp" -#include "stream.h" -#include "../types/inc/GlyphWidth.hpp" - #include +#include #include "misc.h" +#include "stream.h" #include "../interactivity/inc/ServiceLocator.hpp" +#include "../types/inc/GlyphWidth.hpp" #define INPUT_BUFFER_DEFAULT_INPUT_MODE (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT) @@ -87,10 +87,10 @@ void InputBuffer::Consume(bool isUnicode, std::wstring_view& source, std::span(s.size()), &buffer[0], sizeof(buffer), nullptr, nullptr); THROW_LAST_ERROR_IF(length <= 0); std::string_view slice{ &buffer[0], gsl::narrow_cast(length) }; @@ -98,10 +98,24 @@ void InputBuffer::Consume(bool isUnicode, std::wstring_view& source, std::span Date: Tue, 28 Nov 2023 05:31:06 +0800 Subject: [PATCH 017/603] Fix Control+Space not sent to program running in terminal (#16298) Converts null byte to specific input event, so that it's properly delivered to the program running in the terminal. Closes #15939 (cherry picked from commit 8747a39a07dd601141a2ce2ee6eb076a8a5599b6) Service-Card-Id: 91201444 Service-Version: 1.19 --- src/host/inputBuffer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index 5150a21427d..abdfed1961f 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -830,6 +830,17 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType& for (const auto& wch : text) { + if (wch == UNICODE_NULL) + { + // Convert null byte back to input event with proper control state + const auto zeroKey = OneCoreSafeVkKeyScanW(0); + uint32_t ctrlState = 0; + WI_SetFlagIf(ctrlState, SHIFT_PRESSED, WI_IsFlagSet(zeroKey, 0x100)); + WI_SetFlagIf(ctrlState, LEFT_CTRL_PRESSED, WI_IsFlagSet(zeroKey, 0x200)); + WI_SetFlagIf(ctrlState, LEFT_ALT_PRESSED, WI_IsFlagSet(zeroKey, 0x400)); + _storage.push_back(SynthesizeKeyEvent(true, 1, LOBYTE(zeroKey), 0, wch, ctrlState)); + continue; + } _storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); } From d4292d16cc9256283898a93887ceb5246f36132f Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 27 Nov 2023 22:34:13 +0100 Subject: [PATCH 018/603] Fix scrolling with SetConsoleWindowInfo (#16334) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 81b7e54 caused a regression in `SetConsoleWindowInfo` and any other function that used the `WriteToScreen` helper. This is because it assumes that it can place the viewport anywhere randomly and it was written at a time where `TriggerScroll` didn't exist yet (there was no need for that (also not today, but that's being worked on)). Caching the viewport meant that `WriteToScreen`'s call to `TriggerRedraw` would pick up the viewport from the last rendered frame, which would cause the intersection of both to be potentially empty and nothing to be drawn on the screen. This commit reverts 81b7e54 as I found that it has no or negligible impact on performance at this point, likely due to the overall vastly better performance of conhost nowadays. Closes #15932 ## Validation Steps Performed * Scroll the viewport by entire pages worth of content using `SetConsoleWindowInfo` - see #15932 * The screen and scrollbars update immediately ✅ (cherry picked from commit 7a1b6f9d2a9d564375fdc9792c506937b6474e67) Service-Card-Id: 91152167 Service-Version: 1.19 --- src/renderer/base/renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 917c5ba05f9..2bf98404ae2 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -217,7 +217,7 @@ void Renderer::TriggerSystemRedraw(const til::rect* const prcDirtyClient) // - void Renderer::TriggerRedraw(const Viewport& region) { - auto view = _viewport; + auto view = _pData->GetViewport(); auto srUpdateRegion = region.ToExclusive(); // If the dirty region has double width lines, we need to double the size of From dd83ba62cfb1591b7e6f509b1ca2cfb0ff933c15 Mon Sep 17 00:00:00 2001 From: Adam Reynolds Date: Thu, 30 Nov 2023 01:58:41 -0800 Subject: [PATCH 019/603] Fixed crash when cloud shell provider timed out or was closed waiting for login (#16364) ## Summary of the Pull Request Cloud shell connection calls out to Azure to do a device code login. When the polling interval is exceeded or the tab is closed, the method doing the connection polling returns `nullptr`, and `AzureConnection` immediately tries to `GetNamedString` from it, causing a crash. This doesn't repro on Terminal Stable or Preview, suggesting it's pretty recent related to the update of this azureconnection. This is just a proposed fix, not sure if you want to do more extensive changes to the affected class or not, so marking this as a draft. ## References and Relevant Issues * N/A - encountered this while using the terminal myself ## PR Checklist/Validation Tested out a local dev build: - [x] Terminal doesn't crash when cloudshell polling interval exceeded - [x] Terminal doesn't crash when cloudshell tab closed while polling for Azure login (cherry picked from commit 0c4751ba30333e9a47d0992fe044242867ab86ad) Service-Card-Id: 91232784 Service-Version: 1.19 --- src/cascadia/TerminalConnection/AzureConnection.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index 708518509b5..11c49838f95 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -604,6 +604,15 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // Wait for user authentication and obtain the access/refresh tokens auto authenticatedResponse = _WaitForUser(devCode, pollInterval, expiresIn); + + // If user closed tab, `_WaitForUser` returns nullptr + // This also occurs if the connection times out, when polling time exceeds the expiry time + if (!authenticatedResponse) + { + _transitionToState(ConnectionState::Failed); + return; + } + _setAccessToken(authenticatedResponse.GetNamedString(L"access_token")); _refreshToken = authenticatedResponse.GetNamedString(L"refresh_token"); From 17d24bf0559731f281f17d43e5b19d7e1cb8a845 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 30 Nov 2023 15:55:06 +0100 Subject: [PATCH 020/603] Fix dwControlKeyState always including ENHANCED_KEY (#16335) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since all VT parameters are treated to be at least 1 (and 1 if they're absent or 0), `modifierParam > 0` was always true. This meant that `ENHANCED_KEY` was always being set. It's unclear why `ENHANCED_KEY` was used there, but it's likely not needed in general. Closes #16266 ## Validation Steps Performed * Can't test this unless we fix the win32 input mode issue #16343 ❌ (cherry picked from commit be9fc200c7b22c544103ec3304ca31c3b984031e) Service-Card-Id: 91159301 Service-Version: 1.19 --- src/terminal/parser/InputStateMachineEngine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 95fb5a3e509..7ea26ce9c84 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -794,7 +794,6 @@ DWORD InputStateMachineEngine::_GetModifier(const size_t modifierParam) noexcept // VT Modifiers are 1+(modifier flags) const auto vtParam = modifierParam - 1; DWORD modifierState = 0; - WI_SetFlagIf(modifierState, ENHANCED_KEY, modifierParam > 0); WI_SetFlagIf(modifierState, SHIFT_PRESSED, WI_IsFlagSet(vtParam, VT_SHIFT)); WI_SetFlagIf(modifierState, LEFT_ALT_PRESSED, WI_IsFlagSet(vtParam, VT_ALT)); WI_SetFlagIf(modifierState, LEFT_CTRL_PRESSED, WI_IsFlagSet(vtParam, VT_CTRL)); From e33de75402bda8e3ace589a938ec762238e68e3d Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 4 Dec 2023 21:14:26 +0100 Subject: [PATCH 021/603] Fix backspacing over control visualizers (#16400) During `!measureOnly` the old code would increment `distance` twice. Now it doesn't. :) Closes #16356 ## Validation Steps Performed See updated test instructions in `doc/COOKED_READ_DATA.md` (cherry picked from commit 654b755161f2635a16e2878e54941aee8d552075) Service-Card-Id: 91232745 Service-Version: 1.19 --- doc/COOKED_READ_DATA.md | 6 ++++++ src/host/readDataCooked.cpp | 19 ++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/doc/COOKED_READ_DATA.md b/doc/COOKED_READ_DATA.md index 113a51bb749..f4b463f7aa6 100644 --- a/doc/COOKED_READ_DATA.md +++ b/doc/COOKED_READ_DATA.md @@ -15,6 +15,12 @@ All of the following ✅ marks must be fulfilled during manual testing: * Press tab: Autocomplete to "a😊b.txt" ✅ * Navigate the cursor right past the "a" * Press tab twice: Autocomplete to "a😟b.txt" ✅ +* Execute `printf(" "); gets(buffer);` in C (or equivalent) + * Press Tab, A, Ctrl+V, Tab, A ✅ + * The prompt is " A^V A" ✅ + * Cursor navigation works ✅ + * Backspacing/Deleting random parts of it works ✅ + * It never deletes the initial 4 spaces ✅ * Backspace deletes preceding glyphs ✅ * Ctrl+Backspace deletes preceding words ✅ * Escape clears input ✅ diff --git a/src/host/readDataCooked.cpp b/src/host/readDataCooked.cpp index c842bab312d..4869e2ad91a 100644 --- a/src/host/readDataCooked.cpp +++ b/src/host/readDataCooked.cpp @@ -938,22 +938,31 @@ ptrdiff_t COOKED_READ_DATA::_writeCharsImpl(const std::wstring_view& text, const const auto wch = *it; if (wch == UNICODE_TAB) { - const auto col = _getColumnAtRelativeCursorPosition(distance + cursorOffset); - const auto remaining = width - col; - distance += std::min(remaining, 8 - (col & 7)); buf[0] = L'\t'; len = 1; } else { // In the interactive mode we replace C0 control characters (0x00-0x1f) with ASCII representations like ^C (= 0x03). - distance += 2; buf[0] = L'^'; buf[1] = gsl::narrow_cast(wch + L'@'); len = 2; } - if (!measureOnly) + if (measureOnly) + { + if (wch == UNICODE_TAB) + { + const auto col = _getColumnAtRelativeCursorPosition(distance + cursorOffset); + const auto remaining = width - col; + distance += std::min(remaining, 8 - (col & 7)); + } + else + { + distance += 2; + } + } + else { distance += _writeCharsUnprocessed({ &buf[0], len }); } From 204ebf3b198a1f1b6ae1f038cc371c5e1326fc5d Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 4 Dec 2023 21:29:34 +0100 Subject: [PATCH 022/603] Enable AtlasEngine by default (#16277) This enables AtlasEngine by default in the 1.19 release branch. A future change will remove the alternative DxEngine entirely. --- .../CascadiaSettingsSerialization.cpp | 19 ------------------- .../TerminalSettingsModel/MTSMSettings.h | 2 +- .../TerminalSettingsModel/TerminalSettings.h | 2 +- .../UnitTests_Control/ControlCoreTests.cpp | 18 ++++++++++++------ .../ControlInteractivityTests.cpp | 9 ++++++--- src/cascadia/inc/ControlProperties.h | 2 +- src/features.xml | 10 ---------- 7 files changed, 21 insertions(+), 41 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 8964169f207..15e62efd4fa 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -949,25 +949,6 @@ void CascadiaSettings::_researchOnLoad() // Only do this if we're actually being sampled if (TraceLoggingProviderEnabled(g_hSettingsModelProvider, 0, MICROSOFT_KEYWORD_MEASURES)) { - // GH#13936: We're interested in how many users opt out of useAtlasEngine, - // indicating major issues that would require us to disable it by default again. - { - size_t enabled[2]{}; - for (const auto& profile : _activeProfiles) - { - enabled[profile.UseAtlasEngine()]++; - } - - TraceLoggingWrite( - g_hSettingsModelProvider, - "AtlasEngine_Usage", - TraceLoggingDescription("Event emitted upon settings load, containing the number of profiles opted-in/out of useAtlasEngine"), - TraceLoggingUIntPtr(enabled[0], "UseAtlasEngineDisabled", "Number of profiles for which AtlasEngine is disabled"), - TraceLoggingUIntPtr(enabled[1], "UseAtlasEngineEnabled", "Number of profiles for which AtlasEngine is enabled"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - } - // ----------------------------- RE: Themes ---------------------------- const auto numThemes = GlobalSettings().Themes().Size(); const auto themeInUse = GlobalSettings().CurrentTheme().Name(); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 15e91a1d7ae..d0dc6bfcca5 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -90,7 +90,7 @@ Author(s): X(hstring, TabTitle, "tabTitle") \ X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \ X(IEnvironmentVariableMap, EnvironmentVariables, "environment", nullptr) \ - X(bool, UseAtlasEngine, "useAtlasEngine", Feature_AtlasEngine::IsEnabled()) \ + X(bool, UseAtlasEngine, "useAtlasEngine", true) \ X(bool, RightClickContextMenu, "experimental.rightClickContextMenu", false) \ X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ X(bool, Elevate, "elevate", false) \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 60c9b5fb2c2..abc210b9d94 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -148,7 +148,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, IEnvironmentVariableMap, EnvironmentVariables); INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::ScrollbarState, ScrollState, Microsoft::Terminal::Control::ScrollbarState::Visible); - INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAtlasEngine, false); + INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAtlasEngine, true); INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale); diff --git a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp index f34ca5a6bfd..c38c0221c5a 100644 --- a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp @@ -80,9 +80,12 @@ namespace ControlUnitTests void _standardInit(winrt::com_ptr core) { - // "Consolas" ends up with an actual size of 9x21 at 96DPI. So - // let's just arbitrarily start with a 270x420px (30x20 chars) window - core->Initialize(270, 420, 1.0); + // "Consolas" ends up with an actual size of 9x19 at 96DPI. So + // let's just arbitrarily start with a 270x380px (30x20 chars) window + core->Initialize(270, 380, 1.0); +#ifndef NDEBUG + core->_terminal->_suppressLockChecks = true; +#endif VERIFY_IS_TRUE(core->_initializedTerminal); VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height()); } @@ -113,9 +116,12 @@ namespace ControlUnitTests VERIFY_IS_NOT_NULL(core); VERIFY_IS_FALSE(core->_initializedTerminal); - // "Consolas" ends up with an actual size of 9x21 at 96DPI. So - // let's just arbitrarily start with a 270x420px (30x20 chars) window - core->Initialize(270, 420, 1.0); + // "Consolas" ends up with an actual size of 9x19 at 96DPI. So + // let's just arbitrarily start with a 270x380px (30x20 chars) window + core->Initialize(270, 380, 1.0); +#ifndef NDEBUG + core->_terminal->_suppressLockChecks = true; +#endif VERIFY_IS_TRUE(core->_initializedTerminal); VERIFY_ARE_EQUAL(30, core->_terminal->GetViewport().Width()); } diff --git a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp index a48aa968944..192cc2ad9d8 100644 --- a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp @@ -88,9 +88,12 @@ namespace ControlUnitTests void _standardInit(winrt::com_ptr core, winrt::com_ptr interactivity) { - // "Consolas" ends up with an actual size of 9x21 at 96DPI. So - // let's just arbitrarily start with a 270x420px (30x20 chars) window - core->Initialize(270, 420, 1.0); + // "Consolas" ends up with an actual size of 9x19 at 96DPI. So + // let's just arbitrarily start with a 270x380px (30x20 chars) window + core->Initialize(270, 380, 1.0); +#ifndef NDEBUG + core->_terminal->_suppressLockChecks = true; +#endif VERIFY_IS_TRUE(core->_initializedTerminal); VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height()); interactivity->Initialize(); diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index c18e70e7cb2..2c8c8ad48bb 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -72,7 +72,7 @@ X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ X(bool, ForceFullRepaintRendering, false) \ X(bool, SoftwareRendering, false) \ - X(bool, UseAtlasEngine, false) \ + X(bool, UseAtlasEngine, true) \ X(bool, UseBackgroundImageForWindow, false) \ X(bool, ShowMarks, false) \ X(bool, RightClickContextMenu, false) diff --git a/src/features.xml b/src/features.xml index 4656ef0ffd5..be08ced6932 100644 --- a/src/features.xml +++ b/src/features.xml @@ -76,16 +76,6 @@ - - Feature_AtlasEngine - If enabled, AtlasEngine is used by default - AlwaysEnabled - - Release - WindowsInbox - - - Feature_AtlasEnginePresentFallback We don't feel super confident in our usage of the Present1 API, so this settings adds a fallback to Present on error From 91fd7d01018b2400f416d5ae5ef98fc85dfe8583 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 4 Dec 2023 23:58:57 +0100 Subject: [PATCH 023/603] Fix a coroutine AV crash (#16412) tl;dr: A coroutine lambda does not hold onto captured variables. This causes an AV crash when closing tabs. I randomly noticed this in a Debug build as the memory contents got replaced with 0xCD. In a Release build this bug is probably fairly subtle and not common. --- src/cascadia/TerminalApp/TerminalTab.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index c363ec1d9f9..06121ab945d 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -933,9 +933,13 @@ namespace winrt::TerminalApp::implementation ControlEventTokens events{}; events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + // The lambda lives in the `std::function`-style container owned by `control`. That is, when the + // `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to + // copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines. + // See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870 + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - // Check if Tab's lifetime has expired - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { // The title of the control changed, but not necessarily the title of the tab. // Set the tab's text to the active panes' text. @@ -944,8 +948,9 @@ namespace winrt::TerminalApp::implementation }); events.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { // The control's tabColor changed, but it is not necessarily the // active control in this tab. We'll just recalculate the @@ -955,33 +960,36 @@ namespace winrt::TerminalApp::implementation }); events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - // Check if Tab's lifetime has expired - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { tab->_UpdateProgressState(); } }); events.stateToken = control.ConnectionStateChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { tab->_UpdateConnectionClosedState(); } }); events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { tab->_RecalculateAndApplyReadOnly(); } }); events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (const auto tab{ weakThis.get() }) + if (const auto tab{ weakThisCopy.get() }) { if (tab->_focused()) { From 0da37a134a5363080f85a9adc45a79dc6e52ec90 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 00:05:25 +0100 Subject: [PATCH 024/603] Avoid encoding VT via win32 input mode (#16407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset avoids re-encoding output from `AdaptDispatch` via the win32-input-mode mechanism when VT input is enabled. That is, an `AdaptDispatch` output like `\x1b[C` would otherwise result in dozens of characters of input. Related to #16343 ## Validation Steps Performed * Replace conhost with this * Launch a Win32 application inside WSL * ASCII keyboard inputs are represented as single `INPUT_RECORD`s ✅ --- src/host/inputBuffer.cpp | 56 ++++++++++++++++++++++++++++----------- src/host/inputBuffer.hpp | 2 ++ src/host/outputStream.cpp | 15 +---------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index abdfed1961f..184670ffb1d 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -602,6 +602,27 @@ size_t InputBuffer::Write(const std::span& inEvents) } } +void InputBuffer::WriteString(const std::wstring_view& text) +try +{ + if (text.empty()) + { + return; + } + + const auto initiallyEmptyQueue = _storage.empty(); + + _writeString(text); + + if (initiallyEmptyQueue && !_storage.empty()) + { + ServiceLocator::LocateGlobals().hInputEvent.SetEvent(); + } + + WakeUpReadersWaitingForData(); +} +CATCH_LOG() + // This can be considered a "privileged" variant of Write() which allows FOCUS_EVENTs to generate focus VT sequences. // If we didn't do this, someone could write a FOCUS_EVENT_RECORD with WriteConsoleInput, exit without flushing the // input buffer and the next application will suddenly get a "\x1b[I" sequence in their input. See GH#13238. @@ -828,21 +849,7 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType& return; } - for (const auto& wch : text) - { - if (wch == UNICODE_NULL) - { - // Convert null byte back to input event with proper control state - const auto zeroKey = OneCoreSafeVkKeyScanW(0); - uint32_t ctrlState = 0; - WI_SetFlagIf(ctrlState, SHIFT_PRESSED, WI_IsFlagSet(zeroKey, 0x100)); - WI_SetFlagIf(ctrlState, LEFT_CTRL_PRESSED, WI_IsFlagSet(zeroKey, 0x200)); - WI_SetFlagIf(ctrlState, LEFT_ALT_PRESSED, WI_IsFlagSet(zeroKey, 0x400)); - _storage.push_back(SynthesizeKeyEvent(true, 1, LOBYTE(zeroKey), 0, wch, ctrlState)); - continue; - } - _storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); - } + _writeString(text); if (!_vtInputShouldSuppress) { @@ -856,6 +863,25 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType& } } +void InputBuffer::_writeString(const std::wstring_view& text) +{ + for (const auto& wch : text) + { + if (wch == UNICODE_NULL) + { + // Convert null byte back to input event with proper control state + const auto zeroKey = OneCoreSafeVkKeyScanW(0); + uint32_t ctrlState = 0; + WI_SetFlagIf(ctrlState, SHIFT_PRESSED, WI_IsFlagSet(zeroKey, 0x100)); + WI_SetFlagIf(ctrlState, LEFT_CTRL_PRESSED, WI_IsFlagSet(zeroKey, 0x200)); + WI_SetFlagIf(ctrlState, LEFT_ALT_PRESSED, WI_IsFlagSet(zeroKey, 0x400)); + _storage.push_back(SynthesizeKeyEvent(true, 1, LOBYTE(zeroKey), 0, wch, ctrlState)); + continue; + } + _storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); + } +} + TerminalInput& InputBuffer::GetTerminalInput() { return _termInput; diff --git a/src/host/inputBuffer.hpp b/src/host/inputBuffer.hpp index ec7cd75b082..019c6e628bd 100644 --- a/src/host/inputBuffer.hpp +++ b/src/host/inputBuffer.hpp @@ -58,6 +58,7 @@ class InputBuffer final : public ConsoleObjectHeader size_t Prepend(const std::span& inEvents); size_t Write(const INPUT_RECORD& inEvent); size_t Write(const std::span& inEvents); + void WriteString(const std::wstring_view& text); void WriteFocusEvent(bool focused) noexcept; bool WriteMouseEvent(til::point position, unsigned int button, short keyState, short wheelDelta); @@ -96,6 +97,7 @@ class InputBuffer final : public ConsoleObjectHeader void _WriteBuffer(const std::span& inRecords, _Out_ size_t& eventsWritten, _Out_ bool& setWaitEvent); bool _CoalesceEvent(const INPUT_RECORD& inEvent) noexcept; void _HandleTerminalInputCallback(const Microsoft::Console::VirtualTerminal::TerminalInput::StringType& text); + void _writeString(const std::wstring_view& text); #ifdef UNIT_TESTING friend class InputBufferTests; diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index a48e189f00d..24adc7a613f 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -33,24 +33,11 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) : // - void ConhostInternalGetSet::ReturnResponse(const std::wstring_view response) { - InputEventQueue inEvents; - - // generate a paired key down and key up event for every - // character to be sent into the console's input buffer - for (const auto& wch : response) - { - // This wasn't from a real keyboard, so we're leaving key/scan codes blank. - auto keyEvent = SynthesizeKeyEvent(true, 1, 0, 0, wch, 0); - inEvents.push_back(keyEvent); - keyEvent.Event.KeyEvent.bKeyDown = false; - inEvents.push_back(keyEvent); - } - // TODO GH#4954 During the input refactor we may want to add a "priority" input list // to make sure that "response" input is spooled directly into the application. // We switched this to an append (vs. a prepend) to fix GH#1637, a bug where two CPR // could collide with each other. - _io.GetActiveInputBuffer()->Write(inEvents); + _io.GetActiveInputBuffer()->WriteString(response); } // Routine Description: From ab7a2f10c5f5cd3d0b45b46d068ae9a70b6a9af5 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 00:07:19 +0100 Subject: [PATCH 025/603] Fix scroll-forward-disable setting (#16411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The final parameter, `updateBottom`, controls not just whether the `_virtualBottom` is updated, but also whether the position is clamped to be within the existing `_virtualBottom`. Setting this to `false` thus broke scroll-forward as the `_virtualBottom` was now a constant. ## Validation Steps Performed * Disable scroll-foward * Press and hold Ctrl+C * It scrolls past the viewport bottom ✅ --- src/host/outputStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 24adc7a613f..6cb84a4ef5b 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -82,7 +82,7 @@ til::rect ConhostInternalGetSet::GetViewport() const void ConhostInternalGetSet::SetViewportPosition(const til::point position) { auto& info = _io.GetActiveOutputBuffer(); - THROW_IF_FAILED(info.SetViewportOrigin(true, position, false)); + THROW_IF_FAILED(info.SetViewportOrigin(true, position, true)); // SetViewportOrigin() only updates the virtual bottom (the bottom coordinate of the area // in the text buffer a VT client writes its output into) when it's moving downwards. // But this function is meant to truly move the viewport no matter what. Otherwise `tput reset` breaks. From 85eee09854666a6b67e6fbc1a660e9af7827a6ca Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 4 Dec 2023 21:29:34 +0100 Subject: [PATCH 026/603] Enable AtlasEngine by default (#16277) This enables AtlasEngine by default in the 1.19 release branch. A future change will remove the alternative DxEngine entirely. (cherry picked from commit 204ebf3b198a1f1b6ae1f038cc371c5e1326fc5d) Service-Card-Id: 91158876 Service-Version: 1.19 --- .../CascadiaSettingsSerialization.cpp | 19 ------------------- .../TerminalSettingsModel/MTSMSettings.h | 2 +- .../TerminalSettingsModel/TerminalSettings.h | 2 +- .../UnitTests_Control/ControlCoreTests.cpp | 18 ++++++++++++------ .../ControlInteractivityTests.cpp | 9 ++++++--- src/cascadia/inc/ControlProperties.h | 2 +- src/features.xml | 10 ---------- 7 files changed, 21 insertions(+), 41 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 8964169f207..15e62efd4fa 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -949,25 +949,6 @@ void CascadiaSettings::_researchOnLoad() // Only do this if we're actually being sampled if (TraceLoggingProviderEnabled(g_hSettingsModelProvider, 0, MICROSOFT_KEYWORD_MEASURES)) { - // GH#13936: We're interested in how many users opt out of useAtlasEngine, - // indicating major issues that would require us to disable it by default again. - { - size_t enabled[2]{}; - for (const auto& profile : _activeProfiles) - { - enabled[profile.UseAtlasEngine()]++; - } - - TraceLoggingWrite( - g_hSettingsModelProvider, - "AtlasEngine_Usage", - TraceLoggingDescription("Event emitted upon settings load, containing the number of profiles opted-in/out of useAtlasEngine"), - TraceLoggingUIntPtr(enabled[0], "UseAtlasEngineDisabled", "Number of profiles for which AtlasEngine is disabled"), - TraceLoggingUIntPtr(enabled[1], "UseAtlasEngineEnabled", "Number of profiles for which AtlasEngine is enabled"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - } - // ----------------------------- RE: Themes ---------------------------- const auto numThemes = GlobalSettings().Themes().Size(); const auto themeInUse = GlobalSettings().CurrentTheme().Name(); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 15e91a1d7ae..d0dc6bfcca5 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -90,7 +90,7 @@ Author(s): X(hstring, TabTitle, "tabTitle") \ X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \ X(IEnvironmentVariableMap, EnvironmentVariables, "environment", nullptr) \ - X(bool, UseAtlasEngine, "useAtlasEngine", Feature_AtlasEngine::IsEnabled()) \ + X(bool, UseAtlasEngine, "useAtlasEngine", true) \ X(bool, RightClickContextMenu, "experimental.rightClickContextMenu", false) \ X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ X(bool, Elevate, "elevate", false) \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 60c9b5fb2c2..abc210b9d94 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -148,7 +148,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, IEnvironmentVariableMap, EnvironmentVariables); INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::ScrollbarState, ScrollState, Microsoft::Terminal::Control::ScrollbarState::Visible); - INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAtlasEngine, false); + INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAtlasEngine, true); INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale); diff --git a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp index f34ca5a6bfd..c38c0221c5a 100644 --- a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp @@ -80,9 +80,12 @@ namespace ControlUnitTests void _standardInit(winrt::com_ptr core) { - // "Consolas" ends up with an actual size of 9x21 at 96DPI. So - // let's just arbitrarily start with a 270x420px (30x20 chars) window - core->Initialize(270, 420, 1.0); + // "Consolas" ends up with an actual size of 9x19 at 96DPI. So + // let's just arbitrarily start with a 270x380px (30x20 chars) window + core->Initialize(270, 380, 1.0); +#ifndef NDEBUG + core->_terminal->_suppressLockChecks = true; +#endif VERIFY_IS_TRUE(core->_initializedTerminal); VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height()); } @@ -113,9 +116,12 @@ namespace ControlUnitTests VERIFY_IS_NOT_NULL(core); VERIFY_IS_FALSE(core->_initializedTerminal); - // "Consolas" ends up with an actual size of 9x21 at 96DPI. So - // let's just arbitrarily start with a 270x420px (30x20 chars) window - core->Initialize(270, 420, 1.0); + // "Consolas" ends up with an actual size of 9x19 at 96DPI. So + // let's just arbitrarily start with a 270x380px (30x20 chars) window + core->Initialize(270, 380, 1.0); +#ifndef NDEBUG + core->_terminal->_suppressLockChecks = true; +#endif VERIFY_IS_TRUE(core->_initializedTerminal); VERIFY_ARE_EQUAL(30, core->_terminal->GetViewport().Width()); } diff --git a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp index a48aa968944..192cc2ad9d8 100644 --- a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp @@ -88,9 +88,12 @@ namespace ControlUnitTests void _standardInit(winrt::com_ptr core, winrt::com_ptr interactivity) { - // "Consolas" ends up with an actual size of 9x21 at 96DPI. So - // let's just arbitrarily start with a 270x420px (30x20 chars) window - core->Initialize(270, 420, 1.0); + // "Consolas" ends up with an actual size of 9x19 at 96DPI. So + // let's just arbitrarily start with a 270x380px (30x20 chars) window + core->Initialize(270, 380, 1.0); +#ifndef NDEBUG + core->_terminal->_suppressLockChecks = true; +#endif VERIFY_IS_TRUE(core->_initializedTerminal); VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height()); interactivity->Initialize(); diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index c18e70e7cb2..2c8c8ad48bb 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -72,7 +72,7 @@ X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ X(bool, ForceFullRepaintRendering, false) \ X(bool, SoftwareRendering, false) \ - X(bool, UseAtlasEngine, false) \ + X(bool, UseAtlasEngine, true) \ X(bool, UseBackgroundImageForWindow, false) \ X(bool, ShowMarks, false) \ X(bool, RightClickContextMenu, false) diff --git a/src/features.xml b/src/features.xml index 4656ef0ffd5..be08ced6932 100644 --- a/src/features.xml +++ b/src/features.xml @@ -76,16 +76,6 @@ - - Feature_AtlasEngine - If enabled, AtlasEngine is used by default - AlwaysEnabled - - Release - WindowsInbox - - - Feature_AtlasEnginePresentFallback We don't feel super confident in our usage of the Present1 API, so this settings adds a fallback to Present on error From 486f7ef644c74faf77223a54fd0c884a52b66d6c Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 4 Dec 2023 23:58:57 +0100 Subject: [PATCH 027/603] Fix a coroutine AV crash (#16412) tl;dr: A coroutine lambda does not hold onto captured variables. This causes an AV crash when closing tabs. I randomly noticed this in a Debug build as the memory contents got replaced with 0xCD. In a Release build this bug is probably fairly subtle and not common. (cherry picked from commit 91fd7d01018b2400f416d5ae5ef98fc85dfe8583) Service-Card-Id: 91258717 Service-Version: 1.19 --- src/cascadia/TerminalApp/TerminalTab.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index c363ec1d9f9..06121ab945d 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -933,9 +933,13 @@ namespace winrt::TerminalApp::implementation ControlEventTokens events{}; events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + // The lambda lives in the `std::function`-style container owned by `control`. That is, when the + // `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to + // copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines. + // See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870 + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - // Check if Tab's lifetime has expired - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { // The title of the control changed, but not necessarily the title of the tab. // Set the tab's text to the active panes' text. @@ -944,8 +948,9 @@ namespace winrt::TerminalApp::implementation }); events.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { // The control's tabColor changed, but it is not necessarily the // active control in this tab. We'll just recalculate the @@ -955,33 +960,36 @@ namespace winrt::TerminalApp::implementation }); events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - // Check if Tab's lifetime has expired - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { tab->_UpdateProgressState(); } }); events.stateToken = control.ConnectionStateChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { tab->_UpdateConnectionClosedState(); } }); events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.get() }) + if (auto tab{ weakThisCopy.get() }) { tab->_RecalculateAndApplyReadOnly(); } }); events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); - if (const auto tab{ weakThis.get() }) + if (const auto tab{ weakThisCopy.get() }) { if (tab->_focused()) { From 9882154043f33998ee14ca747148be74f2ba8b26 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 00:05:25 +0100 Subject: [PATCH 028/603] Avoid encoding VT via win32 input mode (#16407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset avoids re-encoding output from `AdaptDispatch` via the win32-input-mode mechanism when VT input is enabled. That is, an `AdaptDispatch` output like `\x1b[C` would otherwise result in dozens of characters of input. Related to #16343 ## Validation Steps Performed * Replace conhost with this * Launch a Win32 application inside WSL * ASCII keyboard inputs are represented as single `INPUT_RECORD`s ✅ (cherry picked from commit 0da37a134a5363080f85a9adc45a79dc6e52ec90) Service-Card-Id: 91246942 Service-Version: 1.19 --- src/host/inputBuffer.cpp | 56 ++++++++++++++++++++++++++++----------- src/host/inputBuffer.hpp | 2 ++ src/host/outputStream.cpp | 15 +---------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index abdfed1961f..184670ffb1d 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -602,6 +602,27 @@ size_t InputBuffer::Write(const std::span& inEvents) } } +void InputBuffer::WriteString(const std::wstring_view& text) +try +{ + if (text.empty()) + { + return; + } + + const auto initiallyEmptyQueue = _storage.empty(); + + _writeString(text); + + if (initiallyEmptyQueue && !_storage.empty()) + { + ServiceLocator::LocateGlobals().hInputEvent.SetEvent(); + } + + WakeUpReadersWaitingForData(); +} +CATCH_LOG() + // This can be considered a "privileged" variant of Write() which allows FOCUS_EVENTs to generate focus VT sequences. // If we didn't do this, someone could write a FOCUS_EVENT_RECORD with WriteConsoleInput, exit without flushing the // input buffer and the next application will suddenly get a "\x1b[I" sequence in their input. See GH#13238. @@ -828,21 +849,7 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType& return; } - for (const auto& wch : text) - { - if (wch == UNICODE_NULL) - { - // Convert null byte back to input event with proper control state - const auto zeroKey = OneCoreSafeVkKeyScanW(0); - uint32_t ctrlState = 0; - WI_SetFlagIf(ctrlState, SHIFT_PRESSED, WI_IsFlagSet(zeroKey, 0x100)); - WI_SetFlagIf(ctrlState, LEFT_CTRL_PRESSED, WI_IsFlagSet(zeroKey, 0x200)); - WI_SetFlagIf(ctrlState, LEFT_ALT_PRESSED, WI_IsFlagSet(zeroKey, 0x400)); - _storage.push_back(SynthesizeKeyEvent(true, 1, LOBYTE(zeroKey), 0, wch, ctrlState)); - continue; - } - _storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); - } + _writeString(text); if (!_vtInputShouldSuppress) { @@ -856,6 +863,25 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType& } } +void InputBuffer::_writeString(const std::wstring_view& text) +{ + for (const auto& wch : text) + { + if (wch == UNICODE_NULL) + { + // Convert null byte back to input event with proper control state + const auto zeroKey = OneCoreSafeVkKeyScanW(0); + uint32_t ctrlState = 0; + WI_SetFlagIf(ctrlState, SHIFT_PRESSED, WI_IsFlagSet(zeroKey, 0x100)); + WI_SetFlagIf(ctrlState, LEFT_CTRL_PRESSED, WI_IsFlagSet(zeroKey, 0x200)); + WI_SetFlagIf(ctrlState, LEFT_ALT_PRESSED, WI_IsFlagSet(zeroKey, 0x400)); + _storage.push_back(SynthesizeKeyEvent(true, 1, LOBYTE(zeroKey), 0, wch, ctrlState)); + continue; + } + _storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); + } +} + TerminalInput& InputBuffer::GetTerminalInput() { return _termInput; diff --git a/src/host/inputBuffer.hpp b/src/host/inputBuffer.hpp index ec7cd75b082..019c6e628bd 100644 --- a/src/host/inputBuffer.hpp +++ b/src/host/inputBuffer.hpp @@ -58,6 +58,7 @@ class InputBuffer final : public ConsoleObjectHeader size_t Prepend(const std::span& inEvents); size_t Write(const INPUT_RECORD& inEvent); size_t Write(const std::span& inEvents); + void WriteString(const std::wstring_view& text); void WriteFocusEvent(bool focused) noexcept; bool WriteMouseEvent(til::point position, unsigned int button, short keyState, short wheelDelta); @@ -96,6 +97,7 @@ class InputBuffer final : public ConsoleObjectHeader void _WriteBuffer(const std::span& inRecords, _Out_ size_t& eventsWritten, _Out_ bool& setWaitEvent); bool _CoalesceEvent(const INPUT_RECORD& inEvent) noexcept; void _HandleTerminalInputCallback(const Microsoft::Console::VirtualTerminal::TerminalInput::StringType& text); + void _writeString(const std::wstring_view& text); #ifdef UNIT_TESTING friend class InputBufferTests; diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index a48e189f00d..24adc7a613f 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -33,24 +33,11 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) : // - void ConhostInternalGetSet::ReturnResponse(const std::wstring_view response) { - InputEventQueue inEvents; - - // generate a paired key down and key up event for every - // character to be sent into the console's input buffer - for (const auto& wch : response) - { - // This wasn't from a real keyboard, so we're leaving key/scan codes blank. - auto keyEvent = SynthesizeKeyEvent(true, 1, 0, 0, wch, 0); - inEvents.push_back(keyEvent); - keyEvent.Event.KeyEvent.bKeyDown = false; - inEvents.push_back(keyEvent); - } - // TODO GH#4954 During the input refactor we may want to add a "priority" input list // to make sure that "response" input is spooled directly into the application. // We switched this to an append (vs. a prepend) to fix GH#1637, a bug where two CPR // could collide with each other. - _io.GetActiveInputBuffer()->Write(inEvents); + _io.GetActiveInputBuffer()->WriteString(response); } // Routine Description: From ef32e3e487c87865de4f8f9de45447c105af20fe Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 00:07:19 +0100 Subject: [PATCH 029/603] Fix scroll-forward-disable setting (#16411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The final parameter, `updateBottom`, controls not just whether the `_virtualBottom` is updated, but also whether the position is clamped to be within the existing `_virtualBottom`. Setting this to `false` thus broke scroll-forward as the `_virtualBottom` was now a constant. ## Validation Steps Performed * Disable scroll-foward * Press and hold Ctrl+C * It scrolls past the viewport bottom ✅ (cherry picked from commit ab7a2f10c5f5cd3d0b45b46d068ae9a70b6a9af5) Service-Card-Id: 91258882 Service-Version: 1.19 --- src/host/outputStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 24adc7a613f..6cb84a4ef5b 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -82,7 +82,7 @@ til::rect ConhostInternalGetSet::GetViewport() const void ConhostInternalGetSet::SetViewportPosition(const til::point position) { auto& info = _io.GetActiveOutputBuffer(); - THROW_IF_FAILED(info.SetViewportOrigin(true, position, false)); + THROW_IF_FAILED(info.SetViewportOrigin(true, position, true)); // SetViewportOrigin() only updates the virtual bottom (the bottom coordinate of the area // in the text buffer a VT client writes its output into) when it's moving downwards. // But this function is meant to truly move the viewport no matter what. Otherwise `tput reset` breaks. From 70e51ae28d305ff9cd7430984119648b9f0b04ef Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 02:53:55 +0100 Subject: [PATCH 030/603] Disable win32 input mode on exit (#16408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When ConPTY exits it should attempt to restore the state as it was before it started. This is particularly important for the win32 input mode sequences, as Linux shells don't know what to do with it. Related to #16343 ## Validation Steps Performed * Replace conhost with this * Launch a Win32 application inside WSL * Exit that application * Shell prompt doesn't get filled with win32 input mode sequences ✅ --- .../ConptyRoundtripTests.cpp | 2 +- src/renderer/vt/VtSequences.cpp | 19 ------------------- src/renderer/vt/invalidate.cpp | 8 ++++++++ src/renderer/vt/state.cpp | 6 ++++-- src/renderer/vt/vtrenderer.hpp | 5 ----- src/terminal/adapter/adaptDispatch.cpp | 10 ++++++++-- 6 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index cf15e3ba191..f7948e5c59d 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -1201,7 +1201,7 @@ void ConptyRoundtripTests::PassthroughHardReset() // Write a Hard Reset VT sequence to the host, it should come through to the Terminal // along with a DECSET sequence to re-enable win32 input and focus events. expectedOutput.push_back("\033c"); - expectedOutput.push_back("\033[?9001;1004h"); + expectedOutput.push_back("\033[?9001h\033[?1004h"); hostSm.ProcessString(L"\033c"); const auto termSecondView = term->GetViewport(); diff --git a/src/renderer/vt/VtSequences.cpp b/src/renderer/vt/VtSequences.cpp index 1b728aae500..612d3c06d06 100644 --- a/src/renderer/vt/VtSequences.cpp +++ b/src/renderer/vt/VtSequences.cpp @@ -476,25 +476,6 @@ using namespace Microsoft::Console::Render; return _Write(isReversed ? "\x1b[7m" : "\x1b[27m"); } -// Method Description: -// - Send a sequence to the connected terminal to request win32-input-mode from -// them. This will enable the connected terminal to send us full INPUT_RECORDs -// as input. If the terminal doesn't understand this sequence, it'll just -// ignore it. -// Arguments: -// - -// Return Value: -// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write. -[[nodiscard]] HRESULT VtEngine::_RequestWin32Input() noexcept -{ - return _Write("\x1b[?9001h"); -} - -[[nodiscard]] HRESULT VtEngine::_RequestFocusEventMode() noexcept -{ - return _Write("\x1b[?1004h"); -} - // Method Description: // - Send a sequence to the connected terminal to switch to the alternate or main screen buffer. // Arguments: diff --git a/src/renderer/vt/invalidate.cpp b/src/renderer/vt/invalidate.cpp index 7ef59bc03b0..8f80d3eb8ea 100644 --- a/src/renderer/vt/invalidate.cpp +++ b/src/renderer/vt/invalidate.cpp @@ -126,6 +126,14 @@ CATCH_RETURN(); // - S_OK [[nodiscard]] HRESULT VtEngine::PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept { + // This must be kept in sync with RequestWin32Input(). + // It ensures that we disable the modes that we requested on startup. + // Linux shells for instance don't understand the win32-input-mode 9001. + // + // This can be here, instead of being appended at the end of this final rendering pass, + // because these two states happen to have no influence on the caller's VT parsing. + std::ignore = _Write("\033[?9001l\033[?1004l"); + *pForcePaint = true; return S_OK; } diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index 36e31463cd9..5bb6b7d694d 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -526,11 +526,13 @@ void VtEngine::SetTerminalCursorTextPosition(const til::point cursor) noexcept // - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write. HRESULT VtEngine::RequestWin32Input() noexcept { + // On startup we request the modes we require for optimal functioning + // (namely win32 input mode and focus event mode). + // // It's important that any additional modes set here are also mirrored in // the AdaptDispatch::HardReset method, since that needs to re-enable them // in the connected terminal after passing through an RIS sequence. - RETURN_IF_FAILED(_RequestWin32Input()); - RETURN_IF_FAILED(_RequestFocusEventMode()); + RETURN_IF_FAILED(_Write("\033[?9001h\033[?1004h")); _Flush(); return S_OK; } diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 100e04c549d..676672ee814 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -80,8 +80,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT WriteTerminalUtf8(const std::string_view str) noexcept; [[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0; void SetTerminalOwner(Microsoft::Console::VirtualTerminal::VtIo* const terminalOwner); - void BeginResizeRequest(); - void EndResizeRequest(); void SetResizeQuirk(const bool resizeQuirk); void SetPassthroughMode(const bool passthrough) noexcept; void SetLookingForDSRCallback(std::function pfnLooking) noexcept; @@ -208,11 +206,8 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _RequestCursor() noexcept; [[nodiscard]] HRESULT _ListenForDSR() noexcept; - [[nodiscard]] HRESULT _RequestWin32Input() noexcept; [[nodiscard]] HRESULT _SwitchScreenBuffer(const bool useAltBuffer) noexcept; - [[nodiscard]] HRESULT _RequestFocusEventMode() noexcept; - [[nodiscard]] virtual HRESULT _MoveCursor(const til::point coord) noexcept = 0; [[nodiscard]] HRESULT _RgbUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept; [[nodiscard]] HRESULT _16ColorUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 85582542980..31edacae21d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1917,7 +1917,13 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con return !_api.IsConsolePty(); case DispatchTypes::ModeParams::W32IM_Win32InputMode: _terminalInput.SetInputMode(TerminalInput::Mode::Win32, enable); - return !_PassThroughInputModes(); + // ConPTY requests the Win32InputMode on startup and disables it on shutdown. When nesting ConPTY inside + // ConPTY then this should not bubble up. Otherwise, when the inner ConPTY exits and the outer ConPTY + // passes the disable sequence up to the hosting terminal, we'd stop getting Win32InputMode entirely! + // It also makes more sense to not bubble it up, because this mode is specifically for INPUT_RECORD interop + // and thus entirely between a PTY's input records and its INPUT_RECORD-aware VT-aware console clients. + // Returning true here will mark this as being handled and avoid this. + return true; default: // If no functions to call, overall dispatch was a failure. return false; @@ -3097,7 +3103,7 @@ bool AdaptDispatch::HardReset() if (stateMachine.FlushToTerminal()) { auto& engine = stateMachine.Engine(); - engine.ActionPassThroughString(L"\033[?9001;1004h"); + engine.ActionPassThroughString(L"\033[?9001h\033[?1004h"); } } return true; From 8a9af94095883bd5ea726ee64b6eec059d60810a Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 02:53:55 +0100 Subject: [PATCH 031/603] Disable win32 input mode on exit (#16408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When ConPTY exits it should attempt to restore the state as it was before it started. This is particularly important for the win32 input mode sequences, as Linux shells don't know what to do with it. Related to #16343 ## Validation Steps Performed * Replace conhost with this * Launch a Win32 application inside WSL * Exit that application * Shell prompt doesn't get filled with win32 input mode sequences ✅ (cherry picked from commit 70e51ae28d305ff9cd7430984119648b9f0b04ef) Service-Card-Id: 91246943 Service-Version: 1.19 --- .../ConptyRoundtripTests.cpp | 2 +- src/renderer/vt/VtSequences.cpp | 19 ------------------- src/renderer/vt/invalidate.cpp | 8 ++++++++ src/renderer/vt/state.cpp | 6 ++++-- src/renderer/vt/vtrenderer.hpp | 5 ----- src/terminal/adapter/adaptDispatch.cpp | 10 ++++++++-- 6 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index cf15e3ba191..f7948e5c59d 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -1201,7 +1201,7 @@ void ConptyRoundtripTests::PassthroughHardReset() // Write a Hard Reset VT sequence to the host, it should come through to the Terminal // along with a DECSET sequence to re-enable win32 input and focus events. expectedOutput.push_back("\033c"); - expectedOutput.push_back("\033[?9001;1004h"); + expectedOutput.push_back("\033[?9001h\033[?1004h"); hostSm.ProcessString(L"\033c"); const auto termSecondView = term->GetViewport(); diff --git a/src/renderer/vt/VtSequences.cpp b/src/renderer/vt/VtSequences.cpp index 1b728aae500..612d3c06d06 100644 --- a/src/renderer/vt/VtSequences.cpp +++ b/src/renderer/vt/VtSequences.cpp @@ -476,25 +476,6 @@ using namespace Microsoft::Console::Render; return _Write(isReversed ? "\x1b[7m" : "\x1b[27m"); } -// Method Description: -// - Send a sequence to the connected terminal to request win32-input-mode from -// them. This will enable the connected terminal to send us full INPUT_RECORDs -// as input. If the terminal doesn't understand this sequence, it'll just -// ignore it. -// Arguments: -// - -// Return Value: -// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write. -[[nodiscard]] HRESULT VtEngine::_RequestWin32Input() noexcept -{ - return _Write("\x1b[?9001h"); -} - -[[nodiscard]] HRESULT VtEngine::_RequestFocusEventMode() noexcept -{ - return _Write("\x1b[?1004h"); -} - // Method Description: // - Send a sequence to the connected terminal to switch to the alternate or main screen buffer. // Arguments: diff --git a/src/renderer/vt/invalidate.cpp b/src/renderer/vt/invalidate.cpp index 7ef59bc03b0..8f80d3eb8ea 100644 --- a/src/renderer/vt/invalidate.cpp +++ b/src/renderer/vt/invalidate.cpp @@ -126,6 +126,14 @@ CATCH_RETURN(); // - S_OK [[nodiscard]] HRESULT VtEngine::PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept { + // This must be kept in sync with RequestWin32Input(). + // It ensures that we disable the modes that we requested on startup. + // Linux shells for instance don't understand the win32-input-mode 9001. + // + // This can be here, instead of being appended at the end of this final rendering pass, + // because these two states happen to have no influence on the caller's VT parsing. + std::ignore = _Write("\033[?9001l\033[?1004l"); + *pForcePaint = true; return S_OK; } diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index 8408ee3aeb6..df35691a361 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -525,11 +525,13 @@ void VtEngine::SetTerminalCursorTextPosition(const til::point cursor) noexcept // - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write. HRESULT VtEngine::RequestWin32Input() noexcept { + // On startup we request the modes we require for optimal functioning + // (namely win32 input mode and focus event mode). + // // It's important that any additional modes set here are also mirrored in // the AdaptDispatch::HardReset method, since that needs to re-enable them // in the connected terminal after passing through an RIS sequence. - RETURN_IF_FAILED(_RequestWin32Input()); - RETURN_IF_FAILED(_RequestFocusEventMode()); + RETURN_IF_FAILED(_Write("\033[?9001h\033[?1004h")); _Flush(); return S_OK; } diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 15de6e0e760..094bebec385 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -80,8 +80,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT WriteTerminalUtf8(const std::string_view str) noexcept; [[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0; void SetTerminalOwner(Microsoft::Console::VirtualTerminal::VtIo* const terminalOwner); - void BeginResizeRequest(); - void EndResizeRequest(); void SetResizeQuirk(const bool resizeQuirk); void SetPassthroughMode(const bool passthrough) noexcept; void SetLookingForDSRCallback(std::function pfnLooking) noexcept; @@ -208,11 +206,8 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _RequestCursor() noexcept; [[nodiscard]] HRESULT _ListenForDSR() noexcept; - [[nodiscard]] HRESULT _RequestWin32Input() noexcept; [[nodiscard]] HRESULT _SwitchScreenBuffer(const bool useAltBuffer) noexcept; - [[nodiscard]] HRESULT _RequestFocusEventMode() noexcept; - [[nodiscard]] virtual HRESULT _MoveCursor(const til::point coord) noexcept = 0; [[nodiscard]] HRESULT _RgbUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept; [[nodiscard]] HRESULT _16ColorUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 85582542980..31edacae21d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1917,7 +1917,13 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con return !_api.IsConsolePty(); case DispatchTypes::ModeParams::W32IM_Win32InputMode: _terminalInput.SetInputMode(TerminalInput::Mode::Win32, enable); - return !_PassThroughInputModes(); + // ConPTY requests the Win32InputMode on startup and disables it on shutdown. When nesting ConPTY inside + // ConPTY then this should not bubble up. Otherwise, when the inner ConPTY exits and the outer ConPTY + // passes the disable sequence up to the hosting terminal, we'd stop getting Win32InputMode entirely! + // It also makes more sense to not bubble it up, because this mode is specifically for INPUT_RECORD interop + // and thus entirely between a PTY's input records and its INPUT_RECORD-aware VT-aware console clients. + // Returning true here will mark this as being handled and avoid this. + return true; default: // If no functions to call, overall dispatch was a failure. return false; @@ -3097,7 +3103,7 @@ bool AdaptDispatch::HardReset() if (stateMachine.FlushToTerminal()) { auto& engine = stateMachine.Engine(); - engine.ActionPassThroughString(L"\033[?9001;1004h"); + engine.ActionPassThroughString(L"\033[?9001h\033[?1004h"); } } return true; From 71a6f26e6ece656084e87de1a528c4a8072eeabd Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 03:02:46 +0100 Subject: [PATCH 032/603] Improve conhost's scrolling performance (#16333) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `EnableScrollbar()` and especially `SetScrollInfo()` are prohibitively expensive functions nowadays. This improves throughput of good old `type` in cmd.exe by ~10x, by briefly releasing the console lock. ## Validation Steps Performed * `type`ing a file in `cmd` is as fast while the window is scrolling as it is while it isn't scrolling ✅ * Scrollbar pops in and out when scroll-forward is disabled ✅ --- src/host/consoleInformation.cpp | 5 ++ src/host/renderData.cpp | 6 ++- src/host/screenInfo.cpp | 54 +++++---------------- src/host/screenInfo.hpp | 9 +++- src/host/server.h | 1 + src/interactivity/inc/IConsoleWindow.hpp | 7 --- src/interactivity/onecore/ConsoleWindow.cpp | 14 ------ src/interactivity/onecore/ConsoleWindow.hpp | 3 -- src/interactivity/win32/window.cpp | 49 ++++++++++--------- src/interactivity/win32/window.hpp | 8 +-- src/interactivity/win32/windowproc.cpp | 13 ++++- 11 files changed, 68 insertions(+), 101 deletions(-) diff --git a/src/host/consoleInformation.cpp b/src/host/consoleInformation.cpp index a99a8871688..074bd56911f 100644 --- a/src/host/consoleInformation.cpp +++ b/src/host/consoleInformation.cpp @@ -35,6 +35,11 @@ void CONSOLE_INFORMATION::UnlockConsole() noexcept _lock.unlock(); } +til::recursive_ticket_lock_suspension CONSOLE_INFORMATION::SuspendLock() noexcept +{ + return _lock.suspend(); +} + ULONG CONSOLE_INFORMATION::GetCSRecursionCount() const noexcept { return _lock.recursion_depth(); diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index d327c0b9d61..5c89edae9a9 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -87,14 +87,16 @@ std::vector RenderData::GetSelectionRects() noexcept // they're done with any querying they need to do. void RenderData::LockConsole() noexcept { - ::LockConsole(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + gci.LockConsole(); } // Method Description: // - Unlocks the console after a call to RenderData::LockConsole. void RenderData::UnlockConsole() noexcept { - ::UnlockConsole(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + gci.UnlockConsole(); } // Method Description: diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index d36cdeab3a9..2701b4e2072 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -34,7 +34,6 @@ SCREEN_INFORMATION::SCREEN_INFORMATION( const TextAttribute popupAttributes, const FontInfo fontInfo) : OutputMode{ ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT }, - ResizingWindow{ 0 }, WheelDelta{ 0 }, HWheelDelta{ 0 }, _textBuffer{ nullptr }, @@ -641,64 +640,35 @@ VOID SCREEN_INFORMATION::UpdateScrollBars() return; } - if (gci.Flags & CONSOLE_UPDATING_SCROLL_BARS) + if (gci.Flags & CONSOLE_UPDATING_SCROLL_BARS || ServiceLocator::LocateConsoleWindow() == nullptr) { return; } gci.Flags |= CONSOLE_UPDATING_SCROLL_BARS; - - if (ServiceLocator::LocateConsoleWindow() != nullptr) - { - ServiceLocator::LocateConsoleWindow()->PostUpdateScrollBars(); - } + LOG_IF_WIN32_BOOL_FALSE(ServiceLocator::LocateConsoleWindow()->PostUpdateScrollBars()); } -VOID SCREEN_INFORMATION::InternalUpdateScrollBars() +SCREEN_INFORMATION::ScrollBarState SCREEN_INFORMATION::FetchScrollBarState() { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto pWindow = ServiceLocator::LocateConsoleWindow(); - WI_ClearFlag(gci.Flags, CONSOLE_UPDATING_SCROLL_BARS); - if (!IsActiveScreenBuffer()) - { - return; - } - - ResizingWindow++; - - if (pWindow != nullptr) - { - const auto buffer = GetBufferSize(); - - // If this is the main buffer, make sure we enable both of the scroll bars. - // The alt buffer likely disabled the scroll bars, this is the only - // way to re-enable it. - if (!_IsAltBuffer()) - { - pWindow->EnableBothScrollBars(); - } - - pWindow->UpdateScrollBar(true, - _IsAltBuffer(), - _viewport.Height(), - gci.IsTerminalScrolling() ? _virtualBottom : buffer.BottomInclusive(), - _viewport.Top()); - pWindow->UpdateScrollBar(false, - _IsAltBuffer(), - _viewport.Width(), - buffer.RightInclusive(), - _viewport.Left()); - } - // Fire off an event to let accessibility apps know the layout has changed. if (_pAccessibilityNotifier) { _pAccessibilityNotifier->NotifyConsoleLayoutEvent(); } - ResizingWindow--; + const auto buffer = GetBufferSize(); + const auto isAltBuffer = _IsAltBuffer(); + const auto maxSizeVer = gci.IsTerminalScrolling() ? _virtualBottom : buffer.BottomInclusive(); + const auto maxSizeHor = buffer.RightInclusive(); + return ScrollBarState{ + .maxSize = { maxSizeHor, maxSizeVer }, + .viewport = _viewport.ToExclusive(), + .isAltBuffer = isAltBuffer, + }; } // Routine Description: diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index 09fca19809b..c0351050566 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -99,8 +99,14 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console bool HasAccessibilityEventing() const noexcept; void NotifyAccessibilityEventing(const til::CoordType sStartX, const til::CoordType sStartY, const til::CoordType sEndX, const til::CoordType sEndY); + struct ScrollBarState + { + til::size maxSize; + til::rect viewport; + bool isAltBuffer = false; + }; void UpdateScrollBars(); - void InternalUpdateScrollBars(); + ScrollBarState FetchScrollBarState(); bool IsMaximizedBoth() const; bool IsMaximizedX() const; @@ -158,7 +164,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console bool CursorIsDoubleWidth() const; DWORD OutputMode; - WORD ResizingWindow; // > 0 if we should ignore WM_SIZE messages short WheelDelta; short HWheelDelta; diff --git a/src/host/server.h b/src/host/server.h index 3332cc6b383..cfa1ba14dc6 100644 --- a/src/host/server.h +++ b/src/host/server.h @@ -102,6 +102,7 @@ class CONSOLE_INFORMATION : void LockConsole() noexcept; void UnlockConsole() noexcept; + til::recursive_ticket_lock_suspension SuspendLock() noexcept; bool IsConsoleLocked() const noexcept; ULONG GetCSRecursionCount() const noexcept; diff --git a/src/interactivity/inc/IConsoleWindow.hpp b/src/interactivity/inc/IConsoleWindow.hpp index 232957ffb6c..fba098ea78d 100644 --- a/src/interactivity/inc/IConsoleWindow.hpp +++ b/src/interactivity/inc/IConsoleWindow.hpp @@ -25,13 +25,6 @@ namespace Microsoft::Console::Types public: virtual ~IConsoleWindow() = default; - virtual BOOL EnableBothScrollBars() = 0; - virtual int UpdateScrollBar(_In_ bool isVertical, - _In_ bool isAltBuffer, - _In_ UINT pageSize, - _In_ int maxSize, - _In_ int viewportPosition) = 0; - virtual bool IsInFullscreen() const = 0; virtual void SetIsFullscreen(const bool fFullscreenEnabled) = 0; diff --git a/src/interactivity/onecore/ConsoleWindow.cpp b/src/interactivity/onecore/ConsoleWindow.cpp index f6b7fac3c89..8d8fb9dcdc4 100644 --- a/src/interactivity/onecore/ConsoleWindow.cpp +++ b/src/interactivity/onecore/ConsoleWindow.cpp @@ -11,20 +11,6 @@ using namespace Microsoft::Console::Interactivity::OneCore; using namespace Microsoft::Console::Types; -BOOL ConsoleWindow::EnableBothScrollBars() noexcept -{ - return FALSE; -} - -int ConsoleWindow::UpdateScrollBar(bool /*isVertical*/, - bool /*isAltBuffer*/, - UINT /*pageSize*/, - int /*maxSize*/, - int /*viewportPosition*/) noexcept -{ - return 0; -} - bool ConsoleWindow::IsInFullscreen() const noexcept { return true; diff --git a/src/interactivity/onecore/ConsoleWindow.hpp b/src/interactivity/onecore/ConsoleWindow.hpp index 4ab28d8626d..530aafd13cb 100644 --- a/src/interactivity/onecore/ConsoleWindow.hpp +++ b/src/interactivity/onecore/ConsoleWindow.hpp @@ -24,9 +24,6 @@ namespace Microsoft::Console::Interactivity::OneCore { public: // Inherited via IConsoleWindow - BOOL EnableBothScrollBars() noexcept override; - int UpdateScrollBar(bool isVertical, bool isAltBuffer, UINT pageSize, int maxSize, int viewportPosition) noexcept override; - bool IsInFullscreen() const noexcept override; void SetIsFullscreen(const bool fFullscreenEnabled) noexcept override; void ChangeViewport(const til::inclusive_rect& NewWindow) override; diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp index 3f802740588..599d1f376a7 100644 --- a/src/interactivity/win32/window.cpp +++ b/src/interactivity/win32/window.cpp @@ -438,7 +438,7 @@ void Window::_CloseWindow() const ShowWindow(hWnd, wShowWindow); auto& siAttached = GetScreenInfo(); - siAttached.InternalUpdateScrollBars(); + siAttached.UpdateScrollBars(); } return status; @@ -591,7 +591,7 @@ void Window::_UpdateWindowSize(const til::size sizeNew) if (WI_IsFlagClear(gci.Flags, CONSOLE_IS_ICONIC)) { - ScreenInfo.InternalUpdateScrollBars(); + ScreenInfo.UpdateScrollBars(); SetWindowPos(GetWindowHandle(), nullptr, @@ -621,7 +621,7 @@ void Window::_UpdateWindowSize(const til::size sizeNew) if (!IsInFullscreen() && !IsInMaximized()) { // Figure out how big to make the window, given the desired client area size. - siAttached.ResizingWindow++; + _resizingWindow++; // First get the buffer viewport size const auto WindowDimensions = siAttached.GetViewport().Dimensions(); @@ -691,7 +691,7 @@ void Window::_UpdateWindowSize(const til::size sizeNew) // If the change wasn't substantial, we may still need to update scrollbar positions. Note that PSReadLine // scrolls the window via Console.SetWindowPosition, which ultimately calls down to SetConsoleWindowInfo, // which ends up in this function. - siAttached.InternalUpdateScrollBars(); + siAttached.UpdateScrollBars(); } // MSFT: 12092729 @@ -716,7 +716,7 @@ void Window::_UpdateWindowSize(const til::size sizeNew) // an additional Buffer message with the same size again and do nothing special. ScreenBufferSizeChange(siAttached.GetActiveBuffer().GetBufferSize().Dimensions()); - siAttached.ResizingWindow--; + _resizingWindow--; } LOG_IF_FAILED(ConsoleImeResizeCompStrView()); @@ -875,26 +875,29 @@ void Window::HorizontalScroll(const WORD wScrollCommand, const WORD wAbsoluteCha LOG_IF_FAILED(ScreenInfo.SetViewportOrigin(true, NewOrigin, false)); } -BOOL Window::EnableBothScrollBars() +void Window::UpdateScrollBars(const SCREEN_INFORMATION::ScrollBarState& state) { - return EnableScrollBar(_hWnd, SB_BOTH, ESB_ENABLE_BOTH); -} + // If this is the main buffer, make sure we enable both of the scroll bars. + // The alt buffer likely disabled the scroll bars, this is the only way to re-enable it. + if (!state.isAltBuffer) + { + EnableScrollBar(_hWnd, SB_BOTH, ESB_ENABLE_BOTH); + } -int Window::UpdateScrollBar(bool isVertical, - bool isAltBuffer, - UINT pageSize, - int maxSize, - int viewportPosition) -{ - SCROLLINFO si; - si.cbSize = sizeof(si); - si.fMask = isAltBuffer ? SIF_ALL | SIF_DISABLENOSCROLL : SIF_ALL; - si.nPage = pageSize; - si.nMin = 0; - si.nMax = maxSize; - si.nPos = viewportPosition; - - return SetScrollInfo(_hWnd, isVertical ? SB_VERT : SB_HORZ, &si, TRUE); + SCROLLINFO si{ + .cbSize = sizeof(SCROLLINFO), + .fMask = static_cast(state.isAltBuffer ? SIF_ALL | SIF_DISABLENOSCROLL : SIF_ALL), + }; + + si.nMax = state.maxSize.width; + si.nPage = state.viewport.width(); + si.nPos = state.viewport.left; + SetScrollInfo(_hWnd, SB_HORZ, &si, TRUE); + + si.nMax = state.maxSize.height; + si.nPage = state.viewport.height(); + si.nPos = state.viewport.top; + SetScrollInfo(_hWnd, SB_VERT, &si, TRUE); } // Routine Description: diff --git a/src/interactivity/win32/window.hpp b/src/interactivity/win32/window.hpp index 45db4e7fbe6..7550b18c9d8 100644 --- a/src/interactivity/win32/window.hpp +++ b/src/interactivity/win32/window.hpp @@ -65,12 +65,7 @@ namespace Microsoft::Console::Interactivity::Win32 void HorizontalScroll(const WORD wScrollCommand, const WORD wAbsoluteChange); - BOOL EnableBothScrollBars(); - int UpdateScrollBar(bool isVertical, - bool isAltBuffer, - UINT pageSize, - int maxSize, - int viewportPosition); + void UpdateScrollBars(const SCREEN_INFORMATION::ScrollBarState& state); void UpdateWindowSize(const til::size coordSizeInChars); void UpdateWindowPosition(_In_ const til::point ptNewPos) const; @@ -185,6 +180,7 @@ namespace Microsoft::Console::Interactivity::Win32 static void s_ReinitializeFontsForDPIChange(); + WORD _resizingWindow = 0; // > 0 if we should ignore WM_SIZE messages bool _fInDPIChange = false; static void s_ConvertWindowPosToWindowRect(const LPWINDOWPOS lpWindowPos, diff --git a/src/interactivity/win32/windowproc.cpp b/src/interactivity/win32/windowproc.cpp index f215a166119..37def3100da 100644 --- a/src/interactivity/win32/windowproc.cpp +++ b/src/interactivity/win32/windowproc.cpp @@ -669,7 +669,16 @@ using namespace Microsoft::Console::Types; case CM_UPDATE_SCROLL_BARS: { - ScreenInfo.InternalUpdateScrollBars(); + const auto state = ScreenInfo.FetchScrollBarState(); + + // EnableScrollbar() and especially SetScrollInfo() are prohibitively expensive functions nowadays. + // Unlocking early here improves throughput of good old `type` in cmd.exe by ~10x. + UnlockConsole(); + Unlock = FALSE; + + _resizingWindow++; + UpdateScrollBars(state); + _resizingWindow--; break; } @@ -780,7 +789,7 @@ void Window::_HandleWindowPosChanged(const LPARAM lParam) // CONSOLE_IS_ICONIC bit appropriately. doing so in the WM_SIZE handler is incorrect because the WM_SIZE // comes after the WM_ERASEBKGND during SetWindowPos() processing, and the WM_ERASEBKGND needs to know if // the console window is iconic or not. - if (!ScreenInfo.ResizingWindow && (lpWindowPos->cx || lpWindowPos->cy) && !IsIconic(hWnd)) + if (!_resizingWindow && (lpWindowPos->cx || lpWindowPos->cy) && !IsIconic(hWnd)) { // calculate the dimensions for the newly proposed window rectangle til::rect rcNew; From 5f5ef10571a17af4feeafd6be8729085fe5a4ac0 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 20:37:58 +0100 Subject: [PATCH 033/603] Fix ConPTY inputs incorrectly being treated as plain text (#16352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is my proposal to avoid aborting ConPTY input parsing because a read accidentally got split up into more than one chunk. This happens a lot with WSL for me, as I often get (for instance) a `\x1b[67;46;99;0;32;` input followed immediately by a `1_` input. The current logic would cause both of these to be flushed out to the client application. This PR fixes the issue by only flushing either a standalone escape character or a escape+character combination. It basically limits the previous code to just `VTStates::Ground` and `VTStates::Escape`. I'm not using the `_state` member, because `VTStates::OscParam` makes no distinction between `\x1b]` and `\x1b]1234` and I only want to flush the former. I felt like checking the contents of `run` directly is easier to understand. Related to #16343 ## Validation Steps Performed * win32-input-mode sequences are now properly buffered ✅ * Standalone alt-key combinations are still being flushed ✅ --- src/terminal/parser/stateMachine.cpp | 70 ++++++------------- .../parser/ut_parser/InputEngineTest.cpp | 14 ++++ 2 files changed, 34 insertions(+), 50 deletions(-) diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index eb22ff95382..643409afda5 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -2127,6 +2127,8 @@ void StateMachine::ProcessString(const std::wstring_view string) // If we're at the end of the string and have remaining un-printed characters, if (_state != VTStates::Ground) { + const auto run = _CurrentRun(); + // One of the "weird things" in VT input is the case of something like // alt+[. In VT, that's encoded as `\x1b[`. However, that's // also the start of a CSI, and could be the start of a longer sequence, @@ -2136,60 +2138,28 @@ void StateMachine::ProcessString(const std::wstring_view string) // alt+[, A would be processed like `\x1b[A`, // which is _wrong_). // - // Fortunately, for VT input, each keystroke comes in as an individual - // write operation. So, if at the end of processing a string for the - // InputEngine, we find that we're not in the Ground state, that implies - // that we've processed some input, but not dispatched it yet. This - // block at the end of `ProcessString` will then re-process the - // undispatched string, but it will ensure that it dispatches on the - // last character of the string. For our previous `\x1b[` scenario, that - // means we'll make sure to call `_ActionEscDispatch('[')`., which will - // properly decode the string as alt+[. - const auto run = _CurrentRun(); - + // At the same time, input may be broken up arbitrarily, depending on the pipe's + // buffer size, our read-buffer size, the sender's write-buffer size, and more. + // In fact, with the current WSL, input is broken up in 16 byte chunks (Why? :(), + // which breaks up many of our longer sequences, like our Win32InputMode ones. + // + // As a heuristic, this code specifically checks for a trailing Esc or Alt+key. if (_isEngineForInput) { - // Reset our state, and put all but the last char in again. - ResetState(); - _processingLastCharacter = false; - // Chars to flush are [pwchSequenceStart, pwchCurr) - auto wchIter = run.cbegin(); - while (wchIter < run.cend() - 1) - { - ProcessCharacter(*wchIter); - wchIter++; - } - // Manually execute the last char [pwchCurr] - _processingLastCharacter = true; - switch (_state) + if (run.size() <= 2 && run.front() == L'\x1b') { - case VTStates::Ground: - _ActionExecute(*wchIter); - break; - case VTStates::Escape: - case VTStates::EscapeIntermediate: - _ActionEscDispatch(*wchIter); - break; - case VTStates::CsiEntry: - case VTStates::CsiIntermediate: - case VTStates::CsiIgnore: - case VTStates::CsiParam: - case VTStates::CsiSubParam: - _ActionCsiDispatch(*wchIter); - break; - case VTStates::OscParam: - case VTStates::OscString: - case VTStates::OscTermination: - _ActionOscDispatch(*wchIter); - break; - case VTStates::Ss3Entry: - case VTStates::Ss3Param: - _ActionSs3Dispatch(*wchIter); - break; + _EnterGround(); + if (run.size() == 1) + { + _ActionExecute(L'\x1b'); + } + else + { + _EnterEscape(); + _ActionEscDispatch(run.back()); + } + _EnterGround(); } - // microsoft/terminal#2746: Make sure to return to the ground state - // after dispatching the characters - _EnterGround(); } else if (_state != VTStates::SosPmApcString && _state != VTStates::DcsPassThrough && _state != VTStates::DcsIgnore) { diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index 2e21c77c74f..fcb23734be5 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -260,6 +260,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest TEST_METHOD(AltCtrlDTest); TEST_METHOD(AltIntermediateTest); TEST_METHOD(AltBackspaceEnterTest); + TEST_METHOD(ChunkedSequence); TEST_METHOD(SGRMouseTest_ButtonClick); TEST_METHOD(SGRMouseTest_Modifiers); TEST_METHOD(SGRMouseTest_Movement); @@ -1041,6 +1042,19 @@ void InputEngineTest::AltBackspaceEnterTest() VerifyExpectedInputDrained(); } +void InputEngineTest::ChunkedSequence() +{ + // This test ensures that a DSC sequence that's split up into multiple chunks isn't + // confused with a single Alt+key combination like in the AltBackspaceEnterTest(). + // Basically, it tests the selectivity of the AltBackspaceEnterTest() fix. + + auto dispatch = std::make_unique(nullptr, nullptr); + auto inputEngine = std::make_unique(std::move(dispatch)); + StateMachine stateMachine{ std::move(inputEngine) }; + stateMachine.ProcessString(L"\x1b[1"); + VERIFY_ARE_EQUAL(StateMachine::VTStates::CsiParam, stateMachine._state); +} + // Method Description: // - Writes an SGR VT sequence based on the necessary parameters // Arguments: From f9652983f1e3d5cf44305d571cca2b99bf710d49 Mon Sep 17 00:00:00 2001 From: debghs <145260557+debghs@users.noreply.github.com> Date: Wed, 6 Dec 2023 03:09:00 +0530 Subject: [PATCH 034/603] Minor grammar fixes for the vintage AddASetting.md doc (#16188) ## Summary of the Pull Request Added some Punctuation Marks as Required. ## References and Relevant Issues None. ## Detailed Description of the Pull Request / Additional comments There were some missing Punctuation Marks(Ex: Colon(:) and Full Stop(.)), so I have added them. ## Validation Steps Performed --- doc/AddASetting.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/AddASetting.md b/doc/AddASetting.md index 2d4c414d910..59128125d71 100644 --- a/doc/AddASetting.md +++ b/doc/AddASetting.md @@ -5,10 +5,10 @@ `.../console/published/wincon.w` in the OS repo when you submit the PR. The branch won't build without it. * For now, you can update winconp.h with your consumable changes. - * Define registry name (ex `CONSOLE_REGISTRY_CURSORCOLOR`) - * Add the setting to `CONSOLE_STATE_INFO` + * Define registry name (ex: `CONSOLE_REGISTRY_CURSORCOLOR`) + * Add the setting to `CONSOLE_STATE_INFO`. * Define the property key ID and the property key itself. - - Yes, the large majority of the `DEFINE_PROPERTYKEY` defs are the same, it's only the last byte of the guid that changes + - Yes, the large majority of the `DEFINE_PROPERTYKEY` defs are the same, it's only the last byte of the guid that changes. 2. Add matching fields to Settings.hpp - Add getters, setters, the whole drill. @@ -17,9 +17,9 @@ - We need to add it to *reading and writing* the registry from the propsheet, and *reading* the link from the propsheet. Yes, that's weird, but the propsheet is smart enough to re-use ShortcutSerialization::s_SetLinkValues, but not smart enough to do the same with RegistrySerialization. - `src/propsheet/registry.cpp` - `propsheet/registry.cpp@InitRegistryValues` should initialize the default value for the property. - - `propsheet/registry.cpp@GetRegistryValues` should make sure to read the property from the registry + - `propsheet/registry.cpp@GetRegistryValues` should make sure to read the property from the registry. -4. Add the field to the propslib registry map +4. Add the field to the propslib registry map. 5. Add the value to `ShortcutSerialization.cpp` - Read the value in `ShortcutSerialization::s_PopulateV2Properties` @@ -30,11 +30,11 @@ Now, your new setting should be stored just like all the other properties. 7. Update the feature test properties to get add the setting as well - `ft_uia/Common/NativeMethods.cs@WinConP`: - - `Wtypes.PROPERTYKEY PKEY_Console_` - - `NT_CONSOLE_PROPS` + - `Wtypes.PROPERTYKEY PKEY_Console_`. + - `NT_CONSOLE_PROPS`. 8. Add the default value for the setting to `win32k-settings.man` - If the setting shouldn't default to 0 or `nullptr`, then you'll need to set the default value of the setting in `win32k-settings.man`. -9. Update `Settings::InitFromStateInfo` and `Settings::CreateConsoleStateInfo` to get/set the value in a CONSOLE_STATE_INFO appropriately +9. Update `Settings::InitFromStateInfo` and `Settings::CreateConsoleStateInfo` to get/set the value in a CONSOLE_STATE_INFO appropriately. From 9967851bf83dc1edf1acb510a8535d8b0bca9a6b Mon Sep 17 00:00:00 2001 From: Jvr <109031036+Jvr2022@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:30:21 +0100 Subject: [PATCH 035/603] Update xamlstyler to 3.2311.2 (#16422) Update xalmstyler to 3.2311.2 --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index e56b4d46c55..4479fcd4875 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "XamlStyler.Console": { - "version": "3.2206.4", + "version": "3.2311.2", "commands": [ "xstyler" ] From 65d2d3dcec78e1ff075cc0ae383f385a0771eb20 Mon Sep 17 00:00:00 2001 From: Jvr <109031036+Jvr2022@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:31:52 +0100 Subject: [PATCH 036/603] Update actions/add-to-project to version 0.5.0 (#16084) Update actions/add-to-project to version 0.5.0 --- .github/workflows/addToProject.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/addToProject.yml b/.github/workflows/addToProject.yml index 3fbb2a7b2cb..4baa537dd19 100644 --- a/.github/workflows/addToProject.yml +++ b/.github/workflows/addToProject.yml @@ -7,13 +7,13 @@ on: - labeled - unlabeled -permissions: {} +permissions: {} jobs: add-to-project: name: Add issue to project runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@v0.3.0 + - uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/orgs/microsoft/projects/159 github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} From dc986e44898c0927010b258dcfc06986a3a9885e Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:40:23 -0500 Subject: [PATCH 037/603] Check spelling 0.0.22 (#16127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upgrades check-spelling to [v0.0.22](https://github.com/check-spelling/check-spelling/releases/tag/v0.0.22) * refreshes workflow * enables dependabot PRs to trigger CI (so that in the future you'll be able to see breaking changes to the dictionary paths) * refreshes metadata * built-in handling of `\n`/`\r`/`\t` is removed -- This means that the `patterns/0_*.txt` files can be removed. * this specific PR includes some shim content, in `allow/check-spelling-0.0.21.txt` -- once it this PR merges, it can be removed on a branch and the next CI will clean out items from `expect.txt` relating to the `\r` stuff and suggest replacement content. * talking to the bot is enabled for forks (but not the master repository) * SARIF reporting is enabled for PRs w/in a single repository (not across forks) * In job reports, there's a summary table (space permitting) linking to instances (this is a poor man's SARIF report) * When a pattern splits a thing that results in check-spelling finding an unrecognized token, that's reported with a distinct category * When there are items in expect that not longer match anything but more specific items do (e.g. `microsoft` vs. `Microsoft`), there's now a specific category with help/advice * Fancier excludes suggestions (excluding directories, file types, ...) * Refreshed dictionaries * The comment now links to the job summary (which includes SARIF link if available, the details view, and a generated commit that people can use if they're ok w/ the expect changes and don't want to run perl) Validation ---------- 1. the branch was developed in https://github.com/check-spelling-sandbox/terminal/actions?query=branch%3Acheck-spelling-0.0.22 2. ensuring compatibility with 0.0.21 was done in https://github.com/check-spelling-sandbox/terminal/pull/3 3. this version has been in development for a year and has quite a few improvements, we've been actively dogfooding it throughout this period 😄 Additional Fixes ---------------- spelling: the spelling: shouldn't spelling: no spelling: macos spelling: github spelling: fine-grained spelling: coarse-grained Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- .github/actions/spelling/advice.md | 6 +- .github/actions/spelling/allow/allow.txt | 3 + .../spelling/allow/check-spelling-0.0.21.txt | 224 +++++++++++++++++ .github/actions/spelling/allow/names.txt | 1 + .github/actions/spelling/candidate.patterns | 200 ++++++++++++--- .github/actions/spelling/excludes.txt | 59 +++-- .github/actions/spelling/expect/expect.txt | 227 ++++++------------ .github/actions/spelling/expect/web.txt | 1 - .../actions/spelling/line_forbidden.patterns | 73 +++++- .github/actions/spelling/patterns/0_n.txt | 2 - .github/actions/spelling/patterns/0_r.txt | 8 - .github/actions/spelling/patterns/0_t.txt | 13 - .github/actions/spelling/patterns/README.md | 4 - .../actions/spelling/patterns/patterns.txt | 128 +++++++--- .github/actions/spelling/reject.txt | 1 + .github/workflows/similarIssues.yml | 10 +- .github/workflows/spelling2.yml | 113 +++++++-- build/pipelines/ci.yml | 2 +- .../templates-v2/job-index-github-codenav.yml | 2 +- doc/Niksa.md | 2 +- .../#2634 - Broadcast Input.md | 2 +- .../#3327 - Application Theming.md | 2 +- src/renderer/atlas/AtlasEngine.r.cpp | 2 +- src/renderer/atlas/BackendD2D.cpp | 2 +- src/renderer/atlas/BackendD3D.cpp | 2 +- 25 files changed, 753 insertions(+), 336 deletions(-) create mode 100644 .github/actions/spelling/allow/check-spelling-0.0.21.txt delete mode 100644 .github/actions/spelling/patterns/0_n.txt delete mode 100644 .github/actions/spelling/patterns/0_r.txt delete mode 100644 .github/actions/spelling/patterns/0_t.txt diff --git a/.github/actions/spelling/advice.md b/.github/actions/spelling/advice.md index d82df49ee22..536c601aec2 100644 --- a/.github/actions/spelling/advice.md +++ b/.github/actions/spelling/advice.md @@ -6,8 +6,6 @@ By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later. -:warning: The command is written for posix shells. If it doesn't work for you, you can manually _add_ (one word per line) / _remove_ items to `expect.txt` and the `excludes.txt` files. - If the listed items are: * ... **misspelled**, then please *correct* them instead of using the command. @@ -36,7 +34,9 @@ https://www.regexplanet.com/advanced/perl/) yours before committing to verify it * well-formed pattern. - If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it, + If you can write a [pattern]( +https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns +) that would match it, try adding it to the `patterns.txt` file. Patterns are Perl 5 Regular Expressions - you can [test]( diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index bccfe086aeb..07eb75ab82d 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -55,6 +55,7 @@ hyperlinks iconify img inlined +issuetitle It'd kje libfuzzer @@ -95,6 +96,7 @@ rlig runtimes servicebus shcha +similaritytolerance slnt Sos ssh @@ -122,6 +124,7 @@ walkthroughs We'd westus wildcards +workarounds XBox YBox yeru diff --git a/.github/actions/spelling/allow/check-spelling-0.0.21.txt b/.github/actions/spelling/allow/check-spelling-0.0.21.txt new file mode 100644 index 00000000000..9463838f115 --- /dev/null +++ b/.github/actions/spelling/allow/check-spelling-0.0.21.txt @@ -0,0 +1,224 @@ +aarch +abi +activatable +aef +amd +applets +argb +ASDF +ative +ativecxapp +AVX +azzle +BGR +bigints +bignum +bitmask +bitmasks +blackhole +Bopomofo +BOTTOMLEFT +BOTTOMRIGHT +bsr +bstr +cbuffer +cci +changelist +changelists +Checkin +chk +cielab +cls +clz +CMake +cmt +Cmts +cmyk +cname +cnt +CNTRL +codepage +codepages +codepoints +colorspaces +COMDAT +configurability +cpuid +cred +Crt +dbg +distro +distros +django +DLOAD +ect +ectread +edist +egistry +elease +elemetry +elems +emacs +emark +emplates +enderer +endregion +endverbatim +eplace +erm +erminal +erminalcore +erminalinput +esource +esources +esthelper +estlist +estmd +estpasses +ests +esult +esultmacros +etfx +eturn +ewdelete +ewtonsoft +flyout +flyouts +FONTFAMILY +FONTSIZE +FONTWEIGHT +formattable +framebuffer +FSCTL +GAUSSIAN +getch +GETFONTSIZE +GETPOS +GNUC +groupbox +Gsub +GTR +HORZ +hread +hrottled +hrow +hsl +HTMLTo +icket +Iconified +idx +ihilist +Imm +inheritdoc +inlines +ioctl +keydown +keydowns +keyup +keyups +lnk +lss +MMIX +MOUSEMOVE +movl +movsb +msys +nameof +natvis +Newdelete +Newtonsoft +Nop +NOUPDATE +nullability +numpad +oklab +ools +OOM +osign +ote +otepad +ouicompat +passthrough +pcs +pgdn +pgup +pkey +pname +popcnt +prefs +prepopulated +ptrs +Pvf +qos +Rasterizer +RCDATA +rcl +rects +reparent +reparented +RGBCOLOR +roundtrips +RTTI +safearray +sapi +scancode +scancodes +scrollbars +Sdl +SETCOLOR +SETFONT +Shl +SND +srgb +Statusline +stl +stosb +strerror +strrev +subc +subfolders +subkey +subkeys +swappable +taskbar +tdll +testenv +testenvs +texel +TExpected +tioapi +titlebar +titlebars +TOPLEFT +TOPRIGHT +tprivapi +tsf +typelib +typeparam +UDM +uget +ules +umul +umulh +uninitialize +Unserialize +untimes +upkg +utc +vcpkg +vec +vectorized +vtable +vtables +WCAG +WINDOWTITLE +workaround +xchgl +xgetbv +XOFF +XON +YDPI +YOffset +ype +ypes +ZIndex diff --git a/.github/actions/spelling/allow/names.txt b/.github/actions/spelling/allow/names.txt index 0409931d020..11a1f95486b 100644 --- a/.github/actions/spelling/allow/names.txt +++ b/.github/actions/spelling/allow/names.txt @@ -6,6 +6,7 @@ bhoj Bhojwani Bluloco carlos +craigloewen dhowett Diviness dsafa diff --git a/.github/actions/spelling/candidate.patterns b/.github/actions/spelling/candidate.patterns index 400f103ea28..80f31f92e4f 100644 --- a/.github/actions/spelling/candidate.patterns +++ b/.github/actions/spelling/candidate.patterns @@ -1,23 +1,37 @@ # marker to ignore all code on line ^.*/\* #no-spell-check-line \*/.*$ -# marker for ignoring a comment to the end of the line -// #no-spell-check.*$ +# marker to ignore all code on line +^.*\bno-spell-check(?:-line|)(?:\s.*|)$ + +# https://cspell.org/configuration/document-settings/ +# cspell inline +^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b # patch hunk comments ^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .* # git index header -index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40} +index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40} + +# file permissions +['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s] + +# css url wrappings +\burl\([^)]+\) # cid urls (['"])cid:.*?\g{-1} # data url in parens -\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\) +#\(data:(?:[^) ][^)]*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\) + # data url in quotes -([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1} +([`'"])data:(?:[^ `'"].*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1} # data url data:[-a-zA-Z=;:/0-9+]*,\S* +# https/http/file urls +(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|] + # mailto urls mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} @@ -35,6 +49,9 @@ magnet:[?=:\w]+ # asciinema \basciinema\.org/a/[0-9a-zA-Z]+ +# asciinema v2 +^\[\d+\.\d+, "[io]", ".*"\]$ + # apple \bdeveloper\.apple\.com/[-\w?=/]+ # Apple music @@ -89,7 +106,7 @@ vpc-\w+ # Google Drive \bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]* # Google Groups -\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)* +\bgroups\.google\.com(?:/[a-z]+/(?:#!|)[^/\s"]+)* # Google Maps \bmaps\.google\.com/maps\?[\w&;=]* # Google themes @@ -117,6 +134,8 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+. (?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|) # GitHub SHAs \bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b +# GitHub SHA refs +\[([0-9a-f]+)\]\(https://(?:www\.|)github.com/[-\w]+/[-\w]+/commit/\g{-1}[0-9a-f]* # GitHub wiki \bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b # githubusercontent @@ -128,9 +147,9 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+. # git.io \bgit\.io/[0-9a-zA-Z]+ # GitHub JSON -"node_id": "[-a-zA-Z=;:/0-9+]*" +"node_id": "[-a-zA-Z=;:/0-9+_]*" # Contributor -\[[^\]]+\]\(https://github\.com/[^/\s"]+\) +\[[^\]]+\]\(https://github\.com/[^/\s"]+/?\) # GHSA GHSA(?:-[0-9a-z]{4}){3} @@ -143,8 +162,8 @@ GHSA(?:-[0-9a-z]{4}){3} # GitLab commits \bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b -# binanace -accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]* +# binance +accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]* # bitbucket diff \bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+ @@ -280,9 +299,9 @@ slack://[a-zA-Z0-9?&=]+ \bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+ # ipfs protocol -ipfs://[0-9a-z]* +ipfs://[0-9a-zA-Z]{3,} # ipfs url -/ipfs/[0-9a-z]* +/ipfs/[0-9a-zA-Z]{3,} # w3 \bw3\.org/[-0-9a-zA-Z/#.]+ @@ -359,22 +378,33 @@ ipfs://[0-9a-z]* # tinyurl \btinyurl\.com/\w+ +# codepen +\bcodepen\.io/[\w/]+ + +# registry.npmjs.org +\bregistry\.npmjs\.org/(?:@[^/"']+/|)[^/"']+/-/[-\w@.]+ + # getopts \bgetopts\s+(?:"[^"]+"|'[^']+') # ANSI color codes -(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m +(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m # URL escaped characters -\%[0-9A-F][A-F] +\%[0-9A-F][A-F](?=[A-Za-z]) +# lower URL escaped characters +\%[0-9a-f][a-f](?=[a-z]{2,}) # IPv6 -\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b +#\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b + # c99 hex digits (not the full format, just one I've seen) 0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP] +# Punycode +\bxn--[-0-9a-z]+ # sha sha\d+:[0-9]*[a-f]{3,}[0-9a-f]* # sha-... -- uses a fancy capture -(['"]|")[0-9a-f]{40,}\g{-1} +(\\?['"]|")[0-9a-f]{40,}\g{-1} # hex runs \b[0-9a-fA-F]{16,}\b # hex in url queries @@ -389,18 +419,21 @@ sha\d+:[0-9]*[a-f]{3,}[0-9a-f]* # Well known gpg keys .well-known/openpgpkey/[\w./]+ +# pki +-----BEGIN.*-----END + # uuid: \b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b # hex digits including css/html color classes: -(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b +(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b # integrity -integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}" +integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1} # https://www.gnu.org/software/groff/manual/groff.html # man troff content \\f[BCIPR] -# ' -\\\(aq +# '/" +\\\([ad]q # .desktop mime types ^MimeTypes?=.*$ @@ -409,21 +442,33 @@ integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}" # Localized .desktop content Name\[[^\]]+\]=.* -# IServiceProvider -\bI(?=(?:[A-Z][a-z]{2,})+\b) +# IServiceProvider / isAThing +\b(?:I|isA)(?=(?:[A-Z][a-z]{2,})+\b) # crypt -"\$2[ayb]\$.{56}" +(['"])\$2[ayb]\$.{56}\g{-1} # scrypt / argon \$(?:scrypt|argon\d+[di]*)\$\S+ +# go.sum +\bh1:\S+ + +# scala modules +("[^"]+"\s*%%?\s*){2,3}"[^"]+" + # Input to GitHub JSON -content: "[-a-zA-Z=;:/0-9+]*=" +content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1} -# Python stringprefix / binaryprefix +# This does not cover multiline strings, if your repository has them, +# you'll want to remove the `(?=.*?")` suffix. +# The `(?=.*?")` suffix should limit the false positives rate +# printf +#%(?:(?:(?:hh?|ll?|[jzt])?[diuoxn]|l?[cs]|L?[fega]|p)(?=[a-z]{2,})|(?:X|L?[FEGA]|p)(?=[a-zA-Z]{2,}))(?=[_a-zA-Z]+\b)(?!%)(?=.*?['"]) + +# Python string prefix / binary prefix # Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings -(?|m([|!/@#,;']).*?\g{-1}) + +# perl qr regex +(?|\(.*?\)|([|!/@#,;']).*?\g{-1}) # Go regular expressions regexp?\.MustCompile\(`[^`]*`\) +# regex choice +\(\?:[^)]+\|[^)]+\) + +# proto +^\s*(\w+)\s\g{-1} = + # sed regular expressions sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2} +# node packages +(["'])\@[^/'" ]+/[^/'" ]+\g{-1} + # go install go install(?:\s+[a-z]+\.[-@\w/.]+)+ +# jetbrains schema https://youtrack.jetbrains.com/issue/RSRP-489571 +urn:shemas-jetbrains-com + # kubernetes pod status lists # https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase \w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+ @@ -460,19 +524,47 @@ go install(?:\s+[a-z]+\.[-@\w/.]+)+ -[0-9a-f]{10}-\w{5}\s # posthog secrets -posthog\.init\((['"])phc_[^"',]+\g{-1}, +([`'"])phc_[^"',]+\g{-1} # xcode # xcodeproject scenes -(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}" +(?:Controller|destination|ID|id)="\w{3}-\w{2}-\w{3}" # xcode api botches customObjectInstantitationMethod +# configure flags +.* \| --\w{2,}.*?(?=\w+\s\w+) + # font awesome classes \.fa-[-a-z0-9]+ +# bearer auth +(['"])Bear[e][r] .*?\g{-1} + +# basic auth +(['"])Basic [-a-zA-Z=;:/0-9+]{3,}\g{-1} + +# base64 encoded content +#([`'"])[-a-zA-Z=;:/0-9+]+=\g{-1} +# base64 encoded content in xml/sgml +>[-a-zA-Z=;:/0-9+]+== 0.0.22) +\\\w{2,}\{ + +# eslint +"varsIgnorePattern": ".+" + +# Windows short paths +[/\\][^/\\]{5,6}~\d{1,2}[/\\] + +# in check-spelling@v0.0.22+, printf markers aren't automatically consumed +# printf markers +#(?v# (?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) -# Compiler flags (Scala) -(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) -# Compiler flags -#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) + +# Compiler flags (Unix, Java/Scala) +# Use if you have things like `-Pdocker` and want to treat them as `docker` +#(?:^|[\t ,>"'`=(])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) + +# Compiler flags (Windows / PowerShell) +# This is a subset of the more general compiler flags pattern. +# It avoids matching `-Path` to prevent it from being treated as `ath` +#(?:^|[\t ,"'`=(])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})) # Compiler flags (linker) ,-B + # curl arguments \b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)* # set arguments diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt index 441d1f295ca..57d475dc876 100644 --- a/.github/actions/spelling/excludes.txt +++ b/.github/actions/spelling/excludes.txt @@ -1,21 +1,24 @@ # See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes -(?:(?i)\.png$) (?:^|/)(?i)COPYRIGHT (?:^|/)(?i)LICEN[CS]E (?:^|/)3rdparty/ (?:^|/)dirs$ -(?:^|/)go\.mod$ (?:^|/)go\.sum$ (?:^|/)package(?:-lock|)\.json$ +(?:^|/)Pipfile$ +(?:^|/)pyproject.toml +(?:^|/)requirements(?:-dev|-doc|-test|)\.txt$ (?:^|/)sources(?:|\.dep)$ (?:^|/)vendor/ \.a$ \.ai$ +\.all-contributorsrc$ \.avi$ \.bmp$ \.bz2$ \.cer$ \.class$ +\.coveragerc$ \.crl$ \.crt$ \.csr$ @@ -27,11 +30,15 @@ \.eps$ \.exe$ \.gif$ +\.git-blame-ignore-revs$ \.gitattributes$ +\.gitignore$ +\.gitkeep$ \.graffle$ \.gz$ \.icns$ \.ico$ +\.ipynb$ \.jar$ \.jks$ \.jpeg$ @@ -41,61 +48,62 @@ \.lock$ \.map$ \.min\.. +\.mo$ \.mod$ \.mp3$ \.mp4$ \.o$ \.ocf$ \.otf$ +\.p12$ +\.parquet$ \.pbxproj$ \.pdf$ \.pem$ +\.pfx$ \.png$ \.psd$ \.pyc$ +\.pylintrc$ +\.qm$ \.runsettings$ \.s$ \.sig$ \.so$ \.svg$ \.svgz$ -\.svgz?$ +\.sys$ \.tar$ \.tgz$ \.tiff?$ \.ttf$ +\.vcxproj\.filters$ \.vsdx$ \.wav$ \.webm$ \.webp$ \.woff -\.woff2?$ \.xcf$ -\.xls \.xlsx?$ \.xpm$ -\.yml$ +\.xz$ \.zip$ ^\.github/actions/spelling/ -^\.github/fabricbot.json$ -^\.gitignore$ -^\Q.git-blame-ignore-revs\E$ ^\Q.github/workflows/spelling.yml\E$ -^\Qdoc/reference/windows-terminal-logo.ans\E$ -^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$ -^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$ +^\Qbuild/config/release.gdnbaselines\E$ ^\Qsrc/host/ft_host/chafa.txt\E$ -^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$ -^\XamlStyler.json$ +^\Qsrc/host/ft_uia/run.bat\E$ +^\Qsrc/host/runft.bat\E$ +^\Qsrc/tools/lnkd/lnkd.bat\E$ +^\Qsrc/tools/pixels/pixels.bat\E$ ^build/config/ ^consolegit2gitfilters\.json$ ^dep/ -^doc/reference/master-sequence-list.csv$ +^doc/reference/master-sequence-list\.csv$ ^doc/reference/UTF8-torture-test\.txt$ +^doc/reference/windows-terminal-logo\.ans$ ^oss/ -^src/host/ft_uia/run\.bat$ -^src/host/runft\.bat$ -^src/host/runut\.bat$ +^samples/PixelShaders/Screenshots/ ^src/interactivity/onecore/BgfxEngine\. ^src/renderer/atlas/ ^src/renderer/wddmcon/WddmConRenderer\. @@ -107,14 +115,13 @@ ^src/terminal/parser/ut_parser/Base64Test.cpp$ ^src/terminal/parser/ut_parser/run\.bat$ ^src/tools/benchcat +^src/tools/integrity/dirs$ ^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$ -^src/tools/lnkd/lnkd\.bat$ -^src/tools/pixels/pixels\.bat$ -^src/tools/RenderingTests/main.cpp$ +^src/tools/RenderingTests/main\.cpp$ ^src/tools/texttests/fira\.txt$ -^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$ -^src/types/ColorFix.cpp -^src/types/ut_types/UtilsTests.cpp$ -^tools/ReleaseEngineering/ServicingPipeline.ps1$ +^src/tools/U8U16Test/(?!en)..\. +^src/types/ColorFix\.cpp$ +^src/types/ut_types/UtilsTests\.cpp$ +^tools/ReleaseEngineering/ServicingPipeline\.ps1$ +^XamlStyler\.json$ ignore$ -SUMS$ diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index f38eddb9d3f..2de1494a028 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1,18 +1,16 @@ aabbcc -aarch ABANDONFONT abbcc ABCDEFGHIJKLMNOPQRSTUVWXY ABCF abgr -abi ABORTIFHUNG +ACCESSTOKEN acidev ACIOSS ACover actctx ACTCTXW -activatable ADDALIAS ADDREF ADDSTRING @@ -32,7 +30,7 @@ ALTERNATENAME ALTF ALTNUMPAD ALWAYSTIP -amd +aml ansicpg ANSISYS ANSISYSRC @@ -50,25 +48,23 @@ APPBARDATA appcontainer appium appletname -applets applicationmodel APPLMODAL appmodel APPWINDOW +APPXMANIFESTVERSION APrep apsect APSTUDIO archeologists -argb +Argb ARRAYSIZE ARROWKEYS asan ASBSET -ASDF asdfghjkl ASetting ASingle -asmx ASYNCWINDOWPOS atch ATest @@ -85,9 +81,8 @@ AUTORADIOBUTTON autoscrolling Autowrap AVerify -AVX awch -azzle +azurecr backgrounded Backgrounder backgrounding @@ -107,15 +102,16 @@ benchcat bgfx bgidx Bgk -BGR bgra BHID bigobj +binlog binplace binplaced +binskim bitcoin bitcrazed -bitmask +bitmasks BITOPERATION BKCOLOR BKGND @@ -126,10 +122,7 @@ BODGY BOLDFONT BOOLIFY bools -Bopomofo Borland -BOTTOMLEFT -BOTTOMRIGHT boutput boxheader BPBF @@ -139,7 +132,6 @@ branchconfig brandings Browsable Bspace -bstr BTNFACE bufferout buffersize @@ -162,11 +154,9 @@ cbiex CBN CBoolean cbt -cbuffer CCCBB cch CCHAR -cci CCmd ccolor CCom @@ -184,18 +174,15 @@ cfte CFuzz cgscrn chafa -changelist +changelists chaof charinfo CHARSETINFO chcbpat chh -chk chshdng CHT Cic -cielab -Cielab CLE cleartype CLICKACTIVE @@ -205,7 +192,7 @@ CLIPCHILDREN CLIPSIBLINGS closetest cloudconsole -cls +cloudvault CLSCTX clsids CLUSTERMAP @@ -214,23 +201,19 @@ cmder CMDEXT cmh CMOUSEBUTTONS -cmt +Cmts cmw -cmyk CNL cnn -cnt -CNTRL Codeflow -codepage +codenav +codepages codepath -codepoints coinit colorizing COLORMATRIX COLORREFs colorschemes -colorspaces colorspec colortable colortbl @@ -239,7 +222,6 @@ colortool COLR combaseapi comctl -COMDAT commandline commctrl commdlg @@ -256,7 +238,6 @@ conddkrefs condrv conechokey conemu -configurability conhost conime conimeinfo @@ -318,11 +299,10 @@ cpx CREATESCREENBUFFER CREATESTRUCT CREATESTRUCTW -cred +createvpack crisman CRLFs crloew -Crt CRTLIBS csbi csbiex @@ -379,7 +359,6 @@ DATABLOCK DBatch dbcs DBCSFONT -dbg DBGALL DBGCHARS DBGFONTS @@ -507,14 +486,13 @@ DISABLENOSCROLL DISPLAYATTRIBUTE DISPLAYATTRIBUTEPROPERTY DISPLAYCHANGE -distro +distros dlg DLGC DLLGETVERSIONPROC dllinit dllmain DLLVERSIONINFO -DLOAD DLOOK DONTCARE doskey @@ -564,7 +542,6 @@ ECH echokey ecount ECpp -ect Edgium EDITKEYS EDITTEXT @@ -576,13 +553,10 @@ EHsc EINS EJO ELEMENTNOTAVAILABLE -elems -emacs EMPTYBOX enabledelayedexpansion ENDCAP endptr -endregion ENTIREBUFFER entrypoints ENU @@ -590,10 +564,12 @@ ENUMLOGFONT ENUMLOGFONTEX enumranges EOK -eplace EPres EQU ERASEBKGND +ESFCIB +esrp +ESV ETW EUDC EVENTID @@ -648,7 +624,7 @@ FIter FIXEDCONVERTED FIXEDFILEINFO Flg -flyout +flyouts fmodern fmtarg fmtid @@ -658,27 +634,20 @@ fontdlg FONTENUMDATA FONTENUMPROC FONTFACE -FONTFAMILY FONTHEIGHT fontinfo FONTOK -FONTSIZE FONTSTRING -fonttbl FONTTYPE -FONTWEIGHT FONTWIDTH FONTWINDOW -fooo FORCEOFFFEEDBACK FORCEONFEEDBACK -framebuffer FRAMECHANGED fre frontends fsanitize Fscreen -FSCTL FSINFOCLASS fte Ftm @@ -692,12 +661,12 @@ fuzzwrapper fwdecl fwe fwlink -GAUSSIAN gci gcx gdi gdip gdirenderer +gdnbaselines Geddy geopol GETALIAS @@ -707,7 +676,6 @@ GETALIASEXES GETALIASEXESLENGTH GETAUTOHIDEBAREX GETCARETWIDTH -getch GETCLIENTAREAANIMATION GETCOMMANDHISTORY GETCOMMANDHISTORYLENGTH @@ -723,7 +691,6 @@ GETDISPLAYSIZE GETDLGCODE GETDPISCALEDSIZE GETFONTINFO -GETFONTSIZE GETHARDWARESTATE GETHUNGAPPTIMEOUT GETICON @@ -738,7 +705,6 @@ GETMOUSEVANISH GETNUMBEROFFONTS GETNUMBEROFINPUTEVENTS GETOBJECT -GETPOS GETSELECTIONINFO getset GETTEXTLEN @@ -754,13 +720,14 @@ gfx GGI GHIJK GHIJKL +gitcheckin gitfilters +gitlab gitmodules gle GLOBALFOCUS GLYPHENTRY GMEM -GNUC Goldmine gonce goutput @@ -768,11 +735,9 @@ GREENSCROLL Grehan Greyscale gridline -groupbox gset gsl GTP -GTR guc guidatom GValue @@ -823,9 +788,9 @@ hmod hmodule hmon homoglyph -HORZ hostable hostlib +Hostx HPA hpcon HPCON @@ -835,9 +800,7 @@ HPR HProvider HREDRAW hresult -hrottled hscroll -hsl hstr hstring HTBOTTOMLEFT @@ -847,7 +810,6 @@ HTCLIENT HTLEFT HTMAXBUTTON HTMINBUTTON -HTMLTo HTRIGHT HTTOP HTTOPLEFT @@ -858,9 +820,7 @@ hwheel hwnd HWNDPARENT iccex -icket ICONERROR -Iconified ICONINFORMATION IConsole ICONSTOP @@ -873,7 +833,6 @@ idllib IDOK IDR idth -idx IDXGI IEnd IEnum @@ -885,14 +844,12 @@ IHosted iid IIo ime -Imm IMPEXP inbox inclusivity INCONTEXT INFOEX inheritcursor -inheritdoc inheritfrom INITCOMMONCONTROLSEX INITDIALOG @@ -900,7 +857,6 @@ initguid INITMENU inkscape INLINEPREFIX -inlines inproc Inputkeyinfo INPUTPROCESSORPROFILE @@ -914,7 +870,7 @@ Interner intsafe INVALIDARG INVALIDATERECT -ioctl +Ioctl ipch ipp IProperty @@ -933,9 +889,9 @@ ivalid IWIC IXP jconcpp +JLO JOBOBJECT JOBOBJECTINFOCLASS -jpe JPN jsoncpp Jsons @@ -953,13 +909,13 @@ kernelbase kernelbasestaging KEYBDINPUT keychord -keydown +keydowns KEYFIRST KEYLAST Keymapping keyscan keystate -keyup +keyups khome KILLACTIVE KILLFOCUS @@ -993,6 +949,7 @@ LEFTALIGN libpopcnt libsancov libtickit +licate LIMITTEXT LINEDOWN LINESELECTION @@ -1005,7 +962,6 @@ listptrsize lld llx LMENU -lnk lnkd lnkfile LNM @@ -1063,12 +1019,12 @@ lpwstr LRESULT lsb lsconfig -lss lstatus lstrcmp lstrcmpi LTEXT LTLTLTLTL +ltsc LUID luma lval @@ -1113,6 +1069,7 @@ MENUITEMINFO MENUSELECT messageext metaproj +microsoftpublicsymbols midl mii MIIM @@ -1127,7 +1084,6 @@ Mip MMBB mmcc MMCPL -MMIX mmsystem MNC MNOPQ @@ -1141,12 +1097,12 @@ MONITORINFOF MOUSEACTIVATE MOUSEFIRST MOUSEHWHEEL -MOUSEMOVE MOVESTART msb msctf msctls msdata +MSDL msft MSGCMDLINEF MSGF @@ -1160,21 +1116,19 @@ MSIL msix msrc MSVCRTD -msys MTSM munged munges murmurhash muxes myapplet +mybranch mydir MYMAX Mypair Myval NAMELENGTH -nameof namestream -natvis NCCALCSIZE NCCREATE NCLBUTTONDOWN @@ -1187,6 +1141,8 @@ NCRBUTTONUP NCXBUTTONDOWN NCXBUTTONUP NEL +nerf +nerror netcoreapp netstandard NEWCPLINFO @@ -1226,7 +1182,6 @@ NONINFRINGEMENT NONPREROTATED nonspace NOOWNERZORDER -Nop NOPAINT noprofile NOREDRAW @@ -1248,7 +1203,6 @@ NOTRACK NOTSUPPORTED nouicompat nounihan -NOUPDATE NOYIELD NOZORDER nrcs @@ -1272,28 +1226,25 @@ ntuser NTVDM ntverp nugetversions -nullability nullness nullonfailure nullopts numlock -numpad NUMSCROLL +NUnit nupkg NVIDIA OACR objbase ocolor -odl oemcp OEMFONT OEMFORMAT OEMs offboarded -oklab -Oklab OLEAUT OLECHAR +onebranch onecore ONECOREBASE ONECORESDKTOOLS @@ -1304,7 +1255,6 @@ onecoreuuid ONECOREWINDOWS onehalf oneseq -OOM openbash opencode opencon @@ -1313,24 +1263,23 @@ openconsoleproxy openps openvt ORIGINALFILENAME +orking osc OSDEPENDSROOT OSG OSGENG -osign oss -otepad -ouicompat -OUnter outdir OUTOFCONTEXT Outptr outstr OVERLAPPEDWINDOW OWNDC +owneralias OWNERDRAWFIXED packagename packageuwp +PACKAGEVERSIONNUMBER PACKCOORD PACKVERSION pagedown @@ -1341,9 +1290,9 @@ PALPC pankaj parentable parms -passthrough PATCOPY pathcch +Pathto PATTERNID pcat pcb @@ -1362,7 +1311,6 @@ PCONSOLEENDTASK PCONSOLESETFOREGROUND PCONSOLEWINDOWOWNER pcoord -pcs pcshell PCSHORT PCSR @@ -1371,8 +1319,9 @@ PCWCH PCWCHAR PCWSTR pda -Pdbs +pdbs pdbstr +PDPs pdtobj pdw pdx @@ -1389,10 +1338,9 @@ PFONT PFONTENUMDATA PFS pgd -pgdn +pgomgr PGONu pguid -pgup phhook phwnd pidl @@ -1405,7 +1353,6 @@ pipestr pixelheight PIXELSLIST PJOBOBJECT -pkey platforming playsound ploc @@ -1418,6 +1365,7 @@ pntm POBJECT Podcast POINTSLIST +policheck POLYTEXTW poppack POPUPATTR @@ -1446,8 +1394,6 @@ prealigned prect prefast preflighting -prefs -prepopulated presorted PREVENTPINNING PREVIEWLABEL @@ -1459,6 +1405,7 @@ prioritization processenv processhost PROCESSINFOCLASS +PRODEXT PROPERTYID PROPERTYKEY PROPERTYVAL @@ -1490,7 +1437,6 @@ psr PSTR psz ptch -ptrs ptsz PTYIn PUCHAR @@ -1501,7 +1447,6 @@ pwstr pwsz pythonw Qaabbcc -qos QUERYOPEN QUESTIONMARK quickedit @@ -1523,9 +1468,7 @@ RBUTTONDBLCLK RBUTTONDOWN RBUTTONUP rcch -RCDATA rcelms -rcl rclsid RCOA RCOCA @@ -1540,7 +1483,7 @@ READCONSOLEOUTPUTSTRING READMODE reallocs reamapping -rects +rectread redef redefinable Redir @@ -1559,7 +1502,7 @@ remoting renamer renderengine rendersize -reparent +reparented reparenting replatformed Replymessage @@ -1571,12 +1514,12 @@ Resequence RESETCONTENT resheader resmimetype +resultmacros resw resx rfa rfid rftp -RGBCOLOR rgbi rgbs rgci @@ -1587,7 +1530,6 @@ rgn rgp rgpwsz rgrc -rgs rgui rgw RIGHTALIGN @@ -1598,8 +1540,8 @@ RIS roadmap robomac rosetta -roundtrips RRF +rrr RRRGGGBB rsas rtcore @@ -1607,7 +1549,6 @@ RTEXT RTFTo RTLREADING Rtn -RTTI ruleset runas RUNDLL @@ -1625,16 +1566,14 @@ RVERTICAL rvpa RWIN rxvt -safearray safemath -sapi sba SBCS SBCSDBCS sbi sbiex -sbold -scancode +sbom +scancodes scanline schemename SCL @@ -1646,7 +1585,6 @@ screeninfo screenshots scriptload scrollback -scrollbars SCROLLFORWARD SCROLLINFO scrolllock @@ -1655,7 +1593,6 @@ SCROLLSCALE SCROLLSCREENBUFFER scursor sddl -sdeleted SDKDDK securityappcontainer segfault @@ -1667,7 +1604,6 @@ Selfhosters SERVERDLL SETACTIVE SETBUDDYINT -SETCOLOR setcp SETCURSEL SETCURSOR @@ -1675,7 +1611,6 @@ SETCURSORINFO SETCURSORPOSITION SETDISPLAYMODE SETFOCUS -SETFONT SETFOREGROUND SETHARDWARESTATE SETHOTKEY @@ -1693,6 +1628,7 @@ SETSCREENBUFFERSIZE SETSEL SETTEXTATTRIBUTE SETTINGCHANGE +setvariable Setwindow SETWINDOWINFO SFGAO @@ -1710,7 +1646,6 @@ shellscalingapi SHFILEINFO SHGFI SHIFTJIS -Shl shlguid shlobj shlwapi @@ -1726,6 +1661,7 @@ SHOWWINDOW sidebyside SIF SIGDN +Signtool SINGLEFLAG SINGLETHREADED siup @@ -1745,7 +1681,6 @@ slpit SManifest SMARTQUOTE SMTO -SND SOLIDBOX Solutiondir somefile @@ -1758,8 +1693,6 @@ srcsrv SRCSRVTRG srctool srect -srgb -Srgb srv srvinit srvpipe @@ -1773,7 +1706,6 @@ STARTUPINFOW STARTWPARMS STARTWPARMSA STARTWPARMSW -Statusline stb stdafx STDAPI @@ -1783,26 +1715,21 @@ STDEXT STDMETHODCALLTYPE STDMETHODIMP STGM -stl -Stri Stringable STRINGTABLE -strrev strsafe STUBHEAD STUVWX stylecop SUA subcompartment -subfolders -subkey +subkeys SUBLANG subresource subsystemconsole subsystemwindows swapchain swapchainpanel -swappable SWMR SWP SYMED @@ -1828,8 +1755,6 @@ targetentrypoint TARGETLIBS TARGETNAME targetver -taskbar -tbar TBase tbc tbi @@ -1838,8 +1763,8 @@ TBM tchar TCHFORMAT TCI -tcommandline tcommands +tdbuild Tdd TDelegated TDP @@ -1856,7 +1781,7 @@ terminfo TEs testcon testd -testenv +testenvs testlab testlist testmd @@ -1865,7 +1790,6 @@ TESTNULL testpass testpasses TEXCOORD -texel TExpected textattribute TEXTATTRIBUTEID @@ -1878,14 +1802,12 @@ TEXTMETRICW textmode texttests TFCAT -tfoo TFunction -tga THUMBPOSITION THUMBTRACK TIcon tilunittests -titlebar +titlebars TITLEISLINKNAME TJson TLambda @@ -1902,15 +1824,11 @@ toolbars TOOLINFO TOOLWINDOW TOPDOWNDIB -TOPLEFT -TOPRIGHT TOpt tosign touchpad Tpp Tpqrst -tprivapi -tput tracelog tracelogging traceloggingprovider @@ -1927,8 +1845,8 @@ TRIANGLESTRIP Tribool TRIMZEROHEADINGS trx +tsa tsattrs -tsf tsgr tsm TStr @@ -1937,15 +1855,12 @@ TSub TTBITMAP TTFONT TTFONTLIST -tthe -tthis TTM TTo tvpp +tvtseq Txtev typechecked -typelib -typeparam TYUI UAC uap @@ -1956,10 +1871,8 @@ ucd uch UChars udk -UDM uer UError -uget uia UIACCESS uiacore @@ -1968,8 +1881,6 @@ uielem UIELEMENTENABLEDONLY UINTs ulcch -umul -umulh Unadvise unattend UNCPRIORITY @@ -1978,7 +1889,6 @@ unhighlighting unhosted UNICODETEXT UNICRT -uninitialize Unintense Uniscribe unittesting @@ -1989,7 +1899,6 @@ UNORM unparseable unregistering untextured -untimes UPDATEDISPLAY UPDOWN UPKEY @@ -2018,8 +1927,6 @@ USESTDHANDLES usp USRDLL utext -UText -UTEXT utr UVWXY UVWXYZ @@ -2029,12 +1936,9 @@ uxtheme Vanara vararg vclib -vcpkg vcprintf vcxitems -vec vectorize -vectorized VERCTRL VERTBAR VFT @@ -2048,6 +1952,9 @@ vkey VKKEYSCAN VMs VPA +vpack +vpackdirectory +VPACKMANIFESTDIRECTORY VPR VProc VRaw @@ -2057,7 +1964,9 @@ vsconfig vscprintf VSCROLL vsdevshell +vse vsinfo +vsinstalldir vso vspath VSTAMP @@ -2147,16 +2056,17 @@ windowsshell windowsterminal windowsx windowtheme -WINDOWTITLE winevent wingdi winget +wingetcreate WINIDE winioctl winmd winmeta winmgr winmm +WINMSAPP winnt Winperf WInplace @@ -2182,13 +2092,13 @@ WNegative WNull wnwb workarea -workaround WOutside WOWARM WOWx wparam WPartial wpf +wpfdotnet WPR WPrep WPresent @@ -2246,6 +2156,7 @@ xdy XEncoding xes xff +XFG XFile XFORM xin @@ -2273,9 +2184,7 @@ yact YCast YCENTER YCount -YDPI YLimit -YOffset YSubstantial YVIRTUALSCREEN YWalk diff --git a/.github/actions/spelling/expect/web.txt b/.github/actions/spelling/expect/web.txt index 8f5b59ac730..39e8cc78d55 100644 --- a/.github/actions/spelling/expect/web.txt +++ b/.github/actions/spelling/expect/web.txt @@ -1,4 +1,3 @@ -WCAG winui appshellintegration mdtauk diff --git a/.github/actions/spelling/line_forbidden.patterns b/.github/actions/spelling/line_forbidden.patterns index 31ad2ddcd26..41cf9272ed2 100644 --- a/.github/actions/spelling/line_forbidden.patterns +++ b/.github/actions/spelling/line_forbidden.patterns @@ -1,4 +1,6 @@ -# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere +# reject `m_data` as VxWorks defined it and that breaks things if it's used elsewhere +# see [fprime](https://github.com/nasa/fprime/commit/d589f0a25c59ea9a800d851ea84c2f5df02fb529) +# and [Qt](https://github.com/qtproject/qt-solutions/blame/fb7bc42bfcc578ff3fa3b9ca21a41e96eb37c1c7/qtscriptclassic/src/qscriptbuffer_p.h#L46) # \bm_data\b # If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test, @@ -6,40 +8,72 @@ # to use this: #\bfit\( +# s.b. anymore +\bany more[,.] + # s.b. GitHub -\bGithub\b +(?)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|"'`=(])-(?:D(?=[A-Z])|[WX]|f(?=[ms]))(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) + +# hit-count: 60 file-count: 35 # version suffix v# (?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) -# hit-count: 20 file-count: 9 -# hex runs -\b[0-9a-fA-F]{16,}\b +# hit-count: 2 file-count: 2 +# This does not cover multiline strings, if your repository has them, +# you'll want to remove the `(?=.*?")` suffix. +# The `(?=.*?")` suffix should limit the false positives rate +# printf +%(?:s)(?!ize)(?=[a-z]{2,}) -# hit-count: 10 file-count: 7 +# hit-count: 16 file-count: 10 # uuid: \b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b +# hit-count: 13 file-count: 4 +# Non-English +[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,} + +# hit-count: 7 file-count: 5 +# hex digits including css/html color classes: +(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b + +# hit-count: 7 file-count: 1 +# regex choice +\(\?:[^)]+\|[^)]+\) + # hit-count: 4 file-count: 4 -# mailto urls -mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} +# tar arguments +\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ # hit-count: 4 file-count: 1 # ANSI color codes -(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m +(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m + +# hit-count: 4 file-count: 1 +# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally ) +# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review - +# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into: +## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary). +## You could manually change `(?i)X...` to use `[Xx]...` +## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path) +# Lorem +(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])* + +# hit-count: 3 file-count: 3 +# mailto urls +mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} # hit-count: 2 file-count: 1 -# latex -\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+) +# Python string prefix / binary prefix +# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings +(?= 0.0.22) +\\\w{2,}\{ # hit-count: 1 file-count: 1 -# French -# This corpus only had capital letters, but you probably want lowercase ones as well. -\b[LN]'+[a-z]{2,}\b +# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long... +\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b + +# Questionably acceptable forms of `in to` +# Personally, I prefer `log into`, but people object +# https://www.tprteaching.com/log-into-log-in-to-login/ +\b(?:[Ll]og|[Ss]ign) in to\b + +# to opt in +\bto opt in\b # acceptable duplicates # ls directory listings -[-bcdlpsw](?:[-r][-w][-sx]){3}\s+\d+\s+(\S+)\s+\g{-1}\s+\d+\s+ -# C/idl types + English ... -\s(Guid|long|LONG|that) \g{-1}\s - -# javadoc / .net -(?:[\\@](?:groupname|param)|(?:public|private)(?:\s+static|\s+readonly)*)\s+(\w+)\s+\g{-1}\s +[-bcdlpsw](?:[-r][-w][-Ssx]){3}\s+\d+\s+\S+\s+\S+\s+\d+\s+ +# mount +\bmount\s+-t\s+(\w+)\s+\g{-1}\b +# C types and repeated CSS values +\s(auto|center|div|Guid|inherit|long|LONG|none|normal|solid|that|thin|transparent|very)(?: \g{-1})+\s +# C struct +\bstruct\s+(\w+)\s+\g{-1}\b +# go templates +\s(\w+)\s+\g{-1}\s+\`(?:graphql|inject|json|yaml): +# doxygen / javadoc / .net +(?:[\\@](?:brief|groupname|t?param|return|retval)|(?:public|private|\[Parameter(?:\(.+\)|)\])(?:\s+static|\s+override|\s+readonly)*)(?:\s+\{\w+\}|)\s+(\w+)\s+\g{-1}\s # Commit message -- Signed-off-by and friends ^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$ diff --git a/.github/actions/spelling/reject.txt b/.github/actions/spelling/reject.txt index 301719de47e..2ac1670d534 100644 --- a/.github/actions/spelling/reject.txt +++ b/.github/actions/spelling/reject.txt @@ -1,6 +1,7 @@ ^attache$ ^attacher$ ^attachers$ +^bellow$ benefitting occurences? ^dependan.* diff --git a/.github/workflows/similarIssues.yml b/.github/workflows/similarIssues.yml index b0fcc3d306b..f3d17ac11b5 100644 --- a/.github/workflows/similarIssues.yml +++ b/.github/workflows/similarIssues.yml @@ -5,19 +5,19 @@ on: types: [opened] jobs: - getsimilarissues: + getSimilarIssues: runs-on: ubuntu-latest outputs: - message: ${{ steps.getbody.outputs.message }} + message: ${{ steps.getBody.outputs.message }} steps: - - id: getbody + - id: getBody uses: craigloewen-msft/GitGudSimilarIssues@main with: issuetitle: ${{ github.event.issue.title }} repo: ${{ github.repository }} similaritytolerance: "0.8" add-comment: - needs: getsimilarissues + needs: getSimilarIssues runs-on: ubuntu-latest permissions: issues: write @@ -28,4 +28,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NUMBER: ${{ github.event.issue.number }} REPO: ${{ github.repository }} - BODY: ${{ needs.getsimilarissues.outputs.message }} + BODY: ${{ needs.getSimilarIssues.outputs.message }} diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index 446b24343ed..2778a7e9e5d 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -5,7 +5,7 @@ name: Spell checking # https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions # # `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment -# (in odd cases, it might actually run just to collapse a commment, but that's fairly rare) +# (in odd cases, it might actually run just to collapse a comment, but that's fairly rare) # it needs `contents: write` in order to add a comment. # # `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment @@ -34,6 +34,29 @@ name: Spell checking # # For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key +# Sarif reporting +# +# Access to Sarif reports is generally restricted (by GitHub) to members of the repository. +# +# Requires enabling `security-events: write` +# and configuring the action with `use_sarif: 1` +# +# For information on the feature, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Sarif-output + +# Minimal workflow structure: +# +# on: +# push: +# ... +# pull_request_target: +# ... +# jobs: +# # you only want the spelling job, all others should be omitted +# spelling: +# # remove `security-events: write` and `use_sarif: 1` +# # remove `experimental_apply_changes_via_bot: 1` +# ... otherwise adjust the `with:` as you wish + on: push: branches: @@ -43,8 +66,6 @@ on: pull_request_target: branches: - "**" - tags-ignore: - - "**" types: - 'opened' - 'reopened' @@ -60,10 +81,11 @@ jobs: contents: read pull-requests: read actions: read + security-events: write outputs: followup: ${{ steps.spelling.outputs.followup }} runs-on: ubuntu-latest - if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'" + if: ${{ contains(github.event_name, 'pull_request') || github.event_name == 'push' }} concurrency: group: spelling-${{ github.event.pull_request.number || github.ref }} # note: If you use only_check_changed_files, you do not want cancel-in-progress @@ -71,35 +93,50 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@v0.0.21 + uses: check-spelling/check-spelling@v0.0.22 with: - suppress_push_for_open_pull_request: 1 + suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} checkout: true check_file_names: 1 - spell_check_this: check-spelling/spell-check-this@prerelease + spell_check_this: microsoft/terminal@main post_comment: 0 use_magic_file: 1 - extra_dictionary_limit: 10 + report-timing: 1 + warnings: bad-regex,binary-file,deprecated-feature,ignored-expect-variant,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check + experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} + use_sarif: ${{ (!github.event.pull_request || (github.event.pull_request.head.repo.full_name == github.repository)) && 1 }} + extra_dictionary_limit: 20 extra_dictionaries: - cspell:software-terms/src/software-terms.txt - cspell:python/src/python/python-lib.txt - cspell:node/node.txt - cspell:cpp/src/stdlib-c.txt + cspell:software-terms/dict/softwareTerms.txt cspell:cpp/src/stdlib-cpp.txt - cspell:fullstack/fullstack.txt + cspell:lorem-ipsum/dictionary.txt + cspell:cpp/src/stdlib-c.txt + cspell:php/dict/php.txt cspell:filetypes/filetypes.txt - cspell:html/html.txt - cspell:cpp/src/compiler-msvc.txt + cspell:java/src/java.txt cspell:python/src/common/extra.txt - cspell:powershell/powershell.txt + cspell:node/dict/node.txt + cspell:java/src/java-terms.txt cspell:aws/aws.txt + cspell:typescript/dict/typescript.txt + cspell:dotnet/dict/dotnet.txt + cspell:golang/dict/go.txt + cspell:fullstack/dict/fullstack.txt + cspell:cpp/src/compiler-msvc.txt + cspell:python/src/python/python-lib.txt + cspell:mnemonics/src/mnemonics.txt + cspell:cpp/src/stdlib-cmath.txt + cspell:css/dict/css.txt cspell:cpp/src/lang-keywords.txt - cspell:npm/npm.txt - cspell:dotnet/dotnet.txt + cspell:django/dict/django.txt cspell:python/src/python/python.txt - cspell:css/css.txt - cspell:cpp/src/stdlib-cmath.txt - check_extra_dictionaries: '' + cspell:html/dict/html.txt + cspell:cpp/src/ecosystem.txt + cspell:cpp/src/compiler-clang-attributes.txt + cspell:npm/dict/npm.txt + cspell:r/src/r.txt + cspell:powershell/dict/powershell.txt + cspell:csharp/csharp.txt comment-push: name: Report (Push) @@ -111,10 +148,10 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@v0.0.21 + uses: check-spelling/check-spelling@v0.0.22 with: checkout: true - spell_check_this: check-spelling/spell-check-this@prerelease + spell_check_this: microsoft/terminal@main task: ${{ needs.spelling.outputs.followup }} comment-pr: @@ -123,12 +160,38 @@ jobs: runs-on: ubuntu-latest needs: spelling permissions: + contents: read pull-requests: write if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@v0.0.21 + uses: check-spelling/check-spelling@v0.0.22 with: checkout: true - spell_check_this: check-spelling/spell-check-this@prerelease + spell_check_this: microsoft/terminal@main task: ${{ needs.spelling.outputs.followup }} + experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} + + update: + name: Update PR + permissions: + contents: write + pull-requests: write + actions: read + runs-on: ubuntu-latest + if: ${{ + github.repository_owner != 'microsoft' && + github.event_name == 'issue_comment' && + github.event.issue.pull_request && + contains(github.event.comment.body, '@check-spelling-bot apply') + }} + concurrency: + group: spelling-update-${{ github.event.issue.number }} + cancel-in-progress: false + steps: + - name: apply spelling updates + uses: check-spelling/check-spelling@v0.0.22 + with: + experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} + checkout: true + ssh_key: "${{ secrets.CHECK_SPELLING }}" diff --git a/build/pipelines/ci.yml b/build/pipelines/ci.yml index 4ad72566a15..ab3490bc58b 100644 --- a/build/pipelines/ci.yml +++ b/build/pipelines/ci.yml @@ -102,7 +102,7 @@ stages: - ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: - stage: CodeIndexer - displayName: Github CodeNav Indexer + displayName: GitHub CodeNav Indexer dependsOn: [] jobs: - template: ./templates-v2/job-index-github-codenav.yml diff --git a/build/pipelines/templates-v2/job-index-github-codenav.yml b/build/pipelines/templates-v2/job-index-github-codenav.yml index e2edf55e651..b59b0a436ef 100644 --- a/build/pipelines/templates-v2/job-index-github-codenav.yml +++ b/build/pipelines/templates-v2/job-index-github-codenav.yml @@ -1,6 +1,6 @@ jobs: - job: CodeNavIndexer - displayName: Run Github CodeNav Indexer + displayName: Run GitHub CodeNav Indexer pool: { vmImage: windows-2022 } steps: diff --git a/doc/Niksa.md b/doc/Niksa.md index 5efcdda0abd..ec6af1f1463 100644 --- a/doc/Niksa.md +++ b/doc/Niksa.md @@ -51,7 +51,7 @@ Will this UI enhancement come to other apps on Windows? Almost certainly not. Th Will we try to keep it from regressing? Yes! Right now it's sort of a manual process. We identify that something is getting slow and then we go haul out [WPR](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder) and start taking traces. We stare down the hot paths and try to reason out what is going on and then improve them. For instance, in the last cycle or two, we focused on heap allocations as a major area where we could improve our end-to-end performance, changing a ton of our code to use stack-constructed iterator-like facades over the underlying request buffer instead of translating and allocating it into a new heap space for each level of processing. -As an aside, @bitcrazed wants us to automate performance tests in some conhost specific way, but I haven't quite figured out a controlled environment to do this in yet. The Windows Engineering System runs performance tests each night that give us a coarse grained way of knowing if we messed something up for the whole operating system, and they technically offer a fine grained way for us to insert our own performance tests... but I just haven't got around to that yet. If you have an idea for a way for us to do this in an automated fashion, I'm all ears. +As an aside, @bitcrazed wants us to automate performance tests in some conhost specific way, but I haven't quite figured out a controlled environment to do this in yet. The Windows Engineering System runs performance tests each night that give us a coarse-grained way of knowing if we messed something up for the whole operating system, and they technically offer a fine-grained way for us to insert our own performance tests... but I just haven't got around to that yet. If you have an idea for a way for us to do this in an automated fashion, I'm all ears. If there's anything else you'd like to know, let me know. I could go on all day. I deleted like 15 tangents from this reply before posting it.... diff --git a/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md b/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md index 76cc91f5083..01689b8f430 100644 --- a/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md +++ b/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md @@ -290,7 +290,7 @@ though. **I recommend we ignore this for now, and leave this as a follow-up**. For reference, refer to the following from iTerm2: ![image](https://user-images.githubusercontent.com/2578976/64075757-fa971980-ccee-11e9-9e44-47aaf3bca76c.png) -We don't have a menu bar like on MacOS, but we do have a tab context menu. We +We don't have a menu bar like on macOS, but we do have a tab context menu. We could add these items as a nested entry under each tab. If we wanted to do this, we should also make sure to dynamically change the icon of the MenuItem to reflect the current broadcast state. diff --git a/doc/specs/drafts/#3327 - Application Theming/#3327 - Application Theming.md b/doc/specs/drafts/#3327 - Application Theming/#3327 - Application Theming.md index e0f6d865541..e956b334913 100644 --- a/doc/specs/drafts/#3327 - Application Theming/#3327 - Application Theming.md +++ b/doc/specs/drafts/#3327 - Application Theming/#3327 - Application Theming.md @@ -373,7 +373,7 @@ changes, or the active pane in a tab changes: `TabRowControl` to match. The `tab.cornerRadius` might be a bit trickier to implement. Currently, there's -not a XAML resource that controls this, nor is this something that's exposed by +no XAML resource that controls this, nor is this something that's exposed by the TabView control. Fortunately, this is something that's exposed to us programmatically. We'll need to manually set that value on each `TabViewItem` as we create new tabs. When we reload settings, we'll need to make sure to come diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index 1fac8f0e834..92649553245 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -217,7 +217,7 @@ void AtlasEngine::_recreateBackend() if (hr == DXGI_ERROR_SDK_COMPONENT_MISSING) { // This might happen if you don't have "Graphics debugger and GPU - // profiler for DirectX" installed in VS. We shouln't just explode if + // profiler for DirectX" installed in VS. We shouldn't just explode if // you don't though - instead, disable debugging and try again. WI_ClearFlag(deviceFlags, D3D11_CREATE_DEVICE_DEBUG); diff --git a/src/renderer/atlas/BackendD2D.cpp b/src/renderer/atlas/BackendD2D.cpp index ea4ea0923eb..05f79c78c20 100644 --- a/src/renderer/atlas/BackendD2D.cpp +++ b/src/renderer/atlas/BackendD2D.cpp @@ -332,7 +332,7 @@ void BackendD2D::_drawTextResetLineRendition(const ShapedRow* row) const noexcep } } -// Returns the theoretical/design design size of the given `DWRITE_GLYPH_RUN`, relative the the given baseline origin. +// Returns the theoretical/design design size of the given `DWRITE_GLYPH_RUN`, relative the given baseline origin. // This algorithm replicates what DirectWrite does internally to provide `IDWriteTextLayout::GetMetrics`. f32r BackendD2D::_getGlyphRunDesignBounds(const DWRITE_GLYPH_RUN& glyphRun, f32 baselineX, f32 baselineY) { diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 41fefa6ea2b..51494df7b23 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -1932,7 +1932,7 @@ void BackendD3D::_drawCursorForeground() } } // We can also skip any instances (= any rows) at the beginning that are clearly not overlapping with - // the cursor. This reduces the the CPU cost of this function by roughly half (a few microseconds). + // the cursor. This reduces the CPU cost of this function by roughly half (a few microseconds). for (; instancesOffset < instancesCount; ++instancesOffset) { const auto& it = _instances[instancesOffset]; From 306f31acf482ce84701aabee52981575277f16bb Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 6 Dec 2023 05:20:15 -0600 Subject: [PATCH 038/603] ci: remove the check-spelling-0.0.21 shim (#16424) As noted by @jsoref in #16127, we could eventually remove this and also check-spelling would make suggestions on what patterns to use. --- .../spelling/allow/check-spelling-0.0.21.txt | 224 ------------------ 1 file changed, 224 deletions(-) delete mode 100644 .github/actions/spelling/allow/check-spelling-0.0.21.txt diff --git a/.github/actions/spelling/allow/check-spelling-0.0.21.txt b/.github/actions/spelling/allow/check-spelling-0.0.21.txt deleted file mode 100644 index 9463838f115..00000000000 --- a/.github/actions/spelling/allow/check-spelling-0.0.21.txt +++ /dev/null @@ -1,224 +0,0 @@ -aarch -abi -activatable -aef -amd -applets -argb -ASDF -ative -ativecxapp -AVX -azzle -BGR -bigints -bignum -bitmask -bitmasks -blackhole -Bopomofo -BOTTOMLEFT -BOTTOMRIGHT -bsr -bstr -cbuffer -cci -changelist -changelists -Checkin -chk -cielab -cls -clz -CMake -cmt -Cmts -cmyk -cname -cnt -CNTRL -codepage -codepages -codepoints -colorspaces -COMDAT -configurability -cpuid -cred -Crt -dbg -distro -distros -django -DLOAD -ect -ectread -edist -egistry -elease -elemetry -elems -emacs -emark -emplates -enderer -endregion -endverbatim -eplace -erm -erminal -erminalcore -erminalinput -esource -esources -esthelper -estlist -estmd -estpasses -ests -esult -esultmacros -etfx -eturn -ewdelete -ewtonsoft -flyout -flyouts -FONTFAMILY -FONTSIZE -FONTWEIGHT -formattable -framebuffer -FSCTL -GAUSSIAN -getch -GETFONTSIZE -GETPOS -GNUC -groupbox -Gsub -GTR -HORZ -hread -hrottled -hrow -hsl -HTMLTo -icket -Iconified -idx -ihilist -Imm -inheritdoc -inlines -ioctl -keydown -keydowns -keyup -keyups -lnk -lss -MMIX -MOUSEMOVE -movl -movsb -msys -nameof -natvis -Newdelete -Newtonsoft -Nop -NOUPDATE -nullability -numpad -oklab -ools -OOM -osign -ote -otepad -ouicompat -passthrough -pcs -pgdn -pgup -pkey -pname -popcnt -prefs -prepopulated -ptrs -Pvf -qos -Rasterizer -RCDATA -rcl -rects -reparent -reparented -RGBCOLOR -roundtrips -RTTI -safearray -sapi -scancode -scancodes -scrollbars -Sdl -SETCOLOR -SETFONT -Shl -SND -srgb -Statusline -stl -stosb -strerror -strrev -subc -subfolders -subkey -subkeys -swappable -taskbar -tdll -testenv -testenvs -texel -TExpected -tioapi -titlebar -titlebars -TOPLEFT -TOPRIGHT -tprivapi -tsf -typelib -typeparam -UDM -uget -ules -umul -umulh -uninitialize -Unserialize -untimes -upkg -utc -vcpkg -vec -vectorized -vtable -vtables -WCAG -WINDOWTITLE -workaround -xchgl -xgetbv -XOFF -XON -YDPI -YOffset -ype -ypes -ZIndex From efbfb12145da959f74c368cdfc92f7892ce724d5 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Dec 2023 20:37:58 +0100 Subject: [PATCH 039/603] Fix ConPTY inputs incorrectly being treated as plain text (#16352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is my proposal to avoid aborting ConPTY input parsing because a read accidentally got split up into more than one chunk. This happens a lot with WSL for me, as I often get (for instance) a `\x1b[67;46;99;0;32;` input followed immediately by a `1_` input. The current logic would cause both of these to be flushed out to the client application. This PR fixes the issue by only flushing either a standalone escape character or a escape+character combination. It basically limits the previous code to just `VTStates::Ground` and `VTStates::Escape`. I'm not using the `_state` member, because `VTStates::OscParam` makes no distinction between `\x1b]` and `\x1b]1234` and I only want to flush the former. I felt like checking the contents of `run` directly is easier to understand. Related to #16343 ## Validation Steps Performed * win32-input-mode sequences are now properly buffered ✅ * Standalone alt-key combinations are still being flushed ✅ (cherry picked from commit 5f5ef10571a17af4feeafd6be8729085fe5a4ac0) Service-Card-Id: 91270261 Service-Version: 1.19 --- src/terminal/parser/stateMachine.cpp | 70 ++++++------------- .../parser/ut_parser/InputEngineTest.cpp | 14 ++++ 2 files changed, 34 insertions(+), 50 deletions(-) diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index eb22ff95382..643409afda5 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -2127,6 +2127,8 @@ void StateMachine::ProcessString(const std::wstring_view string) // If we're at the end of the string and have remaining un-printed characters, if (_state != VTStates::Ground) { + const auto run = _CurrentRun(); + // One of the "weird things" in VT input is the case of something like // alt+[. In VT, that's encoded as `\x1b[`. However, that's // also the start of a CSI, and could be the start of a longer sequence, @@ -2136,60 +2138,28 @@ void StateMachine::ProcessString(const std::wstring_view string) // alt+[, A would be processed like `\x1b[A`, // which is _wrong_). // - // Fortunately, for VT input, each keystroke comes in as an individual - // write operation. So, if at the end of processing a string for the - // InputEngine, we find that we're not in the Ground state, that implies - // that we've processed some input, but not dispatched it yet. This - // block at the end of `ProcessString` will then re-process the - // undispatched string, but it will ensure that it dispatches on the - // last character of the string. For our previous `\x1b[` scenario, that - // means we'll make sure to call `_ActionEscDispatch('[')`., which will - // properly decode the string as alt+[. - const auto run = _CurrentRun(); - + // At the same time, input may be broken up arbitrarily, depending on the pipe's + // buffer size, our read-buffer size, the sender's write-buffer size, and more. + // In fact, with the current WSL, input is broken up in 16 byte chunks (Why? :(), + // which breaks up many of our longer sequences, like our Win32InputMode ones. + // + // As a heuristic, this code specifically checks for a trailing Esc or Alt+key. if (_isEngineForInput) { - // Reset our state, and put all but the last char in again. - ResetState(); - _processingLastCharacter = false; - // Chars to flush are [pwchSequenceStart, pwchCurr) - auto wchIter = run.cbegin(); - while (wchIter < run.cend() - 1) - { - ProcessCharacter(*wchIter); - wchIter++; - } - // Manually execute the last char [pwchCurr] - _processingLastCharacter = true; - switch (_state) + if (run.size() <= 2 && run.front() == L'\x1b') { - case VTStates::Ground: - _ActionExecute(*wchIter); - break; - case VTStates::Escape: - case VTStates::EscapeIntermediate: - _ActionEscDispatch(*wchIter); - break; - case VTStates::CsiEntry: - case VTStates::CsiIntermediate: - case VTStates::CsiIgnore: - case VTStates::CsiParam: - case VTStates::CsiSubParam: - _ActionCsiDispatch(*wchIter); - break; - case VTStates::OscParam: - case VTStates::OscString: - case VTStates::OscTermination: - _ActionOscDispatch(*wchIter); - break; - case VTStates::Ss3Entry: - case VTStates::Ss3Param: - _ActionSs3Dispatch(*wchIter); - break; + _EnterGround(); + if (run.size() == 1) + { + _ActionExecute(L'\x1b'); + } + else + { + _EnterEscape(); + _ActionEscDispatch(run.back()); + } + _EnterGround(); } - // microsoft/terminal#2746: Make sure to return to the ground state - // after dispatching the characters - _EnterGround(); } else if (_state != VTStates::SosPmApcString && _state != VTStates::DcsPassThrough && _state != VTStates::DcsIgnore) { diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index 2e21c77c74f..fcb23734be5 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -260,6 +260,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest TEST_METHOD(AltCtrlDTest); TEST_METHOD(AltIntermediateTest); TEST_METHOD(AltBackspaceEnterTest); + TEST_METHOD(ChunkedSequence); TEST_METHOD(SGRMouseTest_ButtonClick); TEST_METHOD(SGRMouseTest_Modifiers); TEST_METHOD(SGRMouseTest_Movement); @@ -1041,6 +1042,19 @@ void InputEngineTest::AltBackspaceEnterTest() VerifyExpectedInputDrained(); } +void InputEngineTest::ChunkedSequence() +{ + // This test ensures that a DSC sequence that's split up into multiple chunks isn't + // confused with a single Alt+key combination like in the AltBackspaceEnterTest(). + // Basically, it tests the selectivity of the AltBackspaceEnterTest() fix. + + auto dispatch = std::make_unique(nullptr, nullptr); + auto inputEngine = std::make_unique(std::move(dispatch)); + StateMachine stateMachine{ std::move(inputEngine) }; + stateMachine.ProcessString(L"\x1b[1"); + VERIFY_ARE_EQUAL(StateMachine::VTStates::CsiParam, stateMachine._state); +} + // Method Description: // - Writes an SGR VT sequence based on the necessary parameters // Arguments: From f63e25b87c9f08a7c51887b9b1e46bf6b9010348 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 21 Nov 2023 21:50:59 +0100 Subject: [PATCH 040/603] Fix chunked soft fonts not working (#16349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset fixes an issue caused by #15991 where "chunked" escape sequences would get corrupted. The fix is to simply not flush eagerly anymore. I tried my best to keep the input lag reduction from #15991, but unfortunately this isn't possible for console APIs. Closes #16079 ## Validation Steps Performed * `type ascii.com` produces soft font ASCII characters ✅ (cherry picked from commit bdf2f6f2740168363bdba898697e28baa8d7beb7) Service-Card-Id: 91283205 Service-Version: 1.19 --- src/host/VtIo.cpp | 4 ---- src/renderer/vt/state.cpp | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index c286a5e8aca..b0c33671727 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -467,10 +467,6 @@ void VtIo::SendCloseEvent() void VtIo::CorkRenderer(bool corked) const noexcept { _pVtRenderEngine->Cork(corked); - if (!corked) - { - LOG_IF_FAILED(ServiceLocator::LocateGlobals().pRender->PaintFrame()); - } } #ifdef UNIT_TESTING diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index df35691a361..5bb6b7d694d 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -167,6 +167,7 @@ void VtEngine::_flushImpl() noexcept void VtEngine::Cork(bool corked) noexcept { _corked = corked; + _Flush(); } // Method Description: From 20dad624710249f3a4ee77c47a58d4ca367a820b Mon Sep 17 00:00:00 2001 From: Ryan Luu Date: Wed, 6 Dec 2023 15:31:30 -0800 Subject: [PATCH 041/603] Fix markdown alerts syntax in README (#16434) Changes any references of `> **Note**\` with `> [!NOTE]` to match the new syntax for markdown files in GitHub. Fixes the 14 November 2023 update to the alerts syntax in markdown files: > ## Update - 14 November 2023 > * The initial syntax using e.g. **Note** isn't supported any longer. > > https://github.com/orgs/community/discussions/16925 --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f06a7053a18..462731aa594 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Related repositories include: ## Installing and running Windows Terminal -> **Note**\ +> [!NOTE] > Windows Terminal requires Windows 10 2004 (build 19041) or later ### Microsoft Store [Recommended] @@ -53,7 +53,7 @@ fails for any reason, you can try the following command at a PowerShell prompt: Add-AppxPackage Microsoft.WindowsTerminal_.msixbundle ``` -> **Note**\ +> [!NOTE] > If you install Terminal manually: > > * You may need to install the [VC++ v14 Desktop Framework Package](https://docs.microsoft.com/troubleshoot/cpp/c-runtime-packages-desktop-bridge#how-to-install-and-update-desktop-framework-packages). @@ -72,7 +72,7 @@ package: winget install --id Microsoft.WindowsTerminal -e ``` -> **Note**\ +> [!NOTE] > Dependency support is available in WinGet version [1.6.2631 or later](https://github.com/microsoft/winget-cli/releases). To install the Terminal stable release 1.18 or later, please make sure you have the updated version of the WinGet client. #### Via Chocolatey (unofficial) @@ -262,7 +262,7 @@ Cause: You're launching the incorrect solution in Visual Studio. Solution: Make sure you're building & deploying the `CascadiaPackage` project in Visual Studio. -> **Note**\ +> [!NOTE] > `OpenConsole.exe` is just a locally-built `conhost.exe`, the classic > Windows Console that hosts Windows' command-line infrastructure. OpenConsole > is used by Windows Terminal to connect to and communicate with command-line From 17867af5349a66fa8121d1c930dd9587e45f5117 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 8 Dec 2023 15:01:55 -0600 Subject: [PATCH 042/603] conpty: request DSR-CPR before Win32 input mode (#16445) This prevents an issue in conhost where older versions of Windows Terminal (including the ones currently inbox in Windows, as well as stable and preview) will *still* cause WSL interop to hang on startup. Since VT input is erroneously re-encoded as Win32 input events on those versions, we need to make sure we request the cursor position *before* enabling Win32 input mode. That way, the CPR we get back is properly encoded. --- src/host/VtIo.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index b0c33671727..f9a6b722767 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -263,12 +263,6 @@ bool VtIo::IsUsingVt() const CATCH_RETURN(); } - // GH#4999 - Send a sequence to the connected terminal to request - // win32-input-mode from them. This will enable the connected terminal to - // send us full INPUT_RECORDs as input. If the terminal doesn't understand - // this sequence, it'll just ignore it. - LOG_IF_FAILED(_pVtRenderEngine->RequestWin32Input()); - // MSFT: 15813316 // If the terminal application wants us to inherit the cursor position, // we're going to emit a VT sequence to ask for the cursor position, then @@ -287,6 +281,12 @@ bool VtIo::IsUsingVt() const } } + // GH#4999 - Send a sequence to the connected terminal to request + // win32-input-mode from them. This will enable the connected terminal to + // send us full INPUT_RECORDs as input. If the terminal doesn't understand + // this sequence, it'll just ignore it. + LOG_IF_FAILED(_pVtRenderEngine->RequestWin32Input()); + if (_pVtInputThread) { LOG_IF_FAILED(_pVtInputThread->Start()); From bcc01d96bf856a043dfc792dad770c4e433fd93f Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 8 Dec 2023 15:01:55 -0600 Subject: [PATCH 043/603] conpty: request DSR-CPR before Win32 input mode (#16445) This prevents an issue in conhost where older versions of Windows Terminal (including the ones currently inbox in Windows, as well as stable and preview) will *still* cause WSL interop to hang on startup. Since VT input is erroneously re-encoded as Win32 input events on those versions, we need to make sure we request the cursor position *before* enabling Win32 input mode. That way, the CPR we get back is properly encoded. (cherry picked from commit 17867af5349a66fa8121d1c930dd9587e45f5117) Service-Card-Id: 91301135 Service-Version: 1.19 --- src/host/VtIo.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index b0c33671727..f9a6b722767 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -263,12 +263,6 @@ bool VtIo::IsUsingVt() const CATCH_RETURN(); } - // GH#4999 - Send a sequence to the connected terminal to request - // win32-input-mode from them. This will enable the connected terminal to - // send us full INPUT_RECORDs as input. If the terminal doesn't understand - // this sequence, it'll just ignore it. - LOG_IF_FAILED(_pVtRenderEngine->RequestWin32Input()); - // MSFT: 15813316 // If the terminal application wants us to inherit the cursor position, // we're going to emit a VT sequence to ask for the cursor position, then @@ -287,6 +281,12 @@ bool VtIo::IsUsingVt() const } } + // GH#4999 - Send a sequence to the connected terminal to request + // win32-input-mode from them. This will enable the connected terminal to + // send us full INPUT_RECORDs as input. If the terminal doesn't understand + // this sequence, it'll just ignore it. + LOG_IF_FAILED(_pVtRenderEngine->RequestWin32Input()); + if (_pVtInputThread) { LOG_IF_FAILED(_pVtInputThread->Start()); From f5b45c25c9dfe27e03fbea1c7d82a6dc2a009343 Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Fri, 15 Dec 2023 01:17:14 +0530 Subject: [PATCH 044/603] Fix curlyline rendering in `AtlasEngine` and `GDIRenderer` (#16444) Fixes Curlyline being drawn as single underline in some cases **Detailed Description** - Curlyline is drawn at all font sizes. - We might render a curlyline that is clipped in cases where we don't have enough space to draw a full curlyline. This is to give users a consistent view of Curlylines. Previously in those cases, it was drawn as a single underline. - Removed minimum threshold `minCurlyLinePeakHeight` for Curlyline drawing. - GDIRender changes: - Underline offset now points to the (vertical) mid position of the underline. Removes redundant `underlineMidY` calculation inside the draw call. Closes #16288 --- src/renderer/atlas/BackendD3D.cpp | 56 +++++++++++++++---------------- src/renderer/atlas/BackendD3D.h | 2 +- src/renderer/gdi/paint.cpp | 29 ++++++++-------- src/renderer/gdi/state.cpp | 51 ++++++++++++---------------- 4 files changed, 65 insertions(+), 73 deletions(-) diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 51494df7b23..e68fb2a7a57 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -306,37 +306,35 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p) { const auto& font = *p.s->font; - // The max height of Curly line peak in `em` units. - const auto maxCurlyLinePeakHeightEm = 0.075f; - // We aim for atleast 1px height, but since we draw 1px smaller curly line, - // we aim for 2px height as a result. - const auto minCurlyLinePeakHeight = 2.0f; - - // Curlyline uses the gap between cell bottom and singly underline position - // as the height of the wave's peak. The baseline for curly-line is at the - // middle of singly underline. The gap could be too big, so we also apply - // a limit on the peak height. - const auto strokeHalfWidth = font.underline.height / 2.0f; - const auto underlineMidY = font.underline.position + strokeHalfWidth; - const auto cellBottomGap = font.cellSize.y - underlineMidY - strokeHalfWidth; - const auto maxCurlyLinePeakHeight = maxCurlyLinePeakHeightEm * font.fontSize; - auto curlyLinePeakHeight = std::min(cellBottomGap, maxCurlyLinePeakHeight); + // Curlyline is drawn with a desired height relative to the font size. The + // baseline of curlyline is at the middle of singly underline. When there's + // limited space to draw a curlyline, we apply a limit on the peak height. + { + // initialize curlyline peak height to a desired value. Clamp it to at + // least 1. + constexpr auto curlyLinePeakHeightEm = 0.075f; + _curlyLinePeakHeight = std::max(1.0f, std::roundf(curlyLinePeakHeightEm * font.fontSize)); + + // calc the limit we need to apply + const auto strokeHalfWidth = std::floor(font.underline.height / 2.0f); + const auto underlineMidY = font.underline.position + strokeHalfWidth; + const auto maxDrawableCurlyLinePeakHeight = font.cellSize.y - underlineMidY - font.underline.height; + + // if the limit is <= 0 (no height at all), stick with the desired height. + // This is how we force a curlyline even when there's no space, though it + // might be clipped at the bottom. + if (maxDrawableCurlyLinePeakHeight > 0.0f) + { + _curlyLinePeakHeight = std::min(_curlyLinePeakHeight, maxDrawableCurlyLinePeakHeight); + } - // When it's too small to be curly, make it straight. - if (curlyLinePeakHeight < minCurlyLinePeakHeight) - { - curlyLinePeakHeight = 0; + const auto curlyUnderlinePos = underlineMidY - _curlyLinePeakHeight - font.underline.height; + const auto curlyUnderlineWidth = 2.0f * (_curlyLinePeakHeight + font.underline.height); + const auto curlyUnderlinePosU16 = gsl::narrow_cast(lrintf(curlyUnderlinePos)); + const auto curlyUnderlineWidthU16 = gsl::narrow_cast(lrintf(curlyUnderlineWidth)); + _curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 }; } - // We draw a smaller curly line (-1px) to avoid clipping due to the rounding. - _curlyLineDrawPeakHeight = std::max(0.0f, curlyLinePeakHeight - 1.0f); - - const auto curlyUnderlinePos = font.underline.position - curlyLinePeakHeight; - const auto curlyUnderlineWidth = 2.0f * (curlyLinePeakHeight + strokeHalfWidth); - const auto curlyUnderlinePosU16 = gsl::narrow_cast(lrintf(curlyUnderlinePos)); - const auto curlyUnderlineWidthU16 = gsl::narrow_cast(lrintf(curlyUnderlineWidth)); - _curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 }; - DWrite_GetRenderParams(p.dwriteFactory.get(), &_gamma, &_cleartypeEnhancedContrast, &_grayscaleEnhancedContrast, _textRenderingParams.put()); // Clearing the atlas requires BeginDraw(), which is expensive. Defer this until we need Direct2D anyways. _fontChangedResetGlyphAtlas = true; @@ -576,7 +574,7 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast; data.underlineWidth = p.s->font->underline.height; data.curlyLineWaveFreq = 2.0f * 3.14f / p.s->font->cellSize.x; - data.curlyLinePeakHeight = _curlyLineDrawPeakHeight; + data.curlyLinePeakHeight = _curlyLinePeakHeight; data.curlyLineCellOffset = p.s->font->underline.position + p.s->font->underline.height / 2.0f; p.deviceContext->UpdateSubresource(_psConstantBuffer.get(), 0, nullptr, &data, 0, 0); } diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 0befe8fe342..4c248ed3ff3 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -291,7 +291,7 @@ namespace Microsoft::Console::Render::Atlas // The bounding rect of _cursorRects in pixels. til::rect _cursorPosition; - f32 _curlyLineDrawPeakHeight = 0; + f32 _curlyLinePeakHeight = 0.0f; FontDecorationPosition _curlyUnderline; bool _requiresContinuousRedraw = false; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 9722703d105..222576390e2 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -536,27 +536,30 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) const auto DrawLine = [=](const auto x, const auto y, const auto w, const auto h) { return PatBlt(_hdcMemoryContext, x, y, w, h, PATCOPY); }; - const auto DrawStrokedLine = [&](const auto x, const auto y, const auto w) { + const auto DrawStrokedLine = [&](const til::CoordType x, const til::CoordType y, const unsigned w) { RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); - RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, x + w, y)); + RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, gsl::narrow_cast(x + w), y)); return S_OK; }; - const auto DrawCurlyLine = [&](const auto x, const auto y, const auto cCurlyLines) { + const auto DrawCurlyLine = [&](const til::CoordType x, const til::CoordType y, const size_t cCurlyLines) { const auto curlyLineWidth = fontWidth; - const auto curlyLineHalfWidth = lrintf(curlyLineWidth / 2.0f); - const auto controlPointHeight = gsl::narrow_cast(std::floor(3.5f * _lineMetrics.curlylinePeakHeight)); + const auto curlyLineHalfWidth = std::lround(curlyLineWidth / 2.0f); + const auto controlPointHeight = std::lround(3.5f * _lineMetrics.curlylinePeakHeight); + // Each curlyLine requires 3 `POINT`s const auto cPoints = gsl::narrow(3 * cCurlyLines); std::vector points; points.reserve(cPoints); + auto start = x; - for (auto i = 0u; i < cCurlyLines; i++) + for (size_t i = 0; i < cCurlyLines; i++) { points.emplace_back(start + curlyLineHalfWidth, y - controlPointHeight); points.emplace_back(start + curlyLineHalfWidth, y + controlPointHeight); points.emplace_back(start + curlyLineWidth, y); start += curlyLineWidth; } + RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); RETURN_HR_IF(E_FAIL, !PolyBezierTo(_hdcMemoryContext, points.data(), cPoints)); return S_OK; @@ -619,28 +622,26 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) const auto prevPen = wil::SelectObject(_hdcMemoryContext, hpen.get()); RETURN_HR_IF_NULL(E_FAIL, prevPen.get()); - const auto underlineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset + _lineMetrics.underlineWidth / 2.0f); if (lines.test(GridLines::Underline)) { - return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); } else if (lines.test(GridLines::DoubleUnderline)) { - const auto doubleUnderlineBottomLineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset2 + _lineMetrics.underlineWidth / 2.0f); - RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells)); - return DrawStrokedLine(ptTarget.x, doubleUnderlineBottomLineMidY, widthOfAllCells); + RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells)); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset2, widthOfAllCells); } else if (lines.test(GridLines::CurlyUnderline)) { - return DrawCurlyLine(ptTarget.x, underlineMidY, cchLine); + return DrawCurlyLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, cchLine); } else if (lines.test(GridLines::DottedUnderline)) { - return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); } else if (lines.test(GridLines::DashedUnderline)) { - return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); } return S_OK; diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 099c2f43f78..13fd0ea59fc 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -11,15 +11,6 @@ using namespace Microsoft::Console::Render; -namespace -{ - // The max height of Curly line peak in `em` units. - constexpr auto MaxCurlyLinePeakHeightEm = 0.075f; - - // The min height of Curly line peak. - constexpr auto MinCurlyLinePeakHeight = 2.0f; -} - // Routine Description: // - Creates a new GDI-based rendering engine // - NOTE: Will throw if initialization failure. Caller must catch. @@ -406,29 +397,31 @@ GdiEngine::~GdiEngine() _lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset - _lineMetrics.gridlineWidth; } - // Curly line doesn't render properly below 1px stroke width. Make it a straight line. - if (_lineMetrics.underlineWidth < 1) - { - _lineMetrics.curlylinePeakHeight = 0; - } - else + // Since we use GDI pen for drawing, the underline offset should point to + // the center of the underline. + const auto underlineHalfWidth = gsl::narrow_cast(std::floor(_lineMetrics.underlineWidth / 2.0f)); + _lineMetrics.underlineOffset += underlineHalfWidth; + _lineMetrics.underlineOffset2 += underlineHalfWidth; + + // Curlyline is drawn with a desired height relative to the font size. The + // baseline of curlyline is at the middle of singly underline. When there's + // limited space to draw a curlyline, we apply a limit on the peak height. { - // Curlyline uses the gap between cell bottom and singly underline - // position as the height of the wave's peak. The baseline for curly - // line is at the middle of singly underline. The gap could be too big, - // so we also apply a limit on the peak height. - const auto strokeHalfWidth = _lineMetrics.underlineWidth / 2.0f; - const auto underlineMidY = _lineMetrics.underlineOffset + strokeHalfWidth; - const auto cellBottomGap = Font.GetSize().height - underlineMidY - strokeHalfWidth; - const auto maxCurlyLinePeakHeight = MaxCurlyLinePeakHeightEm * fontSize; - auto curlyLinePeakHeight = std::min(cellBottomGap, maxCurlyLinePeakHeight); - - // When it's too small to be curly, make it a straight line. - if (curlyLinePeakHeight < MinCurlyLinePeakHeight) + // initialize curlyline peak height to a desired value. Clamp it to at + // least 1. + constexpr auto curlyLinePeakHeightEm = 0.075f; + _lineMetrics.curlylinePeakHeight = gsl::narrow_cast(std::max(1L, std::lround(curlyLinePeakHeightEm * fontSize))); + + // calc the limit we need to apply + const auto maxDrawableCurlyLinePeakHeight = Font.GetSize().height - _lineMetrics.underlineOffset - _lineMetrics.underlineWidth; + + // if the limit is <= 0 (no height at all), stick with the desired height. + // This is how we force a curlyline even when there's no space, though it + // might be clipped at the bottom. + if (maxDrawableCurlyLinePeakHeight > 0.0f) { - curlyLinePeakHeight = 0.0f; + _lineMetrics.curlylinePeakHeight = std::min(_lineMetrics.curlylinePeakHeight, maxDrawableCurlyLinePeakHeight); } - _lineMetrics.curlylinePeakHeight = gsl::narrow_cast(std::floor(curlyLinePeakHeight)); } // Now find the size of a 0 in this current font and save it for conversions done later. From 306cb404dcd937ee2548dcbe5edbd2a621c53448 Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Wed, 27 Sep 2023 23:20:09 +0530 Subject: [PATCH 045/603] Use MSWord compatible RTF sequence for background text color (#16035) The `GenRTF(...)` was using `\highlight` control word for sending background text color in the RTF format during a copy command. This doesn't work correctly, since many applications (E.g. MSWord) don't support full RGB with `\highlight`, and instead uses an approximation of what is received. For example, `rgb(197, 15, 31)` becomes `rgb(255, 0, 255)`. Also, the standard way of using background colors is `\cbN` control word, which isn't supported as per the [RTF Spec 1.9.1] in Word. But it briefly mentioned a workaround at Pg. 23, which seems to work on all the RTF editors I tested. The PR makes the changes to use `\chshdng0\chcbpatN` for the background coloring. Also did some refactoring to make the implementation concise. ## Validation Steps Performed Verified that the background is correctly copied on below editors: - MSWord - WordPad - LibreOffice - Outlook [RTF Spec 1.9.1]: https://msopenspecs.azureedge.net/files/Archive_References/[MSFT-RTF].pdf (cherry picked from commit 310814bb30473c4d5310dff60ff040cc3719e075) Service-Card-Id: 91349195 Service-Version: 1.19 --- .github/actions/spelling/expect/expect.txt | 2 + src/buffer/out/textBuffer.cpp | 85 +++++++++------------- 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index acd73a56a9d..3ea1b7e958f 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -188,8 +188,10 @@ changelist chaof charinfo CHARSETINFO +chcbpat chh chk +chshdng CHT Cic cielab diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index aa61b63cc29..52a5c6e3b72 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2277,6 +2277,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, // Routine Description: // - Generates an RTF document based on the passed in text and color data // RTF 1.5 Spec: https://www.biblioscape.com/rtf15_spec.htm +// RTF 1.9.1 Spec: https://msopenspecs.azureedge.net/files/Archive_References/[MSFT-RTF].pdf // Arguments: // - rows - the text and color data we will format & encapsulate // - backgroundColor - default background color for characters, also used in padding @@ -2296,10 +2297,18 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi // Standard RTF header. // This is similar to the header generated by WordPad. - // \ansi - specifies that the ANSI char set is used in the current doc - // \ansicpg1252 - represents the ANSI code page which is used to perform the Unicode to ANSI conversion when writing RTF text - // \deff0 - specifies that the default font for the document is the one at index 0 in the font table - // \nouicompat - ? + // \ansi: + // Specifies that the ANSI char set is used in the current doc. + // \ansicpg1252: + // Represents the ANSI code page which is used to perform + // the Unicode to ANSI conversion when writing RTF text. + // \deff0: + // Specifies that the default font for the document is the one + // at index 0 in the font table. + // \nouicompat: + // Some features are blocked by default to maintain compatibility + // with older programs (Eg. Word 97-2003). `nouicompat` disables this + // behavior, and unblocks these features. See: Spec 1.9.1, Pg. 51. rtfBuilder << "\\rtf1\\ansi\\ansicpg1252\\deff0\\nouicompat"; // font table @@ -2308,17 +2317,25 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi // map to keep track of colors: // keys are colors represented by COLORREF // values are indices of the corresponding colors in the color table - std::unordered_map colorMap; - auto nextColorIndex = 1; // leave 0 for the default color and start from 1. + std::unordered_map colorMap; // RTF color table std::ostringstream colorTableBuilder; colorTableBuilder << "{\\colortbl ;"; - colorTableBuilder << "\\red" << static_cast(GetRValue(backgroundColor)) - << "\\green" << static_cast(GetGValue(backgroundColor)) - << "\\blue" << static_cast(GetBValue(backgroundColor)) - << ";"; - colorMap[backgroundColor] = nextColorIndex++; + + const auto getColorTableIndex = [&](const COLORREF color) -> size_t { + // Exclude the 0 index for the default color, and start with 1. + + const auto [it, inserted] = colorMap.emplace(color, colorMap.size() + 1); + if (inserted) + { + colorTableBuilder << "\\red" << static_cast(GetRValue(color)) + << "\\green" << static_cast(GetGValue(color)) + << "\\blue" << static_cast(GetBValue(color)) + << ";"; + } + return it->second; + }; // content std::ostringstream contentBuilder; @@ -2328,7 +2345,12 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi // \fs specifies font size in half-points i.e. \fs20 results in a font size // of 10 pts. That's why, font size is multiplied by 2 here. contentBuilder << "\\pard\\slmult1\\f0\\fs" << std::to_string(2 * fontHeightPoints) - << "\\highlight1" + // Set the background color for the page. But, the + // standard way (\cbN) to do this isn't supported in Word. + // However, the following control words sequence works + // in Word (and other RTF editors also) for applying the + // text background color. See: Spec 1.9.1, Pg. 23. + << "\\chshdng0\\chcbpat" << getColorTableIndex(backgroundColor) << " "; std::optional fgColor = std::nullopt; @@ -2378,43 +2400,8 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi if (colorChanged) { writeAccumulatedChars(false); - - auto bkColorIndex = 0; - if (colorMap.find(bkColor.value()) != colorMap.end()) - { - // color already exists in the map, just retrieve the index - bkColorIndex = colorMap[bkColor.value()]; - } - else - { - // color not present in the map, so add it - colorTableBuilder << "\\red" << static_cast(GetRValue(bkColor.value())) - << "\\green" << static_cast(GetGValue(bkColor.value())) - << "\\blue" << static_cast(GetBValue(bkColor.value())) - << ";"; - colorMap[bkColor.value()] = nextColorIndex; - bkColorIndex = nextColorIndex++; - } - - auto fgColorIndex = 0; - if (colorMap.find(fgColor.value()) != colorMap.end()) - { - // color already exists in the map, just retrieve the index - fgColorIndex = colorMap[fgColor.value()]; - } - else - { - // color not present in the map, so add it - colorTableBuilder << "\\red" << static_cast(GetRValue(fgColor.value())) - << "\\green" << static_cast(GetGValue(fgColor.value())) - << "\\blue" << static_cast(GetBValue(fgColor.value())) - << ";"; - colorMap[fgColor.value()] = nextColorIndex; - fgColorIndex = nextColorIndex++; - } - - contentBuilder << "\\highlight" << bkColorIndex - << "\\cf" << fgColorIndex + contentBuilder << "\\chshdng0\\chcbpat" << getColorTableIndex(bkColor.value()) + << "\\cf" << getColorTableIndex(fgColor.value()) << " "; } From 171a21ad48eca9f57a3ae5692fe9a5c64e9ad276 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 15 Dec 2023 20:17:42 +0100 Subject: [PATCH 046/603] Increase VtInputThread buffer size (#16470) This makes 3 improvements: * 16x larger input buffer size improves behavior when pasting clipboard contents while the win32-input-mode is enabled, as each input character is roughly 15-20x longer after encoding. * Translate UTF8 to UTF16 outside of the console lock. * Preserve the UTF16 buffer between reads for less mallocs. --- src/host/VtInputThread.cpp | 15 ++++++++------- src/host/VtInputThread.hpp | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/host/VtInputThread.cpp b/src/host/VtInputThread.cpp index 4a4b773cb02..b9b2aefb0aa 100644 --- a/src/host/VtInputThread.cpp +++ b/src/host/VtInputThread.cpp @@ -69,7 +69,7 @@ DWORD WINAPI VtInputThread::StaticVtInputThreadProc(_In_ LPVOID lpParameter) // - true if you should continue reading bool VtInputThread::DoReadInput() { - char buffer[256]; + char buffer[4096]; DWORD dwRead = 0; const auto ok = ReadFile(_hFile.get(), buffer, ARRAYSIZE(buffer), &dwRead, nullptr); @@ -89,6 +89,12 @@ bool VtInputThread::DoReadInput() return false; } + // If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it. + if (FAILED_LOG(til::u8u16({ buffer, gsl::narrow_cast(dwRead) }, _wstr, _u8State))) + { + return true; + } + try { // Make sure to call the GLOBAL Lock/Unlock, not the gci's lock/unlock. @@ -99,12 +105,7 @@ bool VtInputThread::DoReadInput() LockConsole(); const auto unlock = wil::scope_exit([&] { UnlockConsole(); }); - std::wstring wstr; - // If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it. - if (SUCCEEDED_LOG(til::u8u16({ buffer, gsl::narrow_cast(dwRead) }, wstr, _u8State))) - { - _pInputStateMachine->ProcessString(wstr); - } + _pInputStateMachine->ProcessString(_wstr); } CATCH_LOG(); diff --git a/src/host/VtInputThread.hpp b/src/host/VtInputThread.hpp index 7652a53887f..d058c425bc4 100644 --- a/src/host/VtInputThread.hpp +++ b/src/host/VtInputThread.hpp @@ -39,5 +39,6 @@ namespace Microsoft::Console std::unique_ptr _pInputStateMachine; til::u8state _u8State; + std::wstring _wstr; }; } From 28acc102a502f9e5f2f7e216cf87e7d6f20cbc96 Mon Sep 17 00:00:00 2001 From: e82eric Date: Fri, 15 Dec 2023 16:13:49 -0500 Subject: [PATCH 047/603] Highlight all search results while the search box is open (#16227) **FIRST TIME CONTRIBUTOR** Follows the existing selection code as much as possible. Updated logic that finds selection rectangles to also identify search rectangles. Right now, this feature only works in the new Atlas engine -- it uses the background and foreground color bitmaps to quickly and efficiently set the colors of a whole region of text. Closes #7561 Co-authored-by: Leonard Hecker --- src/buffer/out/search.cpp | 23 ++++++++++ src/buffer/out/search.h | 1 + src/cascadia/TerminalControl/ControlCore.cpp | 3 +- src/cascadia/TerminalCore/Terminal.hpp | 4 ++ .../TerminalCore/TerminalSelection.cpp | 35 ++++++++++++++ .../TerminalCore/terminalrenderdata.cpp | 35 ++++++++++++++ .../UnitTests_TerminalCore/ScrollTest.cpp | 1 + src/host/renderData.cpp | 14 ++++++ src/host/renderData.hpp | 2 + src/host/ut_host/VtIoTests.cpp | 9 ++++ src/interactivity/onecore/BgfxEngine.cpp | 5 ++ src/interactivity/onecore/BgfxEngine.hpp | 1 + src/renderer/atlas/AtlasEngine.cpp | 34 ++++++++++++++ src/renderer/atlas/AtlasEngine.h | 1 + src/renderer/base/renderer.cpp | 46 ++++++++++++++++++- src/renderer/base/renderer.hpp | 2 + src/renderer/dx/DxRenderer.cpp | 8 ++++ src/renderer/dx/DxRenderer.hpp | 1 + src/renderer/gdi/gdirenderer.hpp | 1 + src/renderer/gdi/paint.cpp | 7 +++ src/renderer/inc/IRenderData.hpp | 2 + src/renderer/inc/IRenderEngine.hpp | 1 + src/renderer/uia/UiaRenderer.cpp | 5 ++ src/renderer/uia/UiaRenderer.hpp | 1 + src/renderer/vt/paint.cpp | 5 ++ src/renderer/vt/vtrenderer.hpp | 1 + src/renderer/wddmcon/WddmConRenderer.cpp | 5 ++ src/renderer/wddmcon/WddmConRenderer.hpp | 1 + 28 files changed, 251 insertions(+), 3 deletions(-) diff --git a/src/buffer/out/search.cpp b/src/buffer/out/search.cpp index 9707e8fdeaa..fd8942e0bac 100644 --- a/src/buffer/out/search.cpp +++ b/src/buffer/out/search.cpp @@ -111,6 +111,28 @@ const til::point_span* Search::GetCurrent() const noexcept return nullptr; } +void Search::HighlightResults() const +{ + std::vector toSelect; + const auto& textBuffer = _renderData->GetTextBuffer(); + + for (const auto& r : _results) + { + const auto rbStart = textBuffer.BufferToScreenPosition(r.start); + const auto rbEnd = textBuffer.BufferToScreenPosition(r.end); + + til::inclusive_rect re; + re.top = rbStart.y; + re.bottom = rbEnd.y; + re.left = rbStart.x; + re.right = rbEnd.x; + + toSelect.emplace_back(re); + } + + _renderData->SelectSearchRegions(std::move(toSelect)); +} + // Routine Description: // - Takes the found word and selects it in the screen buffer @@ -127,6 +149,7 @@ bool Search::SelectCurrent() const return true; } + _renderData->ClearSelection(); return false; } diff --git a/src/buffer/out/search.h b/src/buffer/out/search.h index c2d035e3e9c..a338f1272c4 100644 --- a/src/buffer/out/search.h +++ b/src/buffer/out/search.h @@ -33,6 +33,7 @@ class Search final void FindNext() noexcept; const til::point_span* GetCurrent() const noexcept; + void HighlightResults() const; bool SelectCurrent() const; const std::vector& Results() const noexcept; diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 734e1b98d2a..4efcabf711a 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1652,6 +1652,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive)) { + _searcher.HighlightResults(); _searcher.MoveToCurrentSelection(); _cachedSearchResultRows = {}; } @@ -1668,7 +1669,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // DO NOT call _updateSelectionUI() here. // We don't want to show the markers so manually tell it to clear it. _terminal->SetBlockSelection(false); - _renderer->TriggerSelection(); _UpdateSelectionMarkersHandlers(*this, winrt::make(true)); foundResults->TotalMatches(gsl::narrow(_searcher.Results().size())); @@ -1676,6 +1676,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _terminal->AlwaysNotifyOnBufferRotation(true); } + _renderer->TriggerSelection(); // Raise a FoundMatch event, which the control will use to notify // narrator if there was any results in the buffer diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 3e2ffe4c083..603e0e521ba 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -215,10 +215,12 @@ class Microsoft::Terminal::Core::Terminal final : std::pair GetAttributeColors(const TextAttribute& attr) const noexcept override; std::vector GetSelectionRects() noexcept override; + std::vector GetSearchSelectionRects() noexcept override; const bool IsSelectionActive() const noexcept override; const bool IsBlockSelection() const noexcept override; void ClearSelection() override; void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override; + void SelectSearchRegions(std::vector source) override; const til::point GetSelectionAnchor() const noexcept override; const til::point GetSelectionEnd() const noexcept override; const std::wstring_view GetConsoleTitle() const noexcept override; @@ -377,6 +379,7 @@ class Microsoft::Terminal::Core::Terminal final : til::point pivot; }; std::optional _selection; + std::vector _searchSelections; bool _blockSelection = false; std::wstring _wordDelimiters; SelectionExpansion _multiClickSelectionMode = SelectionExpansion::Char; @@ -464,6 +467,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region TextSelection // These methods are defined in TerminalSelection.cpp std::vector _GetSelectionRects() const noexcept; + std::vector _GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept; std::vector _GetSelectionSpans() const noexcept; std::pair _PivotSelection(const til::point targetPos, bool& targetStart) const noexcept; std::pair _ExpandSelectionAnchors(std::pair anchors) const; diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 1978f5738bc..cfbc004b7d7 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -63,6 +63,40 @@ std::vector Terminal::_GetSelectionRects() const noexcept return result; } +// Method Description: +// - Helper to determine the selected region of the buffer. Used for rendering. +// Return Value: +// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin. +std::vector Terminal::_GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept +{ + std::vector result; + try + { + auto lowerIt = std::lower_bound(_searchSelections.begin(), _searchSelections.end(), viewport.Top(), [](const til::inclusive_rect& rect, til::CoordType value) { + return rect.top < value; + }); + + auto upperIt = std::upper_bound(_searchSelections.begin(), _searchSelections.end(), viewport.BottomExclusive(), [](til::CoordType value, const til::inclusive_rect& rect) { + return value < rect.top; + }); + + for (auto selection = lowerIt; selection != upperIt; ++selection) + { + const auto start = til::point{ selection->left, selection->top }; + const auto end = til::point{ selection->right, selection->top }; + const auto adj = _activeBuffer().GetTextRects(start, end, _blockSelection, false); + for (auto a : adj) + { + result.emplace_back(a); + } + } + + return result; + } + CATCH_LOG(); + return result; +} + // Method Description: // - Identical to GetTextRects if it's a block selection, else returns a single span for the whole selection. // Return Value: @@ -824,6 +858,7 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos) noex void Terminal::ClearSelection() { _assertLocked(); + _searchSelections.clear(); _selection = std::nullopt; _selectionMode = SelectionInteractionMode::None; _selectionIsTargetingUrl = false; diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 034a65ab784..8dd9b4dae9a 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -150,6 +150,24 @@ catch (...) return {}; } +std::vector Terminal::GetSearchSelectionRects() noexcept +try +{ + std::vector result; + + for (const auto& lineRect : _GetSearchSelectionRects(_GetVisibleViewport())) + { + result.emplace_back(Viewport::FromInclusive(lineRect)); + } + + return result; +} +catch (...) +{ + LOG_CAUGHT_EXCEPTION(); + return {}; +} + void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd) { #pragma warning(push) @@ -188,6 +206,23 @@ void Terminal::SelectNewRegion(const til::point coordStart, const til::point coo SetSelectionEnd(realCoordEnd, SelectionExpansion::Char); } +void Terminal::SelectSearchRegions(std::vector rects) +{ + _searchSelections.clear(); + for (auto& rect : rects) + { + rect.top -= _VisibleStartIndex(); + rect.bottom -= _VisibleStartIndex(); + + const auto realStart = _ConvertToBufferCell(til::point{ rect.left, rect.top }); + const auto realEnd = _ConvertToBufferCell(til::point{ rect.right, rect.bottom }); + + auto rr = til::inclusive_rect{ realStart.x, realStart.y, realEnd.x, realEnd.y }; + + _searchSelections.emplace_back(rr); + } +} + const std::wstring_view Terminal::GetConsoleTitle() const noexcept { _assertLocked(); diff --git a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp index d5baebadf26..21022f30333 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp @@ -59,6 +59,7 @@ namespace HRESULT PaintBufferLine(std::span /*clusters*/, til::point /*coord*/, bool /*fTrimLeft*/, bool /*lineWrapped*/) noexcept { return S_OK; } HRESULT PaintBufferGridLines(GridLineSet /*lines*/, COLORREF /*gridlineColor*/, COLORREF /*underlineColor*/, size_t /*cchLine*/, til::point /*coordTarget*/) noexcept { return S_OK; } HRESULT PaintSelection(const til::rect& /*rect*/) noexcept { return S_OK; } + HRESULT PaintSelections(const std::vector& /*rects*/) noexcept { return S_OK; } HRESULT PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; } HRESULT UpdateDrawingBrushes(const TextAttribute& /*textAttributes*/, const RenderSettings& /*renderSettings*/, gsl::not_null /*pData*/, bool /*usingSoftFont*/, bool /*isSettingDefaultBrushes*/) noexcept { return S_OK; } HRESULT UpdateFont(const FontInfoDesired& /*FontInfoDesired*/, _Out_ FontInfo& /*FontInfo*/) noexcept { return S_OK; } diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index 5c89edae9a9..d9b094626ba 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -79,6 +79,16 @@ std::vector RenderData::GetSelectionRects() noexcept return result; } +// Method Description: +// - Retrieves one rectangle per line describing the area of the viewport +// that should be highlighted in some way to represent a user-interactive selection +// Return Value: +// - Vector of Viewports describing the area selected +std::vector RenderData::GetSearchSelectionRects() noexcept +{ + return {}; +} + // Method Description: // - Lock the console for reading the contents of the buffer. Ensures that the // contents of the console won't be changed in the middle of a paint @@ -371,6 +381,10 @@ void RenderData::SelectNewRegion(const til::point coordStart, const til::point c Selection::Instance().SelectNewRegion(coordStart, coordEnd); } +void RenderData::SelectSearchRegions(std::vector source) +{ +} + // Routine Description: // - Gets the current selection anchor position // Arguments: diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index 5e649353cd7..52056d7a6f5 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -26,6 +26,7 @@ class RenderData final : const FontInfo& GetFontInfo() const noexcept override; std::vector GetSelectionRects() noexcept override; + std::vector GetSearchSelectionRects() noexcept override; void LockConsole() noexcept override; void UnlockConsole() noexcept override; @@ -54,6 +55,7 @@ class RenderData final : const bool IsBlockSelection() const noexcept override; void ClearSelection() override; void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override; + void SelectSearchRegions(std::vector source) override; const til::point GetSelectionAnchor() const noexcept override; const til::point GetSelectionEnd() const noexcept override; const bool IsUiaDataInitialized() const noexcept override { return true; } diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index 902f711771d..7f4796df60d 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -282,6 +282,11 @@ class MockRenderData : public IRenderData return std::vector{}; } + std::vector GetSearchSelectionRects() noexcept override + { + return std::vector{}; + } + void LockConsole() noexcept override { } @@ -363,6 +368,10 @@ class MockRenderData : public IRenderData { } + void SelectSearchRegions(std::vector /*source*/) override + { + } + const til::point GetSelectionAnchor() const noexcept { return {}; diff --git a/src/interactivity/onecore/BgfxEngine.cpp b/src/interactivity/onecore/BgfxEngine.cpp index d508603d5f9..a730ae8219e 100644 --- a/src/interactivity/onecore/BgfxEngine.cpp +++ b/src/interactivity/onecore/BgfxEngine.cpp @@ -161,6 +161,11 @@ CATCH_RETURN() return S_OK; } +[[nodiscard]] HRESULT BgfxEngine::PaintSelections(const std::vector& /*rects*/) noexcept +{ + return S_OK; +} + [[nodiscard]] HRESULT BgfxEngine::PaintCursor(const CursorOptions& options) noexcept try { diff --git a/src/interactivity/onecore/BgfxEngine.hpp b/src/interactivity/onecore/BgfxEngine.hpp index e9759ce0306..c787baba392 100644 --- a/src/interactivity/onecore/BgfxEngine.hpp +++ b/src/interactivity/onecore/BgfxEngine.hpp @@ -53,6 +53,7 @@ namespace Microsoft::Console::Render const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; + [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index 7438b93543e..93821394ce1 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -414,6 +414,40 @@ try } CATCH_RETURN() +[[nodiscard]] HRESULT AtlasEngine::PaintSelections(const std::vector& rects) noexcept +try +{ + if (rects.empty()) + { + return S_OK; + } + + for (const auto& rect : rects) + { + const auto y = gsl::narrow_cast(clamp(rect.top, 0, _p.s->viewportCellCount.y)); + const auto from = gsl::narrow_cast(clamp(rect.left, 0, _p.s->viewportCellCount.x - 1)); + const auto to = gsl::narrow_cast(clamp(rect.right, from, _p.s->viewportCellCount.x)); + + if (rect.bottom <= 0 || rect.top >= _p.s->viewportCellCount.y) + { + continue; + } + + const auto bg = &_p.backgroundBitmap[_p.colorBitmapRowStride * y]; + const auto fg = &_p.foregroundBitmap[_p.colorBitmapRowStride * y]; + std::fill(bg + from, bg + to, 0xff3296ff); + std::fill(fg + from, fg + to, 0xff000000); + } + + for (int i = 0; i < 2; ++i) + { + _p.colorBitmapGenerations[i].bump(); + } + + return S_OK; +} +CATCH_RETURN() + [[nodiscard]] HRESULT AtlasEngine::PaintCursor(const CursorOptions& options) noexcept try { diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 4ec09a9e931..8d6d76795c0 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -45,6 +45,7 @@ namespace Microsoft::Console::Render::Atlas [[nodiscard]] HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; + [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 0b1d6b004cb..c7c5c491815 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -362,6 +362,7 @@ void Renderer::TriggerSelection() { // Get selection rectangles auto rects = _GetSelectionRects(); + auto searchSelections = _GetSearchSelectionRects(); // Make a viewport representing the coordinates that are currently presentable. const til::rect viewport{ _pData->GetViewport().Dimensions() }; @@ -374,11 +375,14 @@ void Renderer::TriggerSelection() FOREACH_ENGINE(pEngine) { + LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSearchSelection)); LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection)); + LOG_IF_FAILED(pEngine->InvalidateSelection(searchSelections)); LOG_IF_FAILED(pEngine->InvalidateSelection(rects)); } _previousSelection = std::move(rects); + _previousSearchSelection = std::move(searchSelections); NotifyPaintFrame(); } @@ -1206,9 +1210,20 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine) // Get selection rectangles const auto rectangles = _GetSelectionRects(); - for (const auto& rect : rectangles) + const auto searchRectangles = _GetSearchSelectionRects(); + + std::vector dirtySearchRectangles; + for (auto& dirtyRect : dirtyAreas) { - for (auto& dirtyRect : dirtyAreas) + for (const auto& sr : searchRectangles) + { + if (const auto rectCopy = sr & dirtyRect) + { + dirtySearchRectangles.emplace_back(rectCopy); + } + } + + for (const auto& rect : rectangles) { if (const auto rectCopy = rect & dirtyRect) { @@ -1216,6 +1231,11 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine) } } } + + if (!dirtySearchRectangles.empty()) + { + LOG_IF_FAILED(pEngine->PaintSelections(std::move(dirtySearchRectangles))); + } } CATCH_LOG(); } @@ -1281,6 +1301,28 @@ std::vector Renderer::_GetSelectionRects() const return result; } +std::vector Renderer::_GetSearchSelectionRects() const +{ + const auto& buffer = _pData->GetTextBuffer(); + auto rects = _pData->GetSearchSelectionRects(); + // Adjust rectangles to viewport + auto view = _pData->GetViewport(); + + std::vector result; + result.reserve(rects.size()); + + for (auto rect : rects) + { + // Convert buffer offsets to the equivalent range of screen cells + // expected by callers, taking line rendition into account. + const auto lineRendition = buffer.GetLineRendition(rect.Top()); + rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition)); + result.emplace_back(view.ConvertToOrigin(rect).ToExclusive()); + } + + return result; +} + // Method Description: // - Offsets all of the selection rectangles we might be holding onto // as the previously selected area. If the whole viewport scrolls, diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index ae3ed2cfda9..1cd61799a8f 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -110,6 +110,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes); [[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine); std::vector _GetSelectionRects() const; + std::vector _GetSearchSelectionRects() const; void _ScrollPreviousSelection(const til::point delta); [[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine); bool _isInHoveredInterval(til::point coordTarget) const noexcept; @@ -127,6 +128,7 @@ namespace Microsoft::Console::Render Microsoft::Console::Types::Viewport _viewport; std::vector _clusterBuffer; std::vector _previousSelection; + std::vector _previousSearchSelection; std::function _pfnBackgroundColorChanged; std::function _pfnFrameColorChanged; std::function _pfnRendererEnteredErrorState; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 064d219f72a..e03cfb20081 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1844,6 +1844,14 @@ try } CATCH_RETURN() +[[nodiscard]] HRESULT DxEngine::PaintSelections(const std::vector& rects) noexcept +try +{ + UNREFERENCED_PARAMETER(rects); + return S_OK; +} +CATCH_RETURN() + // Routine Description: // - Does nothing. Our cursor is drawn in CustomTextRenderer::DrawGlyphRun, // either above or below the text. diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 877b3f1adfa..990c77e18e5 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -108,6 +108,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; + [[nodiscard]] HRESULT PaintSelections(const std::vector& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 2cc5b4dc14c..1d855d2746e 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -57,6 +57,7 @@ namespace Microsoft::Console::Render const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; + [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 222576390e2..5951c57ef29 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -815,6 +815,13 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) return S_OK; } +[[nodiscard]] HRESULT GdiEngine::PaintSelections(const std::vector& rects) noexcept +{ + UNREFERENCED_PARAMETER(rects); + + return S_OK; +} + #ifdef DBG void GdiEngine::_CreateDebugWindow() diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index 69e0dc2a0e6..cfc035a7f9b 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -47,6 +47,7 @@ namespace Microsoft::Console::Render virtual const TextBuffer& GetTextBuffer() const noexcept = 0; virtual const FontInfo& GetFontInfo() const noexcept = 0; virtual std::vector GetSelectionRects() noexcept = 0; + virtual std::vector GetSearchSelectionRects() noexcept = 0; virtual void LockConsole() noexcept = 0; virtual void UnlockConsole() noexcept = 0; @@ -71,6 +72,7 @@ namespace Microsoft::Console::Render virtual const bool IsBlockSelection() const = 0; virtual void ClearSelection() = 0; virtual void SelectNewRegion(const til::point coordStart, const til::point coordEnd) = 0; + virtual void SelectSearchRegions(std::vector source) = 0; virtual const til::point GetSelectionAnchor() const noexcept = 0; virtual const til::point GetSelectionEnd() const noexcept = 0; virtual const bool IsUiaDataInitialized() const noexcept = 0; diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index afcaa5aff59..016f0f10baa 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -78,6 +78,7 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept = 0; [[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF gridlineColor, COLORREF underlineColor, size_t cchLine, til::point coordTarget) noexcept = 0; [[nodiscard]] virtual HRESULT PaintSelection(const til::rect& rect) noexcept = 0; + [[nodiscard]] virtual HRESULT PaintSelections(const std::vector& rects) noexcept = 0; [[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept = 0; diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index d1e72fbd0eb..a245491dead 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -386,6 +386,11 @@ void UiaEngine::WaitUntilCanRender() noexcept return S_FALSE; } +[[nodiscard]] HRESULT UiaEngine::PaintSelections(const std::vector& /*rect*/) noexcept +{ + return S_FALSE; +} + // Routine Description: // - Draws the cursor on the screen // For UIA, this doesn't mean anything. So do nothing. diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 4625ca155cb..bd1734c5d64 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -51,6 +51,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PaintBufferLine(const std::span clusters, const til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; + [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, const gsl::not_null pData, const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index 99f8853a98f..d8e3e66686b 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -233,6 +233,11 @@ using namespace Microsoft::Console::Types; return S_OK; } +[[nodiscard]] HRESULT VtEngine::PaintSelections(const std::vector& /*rect*/) noexcept +{ + return S_OK; +} + // Routine Description: // - Write a VT sequence to change the current colors of text. Writes true RGB // color sequences. diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 676672ee814..7a974850afc 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -64,6 +64,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; + [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override; diff --git a/src/renderer/wddmcon/WddmConRenderer.cpp b/src/renderer/wddmcon/WddmConRenderer.cpp index fda8ae6c21b..db729467cae 100644 --- a/src/renderer/wddmcon/WddmConRenderer.cpp +++ b/src/renderer/wddmcon/WddmConRenderer.cpp @@ -298,6 +298,11 @@ CATCH_RETURN() return S_OK; } +[[nodiscard]] HRESULT WddmConEngine::PaintSelections(const std::vector& /*rects*/) noexcept +{ + return S_OK; +} + [[nodiscard]] HRESULT WddmConEngine::PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; diff --git a/src/renderer/wddmcon/WddmConRenderer.hpp b/src/renderer/wddmcon/WddmConRenderer.hpp index 954dc226946..930fbe6dea6 100644 --- a/src/renderer/wddmcon/WddmConRenderer.hpp +++ b/src/renderer/wddmcon/WddmConRenderer.hpp @@ -46,6 +46,7 @@ namespace Microsoft::Console::Render const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; + [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; From 99193c9a3f462b3177b6f596706f11be2074efa5 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 16 Dec 2023 00:02:24 +0100 Subject: [PATCH 048/603] Put the final touches on GDI's underlines (#16475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While #16444 left wavy lines in an amazing state already, there were a few more things that could be done to make GDI look more consistent with other well known Windows applications. But before that, a couple unrelated, but helpful changes were made: * `GdiEngine::UpdateFont` was heavily modified to do all calculations in floats. All modern CPUs have fast FPUs and even the fairly slow `lroundf` function is so fast (relatively) nowadays that in a cold path like this, we can liberally call it to convert back to `int`s. This makes intermediate calculation more accurate and consistent. * `GdiEngine::PaintBufferGridLines` was exception-unsafe due to its use of a `std::vector` with catch clause and this PR fixes that. Additionally, the vector was swapped out with a `til::small_vector` to reduce heap allocations. (Arena allocators!) * RenderingTests was updated to cover styled underlines With that in place, these improvements were done: * Word's double-underline algorithm was ported over from `AtlasEngine`. It uses a half underline-width (aka `thinLineWidth`) which will now also be used for wavy lines to make them look a bit more filigrane. * The Bézier curve for wavy/curly underlines was modified to use control points at (0.5,0.5) and (0.5,-0.5) respectively. This results in a maxima at y=0.1414 which is much closer to a sine curve with a maxima at 1/(2pi) = 0.1592. Previously, the maxima was a lot higher (roughly 4x) depending on the aspect ratio of the glyphs. * Wavy underlines don't depend on the aspect ratio of glyphs anymore. This previously led to several problems depending on the exact font. The old renderer would draw exactly 3 periods of the wave into each cell which would also ensure continuity between cells. Unfortunately, this meant that waves could look inconsistent. The new approach always uses the aforementioned sine-like waves. * The wavy underline offset was clamped so that it's never outside of bounds of a line. This avoids clipping. ## Validation Steps Performed * Compile RenderingTests and run it * Using Consolas, MS Gothic and Cascadia Code while Ctrl+Scrolling up and down works as expected without clipping ✅ --- .github/actions/spelling/expect/expect.txt | 15 -- src/renderer/gdi/gdirenderer.hpp | 10 +- src/renderer/gdi/paint.cpp | 85 ++++++----- src/renderer/gdi/state.cpp | 160 ++++++++++++--------- src/tools/RenderingTests/main.cpp | 147 +++++++++++++------ 5 files changed, 256 insertions(+), 161 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 2de1494a028..6ae7a458582 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -294,7 +294,6 @@ CPPCORECHECK cppcorecheckrules cpprestsdk cppwinrt -CProc cpx CREATESCREENBUFFER CREATESTRUCT @@ -324,7 +323,6 @@ CTRLVOLUME Ctxt CUF cupxy -curlyline CURRENTFONT currentmode CURRENTPAGE @@ -583,7 +581,6 @@ EXETYPE exeuwp exewin exitwin -expectedinput EXPUNGECOMMANDHISTORY EXSTYLE EXTENDEDEDITKEY @@ -795,7 +792,6 @@ HPA hpcon HPCON hpen -hpj HPR HProvider HREDRAW @@ -978,7 +974,6 @@ logissue losslessly loword lparam -LPCCH lpch LPCPLINFO LPCREATESTRUCT @@ -1177,7 +1172,6 @@ NOMOVE NONALERT nonbreaking nonclient -NONCONST NONINFRINGEMENT NONPREROTATED nonspace @@ -1525,17 +1519,14 @@ rgbs rgci rgfae rgfte -rgi rgn rgp rgpwsz rgrc -rgui rgw RIGHTALIGN RIGHTBUTTON riid -Rike RIS roadmap robomac @@ -1685,7 +1676,6 @@ SOLIDBOX Solutiondir somefile sourced -spammy SRCCODEPAGE SRCCOPY SRCINVERT @@ -1831,7 +1821,6 @@ Tpp Tpqrst tracelog tracelogging -traceloggingprovider traceviewpp trackbar TRACKCOMPOSITION @@ -1915,7 +1904,6 @@ USEFILLATTRIBUTE USEGLYPHCHARS USEHICON USEPOSITION -userbase USERDATA userdpiapi Userp @@ -1956,8 +1944,6 @@ vpack vpackdirectory VPACKMANIFESTDIRECTORY VPR -VProc -VRaw VREDRAW vsc vsconfig @@ -2001,7 +1987,6 @@ WCIA WCIW WCSHELPER wcsicmp -wcsnicmp wcsrev wddm wddmcon diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 1d855d2746e..13ecdd25372 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -117,12 +117,16 @@ namespace Microsoft::Console::Render struct LineMetrics { int gridlineWidth; - int underlineOffset; - int underlineOffset2; + int thinLineWidth; + int underlineCenter; int underlineWidth; + int doubleUnderlinePosTop; + int doubleUnderlinePosBottom; int strikethroughOffset; int strikethroughWidth; - int curlylinePeakHeight; + int curlyLineCenter; + int curlyLinePeriod; + int curlyLineControlPointOffset; }; LineMetrics _lineMetrics; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 5951c57ef29..4dbb82d9c6d 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -2,9 +2,10 @@ // Licensed under the MIT license. #include "precomp.h" -#include #include "gdirenderer.hpp" +#include + #include "../inc/unicode.hpp" #pragma hdrstop @@ -516,6 +517,7 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) // Return Value: // - S_OK or suitable GDI HRESULT error or E_FAIL for GDI errors in functions that don't reliably return a specific error code. [[nodiscard]] HRESULT GdiEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept +try { LOG_IF_FAILED(_FlushBufferLines()); @@ -531,38 +533,50 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) // Get the font size so we know the size of the rectangle lines we'll be inscribing. const auto fontWidth = _GetFontSize().width; const auto fontHeight = _GetFontSize().height; - const auto widthOfAllCells = fontWidth * gsl::narrow_cast(cchLine); + const auto widthOfAllCells = fontWidth * gsl::narrow_cast(cchLine); - const auto DrawLine = [=](const auto x, const auto y, const auto w, const auto h) { + const auto DrawLine = [=](const til::CoordType x, const til::CoordType y, const til::CoordType w, const til::CoordType h) { return PatBlt(_hdcMemoryContext, x, y, w, h, PATCOPY); }; - const auto DrawStrokedLine = [&](const til::CoordType x, const til::CoordType y, const unsigned w) { + const auto DrawStrokedLine = [&](const til::CoordType x, const til::CoordType y, const til::CoordType w) { RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); - RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, gsl::narrow_cast(x + w), y)); + RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, x + w, y)); return S_OK; }; - const auto DrawCurlyLine = [&](const til::CoordType x, const til::CoordType y, const size_t cCurlyLines) { - const auto curlyLineWidth = fontWidth; - const auto curlyLineHalfWidth = std::lround(curlyLineWidth / 2.0f); - const auto controlPointHeight = std::lround(3.5f * _lineMetrics.curlylinePeakHeight); - - // Each curlyLine requires 3 `POINT`s - const auto cPoints = gsl::narrow(3 * cCurlyLines); - std::vector points; - points.reserve(cPoints); - - auto start = x; - for (size_t i = 0; i < cCurlyLines; i++) + const auto DrawCurlyLine = [&](const til::CoordType begX, const til::CoordType y, const til::CoordType width) { + const auto period = _lineMetrics.curlyLinePeriod; + const auto halfPeriod = period / 2; + const auto controlPointOffset = _lineMetrics.curlyLineControlPointOffset; + + // To ensure proper continuity of the wavy line between cells of different line color + // this code starts/ends the line earlier/later than it should and then clips it. + // Clipping in GDI is expensive, but it was the easiest approach. + // I've noticed that subtracting -1px prevents missing pixels when GDI draws. They still + // occur at certain (small) font sizes, but I couldn't figure out how to prevent those. + const auto lineStart = ((begX - 1) / period) * period; + const auto lineEnd = begX + width; + + IntersectClipRect(_hdcMemoryContext, begX, ptTarget.y, begX + width, ptTarget.y + fontHeight); + const auto restoreRegion = wil::scope_exit([&]() { + // Luckily no one else uses clip regions. They're weird to use. + SelectClipRgn(_hdcMemoryContext, nullptr); + }); + + // You can assume that each cell has roughly 5 POINTs on average. 128 POINTs is 1KiB. + til::small_vector points; + + // This is the start point of the Bézier curve. + points.emplace_back(lineStart, y); + + for (auto x = lineStart; x < lineEnd; x += period) { - points.emplace_back(start + curlyLineHalfWidth, y - controlPointHeight); - points.emplace_back(start + curlyLineHalfWidth, y + controlPointHeight); - points.emplace_back(start + curlyLineWidth, y); - start += curlyLineWidth; + points.emplace_back(x + halfPeriod, y - controlPointOffset); + points.emplace_back(x + halfPeriod, y + controlPointOffset); + points.emplace_back(x + period, y); } - RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); - RETURN_HR_IF(E_FAIL, !PolyBezierTo(_hdcMemoryContext, points.data(), cPoints)); - return S_OK; + const auto cpt = gsl::narrow_cast(points.size()); + return PolyBezier(_hdcMemoryContext, points.data(), cpt); }; if (lines.test(GridLines::Left)) @@ -605,7 +619,6 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) RETURN_HR_IF(E_FAIL, !DrawLine(ptTarget.x, y, widthOfAllCells, _lineMetrics.strikethroughWidth)); } - // Create a pen matching the underline style. DWORD underlinePenType = PS_SOLID; if (lines.test(GridLines::DottedUnderline)) { @@ -615,8 +628,15 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) { underlinePenType = PS_DASH; } + + DWORD underlineWidth = _lineMetrics.underlineWidth; + if (lines.any(GridLines::DoubleUnderline, GridLines::CurlyUnderline)) + { + underlineWidth = _lineMetrics.thinLineWidth; + } + const LOGBRUSH brushProp{ .lbStyle = BS_SOLID, .lbColor = underlineColor }; - wil::unique_hpen hpen(ExtCreatePen(underlinePenType | PS_GEOMETRIC | PS_ENDCAP_FLAT, _lineMetrics.underlineWidth, &brushProp, 0, nullptr)); + wil::unique_hpen hpen(ExtCreatePen(underlinePenType | PS_GEOMETRIC | PS_ENDCAP_FLAT, underlineWidth, &brushProp, 0, nullptr)); // Apply the pen. const auto prevPen = wil::SelectObject(_hdcMemoryContext, hpen.get()); @@ -624,28 +644,29 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) if (lines.test(GridLines::Underline)) { - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineCenter, widthOfAllCells); } else if (lines.test(GridLines::DoubleUnderline)) { - RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells)); - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset2, widthOfAllCells); + RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.doubleUnderlinePosTop, widthOfAllCells)); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.doubleUnderlinePosBottom, widthOfAllCells); } else if (lines.test(GridLines::CurlyUnderline)) { - return DrawCurlyLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, cchLine); + return DrawCurlyLine(ptTarget.x, ptTarget.y + _lineMetrics.curlyLineCenter, widthOfAllCells); } else if (lines.test(GridLines::DottedUnderline)) { - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineCenter, widthOfAllCells); } else if (lines.test(GridLines::DashedUnderline)) { - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineCenter, widthOfAllCells); } return S_OK; } +CATCH_RETURN(); // Routine Description: // - Draws the cursor on the screen diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 13fd0ea59fc..f993c6f74e6 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -344,85 +344,109 @@ GdiEngine::~GdiEngine() // There is no font metric for the grid line width, so we use a small // multiple of the font size, which typically rounds to a pixel. - const auto fontSize = _tmFontMetrics.tmHeight - _tmFontMetrics.tmInternalLeading; - _lineMetrics.gridlineWidth = std::lround(fontSize * 0.025); + const auto cellHeight = static_cast(Font.GetSize().height); + const auto fontSize = static_cast(_tmFontMetrics.tmHeight - _tmFontMetrics.tmInternalLeading); + const auto baseline = static_cast(_tmFontMetrics.tmAscent); + float idealGridlineWidth = std::max(1.0f, fontSize * 0.025f); + float idealUnderlineTop = 0; + float idealUnderlineWidth = 0; + float idealStrikethroughTop = 0; + float idealStrikethroughWidth = 0; OUTLINETEXTMETRICW outlineMetrics; if (GetOutlineTextMetricsW(_hdcMemoryContext, sizeof(outlineMetrics), &outlineMetrics)) { // For TrueType fonts, the other line metrics can be obtained from // the font's outline text metric structure. - _lineMetrics.underlineOffset = outlineMetrics.otmsUnderscorePosition; - _lineMetrics.underlineWidth = outlineMetrics.otmsUnderscoreSize; - _lineMetrics.strikethroughOffset = outlineMetrics.otmsStrikeoutPosition; - _lineMetrics.strikethroughWidth = outlineMetrics.otmsStrikeoutSize; + idealUnderlineTop = static_cast(baseline - outlineMetrics.otmsUnderscorePosition); + idealUnderlineWidth = static_cast(outlineMetrics.otmsUnderscoreSize); + idealStrikethroughWidth = static_cast(outlineMetrics.otmsStrikeoutSize); + idealStrikethroughTop = static_cast(baseline - outlineMetrics.otmsStrikeoutPosition); } else { - // If we can't obtain the outline metrics for the font, we just pick - // some reasonable values for the offsets and widths. - _lineMetrics.underlineOffset = -std::lround(fontSize * 0.05); - _lineMetrics.underlineWidth = _lineMetrics.gridlineWidth; - _lineMetrics.strikethroughOffset = std::lround(_tmFontMetrics.tmAscent / 3.0); - _lineMetrics.strikethroughWidth = _lineMetrics.gridlineWidth; + // If we can't obtain the outline metrics for the font, we just pick some reasonable values for the offsets and widths. + idealUnderlineTop = std::max(1.0f, roundf(baseline - fontSize * 0.05f)); + idealUnderlineWidth = idealGridlineWidth; + idealStrikethroughTop = std::max(1.0f, roundf(baseline * (2.0f / 3.0f))); + idealStrikethroughWidth = idealGridlineWidth; } - // We always want the lines to be visible, so if a stroke width ends - // up being zero, we need to make it at least 1 pixel. - _lineMetrics.gridlineWidth = std::max(_lineMetrics.gridlineWidth, 1); - _lineMetrics.underlineWidth = std::max(_lineMetrics.underlineWidth, 1); - _lineMetrics.strikethroughWidth = std::max(_lineMetrics.strikethroughWidth, 1); - - // Offsets are relative to the base line of the font, so we subtract - // from the ascent to get an offset relative to the top of the cell. - const auto ascent = _tmFontMetrics.tmAscent; - _lineMetrics.underlineOffset = ascent - _lineMetrics.underlineOffset; - _lineMetrics.strikethroughOffset = ascent - _lineMetrics.strikethroughOffset; - - // For double underlines we need a second offset, just below the first, - // but with a bit of a gap (about double the grid line width). - _lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset + - _lineMetrics.underlineWidth + - std::lround(fontSize * 0.05); - - // However, we don't want the underline to extend past the bottom of the - // cell, so we clamp the offset to fit just inside. - const auto maxUnderlineOffset = Font.GetSize().height - _lineMetrics.underlineWidth; - _lineMetrics.underlineOffset2 = std::min(_lineMetrics.underlineOffset2, maxUnderlineOffset); - - // But if the resulting gap isn't big enough even to register as a thicker - // line, it's better to place the second line slightly above the first. - if (_lineMetrics.underlineOffset2 < _lineMetrics.underlineOffset + _lineMetrics.gridlineWidth) - { - _lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset - _lineMetrics.gridlineWidth; - } - - // Since we use GDI pen for drawing, the underline offset should point to - // the center of the underline. - const auto underlineHalfWidth = gsl::narrow_cast(std::floor(_lineMetrics.underlineWidth / 2.0f)); - _lineMetrics.underlineOffset += underlineHalfWidth; - _lineMetrics.underlineOffset2 += underlineHalfWidth; - - // Curlyline is drawn with a desired height relative to the font size. The - // baseline of curlyline is at the middle of singly underline. When there's - // limited space to draw a curlyline, we apply a limit on the peak height. - { - // initialize curlyline peak height to a desired value. Clamp it to at - // least 1. - constexpr auto curlyLinePeakHeightEm = 0.075f; - _lineMetrics.curlylinePeakHeight = gsl::narrow_cast(std::max(1L, std::lround(curlyLinePeakHeightEm * fontSize))); - - // calc the limit we need to apply - const auto maxDrawableCurlyLinePeakHeight = Font.GetSize().height - _lineMetrics.underlineOffset - _lineMetrics.underlineWidth; - - // if the limit is <= 0 (no height at all), stick with the desired height. - // This is how we force a curlyline even when there's no space, though it - // might be clipped at the bottom. - if (maxDrawableCurlyLinePeakHeight > 0.0f) - { - _lineMetrics.curlylinePeakHeight = std::min(_lineMetrics.curlylinePeakHeight, maxDrawableCurlyLinePeakHeight); - } - } + // GdiEngine::PaintBufferGridLines paints underlines using HPEN and LineTo, etc., which draws lines centered on the given coordinates. + // This means we need to shift the limit (cellHeight - underlineWidth) and offset (idealUnderlineTop) by half the width. + const auto underlineWidth = std::max(1.0f, roundf(idealUnderlineWidth)); + const auto underlineCenter = std::min(floorf(cellHeight - underlineWidth / 2.0f), roundf(idealUnderlineTop + underlineWidth / 2.0f)); + + const auto strikethroughWidth = std::max(1.0f, roundf(idealStrikethroughWidth)); + const auto strikethroughOffset = std::min(cellHeight - strikethroughWidth, roundf(idealStrikethroughTop)); + + // For double underlines we loosely follow what Word does: + // 1. The lines are half the width of an underline + // 2. Ideally the bottom line is aligned with the bottom of the underline + // 3. The top underline is vertically in the middle between baseline and ideal bottom underline + // 4. If the top line gets too close to the baseline the underlines are shifted downwards + // 5. The minimum gap between the two lines appears to be similar to Tex (1.2pt) + // (Additional notes below.) + + // 1. + const auto thinLineWidth = std::max(1.0f, roundf(idealUnderlineWidth / 2.0f)); + // 2. + auto doubleUnderlinePosBottom = underlineCenter + underlineWidth - thinLineWidth; + // 3. Since we don't align the center of our two lines, but rather the top borders + // we need to subtract half a line width from our center point. + auto doubleUnderlinePosTop = roundf((baseline + doubleUnderlinePosBottom - thinLineWidth) / 2.0f); + // 4. + doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + thinLineWidth); + // 5. The gap is only the distance _between_ the lines, but we need the distance from the + // top border of the top and bottom lines, which includes an additional line width. + const auto doubleUnderlineGap = std::max(1.0f, roundf(1.2f / 72.0f * _iCurrentDpi)); + doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + thinLineWidth); + // Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries. + doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, cellHeight - thinLineWidth); + + // The wave line is drawn using a cubic Bézier curve (PolyBezier), because that happens to be cheap with GDI. + // We use a Bézier curve where, if the start (a) and end (c) points are at (0,0) and (1,0), the control points are + // at (0.5,0.5) (b) and (0.5,-0.5) (d) respectively. Like this but a/b/c/d are square and the lines are round: + // + // b + // + // ^ + // / \ here's some text so the compiler ignores the trailing \ character + // a \ c + // \ / + // v + // + // d + // + // If you punch x=0.25 into the cubic bezier formula you get y=0.140625. This constant is + // important to us because it (plus the line width) tells us the amplitude of the wave. + // + // We can use the inverse of the constant to figure out how many px one period of the wave has to be to end up being 1px tall. + // In our case we want the amplitude of the wave to have a peak-to-peak amplitude that matches our double-underline. + const auto doubleUnderlineHalfDistance = 0.5f * (doubleUnderlinePosBottom - doubleUnderlinePosTop); + const auto doubleUnderlineCenter = doubleUnderlinePosTop + doubleUnderlineHalfDistance; + const auto curlyLineIdealAmplitude = std::max(1.0f, doubleUnderlineHalfDistance); + // Since GDI can't deal with fractional pixels, we first calculate the control point offsets (0.5 and -0.5) by multiplying by 0.5 and + // then undo that by multiplying by 2.0 for the period. This ensures that our control points can be at curlyLinePeriod/2, an integer. + const auto curlyLineControlPointOffset = roundf(curlyLineIdealAmplitude * (1.0f / 0.140625f) * 0.5f); + const auto curlyLinePeriod = curlyLineControlPointOffset * 2.0f; + // We can reverse the above to get back the actual amplitude of our Bézier curve. The line + // will be drawn with a width of thinLineWidth in the center of the curve (= 0.5x padding). + const auto curlyLineAmplitude = 0.140625f * curlyLinePeriod + 0.5f * thinLineWidth; + // To make the wavy line with its double-underline amplitude look consistent with the double-underline we position it at its center. + const auto curlyLineOffset = std::min(roundf(doubleUnderlineCenter), floorf(cellHeight - curlyLineAmplitude)); + + _lineMetrics.gridlineWidth = lroundf(idealGridlineWidth); + _lineMetrics.thinLineWidth = lroundf(thinLineWidth); + _lineMetrics.underlineCenter = lroundf(underlineCenter); + _lineMetrics.underlineWidth = lroundf(underlineWidth); + _lineMetrics.doubleUnderlinePosTop = lroundf(doubleUnderlinePosTop); + _lineMetrics.doubleUnderlinePosBottom = lroundf(doubleUnderlinePosBottom); + _lineMetrics.strikethroughOffset = lroundf(strikethroughOffset); + _lineMetrics.strikethroughWidth = lroundf(strikethroughWidth); + _lineMetrics.curlyLineCenter = lroundf(curlyLineOffset); + _lineMetrics.curlyLinePeriod = lroundf(curlyLinePeriod); + _lineMetrics.curlyLineControlPointOffset = lroundf(curlyLineControlPointOffset); // Now find the size of a 0 in this current font and save it for conversions done later. _coordFontLast = Font.GetSize(); diff --git a/src/tools/RenderingTests/main.cpp b/src/tools/RenderingTests/main.cpp index a82e38bb818..45dfcf66ea7 100644 --- a/src/tools/RenderingTests/main.cpp +++ b/src/tools/RenderingTests/main.cpp @@ -5,7 +5,39 @@ #include #include -#include +#include + +// The following list of colors is only used as a debug aid and not part of the final product. +// They're licensed under: +// +// Apache-Style Software License for ColorBrewer software and ColorBrewer Color Schemes +// +// Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +namespace colorbrewer +{ + inline constexpr uint32_t pastel1[]{ + 0xfbb4ae, + 0xb3cde3, + 0xccebc5, + 0xdecbe4, + 0xfed9a6, + 0xffffcc, + 0xe5d8bd, + 0xfddaec, + 0xf2f2f2, + }; +} // Another variant of "defer" for C++. namespace @@ -110,64 +142,93 @@ int main() }; { - struct ConsoleAttributeTest + struct AttributeTest { const wchar_t* text = nullptr; WORD attribute = 0; }; - static constexpr ConsoleAttributeTest consoleAttributeTests[]{ - { L"Console attributes:", 0 }, + + { + static constexpr AttributeTest consoleAttributeTests[]{ + { L"Console attributes:", 0 }, #define MAKE_TEST_FOR_ATTRIBUTE(attr) { L## #attr, attr } - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_HORIZONTAL), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_LVERTICAL), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_RVERTICAL), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_REVERSE_VIDEO), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_UNDERSCORE), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_HORIZONTAL), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_LVERTICAL), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_RVERTICAL), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_REVERSE_VIDEO), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_UNDERSCORE), #undef MAKE_TEST_FOR_ATTRIBUTE - { L"all gridlines", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE }, - { L"all attributes", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_REVERSE_VIDEO | COMMON_LVB_UNDERSCORE }, - }; + { L"all gridlines", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE }, + { L"all attributes", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_REVERSE_VIDEO | COMMON_LVB_UNDERSCORE }, + }; - SHORT row = 2; - for (const auto& t : consoleAttributeTests) - { - const auto length = static_cast(wcslen(t.text)); - printfUTF16(L"\x1B[%d;5H%s", row + 1, t.text); + SHORT row = 2; + for (const auto& t : consoleAttributeTests) + { + const auto length = static_cast(wcslen(t.text)); + printfUTF16(L"\x1B[%d;5H%s", row + 1, t.text); - WORD attributes[32]; - std::fill_n(&attributes[0], length, static_cast(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | t.attribute)); + WORD attributes[32]; + std::fill_n(&attributes[0], length, static_cast(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | t.attribute)); - DWORD numberOfAttrsWritten; - WriteConsoleOutputAttribute(outputHandle, attributes, length, { 4, row }, &numberOfAttrsWritten); + DWORD numberOfAttrsWritten; + WriteConsoleOutputAttribute(outputHandle, attributes, length, { 4, row }, &numberOfAttrsWritten); - row += 2; + row += 2; + } } - struct VTAttributeTest { - const wchar_t* text = nullptr; - int sgr = 0; - }; - static constexpr VTAttributeTest vtAttributeTests[]{ - { L"ANSI escape SGR:", 0 }, - { L"bold", 1 }, - { L"faint", 2 }, - { L"italic", 3 }, - { L"underline", 4 }, - { L"reverse", 7 }, - { L"strikethrough", 9 }, - { L"double underline", 21 }, - { L"overlined", 53 }, - }; + static constexpr AttributeTest basicSGR[]{ + { L"bold", 1 }, + { L"faint", 2 }, + { L"italic", 3 }, + { L"underline", 4 }, + { L"reverse", 7 }, + { L"strikethrough", 9 }, + { L"double underline", 21 }, + { L"overlined", 53 }, + }; + + printfUTF16(L"\x1B[3;39HANSI escape SGR:"); + + int row = 5; + for (const auto& t : basicSGR) + { + printfUTF16(L"\x1B[%d;39H\x1b[%dm%s\x1b[m", row, t.attribute, t.text); + row += 2; + } - row = 3; - for (const auto& t : vtAttributeTests) - { - printfUTF16(L"\x1B[%d;45H\x1b[%dm%s\x1b[m", row, t.sgr, t.text); - row += 2; + printfUTF16(L"\x1B[%d;39H\x1b]8;;https://example.com\x1b\\hyperlink\x1b]8;;\x1b\\", row); } - printfUTF16(L"\x1B[%d;45H\x1b]8;;https://example.com\x1b\\hyperlink\x1b]8;;\x1b\\", row); + { + static constexpr AttributeTest styledUnderlines[]{ + { L"straight", 1 }, + { L"double", 2 }, + { L"curly", 3 }, + { L"dotted", 4 }, + { L"dashed", 5 }, + }; + + printfUTF16(L"\x1B[3;63HStyled Underlines:"); + + int row = 5; + for (const auto& t : styledUnderlines) + { + printfUTF16(L"\x1B[%d;63H\x1b[4:%dm", row, t.attribute); + + const auto len = wcslen(t.text); + for (size_t i = 0; i < len; ++i) + { + const auto color = colorbrewer::pastel1[i % std::size(colorbrewer::pastel1)]; + printfUTF16(L"\x1B[58:2::%d:%d:%dm%c", (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, t.text[i]); + } + + printfUTF16(L"\x1b[m"); + row += 2; + } + } wait(); clear(); From a65d5f321f72daaf80d30b2ce4dc8547c1c282b3 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 16 Dec 2023 00:29:09 +0100 Subject: [PATCH 049/603] Add missing TraceLoggingRegister calls (#16467) 17cc109 and e9de646 both made the same mistake: When cleaning up our telemetry code they also removed the calls to `TraceLoggingRegister` which also broke regular tracing. Windows Defender in particular uses the "CookedRead" event to monitor for malicious shell commands. This doesn't fix it the "right way", because destructors of statics aren't executed when DLLs are unloaded. But I felt like that this is fine because we have way more statics than that in conhost land, all of which have the same kind of issue. --- src/host/exe/exemain.cpp | 1 + src/host/telemetry.cpp | 6 ------ src/host/tracing.cpp | 6 ++++++ src/terminal/parser/tracing.cpp | 8 ++++++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/host/exe/exemain.cpp b/src/host/exe/exemain.cpp index 7436cc8cb75..d0d2b81b514 100644 --- a/src/host/exe/exemain.cpp +++ b/src/host/exe/exemain.cpp @@ -222,6 +222,7 @@ int CALLBACK wWinMain( _In_ PWSTR /*pwszCmdLine*/, _In_ int /*nCmdShow*/) { + TraceLoggingRegister(g_hConhostV2EventTraceProvider); wil::SetResultLoggingCallback(&Tracing::TraceFailure); Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().hInstance = hInstance; diff --git a/src/host/telemetry.cpp b/src/host/telemetry.cpp index fb25c7f6818..9b1d7256799 100644 --- a/src/host/telemetry.cpp +++ b/src/host/telemetry.cpp @@ -4,12 +4,6 @@ #include "precomp.h" #include "telemetry.hpp" -TRACELOGGING_DEFINE_PROVIDER(g_hConhostV2EventTraceProvider, - "Microsoft.Windows.Console.Host", - // {fe1ff234-1f09-50a8-d38d-c44fab43e818} - (0xfe1ff234, 0x1f09, 0x50a8, 0xd3, 0x8d, 0xc4, 0x4f, 0xab, 0x43, 0xe8, 0x18), - TraceLoggingOptionMicrosoftTelemetry()); - // This code remains to serve as template if we ever need telemetry for conhost again. // The git history for this file may prove useful. #if 0 diff --git a/src/host/tracing.cpp b/src/host/tracing.cpp index 14a4db03e4c..c3ae744f41f 100644 --- a/src/host/tracing.cpp +++ b/src/host/tracing.cpp @@ -6,6 +6,12 @@ #include "../types/UiaTextRangeBase.hpp" #include "../types/ScreenInfoUiaProviderBase.h" +TRACELOGGING_DEFINE_PROVIDER(g_hConhostV2EventTraceProvider, + "Microsoft.Windows.Console.Host", + // {fe1ff234-1f09-50a8-d38d-c44fab43e818} + (0xfe1ff234, 0x1f09, 0x50a8, 0xd3, 0x8d, 0xc4, 0x4f, 0xab, 0x43, 0xe8, 0x18), + TraceLoggingOptionMicrosoftTelemetry()); + using namespace Microsoft::Console::Types; // NOTE: See `til.h` for which keyword flags are reserved diff --git a/src/terminal/parser/tracing.cpp b/src/terminal/parser/tracing.cpp index 9ab684ac95d..862c9c1ed80 100644 --- a/src/terminal/parser/tracing.cpp +++ b/src/terminal/parser/tracing.cpp @@ -7,6 +7,7 @@ using namespace Microsoft::Console::VirtualTerminal; #pragma warning(push) +#pragma warning(disable : 26426) // Global initializer calls a non-constexpr function '...' (i.22).) #pragma warning(disable : 26447) // The function is declared 'noexcept' but calls function '_tlgWrapBinary()' which may throw exceptions #pragma warning(disable : 26477) // Use 'nullptr' rather than 0 or NULL @@ -15,6 +16,13 @@ TRACELOGGING_DEFINE_PROVIDER(g_hConsoleVirtTermParserEventTraceProvider, // {c9ba2a84-d3ca-5e19-2bd6-776a0910cb9d} (0xc9ba2a84, 0xd3ca, 0x5e19, 0x2b, 0xd6, 0x77, 0x6a, 0x09, 0x10, 0xcb, 0x9d)); +static const auto cleanup = []() noexcept { + TraceLoggingRegister(g_hConsoleVirtTermParserEventTraceProvider); + return wil::scope_exit([]() noexcept { + TraceLoggingUnregister(g_hConsoleVirtTermParserEventTraceProvider); + }); +}(); + void ParserTracing::TraceStateChange(_In_z_ const wchar_t* name) const noexcept { TraceLoggingWrite(g_hConsoleVirtTermParserEventTraceProvider, From 5d85eb3e24f245bc3fb290ee91519dbacdd8d97f Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 16 Dec 2023 00:31:48 +0100 Subject: [PATCH 050/603] COOKED_READ: A minor cleanup (#16463) This is just a minor, unimportant cleanup to remove code duplication in `_flushBuffer`, which called `SetCursorPosition` twice each time the cursor position changed. --- src/host/readDataCooked.cpp | 21 ++++++++++++--------- src/host/readDataCooked.hpp | 1 + 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/host/readDataCooked.cpp b/src/host/readDataCooked.cpp index 4869e2ad91a..a05bed8a46a 100644 --- a/src/host/readDataCooked.cpp +++ b/src/host/readDataCooked.cpp @@ -845,15 +845,12 @@ void COOKED_READ_DATA::_flushBuffer() // If the contents of _buffer became shorter we'll have to erase the previously printed contents. _erase(eraseDistance); - _offsetCursorPosition(-eraseDistance - distanceAfterCursor); + // Using the *Always() variant ensures that we reset the blinking timer, etc., even if the cursor didn't move. + _offsetCursorPositionAlways(-eraseDistance - distanceAfterCursor); _buffer.MarkAsClean(); _distanceCursor = distanceBeforeCursor; _distanceEnd = distanceEnd; - - const auto pos = _screenInfo.GetTextBuffer().GetCursor().GetPosition(); - _screenInfo.MakeCursorVisible(pos); - std::ignore = _screenInfo.SetCursorPosition(pos, true); } // This is just a small helper to fill the next N cells starting at the current cursor position with whitespace. @@ -1045,15 +1042,21 @@ til::point COOKED_READ_DATA::_offsetPosition(til::point pos, ptrdiff_t distance) }; } -// This moves the cursor `distance`-many cells back up in the buffer. -// It's intended to be used in combination with _writeChars. +// See _offsetCursorPositionAlways(). This wrapper is just here to avoid doing +// expensive cursor movements when there's nothing to move. A no-op wrapper. void COOKED_READ_DATA::_offsetCursorPosition(ptrdiff_t distance) const { - if (distance == 0) + if (distance != 0) { - return; + _offsetCursorPositionAlways(distance); } +} +// This moves the cursor `distance`-many cells around in the buffer. +// It's intended to be used in combination with _writeChars. +// Usually you should use _offsetCursorPosition() to no-op distance==0. +void COOKED_READ_DATA::_offsetCursorPositionAlways(ptrdiff_t distance) const +{ const auto& textBuffer = _screenInfo.GetTextBuffer(); const auto& cursor = textBuffer.GetCursor(); const auto pos = _offsetPosition(cursor.GetPosition(), distance); diff --git a/src/host/readDataCooked.hpp b/src/host/readDataCooked.hpp index 9502eaa5c2d..035c3c96679 100644 --- a/src/host/readDataCooked.hpp +++ b/src/host/readDataCooked.hpp @@ -160,6 +160,7 @@ class COOKED_READ_DATA final : public ReadData ptrdiff_t _writeCharsUnprocessed(const std::wstring_view& text) const; til::point _offsetPosition(til::point pos, ptrdiff_t distance) const; void _offsetCursorPosition(ptrdiff_t distance) const; + void _offsetCursorPositionAlways(ptrdiff_t distance) const; til::CoordType _getColumnAtRelativeCursorPosition(ptrdiff_t distance) const; void _popupPush(PopupKind kind); From 91e97c169ee30adb41ac74f0dbb9b8e0e267d1a4 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 15 Dec 2023 20:17:42 +0100 Subject: [PATCH 051/603] Increase VtInputThread buffer size (#16470) This makes 3 improvements: * 16x larger input buffer size improves behavior when pasting clipboard contents while the win32-input-mode is enabled, as each input character is roughly 15-20x longer after encoding. * Translate UTF8 to UTF16 outside of the console lock. * Preserve the UTF16 buffer between reads for less mallocs. (cherry picked from commit 171a21ad48eca9f57a3ae5692fe9a5c64e9ad276) Service-Card-Id: 91347494 Service-Version: 1.19 --- src/host/VtInputThread.cpp | 15 ++++++++------- src/host/VtInputThread.hpp | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/host/VtInputThread.cpp b/src/host/VtInputThread.cpp index 4a4b773cb02..b9b2aefb0aa 100644 --- a/src/host/VtInputThread.cpp +++ b/src/host/VtInputThread.cpp @@ -69,7 +69,7 @@ DWORD WINAPI VtInputThread::StaticVtInputThreadProc(_In_ LPVOID lpParameter) // - true if you should continue reading bool VtInputThread::DoReadInput() { - char buffer[256]; + char buffer[4096]; DWORD dwRead = 0; const auto ok = ReadFile(_hFile.get(), buffer, ARRAYSIZE(buffer), &dwRead, nullptr); @@ -89,6 +89,12 @@ bool VtInputThread::DoReadInput() return false; } + // If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it. + if (FAILED_LOG(til::u8u16({ buffer, gsl::narrow_cast(dwRead) }, _wstr, _u8State))) + { + return true; + } + try { // Make sure to call the GLOBAL Lock/Unlock, not the gci's lock/unlock. @@ -99,12 +105,7 @@ bool VtInputThread::DoReadInput() LockConsole(); const auto unlock = wil::scope_exit([&] { UnlockConsole(); }); - std::wstring wstr; - // If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it. - if (SUCCEEDED_LOG(til::u8u16({ buffer, gsl::narrow_cast(dwRead) }, wstr, _u8State))) - { - _pInputStateMachine->ProcessString(wstr); - } + _pInputStateMachine->ProcessString(_wstr); } CATCH_LOG(); diff --git a/src/host/VtInputThread.hpp b/src/host/VtInputThread.hpp index 7652a53887f..d058c425bc4 100644 --- a/src/host/VtInputThread.hpp +++ b/src/host/VtInputThread.hpp @@ -39,5 +39,6 @@ namespace Microsoft::Console std::unique_ptr _pInputStateMachine; til::u8state _u8State; + std::wstring _wstr; }; } From bc18348967887ad0412f5eff93d089430cfda679 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 16 Dec 2023 00:50:06 +0100 Subject: [PATCH 052/603] Fix parsing of chunked win32-input-mode sequences (#16466) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even with the previous fixes we still randomly encounter win32- input-mode sequences that are broken up in exactly such a way that e.g. lone escape keys are encounters. Those for instance clear the current prompt. The remaining parts of the sequence are then visible. This changeset fixes the issue by skipping the entire force-to-ground code whenever we saw at least 1 win32-input-mode sequence. Related to #16343 ## Validation Steps Performed * Host a ConPTY inside ConPTY (= double the trouble) with cmd.exe * Paste random amounts of text * In the old code spurious `[..._` strings are seen * In the new code they're consistently gone ✅ --- src/terminal/parser/IStateMachineEngine.hpp | 2 ++ src/terminal/parser/InputStateMachineEngine.cpp | 6 ++++++ src/terminal/parser/InputStateMachineEngine.hpp | 2 ++ src/terminal/parser/OutputStateMachineEngine.cpp | 5 +++++ src/terminal/parser/OutputStateMachineEngine.hpp | 2 ++ src/terminal/parser/stateMachine.cpp | 8 +++++++- src/terminal/parser/ut_parser/StateMachineTest.cpp | 5 +++++ 7 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/terminal/parser/IStateMachineEngine.hpp b/src/terminal/parser/IStateMachineEngine.hpp index 371ba96c1d5..3f0356ebb84 100644 --- a/src/terminal/parser/IStateMachineEngine.hpp +++ b/src/terminal/parser/IStateMachineEngine.hpp @@ -28,6 +28,8 @@ namespace Microsoft::Console::VirtualTerminal IStateMachineEngine& operator=(const IStateMachineEngine&) = default; IStateMachineEngine& operator=(IStateMachineEngine&&) = default; + virtual bool EncounteredWin32InputModeSequence() const noexcept = 0; + virtual bool ActionExecute(const wchar_t wch) = 0; virtual bool ActionExecuteFromEscape(const wchar_t wch) = 0; virtual bool ActionPrint(const wchar_t wch) = 0; diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 7ea26ce9c84..cdc270c1858 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -102,6 +102,11 @@ InputStateMachineEngine::InputStateMachineEngine(std::unique_ptrWriteCtrlKey(key); + _encounteredWin32InputModeSequence = true; break; } default: diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index 74af20a9b39..b0687f6a0b3 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -132,6 +132,7 @@ namespace Microsoft::Console::VirtualTerminal InputStateMachineEngine(std::unique_ptr pDispatch, const bool lookingForDSR); + bool EncounteredWin32InputModeSequence() const noexcept override; void SetLookingForDSR(const bool looking) noexcept; bool ActionExecute(const wchar_t wch) override; @@ -167,6 +168,7 @@ namespace Microsoft::Console::VirtualTerminal const std::unique_ptr _pDispatch; std::function _pfnFlushToInputQueue; bool _lookingForDSR; + bool _encounteredWin32InputModeSequence = false; DWORD _mouseButtonState = 0; std::chrono::milliseconds _doubleClickTime; std::optional _lastMouseClickPos{}; diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index e744c95ad08..68a45916df4 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -23,6 +23,11 @@ OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr pDispatch); + bool EncounteredWin32InputModeSequence() const noexcept override; + bool ActionExecute(const wchar_t wch) override; bool ActionExecuteFromEscape(const wchar_t wch) override; diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index 643409afda5..88727ad6ec2 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -2144,9 +2144,15 @@ void StateMachine::ProcessString(const std::wstring_view string) // which breaks up many of our longer sequences, like our Win32InputMode ones. // // As a heuristic, this code specifically checks for a trailing Esc or Alt+key. + // If we encountered a win32-input-mode sequence before, we know that our \x1b[?9001h + // request to enable them was successful. While a client may still send \x1b{some char} + // intentionally, it's far more likely now that we're looking at a broken up sequence. + // The most common win32-input-mode is ConPTY itself after all, and we never emit + // \x1b{some char} once it's enabled. if (_isEngineForInput) { - if (run.size() <= 2 && run.front() == L'\x1b') + const auto win32 = _engine->EncounteredWin32InputModeSequence(); + if (!win32 && run.size() <= 2 && run.front() == L'\x1b') { _EnterGround(); if (run.size() == 1) diff --git a/src/terminal/parser/ut_parser/StateMachineTest.cpp b/src/terminal/parser/ut_parser/StateMachineTest.cpp index 4893964c249..d7e49537207 100644 --- a/src/terminal/parser/ut_parser/StateMachineTest.cpp +++ b/src/terminal/parser/ut_parser/StateMachineTest.cpp @@ -40,6 +40,11 @@ class Microsoft::Console::VirtualTerminal::TestStateMachineEngine : public IStat dcsDataString.clear(); } + bool EncounteredWin32InputModeSequence() const noexcept override + { + return false; + } + bool ActionExecute(const wchar_t wch) override { executed += wch; From 63c3573a13cc96bfa3d655f0f1898ae250e2fe35 Mon Sep 17 00:00:00 2001 From: js324 Date: Fri, 15 Dec 2023 18:50:45 -0500 Subject: [PATCH 053/603] Wrap word-wise selection when the word is actually wrapped (#16441) Added wrapping to highlighted selection when selecting a word, added tests for it ## Detailed Description of the Pull Request / Additional comments - Modified GetWordStart and GetWordEnd and their helpers to no longer be bounded by the right and left viewport ranges - Kept same functionality (does not wrap) when selecting wrapped whitespace - Added tests to TextBufferTests.cpp to include cases of wrapping text ## Validation Steps Performed - Ran locally and verified selection works properly - Tests passed locally Closes #4009 --- ...cdb9b77d6827c0202f51acd4205b017015bfff.txt | 5 + src/buffer/out/textBuffer.cpp | 63 ++++++----- src/host/ut_host/TextBufferTests.cpp | 100 ++++++++++++++++-- 3 files changed, 128 insertions(+), 40 deletions(-) create mode 100644 .github/actions/spelling/expect/04cdb9b77d6827c0202f51acd4205b017015bfff.txt diff --git a/.github/actions/spelling/expect/04cdb9b77d6827c0202f51acd4205b017015bfff.txt b/.github/actions/spelling/expect/04cdb9b77d6827c0202f51acd4205b017015bfff.txt new file mode 100644 index 00000000000..f117f5081da --- /dev/null +++ b/.github/actions/spelling/expect/04cdb9b77d6827c0202f51acd4205b017015bfff.txt @@ -0,0 +1,5 @@ +EOB +swrapped +wordi +wordiswrapped +wrappe diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 52a5c6e3b72..528d8fbf4d3 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1329,36 +1329,32 @@ til::point TextBuffer::_GetWordStartForAccessibility(const til::point target, co { auto result = target; const auto bufferSize = GetSize(); - auto stayAtOrigin = false; // ignore left boundary. Continue until readable text found while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar) { - if (!bufferSize.DecrementInBounds(result)) + if (result == bufferSize.Origin()) { - // first char in buffer is a DelimiterChar or ControlChar - // we can't move any further back - stayAtOrigin = true; - break; + //looped around and hit origin (no word between origin and target) + return result; } + bufferSize.DecrementInBounds(result); } // make sure we expand to the left boundary or the beginning of the word while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar) { - if (!bufferSize.DecrementInBounds(result)) + if (result == bufferSize.Origin()) { // first char in buffer is a RegularChar // we can't move any further back - break; + return result; } + bufferSize.DecrementInBounds(result); } - // move off of delimiter and onto word start - if (!stayAtOrigin && _GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar) - { - bufferSize.IncrementInBounds(result); - } + // move off of delimiter + bufferSize.IncrementInBounds(result); return result; } @@ -1376,10 +1372,16 @@ til::point TextBuffer::_GetWordStartForSelection(const til::point target, const const auto bufferSize = GetSize(); const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters); + const bool isControlChar = initialDelimiter == DelimiterClass::ControlChar; // expand left until we hit the left boundary or a different delimiter class - while (result.x > bufferSize.Left() && (_GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter)) + while (result != bufferSize.Origin() && _GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter) { + //prevent selection wrapping on whitespace selection + if (isControlChar && result.x == bufferSize.Left()) + { + break; + } bufferSize.DecrementInBounds(result); } @@ -1457,25 +1459,21 @@ til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, cons } else { - auto iter{ GetCellDataAt(result, bufferSize) }; - while (iter && iter.Pos() != limit && _GetDelimiterClassAt(iter.Pos(), wordDelimiters) == DelimiterClass::RegularChar) + while (result != limit && result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar) { // Iterate through readable text - ++iter; + bufferSize.IncrementInBounds(result); } - while (iter && iter.Pos() != limit && _GetDelimiterClassAt(iter.Pos(), wordDelimiters) != DelimiterClass::RegularChar) + while (result != limit && result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar) { // expand to the beginning of the NEXT word - ++iter; + bufferSize.IncrementInBounds(result); } - result = iter.Pos(); - - // Special case: we tried to move one past the end of the buffer, - // but iter prevented that (because that pos doesn't exist). + // Special case: we tried to move one past the end of the buffer // Manually increment onto the EndExclusive point. - if (!iter) + if (result == bufferSize.BottomRightInclusive()) { bufferSize.IncrementInBounds(result, true); } @@ -1495,19 +1493,18 @@ til::point TextBuffer::_GetWordEndForSelection(const til::point target, const st { const auto bufferSize = GetSize(); - // can't expand right - if (target.x == bufferSize.RightInclusive()) - { - return target; - } - auto result = target; const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters); + const bool isControlChar = initialDelimiter == DelimiterClass::ControlChar; - // expand right until we hit the right boundary or a different delimiter class - while (result.x < bufferSize.RightInclusive() && (_GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter)) + // expand right until we hit the right boundary as a ControlChar or a different delimiter class + while (result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter) { - bufferSize.IncrementInBounds(result); + if (isControlChar && result.x == bufferSize.RightInclusive()) + { + break; + } + bufferSize.IncrementInBoundsCircular(result); } if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter) diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index d1c7395635f..9b330e91d86 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -2118,10 +2118,11 @@ void TextBufferTests::TestAppendRTFText() void TextBufferTests::WriteLinesToBuffer(const std::vector& text, TextBuffer& buffer) { const auto bufferSize = buffer.GetSize(); - + int rowsWrapped{}; for (size_t row = 0; row < text.size(); ++row) { auto line = text[row]; + if (!line.empty()) { // TODO GH#780: writing up to (but not past) the end of the line @@ -2133,7 +2134,12 @@ void TextBufferTests::WriteLinesToBuffer(const std::vector& text, } OutputCellIterator iter{ line }; - buffer.Write(iter, { 0, gsl::narrow(row) }, wrap); + buffer.Write(iter, { 0, gsl::narrow(row + rowsWrapped) }, wrap); + //prevent bug that overwrites wrapped rows + if (line.size() > static_cast(bufferSize.RightExclusive())) + { + rowsWrapped += static_cast(line.size()) / bufferSize.RightExclusive(); + } } } } @@ -2245,6 +2251,88 @@ void TextBufferTests::GetWordBoundaries() const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled; VERIFY_ARE_EQUAL(expected, result); } + + _buffer->Reset(); + _buffer->ResizeTraditional({ 10, 5 }); + const std::vector secondText = { L"this wordiswrapped", + L"spaces wrapped reachEOB" }; + //Buffer looks like: + // this wordi + // swrapped + // spaces + // wrappe + // d reachEOB + WriteLinesToBuffer(secondText, *_buffer); + testData = { + { { 0, 0 }, { { 0, 0 }, { 0, 0 } } }, + { { 1, 0 }, { { 0, 0 }, { 0, 0 } } }, + { { 4, 0 }, { { 4, 0 }, { 0, 0 } } }, + { { 5, 0 }, { { 5, 0 }, { 5, 0 } } }, + { { 7, 0 }, { { 5, 0 }, { 5, 0 } } }, + + { { 4, 1 }, { { 5, 0 }, { 5, 0 } } }, + { { 7, 1 }, { { 5, 0 }, { 5, 0 } } }, + { { 9, 1 }, { { 8, 1 }, { 5, 0 } } }, + + { { 0, 2 }, { { 0, 2 }, { 0, 2 } } }, + { { 7, 2 }, { { 6, 2 }, { 0, 2 } } }, + + { { 1, 3 }, { { 0, 3 }, { 0, 2 } } }, + { { 4, 3 }, { { 4, 3 }, { 4, 3 } } }, + { { 8, 3 }, { { 4, 3 }, { 4, 3 } } }, + + { { 0, 4 }, { { 4, 3 }, { 4, 3 } } }, + { { 1, 4 }, { { 1, 4 }, { 4, 3 } } }, + { { 9, 4 }, { { 2, 4 }, { 2, 4 } } }, + }; + for (const auto& test : testData) + { + Log::Comment(NoThrowString().Format(L"Testing til::point (%hd, %hd)", test.startPos.x, test.startPos.y)); + const auto result = _buffer->GetWordStart(test.startPos, delimiters, accessibilityMode); + const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled; + VERIFY_ARE_EQUAL(expected, result); + } + + //GetWordEnd for Wrapping Text + //Buffer looks like: + // this wordi + // swrapped + // spaces + // wrappe + // d reachEOB + testData = { + // tests for first line of text + { { 0, 0 }, { { 3, 0 }, { 5, 0 } } }, + { { 1, 0 }, { { 3, 0 }, { 5, 0 } } }, + { { 4, 0 }, { { 4, 0 }, { 5, 0 } } }, + { { 5, 0 }, { { 7, 1 }, { 0, 2 } } }, + { { 7, 0 }, { { 7, 1 }, { 0, 2 } } }, + + { { 4, 1 }, { { 7, 1 }, { 0, 2 } } }, + { { 7, 1 }, { { 7, 1 }, { 0, 2 } } }, + { { 9, 1 }, { { 9, 1 }, { 0, 2 } } }, + + { { 0, 2 }, { { 5, 2 }, { 4, 3 } } }, + { { 7, 2 }, { { 9, 2 }, { 4, 3 } } }, + + { { 1, 3 }, { { 3, 3 }, { 4, 3 } } }, + { { 4, 3 }, { { 0, 4 }, { 2, 4 } } }, + { { 8, 3 }, { { 0, 4 }, { 2, 4 } } }, + + { { 0, 4 }, { { 0, 4 }, { 2, 4 } } }, + { { 1, 4 }, { { 1, 4 }, { 2, 4 } } }, + { { 4, 4 }, { { 9, 4 }, { 0, 5 } } }, + { { 9, 4 }, { { 9, 4 }, { 0, 5 } } }, + }; + // clang-format on + + for (const auto& test : testData) + { + Log::Comment(NoThrowString().Format(L"TestEnd til::point (%hd, %hd)", test.startPos.x, test.startPos.y)); + auto result = _buffer->GetWordEnd(test.startPos, delimiters, accessibilityMode); + const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled; + VERIFY_ARE_EQUAL(expected, result); + } } void TextBufferTests::MoveByWord() @@ -2571,9 +2659,7 @@ void TextBufferTests::GetText() // Setup: Write lines of text to the buffer const std::vector bufferText = { L"1234567", - L"", - L" 345", - L"123 ", + L" 345123 ", L"" }; WriteLinesToBuffer(bufferText, *_buffer); // buffer should look like this: @@ -2653,7 +2739,7 @@ void TextBufferTests::GetText() Log::Comment(L"Standard Copy to Clipboard"); expectedText += L"12345"; expectedText += L"67\r\n"; - expectedText += L" 345\r\n"; + expectedText += L" 345"; expectedText += L"123 \r\n"; } else @@ -2661,7 +2747,7 @@ void TextBufferTests::GetText() Log::Comment(L"UI Automation"); expectedText += L"12345"; expectedText += L"67 \r\n"; - expectedText += L" 345\r\n"; + expectedText += L" 345"; expectedText += L"123 "; expectedText += L" \r\n"; expectedText += L" "; From 2546c02adb7e596970d7be531aa5e178e9a71b91 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 16 Dec 2023 00:50:06 +0100 Subject: [PATCH 054/603] Fix parsing of chunked win32-input-mode sequences (#16466) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even with the previous fixes we still randomly encounter win32- input-mode sequences that are broken up in exactly such a way that e.g. lone escape keys are encounters. Those for instance clear the current prompt. The remaining parts of the sequence are then visible. This changeset fixes the issue by skipping the entire force-to-ground code whenever we saw at least 1 win32-input-mode sequence. Related to #16343 ## Validation Steps Performed * Host a ConPTY inside ConPTY (= double the trouble) with cmd.exe * Paste random amounts of text * In the old code spurious `[..._` strings are seen * In the new code they're consistently gone ✅ (cherry picked from commit bc18348967887ad0412f5eff93d089430cfda679) Service-Card-Id: 91337332 Service-Version: 1.19 --- src/terminal/parser/IStateMachineEngine.hpp | 2 ++ src/terminal/parser/InputStateMachineEngine.cpp | 6 ++++++ src/terminal/parser/InputStateMachineEngine.hpp | 2 ++ src/terminal/parser/OutputStateMachineEngine.cpp | 5 +++++ src/terminal/parser/OutputStateMachineEngine.hpp | 2 ++ src/terminal/parser/stateMachine.cpp | 8 +++++++- src/terminal/parser/ut_parser/StateMachineTest.cpp | 5 +++++ 7 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/terminal/parser/IStateMachineEngine.hpp b/src/terminal/parser/IStateMachineEngine.hpp index 371ba96c1d5..3f0356ebb84 100644 --- a/src/terminal/parser/IStateMachineEngine.hpp +++ b/src/terminal/parser/IStateMachineEngine.hpp @@ -28,6 +28,8 @@ namespace Microsoft::Console::VirtualTerminal IStateMachineEngine& operator=(const IStateMachineEngine&) = default; IStateMachineEngine& operator=(IStateMachineEngine&&) = default; + virtual bool EncounteredWin32InputModeSequence() const noexcept = 0; + virtual bool ActionExecute(const wchar_t wch) = 0; virtual bool ActionExecuteFromEscape(const wchar_t wch) = 0; virtual bool ActionPrint(const wchar_t wch) = 0; diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 7ea26ce9c84..cdc270c1858 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -102,6 +102,11 @@ InputStateMachineEngine::InputStateMachineEngine(std::unique_ptrWriteCtrlKey(key); + _encounteredWin32InputModeSequence = true; break; } default: diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index 74af20a9b39..b0687f6a0b3 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -132,6 +132,7 @@ namespace Microsoft::Console::VirtualTerminal InputStateMachineEngine(std::unique_ptr pDispatch, const bool lookingForDSR); + bool EncounteredWin32InputModeSequence() const noexcept override; void SetLookingForDSR(const bool looking) noexcept; bool ActionExecute(const wchar_t wch) override; @@ -167,6 +168,7 @@ namespace Microsoft::Console::VirtualTerminal const std::unique_ptr _pDispatch; std::function _pfnFlushToInputQueue; bool _lookingForDSR; + bool _encounteredWin32InputModeSequence = false; DWORD _mouseButtonState = 0; std::chrono::milliseconds _doubleClickTime; std::optional _lastMouseClickPos{}; diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index e744c95ad08..68a45916df4 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -23,6 +23,11 @@ OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr pDispatch); + bool EncounteredWin32InputModeSequence() const noexcept override; + bool ActionExecute(const wchar_t wch) override; bool ActionExecuteFromEscape(const wchar_t wch) override; diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index 643409afda5..88727ad6ec2 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -2144,9 +2144,15 @@ void StateMachine::ProcessString(const std::wstring_view string) // which breaks up many of our longer sequences, like our Win32InputMode ones. // // As a heuristic, this code specifically checks for a trailing Esc or Alt+key. + // If we encountered a win32-input-mode sequence before, we know that our \x1b[?9001h + // request to enable them was successful. While a client may still send \x1b{some char} + // intentionally, it's far more likely now that we're looking at a broken up sequence. + // The most common win32-input-mode is ConPTY itself after all, and we never emit + // \x1b{some char} once it's enabled. if (_isEngineForInput) { - if (run.size() <= 2 && run.front() == L'\x1b') + const auto win32 = _engine->EncounteredWin32InputModeSequence(); + if (!win32 && run.size() <= 2 && run.front() == L'\x1b') { _EnterGround(); if (run.size() == 1) diff --git a/src/terminal/parser/ut_parser/StateMachineTest.cpp b/src/terminal/parser/ut_parser/StateMachineTest.cpp index 4893964c249..d7e49537207 100644 --- a/src/terminal/parser/ut_parser/StateMachineTest.cpp +++ b/src/terminal/parser/ut_parser/StateMachineTest.cpp @@ -40,6 +40,11 @@ class Microsoft::Console::VirtualTerminal::TestStateMachineEngine : public IStat dcsDataString.clear(); } + bool EncounteredWin32InputModeSequence() const noexcept override + { + return false; + } + bool ActionExecute(const wchar_t wch) override { executed += wch; From 322fdd027c428dafe3e8f43f0f37741577bb10ea Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Fri, 10 Nov 2023 06:17:07 +0530 Subject: [PATCH 055/603] Support rendering of underline style and color (#16097) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for underline style and color in the renderer > [!IMPORTANT] > The PR adds underline style and color feature to AtlasEngine (WT) and GDIRenderer (Conhost) only. After the underline style and color feature addition to Conpty, this PR takes it further and add support for rendering them to the screen! Out of five underline styles, we already supported rendering for 3 of those types (Singly, Doubly, Dotted) in some form in our (Atlas) renderer. The PR adds the remaining types, namely, Dashed and Curly underlines support to the renderer. - All renderer engines now receive both gridline and underline color, and the latter is used for drawing the underlines. **When no underline color is set, we use the foreground color.** - Curly underline is rendered using `sin()` within the pixel shader. - To draw underlines for DECDWL and DECDHL, we send the line rendition scale within `QuadInstance`'s texcoord attribute. - In GDI renderer, dashed and dotted underline is drawn using `HPEN` with a desired style. Curly line is a cubic Bezier that draws one wave per cell. ## PR Checklist - ✅ Set the underline color to underlines only, without affecting the gridline color. - ❌ Port to DX renderer. (Not planned as DX renderer soon to be replaced by **AtlasEngine**) - ✅ Port underline coloring and style to GDI renderer (Conhost). - ✅ Wide/Tall `CurlyUnderline` variant for `DECDWL`/`DECDHL`. Closes #7228 (cherry picked from commit e268c1c952f21c1b41ceac6ace7778d2b78620bf) Service-Card-Id: 91349180 Service-Version: 1.19 --- .github/actions/spelling/expect/expect.txt | 4 + .../UnitTests_TerminalCore/ScrollTest.cpp | 2 +- src/interactivity/onecore/BgfxEngine.cpp | 7 +- src/interactivity/onecore/BgfxEngine.hpp | 2 +- src/renderer/atlas/AtlasEngine.cpp | 7 +- src/renderer/atlas/AtlasEngine.h | 2 +- src/renderer/atlas/BackendD2D.cpp | 28 +++--- src/renderer/atlas/BackendD3D.cpp | 92 +++++++++++++----- src/renderer/atlas/BackendD3D.h | 27 +++--- src/renderer/atlas/common.h | 4 +- src/renderer/atlas/shader_common.hlsl | 5 +- src/renderer/atlas/shader_ps.hlsl | 27 +++++- src/renderer/atlas/shader_vs.hlsl | 1 + src/renderer/base/RenderSettings.cpp | 41 ++++++++ src/renderer/base/renderer.cpp | 17 +++- src/renderer/dx/DxRenderer.cpp | 40 ++++---- src/renderer/dx/DxRenderer.hpp | 2 +- src/renderer/gdi/gdirenderer.hpp | 4 +- src/renderer/gdi/paint.cpp | 95 ++++++++++++++----- src/renderer/gdi/state.cpp | 34 +++++++ src/renderer/inc/IRenderEngine.hpp | 5 +- src/renderer/inc/RenderSettings.hpp | 1 + src/renderer/uia/UiaRenderer.cpp | 10 +- src/renderer/uia/UiaRenderer.hpp | 2 +- src/renderer/vt/paint.cpp | 6 +- src/renderer/vt/vtrenderer.hpp | 2 +- src/renderer/wddmcon/WddmConRenderer.cpp | 7 +- src/renderer/wddmcon/WddmConRenderer.hpp | 2 +- src/types/UiaTextRangeBase.cpp | 30 ++++-- 29 files changed, 376 insertions(+), 130 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 3ea1b7e958f..454a7bfb6f6 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -344,6 +344,7 @@ CTRLVOLUME Ctxt CUF cupxy +curlyline CURRENTFONT currentmode CURRENTPAGE @@ -579,6 +580,7 @@ elems emacs EMPTYBOX enabledelayedexpansion +ENDCAP endptr endregion ENTIREBUFFER @@ -827,6 +829,7 @@ hostlib HPA hpcon HPCON +hpen hpj HPR HProvider @@ -1011,6 +1014,7 @@ LOBYTE localappdata locsrc Loewen +LOGBRUSH LOGFONT LOGFONTA LOGFONTW diff --git a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp index 4b456e7cd5b..d5baebadf26 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp @@ -57,7 +57,7 @@ namespace HRESULT InvalidateCircling(_Out_ bool* /*pForcePaint*/) noexcept { return S_OK; } HRESULT PaintBackground() noexcept { return S_OK; } HRESULT PaintBufferLine(std::span /*clusters*/, til::point /*coord*/, bool /*fTrimLeft*/, bool /*lineWrapped*/) noexcept { return S_OK; } - HRESULT PaintBufferGridLines(GridLineSet /*lines*/, COLORREF /*color*/, size_t /*cchLine*/, til::point /*coordTarget*/) noexcept { return S_OK; } + HRESULT PaintBufferGridLines(GridLineSet /*lines*/, COLORREF /*gridlineColor*/, COLORREF /*underlineColor*/, size_t /*cchLine*/, til::point /*coordTarget*/) noexcept { return S_OK; } HRESULT PaintSelection(const til::rect& /*rect*/) noexcept { return S_OK; } HRESULT PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; } HRESULT UpdateDrawingBrushes(const TextAttribute& /*textAttributes*/, const RenderSettings& /*renderSettings*/, gsl::not_null /*pData*/, bool /*usingSoftFont*/, bool /*isSettingDefaultBrushes*/) noexcept { return S_OK; } diff --git a/src/interactivity/onecore/BgfxEngine.cpp b/src/interactivity/onecore/BgfxEngine.cpp index ce6b903ffd1..d508603d5f9 100644 --- a/src/interactivity/onecore/BgfxEngine.cpp +++ b/src/interactivity/onecore/BgfxEngine.cpp @@ -147,9 +147,10 @@ CATCH_RETURN() CATCH_RETURN() } -[[nodiscard]] HRESULT BgfxEngine::PaintBufferGridLines(GridLineSet const /*lines*/, - COLORREF const /*color*/, - size_t const /*cchLine*/, +[[nodiscard]] HRESULT BgfxEngine::PaintBufferGridLines(const GridLineSet /*lines*/, + const COLORREF /*gridlineColor*/, + const COLORREF /*underlineColor*/, + const size_t /*cchLine*/, const til::point /*coordTarget*/) noexcept { return S_OK; diff --git a/src/interactivity/onecore/BgfxEngine.hpp b/src/interactivity/onecore/BgfxEngine.hpp index 31fa49c8522..e9759ce0306 100644 --- a/src/interactivity/onecore/BgfxEngine.hpp +++ b/src/interactivity/onecore/BgfxEngine.hpp @@ -51,7 +51,7 @@ namespace Microsoft::Console::Render const til::point coord, const bool trimLeft, const bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet const lines, COLORREF const color, size_t const cchLine, til::point const coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index e46bb250835..7438b93543e 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -375,7 +375,7 @@ try } CATCH_RETURN() -[[nodiscard]] HRESULT AtlasEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF color, const size_t cchLine, const til::point coordTarget) noexcept +[[nodiscard]] HRESULT AtlasEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept try { const auto shift = gsl::narrow_cast(_api.lineRendition != LineRendition::SingleWidth); @@ -383,8 +383,9 @@ try const auto y = gsl::narrow_cast(clamp(coordTarget.y, 0, _p.s->viewportCellCount.y)); const auto from = gsl::narrow_cast(clamp(x << shift, 0, _p.s->viewportCellCount.x - 1)); const auto to = gsl::narrow_cast(clamp((x + cchLine) << shift, from, _p.s->viewportCellCount.x)); - const auto fg = gsl::narrow_cast(color) | 0xff000000; - _p.rows[y]->gridLineRanges.emplace_back(lines, fg, from, to); + const auto glColor = gsl::narrow_cast(gridlineColor) | 0xff000000; + const auto ulColor = gsl::narrow_cast(underlineColor) | 0xff000000; + _p.rows[y]->gridLineRanges.emplace_back(lines, glColor, ulColor, from, to); return S_OK; } CATCH_RETURN() diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index b135a8989f8..4ec09a9e931 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -43,7 +43,7 @@ namespace Microsoft::Console::Render::Atlas [[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept override; [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept override; diff --git a/src/renderer/atlas/BackendD2D.cpp b/src/renderer/atlas/BackendD2D.cpp index 301738954aa..ea4ea0923eb 100644 --- a/src/renderer/atlas/BackendD2D.cpp +++ b/src/renderer/atlas/BackendD2D.cpp @@ -409,7 +409,7 @@ void BackendD2D::_drawGridlineRow(const RenderingPayload& p, const ShapedRow* ro D2D1_POINT_2F point0{ 0, static_cast(textCellCenter) }; D2D1_POINT_2F point1{ 0, static_cast(textCellCenter + cellSize.y) }; - const auto brush = _brushWithColor(r.color); + const auto brush = _brushWithColor(r.gridlineColor); const f32 w = pos.height; const f32 hw = w * 0.5f; @@ -421,11 +421,11 @@ void BackendD2D::_drawGridlineRow(const RenderingPayload& p, const ShapedRow* ro _renderTarget->DrawLine(point0, point1, brush, w, nullptr); } }; - const auto appendHorizontalLine = [&](const GridLineRange& r, FontDecorationPosition pos, ID2D1StrokeStyle* strokeStyle) { + const auto appendHorizontalLine = [&](const GridLineRange& r, FontDecorationPosition pos, ID2D1StrokeStyle* strokeStyle, const u32 color) { const auto from = r.from >> widthShift; const auto to = r.to >> widthShift; - const auto brush = _brushWithColor(r.color); + const auto brush = _brushWithColor(color); const f32 w = pos.height; const f32 centerY = textCellCenter + pos.position + w * 0.5f; const D2D1_POINT_2F point0{ static_cast(from * cellSize.x), centerY }; @@ -448,32 +448,32 @@ void BackendD2D::_drawGridlineRow(const RenderingPayload& p, const ShapedRow* ro } if (r.lines.test(GridLines::Top)) { - appendHorizontalLine(r, p.s->font->gridTop, nullptr); + appendHorizontalLine(r, p.s->font->gridTop, nullptr, r.gridlineColor); } if (r.lines.test(GridLines::Bottom)) { - appendHorizontalLine(r, p.s->font->gridBottom, nullptr); + appendHorizontalLine(r, p.s->font->gridBottom, nullptr, r.gridlineColor); + } + if (r.lines.test(GridLines::Strikethrough)) + { + appendHorizontalLine(r, p.s->font->strikethrough, nullptr, r.gridlineColor); } if (r.lines.test(GridLines::Underline)) { - appendHorizontalLine(r, p.s->font->underline, nullptr); + appendHorizontalLine(r, p.s->font->underline, nullptr, r.underlineColor); } - if (r.lines.test(GridLines::HyperlinkUnderline)) + else if (r.lines.any(GridLines::DottedUnderline, GridLines::HyperlinkUnderline)) { - appendHorizontalLine(r, p.s->font->underline, _dottedStrokeStyle.get()); + appendHorizontalLine(r, p.s->font->underline, _dottedStrokeStyle.get(), r.underlineColor); } - if (r.lines.test(GridLines::DoubleUnderline)) + else if (r.lines.test(GridLines::DoubleUnderline)) { for (const auto pos : p.s->font->doubleUnderline) { - appendHorizontalLine(r, pos, nullptr); + appendHorizontalLine(r, pos, nullptr, r.underlineColor); } } - if (r.lines.test(GridLines::Strikethrough)) - { - appendHorizontalLine(r, p.s->font->strikethrough, nullptr); - } } } diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 19334b3a9d4..41fefa6ea2b 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -90,7 +90,8 @@ BackendD3D::BackendD3D(const RenderingPayload& p) { static constexpr D3D11_INPUT_ELEMENT_DESC layout[]{ { "SV_Position", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "shadingType", 0, DXGI_FORMAT_R32_UINT, 1, offsetof(QuadInstance, shadingType), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, + { "shadingType", 0, DXGI_FORMAT_R16_UINT, 1, offsetof(QuadInstance, shadingType), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, + { "renditionScale", 0, DXGI_FORMAT_R8G8_UINT, 1, offsetof(QuadInstance, renditionScale), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, { "position", 0, DXGI_FORMAT_R16G16_SINT, 1, offsetof(QuadInstance, position), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, { "size", 0, DXGI_FORMAT_R16G16_UINT, 1, offsetof(QuadInstance, size), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, { "texcoord", 0, DXGI_FORMAT_R16G16_UINT, 1, offsetof(QuadInstance, texcoord), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, @@ -305,6 +306,37 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p) { const auto& font = *p.s->font; + // The max height of Curly line peak in `em` units. + const auto maxCurlyLinePeakHeightEm = 0.075f; + // We aim for atleast 1px height, but since we draw 1px smaller curly line, + // we aim for 2px height as a result. + const auto minCurlyLinePeakHeight = 2.0f; + + // Curlyline uses the gap between cell bottom and singly underline position + // as the height of the wave's peak. The baseline for curly-line is at the + // middle of singly underline. The gap could be too big, so we also apply + // a limit on the peak height. + const auto strokeHalfWidth = font.underline.height / 2.0f; + const auto underlineMidY = font.underline.position + strokeHalfWidth; + const auto cellBottomGap = font.cellSize.y - underlineMidY - strokeHalfWidth; + const auto maxCurlyLinePeakHeight = maxCurlyLinePeakHeightEm * font.fontSize; + auto curlyLinePeakHeight = std::min(cellBottomGap, maxCurlyLinePeakHeight); + + // When it's too small to be curly, make it straight. + if (curlyLinePeakHeight < minCurlyLinePeakHeight) + { + curlyLinePeakHeight = 0; + } + + // We draw a smaller curly line (-1px) to avoid clipping due to the rounding. + _curlyLineDrawPeakHeight = std::max(0.0f, curlyLinePeakHeight - 1.0f); + + const auto curlyUnderlinePos = font.underline.position - curlyLinePeakHeight; + const auto curlyUnderlineWidth = 2.0f * (curlyLinePeakHeight + strokeHalfWidth); + const auto curlyUnderlinePosU16 = gsl::narrow_cast(lrintf(curlyUnderlinePos)); + const auto curlyUnderlineWidthU16 = gsl::narrow_cast(lrintf(curlyUnderlineWidth)); + _curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 }; + DWrite_GetRenderParams(p.dwriteFactory.get(), &_gamma, &_cleartypeEnhancedContrast, &_grayscaleEnhancedContrast, _textRenderingParams.put()); // Clearing the atlas requires BeginDraw(), which is expensive. Defer this until we need Direct2D anyways. _fontChangedResetGlyphAtlas = true; @@ -543,6 +575,9 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const DWrite_GetGammaRatios(_gamma, data.gammaRatios); data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast; data.underlineWidth = p.s->font->underline.height; + data.curlyLineWaveFreq = 2.0f * 3.14f / p.s->font->cellSize.x; + data.curlyLinePeakHeight = _curlyLineDrawPeakHeight; + data.curlyLineCellOffset = p.s->font->underline.position + p.s->font->underline.height / 2.0f; p.deviceContext->UpdateSubresource(_psConstantBuffer.get(), 0, nullptr, &data, 0, 0); } } @@ -1024,7 +1059,7 @@ void BackendD3D::_drawText(RenderingPayload& p) goto drawGlyphRetry; } - if (glyphEntry.data.GetShadingType() != ShadingType::Default) + if (glyphEntry.data.shadingType != ShadingType::Default) { auto l = static_cast(lrintf((baselineX + row->glyphOffsets[x].advanceOffset) * scaleX)); auto t = static_cast(lrintf((baselineY - row->glyphOffsets[x].ascenderOffset) * scaleY)); @@ -1036,7 +1071,7 @@ void BackendD3D::_drawText(RenderingPayload& p) row->dirtyBottom = std::max(row->dirtyBottom, t + glyphEntry.data.size.y); _appendQuad() = { - .shadingType = glyphEntry.data.GetShadingType(), + .shadingType = glyphEntry.data.shadingType, .position = { static_cast(l), static_cast(t) }, .size = glyphEntry.data.size, .texcoord = glyphEntry.data.texcoord, @@ -1458,7 +1493,7 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const AtlasFontFaceEntryI const auto triggerRight = _ligatureOverhangTriggerRight * horizontalScale; const auto overlapSplit = rect.w >= p.s->font->cellSize.x && (bl <= triggerLeft || br >= triggerRight); - glyphEntry.data.shadingType = static_cast(isColorGlyph ? ShadingType::TextPassthrough : _textShadingType); + glyphEntry.data.shadingType = isColorGlyph ? ShadingType::TextPassthrough : _textShadingType; glyphEntry.data.overlapSplit = overlapSplit; glyphEntry.data.offset.x = bl; glyphEntry.data.offset.y = bt; @@ -1527,7 +1562,7 @@ bool BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const AtlasFontFa _drawSoftFontGlyphInBitmap(p, glyphEntry); _d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &dest, 1, interpolation, nullptr, nullptr); - glyphEntry.data.shadingType = static_cast(ShadingType::TextGrayscale); + glyphEntry.data.shadingType = ShadingType::TextGrayscale; glyphEntry.data.overlapSplit = 0; glyphEntry.data.offset.x = 0; glyphEntry.data.offset.y = -baseline; @@ -1631,11 +1666,11 @@ void BackendD3D::_splitDoubleHeightGlyph(const RenderingPayload& p, const AtlasF // double-height row. This effectively turns the other (unneeded) side into whitespace. if (!top.data.size.y) { - top.data.shadingType = static_cast(ShadingType::Default); + top.data.shadingType = ShadingType::Default; } if (!bottom.data.size.y) { - bottom.data.shadingType = static_cast(ShadingType::Default); + bottom.data.shadingType = ShadingType::Default; } } @@ -1647,8 +1682,6 @@ void BackendD3D::_drawGridlines(const RenderingPayload& p, u16 y) const auto verticalShift = static_cast(row->lineRendition >= LineRendition::DoubleHeightTop); const auto cellSize = p.s->font->cellSize; - const auto dottedLineType = horizontalShift ? ShadingType::DottedLineWide : ShadingType::DottedLine; - const auto rowTop = static_cast(cellSize.y * y); const auto rowBottom = static_cast(rowTop + cellSize.y); @@ -1675,11 +1708,11 @@ void BackendD3D::_drawGridlines(const RenderingPayload& p, u16 y) .shadingType = ShadingType::SolidLine, .position = { static_cast(posX), rowTop }, .size = { width, p.s->font->cellSize.y }, - .color = r.color, + .color = r.gridlineColor, }; } }; - const auto appendHorizontalLine = [&](const GridLineRange& r, FontDecorationPosition pos, ShadingType shadingType) { + const auto appendHorizontalLine = [&](const GridLineRange& r, FontDecorationPosition pos, ShadingType shadingType, const u32 color) { const auto offset = pos.position << verticalShift; const auto height = static_cast(pos.height << verticalShift); @@ -1695,9 +1728,10 @@ void BackendD3D::_drawGridlines(const RenderingPayload& p, u16 y) { _appendQuad() = { .shadingType = shadingType, + .renditionScale = { static_cast(1 << horizontalShift), static_cast(1 << verticalShift) }, .position = { left, static_cast(rt) }, .size = { width, static_cast(rb - rt) }, - .color = r.color, + .color = color, }; } }; @@ -1717,32 +1751,40 @@ void BackendD3D::_drawGridlines(const RenderingPayload& p, u16 y) } if (r.lines.test(GridLines::Top)) { - appendHorizontalLine(r, p.s->font->gridTop, ShadingType::SolidLine); + appendHorizontalLine(r, p.s->font->gridTop, ShadingType::SolidLine, r.gridlineColor); } if (r.lines.test(GridLines::Bottom)) { - appendHorizontalLine(r, p.s->font->gridBottom, ShadingType::SolidLine); + appendHorizontalLine(r, p.s->font->gridBottom, ShadingType::SolidLine, r.gridlineColor); + } + if (r.lines.test(GridLines::Strikethrough)) + { + appendHorizontalLine(r, p.s->font->strikethrough, ShadingType::SolidLine, r.gridlineColor); } if (r.lines.test(GridLines::Underline)) { - appendHorizontalLine(r, p.s->font->underline, ShadingType::SolidLine); + appendHorizontalLine(r, p.s->font->underline, ShadingType::SolidLine, r.underlineColor); } - if (r.lines.test(GridLines::HyperlinkUnderline)) + else if (r.lines.any(GridLines::DottedUnderline, GridLines::HyperlinkUnderline)) { - appendHorizontalLine(r, p.s->font->underline, dottedLineType); + appendHorizontalLine(r, p.s->font->underline, ShadingType::DottedLine, r.underlineColor); } - if (r.lines.test(GridLines::DoubleUnderline)) + else if (r.lines.test(GridLines::DashedUnderline)) + { + appendHorizontalLine(r, p.s->font->underline, ShadingType::DashedLine, r.underlineColor); + } + else if (r.lines.test(GridLines::CurlyUnderline)) + { + appendHorizontalLine(r, _curlyUnderline, ShadingType::CurlyLine, r.underlineColor); + } + else if (r.lines.test(GridLines::DoubleUnderline)) { for (const auto pos : p.s->font->doubleUnderline) { - appendHorizontalLine(r, pos, ShadingType::SolidLine); + appendHorizontalLine(r, pos, ShadingType::SolidLine, r.underlineColor); } } - if (r.lines.test(GridLines::Strikethrough)) - { - appendHorizontalLine(r, p.s->font->strikethrough, ShadingType::SolidLine); - } } } @@ -2042,6 +2084,8 @@ size_t BackendD3D::_drawCursorForegroundSlowPath(const CursorRect& c, size_t off auto& target = _instances[offset + i]; target.shadingType = it.shadingType; + target.renditionScale.x = it.renditionScale.x; + target.renditionScale.y = it.renditionScale.y; target.position.x = static_cast(cutout.left); target.position.y = static_cast(cutout.top); target.size.x = static_cast(cutout.right - cutout.left); @@ -2059,6 +2103,8 @@ size_t BackendD3D::_drawCursorForegroundSlowPath(const CursorRect& c, size_t off auto& target = cutoutCount ? _appendQuad() : _instances[offset]; target.shadingType = it.shadingType; + target.renditionScale.x = it.renditionScale.x; + target.renditionScale.y = it.renditionScale.y; target.position.x = static_cast(intersectionL); target.position.y = static_cast(intersectionT); target.size.x = static_cast(intersectionR - intersectionL); diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 14f4ca2943e..0befe8fe342 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -42,6 +42,9 @@ namespace Microsoft::Console::Render::Atlas alignas(sizeof(f32x4)) f32 gammaRatios[4]{}; alignas(sizeof(f32)) f32 enhancedContrast = 0; alignas(sizeof(f32)) f32 underlineWidth = 0; + alignas(sizeof(f32)) f32 curlyLinePeakHeight = 0; + alignas(sizeof(f32)) f32 curlyLineWaveFreq = 0; + alignas(sizeof(f32)) f32 curlyLineCellOffset = 0; #pragma warning(suppress : 4324) // 'PSConstBuffer': structure was padded due to alignment specifier }; @@ -55,7 +58,7 @@ namespace Microsoft::Console::Render::Atlas #pragma warning(suppress : 4324) // 'CustomConstBuffer': structure was padded due to alignment specifier }; - enum class ShadingType : u32 + enum class ShadingType : u16 { Default = 0, Background = 0, @@ -66,12 +69,13 @@ namespace Microsoft::Console::Render::Atlas TextClearType = 2, TextPassthrough = 3, DottedLine = 4, - DottedLineWide = 5, + DashedLine = 5, + CurlyLine = 6, // All items starting here will be drawing as a solid RGBA color - SolidLine = 6, + SolidLine = 7, - Cursor = 7, - Selection = 8, + Cursor = 8, + Selection = 9, TextDrawingFirst = TextGrayscale, TextDrawingLast = SolidLine, @@ -86,7 +90,8 @@ namespace Microsoft::Console::Render::Atlas // impact on performance and power draw. If (when?) displays with >32k resolution make their // appearance in the future, this should be changed to f32x2. But if you do so, please change // all other occurrences of i16x2 positions/offsets throughout the class to keep it consistent. - alignas(u32) ShadingType shadingType; + alignas(u16) ShadingType shadingType; + alignas(u16) u8x2 renditionScale; alignas(u32) i16x2 position; alignas(u32) u16x2 size; alignas(u32) u16x2 texcoord; @@ -95,16 +100,11 @@ namespace Microsoft::Console::Render::Atlas struct alignas(u32) AtlasGlyphEntryData { - u16 shadingType; + ShadingType shadingType; u16 overlapSplit; i16x2 offset; u16x2 size; u16x2 texcoord; - - constexpr ShadingType GetShadingType() const noexcept - { - return static_cast(shadingType); - } }; // NOTE: Don't initialize any members in this struct. This ensures that no @@ -291,6 +291,9 @@ namespace Microsoft::Console::Render::Atlas // The bounding rect of _cursorRects in pixels. til::rect _cursorPosition; + f32 _curlyLineDrawPeakHeight = 0; + FontDecorationPosition _curlyUnderline; + bool _requiresContinuousRedraw = false; #if ATLAS_DEBUG_SHOW_DIRTY diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index b8fa3ac0d59..014f5487118 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -125,6 +125,7 @@ namespace Microsoft::Console::Render::Atlas }; using u8 = uint8_t; + using u8x2 = vec2; using u16 = uint16_t; using u16x2 = vec2; @@ -426,7 +427,8 @@ namespace Microsoft::Console::Render::Atlas struct GridLineRange { GridLineSet lines; - u32 color = 0; + u32 gridlineColor = 0; + u32 underlineColor = 0; u16 from = 0; u16 to = 0; }; diff --git a/src/renderer/atlas/shader_common.hlsl b/src/renderer/atlas/shader_common.hlsl index 957b17ac6ad..d712f081f28 100644 --- a/src/renderer/atlas/shader_common.hlsl +++ b/src/renderer/atlas/shader_common.hlsl @@ -7,13 +7,15 @@ #define SHADING_TYPE_TEXT_CLEARTYPE 2 #define SHADING_TYPE_TEXT_PASSTHROUGH 3 #define SHADING_TYPE_DOTTED_LINE 4 -#define SHADING_TYPE_DOTTED_LINE_WIDE 5 +#define SHADING_TYPE_DASHED_LINE 5 +#define SHADING_TYPE_CURLY_LINE 6 // clang-format on struct VSData { float2 vertex : SV_Position; uint shadingType : shadingType; + uint2 renditionScale : renditionScale; int2 position : position; uint2 size : size; uint2 texcoord : texcoord; @@ -25,6 +27,7 @@ struct PSData float4 position : SV_Position; float2 texcoord : texcoord; nointerpolation uint shadingType : shadingType; + nointerpolation uint2 renditionScale : renditionScale; nointerpolation float4 color : color; }; diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl index 2cb92947ce2..e19ba955fe5 100644 --- a/src/renderer/atlas/shader_ps.hlsl +++ b/src/renderer/atlas/shader_ps.hlsl @@ -12,6 +12,9 @@ cbuffer ConstBuffer : register(b0) float4 gammaRatios; float enhancedContrast; float underlineWidth; + float curlyLinePeakHeight; + float curlyLineWaveFreq; + float curlyLineCellOffset; } Texture2D background : register(t0); @@ -73,18 +76,36 @@ Output main(PSData data) : SV_Target } case SHADING_TYPE_DOTTED_LINE: { - const bool on = frac(data.position.x / (2.0f * underlineWidth)) < 0.5f; + const bool on = frac(data.position.x / (2.0f * underlineWidth * data.renditionScale.x)) < 0.5f; color = on * premultiplyColor(data.color); weights = color.aaaa; break; } - case SHADING_TYPE_DOTTED_LINE_WIDE: + case SHADING_TYPE_DASHED_LINE: { - const bool on = frac(data.position.x / (4.0f * underlineWidth)) < 0.5f; + const bool on = frac(data.position.x / (backgroundCellSize.x * data.renditionScale.x)) < 0.5f; color = on * premultiplyColor(data.color); weights = color.aaaa; break; } + case SHADING_TYPE_CURLY_LINE: + { + uint cellRow = floor(data.position.y / backgroundCellSize.y); + // Use the previous cell when drawing 'Double Height' curly line. + cellRow -= data.renditionScale.y - 1; + const float cellTop = cellRow * backgroundCellSize.y; + const float centerY = cellTop + curlyLineCellOffset * data.renditionScale.y; + const float strokeWidthHalf = underlineWidth * data.renditionScale.y / 2.0f; + const float amp = curlyLinePeakHeight * data.renditionScale.y; + const float freq = curlyLineWaveFreq / data.renditionScale.x; + + const float s = sin(data.position.x * freq); + const float d = abs(centerY - (s * amp) - data.position.y); + const float a = 1 - saturate(d - strokeWidthHalf); + color = a * premultiplyColor(data.color); + weights = color.aaaa; + break; + } default: { color = premultiplyColor(data.color); diff --git a/src/renderer/atlas/shader_vs.hlsl b/src/renderer/atlas/shader_vs.hlsl index 49b9030b156..eb96fcf0e45 100644 --- a/src/renderer/atlas/shader_vs.hlsl +++ b/src/renderer/atlas/shader_vs.hlsl @@ -15,6 +15,7 @@ PSData main(VSData data) PSData output; output.color = data.color; output.shadingType = data.shadingType; + output.renditionScale = data.renditionScale; // positionScale is expected to be float2(2.0f / sizeInPixel.x, -2.0f / sizeInPixel.y). Together with the // addition below this will transform our "position" from pixel into normalized device coordinate (NDC) space. output.position.xy = (data.position + data.vertex.xy * data.size) * positionScale + float2(-1.0f, 1.0f); diff --git a/src/renderer/base/RenderSettings.cpp b/src/renderer/base/RenderSettings.cpp index 03c4795635a..f885957796a 100644 --- a/src/renderer/base/RenderSettings.cpp +++ b/src/renderer/base/RenderSettings.cpp @@ -212,6 +212,47 @@ std::pair RenderSettings::GetAttributeColorsWithAlpha(const return { fg, bg }; } +// Routine Description: +// - Calculates the RGB underline color of a given text attribute, using the +// current color table configuration and active render settings. +// - Returns the current foreground color when the underline color isn't set. +// Arguments: +// - attr - The TextAttribute to retrieve the underline color from. +// Return Value: +// - The color value of the attribute's underline. +COLORREF RenderSettings::GetAttributeUnderlineColor(const TextAttribute& attr) const noexcept +{ + const auto [fg, bg] = GetAttributeColors(attr); + const auto ulTextColor = attr.GetUnderlineColor(); + if (ulTextColor.IsDefault()) + { + return fg; + } + + const auto defaultUlIndex = GetColorAliasIndex(ColorAlias::DefaultForeground); + auto ul = ulTextColor.GetColor(_colorTable, defaultUlIndex, true); + if (attr.IsInvisible()) + { + ul = bg; + } + + // We intentionally aren't _only_ checking for attr.IsInvisible here, because we also want to + // catch the cases where the ul was intentionally set to be the same as the bg. In either case, + // don't adjust the underline color. + if constexpr (Feature_AdjustIndistinguishableText::IsEnabled()) + { + if ( + ul != bg && + (_renderMode.test(Mode::AlwaysDistinguishableColors) || + (_renderMode.test(Mode::IndexedDistinguishableColors) && ulTextColor.IsDefaultOrLegacy() && attr.GetBackground().IsDefaultOrLegacy()))) + { + ul = ColorFix::GetPerceivableColor(ul, bg, 0.5f * 0.5f); + } + } + + return ul; +} + // Routine Description: // - Increments the position in the blink cycle, toggling the blink rendition // state on every second call, potentially triggering a redraw of the given diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 2bf98404ae2..0b1d6b004cb 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -955,13 +955,21 @@ GridLineSet Renderer::s_GetGridlines(const TextAttribute& textAttribute) noexcep { case UnderlineStyle::NoUnderline: break; + case UnderlineStyle::SinglyUnderlined: + lines.set(GridLines::Underline); + break; case UnderlineStyle::DoublyUnderlined: lines.set(GridLines::DoubleUnderline); break; - case UnderlineStyle::SinglyUnderlined: case UnderlineStyle::CurlyUnderlined: + lines.set(GridLines::CurlyUnderline); + break; case UnderlineStyle::DottedUnderlined: + lines.set(GridLines::DottedUnderline); + break; case UnderlineStyle::DashedUnderlined: + lines.set(GridLines::DashedUnderline); + break; default: lines.set(GridLines::Underline); break; @@ -1002,10 +1010,11 @@ void Renderer::_PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngin // Return early if there are no lines to paint. if (lines.any()) { - // Get the current foreground color to render the lines. - const auto rgb = _renderSettings.GetAttributeColors(textAttribute).first; + // Get the current foreground and underline colors to render the lines. + const auto fg = _renderSettings.GetAttributeColors(textAttribute).first; + const auto underlineColor = _renderSettings.GetAttributeUnderlineColor(textAttribute); // Draw the lines - LOG_IF_FAILED(pEngine->PaintBufferGridLines(lines, rgb, cchLine, coordTarget)); + LOG_IF_FAILED(pEngine->PaintBufferGridLines(lines, fg, underlineColor, cchLine, coordTarget)); } } diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index f74bf528dc1..064d219f72a 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1696,14 +1696,16 @@ CATCH_RETURN() // - Paints lines around cells (draws in pieces of the grid) // Arguments: // - lines - Which grid lines (top, left, bottom, right) to draw -// - color - The color to use for drawing the lines +// - gridlineColor - The color to use for drawing the gridlines +// - underlineColor - The color to use for drawing the underlines // - cchLine - Length of the line to draw in character cells // - coordTarget - The X,Y character position in the grid where we should start drawing // - We will draw rightward (+X) from here // Return Value: // - S_OK or relevant DirectX error [[nodiscard]] HRESULT DxEngine::PaintBufferGridLines(const GridLineSet lines, - COLORREF const color, + const COLORREF gridlineColor, + const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept try @@ -1711,8 +1713,6 @@ try const auto existingColor = _d2dBrushForeground->GetColor(); const auto restoreBrushOnExit = wil::scope_exit([&]() noexcept { _d2dBrushForeground->SetColor(existingColor); }); - _d2dBrushForeground->SetColor(_ColorFFromColorRef(color | 0xff000000)); - const auto font = _fontRenderData->GlyphCell().to_d2d_size(); const D2D_POINT_2F target = { coordTarget.x * font.width, coordTarget.y * font.height }; const auto fullRunWidth = font.width * gsl::narrow_cast(cchLine); @@ -1721,10 +1721,12 @@ try _d2dDeviceContext->DrawLine({ x0, y0 }, { x1, y1 }, _d2dBrushForeground.Get(), strokeWidth, _strokeStyle.Get()); }; - const auto DrawHyperlinkLine = [=](const auto x0, const auto y0, const auto x1, const auto y1, const auto strokeWidth) noexcept { + const auto DrawDottedLine = [=](const auto x0, const auto y0, const auto x1, const auto y1, const auto strokeWidth) noexcept { _d2dDeviceContext->DrawLine({ x0, y0 }, { x1, y1 }, _d2dBrushForeground.Get(), strokeWidth, _dashStrokeStyle.Get()); }; + _d2dBrushForeground->SetColor(_ColorFFromColorRef(gridlineColor | 0xff000000)); + // NOTE: Line coordinates are centered within the line, so they need to be // offset by half the stroke width. For the start coordinate we add half // the stroke width, and for the end coordinate we subtract half the width. @@ -1773,10 +1775,22 @@ try } } + if (lines.test(GridLines::Strikethrough)) + { + const auto halfStrikethroughWidth = lineMetrics.strikethroughWidth / 2.0f; + const auto startX = target.x + halfStrikethroughWidth; + const auto endX = target.x + fullRunWidth - halfStrikethroughWidth; + const auto y = target.y + lineMetrics.strikethroughOffset; + + DrawLine(startX, y, endX, y, lineMetrics.strikethroughWidth); + } + + _d2dBrushForeground->SetColor(_ColorFFromColorRef(underlineColor | 0xff000000)); + // In the case of the underline and strikethrough offsets, the stroke width // is already accounted for, so they don't require further adjustments. - if (lines.any(GridLines::Underline, GridLines::DoubleUnderline, GridLines::HyperlinkUnderline)) + if (lines.any(GridLines::Underline, GridLines::DoubleUnderline, GridLines::DottedUnderline, GridLines::HyperlinkUnderline)) { const auto halfUnderlineWidth = lineMetrics.underlineWidth / 2.0f; const auto startX = target.x + halfUnderlineWidth; @@ -1788,9 +1802,9 @@ try DrawLine(startX, y, endX, y, lineMetrics.underlineWidth); } - if (lines.test(GridLines::HyperlinkUnderline)) + if (lines.any(GridLines::DottedUnderline, GridLines::HyperlinkUnderline)) { - DrawHyperlinkLine(startX, y, endX, y, lineMetrics.underlineWidth); + DrawDottedLine(startX, y, endX, y, lineMetrics.underlineWidth); } if (lines.test(GridLines::DoubleUnderline)) @@ -1801,16 +1815,6 @@ try } } - if (lines.test(GridLines::Strikethrough)) - { - const auto halfStrikethroughWidth = lineMetrics.strikethroughWidth / 2.0f; - const auto startX = target.x + halfStrikethroughWidth; - const auto endX = target.x + fullRunWidth - halfStrikethroughWidth; - const auto y = target.y + lineMetrics.strikethroughOffset; - - DrawLine(startX, y, endX, y, lineMetrics.strikethroughWidth); - } - return S_OK; } CATCH_RETURN() diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index bfb11205a05..877b3f1adfa 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -106,7 +106,7 @@ namespace Microsoft::Console::Render const bool fTrimLeft, const bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet const lines, COLORREF const color, size_t const cchLine, til::point const coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index a1ab2290a97..2cc5b4dc14c 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -52,7 +52,8 @@ namespace Microsoft::Console::Render const bool trimLeft, const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, - const COLORREF color, + const COLORREF gridlineColor, + const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; @@ -120,6 +121,7 @@ namespace Microsoft::Console::Render int underlineWidth; int strikethroughOffset; int strikethroughWidth; + int curlylinePeakHeight; }; LineMetrics _lineMetrics; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 17dc9bddd82..9722703d105 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -509,27 +509,24 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) // - Draws up to one line worth of grid lines on top of characters. // Arguments: // - lines - Enum defining which edges of the rectangle to draw -// - color - The color to use for drawing the edges. +// - gridlineColor - The color to use for drawing the gridlines. +// - underlineColor - The color to use for drawing the underlines. // - cchLine - How many characters we should draw the grid lines along (left to right in a row) // - coordTarget - The starting X/Y position of the first character to draw on. // Return Value: // - S_OK or suitable GDI HRESULT error or E_FAIL for GDI errors in functions that don't reliably return a specific error code. -[[nodiscard]] HRESULT GdiEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF color, const size_t cchLine, const til::point coordTarget) noexcept +[[nodiscard]] HRESULT GdiEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept { LOG_IF_FAILED(_FlushBufferLines()); // Convert the target from characters to pixels. const auto ptTarget = coordTarget * _GetFontSize(); - // Set the brush color as requested and save the previous brush to restore at the end. - wil::unique_hbrush hbr(CreateSolidBrush(color)); - RETURN_HR_IF_NULL(E_FAIL, hbr.get()); - - wil::unique_hbrush hbrPrev(SelectBrush(_hdcMemoryContext, hbr.get())); - RETURN_HR_IF_NULL(E_FAIL, hbrPrev.get()); - hbr.release(); // If SelectBrush was successful, GDI owns the brush. Release for now. - // On exit, be sure we try to put the brush back how it was originally. - auto restoreBrushOnExit = wil::scope_exit([&] { hbr.reset(SelectBrush(_hdcMemoryContext, hbrPrev.get())); }); + // Create a brush with the gridline color, and apply it. + wil::unique_hbrush hbr(CreateSolidBrush(gridlineColor)); + RETURN_HR_IF_NULL(E_FAIL, hbr.get()); + const auto prevBrush = wil::SelectObject(_hdcMemoryContext, hbr.get()); + RETURN_HR_IF_NULL(E_FAIL, prevBrush.get()); // Get the font size so we know the size of the rectangle lines we'll be inscribing. const auto fontWidth = _GetFontSize().width; @@ -539,6 +536,31 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) const auto DrawLine = [=](const auto x, const auto y, const auto w, const auto h) { return PatBlt(_hdcMemoryContext, x, y, w, h, PATCOPY); }; + const auto DrawStrokedLine = [&](const auto x, const auto y, const auto w) { + RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); + RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, x + w, y)); + return S_OK; + }; + const auto DrawCurlyLine = [&](const auto x, const auto y, const auto cCurlyLines) { + const auto curlyLineWidth = fontWidth; + const auto curlyLineHalfWidth = lrintf(curlyLineWidth / 2.0f); + const auto controlPointHeight = gsl::narrow_cast(std::floor(3.5f * _lineMetrics.curlylinePeakHeight)); + // Each curlyLine requires 3 `POINT`s + const auto cPoints = gsl::narrow(3 * cCurlyLines); + std::vector points; + points.reserve(cPoints); + auto start = x; + for (auto i = 0u; i < cCurlyLines; i++) + { + points.emplace_back(start + curlyLineHalfWidth, y - controlPointHeight); + points.emplace_back(start + curlyLineHalfWidth, y + controlPointHeight); + points.emplace_back(start + curlyLineWidth, y); + start += curlyLineWidth; + } + RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); + RETURN_HR_IF(E_FAIL, !PolyBezierTo(_hdcMemoryContext, points.data(), cPoints)); + return S_OK; + }; if (lines.test(GridLines::Left)) { @@ -574,22 +596,51 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) RETURN_HR_IF(E_FAIL, !DrawLine(ptTarget.x, y, widthOfAllCells, _lineMetrics.gridlineWidth)); } - if (lines.any(GridLines::Underline, GridLines::DoubleUnderline)) + if (lines.test(GridLines::Strikethrough)) { - const auto y = ptTarget.y + _lineMetrics.underlineOffset; - RETURN_HR_IF(E_FAIL, !DrawLine(ptTarget.x, y, widthOfAllCells, _lineMetrics.underlineWidth)); + const auto y = ptTarget.y + _lineMetrics.strikethroughOffset; + RETURN_HR_IF(E_FAIL, !DrawLine(ptTarget.x, y, widthOfAllCells, _lineMetrics.strikethroughWidth)); + } - if (lines.test(GridLines::DoubleUnderline)) - { - const auto y2 = ptTarget.y + _lineMetrics.underlineOffset2; - RETURN_HR_IF(E_FAIL, !DrawLine(ptTarget.x, y2, widthOfAllCells, _lineMetrics.underlineWidth)); - } + // Create a pen matching the underline style. + DWORD underlinePenType = PS_SOLID; + if (lines.test(GridLines::DottedUnderline)) + { + underlinePenType = PS_DOT; + } + else if (lines.test(GridLines::DashedUnderline)) + { + underlinePenType = PS_DASH; } + const LOGBRUSH brushProp{ .lbStyle = BS_SOLID, .lbColor = underlineColor }; + wil::unique_hpen hpen(ExtCreatePen(underlinePenType | PS_GEOMETRIC | PS_ENDCAP_FLAT, _lineMetrics.underlineWidth, &brushProp, 0, nullptr)); - if (lines.test(GridLines::Strikethrough)) + // Apply the pen. + const auto prevPen = wil::SelectObject(_hdcMemoryContext, hpen.get()); + RETURN_HR_IF_NULL(E_FAIL, prevPen.get()); + + const auto underlineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset + _lineMetrics.underlineWidth / 2.0f); + if (lines.test(GridLines::Underline)) { - const auto y = ptTarget.y + _lineMetrics.strikethroughOffset; - RETURN_HR_IF(E_FAIL, !DrawLine(ptTarget.x, y, widthOfAllCells, _lineMetrics.strikethroughWidth)); + return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + } + else if (lines.test(GridLines::DoubleUnderline)) + { + const auto doubleUnderlineBottomLineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset2 + _lineMetrics.underlineWidth / 2.0f); + RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells)); + return DrawStrokedLine(ptTarget.x, doubleUnderlineBottomLineMidY, widthOfAllCells); + } + else if (lines.test(GridLines::CurlyUnderline)) + { + return DrawCurlyLine(ptTarget.x, underlineMidY, cchLine); + } + else if (lines.test(GridLines::DottedUnderline)) + { + return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + } + else if (lines.test(GridLines::DashedUnderline)) + { + return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); } return S_OK; diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 556f2df02fb..099c2f43f78 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -11,6 +11,15 @@ using namespace Microsoft::Console::Render; +namespace +{ + // The max height of Curly line peak in `em` units. + constexpr auto MaxCurlyLinePeakHeightEm = 0.075f; + + // The min height of Curly line peak. + constexpr auto MinCurlyLinePeakHeight = 2.0f; +} + // Routine Description: // - Creates a new GDI-based rendering engine // - NOTE: Will throw if initialization failure. Caller must catch. @@ -397,6 +406,31 @@ GdiEngine::~GdiEngine() _lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset - _lineMetrics.gridlineWidth; } + // Curly line doesn't render properly below 1px stroke width. Make it a straight line. + if (_lineMetrics.underlineWidth < 1) + { + _lineMetrics.curlylinePeakHeight = 0; + } + else + { + // Curlyline uses the gap between cell bottom and singly underline + // position as the height of the wave's peak. The baseline for curly + // line is at the middle of singly underline. The gap could be too big, + // so we also apply a limit on the peak height. + const auto strokeHalfWidth = _lineMetrics.underlineWidth / 2.0f; + const auto underlineMidY = _lineMetrics.underlineOffset + strokeHalfWidth; + const auto cellBottomGap = Font.GetSize().height - underlineMidY - strokeHalfWidth; + const auto maxCurlyLinePeakHeight = MaxCurlyLinePeakHeightEm * fontSize; + auto curlyLinePeakHeight = std::min(cellBottomGap, maxCurlyLinePeakHeight); + + // When it's too small to be curly, make it a straight line. + if (curlyLinePeakHeight < MinCurlyLinePeakHeight) + { + curlyLinePeakHeight = 0.0f; + } + _lineMetrics.curlylinePeakHeight = gsl::narrow_cast(std::floor(curlyLinePeakHeight)); + } + // Now find the size of a 0 in this current font and save it for conversions done later. _coordFontLast = Font.GetSize(); diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 46221ae911d..afcaa5aff59 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -41,6 +41,9 @@ namespace Microsoft::Console::Render Right, Underline, DoubleUnderline, + CurlyUnderline, + DottedUnderline, + DashedUnderline, Strikethrough, HyperlinkUnderline }; @@ -73,7 +76,7 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept = 0; [[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0; [[nodiscard]] virtual HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept = 0; - [[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept = 0; + [[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF gridlineColor, COLORREF underlineColor, size_t cchLine, til::point coordTarget) noexcept = 0; [[nodiscard]] virtual HRESULT PaintSelection(const til::rect& rect) noexcept = 0; [[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept = 0; diff --git a/src/renderer/inc/RenderSettings.hpp b/src/renderer/inc/RenderSettings.hpp index 4b6e7c3981c..c836bdde848 100644 --- a/src/renderer/inc/RenderSettings.hpp +++ b/src/renderer/inc/RenderSettings.hpp @@ -41,6 +41,7 @@ namespace Microsoft::Console::Render size_t GetColorAliasIndex(const ColorAlias alias) const noexcept; std::pair GetAttributeColors(const TextAttribute& attr) const noexcept; std::pair GetAttributeColorsWithAlpha(const TextAttribute& attr) const noexcept; + COLORREF GetAttributeUnderlineColor(const TextAttribute& attr) const noexcept; void ToggleBlinkRendition(class Renderer& renderer) noexcept; private: diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index f39bcbe4fbf..d1e72fbd0eb 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -357,14 +357,16 @@ void UiaEngine::WaitUntilCanRender() noexcept // For UIA, this doesn't mean anything. So do nothing. // Arguments: // - lines - -// - color - +// - gridlineColor - +// - underlineColor - // - cchLine - // - coordTarget - // Return Value: // - S_FALSE -[[nodiscard]] HRESULT UiaEngine::PaintBufferGridLines(GridLineSet const /*lines*/, - COLORREF const /*color*/, - size_t const /*cchLine*/, +[[nodiscard]] HRESULT UiaEngine::PaintBufferGridLines(const GridLineSet /*lines*/, + const COLORREF /*gridlineColor*/, + const COLORREF /*underlineColor*/, + const size_t /*cchLine*/, const til::point /*coordTarget*/) noexcept { return S_FALSE; diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 5e486b7e0e1..4625ca155cb 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -49,7 +49,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override; [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] HRESULT PaintBufferLine(const std::span clusters, const til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF color, const size_t cchLine, const til::point coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, const gsl::not_null pData, const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept override; diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index 801390085c0..99f8853a98f 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -187,13 +187,15 @@ using namespace Microsoft::Console::Types; // - Draws up to one line worth of grid lines on top of characters. // Arguments: // - lines - Enum defining which edges of the rectangle to draw -// - color - The color to use for drawing the edges. +// - gridlineColor - The color to use for drawing the gridlines. +// - underlineColor - The color to use for drawing the underlines. // - cchLine - How many characters we should draw the grid lines along (left to right in a row) // - coordTarget - The starting X/Y position of the first character to draw on. // Return Value: // - S_OK [[nodiscard]] HRESULT VtEngine::PaintBufferGridLines(const GridLineSet /*lines*/, - const COLORREF /*color*/, + const COLORREF /*gridlineColor*/, + const COLORREF /*underlineColor*/, const size_t /*cchLine*/, const til::point /*coordTarget*/) noexcept { diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 094bebec385..676672ee814 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -62,7 +62,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept override; [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; diff --git a/src/renderer/wddmcon/WddmConRenderer.cpp b/src/renderer/wddmcon/WddmConRenderer.cpp index 378b1dfd086..fda8ae6c21b 100644 --- a/src/renderer/wddmcon/WddmConRenderer.cpp +++ b/src/renderer/wddmcon/WddmConRenderer.cpp @@ -284,9 +284,10 @@ CATCH_RETURN() CATCH_RETURN(); } -[[nodiscard]] HRESULT WddmConEngine::PaintBufferGridLines(GridLineSet const /*lines*/, - COLORREF const /*color*/, - size_t const /*cchLine*/, +[[nodiscard]] HRESULT WddmConEngine::PaintBufferGridLines(const GridLineSet /*lines*/, + const COLORREF /*gridlineColor*/, + const COLORREF /*underlineColor*/, + const size_t /*cchLine*/, const til::point /*coordTarget*/) noexcept { return S_OK; diff --git a/src/renderer/wddmcon/WddmConRenderer.hpp b/src/renderer/wddmcon/WddmConRenderer.hpp index a658ef51180..954dc226946 100644 --- a/src/renderer/wddmcon/WddmConRenderer.hpp +++ b/src/renderer/wddmcon/WddmConRenderer.hpp @@ -44,7 +44,7 @@ namespace Microsoft::Console::Render const til::point coord, const bool trimLeft, const bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet const lines, COLORREF const color, size_t const cchLine, til::point const coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index af5ebb431ea..419acf1ff0b 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -405,16 +405,22 @@ std::optional UiaTextRangeBase::_verifyAttr(TEXTATTRIBUTEID attributeId, V THROW_HR_IF(E_INVALIDARG, val.vt != VT_I4); // The underline style is stored as a TextDecorationLineStyle. - // However, The text buffer doesn't have that many different styles for being underlined. - // Instead, we only have single and double underlined. + // However, The text buffer doesn't have all the different styles for being underlined. + // Instead, we only use a subset of them. switch (val.lVal) { case TextDecorationLineStyle_None: return !attr.IsUnderlined(); + case TextDecorationLineStyle_Single: + return attr.GetUnderlineStyle() == UnderlineStyle::SinglyUnderlined; case TextDecorationLineStyle_Double: return attr.GetUnderlineStyle() == UnderlineStyle::DoublyUnderlined; - case TextDecorationLineStyle_Single: // singly underlined and extended styles are treated the same - return attr.IsUnderlined() && attr.GetUnderlineStyle() != UnderlineStyle::DoublyUnderlined; + case TextDecorationLineStyle_Wavy: + return attr.GetUnderlineStyle() == UnderlineStyle::CurlyUnderlined; + case TextDecorationLineStyle_Dot: + return attr.GetUnderlineStyle() == UnderlineStyle::DottedUnderlined; + case TextDecorationLineStyle_Dash: + return attr.GetUnderlineStyle() == UnderlineStyle::DashedUnderlined; default: return std::nullopt; } @@ -697,18 +703,26 @@ bool UiaTextRangeBase::_initializeAttrQuery(TEXTATTRIBUTEID attributeId, VARIANT const auto style = attr.GetUnderlineStyle(); switch (style) { + case UnderlineStyle::NoUnderline: + pRetVal->lVal = TextDecorationLineStyle_None; + return true; case UnderlineStyle::SinglyUnderlined: pRetVal->lVal = TextDecorationLineStyle_Single; return true; case UnderlineStyle::DoublyUnderlined: pRetVal->lVal = TextDecorationLineStyle_Double; return true; - case UnderlineStyle::NoUnderline: - pRetVal->lVal = TextDecorationLineStyle_None; + case UnderlineStyle::CurlyUnderlined: + pRetVal->lVal = TextDecorationLineStyle_Wavy; + return true; + case UnderlineStyle::DottedUnderlined: + pRetVal->lVal = TextDecorationLineStyle_Dot; + return true; + case UnderlineStyle::DashedUnderlined: + pRetVal->lVal = TextDecorationLineStyle_Dash; return true; + // Out of range styles are treated as singly underlined. default: - // TODO: Handle other underline styles once they're supported in the graphic renderer. - // For now, extended styles are treated (and rendered) as single underline. pRetVal->lVal = TextDecorationLineStyle_Single; return true; } From a62fb06b32e7af69dfa4653bab7a9d19dbf369bb Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Fri, 15 Dec 2023 01:17:14 +0530 Subject: [PATCH 056/603] Fix curlyline rendering in `AtlasEngine` and `GDIRenderer` (#16444) Fixes Curlyline being drawn as single underline in some cases **Detailed Description** - Curlyline is drawn at all font sizes. - We might render a curlyline that is clipped in cases where we don't have enough space to draw a full curlyline. This is to give users a consistent view of Curlylines. Previously in those cases, it was drawn as a single underline. - Removed minimum threshold `minCurlyLinePeakHeight` for Curlyline drawing. - GDIRender changes: - Underline offset now points to the (vertical) mid position of the underline. Removes redundant `underlineMidY` calculation inside the draw call. Closes #16288 (cherry picked from commit f5b45c25c9dfe27e03fbea1c7d82a6dc2a009343) Service-Card-Id: 91349182 Service-Version: 1.19 --- src/renderer/atlas/BackendD3D.cpp | 56 +++++++++++++++---------------- src/renderer/atlas/BackendD3D.h | 2 +- src/renderer/gdi/paint.cpp | 29 ++++++++-------- src/renderer/gdi/state.cpp | 51 ++++++++++++---------------- 4 files changed, 65 insertions(+), 73 deletions(-) diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 41fefa6ea2b..74b64c53571 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -306,37 +306,35 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p) { const auto& font = *p.s->font; - // The max height of Curly line peak in `em` units. - const auto maxCurlyLinePeakHeightEm = 0.075f; - // We aim for atleast 1px height, but since we draw 1px smaller curly line, - // we aim for 2px height as a result. - const auto minCurlyLinePeakHeight = 2.0f; - - // Curlyline uses the gap between cell bottom and singly underline position - // as the height of the wave's peak. The baseline for curly-line is at the - // middle of singly underline. The gap could be too big, so we also apply - // a limit on the peak height. - const auto strokeHalfWidth = font.underline.height / 2.0f; - const auto underlineMidY = font.underline.position + strokeHalfWidth; - const auto cellBottomGap = font.cellSize.y - underlineMidY - strokeHalfWidth; - const auto maxCurlyLinePeakHeight = maxCurlyLinePeakHeightEm * font.fontSize; - auto curlyLinePeakHeight = std::min(cellBottomGap, maxCurlyLinePeakHeight); + // Curlyline is drawn with a desired height relative to the font size. The + // baseline of curlyline is at the middle of singly underline. When there's + // limited space to draw a curlyline, we apply a limit on the peak height. + { + // initialize curlyline peak height to a desired value. Clamp it to at + // least 1. + constexpr auto curlyLinePeakHeightEm = 0.075f; + _curlyLinePeakHeight = std::max(1.0f, std::roundf(curlyLinePeakHeightEm * font.fontSize)); + + // calc the limit we need to apply + const auto strokeHalfWidth = std::floor(font.underline.height / 2.0f); + const auto underlineMidY = font.underline.position + strokeHalfWidth; + const auto maxDrawableCurlyLinePeakHeight = font.cellSize.y - underlineMidY - font.underline.height; + + // if the limit is <= 0 (no height at all), stick with the desired height. + // This is how we force a curlyline even when there's no space, though it + // might be clipped at the bottom. + if (maxDrawableCurlyLinePeakHeight > 0.0f) + { + _curlyLinePeakHeight = std::min(_curlyLinePeakHeight, maxDrawableCurlyLinePeakHeight); + } - // When it's too small to be curly, make it straight. - if (curlyLinePeakHeight < minCurlyLinePeakHeight) - { - curlyLinePeakHeight = 0; + const auto curlyUnderlinePos = underlineMidY - _curlyLinePeakHeight - font.underline.height; + const auto curlyUnderlineWidth = 2.0f * (_curlyLinePeakHeight + font.underline.height); + const auto curlyUnderlinePosU16 = gsl::narrow_cast(lrintf(curlyUnderlinePos)); + const auto curlyUnderlineWidthU16 = gsl::narrow_cast(lrintf(curlyUnderlineWidth)); + _curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 }; } - // We draw a smaller curly line (-1px) to avoid clipping due to the rounding. - _curlyLineDrawPeakHeight = std::max(0.0f, curlyLinePeakHeight - 1.0f); - - const auto curlyUnderlinePos = font.underline.position - curlyLinePeakHeight; - const auto curlyUnderlineWidth = 2.0f * (curlyLinePeakHeight + strokeHalfWidth); - const auto curlyUnderlinePosU16 = gsl::narrow_cast(lrintf(curlyUnderlinePos)); - const auto curlyUnderlineWidthU16 = gsl::narrow_cast(lrintf(curlyUnderlineWidth)); - _curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 }; - DWrite_GetRenderParams(p.dwriteFactory.get(), &_gamma, &_cleartypeEnhancedContrast, &_grayscaleEnhancedContrast, _textRenderingParams.put()); // Clearing the atlas requires BeginDraw(), which is expensive. Defer this until we need Direct2D anyways. _fontChangedResetGlyphAtlas = true; @@ -576,7 +574,7 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast; data.underlineWidth = p.s->font->underline.height; data.curlyLineWaveFreq = 2.0f * 3.14f / p.s->font->cellSize.x; - data.curlyLinePeakHeight = _curlyLineDrawPeakHeight; + data.curlyLinePeakHeight = _curlyLinePeakHeight; data.curlyLineCellOffset = p.s->font->underline.position + p.s->font->underline.height / 2.0f; p.deviceContext->UpdateSubresource(_psConstantBuffer.get(), 0, nullptr, &data, 0, 0); } diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 0befe8fe342..4c248ed3ff3 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -291,7 +291,7 @@ namespace Microsoft::Console::Render::Atlas // The bounding rect of _cursorRects in pixels. til::rect _cursorPosition; - f32 _curlyLineDrawPeakHeight = 0; + f32 _curlyLinePeakHeight = 0.0f; FontDecorationPosition _curlyUnderline; bool _requiresContinuousRedraw = false; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 9722703d105..222576390e2 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -536,27 +536,30 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) const auto DrawLine = [=](const auto x, const auto y, const auto w, const auto h) { return PatBlt(_hdcMemoryContext, x, y, w, h, PATCOPY); }; - const auto DrawStrokedLine = [&](const auto x, const auto y, const auto w) { + const auto DrawStrokedLine = [&](const til::CoordType x, const til::CoordType y, const unsigned w) { RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); - RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, x + w, y)); + RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, gsl::narrow_cast(x + w), y)); return S_OK; }; - const auto DrawCurlyLine = [&](const auto x, const auto y, const auto cCurlyLines) { + const auto DrawCurlyLine = [&](const til::CoordType x, const til::CoordType y, const size_t cCurlyLines) { const auto curlyLineWidth = fontWidth; - const auto curlyLineHalfWidth = lrintf(curlyLineWidth / 2.0f); - const auto controlPointHeight = gsl::narrow_cast(std::floor(3.5f * _lineMetrics.curlylinePeakHeight)); + const auto curlyLineHalfWidth = std::lround(curlyLineWidth / 2.0f); + const auto controlPointHeight = std::lround(3.5f * _lineMetrics.curlylinePeakHeight); + // Each curlyLine requires 3 `POINT`s const auto cPoints = gsl::narrow(3 * cCurlyLines); std::vector points; points.reserve(cPoints); + auto start = x; - for (auto i = 0u; i < cCurlyLines; i++) + for (size_t i = 0; i < cCurlyLines; i++) { points.emplace_back(start + curlyLineHalfWidth, y - controlPointHeight); points.emplace_back(start + curlyLineHalfWidth, y + controlPointHeight); points.emplace_back(start + curlyLineWidth, y); start += curlyLineWidth; } + RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); RETURN_HR_IF(E_FAIL, !PolyBezierTo(_hdcMemoryContext, points.data(), cPoints)); return S_OK; @@ -619,28 +622,26 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) const auto prevPen = wil::SelectObject(_hdcMemoryContext, hpen.get()); RETURN_HR_IF_NULL(E_FAIL, prevPen.get()); - const auto underlineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset + _lineMetrics.underlineWidth / 2.0f); if (lines.test(GridLines::Underline)) { - return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); } else if (lines.test(GridLines::DoubleUnderline)) { - const auto doubleUnderlineBottomLineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset2 + _lineMetrics.underlineWidth / 2.0f); - RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells)); - return DrawStrokedLine(ptTarget.x, doubleUnderlineBottomLineMidY, widthOfAllCells); + RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells)); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset2, widthOfAllCells); } else if (lines.test(GridLines::CurlyUnderline)) { - return DrawCurlyLine(ptTarget.x, underlineMidY, cchLine); + return DrawCurlyLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, cchLine); } else if (lines.test(GridLines::DottedUnderline)) { - return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); } else if (lines.test(GridLines::DashedUnderline)) { - return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); } return S_OK; diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 099c2f43f78..13fd0ea59fc 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -11,15 +11,6 @@ using namespace Microsoft::Console::Render; -namespace -{ - // The max height of Curly line peak in `em` units. - constexpr auto MaxCurlyLinePeakHeightEm = 0.075f; - - // The min height of Curly line peak. - constexpr auto MinCurlyLinePeakHeight = 2.0f; -} - // Routine Description: // - Creates a new GDI-based rendering engine // - NOTE: Will throw if initialization failure. Caller must catch. @@ -406,29 +397,31 @@ GdiEngine::~GdiEngine() _lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset - _lineMetrics.gridlineWidth; } - // Curly line doesn't render properly below 1px stroke width. Make it a straight line. - if (_lineMetrics.underlineWidth < 1) - { - _lineMetrics.curlylinePeakHeight = 0; - } - else + // Since we use GDI pen for drawing, the underline offset should point to + // the center of the underline. + const auto underlineHalfWidth = gsl::narrow_cast(std::floor(_lineMetrics.underlineWidth / 2.0f)); + _lineMetrics.underlineOffset += underlineHalfWidth; + _lineMetrics.underlineOffset2 += underlineHalfWidth; + + // Curlyline is drawn with a desired height relative to the font size. The + // baseline of curlyline is at the middle of singly underline. When there's + // limited space to draw a curlyline, we apply a limit on the peak height. { - // Curlyline uses the gap between cell bottom and singly underline - // position as the height of the wave's peak. The baseline for curly - // line is at the middle of singly underline. The gap could be too big, - // so we also apply a limit on the peak height. - const auto strokeHalfWidth = _lineMetrics.underlineWidth / 2.0f; - const auto underlineMidY = _lineMetrics.underlineOffset + strokeHalfWidth; - const auto cellBottomGap = Font.GetSize().height - underlineMidY - strokeHalfWidth; - const auto maxCurlyLinePeakHeight = MaxCurlyLinePeakHeightEm * fontSize; - auto curlyLinePeakHeight = std::min(cellBottomGap, maxCurlyLinePeakHeight); - - // When it's too small to be curly, make it a straight line. - if (curlyLinePeakHeight < MinCurlyLinePeakHeight) + // initialize curlyline peak height to a desired value. Clamp it to at + // least 1. + constexpr auto curlyLinePeakHeightEm = 0.075f; + _lineMetrics.curlylinePeakHeight = gsl::narrow_cast(std::max(1L, std::lround(curlyLinePeakHeightEm * fontSize))); + + // calc the limit we need to apply + const auto maxDrawableCurlyLinePeakHeight = Font.GetSize().height - _lineMetrics.underlineOffset - _lineMetrics.underlineWidth; + + // if the limit is <= 0 (no height at all), stick with the desired height. + // This is how we force a curlyline even when there's no space, though it + // might be clipped at the bottom. + if (maxDrawableCurlyLinePeakHeight > 0.0f) { - curlyLinePeakHeight = 0.0f; + _lineMetrics.curlylinePeakHeight = std::min(_lineMetrics.curlylinePeakHeight, maxDrawableCurlyLinePeakHeight); } - _lineMetrics.curlylinePeakHeight = gsl::narrow_cast(std::floor(curlyLinePeakHeight)); } // Now find the size of a 0 in this current font and save it for conversions done later. From acb06304c2002af43c321dd88fda3ae80c244c66 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 16 Dec 2023 00:02:24 +0100 Subject: [PATCH 057/603] Put the final touches on GDI's underlines (#16475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While #16444 left wavy lines in an amazing state already, there were a few more things that could be done to make GDI look more consistent with other well known Windows applications. But before that, a couple unrelated, but helpful changes were made: * `GdiEngine::UpdateFont` was heavily modified to do all calculations in floats. All modern CPUs have fast FPUs and even the fairly slow `lroundf` function is so fast (relatively) nowadays that in a cold path like this, we can liberally call it to convert back to `int`s. This makes intermediate calculation more accurate and consistent. * `GdiEngine::PaintBufferGridLines` was exception-unsafe due to its use of a `std::vector` with catch clause and this PR fixes that. Additionally, the vector was swapped out with a `til::small_vector` to reduce heap allocations. (Arena allocators!) * RenderingTests was updated to cover styled underlines With that in place, these improvements were done: * Word's double-underline algorithm was ported over from `AtlasEngine`. It uses a half underline-width (aka `thinLineWidth`) which will now also be used for wavy lines to make them look a bit more filigrane. * The Bézier curve for wavy/curly underlines was modified to use control points at (0.5,0.5) and (0.5,-0.5) respectively. This results in a maxima at y=0.1414 which is much closer to a sine curve with a maxima at 1/(2pi) = 0.1592. Previously, the maxima was a lot higher (roughly 4x) depending on the aspect ratio of the glyphs. * Wavy underlines don't depend on the aspect ratio of glyphs anymore. This previously led to several problems depending on the exact font. The old renderer would draw exactly 3 periods of the wave into each cell which would also ensure continuity between cells. Unfortunately, this meant that waves could look inconsistent. The new approach always uses the aforementioned sine-like waves. * The wavy underline offset was clamped so that it's never outside of bounds of a line. This avoids clipping. * Compile RenderingTests and run it * Using Consolas, MS Gothic and Cascadia Code while Ctrl+Scrolling up and down works as expected without clipping ✅ (cherry picked from commit 99193c9a3f462b3177b6f596706f11be2074efa5) Service-Card-Id: 91356394 Service-Version: 1.19 --- .github/actions/spelling/expect/expect.txt | 13 -- src/renderer/gdi/gdirenderer.hpp | 10 +- src/renderer/gdi/paint.cpp | 85 ++++++----- src/renderer/gdi/state.cpp | 160 ++++++++++++--------- src/tools/RenderingTests/main.cpp | 147 +++++++++++++------ 5 files changed, 256 insertions(+), 159 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 454a7bfb6f6..b308b81caaf 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -313,7 +313,6 @@ CPPCORECHECK cppcorecheckrules cpprestsdk cppwinrt -CProc cpx CREATESCREENBUFFER CREATESTRUCT @@ -344,7 +343,6 @@ CTRLVOLUME Ctxt CUF cupxy -curlyline CURRENTFONT currentmode CURRENTPAGE @@ -607,7 +605,6 @@ EXETYPE exeuwp exewin exitwin -expectedinput EXPUNGECOMMANDHISTORY EXSTYLE EXTENDEDEDITKEY @@ -830,7 +827,6 @@ HPA hpcon HPCON hpen -hpj HPR HProvider HREDRAW @@ -1022,7 +1018,6 @@ logissue losslessly loword lparam -LPCCH lpch LPCPLINFO LPCREATESTRUCT @@ -1221,7 +1216,6 @@ NOMOVE NONALERT nonbreaking nonclient -NONCONST NONINFRINGEMENT NONPREROTATED nonspace @@ -1582,7 +1576,6 @@ rgbs rgci rgfae rgfte -rgi rgn rgp rgpwsz @@ -1751,7 +1744,6 @@ SOLIDBOX Solutiondir somefile sourced -spammy SRCCODEPAGE SRCCOPY SRCINVERT @@ -1914,7 +1906,6 @@ tprivapi tput tracelog tracelogging -traceloggingprovider traceviewpp trackbar TRACKCOMPOSITION @@ -2007,7 +1998,6 @@ USEFILLATTRIBUTE USEGLYPHCHARS USEHICON USEPOSITION -userbase USERDATA userdpiapi Userp @@ -2050,8 +2040,6 @@ VKKEYSCAN VMs VPA VPR -VProc -VRaw VREDRAW vsc vsconfig @@ -2093,7 +2081,6 @@ WCIA WCIW WCSHELPER wcsicmp -wcsnicmp wcsrev wddm wddmcon diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 2cc5b4dc14c..e4bbb029fc2 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -116,12 +116,16 @@ namespace Microsoft::Console::Render struct LineMetrics { int gridlineWidth; - int underlineOffset; - int underlineOffset2; + int thinLineWidth; + int underlineCenter; int underlineWidth; + int doubleUnderlinePosTop; + int doubleUnderlinePosBottom; int strikethroughOffset; int strikethroughWidth; - int curlylinePeakHeight; + int curlyLineCenter; + int curlyLinePeriod; + int curlyLineControlPointOffset; }; LineMetrics _lineMetrics; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 222576390e2..30c4ad2ad2d 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -2,9 +2,10 @@ // Licensed under the MIT license. #include "precomp.h" -#include #include "gdirenderer.hpp" +#include + #include "../inc/unicode.hpp" #pragma hdrstop @@ -516,6 +517,7 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) // Return Value: // - S_OK or suitable GDI HRESULT error or E_FAIL for GDI errors in functions that don't reliably return a specific error code. [[nodiscard]] HRESULT GdiEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept +try { LOG_IF_FAILED(_FlushBufferLines()); @@ -531,38 +533,50 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) // Get the font size so we know the size of the rectangle lines we'll be inscribing. const auto fontWidth = _GetFontSize().width; const auto fontHeight = _GetFontSize().height; - const auto widthOfAllCells = fontWidth * gsl::narrow_cast(cchLine); + const auto widthOfAllCells = fontWidth * gsl::narrow_cast(cchLine); - const auto DrawLine = [=](const auto x, const auto y, const auto w, const auto h) { + const auto DrawLine = [=](const til::CoordType x, const til::CoordType y, const til::CoordType w, const til::CoordType h) { return PatBlt(_hdcMemoryContext, x, y, w, h, PATCOPY); }; - const auto DrawStrokedLine = [&](const til::CoordType x, const til::CoordType y, const unsigned w) { + const auto DrawStrokedLine = [&](const til::CoordType x, const til::CoordType y, const til::CoordType w) { RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); - RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, gsl::narrow_cast(x + w), y)); + RETURN_HR_IF(E_FAIL, !LineTo(_hdcMemoryContext, x + w, y)); return S_OK; }; - const auto DrawCurlyLine = [&](const til::CoordType x, const til::CoordType y, const size_t cCurlyLines) { - const auto curlyLineWidth = fontWidth; - const auto curlyLineHalfWidth = std::lround(curlyLineWidth / 2.0f); - const auto controlPointHeight = std::lround(3.5f * _lineMetrics.curlylinePeakHeight); - - // Each curlyLine requires 3 `POINT`s - const auto cPoints = gsl::narrow(3 * cCurlyLines); - std::vector points; - points.reserve(cPoints); - - auto start = x; - for (size_t i = 0; i < cCurlyLines; i++) + const auto DrawCurlyLine = [&](const til::CoordType begX, const til::CoordType y, const til::CoordType width) { + const auto period = _lineMetrics.curlyLinePeriod; + const auto halfPeriod = period / 2; + const auto controlPointOffset = _lineMetrics.curlyLineControlPointOffset; + + // To ensure proper continuity of the wavy line between cells of different line color + // this code starts/ends the line earlier/later than it should and then clips it. + // Clipping in GDI is expensive, but it was the easiest approach. + // I've noticed that subtracting -1px prevents missing pixels when GDI draws. They still + // occur at certain (small) font sizes, but I couldn't figure out how to prevent those. + const auto lineStart = ((begX - 1) / period) * period; + const auto lineEnd = begX + width; + + IntersectClipRect(_hdcMemoryContext, begX, ptTarget.y, begX + width, ptTarget.y + fontHeight); + const auto restoreRegion = wil::scope_exit([&]() { + // Luckily no one else uses clip regions. They're weird to use. + SelectClipRgn(_hdcMemoryContext, nullptr); + }); + + // You can assume that each cell has roughly 5 POINTs on average. 128 POINTs is 1KiB. + til::small_vector points; + + // This is the start point of the Bézier curve. + points.emplace_back(lineStart, y); + + for (auto x = lineStart; x < lineEnd; x += period) { - points.emplace_back(start + curlyLineHalfWidth, y - controlPointHeight); - points.emplace_back(start + curlyLineHalfWidth, y + controlPointHeight); - points.emplace_back(start + curlyLineWidth, y); - start += curlyLineWidth; + points.emplace_back(x + halfPeriod, y - controlPointOffset); + points.emplace_back(x + halfPeriod, y + controlPointOffset); + points.emplace_back(x + period, y); } - RETURN_HR_IF(E_FAIL, !MoveToEx(_hdcMemoryContext, x, y, nullptr)); - RETURN_HR_IF(E_FAIL, !PolyBezierTo(_hdcMemoryContext, points.data(), cPoints)); - return S_OK; + const auto cpt = gsl::narrow_cast(points.size()); + return PolyBezier(_hdcMemoryContext, points.data(), cpt); }; if (lines.test(GridLines::Left)) @@ -605,7 +619,6 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) RETURN_HR_IF(E_FAIL, !DrawLine(ptTarget.x, y, widthOfAllCells, _lineMetrics.strikethroughWidth)); } - // Create a pen matching the underline style. DWORD underlinePenType = PS_SOLID; if (lines.test(GridLines::DottedUnderline)) { @@ -615,8 +628,15 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) { underlinePenType = PS_DASH; } + + DWORD underlineWidth = _lineMetrics.underlineWidth; + if (lines.any(GridLines::DoubleUnderline, GridLines::CurlyUnderline)) + { + underlineWidth = _lineMetrics.thinLineWidth; + } + const LOGBRUSH brushProp{ .lbStyle = BS_SOLID, .lbColor = underlineColor }; - wil::unique_hpen hpen(ExtCreatePen(underlinePenType | PS_GEOMETRIC | PS_ENDCAP_FLAT, _lineMetrics.underlineWidth, &brushProp, 0, nullptr)); + wil::unique_hpen hpen(ExtCreatePen(underlinePenType | PS_GEOMETRIC | PS_ENDCAP_FLAT, underlineWidth, &brushProp, 0, nullptr)); // Apply the pen. const auto prevPen = wil::SelectObject(_hdcMemoryContext, hpen.get()); @@ -624,28 +644,29 @@ bool GdiEngine::FontHasWesternScript(HDC hdc) if (lines.test(GridLines::Underline)) { - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineCenter, widthOfAllCells); } else if (lines.test(GridLines::DoubleUnderline)) { - RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells)); - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset2, widthOfAllCells); + RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.doubleUnderlinePosTop, widthOfAllCells)); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.doubleUnderlinePosBottom, widthOfAllCells); } else if (lines.test(GridLines::CurlyUnderline)) { - return DrawCurlyLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, cchLine); + return DrawCurlyLine(ptTarget.x, ptTarget.y + _lineMetrics.curlyLineCenter, widthOfAllCells); } else if (lines.test(GridLines::DottedUnderline)) { - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineCenter, widthOfAllCells); } else if (lines.test(GridLines::DashedUnderline)) { - return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineOffset, widthOfAllCells); + return DrawStrokedLine(ptTarget.x, ptTarget.y + _lineMetrics.underlineCenter, widthOfAllCells); } return S_OK; } +CATCH_RETURN(); // Routine Description: // - Draws the cursor on the screen diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 13fd0ea59fc..f993c6f74e6 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -344,85 +344,109 @@ GdiEngine::~GdiEngine() // There is no font metric for the grid line width, so we use a small // multiple of the font size, which typically rounds to a pixel. - const auto fontSize = _tmFontMetrics.tmHeight - _tmFontMetrics.tmInternalLeading; - _lineMetrics.gridlineWidth = std::lround(fontSize * 0.025); + const auto cellHeight = static_cast(Font.GetSize().height); + const auto fontSize = static_cast(_tmFontMetrics.tmHeight - _tmFontMetrics.tmInternalLeading); + const auto baseline = static_cast(_tmFontMetrics.tmAscent); + float idealGridlineWidth = std::max(1.0f, fontSize * 0.025f); + float idealUnderlineTop = 0; + float idealUnderlineWidth = 0; + float idealStrikethroughTop = 0; + float idealStrikethroughWidth = 0; OUTLINETEXTMETRICW outlineMetrics; if (GetOutlineTextMetricsW(_hdcMemoryContext, sizeof(outlineMetrics), &outlineMetrics)) { // For TrueType fonts, the other line metrics can be obtained from // the font's outline text metric structure. - _lineMetrics.underlineOffset = outlineMetrics.otmsUnderscorePosition; - _lineMetrics.underlineWidth = outlineMetrics.otmsUnderscoreSize; - _lineMetrics.strikethroughOffset = outlineMetrics.otmsStrikeoutPosition; - _lineMetrics.strikethroughWidth = outlineMetrics.otmsStrikeoutSize; + idealUnderlineTop = static_cast(baseline - outlineMetrics.otmsUnderscorePosition); + idealUnderlineWidth = static_cast(outlineMetrics.otmsUnderscoreSize); + idealStrikethroughWidth = static_cast(outlineMetrics.otmsStrikeoutSize); + idealStrikethroughTop = static_cast(baseline - outlineMetrics.otmsStrikeoutPosition); } else { - // If we can't obtain the outline metrics for the font, we just pick - // some reasonable values for the offsets and widths. - _lineMetrics.underlineOffset = -std::lround(fontSize * 0.05); - _lineMetrics.underlineWidth = _lineMetrics.gridlineWidth; - _lineMetrics.strikethroughOffset = std::lround(_tmFontMetrics.tmAscent / 3.0); - _lineMetrics.strikethroughWidth = _lineMetrics.gridlineWidth; + // If we can't obtain the outline metrics for the font, we just pick some reasonable values for the offsets and widths. + idealUnderlineTop = std::max(1.0f, roundf(baseline - fontSize * 0.05f)); + idealUnderlineWidth = idealGridlineWidth; + idealStrikethroughTop = std::max(1.0f, roundf(baseline * (2.0f / 3.0f))); + idealStrikethroughWidth = idealGridlineWidth; } - // We always want the lines to be visible, so if a stroke width ends - // up being zero, we need to make it at least 1 pixel. - _lineMetrics.gridlineWidth = std::max(_lineMetrics.gridlineWidth, 1); - _lineMetrics.underlineWidth = std::max(_lineMetrics.underlineWidth, 1); - _lineMetrics.strikethroughWidth = std::max(_lineMetrics.strikethroughWidth, 1); - - // Offsets are relative to the base line of the font, so we subtract - // from the ascent to get an offset relative to the top of the cell. - const auto ascent = _tmFontMetrics.tmAscent; - _lineMetrics.underlineOffset = ascent - _lineMetrics.underlineOffset; - _lineMetrics.strikethroughOffset = ascent - _lineMetrics.strikethroughOffset; - - // For double underlines we need a second offset, just below the first, - // but with a bit of a gap (about double the grid line width). - _lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset + - _lineMetrics.underlineWidth + - std::lround(fontSize * 0.05); - - // However, we don't want the underline to extend past the bottom of the - // cell, so we clamp the offset to fit just inside. - const auto maxUnderlineOffset = Font.GetSize().height - _lineMetrics.underlineWidth; - _lineMetrics.underlineOffset2 = std::min(_lineMetrics.underlineOffset2, maxUnderlineOffset); - - // But if the resulting gap isn't big enough even to register as a thicker - // line, it's better to place the second line slightly above the first. - if (_lineMetrics.underlineOffset2 < _lineMetrics.underlineOffset + _lineMetrics.gridlineWidth) - { - _lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset - _lineMetrics.gridlineWidth; - } - - // Since we use GDI pen for drawing, the underline offset should point to - // the center of the underline. - const auto underlineHalfWidth = gsl::narrow_cast(std::floor(_lineMetrics.underlineWidth / 2.0f)); - _lineMetrics.underlineOffset += underlineHalfWidth; - _lineMetrics.underlineOffset2 += underlineHalfWidth; - - // Curlyline is drawn with a desired height relative to the font size. The - // baseline of curlyline is at the middle of singly underline. When there's - // limited space to draw a curlyline, we apply a limit on the peak height. - { - // initialize curlyline peak height to a desired value. Clamp it to at - // least 1. - constexpr auto curlyLinePeakHeightEm = 0.075f; - _lineMetrics.curlylinePeakHeight = gsl::narrow_cast(std::max(1L, std::lround(curlyLinePeakHeightEm * fontSize))); - - // calc the limit we need to apply - const auto maxDrawableCurlyLinePeakHeight = Font.GetSize().height - _lineMetrics.underlineOffset - _lineMetrics.underlineWidth; - - // if the limit is <= 0 (no height at all), stick with the desired height. - // This is how we force a curlyline even when there's no space, though it - // might be clipped at the bottom. - if (maxDrawableCurlyLinePeakHeight > 0.0f) - { - _lineMetrics.curlylinePeakHeight = std::min(_lineMetrics.curlylinePeakHeight, maxDrawableCurlyLinePeakHeight); - } - } + // GdiEngine::PaintBufferGridLines paints underlines using HPEN and LineTo, etc., which draws lines centered on the given coordinates. + // This means we need to shift the limit (cellHeight - underlineWidth) and offset (idealUnderlineTop) by half the width. + const auto underlineWidth = std::max(1.0f, roundf(idealUnderlineWidth)); + const auto underlineCenter = std::min(floorf(cellHeight - underlineWidth / 2.0f), roundf(idealUnderlineTop + underlineWidth / 2.0f)); + + const auto strikethroughWidth = std::max(1.0f, roundf(idealStrikethroughWidth)); + const auto strikethroughOffset = std::min(cellHeight - strikethroughWidth, roundf(idealStrikethroughTop)); + + // For double underlines we loosely follow what Word does: + // 1. The lines are half the width of an underline + // 2. Ideally the bottom line is aligned with the bottom of the underline + // 3. The top underline is vertically in the middle between baseline and ideal bottom underline + // 4. If the top line gets too close to the baseline the underlines are shifted downwards + // 5. The minimum gap between the two lines appears to be similar to Tex (1.2pt) + // (Additional notes below.) + + // 1. + const auto thinLineWidth = std::max(1.0f, roundf(idealUnderlineWidth / 2.0f)); + // 2. + auto doubleUnderlinePosBottom = underlineCenter + underlineWidth - thinLineWidth; + // 3. Since we don't align the center of our two lines, but rather the top borders + // we need to subtract half a line width from our center point. + auto doubleUnderlinePosTop = roundf((baseline + doubleUnderlinePosBottom - thinLineWidth) / 2.0f); + // 4. + doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + thinLineWidth); + // 5. The gap is only the distance _between_ the lines, but we need the distance from the + // top border of the top and bottom lines, which includes an additional line width. + const auto doubleUnderlineGap = std::max(1.0f, roundf(1.2f / 72.0f * _iCurrentDpi)); + doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + thinLineWidth); + // Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries. + doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, cellHeight - thinLineWidth); + + // The wave line is drawn using a cubic Bézier curve (PolyBezier), because that happens to be cheap with GDI. + // We use a Bézier curve where, if the start (a) and end (c) points are at (0,0) and (1,0), the control points are + // at (0.5,0.5) (b) and (0.5,-0.5) (d) respectively. Like this but a/b/c/d are square and the lines are round: + // + // b + // + // ^ + // / \ here's some text so the compiler ignores the trailing \ character + // a \ c + // \ / + // v + // + // d + // + // If you punch x=0.25 into the cubic bezier formula you get y=0.140625. This constant is + // important to us because it (plus the line width) tells us the amplitude of the wave. + // + // We can use the inverse of the constant to figure out how many px one period of the wave has to be to end up being 1px tall. + // In our case we want the amplitude of the wave to have a peak-to-peak amplitude that matches our double-underline. + const auto doubleUnderlineHalfDistance = 0.5f * (doubleUnderlinePosBottom - doubleUnderlinePosTop); + const auto doubleUnderlineCenter = doubleUnderlinePosTop + doubleUnderlineHalfDistance; + const auto curlyLineIdealAmplitude = std::max(1.0f, doubleUnderlineHalfDistance); + // Since GDI can't deal with fractional pixels, we first calculate the control point offsets (0.5 and -0.5) by multiplying by 0.5 and + // then undo that by multiplying by 2.0 for the period. This ensures that our control points can be at curlyLinePeriod/2, an integer. + const auto curlyLineControlPointOffset = roundf(curlyLineIdealAmplitude * (1.0f / 0.140625f) * 0.5f); + const auto curlyLinePeriod = curlyLineControlPointOffset * 2.0f; + // We can reverse the above to get back the actual amplitude of our Bézier curve. The line + // will be drawn with a width of thinLineWidth in the center of the curve (= 0.5x padding). + const auto curlyLineAmplitude = 0.140625f * curlyLinePeriod + 0.5f * thinLineWidth; + // To make the wavy line with its double-underline amplitude look consistent with the double-underline we position it at its center. + const auto curlyLineOffset = std::min(roundf(doubleUnderlineCenter), floorf(cellHeight - curlyLineAmplitude)); + + _lineMetrics.gridlineWidth = lroundf(idealGridlineWidth); + _lineMetrics.thinLineWidth = lroundf(thinLineWidth); + _lineMetrics.underlineCenter = lroundf(underlineCenter); + _lineMetrics.underlineWidth = lroundf(underlineWidth); + _lineMetrics.doubleUnderlinePosTop = lroundf(doubleUnderlinePosTop); + _lineMetrics.doubleUnderlinePosBottom = lroundf(doubleUnderlinePosBottom); + _lineMetrics.strikethroughOffset = lroundf(strikethroughOffset); + _lineMetrics.strikethroughWidth = lroundf(strikethroughWidth); + _lineMetrics.curlyLineCenter = lroundf(curlyLineOffset); + _lineMetrics.curlyLinePeriod = lroundf(curlyLinePeriod); + _lineMetrics.curlyLineControlPointOffset = lroundf(curlyLineControlPointOffset); // Now find the size of a 0 in this current font and save it for conversions done later. _coordFontLast = Font.GetSize(); diff --git a/src/tools/RenderingTests/main.cpp b/src/tools/RenderingTests/main.cpp index a82e38bb818..45dfcf66ea7 100644 --- a/src/tools/RenderingTests/main.cpp +++ b/src/tools/RenderingTests/main.cpp @@ -5,7 +5,39 @@ #include #include -#include +#include + +// The following list of colors is only used as a debug aid and not part of the final product. +// They're licensed under: +// +// Apache-Style Software License for ColorBrewer software and ColorBrewer Color Schemes +// +// Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +namespace colorbrewer +{ + inline constexpr uint32_t pastel1[]{ + 0xfbb4ae, + 0xb3cde3, + 0xccebc5, + 0xdecbe4, + 0xfed9a6, + 0xffffcc, + 0xe5d8bd, + 0xfddaec, + 0xf2f2f2, + }; +} // Another variant of "defer" for C++. namespace @@ -110,64 +142,93 @@ int main() }; { - struct ConsoleAttributeTest + struct AttributeTest { const wchar_t* text = nullptr; WORD attribute = 0; }; - static constexpr ConsoleAttributeTest consoleAttributeTests[]{ - { L"Console attributes:", 0 }, + + { + static constexpr AttributeTest consoleAttributeTests[]{ + { L"Console attributes:", 0 }, #define MAKE_TEST_FOR_ATTRIBUTE(attr) { L## #attr, attr } - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_HORIZONTAL), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_LVERTICAL), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_RVERTICAL), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_REVERSE_VIDEO), - MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_UNDERSCORE), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_HORIZONTAL), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_LVERTICAL), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_GRID_RVERTICAL), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_REVERSE_VIDEO), + MAKE_TEST_FOR_ATTRIBUTE(COMMON_LVB_UNDERSCORE), #undef MAKE_TEST_FOR_ATTRIBUTE - { L"all gridlines", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE }, - { L"all attributes", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_REVERSE_VIDEO | COMMON_LVB_UNDERSCORE }, - }; + { L"all gridlines", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE }, + { L"all attributes", COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_REVERSE_VIDEO | COMMON_LVB_UNDERSCORE }, + }; - SHORT row = 2; - for (const auto& t : consoleAttributeTests) - { - const auto length = static_cast(wcslen(t.text)); - printfUTF16(L"\x1B[%d;5H%s", row + 1, t.text); + SHORT row = 2; + for (const auto& t : consoleAttributeTests) + { + const auto length = static_cast(wcslen(t.text)); + printfUTF16(L"\x1B[%d;5H%s", row + 1, t.text); - WORD attributes[32]; - std::fill_n(&attributes[0], length, static_cast(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | t.attribute)); + WORD attributes[32]; + std::fill_n(&attributes[0], length, static_cast(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | t.attribute)); - DWORD numberOfAttrsWritten; - WriteConsoleOutputAttribute(outputHandle, attributes, length, { 4, row }, &numberOfAttrsWritten); + DWORD numberOfAttrsWritten; + WriteConsoleOutputAttribute(outputHandle, attributes, length, { 4, row }, &numberOfAttrsWritten); - row += 2; + row += 2; + } } - struct VTAttributeTest { - const wchar_t* text = nullptr; - int sgr = 0; - }; - static constexpr VTAttributeTest vtAttributeTests[]{ - { L"ANSI escape SGR:", 0 }, - { L"bold", 1 }, - { L"faint", 2 }, - { L"italic", 3 }, - { L"underline", 4 }, - { L"reverse", 7 }, - { L"strikethrough", 9 }, - { L"double underline", 21 }, - { L"overlined", 53 }, - }; + static constexpr AttributeTest basicSGR[]{ + { L"bold", 1 }, + { L"faint", 2 }, + { L"italic", 3 }, + { L"underline", 4 }, + { L"reverse", 7 }, + { L"strikethrough", 9 }, + { L"double underline", 21 }, + { L"overlined", 53 }, + }; + + printfUTF16(L"\x1B[3;39HANSI escape SGR:"); + + int row = 5; + for (const auto& t : basicSGR) + { + printfUTF16(L"\x1B[%d;39H\x1b[%dm%s\x1b[m", row, t.attribute, t.text); + row += 2; + } - row = 3; - for (const auto& t : vtAttributeTests) - { - printfUTF16(L"\x1B[%d;45H\x1b[%dm%s\x1b[m", row, t.sgr, t.text); - row += 2; + printfUTF16(L"\x1B[%d;39H\x1b]8;;https://example.com\x1b\\hyperlink\x1b]8;;\x1b\\", row); } - printfUTF16(L"\x1B[%d;45H\x1b]8;;https://example.com\x1b\\hyperlink\x1b]8;;\x1b\\", row); + { + static constexpr AttributeTest styledUnderlines[]{ + { L"straight", 1 }, + { L"double", 2 }, + { L"curly", 3 }, + { L"dotted", 4 }, + { L"dashed", 5 }, + }; + + printfUTF16(L"\x1B[3;63HStyled Underlines:"); + + int row = 5; + for (const auto& t : styledUnderlines) + { + printfUTF16(L"\x1B[%d;63H\x1b[4:%dm", row, t.attribute); + + const auto len = wcslen(t.text); + for (size_t i = 0; i < len; ++i) + { + const auto color = colorbrewer::pastel1[i % std::size(colorbrewer::pastel1)]; + printfUTF16(L"\x1B[58:2::%d:%d:%dm%c", (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, t.text[i]); + } + + printfUTF16(L"\x1b[m"); + row += 2; + } + } wait(); clear(); From b02316b37c78fd2efefdfba676a504e816176fdf Mon Sep 17 00:00:00 2001 From: Craig Loewen Date: Mon, 8 Jan 2024 11:20:37 -0500 Subject: [PATCH 058/603] Update similarIssues.yml to have a lower tolerance (#16530) The tolerance value for a similar repo was changed from 0.8 to 0.75. This is because I changed the backend service for this to use pinecone instead of Azure AI search (see here https://github.com/craigloewen-msft/GitGudIssues/commit/f72fa59e23c0b7501b613daea95371cb13831d42 ) and the metric changed as a result of that. They are slightly lower than they were before, so this should offset that. --- .github/workflows/similarIssues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/similarIssues.yml b/.github/workflows/similarIssues.yml index f3d17ac11b5..b199c3b7a80 100644 --- a/.github/workflows/similarIssues.yml +++ b/.github/workflows/similarIssues.yml @@ -15,7 +15,7 @@ jobs: with: issuetitle: ${{ github.event.issue.title }} repo: ${{ github.repository }} - similaritytolerance: "0.8" + similaritytolerance: "0.75" add-comment: needs: getSimilarIssues runs-on: ubuntu-latest From 375d00d0cd2c2934b0d136dcf7c295a5f99b07d5 Mon Sep 17 00:00:00 2001 From: Craig Loewen Date: Mon, 8 Jan 2024 15:30:41 -0500 Subject: [PATCH 059/603] Fix similarIssues.yml to not fail when no similar issues found (#16542) Added an if statement to similarIssues.yml so that the logic can be updated to not show as 'failure' when no similar issue is found. Related: https://github.com/craigloewen-msft/GitGudSimilarIssues/issues/33 --- .github/workflows/similarIssues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/similarIssues.yml b/.github/workflows/similarIssues.yml index b199c3b7a80..fa93ad8597d 100644 --- a/.github/workflows/similarIssues.yml +++ b/.github/workflows/similarIssues.yml @@ -21,6 +21,7 @@ jobs: runs-on: ubuntu-latest permissions: issues: write + if: needs.getSimilarIssues.outputs.message != '' steps: - name: Add comment run: gh issue comment "$NUMBER" --repo "$REPO" --body "$BODY" From c4c06dadadcc2b1b722944961bf12826ccad3a67 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 9 Jan 2024 12:11:14 -0800 Subject: [PATCH 060/603] Remove EDP auditing completely (#16460) This pull request started out very differently. I was going to move all the EDP code from the internal `conint` project into the public, because EDP is [fully documented]! Well, it doesn't have any headers in the SDK. Or import libraries. And it's got a deprecation notice: > [!NOTE] > Starting in July 2022, Microsoft is deprecating Windows Information > Protection (WIP) and the APIs that support WIP. Microsoft will continue > to support WIP on supported versions of Windows. New versions of Windows > won't include new capabilities for WIP, and it won't be supported in > future versions of Windows. So I'm blasting it out the airlock instead. [fully documented]: https://learn.microsoft.com/en-us/windows/win32/devnotes/windows-information-protection-api --- .github/actions/spelling/expect/expect.txt | 1 - src/host/res.rc | 3 --- src/host/resource.h | 1 - src/host/sources.inc | 2 -- src/inc/conint.h | 5 ----- src/interactivity/win32/Clipboard.cpp | 2 -- src/interactivity/win32/resource.h | 1 - src/interactivity/win32/ut_interactivity_win32/sources | 2 -- src/internal/stubs.cpp | 4 ---- src/propsheet/sources | 2 -- src/terminal/adapter/ut_adapter/sources | 2 -- src/terminal/parser/ut_parser/sources | 2 -- 12 files changed, 27 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 6ae7a458582..c3a8eb95f17 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -544,7 +544,6 @@ Edgium EDITKEYS EDITTEXT EDITUPDATE -edputil Efast efghijklmn EHsc diff --git a/src/host/res.rc b/src/host/res.rc index b35e9c87929..6f566147e33 100644 --- a/src/host/res.rc +++ b/src/host/res.rc @@ -61,9 +61,6 @@ BEGIN ID_CONSOLE_FMT_WINDOWTITLE, "%s%s" -/* WIP Audit destination name */ - ID_CONSOLE_WIP_DESTINATIONNAME, "console application" - /* Menu items that replace the standard ones. These don't have the accelerators */ SC_CLOSE, "&Close" diff --git a/src/host/resource.h b/src/host/resource.h index f81be52a827..9ea8cf549b6 100644 --- a/src/host/resource.h +++ b/src/host/resource.h @@ -28,7 +28,6 @@ Author(s): #define ID_CONSOLE_MSGMARKMODE 0x100C #define ID_CONSOLE_MSGSCROLLMODE 0x100D #define ID_CONSOLE_FMT_WINDOWTITLE 0x100E -#define ID_CONSOLE_WIP_DESTINATIONNAME 0x100F // Menu Item strings #define ID_CONSOLE_COPY 0xFFF0 diff --git a/src/host/sources.inc b/src/host/sources.inc index 92b2629aaf8..17fd8f8df14 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -138,7 +138,6 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3d11.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ @@ -206,7 +205,6 @@ DELAYLOAD = \ api-ms-win-shell-dataobject-l1.dll; \ api-ms-win-shell-namespace-l1.dll; \ ext-ms-win-dwmapi-ext-l1.dll; \ - ext-ms-win-edputil-policy-l1.dll; \ ext-ms-win-usp10-l1.dll; \ ext-ms-win-gdi-dc-l1.dll; \ ext-ms-win-gdi-dc-create-l1.dll; \ diff --git a/src/inc/conint.h b/src/inc/conint.h index 2a3c8d0fd54..495b390f2bb 100644 --- a/src/inc/conint.h +++ b/src/inc/conint.h @@ -35,11 +35,6 @@ namespace Microsoft::Console::Internal } - namespace EdpPolicy - { - void AuditClipboard(const std::wstring_view destinationName) noexcept; - } - namespace Theming { [[nodiscard]] HRESULT TrySetDarkMode(HWND hwnd) noexcept; diff --git a/src/interactivity/win32/Clipboard.cpp b/src/interactivity/win32/Clipboard.cpp index be97047404c..684eb6d70a1 100644 --- a/src/interactivity/win32/Clipboard.cpp +++ b/src/interactivity/win32/Clipboard.cpp @@ -75,8 +75,6 @@ void Clipboard::Paste() StringPaste(pwstr, (ULONG)GlobalSize(ClipboardDataHandle) / sizeof(WCHAR)); // WIP auditing if user is enrolled - static auto DestinationName = _LoadString(ID_CONSOLE_WIP_DESTINATIONNAME); - Microsoft::Console::Internal::EdpPolicy::AuditClipboard(DestinationName); GlobalUnlock(ClipboardDataHandle); diff --git a/src/interactivity/win32/resource.h b/src/interactivity/win32/resource.h index f453557218c..9b82b787ec0 100644 --- a/src/interactivity/win32/resource.h +++ b/src/interactivity/win32/resource.h @@ -25,7 +25,6 @@ Author(s): #define ID_CONSOLE_MSGMARKMODE 0x100C #define ID_CONSOLE_MSGSCROLLMODE 0x100D #define ID_CONSOLE_FMT_WINDOWTITLE 0x100E -#define ID_CONSOLE_WIP_DESTINATIONNAME 0x100F // Menu Item strings #define ID_CONSOLE_COPY 0xFFF0 diff --git a/src/interactivity/win32/ut_interactivity_win32/sources b/src/interactivity/win32/ut_interactivity_win32/sources index 0bfe9148eae..1f417647e44 100644 --- a/src/interactivity/win32/ut_interactivity_win32/sources +++ b/src/interactivity/win32/ut_interactivity_win32/sources @@ -51,7 +51,6 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3dcompiler.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ @@ -116,7 +115,6 @@ DELAYLOAD = \ api-ms-win-shell-dataobject-l1.dll; \ api-ms-win-shell-namespace-l1.dll; \ ext-ms-win-dwmapi-ext-l1.dll; \ - ext-ms-win-edputil-policy-l1.dll; \ ext-ms-win-gdi-dc-l1.dll; \ ext-ms-win-gdi-dc-create-l1.dll; \ ext-ms-win-gdi-draw-l1.dll; \ diff --git a/src/internal/stubs.cpp b/src/internal/stubs.cpp index f0cf811b017..3ac946a209a 100644 --- a/src/internal/stubs.cpp +++ b/src/internal/stubs.cpp @@ -21,10 +21,6 @@ using namespace Microsoft::Console::Internal; return S_OK; } -void EdpPolicy::AuditClipboard(const std::wstring_view /*destinationName*/) noexcept -{ -} - [[nodiscard]] HRESULT Theming::TrySetDarkMode(HWND /*hwnd*/) noexcept { return S_FALSE; diff --git a/src/propsheet/sources b/src/propsheet/sources index 648dd1d1c12..033d3a5396d 100644 --- a/src/propsheet/sources +++ b/src/propsheet/sources @@ -84,7 +84,6 @@ TARGETLIBS = \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\onecoreuap_internal.lib \ $(ONECOREUAP_INTERNAL_SDK_LIB_PATH)\onecoreuapuuid.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ @@ -109,7 +108,6 @@ TARGETLIBS = \ DELAYLOAD = \ ext-ms-win-dwmapi-ext-l1.dll; \ - ext-ms-win-edputil-policy-l1.dll; \ ext-ms-win-uxtheme-themes-l1.dll; \ ext-ms-win-shell32-shellfolders-l1.dll; \ ext-ms-win-gdi-dc-l1.dll; \ diff --git a/src/terminal/adapter/ut_adapter/sources b/src/terminal/adapter/ut_adapter/sources index 81cb43dcfaa..8b931860d9a 100644 --- a/src/terminal/adapter/ut_adapter/sources +++ b/src/terminal/adapter/ut_adapter/sources @@ -49,7 +49,6 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3dcompiler.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ @@ -111,7 +110,6 @@ DELAYLOAD = \ api-ms-win-shell-dataobject-l1.dll; \ api-ms-win-shell-namespace-l1.dll; \ ext-ms-win-dwmapi-ext-l1.dll; \ - ext-ms-win-edputil-policy-l1.dll; \ ext-ms-win-usp10-l1.dll; \ ext-ms-win-gdi-dc-l1.dll; \ ext-ms-win-gdi-dc-create-l1.dll; \ diff --git a/src/terminal/parser/ut_parser/sources b/src/terminal/parser/ut_parser/sources index e754a4e01c2..d4da3cf1d49 100644 --- a/src/terminal/parser/ut_parser/sources +++ b/src/terminal/parser/ut_parser/sources @@ -39,7 +39,6 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3dcompiler.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ @@ -102,7 +101,6 @@ DELAYLOAD = \ api-ms-win-shell-dataobject-l1.dll; \ api-ms-win-shell-namespace-l1.dll; \ ext-ms-win-dwmapi-ext-l1.dll; \ - ext-ms-win-edputil-policy-l1.dll; \ ext-ms-win-gdi-dc-l1.dll; \ ext-ms-win-gdi-dc-create-l1.dll; \ ext-ms-win-gdi-draw-l1.dll; \ From d115500cff5637bac27fff0875e19b87e480f826 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 9 Jan 2024 20:44:27 +0000 Subject: [PATCH 061/603] Enable alternate scroll mode by default (#16535) This PR enables alternate scroll mode by default, and also fixes the precedence so if there is any other mouse tracking mode enabled, that will take priority. ## Validation Steps Performed I've manually tested by viewing a file with `less`, and confirmed that it can now scroll using the mouse wheel by default. Also tested mouse mouse in vim and confirmed that still works. ## PR Checklist Closes #13187 --- src/terminal/input/mouseInput.cpp | 10 +++++----- src/terminal/input/terminalInput.cpp | 2 +- src/terminal/input/terminalInput.hpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/terminal/input/mouseInput.cpp b/src/terminal/input/mouseInput.cpp index ddcb81b7e9b..d17482216ca 100644 --- a/src/terminal/input/mouseInput.cpp +++ b/src/terminal/input/mouseInput.cpp @@ -334,11 +334,6 @@ TerminalInput::OutputType TerminalInput::HandleMouse(const til::point position, _mouseInputState.accumulatedDelta = 0; } - if (ShouldSendAlternateScroll(button, delta)) - { - return _makeAlternateScrollOutput(delta); - } - if (IsTrackingMouseInput()) { // isHover is only true for WM_MOUSEMOVE events @@ -392,6 +387,11 @@ TerminalInput::OutputType TerminalInput::HandleMouse(const til::point position, } } + if (ShouldSendAlternateScroll(button, delta)) + { + return _makeAlternateScrollOutput(delta); + } + return {}; } diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index 855ba74b28a..ae5e94ed22c 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -259,7 +259,7 @@ bool TerminalInput::GetInputMode(const Mode mode) const noexcept void TerminalInput::ResetInputModes() noexcept { - _inputMode = { Mode::Ansi, Mode::AutoRepeat }; + _inputMode = { Mode::Ansi, Mode::AutoRepeat, Mode::AlternateScroll }; _mouseInputState.lastPos = { -1, -1 }; _mouseInputState.lastButton = 0; } diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp index 1bb49cca7b2..17cf2a6fef3 100644 --- a/src/terminal/input/terminalInput.hpp +++ b/src/terminal/input/terminalInput.hpp @@ -70,7 +70,7 @@ namespace Microsoft::Console::VirtualTerminal std::optional _lastVirtualKeyCode; - til::enumset _inputMode{ Mode::Ansi, Mode::AutoRepeat }; + til::enumset _inputMode{ Mode::Ansi, Mode::AutoRepeat, Mode::AlternateScroll }; bool _forceDisableWin32InputMode{ false }; [[nodiscard]] OutputType _makeCharOutput(wchar_t ch); From fb8b1202153890775d7c3e64a0899d5533b20f8e Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 9 Jan 2024 22:00:32 +0100 Subject: [PATCH 062/603] Remove leftover telemetry code (#16468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This cleans up some leftover unused telemetry skeleton code. ## Validation Steps Performed * A TraceLogging viewing application shows events ✅ --- src/host/host-common.vcxitems | 2 -- src/host/lib/hostlib.vcxproj.filters | 6 ++---- src/host/precomp.h | 2 +- src/host/sources.inc | 1 - src/host/telemetry.cpp | 21 ------------------- src/host/telemetry.hpp | 17 --------------- src/interactivity/win32/menu.cpp | 1 - src/interactivity/win32/window.cpp | 1 - src/server/ApiDispatchers.cpp | 1 - src/server/ApiDispatchersInternal.cpp | 1 - src/server/IoDispatchers.cpp | 1 - src/server/ProcessHandle.cpp | 1 - src/terminal/adapter/lib/adapter.vcxproj | 6 +----- .../adapter/lib/adapter.vcxproj.filters | 13 +----------- src/terminal/adapter/precomp.h | 3 --- src/terminal/adapter/sources.inc | 2 -- src/terminal/adapter/telemetry.cpp | 6 ------ src/terminal/adapter/telemetry.hpp | 20 ------------------ src/terminal/adapter/tracing.cpp | 5 ----- src/terminal/adapter/tracing.hpp | 18 ---------------- src/terminal/parser/tracing.hpp | 2 -- 21 files changed, 5 insertions(+), 125 deletions(-) delete mode 100644 src/host/telemetry.cpp delete mode 100644 src/host/telemetry.hpp delete mode 100644 src/terminal/adapter/telemetry.cpp delete mode 100644 src/terminal/adapter/telemetry.hpp delete mode 100644 src/terminal/adapter/tracing.cpp delete mode 100644 src/terminal/adapter/tracing.hpp diff --git a/src/host/host-common.vcxitems b/src/host/host-common.vcxitems index 74d000c3c56..5ae8d7a0f3b 100644 --- a/src/host/host-common.vcxitems +++ b/src/host/host-common.vcxitems @@ -43,7 +43,6 @@ - @@ -96,7 +95,6 @@ - diff --git a/src/host/lib/hostlib.vcxproj.filters b/src/host/lib/hostlib.vcxproj.filters index ff328c21575..0e8177d4bc4 100644 --- a/src/host/lib/hostlib.vcxproj.filters +++ b/src/host/lib/hostlib.vcxproj.filters @@ -99,9 +99,6 @@ Source Files - - Source Files - Source Files @@ -320,5 +317,6 @@ + - + \ No newline at end of file diff --git a/src/host/precomp.h b/src/host/precomp.h index c7f41e06dfe..e28931ebaf4 100644 --- a/src/host/precomp.h +++ b/src/host/precomp.h @@ -66,7 +66,7 @@ Module Name: TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider); #include #include -#include "telemetry.hpp" + #include "tracing.hpp" #ifdef BUILDING_INSIDE_WINIDE diff --git a/src/host/sources.inc b/src/host/sources.inc index 17fd8f8df14..f17c5191ac8 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -72,7 +72,6 @@ SOURCES = \ ..\_output.cpp \ ..\_stream.cpp \ ..\utils.cpp \ - ..\telemetry.cpp \ ..\tracing.cpp \ ..\registry.cpp \ ..\settings.cpp \ diff --git a/src/host/telemetry.cpp b/src/host/telemetry.cpp deleted file mode 100644 index 9b1d7256799..00000000000 --- a/src/host/telemetry.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "telemetry.hpp" - -// This code remains to serve as template if we ever need telemetry for conhost again. -// The git history for this file may prove useful. -#if 0 -Telemetry::Telemetry() -{ - TraceLoggingRegister(g_hConhostV2EventTraceProvider); - TraceLoggingWriteStart(_activity, "ActivityStart"); -} - -Telemetry::~Telemetry() -{ - TraceLoggingWriteStop(_activity, "ActivityStop"); - TraceLoggingUnregister(g_hConhostV2EventTraceProvider); -} -#endif diff --git a/src/host/telemetry.hpp b/src/host/telemetry.hpp deleted file mode 100644 index 619205b9f7b..00000000000 --- a/src/host/telemetry.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#pragma once - -// This code remains to serve as template if we ever need telemetry for conhost again. -// The git history for this file may prove useful. -#if 0 -class Telemetry -{ -public: - Telemetry(); - ~Telemetry(); - -private: - TraceLoggingActivity _activity; -}; -#endif diff --git a/src/interactivity/win32/menu.cpp b/src/interactivity/win32/menu.cpp index 7b59887daaf..44e803931ff 100644 --- a/src/interactivity/win32/menu.cpp +++ b/src/interactivity/win32/menu.cpp @@ -14,7 +14,6 @@ #include "../../host/misc.h" #include "../../host/server.h" #include "../../host/scrolling.hpp" -#include "../../host/telemetry.hpp" #include "../inc/ServiceLocator.hpp" diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp index 599d1f376a7..03eed072c13 100644 --- a/src/interactivity/win32/window.cpp +++ b/src/interactivity/win32/window.cpp @@ -20,7 +20,6 @@ #include "../../host/scrolling.hpp" #include "../../host/srvinit.h" #include "../../host/stream.h" -#include "../../host/telemetry.hpp" #include "../../host/tracing.hpp" #include "../../renderer/base/renderer.hpp" diff --git a/src/server/ApiDispatchers.cpp b/src/server/ApiDispatchers.cpp index 0aa25057591..8039977b5e5 100644 --- a/src/server/ApiDispatchers.cpp +++ b/src/server/ApiDispatchers.cpp @@ -9,7 +9,6 @@ #include "../host/getset.h" #include "../host/stream.h" #include "../host/srvinit.h" -#include "../host/telemetry.hpp" #include "../host/cmdline.h" // Assumes that it will find in the calling environment. diff --git a/src/server/ApiDispatchersInternal.cpp b/src/server/ApiDispatchersInternal.cpp index bff2a62e8d2..cc4e7be3cdb 100644 --- a/src/server/ApiDispatchersInternal.cpp +++ b/src/server/ApiDispatchersInternal.cpp @@ -8,7 +8,6 @@ #include "../host/globals.h" #include "../host/handle.h" #include "../host/server.h" -#include "../host/telemetry.hpp" #include "../host/ntprivapi.hpp" diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index a2c3b175a38..664e8ebab85 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -12,7 +12,6 @@ #include "../host/directio.h" #include "../host/handle.h" #include "../host/srvinit.h" -#include "../host/telemetry.hpp" #include "../interactivity/base/HostSignalInputThread.hpp" #include "../interactivity/inc/ServiceLocator.hpp" diff --git a/src/server/ProcessHandle.cpp b/src/server/ProcessHandle.cpp index bcfd43d1928..eb5798d7648 100644 --- a/src/server/ProcessHandle.cpp +++ b/src/server/ProcessHandle.cpp @@ -6,7 +6,6 @@ #include "ProcessHandle.h" #include "../host/globals.h" -#include "../host/telemetry.hpp" // Routine Description: // - Constructs an instance of the ConsoleProcessHandle Class diff --git a/src/terminal/adapter/lib/adapter.vcxproj b/src/terminal/adapter/lib/adapter.vcxproj index 3ecefa5e838..2780c818dfa 100644 --- a/src/terminal/adapter/lib/adapter.vcxproj +++ b/src/terminal/adapter/lib/adapter.vcxproj @@ -16,9 +16,7 @@ - - Create @@ -32,12 +30,10 @@ - - @@ -50,4 +46,4 @@ - + \ No newline at end of file diff --git a/src/terminal/adapter/lib/adapter.vcxproj.filters b/src/terminal/adapter/lib/adapter.vcxproj.filters index 21524f61cd6..798522c651f 100644 --- a/src/terminal/adapter/lib/adapter.vcxproj.filters +++ b/src/terminal/adapter/lib/adapter.vcxproj.filters @@ -18,15 +18,9 @@ Source Files - - Source Files - Source Files - - Source Files - Source Files @@ -53,15 +47,9 @@ Header Files - - Header Files - Header Files - - Header Files - Header Files @@ -89,5 +77,6 @@ + \ No newline at end of file diff --git a/src/terminal/adapter/precomp.h b/src/terminal/adapter/precomp.h index 838240d9b83..abed7d0b9f6 100644 --- a/src/terminal/adapter/precomp.h +++ b/src/terminal/adapter/precomp.h @@ -19,7 +19,4 @@ Module Name: #include -#include "telemetry.hpp" -#include "tracing.hpp" - #include "../../inc/conattrs.hpp" diff --git a/src/terminal/adapter/sources.inc b/src/terminal/adapter/sources.inc index d1e63430bb4..a7a07a29814 100644 --- a/src/terminal/adapter/sources.inc +++ b/src/terminal/adapter/sources.inc @@ -36,8 +36,6 @@ SOURCES= \ ..\MacroBuffer.cpp \ ..\adaptDispatchGraphics.cpp \ ..\terminalOutput.cpp \ - ..\telemetry.cpp \ - ..\tracing.cpp \ INCLUDES = \ $(INCLUDES); \ diff --git a/src/terminal/adapter/telemetry.cpp b/src/terminal/adapter/telemetry.cpp deleted file mode 100644 index f4a63025a0e..00000000000 --- a/src/terminal/adapter/telemetry.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "telemetry.hpp" diff --git a/src/terminal/adapter/telemetry.hpp b/src/terminal/adapter/telemetry.hpp deleted file mode 100644 index 1a2ba41a862..00000000000 --- a/src/terminal/adapter/telemetry.hpp +++ /dev/null @@ -1,20 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- telemetry.hpp - -Abstract: -- This module is used for recording all telemetry feedback from the console virtual terminal parser - ---*/ -#pragma once - -// Including TraceLogging essentials for the binary -#include -#include -#include -#include - -TRACELOGGING_DECLARE_PROVIDER(g_hConsoleVirtTermParserEventTraceProvider); diff --git a/src/terminal/adapter/tracing.cpp b/src/terminal/adapter/tracing.cpp deleted file mode 100644 index b72682fe914..00000000000 --- a/src/terminal/adapter/tracing.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "tracing.hpp" diff --git a/src/terminal/adapter/tracing.hpp b/src/terminal/adapter/tracing.hpp deleted file mode 100644 index dad93792aed..00000000000 --- a/src/terminal/adapter/tracing.hpp +++ /dev/null @@ -1,18 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- tracing.hpp - -Abstract: -- This module is used for recording tracing/debugging information to the telemetry ETW channel -- The data is not automatically broadcast to telemetry backends as it does not set the TELEMETRY keyword. -- NOTE: Many functions in this file appear to be copy/pastes. This is because the TraceLog documentation warns - to not be "cute" in trying to reduce its macro usages with variables as it can cause unexpected behavior. - ---*/ - -#pragma once - -#include "telemetry.hpp" diff --git a/src/terminal/parser/tracing.hpp b/src/terminal/parser/tracing.hpp index ecc357c3236..56e449f06fe 100644 --- a/src/terminal/parser/tracing.hpp +++ b/src/terminal/parser/tracing.hpp @@ -18,8 +18,6 @@ Module Name: #include #include -TRACELOGGING_DECLARE_PROVIDER(g_hConsoleVirtTermParserEventTraceProvider); - namespace Microsoft::Console::VirtualTerminal { class ParserTracing sealed From 057183b651e254ff29a25a9c3ca4f56032c34048 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 10 Jan 2024 10:06:14 -0800 Subject: [PATCH 063/603] Update SUI Color Scheme colors' AutoProp.Name and ToolTip (#16544) In the Settings UI's Color Scheme page (where you edit the color scheme itself), update the color chip buttons to include the RGB value in the tooltip and screen reader announcements. Closes #15985 Closes #15983 ## Validation Steps Performed Tooltip and screen reader announcement is updated on launch and when a new value is selected. --- .../ColorSchemeViewModel.cpp | 14 ++++++++++++++ .../TerminalSettingsEditor/ColorSchemeViewModel.h | 7 +++++++ .../ColorSchemeViewModel.idl | 1 + .../TerminalSettingsEditor/EditColorScheme.xaml | 4 ++-- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp index 4bd995b6c4a..04e9433bbba 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp @@ -178,6 +178,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Name(TableColorNames[index]); Tag(winrt::box_value(index)); Color(color); + + PropertyChanged({ get_weak(), &ColorTableEntry::_PropertyChangedHandler }); } ColorTableEntry::ColorTableEntry(std::wstring_view tag, Windows::UI::Color color) @@ -185,5 +187,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Name(LocalizedNameForEnumName(L"ColorScheme_", tag, L"Text")); Tag(winrt::box_value(tag)); Color(color); + + PropertyChanged({ get_weak(), &ColorTableEntry::_PropertyChangedHandler }); + } + + void ColorTableEntry::_PropertyChangedHandler(const IInspectable& /*sender*/, const PropertyChangedEventArgs& args) + { + const auto propertyName{ args.PropertyName() }; + if (propertyName == L"Color" || propertyName == L"Name") + { + _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"AccessibleName" }); + } } + } diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h index 539b0636c3c..7c45638e709 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h @@ -63,6 +63,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation ColorTableEntry(uint8_t index, Windows::UI::Color color); ColorTableEntry(std::wstring_view tag, Windows::UI::Color color); + hstring AccessibleName() const + { + return hstring{ fmt::format(FMT_COMPILE(L"{} RGB({}, {}, {})"), _Name, _Color.R, _Color.G, _Color.B) }; + } + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); WINRT_OBSERVABLE_PROPERTY(Windows::UI::Color, Color, _PropertyChangedHandlers); WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers); @@ -70,5 +75,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation private: Windows::UI::Color _color; + + void _PropertyChangedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); }; }; diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.idl b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.idl index 3247ef125e3..b041c2ab93f 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.idl @@ -34,5 +34,6 @@ namespace Microsoft.Terminal.Settings.Editor String Name { get; }; IInspectable Tag; Windows.UI.Color Color; + String AccessibleName { get; }; } } diff --git a/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml b/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml index 8e68567bb82..c98f5c8d6c4 100644 --- a/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml +++ b/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml @@ -55,10 +55,10 @@ - - diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index baa8c0bccff..df9f4ae6f9c 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -135,6 +135,18 @@ Duplicate Button label that duplicates the selected profile and navigates to the duplicate's page. + + This color scheme is part of the built-in settings or an installed extension + + + + To make changes to this color scheme, you must make a copy of it. + + + + Make a copy + This is a call to action; the user can click the button to make a copy of the current color scheme. + Set color scheme as default This is the header for a control that allows the user to set the currently selected color scheme as their default. diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index 9f193851508..4180c8d8c38 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -44,6 +44,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::com_ptr baseLayerProfile; std::vector> profiles; std::unordered_map> profilesByGuid; + std::unordered_map> colorSchemes; + std::unordered_map colorSchemeRemappings; + bool fixupsAppliedDuringLoad{ false }; void clear(); }; @@ -60,6 +63,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content); void FinalizeLayering(); bool DisableDeletedProfiles(); + bool RemapColorSchemeForProfile(const winrt::com_ptr& profile); bool FixupUserSettings(); ParsedSettings inboxSettings; @@ -87,6 +91,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::com_ptr _parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson); void _appendProfile(winrt::com_ptr&& profile, const winrt::guid& guid, ParsedSettings& settings); void _addUserProfileParent(const winrt::com_ptr& profile); + void _addOrMergeUserColorScheme(const winrt::com_ptr& colorScheme); void _executeGenerator(const IDynamicProfileGenerator& generator); std::unordered_set _ignoredNamespaces; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index c22d421975a..e3796a9b7d6 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -113,6 +113,8 @@ void ParsedSettings::clear() baseLayerProfile = {}; profiles.clear(); profilesByGuid.clear(); + colorSchemes.clear(); + fixupsAppliedDuringLoad = false; } // This is a convenience method used by the CascadiaSettings constructor. @@ -123,6 +125,7 @@ SettingsLoader SettingsLoader::Default(const std::string_view& userJSON, const s SettingsLoader loader{ userJSON, inboxJSON }; loader.MergeInboxIntoUserSettings(); loader.FinalizeLayering(); + loader.FixupUserSettings(); return loader; } @@ -210,6 +213,7 @@ void SettingsLoader::ApplyRuntimeInitialSettings() // Adds profiles from .inboxSettings as parents of matching profiles in .userSettings. // That way the user profiles will get appropriate defaults from the generators (like icons and such). // If a matching profile doesn't exist yet in .userSettings, one will be created. +// Additionally, produces a final view of the color schemes from the inbox + user settings void SettingsLoader::MergeInboxIntoUserSettings() { for (const auto& profile : inboxSettings.profiles) @@ -333,6 +337,11 @@ void SettingsLoader::MergeFragmentIntoUserSettings(const winrt::hstring& source, // by MergeInboxIntoUserSettings/FindFragmentsAndMergeIntoUserSettings). void SettingsLoader::FinalizeLayering() { + for (const auto& colorScheme : inboxSettings.colorSchemes) + { + _addOrMergeUserColorScheme(colorScheme.second); + } + // Layer default globals -> user globals userSettings.globals->AddLeastImportantParent(inboxSettings.globals); @@ -403,6 +412,42 @@ bool SettingsLoader::DisableDeletedProfiles() return newGeneratedProfiles; } +bool winrt::Microsoft::Terminal::Settings::Model::implementation::SettingsLoader::RemapColorSchemeForProfile(const winrt::com_ptr& profile) +{ + bool modified{ false }; + + const IAppearanceConfig appearances[] = { + profile->DefaultAppearance(), + profile->UnfocusedAppearance() + }; + + for (auto&& appearance : appearances) + { + if (appearance) + { + if (auto schemeName{ appearance.LightColorSchemeName() }; !schemeName.empty()) + { + if (auto found{ userSettings.colorSchemeRemappings.find(schemeName) }; found != userSettings.colorSchemeRemappings.end()) + { + appearance.LightColorSchemeName(found->second); + modified = true; + } + } + + if (auto schemeName{ appearance.DarkColorSchemeName() }; !schemeName.empty()) + { + if (auto found{ userSettings.colorSchemeRemappings.find(schemeName) }; found != userSettings.colorSchemeRemappings.end()) + { + appearance.DarkColorSchemeName(found->second); + modified = true; + } + } + } + } + + return modified; +} + // Runs migrations and fixups on user settings. // Returns true if something got changed and // the settings need to be saved to disk. @@ -420,10 +465,13 @@ bool SettingsLoader::FixupUserSettings() CommandlinePatch{ DEFAULT_WINDOWS_POWERSHELL_GUID, L"powershell.exe", L"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" }, }; - auto fixedUp = false; + auto fixedUp = userSettings.fixupsAppliedDuringLoad; + fixedUp = RemapColorSchemeForProfile(userSettings.baseLayerProfile) || fixedUp; for (const auto& profile : userSettings.profiles) { + fixedUp = RemapColorSchemeForProfile(profile) || fixedUp; + if (!profile->HasCommandline()) { continue; @@ -574,7 +622,8 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source { if (const auto scheme = ColorScheme::FromJson(schemeJson)) { - settings.globals->AddColorScheme(*scheme); + scheme->Origin(origin); + settings.colorSchemes.emplace(scheme->Name(), std::move(scheme)); } } } @@ -642,7 +691,11 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str { if (const auto scheme = ColorScheme::FromJson(schemeJson)) { - settings.globals->AddColorScheme(*scheme); + scheme->Origin(OriginTag::Fragment); + // Don't add the color scheme to the Fragment's GlobalSettings; that will + // cause layering issues later. Add them to a staging area for later processing. + // (search for STAGED COLORS to find the next step) + settings.colorSchemes.emplace(scheme->Name(), std::move(scheme)); } } CATCH_LOG() @@ -693,6 +746,14 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str } } + // STAGED COLORS are processed here: we merge them into the partially-loaded + // settings directly so that we can resolve conflicts between user-generated + // color schemes and fragment-originated ones. + for (const auto& fragmentColorScheme : settings.colorSchemes) + { + _addOrMergeUserColorScheme(fragmentColorScheme.second); + } + // Add the parsed fragment globals as a parent of the user's settings. // Later, in FinalizeInheritance, this will result in the action map from // the fragments being applied before the user's own settings. @@ -796,6 +857,37 @@ void SettingsLoader::_addUserProfileParent(const winrt::com_ptr& newScheme) +{ + // On entry, all the user color schemes have been loaded. Therefore, any insertions of inbox or fragment schemes + // will fail; we can leverage this to detect when they are equivalent and delete the user's duplicate copies. + // If the user has changed the otherwise "duplicate" scheme, though, we will move it aside. + if (const auto [it, inserted] = userSettings.colorSchemes.emplace(newScheme->Name(), newScheme); !inserted) + { + // This scheme was not inserted because one already existed. + auto existingScheme{ it->second }; + if (existingScheme->Origin() == OriginTag::User) // we only want to impose ordering on User schemes + { + it->second = newScheme; // Stomp the user's existing scheme with the one we just got (to make sure the right Origin is set) + userSettings.fixupsAppliedDuringLoad = true; // Make sure we save the settings. + if (!existingScheme->IsEquivalentForSettingsMergePurposes(newScheme)) + { + hstring newName{ fmt::format(FMT_COMPILE(L"{} (modified)"), existingScheme->Name()) }; + int differentiator = 2; + while (userSettings.colorSchemes.contains(newName)) + { + newName = hstring{ fmt::format(FMT_COMPILE(L"{} (modified {})"), existingScheme->Name(), differentiator++) }; + } + // Rename the user's scheme. + existingScheme->Name(newName); + userSettings.colorSchemeRemappings.emplace(newScheme->Name(), newName); + // And re-add it to the end. + userSettings.colorSchemes.emplace(newName, std::move(existingScheme)); + } + } + } +} + // As the name implies it executes a generator. // Generated profiles are added to .inboxSettings. Used by GenerateProfiles(). void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator) @@ -1064,6 +1156,16 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) : allProfiles.reserve(loader.userSettings.profiles.size()); activeProfiles.reserve(loader.userSettings.profiles.size()); + for (const auto& colorScheme : loader.userSettings.colorSchemes) + { + loader.userSettings.globals->AddColorScheme(*colorScheme.second); + } + + // SettingsLoader and ParsedSettings are supposed to always + // create these two members. We don't want null-pointer exceptions. + assert(loader.userSettings.globals != nullptr); + assert(loader.userSettings.baseLayerProfile != nullptr); + for (const auto& profile : loader.userSettings.profiles) { // If a generator stops producing a certain profile (e.g. WSL or PowerShell were removed) or @@ -1101,11 +1203,6 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) : warnings.emplace_back(Model::SettingsLoadWarnings::DuplicateProfile); } - // SettingsLoader and ParsedSettings are supposed to always - // create these two members. We don't want null-pointer exceptions. - assert(loader.userSettings.globals != nullptr); - assert(loader.userSettings.baseLayerProfile != nullptr); - _globals = loader.userSettings.globals; _baseLayerProfile = loader.userSettings.baseLayerProfile; _allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles)); @@ -1271,14 +1368,14 @@ Json::Value CascadiaSettings::ToJson() const profiles[JsonKey(ProfilesListKey)] = profilesList; json[JsonKey(ProfilesKey)] = profiles; - // TODO GH#8100: - // "schemes" will be an accumulation of _all_ the color schemes - // including all of the ones from defaults.json Json::Value schemes{ Json::ValueType::arrayValue }; for (const auto& entry : _globals->ColorSchemes()) { const auto scheme{ winrt::get_self(entry.Value()) }; - schemes.append(scheme->ToJson()); + if (scheme->Origin() == OriginTag::User) + { + schemes.append(scheme->ToJson()); + } } json[JsonKey(SchemesKey)] = schemes; diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.cpp b/src/cascadia/TerminalSettingsModel/ColorScheme.cpp index 62f405e48fb..6b49fed7d65 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.cpp +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.cpp @@ -52,7 +52,8 @@ ColorScheme::ColorScheme() noexcept : } ColorScheme::ColorScheme(const winrt::hstring& name) noexcept : - _Name{ name } + _Name{ name }, + _Origin{ OriginTag::User } { const auto table = Utils::CampbellColorTable(); std::copy_n(table.data(), table.size(), _table.data()); @@ -67,6 +68,7 @@ winrt::com_ptr ColorScheme::Copy() const scheme->_SelectionBackground = _SelectionBackground; scheme->_CursorColor = _CursorColor; scheme->_table = _table; + scheme->_Origin = _Origin; return scheme; } @@ -188,3 +190,11 @@ winrt::Microsoft::Terminal::Core::Scheme ColorScheme::ToCoreScheme() const noexc coreScheme.BrightWhite = Table()[15]; return coreScheme; } + +bool ColorScheme::IsEquivalentForSettingsMergePurposes(const winrt::com_ptr& other) noexcept +{ + // The caller likely only got here if the names were the same, so skip checking that one. + // We do not care about the cursor color or the selection background, as the main reason we are + // doing equivalence merging is to replace old, poorly-specified versions of those two properties. + return _table == other->_table && _Background == other->_Background && _Foreground == other->_Foreground; +} diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.h b/src/cascadia/TerminalSettingsModel/ColorScheme.h index f72b7429fce..82d31cea81f 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.h +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.h @@ -51,7 +51,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation com_array Table() const noexcept; void SetColorTableEntry(uint8_t index, const Core::Color& value) noexcept; + bool IsEquivalentForSettingsMergePurposes(const winrt::com_ptr& other) noexcept; + WINRT_PROPERTY(winrt::hstring, Name); + WINRT_PROPERTY(OriginTag, Origin, OriginTag::None); WINRT_PROPERTY(Core::Color, Foreground, static_cast(DEFAULT_FOREGROUND)); WINRT_PROPERTY(Core::Color, Background, static_cast(DEFAULT_BACKGROUND)); WINRT_PROPERTY(Core::Color, SelectionBackground, static_cast(DEFAULT_FOREGROUND)); diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.idl b/src/cascadia/TerminalSettingsModel/ColorScheme.idl index 8e758cfa6b2..dd55624a530 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.idl +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.idl @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "ISettingsModelObject.idl"; + namespace Microsoft.Terminal.Settings.Model { - [default_interface] runtimeclass ColorScheme : Windows.Foundation.IStringable { + [default_interface] runtimeclass ColorScheme : Windows.Foundation.IStringable, ISettingsModelObject { ColorScheme(); ColorScheme(String name); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 5cc65d913e4..a720c261b39 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -9,6 +9,8 @@ #include "GlobalAppSettings.g.cpp" +#include + using namespace Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; using namespace winrt::Windows::UI::Xaml; @@ -34,13 +36,6 @@ void GlobalAppSettings::_FinalizeInheritance() { _actionMap->AddLeastImportantParent(parent->_actionMap); _keybindingsWarnings.insert(_keybindingsWarnings.end(), parent->_keybindingsWarnings.begin(), parent->_keybindingsWarnings.end()); - for (const auto& [k, v] : parent->_colorSchemes) - { - if (!_colorSchemes.HasKey(k)) - { - _colorSchemes.Insert(k, v); - } - } for (const auto& [k, v] : parent->_themes) { @@ -183,6 +178,28 @@ void GlobalAppSettings::RemoveColorScheme(hstring schemeName) _colorSchemes.TryRemove(schemeName); } +winrt::Microsoft::Terminal::Settings::Model::ColorScheme GlobalAppSettings::DuplicateColorScheme(const Model::ColorScheme& source) +{ + THROW_HR_IF_NULL(E_INVALIDARG, source); + + auto newName = fmt::format(FMT_COMPILE(L"{} ({})"), source.Name(), RS_(L"CopySuffix")); + auto nextCandidateIndex = 2; + + // Check if this name already exists and if so, append a number + while (_colorSchemes.HasKey(newName)) + { + // There is a theoretical unsigned integer wraparound, which is OK + newName = fmt::format(FMT_COMPILE(L"{} ({} {})"), source.Name(), RS_(L"CopySuffix"), nextCandidateIndex++); + } + + auto duplicated{ winrt::get_self(source)->Copy() }; + duplicated->Name(hstring{ newName }); + duplicated->Origin(OriginTag::User); + + AddColorScheme(*duplicated); + return *duplicated; +} + // Method Description: // - Return the warnings that we've collected during parsing the JSON for the // keybindings. It's possible that the user provided keybindings have some diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 1af8479e0c6..21d73483c02 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -44,6 +44,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Windows::Foundation::Collections::IMapView ColorSchemes() noexcept; void AddColorScheme(const Model::ColorScheme& scheme); void RemoveColorScheme(hstring schemeName); + Model::ColorScheme DuplicateColorScheme(const Model::ColorScheme& scheme); Model::ActionMap ActionMap() const noexcept; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 20cb1d90528..612262d4b11 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -106,6 +106,7 @@ namespace Microsoft.Terminal.Settings.Model Windows.Foundation.Collections.IMapView ColorSchemes(); void AddColorScheme(ColorScheme scheme); void RemoveColorScheme(String schemeName); + ColorScheme DuplicateColorScheme(ColorScheme scheme); ActionMap ActionMap { get; }; diff --git a/src/cascadia/TerminalSettingsModel/ISettingsModelObject.idl b/src/cascadia/TerminalSettingsModel/ISettingsModelObject.idl new file mode 100644 index 00000000000..21bc4b9c326 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/ISettingsModelObject.idl @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace Microsoft.Terminal.Settings.Model +{ + // This tag is used to identify the context in which this object was created + enum OriginTag + { + None = 0, + User, + InBox, + Generated, + Fragment, + ProfilesDefaults + }; + + interface ISettingsModelObject { + OriginTag Origin { get; }; + } +} diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index 2f77b745661..7c54f9e65a9 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -237,6 +237,7 @@ + diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 14e074bc6db..9023fd16edb 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -2,6 +2,7 @@ // Licensed under the MIT license. import "IAppearanceConfig.idl"; +import "ISettingsModelObject.idl"; import "FontConfig.idl"; #include "IInheritable.idl.h" @@ -13,17 +14,6 @@ import "FontConfig.idl"; namespace Microsoft.Terminal.Settings.Model { - // This tag is used to identify the context in which the Profile was created - enum OriginTag - { - None = 0, - User, - InBox, - Generated, - Fragment, - ProfilesDefaults - }; - enum CloseOnExitMode { Never = 0, @@ -42,7 +32,7 @@ namespace Microsoft.Terminal.Settings.Model All = 0xffffffff }; - [default_interface] runtimeclass Profile : Windows.Foundation.IStringable { + [default_interface] runtimeclass Profile : Windows.Foundation.IStringable, ISettingsModelObject { Profile(); Profile(Guid guid); @@ -51,7 +41,6 @@ namespace Microsoft.Terminal.Settings.Model // True if the user explicitly removed this Profile from settings.json. Boolean Deleted { get; }; - OriginTag Origin { get; }; // Helper for magically using a commandline for an icon for a profile // without an explicit icon. From e7796e7db3662c9edc5a4306e647602f07690166 Mon Sep 17 00:00:00 2001 From: Comzyh Date: Thu, 29 Feb 2024 00:34:40 +0800 Subject: [PATCH 135/603] Fix the hyperlink detection when there are leading wide glyph in the row (#16775) ## Summary of the Pull Request URL detection was broken again in #15858. When the regex matched, we calculate the column(cell) by its offset, we use forward or backward iteration of the column to find the correct column that displays the glyphs of `_chars[offset]`. https://github.com/microsoft/terminal/blob/abf5d9423a23b13e9af4c19ca35858f9aaf0a63f/src/buffer/out/Row.cpp#L95-L104 However, when calculating the `currentOffset` we forget that MSB of `_charOffsets[col]` could be `1`, or col is pointing to another glyph in preceding column. https://github.com/microsoft/terminal/blob/abf5d9423a23b13e9af4c19ca35858f9aaf0a63f/src/buffer/out/Row.hpp#L223-L226 --- src/buffer/out/Row.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 87ab005d3bb..7729cdcdbd4 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -97,7 +97,7 @@ til::CoordType CharToColumnMapper::GetLeadingColumnAt(ptrdiff_t offset) noexcept offset = clamp(offset, 0, _lastCharOffset); auto col = _currentColumn; - const auto currentOffset = _charOffsets[col]; + const auto currentOffset = _charOffsets[col] & CharOffsetsMask; // Goal: Move the _currentColumn cursor to a cell which contains the given target offset. // Depending on where the target offset is we have to either search forward or backward. From badc00e83bf8f4109f40e9533e8d9d894abfc531 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 28 Feb 2024 17:57:22 +0100 Subject: [PATCH 136/603] Add new and extend existing til::string helpers (#16772) `wstring_case_insensitive_compare` is not a great name for what it does as it's incorrect to use for regular (human readable) strings. This PR thus renames it to `env_key_sorter`. `compare_string_ordinal` was renamed to `compare_ordinal_insensitive` to make sure callers know that the comparison is insensitive (this may otherwise be incorrect in certain contexts after all). The return value was changed to match `memcmp` so that the API is detached from its underlying implementation (= NLS). `compare_linguistic_insensitive` and `contains_linguistic_insensitive` were added to sort and filter human-readable strings respectively. `prefix_split` was extended to allow for needles that are just a single character. This significantly improves the generated assembly and is also usually what someone would want to actually use. I've left the string-as-needle variant in just in case. This PR is prep-work for #2664 --- .../TerminalConnection/ConptyConnection.cpp | 2 +- .../TerminalSettingsEditor/Appearances.h | 8 -- .../ProfileViewModel.cpp | 11 +- .../TerminalSettingsEditor/ProfileViewModel.h | 1 - .../CascadiaSettings.cpp | 5 +- .../KeyChordSerialization.cpp | 2 +- src/inc/til/env.h | 28 +++- src/inc/til/string.h | 124 +++++++++++++----- src/til/ut_til/string.cpp | 18 +++ 9 files changed, 141 insertions(+), 58 deletions(-) diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index 67c3e5699bd..d5789a2b3e5 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -98,7 +98,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation if (_environment) { // Order the environment variable names so that resolution order is consistent - std::set keys{}; + std::set keys{}; for (const auto item : _environment) { keys.insert(item.Key().c_str()); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index 4f220ef5e78..f89d0fa2305 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -25,14 +25,6 @@ Author(s): namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { - struct FontComparator - { - bool operator()(const Font& lhs, const Font& rhs) const - { - return lhs.LocalizedName() < rhs.LocalizedName(); - } - }; - struct Font : FontT { public: diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index 5f7517068b4..4b1ee724ad9 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -5,6 +5,7 @@ #include "ProfileViewModel.h" #include "ProfileViewModel.g.cpp" #include "EnumEntry.h" +#include "Appearances.h" #include #include "../WinRTUtils/inc/Utils.h" @@ -154,11 +155,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation CATCH_LOG(); } + const auto comparator = [&](const Editor::Font& lhs, const Editor::Font& rhs) { + const auto a = lhs.LocalizedName(); + const auto b = rhs.LocalizedName(); + return til::compare_linguistic_insensitive(a, b) < 0; + }; + // sort and save the lists - std::sort(begin(fontList), end(fontList), FontComparator()); + std::sort(begin(fontList), end(fontList), comparator); _FontList = single_threaded_observable_vector(std::move(fontList)); - std::sort(begin(monospaceFontList), end(monospaceFontList), FontComparator()); + std::sort(begin(monospaceFontList), end(monospaceFontList), comparator); _MonospaceFontList = single_threaded_observable_vector(std::move(monospaceFontList)); } CATCH_LOG(); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index 7770c1b2f3a..c7c836f818b 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -8,7 +8,6 @@ #include "ProfileViewModel.g.h" #include "Utils.h" #include "ViewModelHelpers.h" -#include "Appearances.h" namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index ad9ac6df600..5971d1e7b99 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -13,9 +13,8 @@ #include #include -#include #include -#include +#include using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings; @@ -555,7 +554,7 @@ void CascadiaSettings::_validateProfileEnvironmentVariables() { for (const auto& profile : _allProfiles) { - std::set envVarNames{}; + std::set envVarNames{}; if (profile.EnvironmentVariables() == nullptr) { continue; diff --git a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp index c4a5a0cfec0..b54aa289afc 100644 --- a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp @@ -153,7 +153,7 @@ static KeyChord _fromString(std::wstring_view wstr) while (!wstr.empty()) { - const auto part = til::prefix_split(wstr, L"+"); + const auto part = til::prefix_split(wstr, L'+'); if (til::equals_insensitive_ascii(part, CTRL_KEY)) { diff --git a/src/inc/til/env.h b/src/inc/til/env.h index 1546a83afb6..33d7fa70e40 100644 --- a/src/inc/til/env.h +++ b/src/inc/til/env.h @@ -19,6 +19,22 @@ class EnvTests; namespace til // Terminal Implementation Library. Also: "Today I Learned" { + // A case-insensitive wide-character map is used to store environment variables + // due to documented requirements: + // + // "All strings in the environment block must be sorted alphabetically by name. + // The sort is case-insensitive, Unicode order, without regard to locale. + // Because the equal sign is a separator, it must not be used in the name of + // an environment variable." + // https://docs.microsoft.com/en-us/windows/desktop/ProcThread/changing-environment-variables + struct env_key_sorter + { + [[nodiscard]] bool operator()(const std::wstring& lhs, const std::wstring& rhs) const noexcept + { + return compare_ordinal_insensitive(lhs, rhs) < 0; + } + }; + namespace details { @@ -161,7 +177,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" friend class ::EnvTests; #endif - std::map _envMap{}; + std::map _envMap{}; // We make copies of the environment variable names to ensure they are null terminated. void get(wil::zwstring_view variable) @@ -348,8 +364,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" { static constexpr std::wstring_view temp{ L"temp" }; static constexpr std::wstring_view tmp{ L"tmp" }; - if (til::compare_string_ordinal(var, temp) == CSTR_EQUAL || - til::compare_string_ordinal(var, tmp) == CSTR_EQUAL) + if (til::compare_ordinal_insensitive(var, temp) == 0 || + til::compare_ordinal_insensitive(var, tmp) == 0) { return til::details::wil_env::GetShortPathNameW(value.data()); } @@ -364,9 +380,9 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" static constexpr std::wstring_view path{ L"Path" }; static constexpr std::wstring_view libPath{ L"LibPath" }; static constexpr std::wstring_view os2LibPath{ L"Os2LibPath" }; - return til::compare_string_ordinal(input, path) == CSTR_EQUAL || - til::compare_string_ordinal(input, libPath) == CSTR_EQUAL || - til::compare_string_ordinal(input, os2LibPath) == CSTR_EQUAL; + return til::compare_ordinal_insensitive(input, path) == 0 || + til::compare_ordinal_insensitive(input, libPath) == 0 || + til::compare_ordinal_insensitive(input, os2LibPath) == 0; } void parse(const wchar_t* lastCh) diff --git a/src/inc/til/string.h b/src/inc/til/string.h index c32de1e0531..e76639000cf 100644 --- a/src/inc/til/string.h +++ b/src/inc/til/string.h @@ -315,65 +315,117 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" // * return "foo" // If the needle cannot be found the "str" argument is returned as is. template - std::basic_string_view prefix_split(std::basic_string_view& str, const std::basic_string_view& needle) noexcept + constexpr std::basic_string_view prefix_split(std::basic_string_view& str, const std::basic_string_view& needle) noexcept { using view_type = std::basic_string_view; - const auto idx = str.find(needle); - // > If the needle cannot be found the "str" argument is returned as is. - // ...but if needle is empty, idx will always be npos, forcing us to return str. - if (idx == view_type::npos || needle.empty()) - { - return std::exchange(str, {}); - } + const auto needleLen = needle.size(); + const auto idx = needleLen == 0 ? str.size() : str.find(needle); + const auto prefixIdx = std::min(str.size(), idx); + const auto suffixIdx = std::min(str.size(), prefixIdx + needle.size()); - const auto suffixIdx = idx + needle.size(); - const view_type result{ str.data(), idx }; + const view_type result{ str.data(), prefixIdx }; #pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead str = { str.data() + suffixIdx, str.size() - suffixIdx }; return result; } - inline std::string_view prefix_split(std::string_view& str, const std::string_view& needle) noexcept + constexpr std::string_view prefix_split(std::string_view& str, const std::string_view& needle) noexcept { return prefix_split<>(str, needle); } - inline std::wstring_view prefix_split(std::wstring_view& str, const std::wstring_view& needle) noexcept + constexpr std::wstring_view prefix_split(std::wstring_view& str, const std::wstring_view& needle) noexcept { return prefix_split<>(str, needle); } - // - // A case-insensitive wide-character map is used to store environment variables - // due to documented requirements: - // - // "All strings in the environment block must be sorted alphabetically by name. - // The sort is case-insensitive, Unicode order, without regard to locale. - // Because the equal sign is a separator, it must not be used in the name of - // an environment variable." - // https://docs.microsoft.com/en-us/windows/desktop/ProcThread/changing-environment-variables - // - // - Returns CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN - [[nodiscard]] inline int compare_string_ordinal(const std::wstring_view& lhs, const std::wstring_view& rhs) noexcept - { - const auto result = CompareStringOrdinal( - lhs.data(), - ::base::saturated_cast(lhs.size()), - rhs.data(), - ::base::saturated_cast(rhs.size()), - TRUE); - FAIL_FAST_LAST_ERROR_IF(!result); + // Give the arguments ("foo bar baz", " "), this method will + // * modify the first argument to "bar baz" + // * return "foo" + // If the needle cannot be found the "str" argument is returned as is. + template + constexpr std::basic_string_view prefix_split(std::basic_string_view& str, T ch) noexcept + { + using view_type = std::basic_string_view; + + const auto idx = str.find(ch); + const auto prefixIdx = std::min(str.size(), idx); + const auto suffixIdx = std::min(str.size(), prefixIdx + 1); + + const view_type result{ str.data(), prefixIdx }; +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead + str = { str.data() + suffixIdx, str.size() - suffixIdx }; return result; } - struct wstring_case_insensitive_compare + template + constexpr std::basic_string_view trim(const std::basic_string_view& str, const T ch) noexcept { - [[nodiscard]] bool operator()(const std::wstring& lhs, const std::wstring& rhs) const noexcept + auto beg = str.data(); + auto end = beg + str.size(); + + for (; beg != end && *beg == ch; ++beg) { - return compare_string_ordinal(lhs, rhs) == CSTR_LESS_THAN; } - }; + + for (; beg != end && end[-1] == ch; --end) + { + } + + return { beg, end }; + } + + // This function is appropriate for case-insensitive equivalence testing of file paths and other "system" strings. + // Similar to memcmp, this returns <0, 0 or >0. + inline int compare_ordinal_insensitive(const std::wstring_view& lhs, const std::wstring_view& rhs) noexcept + { + const auto lhsLen = ::base::saturated_cast(lhs.size()); + const auto rhsLen = ::base::saturated_cast(rhs.size()); + // MSDN: + // > To maintain the C runtime convention of comparing strings, + // > the value 2 can be subtracted from a nonzero return value. + // > [...] + // > The function returns 0 if it does not succeed. [...] following error codes: + // > * ERROR_INVALID_PARAMETER. Any of the parameter values was invalid. + // -> We can just subtract 2. + return CompareStringOrdinal(lhs.data(), lhsLen, rhs.data(), rhsLen, TRUE) - 2; + } + + // This function is appropriate for sorting strings primarily used for human consumption, like a list of file names. + // Similar to memcmp, this returns <0, 0 or >0. + inline int compare_linguistic_insensitive(const std::wstring_view& lhs, const std::wstring_view& rhs) noexcept + { + const auto lhsLen = ::base::saturated_cast(lhs.size()); + const auto rhsLen = ::base::saturated_cast(rhs.size()); + // MSDN: + // > To maintain the C runtime convention of comparing strings, + // > the value 2 can be subtracted from a nonzero return value. + // > [...] + // > The function returns 0 if it does not succeed. [...] following error codes: + // > * ERROR_INVALID_FLAGS. The values supplied for flags were invalid. + // > * ERROR_INVALID_PARAMETER. Any of the parameter values was invalid. + // -> We can just subtract 2. +#pragma warning(suppress : 26477) // Use 'nullptr' rather than 0 or NULL (es.47). + return CompareStringEx(LOCALE_NAME_USER_DEFAULT, LINGUISTIC_IGNORECASE, lhs.data(), lhsLen, rhs.data(), rhsLen, nullptr, nullptr, 0) - 2; + } + + // This function is appropriate for strings primarily used for human consumption, like a list of file names. + inline bool contains_linguistic_insensitive(const std::wstring_view& str, const std::wstring_view& needle) noexcept + { + const auto strLen = ::base::saturated_cast(str.size()); + const auto needleLen = ::base::saturated_cast(needle.size()); + // MSDN: + // > Returns a 0-based index into the source string indicated by lpStringSource if successful. + // > [...] + // > The function returns -1 if it does not succeed. + // > * ERROR_INVALID_FLAGS. The values supplied for flags were not valid. + // > * ERROR_INVALID_PARAMETER. Any of the parameter values was invalid. + // > * ERROR_SUCCESS. The action completed successfully but yielded no results. + // -> We can just check for -1. +#pragma warning(suppress : 26477) // Use 'nullptr' rather than 0 or NULL (es.47). + return FindNLSStringEx(LOCALE_NAME_USER_DEFAULT, LINGUISTIC_IGNORECASE, str.data(), strLen, needle.data(), needleLen, nullptr, nullptr, nullptr, 0) != -1; + } // Implement to_int in terms of to_ulong by negating its result. to_ulong does not expect // to be passed signed numbers and will return an error accordingly. That error when diff --git a/src/til/ut_til/string.cpp b/src/til/ut_til/string.cpp index 76bb6198b58..26fbf92ea47 100644 --- a/src/til/ut_til/string.cpp +++ b/src/til/ut_til/string.cpp @@ -170,6 +170,24 @@ class StringTests } } + TEST_METHOD(prefix_split_char) + { + { + std::string_view s{ "" }; + VERIFY_ARE_EQUAL("", til::prefix_split(s, ' ')); + VERIFY_ARE_EQUAL("", s); + } + { + std::string_view s{ "foo bar baz" }; + VERIFY_ARE_EQUAL("foo", til::prefix_split(s, ' ')); + VERIFY_ARE_EQUAL("bar baz", s); + VERIFY_ARE_EQUAL("bar", til::prefix_split(s, ' ')); + VERIFY_ARE_EQUAL("baz", s); + VERIFY_ARE_EQUAL("baz", til::prefix_split(s, ' ')); + VERIFY_ARE_EQUAL("", s); + } + } + TEST_METHOD(CleanPathAndFilename) { VERIFY_ARE_EQUAL(LR"(CUsersGeddyMusicAnalog Man)", til::clean_filename(LR"(C:\Users\Geddy\Music\"Analog Man")")); From 94e74d22c65ecec7fb3baacb837527f254bddf1f Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 28 Feb 2024 18:25:58 +0100 Subject: [PATCH 137/603] Make shaded block glyphs look even betterer (#16760) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shaded glyphs (U+2591..3, etc.) all have one problem in common: The cell size may not be evenly divisible by the pixel/dot size in the glyph. This either results in blurring, or in moiré-like patterns at the edges of the cells with its neighbors, because they happen to start with a pattern that overlaps with the end of the previous cell. This PR solves the issue by moving the pixel/dot pattern generation into the shader. That way the pixel/dot location can be made dependent on the viewport-position of the actual underlying pixels, which avoids repeating patterns between cells. The PR contains some additional modifications, all of which either extend or improve the existing debug facilities in AtlasEngine. Suppressing whitespaces changes makes the diff way smaller. --- src/renderer/atlas/AtlasEngine.api.cpp | 20 +-- src/renderer/atlas/Backend.h | 12 ++ src/renderer/atlas/BackendD3D.cpp | 193 ++++++++++++++----------- src/renderer/atlas/BackendD3D.h | 24 +-- src/renderer/atlas/BuiltinGlyphs.cpp | 75 +++------- src/renderer/atlas/dwrite.hlsl | 4 +- src/renderer/atlas/shader_common.hlsl | 21 ++- src/renderer/atlas/shader_ps.hlsl | 86 ++++++++++- src/renderer/gdi/gdirenderer.hpp | 2 +- src/renderer/gdi/paint.cpp | 2 +- src/renderer/gdi/state.cpp | 18 +-- 11 files changed, 270 insertions(+), 187 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index fa7290371e8..30cac1da931 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -688,10 +688,11 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo const auto underlineWidth = std::max(1.0f, std::roundf(underlineThickness)); const auto strikethroughPos = std::roundf(baseline + strikethroughPosition); const auto strikethroughWidth = std::max(1.0f, std::roundf(strikethroughThickness)); - const auto thinLineWidth = std::max(1.0f, std::roundf(underlineThickness / 2.0f)); + const auto doubleUnderlineWidth = std::max(1.0f, std::roundf(underlineThickness / 2.0f)); + const auto thinLineWidth = std::max(1.0f, std::roundf(std::max(adjustedWidth / 16.0f, adjustedHeight / 32.0f))); // For double underlines we loosely follow what Word does: - // 1. The lines are half the width of an underline (= thinLineWidth) + // 1. The lines are half the width of an underline (= doubleUnderlineWidth) // 2. Ideally the bottom line is aligned with the bottom of the underline // 3. The top underline is vertically in the middle between baseline and ideal bottom underline // 4. If the top line gets too close to the baseline the underlines are shifted downwards @@ -699,18 +700,18 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo // (Additional notes below.) // 2. - auto doubleUnderlinePosBottom = underlinePos + underlineWidth - thinLineWidth; + auto doubleUnderlinePosBottom = underlinePos + underlineWidth - doubleUnderlineWidth; // 3. Since we don't align the center of our two lines, but rather the top borders // we need to subtract half a line width from our center point. - auto doubleUnderlinePosTop = std::roundf((baseline + doubleUnderlinePosBottom - thinLineWidth) / 2.0f); + auto doubleUnderlinePosTop = std::roundf((baseline + doubleUnderlinePosBottom - doubleUnderlineWidth) / 2.0f); // 4. - doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + thinLineWidth); + doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + doubleUnderlineWidth); // 5. The gap is only the distance _between_ the lines, but we need the distance from the // top border of the top and bottom lines, which includes an additional line width. const auto doubleUnderlineGap = std::max(1.0f, std::roundf(1.2f / 72.0f * dpi)); - doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + thinLineWidth); + doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + doubleUnderlineWidth); // Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries. - doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, adjustedHeight - thinLineWidth); + doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, adjustedHeight - doubleUnderlineWidth); const auto cellWidth = gsl::narrow(lrintf(adjustedWidth)); const auto cellHeight = gsl::narrow(lrintf(adjustedHeight)); @@ -749,6 +750,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo const auto strikethroughWidthU16 = gsl::narrow_cast(lrintf(strikethroughWidth)); const auto doubleUnderlinePosTopU16 = gsl::narrow_cast(lrintf(doubleUnderlinePosTop)); const auto doubleUnderlinePosBottomU16 = gsl::narrow_cast(lrintf(doubleUnderlinePosBottom)); + const auto doubleUnderlineWidthU16 = gsl::narrow_cast(lrintf(doubleUnderlineWidth)); // NOTE: From this point onward no early returns or throwing code should exist, // as we might cause _api to be in an inconsistent state otherwise. @@ -771,8 +773,8 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo fontMetrics->underline = { underlinePosU16, underlineWidthU16 }; fontMetrics->strikethrough = { strikethroughPosU16, strikethroughWidthU16 }; - fontMetrics->doubleUnderline[0] = { doubleUnderlinePosTopU16, thinLineWidthU16 }; - fontMetrics->doubleUnderline[1] = { doubleUnderlinePosBottomU16, thinLineWidthU16 }; + fontMetrics->doubleUnderline[0] = { doubleUnderlinePosTopU16, doubleUnderlineWidthU16 }; + fontMetrics->doubleUnderline[1] = { doubleUnderlinePosBottomU16, doubleUnderlineWidthU16 }; fontMetrics->overline = { 0, underlineWidthU16 }; fontMetrics->builtinGlyphs = fontInfoDesired.GetEnableBuiltinGlyphs(); diff --git a/src/renderer/atlas/Backend.h b/src/renderer/atlas/Backend.h index 9f2736eceef..ad22c1b20dd 100644 --- a/src/renderer/atlas/Backend.h +++ b/src/renderer/atlas/Backend.h @@ -7,6 +7,14 @@ namespace Microsoft::Console::Render::Atlas { + // Don't use this definition in the code elsewhere. + // It only exists to make the definitions below possible. +#ifdef NDEBUG +#define ATLAS_DEBUG__IS_DEBUG 0 +#else +#define ATLAS_DEBUG__IS_DEBUG 1 +#endif + // If set to 1, this will cause the entire viewport to be invalidated at all times. // Helpful for benchmarking our text shaping code based on DirectWrite. #define ATLAS_DEBUG_DISABLE_PARTIAL_INVALIDATION 0 @@ -14,6 +22,10 @@ namespace Microsoft::Console::Render::Atlas // Redraw at display refresh rate at all times. This helps with shader debugging. #define ATLAS_DEBUG_CONTINUOUS_REDRAW 0 + // Hot reload the builtin .hlsl files whenever they change on disk. + // Enabled by default in debug builds. +#define ATLAS_DEBUG_SHADER_HOT_RELOAD ATLAS_DEBUG__IS_DEBUG + // Disables the use of DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. // This helps with benchmarking the application as it'll run beyond display refresh rate. #define ATLAS_DEBUG_DISABLE_FRAME_LATENCY_WAITABLE_OBJECT 0 diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index dd4dcbf0c22..308518ec27e 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -25,6 +25,8 @@ TIL_FAST_MATH_BEGIN +#pragma warning(disable : 4100) // '...': unreferenced formal parameter +#pragma warning(disable : 26440) // Function '...' can be declared 'noexcept'(f.6). // This code packs various data into smaller-than-int types to save both CPU and GPU memory. This warning would force // us to add dozens upon dozens of gsl::narrow_cast<>s throughout the file which is more annoying than helpful. #pragma warning(disable : 4242) // '=': conversion from '...' to '...', possible loss of data @@ -158,7 +160,7 @@ BackendD3D::BackendD3D(const RenderingPayload& p) THROW_IF_FAILED(p.device->CreateBlendState(&desc, _blendState.addressof())); } -#ifndef NDEBUG +#if ATLAS_DEBUG_SHADER_HOT_RELOAD _sourceDirectory = std::filesystem::path{ __FILE__ }.parent_path(); _sourceCodeWatcher = wil::make_folder_change_reader_nothrow(_sourceDirectory.c_str(), false, wil::FolderChangeEvents::FileName | wil::FolderChangeEvents::LastWriteTime, [this](wil::FolderChangeEvent, PCWSTR path) { if (til::ends_with(path, L".hlsl")) @@ -186,9 +188,7 @@ void BackendD3D::Render(RenderingPayload& p) _handleSettingsUpdate(p); } -#ifndef NDEBUG _debugUpdateShaders(p); -#endif // After a Present() the render target becomes unbound. p.deviceContext->OMSetRenderTargets(1, _customRenderTargetView ? _customRenderTargetView.addressof() : _renderTargetView.addressof(), nullptr); @@ -205,9 +205,7 @@ void BackendD3D::Render(RenderingPayload& p) _drawCursorBackground(p); _drawText(p); _drawSelection(p); -#if ATLAS_DEBUG_SHOW_DIRTY _debugShowDirty(p); -#endif _flushQuads(p); if (_customPixelShader) @@ -215,9 +213,7 @@ void BackendD3D::Render(RenderingPayload& p) _executeCustomShader(p); } -#if ATLAS_DEBUG_DUMP_RENDER_TARGET _debugDumpRenderTarget(p); -#endif } bool BackendD3D::RequiresContinuousRedraw() noexcept @@ -277,13 +273,15 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p) // limited space to draw a curlyline, we apply a limit on the peak height. { const auto cellHeight = static_cast(font.cellSize.y); - const auto strokeWidth = static_cast(font.thinLineWidth); + const auto duTop = static_cast(font.doubleUnderline[0].position); + const auto duBottom = static_cast(font.doubleUnderline[1].position); + const auto duHeight = static_cast(font.doubleUnderline[0].height); // This gives it the same position and height as our double-underline. There's no particular reason for that, apart from // it being simple to implement and robust against more peculiar fonts with unusually large/small descenders, etc. // We still need to ensure though that it doesn't clip out of the cellHeight at the bottom. - const auto height = std::max(3.0f, static_cast(font.doubleUnderline[1].position + font.doubleUnderline[1].height - font.doubleUnderline[0].position)); - const auto top = std::min(static_cast(font.doubleUnderline[0].position), floorf(cellHeight - height - strokeWidth)); + const auto height = std::max(3.0f, duBottom + duHeight - duTop); + const auto top = std::min(duTop, floorf(cellHeight - height - duHeight)); _curlyLineHalfHeight = height * 0.5f; _curlyUnderline.position = gsl::narrow_cast(lrintf(top)); @@ -532,8 +530,9 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const DWrite_GetGammaRatios(_gamma, data.gammaRatios); data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast; data.underlineWidth = p.s->font->underline.height; - data.thinLineWidth = p.s->font->thinLineWidth; + data.doubleUnderlineWidth = p.s->font->doubleUnderline[0].height; data.curlyLineHalfHeight = _curlyLineHalfHeight; + data.shadedGlyphDotSize = std::max(1.0f, std::roundf(std::max(p.s->font->cellSize.x / 16.0f, p.s->font->cellSize.y / 32.0f))); p.deviceContext->UpdateSubresource(_psConstantBuffer.get(), 0, nullptr, &data, 0, 0); } } @@ -570,92 +569,101 @@ void BackendD3D::_setupDeviceContextState(const RenderingPayload& p) p.deviceContext->OMSetRenderTargets(1, _customRenderTargetView ? _customRenderTargetView.addressof() : _renderTargetView.addressof(), nullptr); } -#ifndef NDEBUG void BackendD3D::_debugUpdateShaders(const RenderingPayload& p) noexcept -try { - const auto invalidationTime = _sourceCodeInvalidationTime.load(std::memory_order_relaxed); - - if (invalidationTime == INT64_MAX || invalidationTime > std::chrono::steady_clock::now().time_since_epoch().count()) +#if ATLAS_DEBUG_SHADER_HOT_RELOAD + try { - return; - } - - _sourceCodeInvalidationTime.store(INT64_MAX, std::memory_order_relaxed); + const auto invalidationTime = _sourceCodeInvalidationTime.load(std::memory_order_relaxed); - static const auto compile = [](const std::filesystem::path& path, const char* target) { - wil::com_ptr error; - wil::com_ptr blob; - const auto hr = D3DCompileFromFile( - /* pFileName */ path.c_str(), - /* pDefines */ nullptr, - /* pInclude */ D3D_COMPILE_STANDARD_FILE_INCLUDE, - /* pEntrypoint */ "main", - /* pTarget */ target, - /* Flags1 */ D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS, - /* Flags2 */ 0, - /* ppCode */ blob.addressof(), - /* ppErrorMsgs */ error.addressof()); - - if (error) + if (invalidationTime == INT64_MAX || invalidationTime > std::chrono::steady_clock::now().time_since_epoch().count()) { - std::thread t{ [error = std::move(error)]() noexcept { - MessageBoxA(nullptr, static_cast(error->GetBufferPointer()), "Compilation error", MB_ICONERROR | MB_OK); - } }; - t.detach(); + return; } - THROW_IF_FAILED(hr); - return blob; - }; + _sourceCodeInvalidationTime.store(INT64_MAX, std::memory_order_relaxed); - struct FileVS - { - std::wstring_view filename; - wil::com_ptr BackendD3D::*target; - }; - struct FilePS - { - std::wstring_view filename; - wil::com_ptr BackendD3D::*target; - }; + static constexpr auto flags = + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS +#ifndef NDEBUG + | D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION +#endif + ; + + static const auto compile = [](const std::filesystem::path& path, const char* target) { + wil::com_ptr error; + wil::com_ptr blob; + const auto hr = D3DCompileFromFile( + /* pFileName */ path.c_str(), + /* pDefines */ nullptr, + /* pInclude */ D3D_COMPILE_STANDARD_FILE_INCLUDE, + /* pEntrypoint */ "main", + /* pTarget */ target, + /* Flags1 */ flags, + /* Flags2 */ 0, + /* ppCode */ blob.addressof(), + /* ppErrorMsgs */ error.addressof()); - static constexpr std::array filesVS{ - FileVS{ L"shader_vs.hlsl", &BackendD3D::_vertexShader }, - }; - static constexpr std::array filesPS{ - FilePS{ L"shader_ps.hlsl", &BackendD3D::_pixelShader }, - }; + if (error) + { + std::thread t{ [error = std::move(error)]() noexcept { + MessageBoxA(nullptr, static_cast(error->GetBufferPointer()), "Compilation error", MB_ICONERROR | MB_OK); + } }; + t.detach(); + } - std::array, filesVS.size()> compiledVS; - std::array, filesPS.size()> compiledPS; + THROW_IF_FAILED(hr); + return blob; + }; - // Compile our files before moving them into `this` below to ensure we're - // always in a consistent state where all shaders are seemingly valid. - for (size_t i = 0; i < filesVS.size(); ++i) - { - const auto blob = compile(_sourceDirectory / filesVS[i].filename, "vs_4_0"); - THROW_IF_FAILED(p.device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, compiledVS[i].addressof())); - } - for (size_t i = 0; i < filesPS.size(); ++i) - { - const auto blob = compile(_sourceDirectory / filesPS[i].filename, "ps_4_0"); - THROW_IF_FAILED(p.device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, compiledPS[i].addressof())); - } + struct FileVS + { + std::wstring_view filename; + wil::com_ptr BackendD3D::*target; + }; + struct FilePS + { + std::wstring_view filename; + wil::com_ptr BackendD3D::*target; + }; - for (size_t i = 0; i < filesVS.size(); ++i) - { - this->*filesVS[i].target = std::move(compiledVS[i]); - } - for (size_t i = 0; i < filesPS.size(); ++i) - { - this->*filesPS[i].target = std::move(compiledPS[i]); - } + static constexpr std::array filesVS{ + FileVS{ L"shader_vs.hlsl", &BackendD3D::_vertexShader }, + }; + static constexpr std::array filesPS{ + FilePS{ L"shader_ps.hlsl", &BackendD3D::_pixelShader }, + }; - _setupDeviceContextState(p); -} -CATCH_LOG() + std::array, filesVS.size()> compiledVS; + std::array, filesPS.size()> compiledPS; + + // Compile our files before moving them into `this` below to ensure we're + // always in a consistent state where all shaders are seemingly valid. + for (size_t i = 0; i < filesVS.size(); ++i) + { + const auto blob = compile(_sourceDirectory / filesVS[i].filename, "vs_4_0"); + THROW_IF_FAILED(p.device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, compiledVS[i].addressof())); + } + for (size_t i = 0; i < filesPS.size(); ++i) + { + const auto blob = compile(_sourceDirectory / filesPS[i].filename, "ps_4_0"); + THROW_IF_FAILED(p.device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, compiledPS[i].addressof())); + } + + for (size_t i = 0; i < filesVS.size(); ++i) + { + this->*filesVS[i].target = std::move(compiledVS[i]); + } + for (size_t i = 0; i < filesPS.size(); ++i) + { + this->*filesPS[i].target = std::move(compiledPS[i]); + } + + _setupDeviceContextState(p); + } + CATCH_LOG() #endif +} void BackendD3D::_d2dBeginDrawing() noexcept { @@ -980,6 +988,11 @@ void BackendD3D::_drawText(RenderingPayload& p) } } + const u8x2 renditionScale{ + static_cast(row->lineRendition != LineRendition::SingleWidth ? 2 : 1), + static_cast(row->lineRendition >= LineRendition::DoubleHeightTop ? 2 : 1), + }; + for (const auto& m : row->mappings) { auto x = m.glyphsFrom; @@ -1028,6 +1041,7 @@ void BackendD3D::_drawText(RenderingPayload& p) _appendQuad() = { .shadingType = static_cast(glyphEntry->shadingType), + .renditionScale = renditionScale, .position = { static_cast(l), static_cast(t) }, .size = glyphEntry->size, .texcoord = glyphEntry->texcoord, @@ -1394,6 +1408,8 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa _drawGlyphAtlasAllocate(p, rect); _d2dBeginDrawing(); + auto shadingType = ShadingType::TextGrayscale; + if (BuiltinGlyphs::IsSoftFontChar(glyphIndex)) { _drawSoftFontGlyph(p, rect, glyphIndex); @@ -1407,10 +1423,11 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa static_cast(rect.y + rect.h), }; BuiltinGlyphs::DrawBuiltinGlyph(p.d2dFactory.get(), _d2dRenderTarget.get(), _brush.get(), r, glyphIndex); + shadingType = ShadingType::TextBuiltinGlyph; } const auto glyphEntry = _drawGlyphAllocateEntry(row, fontFaceEntry, glyphIndex); - glyphEntry->shadingType = ShadingType::TextGrayscale; + glyphEntry->shadingType = shadingType; glyphEntry->overlapSplit = 0; glyphEntry->offset.x = 0; glyphEntry->offset.y = -baseline; @@ -2023,9 +2040,9 @@ void BackendD3D::_drawSelection(const RenderingPayload& p) } } -#if ATLAS_DEBUG_SHOW_DIRTY void BackendD3D::_debugShowDirty(const RenderingPayload& p) { +#if ATLAS_DEBUG_SHOW_DIRTY _presentRects[_presentRectsPos] = p.dirtyRectInPx; _presentRectsPos = (_presentRectsPos + 1) % std::size(_presentRects); @@ -2048,12 +2065,12 @@ void BackendD3D::_debugShowDirty(const RenderingPayload& p) }; } } -} #endif +} -#if ATLAS_DEBUG_DUMP_RENDER_TARGET void BackendD3D::_debugDumpRenderTarget(const RenderingPayload& p) { +#if ATLAS_DEBUG_DUMP_RENDER_TARGET if (_dumpRenderTargetCounter == 0) { ExpandEnvironmentStringsW(ATLAS_DEBUG_DUMP_RENDER_TARGET_PATH, &_dumpRenderTargetBasePath[0], gsl::narrow_cast(std::size(_dumpRenderTargetBasePath))); @@ -2064,8 +2081,8 @@ void BackendD3D::_debugDumpRenderTarget(const RenderingPayload& p) swprintf_s(path, L"%s\\%u_%08zu.png", &_dumpRenderTargetBasePath[0], GetCurrentProcessId(), _dumpRenderTargetCounter); SaveTextureToPNG(p.deviceContext.get(), _swapChainManager.GetBuffer().get(), p.s->font->dpi, &path[0]); _dumpRenderTargetCounter++; -} #endif +} void BackendD3D::_executeCustomShader(RenderingPayload& p) { diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 3fddb161d49..1ae6b139b4c 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -42,8 +42,9 @@ namespace Microsoft::Console::Render::Atlas alignas(sizeof(f32x4)) f32 gammaRatios[4]{}; alignas(sizeof(f32)) f32 enhancedContrast = 0; alignas(sizeof(f32)) f32 underlineWidth = 0; - alignas(sizeof(f32)) f32 thinLineWidth = 0; + alignas(sizeof(f32)) f32 doubleUnderlineWidth = 0; alignas(sizeof(f32)) f32 curlyLineHalfHeight = 0; + alignas(sizeof(f32)) f32 shadedGlyphDotSize = 0; #pragma warning(suppress : 4324) // 'PSConstBuffer': structure was padded due to alignment specifier }; @@ -64,17 +65,18 @@ namespace Microsoft::Console::Render::Atlas // This block of values will be used for the TextDrawingFirst/Last range and need to stay together. // This is used to quickly check if an instance is related to a "text drawing primitive". - TextGrayscale = 1, - TextClearType = 2, - TextPassthrough = 3, - DottedLine = 4, - DashedLine = 5, - CurlyLine = 6, + TextGrayscale, + TextClearType, + TextBuiltinGlyph, + TextPassthrough, + DottedLine, + DashedLine, + CurlyLine, // All items starting here will be drawing as a solid RGBA color - SolidLine = 7, + SolidLine, - Cursor = 8, - Selection = 9, + Cursor, + Selection, TextDrawingFirst = TextGrayscale, TextDrawingLast = SolidLine, @@ -305,7 +307,7 @@ namespace Microsoft::Console::Render::Atlas size_t _colorizeGlyphAtlasCounter = 0; #endif -#ifndef NDEBUG +#if ATLAS_DEBUG_SHADER_HOT_RELOAD std::filesystem::path _sourceDirectory; wil::unique_folder_change_reader_nothrow _sourceCodeWatcher; std::atomic _sourceCodeInvalidationTime{ INT64_MAX }; diff --git a/src/renderer/atlas/BuiltinGlyphs.cpp b/src/renderer/atlas/BuiltinGlyphs.cpp index 541d5690324..c46bfc01fc0 100644 --- a/src/renderer/atlas/BuiltinGlyphs.cpp +++ b/src/renderer/atlas/BuiltinGlyphs.cpp @@ -1071,57 +1071,6 @@ static const Instruction* GetInstructions(char32_t codepoint) noexcept return nullptr; } -static wil::com_ptr createShadedBitmapBrush(ID2D1DeviceContext* renderTarget, Shape shape) -{ - static constexpr u32 _ = 0; - static constexpr u32 w = 0xffffffff; - static constexpr u32 size = 4; - // clang-format off - static constexpr u32 shades[3][size * size] = { - { - w, _, _, _, - w, _, _, _, - _, _, w, _, - _, _, w, _, - }, - { - w, _, w, _, - _, w, _, w, - w, _, w, _, - _, w, _, w, - }, - { - _, w, w, w, - _, w, w, w, - w, w, _, w, - w, w, _, w, - }, - }; - // clang-format on - - static constexpr D2D1_SIZE_U bitmapSize{ size, size }; - static constexpr D2D1_BITMAP_PROPERTIES bitmapProps{ - .pixelFormat = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED }, - .dpiX = 96, - .dpiY = 96, - }; - static constexpr D2D1_BITMAP_BRUSH_PROPERTIES bitmapBrushProps{ - .extendModeX = D2D1_EXTEND_MODE_WRAP, - .extendModeY = D2D1_EXTEND_MODE_WRAP, - .interpolationMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR - }; - - assert(shape < ARRAYSIZE(shades)); - - wil::com_ptr bitmap; - THROW_IF_FAILED(renderTarget->CreateBitmap(bitmapSize, &shades[shape][0], sizeof(u32) * size, &bitmapProps, bitmap.addressof())); - - wil::com_ptr bitmapBrush; - THROW_IF_FAILED(renderTarget->CreateBitmapBrush(bitmap.get(), &bitmapBrushProps, nullptr, bitmapBrush.addressof())); - - return bitmapBrush; -} - void BuiltinGlyphs::DrawBuiltinGlyph(ID2D1Factory* factory, ID2D1DeviceContext* renderTarget, ID2D1SolidColorBrush* brush, const D2D1_RECT_F& rect, char32_t codepoint) { renderTarget->PushAxisAlignedClip(&rect, D2D1_ANTIALIAS_MODE_ALIASED); @@ -1188,16 +1137,28 @@ void BuiltinGlyphs::DrawBuiltinGlyph(ID2D1Factory* factory, ID2D1DeviceContext* case Shape_Filled025: case Shape_Filled050: case Shape_Filled075: - { - const D2D1_RECT_F r{ begXabs, begYabs, endXabs, endYabs }; - const auto bitmapBrush = createShadedBitmapBrush(renderTarget, shape); - renderTarget->FillRectangle(&r, bitmapBrush.get()); - break; - } case Shape_Filled100: { + // This code works in tandem with SHADING_TYPE_TEXT_BUILTIN_GLYPH in our pixel shader. + // Unless someone removed it, it should have a lengthy comment visually explaining + // what each of the 3 RGB components do. The short version is: + // R: stretch the checkerboard pattern (Shape_Filled050) horizontally + // G: invert the pixels + // B: overrides the above and fills it + static constexpr D2D1_COLOR_F colors[] = { + { 1, 0, 0, 1 }, // Shape_Filled025 + { 0, 0, 0, 1 }, // Shape_Filled050 + { 1, 1, 0, 1 }, // Shape_Filled075 + { 1, 1, 1, 1 }, // Shape_Filled100 + }; + + const auto brushColor = brush->GetColor(); + brush->SetColor(&colors[shape]); + const D2D1_RECT_F r{ begXabs, begYabs, endXabs, endYabs }; renderTarget->FillRectangle(&r, brush); + + brush->SetColor(&brushColor); break; } case Shape_LightLine: diff --git a/src/renderer/atlas/dwrite.hlsl b/src/renderer/atlas/dwrite.hlsl index ad7493275b7..955b9f57e5c 100644 --- a/src/renderer/atlas/dwrite.hlsl +++ b/src/renderer/atlas/dwrite.hlsl @@ -37,12 +37,12 @@ float3 DWrite_EnhanceContrast3(float3 alpha, float k) float DWrite_ApplyAlphaCorrection(float a, float f, float4 g) { - return a + a * (1 - a) * ((g.x * f + g.y) * a + (g.z * f + g.w)); + return a + a * (1.0f - a) * ((g.x * f + g.y) * a + (g.z * f + g.w)); } float3 DWrite_ApplyAlphaCorrection3(float3 a, float3 f, float4 g) { - return a + a * (1 - a) * ((g.x * f + g.y) * a + (g.z * f + g.w)); + return a + a * (1.0f - a) * ((g.x * f + g.y) * a + (g.z * f + g.w)); } // Call this function to get the same gamma corrected alpha blending effect diff --git a/src/renderer/atlas/shader_common.hlsl b/src/renderer/atlas/shader_common.hlsl index d712f081f28..51eb524b86b 100644 --- a/src/renderer/atlas/shader_common.hlsl +++ b/src/renderer/atlas/shader_common.hlsl @@ -1,15 +1,22 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -// clang-format off +// Depends on the background texture #define SHADING_TYPE_TEXT_BACKGROUND 0 + +// Depends on the glyphAtlas texture #define SHADING_TYPE_TEXT_GRAYSCALE 1 #define SHADING_TYPE_TEXT_CLEARTYPE 2 -#define SHADING_TYPE_TEXT_PASSTHROUGH 3 -#define SHADING_TYPE_DOTTED_LINE 4 -#define SHADING_TYPE_DASHED_LINE 5 -#define SHADING_TYPE_CURLY_LINE 6 -// clang-format on +#define SHADING_TYPE_TEXT_BUILTIN_GLYPH 3 +#define SHADING_TYPE_TEXT_PASSTHROUGH 4 + +// Independent of any textures +#define SHADING_TYPE_DOTTED_LINE 5 +#define SHADING_TYPE_DASHED_LINE 6 +#define SHADING_TYPE_CURLY_LINE 7 +#define SHADING_TYPE_SOLID_LINE 8 +#define SHADING_TYPE_CURSOR 9 +#define SHADING_TYPE_SELECTION 10 struct VSData { @@ -27,7 +34,7 @@ struct PSData float4 position : SV_Position; float2 texcoord : texcoord; nointerpolation uint shadingType : shadingType; - nointerpolation uint2 renditionScale : renditionScale; + nointerpolation float2 renditionScale : renditionScale; nointerpolation float4 color : color; }; diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl index d3c0bfac6c3..d8d894be13e 100644 --- a/src/renderer/atlas/shader_ps.hlsl +++ b/src/renderer/atlas/shader_ps.hlsl @@ -12,8 +12,9 @@ cbuffer ConstBuffer : register(b0) float4 gammaRatios; float enhancedContrast; float underlineWidth; - float thinLineWidth; + float doubleUnderlineWidth; float curlyLineHalfHeight; + float shadedGlyphDotSize; } Texture2D background : register(t0); @@ -67,6 +68,87 @@ Output main(PSData data) : SV_Target color = weights * data.color; break; } + case SHADING_TYPE_TEXT_BUILTIN_GLYPH: + { + // The RGB components of builtin glyphs are used to control the generation of pixel patterns in this shader. + // Below you can see their intended effects where # indicates lit pixels. + // + // .r = stretch + // 0: #_#_#_#_ + // _#_#_#_# + // #_#_#_#_ + // _#_#_#_# + // + // 1: #___#___ + // __#___#_ + // #___#___ + // __#___#_ + // + // .g = invert + // 0: #_#_#_#_ + // _#_#_#_# + // #_#_#_#_ + // _#_#_#_# + // + // 1: _#_#_#_# + // #_#_#_#_ + // _#_#_#_# + // #_#_#_#_ + // + // .r = fill + // 0: #_#_#_#_ + // _#_#_#_# + // #_#_#_#_ + // _#_#_#_# + // + // 1: ######## + // ######## + // ######## + // ######## + // + float4 glyph = glyphAtlas[data.texcoord]; + float2 pos = floor(data.position.xy / (shadedGlyphDotSize * data.renditionScale)); + + // A series of on/off/on/off/on/off pixels can be generated with: + // step(frac(x * 0.5f), 0) + // The inner frac(x * 0.5f) will generate a series of + // 0, 0.5, 0, 0.5, 0, 0.5 + // and the step() will transform that to + // 1, 0, 1, 0, 1, 0 + // + // We can now turn that into a checkerboard pattern quite easily, + // if we imagine the fields of the checkerboard like this: + // +---+---+---+ + // | 0 | 1 | 2 | + // +---+---+---+ + // | 1 | 2 | 3 | + // +---+---+---+ + // | 2 | 3 | 4 | + // +---+---+---+ + // + // Because this means we just need to set + // x = pos.x + pos.y + // and so we end up with + // step(frac(dot(pos, 0.5f)), 0) + // + // Finally, we need to implement the "stretch" explained above, which can + // be easily achieved by simply replacing the factor 0.5 with 0.25 like so + // step(frac(x * 0.25f), 0) + // as this gets us + // 0, 0.25, 0.5, 0.75, 0, 0.25, 0.5, 0.75 + // = 1, 0, 0, 0, 1, 0, 0, 0 + // + // Of course we only want to apply that to the X axis, which means + // below we end up having 2 different multipliers for the dot(). + float stretched = step(frac(dot(pos, float2(glyph.r * -0.25f + 0.5f, 0.5f))), 0) * glyph.a; + // Thankfully the remaining 2 operations are a lot simpler. + float inverted = abs(glyph.g - stretched); + float filled = max(glyph.b, inverted); + + color = premultiplyColor(data.color) * filled; + weights = color.aaaa; + break; + } case SHADING_TYPE_TEXT_PASSTHROUGH: { color = glyphAtlas[data.texcoord]; @@ -89,7 +171,7 @@ Output main(PSData data) : SV_Target } case SHADING_TYPE_CURLY_LINE: { - const float strokeWidthHalf = thinLineWidth * data.renditionScale.y * 0.5f; + const float strokeWidthHalf = doubleUnderlineWidth * data.renditionScale.y * 0.5f; const float amp = (curlyLineHalfHeight - strokeWidthHalf) * data.renditionScale.y; const float freq = data.renditionScale.x / curlyLineHalfHeight * 1.57079632679489661923f; const float s = sin(data.position.x * freq) * amp; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 13ecdd25372..601085c35ff 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -117,11 +117,11 @@ namespace Microsoft::Console::Render struct LineMetrics { int gridlineWidth; - int thinLineWidth; int underlineCenter; int underlineWidth; int doubleUnderlinePosTop; int doubleUnderlinePosBottom; + int doubleUnderlineWidth; int strikethroughOffset; int strikethroughWidth; int curlyLineCenter; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 4dbb82d9c6d..29f8dfc43ad 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -632,7 +632,7 @@ try DWORD underlineWidth = _lineMetrics.underlineWidth; if (lines.any(GridLines::DoubleUnderline, GridLines::CurlyUnderline)) { - underlineWidth = _lineMetrics.thinLineWidth; + underlineWidth = _lineMetrics.doubleUnderlineWidth; } const LOGBRUSH brushProp{ .lbStyle = BS_SOLID, .lbColor = underlineColor }; diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index f993c6f74e6..7563a68b7df 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -389,20 +389,20 @@ GdiEngine::~GdiEngine() // (Additional notes below.) // 1. - const auto thinLineWidth = std::max(1.0f, roundf(idealUnderlineWidth / 2.0f)); + const auto doubleUnderlineWidth = std::max(1.0f, roundf(idealUnderlineWidth / 2.0f)); // 2. - auto doubleUnderlinePosBottom = underlineCenter + underlineWidth - thinLineWidth; + auto doubleUnderlinePosBottom = underlineCenter + underlineWidth - doubleUnderlineWidth; // 3. Since we don't align the center of our two lines, but rather the top borders // we need to subtract half a line width from our center point. - auto doubleUnderlinePosTop = roundf((baseline + doubleUnderlinePosBottom - thinLineWidth) / 2.0f); + auto doubleUnderlinePosTop = roundf((baseline + doubleUnderlinePosBottom - doubleUnderlineWidth) / 2.0f); // 4. - doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + thinLineWidth); + doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + doubleUnderlineWidth); // 5. The gap is only the distance _between_ the lines, but we need the distance from the // top border of the top and bottom lines, which includes an additional line width. const auto doubleUnderlineGap = std::max(1.0f, roundf(1.2f / 72.0f * _iCurrentDpi)); - doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + thinLineWidth); + doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + doubleUnderlineWidth); // Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries. - doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, cellHeight - thinLineWidth); + doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, cellHeight - doubleUnderlineWidth); // The wave line is drawn using a cubic Bézier curve (PolyBezier), because that happens to be cheap with GDI. // We use a Bézier curve where, if the start (a) and end (c) points are at (0,0) and (1,0), the control points are @@ -431,13 +431,13 @@ GdiEngine::~GdiEngine() const auto curlyLineControlPointOffset = roundf(curlyLineIdealAmplitude * (1.0f / 0.140625f) * 0.5f); const auto curlyLinePeriod = curlyLineControlPointOffset * 2.0f; // We can reverse the above to get back the actual amplitude of our Bézier curve. The line - // will be drawn with a width of thinLineWidth in the center of the curve (= 0.5x padding). - const auto curlyLineAmplitude = 0.140625f * curlyLinePeriod + 0.5f * thinLineWidth; + // will be drawn with a width of doubleUnderlineWidth in the center of the curve (= 0.5x padding). + const auto curlyLineAmplitude = 0.140625f * curlyLinePeriod + 0.5f * doubleUnderlineWidth; // To make the wavy line with its double-underline amplitude look consistent with the double-underline we position it at its center. const auto curlyLineOffset = std::min(roundf(doubleUnderlineCenter), floorf(cellHeight - curlyLineAmplitude)); _lineMetrics.gridlineWidth = lroundf(idealGridlineWidth); - _lineMetrics.thinLineWidth = lroundf(thinLineWidth); + _lineMetrics.doubleUnderlineWidth = lroundf(doubleUnderlineWidth); _lineMetrics.underlineCenter = lroundf(underlineCenter); _lineMetrics.underlineWidth = lroundf(underlineWidth); _lineMetrics.doubleUnderlinePosTop = lroundf(doubleUnderlinePosTop); From 30dbd3b55416cceaff2b4db51e72a398fb05df9f Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Thu, 29 Feb 2024 11:00:04 -0600 Subject: [PATCH 138/603] Make the Settings Model tests into proper CI tests (#16773) This pull request removes the need for the SettingsModel tests to run in a UAP harness and puts them into the standard CI rotation. This required some changes to `Run-Tests.ps1` to ensure that the right `te.exe` is selected for each test harness. It's a bit annoying, but for things that depend on a `resources.pri`, that file must be in the same directory as the EXE that is hosting the test. Not the DLL, mind you, the EXE. In our case, that's `TE.ProcessHost.exe` The bulk of the change is honestly namespace tidying. Co-authored-by: Mike Griese Co-authored-by: Leonard Hecker --- OpenConsole.sln | 3 +- .../templates-v2/job-run-pgo-tests.yml | 1 + .../templates-v2/job-test-project.yml | 2 + build/scripts/Run-Tests.ps1 | 44 +++++++++++++++---- .../LocalTests_SettingsModel.def | 3 -- .../LocalTests_TerminalApp/SettingsTests.cpp | 2 +- .../TestHostApp/TestHostApp.vcxproj | 1 - .../TerminalSettingsModel/ActionMap.h | 8 ++-- src/cascadia/TerminalSettingsModel/Command.h | 6 +-- .../TerminalSettingsModel/GlobalAppSettings.h | 2 +- .../TerminalSettingsModel/Profile.cpp | 6 ++- src/cascadia/TerminalSettingsModel/Profile.h | 10 ++--- .../TerminalSettingsModel/TerminalSettings.h | 4 +- .../ColorSchemeTests.cpp | 17 +------ .../CommandTests.cpp | 17 +------ .../DeserializationTests.cpp | 17 +------ .../JsonTestClass.h | 0 .../KeyBindingsTests.cpp | 17 +------ .../NewTabMenuTests.cpp | 17 +------ .../ProfileTests.cpp | 17 +------ .../SerializationTests.cpp | 42 +++++++++++------- .../SettingsModel.UnitTests.vcxproj} | 19 ++++++-- .../TerminalSettingsTests.cpp | 19 ++------ .../TestUtils.h | 0 .../ThemeTests.cpp | 17 +------ .../UnitTests_SettingsModel.def | 1 + .../pch.cpp | 0 .../pch.h | 0 tools/OpenConsole.psm1 | 12 +++-- tools/runut.cmd | 14 +++++- tools/tests.xml | 2 +- 31 files changed, 142 insertions(+), 178 deletions(-) delete mode 100644 src/cascadia/LocalTests_SettingsModel/LocalTests_SettingsModel.def rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/ColorSchemeTests.cpp (96%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/CommandTests.cpp (95%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/DeserializationTests.cpp (96%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/JsonTestClass.h (100%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/KeyBindingsTests.cpp (95%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/NewTabMenuTests.cpp (75%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/ProfileTests.cpp (93%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/SerializationTests.cpp (95%) rename src/cascadia/{LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj => UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj} (87%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/TerminalSettingsTests.cpp (96%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/TestUtils.h (100%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/ThemeTests.cpp (89%) create mode 100644 src/cascadia/UnitTests_SettingsModel/UnitTests_SettingsModel.def rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/pch.cpp (100%) rename src/cascadia/{LocalTests_SettingsModel => UnitTests_SettingsModel}/pch.h (100%) diff --git a/OpenConsole.sln b/OpenConsole.sln index e699c77cece..1a05e24b750 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -270,7 +270,6 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia\LocalTests_TerminalApp\TestHostApp\TestHostApp.vcxproj", "{A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {CA5CAD1A-9B68-456A-B13E-C8218070DC42} {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} EndProjectSection EndProject @@ -350,7 +349,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", "src\cascadia\LocalTests_SettingsModel\SettingsModel.LocalTests.vcxproj", "{CA5CAD1A-9B68-456A-B13E-C8218070DC42}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_SettingsModel", "src\cascadia\UnitTests_SettingsModel\SettingsModel.UnitTests.vcxproj", "{CA5CAD1A-9B68-456A-B13E-C8218070DC42}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} diff --git a/build/pipelines/templates-v2/job-run-pgo-tests.yml b/build/pipelines/templates-v2/job-run-pgo-tests.yml index 817d97ff9ce..f4208bf9553 100644 --- a/build/pipelines/templates-v2/job-run-pgo-tests.yml +++ b/build/pipelines/templates-v2/job-run-pgo-tests.yml @@ -56,6 +56,7 @@ jobs: - task: PowerShell@2 displayName: 'Run PGO Tests' inputs: + pwsh: true targetType: filePath filePath: build\scripts\Run-Tests.ps1 arguments: >- diff --git a/build/pipelines/templates-v2/job-test-project.yml b/build/pipelines/templates-v2/job-test-project.yml index 1cd8e2bef67..5c53d76cfd0 100644 --- a/build/pipelines/templates-v2/job-test-project.yml +++ b/build/pipelines/templates-v2/job-test-project.yml @@ -44,6 +44,7 @@ jobs: - task: PowerShell@2 displayName: 'Run Unit Tests' inputs: + pwsh: true targetType: filePath filePath: build\scripts\Run-Tests.ps1 arguments: -MatchPattern '*unit.test*.dll' -Platform '$(OutputBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}' -Root "$(Terminal.BinDir)" @@ -52,6 +53,7 @@ jobs: - task: PowerShell@2 displayName: 'Run Feature Tests' inputs: + pwsh: true targetType: filePath filePath: build\scripts\Run-Tests.ps1 arguments: -MatchPattern '*feature.test*.dll' -Platform '$(OutputBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}' -Root "$(Terminal.BinDir)" diff --git a/build/scripts/Run-Tests.ps1 b/build/scripts/Run-Tests.ps1 index bd4cf28fc50..c9a37001c72 100644 --- a/build/scripts/Run-Tests.ps1 +++ b/build/scripts/Run-Tests.ps1 @@ -16,22 +16,48 @@ Param( # Find test DLLs based on the provided root, match pattern, and recursion $testDlls = Get-ChildItem -Path $Root -Recurse -Filter $MatchPattern -$args = @() +$teArgs = @() # Check if the LogPath parameter is provided and enable WTT logging if ($LogPath) { - $args += '/enablewttlogging' - $args += '/appendwttlogging' - $args += "/logFile:$LogPath" + $teArgs += '/enablewttlogging' + $teArgs += '/appendwttlogging' + $teArgs += "/logFile:$LogPath" Write-Host "WTT Logging Enabled" } -# Invoke the te.exe executable with arguments and test DLLs -& "$Root\te.exe" $args $testDlls.FullName $AdditionalTaefArguments +$rootTe = "$Root\te.exe" -# Check the exit code of the te.exe process and exit accordingly -if ($LASTEXITCODE -ne 0) { - Exit $LASTEXITCODE +# Some of our test fixtures depend on resources.pri in the same folder as the .exe hosting them. +# Unfortunately, that means that we need to run the te.exe *next to* each test DLL we discover. +# This code establishes a mapping from te.exe to test DLL (or DLLs) +$testDllTaefGroups = $testDlls | % { + $localTe = Get-Item (Join-Path (Split-Path $_ -Parent) "te.exe") -EA:Ignore + If ($null -eq $localTe) { + $finalTePath = $rootTe + } Else { + $finalTePath = $localTe.FullName + } + [PSCustomObject]@{ + TePath = $finalTePath; + TestDll = $_; + } +} + +# Invoke the te.exe executables with arguments and test DLLs +$anyFailed = $false +$testDllTaefGroups | Group-Object TePath | % { + $te = $_.Group[0].TePath + $dlls = $_.Group.TestDll + Write-Verbose "Running $te (for $($dlls.Name))" + & $te $teArgs $dlls.FullName $AdditionalTaefArguments + if ($LASTEXITCODE -ne 0) { + $anyFailed = $true + } +} + +if ($anyFailed) { + Exit 1 } Exit 0 diff --git a/src/cascadia/LocalTests_SettingsModel/LocalTests_SettingsModel.def b/src/cascadia/LocalTests_SettingsModel/LocalTests_SettingsModel.def deleted file mode 100644 index ba15818ddb1..00000000000 --- a/src/cascadia/LocalTests_SettingsModel/LocalTests_SettingsModel.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS -DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE -DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp index a940cc915f3..8116e9e4574 100644 --- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -4,7 +4,7 @@ #include "pch.h" #include "../TerminalApp/TerminalPage.h" -#include "../LocalTests_SettingsModel/TestUtils.h" +#include "../UnitTests_SettingsModel/TestUtils.h" using namespace Microsoft::Console; using namespace WEX::Logging; diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 9e2029c9bcc..8d431bffcc0 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -133,7 +133,6 @@ - diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index d59dc0677d8..937d79f66cb 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -20,7 +20,7 @@ Author(s): #include "Command.h" // fwdecl unittest classes -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { class KeyBindingsTests; class DeserializationTests; @@ -123,8 +123,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // than is necessary to be serialized. std::unordered_map _MaskingActions; - friend class SettingsModelLocalTests::KeyBindingsTests; - friend class SettingsModelLocalTests::DeserializationTests; - friend class SettingsModelLocalTests::TerminalSettingsTests; + friend class SettingsModelUnitTests::KeyBindingsTests; + friend class SettingsModelUnitTests::DeserializationTests; + friend class SettingsModelUnitTests::TerminalSettingsTests; }; } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 4418716c815..a432fe9a370 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -25,7 +25,7 @@ Author(s): #include "SettingsTypes.h" // fwdecl unittest classes -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { class DeserializationTests; class CommandTests; @@ -87,8 +87,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static std::vector _expandCommand(Command* const expandable, Windows::Foundation::Collections::IVectorView profiles, Windows::Foundation::Collections::IVectorView schemes); - friend class SettingsModelLocalTests::DeserializationTests; - friend class SettingsModelLocalTests::CommandTests; + friend class SettingsModelUnitTests::DeserializationTests; + friend class SettingsModelUnitTests::CommandTests; }; } diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 21d73483c02..79f40342254 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -27,7 +27,7 @@ Author(s): #include "RemainingProfilesEntry.h" // fwdecl unittest classes -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { class DeserializationTests; class ColorSchemeTests; diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp index f31cd4e85f7..0db012e7bc7 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.cpp +++ b/src/cascadia/TerminalSettingsModel/Profile.cpp @@ -317,7 +317,11 @@ Json::Value Profile::ToJson() const JsonUtils::SetValueForKey(json, GuidKey, writeBasicSettings ? Guid() : _Guid); JsonUtils::SetValueForKey(json, HiddenKey, writeBasicSettings ? Hidden() : _Hidden); JsonUtils::SetValueForKey(json, SourceKey, writeBasicSettings ? Source() : _Source); - JsonUtils::SetValueForKey(json, IconKey, writeBasicSettings ? Icon() : _Icon); + + // Recall: Icon isn't actually a setting in the MTSM_PROFILE_SETTINGS. We + // defined it manually in Profile, so make sure we only serialize the Icon + // if the user actually changed it here. + JsonUtils::SetValueForKey(json, IconKey, (writeBasicSettings && HasIcon()) ? Icon() : _Icon); // PermissiveStringConverter is unnecessary for serialization JsonUtils::SetValueForKey(json, PaddingKey, _Padding); diff --git a/src/cascadia/TerminalSettingsModel/Profile.h b/src/cascadia/TerminalSettingsModel/Profile.h index 1414dc0be1d..6fdd5c3ed57 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.h +++ b/src/cascadia/TerminalSettingsModel/Profile.h @@ -54,7 +54,7 @@ Author(s): #include "FontConfig.h" // fwdecl unittest classes -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { class DeserializationTests; class ProfileTests; @@ -147,10 +147,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring _evaluateIcon() const; - friend class SettingsModelLocalTests::DeserializationTests; - friend class SettingsModelLocalTests::ProfileTests; - friend class SettingsModelLocalTests::ColorSchemeTests; - friend class SettingsModelLocalTests::KeyBindingsTests; + friend class SettingsModelUnitTests::DeserializationTests; + friend class SettingsModelUnitTests::ProfileTests; + friend class SettingsModelUnitTests::ColorSchemeTests; + friend class SettingsModelUnitTests::KeyBindingsTests; friend class TerminalAppUnitTests::DynamicProfileTests; friend class TerminalAppUnitTests::JsonTests; }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 0e7346a5960..22fba938723 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -25,7 +25,7 @@ using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap; // fwdecl unittest classes -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { class TerminalSettingsTests; } @@ -182,7 +182,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation const Windows::Foundation::Collections::IMapView& schemes, const winrt::Microsoft::Terminal::Settings::Model::Theme currentTheme); - friend class SettingsModelLocalTests::TerminalSettingsTests; + friend class SettingsModelUnitTests::TerminalSettingsTests; }; } diff --git a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/UnitTests_SettingsModel/ColorSchemeTests.cpp similarity index 96% rename from src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp rename to src/cascadia/UnitTests_SettingsModel/ColorSchemeTests.cpp index a537c3dc698..b2198160671 100644 --- a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/ColorSchemeTests.cpp @@ -16,24 +16,11 @@ using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class ColorSchemeTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(ColorSchemeTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(ColorSchemeTests); TEST_METHOD(ParseSimpleColorScheme); TEST_METHOD(LayerColorSchemesOnArray); diff --git a/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp similarity index 95% rename from src/cascadia/LocalTests_SettingsModel/CommandTests.cpp rename to src/cascadia/UnitTests_SettingsModel/CommandTests.cpp index cf8116033a1..f68b0224a47 100644 --- a/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp @@ -15,24 +15,11 @@ using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class CommandTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(CommandTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(CommandTests); TEST_METHOD(ManyCommandsSameAction); TEST_METHOD(LayerCommand); diff --git a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp similarity index 96% rename from src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp rename to src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp index 50d649d7a5d..e546cf3305e 100644 --- a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp @@ -19,24 +19,11 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class DeserializationTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(DeserializationTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(DeserializationTests); TEST_METHOD(ValidateProfilesExist); TEST_METHOD(ValidateDefaultProfileExists); diff --git a/src/cascadia/LocalTests_SettingsModel/JsonTestClass.h b/src/cascadia/UnitTests_SettingsModel/JsonTestClass.h similarity index 100% rename from src/cascadia/LocalTests_SettingsModel/JsonTestClass.h rename to src/cascadia/UnitTests_SettingsModel/JsonTestClass.h diff --git a/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp similarity index 95% rename from src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp rename to src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp index b2e77f5b4e6..8eed7276f1b 100644 --- a/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp @@ -17,24 +17,11 @@ using namespace WEX::TestExecution; using namespace WEX::Common; using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class KeyBindingsTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(KeyBindingsTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(KeyBindingsTests); TEST_METHOD(KeyChords); TEST_METHOD(ManyKeysSameAction); diff --git a/src/cascadia/LocalTests_SettingsModel/NewTabMenuTests.cpp b/src/cascadia/UnitTests_SettingsModel/NewTabMenuTests.cpp similarity index 75% rename from src/cascadia/LocalTests_SettingsModel/NewTabMenuTests.cpp rename to src/cascadia/UnitTests_SettingsModel/NewTabMenuTests.cpp index f9e4625e995..d5bc0a75161 100644 --- a/src/cascadia/LocalTests_SettingsModel/NewTabMenuTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/NewTabMenuTests.cpp @@ -18,24 +18,11 @@ using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class NewTabMenuTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(NewTabMenuTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(NewTabMenuTests); TEST_METHOD(DefaultsToRemainingProfiles); TEST_METHOD(ParseEmptyFolder); diff --git a/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp b/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp similarity index 93% rename from src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp rename to src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp index a73b2bec3e4..047d3c89cfb 100644 --- a/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp @@ -15,24 +15,11 @@ using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class ProfileTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(ProfileTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(ProfileTests); TEST_METHOD(ProfileGeneratesGuid); TEST_METHOD(LayerProfileProperties); diff --git a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp similarity index 95% rename from src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp rename to src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp index b4778e8b814..c75da633ac2 100644 --- a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp @@ -16,24 +16,11 @@ using namespace WEX::Common; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class SerializationTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(SerializationTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(SerializationTests); TEST_METHOD(GlobalSettings); TEST_METHOD(Profile); @@ -212,9 +199,34 @@ namespace SettingsModelLocalTests "source": "local" })" }; + static constexpr std::string_view profileWithIcon{ R"( + { + "guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}", + "name": "profileWithIcon", + "hidden": false, + "icon": "foo.png" + })" }; + static constexpr std::string_view profileWithNullIcon{ R"( + { + "guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}", + "name": "profileWithNullIcon", + "hidden": false, + "icon": null + })" }; + static constexpr std::string_view profileWithNoIcon{ R"( + { + "guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}", + "name": "profileWithNoIcon", + "hidden": false, + "icon": "none" + })" }; + RoundtripTest(profileString); RoundtripTest(smallProfileString); RoundtripTest(weirdProfileString); + RoundtripTest(profileWithIcon); + RoundtripTest(profileWithNullIcon); + RoundtripTest(profileWithNoIcon); } void SerializationTests::ColorScheme() diff --git a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj b/src/cascadia/UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj similarity index 87% rename from src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj rename to src/cascadia/UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj index dbd279005be..6210385b26a 100644 --- a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj +++ b/src/cascadia/UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj @@ -14,9 +14,9 @@ {CA5CAD1A-9B68-456A-B13E-C8218070DC42} Win32Proj - SettingsModelLocalTests - LocalTests_SettingsModel - SettingsModel.LocalTests + SettingsModelUnitTests + UnitTests_SettingsModel + SettingsModel.Unit.Tests DynamicLibrary true @@ -64,7 +64,6 @@ _ConsoleGenerateAdditionalWinmdManifests step won't gather the winmd's --> - @@ -98,4 +97,16 @@ + + + + + + + + + diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp similarity index 96% rename from src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp rename to src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp index 4dbc7f88db8..f45fd1624f7 100644 --- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp @@ -16,24 +16,11 @@ using namespace WEX::Common; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class TerminalSettingsTests { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(TerminalSettingsTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(TerminalSettingsTests); TEST_METHOD(TryCreateWinRTType); TEST_METHOD(TestTerminalArgsForBinding); @@ -153,7 +140,7 @@ namespace SettingsModelLocalTests g.Data4[7]); } - const auto tmpdir = std::filesystem::temp_directory_path(); + const auto tmpdir = std::filesystem::canonical(std::filesystem::temp_directory_path()); const auto dir1 = tmpdir / guid; const auto dir2 = tmpdir / (guid + L" two"); const auto file1 = dir1 / L"file 1.exe"; diff --git a/src/cascadia/LocalTests_SettingsModel/TestUtils.h b/src/cascadia/UnitTests_SettingsModel/TestUtils.h similarity index 100% rename from src/cascadia/LocalTests_SettingsModel/TestUtils.h rename to src/cascadia/UnitTests_SettingsModel/TestUtils.h diff --git a/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp b/src/cascadia/UnitTests_SettingsModel/ThemeTests.cpp similarity index 89% rename from src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp rename to src/cascadia/UnitTests_SettingsModel/ThemeTests.cpp index c7bf11326af..512d9b20aa5 100644 --- a/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/ThemeTests.cpp @@ -17,24 +17,11 @@ using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; -namespace SettingsModelLocalTests +namespace SettingsModelUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class ThemeTests : public JsonTestClass { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. - BEGIN_TEST_CLASS(ThemeTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") - END_TEST_CLASS() + TEST_CLASS(ThemeTests); TEST_METHOD(ParseSimpleTheme); TEST_METHOD(ParseEmptyTheme); diff --git a/src/cascadia/UnitTests_SettingsModel/UnitTests_SettingsModel.def b/src/cascadia/UnitTests_SettingsModel/UnitTests_SettingsModel.def new file mode 100644 index 00000000000..e80a637aa25 --- /dev/null +++ b/src/cascadia/UnitTests_SettingsModel/UnitTests_SettingsModel.def @@ -0,0 +1 @@ +EXPORTS diff --git a/src/cascadia/LocalTests_SettingsModel/pch.cpp b/src/cascadia/UnitTests_SettingsModel/pch.cpp similarity index 100% rename from src/cascadia/LocalTests_SettingsModel/pch.cpp rename to src/cascadia/UnitTests_SettingsModel/pch.cpp diff --git a/src/cascadia/LocalTests_SettingsModel/pch.h b/src/cascadia/UnitTests_SettingsModel/pch.h similarity index 100% rename from src/cascadia/LocalTests_SettingsModel/pch.h rename to src/cascadia/UnitTests_SettingsModel/pch.h diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index 3dd17957161..0f25a8e93a8 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -169,7 +169,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'unitRemoting', 'unitControl')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'unitSettingsModel', 'unitRemoting', 'unitControl')] [string]$Test, [parameter(Mandatory=$false)] @@ -232,13 +232,19 @@ function Invoke-OpenConsoleTests() # run selected tests foreach ($t in $TestsToRun) { + $currentTaefExe = $TaefExePath + if ($t.isolatedTaef -eq "true") + { + $currentTaefExe = (Join-Path (Split-Path (Join-Path $BinDir $t.binary)) "te.exe") + } + if ($t.type -eq "unit") { - & $TaefExePath "$BinDir\$($t.binary)" $TaefArgs + & $currentTaefExe "$BinDir\$($t.binary)" $TaefArgs } elseif ($t.type -eq "ft") { - Invoke-TaefInNewWindow -OpenConsolePath $OpenConsolePath -TaefPath $TaefExePath -TestDll "$BinDir\$($t.binary)" -TaefArgs $TaefArgs + Invoke-TaefInNewWindow -OpenConsolePath $OpenConsolePath -TaefPath $currentTaefExe -TestDll "$BinDir\$($t.binary)" -TaefArgs $TaefArgs } else { diff --git a/tools/runut.cmd b/tools/runut.cmd index 02bcc47d192..62614d15d94 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -13,7 +13,7 @@ if "%PLATFORM%" == "Win32" ( set _TestHostAppPath=%OPENCON%\bin\%_LAST_BUILD_CONF%\TestHostApp ) -call %TAEF% ^ +%TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Conhost.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\TextBuffer.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalCore\Terminal.Core.Unit.Tests.dll ^ @@ -26,6 +26,16 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_Remoting\Remoting.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_Control\Control.Unit.Tests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ - %_TestHostAppPath%\SettingsModel.LocalTests.dll ^ %* +set _EarlyTestFail=%ERRORLEVEL% + +%OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_SettingsModel\te.exe ^ + %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_SettingsModel\SettingsModel.Unit.Tests.dll ^ + %* + +if %_EarlyTestFail% NEQ 0 ( + exit /b %_EarlyTestFail% +) + +exit /b %ERRORLEVEL% diff --git a/tools/tests.xml b/tools/tests.xml index c6085b0b468..4301c09b2a3 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -5,7 +5,7 @@ - + From 99042d2f0cce6b6ede989aa630d391609c42729b Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Thu, 29 Feb 2024 10:39:26 -0800 Subject: [PATCH 139/603] Allow editing font axes in the Settings UI (#16104) ## Summary of the Pull Request Allow editing of font features and axes in the SUI to get the UI closer towards JSON parity The allowed font axes are obtained directly from the currently selected font, and their display names are presented to the user in the user's current locale (if it exists). Otherwise, we just display the axis tag to the user. ## References and Relevant Issues #10000 ## Validation Steps Performed - [x] Font Axes can be added/changed/removed from the Settings UI ![image](https://github.com/microsoft/terminal/assets/26824113/b1c3ed57-e329-4893-9f15-7b60154b5ea0) ![image](https://github.com/microsoft/terminal/assets/26824113/e1f1ea22-857d-4392-8a15-f81539fe9257) ## PR Checklist - [ ] Closes #xxx - [ ] Tests added/passed - [ ] Documentation updated - If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx - [ ] Schema updated (if necessary) --- .github/actions/spelling/allow/allow.txt | 1 + .../TerminalSettingsEditor/Appearances.cpp | 316 ++++++++++++++++++ .../TerminalSettingsEditor/Appearances.h | 42 +++ .../TerminalSettingsEditor/Appearances.idl | 21 ++ .../TerminalSettingsEditor/Appearances.xaml | 63 ++++ .../ProfileViewModel.cpp | 4 + .../Resources/en-US/Resources.resw | 16 + .../SettingContainer.cpp | 11 + .../TerminalSettingsEditor/SettingContainer.h | 2 + .../SettingContainer.idl | 2 + .../TerminalSettingsModel/FontConfig.cpp | 12 + 11 files changed, 490 insertions(+) diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 27466018802..3ece2375e52 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -17,6 +17,7 @@ CMMI copyable Counterintuitively CtrlDToClose +CVS CUI cybersecurity dalet diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index 8a3c9f943a2..848cb99349f 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "Appearances.h" #include "Appearances.g.cpp" +#include "AxisKeyValuePair.g.cpp" #include "EnumEntry.h" #include @@ -42,6 +43,154 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _hasPowerlineCharacters.value_or(false); } + Windows::Foundation::Collections::IMap Font::FontAxesTagsAndNames() + { + if (!_fontAxesTagsAndNames) + { + wil::com_ptr font; + THROW_IF_FAILED(_family->GetFont(0, font.put())); + wil::com_ptr fontFace; + THROW_IF_FAILED(font->CreateFontFace(fontFace.put())); + wil::com_ptr fontFace5; + if (fontFace5 = fontFace.try_query()) + { + wil::com_ptr fontResource; + THROW_IF_FAILED(fontFace5->GetFontResource(fontResource.put())); + + const auto axesCount = fontFace5->GetFontAxisValueCount(); + if (axesCount > 0) + { + std::vector axesVector(axesCount); + fontFace5->GetFontAxisValues(axesVector.data(), axesCount); + + uint32_t localeIndex; + BOOL localeExists; + wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; + const auto localeToTry = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) ? localeName : L"en-US"; + + std::unordered_map fontAxesTagsAndNames; + for (uint32_t i = 0; i < axesCount; ++i) + { + wil::com_ptr names; + THROW_IF_FAILED(fontResource->GetAxisNames(i, names.put())); + + if (!SUCCEEDED(names->FindLocaleName(localeToTry, &localeIndex, &localeExists)) || !localeExists) + { + // default to the first locale in the list + localeIndex = 0; + } + + UINT32 length = 0; + if (SUCCEEDED(names->GetStringLength(localeIndex, &length))) + { + winrt::impl::hstring_builder builder{ length }; + if (SUCCEEDED(names->GetString(localeIndex, builder.data(), length + 1))) + { + fontAxesTagsAndNames.insert(std::pair(_axisTagToString(axesVector[i].axisTag), builder.to_hstring())); + continue; + } + } + // if there was no name found, it means the font does not actually support this axis + // don't insert anything into the vector in this case + } + _fontAxesTagsAndNames = winrt::single_threaded_map(std::move(fontAxesTagsAndNames)); + } + } + } + return _fontAxesTagsAndNames; + } + + winrt::hstring Font::_axisTagToString(DWRITE_FONT_AXIS_TAG tag) + { + std::wstring result; + for (int i = 0; i < 4; ++i) + { + result.push_back((tag >> (i * 8)) & 0xFF); + } + return winrt::hstring{ result }; + } + + AxisKeyValuePair::AxisKeyValuePair(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap) : + _AxisKey{ axisKey }, + _AxisValue{ axisValue }, + _baseMap{ baseMap }, + _tagToNameMap{ tagToNameMap } + { + if (_tagToNameMap.HasKey(_AxisKey)) + { + int32_t i{ 0 }; + // IMap guarantees that the iteration order is the same every time + // so this conversion of key to index is safe + for (const auto tagAndName : _tagToNameMap) + { + if (tagAndName.Key() == _AxisKey) + { + _AxisIndex = i; + break; + } + ++i; + } + } + } + + winrt::hstring AxisKeyValuePair::AxisKey() + { + return _AxisKey; + } + + float AxisKeyValuePair::AxisValue() + { + return _AxisValue; + } + + int32_t AxisKeyValuePair::AxisIndex() + { + return _AxisIndex; + } + + void AxisKeyValuePair::AxisValue(float axisValue) + { + if (axisValue != _AxisValue) + { + _baseMap.Remove(_AxisKey); + _AxisValue = axisValue; + _baseMap.Insert(_AxisKey, _AxisValue); + _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"AxisValue" }); + } + } + + void AxisKeyValuePair::AxisKey(winrt::hstring axisKey) + { + if (axisKey != _AxisKey) + { + _baseMap.Remove(_AxisKey); + _AxisKey = axisKey; + _baseMap.Insert(_AxisKey, _AxisValue); + _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"AxisKey" }); + } + } + + void AxisKeyValuePair::AxisIndex(int32_t axisIndex) + { + if (axisIndex != _AxisIndex) + { + _AxisIndex = axisIndex; + + int32_t i{ 0 }; + // same as in the constructor, iterating through IMap + // gives us the same order every time + for (const auto tagAndName : _tagToNameMap) + { + if (i == _AxisIndex) + { + AxisKey(tagAndName.Key()); + break; + } + ++i; + } + } + } + AppearanceViewModel::AppearanceViewModel(const Model::AppearanceConfig& appearance) : _appearance{ appearance } { @@ -60,8 +209,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // box, prevent it from ever being changed again. _NotifyChanges(L"UseDesktopBGImage", L"BackgroundImageSettingsVisible"); } + else if (viewModelProperty == L"FontAxes") + { + // this is a weird one + // we manually make the observable vector based on the map in the settings model + // (this is due to xaml being unable to bind a list view to a map) + // so when the FontAxes change (say from the reset button), reinitialize the observable vector + InitializeFontAxesVector(); + } }); + InitializeFontAxesVector(); + // Cache the original BG image path. If the user clicks "Use desktop // wallpaper", then un-checks it, this is the string we'll restore to // them. @@ -205,6 +364,119 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation LightColorSchemeName(val.Name()); } + void AppearanceViewModel::AddNewAxisKeyValuePair() + { + if (!_appearance.SourceProfile().FontInfo().FontAxes()) + { + _appearance.SourceProfile().FontInfo().FontAxes(winrt::single_threaded_map()); + } + auto fontAxesMap = _appearance.SourceProfile().FontInfo().FontAxes(); + + // find one axis that does not already exist, and add that + // if there are no more possible axes to add, the button is disabled so there shouldn't be a way to get here + const auto possibleAxesTagsAndNames = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames(); + for (const auto tagAndName : possibleAxesTagsAndNames) + { + if (!fontAxesMap.HasKey(tagAndName.Key())) + { + fontAxesMap.Insert(tagAndName.Key(), gsl::narrow(0)); + FontAxesVector().Append(_CreateAxisKeyValuePairHelper(tagAndName.Key(), gsl::narrow(0), fontAxesMap, possibleAxesTagsAndNames)); + break; + } + } + _NotifyChanges(L"CanFontAxesBeAdded"); + } + + void AppearanceViewModel::DeleteAxisKeyValuePair(winrt::hstring key) + { + for (uint32_t i = 0; i < _FontAxesVector.Size(); i++) + { + if (_FontAxesVector.GetAt(i).AxisKey() == key) + { + FontAxesVector().RemoveAt(i); + _appearance.SourceProfile().FontInfo().FontAxes().Remove(key); + if (_FontAxesVector.Size() == 0) + { + _appearance.SourceProfile().FontInfo().ClearFontAxes(); + } + break; + } + } + _NotifyChanges(L"CanFontAxesBeAdded"); + } + + void AppearanceViewModel::InitializeFontAxesVector() + { + if (!_FontAxesVector) + { + _FontAxesVector = winrt::single_threaded_observable_vector(); + } + + _FontAxesVector.Clear(); + if (const auto fontAxesMap = _appearance.SourceProfile().FontInfo().FontAxes()) + { + const auto fontAxesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames(); + for (const auto axis : fontAxesMap) + { + // only show the axes that the font supports + // any axes that the font doesn't support continue to be stored in the json, we just don't show them in the UI + if (fontAxesTagToNameMap.HasKey(axis.Key())) + { + _FontAxesVector.Append(_CreateAxisKeyValuePairHelper(axis.Key(), axis.Value(), fontAxesMap, fontAxesTagToNameMap)); + } + } + } + _NotifyChanges(L"AreFontAxesAvailable", L"CanFontAxesBeAdded"); + } + + // Method Description: + // - Determines whether the currently selected font has any variable font axes + bool AppearanceViewModel::AreFontAxesAvailable() + { + return ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames().Size() > 0; + } + + // Method Description: + // - Determines whether the currently selected font has any variable font axes that have not already been set + bool AppearanceViewModel::CanFontAxesBeAdded() + { + if (const auto fontAxesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames(); fontAxesTagToNameMap.Size() > 0) + { + if (const auto fontAxesMap = _appearance.SourceProfile().FontInfo().FontAxes()) + { + for (const auto tagAndName : fontAxesTagToNameMap) + { + if (!fontAxesMap.HasKey(tagAndName.Key())) + { + // we found an axis that has not been set + return true; + } + } + // all possible axes have been set already + return false; + } + // the font supports font axes but the profile has none set + return true; + } + // the font does not support any font axes + return false; + } + + // Method Description: + // - Creates an AxisKeyValuePair and sets up an event handler for it + Editor::AxisKeyValuePair AppearanceViewModel::_CreateAxisKeyValuePairHelper(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap) + { + const auto axisKeyValuePair = winrt::make(axisKey, axisValue, baseMap, tagToNameMap); + // when either the key or the value changes, send an event for the preview control to catch + axisKeyValuePair.PropertyChanged([weakThis = get_weak()](auto& /*sender*/, auto& /*e*/) { + if (auto appVM{ weakThis.get() }) + { + appVM->_NotifyChanges(L"AxisKeyValuePair"); + } + }); + return axisKeyValuePair; + } + DependencyProperty Appearances::_AppearanceProperty{ nullptr }; Appearances::Appearances() : @@ -271,6 +543,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation const auto backgroundImgCheckboxTooltip{ ToolTipService::GetToolTip(UseDesktopImageCheckBox()) }; Automation::AutomationProperties::SetFullDescription(UseDesktopImageCheckBox(), unbox_value(backgroundImgCheckboxTooltip)); + _FontAxesNames = winrt::single_threaded_observable_vector(); + FontAxesNamesCVS().Source(_FontAxesNames); + INITIALIZE_BINDABLE_ENUM_SETTING(IntenseTextStyle, IntenseTextStyle, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle, L"Appearance_IntenseTextStyle", L"Content"); } @@ -330,6 +605,28 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { ShowProportionalFontWarning(false); } + + _FontAxesNames.Clear(); + const auto axesTagsAndNames = newFontFace.FontAxesTagsAndNames(); + for (const auto tagAndName : axesTagsAndNames) + { + _FontAxesNames.Append(tagAndName.Value()); + } + + // when the font face changes, we have to tell the view model to update the font axes vector + // since the new font may not have the same possible axes as the previous one + Appearance().InitializeFontAxesVector(); + if (!Appearance().AreFontAxesAvailable()) + { + // if the previous font had available font axes and the expander was expanded, + // at this point the expander would be set to disabled so manually collapse it + FontAxesContainer().SetExpanded(false); + FontAxesContainer().HelpText(RS_(L"Profile_FontAxesUnavailable/Text")); + } + else + { + FontAxesContainer().HelpText(RS_(L"Profile_FontAxesAvailable/Text")); + } } void Appearances::_ViewModelChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*args*/) @@ -348,6 +645,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation biButton.IsChecked(biButton.Tag().as() == biAlignmentVal); } + FontAxesCVS().Source(Appearance().FontAxesVector()); + Appearance().AreFontAxesAvailable() ? FontAxesContainer().HelpText(RS_(L"Profile_FontAxesAvailable/Text")) : FontAxesContainer().HelpText(RS_(L"Profile_FontAxesUnavailable/Text")); + _ViewModelChangedRevoker = Appearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) { const auto settingName{ args.PropertyName() }; if (settingName == L"CursorShape") @@ -470,6 +770,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } + void Appearances::DeleteAxisKeyValuePair_Click(const IInspectable& sender, const RoutedEventArgs& /*e*/) + { + if (const auto& button{ sender.try_as() }) + { + if (const auto& tag{ button.Tag().try_as() }) + { + Appearance().DeleteAxisKeyValuePair(tag.value()); + } + } + } + + void Appearances::AddNewAxisKeyValuePair_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/) + { + Appearance().AddNewAxisKeyValuePair(); + } + bool Appearances::IsVintageCursor() const { return Appearance().CursorShape() == Core::CursorStyle::Vintage; diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index f89d0fa2305..53a244ab0d9 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -17,6 +17,7 @@ Author(s): #pragma once #include "Font.g.h" +#include "AxisKeyValuePair.g.h" #include "Appearances.g.h" #include "AppearanceViewModel.g.h" #include "Utils.h" @@ -37,6 +38,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation hstring ToString() { return _LocalizedName; } bool HasPowerlineCharacters(); + Windows::Foundation::Collections::IMap FontAxesTagsAndNames(); WINRT_PROPERTY(hstring, Name); WINRT_PROPERTY(hstring, LocalizedName); @@ -44,6 +46,31 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation private: winrt::com_ptr _family; std::optional _hasPowerlineCharacters; + winrt::hstring _axisTagToString(DWRITE_FONT_AXIS_TAG tag); + Windows::Foundation::Collections::IMap _fontAxesTagsAndNames; + }; + + struct AxisKeyValuePair : AxisKeyValuePairT, ViewModelHelper + { + AxisKeyValuePair(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); + + winrt::hstring AxisKey(); + void AxisKey(winrt::hstring axisKey); + + float AxisValue(); + void AxisValue(float axisValue); + + int32_t AxisIndex(); + void AxisIndex(int32_t axisIndex); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + + private: + winrt::hstring _AxisKey; + float _AxisValue; + int32_t _AxisIndex; + Windows::Foundation::Collections::IMap _baseMap{ nullptr }; + Windows::Foundation::Collections::IMap _tagToNameMap{ nullptr }; }; struct AppearanceViewModel : AppearanceViewModelT, ViewModelHelper @@ -69,6 +96,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Editor::ColorSchemeViewModel CurrentColorScheme(); void CurrentColorScheme(const Editor::ColorSchemeViewModel& val); + void AddNewAxisKeyValuePair(); + void DeleteAxisKeyValuePair(winrt::hstring key); + void InitializeFontAxesVector(); + bool AreFontAxesAvailable(); + bool CanFontAxesBeAdded(); + WINRT_PROPERTY(bool, IsDefault, false); // These settings are not defined in AppearanceConfig, so we grab them @@ -79,6 +112,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontFace); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontSize); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontWeight); + OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontAxes); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), EnableBuiltinGlyphs); OBSERVABLE_PROJECTED_SETTING(_appearance, RetroTerminalEffect); @@ -93,10 +127,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance, IntenseTextStyle); OBSERVABLE_PROJECTED_SETTING(_appearance, AdjustIndistinguishableColors); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, SchemesList, _propertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontAxesVector, _propertyChangedHandlers, nullptr); private: Model::AppearanceConfig _appearance; winrt::hstring _lastBgImagePath; + + Editor::AxisKeyValuePair _CreateAxisKeyValuePairHelper(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); }; struct Appearances : AppearancesT @@ -117,6 +154,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation fire_and_forget BackgroundImage_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void BIAlignment_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void FontFace_SelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e); + void DeleteAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + void AddNewAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); // manually bind FontWeight Windows::Foundation::IInspectable CurrentFontWeight() const; @@ -144,6 +183,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Windows::Foundation::Collections::IMap _FontWeightMap; Editor::EnumEntry _CustomFontWeight{ nullptr }; + Windows::Foundation::Collections::IObservableVector _FontAxesNames; + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; static void _ViewModelChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); void _UpdateWithNewViewModel(); @@ -153,4 +194,5 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation { BASIC_FACTORY(Appearances); + BASIC_FACTORY(AxisKeyValuePair); } diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.idl b/src/cascadia/TerminalSettingsEditor/Appearances.idl index ddab88a6beb..68bd887d217 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.idl +++ b/src/cascadia/TerminalSettingsEditor/Appearances.idl @@ -13,6 +13,8 @@ import "ColorSchemesPageViewModel.idl"; OBSERVABLE_PROJECTED_SETTING(Type, Name); \ Object Name##OverrideSource { get; } +#define COMMA , + namespace Microsoft.Terminal.Settings.Editor { runtimeclass Font : Windows.Foundation.IStringable @@ -20,6 +22,17 @@ namespace Microsoft.Terminal.Settings.Editor String Name { get; }; String LocalizedName { get; }; Boolean HasPowerlineCharacters { get; }; + Windows.Foundation.Collections.IMap FontAxesTagsAndNames { get; }; + } + + // We have to make this because we cannot bind an IObservableMap to a ListView in XAML (in c++) + // So instead we make an IObservableVector of these AxisKeyValuePair objects + runtimeclass AxisKeyValuePair : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + AxisKeyValuePair(String axisKey, Single axisValue, Windows.Foundation.Collections.IMap baseMap, Windows.Foundation.Collections.IMap tagToNameMap); + String AxisKey; + Single AxisValue; + Int32 AxisIndex; } runtimeclass AppearanceViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged @@ -37,6 +50,14 @@ namespace Microsoft.Terminal.Settings.Editor ColorSchemeViewModel CurrentColorScheme; Windows.Foundation.Collections.IObservableVector SchemesList; + void AddNewAxisKeyValuePair(); + void DeleteAxisKeyValuePair(String key); + void InitializeFontAxesVector(); + Boolean AreFontAxesAvailable { get; }; + Boolean CanFontAxesBeAdded { get; }; + Windows.Foundation.Collections.IObservableVector FontAxesVector; + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.Collections.IMap, FontAxes); + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, FontFace); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Double, LineHeight); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index f1f13d8fbd6..0b5a9c2abfa 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -37,6 +37,8 @@ Background="{x:Bind local:Converters.ColorToBrush(Color)}" CornerRadius="1" /> + @@ -286,6 +288,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Browse... Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. + + Add new + Button label that adds a new font axis for the current font. + Background image opacity Name for a control to choose the opacity of the image presented on the background of the app. @@ -922,6 +926,18 @@ Sets the weight (lightness or heaviness of the strokes) for the given font. A description for what the "font weight" setting does. Presented near "Profile_FontWeight". + + Variable font axes + Header for a control to allow editing the font axes. + + + Add or remove font axes for the given font. + A description for what the "font axes" setting does. Presented near "Profile_FontAxes". + + + The selected font has no variable font axes. + A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". + General Header for a sub-page of profile settings focused on more general scenarios. diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp index 539165294c6..9bf3912e3aa 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp @@ -185,6 +185,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } + void SettingContainer::SetExpanded(bool expanded) + { + if (const auto& child{ GetTemplateChild(L"Expander") }) + { + if (const auto& expander{ child.try_as() }) + { + expander.IsExpanded(expanded); + } + } + } + // Method Description: // - Updates the override system visibility and text // Arguments: diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.h b/src/cascadia/TerminalSettingsEditor/SettingContainer.h index 5b48b438f51..be543681b3c 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.h +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.h @@ -29,6 +29,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnApplyTemplate(); + void SetExpanded(bool expanded); + DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Header); DEPENDENCY_PROPERTY(hstring, HelpText); DEPENDENCY_PROPERTY(hstring, CurrentValue); diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.idl b/src/cascadia/TerminalSettingsEditor/SettingContainer.idl index 1148e0eb6d9..8b5fd0eba95 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.idl +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.idl @@ -7,6 +7,8 @@ namespace Microsoft.Terminal.Settings.Editor { SettingContainer(); + void SetExpanded(Boolean expanded); + IInspectable Header; static Windows.UI.Xaml.DependencyProperty HeaderProperty { get; }; diff --git a/src/cascadia/TerminalSettingsModel/FontConfig.cpp b/src/cascadia/TerminalSettingsModel/FontConfig.cpp index fafdf0976bc..b01ec960658 100644 --- a/src/cascadia/TerminalSettingsModel/FontConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/FontConfig.cpp @@ -30,6 +30,18 @@ winrt::com_ptr FontConfig::CopyFontInfo(const FontConfig* source, wi MTSM_FONT_SETTINGS(FONT_SETTINGS_COPY) #undef FONT_SETTINGS_COPY + // We cannot simply copy the font axes and features with `fontInfo->_FontAxes = source->_FontAxes;` + // since that'll just create a reference; we have to manually copy the values. + if (source->_FontAxes) + { + std::map fontAxes; + for (const auto keyValuePair : source->_FontAxes.value()) + { + fontAxes.insert(std::pair(keyValuePair.Key(), keyValuePair.Value())); + } + fontInfo->_FontAxes = winrt::single_threaded_map(std::move(fontAxes)); + } + return fontInfo; } From 6e451a2d4b6574aed846385b8223894472366805 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Thu, 29 Feb 2024 12:08:52 -0800 Subject: [PATCH 140/603] Allow editing font features in the Settings UI (#16678) ## Summary of the Pull Request **Targets #16104** Same as #16104, but for font features ## References and Relevant Issues #10000 ## Validation Steps Performed Font features are detected correctly and can be set in the settings UI ![image](https://github.com/microsoft/terminal/assets/26824113/054c30fa-c584-4b71-872d-d956526c373b) ![image](https://github.com/microsoft/terminal/assets/26824113/484a20eb-abe9-478c-99cf-f63939ab4c5b) ## PR Checklist - [ ] Closes #xxx - [ ] Tests added/passed - [ ] Documentation updated - If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx - [ ] Schema updated (if necessary) --- .github/actions/spelling/allow/apis.txt | 8 + .../TerminalSettingsEditor/Appearances.cpp | 344 +++++++++++++++++- .../TerminalSettingsEditor/Appearances.h | 47 ++- .../TerminalSettingsEditor/Appearances.idl | 17 + .../TerminalSettingsEditor/Appearances.xaml | 63 ++++ .../Resources/en-US/Resources.resw | 120 ++++++ .../TerminalSettingsModel/FontConfig.cpp | 10 + 7 files changed, 605 insertions(+), 4 deletions(-) diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 5ae7e5753df..839fcbe76b2 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -1,3 +1,5 @@ +aalt +abvm ACCEPTFILES ACCESSDENIED acl @@ -36,6 +38,7 @@ delayimp DERR dlldata DNE +dnom DONTADDTORECENT DWMSBT DWMWA @@ -49,6 +52,7 @@ EXPCMDFLAGS EXPCMDSTATE filetime FILTERSPEC +fina FORCEFILESYSTEM FORCEMINIMIZE frac @@ -120,6 +124,7 @@ LSHIFT LTGRAY MAINWINDOW MAXIMIZEBOX +medi memchr memicmp MENUCOMMAND @@ -150,6 +155,7 @@ NOTIFYBYPOS NOTIFYICON NOTIFYICONDATA ntprivapi +numr oaidl ocidl ODR @@ -175,9 +181,11 @@ REGCLS RETURNCMD rfind RLO +rnrn ROOTOWNER roundf RSHIFT +rvrn SACL schandle SEH diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index 848cb99349f..0ba4f344e23 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -5,6 +5,7 @@ #include "Appearances.h" #include "Appearances.g.cpp" #include "AxisKeyValuePair.g.cpp" +#include "FeatureKeyValuePair.g.cpp" #include "EnumEntry.h" #include @@ -19,6 +20,20 @@ using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Microsoft::Terminal::Settings::Model; +static constexpr std::array DefaultFeatures{ + L"rlig", + L"locl", + L"ccmp", + L"calt", + L"liga", + L"clig", + L"rnrn", + L"kern", + L"mark", + L"mkmk", + L"dist" +}; + namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { bool Font::HasPowerlineCharacters() @@ -86,7 +101,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::impl::hstring_builder builder{ length }; if (SUCCEEDED(names->GetString(localeIndex, builder.data(), length + 1))) { - fontAxesTagsAndNames.insert(std::pair(_axisTagToString(axesVector[i].axisTag), builder.to_hstring())); + fontAxesTagsAndNames.insert(std::pair(_tagToString(axesVector[i].axisTag), builder.to_hstring())); continue; } } @@ -100,7 +115,47 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _fontAxesTagsAndNames; } - winrt::hstring Font::_axisTagToString(DWRITE_FONT_AXIS_TAG tag) + IMap Font::FontFeaturesTagsAndNames() + { + if (!_fontFeaturesTagsAndNames) + { + wil::com_ptr font; + THROW_IF_FAILED(_family->GetFont(0, font.put())); + wil::com_ptr fontFace; + THROW_IF_FAILED(font->CreateFontFace(fontFace.put())); + + wil::com_ptr factory; + THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<::IUnknown**>(factory.addressof()))); + wil::com_ptr textAnalyzer; + factory->CreateTextAnalyzer(textAnalyzer.addressof()); + wil::com_ptr textAnalyzer2 = textAnalyzer.query(); + + DWRITE_SCRIPT_ANALYSIS scriptAnalysis{}; + UINT32 tagCount; + // we have to call GetTypographicFeatures twice, first to get the actual count then to get the features + std::ignore = textAnalyzer2->GetTypographicFeatures(fontFace.get(), scriptAnalysis, L"en-us", 0, &tagCount, nullptr); + std::vector tags{ tagCount }; + textAnalyzer2->GetTypographicFeatures(fontFace.get(), scriptAnalysis, L"en-us", tagCount, &tagCount, tags.data()); + + std::unordered_map fontFeaturesTagsAndNames; + for (auto tag : tags) + { + const auto tagString = _tagToString(tag); + hstring formattedResourceString{ fmt::format(L"Profile_FontFeature_{}", tagString) }; + hstring localizedName{ tagString }; + // we have resource strings for common font features, see if one for this feature exists + if (HasLibraryResourceWithName(formattedResourceString)) + { + localizedName = GetLibraryResourceString(formattedResourceString); + } + fontFeaturesTagsAndNames.insert(std::pair(tagString, localizedName)); + } + _fontFeaturesTagsAndNames = winrt::single_threaded_map(std::move(fontFeaturesTagsAndNames)); + } + return _fontFeaturesTagsAndNames; + } + + winrt::hstring Font::_tagToString(DWRITE_FONT_AXIS_TAG tag) { std::wstring result; for (int i = 0; i < 4; ++i) @@ -110,6 +165,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return winrt::hstring{ result }; } + hstring Font::_tagToString(DWRITE_FONT_FEATURE_TAG tag) + { + std::wstring result; + for (int i = 0; i < 4; ++i) + { + result.push_back((tag >> (i * 8)) & 0xFF); + } + return hstring{ result }; + } + AxisKeyValuePair::AxisKeyValuePair(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap) : _AxisKey{ axisKey }, _AxisValue{ axisValue }, @@ -191,6 +256,87 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } + FeatureKeyValuePair::FeatureKeyValuePair(hstring featureKey, uint32_t featureValue, const IMap& baseMap, const IMap& tagToNameMap) : + _FeatureKey{ featureKey }, + _FeatureValue{ featureValue }, + _baseMap{ baseMap }, + _tagToNameMap{ tagToNameMap } + { + if (_tagToNameMap.HasKey(_FeatureKey)) + { + int32_t i{ 0 }; + // this loop assumes that every time we iterate through the map + // we get the same ordering + for (const auto tagAndName : _tagToNameMap) + { + if (tagAndName.Key() == _FeatureKey) + { + _FeatureIndex = i; + break; + } + ++i; + } + } + } + + hstring FeatureKeyValuePair::FeatureKey() + { + return _FeatureKey; + } + + uint32_t FeatureKeyValuePair::FeatureValue() + { + return _FeatureValue; + } + + int32_t FeatureKeyValuePair::FeatureIndex() + { + return _FeatureIndex; + } + + void FeatureKeyValuePair::FeatureValue(uint32_t featureValue) + { + if (featureValue != _FeatureValue) + { + _baseMap.Remove(_FeatureKey); + _FeatureValue = featureValue; + _baseMap.Insert(_FeatureKey, _FeatureValue); + _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"FeatureValue" }); + } + } + + void FeatureKeyValuePair::FeatureKey(hstring featureKey) + { + if (featureKey != _FeatureKey) + { + _baseMap.Remove(_FeatureKey); + _FeatureKey = featureKey; + _baseMap.Insert(_FeatureKey, _FeatureValue); + _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"FeatureKey" }); + } + } + + void FeatureKeyValuePair::FeatureIndex(int32_t featureIndex) + { + if (featureIndex != _FeatureIndex) + { + _FeatureIndex = featureIndex; + + int32_t i{ 0 }; + // same as in the constructor, this assumes that iterating through the map + // gives us the same order every time + for (const auto tagAndName : _tagToNameMap) + { + if (i == _FeatureIndex) + { + FeatureKey(tagAndName.Key()); + break; + } + ++i; + } + } + } + AppearanceViewModel::AppearanceViewModel(const Model::AppearanceConfig& appearance) : _appearance{ appearance } { @@ -217,9 +363,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // so when the FontAxes change (say from the reset button), reinitialize the observable vector InitializeFontAxesVector(); } + else if (viewModelProperty == L"FontFeatures") + { + // same as the FontAxes one + InitializeFontFeaturesVector(); + } }); InitializeFontAxesVector(); + InitializeFontFeaturesVector(); // Cache the original BG image path. If the user clicks "Use desktop // wallpaper", then un-checks it, this is the string we'll restore to @@ -477,6 +629,150 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return axisKeyValuePair; } + void AppearanceViewModel::AddNewFeatureKeyValuePair() + { + const auto fontInfo = _appearance.SourceProfile().FontInfo(); + auto fontFeaturesMap = fontInfo.FontFeatures(); + if (!fontFeaturesMap) + { + fontFeaturesMap = winrt::single_threaded_map(); + fontInfo.FontFeatures(fontFeaturesMap); + } + + // find one feature that does not already exist, and add that + // if there are no more possible features to add, the button is disabled so there shouldn't be a way to get here + const auto possibleFeaturesTagsAndNames = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); + for (const auto tagAndName : possibleFeaturesTagsAndNames) + { + const auto featureKey = tagAndName.Key(); + if (!fontFeaturesMap.HasKey(featureKey)) + { + const auto featureDefaultValue = _IsDefaultFeature(featureKey) ? 1 : 0; + fontFeaturesMap.Insert(featureKey, featureDefaultValue); + FontFeaturesVector().Append(_CreateFeatureKeyValuePairHelper(featureKey, featureDefaultValue, fontFeaturesMap, possibleFeaturesTagsAndNames)); + break; + } + } + _NotifyChanges(L"CanFontFeaturesBeAdded"); + } + + void AppearanceViewModel::DeleteFeatureKeyValuePair(hstring key) + { + for (uint32_t i = 0; i < _FontFeaturesVector.Size(); i++) + { + if (_FontFeaturesVector.GetAt(i).FeatureKey() == key) + { + FontFeaturesVector().RemoveAt(i); + _appearance.SourceProfile().FontInfo().FontFeatures().Remove(key); + if (_FontFeaturesVector.Size() == 0) + { + _appearance.SourceProfile().FontInfo().ClearFontFeatures(); + } + break; + } + } + _NotifyChanges(L"CanFontAxesBeAdded"); + } + + void AppearanceViewModel::InitializeFontFeaturesVector() + { + if (!_FontFeaturesVector) + { + _FontFeaturesVector = single_threaded_observable_vector(); + } + + _FontFeaturesVector.Clear(); + if (const auto fontFeaturesMap = _appearance.SourceProfile().FontInfo().FontFeatures()) + { + const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); + for (const auto feature : fontFeaturesMap) + { + const auto featureKey = feature.Key(); + // only show the features that the font supports + // any features that the font doesn't support continue to be stored in the json, we just don't show them in the UI + if (fontFeaturesTagToNameMap.HasKey(featureKey)) + { + _FontFeaturesVector.Append(_CreateFeatureKeyValuePairHelper(featureKey, feature.Value(), fontFeaturesMap, fontFeaturesTagToNameMap)); + } + } + } + _NotifyChanges(L"AreFontFeaturesAvailable", L"CanFontFeaturesBeAdded"); + } + + // Method Description: + // - Determines whether the currently selected font has any font features + bool AppearanceViewModel::AreFontFeaturesAvailable() + { + return ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames().Size() > 0; + } + + // Method Description: + // - Determines whether the currently selected font has any font features that have not already been set + bool AppearanceViewModel::CanFontFeaturesBeAdded() + { + if (const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); fontFeaturesTagToNameMap.Size() > 0) + { + if (const auto fontFeaturesMap = _appearance.SourceProfile().FontInfo().FontFeatures()) + { + for (const auto tagAndName : fontFeaturesTagToNameMap) + { + if (!fontFeaturesMap.HasKey(tagAndName.Key())) + { + // we found a feature that has not been set + return true; + } + } + // all possible features have been set already + return false; + } + // the font supports font features but the profile has none set + return true; + } + // the font does not support any font features + return false; + } + + // Method Description: + // - Creates a FeatureKeyValuePair and sets up an event handler for it + Editor::FeatureKeyValuePair AppearanceViewModel::_CreateFeatureKeyValuePairHelper(hstring featureKey, uint32_t featureValue, const IMap& baseMap, const IMap& tagToNameMap) + { + const auto featureKeyValuePair = winrt::make(featureKey, featureValue, baseMap, tagToNameMap); + // when either the key or the value changes, send an event for the preview control to catch + featureKeyValuePair.PropertyChanged([weakThis = get_weak()](auto& sender, const PropertyChangedEventArgs& args) { + if (auto appVM{ weakThis.get() }) + { + appVM->_NotifyChanges(L"FeatureKeyValuePair"); + const auto settingName{ args.PropertyName() }; + if (settingName == L"FeatureKey") + { + const auto senderPair = sender.as(); + const auto senderKey = senderPair->FeatureKey(); + if (appVM->_IsDefaultFeature(senderKey)) + { + senderPair->FeatureValue(1); + } + else + { + senderPair->FeatureValue(0); + } + } + } + }); + return featureKeyValuePair; + } + + bool AppearanceViewModel::_IsDefaultFeature(winrt::hstring featureKey) + { + for (const auto defaultFeature : DefaultFeatures) + { + if (defaultFeature == featureKey) + { + return true; + } + } + return false; + } + DependencyProperty Appearances::_AppearanceProperty{ nullptr }; Appearances::Appearances() : @@ -546,6 +842,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _FontAxesNames = winrt::single_threaded_observable_vector(); FontAxesNamesCVS().Source(_FontAxesNames); + _FontFeaturesNames = winrt::single_threaded_observable_vector(); + FontFeaturesNamesCVS().Source(_FontFeaturesNames); + INITIALIZE_BINDABLE_ENUM_SETTING(IntenseTextStyle, IntenseTextStyle, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle, L"Appearance_IntenseTextStyle", L"Content"); } @@ -613,7 +912,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _FontAxesNames.Append(tagAndName.Value()); } - // when the font face changes, we have to tell the view model to update the font axes vector + _FontFeaturesNames.Clear(); + const auto featuresTagsAndNames = newFontFace.FontFeaturesTagsAndNames(); + for (const auto tagAndName : featuresTagsAndNames) + { + _FontFeaturesNames.Append(tagAndName.Value()); + } + + // when the font face changes, we have to tell the view model to update the font axes/features vectors // since the new font may not have the same possible axes as the previous one Appearance().InitializeFontAxesVector(); if (!Appearance().AreFontAxesAvailable()) @@ -627,6 +933,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { FontAxesContainer().HelpText(RS_(L"Profile_FontAxesAvailable/Text")); } + + Appearance().InitializeFontFeaturesVector(); + if (!Appearance().AreFontFeaturesAvailable()) + { + // if the previous font had available font features and the expander was expanded, + // at this point the expander would be set to disabled so manually collapse it + FontFeaturesContainer().SetExpanded(false); + FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesUnavailable/Text")); + } + else + { + FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesAvailable/Text")); + } } void Appearances::_ViewModelChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*args*/) @@ -648,6 +967,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation FontAxesCVS().Source(Appearance().FontAxesVector()); Appearance().AreFontAxesAvailable() ? FontAxesContainer().HelpText(RS_(L"Profile_FontAxesAvailable/Text")) : FontAxesContainer().HelpText(RS_(L"Profile_FontAxesUnavailable/Text")); + FontFeaturesCVS().Source(Appearance().FontFeaturesVector()); + Appearance().AreFontFeaturesAvailable() ? FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesAvailable/Text")) : FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesUnavailable/Text")); + _ViewModelChangedRevoker = Appearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) { const auto settingName{ args.PropertyName() }; if (settingName == L"CursorShape") @@ -786,6 +1108,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Appearance().AddNewAxisKeyValuePair(); } + void Appearances::DeleteFeatureKeyValuePair_Click(const IInspectable& sender, const RoutedEventArgs& /*e*/) + { + if (const auto& button{ sender.try_as() }) + { + if (const auto& tag{ button.Tag().try_as() }) + { + Appearance().DeleteFeatureKeyValuePair(tag.value()); + } + } + } + + void Appearances::AddNewFeatureKeyValuePair_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/) + { + Appearance().AddNewFeatureKeyValuePair(); + } + bool Appearances::IsVintageCursor() const { return Appearance().CursorShape() == Core::CursorStyle::Vintage; diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index 53a244ab0d9..d72bb418693 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -18,11 +18,13 @@ Author(s): #include "Font.g.h" #include "AxisKeyValuePair.g.h" +#include "FeatureKeyValuePair.g.h" #include "Appearances.g.h" #include "AppearanceViewModel.g.h" #include "Utils.h" #include "ViewModelHelpers.h" #include "SettingContainer.h" +#include namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { @@ -39,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation hstring ToString() { return _LocalizedName; } bool HasPowerlineCharacters(); Windows::Foundation::Collections::IMap FontAxesTagsAndNames(); + Windows::Foundation::Collections::IMap FontFeaturesTagsAndNames(); WINRT_PROPERTY(hstring, Name); WINRT_PROPERTY(hstring, LocalizedName); @@ -46,8 +49,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation private: winrt::com_ptr _family; std::optional _hasPowerlineCharacters; - winrt::hstring _axisTagToString(DWRITE_FONT_AXIS_TAG tag); + + winrt::hstring _tagToString(DWRITE_FONT_AXIS_TAG tag); + winrt::hstring _tagToString(DWRITE_FONT_FEATURE_TAG tag); + Windows::Foundation::Collections::IMap _fontAxesTagsAndNames; + Windows::Foundation::Collections::IMap _fontFeaturesTagsAndNames; }; struct AxisKeyValuePair : AxisKeyValuePairT, ViewModelHelper @@ -73,6 +80,29 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Windows::Foundation::Collections::IMap _tagToNameMap{ nullptr }; }; + struct FeatureKeyValuePair : FeatureKeyValuePairT, ViewModelHelper + { + FeatureKeyValuePair(winrt::hstring featureKey, uint32_t featureValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); + + winrt::hstring FeatureKey(); + void FeatureKey(winrt::hstring featureKey); + + uint32_t FeatureValue(); + void FeatureValue(uint32_t featureValue); + + int32_t FeatureIndex(); + void FeatureIndex(int32_t featureIndex); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + + private: + winrt::hstring _FeatureKey; + uint32_t _FeatureValue; + int32_t _FeatureIndex; + Windows::Foundation::Collections::IMap _baseMap{ nullptr }; + Windows::Foundation::Collections::IMap _tagToNameMap{ nullptr }; + }; + struct AppearanceViewModel : AppearanceViewModelT, ViewModelHelper { public: @@ -102,6 +132,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool AreFontAxesAvailable(); bool CanFontAxesBeAdded(); + void AddNewFeatureKeyValuePair(); + void DeleteFeatureKeyValuePair(winrt::hstring key); + void InitializeFontFeaturesVector(); + bool AreFontFeaturesAvailable(); + bool CanFontFeaturesBeAdded(); + WINRT_PROPERTY(bool, IsDefault, false); // These settings are not defined in AppearanceConfig, so we grab them @@ -113,6 +149,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontSize); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontWeight); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontAxes); + OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontFeatures); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), EnableBuiltinGlyphs); OBSERVABLE_PROJECTED_SETTING(_appearance, RetroTerminalEffect); @@ -128,12 +165,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance, AdjustIndistinguishableColors); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, SchemesList, _propertyChangedHandlers, nullptr); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontAxesVector, _propertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontFeaturesVector, _propertyChangedHandlers, nullptr); private: Model::AppearanceConfig _appearance; winrt::hstring _lastBgImagePath; Editor::AxisKeyValuePair _CreateAxisKeyValuePairHelper(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); + Editor::FeatureKeyValuePair _CreateFeatureKeyValuePairHelper(winrt::hstring axisKey, uint32_t axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); + + bool _IsDefaultFeature(winrt::hstring featureTag); }; struct Appearances : AppearancesT @@ -156,6 +197,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void FontFace_SelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e); void DeleteAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void AddNewAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + void DeleteFeatureKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + void AddNewFeatureKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); // manually bind FontWeight Windows::Foundation::IInspectable CurrentFontWeight() const; @@ -184,6 +227,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Editor::EnumEntry _CustomFontWeight{ nullptr }; Windows::Foundation::Collections::IObservableVector _FontAxesNames; + Windows::Foundation::Collections::IObservableVector _FontFeaturesNames; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; static void _ViewModelChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); @@ -195,4 +239,5 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation { BASIC_FACTORY(Appearances); BASIC_FACTORY(AxisKeyValuePair); + BASIC_FACTORY(FeatureKeyValuePair); } diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.idl b/src/cascadia/TerminalSettingsEditor/Appearances.idl index 68bd887d217..17ef8a1867a 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.idl +++ b/src/cascadia/TerminalSettingsEditor/Appearances.idl @@ -23,6 +23,7 @@ namespace Microsoft.Terminal.Settings.Editor String LocalizedName { get; }; Boolean HasPowerlineCharacters { get; }; Windows.Foundation.Collections.IMap FontAxesTagsAndNames { get; }; + Windows.Foundation.Collections.IMap FontFeaturesTagsAndNames { get; }; } // We have to make this because we cannot bind an IObservableMap to a ListView in XAML (in c++) @@ -35,6 +36,14 @@ namespace Microsoft.Terminal.Settings.Editor Int32 AxisIndex; } + runtimeclass FeatureKeyValuePair : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + FeatureKeyValuePair(String featureKey, UInt32 featureValue, Windows.Foundation.Collections.IMap baseMap, Windows.Foundation.Collections.IMap tagToNameMap); + String FeatureKey; + UInt32 FeatureValue; + Int32 FeatureIndex; + } + runtimeclass AppearanceViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged { Boolean IsDefault; @@ -58,6 +67,14 @@ namespace Microsoft.Terminal.Settings.Editor Windows.Foundation.Collections.IObservableVector FontAxesVector; OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.Collections.IMap, FontAxes); + void AddNewFeatureKeyValuePair(); + void DeleteFeatureKeyValuePair(String key); + void InitializeFontFeaturesVector(); + Boolean AreFontFeaturesAvailable { get; }; + Boolean CanFontFeaturesBeAdded { get; }; + Windows.Foundation.Collections.IObservableVector FontFeaturesVector; + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.Collections.IMap, FontFeatures); + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, FontFace); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Double, LineHeight); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index 0b5a9c2abfa..47c01df1fc9 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -39,6 +39,8 @@ + @@ -349,6 +351,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Add new Button label that adds a new font axis for the current font. + + Add new + Button label that adds a new font feature for the current font. + Background image opacity Name for a control to choose the opacity of the image presented on the background of the app. @@ -938,6 +942,18 @@ The selected font has no variable font axes. A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". + + Font features + Header for a control to allow editing the font features. + + + Add or remove font features for the given font. + A description for what the "font features" setting does. Presented near "Profile_FontFeatures". + + + The selected font has no font features. + A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". + General Header for a sub-page of profile settings focused on more general scenarios. @@ -1254,6 +1270,110 @@ Thin This is the formal name for a font weight. + + Required ligatures + This is the formal name for a font feature. + + + Localized forms + This is the formal name for a font feature. + + + Composition/decomposition + This is the formal name for a font feature. + + + Contextual alternates + This is the formal name for a font feature. + + + Standard ligatures + This is the formal name for a font feature. + + + Contextual ligatures + This is the formal name for a font feature. + + + Required variation alternates + This is the formal name for a font feature. + + + Kerning + This is the formal name for a font feature. + + + Mark positioning + This is the formal name for a font feature. + + + Mark to mark positioning + This is the formal name for a font feature. + + + Distance + This is the formal name for a font feature. + + + Access all alternates + This is the formal name for a font feature. + + + Case sensitive forms + This is the formal name for a font feature. + + + Denominator + This is the formal name for a font feature. + + + Terminal forms + This is the formal name for a font feature. + + + Fractions + This is the formal name for a font feature. + + + Initial forms + This is the formal name for a font feature. + + + Medial forms + This is the formal name for a font feature. + + + Numerator + This is the formal name for a font feature. + + + Ordinals + This is the formal name for a font feature. + + + Required contextual alternates + This is the formal name for a font feature. + + + Scientific inferiors + This is the formal name for a font feature. + + + Subscript + This is the formal name for a font feature. + + + Superscript + This is the formal name for a font feature. + + + Slashed zero + This is the formal name for a font feature. + + + Above-base mark positioning + This is the formal name for a font feature. + Launch size Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". diff --git a/src/cascadia/TerminalSettingsModel/FontConfig.cpp b/src/cascadia/TerminalSettingsModel/FontConfig.cpp index b01ec960658..fd9a181ae30 100644 --- a/src/cascadia/TerminalSettingsModel/FontConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/FontConfig.cpp @@ -42,6 +42,16 @@ winrt::com_ptr FontConfig::CopyFontInfo(const FontConfig* source, wi fontInfo->_FontAxes = winrt::single_threaded_map(std::move(fontAxes)); } + if (source->_FontFeatures) + { + std::map fontFeatures; + for (const auto keyValuePair : source->_FontFeatures.value()) + { + fontFeatures.insert(std::pair(keyValuePair.Key(), keyValuePair.Value())); + } + fontInfo->_FontFeatures = winrt::single_threaded_map(std::move(fontFeatures)); + } + return fontInfo; } From b780d8ab7e1cdbc7066cd67782126df214872a98 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 29 Feb 2024 21:25:29 +0100 Subject: [PATCH 141/603] A minor cleanup of ProfileViewModel (#16788) This is just a minor cleanup I did as a drive-by while working on customized font fallback. The benefit of this change is that it's a tiny bit less expensive, but also that it's a lot easier to read. The split into "get index" and "get string by index" helps us to more easily handle both, missing locales and locale fallback. The code that ties everything together then ends up being just 7 lines. --- .../TerminalSettingsEditor/Appearances.h | 6 +- .../ProfileViewModel.cpp | 86 ++++++++----------- 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index d72bb418693..651523b7a69 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -31,9 +31,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation struct Font : FontT { public: - Font(std::wstring name, std::wstring localizedName, IDWriteFontFamily* family) : - _Name{ name }, - _LocalizedName{ localizedName } + Font(winrt::hstring name, winrt::hstring localizedName, IDWriteFontFamily* family) : + _Name{ std::move(name) }, + _LocalizedName{ std::move(localizedName) } { _family.copy_from(family); } diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index db991b9230d..5682ae5cc18 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -22,7 +22,7 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { - static Editor::Font _FontObjectForDWriteFont(IDWriteFontFamily* family); + static Editor::Font fontObjectForDWriteFont(IDWriteFontFamily* family, const wchar_t* locale); Windows::Foundation::Collections::IObservableVector ProfileViewModel::_MonospaceFontList{ nullptr }; Windows::Foundation::Collections::IObservableVector ProfileViewModel::_FontList{ nullptr }; @@ -119,6 +119,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation wil::com_ptr fontCollection; THROW_IF_FAILED(factory->GetSystemFontCollection(fontCollection.addressof(), TRUE)); + wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; + if (!GetUserDefaultLocaleName(&localeName[0], LOCALE_NAME_MAX_LENGTH)) + { + memcpy(&localeName[0], L"en-US", 12); + } + for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i) { try @@ -128,7 +134,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation THROW_IF_FAILED(fontCollection->GetFontFamily(i, fontFamily.put())); // construct a font entry for tracking - if (const auto fontEntry{ _FontObjectForDWriteFont(fontFamily.get()) }) + if (const auto fontEntry{ fontObjectForDWriteFont(fontFamily.get(), &localeName[0]) }) { // check if the font is monospaced try @@ -199,65 +205,43 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return fallbackFont; } - static Editor::Font _FontObjectForDWriteFont(IDWriteFontFamily* family) + static winrt::hstring getLocalizedStringByIndex(IDWriteLocalizedStrings* strings, UINT32 index) { - // used for the font's name as an identifier (i.e. text block's font family property) - std::wstring nameID; - UINT32 nameIDIndex; + UINT32 length = 0; + THROW_IF_FAILED(strings->GetStringLength(index, &length)); - // used for the font's localized name - std::wstring localizedName; - UINT32 localizedNameIndex; + winrt::impl::hstring_builder builder{ length }; + THROW_IF_FAILED(strings->GetString(index, builder.data(), length + 1)); - // get the font's localized names - winrt::com_ptr localizedFamilyNames; - THROW_IF_FAILED(family->GetFamilyNames(localizedFamilyNames.put())); + return builder.to_hstring(); + } - // use our current locale to find the localized name - auto exists{ FALSE }; - HRESULT hr; - wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; - if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH)) - { - hr = localizedFamilyNames->FindLocaleName(localeName, &localizedNameIndex, &exists); - } - if (SUCCEEDED(hr) && !exists) - { - // if we can't find the font for our locale, fallback to the en-us one - // Source: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-findlocalename - hr = localizedFamilyNames->FindLocaleName(L"en-us", &localizedNameIndex, &exists); - } - if (!exists) + static UINT32 getLocalizedStringIndex(IDWriteLocalizedStrings* strings, const wchar_t* locale, UINT32 fallback) + { + UINT32 index; + BOOL exists; + if (FAILED(strings->FindLocaleName(locale, &index, &exists)) || !exists) { - // failed to find the correct locale, using the first one - localizedNameIndex = 0; + index = fallback; } + return index; + } - // get the localized name - UINT32 nameLength; - THROW_IF_FAILED(localizedFamilyNames->GetStringLength(localizedNameIndex, &nameLength)); + static Editor::Font fontObjectForDWriteFont(IDWriteFontFamily* family, const wchar_t* locale) + { + wil::com_ptr familyNames; + THROW_IF_FAILED(family->GetFamilyNames(familyNames.addressof())); - localizedName.resize(nameLength); - THROW_IF_FAILED(localizedFamilyNames->GetString(localizedNameIndex, localizedName.data(), nameLength + 1)); + // If en-us is missing we fall back to whatever is at index 0. + const auto ci = getLocalizedStringIndex(familyNames.get(), L"en-us", 0); + // If our locale is missing we fall back to en-us. + const auto li = getLocalizedStringIndex(familyNames.get(), locale, ci); - // now get the nameID - hr = localizedFamilyNames->FindLocaleName(L"en-us", &nameIDIndex, &exists); - if (FAILED(hr) || !exists) - { - // failed to find it, using the first one - nameIDIndex = 0; - } - - // get the nameID - THROW_IF_FAILED(localizedFamilyNames->GetStringLength(nameIDIndex, &nameLength)); - nameID.resize(nameLength); - THROW_IF_FAILED(localizedFamilyNames->GetString(nameIDIndex, nameID.data(), nameLength + 1)); + auto canonical = getLocalizedStringByIndex(familyNames.get(), ci); + // If the canonical/localized indices are the same, there's no need to get the other string. + auto localized = ci == li ? canonical : getLocalizedStringByIndex(familyNames.get(), li); - if (!nameID.empty() && !localizedName.empty()) - { - return make(nameID, localizedName, family); - } - return nullptr; + return make(std::move(canonical), std::move(localized), family); } winrt::guid ProfileViewModel::OriginalProfileGuid() const noexcept From 043d5cd4840d6cab1e48bcaf2e6ac1038f6f0333 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 29 Feb 2024 22:59:15 +0100 Subject: [PATCH 142/603] Fix bugs in CharToColumnMapper (#16787) Aside from overall simplifying `CharToColumnMapper` this fixes 2 bugs: * The backward search loop may have iterated 1 column too far, because it didn't stop at `*current <= *target`, but rather at `*(current - 1) <= *target`. This issue was only apparent when surrogate pairs were being used in a row. * When the target offset is that of a trailing surrogate pair the forward search loop may have iterated 1 column too far. It's somewhat unlikely for this to happen since this code is only used through ICU, but you never know. This is a continuation of PR #16775. --- src/buffer/out/Row.cpp | 56 +++++++---------- src/buffer/out/Row.hpp | 2 +- .../TextBuffer.Unit.Tests.vcxproj | 3 +- .../out/ut_textbuffer/UTextAdapterTests.cpp | 63 +++++++++++++++++++ src/buffer/out/ut_textbuffer/sources | 1 + src/inc/til/point.h | 12 ++++ 6 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 7729cdcdbd4..4722dc0dfc4 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -92,47 +92,35 @@ CharToColumnMapper::CharToColumnMapper(const wchar_t* chars, const uint16_t* cha // If given a position (`offset`) inside the ROW's text, this function will return the corresponding column. // This function in particular returns the glyph's first column. -til::CoordType CharToColumnMapper::GetLeadingColumnAt(ptrdiff_t offset) noexcept +til::CoordType CharToColumnMapper::GetLeadingColumnAt(ptrdiff_t targetOffset) noexcept { - offset = clamp(offset, 0, _lastCharOffset); + targetOffset = clamp(targetOffset, 0, _lastCharOffset); + + // This code needs to fulfill two conditions on top of the obvious (a forward/backward search): + // A: We never want to stop on a column that is marked with CharOffsetsTrailer (= "GetLeadingColumn"). + // B: With these parameters we always want to stop at currentOffset=4: + // _charOffsets={4, 6} + // currentOffset=4 *OR* 6 + // targetOffset=5 + // This is because we're being asked for a "LeadingColumn", while the caller gave us the offset of a + // trailing surrogate pair or similar. Returning the column of the leading half is the correct choice. auto col = _currentColumn; - const auto currentOffset = _charOffsets[col] & CharOffsetsMask; + auto currentOffset = _charOffsets[col]; - // Goal: Move the _currentColumn cursor to a cell which contains the given target offset. - // Depending on where the target offset is we have to either search forward or backward. - if (offset < currentOffset) + // A plain forward-search until we find our targetOffset. + // This loop may iterate too far and thus violate our example in condition B, however... + while (targetOffset > (currentOffset & CharOffsetsMask)) { - // Backward search. - // Goal: Find the first preceding column where the offset is <= the target offset. This results in the first - // cell that contains our target offset, even if that offset is in the middle of a long grapheme. - // - // We abuse the fact that the trailing half of wide glyphs is marked with CharOffsetsTrailer to our advantage. - // Since they're >0x8000, the `offset < _charOffsets[col]` check will always be true and ensure we iterate over them. - // - // Since _charOffsets cannot contain negative values and because offset has been - // clamped to be positive we naturally exit when reaching the first column. - for (; offset < _charOffsets[col - 1]; --col) - { - } + currentOffset = _charOffsets[++col]; } - else if (offset > currentOffset) + // This backward-search is not just a counter-part to the above, but simultaneously also handles conditions A and B. + // It abuses the fact that columns marked with CharOffsetsTrailer are >0x8000 and targetOffset is always <0x8000. + // This means we skip all "trailer" columns when iterating backwards, and only stop on a non-trailer (= condition A). + // Condition B is fixed simply because we iterate backwards after the forward-search (in that exact order). + while (targetOffset < currentOffset) { - // Forward search. - // Goal: Find the first subsequent column where the offset is > the target offset. - // We stop 1 column before that however so that the next loop works correctly. - // It's the inverse of the loop above. - // - // Since offset has been clamped to be at most 1 less than the maximum - // _charOffsets value the loop naturally exits before hitting the end. - for (; offset >= (_charOffsets[col + 1] & CharOffsetsMask); ++col) - { - } - // Now that we found the cell that definitely includes this char offset, - // we have to iterate back to the cell's starting column. - for (; WI_IsFlagSet(_charOffsets[col], CharOffsetsTrailer); --col) - { - } + currentOffset = _charOffsets[--col]; } _currentColumn = col; diff --git a/src/buffer/out/Row.hpp b/src/buffer/out/Row.hpp index af8088c3ccd..197343df6d8 100644 --- a/src/buffer/out/Row.hpp +++ b/src/buffer/out/Row.hpp @@ -71,7 +71,7 @@ struct CharToColumnMapper { CharToColumnMapper(const wchar_t* chars, const uint16_t* charOffsets, ptrdiff_t lastCharOffset, til::CoordType currentColumn) noexcept; - til::CoordType GetLeadingColumnAt(ptrdiff_t offset) noexcept; + til::CoordType GetLeadingColumnAt(ptrdiff_t targetOffset) noexcept; til::CoordType GetTrailingColumnAt(ptrdiff_t offset) noexcept; til::CoordType GetLeadingColumnAt(const wchar_t* str) noexcept; til::CoordType GetTrailingColumnAt(const wchar_t* str) noexcept; diff --git a/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj b/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj index ad3b48c7cb5..c52d012baec 100644 --- a/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj +++ b/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj @@ -14,6 +14,7 @@ + Create @@ -41,4 +42,4 @@ - \ No newline at end of file + diff --git a/src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp b/src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp new file mode 100644 index 00000000000..ee879f4f677 --- /dev/null +++ b/src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "WexTestClass.h" +#include "../textBuffer.hpp" +#include "../../renderer/inc/DummyRenderer.hpp" + +template<> +class WEX::TestExecution::VerifyOutputTraits> +{ +public: + static WEX::Common::NoThrowString ToString(const std::vector& vec) + { + WEX::Common::NoThrowString str; + str.Append(L"{ "); + for (size_t i = 0; i < vec.size(); ++i) + { + const auto& s = vec[i]; + if (i != 0) + { + str.Append(L", "); + } + str.AppendFormat(L"{(%d, %d), (%d, %d)}", s.start.x, s.start.y, s.end.x, s.end.y); + } + str.Append(L" }"); + return str; + } +}; + +class UTextAdapterTests +{ + TEST_CLASS(UTextAdapterTests); + + TEST_METHOD(Unicode) + { + DummyRenderer renderer; + TextBuffer buffer{ til::size{ 24, 1 }, TextAttribute{}, 0, false, renderer }; + + RowWriteState state{ + .text = L"abc 𝒶𝒷𝒸 abc ネコちゃん", + }; + buffer.Write(0, TextAttribute{}, state); + VERIFY_IS_TRUE(state.text.empty()); + + static constexpr auto s = [](til::CoordType beg, til::CoordType end) -> til::point_span { + return { { beg, 0 }, { end, 0 } }; + }; + + auto expected = std::vector{ s(0, 2), s(8, 10) }; + auto actual = buffer.SearchText(L"abc", false); + VERIFY_ARE_EQUAL(expected, actual); + + expected = std::vector{ s(5, 5) }; + actual = buffer.SearchText(L"𝒷", false); + VERIFY_ARE_EQUAL(expected, actual); + + expected = std::vector{ s(12, 15) }; + actual = buffer.SearchText(L"ネコ", false); + VERIFY_ARE_EQUAL(expected, actual); + } +}; diff --git a/src/buffer/out/ut_textbuffer/sources b/src/buffer/out/ut_textbuffer/sources index 570467fce63..842246aaee6 100644 --- a/src/buffer/out/ut_textbuffer/sources +++ b/src/buffer/out/ut_textbuffer/sources @@ -17,6 +17,7 @@ SOURCES = \ ReflowTests.cpp \ TextColorTests.cpp \ TextAttributeTests.cpp \ + UTextAdapterTests.cpp \ DefaultResource.rc \ TARGETLIBS = \ diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 332b6fd8e0b..4da8d5c6ddb 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -276,6 +276,18 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" { til::point start; til::point end; + + constexpr bool operator==(const point_span& rhs) const noexcept + { + // `__builtin_memcmp` isn't an official standard, but it's the + // only way at the time of writing to get a constexpr `memcmp`. + return __builtin_memcmp(this, &rhs, sizeof(rhs)) == 0; + } + + constexpr bool operator!=(const point_span& rhs) const noexcept + { + return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0; + } }; } From ec5d246b35fbb0feb1bc2cbbff1b4b11362a2824 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Fri, 1 Mar 2024 03:43:57 -0800 Subject: [PATCH 143/603] Fix being unable to delete a changed Font Axis or Font Feature (#16790) Make sure the delete button's `Tag` updates when the selected axis/feature changes, so that the correct key value gets propagated when the delete button is clicked. Refs #16678 #16104 ## Validation Steps Performed 1. Add a new feature/axis 2. Change the key 3. Click the delete button 4. Delete button works --- src/cascadia/TerminalSettingsEditor/Appearances.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index 47c01df1fc9..5f73b299c11 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -329,7 +329,7 @@ HorizontalAlignment="Right" Click="DeleteAxisKeyValuePair_Click" Style="{StaticResource DeleteButtonStyle}" - Tag="{x:Bind AxisKey}"> + Tag="{x:Bind AxisKey, Mode=OneWay}"> @@ -390,7 +390,7 @@ HorizontalAlignment="Right" Click="DeleteFeatureKeyValuePair_Click" Style="{StaticResource DeleteButtonStyle}" - Tag="{x:Bind FeatureKey}"> + Tag="{x:Bind FeatureKey, Mode=OneWay}"> From ad51b22f440a57ff8381499c84e19b9f1d174cab Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 1 Mar 2024 11:56:49 -0600 Subject: [PATCH 144/603] Introduce Microsoft.Terminal.UI and consolidate UI helpers (#15107) This pull request introduces the module Microsoft.Terminal.UI.dll, and moves into it the following things: - Any `IDirectKeyListener` - All XAML converter helpers from around the project - ... including `IconPathConverter` from TerminalSettingsModel - ... but not `EmptyStringVisibilityConverter`, which has died It also adds a XAML Markup Extension named `mtu:ResourceString`, which will allow us to refer to string resources directly from XAML. It will allow us to remove all of the places in the code where we manually set resources on XAML controls. --------- Co-authored-by: Leonard Hecker --- .github/actions/spelling/allow/microsoft.txt | 1 + OpenConsole.sln | 26 +++++++ src/cascadia/TerminalApp/CommandPalette.idl | 3 +- src/cascadia/TerminalApp/CommandPalette.xaml | 24 ++---- .../EmptyStringVisibilityConverter.cpp | 38 --------- .../EmptyStringVisibilityConverter.h | 26 ------- .../EmptyStringVisibilityConverter.idl | 19 ----- .../TerminalApp/IDirectKeyListener.idl | 14 ---- src/cascadia/TerminalApp/PaletteItem.cpp | 2 +- src/cascadia/TerminalApp/SettingsTab.cpp | 2 +- .../TerminalApp/SuggestionsControl.idl | 3 +- .../TerminalApp/SuggestionsControl.xaml | 6 +- .../TerminalApp/TerminalAppLib.vcxproj | 11 +-- .../TerminalAppLib.vcxproj.filters | 4 +- src/cascadia/TerminalApp/TerminalPage.cpp | 4 +- src/cascadia/TerminalApp/TerminalPage.idl | 3 +- src/cascadia/TerminalApp/TerminalTab.cpp | 4 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 4 +- src/cascadia/TerminalApp/TerminalWindow.idl | 3 +- .../TerminalApp/dll/TerminalApp.vcxproj | 3 + src/cascadia/TerminalApp/pch.h | 1 + .../TerminalControl/ControlInteractivity.cpp | 1 - .../TerminalControl/IDirectKeyListener.idl | 14 ---- .../ScrollBarVisualStateManager.cpp | 2 + .../ScrollBarVisualStateManager.h | 4 +- src/cascadia/TerminalControl/TermControl.idl | 8 +- .../TermControlAutomationPeer.h | 3 +- .../TerminalControlLib.vcxproj | 5 +- .../dll/TerminalControl.vcxproj | 5 ++ src/cascadia/TerminalControl/pch.h | 2 + .../TerminalSettingsEditor/Actions.xaml | 9 +-- .../TerminalSettingsEditor/AddProfile.xaml | 4 +- .../TerminalSettingsEditor/Appearances.cpp | 4 +- .../TerminalSettingsEditor/Appearances.xaml | 21 ++--- .../TerminalSettingsEditor/ColorSchemes.xaml | 7 +- .../TerminalSettingsEditor/Converters.h | 30 ------- .../EditColorScheme.xaml | 9 ++- .../GlobalAppearance.xaml | 3 +- .../TerminalSettingsEditor/Launch.xaml | 14 ++-- .../TerminalSettingsEditor/MainPage.cpp | 4 +- .../TerminalSettingsEditor/MainPage.xaml | 1 + ...Microsoft.Terminal.Settings.Editor.vcxproj | 12 +-- ...t.Terminal.Settings.Editor.vcxproj.filters | 5 +- .../TerminalSettingsEditor/ProfileViewModel.h | 2 +- .../Profiles_Appearance.xaml | 9 ++- .../TerminalSettingsEditor/Profiles_Base.xaml | 13 ++-- src/cascadia/TerminalSettingsEditor/pch.h | 1 + .../TerminalSettingsModel/IconPathConverter.h | 29 ------- .../IconPathConverter.idl | 22 ------ ...crosoft.Terminal.Settings.ModelLib.vcxproj | 9 +-- ...Terminal.Settings.ModelLib.vcxproj.filters | 3 - .../Microsoft.Terminal.Settings.Model.vcxproj | 3 +- src/cascadia/TerminalSettingsModel/pch.h | 3 - .../Converters.cpp | 22 +++--- src/cascadia/UIHelpers/Converters.h | 33 ++++++++ .../Converters.idl | 22 ++++-- src/cascadia/UIHelpers/IDirectKeyListener.idl | 10 +++ .../IconPathConverter.cpp | 68 +++++----------- src/cascadia/UIHelpers/IconPathConverter.h | 20 +++++ src/cascadia/UIHelpers/IconPathConverter.idl | 13 ++++ .../UIHelpers/Microsoft.Terminal.UI.def | 3 + src/cascadia/UIHelpers/ResourceString.cpp | 17 ++++ src/cascadia/UIHelpers/ResourceString.h | 43 ++++++++++ src/cascadia/UIHelpers/ResourceString.idl | 9 +++ src/cascadia/UIHelpers/UIHelpers.vcxproj | 78 +++++++++++++++++++ .../UIHelpers/UIHelpers.vcxproj.filters | 38 +++++++++ src/cascadia/UIHelpers/init.cpp | 21 +++++ src/cascadia/UIHelpers/pch.cpp | 4 + src/cascadia/UIHelpers/pch.h | 46 +++++++++++ src/cascadia/UnitTests_SettingsModel/pch.h | 1 - .../WindowsTerminal/WindowsTerminal.vcxproj | 1 + src/cascadia/WindowsTerminal/pch.h | 1 + 72 files changed, 524 insertions(+), 388 deletions(-) delete mode 100644 src/cascadia/TerminalApp/EmptyStringVisibilityConverter.cpp delete mode 100644 src/cascadia/TerminalApp/EmptyStringVisibilityConverter.h delete mode 100644 src/cascadia/TerminalApp/EmptyStringVisibilityConverter.idl delete mode 100644 src/cascadia/TerminalApp/IDirectKeyListener.idl delete mode 100644 src/cascadia/TerminalControl/IDirectKeyListener.idl delete mode 100644 src/cascadia/TerminalSettingsEditor/Converters.h delete mode 100644 src/cascadia/TerminalSettingsModel/IconPathConverter.h delete mode 100644 src/cascadia/TerminalSettingsModel/IconPathConverter.idl rename src/cascadia/{TerminalSettingsEditor => UIHelpers}/Converters.cpp (79%) create mode 100644 src/cascadia/UIHelpers/Converters.h rename src/cascadia/{TerminalSettingsEditor => UIHelpers}/Converters.idl (85%) create mode 100644 src/cascadia/UIHelpers/IDirectKeyListener.idl rename src/cascadia/{TerminalSettingsModel => UIHelpers}/IconPathConverter.cpp (78%) create mode 100644 src/cascadia/UIHelpers/IconPathConverter.h create mode 100644 src/cascadia/UIHelpers/IconPathConverter.idl create mode 100644 src/cascadia/UIHelpers/Microsoft.Terminal.UI.def create mode 100644 src/cascadia/UIHelpers/ResourceString.cpp create mode 100644 src/cascadia/UIHelpers/ResourceString.h create mode 100644 src/cascadia/UIHelpers/ResourceString.idl create mode 100644 src/cascadia/UIHelpers/UIHelpers.vcxproj create mode 100644 src/cascadia/UIHelpers/UIHelpers.vcxproj.filters create mode 100644 src/cascadia/UIHelpers/init.cpp create mode 100644 src/cascadia/UIHelpers/pch.cpp create mode 100644 src/cascadia/UIHelpers/pch.h diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index d14b0a75e3a..5e7aa5c06ee 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -46,6 +46,7 @@ MSAA msixbundle MSVC MSVCP +mtu muxc netcore Onefuzz diff --git a/OpenConsole.sln b/OpenConsole.sln index 1a05e24b750..809dd25a346 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -412,6 +412,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TerminalStress", "src\tools EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderingTests", "src\tools\RenderingTests\RenderingTests.vcxproj", "{37C995E0-2349-4154-8E77-4A52C0C7F46D}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UIHelpers", "src\cascadia\UIHelpers\UIHelpers.vcxproj", "{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "benchcat", "src\tools\benchcat\benchcat.vcxproj", "{2C836962-9543-4CE5-B834-D28E1F124B66}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConsoleMonitor", "src\tools\ConsoleMonitor\ConsoleMonitor.vcxproj", "{328729E9-6723-416E-9C98-951F1473BBE1}" @@ -2356,6 +2358,29 @@ Global {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|ARM64.ActiveCfg = Release|ARM64 {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x64.ActiveCfg = Release|x64 {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x86.ActiveCfg = Release|Win32 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|x64.Build.0 = AuditMode|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|Any CPU.ActiveCfg = Debug|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.Build.0 = Debug|ARM64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.ActiveCfg = Debug|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.Build.0 = Debug|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x86.ActiveCfg = Debug|Win32 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x86.Build.0 = Debug|Win32 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|Any CPU.ActiveCfg = Release|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.ActiveCfg = Release|ARM64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.Build.0 = Release|ARM64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.ActiveCfg = Release|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.Build.0 = Release|x64 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.ActiveCfg = Release|Win32 + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.Build.0 = Release|Win32 {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|ARM64.ActiveCfg = Release|ARM64 {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|x64.ActiveCfg = Release|x64 @@ -2511,6 +2536,7 @@ Global {3C67784E-1453-49C2-9660-483E2CC7F7AD} = {40BD8415-DD93-4200-8D82-498DDDC08CC8} {613CCB57-5FA9-48EF-80D0-6B1E319E20C4} = {A10C4720-DCA4-4640-9749-67F4314F527C} {37C995E0-2349-4154-8E77-4A52C0C7F46D} = {A10C4720-DCA4-4640-9749-67F4314F527C} + {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} {2C836962-9543-4CE5-B834-D28E1F124B66} = {A10C4720-DCA4-4640-9749-67F4314F527C} {328729E9-6723-416E-9C98-951F1473BBE1} = {A10C4720-DCA4-4640-9749-67F4314F527C} {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48} = {A10C4720-DCA4-4640-9749-67F4314F527C} diff --git a/src/cascadia/TerminalApp/CommandPalette.idl b/src/cascadia/TerminalApp/CommandPalette.idl index 4bb72f46dda..e73d935a883 100644 --- a/src/cascadia/TerminalApp/CommandPalette.idl +++ b/src/cascadia/TerminalApp/CommandPalette.idl @@ -2,13 +2,12 @@ // Licensed under the MIT license. import "TabBase.idl"; -import "IDirectKeyListener.idl"; import "HighlightedTextControl.idl"; import "FilteredCommand.idl"; namespace TerminalApp { - [default_interface] runtimeclass CommandPalette : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener + [default_interface] runtimeclass CommandPalette : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged, Microsoft.Terminal.UI.IDirectKeyListener { CommandPalette(); diff --git a/src/cascadia/TerminalApp/CommandPalette.xaml b/src/cascadia/TerminalApp/CommandPalette.xaml index c8100e3568c..2e143a5f12c 100644 --- a/src/cascadia/TerminalApp/CommandPalette.xaml +++ b/src/cascadia/TerminalApp/CommandPalette.xaml @@ -9,7 +9,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:TerminalApp" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:model="using:Microsoft.Terminal.Settings.Model" + xmlns:mtu="using:Microsoft.Terminal.UI" xmlns:mux="using:Microsoft.UI.Xaml.Controls" AllowFocusOnInteraction="True" AutomationProperties.Name="{x:Bind ControlName, Mode=OneWay}" @@ -23,12 +23,6 @@ - - - - - - + Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(Item.KeyChordText), Mode=OneWay}"> + Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(Item.KeyChordText), Mode=OneWay}"> + Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(PrefixCharacter), Mode=OneWay}" /> + Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(ParentCommandName), Mode=OneWay}"> + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b9c81abe67a..5ed6cf6c8ac 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -407,7 +407,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // get at its private implementation _searchBox.copy_from(winrt::get_self(searchBox)); - _searchBox->Visibility(Visibility::Visible); // If a text is selected inside terminal, use it to populate the search box. // If the search box already contains a value, it will be overridden. @@ -423,7 +422,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - _searchBox->SetFocusOnTextbox(); + _searchBox->Open([searchBox]() { searchBox.SetFocusOnTextbox(); }); } } } @@ -504,7 +503,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const RoutedEventArgs& /*args*/) { _core.ClearSearch(); - _searchBox->Visibility(Visibility::Collapsed); + _searchBox->Close(); // Set focus back to terminal control this->Focus(FocusState::Programmatic); diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 57bc4fa9308..043a95d3b2e 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1302,8 +1302,7 @@ x:Load="False" Closed="_CloseSearchBoxControl" Search="_Search" - SearchChanged="_SearchChanged" - Visibility="Collapsed" /> + SearchChanged="_SearchChanged" /> #include #include +#include #include #include #include From ba34abb7b3cdc00c943ea4d033e7310e11087bd2 Mon Sep 17 00:00:00 2001 From: Jvr <109031036+Jvr2022@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:48:06 +0100 Subject: [PATCH 163/603] Update to CLI11 version 2.4.1 (#16669) ## Summary of the Pull Request update to the latest cli11 version ## References and Relevant Issues none ## Detailed Description of the Pull Request / Additional comments none ## Validation Steps Performed ## PR Checklist - [ ] Closes #xxx - [ ] Tests added/passed - [ ] Documentation updated - If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx - [ ] Schema updated (if necessary) --------- Co-authored-by: Leonard Hecker --- dep/CLI11/CLI11.hpp | 18649 +++++++++++++++++++----------------- dep/CLI11/README.md | 4 +- dep/CLI11/cgmanifest.json | 2 +- 3 files changed, 9954 insertions(+), 8701 deletions(-) diff --git a/dep/CLI11/CLI11.hpp b/dep/CLI11/CLI11.hpp index 9e502bf960e..d06f17604b2 100644 --- a/dep/CLI11/CLI11.hpp +++ b/dep/CLI11/CLI11.hpp @@ -1,15 +1,11 @@ -#pragma once - -// CLI11: Version 1.9.1 +// CLI11: Version 2.4.1 // Originally designed by Henry Schreiner // https://github.com/CLIUtils/CLI11 // // This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts -// from: v1.9.1 +// from: v2.4.1 // -// From LICENSE: -// -// CLI11 1.8 Copyright (c) 2017-2019 University of Cincinnati, developed by Henry +// CLI11 2.4.1 Copyright (c) 2017-2024 University of Cincinnati, developed by Henry // Schreiner under NSF AWARD 1414736. All rights reserved. // // Redistribution and use in source and binary forms of CLI11, with or without @@ -35,11 +31,18 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Standard combined includes: +#pragma once +// Standard combined includes: #include +#include +#include +#include #include #include +#include +#include +#include #include #include #include @@ -60,16 +63,16 @@ #include #include -// Verbatim copy from Version.hpp: -#define CLI11_VERSION_MAJOR 1 -#define CLI11_VERSION_MINOR 9 +#define CLI11_VERSION_MAJOR 2 +#define CLI11_VERSION_MINOR 4 #define CLI11_VERSION_PATCH 1 -#define CLI11_VERSION "1.9.1" +#define CLI11_VERSION "2.4.1" + + -// Verbatim copy from Macros.hpp: -// The following version macro is very similar to the one in PyBind11 +// The following version macro is very similar to the one in pybind11 #if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) #if __cplusplus >= 201402L #define CLI11_CPP14 @@ -87,7 +90,7 @@ #define CLI11_CPP14 #if _MSVC_LANG > 201402L && _MSC_VER >= 1910 #define CLI11_CPP17 -#if __MSVC_LANG > 201703L && _MSC_VER >= 1910 +#if _MSVC_LANG > 201703L && _MSC_VER >= 1910 #define CLI11_CPP20 #endif #endif @@ -102,15 +105,40 @@ #define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) #endif -// Verbatim copy from Validators.hpp: +// GCC < 10 doesn't ignore this in unevaluated contexts +#if !defined(CLI11_CPP17) || \ + (defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 10 && __GNUC__ > 4) +#define CLI11_NODISCARD +#else +#define CLI11_NODISCARD [[nodiscard]] +#endif + +/** detection of rtti */ +#ifndef CLI11_USE_STATIC_RTTI +#if(defined(_HAS_STATIC_RTTI) && _HAS_STATIC_RTTI) +#define CLI11_USE_STATIC_RTTI 1 +#elif defined(__cpp_rtti) +#if(defined(_CPPRTTI) && _CPPRTTI == 0) +#define CLI11_USE_STATIC_RTTI 1 +#else +#define CLI11_USE_STATIC_RTTI 0 +#endif +#elif(defined(__GCC_RTTI) && __GXX_RTTI) +#define CLI11_USE_STATIC_RTTI 0 +#else +#define CLI11_USE_STATIC_RTTI 1 +#endif +#endif -// C standard library -// Only needed for existence checking +/** availability */ #if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM #if __has_include() // Filesystem cannot be used if targeting macOS < 10.15 #if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 #define CLI11_HAS_FILESYSTEM 0 +#elif defined(__wasi__) +// As of wasi-sdk-14, filesystem is not implemented +#define CLI11_HAS_FILESYSTEM 0 #else #include #if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703 @@ -129,9585 +157,10810 @@ #endif #endif -#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 -#include // NOLINT(build/include) +/** availability */ +#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 5 +#define CLI11_HAS_CODECVT 0 #else -#include -#include +#define CLI11_HAS_CODECVT 1 +#include #endif -// From Version.hpp: - -// From Macros.hpp: - -// From StringTools.hpp: - -namespace CLI -{ - /// Include the items in this namespace to get free conversion of enums to/from streams. - /// (This is available inside CLI as well, so CLI11 will use this without a using statement). - namespace enums - { - /// output streaming for enumerations - template::value>::type> - std::ostream& operator<<(std::ostream& in, const T& item) - { - // make sure this is out of the detail namespace otherwise it won't be found when needed - return in << static_cast::type>(item); - } - - } // namespace enums - - /// Export to CLI namespace - using enums::operator<<; - - namespace detail - { - /// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not - /// produce overflow for some expected uses - constexpr int expected_max_vector_size{ 1 << 29 }; - // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c - /// Split a string by a delim - inline std::vector split(const std::string& s, char delim) - { - std::vector elems; - // Check to see if empty string, give consistent result - if (s.empty()) - elems.emplace_back(); - else - { - std::stringstream ss; - ss.str(s); - std::string item; - while (std::getline(ss, item, delim)) - { - elems.push_back(item); - } - } - return elems; - } - - /// Simple function to join a string - template - std::string join(const T& v, std::string delim = ",") - { - std::ostringstream s; - auto beg = std::begin(v); - auto end = std::end(v); - if (beg != end) - s << *beg++; - while (beg != end) - { - s << delim << *beg++; - } - return s.str(); - } - - /// Simple function to join a string from processed elements - template::value>::type> - std::string join(const T& v, Callable func, std::string delim = ",") - { - std::ostringstream s; - auto beg = std::begin(v); - auto end = std::end(v); - if (beg != end) - s << func(*beg++); - while (beg != end) - { - s << delim << func(*beg++); - } - return s.str(); - } - - /// Join a string in reverse order - template - std::string rjoin(const T& v, std::string delim = ",") - { - std::ostringstream s; - for (std::size_t start = 0; start < v.size(); start++) - { - if (start > 0) - s << delim; - s << v[v.size() - start - 1]; - } - return s.str(); - } - - // Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string - - /// Trim whitespace from left of string - inline std::string& ltrim(std::string& str) - { - auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(str.begin(), it); - return str; - } - - /// Trim anything from left of string - inline std::string& ltrim(std::string& str, const std::string& filter) - { - auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(str.begin(), it); - return str; - } - - /// Trim whitespace from right of string - inline std::string& rtrim(std::string& str) - { - auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(it.base(), str.end()); - return str; - } - - /// Trim anything from right of string - inline std::string& rtrim(std::string& str, const std::string& filter) - { - auto it = - std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(it.base(), str.end()); - return str; - } - - /// Trim whitespace from string - inline std::string& trim(std::string& str) { return ltrim(rtrim(str)); } - - /// Trim anything from string - inline std::string& trim(std::string& str, const std::string filter) { return ltrim(rtrim(str, filter), filter); } - - /// Make a copy of the string and then trim it - inline std::string trim_copy(const std::string& str) - { - std::string s = str; - return trim(s); - } - - /// remove quotes at the front and back of a string either '"' or '\'' - inline std::string& remove_quotes(std::string& str) - { - if (str.length() > 1 && (str.front() == '"' || str.front() == '\'')) - { - if (str.front() == str.back()) - { - str.pop_back(); - str.erase(str.begin(), str.begin() + 1); - } - } - return str; - } - - /// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered) - inline std::string trim_copy(const std::string& str, const std::string& filter) - { - std::string s = str; - return trim(s, filter); - } - /// Print a two part "help" string - inline std::ostream& format_help(std::ostream& out, std::string name, std::string description, std::size_t wid) - { - name = " " + name; - out << std::setw(static_cast(wid)) << std::left << name; - if (!description.empty()) - { - if (name.length() >= wid) - out << "\n" - << std::setw(static_cast(wid)) << ""; - for (const char c : description) - { - out.put(c); - if (c == '\n') - { - out << std::setw(static_cast(wid)) << ""; - } - } - } - out << "\n"; - return out; - } +/** disable deprecations */ +#if defined(__GNUC__) // GCC or clang +#define CLI11_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") +#define CLI11_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") - /// Verify the first character of an option - template - bool valid_first_char(T c) - { - return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@'; - } +#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") - /// Verify following characters of an option - template - bool valid_later_char(T c) - { - return valid_first_char(c) || c == '.' || c == '-'; - } +#elif defined(_MSC_VER) +#define CLI11_DIAGNOSTIC_PUSH __pragma(warning(push)) +#define CLI11_DIAGNOSTIC_POP __pragma(warning(pop)) - /// Verify an option name - inline bool valid_name_string(const std::string& str) - { - if (str.empty() || !valid_first_char(str[0])) - return false; - for (auto c : str.substr(1)) - if (!valid_later_char(c)) - return false; - return true; - } +#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED __pragma(warning(disable : 4996)) - /// Verify that str consists of letters only - inline bool isalpha(const std::string& str) - { - return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); }); - } +#else +#define CLI11_DIAGNOSTIC_PUSH +#define CLI11_DIAGNOSTIC_POP - /// Return a lower case version of a string - inline std::string to_lower(std::string str) - { - std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type& x) { - return std::tolower(x, std::locale()); - }); - return str; - } +#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED - /// remove underscores from a string - inline std::string remove_underscore(std::string str) - { - str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str)); - return str; - } +#endif - /// Find and replace a substring with another substring - inline std::string find_and_replace(std::string str, std::string from, std::string to) - { - std::size_t start_pos = 0; +/** Inline macro **/ +#ifdef CLI11_COMPILE +#define CLI11_INLINE +#else +#define CLI11_INLINE inline +#endif - while ((start_pos = str.find(from, start_pos)) != std::string::npos) - { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); - } - return str; - } - /// check if the flag definitions has possible false flags - inline bool has_default_flag_values(const std::string& flags) - { - return (flags.find_first_of("{!") != std::string::npos); - } +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +#include // NOLINT(build/include) +#else +#include +#include +#endif - inline void remove_default_flag_values(std::string& flags) - { - auto loc = flags.find_first_of('{'); - while (loc != std::string::npos) - { - auto finish = flags.find_first_of("},", loc + 1); - if ((finish != std::string::npos) && (flags[finish] == '}')) - { - flags.erase(flags.begin() + static_cast(loc), - flags.begin() + static_cast(finish) + 1); - } - loc = flags.find_first_of('{', loc + 1); - } - flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); - } - - /// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores - inline std::ptrdiff_t find_member(std::string name, - const std::vector names, - bool ignore_case = false, - bool ignore_underscore = false) - { - auto it = std::end(names); - if (ignore_case) - { - if (ignore_underscore) - { - name = detail::to_lower(detail::remove_underscore(name)); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(detail::remove_underscore(local_name)) == name; - }); - } - else - { - name = detail::to_lower(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(local_name) == name; - }); - } - } - else if (ignore_underscore) - { - name = detail::remove_underscore(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::remove_underscore(local_name) == name; - }); - } - else - it = std::find(std::begin(names), std::end(names), name); - - return (it != std::end(names)) ? (it - std::begin(names)) : (-1); - } - - /// Find a trigger string and call a modify callable function that takes the current string and starting position of the - /// trigger and returns the position in the string to search for the next trigger string - template - inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) - { - std::size_t start_pos = 0; - while ((start_pos = str.find(trigger, start_pos)) != std::string::npos) - { - start_pos = modify(str, start_pos); - } - return str; - } - - /// Split a string '"one two" "three"' into 'one two', 'three' - /// Quote characters can be ` ' or " - inline std::vector split_up(std::string str, char delimiter = '\0') - { - const std::string delims("\'\"`"); - auto find_ws = [delimiter](char ch) { - return (delimiter == '\0') ? (std::isspace(ch, std::locale()) != 0) : (ch == delimiter); - }; - trim(str); - - std::vector output; - bool embeddedQuote = false; - char keyChar = ' '; - while (!str.empty()) - { - if (delims.find_first_of(str[0]) != std::string::npos) - { - keyChar = str[0]; - auto end = str.find_first_of(keyChar, 1); - while ((end != std::string::npos) && (str[end - 1] == '\\')) - { // deal with escaped quotes - end = str.find_first_of(keyChar, end + 1); - embeddedQuote = true; - } - if (end != std::string::npos) - { - output.push_back(str.substr(1, end - 1)); - str = str.substr(end + 1); - } - else - { - output.push_back(str.substr(1)); - str = ""; - } - } - else - { - auto it = std::find_if(std::begin(str), std::end(str), find_ws); - if (it != std::end(str)) - { - std::string value = std::string(str.begin(), it); - output.push_back(value); - str = std::string(it + 1, str.end()); - } - else - { - output.push_back(str); - str = ""; - } - } - // transform any embedded quotes into the regular character - if (embeddedQuote) - { - output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar)); - embeddedQuote = false; - } - trim(str); - } - return output; - } - - /// Add a leader to the beginning of all new lines (nothing is added - /// at the start of the first line). `"; "` would be for ini files - /// - /// Can't use Regex, or this would be a subs. - inline std::string fix_newlines(const std::string& leader, std::string input) - { - std::string::size_type n = 0; - while (n != std::string::npos && n < input.size()) - { - n = input.find('\n', n); - if (n != std::string::npos) - { - input = input.substr(0, n + 1) + leader + input.substr(n + 1); - n += leader.size(); - } - } - return input; - } - - /// This function detects an equal or colon followed by an escaped quote after an argument - /// then modifies the string to replace the equality with a space. This is needed - /// to allow the split up function to work properly and is intended to be used with the find_and_modify function - /// the return value is the offset+1 which is required by the find_and_modify function. - inline std::size_t escape_detect(std::string& str, std::size_t offset) - { - auto next = str[offset + 1]; - if ((next == '\"') || (next == '\'') || (next == '`')) - { - auto astart = str.find_last_of("-/ \"\'`", offset - 1); - if (astart != std::string::npos) - { - if (str[astart] == ((str[offset] == '=') ? '-' : '/')) - str[offset] = ' '; // interpret this as a space so the split_up works properly - } - } - return offset + 1; - } - /// Add quotes if the string contains spaces - inline std::string& add_quotes_if_needed(std::string& str) - { - if ((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) - { - char quote = str.find('"') < str.find('\'') ? '\'' : '"'; - if (str.find(' ') != std::string::npos) - { - str.insert(0, 1, quote); - str.append(1, quote); - } - } - return str; - } - } // namespace detail -} // namespace CLI +#ifdef CLI11_CPP17 +#include +#endif // CLI11_CPP17 -// From Error.hpp: +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +#include +#include // NOLINT(build/include) +#endif // CLI11_HAS_FILESYSTEM + + + +#if defined(_WIN32) +#if !(defined(_AMD64_) || defined(_X86_) || defined(_ARM_)) +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || \ + defined(_M_AMD64) +#define _AMD64_ +#elif defined(i386) || defined(__i386) || defined(__i386__) || defined(__i386__) || defined(_M_IX86) +#define _X86_ +#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARMT) +#define _ARM_ +#elif defined(__aarch64__) || defined(_M_ARM64) +#define _ARM64_ +#elif defined(_M_ARM64EC) +#define _ARM64EC_ +#endif +#endif -namespace CLI -{ -// Use one of these on all error classes. -// These are temporary and are undef'd at the end of this file. -#define CLI11_ERROR_DEF(parent, name) \ -protected: \ - name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ - name(std::string ename, std::string msg, ExitCodes exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ - \ -public: \ - name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \ - name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {} +// first +#ifndef NOMINMAX +// if NOMINMAX is already defined we don't want to mess with that either way +#define NOMINMAX +#include +#undef NOMINMAX +#else +#include +#endif -// This is added after the one above if a class is used directly and builds its own message -#define CLI11_ERROR_SIMPLE(name) \ - explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} +// second +#include +// third +#include +#include +#endif - /// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, - /// int values from e.get_error_code(). - enum class ExitCodes - { - Success = 0, - IncorrectConstruction = 100, - BadNameString, - OptionAlreadyAdded, - FileError, - ConversionError, - ValidationError, - RequiredError, - RequiresError, - ExcludesError, - ExtrasError, - ConfigError, - InvalidError, - HorribleError, - OptionNotFound, - ArgumentMismatch, - BaseClass = 127 - }; - // Error definitions +namespace CLI { - /// @defgroup error_group Errors - /// @brief Errors thrown by CLI11 - /// - /// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors. - /// @{ - /// All errors derive from this one - class Error : public std::runtime_error - { - int actual_exit_code; - std::string error_name{ "Error" }; +/// Convert a wide string to a narrow string. +CLI11_INLINE std::string narrow(const std::wstring &str); +CLI11_INLINE std::string narrow(const wchar_t *str); +CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t size); - public: - int get_exit_code() const { return actual_exit_code; } +/// Convert a narrow string to a wide string. +CLI11_INLINE std::wstring widen(const std::string &str); +CLI11_INLINE std::wstring widen(const char *str); +CLI11_INLINE std::wstring widen(const char *str, std::size_t size); - std::string get_name() const { return error_name; } +#ifdef CLI11_CPP17 +CLI11_INLINE std::string narrow(std::wstring_view str); +CLI11_INLINE std::wstring widen(std::string_view str); +#endif // CLI11_CPP17 - Error(std::string name, std::string msg, int exit_code = static_cast(ExitCodes::BaseClass)) : - runtime_error(msg), - actual_exit_code(exit_code), - error_name(std::move(name)) {} +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +/// Convert a char-string to a native path correctly. +CLI11_INLINE std::filesystem::path to_path(std::string_view str); +#endif // CLI11_HAS_FILESYSTEM - Error(std::string name, std::string msg, ExitCodes exit_code) : - Error(name, msg, static_cast(exit_code)) {} - }; - // Note: Using Error::Error constructors does not work on GCC 4.7 - /// Construction errors (not in parsing) - class ConstructionError : public Error - { - CLI11_ERROR_DEF(Error, ConstructionError) - }; - /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) - class IncorrectConstruction : public ConstructionError - { - CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) - CLI11_ERROR_SIMPLE(IncorrectConstruction) - static IncorrectConstruction PositionalFlag(std::string name) - { - return IncorrectConstruction(name + ": Flags cannot be positional"); - } - static IncorrectConstruction Set0Opt(std::string name) - { - return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); - } - static IncorrectConstruction SetFlag(std::string name) - { - return IncorrectConstruction(name + ": Cannot set an expected number for flags"); - } - static IncorrectConstruction ChangeNotVector(std::string name) - { - return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); - } - static IncorrectConstruction AfterMultiOpt(std::string name) - { - return IncorrectConstruction( - name + ": You can't change expected arguments after you've changed the multi option policy!"); - } - static IncorrectConstruction MissingOption(std::string name) - { - return IncorrectConstruction("Option " + name + " is not defined"); - } - static IncorrectConstruction MultiOptionPolicy(std::string name) - { - return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options"); - } - }; +namespace detail { - /// Thrown on construction of a bad name - class BadNameString : public ConstructionError - { - CLI11_ERROR_DEF(ConstructionError, BadNameString) - CLI11_ERROR_SIMPLE(BadNameString) - static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } - static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } - static BadNameString DashesOnly(std::string name) - { - return BadNameString("Must have a name, not just dashes: " + name); - } - static BadNameString MultiPositionalNames(std::string name) - { - return BadNameString("Only one positional name allowed, remove: " + name); - } - }; +#if !CLI11_HAS_CODECVT +/// Attempt to set one of the acceptable unicode locales for conversion +CLI11_INLINE void set_unicode_locale() { + static const std::array unicode_locales{{"C.UTF-8", "en_US.UTF-8", ".UTF-8"}}; - /// Thrown when an option already exists - class OptionAlreadyAdded : public ConstructionError - { - CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) - explicit OptionAlreadyAdded(std::string name) : - OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} - static OptionAlreadyAdded Requires(std::string name, std::string other) - { - return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded); - } - static OptionAlreadyAdded Excludes(std::string name, std::string other) - { - return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded); + for(const auto &locale_name : unicode_locales) { + if(std::setlocale(LC_ALL, locale_name) != nullptr) { + return; } - }; - - // Parsing errors + } + throw std::runtime_error("CLI::narrow: could not set locale to C.UTF-8"); +} - /// Anything that can error in Parse - class ParseError : public Error - { - CLI11_ERROR_DEF(Error, ParseError) - }; +template struct scope_guard_t { + F closure; - // Not really "errors" + explicit scope_guard_t(F closure_) : closure(closure_) {} + ~scope_guard_t() { closure(); } +}; - /// This is a successful completion on parsing, supposed to exit - class Success : public ParseError - { - CLI11_ERROR_DEF(ParseError, Success) - Success() : - Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} - }; +template CLI11_NODISCARD CLI11_INLINE scope_guard_t scope_guard(F &&closure) { + return scope_guard_t{std::forward(closure)}; +} - /// -h or --help on command line - class CallForHelp : public ParseError - { - CLI11_ERROR_DEF(ParseError, CallForHelp) - CallForHelp() : - CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} - }; +#endif // !CLI11_HAS_CODECVT - /// Usually something like --help-all on command line - class CallForAllHelp : public ParseError - { - CLI11_ERROR_DEF(ParseError, CallForAllHelp) - CallForAllHelp() : - CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} - }; +CLI11_DIAGNOSTIC_PUSH +CLI11_DIAGNOSTIC_IGNORE_DEPRECATED - /// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code. - class RuntimeError : public ParseError - { - CLI11_ERROR_DEF(ParseError, RuntimeError) - explicit RuntimeError(int exit_code = 1) : - RuntimeError("Runtime error", exit_code) {} - }; +CLI11_INLINE std::string narrow_impl(const wchar_t *str, std::size_t str_size) { +#if CLI11_HAS_CODECVT +#ifdef _WIN32 + return std::wstring_convert>().to_bytes(str, str + str_size); - /// Thrown when parsing an INI file and it is missing - class FileError : public ParseError - { - CLI11_ERROR_DEF(ParseError, FileError) - CLI11_ERROR_SIMPLE(FileError) - static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } - }; +#else + return std::wstring_convert>().to_bytes(str, str + str_size); + +#endif // _WIN32 +#else // CLI11_HAS_CODECVT + (void)str_size; + std::mbstate_t state = std::mbstate_t(); + const wchar_t *it = str; + + std::string old_locale = std::setlocale(LC_ALL, nullptr); + auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); }); + set_unicode_locale(); + + std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state); + if(new_size == static_cast(-1)) { + throw std::runtime_error("CLI::narrow: conversion error in std::wcsrtombs at offset " + + std::to_string(it - str)); + } + std::string result(new_size, '\0'); + std::wcsrtombs(const_cast(result.data()), &str, new_size, &state); - /// Thrown when conversion call back fails, such as when an int fails to coerce to a string - class ConversionError : public ParseError - { - CLI11_ERROR_DEF(ParseError, ConversionError) - CLI11_ERROR_SIMPLE(ConversionError) - ConversionError(std::string member, std::string name) : - ConversionError("The value " + member + " is not an allowed value for " + name) {} - ConversionError(std::string name, std::vector results) : - ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} - static ConversionError TooManyInputsFlag(std::string name) - { - return ConversionError(name + ": too many inputs for a flag"); - } - static ConversionError TrueFalse(std::string name) - { - return ConversionError(name + ": Should be true/false or a number"); - } - }; + return result; - /// Thrown when validation of results fails - class ValidationError : public ParseError - { - CLI11_ERROR_DEF(ParseError, ValidationError) - CLI11_ERROR_SIMPLE(ValidationError) - explicit ValidationError(std::string name, std::string msg) : - ValidationError(name + ": " + msg) {} - }; +#endif // CLI11_HAS_CODECVT +} - /// Thrown when a required option is missing - class RequiredError : public ParseError - { - CLI11_ERROR_DEF(ParseError, RequiredError) - explicit RequiredError(std::string name) : - RequiredError(name + " is required", ExitCodes::RequiredError) {} - static RequiredError Subcommand(std::size_t min_subcom) - { - if (min_subcom == 1) - { - return RequiredError("A subcommand"); - } - return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", - ExitCodes::RequiredError); - } - static RequiredError - Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string& option_list) - { - if ((min_option == 1) && (max_option == 1) && (used == 0)) - return RequiredError("Exactly 1 option from [" + option_list + "]"); - if ((min_option == 1) && (max_option == 1) && (used > 1)) - { - return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) + - " were given", - ExitCodes::RequiredError); - } - if ((min_option == 1) && (used == 0)) - return RequiredError("At least 1 option from [" + option_list + "]"); - if (used < min_option) - { - return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " + - std::to_string(used) + "were given from [" + option_list + "]", - ExitCodes::RequiredError); - } - if (max_option == 1) - return RequiredError("Requires at most 1 options be given from [" + option_list + "]", - ExitCodes::RequiredError); - - return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " + - std::to_string(used) + "were given from [" + option_list + "]", - ExitCodes::RequiredError); - } - }; +CLI11_INLINE std::wstring widen_impl(const char *str, std::size_t str_size) { +#if CLI11_HAS_CODECVT +#ifdef _WIN32 + return std::wstring_convert>().from_bytes(str, str + str_size); - /// Thrown when the wrong number of arguments has been received - class ArgumentMismatch : public ParseError - { - CLI11_ERROR_DEF(ParseError, ArgumentMismatch) - CLI11_ERROR_SIMPLE(ArgumentMismatch) - ArgumentMismatch(std::string name, int expected, std::size_t received) : - ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + - ", got " + std::to_string(received)) : - ("Expected at least " + std::to_string(-expected) + " arguments to " + name + - ", got " + std::to_string(received)), - ExitCodes::ArgumentMismatch) {} - - static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) - { - return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " + - std::to_string(received)); - } - static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) - { - return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " + - std::to_string(received)); - } - static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) - { - return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); - } - static ArgumentMismatch FlagOverride(std::string name) - { - return ArgumentMismatch(name + " was given a disallowed flag override"); - } - }; +#else + return std::wstring_convert>().from_bytes(str, str + str_size); + +#endif // _WIN32 +#else // CLI11_HAS_CODECVT + (void)str_size; + std::mbstate_t state = std::mbstate_t(); + const char *it = str; + + std::string old_locale = std::setlocale(LC_ALL, nullptr); + auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); }); + set_unicode_locale(); + + std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state); + if(new_size == static_cast(-1)) { + throw std::runtime_error("CLI::widen: conversion error in std::mbsrtowcs at offset " + + std::to_string(it - str)); + } + std::wstring result(new_size, L'\0'); + std::mbsrtowcs(const_cast(result.data()), &str, new_size, &state); - /// Thrown when a requires option is missing - class RequiresError : public ParseError - { - CLI11_ERROR_DEF(ParseError, RequiresError) - RequiresError(std::string curname, std::string subname) : - RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} - }; + return result; - /// Thrown when an excludes option is present - class ExcludesError : public ParseError - { - CLI11_ERROR_DEF(ParseError, ExcludesError) - ExcludesError(std::string curname, std::string subname) : - ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} - }; +#endif // CLI11_HAS_CODECVT +} - /// Thrown when too many positionals or options are found - class ExtrasError : public ParseError - { - CLI11_ERROR_DEF(ParseError, ExtrasError) - explicit ExtrasError(std::vector args) : - ExtrasError((args.size() > 1 ? "The following arguments were not expected: " : "The following argument was not expected: ") + - detail::rjoin(args, " "), - ExitCodes::ExtrasError) {} - ExtrasError(const std::string& name, std::vector args) : - ExtrasError(name, - (args.size() > 1 ? "The following arguments were not expected: " : "The following argument was not expected: ") + - detail::rjoin(args, " "), - ExitCodes::ExtrasError) {} - }; +CLI11_DIAGNOSTIC_POP - /// Thrown when extra values are found in an INI file - class ConfigError : public ParseError - { - CLI11_ERROR_DEF(ParseError, ConfigError) - CLI11_ERROR_SIMPLE(ConfigError) - static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); } - static ConfigError NotConfigurable(std::string item) - { - return ConfigError(item + ": This option is not allowed in a configuration file"); - } - }; +} // namespace detail - /// Thrown when validation fails before parsing - class InvalidError : public ParseError - { - CLI11_ERROR_DEF(ParseError, InvalidError) - explicit InvalidError(std::string name) : - InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) - { - } - }; +CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { return detail::narrow_impl(str, str_size); } +CLI11_INLINE std::string narrow(const std::wstring &str) { return detail::narrow_impl(str.data(), str.size()); } +// Flawfinder: ignore +CLI11_INLINE std::string narrow(const wchar_t *str) { return detail::narrow_impl(str, std::wcslen(str)); } - /// This is just a safety check to verify selection and parsing match - you should not ever see it - /// Strings are directly added to this error, but again, it should never be seen. - class HorribleError : public ParseError - { - CLI11_ERROR_DEF(ParseError, HorribleError) - CLI11_ERROR_SIMPLE(HorribleError) - }; +CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { return detail::widen_impl(str, str_size); } +CLI11_INLINE std::wstring widen(const std::string &str) { return detail::widen_impl(str.data(), str.size()); } +// Flawfinder: ignore +CLI11_INLINE std::wstring widen(const char *str) { return detail::widen_impl(str, std::strlen(str)); } - // After parsing +#ifdef CLI11_CPP17 +CLI11_INLINE std::string narrow(std::wstring_view str) { return detail::narrow_impl(str.data(), str.size()); } +CLI11_INLINE std::wstring widen(std::string_view str) { return detail::widen_impl(str.data(), str.size()); } +#endif // CLI11_CPP17 - /// Thrown when counting a non-existent option - class OptionNotFound : public Error - { - CLI11_ERROR_DEF(Error, OptionNotFound) - explicit OptionNotFound(std::string name) : - OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +CLI11_INLINE std::filesystem::path to_path(std::string_view str) { + return std::filesystem::path{ +#ifdef _WIN32 + widen(str) +#else + str +#endif // _WIN32 }; +} +#endif // CLI11_HAS_FILESYSTEM -#undef CLI11_ERROR_DEF -#undef CLI11_ERROR_SIMPLE - - /// @} - -} // namespace CLI -// From TypeTools.hpp: -namespace CLI -{ - // Type tools - // Utilities for type enabling - namespace detail - { - // Based generally on https://rmf.io/cxx11/almost-static-if - /// Simple empty scoped class - enum class enabler - { - }; +namespace detail { +#ifdef _WIN32 +/// Decode and return UTF-8 argv from GetCommandLineW. +CLI11_INLINE std::vector compute_win32_argv(); +#endif +} // namespace detail - /// An instance to use in EnableIf - constexpr enabler dummy = {}; - } // namespace detail - /// A copy of enable_if_t from C++14, compatible with C++11. - /// - /// We could check to see if C++14 is being used, but it does not hurt to redefine this - /// (even Google does this: https://github.com/google/skia/blob/master/include/private/SkTLogic.h) - /// It is not in the std namespace anyway, so no harm done. - template - using enable_if_t = typename std::enable_if::type; - - /// A copy of std::void_t from C++17 (helper for C++11 and C++14) - template - struct make_void - { - using type = void; - }; - /// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine - template - using void_t = typename make_void::type; +namespace detail { - /// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine - template - using conditional_t = typename std::conditional::type; +#ifdef _WIN32 +CLI11_INLINE std::vector compute_win32_argv() { + std::vector result; + int argc = 0; - /// Check to see if something is a vector (fail check by default) - template - struct is_vector : std::false_type - { - }; + auto deleter = [](wchar_t **ptr) { LocalFree(ptr); }; + // NOLINTBEGIN(*-avoid-c-arrays) + auto wargv = std::unique_ptr(CommandLineToArgvW(GetCommandLineW(), &argc), deleter); + // NOLINTEND(*-avoid-c-arrays) - /// Check to see if something is a vector (true if actually a vector) - template - struct is_vector> : std::true_type - { - }; + if(wargv == nullptr) { + throw std::runtime_error("CommandLineToArgvW failed with code " + std::to_string(GetLastError())); + } - /// Check to see if something is a vector (true if actually a const vector) - template - struct is_vector> : std::true_type - { - }; + result.reserve(static_cast(argc)); + for(size_t i = 0; i < static_cast(argc); ++i) { + result.push_back(narrow(wargv[i])); + } - /// Check to see if something is bool (fail check by default) - template - struct is_bool : std::false_type - { - }; + return result; +} +#endif - /// Check to see if something is bool (true if actually a bool) - template<> - struct is_bool : std::true_type - { - }; +} // namespace detail - /// Check to see if something is a shared pointer - template - struct is_shared_ptr : std::false_type - { - }; - /// Check to see if something is a shared pointer (True if really a shared pointer) - template - struct is_shared_ptr> : std::true_type - { - }; - /// Check to see if something is a shared pointer (True if really a shared pointer) - template - struct is_shared_ptr> : std::true_type - { - }; - /// Check to see if something is copyable pointer - template - struct is_copyable_ptr - { - static bool const value = is_shared_ptr::value || std::is_pointer::value; - }; +/// Include the items in this namespace to get free conversion of enums to/from streams. +/// (This is available inside CLI as well, so CLI11 will use this without a using statement). +namespace enums { - /// This can be specialized to override the type deduction for IsMember. - template - struct IsMemberType - { - using type = T; - }; +/// output streaming for enumerations +template ::value>::type> +std::ostream &operator<<(std::ostream &in, const T &item) { + // make sure this is out of the detail namespace otherwise it won't be found when needed + return in << static_cast::type>(item); +} - /// The main custom type needed here is const char * should be a string. - template<> - struct IsMemberType - { - using type = std::string; - }; +} // namespace enums - namespace detail - { - // These are utilities for IsMember and other transforming objects +/// Export to CLI namespace +using enums::operator<<; - /// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that - /// pointer_traits be valid. +namespace detail { +/// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not +/// produce overflow for some expected uses +constexpr int expected_max_vector_size{1 << 29}; +// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c +/// Split a string by a delim +CLI11_INLINE std::vector split(const std::string &s, char delim); - /// not a pointer - template - struct element_type - { - using type = T; - }; +/// Simple function to join a string +template std::string join(const T &v, std::string delim = ",") { + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + if(beg != end) + s << *beg++; + while(beg != end) { + s << delim << *beg++; + } + return s.str(); +} + +/// Simple function to join a string from processed elements +template ::value>::type> +std::string join(const T &v, Callable func, std::string delim = ",") { + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + auto loc = s.tellp(); + while(beg != end) { + auto nloc = s.tellp(); + if(nloc > loc) { + s << delim; + loc = nloc; + } + s << func(*beg++); + } + return s.str(); +} + +/// Join a string in reverse order +template std::string rjoin(const T &v, std::string delim = ",") { + std::ostringstream s; + for(std::size_t start = 0; start < v.size(); start++) { + if(start > 0) + s << delim; + s << v[v.size() - start - 1]; + } + return s.str(); +} + +// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string + +/// Trim whitespace from left of string +CLI11_INLINE std::string <rim(std::string &str); + +/// Trim anything from left of string +CLI11_INLINE std::string <rim(std::string &str, const std::string &filter); + +/// Trim whitespace from right of string +CLI11_INLINE std::string &rtrim(std::string &str); + +/// Trim anything from right of string +CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter); + +/// Trim whitespace from string +inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); } + +/// Trim anything from string +inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); } + +/// Make a copy of the string and then trim it +inline std::string trim_copy(const std::string &str) { + std::string s = str; + return trim(s); +} + +/// remove quotes at the front and back of a string either '"' or '\'' +CLI11_INLINE std::string &remove_quotes(std::string &str); + +/// remove quotes from all elements of a string vector and process escaped components +CLI11_INLINE void remove_quotes(std::vector &args); + +/// Add a leader to the beginning of all new lines (nothing is added +/// at the start of the first line). `"; "` would be for ini files +/// +/// Can't use Regex, or this would be a subs. +CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input); + +/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered) +inline std::string trim_copy(const std::string &str, const std::string &filter) { + std::string s = str; + return trim(s, filter); +} +/// Print a two part "help" string +CLI11_INLINE std::ostream & +format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid); + +/// Print subcommand aliases +CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid); + +/// Verify the first character of an option +/// - is a trigger character, ! has special meaning and new lines would just be annoying to deal with +template bool valid_first_char(T c) { + return ((c != '-') && (static_cast(c) > 33)); // space and '!' not allowed +} + +/// Verify following characters of an option +template bool valid_later_char(T c) { + // = and : are value separators, { has special meaning for option defaults, + // and control codes other than tab would just be annoying to deal with in many places allowing space here has too + // much potential for inadvertent entry errors and bugs + return ((c != '=') && (c != ':') && (c != '{') && ((static_cast(c) > 32) || c == '\t')); +} + +/// Verify an option/subcommand name +CLI11_INLINE bool valid_name_string(const std::string &str); + +/// Verify an app name +inline bool valid_alias_name_string(const std::string &str) { + static const std::string badChars(std::string("\n") + '\0'); + return (str.find_first_of(badChars) == std::string::npos); +} + +/// check if a string is a container segment separator (empty or "%%") +inline bool is_separator(const std::string &str) { + static const std::string sep("%%"); + return (str.empty() || str == sep); +} + +/// Verify that str consists of letters only +inline bool isalpha(const std::string &str) { + return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); }); +} + +/// Return a lower case version of a string +inline std::string to_lower(std::string str) { + std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) { + return std::tolower(x, std::locale()); + }); + return str; +} + +/// remove underscores from a string +inline std::string remove_underscore(std::string str) { + str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str)); + return str; +} + +/// Find and replace a substring with another substring +CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to); + +/// check if the flag definitions has possible false flags +inline bool has_default_flag_values(const std::string &flags) { + return (flags.find_first_of("{!") != std::string::npos); +} + +CLI11_INLINE void remove_default_flag_values(std::string &flags); + +/// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores +CLI11_INLINE std::ptrdiff_t find_member(std::string name, + const std::vector names, + bool ignore_case = false, + bool ignore_underscore = false); + +/// Find a trigger string and call a modify callable function that takes the current string and starting position of the +/// trigger and returns the position in the string to search for the next trigger string +template inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) { + std::size_t start_pos = 0; + while((start_pos = str.find(trigger, start_pos)) != std::string::npos) { + start_pos = modify(str, start_pos); + } + return str; +} - template - struct element_type::value>::type> - { - using type = typename std::pointer_traits::element_type; - }; +/// close a sequence of characters indicated by a closure character. Brackets allows sub sequences +/// recognized bracket sequences include "'`[(<{ other closure characters are assumed to be literal strings +CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char); - /// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of - /// the container - template - struct element_value_type - { - using type = typename element_type::type::value_type; - }; +/// Split a string '"one two" "three"' into 'one two', 'three' +/// Quote characters can be ` ' or " or bracket characters [{(< with matching to the matching bracket +CLI11_INLINE std::vector split_up(std::string str, char delimiter = '\0'); - /// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing. - template - struct pair_adaptor : std::false_type - { - using value_type = typename T::value_type; - using first_type = typename std::remove_const::type; - using second_type = typename std::remove_const::type; - - /// Get the first value (really just the underlying value) - template - static auto first(Q&& pair_value) -> decltype(std::forward(pair_value)) - { - return std::forward(pair_value); - } - /// Get the second value (really just the underlying value) - template - static auto second(Q&& pair_value) -> decltype(std::forward(pair_value)) - { - return std::forward(pair_value); - } - }; +/// get the value of an environmental variable or empty string if empty +CLI11_INLINE std::string get_environment_value(const std::string &env_name); - /// Adaptor for map-like structure (true version, must have key_type and mapped_type). - /// This wraps a mapped container in a few utilities access it in a general way. - template - struct pair_adaptor< - T, - conditional_t, void>> : std::true_type - { - using value_type = typename T::value_type; - using first_type = typename std::remove_const::type; - using second_type = typename std::remove_const::type; - - /// Get the first value (really just the underlying value) - template - static auto first(Q&& pair_value) -> decltype(std::get<0>(std::forward(pair_value))) - { - return std::get<0>(std::forward(pair_value)); - } - /// Get the second value (really just the underlying value) - template - static auto second(Q&& pair_value) -> decltype(std::get<1>(std::forward(pair_value))) - { - return std::get<1>(std::forward(pair_value)); - } - }; +/// This function detects an equal or colon followed by an escaped quote after an argument +/// then modifies the string to replace the equality with a space. This is needed +/// to allow the split up function to work properly and is intended to be used with the find_and_modify function +/// the return value is the offset+1 which is required by the find_and_modify function. +CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset); -// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning -// in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in -// brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a -// little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out. -// But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be -// suppressed -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnarrowing" -#endif - // check for constructibility from a specific type and copy assignable used in the parse detection - template - class is_direct_constructible - { - template - static auto test(int, std::true_type) -> decltype( -// NVCC warns about narrowing conversions here -#ifdef __CUDACC__ -#pragma diag_suppress 2361 -#endif - TT { std::declval() } -#ifdef __CUDACC__ -#pragma diag_default 2361 -#endif - , - std::is_move_assignable()); +/// @brief detect if a string has escapable characters +/// @param str the string to do the detection on +/// @return true if the string has escapable characters +CLI11_INLINE bool has_escapable_character(const std::string &str); - template - static auto test(int, std::false_type) -> std::false_type; +/// @brief escape all escapable characters +/// @param str the string to escape +/// @return a string with the escapble characters escaped with '\' +CLI11_INLINE std::string add_escaped_characters(const std::string &str); - template - static auto test(...) -> std::false_type; +/// @brief replace the escaped characters with their equivalent +CLI11_INLINE std::string remove_escaped_characters(const std::string &str); - public: - static constexpr bool value = decltype(test(0, typename std::is_constructible::type()))::value; - }; -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif +/// generate a string with all non printable characters escaped to hex codes +CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape); - // Check for output streamability - // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream +CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string); - template - class is_ostreamable - { - template - static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); +/// extract an escaped binary_string +CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string); - template - static auto test(...) -> std::false_type; +/// process a quoted string, remove the quotes and if appropriate handle escaped characters +CLI11_INLINE bool process_quoted_string(std::string &str, char string_char = '\"', char literal_char = '\''); - public: - static constexpr bool value = decltype(test(0))::value; - }; +} // namespace detail - /// Check for input streamability - template - class is_istreamable - { - template - static auto test(int) -> decltype(std::declval() >> std::declval(), std::true_type()); - template - static auto test(...) -> std::false_type; - public: - static constexpr bool value = decltype(test(0))::value; - }; - /// Templated operation to get a value from a stream - template::value, detail::enabler> = detail::dummy> - bool from_stream(const std::string& istring, T& obj) - { - std::istringstream is; - is.str(istring); - is >> obj; - return !is.fail() && !is.rdbuf()->in_avail(); +namespace detail { +CLI11_INLINE std::vector split(const std::string &s, char delim) { + std::vector elems; + // Check to see if empty string, give consistent result + if(s.empty()) { + elems.emplace_back(); + } else { + std::stringstream ss; + ss.str(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(item); } - - template::value, detail::enabler> = detail::dummy> - bool from_stream(const std::string& /*istring*/, T& /*obj*/) - { - return false; + } + return elems; +} + +CLI11_INLINE std::string <rim(std::string &str) { + auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(str.begin(), it); + return str; +} + +CLI11_INLINE std::string <rim(std::string &str, const std::string &filter) { + auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(str.begin(), it); + return str; +} + +CLI11_INLINE std::string &rtrim(std::string &str) { + auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(it.base(), str.end()); + return str; +} + +CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) { + auto it = + std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(it.base(), str.end()); + return str; +} + +CLI11_INLINE std::string &remove_quotes(std::string &str) { + if(str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) { + if(str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); } + } + return str; +} - // Check for tuple like types, as in classes with a tuple_size type trait - template - class is_tuple_like - { - template - // static auto test(int) - // -> decltype(std::conditional<(std::tuple_size::value > 0), std::true_type, std::false_type>::type()); - static auto test(int) -> decltype(std::tuple_size::value, std::true_type{}); - template - static auto test(...) -> std::false_type; - - public: - static constexpr bool value = decltype(test(0))::value; - }; - - /// Convert an object to a string (directly forward if this can become a string) - template::value, detail::enabler> = detail::dummy> - auto to_string(T&& value) -> decltype(std::forward(value)) - { - return std::forward(value); - } - - /// Construct a string from the object - template::value && !std::is_convertible::value, - detail::enabler> = detail::dummy> - std::string to_string(const T& value) - { - return std::string(value); - } - - /// Convert an object to a string (streaming must be supported for that type) - template::value && !std::is_constructible::value && - is_ostreamable::value, - detail::enabler> = detail::dummy> - std::string to_string(T&& value) - { - std::stringstream stream; - stream << value; - return stream.str(); +CLI11_INLINE std::string &remove_outer(std::string &str, char key) { + if(str.length() > 1 && (str.front() == key)) { + if(str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); } + } + return str; +} - /// If conversion is not supported, return an empty string (streaming is not supported for that type) - template::value && !is_ostreamable::value && - !is_vector::type>::type>::value, - detail::enabler> = detail::dummy> - std::string to_string(T&&) - { - return std::string{}; +CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input) { + std::string::size_type n = 0; + while(n != std::string::npos && n < input.size()) { + n = input.find('\n', n); + if(n != std::string::npos) { + input = input.substr(0, n + 1) + leader + input.substr(n + 1); + n += leader.size(); } - - /// convert a vector to a string - template::value && !is_ostreamable::value && - is_vector::type>::type>::value, - detail::enabler> = detail::dummy> - std::string to_string(T&& variable) - { - std::vector defaults; - defaults.reserve(variable.size()); - auto cval = variable.begin(); - auto end = variable.end(); - while (cval != end) - { - defaults.emplace_back(CLI::detail::to_string(*cval)); - ++cval; - } - return std::string("[" + detail::join(defaults) + "]"); - } - - /// special template overload - template::value, detail::enabler> = detail::dummy> - auto checked_to_string(T&& value) -> decltype(to_string(std::forward(value))) - { - return to_string(std::forward(value)); - } - - /// special template overload - template::value, detail::enabler> = detail::dummy> - std::string checked_to_string(T&&) - { - return std::string{}; + } + return input; +} + +CLI11_INLINE std::ostream & +format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid) { + name = " " + name; + out << std::setw(static_cast(wid)) << std::left << name; + if(!description.empty()) { + if(name.length() >= wid) + out << "\n" << std::setw(static_cast(wid)) << ""; + for(const char c : description) { + out.put(c); + if(c == '\n') { + out << std::setw(static_cast(wid)) << ""; + } } - /// get a string as a convertible value for arithmetic types - template::value, detail::enabler> = detail::dummy> - std::string value_string(const T& value) - { - return std::to_string(value); - } - /// get a string as a convertible value for enumerations - template::value, detail::enabler> = detail::dummy> - std::string value_string(const T& value) - { - return std::to_string(static_cast::type>(value)); - } - /// for other types just use the regular to_string function - template::value && !std::is_arithmetic::value, detail::enabler> = detail::dummy> - auto value_string(const T& value) -> decltype(to_string(value)) - { - return to_string(value); - } - - /// This will only trigger for actual void type - template - struct type_count - { - static const int value{ 0 }; - }; - - /// Set of overloads to get the type size of an object - template - struct type_count::value>::type> - { - static constexpr int value{ std::tuple_size::value }; - }; - /// Type size for regular object types that do not look like a tuple - template - struct type_count< - T, - typename std::enable_if::value && !is_tuple_like::value && !std::is_void::value>::type> - { - static constexpr int value{ 1 }; - }; - - /// Type size of types that look like a vector - template - struct type_count::value>::type> - { - static constexpr int value{ is_vector::value ? expected_max_vector_size : type_count::value }; - }; - - /// This will only trigger for actual void type - template - struct expected_count - { - static const int value{ 0 }; - }; + } + out << "\n"; + return out; +} + +CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid) { + if(!aliases.empty()) { + out << std::setw(static_cast(wid)) << " aliases: "; + bool front = true; + for(const auto &alias : aliases) { + if(!front) { + out << ", "; + } else { + front = false; + } + out << detail::fix_newlines(" ", alias); + } + out << "\n"; + } + return out; +} - /// For most types the number of expected items is 1 - template - struct expected_count::value && !std::is_void::value>::type> - { - static constexpr int value{ 1 }; - }; - /// number of expected items in a vector - template - struct expected_count::value>::type> - { - static constexpr int value{ expected_max_vector_size }; - }; +CLI11_INLINE bool valid_name_string(const std::string &str) { + if(str.empty() || !valid_first_char(str[0])) { + return false; + } + auto e = str.end(); + for(auto c = str.begin() + 1; c != e; ++c) + if(!valid_later_char(*c)) + return false; + return true; +} - // Enumeration of the different supported categorizations of objects - enum class object_category : int - { - integral_value = 2, - unsigned_integral = 4, - enumeration = 6, - boolean_value = 8, - floating_point = 10, - number_constructible = 12, - double_constructible = 14, - integer_constructible = 16, - vector_value = 30, - tuple_value = 35, - // string assignable or greater used in a condition so anything string like must come last - string_assignable = 50, - string_constructible = 60, - other = 200, +CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) { - }; + std::size_t start_pos = 0; - /// some type that is not otherwise recognized - template - struct classify_object - { - static constexpr object_category value{ object_category::other }; - }; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } - /// Set of overloads to classify an object according to type - template - struct classify_object::value && std::is_signed::value && - !is_bool::value && !std::is_enum::value>::type> - { - static constexpr object_category value{ object_category::integral_value }; - }; + return str; +} - /// Unsigned integers - template - struct classify_object< - T, - typename std::enable_if::value && std::is_unsigned::value && !is_bool::value>::type> - { - static constexpr object_category value{ object_category::unsigned_integral }; - }; +CLI11_INLINE void remove_default_flag_values(std::string &flags) { + auto loc = flags.find_first_of('{', 2); + while(loc != std::string::npos) { + auto finish = flags.find_first_of("},", loc + 1); + if((finish != std::string::npos) && (flags[finish] == '}')) { + flags.erase(flags.begin() + static_cast(loc), + flags.begin() + static_cast(finish) + 1); + } + loc = flags.find_first_of('{', loc + 1); + } + flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); +} + +CLI11_INLINE std::ptrdiff_t +find_member(std::string name, const std::vector names, bool ignore_case, bool ignore_underscore) { + auto it = std::end(names); + if(ignore_case) { + if(ignore_underscore) { + name = detail::to_lower(detail::remove_underscore(name)); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(detail::remove_underscore(local_name)) == name; + }); + } else { + name = detail::to_lower(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(local_name) == name; + }); + } - /// Boolean values - template - struct classify_object::value>::type> - { - static constexpr object_category value{ object_category::boolean_value }; - }; + } else if(ignore_underscore) { + name = detail::remove_underscore(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::remove_underscore(local_name) == name; + }); + } else { + it = std::find(std::begin(names), std::end(names), name); + } - /// Floats - template - struct classify_object::value>::type> - { - static constexpr object_category value{ object_category::floating_point }; - }; + return (it != std::end(names)) ? (it - std::begin(names)) : (-1); +} + +static const std::string escapedChars("\b\t\n\f\r\"\\"); +static const std::string escapedCharsCode("btnfr\"\\"); +static const std::string bracketChars{"\"'`[(<{"}; +static const std::string matchBracketChars("\"'`])>}"); + +CLI11_INLINE bool has_escapable_character(const std::string &str) { + return (str.find_first_of(escapedChars) != std::string::npos); +} + +CLI11_INLINE std::string add_escaped_characters(const std::string &str) { + std::string out; + out.reserve(str.size() + 4); + for(char s : str) { + auto sloc = escapedChars.find_first_of(s); + if(sloc != std::string::npos) { + out.push_back('\\'); + out.push_back(escapedCharsCode[sloc]); + } else { + out.push_back(s); + } + } + return out; +} + +CLI11_INLINE std::uint32_t hexConvert(char hc) { + int hcode{0}; + if(hc >= '0' && hc <= '9') { + hcode = (hc - '0'); + } else if(hc >= 'A' && hc <= 'F') { + hcode = (hc - 'A' + 10); + } else if(hc >= 'a' && hc <= 'f') { + hcode = (hc - 'a' + 10); + } else { + hcode = -1; + } + return static_cast(hcode); +} + +CLI11_INLINE char make_char(std::uint32_t code) { return static_cast(static_cast(code)); } + +CLI11_INLINE void append_codepoint(std::string &str, std::uint32_t code) { + if(code < 0x80) { // ascii code equivalent + str.push_back(static_cast(code)); + } else if(code < 0x800) { // \u0080 to \u07FF + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + str.push_back(make_char(0xC0 | code >> 6)); + str.push_back(make_char(0x80 | (code & 0x3F))); + } else if(code < 0x10000) { // U+0800...U+FFFF + if(0xD800 <= code && code <= 0xDFFF) { + throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8."); + } + // 1110yyyy 10yxxxxx 10xxxxxx + str.push_back(make_char(0xE0 | code >> 12)); + str.push_back(make_char(0x80 | (code >> 6 & 0x3F))); + str.push_back(make_char(0x80 | (code & 0x3F))); + } else if(code < 0x110000) { // U+010000 ... U+10FFFF + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + str.push_back(make_char(0xF0 | code >> 18)); + str.push_back(make_char(0x80 | (code >> 12 & 0x3F))); + str.push_back(make_char(0x80 | (code >> 6 & 0x3F))); + str.push_back(make_char(0x80 | (code & 0x3F))); + } +} + +CLI11_INLINE std::string remove_escaped_characters(const std::string &str) { + + std::string out; + out.reserve(str.size()); + for(auto loc = str.begin(); loc < str.end(); ++loc) { + if(*loc == '\\') { + if(str.end() - loc < 2) { + throw std::invalid_argument("invalid escape sequence " + str); + } + auto ecloc = escapedCharsCode.find_first_of(*(loc + 1)); + if(ecloc != std::string::npos) { + out.push_back(escapedChars[ecloc]); + ++loc; + } else if(*(loc + 1) == 'u') { + // must have 4 hex characters + if(str.end() - loc < 6) { + throw std::invalid_argument("unicode sequence must have 4 hex codes " + str); + } + std::uint32_t code{0}; + std::uint32_t mplier{16 * 16 * 16}; + for(int ii = 2; ii < 6; ++ii) { + std::uint32_t res = hexConvert(*(loc + ii)); + if(res > 0x0F) { + throw std::invalid_argument("unicode sequence must have 4 hex codes " + str); + } + code += res * mplier; + mplier = mplier / 16; + } + append_codepoint(out, code); + loc += 5; + } else if(*(loc + 1) == 'U') { + // must have 8 hex characters + if(str.end() - loc < 10) { + throw std::invalid_argument("unicode sequence must have 8 hex codes " + str); + } + std::uint32_t code{0}; + std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16}; + for(int ii = 2; ii < 10; ++ii) { + std::uint32_t res = hexConvert(*(loc + ii)); + if(res > 0x0F) { + throw std::invalid_argument("unicode sequence must have 8 hex codes " + str); + } + code += res * mplier; + mplier = mplier / 16; + } + append_codepoint(out, code); + loc += 9; + } else if(*(loc + 1) == '0') { + out.push_back('\0'); + ++loc; + } else { + throw std::invalid_argument(std::string("unrecognized escape sequence \\") + *(loc + 1) + " in " + str); + } + } else { + out.push_back(*loc); + } + } + return out; +} - /// String and similar direct assignment - template - struct classify_object< - T, - typename std::enable_if::value && !std::is_integral::value && - std::is_assignable::value && !is_vector::value>::type> - { - static constexpr object_category value{ object_category::string_assignable }; - }; +CLI11_INLINE std::size_t close_string_quote(const std::string &str, std::size_t start, char closure_char) { + std::size_t loc{0}; + for(loc = start + 1; loc < str.size(); ++loc) { + if(str[loc] == closure_char) { + break; + } + if(str[loc] == '\\') { + // skip the next character for escaped sequences + ++loc; + } + } + return loc; +} + +CLI11_INLINE std::size_t close_literal_quote(const std::string &str, std::size_t start, char closure_char) { + auto loc = str.find_first_of(closure_char, start + 1); + return (loc != std::string::npos ? loc : str.size()); +} + +CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char) { + + auto bracket_loc = matchBracketChars.find(closure_char); + switch(bracket_loc) { + case 0: + return close_string_quote(str, start, closure_char); + case 1: + case 2: + case std::string::npos: + return close_literal_quote(str, start, closure_char); + default: + break; + } - /// String and similar constructible and copy assignment - template - struct classify_object< - T, - typename std::enable_if::value && !std::is_integral::value && - !std::is_assignable::value && - std::is_constructible::value && !is_vector::value>::type> - { - static constexpr object_category value{ object_category::string_constructible }; - }; + std::string closures(1, closure_char); + auto loc = start + 1; - /// Enumerations - template - struct classify_object::value>::type> - { - static constexpr object_category value{ object_category::enumeration }; + while(loc < str.size()) { + if(str[loc] == closures.back()) { + closures.pop_back(); + if(closures.empty()) { + return loc; + } + } + bracket_loc = bracketChars.find(str[loc]); + if(bracket_loc != std::string::npos) { + switch(bracket_loc) { + case 0: + loc = close_string_quote(str, loc, str[loc]); + break; + case 1: + case 2: + loc = close_literal_quote(str, loc, str[loc]); + break; + default: + closures.push_back(matchBracketChars[bracket_loc]); + break; + } + } + ++loc; + } + if(loc > str.size()) { + loc = str.size(); + } + return loc; +} + +CLI11_INLINE std::vector split_up(std::string str, char delimiter) { + + auto find_ws = [delimiter](char ch) { + return (delimiter == '\0') ? std::isspace(ch, std::locale()) : (ch == delimiter); + }; + trim(str); + + std::vector output; + while(!str.empty()) { + if(bracketChars.find_first_of(str[0]) != std::string::npos) { + auto bracketLoc = bracketChars.find_first_of(str[0]); + auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]); + if(end >= str.size()) { + output.push_back(std::move(str)); + str.clear(); + } else { + output.push_back(str.substr(0, end + 1)); + if(end + 2 < str.size()) { + str = str.substr(end + 2); + } else { + str.clear(); + } + } + + } else { + auto it = std::find_if(std::begin(str), std::end(str), find_ws); + if(it != std::end(str)) { + std::string value = std::string(str.begin(), it); + output.push_back(value); + str = std::string(it + 1, str.end()); + } else { + output.push_back(str); + str.clear(); + } + } + trim(str); + } + return output; +} + +CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) { + auto next = str[offset + 1]; + if((next == '\"') || (next == '\'') || (next == '`')) { + auto astart = str.find_last_of("-/ \"\'`", offset - 1); + if(astart != std::string::npos) { + if(str[astart] == ((str[offset] == '=') ? '-' : '/')) + str[offset] = ' '; // interpret this as a space so the split_up works properly + } + } + return offset + 1; +} + +CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape) { + // s is our escaped output string + std::string escaped_string{}; + // loop through all characters + for(char c : string_to_escape) { + // check if a given character is printable + // the cast is necessary to avoid undefined behaviour + if(isprint(static_cast(c)) == 0) { + std::stringstream stream; + // if the character is not printable + // we'll convert it to a hex string using a stringstream + // note that since char is signed we have to cast it to unsigned first + stream << std::hex << static_cast(static_cast(c)); + std::string code = stream.str(); + escaped_string += std::string("\\x") + (code.size() < 2 ? "0" : "") + code; + + } else { + escaped_string.push_back(c); + } + } + if(escaped_string != string_to_escape) { + auto sqLoc = escaped_string.find('\''); + while(sqLoc != std::string::npos) { + escaped_string.replace(sqLoc, sqLoc + 1, "\\x27"); + sqLoc = escaped_string.find('\''); + } + escaped_string.insert(0, "'B\"("); + escaped_string.push_back(')'); + escaped_string.push_back('"'); + escaped_string.push_back('\''); + } + return escaped_string; +} + +CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string) { + size_t ssize = escaped_string.size(); + if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) { + return true; + } + return (escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0); +} + +CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string) { + std::size_t start{0}; + std::size_t tail{0}; + size_t ssize = escaped_string.size(); + if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) { + start = 3; + tail = 2; + } else if(escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0) { + start = 4; + tail = 3; + } + + if(start == 0) { + return escaped_string; + } + std::string outstring; + + outstring.reserve(ssize - start - tail); + std::size_t loc = start; + while(loc < ssize - tail) { + // ssize-2 to skip )" at the end + if(escaped_string[loc] == '\\' && (escaped_string[loc + 1] == 'x' || escaped_string[loc + 1] == 'X')) { + auto c1 = escaped_string[loc + 2]; + auto c2 = escaped_string[loc + 3]; + + std::uint32_t res1 = hexConvert(c1); + std::uint32_t res2 = hexConvert(c2); + if(res1 <= 0x0F && res2 <= 0x0F) { + loc += 4; + outstring.push_back(static_cast(res1 * 16 + res2)); + continue; + } + } + outstring.push_back(escaped_string[loc]); + ++loc; + } + return outstring; +} + +CLI11_INLINE void remove_quotes(std::vector &args) { + for(auto &arg : args) { + if(arg.front() == '\"' && arg.back() == '\"') { + remove_quotes(arg); + // only remove escaped for string arguments not literal strings + arg = remove_escaped_characters(arg); + } else { + remove_quotes(arg); + } + } +} + +CLI11_INLINE bool process_quoted_string(std::string &str, char string_char, char literal_char) { + if(str.size() <= 1) { + return false; + } + if(detail::is_binary_escaped_string(str)) { + str = detail::extract_binary_string(str); + return true; + } + if(str.front() == string_char && str.back() == string_char) { + detail::remove_outer(str, string_char); + if(str.find_first_of('\\') != std::string::npos) { + str = detail::remove_escaped_characters(str); + } + return true; + } + if((str.front() == literal_char || str.front() == '`') && str.back() == str.front()) { + detail::remove_outer(str, str.front()); + return true; + } + return false; +} + +std::string get_environment_value(const std::string &env_name) { + char *buffer = nullptr; + std::string ename_string; + +#ifdef _MSC_VER + // Windows version + std::size_t sz = 0; + if(_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer != nullptr) { + ename_string = std::string(buffer); + free(buffer); + } +#else + // This also works on Windows, but gives a warning + buffer = std::getenv(env_name.c_str()); + if(buffer != nullptr) { + ename_string = std::string(buffer); + } +#endif + return ename_string; +} + +} // namespace detail + + + +// Use one of these on all error classes. +// These are temporary and are undef'd at the end of this file. +#define CLI11_ERROR_DEF(parent, name) \ + protected: \ + name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ + name(std::string ename, std::string msg, ExitCodes exit_code) \ + : parent(std::move(ename), std::move(msg), exit_code) {} \ + \ + public: \ + name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \ + name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {} + +// This is added after the one above if a class is used directly and builds its own message +#define CLI11_ERROR_SIMPLE(name) \ + explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} + +/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, +/// int values from e.get_error_code(). +enum class ExitCodes { + Success = 0, + IncorrectConstruction = 100, + BadNameString, + OptionAlreadyAdded, + FileError, + ConversionError, + ValidationError, + RequiredError, + RequiresError, + ExcludesError, + ExtrasError, + ConfigError, + InvalidError, + HorribleError, + OptionNotFound, + ArgumentMismatch, + BaseClass = 127 +}; + +// Error definitions + +/// @defgroup error_group Errors +/// @brief Errors thrown by CLI11 +/// +/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors. +/// @{ + +/// All errors derive from this one +class Error : public std::runtime_error { + int actual_exit_code; + std::string error_name{"Error"}; + + public: + CLI11_NODISCARD int get_exit_code() const { return actual_exit_code; } + + CLI11_NODISCARD std::string get_name() const { return error_name; } + + Error(std::string name, std::string msg, int exit_code = static_cast(ExitCodes::BaseClass)) + : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {} + + Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast(exit_code)) {} +}; + +// Note: Using Error::Error constructors does not work on GCC 4.7 + +/// Construction errors (not in parsing) +class ConstructionError : public Error { + CLI11_ERROR_DEF(Error, ConstructionError) +}; + +/// Thrown when an option is set to conflicting values (non-vector and multi args, for example) +class IncorrectConstruction : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) + CLI11_ERROR_SIMPLE(IncorrectConstruction) + static IncorrectConstruction PositionalFlag(std::string name) { + return IncorrectConstruction(name + ": Flags cannot be positional"); + } + static IncorrectConstruction Set0Opt(std::string name) { + return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); + } + static IncorrectConstruction SetFlag(std::string name) { + return IncorrectConstruction(name + ": Cannot set an expected number for flags"); + } + static IncorrectConstruction ChangeNotVector(std::string name) { + return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); + } + static IncorrectConstruction AfterMultiOpt(std::string name) { + return IncorrectConstruction( + name + ": You can't change expected arguments after you've changed the multi option policy!"); + } + static IncorrectConstruction MissingOption(std::string name) { + return IncorrectConstruction("Option " + name + " is not defined"); + } + static IncorrectConstruction MultiOptionPolicy(std::string name) { + return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options"); + } +}; + +/// Thrown on construction of a bad name +class BadNameString : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, BadNameString) + CLI11_ERROR_SIMPLE(BadNameString) + static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } + static BadNameString MissingDash(std::string name) { + return BadNameString("Long names strings require 2 dashes " + name); + } + static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } + static BadNameString BadPositionalName(std::string name) { + return BadNameString("Invalid positional Name: " + name); + } + static BadNameString DashesOnly(std::string name) { + return BadNameString("Must have a name, not just dashes: " + name); + } + static BadNameString MultiPositionalNames(std::string name) { + return BadNameString("Only one positional name allowed, remove: " + name); + } +}; + +/// Thrown when an option already exists +class OptionAlreadyAdded : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) + explicit OptionAlreadyAdded(std::string name) + : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} + static OptionAlreadyAdded Requires(std::string name, std::string other) { + return {name + " requires " + other, ExitCodes::OptionAlreadyAdded}; + } + static OptionAlreadyAdded Excludes(std::string name, std::string other) { + return {name + " excludes " + other, ExitCodes::OptionAlreadyAdded}; + } +}; + +// Parsing errors + +/// Anything that can error in Parse +class ParseError : public Error { + CLI11_ERROR_DEF(Error, ParseError) +}; + +// Not really "errors" + +/// This is a successful completion on parsing, supposed to exit +class Success : public ParseError { + CLI11_ERROR_DEF(ParseError, Success) + Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} +}; + +/// -h or --help on command line +class CallForHelp : public Success { + CLI11_ERROR_DEF(Success, CallForHelp) + CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// Usually something like --help-all on command line +class CallForAllHelp : public Success { + CLI11_ERROR_DEF(Success, CallForAllHelp) + CallForAllHelp() + : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// -v or --version on command line +class CallForVersion : public Success { + CLI11_ERROR_DEF(Success, CallForVersion) + CallForVersion() + : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// Does not output a diagnostic in CLI11_PARSE, but allows main() to return with a specific error code. +class RuntimeError : public ParseError { + CLI11_ERROR_DEF(ParseError, RuntimeError) + explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {} +}; + +/// Thrown when parsing an INI file and it is missing +class FileError : public ParseError { + CLI11_ERROR_DEF(ParseError, FileError) + CLI11_ERROR_SIMPLE(FileError) + static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } +}; + +/// Thrown when conversion call back fails, such as when an int fails to coerce to a string +class ConversionError : public ParseError { + CLI11_ERROR_DEF(ParseError, ConversionError) + CLI11_ERROR_SIMPLE(ConversionError) + ConversionError(std::string member, std::string name) + : ConversionError("The value " + member + " is not an allowed value for " + name) {} + ConversionError(std::string name, std::vector results) + : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} + static ConversionError TooManyInputsFlag(std::string name) { + return ConversionError(name + ": too many inputs for a flag"); + } + static ConversionError TrueFalse(std::string name) { + return ConversionError(name + ": Should be true/false or a number"); + } +}; + +/// Thrown when validation of results fails +class ValidationError : public ParseError { + CLI11_ERROR_DEF(ParseError, ValidationError) + CLI11_ERROR_SIMPLE(ValidationError) + explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {} +}; + +/// Thrown when a required option is missing +class RequiredError : public ParseError { + CLI11_ERROR_DEF(ParseError, RequiredError) + explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} + static RequiredError Subcommand(std::size_t min_subcom) { + if(min_subcom == 1) { + return RequiredError("A subcommand"); + } + return {"Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError}; + } + static RequiredError + Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) { + if((min_option == 1) && (max_option == 1) && (used == 0)) + return RequiredError("Exactly 1 option from [" + option_list + "]"); + if((min_option == 1) && (max_option == 1) && (used > 1)) { + return {"Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) + + " were given", + ExitCodes::RequiredError}; + } + if((min_option == 1) && (used == 0)) + return RequiredError("At least 1 option from [" + option_list + "]"); + if(used < min_option) { + return {"Requires at least " + std::to_string(min_option) + " options used and only " + + std::to_string(used) + "were given from [" + option_list + "]", + ExitCodes::RequiredError}; + } + if(max_option == 1) + return {"Requires at most 1 options be given from [" + option_list + "]", ExitCodes::RequiredError}; + + return {"Requires at most " + std::to_string(max_option) + " options be used and " + std::to_string(used) + + "were given from [" + option_list + "]", + ExitCodes::RequiredError}; + } +}; + +/// Thrown when the wrong number of arguments has been received +class ArgumentMismatch : public ParseError { + CLI11_ERROR_DEF(ParseError, ArgumentMismatch) + CLI11_ERROR_SIMPLE(ArgumentMismatch) + ArgumentMismatch(std::string name, int expected, std::size_t received) + : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + + ", got " + std::to_string(received)) + : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + + ", got " + std::to_string(received)), + ExitCodes::ArgumentMismatch) {} + + static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) { + return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) { + return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { + return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); + } + static ArgumentMismatch FlagOverride(std::string name) { + return ArgumentMismatch(name + " was given a disallowed flag override"); + } + static ArgumentMismatch PartialType(std::string name, int num, std::string type) { + return ArgumentMismatch(name + ": " + type + " only partially specified: " + std::to_string(num) + + " required for each element"); + } +}; + +/// Thrown when a requires option is missing +class RequiresError : public ParseError { + CLI11_ERROR_DEF(ParseError, RequiresError) + RequiresError(std::string curname, std::string subname) + : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} +}; + +/// Thrown when an excludes option is present +class ExcludesError : public ParseError { + CLI11_ERROR_DEF(ParseError, ExcludesError) + ExcludesError(std::string curname, std::string subname) + : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} +}; + +/// Thrown when too many positionals or options are found +class ExtrasError : public ParseError { + CLI11_ERROR_DEF(ParseError, ExtrasError) + explicit ExtrasError(std::vector args) + : ExtrasError((args.size() > 1 ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} + ExtrasError(const std::string &name, std::vector args) + : ExtrasError(name, + (args.size() > 1 ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} +}; + +/// Thrown when extra values are found in an INI file +class ConfigError : public ParseError { + CLI11_ERROR_DEF(ParseError, ConfigError) + CLI11_ERROR_SIMPLE(ConfigError) + static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); } + static ConfigError NotConfigurable(std::string item) { + return ConfigError(item + ": This option is not allowed in a configuration file"); + } +}; + +/// Thrown when validation fails before parsing +class InvalidError : public ParseError { + CLI11_ERROR_DEF(ParseError, InvalidError) + explicit InvalidError(std::string name) + : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) { + } +}; + +/// This is just a safety check to verify selection and parsing match - you should not ever see it +/// Strings are directly added to this error, but again, it should never be seen. +class HorribleError : public ParseError { + CLI11_ERROR_DEF(ParseError, HorribleError) + CLI11_ERROR_SIMPLE(HorribleError) +}; + +// After parsing + +/// Thrown when counting a non-existent option +class OptionNotFound : public Error { + CLI11_ERROR_DEF(Error, OptionNotFound) + explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} +}; + +#undef CLI11_ERROR_DEF +#undef CLI11_ERROR_SIMPLE + +/// @} + + + + +// Type tools + +// Utilities for type enabling +namespace detail { +// Based generally on https://rmf.io/cxx11/almost-static-if +/// Simple empty scoped class +enum class enabler {}; + +/// An instance to use in EnableIf +constexpr enabler dummy = {}; +} // namespace detail + +/// A copy of enable_if_t from C++14, compatible with C++11. +/// +/// We could check to see if C++14 is being used, but it does not hurt to redefine this +/// (even Google does this: https://github.com/google/skia/blob/main/include/private/SkTLogic.h) +/// It is not in the std namespace anyway, so no harm done. +template using enable_if_t = typename std::enable_if::type; + +/// A copy of std::void_t from C++17 (helper for C++11 and C++14) +template struct make_void { + using type = void; +}; + +/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine +template using void_t = typename make_void::type; + +/// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine +template using conditional_t = typename std::conditional::type; + +/// Check to see if something is bool (fail check by default) +template struct is_bool : std::false_type {}; + +/// Check to see if something is bool (true if actually a bool) +template <> struct is_bool : std::true_type {}; + +/// Check to see if something is a shared pointer +template struct is_shared_ptr : std::false_type {}; + +/// Check to see if something is a shared pointer (True if really a shared pointer) +template struct is_shared_ptr> : std::true_type {}; + +/// Check to see if something is a shared pointer (True if really a shared pointer) +template struct is_shared_ptr> : std::true_type {}; + +/// Check to see if something is copyable pointer +template struct is_copyable_ptr { + static bool const value = is_shared_ptr::value || std::is_pointer::value; +}; + +/// This can be specialized to override the type deduction for IsMember. +template struct IsMemberType { + using type = T; +}; + +/// The main custom type needed here is const char * should be a string. +template <> struct IsMemberType { + using type = std::string; +}; + +namespace detail { + +// These are utilities for IsMember and other transforming objects + +/// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that +/// pointer_traits be valid. + +/// not a pointer +template struct element_type { + using type = T; +}; + +template struct element_type::value>::type> { + using type = typename std::pointer_traits::element_type; +}; + +/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of +/// the container +template struct element_value_type { + using type = typename element_type::type::value_type; +}; + +/// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing. +template struct pair_adaptor : std::false_type { + using value_type = typename T::value_type; + using first_type = typename std::remove_const::type; + using second_type = typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template static auto first(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } + /// Get the second value (really just the underlying value) + template static auto second(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } +}; + +/// Adaptor for map-like structure (true version, must have key_type and mapped_type). +/// This wraps a mapped container in a few utilities access it in a general way. +template +struct pair_adaptor< + T, + conditional_t, void>> + : std::true_type { + using value_type = typename T::value_type; + using first_type = typename std::remove_const::type; + using second_type = typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward(pair_value))) { + return std::get<0>(std::forward(pair_value)); + } + /// Get the second value (really just the underlying value) + template static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward(pair_value))) { + return std::get<1>(std::forward(pair_value)); + } +}; + +// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning +// in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in +// brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a +// little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out. +// But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be +// suppressed +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +#endif +// check for constructibility from a specific type and copy assignable used in the parse detection +template class is_direct_constructible { + template + static auto test(int, std::true_type) -> decltype( +// NVCC warns about narrowing conversions here +#ifdef __CUDACC__ +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diag_suppress 2361 +#else +#pragma diag_suppress 2361 +#endif +#endif + TT{std::declval()} +#ifdef __CUDACC__ +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diag_default 2361 +#else +#pragma diag_default 2361 +#endif +#endif + , + std::is_move_assignable()); + + template static auto test(int, std::false_type) -> std::false_type; + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0, typename std::is_constructible::type()))::value; +}; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +// Check for output streamability +// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream + +template class is_ostreamable { + template + static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Check for input streamability +template class is_istreamable { + template + static auto test(int) -> decltype(std::declval() >> std::declval(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Check for complex +template class is_complex { + template + static auto test(int) -> decltype(std::declval().real(), std::declval().imag(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Templated operation to get a value from a stream +template ::value, detail::enabler> = detail::dummy> +bool from_stream(const std::string &istring, T &obj) { + std::istringstream is; + is.str(istring); + is >> obj; + return !is.fail() && !is.rdbuf()->in_avail(); +} + +template ::value, detail::enabler> = detail::dummy> +bool from_stream(const std::string & /*istring*/, T & /*obj*/) { + return false; +} + +// check to see if an object is a mutable container (fail by default) +template struct is_mutable_container : std::false_type {}; + +/// type trait to test if a type is a mutable container meaning it has a value_type, it has an iterator, a clear, and +/// end methods and an insert function. And for our purposes we exclude std::string and types that can be constructed +/// from a std::string +template +struct is_mutable_container< + T, + conditional_t().end()), + decltype(std::declval().clear()), + decltype(std::declval().insert(std::declval().end())>(), + std::declval()))>, + void>> : public conditional_t::value || + std::is_constructible::value, + std::false_type, + std::true_type> {}; + +// check to see if an object is a mutable container (fail by default) +template struct is_readable_container : std::false_type {}; + +/// type trait to test if a type is a container meaning it has a value_type, it has an iterator, a clear, and an end +/// methods and an insert function. And for our purposes we exclude std::string and types that can be constructed from +/// a std::string +template +struct is_readable_container< + T, + conditional_t().end()), decltype(std::declval().begin())>, void>> + : public std::true_type {}; + +// check to see if an object is a wrapper (fail by default) +template struct is_wrapper : std::false_type {}; + +// check if an object is a wrapper (it has a value_type defined) +template +struct is_wrapper, void>> : public std::true_type {}; + +// Check for tuple like types, as in classes with a tuple_size type trait +template class is_tuple_like { + template + // static auto test(int) + // -> decltype(std::conditional<(std::tuple_size::value > 0), std::true_type, std::false_type>::type()); + static auto test(int) -> decltype(std::tuple_size::type>::value, std::true_type{}); + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Convert an object to a string (directly forward if this can become a string) +template ::value, detail::enabler> = detail::dummy> +auto to_string(T &&value) -> decltype(std::forward(value)) { + return std::forward(value); +} + +/// Construct a string from the object +template ::value && !std::is_convertible::value, + detail::enabler> = detail::dummy> +std::string to_string(const T &value) { + return std::string(value); // NOLINT(google-readability-casting) +} + +/// Convert an object to a string (streaming must be supported for that type) +template ::value && !std::is_constructible::value && + is_ostreamable::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&value) { + std::stringstream stream; + stream << value; + return stream.str(); +} + +/// If conversion is not supported, return an empty string (streaming is not supported for that type) +template ::value && !is_ostreamable::value && + !is_readable_container::type>::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&) { + return {}; +} + +/// convert a readable container to a string +template ::value && !is_ostreamable::value && + is_readable_container::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&variable) { + auto cval = variable.begin(); + auto end = variable.end(); + if(cval == end) { + return {"{}"}; + } + std::vector defaults; + while(cval != end) { + defaults.emplace_back(CLI::detail::to_string(*cval)); + ++cval; + } + return {"[" + detail::join(defaults) + "]"}; +} + +/// special template overload +template ::value, detail::enabler> = detail::dummy> +auto checked_to_string(T &&value) -> decltype(to_string(std::forward(value))) { + return to_string(std::forward(value)); +} + +/// special template overload +template ::value, detail::enabler> = detail::dummy> +std::string checked_to_string(T &&) { + return std::string{}; +} +/// get a string as a convertible value for arithmetic types +template ::value, detail::enabler> = detail::dummy> +std::string value_string(const T &value) { + return std::to_string(value); +} +/// get a string as a convertible value for enumerations +template ::value, detail::enabler> = detail::dummy> +std::string value_string(const T &value) { + return std::to_string(static_cast::type>(value)); +} +/// for other types just use the regular to_string function +template ::value && !std::is_arithmetic::value, detail::enabler> = detail::dummy> +auto value_string(const T &value) -> decltype(to_string(value)) { + return to_string(value); +} + +/// template to get the underlying value type if it exists or use a default +template struct wrapped_type { + using type = def; +}; + +/// Type size for regular object types that do not look like a tuple +template struct wrapped_type::value>::type> { + using type = typename T::value_type; +}; + +/// This will only trigger for actual void type +template struct type_count_base { + static const int value{0}; +}; + +/// Type size for regular object types that do not look like a tuple +template +struct type_count_base::value && !is_mutable_container::value && + !std::is_void::value>::type> { + static constexpr int value{1}; +}; + +/// the base tuple size +template +struct type_count_base::value && !is_mutable_container::value>::type> { + static constexpr int value{std::tuple_size::value}; +}; + +/// Type count base for containers is the type_count_base of the individual element +template struct type_count_base::value>::type> { + static constexpr int value{type_count_base::value}; +}; + +/// Set of overloads to get the type size of an object + +/// forward declare the subtype_count structure +template struct subtype_count; + +/// forward declare the subtype_count_min structure +template struct subtype_count_min; + +/// This will only trigger for actual void type +template struct type_count { + static const int value{0}; +}; + +/// Type size for regular object types that do not look like a tuple +template +struct type_count::value && !is_tuple_like::value && !is_complex::value && + !std::is_void::value>::type> { + static constexpr int value{1}; +}; + +/// Type size for complex since it sometimes looks like a wrapper +template struct type_count::value>::type> { + static constexpr int value{2}; +}; + +/// Type size of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) +template struct type_count::value>::type> { + static constexpr int value{subtype_count::value}; +}; + +/// Type size of types that are wrappers,except containers complex and tuples(which can also be wrappers sometimes) +template +struct type_count::value && !is_complex::value && !is_tuple_like::value && + !is_mutable_container::value>::type> { + static constexpr int value{type_count::value}; +}; + +/// 0 if the index > tuple size +template +constexpr typename std::enable_if::value, int>::type tuple_type_size() { + return 0; +} + +/// Recursively generate the tuple type name +template + constexpr typename std::enable_if < I::value, int>::type tuple_type_size() { + return subtype_count::type>::value + tuple_type_size(); +} + +/// Get the type size of the sum of type sizes for all the individual tuple types +template struct type_count::value>::type> { + static constexpr int value{tuple_type_size()}; +}; + +/// definition of subtype count +template struct subtype_count { + static constexpr int value{is_mutable_container::value ? expected_max_vector_size : type_count::value}; +}; + +/// This will only trigger for actual void type +template struct type_count_min { + static const int value{0}; +}; + +/// Type size for regular object types that do not look like a tuple +template +struct type_count_min< + T, + typename std::enable_if::value && !is_tuple_like::value && !is_wrapper::value && + !is_complex::value && !std::is_void::value>::type> { + static constexpr int value{type_count::value}; +}; + +/// Type size for complex since it sometimes looks like a wrapper +template struct type_count_min::value>::type> { + static constexpr int value{1}; +}; + +/// Type size min of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) +template +struct type_count_min< + T, + typename std::enable_if::value && !is_complex::value && !is_tuple_like::value>::type> { + static constexpr int value{subtype_count_min::value}; +}; + +/// 0 if the index > tuple size +template +constexpr typename std::enable_if::value, int>::type tuple_type_size_min() { + return 0; +} + +/// Recursively generate the tuple type name +template + constexpr typename std::enable_if < I::value, int>::type tuple_type_size_min() { + return subtype_count_min::type>::value + tuple_type_size_min(); +} + +/// Get the type size of the sum of type sizes for all the individual tuple types +template struct type_count_min::value>::type> { + static constexpr int value{tuple_type_size_min()}; +}; + +/// definition of subtype count +template struct subtype_count_min { + static constexpr int value{is_mutable_container::value + ? ((type_count::value < expected_max_vector_size) ? type_count::value : 0) + : type_count_min::value}; +}; + +/// This will only trigger for actual void type +template struct expected_count { + static const int value{0}; +}; + +/// For most types the number of expected items is 1 +template +struct expected_count::value && !is_wrapper::value && + !std::is_void::value>::type> { + static constexpr int value{1}; +}; +/// number of expected items in a vector +template struct expected_count::value>::type> { + static constexpr int value{expected_max_vector_size}; +}; + +/// number of expected items in a vector +template +struct expected_count::value && is_wrapper::value>::type> { + static constexpr int value{expected_count::value}; +}; + +// Enumeration of the different supported categorizations of objects +enum class object_category : int { + char_value = 1, + integral_value = 2, + unsigned_integral = 4, + enumeration = 6, + boolean_value = 8, + floating_point = 10, + number_constructible = 12, + double_constructible = 14, + integer_constructible = 16, + // string like types + string_assignable = 23, + string_constructible = 24, + wstring_assignable = 25, + wstring_constructible = 26, + other = 45, + // special wrapper or container types + wrapper_value = 50, + complex_number = 60, + tuple_value = 70, + container_value = 80, + +}; + +/// Set of overloads to classify an object according to type + +/// some type that is not otherwise recognized +template struct classify_object { + static constexpr object_category value{object_category::other}; +}; + +/// Signed integers +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_same::value && std::is_signed::value && + !is_bool::value && !std::is_enum::value>::type> { + static constexpr object_category value{object_category::integral_value}; +}; + +/// Unsigned integers +template +struct classify_object::value && std::is_unsigned::value && + !std::is_same::value && !is_bool::value>::type> { + static constexpr object_category value{object_category::unsigned_integral}; +}; + +/// single character values +template +struct classify_object::value && !std::is_enum::value>::type> { + static constexpr object_category value{object_category::char_value}; +}; + +/// Boolean values +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::boolean_value}; +}; + +/// Floats +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::floating_point}; +}; +#if defined _MSC_VER +// in MSVC wstring should take precedence if available this isn't as useful on other compilers due to the broader use of +// utf-8 encoding +#define WIDE_STRING_CHECK \ + !std::is_assignable::value && !std::is_constructible::value +#define STRING_CHECK true +#else +#define WIDE_STRING_CHECK true +#define STRING_CHECK !std::is_assignable::value && !std::is_constructible::value +#endif + +/// String and similar direct assignment +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_integral::value && WIDE_STRING_CHECK && + std::is_assignable::value>::type> { + static constexpr object_category value{object_category::string_assignable}; +}; + +/// String and similar constructible and copy assignment +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_integral::value && + !std::is_assignable::value && (type_count::value == 1) && + WIDE_STRING_CHECK && std::is_constructible::value>::type> { + static constexpr object_category value{object_category::string_constructible}; +}; + +/// Wide strings +template +struct classify_object::value && !std::is_integral::value && + STRING_CHECK && std::is_assignable::value>::type> { + static constexpr object_category value{object_category::wstring_assignable}; +}; + +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_integral::value && + !std::is_assignable::value && (type_count::value == 1) && + STRING_CHECK && std::is_constructible::value>::type> { + static constexpr object_category value{object_category::wstring_constructible}; +}; + +/// Enumerations +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::enumeration}; +}; + +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::complex_number}; +}; + +/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, +/// vectors, and enumerations +template struct uncommon_type { + using type = typename std::conditional< + !std::is_floating_point::value && !std::is_integral::value && + !std::is_assignable::value && !std::is_constructible::value && + !std::is_assignable::value && !std::is_constructible::value && + !is_complex::value && !is_mutable_container::value && !std::is_enum::value, + std::true_type, + std::false_type>::type; + static constexpr bool value = type::value; +}; + +/// wrapper type +template +struct classify_object::value && is_wrapper::value && + !is_tuple_like::value && uncommon_type::value)>::type> { + static constexpr object_category value{object_category::wrapper_value}; +}; + +/// Assignable from double or int +template +struct classify_object::value && type_count::value == 1 && + !is_wrapper::value && is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::number_constructible}; +}; + +/// Assignable from int +template +struct classify_object::value && type_count::value == 1 && + !is_wrapper::value && !is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::integer_constructible}; +}; + +/// Assignable from double +template +struct classify_object::value && type_count::value == 1 && + !is_wrapper::value && is_direct_constructible::value && + !is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::double_constructible}; +}; + +/// Tuple type +template +struct classify_object< + T, + typename std::enable_if::value && + ((type_count::value >= 2 && !is_wrapper::value) || + (uncommon_type::value && !is_direct_constructible::value && + !is_direct_constructible::value) || + (uncommon_type::value && type_count::value >= 2))>::type> { + static constexpr object_category value{object_category::tuple_value}; + // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be + // constructed from just the first element so tuples of can be constructed from a string, which + // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2 + // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out + // those cases that are caught by other object classifications +}; + +/// container type +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::container_value}; +}; + +// Type name print + +/// Was going to be based on +/// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template +/// But this is cleaner and works better in this case + +template ::value == object_category::char_value, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "CHAR"; +} + +template ::value == object_category::integral_value || + classify_object::value == object_category::integer_constructible, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "INT"; +} + +template ::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "UINT"; +} + +template ::value == object_category::floating_point || + classify_object::value == object_category::number_constructible || + classify_object::value == object_category::double_constructible, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "FLOAT"; +} + +/// Print name for enumeration types +template ::value == object_category::enumeration, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "ENUM"; +} + +/// Print name for enumeration types +template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "BOOLEAN"; +} + +/// Print name for enumeration types +template ::value == object_category::complex_number, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "COMPLEX"; +} + +/// Print for all other types +template ::value >= object_category::string_assignable && + classify_object::value <= object_category::other, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "TEXT"; +} +/// typename for tuple value +template ::value == object_category::tuple_value && type_count_base::value >= 2, + detail::enabler> = detail::dummy> +std::string type_name(); // forward declaration + +/// Generate type name for a wrapper or container value +template ::value == object_category::container_value || + classify_object::value == object_category::wrapper_value, + detail::enabler> = detail::dummy> +std::string type_name(); // forward declaration + +/// Print name for single element tuple types +template ::value == object_category::tuple_value && type_count_base::value == 1, + detail::enabler> = detail::dummy> +inline std::string type_name() { + return type_name::type>::type>(); +} + +/// Empty string if the index > tuple size +template +inline typename std::enable_if::value, std::string>::type tuple_name() { + return std::string{}; +} + +/// Recursively generate the tuple type name +template +inline typename std::enable_if<(I < type_count_base::value), std::string>::type tuple_name() { + auto str = std::string{type_name::type>::type>()} + ',' + + tuple_name(); + if(str.back() == ',') + str.pop_back(); + return str; +} + +/// Print type name for tuples with 2 or more elements +template ::value == object_category::tuple_value && type_count_base::value >= 2, + detail::enabler>> +inline std::string type_name() { + auto tname = std::string(1, '[') + tuple_name(); + tname.push_back(']'); + return tname; +} + +/// get the type name for a type that has a value_type member +template ::value == object_category::container_value || + classify_object::value == object_category::wrapper_value, + detail::enabler>> +inline std::string type_name() { + return type_name(); +} + +// Lexical cast + +/// Convert to an unsigned integral +template ::value, detail::enabler> = detail::dummy> +bool integral_conversion(const std::string &input, T &output) noexcept { + if(input.empty() || input.front() == '-') { + return false; + } + char *val{nullptr}; + errno = 0; + std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0); + if(errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + if(val == (input.c_str() + input.size()) && static_cast(output) == output_ll) { + return true; + } + val = nullptr; + std::int64_t output_sll = std::strtoll(input.c_str(), &val, 0); + if(val == (input.c_str() + input.size())) { + output = (output_sll < 0) ? static_cast(0) : static_cast(output_sll); + return (static_cast(output) == output_sll); + } + // remove separators + if(input.find_first_of("_'") != std::string::npos) { + std::string nstring = input; + nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end()); + nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end()); + return integral_conversion(nstring, output); + } + if(input.compare(0, 2, "0o") == 0) { + val = nullptr; + errno = 0; + output_ll = std::strtoull(input.c_str() + 2, &val, 8); + if(errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); + } + if(input.compare(0, 2, "0b") == 0) { + val = nullptr; + errno = 0; + output_ll = std::strtoull(input.c_str() + 2, &val, 2); + if(errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); + } + return false; +} + +/// Convert to a signed integral +template ::value, detail::enabler> = detail::dummy> +bool integral_conversion(const std::string &input, T &output) noexcept { + if(input.empty()) { + return false; + } + char *val = nullptr; + errno = 0; + std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0); + if(errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + if(val == (input.c_str() + input.size()) && static_cast(output) == output_ll) { + return true; + } + if(input == "true") { + // this is to deal with a few oddities with flags and wrapper int types + output = static_cast(1); + return true; + } + // remove separators + if(input.find_first_of("_'") != std::string::npos) { + std::string nstring = input; + nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end()); + nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end()); + return integral_conversion(nstring, output); + } + if(input.compare(0, 2, "0o") == 0) { + val = nullptr; + errno = 0; + output_ll = std::strtoll(input.c_str() + 2, &val, 8); + if(errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); + } + if(input.compare(0, 2, "0b") == 0) { + val = nullptr; + errno = 0; + output_ll = std::strtoll(input.c_str() + 2, &val, 2); + if(errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); + } + return false; +} + +/// Convert a flag into an integer value typically binary flags sets errno to nonzero if conversion failed +inline std::int64_t to_flag_value(std::string val) noexcept { + static const std::string trueString("true"); + static const std::string falseString("false"); + if(val == trueString) { + return 1; + } + if(val == falseString) { + return -1; + } + val = detail::to_lower(val); + std::int64_t ret = 0; + if(val.size() == 1) { + if(val[0] >= '1' && val[0] <= '9') { + return (static_cast(val[0]) - '0'); + } + switch(val[0]) { + case '0': + case 'f': + case 'n': + case '-': + ret = -1; + break; + case 't': + case 'y': + case '+': + ret = 1; + break; + default: + errno = EINVAL; + return -1; + } + return ret; + } + if(val == trueString || val == "on" || val == "yes" || val == "enable") { + ret = 1; + } else if(val == falseString || val == "off" || val == "no" || val == "disable") { + ret = -1; + } else { + char *loc_ptr{nullptr}; + ret = std::strtoll(val.c_str(), &loc_ptr, 0); + if(loc_ptr != (val.c_str() + val.size()) && errno == 0) { + errno = EINVAL; + } + } + return ret; +} + +/// Integer conversion +template ::value == object_category::integral_value || + classify_object::value == object_category::unsigned_integral, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + return integral_conversion(input, output); +} + +/// char values +template ::value == object_category::char_value, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + if(input.size() == 1) { + output = static_cast(input[0]); + return true; + } + return integral_conversion(input, output); +} + +/// Boolean values +template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + errno = 0; + auto out = to_flag_value(input); + if(errno == 0) { + output = (out > 0); + } else if(errno == ERANGE) { + output = (input[0] != '-'); + } else { + return false; + } + return true; +} + +/// Floats +template ::value == object_category::floating_point, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + if(input.empty()) { + return false; + } + char *val = nullptr; + auto output_ld = std::strtold(input.c_str(), &val); + output = static_cast(output_ld); + if(val == (input.c_str() + input.size())) { + return true; + } + // remove separators + if(input.find_first_of("_'") != std::string::npos) { + std::string nstring = input; + nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end()); + nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end()); + return lexical_cast(nstring, output); + } + return false; +} + +/// complex +template ::value == object_category::complex_number, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + using XC = typename wrapped_type::type; + XC x{0.0}, y{0.0}; + auto str1 = input; + bool worked = false; + auto nloc = str1.find_last_of("+-"); + if(nloc != std::string::npos && nloc > 0) { + worked = lexical_cast(str1.substr(0, nloc), x); + str1 = str1.substr(nloc); + if(str1.back() == 'i' || str1.back() == 'j') + str1.pop_back(); + worked = worked && lexical_cast(str1, y); + } else { + if(str1.back() == 'i' || str1.back() == 'j') { + str1.pop_back(); + worked = lexical_cast(str1, y); + x = XC{0}; + } else { + worked = lexical_cast(str1, x); + y = XC{0}; + } + } + if(worked) { + output = T{x, y}; + return worked; + } + return from_stream(input, output); +} + +/// String and similar direct assignment +template ::value == object_category::string_assignable, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = input; + return true; +} + +/// String and similar constructible and copy assignment +template < + typename T, + enable_if_t::value == object_category::string_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = T(input); + return true; +} + +/// Wide strings +template < + typename T, + enable_if_t::value == object_category::wstring_assignable, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = widen(input); + return true; +} + +template < + typename T, + enable_if_t::value == object_category::wstring_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = T{widen(input)}; + return true; +} + +/// Enumerations +template ::value == object_category::enumeration, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + typename std::underlying_type::type val; + if(!integral_conversion(input, val)) { + return false; + } + output = static_cast(val); + return true; +} + +/// wrapper types +template ::value == object_category::wrapper_value && + std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + typename T::value_type val; + if(lexical_cast(input, val)) { + output = val; + return true; + } + return from_stream(input, output); +} + +template ::value == object_category::wrapper_value && + !std::is_assignable::value && std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + typename T::value_type val; + if(lexical_cast(input, val)) { + output = T{val}; + return true; + } + return from_stream(input, output); +} + +/// Assignable from double or int +template < + typename T, + enable_if_t::value == object_category::number_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val = 0; + if(integral_conversion(input, val)) { + output = T(val); + return true; + } + + double dval = 0.0; + if(lexical_cast(input, dval)) { + output = T{dval}; + return true; + } + + return from_stream(input, output); +} + +/// Assignable from int +template < + typename T, + enable_if_t::value == object_category::integer_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val = 0; + if(integral_conversion(input, val)) { + output = T(val); + return true; + } + return from_stream(input, output); +} + +/// Assignable from double +template < + typename T, + enable_if_t::value == object_category::double_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + double val = 0.0; + if(lexical_cast(input, val)) { + output = T{val}; + return true; + } + return from_stream(input, output); +} + +/// Non-string convertible from an int +template ::value == object_category::other && std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val = 0; + if(integral_conversion(input, val)) { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4800) +#endif + // with Atomic this could produce a warning due to the conversion but if atomic gets here it is an old style + // so will most likely still work + output = val; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + return true; + } + // LCOV_EXCL_START + // This version of cast is only used for odd cases in an older compilers the fail over + // from_stream is tested elsewhere an not relevant for coverage here + return from_stream(input, output); + // LCOV_EXCL_STOP +} + +/// Non-string parsable by a stream +template ::value == object_category::other && !std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + static_assert(is_istreamable::value, + "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it " + "is convertible from another type use the add_option(...) with XC being the known type"); + return from_stream(input, output); +} + +/// Assign a value through lexical cast operations +/// Strings can be empty so we need to do a little different +template ::value && + (classify_object::value == object_category::string_assignable || + classify_object::value == object_category::string_constructible || + classify_object::value == object_category::wstring_assignable || + classify_object::value == object_category::wstring_constructible), + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + return lexical_cast(input, output); +} + +/// Assign a value through lexical cast operations +template ::value && std::is_assignable::value && + classify_object::value != object_category::string_assignable && + classify_object::value != object_category::string_constructible && + classify_object::value != object_category::wstring_assignable && + classify_object::value != object_category::wstring_constructible, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + if(input.empty()) { + output = AssignTo{}; + return true; + } + + return lexical_cast(input, output); +} + +/// Assign a value through lexical cast operations +template ::value && !std::is_assignable::value && + classify_object::value == object_category::wrapper_value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + if(input.empty()) { + typename AssignTo::value_type emptyVal{}; + output = emptyVal; + return true; + } + return lexical_cast(input, output); +} + +/// Assign a value through lexical cast operations for int compatible values +/// mainly for atomic operations on some compilers +template ::value && !std::is_assignable::value && + classify_object::value != object_category::wrapper_value && + std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + if(input.empty()) { + output = 0; + return true; + } + int val{0}; + if(lexical_cast(input, val)) { +#if defined(__clang__) +/* on some older clang compilers */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" +#endif + output = val; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + return true; + } + return false; +} + +/// Assign a value converted from a string in lexical cast to the output value directly +template ::value && std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + ConvertTo val{}; + bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; + if(parse_result) { + output = val; + } + return parse_result; +} + +/// Assign a value from a lexical cast through constructing a value and move assigning it +template < + typename AssignTo, + typename ConvertTo, + enable_if_t::value && !std::is_assignable::value && + std::is_move_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + ConvertTo val{}; + bool parse_result = input.empty() ? true : lexical_cast(input, val); + if(parse_result) { + output = AssignTo(val); // use () form of constructor to allow some implicit conversions + } + return parse_result; +} + +/// primary lexical conversion operation, 1 string to 1 type of some kind +template ::value <= object_category::other && + classify_object::value <= object_category::wrapper_value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + return lexical_assign(strings[0], output); +} + +/// Lexical conversion if there is only one element but the conversion type is for two, then call a two element +/// constructor +template ::value <= 2) && expected_count::value == 1 && + is_tuple_like::value && type_count_base::value == 2, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + // the remove const is to handle pair types coming from a container + using FirstType = typename std::remove_const::type>::type; + using SecondType = typename std::tuple_element<1, ConvertTo>::type; + FirstType v1; + SecondType v2; + bool retval = lexical_assign(strings[0], v1); + retval = retval && lexical_assign((strings.size() > 1) ? strings[1] : std::string{}, v2); + if(retval) { + output = AssignTo{v1, v2}; + } + return retval; +} + +/// Lexical conversion of a container types of single elements +template ::value && is_mutable_container::value && + type_count::value == 1, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + output.erase(output.begin(), output.end()); + if(strings.empty()) { + return true; + } + if(strings.size() == 1 && strings[0] == "{}") { + return true; + } + bool skip_remaining = false; + if(strings.size() == 2 && strings[0] == "{}" && is_separator(strings[1])) { + skip_remaining = true; + } + for(const auto &elem : strings) { + typename AssignTo::value_type out; + bool retval = lexical_assign(elem, out); + if(!retval) { + return false; + } + output.insert(output.end(), std::move(out)); + if(skip_remaining) { + break; + } + } + return (!output.empty()); +} + +/// Lexical conversion for complex types +template ::value, detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + + if(strings.size() >= 2 && !strings[1].empty()) { + using XC2 = typename wrapped_type::type; + XC2 x{0.0}, y{0.0}; + auto str1 = strings[1]; + if(str1.back() == 'i' || str1.back() == 'j') { + str1.pop_back(); + } + auto worked = lexical_cast(strings[0], x) && lexical_cast(str1, y); + if(worked) { + output = ConvertTo{x, y}; + } + return worked; + } + return lexical_assign(strings[0], output); +} + +/// Conversion to a vector type using a particular single type as the conversion type +template ::value && (expected_count::value == 1) && + (type_count::value == 1), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + bool retval = true; + output.clear(); + output.reserve(strings.size()); + for(const auto &elem : strings) { + + output.emplace_back(); + retval = retval && lexical_assign(elem, output.back()); + } + return (!output.empty()) && retval; +} + +// forward declaration + +/// Lexical conversion of a container types with conversion type of two elements +template ::value && is_mutable_container::value && + type_count_base::value == 2, + detail::enabler> = detail::dummy> +bool lexical_conversion(std::vector strings, AssignTo &output); + +/// Lexical conversion of a vector types with type_size >2 forward declaration +template ::value && is_mutable_container::value && + type_count_base::value != 2 && + ((type_count::value > 2) || + (type_count::value > type_count_base::value)), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output); + +/// Conversion for tuples +template ::value && is_tuple_like::value && + (type_count_base::value != type_count::value || + type_count::value > 2), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output); // forward declaration + +/// Conversion for operations where the assigned type is some class but the conversion is a mutable container or large +/// tuple +template ::value && !is_mutable_container::value && + classify_object::value != object_category::wrapper_value && + (is_mutable_container::value || type_count::value > 2), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + + if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { + ConvertTo val; + auto retval = lexical_conversion(strings, val); + output = AssignTo{val}; + return retval; + } + output = AssignTo{}; + return true; +} + +/// function template for converting tuples if the static Index is greater than the tuple size +template +inline typename std::enable_if<(I >= type_count_base::value), bool>::type +tuple_conversion(const std::vector &, AssignTo &) { + return true; +} + +/// Conversion of a tuple element where the type size ==1 and not a mutable container +template +inline typename std::enable_if::value && type_count::value == 1, bool>::type +tuple_type_conversion(std::vector &strings, AssignTo &output) { + auto retval = lexical_assign(strings[0], output); + strings.erase(strings.begin()); + return retval; +} + +/// Conversion of a tuple element where the type size !=1 but the size is fixed and not a mutable container +template +inline typename std::enable_if::value && (type_count::value > 1) && + type_count::value == type_count_min::value, + bool>::type +tuple_type_conversion(std::vector &strings, AssignTo &output) { + auto retval = lexical_conversion(strings, output); + strings.erase(strings.begin(), strings.begin() + type_count::value); + return retval; +} + +/// Conversion of a tuple element where the type is a mutable container or a type with different min and max type sizes +template +inline typename std::enable_if::value || + type_count::value != type_count_min::value, + bool>::type +tuple_type_conversion(std::vector &strings, AssignTo &output) { + + std::size_t index{subtype_count_min::value}; + const std::size_t mx_count{subtype_count::value}; + const std::size_t mx{(std::min)(mx_count, strings.size() - 1)}; + + while(index < mx) { + if(is_separator(strings[index])) { + break; + } + ++index; + } + bool retval = lexical_conversion( + std::vector(strings.begin(), strings.begin() + static_cast(index)), output); + if(strings.size() > index) { + strings.erase(strings.begin(), strings.begin() + static_cast(index) + 1); + } else { + strings.clear(); + } + return retval; +} + +/// Tuple conversion operation +template +inline typename std::enable_if<(I < type_count_base::value), bool>::type +tuple_conversion(std::vector strings, AssignTo &output) { + bool retval = true; + using ConvertToElement = typename std:: + conditional::value, typename std::tuple_element::type, ConvertTo>::type; + if(!strings.empty()) { + retval = retval && tuple_type_conversion::type, ConvertToElement>( + strings, std::get(output)); + } + retval = retval && tuple_conversion(std::move(strings), output); + return retval; +} + +/// Lexical conversion of a container types with tuple elements of size 2 +template ::value && is_mutable_container::value && + type_count_base::value == 2, + detail::enabler>> +bool lexical_conversion(std::vector strings, AssignTo &output) { + output.clear(); + while(!strings.empty()) { + + typename std::remove_const::type>::type v1; + typename std::tuple_element<1, typename ConvertTo::value_type>::type v2; + bool retval = tuple_type_conversion(strings, v1); + if(!strings.empty()) { + retval = retval && tuple_type_conversion(strings, v2); + } + if(retval) { + output.insert(output.end(), typename AssignTo::value_type{v1, v2}); + } else { + return false; + } + } + return (!output.empty()); +} + +/// lexical conversion of tuples with type count>2 or tuples of types of some element with a type size>=2 +template ::value && is_tuple_like::value && + (type_count_base::value != type_count::value || + type_count::value > 2), + detail::enabler>> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + static_assert( + !is_tuple_like::value || type_count_base::value == type_count_base::value, + "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); + return tuple_conversion(strings, output); +} + +/// Lexical conversion of a vector types for everything but tuples of two elements and types of size 1 +template ::value && is_mutable_container::value && + type_count_base::value != 2 && + ((type_count::value > 2) || + (type_count::value > type_count_base::value)), + detail::enabler>> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + bool retval = true; + output.clear(); + std::vector temp; + std::size_t ii{0}; + std::size_t icount{0}; + std::size_t xcm{type_count::value}; + auto ii_max = strings.size(); + while(ii < ii_max) { + temp.push_back(strings[ii]); + ++ii; + ++icount; + if(icount == xcm || is_separator(temp.back()) || ii == ii_max) { + if(static_cast(xcm) > type_count_min::value && is_separator(temp.back())) { + temp.pop_back(); + } + typename AssignTo::value_type temp_out; + retval = retval && + lexical_conversion(temp, temp_out); + temp.clear(); + if(!retval) { + return false; + } + output.insert(output.end(), std::move(temp_out)); + icount = 0; + } + } + return retval; +} + +/// conversion for wrapper types +template ::value == object_category::wrapper_value && + std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + if(strings.empty() || strings.front().empty()) { + output = ConvertTo{}; + return true; + } + typename ConvertTo::value_type val; + if(lexical_conversion(strings, val)) { + output = ConvertTo{val}; + return true; + } + return false; +} + +/// conversion for wrapper types +template ::value == object_category::wrapper_value && + !std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + using ConvertType = typename ConvertTo::value_type; + if(strings.empty() || strings.front().empty()) { + output = ConvertType{}; + return true; + } + ConvertType val; + if(lexical_conversion(strings, val)) { + output = val; + return true; + } + return false; +} + +/// Sum a vector of strings +inline std::string sum_string_vector(const std::vector &values) { + double val{0.0}; + bool fail{false}; + std::string output; + for(const auto &arg : values) { + double tv{0.0}; + auto comp = lexical_cast(arg, tv); + if(!comp) { + errno = 0; + auto fv = detail::to_flag_value(arg); + fail = (errno != 0); + if(fail) { + break; + } + tv = static_cast(fv); + } + val += tv; + } + if(fail) { + for(const auto &arg : values) { + output.append(arg); + } + } else { + std::ostringstream out; + out.precision(16); + out << val; + output = out.str(); + } + return output; +} + +} // namespace detail + + + +namespace detail { + +// Returns false if not a short option. Otherwise, sets opt name and rest and returns true +CLI11_INLINE bool split_short(const std::string ¤t, std::string &name, std::string &rest); + +// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true +CLI11_INLINE bool split_long(const std::string ¤t, std::string &name, std::string &value); + +// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true +CLI11_INLINE bool split_windows_style(const std::string ¤t, std::string &name, std::string &value); + +// Splits a string into multiple long and short names +CLI11_INLINE std::vector split_names(std::string current); + +/// extract default flag values either {def} or starting with a ! +CLI11_INLINE std::vector> get_default_flag_values(const std::string &str); + +/// Get a vector of short names, one of long names, and a single name +CLI11_INLINE std::tuple, std::vector, std::string> +get_names(const std::vector &input); + +} // namespace detail + + + +namespace detail { + +CLI11_INLINE bool split_short(const std::string ¤t, std::string &name, std::string &rest) { + if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) { + name = current.substr(1, 1); + rest = current.substr(2); + return true; + } + return false; +} + +CLI11_INLINE bool split_long(const std::string ¤t, std::string &name, std::string &value) { + if(current.size() > 2 && current.compare(0, 2, "--") == 0 && valid_first_char(current[2])) { + auto loc = current.find_first_of('='); + if(loc != std::string::npos) { + name = current.substr(2, loc - 2); + value = current.substr(loc + 1); + } else { + name = current.substr(2); + value = ""; + } + return true; + } + return false; +} + +CLI11_INLINE bool split_windows_style(const std::string ¤t, std::string &name, std::string &value) { + if(current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) { + auto loc = current.find_first_of(':'); + if(loc != std::string::npos) { + name = current.substr(1, loc - 1); + value = current.substr(loc + 1); + } else { + name = current.substr(1); + value = ""; + } + return true; + } + return false; +} + +CLI11_INLINE std::vector split_names(std::string current) { + std::vector output; + std::size_t val = 0; + while((val = current.find(',')) != std::string::npos) { + output.push_back(trim_copy(current.substr(0, val))); + current = current.substr(val + 1); + } + output.push_back(trim_copy(current)); + return output; +} + +CLI11_INLINE std::vector> get_default_flag_values(const std::string &str) { + std::vector flags = split_names(str); + flags.erase(std::remove_if(flags.begin(), + flags.end(), + [](const std::string &name) { + return ((name.empty()) || (!(((name.find_first_of('{') != std::string::npos) && + (name.back() == '}')) || + (name[0] == '!')))); + }), + flags.end()); + std::vector> output; + output.reserve(flags.size()); + for(auto &flag : flags) { + auto def_start = flag.find_first_of('{'); + std::string defval = "false"; + if((def_start != std::string::npos) && (flag.back() == '}')) { + defval = flag.substr(def_start + 1); + defval.pop_back(); + flag.erase(def_start, std::string::npos); // NOLINT(readability-suspicious-call-argument) + } + flag.erase(0, flag.find_first_not_of("-!")); + output.emplace_back(flag, defval); + } + return output; +} + +CLI11_INLINE std::tuple, std::vector, std::string> +get_names(const std::vector &input) { + + std::vector short_names; + std::vector long_names; + std::string pos_name; + for(std::string name : input) { + if(name.length() == 0) { + continue; + } + if(name.length() > 1 && name[0] == '-' && name[1] != '-') { + if(name.length() == 2 && valid_first_char(name[1])) + short_names.emplace_back(1, name[1]); + else if(name.length() > 2) + throw BadNameString::MissingDash(name); + else + throw BadNameString::OneCharName(name); + } else if(name.length() > 2 && name.substr(0, 2) == "--") { + name = name.substr(2); + if(valid_name_string(name)) + long_names.push_back(name); + else + throw BadNameString::BadLongName(name); + } else if(name == "-" || name == "--") { + throw BadNameString::DashesOnly(name); + } else { + if(!pos_name.empty()) + throw BadNameString::MultiPositionalNames(name); + if(valid_name_string(name)) { + pos_name = name; + } else { + throw BadNameString::BadPositionalName(name); + } + } + } + return std::make_tuple(short_names, long_names, pos_name); +} + +} // namespace detail + + + +class App; + +/// Holds values to load into Options +struct ConfigItem { + /// This is the list of parents + std::vector parents{}; + + /// This is the name + std::string name{}; + /// Listing of inputs + std::vector inputs{}; + + /// The list of parents and name joined by "." + CLI11_NODISCARD std::string fullname() const { + std::vector tmp = parents; + tmp.emplace_back(name); + return detail::join(tmp, "."); + } +}; + +/// This class provides a converter for configuration files. +class Config { + protected: + std::vector items{}; + + public: + /// Convert an app into a configuration + virtual std::string to_config(const App *, bool, bool, std::string) const = 0; + + /// Convert a configuration into an app + virtual std::vector from_config(std::istream &) const = 0; + + /// Get a flag value + CLI11_NODISCARD virtual std::string to_flag(const ConfigItem &item) const { + if(item.inputs.size() == 1) { + return item.inputs.at(0); + } + if(item.inputs.empty()) { + return "{}"; + } + throw ConversionError::TooManyInputsFlag(item.fullname()); // LCOV_EXCL_LINE + } + + /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure + CLI11_NODISCARD std::vector from_file(const std::string &name) const { + std::ifstream input{name}; + if(!input.good()) + throw FileError::Missing(name); + + return from_config(input); + } + + /// Virtual destructor + virtual ~Config() = default; +}; + +/// This converter works with INI/TOML files; to write INI files use ConfigINI +class ConfigBase : public Config { + protected: + /// the character used for comments + char commentChar = '#'; + /// the character used to start an array '\0' is a default to not use + char arrayStart = '['; + /// the character used to end an array '\0' is a default to not use + char arrayEnd = ']'; + /// the character used to separate elements in an array + char arraySeparator = ','; + /// the character used separate the name from the value + char valueDelimiter = '='; + /// the character to use around strings + char stringQuote = '"'; + /// the character to use around single characters and literal strings + char literalQuote = '\''; + /// the maximum number of layers to allow + uint8_t maximumLayers{255}; + /// the separator used to separator parent layers + char parentSeparatorChar{'.'}; + /// Specify the configuration index to use for arrayed sections + int16_t configIndex{-1}; + /// Specify the configuration section that should be used + std::string configSection{}; + + public: + std::string + to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override; + + std::vector from_config(std::istream &input) const override; + /// Specify the configuration for comment characters + ConfigBase *comment(char cchar) { + commentChar = cchar; + return this; + } + /// Specify the start and end characters for an array + ConfigBase *arrayBounds(char aStart, char aEnd) { + arrayStart = aStart; + arrayEnd = aEnd; + return this; + } + /// Specify the delimiter character for an array + ConfigBase *arrayDelimiter(char aSep) { + arraySeparator = aSep; + return this; + } + /// Specify the delimiter between a name and value + ConfigBase *valueSeparator(char vSep) { + valueDelimiter = vSep; + return this; + } + /// Specify the quote characters used around strings and literal strings + ConfigBase *quoteCharacter(char qString, char literalChar) { + stringQuote = qString; + literalQuote = literalChar; + return this; + } + /// Specify the maximum number of parents + ConfigBase *maxLayers(uint8_t layers) { + maximumLayers = layers; + return this; + } + /// Specify the separator to use for parent layers + ConfigBase *parentSeparator(char sep) { + parentSeparatorChar = sep; + return this; + } + /// get a reference to the configuration section + std::string §ionRef() { return configSection; } + /// get the section + CLI11_NODISCARD const std::string §ion() const { return configSection; } + /// specify a particular section of the configuration file to use + ConfigBase *section(const std::string §ionName) { + configSection = sectionName; + return this; + } + + /// get a reference to the configuration index + int16_t &indexRef() { return configIndex; } + /// get the section index + CLI11_NODISCARD int16_t index() const { return configIndex; } + /// specify a particular index in the section to use (-1) for all sections to use + ConfigBase *index(int16_t sectionIndex) { + configIndex = sectionIndex; + return this; + } +}; + +/// the default Config is the TOML file format +using ConfigTOML = ConfigBase; + +/// ConfigINI generates a "standard" INI compliant output +class ConfigINI : public ConfigTOML { + + public: + ConfigINI() { + commentChar = ';'; + arrayStart = '\0'; + arrayEnd = '\0'; + arraySeparator = ' '; + valueDelimiter = '='; + } +}; + + + +class Option; + +/// @defgroup validator_group Validators + +/// @brief Some validators that are provided +/// +/// These are simple `std::string(const std::string&)` validators that are useful. They return +/// a string if the validation fails. A custom struct is provided, as well, with the same user +/// semantics, but with the ability to provide a new type name. +/// @{ + +/// +class Validator { + protected: + /// This is the description function, if empty the description_ will be used + std::function desc_function_{[]() { return std::string{}; }}; + + /// This is the base function that is to be called. + /// Returns a string error message if validation fails. + std::function func_{[](std::string &) { return std::string{}; }}; + /// The name for search purposes of the Validator + std::string name_{}; + /// A Validator will only apply to an indexed value (-1 is all elements) + int application_index_ = -1; + /// Enable for Validator to allow it to be disabled if need be + bool active_{true}; + /// specify that a validator should not modify the input + bool non_modifying_{false}; + + Validator(std::string validator_desc, std::function func) + : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(func)) {} + + public: + Validator() = default; + /// Construct a Validator with just the description string + explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {} + /// Construct Validator from basic information + Validator(std::function op, std::string validator_desc, std::string validator_name = "") + : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)), + name_(std::move(validator_name)) {} + /// Set the Validator operation function + Validator &operation(std::function op) { + func_ = std::move(op); + return *this; + } + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(std::string &str) const; + + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(const std::string &str) const { + std::string value = str; + return (active_) ? func_(value) : std::string{}; + } + + /// Specify the type string + Validator &description(std::string validator_desc) { + desc_function_ = [validator_desc]() { return validator_desc; }; + return *this; + } + /// Specify the type string + CLI11_NODISCARD Validator description(std::string validator_desc) const; + + /// Generate type description information for the Validator + CLI11_NODISCARD std::string get_description() const { + if(active_) { + return desc_function_(); + } + return std::string{}; + } + /// Specify the type string + Validator &name(std::string validator_name) { + name_ = std::move(validator_name); + return *this; + } + /// Specify the type string + CLI11_NODISCARD Validator name(std::string validator_name) const { + Validator newval(*this); + newval.name_ = std::move(validator_name); + return newval; + } + /// Get the name of the Validator + CLI11_NODISCARD const std::string &get_name() const { return name_; } + /// Specify whether the Validator is active or not + Validator &active(bool active_val = true) { + active_ = active_val; + return *this; + } + /// Specify whether the Validator is active or not + CLI11_NODISCARD Validator active(bool active_val = true) const { + Validator newval(*this); + newval.active_ = active_val; + return newval; + } + + /// Specify whether the Validator can be modifying or not + Validator &non_modifying(bool no_modify = true) { + non_modifying_ = no_modify; + return *this; + } + /// Specify the application index of a validator + Validator &application_index(int app_index) { + application_index_ = app_index; + return *this; + } + /// Specify the application index of a validator + CLI11_NODISCARD Validator application_index(int app_index) const { + Validator newval(*this); + newval.application_index_ = app_index; + return newval; + } + /// Get the current value of the application index + CLI11_NODISCARD int get_application_index() const { return application_index_; } + /// Get a boolean if the validator is active + CLI11_NODISCARD bool get_active() const { return active_; } + + /// Get a boolean if the validator is allowed to modify the input returns true if it can modify the input + CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; } + + /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the + /// same. + Validator operator&(const Validator &other) const; + + /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the + /// same. + Validator operator|(const Validator &other) const; + + /// Create a validator that fails when a given validator succeeds + Validator operator!() const; + + private: + void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger); +}; + +/// Class wrapping some of the accessors of Validator +class CustomValidator : public Validator { + public: +}; +// The implementation of the built in validators is using the Validator class; +// the user is only expected to use the const (static) versions (since there's no setup). +// Therefore, this is in detail. +namespace detail { + +/// CLI enumeration of different file types +enum class path_type { nonexistent, file, directory }; + +/// get the type of the path from a file name +CLI11_INLINE path_type check_path(const char *file) noexcept; + +/// Check for an existing file (returns error message if check fails) +class ExistingFileValidator : public Validator { + public: + ExistingFileValidator(); +}; + +/// Check for an existing directory (returns error message if check fails) +class ExistingDirectoryValidator : public Validator { + public: + ExistingDirectoryValidator(); +}; + +/// Check for an existing path +class ExistingPathValidator : public Validator { + public: + ExistingPathValidator(); +}; + +/// Check for an non-existing path +class NonexistentPathValidator : public Validator { + public: + NonexistentPathValidator(); +}; + +/// Validate the given string is a legal ipv4 address +class IPV4Validator : public Validator { + public: + IPV4Validator(); +}; + +class EscapedStringTransformer : public Validator { + public: + EscapedStringTransformer(); +}; + +} // namespace detail + +// Static is not needed here, because global const implies static. + +/// Check for existing file (returns error message if check fails) +const detail::ExistingFileValidator ExistingFile; + +/// Check for an existing directory (returns error message if check fails) +const detail::ExistingDirectoryValidator ExistingDirectory; + +/// Check for an existing path +const detail::ExistingPathValidator ExistingPath; + +/// Check for an non-existing path +const detail::NonexistentPathValidator NonexistentPath; + +/// Check for an IP4 address +const detail::IPV4Validator ValidIPV4; + +/// convert escaped characters into their associated values +const detail::EscapedStringTransformer EscapedString; + +/// Validate the input as a particular type +template class TypeValidator : public Validator { + public: + explicit TypeValidator(const std::string &validator_name) + : Validator(validator_name, [](std::string &input_string) { + using CLI::detail::lexical_cast; + auto val = DesiredType(); + if(!lexical_cast(input_string, val)) { + return std::string("Failed parsing ") + input_string + " as a " + detail::type_name(); + } + return std::string(); + }) {} + TypeValidator() : TypeValidator(detail::type_name()) {} +}; + +/// Check for a number +const TypeValidator Number("NUMBER"); + +/// Modify a path if the file is a particular default location, can be used as Check or transform +/// with the error return optionally disabled +class FileOnDefaultPath : public Validator { + public: + explicit FileOnDefaultPath(std::string default_path, bool enableErrorReturn = true); +}; + +/// Produce a range (factory). Min and max are inclusive. +class Range : public Validator { + public: + /// This produces a range with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is not + /// needed to provide nice syntax for Range(a,b). + template + Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) { + if(validator_name.empty()) { + std::stringstream out; + out << detail::type_name() << " in [" << min_val << " - " << max_val << "]"; + description(out.str()); + } + + func_ = [min_val, max_val](std::string &input) { + using CLI::detail::lexical_cast; + T val; + bool converted = lexical_cast(input, val); + if((!converted) || (val < min_val || val > max_val)) { + std::stringstream out; + out << "Value " << input << " not in range ["; + out << min_val << " - " << max_val << "]"; + return out.str(); + } + return std::string{}; + }; + } + + /// Range of one value is 0 to value + template + explicit Range(T max_val, const std::string &validator_name = std::string{}) + : Range(static_cast(0), max_val, validator_name) {} +}; + +/// Check for a non negative number +const Range NonNegativeNumber((std::numeric_limits::max)(), "NONNEGATIVE"); + +/// Check for a positive valued number (val>0.0), ::min here is the smallest positive number +const Range PositiveNumber((std::numeric_limits::min)(), (std::numeric_limits::max)(), "POSITIVE"); + +/// Produce a bounded range (factory). Min and max are inclusive. +class Bound : public Validator { + public: + /// This bounds a value with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is not + /// needed to provide nice syntax for Range(a,b). + template Bound(T min_val, T max_val) { + std::stringstream out; + out << detail::type_name() << " bounded to [" << min_val << " - " << max_val << "]"; + description(out.str()); + + func_ = [min_val, max_val](std::string &input) { + using CLI::detail::lexical_cast; + T val; + bool converted = lexical_cast(input, val); + if(!converted) { + return std::string("Value ") + input + " could not be converted"; + } + if(val < min_val) + input = detail::to_string(min_val); + else if(val > max_val) + input = detail::to_string(max_val); + + return std::string{}; + }; + } + + /// Range of one value is 0 to value + template explicit Bound(T max_val) : Bound(static_cast(0), max_val) {} +}; + +namespace detail { +template ::type>::value, detail::enabler> = detail::dummy> +auto smart_deref(T value) -> decltype(*value) { + return *value; +} + +template < + typename T, + enable_if_t::type>::value, detail::enabler> = detail::dummy> +typename std::remove_reference::type &smart_deref(T &value) { + return value; +} +/// Generate a string representation of a set +template std::string generate_set(const T &set) { + using element_t = typename detail::element_type::type; + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(set), + [](const iteration_type_t &v) { return detail::pair_adaptor::first(v); }, + ",")); + out.push_back('}'); + return out; +} + +/// Generate a string representation of a map +template std::string generate_map(const T &map, bool key_only = false) { + using element_t = typename detail::element_type::type; + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(map), + [key_only](const iteration_type_t &v) { + std::string res{detail::to_string(detail::pair_adaptor::first(v))}; + + if(!key_only) { + res.append("->"); + res += detail::to_string(detail::pair_adaptor::second(v)); + } + return res; + }, + ",")); + out.push_back('}'); + return out; +} + +template struct has_find { + template + static auto test(int) -> decltype(std::declval().find(std::declval()), std::true_type()); + template static auto test(...) -> decltype(std::false_type()); + + static const auto value = decltype(test(0))::value; + using type = std::integral_constant; +}; + +/// A search function +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) -> std::pair { + using element_t = typename detail::element_type::type; + auto &setref = detail::smart_deref(set); + auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) { + return (detail::pair_adaptor::first(v) == val); + }); + return {(it != std::end(setref)), it}; +} + +/// A search function that uses the built in find function +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) -> std::pair { + auto &setref = detail::smart_deref(set); + auto it = setref.find(val); + return {(it != std::end(setref)), it}; +} + +/// A search function with a filter function +template +auto search(const T &set, const V &val, const std::function &filter_function) + -> std::pair { + using element_t = typename detail::element_type::type; + // do the potentially faster first search + auto res = search(set, val); + if((res.first) || (!(filter_function))) { + return res; + } + // if we haven't found it do the longer linear search with all the element translations + auto &setref = detail::smart_deref(set); + auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) { + V a{detail::pair_adaptor::first(v)}; + a = filter_function(a); + return (a == val); + }); + return {(it != std::end(setref)), it}; +} + +// the following suggestion was made by Nikita Ofitserov(@himikof) +// done in templates to prevent compiler warnings on negation of unsigned numbers + +/// Do a check for overflow on signed numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + if((a > 0) == (b > 0)) { + return ((std::numeric_limits::max)() / (std::abs)(a) < (std::abs)(b)); + } + return ((std::numeric_limits::min)() / (std::abs)(a) > -(std::abs)(b)); +} +/// Do a check for overflow on unsigned numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + return ((std::numeric_limits::max)() / a < b); +} + +/// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. +template typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { + if(a == 0 || b == 0 || a == 1 || b == 1) { + a *= b; + return true; + } + if(a == (std::numeric_limits::min)() || b == (std::numeric_limits::min)()) { + return false; + } + if(overflowCheck(a, b)) { + return false; + } + a *= b; + return true; +} + +/// Performs a *= b; if it doesn't equal infinity. Returns false otherwise. +template +typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { + T c = a * b; + if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) { + return false; + } + a = c; + return true; +} + +} // namespace detail +/// Verify items are in a set +class IsMember : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction using an initializer list + template + IsMember(std::initializer_list values, Args &&...args) + : IsMember(std::vector(values), std::forward(args)...) {} + + /// This checks to see if an item is in a set (empty function) + template explicit IsMember(T &&set) : IsMember(std::forward(set), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit IsMember(T set, F filter_function) { + + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + + using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones + // (const char * to std::string) + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + // This is the type name for help, it will take the current version of the set contents + desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); }; + + // This is the function that validates + // It stores a copy of the set pointer-like, so shared_ptr will stay alive + func_ = [set, filter_fn](std::string &input) { + using CLI::detail::lexical_cast; + local_item_t b; + if(!lexical_cast(input, b)) { + throw ValidationError(input); // name is added later + } + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(set, b, filter_fn); + if(res.first) { + // Make sure the version in the input string is identical to the one in the set + if(filter_fn) { + input = detail::value_string(detail::pair_adaptor::first(*(res.second))); + } + + // Return empty error string (success) + return std::string{}; + } + + // If you reach this point, the result was not found + return input + " not in " + detail::generate_set(detail::smart_deref(set)); + }; + } + + /// You can pass in as many filter functions as you like, they nest (string only currently) + template + IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) + : IsMember( + std::forward(set), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// definition of the default transformation object +template using TransformPairs = std::vector>; + +/// Translate named items to other or a value set +class Transformer : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction + template + Transformer(std::initializer_list> values, Args &&...args) + : Transformer(TransformPairs(values), std::forward(args)...) {} + + /// direct map of std::string to std::string + template explicit Transformer(T &&mapping) : Transformer(std::forward(mapping), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit Transformer(T mapping, F filter_function) { + + static_assert(detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones + // (const char * to std::string) + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + // This is the type name for help, it will take the current version of the set contents + desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); }; + + func_ = [mapping, filter_fn](std::string &input) { + using CLI::detail::lexical_cast; + local_item_t b; + if(!lexical_cast(input, b)) { + return std::string(); + // there is no possible way we can match anything in the mapping if we can't convert so just return + } + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if(res.first) { + input = detail::value_string(detail::pair_adaptor::second(*res.second)); + } + return std::string{}; + }; + } + + /// You can pass in as many filter functions as you like, they nest + template + Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) + : Transformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// translate named items to other or a value set +class CheckedTransformer : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction + template + CheckedTransformer(std::initializer_list> values, Args &&...args) + : CheckedTransformer(TransformPairs(values), std::forward(args)...) {} + + /// direct map of std::string to std::string + template explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit CheckedTransformer(T mapping, F filter_function) { + + static_assert(detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones + // (const char * to std::string) + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + auto tfunc = [mapping]() { + std::string out("value in "); + out += detail::generate_map(detail::smart_deref(mapping)) + " OR {"; + out += detail::join( + detail::smart_deref(mapping), + [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor::second(v)); }, + ","); + out.push_back('}'); + return out; + }; + + desc_function_ = tfunc; + + func_ = [mapping, tfunc, filter_fn](std::string &input) { + using CLI::detail::lexical_cast; + local_item_t b; + bool converted = lexical_cast(input, b); + if(converted) { + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if(res.first) { + input = detail::value_string(detail::pair_adaptor::second(*res.second)); + return std::string{}; + } + } + for(const auto &v : detail::smart_deref(mapping)) { + auto output_string = detail::value_string(detail::pair_adaptor::second(v)); + if(output_string == input) { + return std::string(); + } + } + + return "Check " + input + " " + tfunc() + " FAILED"; + }; + } + + /// You can pass in as many filter functions as you like, they nest + template + CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) + : CheckedTransformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// Helper function to allow ignore_case to be passed to IsMember or Transform +inline std::string ignore_case(std::string item) { return detail::to_lower(item); } + +/// Helper function to allow ignore_underscore to be passed to IsMember or Transform +inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); } + +/// Helper function to allow checks to ignore spaces to be passed to IsMember or Transform +inline std::string ignore_space(std::string item) { + item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item)); + item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item)); + return item; +} + +/// Multiply a number by a factor using given mapping. +/// Can be used to write transforms for SIZE or DURATION inputs. +/// +/// Example: +/// With mapping = `{"b"->1, "kb"->1024, "mb"->1024*1024}` +/// one can recognize inputs like "100", "12kb", "100 MB", +/// that will be automatically transformed to 100, 14448, 104857600. +/// +/// Output number type matches the type in the provided mapping. +/// Therefore, if it is required to interpret real inputs like "0.42 s", +/// the mapping should be of a type or . +class AsNumberWithUnit : public Validator { + public: + /// Adjust AsNumberWithUnit behavior. + /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched. + /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError + /// if UNIT_REQUIRED is set and unit literal is not found. + enum Options { + CASE_SENSITIVE = 0, + CASE_INSENSITIVE = 1, + UNIT_OPTIONAL = 0, + UNIT_REQUIRED = 2, + DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL + }; + + template + explicit AsNumberWithUnit(std::map mapping, + Options opts = DEFAULT, + const std::string &unit_name = "UNIT") { + description(generate_description(unit_name, opts)); + validate_mapping(mapping, opts); + + // transform function + func_ = [mapping, opts](std::string &input) -> std::string { + Number num{}; + + detail::rtrim(input); + if(input.empty()) { + throw ValidationError("Input is empty"); + } + + // Find split position between number and prefix + auto unit_begin = input.end(); + while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) { + --unit_begin; + } + + std::string unit{unit_begin, input.end()}; + input.resize(static_cast(std::distance(input.begin(), unit_begin))); + detail::trim(input); + + if(opts & UNIT_REQUIRED && unit.empty()) { + throw ValidationError("Missing mandatory unit"); + } + if(opts & CASE_INSENSITIVE) { + unit = detail::to_lower(unit); + } + if(unit.empty()) { + using CLI::detail::lexical_cast; + if(!lexical_cast(input, num)) { + throw ValidationError(std::string("Value ") + input + " could not be converted to " + + detail::type_name()); + } + // No need to modify input if no unit passed + return {}; + } + + // find corresponding factor + auto it = mapping.find(unit); + if(it == mapping.end()) { + throw ValidationError(unit + + " unit not recognized. " + "Allowed values: " + + detail::generate_map(mapping, true)); + } + + if(!input.empty()) { + using CLI::detail::lexical_cast; + bool converted = lexical_cast(input, num); + if(!converted) { + throw ValidationError(std::string("Value ") + input + " could not be converted to " + + detail::type_name()); + } + // perform safe multiplication + bool ok = detail::checked_multiply(num, it->second); + if(!ok) { + throw ValidationError(detail::to_string(num) + " multiplied by " + unit + + " factor would cause number overflow. Use smaller value."); + } + } else { + num = static_cast(it->second); + } + + input = detail::to_string(num); + + return {}; }; + } + + private: + /// Check that mapping contains valid units. + /// Update mapping for CASE_INSENSITIVE mode. + template static void validate_mapping(std::map &mapping, Options opts) { + for(auto &kv : mapping) { + if(kv.first.empty()) { + throw ValidationError("Unit must not be empty."); + } + if(!detail::isalpha(kv.first)) { + throw ValidationError("Unit must contain only letters."); + } + } + + // make all units lowercase if CASE_INSENSITIVE + if(opts & CASE_INSENSITIVE) { + std::map lower_mapping; + for(auto &kv : mapping) { + auto s = detail::to_lower(kv.first); + if(lower_mapping.count(s)) { + throw ValidationError(std::string("Several matching lowercase unit representations are found: ") + + s); + } + lower_mapping[detail::to_lower(kv.first)] = kv.second; + } + mapping = std::move(lower_mapping); + } + } + + /// Generate description like this: NUMBER [UNIT] + template static std::string generate_description(const std::string &name, Options opts) { + std::stringstream out; + out << detail::type_name() << ' '; + if(opts & UNIT_REQUIRED) { + out << name; + } else { + out << '[' << name << ']'; + } + return out.str(); + } +}; + +inline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a, const AsNumberWithUnit::Options &b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +/// Converts a human-readable size string (with unit literal) to uin64_t size. +/// Example: +/// "100" => 100 +/// "1 b" => 100 +/// "10Kb" => 10240 // you can configure this to be interpreted as kilobyte (*1000) or kibibyte (*1024) +/// "10 KB" => 10240 +/// "10 kb" => 10240 +/// "10 kib" => 10240 // *i, *ib are always interpreted as *bibyte (*1024) +/// "10kb" => 10240 +/// "2 MB" => 2097152 +/// "2 EiB" => 2^61 // Units up to exibyte are supported +class AsSizeValue : public AsNumberWithUnit { + public: + using result_t = std::uint64_t; + + /// If kb_is_1000 is true, + /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024 + /// (same applies to higher order units as well). + /// Otherwise, interpret all literals as factors of 1024. + /// The first option is formally correct, but + /// the second interpretation is more wide-spread + /// (see https://en.wikipedia.org/wiki/Binary_prefix). + explicit AsSizeValue(bool kb_is_1000); + + private: + /// Get mapping + static std::map init_mapping(bool kb_is_1000); + + /// Cache calculated mapping + static std::map get_mapping(bool kb_is_1000); +}; + +namespace detail { +/// Split a string into a program name and command line arguments +/// the string is assumed to contain a file name followed by other arguments +/// the return value contains is a pair with the first argument containing the program name and the second +/// everything else. +CLI11_INLINE std::pair split_program_name(std::string commandline); + +} // namespace detail +/// @} + + + + +CLI11_INLINE std::string Validator::operator()(std::string &str) const { + std::string retstring; + if(active_) { + if(non_modifying_) { + std::string value = str; + retstring = func_(value); + } else { + retstring = func_(str); + } + } + return retstring; +} + +CLI11_NODISCARD CLI11_INLINE Validator Validator::description(std::string validator_desc) const { + Validator newval(*this); + newval.desc_function_ = [validator_desc]() { return validator_desc; }; + return newval; +} + +CLI11_INLINE Validator Validator::operator&(const Validator &other) const { + Validator newval; + + newval._merge_description(*this, other, " AND "); + + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; + + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if(!s1.empty() && !s2.empty()) + return std::string("(") + s1 + ") AND (" + s2 + ")"; + return s1 + s2; + }; + + newval.active_ = active_ && other.active_; + newval.application_index_ = application_index_; + return newval; +} + +CLI11_INLINE Validator Validator::operator|(const Validator &other) const { + Validator newval; + + newval._merge_description(*this, other, " OR "); + + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; + + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if(s1.empty() || s2.empty()) + return std::string(); + + return std::string("(") + s1 + ") OR (" + s2 + ")"; + }; + newval.active_ = active_ && other.active_; + newval.application_index_ = application_index_; + return newval; +} + +CLI11_INLINE Validator Validator::operator!() const { + Validator newval; + const std::function &dfunc1 = desc_function_; + newval.desc_function_ = [dfunc1]() { + auto str = dfunc1(); + return (!str.empty()) ? std::string("NOT ") + str : std::string{}; + }; + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + + newval.func_ = [f1, dfunc1](std::string &test) -> std::string { + std::string s1 = f1(test); + if(s1.empty()) { + return std::string("check ") + dfunc1() + " succeeded improperly"; + } + return std::string{}; + }; + newval.active_ = active_; + newval.application_index_ = application_index_; + return newval; +} + +CLI11_INLINE void +Validator::_merge_description(const Validator &val1, const Validator &val2, const std::string &merger) { + + const std::function &dfunc1 = val1.desc_function_; + const std::function &dfunc2 = val2.desc_function_; + + desc_function_ = [=]() { + std::string f1 = dfunc1(); + std::string f2 = dfunc2(); + if((f1.empty()) || (f2.empty())) { + return f1 + f2; + } + return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')'; + }; +} + +namespace detail { + +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +CLI11_INLINE path_type check_path(const char *file) noexcept { + std::error_code ec; + auto stat = std::filesystem::status(to_path(file), ec); + if(ec) { + return path_type::nonexistent; + } + switch(stat.type()) { + case std::filesystem::file_type::none: // LCOV_EXCL_LINE + case std::filesystem::file_type::not_found: + return path_type::nonexistent; // LCOV_EXCL_LINE + case std::filesystem::file_type::directory: + return path_type::directory; + case std::filesystem::file_type::symlink: + case std::filesystem::file_type::block: + case std::filesystem::file_type::character: + case std::filesystem::file_type::fifo: + case std::filesystem::file_type::socket: + case std::filesystem::file_type::regular: + case std::filesystem::file_type::unknown: + default: + return path_type::file; + } +} +#else +CLI11_INLINE path_type check_path(const char *file) noexcept { +#if defined(_MSC_VER) + struct __stat64 buffer; + if(_stat64(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; + } +#else + struct stat buffer; + if(stat(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; + } +#endif + return path_type::nonexistent; +} +#endif + +CLI11_INLINE ExistingFileValidator::ExistingFileValidator() : Validator("FILE") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistent) { + return "File does not exist: " + filename; + } + if(path_result == path_type::directory) { + return "File is actually a directory: " + filename; + } + return std::string(); + }; +} + +CLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator() : Validator("DIR") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistent) { + return "Directory does not exist: " + filename; + } + if(path_result == path_type::file) { + return "Directory is actually a file: " + filename; + } + return std::string(); + }; +} + +CLI11_INLINE ExistingPathValidator::ExistingPathValidator() : Validator("PATH(existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistent) { + return "Path does not exist: " + filename; + } + return std::string(); + }; +} + +CLI11_INLINE NonexistentPathValidator::NonexistentPathValidator() : Validator("PATH(non-existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result != path_type::nonexistent) { + return "Path already exists: " + filename; + } + return std::string(); + }; +} + +CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") { + func_ = [](std::string &ip_addr) { + auto result = CLI::detail::split(ip_addr, '.'); + if(result.size() != 4) { + return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')'; + } + int num = 0; + for(const auto &var : result) { + using CLI::detail::lexical_cast; + bool retval = lexical_cast(var, num); + if(!retval) { + return std::string("Failed parsing number (") + var + ')'; + } + if(num < 0 || num > 255) { + return std::string("Each IP number must be between 0 and 255 ") + var; + } + } + return std::string{}; + }; +} + +CLI11_INLINE EscapedStringTransformer::EscapedStringTransformer() { + func_ = [](std::string &str) { + try { + if(str.size() > 1 && (str.front() == '\"' || str.front() == '\'' || str.front() == '`') && + str.front() == str.back()) { + process_quoted_string(str); + } else if(str.find_first_of('\\') != std::string::npos) { + if(detail::is_binary_escaped_string(str)) { + str = detail::extract_binary_string(str); + } else { + str = remove_escaped_characters(str); + } + } + return std::string{}; + } catch(const std::invalid_argument &ia) { + return std::string(ia.what()); + } + }; +} +} // namespace detail + +CLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path, bool enableErrorReturn) + : Validator("FILE") { + func_ = [default_path, enableErrorReturn](std::string &filename) { + auto path_result = detail::check_path(filename.c_str()); + if(path_result == detail::path_type::nonexistent) { + std::string test_file_path = default_path; + if(default_path.back() != '/' && default_path.back() != '\\') { + // Add folder separator + test_file_path += '/'; + } + test_file_path.append(filename); + path_result = detail::check_path(test_file_path.c_str()); + if(path_result == detail::path_type::file) { + filename = test_file_path; + } else { + if(enableErrorReturn) { + return "File does not exist: " + filename; + } + } + } + return std::string{}; + }; +} + +CLI11_INLINE AsSizeValue::AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) { + if(kb_is_1000) { + description("SIZE [b, kb(=1000b), kib(=1024b), ...]"); + } else { + description("SIZE [b, kb(=1024b), ...]"); + } +} + +CLI11_INLINE std::map AsSizeValue::init_mapping(bool kb_is_1000) { + std::map m; + result_t k_factor = kb_is_1000 ? 1000 : 1024; + result_t ki_factor = 1024; + result_t k = 1; + result_t ki = 1; + m["b"] = 1; + for(std::string p : {"k", "m", "g", "t", "p", "e"}) { + k *= k_factor; + ki *= ki_factor; + m[p] = k; + m[p + "b"] = k; + m[p + "i"] = ki; + m[p + "ib"] = ki; + } + return m; +} + +CLI11_INLINE std::map AsSizeValue::get_mapping(bool kb_is_1000) { + if(kb_is_1000) { + static auto m = init_mapping(true); + return m; + } + static auto m = init_mapping(false); + return m; +} + +namespace detail { + +CLI11_INLINE std::pair split_program_name(std::string commandline) { + // try to determine the programName + std::pair vals; + trim(commandline); + auto esp = commandline.find_first_of(' ', 1); + while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) { + esp = commandline.find_first_of(' ', esp + 1); + if(esp == std::string::npos) { + // if we have reached the end and haven't found a valid file just assume the first argument is the + // program name + if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') { + bool embeddedQuote = false; + auto keyChar = commandline[0]; + auto end = commandline.find_first_of(keyChar, 1); + while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes + end = commandline.find_first_of(keyChar, end + 1); + embeddedQuote = true; + } + if(end != std::string::npos) { + vals.first = commandline.substr(1, end - 1); + esp = end + 1; + if(embeddedQuote) { + vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar)); + } + } else { + esp = commandline.find_first_of(' ', 1); + } + } else { + esp = commandline.find_first_of(' ', 1); + } - /// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, - /// vectors, and enumerations - template - struct uncommon_type - { - using type = typename std::conditional::value && !std::is_integral::value && - !std::is_assignable::value && - !std::is_constructible::value && !is_vector::value && - !std::is_enum::value, - std::true_type, - std::false_type>::type; - static constexpr bool value = type::value; - }; + break; + } + } + if(vals.first.empty()) { + vals.first = commandline.substr(0, esp); + rtrim(vals.first); + } - /// Assignable from double or int - template - struct classify_object::value && type_count::value == 1 && - is_direct_constructible::value && - is_direct_constructible::value>::type> - { - static constexpr object_category value{ object_category::number_constructible }; - }; + // strip the program name + vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1) : std::string{}; + ltrim(vals.second); + return vals; +} - /// Assignable from int - template - struct classify_object::value && type_count::value == 1 && - !is_direct_constructible::value && - is_direct_constructible::value>::type> - { - static constexpr object_category value{ object_category::integer_constructible }; - }; +} // namespace detail +/// @} - /// Assignable from double - template - struct classify_object::value && type_count::value == 1 && - is_direct_constructible::value && - !is_direct_constructible::value>::type> - { - static constexpr object_category value{ object_category::double_constructible }; - }; - /// Tuple type - template - struct classify_object::value >= 2 && !is_vector::value) || - (is_tuple_like::value && uncommon_type::value && - !is_direct_constructible::value && - !is_direct_constructible::value)>::type> - { - static constexpr object_category value{ object_category::tuple_value }; - }; - /// Vector type - template - struct classify_object::value>::type> - { - static constexpr object_category value{ object_category::vector_value }; - }; - // Type name print +class Option; +class App; - /// Was going to be based on - /// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template - /// But this is cleaner and works better in this case +/// This enum signifies the type of help requested +/// +/// This is passed in by App; all user classes must accept this as +/// the second argument. - template::value == object_category::integral_value || - classify_object::value == object_category::integer_constructible, - detail::enabler> = detail::dummy> - constexpr const char* type_name() - { - return "INT"; - } +enum class AppFormatMode { + Normal, ///< The normal, detailed help + All, ///< A fully expanded help + Sub, ///< Used when printed as part of expanded subcommand +}; - template::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> - constexpr const char* type_name() - { - return "UINT"; - } +/// This is the minimum requirements to run a formatter. +/// +/// A user can subclass this is if they do not care at all +/// about the structure in CLI::Formatter. +class FormatterBase { + protected: + /// @name Options + ///@{ - template::value == object_category::floating_point || - classify_object::value == object_category::number_constructible || - classify_object::value == object_category::double_constructible, - detail::enabler> = detail::dummy> - constexpr const char* type_name() - { - return "FLOAT"; - } + /// The width of the first column + std::size_t column_width_{30}; - /// Print name for enumeration types - template::value == object_category::enumeration, detail::enabler> = detail::dummy> - constexpr const char* type_name() - { - return "ENUM"; - } + /// @brief The required help printout labels (user changeable) + /// Values are Needs, Excludes, etc. + std::map labels_{}; - /// Print name for enumeration types - template::value == object_category::boolean_value, detail::enabler> = detail::dummy> - constexpr const char* type_name() - { - return "BOOLEAN"; - } + ///@} + /// @name Basic + ///@{ - /// Print for all other types - template::value >= object_category::string_assignable, detail::enabler> = detail::dummy> - constexpr const char* type_name() - { - return "TEXT"; - } + public: + FormatterBase() = default; + FormatterBase(const FormatterBase &) = default; + FormatterBase(FormatterBase &&) = default; + FormatterBase &operator=(const FormatterBase &) = default; + FormatterBase &operator=(FormatterBase &&) = default; - /// Print name for single element tuple types - template::value == object_category::tuple_value && type_count::value == 1, - detail::enabler> = detail::dummy> - inline std::string type_name() - { - return type_name::type>(); - } + /// Adding a destructor in this form to work around bug in GCC 4.7 + virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default) - /// Empty string if the index > tuple size - template - inline typename std::enable_if::value, std::string>::type tuple_name() - { - return std::string{}; - } + /// This is the key method that puts together help + virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; - /// Recursively generate the tuple type name - template - inline typename std::enable_if < I::value, std::string>::type tuple_name() - { - std::string str = std::string(type_name::type>()) + ',' + tuple_name(); - if (str.back() == ',') - str.pop_back(); - return str; - } - - /// Print type name for tuples with 2 or more elements - template::value == object_category::tuple_value && type_count::value >= 2, - detail::enabler> = detail::dummy> - std::string type_name() - { - auto tname = std::string(1, '[') + tuple_name(); - tname.push_back(']'); - return tname; - } - - /// This one should not be used normally, since vector types print the internal type - template::value == object_category::vector_value, detail::enabler> = detail::dummy> - inline std::string type_name() - { - return type_name(); - } - - // Lexical cast - - /// Convert a flag into an integer value typically binary flags - inline std::int64_t to_flag_value(std::string val) - { - static const std::string trueString("true"); - static const std::string falseString("false"); - if (val == trueString) - { - return 1; - } - if (val == falseString) - { - return -1; - } - val = detail::to_lower(val); - std::int64_t ret; - if (val.size() == 1) - { - if (val[0] >= '1' && val[0] <= '9') - { - return (static_cast(val[0]) - '0'); - } - switch (val[0]) - { - case '0': - case 'f': - case 'n': - case '-': - ret = -1; - break; - case 't': - case 'y': - case '+': - ret = 1; - break; - default: - throw std::invalid_argument("unrecognized character"); - } - return ret; - } - if (val == trueString || val == "on" || val == "yes" || val == "enable") - { - ret = 1; - } - else if (val == falseString || val == "off" || val == "no" || val == "disable") - { - ret = -1; - } - else - { - ret = std::stoll(val); - } - return ret; - } - - /// Signed integers - template::value == object_category::integral_value, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - try - { - std::size_t n = 0; - std::int64_t output_ll = std::stoll(input, &n, 0); - output = static_cast(output_ll); - return n == input.size() && static_cast(output) == output_ll; - } - catch (const std::invalid_argument&) - { - return false; - } - catch (const std::out_of_range&) - { - return false; - } - } + ///@} + /// @name Setters + ///@{ - /// Unsigned integers - template::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - if (!input.empty() && input.front() == '-') - return false; // std::stoull happily converts negative values to junk without any errors. + /// Set the "REQUIRED" label + void label(std::string key, std::string val) { labels_[key] = val; } - try - { - std::size_t n = 0; - std::uint64_t output_ll = std::stoull(input, &n, 0); - output = static_cast(output_ll); - return n == input.size() && static_cast(output) == output_ll; - } - catch (const std::invalid_argument&) - { - return false; - } - catch (const std::out_of_range&) - { - return false; - } - } + /// Set the column width + void column_width(std::size_t val) { column_width_ = val; } - /// Boolean values - template::value == object_category::boolean_value, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - try - { - auto out = to_flag_value(input); - output = (out > 0); - return true; - } - catch (const std::invalid_argument&) - { - return false; - } - catch (const std::out_of_range&) - { - // if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still - // valid all we care about the sign - output = (input[0] != '-'); - return true; - } - } + ///@} + /// @name Getters + ///@{ - /// Floats - template::value == object_category::floating_point, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - try - { - std::size_t n = 0; - output = static_cast(std::stold(input, &n)); - return n == input.size(); - } - catch (const std::invalid_argument&) - { - return false; - } - catch (const std::out_of_range&) - { - return false; - } - } + /// Get the current value of a name (REQUIRED, etc.) + CLI11_NODISCARD std::string get_label(std::string key) const { + if(labels_.find(key) == labels_.end()) + return key; + return labels_.at(key); + } - /// String and similar direct assignment - template::value == object_category::string_assignable, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - output = input; - return true; - } + /// Get the current column width + CLI11_NODISCARD std::size_t get_column_width() const { return column_width_; } - /// String and similar constructible and copy assignment - template< - typename T, - enable_if_t::value == object_category::string_constructible, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - output = T(input); - return true; - } + ///@} +}; - /// Enumerations - template::value == object_category::enumeration, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - typename std::underlying_type::type val; - bool retval = detail::lexical_cast(input, val); - if (!retval) - { - return false; - } - output = static_cast(val); - return true; - } +/// This is a specialty override for lambda functions +class FormatterLambda final : public FormatterBase { + using funct_t = std::function; - /// Assignable from double or int - template< - typename T, - enable_if_t::value == object_category::number_constructible, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - int val; - if (lexical_cast(input, val)) - { - output = T(val); - return true; - } - else - { - double dval; - if (lexical_cast(input, dval)) - { - output = T{ dval }; - return true; - } - } - return from_stream(input, output); - } + /// The lambda to hold and run + funct_t lambda_; - /// Assignable from int - template< - typename T, - enable_if_t::value == object_category::integer_constructible, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - int val; - if (lexical_cast(input, val)) - { - output = T(val); - return true; - } - return from_stream(input, output); - } + public: + /// Create a FormatterLambda with a lambda function + explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} - /// Assignable from double - template< - typename T, - enable_if_t::value == object_category::double_constructible, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - double val; - if (lexical_cast(input, val)) - { - output = T{ val }; - return true; - } - return from_stream(input, output); - } - - /// Non-string parsable by a stream - template::value == object_category::other, detail::enabler> = detail::dummy> - bool lexical_cast(const std::string& input, T& output) - { - static_assert(is_istreamable::value, - "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it " - "is convertible from another type use the add_option(...) with XC being the known type"); - return from_stream(input, output); - } - - /// Assign a value through lexical cast operations - template< - typename T, - typename XC, - enable_if_t::value && (classify_object::value == object_category::string_assignable || - classify_object::value == object_category::string_constructible), - detail::enabler> = detail::dummy> - bool lexical_assign(const std::string& input, T& output) - { - return lexical_cast(input, output); - } - - /// Assign a value through lexical cast operations - template::value && classify_object::value != object_category::string_assignable && - classify_object::value != object_category::string_constructible, - detail::enabler> = detail::dummy> - bool lexical_assign(const std::string& input, T& output) - { - if (input.empty()) - { - output = T{}; - return true; - } - return lexical_cast(input, output); - } - - /// Assign a value converted from a string in lexical cast to the output value directly - template< - typename T, - typename XC, - enable_if_t::value && std::is_assignable::value, detail::enabler> = detail::dummy> - bool lexical_assign(const std::string& input, T& output) - { - XC val{}; - bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; - if (parse_result) - { - output = val; - } - return parse_result; - } - - /// Assign a value from a lexical cast through constructing a value and move assigning it - template::value && !std::is_assignable::value && - std::is_move_assignable::value, - detail::enabler> = detail::dummy> - bool lexical_assign(const std::string& input, T& output) - { - XC val{}; - bool parse_result = input.empty() ? true : lexical_cast(input, val); - if (parse_result) - { - output = T(val); // use () form of constructor to allow some implicit conversions - } - return parse_result; - } - /// Lexical conversion if there is only one element - template< - typename T, - typename XC, - enable_if_t::value && !is_tuple_like::value && !is_vector::value && !is_vector::value, - detail::enabler> = detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - return lexical_assign(strings[0], output); - } - - /// Lexical conversion if there is only one element but the conversion type is for two call a two element constructor - template::value == 1 && type_count::value == 2, detail::enabler> = detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - typename std::tuple_element<0, XC>::type v1; - typename std::tuple_element<1, XC>::type v2; - bool retval = lexical_assign(strings[0], v1); - if (strings.size() > 1) - { - retval = retval && lexical_assign(strings[1], v2); - } - if (retval) - { - output = T{ v1, v2 }; - } - return retval; - } + /// Adding a destructor (mostly to make GCC 4.7 happy) + ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default) - /// Lexical conversion of a vector types - template::value == expected_max_vector_size && - expected_count::value == expected_max_vector_size && type_count::value == 1, - detail::enabler> = detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - output.clear(); - output.reserve(strings.size()); - for (const auto& elem : strings) - { - output.emplace_back(); - bool retval = lexical_assign(elem, output.back()); - if (!retval) - { - return false; - } - } - return (!output.empty()); - } - - /// Lexical conversion of a vector types with type size of two - template::value == expected_max_vector_size && - expected_count::value == expected_max_vector_size && type_count::value == 2, - detail::enabler> = detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - output.clear(); - for (std::size_t ii = 0; ii < strings.size(); ii += 2) - { - typename std::tuple_element<0, typename XC::value_type>::type v1; - typename std::tuple_element<1, typename XC::value_type>::type v2; - bool retval = lexical_assign(strings[ii], v1); - if (strings.size() > ii + 1) - { - retval = retval && lexical_assign(strings[ii + 1], v2); - } - if (retval) - { - output.emplace_back(v1, v2); - } - else - { - return false; - } - } - return (!output.empty()); - } + /// This will simply call the lambda function + std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { + return lambda_(app, name, mode); + } +}; + +/// This is the default Formatter for CLI11. It pretty prints help output, and is broken into quite a few +/// overridable methods, to be highly customizable with minimal effort. +class Formatter : public FormatterBase { + public: + Formatter() = default; + Formatter(const Formatter &) = default; + Formatter(Formatter &&) = default; + Formatter &operator=(const Formatter &) = default; + Formatter &operator=(Formatter &&) = default; + + /// @name Overridables + ///@{ + + /// This prints out a group of options with title + /// + CLI11_NODISCARD virtual std::string + make_group(std::string group, bool is_positional, std::vector opts) const; - /// Conversion to a vector type using a particular single type as the conversion type - template::value == expected_max_vector_size) && (expected_count::value == 1) && - (type_count::value == 1), - detail::enabler> = detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - bool retval = true; - output.clear(); - output.reserve(strings.size()); - for (const auto& elem : strings) - { - output.emplace_back(); - retval = retval && lexical_assign(elem, output.back()); - } - return (!output.empty()) && retval; - } - // This one is last since it can call other lexical_conversion functions - /// Lexical conversion if there is only one element but the conversion type is a vector - template::value && !is_vector::value && is_vector::value, detail::enabler> = - detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - if (strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) - { - XC val; - auto retval = lexical_conversion(strings, val); - output = T{ val }; - return retval; - } - output = T{}; - return true; - } + /// This prints out just the positionals "group" + virtual std::string make_positionals(const App *app) const; - /// function template for converting tuples if the static Index is greater than the tuple size - template - inline typename std::enable_if= type_count::value, bool>::type tuple_conversion(const std::vector&, - T&) - { - return true; - } - /// Tuple conversion operation - template - inline typename std::enable_if < - I::value, bool>::type tuple_conversion(const std::vector& strings, T& output) - { - bool retval = true; - if (strings.size() > I) - { - retval = retval && lexical_assign::type, - typename std::conditional::value, - typename std::tuple_element::type, - XC>::type>(strings[I], std::get(output)); - } - retval = retval && tuple_conversion(strings, output); - return retval; - } + /// This prints out all the groups of options + std::string make_groups(const App *app, AppFormatMode mode) const; - /// Conversion for tuples - template::value, detail::enabler> = detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - static_assert( - !is_tuple_like::value || type_count::value == type_count::value, - "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); - return tuple_conversion(strings, output); - } - - /// Lexical conversion of a vector types with type_size >2 - template::value == expected_max_vector_size && - expected_count::value == expected_max_vector_size && (type_count::value > 2), - detail::enabler> = detail::dummy> - bool lexical_conversion(const std::vector& strings, T& output) - { - bool retval = true; - output.clear(); - std::vector temp; - std::size_t ii = 0; - std::size_t icount = 0; - std::size_t xcm = type_count::value; - while (ii < strings.size()) - { - temp.push_back(strings[ii]); - ++ii; - ++icount; - if (icount == xcm || temp.back().empty()) - { - if (static_cast(xcm) == expected_max_vector_size) - { - temp.pop_back(); - } - output.emplace_back(); - retval = retval && lexical_conversion(temp, output.back()); - temp.clear(); - if (!retval) - { - return false; - } - icount = 0; - } - } - return retval; - } - /// Sum a vector of flag representations - /// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is - /// by - /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most - /// common true and false strings then uses stoll to convert the rest for summing - template::value && std::is_unsigned::value, detail::enabler> = detail::dummy> - void sum_flag_vector(const std::vector& flags, T& output) - { - std::int64_t count{ 0 }; - for (auto& flag : flags) - { - count += detail::to_flag_value(flag); - } - output = (count > 0) ? static_cast(count) : T{ 0 }; - } - - /// Sum a vector of flag representations - /// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is - /// by - /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most - /// common true and false strings then uses stoll to convert the rest for summing - template::value && std::is_signed::value, detail::enabler> = detail::dummy> - void sum_flag_vector(const std::vector& flags, T& output) - { - std::int64_t count{ 0 }; - for (auto& flag : flags) - { - count += detail::to_flag_value(flag); - } - output = static_cast(count); - } - - } // namespace detail -} // namespace CLI + /// This prints out all the subcommands + virtual std::string make_subcommands(const App *app, AppFormatMode mode) const; -// From Split.hpp: - -namespace CLI -{ - namespace detail - { - // Returns false if not a short option. Otherwise, sets opt name and rest and returns true - inline bool split_short(const std::string& current, std::string& name, std::string& rest) - { - if (current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) - { - name = current.substr(1, 1); - rest = current.substr(2); - return true; - } - return false; + /// This prints out a subcommand + virtual std::string make_subcommand(const App *sub) const; + + /// This prints out a subcommand in help-all + virtual std::string make_expanded(const App *sub) const; + + /// This prints out all the groups of options + virtual std::string make_footer(const App *app) const; + + /// This displays the description line + virtual std::string make_description(const App *app) const; + + /// This displays the usage line + virtual std::string make_usage(const App *app, std::string name) const; + + /// This puts everything together + std::string make_help(const App * /*app*/, std::string, AppFormatMode) const override; + + ///@} + /// @name Options + ///@{ + + /// This prints out an option help line, either positional or optional form + virtual std::string make_option(const Option *opt, bool is_positional) const { + std::stringstream out; + detail::format_help( + out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_); + return out.str(); + } + + /// @brief This is the name part of an option, Default: left column + virtual std::string make_option_name(const Option *, bool) const; + + /// @brief This is the options part of the name, Default: combined into left column + virtual std::string make_option_opts(const Option *) const; + + /// @brief This is the description. Default: Right column, on new line if left column too large + virtual std::string make_option_desc(const Option *) const; + + /// @brief This is used to print the name on the USAGE line + virtual std::string make_option_usage(const Option *opt) const; + + ///@} +}; + + + + +using results_t = std::vector; +/// callback function definition +using callback_t = std::function; + +class Option; +class App; + +using Option_p = std::unique_ptr + + Vollfarb-Emoji + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Wenn diese Option aktiviert ist, werden Emojis in Vollfarbe angezeigt. + A longer description of the "Profile_EnableColorGlyphs" toggle. + Schriftstärke Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index 4a5900f3ff5..fd1ac2ce47b 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -914,6 +914,14 @@ Cuando está habilitado, el terminal dibuja glifos personalizados para los caracteres de dibujo de elementos de bloque y cuadros, en lugar de usar la fuente. Esta característica solo funciona cuando la aceleración de GPU está disponible. A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + Emoji a todo color + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Cuando se habilita, los emojis se muestran a todo color. + A longer description of the "Profile_EnableColorGlyphs" toggle. + Espesor de la fuente Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index 22b940d8bba..18484d3a24f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -914,6 +914,14 @@ Lorsque cette option est activée, le terminal dessine des glyphes personnalisés pour les éléments de bloc et les caractères de dessin de boîte au lieu d’utiliser la police. Cette fonctionnalité ne fonctionne que lorsque l’accélération GPU est disponible. A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + Emoji en couleurs + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Lorsque cette option est activée, les emojis sont affichés en couleurs. + A longer description of the "Profile_EnableColorGlyphs" toggle. + Épaisseur de la police Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 6b6da3860cc..14e06c19d18 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -914,6 +914,14 @@ Se abilitata, il terminale disegna glifi personalizzati per i caratteri di disegno a blocchi di elementi e caselle anziché usare il tipo di carattere. Questa funzionalità funziona solo quando è disponibile l'accelerazione GPU. A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + Emoji a colori + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Se questa opzione è abilitata, le emoji sono visualizzate a colori. + A longer description of the "Profile_EnableColorGlyphs" toggle. + Spessore del carattere Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index 8eb06b4c57b..b3b67ed772f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -914,6 +914,14 @@ 有効にすると、ターミナルは、フォントを使用する代わりに、ブロック要素とボックス描画文字のカスタム グリフを描画します。この機能は、GPU アクセラレーションが使用可能な場合にのみ機能します。 A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + フル カラーの絵文字 + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + 有効にすると、絵文字がフル カラーで表示されます。 + A longer description of the "Profile_EnableColorGlyphs" toggle. + フォントの太さ Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index 1048e2a696a..d3a2cf1d262 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -914,6 +914,14 @@ 사용하도록 설정하면 터미널은 글꼴을 사용하는 대신 블록 요소 및 상자 그리기 문자에 대한 사용자 지정 문자 모양을 그립니다. 이 기능은 GPU 가속을 사용할 수 있는 경우에만 작동합니다. A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + 풀 컬러 이모지 + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + 사용하도록 설정하면 이모지가 풀 컬러로 표시됩니다. + A longer description of the "Profile_EnableColorGlyphs" toggle. + 글꼴 두께 Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index 31602a7865b..86193e7bf62 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -914,6 +914,14 @@ Quando habilitado, o terminal desenha glifos personalizados para caracteres de desenho de elemento de bloco e caixa em vez de usar a fonte. Esse recurso só funciona quando a Aceleração de GPU está disponível. A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + Emoji colorido + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Quando habilitado, o Emoji é exibido em cores. + A longer description of the "Profile_EnableColorGlyphs" toggle. + Peso da fonte Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw index a89ec711b43..d345810049d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw @@ -118,249 +118,249 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - €г℮äтε Йėщ βцττбⁿ !!! !! + €ŗёãťè Ńєщ βυţτøʼn !!! !! - Пėẅ эmрťÿ ряôƒϊĺé !!! !! + Лεẅ ℮мрťў ρřόƒïłě !!! !! Button label that creates a new profile with default settings. - Đůφℓìсàţз ǻ ргŏƒΐŀë !!! !!! + Ďϋρĺíςаτέ à ргòƒĩĺé !!! !!! This is the header for a control that lets the user duplicate one of their existing profiles. - Ðΰρĺϊ¢ǻтέ Ъúтτόñ !!! ! + Ďüρℓїçάţε Ъūťŧǿŋ !!! ! - Ðŭρľįсдťє !!! + ерℓίċâт℮ !!! Button label that duplicates the selected profile and navigates to the duplicate's page. - Ťĥïş ςθļог šċĥěmè īş рåřт óƒ ţћę вūįĺť-īп ѕёττïиġŝ øŗ ǻй їʼnŝŧдĺℓěð є×тēņşїθⁿ !!! !!! !!! !!! !!! !!! !!! ! + Тħϊś ¢öłóř ѕċĥëме įѕ φāяŧ öƒ ťђё вµīľŧ-ïπ ѕэŧŧιñğѕ öя дⁿ їŋşţåłŀєď ĕхŧęлŝïоņ !!! !!! !!! !!! !!! !!! !!! ! - Τő mάкĕ ćћáñģзѕ тŏ тђίѕ сοℓőř ŝςн℮мě, γоů mŭśт mªķë ä ςθрÿ ôƒ īť. !!! !!! !!! !!! !!! !!! ! + Ŧô mąκε çĥªиĝεŝ тò ţĥїş ςθŀθя šĉħémέ, ўôџ мџѕт måќз å сŏрỳ òƒ īŧ. !!! !!! !!! !!! !!! !!! ! - Маĸé ą сòφγ !!! + Μåк℮ ά сõрỳ !!! This is a call to action; the user can click the button to make a copy of the current color scheme. - Šзŧ ¢σľòя şċħèмε ăş ðèƒáùłţ !!! !!! !! + Śĕţ ςőľбґ ѕсħêмę ǻś ď℮ƒάùľŧ !!! !!! !! This is the header for a control that allows the user to set the currently selected color scheme as their default. - Ąφρŀγ ťнίś ċοĺôґ ŝćнемε τó ãļļ ўőŭг ρŕŏƒіĺ℮ś, ъγ ðêƒáůĺŧ. Īʼnδϊνįďûãľ ряøƒіĺєѕ ĉàи ŝτīļŀ śεľ℮ςт ţћĕιя óώń ¢бľοř şċћēméş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Αρρļŷ ţђîş ¢őľŏг şćĥёmė тô âļℓ ýǿũř рŕσƒіľĕś, вγ đ℮ƒǻùļţ. Îⁿðίνìδµāŀ ρŕøƒïŀêѕ ¢аʼn ѕťíļĺ śέľĕĉţ ţђєίř ōшñ čοľǿř ščĥĕměš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description explaining how this control changes the user's default color scheme. - Гзήдmέ ĉοľоґ şċĥēмę !!! !!! + Ŕėⁿăмз ĉøℓøѓ ś¢ĥèmε !!! !!! This is the header for a control that allows the user to rename the currently selected color scheme. - Βąсĸģŕóμлď !!! + Ъâсĸĝŗõũήð !!! This is the header for a control that lets the user select the background color for text displayed on the screen. - Ъĺäćĸ ! + ßļªçķ ! This is the header for a control that lets the user select the black color for text displayed on the screen. - Ьľύē ! + Ьŀúε ! This is the header for a control that lets the user select the blue color for text displayed on the screen. - ßяίģћτ вļâ¢ķ !!! + Ъŗιğђţ вľªçк !!! This is the header for a control that lets the user select the bright black color for text displayed on the screen. - Ьřΐğĥŧ ьĺúз !!! + Ьřĩġћţ ъŀµē !!! This is the header for a control that lets the user select the bright blue color for text displayed on the screen. - βŕΐĝћŧ ¢γãņ !!! + Βřīġħţ сýåπ !!! This is the header for a control that lets the user select the bright cyan color for text displayed on the screen. - Βřìğĥŧ ģŗεёņ !!! + Вŕīģħт ģгēзň !!! This is the header for a control that lets the user select the bright green color for text displayed on the screen. - Ьгíġħţ φûřρľё !!! + ßŗϊğħт ρűřφĺé !!! This is the header for a control that lets the user select the bright purple color for text displayed on the screen. - βřĩġћт ŗêδ !!! + βѓΐğћτ ŕĕď !!! This is the header for a control that lets the user select the bright red color for text displayed on the screen. - Бřΐğħτ ώнιţę !!! + Ъгîġнŧ ωћĩтē !!! This is the header for a control that lets the user select the bright white color for text displayed on the screen. - βŗįĝђť γěℓļōώ !!! + ßѓїĝћť ýèļŀōώ !!! This is the header for a control that lets the user select the bright yellow color for text displayed on the screen. - Сųґşόř ¢øľοг !!! + Ċџŕŝøґ çóĺőѓ !!! This is the header for a control that lets the user select the text cursor's color displayed on the screen. - €γàⁿ ! + Ċŷăń ! This is the header for a control that lets the user select the cyan color for text displayed on the screen. - ₣ōгεĝŗθũйď !!! + ₣őř℮ġŕоμŋđ !!! This is the header for a control that lets the user select the foreground color for text displayed on the screen. - Ĝґėėņ ! + Ģѓēёи ! This is the header for a control that lets the user select the green color for text displayed on the screen. - Ρΰѓφļз ! + Ρυŗρŀє ! This is the header for a control that lets the user select the purple color for text displayed on the screen. - Ѕëŀеčтΐǿπ ъąсķğŕôυⁿð !!! !!! + Şēłэ¢тíοñ ьά¢кğѓбυňď !!! !!! This is the header for a control that lets the user select the background color for selected text displayed on the screen. - Ґēđ + Ѓèð This is the header for a control that lets the user select the red color for text displayed on the screen. - Ŵнîτэ ! + Ẁнíтė ! This is the header for a control that lets the user select the white color for text displayed on the screen. - Ỳęļľŏẅ ! + ¥ěłļбẅ ! This is the header for a control that lets the user select the yellow color for text displayed on the screen. - Ļãлğμāğз (яёqΰĭřēš ѓэℓãџπċĥ) !!! !!! !! + Łâⁿğμąĝе (řзqũìѓěş ѓéℓāúŋčħ) !!! !!! !! The header for a control allowing users to choose the app's language. - Śзľèςţŝ á ďîšрĺду łǻňģύάğé ƒōŕ ťнē àρρłî¢äŧĭŏň. Ţнĩŝ ŵĭłŀ òνεŗяіďε ỳõΰг δ℮ƒâυľţ Windows ΐŋŧēŗƒáčē ĺαлģüǻğè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ѕēłêĉţś á ðіśрĺãγ łàиĝυâğę ƒŏř ŧн℮ άррĺіċâţĭоņ. Ťћîŝ шįľļ ονėŕřìðě ỳõυѓ ďеƒãџĺт Windows ïňťеґƒäĉέ ĺáлģΰâģε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description explaining how this control changes the app's language. {Locked="Windows"} - Ūŝэ šỳşŧēм đ℮ƒąùŀτ !!! !! + Ũѕé śỳśť℮м ďëƒäμļť !!! !! The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead. - Āłẁāуş šħõω ŧдвѕ !!! ! + ∆ĺщãγѕ ѕнοщ ŧąвš !!! ! Header for a control to toggle if the app should always show the tabs (similar to a website browser). - Ẁħęŋ δĭŝâвľèδ, ťћĕ ťąв вāѓ шΐℓĺ ªρρеąŕ щħëи ą ʼnёώ ţãв ίŝ ĉřéáţєđ. Ĉǻηйôť ьє đíѕăьľęđ ωћιŀз "Ηíðę тћę ţΐţŀ℮ вąг" ιś Фп. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ẅħєň δîśάьł℮δ, тĥê тáв ъдŗ щϊŀĺ ǻрρęáѓ ẃћэπ д ňεŵ ţáъ ίѕ ĉґєάťєď. Ćάйņōŧ ве ðîşäьĺéð ẅћîĺε "Ĥϊδê тђé ťїťļе ъãŕ" ιŝ Ώη. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "always show tabs" setting does. Presented near "Globals_AlwaysShowTabs.Header". This setting cannot be disabled while "Globals_ShowTitlebar.Header" is enabled. - Ρθѕìŧίòñ òƒ ŋěώļў čѓėąťεð τǻъś !!! !!! !!! + Ρθšΐτιöņ øƒ ŋéωłỳ çřĕāŧěð ťãъŝ !!! !!! !!! Header for a control to select position of newly created tabs. - Ѕρёċìƒіėŝ ŵĥéѓ℮ ʼnёώ тàъŝ ąρрēãя ίņ ťħз ţåъ ѓõш. !!! !!! !!! !!! !! + Ѕρє¢ĩƒιεѕ ẁħєŕě ņĕẃ τªвš дφφèåг ĭи ťћě тåъ ґǿω. !!! !!! !!! !!! !! A description for what the "Position of newly created tabs" setting does. - ƒτзř ŧнě łαşт τάь !!! !! + Ńťεŕ ţђê ļªšţ ŧąв !!! !! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the last tab. - Ãţзг ťћέ çυŕгëŋţ тãь !!! !!! + Λƒτěґ ţђз çųřŕёŋŧ ŧдь !!! !!! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the current tab. - Аϋťòmąтīčāŀļỳ ċŏφу ѕзŀę¢ťïōⁿ тǿ ćŀίрвōάŕď !!! !!! !!! !!! + Ąũтоmатïсáłŀу сθφý śēļëçŧïôи τó çļιρъόдřď !!! !!! !!! !!! Header for a control to toggle whether selected text should be copied to the clipboard automatically, or not. - Âŭťθmăťΐ¢дŀļу δěтε¢т ŨГĿś ǻлð mакé ťћęм сļįčķāвľė !!! !!! !!! !!! !!! + Áμτοmáτîċǻŀĺγ đęťёçť ЏΓĿŝ áʼnð мāķё ţђέм ¢ļĭċќдьĺέ !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should automatically detect URLs and make them clickable, or not. - Řęмōνé тяãΐłĭηĝ ẁћĩŧė-ŝрăс℮ íл řęςţãⁿġüℓâѓ ѕęℓєċţϊόπ !!! !!! !!! !!! !!! + Řëмσνē ťŗåίľїήġ ẁћĩτē-śрāčέ ϊņ ŗёçŧäⁿğùĺдř ŝэļзċτïóŋ !!! !!! !!! !!! !!! Header for a control to toggle whether a text selected with block selection should be trimmed of white spaces when copied to the clipboard, or not. - Řèмбνè τŗåϊŀιпğ ẃнίťе-şрâĉę ωћèη ρäşţíņģ !!! !!! !!! !!! + Γемоνė ŧŕǻіŀīñģ ŵнΐţē-ŝρäċе ẅђеñ φâšŧĭʼnĝ !!! !!! !!! !!! Header for a control to toggle whether pasted text should be trimmed of white spaces, or not. - ßеℓøω āя℮ ţнз çџŕřэŋţļγ вοūʼnδ ķēўś, ωћίčн ċαņ ъę mōďïƒïěð ьÿ ėđΐтïŋġ ťĥĕ ĴŜΩЛ šэťŧīήğŝ ƒίļè. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Взľбẅ αяė ţнě ĉùŗřеņтℓγ вóûŋď кęуş, ẅĥΐçђ ċąη вĕ mбδīƒīέđ ъỳ έðĭŧĩňğ τнě ĴŜǾ∏ ŝĕŧťîńġś ƒιľέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer located at the top of the actions page that presents the list of keybindings. - Ďėƒąűℓτ ρґοƒιľё !!! ! + Ðёƒāμĺτ φгǿƒīłé !!! ! Header for a control to select your default profile from the list of profiles you've created. - Рѓσƒïłе τħат θрéňś ẁħел çļіĉќïηğ ţħĕ '+' ίçσή θґ ьŷ ŧŷрìηġ τħе ʼnеẅ ťªв κеў вϊñďīņĝ. !!! !!! !!! !!! !!! !!! !!! !!! + Ρřòƒìĺę тĥат öρëйş шћêʼn ĉŀϊςκîʼnğ ţħе '+' íċóп όŗ вў ťýφįиġ τћė иęш тâъ кέÿ вΐŋďíʼnģ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the default profile is and when it's used. - Ďĕƒàųłŧ ťэŗмïлãł áφрľĩćàŧїôň !!! !!! !! + Ðёƒâцľť ťêгмîйάļ аρρľϊςαтįőʼn !!! !!! !! Header for a drop down that permits the user to select which installed Terminal application will launch when command line tools like CMD are run from the Windows Explorer run box or start menu or anywhere else that they do not already have a graphical window assigned. - Ťне ŧëřmΐиäℓ áφрļϊćαŧîŏπ ŧндť ℓãϋήčђзš щђέń д сбmmäⁿδ-łĭйε αрρℓïсдţĭǿи ìŝ ѓυй ωïťħôΰт ǻⁿ ē×íŝţίήĝ śεšşΐőň, ℓįķê ƒřбм τĥε Şţаяţ Меňµ σř Ѓūⁿ đіàℓόġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ŧĥê τéŕmϊʼnдℓ āρρĺìčåťĭŏή τĥåт łàцπсħёś ẃĥëπ ǻ čσmмäñδ-ℓιń℮ áρφĺĩ¢åţĩöⁿ ĩš яúņ ẁīτĥθũτ ąʼn èхĩśŧΐиġ śēŝśιόň, łíķę ƒřőм ťħë Şтăřт Мεⁿũ οŕ Ŕύп ðіαļоġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - Řеđŗąщ ёñťïяė śĉřĕēņ шĥёⁿ đíśрłаý ùрđдŧėš !!! !!! !!! !!! + Ŕеδřαω ēлŧįřê ѕćяéэи ώнęή ďîѕφļáÿ űрďåτêš !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - Щћ℮ņ ðϊşάвŀέδ, ťħε ŧěѓмìлāĺ ẃϊłℓ ŗėňďεŕ óŋŀу τђэ ύрδăţ℮ѕ тő ŧħė şćґэěñ ъĕтώėęй ƒгámèş. !!! !!! !!! !!! !!! !!! !!! !!! ! + Ẅħëņ ďīѕάьℓĕď, тĥ℮ тєŗmίⁿάł ẃїĺĺ ř℮ńđèŗ σйłỳ τĥè ϋрđǻτєŝ ţσ тнë şċѓëεπ ъĕťẃээⁿ ƒřámêś. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". - Сοℓümηş !! + Çóļцмлš !! Header for a control to choose the number of columns in the terminal's text grid. - Ѓθωś ! + Ŕóẃś ! Header for a control to choose the number of rows in the terminal's text grid. - Ίŋĭţìάľ €óℓцмйş !!! ! + Ĩñíŧĭąĺ €óľμмńš !!! ! Name for a control to choose the number of columns in the terminal's text grid. - Īηĭťíāľ Γθшѕ !!! + Ίņĭŧįáℓ Γǿшś !!! Name for a control to choose the number of rows in the terminal's text grid. - Х φøşїťìôŋ !!! + χ рòśίťιŏπ !!! Header for a control to choose the X coordinate of the terminal's starting position. - Υ ρбśїţіőñ !!! + Ύ рǿѕіτìõи !!! Header for a control to choose the Y coordinate of the terminal's starting position. - Х рбšιţìôή !!! + Х φõşїтĭőη !!! Name for a control to choose the X coordinate of the terminal's starting position. - Ёņτêř ţħė νдľúε ƒŏŗ тђέ х-ćóσгđïηάтė. !!! !!! !!! !! + Εņţєŕ τħê νàłůè ƒøŗ ŧћє ж-čǿóґďïⁿат℮. !!! !!! !!! !! Tooltip for the control that allows the user to choose the X coordinate of the terminal's starting position. - χ + Χ The string to be displayed when there is no current value in the x-coordinate number box. - Ύ рǿŝĭťīőŋ !!! + Υ φǿśіťίǿл !!! Name for a control to choose the Y coordinate of the terminal's starting position. - Єηť℮ŗ τĥé νąłúę ƒóґ ţћε ў-ĉŏőŗđΐπǻŧэ. !!! !!! !!! !! + Ĕņтэř ŧнэ νãļùе ƒθґ ŧħĕ ỳ-ĉőöŕδïπáţέ. !!! !!! !!! !! Tooltip for the control that allows the user to choose the Y coordinate of the terminal's starting position. @@ -368,278 +368,278 @@ The string to be displayed when there is no current value in the y-coordinate number box. - Ľĕτ Ẁіňδόẃś đēçίðè !!! !! + ₤зτ Щΐиðøẃѕ ðèċįðз !!! !! A checkbox for the "launch position" setting. Toggling this control sets the launch position to whatever the Windows operating system decides. - Їƒ έήăъℓęď, µśê ţнė şγѕťєм ďēƒâµℓŧ ℓáũńċђ ρθśįťιőл. !!! !!! !!! !!! !!! + Īƒ ėлªвĺёδ, ūѕè ţħė šŷşŧëm ďэƒāџľţ ļāύήсħ φöѕīтίθŋ. !!! !!! !!! !!! !!! A description for what the "default launch position" checkbox does. Presented near "Globals_DefaultLaunchPositionCheckbox". - Ŵђεń Ŧĕгmϊηäľ ŝτăґţş !!! !!! + Ẃнēи Ťεřmιйàℓ šţдґτś !!! !!! Header for a control to select how the terminal should load its first window. - Ẅħāţ ѕнοüĺď ьè ŝнöщй шħεή ŧћз ƒϊřśτ τėяmіňáł ΐś çŕĕäŧέđ. !!! !!! !!! !!! !!! ! + Ẃђăť şħõύļð вэ şћöωń ώнėņ тћ℮ ƒϊяšŧ тĕřміʼnªľ īš ςгęåťêđ. !!! !!! !!! !!! !!! ! - Öрéń ă ţãь ŵĭτн τђë ďěƒªùłŧ рŕθƒïļε !!! !!! !!! ! + Όρεň å τâв ώΐţћ ŧђз ďĕƒάüļτ рřоƒīŀέ !!! !!! !!! ! An option to choose from for the "First window preference" setting. Open the default profile. - Ôφēπ ẁįηðôωŝ ƒгоm ā φřêνįǿùŝ şèѕšΐσή !!! !!! !!! ! + Öρęņ ŵîиďόшѕ ƒřŏм а ргéνįóûş šęšŝĩбⁿ !!! !!! !!! ! An option to choose from for the "First window preference" setting. Reopen the layouts from the last session. - ₤áμñćн mбďё !!! + Ĺǻϋйċћ мōđ℮ !!! Header for a control to select what mode to launch the terminal in. - Ħøŵ тĥз τεѓмїйâĺ ώíℓℓ άрρêāг øŋ ℓαμйςħ. ₣õçűś щĭĺĺ ħĩδέ τђз τâьŝ âňð ţїťľê ваґ. !!! !!! !!! !!! !!! !!! !!! !!! + Ĥοẃ ţнэ ťéяmīⁿåĺ ωïℓł ąφρзάř ŏñ ŀäũлčн. ₣õċűş шīľŀ ħΐδ℮ тħё тãъŝ άπð ţіŧŀě ьаг. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ľаųή¢ђ рàřāмеť℮ѓś !!! !! + Ĺåμʼnćн ραѓämёťэřŝ !!! !! Header for a set of settings that determine how terminal launches. These settings include the launch mode, launch position and whether the terminal should center itself on launch. - Ѕэтţιńġś тĥàτ čôптŗοℓ ђбώ тĥз ţëгмìňãļ ĺάúйсђзŝ !!! !!! !!! !!! !! + Şётťΐήğѕ ţĥåτ ςоŋţґőł нθŵ ţĥė ŧëгмίʼnàℓ ĺàύńċћёš !!! !!! !!! !!! !! A description for what the "launch parameters" expander contains. Presented near "Globals_LaunchParameters.Header". - Ľāџņçħ môđě !!! + Ĺαüйςђ мŏðé !!! Header for a control to select what mode to launch the terminal in. - Ηбщ ţħê тëѓmіήаľ ẃĩŀľ áрρέаг ŏň ℓäϋň¢ћ. ₣οςũѕ ẅīĺļ нĭđė ţћε тαъš ªʼnð ţíтłε вāя. !!! !!! !!! !!! !!! !!! !!! !!! + Ηöш ŧнё τэяmίйäļ ώιłł àρφєдя σⁿ łàŭηĉħ. ₣ő¢цş ẁιĺℓ нïďĕ ţнé тâвś äʼnδ ţïţłê ъăѓ. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Đėƒăũľŧ !! + Đєƒάцĺŧ !! An option to choose from for the "launch mode" setting. Default option. - ₣ųℓĺ şсŕėęή !!! + ₣ùĺĺ şċяěêή !!! An option to choose from for the "launch mode" setting. Opens the app in a full screen state. - Μãхΐмîżéď !!! + Мą×ímĭźέđ !!! An option to choose from for the "launch mode" setting. Opens the app maximized (covers the whole screen). - Лēẃ îňѕτáňçê ьĕħāνïθř !!! !!! + Ñέш їηśťдпčé ьέђäνíòř !!! !!! Header for a control to select how new app instances are handled. - Ćöйτяöŀš ĥŏẃ ⁿеш тεŗмīлäℓ ιπŝťдʼn¢зš ǻťτаçћ ŧŏ з×ϊѕτїñğ ẅĭńďøẃѕ. !!! !!! !!! !!! !!! !!! + Ĉŏηтřöļŝ ħŏщ пëω ťéřмïήдĺ įлѕţªⁿčзś ãŧτâ¢н το έ×ϊśŧϊπģ ωĩŋďöẃş. !!! !!! !!! !!! !!! !!! A description for what the "windowing behavior" setting does. Presented near "Globals_WindowingBehavior.Header". - Ċřεǻŧé ã ňзω ẁíиδοẁ !!! !!! + Ĉŗзäţ℮ â иεш ώĩŋđбω !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances create a new window. - Аτťāςћ ţб ŧħє møşτ ѓ℮ĉêηŧℓý ūѕěδ щìʼnδŏώ !!! !!! !!! !!! + Άţţǻçħ ţò ťħê мбšŧ гęсėñţĺỳ ϋŝēð ẁîήďøώ !!! !!! !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window. - Λττдćћ тô τħë møŝŧ ѓêçěʼnţĺу ųš℮ď ẁιñđøώ ση τђįş đēŝќτǿφ !!! !!! !!! !!! !!! ! + Âťťàçħ ţõ тĥе мōśτ řěсèйтĺγ ŭşėδ ŵīŋδθẃ őʼn ŧĥϊŝ ďēѕĸţóφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - Ťĥěšз ѕęτťϊņĝš mãÿ ье υšэƒцŀ ƒοґ тѓоúвłзşħõόţìŋġ äň íšşµė, ĥǿώэνêѓ ťђěÿ ẅίĺľ ιmρàςт уоΰґ рēřƒőѓmǻπĉз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Тħĕѕе ѕęťťíňģѕ мāý ъè ùѕзƒüļ ƒόѓ ŧгôύьļėşћόόťĩηġ âņ іśśűэ, ĥбωэνêř тĥєу ŵіľĺ ĩmρâćτ ŷôцг φĕґƒōґмªńčë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. - Нĩδє тĥė ţϊţļε ъàг (ѓęqùīŗèѕ řэℓãùпςĥ) !!! !!! !!! !! + Ήĭδë ŧћέ тïťłë вäѓ (ѓзqüïгєŝ ŗéℓâūň¢ħ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. - Шĥέń δìşãъℓěđ, тне τĭţłэ ьăŕ ώїļĺ άрρèªř ăъóνĕ ťћè ţáьѕ. !!! !!! !!! !!! !!! ! + Ẅħеñ δіѕªъĺєð, ŧнέ ţιťłê ъαя ẃīļļ аρφėαг авŏνé ŧћэ тāвś. !!! !!! !!! !!! !!! ! A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Ūѕё ãćгýŀΐċ мáťёѓΐαľ ĩʼn ŧħē τăь ŗоŵ !!! !!! !!! ! + Ůѕε ăčѓÿℓїč mαтέѓįάł ĭń тħε ťàъ ѓσŵ !!! !!! !!! ! Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Цşę āčŧĭνę τєŕmĩиâℓ τίτłë άš ǻφφŀìčǻτïőñ ţïτŀĕ !!! !!! !!! !!! ! + Ûšĕ áсτĭνĕ τёŗмΐⁿàŀ τįţļë áś ăφφĺïĉªţįθň тіτĺє !!! !!! !!! !!! ! Header for a control to toggle whether the terminal's title is shown as the application title, or not. - Ŵћėň đіšдвľеδ, ŧĥē τιŧłě вдř ẁїĺł ъė 'Тэŗmïηªł'. !!! !!! !!! !!! !! + Ŵђęñ đīśåьļěð, ţнє тιťŀє ьãя ẃιłł вë 'Ťēřмϊиåł'. !!! !!! !!! !!! !! A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"} - Śпαφ ωïηðбω ŕēśĭžїиĝ ťŏ čђāŕàċťěя ġřįð !!! !!! !!! !! + Śпâφ ẃϊиδбẅ řéѕíżíⁿĝ τō ċħªґªçţèѓ ğřįď !!! !!! !!! !! Header for a control to toggle whether the terminal snaps the window to the character grid when resizing, or not. - Ẅћēŋ ďíѕǻъℓ℮đ, τħě ώïлðøщ ωïℓł ŕέšìżė šмóôтнĺў. !!! !!! !!! !!! !! + Ŵнэπ δϊşåъłёδ, тнē ẁїπđõŵ ωіļŀ řεѕìżέ ѕmόοτнłγ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - Цşё šǿƒťшäгë ŗėиðεгīπĝ !!! !!! + Ūŝэ ѕοƒтŵάгē ґèňďĕřιňģ !!! !!! Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - Шнёń ëηавŀèđ, тħě ť℮гmĩⁿåŀ ωíℓļ µšė ŧħę śθƒťẃàґĕ ґēŋďëřэř (ά.ĸ.ά. ẀÀŖР) ίπѕτ℮āð όƒ τĥ℮ ħªřðẁăŕέ õň℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẃĥ℮π ěлàвļ℮đ, ťĥз ţèŕмïñąℓ ẃΐłł ůšê ŧћз ѕθƒťшăŗę ŗέŋδêŕěŗ (ǻ.ĸ.â. ШĂЃΡ) ΐпšτёǻð õƒ ţћė ħâгđщåг℮ òиэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - ₤ăūл¢ђ όŋ mаçђìйĕ šтąѓτυφ !!! !!! ! + Łąüπçђ ôπ mªčћΐηз şτаяţυρ !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. - Ăųтθmªтΐċāłľỳ ļāūñсħ Τёřmíʼnаľ ẁħзņ уõц ĺоğ îņ τθ Windows. !!! !!! !!! !!! !!! !! + Αũтθмǻтίςàℓľý ĺāũиćћ Тēřмįʼnàŀ ŵĥэņ уõŭ ℓбğ ĩⁿ ŧő Windows. !!! !!! !!! !!! !!! !! A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header". {Locked="Windows"} - Ĉēňţέřéď !! + €êńтέя℮δ !! Shorthand, explanatory text displayed when the user has enabled the feature indicated by "Globals_CenterOnLaunch.Text". - Ċеňťзř ŏπ łªŭńçћ !!! ! + Ċ℮йť℮ŗ øň ľªűйсн !!! ! Header for a control to toggle whether the app should launch in the center of the screen, or not. - Ẃћ℮п элάвłéđ, тħё шìпđøш ẃïℓŀ вέ ρľăçēð ιņ ŧђē çėñŧęř σƒ ţђё śčг℮ēʼn ŵн℮ⁿ ĺäųπĉћ℮đ. !!! !!! !!! !!! !!! !!! !!! !!! + Ẅнэй εйăъłèð, ťħê ẅιⁿđоŵ ẁíŀŀ ьē ρļàćёď ιп ťĥ℮ ĉ℮ňťέŕ òƒ ŧћê ščг℮ĕň ẅђзп łäůπснĕδ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "center on launch" setting does. Presented near "Globals_CenterOnLaunch.Header". - Âłωäỳś ôπ тøρ !!! + Αĺẅάỳš ои τòр !!! Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled). - Ť℮ŗмїпаľ шіℓŀ àĺŵâýŝ вз ťђê тöρмǿѕτ ẅΐņðőώ оʼn τĥε ďęѕĸţòφ. !!! !!! !!! !!! !!! !! + Ŧęгмĭпåľ ẅΐℓľ äļщąγŝ в℮ τћē тόφmоšţ ώìʼnδøẃ бʼn ţĥĕ ðęšкτőρ. !!! !!! !!! !!! !!! !! A description for what the "always on top" setting does. Presented near "Globals_AlwaysOnTop.Header". - Тαв ŵιďţн мøďē !!! ! + Ťáв ẁιðťђ mθδë !!! ! Header for a control to choose how wide the tabs are. - Čòмρдςт ŵίŀľ ŝћŕĩñĸ ĭņâċŧìνε тãвš ţσ ŧнę şįżє оƒ ţђε îĉθп. !!! !!! !!! !!! !!! !! + Ćοмрǻсŧ щїŀł ѕħřĭńĸ īйªčŧĭνз τäьş ŧό ţĥє šϊżе õƒ ţĥе ιĉőπ. !!! !!! !!! !!! !!! !! A description for what the "tab width mode" setting does. Presented near "Globals_TabWidthMode.Header". 'Compact' must match the value for <Globals_TabWidthModeCompact.Content>. - Ċómφâčţ !! + Čόмράсτ !! An option to choose from for the "tab width mode" setting. When selected, unselected tabs collapse to show only their icon. The selected tab adjusts to display the content within the tab. - Ёqüªł ! + Εqυàł ! An option to choose from for the "tab width mode" setting. When selected, each tab has the same width. - Ţíŧŀê ŀэñģťћ !!! + Тìŧℓэ ľэņġŧĥ !!! An option to choose from for the "tab width mode" setting. When selected, each tab adjusts its width to the content within the tab. - Δρρłĩçăτіŏņ Ŧћëмэ !!! !! + ∆φρĺîςąтіøπ Ţћ℮мè !!! !! Header for a control to choose the theme colors used in the app. - Ďάяк ! + Đâřĸ ! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. - Úş℮ Ẃιňďоŵŝ ŧĥéмē !!! !! + Úѕё Ẃîήðòώѕ τђзмэ !!! !! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. - Łįġħт ! + Ľіġћт ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. - Ðāŗĸ (Ļέģåĉγ) !!! + Đάřκ (Ľзġªçŷ) !!! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. This is an older version of the "dark" theme - Ŭśė Ẁįпđǿẅş τћёмэ (Ľęģдςγ) !!! !!! ! + Ùşė Шìлďθŵѕ τћεmě (Ľēĝаçÿ) !!! !!! ! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. This is an older version of the "Use Windows theme" theme - Łīğнτ (Ĺěğáςу) !!! ! + Ĺίğħť (Ļĕğâčÿ) !!! ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. This is an older version of the "light" theme - Щőґď δěļΐmιŧ℮řş !!! ! + Ẅθґď ďεľіmΐτзŗŝ !!! ! Header for a control to determine what delimiters to use to identify separate words during word selection. - Τћėšê šγмвόłš ωїĺĺ ь℮ ųѕėð ẅћёñ γòų δöűвℓз-čĺĭ¢ќ ŧ℮хт įʼn ťн℮ ťêґmίиăł όŕ áĉŧїνåťє "Μдяќ mоđë". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ţĥзśε şÿмвоłś ẅιℓĺ ьё џşєđ шĥèņ ỳòú ðσυвĺë-¢ľïčķ тзжт įπ τђэ тėгмίηåł όř дсţіνáţ℮ "Мåґк móδέ". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "word delimiters" setting does. Presented near "Globals_WordDelimiters.Header". "Mark" is used in the sense of "choosing something to interact with." - Ǻррéаѓàлćε !!! + Λрρеąŕаиçє !!! Header for the "appearance" menu item. This navigates to a page that lets you see and modify settings related to the app's appearance. - Ċõŀбř šĉħ℮мèš !!! + Соŀőг ŝčħеmзś !!! Header for the "color schemes" menu item. This navigates to a page that lets you see and modify schemes of colors that can be used by the terminal. - Íηťéŗāĉŧîθň !!! + Ίņŧεґäćťìόл !!! Header for the "interaction" menu item. This navigates to a page that lets you see and modify settings related to the user's mouse and touch interactions with the app. - Ŝŧάгτûρ !! + Śτāŕţūр !! Header for the "startup" menu item. This navigates to a page that lets you see and modify settings related to the app's launch experience (i.e. screen position, mode, etc.) - Øφеň ЈŠÕŅ ƒĭľε !!! ! + Ωφел ĴŜÖŊ ƒīĺé !!! ! Header for a menu item. This opens the JSON file that is used to log the app's settings. - Đэƒąυľτš !! + Ďėƒάüℓťś !! Header for the "defaults" menu item. This navigates to a page that lets you see and modify settings that affect profiles. This is the lowest layer of profile settings that all other profile settings are based on. If a profile doesn't define a setting, this page is responsible for figuring out what that setting is supposed to be. - Яεņδęѓĭлğ !!! + Řěʼnδєŕιńģ !!! Header for the "rendering" menu item. This navigates to a page that lets you see and modify settings related to the app's rendering of text in the terminal. - Ǻçτîöпѕ !! + Äćŧΐоήš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. - ßäčĸģřθµñđ брáсíŧу !!! !! + βāçķğřόцлδ øрãсïţỳ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - βàčкģřöűñδ òφǻĉιτγ !!! !! + βăĉĸģяōΰńď ōφā¢ίту !!! !! Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Ŝĕτŝ тнё σраčιţў ŏƒ тћê ẁìπðōẃ. !!! !!! !!! + Şёτś τђє θрαčїťý òƒ τћë ŵίηðǿш. !!! !!! !!! A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header". - Λðνάⁿ¢ēð !! + ∆δνάņсеδ !! Header for a sub-page of profile settings focused on more advanced scenarios. - AltGr ǻľϊάşĭηģ !!! ! + AltGr åľįäѕιńġ !!! ! Header for a control to toggle whether the app treats ctrl+alt as the AltGr (also known as the Alt Graph) modifier key found on keyboards. {Locked="AltGr"} - Бу ďεƒдúŀŧ, Windows тґέаŧš Ćťѓĺ+Ăℓт ǻš åή ãļιдŝ ƒοг ÁľτĢŕ. Тђïś ŝėτťĩήġ шìľĺ òνēѓŗīđэ Windows' δέƒãµĺť ьέнāνĭοґ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + βý đеƒäůľт, Windows τяęаťš Čтґℓ+Âŀţ ªś дй αŀįαś ƒοя ÄľτĜґ. Ŧħΐš šĕťтîηģ шìłľ óνèяґίđ℮ Windows' δéƒāцľт ьэћдνϊőř. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header". {Locked="Windows"} - Ťęжť дňţįãℓĭάšïлġ !!! !! + Ţежţ ąйŧĩдļíåśīйğ !!! !! Name for a control to select the graphical anti-aliasing format of text. - Τëם ąŋţιáℓΐдšįлġ !!! !! + Ţ℮хţ άлтīāļíдѕíпģ !!! !! Header for a control to select the graphical anti-aliasing format of text. - Ĉōñţяôłŝ ћòω ťέם ïŝ аʼnтĭâĺīäśèð ϊл ŧћё гėńďêŗěѓ. !!! !!! !!! !!! !!! + Ċθŋтřŏℓѕ ħоẃ ťєжт ìš ãŋτîāŀìαŝеď ΐń τђз ř℮лđĕяεř. !!! !!! !!! !!! !!! A description for what the "antialiasing mode" setting does. Presented near "Profile_AntialiasingMode.Header". - Δŀίάѕéδ !! + Δłΐäşеđ !! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer does not use antialiasing techniques. @@ -647,259 +647,259 @@ An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a "ClearType" antialiasing technique. {Locked="ClearType"} - Ġгąýѕćáļĕ !!! + Ğŕáŷŝčăĺĕ !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a grayscale antialiasing technique. - Άρρêǻѓāηćε !!! + Λррєāгǻйςę !!! Header for a sub-page of profile settings focused on customizing the appearance of the profile. - Ьаčќĝґøūņđ ĭмǻģë ρдτн !!! !!! + Вàčκġřöυπđ ίмãġê φдŧħ !!! !!! Name for a control to determine the image presented on the background of the app. - Βāčĸğґőύπδ ïmàğé ρдтћ !!! !!! + Ъáčќģґøϋŋď ìmαğé φåτн !!! !!! Name for a control to determine the image presented on the background of the app. - βäćќğѓθџñď ΐmăģé φáţћ !!! !!! + Ъāςķĝřόûņð ίmαğ℮ φâτħ !!! !!! Header for a control to determine the image presented on the background of the app. - ₣ĩŀэ ℓŏĉåťïοń õƒ ţĥè ιмáġė υşęď įņ ťђé ъãçκğгōŭπð ôƒ τħё ẁїπđóщ. !!! !!! !!! !!! !!! !!! ! + ₣іļê ľõćąτïõή όƒ ťђё ιmāġê űѕеð ìⁿ тђé ъāçќġяōűňδ σƒ тнε ẃїπďóщ. !!! !!! !!! !!! !!! !!! ! A description for what the "background image path" setting does. Presented near "Profile_BackgroundImage". - Вд¢ķġŕőűñđ ïmáġė ªℓϊğńmëйŧ !!! !!! ! + Βáскğѓōúⁿð імáğэ ąłιğπmėńт !!! !!! ! Name for a control to choose the visual alignment of the image presented on the background of the app. - Бдçкġѓøůŋď īmáğє ąℓїğпмêητ !!! !!! ! + Βăçķğŗоūŋď іmäĝė аℓīġʼnмēņţ !!! !!! ! Header for a control to choose the visual alignment of the image presented on the background of the app. - Ŝеţѕ ħŏώ ťђέ вǻςќģřōϋήđ ïmαğз άłĭġňş τō ŧħè ьσûήðªяίēś όƒ ţĥэ ωιŋđøẃ. !!! !!! !!! !!! !!! !!! !!! + Ѕēţѕ ĥош τнз ьǻćĸģŕöůŋđ ĭмâġē āľіģʼnš тθ ţнê ъòūйðáґĭέѕ σƒ τнέ шīиđòŵ. !!! !!! !!! !!! !!! !!! !!! A description for what the "background image alignment" setting does. Presented near "Profile_BackgroundImageAlignment". - Воŧτǿм ! + Ъőŧťσм ! This is the formal name for a visual alignment. - Βòŧťŏм łёƒŧ !!! + Ьбτтõм ŀêƒţ !!! This is the formal name for a visual alignment. - Βŏťŧõm гìġħŧ !!! + Ьσťтом ѓіģĥť !!! This is the formal name for a visual alignment. - Сĕиŧзř ! + Ćέήтęя ! This is the formal name for a visual alignment. - Ĺéƒτ ! + £ĕƒт ! This is the formal name for a visual alignment. - Ѓīğħŧ ! + Ґīğђŧ ! This is the formal name for a visual alignment. - Τθφ + Тǿρ This is the formal name for a visual alignment. - Ŧøρ ļеƒŧ !! + Τбφ ļĕƒť !! This is the formal name for a visual alignment. - Ťоρ řìğĥŧ !!! + Ťóφ ґΐġнŧ !!! This is the formal name for a visual alignment. - Βřσшѕέ... !!! + Вяøωšє... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ǻđđ ňзώ !! + Ăđδ ņēẅ !! Button label that adds a new font axis for the current font. - Áδð ņęẃ !! + Äδδ пéẁ !! Button label that adds a new font feature for the current font. - Ьª¢ķğґøúńð ίмаĝě θφдςĩťў !!! !!! ! + βªçķģřǿűиď їмăĝë брāçίтў !!! !!! ! Name for a control to choose the opacity of the image presented on the background of the app. - Βάсκġřøůηδ іmǻĝę θрд¢įтŷ !!! !!! ! + βåĉкĝґóŭйδ įmäġē ōρàĉīτý !!! !!! ! Header for a control to choose the opacity of the image presented on the background of the app. - Š℮тѕ тĥέ õрàčíтÿ øƒ тĥє вąčќģяøůпð їmąģе. !!! !!! !!! !!! + Şëţś τħè őφåсιτỳ őƒ ťĥę ьâ¢ķğгσυήδ ìmāģë. !!! !!! !!! !!! A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". - Ьдčќğŕθũлđ įмáĝε šţŕёτсħ мóďě !!! !!! !!! + Бâċќģгøųňð īмαğ℮ śтŗèţсн mόďé !!! !!! !!! Name for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Ьáçќģŕöūήď ĩмąġē ŝŧŗēтçħ мõδë !!! !!! !!! + Ъăċķĝřόůŋď іmäġē ѕťřэţсħ моðè !!! !!! !!! Header for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Šéтş ĥοẁ тħё вâсκğгόüńð īmāğ℮ ιš яĕşĭžéđ ţõ ƒΐļł ťћĕ щîⁿďощ. !!! !!! !!! !!! !!! !!! + Şзťŝ ĥощ τĥє вąсķģřοųпđ їmάĝĕ îѕ ŗēŝΐžзð тǿ ƒίĺĺ τнę шιņðθω. !!! !!! !!! !!! !!! !!! A description for what the "background image stretch mode" setting does. Presented near "Profile_BackgroundImageStretchMode". - ₣ίŀŀ ! + ₣ίℓľ ! An option to choose from for the "background image stretch mode" setting. When selected, the image is resized to fill the destination dimensions. The aspect ratio is not preserved. - Лǿʼnε ! + Йôйé ! An option to choose from for the "background image stretch mode" setting. When selected, the image preserves its original size. - Ūńīƒöґм !! + Ûñΐƒøям !! An option to choose from for the "background image stretch mode" setting. When selected,the image is resized to fit in the destination dimensions while it preserves its native aspect ratio. - Üⁿîƒøŕm τб ƒіŀł !!! ! + Ųⁿιƒõŕm тō ƒϊļℓ !!! ! An option to choose from for the "background image stretch mode" setting. When selected, the content is resized to fill the destination dimensions while it preserves its native aspect ratio. But if the aspect ratio of the destination differs, the image is clipped to fit in the space (to fill the space) - Рřοƒìłз тзřmϊпąтĩθñ ъéħâνìóя !!! !!! !! + Ρѓσƒїℓє τэřmíňάтίõи ьêћǻνΐőґ !!! !!! !! Name for a control to select the behavior of a terminal session (a profile) when it closes. - Рŕǿƒιℓë ţеŗmìňāτíøŋ ъęĥåνīóř !!! !!! !! + Ρŗοƒîℓ℮ ţєґmїйãτìоñ ьěнãνīõř !!! !!! !! Header for a control to select the behavior of a terminal session (a profile) when it closes. - Сŀōѕė щħεп рŕōčĕşŝ эжĩтŝ, ƒäĩℓś, ôґ ćŕąşнёś !!! !!! !!! !!! + Ĉĺõѕê ẁнëň ρŗō¢ĕŝş э×ïťš, ƒāιℓŝ, οŕ ćřαŝħзѕ !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled (exit) or uncontrolled (fail or crash) scenario. - Čłбѕě όйļỳ ŵђëη ргŏċéŝѕ ė×íτŝ ŝΰćçëѕšƒûℓľý !!! !!! !!! !!! + Čℓôşё øŋŀý ẃħзи φгöćêšŝ êхįτѕ śűςсєšśƒϋŀŀỳ !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully. - Ñ℮νéř ċļбšε άŭτômâтϊĉąļľý !!! !!! ! + Иένèѓ ĉłőşě άŭτőmåŧĭ¢äľłў !!! !!! ! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal. - Áϋтσmдťĩς !!! + Δџτöмãτĩč !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal. - Ĉŏľöŕ ѕ¢ћ℮мě !!! + Ćŏłοř ŝċħёmē !!! Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user. - Сθммàⁿδ ℓĭή℮ !!! + Ċомmâʼnđ ŀïηë !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Сοмmäпδ łìηē !!! + Ĉόмmªŋð ℓïňê !!! Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Сómмàйď ŀϊʼnė !!! + Çõmмåηď ℓϊņė !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Э×ëсŭŧáвľ℮ űşεδ įи ťћє φгбƒîłę. !!! !!! !!! + Ε×èĉűţàъļé úšëδ ϊň ťћê рřοƒίļè. !!! !!! !!! A description for what the "command line" setting does. Presented near "Profile_Commandline". - βŗσщšе... !!! + βяóώŝё... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Çŭґşǿя ђēίģĥť !!! + Čμŗšôѓ ħèіġнţ !!! Header for a control to determine the height of the text cursor. - Ŝέŧš ŧĥè рэгčèňţªģë ђěϊĝђŧ õƒ ţĥє сűяšоŗ šťаѓτįńģ ƒřом τђę ъóτťōm. Őлļў шθŗķş шϊťћ ţђé νīñţàĝé ĉųяŝøř şћăρє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Šēтš τћэ φεгĉэņťáğе ћεїģнŧ öƒ тће ćúŕŝòř ѕţàѓτĩпĝ ƒгŏm ţћę ъøţтσm. Όʼnłý ẃôřķş ẁîтħ τĥė νΐпŧǻğε сųгšöř šђäρè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "cursor height" setting does. Presented near "Profile_CursorHeight". - Ċùѓšóґ ŝђãрè !!! + Çûřşθŗ śĥąφĕ !!! Name for a control to select the shape of the text cursor. - Ĉµґѕǿя ŝħâφĕ !!! + Ĉϋŕŝőŗ śћàρē !!! Header for a control to select the shape of the text cursor. - Ñзνéѓ ! + Ņзνэř ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will never adjust the text colors. - Фйĺў ƒǿг čόŀσřŝ іл τĥė çōłόŕ šĉĥéмé !!! !!! !!! ! + Òņłỳ ƒőř čθĺοŕś ίņ τнę çóℓбг ѕсћĕmє !!! !!! !!! ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table. - Àŀẅªýş ! + Дľŵдỳš ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. - Ьãř ( ┃ ) !!! + βаř ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. - Змφту вǿж ( ▯ ) !!! ! + Εмρťŷ ьο× ( ▯ ) !!! ! {Locked="▯"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an empty box. The character in the parentheses is used to show what it looks like. - ₣īĺŀзđ ъσж ( █ ) !!! ! + ₣ĭℓŀеď вǿх ( █ ) !!! ! {Locked="█"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a filled box. The character in the parentheses is used to show what it looks like. - Úñðëŕѕćοґз ( ▁ ) !!! ! + Ùŋðэŕśςǿřε ( ▁ ) !!! ! {Locked="▁"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an underscore. The character in the parentheses is used to show what it looks like. - Vιⁿťäğě ( ▃ ) !!! + Vįйťąğё ( ▃ ) !!! {Locked="▃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a thick underscore. This is the vintage and classic look of a cursor for terminals. The character in the parentheses is used to show what it looks like. - Đőυвł℮ ύиďéґşċóŕë ( ‗ ) !!! !!! + Ðθŭъľе ũиđēгşçöř℮ ( ‗ ) !!! !!! {Locked="‗"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a stacked set of two underscores. The character in the parentheses is used to show what it looks like. - ₣ôпт ƒàсё !!! + ₣øŋŧ ƒäĉε !!! Header for a control to select the font for text in the app. - ₣ōлť ƒǻсé !!! + ₣ŏπт ƒª¢з !!! Name for a control to select the font for text in the app. - ₣òиţ śīźĕ !!! + ₣ǿηŧ ѕîžė !!! Header for a control to determine the size of the text in the app. - ₣òņт śіźε !!! + ₣óⁿτ ŝīżе !!! Name for a control to determine the size of the text in the app. - Šīžê òƒ ťћё ƒōⁿτ ĭʼn рøίʼnŧś. !!! !!! !! + Ѕιžè σƒ тђę ƒôñť ĭŋ рôΐŋťѕ. !!! !!! !! A description for what the "font size" setting does. Presented near "Profile_FontSize". - £îñэ ĥеíğђτ !!! + Ļíŋĕ н℮іĝћť !!! Header for a control that sets the text line height. - £īńě ђ℮ίĝĥτ !!! + £іňê нèìĝћт !!! Header for a control that sets the text line height. - Öνéŕřιđë тнę ĺįηє ħзîģђť ōƒ ťħ℮ ţэŕмιńął. Мёªšµřèδ āŝ â mцĺτįрľé øƒ тн℮ ƒóņτ śΐž℮. Ŧћε ðëƒáųłť νąĺūэ δēρэⁿđŝ öи ŷõũř ƒöňŧ äπđ íş υŝџãľŀÿ дгǿũйδ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ονэŕŕíðê ŧћę ľïŋē нéιĝĥţ оƒ ŧĥê ŧĕямíňâľ. Μĕàšũяёđ āś α мųłтїрļè όƒ тħė ƒǿηт ѕїżе. Ťђё δęƒäϋļŧ νǻłůέ đęρеʼnđş οп уόμя ƒбŋŧ ăлď ϊś μŝϋдļľŷ āґσůпδ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "line height" setting does. Presented near "Profile_LineHeight". @@ -907,846 +907,854 @@ "1.2" is a decimal number. - Вΰìŀťîή Ġłурħš !!! ! + βúïłтіⁿ Ğĺỳφђš !!! ! The main label of a toggle. When enabled, certain characters (glyphs) are replaced with better looking ones. - Ẁнėⁿ εňαьŀéð, тĥе ţéяmìʼnдł ðѓάŵş čцšŧöm ġĺўρнŝ ƒöř ъŀбčĸ ęļέmεņŧ дňď ъŏх ďŗăẁιňģ ĉћāяаçŧěѓš įηśτèäď όƒ ΰşίⁿĝ τĥε ƒóʼnť. Тħϊš ƒ℮ªťŭŗз øʼnℓŷ шǿŗќŝ ẅђêп ĢΡÜ ∆сċéľєґǻťïθⁿ įŝ äνäιļåьľе. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Шнєй εйαъľëδ, тħè ťєřmĭлªŀ δřаωş ĉΰšτőm ğļỳрнś ƒŏŗ ъℓøск ёℓεмεňτ αňδ ьõх đяªщįπģ ςђāŕά¢ţёřѕ іиśŧєāδ õƒ ϋŝίήģ ťħέ ƒóйť. Ŧђìѕ ƒěăтűґè биĺγ ωбгќś ώнеη ĞΡŬ Δĉċėŀєřäţìоŋ ΐş âνàïłåъℓê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + ₣μĺℓ-ςбļŏя Εмојì !!! ! + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Шħêņ επáвℓзđ, Éмőĵί дґэ ðїşρłåγєđ ïп ƒΰłŀ сθŀόř. !!! !!! !!! !!! !! + A longer description of the "Profile_EnableColorGlyphs" toggle. + - ₣óпŧ ώèíğĥť !!! + ₣бήт ωĕїĝћť !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣øηт ωέįĝĥţ !!! + ₣οⁿт ώêϊģħť !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣бñť ẅέįģћŧ !!! + ₣őņт щ℮ìğнτ !!! Header for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - Šзťś τĥê ωĕιĝђт (ℓΐģћŧⁿêѕŝ õř н℮àνίňěŝѕ ŏƒ τђę śţяøќěš) ƒóŕ ťђє ĝįνзň ƒбņт. !!! !!! !!! !!! !!! !!! !!! ! + Ŝèťŝ ŧħę шēĭġħţ (ļīģћтπėśś õя ĥзäνīпēşѕ бƒ тħз ŝţґöκ℮ѕ) ƒōř ŧђé ĝìνеп ƒόⁿŧ. !!! !!! !!! !!! !!! !!! !!! ! A description for what the "font weight" setting does. Presented near "Profile_FontWeight". - Vаŗίăвłè ƒοⁿт ä×éѕ !!! !! + Vагíдвĺё ƒŏηŧ ахĕѕ !!! !! Header for a control to allow editing the font axes. - Āďđ ŏŕ ґèмσνę ƒõηŧ ªжєŝ ƒöґ ťħè ğινéñ ƒόήт. !!! !!! !!! !!! + Δďď оř яèmőνé ƒοпŧ ªхеѕ ƒǿŗ τћε ĝĩνэπ ƒόņť. !!! !!! !!! !!! A description for what the "font axes" setting does. Presented near "Profile_FontAxes". - Ŧнэ şέļ℮ċтεđ ƒоŋт ħªş йо νąŕīåъĺě ƒσητ äхèś. !!! !!! !!! !!! ! + Тђę šзĺзсŧеð ƒóňт нαŝ ňô νăгϊàвļê ƒôйť ά×ěѕ. !!! !!! !!! !!! ! A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". - ₣őⁿţ ƒёǻτųŕέѕ !!! + ₣σňτ ƒèąтűřзş !!! Header for a control to allow editing the font features. - Ąδδ ǿґ ґėмōνè ƒóпţ ƒéдτūг℮ŝ ƒøŕ ŧћз ğіνëп ƒσņτ. !!! !!! !!! !!! !! + Аđđ θř ѓέmбν℮ ƒθňτ ƒέάтцřēš ƒŏŕ тћэ ģïνėń ƒőηţ. !!! !!! !!! !!! !! A description for what the "font features" setting does. Presented near "Profile_FontFeatures". - Ţћè śёľęĉтèđ ƒοńŧ ħаѕ ńŏ ƒοⁿτ ƒéάτųгĕŝ. !!! !!! !!! !!! + Ťħе śёℓєçтěď ƒōňţ ĥâѕ ņō ƒбńт ƒéåτüŗěŝ. !!! !!! !!! !!! A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". - Ġęŋзřάĺ !! + Ģęпеŗàľ !! Header for a sub-page of profile settings focused on more general scenarios. - Ηìδé ρřőƒïℓэ ƒřòm ðѓŏφðσẁʼn !!! !!! ! + Нīďë ρяоƒĭĺĕ ƒґθm đяσрδόщи !!! !!! ! Header for a control to toggle whether the profile is shown in a dropdown menu, or not. - Іƒ еπǻвłěδ, ţнз ρґŏƒϊľė щιŀľ иøŧ äρρèăя ìⁿ тђё ŀіşŧ ŏƒ φгôƒīĺεş. Τħîś ćάņ вë ύŝèδ ŧò ђïδě ðêƒαµļŧ φŗõƒΐľєş аⁿδ ðўлªмįсáľľў ģēηέѓāŧèď φřθƒĭłěš, ŵħĩłє ĺêâνìńĝ ţђєm ĭή уöūг ŝèтτιлġş ƒϊĺз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Іƒ эиäъľėδ, ţĥє φŕόƒīŀέ ẅιℓℓ ʼnøτ ǻрφėâг ĩп ŧћę ľīşţ σƒ φяóƒίłěş. Ťĥίŝ ςǻл ъε üşĕď тō нιđé đэƒдџĺŧ ρяõƒíļέš дńδ δγñãміćāļℓŷ ĝеήэѓаŧêð ρяōƒϊŀèś, ώђįℓё łёàνĩпğ τнем ïп γôùѓ ѕęтŧîηĝš ƒîłë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "hidden" setting does. Presented near "Profile_Hidden". - Ŗûи ţђïŝ ρŗŏƒΐľĕ àš Ãðмïήîšťгªťθѓ !!! !!! !!! + Ŗűπ ŧħìѕ ряőƒіŀê ǻš Λďміиϊśţŕãŧõř !!! !!! !!! Header for a control to toggle whether the profile should always open elevated (in an admin window) - Ϊƒ єñáъŀèď, ŧћ℮ φѓǿƒїľĕ ŵіľŀ θрęñ ïň áŋ Λďмīñ ţэŗмĭňǻŀ ωĩήđóŵ дΰţомäтΐ¢άłľŷ. Ĩƒ тнē çцŗřêňτ ŵιиđőẅ īś ąļŗ℮ǻðý ŕùʼnήійğ åš äδmīⁿ, ΐţ'ŀļ ôрёñ ïń τħïѕ ŵĩñđõω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ĩƒ êηāвļêδ, тнě φŗóƒΐℓэ ẃïℓĺ ôрêή īⁿ äи Ąďмîʼn тēŕмįňàľ шīʼnđòẃ áũŧōмãτΐċãℓłу. Īƒ ţђĕ ςũřґэйť шїņđοώ їŝ åļґзàðỳ ŗµņņìňģ ǻş åďmίņ, įŧ'ľľ óφěŋ ïи тђĭş ωîйδõω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Нìśτθѓў ŝíźε !!! + Ĥĩšťòѓў śïźê !!! Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Ηΐšτôґγ şĩźé !!! + Нïѕтояγ śіżē !!! Name for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Тħё ήμмьέя õƒ ļĭŋэš åьóν℮ тђэ øπèš đīŝрℓǻÿєδ īń тђέ ẅīʼnδōẃ ÿθú ςâň ŝςřόĺŀ ъåςќ тó. !!! !!! !!! !!! !!! !!! !!! !!! + Τћё иümьēř őƒ ľįⁿěś äьόνе тђé όŋέş δίšρłаγĕđ ìⁿ ţћэ ŵίņδòẁ ÿöù ĉàи şсгσļľ ъàĉк τо. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "history size" setting does. Presented near "Profile_HistorySize". - Īčσл ! + İčόл ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - І¢бⁿ ! + İĉöη ! Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ίćòń ! + Ίčθл ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ëмбĵϊ όř ĩмàğĕ ƒïļé ĺōċаŧιōņ óƒ ţĥз ΐ¢бʼn υşέđ įň ťће ρřõƒίŀё. !!! !!! !!! !!! !!! !!! + Ęмǿјî őѓ īmάğё ƒіł℮ ļőĉäтΐôπ θƒ τћê ίćǿñ ΰŝęđ ΐñ тĥĕ ρгōƒϊłе. !!! !!! !!! !!! !!! !!! A description for what the "icon" setting does. Presented near "Profile_Icon". - Бŗöẁśê... !!! + βřõώşē... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ρâďðιņĝ !! + Ρǻďδíŋģ !! Name for a control to determine the amount of space between text in a terminal and the edge of the window. - Ρǻðđĭŋġ !! + Ράďďϊʼnġ !! Header for a control to determine the amount of space between text in a terminal and the edge of the window. The space can be any combination of the top, bottom, left, and right side of the window. - Ѕěŧѕ ťĥє φáďδĭπğ дґоüňđ ţħĕ ŧéхτ ωіťћіņ тĥė ẁίπδōω. !!! !!! !!! !!! !!! + Ŝĕťş ŧħе ρãδδĩŋĝ άřóųπď тћê т℮жť ŵίťђïŋ ťнé ẅίņďοẅ. !!! !!! !!! !!! !!! A description for what the "padding" setting does. Presented near "Profile_Padding". - Ŕ℮ťŗø τęřmĩňäŀ ėƒƒεсŧş !!! !!! + Яěţґò τęяmіпåł έƒƒёċтş !!! !!! Header for a control to toggle classic CRT display effects, which gives the terminal a retro look. - Şђόẅ гęťѓò-śтỳĺ℮ ťęŕмîŋάℓ эƒƒĕćτŝ şџċн αѕ ĝĺōшїпģ тéхт âʼnð ѕ¢άń ŀϊńэŝ. !!! !!! !!! !!! !!! !!! !!! + Šђбẁ яεŧґø-şтýłэ ťεřmїηąļ 胃ęċтš ѕμ¢н ªš ğŀøώīлģ ţ℮жť ǻňð şĉăņ ļΐňзš. !!! !!! !!! !!! !!! !!! !!! A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect". "Retro" is a common English prefix that suggests a nostalgic, dated appearance. - Λŭţоmāţïćãĺĺŷ ǻďĵųšт ℓΐĝĥŧήęşŝ бƒ ìлðїşţіηğūїśћаъĺę ťĕхţ !!! !!! !!! !!! !!! ! + Λџţόмǻŧіčǻℓℓý ăδјūѕţ ĺïġћтņėŝŝ ŏƒ ìⁿđιşťĩηĝŭïśндъľę тё×т !!! !!! !!! !!! !!! ! Header for a control to toggle if we should adjust the foreground color's lightness to make it more visible when necessary, based on the background color. - Λųţøмãτĩςªℓļγ вгΐğĥťęиş σř δāŗкęñś тεжţ ŧò мάкë ïŧ mбřè νїśìвℓз. Èνэл щħéŋ ℮ñдвłєð, ťђїŝ ąðјцŝтмęηт ώїℓℓ όňłў ǿςĉΰř шнėñ á çσмвîʼnäτïőņ õƒ ƒοŕέĝŕôџйď ǻиδ ъдςĸğřòΰлδ çôłбѓś шõμŀď ѓéŝцŀť іл φøθŗ ćбπτřâѕť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ãΰτοmáťìĉåļľγ ьґīğĥŧëŋѕ σг ðàгķēŋş ŧĕ×т τό mãķè ĭτ мòгè νìšìъļэ. Éνэŋ щĥėл ёήªъĺĕδ, ţћīѕ äðĵµѕтmèηţ ώіŀľ øлľў оċçύг ẁĥзп ά čőмвιйαтιŏⁿ òƒ ƒǿŕęġяöυйď åŋδ ьąćкģгőųпð çбļŏѓş шбŭłď ŕэѕüŀŧ ìи ρôŏя сοⁿŧŕąŝŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors". - Śčґоľļвąř νìѕīьïℓίŧỳ !!! !!! + Ѕсгбľľъάŗ νīśîьĭŀίŧÿ !!! !!! Name for a control to select the visibility of the scrollbar in a session. - Ѕçяòŀļъāŕ νĩşΐвĭļΐťγ !!! !!! + Şċѓόℓłьäř νìşĩвîłĩţỳ !!! !!! Header for a control to select the visibility of the scrollbar in a session. - Ήïððēή ! + Ηíďď℮ñ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is hidden. - Vĩśϊьłέ !! + Vιşĩвĺέ !! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible but may dynamically shrink. - Âľшªуş ! + Λŀώдуś ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is always visible. - Ѕçяōłĺ ŧσ íйρџŧ ώнéи ŧγφîηĝ !!! !!! !! + Śςŗθĺĺ ŧó îирμт ωнзń ţуφιňĝ !!! !!! !! Header for a control to toggle if keyboard input should automatically scroll to where the input was placed. - Śťаřŧîлğ ðĭґĕçţòяý !!! !! + Ŝťàгτïŋĝ ðïґëçŧǿŕγ !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Ѕτäŗτілğ ðìřэĉтōŕу !!! !! + Šταґŧĩήģ ðΐřê¢τōřỳ !!! !! Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Ŝţаґтįñġ ďĭгèćţòŕγ !!! !! + Šτдŗτїⁿĝ διŗэćťόŗγ !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Ťћë ðįяе¢ŧóгγ τнē φŗόƒįļё śŧăřťş ïņ ŵħзŋ įτ їš łоâđęď. !!! !!! !!! !!! !!! ! + Ŧђз đїřесτöřу ţнē ряόƒíļë šŧăѓτŝ ìň ẁħèń ïŧ ĭś ℓоãďзđ. !!! !!! !!! !!! !!! ! A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory". - Βŕσẅşє... !!! + βŗθωšé... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ûŝê ρâґěňŧ φŕõсєśş đіŕěćţбŕŷ !!! !!! !! + Ųѕέ φáŗéπт ρŗōсэѕѕ δīř℮ĉťояỳ !!! !!! !! A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process. - ݃ епåьľзď, ŧћîş ргöƒĭŀе ẅΐŀļ şραẅи ĭи ťĥê đīгė¢τόřÿ ƒřøm ŵĥïçћ Ŧėřмίπάℓ ẃåš ĺåŭиćĥęď. !!! !!! !!! !!! !!! !!! !!! !!! ! + Іƒ ëⁿåьľĕδ, тĥιś ρŕøƒïŀέ ẅιℓĺ ѕρāẃй ιη тĥë ďΐřêċτόŕỳ ƒѓòм ẅħįćĥ Ť℮řmĩлăĺ ŵąŝ ļåûηсĥèď. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox". - Ηīδэ ιčöñ !!! + Нιđє ісθή !!! A supplementary setting to the "icon" setting. - ΃ ёňάвļ℮đ, τђіş рŗǿƒīļз ẅĩℓℓ ћάνĕ ńō ι¢øπ. !!! !!! !!! !!! + ݃ εηăьľέď, ťĥίѕ ρяόƒϊŀέ ώįĺł ħàνė ηò íćōň. !!! !!! !!! !!! A description for what the supplementary "Hide icon" setting does. Presented near "Profile_HideIconCheckbox". - Şųφрŗ℮śş ťĭťŀè ¢ħªņğėş !!! !!! + Šűρφґзšѕ ţįтľε ĉħªņĝēŝ !!! !!! Header for a control to toggle changes in the app title. - Ìģήоŕè ąрφľíсāţîőʼn ѓēqūёşţş τó čнªńģę τђė ŧíťłé (OSC 2). !!! !!! !!! !!! !!! ! + Ìĝηоřę дφрļîсâţίбл ŕéqυέѕťş тǿ ĉĥáηğè ţĥе τΐτłë (OSC 2). !!! !!! !!! !!! !!! ! A description for what the "suppress application title" setting does. Presented near "Profile_SuppressApplicationTitle". "OSC 2" is a technical term that is understood in the industry. {Locked="OSC 2"} - Ţâв тīŧļè !!! + Таъ тĩţļε !!! Name for a control to determine the title of the tab. This is represented using a text box. - Тǻв ţìţłέ !!! + Τâъ τΐŧłė !!! Header for a control to determine the title of the tab. This is represented using a text box. - Гèφℓãçзѕ ŧћë ρґοƒįℓê лāmè ªŝ тћε ťīτľε ťǿ ρàśѕ τб ťћė ѕħεľļ öņ śтαŗťμφ. !!! !!! !!! !!! !!! !!! !!! + Ґèрľдςэş ţĥе φřǿƒĭļ℮ ňǻmě áѕ τђē тιτĺέ ťö рдѕŝ ťò тĥ℮ śнéļℓ õп şţαŕťũр. !!! !!! !!! !!! !!! !!! !!! A description for what the "tab title" setting does. Presented near "Profile_TabTitle". - Џñƒοċüšēđ ãррėåŗäлçέ !!! !!! + Ũʼnƒσ¢ύŝéð αφрèάřąňсє !!! !!! The header for the section where the unfocused appearance settings can be changed. - Čřèãťĕ Àрρέαŕåň¢ē !!! !! + Čŕéăτė Αρφзάŗаņçė !!! !! Button label that adds an unfocused appearance for this profile. - Đêľеťě ! + Ďéľ℮ŧě ! Button label that deletes the unfocused appearance for this profile. - Έņãьļз âςѓўĺіç мατēřίāļ !!! !!! + Èήâвℓë ąĉѓýļįč мâťěŗīåļ !!! !!! Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Αрφľïĕś å ţгāлśłũ¢ёиţ ŧё×ťџѓě ťō ţђê ваćкģřöµńð ŏƒ тне ŵΐηđοω. !!! !!! !!! !!! !!! !!! + Ãρρļïзŝ ā ŧřаπśℓΰćęπŧ τёхτμřэ ťô ţћê ьдćќĝґбύпď бƒ τнê шіńðòẃ. !!! !!! !!! !!! !!! !!! A description for what the "Profile_UseAcrylic" setting does. - Ūšě đέŝкτòр ώãŀľφаφėŕ !!! !!! + Џѕę đёѕќťőφ ώаļľρаφέѓ !!! !!! A supplementary setting to the "background image" setting. When enabled, the OS desktop wallpaper is used as the background image. Presented near "Profile_BackgroundImage". - Ūśě ŧħе ďęšκťǿρ ώдŀℓрàρея ĩмãĝè áś ťĥз вǻ¢ķģѓõµлδ іmǻģз ƒσг ŧĥз тĕřмΐйàŀ. !!! !!! !!! !!! !!! !!! !!! + Ūѕê ŧћę δěŝκŧőφ щãŀļрàρèŗ įмàġé åś τће вåċķġřόũňð ίмªġē ƒбг ťĥè ţεřmīπáļ. !!! !!! !!! !!! !!! !!! !!! A description for what the supplementary "use desktop image" setting does. Presented near "Profile_UseDesktopImage". - Ðĩşćåґđ çћąπġéŝ !!! ! + Ďĭşςαяđ ςĥαñĝєѕ !!! ! Text label for a destructive button that discards the changes made to the settings. - Đιśςåŗð āľℓ ùиŝäνęð ŝéťтįпĝŝ. !!! !!! !!! + Đίšсäřδ āļľ űņšανêď šέтťιйģś. !!! !!! !!! A description for what the "discard changes" button does. Presented near "Settings_ResetSettingsButton". - Ŝáνз ! + Ŝăν℮ ! Text label for the confirmation button that saves the changes made to the settings. - ⚠ ¥òű ђąνĕ υиѕаνёδ ċĥåπğêŝ. !!! !!! !! + ⚠ Ўöџ ħăνе ûńšάνэð ¢ħáńģεš. !!! !!! !! {Locked="⚠"} A disclaimer that appears when the unsaved changes to the settings are present. - Άδď ǻ пēώ рґõƒĩŀē !!! !! + Ąðđ α пêщ φѓôƒīŀė !!! !! Header for the "add new" menu item. This navigates to the page where users can add a new profile. - Рѓôƒїłёš !! + Ρѓŏƒíŀзѕ !! Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items. - ₣õсûѕ ! + ₣оĉũş ! An option to choose from for the "launch mode" setting. Focus mode is a mode designed to help you focus. - Мαжϊмίżěð ƒóćûš !!! ! + Μā×ίмϊžęď ƒōçųś !!! ! An option to choose from for the "launch mode" setting. Opens the app maximized and in focus mode. - Мåхїmíżеđ ƒύŀļ şċřëзл !!! !!! + Μªжΐmĩžзđ ƒũłℓ śčŕёėи !!! !!! An option to choose from for the "launch mode" setting. Opens the app maximized and in full screen. - ₣υĺł şçґèёʼn ƒòćΰš !!! !! + ₣űŀŀ şçґєзŋ ƒó¢ůş !!! !! An option to choose from for the "launch mode" setting. Opens the app in full screen and in focus mode. - Μă×ĩmížèð ƒџļļ šςг℮ëл ƒöċΰś !!! !!! !! + Мǻ×іmΐžéđ ƒūℓł ѕčŗеèⁿ ƒσčůŝ !!! !!! !! An option to choose from for the "launch mode" setting. Opens the app maximized in full screen and in focus mode. - Ъεĺℓ ʼnõţįƒĭċªţїŏи ѕτуļě !!! !!! + Ьέŀℓ иοŧïƒĭćªţїóи šŧỳļë !!! !!! Name for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Бėĺℓ йοтîƒίĉдťιǿи śţуľę !!! !!! + ß℮ĺļ ņŏţіƒї¢âтΐόή šţÿłé !!! !!! Header for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Ċǿήтґбłś ẃнąŧ нάφрèňѕ ώħёŋ ţĥ℮ áρφŀĭςάţīőń èmίţś â BEL ¢ђāґаċţéя. !!! !!! !!! !!! !!! !!! ! + Ċöήτŗоℓś ωħαť ĥăрρепś шħєń ťђê дρρℓĩčäţιοń έмιţѕ ä BEL čħагä¢тëг. !!! !!! !!! !!! !!! !!! ! A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"} - Ļàúňςħ τђіś àφφŀιĉäτĩбп ŵîťн α ⁿεω ēиνįŕσņмеηť вŀо¢ќ !!! !!! !!! !!! !!! + £ãϋňčĥ тĥіş ąφрļΐςаţїóň шĩτн д иэώ έпνіѓσиmэŋŧ ъŀǿčķ !!! !!! !!! !!! !!! "environment variables" are user-definable values that can affect the way running processes will behave on a computer - Ẅђêŋ ëńαъĺêď, тђë Ţéŕмįйáł ωìℓŀ ġēйеґãţē ă ñéẃ эņνïяǿπмéήť вļбçќ ẃĥёл ςґэăτīπģ ňέẁ ţªьѕ θř рäⁿεś шíтђ ŧћìŝ ρяõƒіļе. Ẁћèή ďìşαвĺ℮ď, ťĥэ τāь/рâʼnе ẁїŀℓ įйŝтéāď îⁿħĕгïţ тħе νąяιâьŀёş ţĥ℮ Ţзŕмīηåŀ ώªŝ šţάґŧеď ẁïŧĥ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Шћéŋ зήдвĺэđ, тĥĕ Τêгмĩлàľ ώįℓŀ ģêηėřąτё â лэŵ ęйνĭґοиmėņť вℓǿċκ ẅђęη ςřέαŧϊņģ пēŵ ŧáъŝ õг ρąņěš ωĩтħ ŧнíŝ рřόƒîĺέ. Ẁђэŋ ðϊśăъĺêđ, ťћē ťάв/φáⁿз ώïļł ìήѕτёąð ιпћεгīţ ţћë νãгіǻьĺэş ţђĕ Τεґмïňął ẅäş şτãѓτэđ ẃìŧћ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - Єⁿâвļε έ×ρęřĩmėⁿтαŀ νίřτüåŀ ŧ℮ŗmîπαľ φдšşţħгóûğђ !!! !!! !!! !!! !! + Êñåвľ℮ ёжφęŗīмéŋţàľ νΐřтϋåĺ ťéяміпаł рāѕśťħгòùĝĥ !!! !!! !!! !!! !! An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - ∆ύđïвłę !! + Ãüđīъłě !! An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. - Ńόŋе ! + Ňőηė ! An option to choose from for the "bell style" setting. When selected, notifications are silenced. - Ðìšάъłĕ φåиė дⁿīmåτĩόηś !!! !!! + Ðïśαъłĕ рåйĕ ăʼnίmдтíόŋŝ !!! !!! Header for a control to toggle animations on panes. "Enabled" value disables the animations. - βľǻčќ ! + Ьłáςќ ! This is the formal name for a font weight. - Ъòľδ ! + Ъσĺď ! This is the formal name for a font weight. - Ćúşťǿм ! + €ūŝŧôм ! This is an entry that allows the user to select their own font weight value outside of the simplified list of options. - Ёхťŕά-Βℓаćќ !!! + Êхŧřǻ-ßŀдςк !!! This is the formal name for a font weight. - Ėжтřǻ-Ьбľđ !!! + Ёжŧгą-ßǿŀð !!! This is the formal name for a font weight. - Єжţŕд-£ĩġћт !!! + Σхтřà-£ιġĥţ !!! This is the formal name for a font weight. - Ĺįģћŧ ! + ₤īğнτ ! This is the formal name for a font weight. - Мĕδίϋm ! + Мęđϊüм ! This is the formal name for a font weight. - Ŋŏřмăł ! + Ñόřmàℓ ! This is the formal name for a font weight. - Śèmī-Βòłδ !!! + Śēмι-Вοľđ !!! This is the formal name for a font weight. - Śεmī-₤įğħţ !!! + Šĕmĩ-₤ìġнт !!! This is the formal name for a font weight. - Ţħĭⁿ ! + Тħĭπ ! This is the formal name for a font weight. - Řεqŭΐřеđ ľîģáŧũŕеѕ !!! !! + Ґèqûίŕęð ŀіġάţüѓęş !!! !! This is the formal name for a font feature. - Ļôċàŀīźэď ƒõѓmş !!! ! + Ľōсаℓϊżèď ƒõямš !!! ! This is the formal name for a font feature. - Ĉοmρθŝιŧіøⁿ/đэсôмρōѕίτїоп !!! !!! ! + Ĉǿмρøŝĭţĭôй/ðêčσmρőśíťįőη !!! !!! ! This is the formal name for a font feature. - Čθńŧęжŧμäł āŀťёŗпªтĕś !!! !!! + Ċóήťēםύдℓ ãℓŧёŗлдťэѕ !!! !!! This is the formal name for a font feature. - Šţåⁿδāŕδ ℓĭġäτύřэš !!! !! + Šтăʼnďàŗđ ľĭġάťùřëѕ !!! !! This is the formal name for a font feature. - Ćòйтĕ×ţũăŀ ℓïğąτųѓęş !!! !!! + Ćǿⁿтĕםŭάℓ ℓĩģàţűřёѕ !!! !!! This is the formal name for a font feature. - Яĕqΰїґеð νªŗіăτîŏň аļťέřⁿдťзś !!! !!! !!! + Γеqцíŕěď νäѓίãτΐōň āłτęґпåŧēŝ !!! !!! !!! This is the formal name for a font feature. - Ќěřñϊлğ !! + Κęřήïήğ !! This is the formal name for a font feature. - Μāѓк φŏŝĩťíôňíлĝ !!! ! + Μǻŕĸ рõşїτіθлįйğ !!! ! This is the formal name for a font feature. - Мαřĸ ťθ мãřĸ φőśįтìøńîпġ !!! !!! ! + Μäѓк ţö мâŗĸ рσśìţїöйΐήğ !!! !!! ! This is the formal name for a font feature. - Đîѕτªⁿċé !! + Ðĩѕŧãń¢ě !! This is the formal name for a font feature. - Δċсэşş ªĺľ аľтëŗⁿàтêѕ !!! !!! + Ǻĉčëśš дℓľ ãĺťęгйαŧēš !!! !!! This is the formal name for a font feature. - Ĉàšê š℮пšιţίνе ƒôѓмŝ !!! !!! + Ċаşè şейśìтîνе ƒόґмś !!! !!! This is the formal name for a font feature. - Ďěñθмΐņάŧŏг !!! + Ďêиómіŋαţǿя !!! This is the formal name for a font feature. - Тεгмĭлдℓ ƒθŕmş !!! ! + Ťзŕmιηăℓ ƒôѓмѕ !!! ! This is the formal name for a font feature. - ₣řαčτϊòηş !!! + ₣ŕãĉŧįόлŝ !!! This is the formal name for a font feature. - Іήίτīáℓ ƒóřмś !!! + Ίйíтīаł ƒόѓmѕ !!! This is the formal name for a font feature. - Мěδīáℓ ƒσяmş !!! + Мзðідļ ƒόямš !!! This is the formal name for a font feature. - Ňŭмэŕàţоя !!! + Ņümзѓåŧŏŕ !!! This is the formal name for a font feature. - Øѓđĩⁿάłš !! + Øřðΐŋаĺѕ !! This is the formal name for a font feature. - Ŕэqύϊřęđ ĉбⁿŧ℮жтμăĺ ãľтêгπàτěš !!! !!! !!! + Ŗēqůїґéð çóńŧĕхŧύǻĺ дŀťэŗňãţêş !!! !!! !!! This is the formal name for a font feature. - Śсįêπţĩƒįċ ιñƒ℮ѓíǿяš !!! !!! + Šсïзйţĭƒîċ ìηƒéгĭǿřś !!! !!! This is the formal name for a font feature. - Şůьśćřīрτ !!! + Şцвşсřĩрτ !!! This is the formal name for a font feature. - Ŝųрêгѕċґĭφţ !!! + Šűφєѓѕćяїрť !!! This is the formal name for a font feature. - Ѕŀâşĥĕď żèґσ !!! + Śĺаѕĥêď żêŗо !!! This is the formal name for a font feature. - Ǻьσνė-ьăśë máґĸ роşíťĩоņιⁿĝ !!! !!! !! + Авθνέ-ъãşэ мâгκ рōŝΐŧіōńíńĝ !!! !!! !! This is the formal name for a font feature. - £âûй¢н śїźε !!! + Ļдűⁿčн šϊżë !!! Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". - Ţĥę ŋüмвēř ôƒ řθшŝ άлð čθℓцmηš ðīşφŀªÿеđ їň ţħê шιπδóщ úφόň ƒīŕŝţ ŀõąδ. Мèāşùřêď ïл ċнąřаćťέŕŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ŧђ℮ ŋúмвęґ οƒ řøẅš аήδ ćǿļύмŋŝ đîśрľаγĕď íη ťĥĕ ωĩйďòŵ µροή ƒířŝτ ĺоàδ. Мέåšυřёď ìи сħăřąçŧèяѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "rows" and "columns" settings do. Presented near "Globals_LaunchSize.Header". - ₤αцņčђ φòśįţιóл !!! ! + Łâµηčĥ ρθśιťĩōπ !!! ! Header for a group of settings that control the launch position of the app. Presented near "Globals_InitialPosX" and "Globals_InitialPosY". - Ťĥз íⁿįтîаĺ φõѕĭţΐθй õƒ тĥë τ℮ŗmíπǻĺ ẅĭήđǿώ úφŏп šтάѓŧüр. Ẅĥεñ ŀάûйςĥïйğ аѕ mª×ϊmϊźεδ, ƒϋĺļ ŝĉŗĕзп, õř ŵíťћ "Ćεŋť℮ŗ бή ĺάϋπсĥ" эńåъļєđ, ţђĭš ΐś úѕęð тǿ ţаŗğėт ŧћε mőņіŧǿř οƒ ïŋτэŗєşť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŧћё īήíťιдŀ рôśϊťîôñ ōƒ ţħέ тĕґміпäł щїлδθш ùρöπ śťåгŧύρ. Ẁђěπ ℓªüйçђìňĝ āŝ ма×ĩmіżєð, ƒúℓĺ ś¢ŕ℮℮п, ôř ẅĩτђ "€ёņŧèг ôñ łąűηçћ" єлáьĺєð, ťĥíѕ ïš ύŝэδ ŧσ ŧàŕģēţ ŧђè mŏηίτóѓ òƒ įⁿт℮ґеşτ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "X position" and "Y position" settings do. Presented near "Globals_LaunchPosition.Header". - Аĺł + Δℓľ An option to choose from for the "bell style" setting. When selected, a combination of the other bell styles is used to notify the user. - Vϊşΰαŀ (ƒĺαŝĥ тąśķъäґ) !!! !!! + Vίѕцдℓ (ƒŀǻşђ ťāšќъăѓ) !!! !!! - ₣ľãšĥ ŧаšķвǻř !!! + ₣ŀαŝħ τâŝќьåя !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the taskbar is flashed. - ₣ĺдşђ ẃίŋδõẁ !!! + ₣ľãşћ шįńđоẅ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the window is flashed. - Αďď ŋēώ !! + Ăδđ пëẁ !! Button label that creates a new color scheme. - Ďєľęτê ĉоĺσŕ ѕςħёmě !!! !!! + Ð℮łéтε çóĺøŕ ѕċĥêmĕ !!! !!! Button label that deletes the selected color scheme. - Ėδΐţ ! + Ëďìт ! Button label that edits the currently selected color scheme. - Ðĕļęтэ ! + Ðέľèтė ! Button label that deletes the selected color scheme. - Лămё ! + Йåме ! The name of the color scheme. Used as a label on the name control. - Τне ńâmз ôƒ ŧћз çőĺбг šçĥèmĕ. !!! !!! !!! + Ŧĥε ʼnάмē öƒ ţнέ ¢őℓòґ ѕçнęмê. !!! !!! !!! Supplementary text (tooltip) for the control used to select a color scheme that is under view. - Ðęľèт℮ φґõƒΐľê !!! ! + Đєļĕţè φѓσƒιŀέ !!! ! Button label that deletes the current profile that is being viewed. - Ťћě йămз ōƒ ťĥё φřôƒĩļё ŧħăţ άφрĕάгş ïл ţĥё ðѓôρδóẅń. !!! !!! !!! !!! !!! + Ţħė ηªмέ бƒ τнз φřσƒîĺē ŧђαţ åρφēąѓš ìπ тħэ ďяόρđōωп. !!! !!! !!! !!! !!! A description for what the "name" setting does. Presented near "Profile_Name". - Ņãmе ! + Ńάmè ! Name for a control to determine the name of the profile. This is a text box. - Пªм℮ ! + Ñăмě ! Header for a control to determine the name of the profile. This is a text box. - Ťřαŋѕράгεʼn¢ў !!! + Ŧяáņśφаŕ℮ńςŷ !!! Header for a group of settings related to transparency, including the acrylic material background of the app. - Βдςкĝŗóüлδ їmäģз !!! ! + Ъą¢ķġřõűήđ ιmąğз !!! ! Header for a group of settings that control the image presented on the background of the app. Presented near "Profile_BackgroundImage" and other keys starting with "Profile_BackgroundImage". - Ĉµŗşόг ! + ȵŗśóř ! Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". - Άδδĩţìŏňäł šèťŧïпġş !!! !!! + Åđðϊτіõήąŀ šěтτįņğŝ !!! !!! Header for the buttons that navigate to additional settings for the profile. - Τэжτ ! + Ţĕ×ţ ! Header for a group of settings that control the appearance of text in the app. - Щīпđόẁ ! + Ẃιйðόώ ! Header for a group of settings that control the appearance of the window frame of the app. - Ōрėή ŷоŭŕ settings.json ƒïĺê. Άŀт+Çℓĭ¢ĸ τó øрёň ýбŭѓ defaults.json ƒίļє. !!! !!! !!! !!! !!! !!! !!! + Öφёή ỳŏüŕ settings.json ƒïℓê. Дĺт+Çłįск ťò όрèň ỳøύř defaults.json ƒιĺз. !!! !!! !!! !!! !!! !!! !!! {Locked="settings.json"}, {Locked="defaults.json"} - Яεπăмě ! + Ŗέʼnåмĕ ! Text label for a button that can be used to begin the renaming process. - Ţĥĩş ćőľõг ŝĉђěmэ ćåńņοτ ьё ďēľєτéď σř гέлăмэď вęςáџśê îт їś îʼnсĺцđęδ ву đέƒāϋĺт. !!! !!! !!! !!! !!! !!! !!! !!! + Ŧнįś ĉбľòŗ ѕςнëmэ çäñπòτ ъē δεļéţ℮ď öř гěⁿаmεď вėсâùŝè ĭţ їš ïŋ¢ŀΰđēđ ьý δĕƒαŭŀτ. !!! !!! !!! !!! !!! !!! !!! !!! Disclaimer presented next to the delete button when it is disabled. - Ўêş, ďęĺέťё ćόľóř šςђéмě !!! !!! ! + Ϋєś, đёŀĕτз čôľőг śçђèmē !!! !!! ! Button label for positive confirmation of deleting a color scheme (presented with "ColorScheme_DeleteConfirmationMessage") - Λґě ýбύ şůгє ўöџ ωâʼnŧ τό đĕłêťё ťĥιš ċôľθѓ śçћĕmε? !!! !!! !!! !!! !!! + Âřе ўόù ŝûŕέ ŷσϋ ώάⁿτ ťŏ ďёℓетé ťђĩš ¢øłòř ѕсĥěмε? !!! !!! !!! !!! !!! A confirmation message displayed when the user intends to delete a color scheme. - Α¢ćзрŧ яэиªмē !!! + Āçċėρŧ ґēиάmε !!! Text label for a button that can be used to confirm a rename operation during the renaming process. - Ĉáⁿ¢εļ яεʼnαмē !!! + Čàηçëł ѓęлаmэ !!! Text label for a button that can be used to cancel a rename operation during the renaming process. - Ѕçħėmèś ðëƒΐⁿęð ћēřè čαй ъé άρφŀĭзδ тǿ γóüя φŕбƒїľéŝ úñđėя ţћё "Âрφёàѓдñĉеŝ" ŝēçτĩбⁿ óƒ ţћэ ргǿƒīľē šёťţιňģş ρąĝèş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ŝċћęмэś ďēƒїńėď нεґе ćàň ъē арρŀïэđ ŧŏ ỳσûг рřσƒίĺĕѕ ûŋδ℮ř тђě "Άφφěãґăлć℮ŝ" ѕĕĉτϊôń оƒ ţħě ρяŏƒїŀё šèτтїńğѕ ρàġęѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A disclaimer presented at the top of a page. "Appearances" should match Profile_Appearance.Header. - Śêτтìⁿğŝ ðéƒιήєď ђëřε шîłľ àφρℓŷ ŧǿ āłĺ φгοƒїŀéŝ цňłėşś тнέý ǻѓέ øνеґгįďδ℮π ьý ă φŕǿƒĩļě'ŝ ŝэтτϊⁿĝš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ѕєτťϊйĝѕ δêƒĭπеđ ĥєřе щίℓļ ªρρĺŷ ŧò ąłŀ ρгǿƒíĺєş µйℓēşş тђεỳ àř℮ óνέѓŕíδđĕň ьỳ а ргόƒιℓ℮'š şєťτїйĝş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. See "Nav_ProfileDefaults.Content" for a description on what the defaults layer does in the app. - Ўёѕ, ðέℓĕтĕ φŕоƒìľé !!! !!! + Ỳēş, đёľèťê φřǿƒιľė !!! !!! Button label for positive confirmation of deleting a profile (presented with "Profile_DeleteConfirmationMessage") - Äřз уόΰ ѕŭŕē ýŏц ŵªⁿť ţθ đєℓэŧе тћïś рřöƒϊľ℮? !!! !!! !!! !!! ! + Ǻѓе ýòũ śúŕ℮ γбû ẅáиτ ţő ďэℓêť℮ ŧĥίś φŕǿƒΐļē? !!! !!! !!! !!! ! A confirmation message displayed when the user intends to delete a profile. - Ŝàνĕ дľļ ũʼnšªνèδ şĕţτĩπģş. !!! !!! ! + Ŝªνě áľŀ цŋśàνзδ śёţтīηğŝ. !!! !!! ! A description for what the "save" button does. Presented near "Settings_SaveSettingsButton". - Ťάъ şωĩťčђêѓ îŋť℮ѓƒåčе ѕтуľê !!! !!! !! + Ťąв şŵìτčнêґ їņτеřƒăćĕ şтŷℓе !!! !!! !! Header for a control to choose how the tab switcher operates. - Ŝêŀëсŧś ẅђіĉħ īйтзŕƒαč℮ ẅĩľľ в℮ ΰŝєď ẃћėʼn ÿоű śẁĭťςħ ŧãвŝ цѕįñĝ ŧħě ќêỳъοåґð. !!! !!! !!! !!! !!! !!! !!! !! + Ŝèľеċτѕ ẃĥісн ϊпŧêŗƒдċè ωĩĺļ ьє ûśзď ώħёʼn ýøµ śщìтċђ тăьŝ ύśîпğ ťћè кĕγъǿåґð. !!! !!! !!! !!! !!! !!! !!! !! A description for what the "tab switcher mode" setting does. Presented near "Globals_TabSwitcherMode.Header". - Şέрдřąţέ ẃíⁿđбẅ, ΐл мőѕŧ ŗęčèйťľў ύŝêð οгδзѓ !!! !!! !!! !!! ! + Şέφдяāτë щīηðοш, ïń мőşт ґêς℮йтľý űśёđ öяďēя !!! !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in most recently used order. - Ŝéφąřàтĕ ẃįŋðőẅ, ìń ťдв šτřїφ ŏгđэŗ !!! !!! !!! ! + Ѕєφåřάтє щīпðǿẅ, ĩи тāв ѕťгιφ όґďеř !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in the order of the tabs at the top of the app. - Тŗªðίŧĭőʼnãľ ʼnǻνїğāťīóή, по šéрäŗдţë щΐйδöẅ !!! !!! !!! !!! + Тŗаðîťϊôηǻļ ñàνĭĝдτîōη, ήб ŝεφάŗàτĕ ώĩʼnδõш !!! !!! !!! !!! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is hidden and does not appear in a separate window. - Ţзхŧ ƒŏґмªтş ţǿ ςőφỳ τό ŧĥз čĺïρьóάгđ !!! !!! !!! !! + Ţехτ ƒöгмǻťŝ ťó сбφÿ ŧø ťћę ¢ŀįφъøдґď !!! !!! !!! !! Header for a control to select the format of copied text. - Рŀāĩπ тěхт ôлĺý !!! ! + Рļǻїи ŧєхţ ôⁿℓỳ !!! ! An option to choose from for the "copy formatting" setting. Store only plain text data. - НŢΜĹ ! + ĦТΜ₤ ! An option to choose from for the "copy formatting" setting. Store only HTML data. - ЃŦ₣ + ŘŢ₣ An option to choose from for the "copy formatting" setting. Store only RTF data. - βòтћ ĦŤΜĻ åʼnđ ГŤ₣ !!! !! + βŏţћ ΗŦМ£ аήď ГŢ₣ !!! !! An option to choose from for the "copy formatting" setting. Store both HTML and RTF data. - Ρľĕąśê ćнθöšє ą ď탃ęřепŧ ňǻmê. !!! !!! !!! + Рĺėаšе ĉћôòşέ ã ðίƒƒэřέʼnť лªmě. !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the subtitle and provides guidance to fix the issue. - Τĥїš ċöℓог ѕснěm℮ ŋáмз īѕ дļřĕáđγ ΐή υşέ. !!! !!! !!! !!! + Τћιѕ čöłǿґ ščĥęmε ńâмē íŝ ǻľřέåðγ îπ ύŝε. !!! !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the title, and explains the issue. - Àŭţŏмǻτїсāļłý ƒóсūŝ φαņέ оñ mσŭѕė ђøνêř !!! !!! !!! !!! + õτómãţιĉāłļỳ ƒŏςûš φǻйë бп мôüśĕ ћöνēя !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. - Раņę ãŋĩмάŧΐôпŝ !!! ! + Рäŋě αņîmαţϊσňš !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. - Ąłщáγѕ δĭѕφľâÿ αη ìçõń ìπ ţĥэ иõŧîƒĩςαťîöņ âŗěå !!! !!! !!! !!! !! + Άŀẅåγѕ đîśφľãý дπ ιćőñ īη ţĥе лοŧιƒϊċªŧĩοʼn άгέą !!! !!! !!! !!! !! Header for a control to toggle whether the notification icon should always be shown. - Ĥìðě Тéґmΐňâł įⁿ тħέ ńóťïƒιćâτіòη αгèǻ ωђĕñ ίţ ΐş mîиímïźėð !!! !!! !!! !!! !!! !!! + Нϊďε Ţέѓmìлаļ ιπ ťнę ⁿöтíƒіćǻťіόπ ăřęд ώнєŋ ĭŧ ίš mιлїmīźэð !!! !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized. - Ŗέśëţ ŧø ΐпħēѓїţзð νäľűĕ. !!! !!! ! + Яěşεŧ ţθ ΐŋђеřіŧēď νãℓųε. !!! !!! ! This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button. - Тεŕмíήąļ ςøłσѓѕ !!! ! + Тёґмιйąľ ćθłόřś !!! ! A header for a grouping of colors in the color scheme. These colors are used for basic parts of the terminal. - Ŝуşţęм ¢øłόřŝ !!! + Şỳŝтĕм ςοŀбŕş !!! A header for a grouping of colors in the color scheme. These colors are used for functional parts of the terminal. - Çôℓôřѕ ! + Ćθľöŕś ! A header for the grouping of colors in the color scheme. - Ґеŝэт ťò νǻłũэ ƒřøm: {} !!! !!! + Яęŝёт τǿ νãŀΰз ƒŗőm: {} !!! !!! {} is replaced by the name of another profile or generator. This is a text label on a button that is dynamically generated and provides more context in the {}. - Ѕħőώ аŀļ ƒθπтš !!! ! + Šћощ ăłł ƒóņтś !!! ! A supplementary setting to the "font face" setting. Toggling this control updates the font face control to show all of the fonts installed. - Īƒ ĕʼnåъł℮ď, ŝĥóẅ åļļ ìňśţаŀľеđ ƒǿпťѕ íη ŧĥε ļīşŧ ăвŏνė. Ôŧħęřŵįśë, ǿñłý ѕħθω ŧнё ĺįşτ ǿƒ мοⁿσšρą¢ě ƒôπţŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Їƒ èñаьļ℮δ, ѕђоẅ äŀľ ΐņśтªℓĺёď ƒőņτš ίⁿ τћè ŀĭѕţ âъσνê. Φţнëŗшĭşе, óйĺў ŝнøŵ τĥє ĺιśť øƒ мöńоšρäςè ƒòпŧŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts". - Ćřĕǻţз Ãрφεåѓдⁿçε !!! !! + Ċŗέăτэ Λφφёàřāņ¢è !!! !! Name for a control which creates an the unfocused appearance settings for this profile. Text must match that of "Profile_AddAppearanceButton.Text". - Сѓεâťэ αη μηƒбçµѕёď áρφëаґаʼn¢ĕ ƒõř ŧħіš ρѓоƒíļê. Τћίš ωίļℓ ьê τћé åррёäŗǻⁿćε òƒ ţħе φřσƒīĺę ωђèń ĭţ ϊş îņǻсŧīνè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + €ř℮àŧë άŋ úņƒоćùŝєð αφрéāґäлčê ƒσґ тћіš ρгŏƒϊļę. Τнΐŝ ŵїĺľ вê τħë дρρěаřªŋçě όƒ тħě ρřоƒΐľė ŵћéл ϊţ ĭŝ įпãċţîνэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the create unfocused appearance button does. - Ďэŀзŧé Ǻφφĕąгǻήčĕ !!! !! + Ď℮ļέŧэ Äφφέăřдπĉę !!! !! Name for a control which deletes an the unfocused appearance settings for this profile. - Ðэłэτє τћě ϋήƒбćύŝέδ ãφрêдŕåʼnċê ƒбř τђīѕ ρѓοƒίļ℮. !!! !!! !!! !!! !!! + Đέļ℮ťё ţђę цñƒόćũŝĕď ãφрзαŗåʼn¢ē ƒőř ţђїš ρгøƒϊłē. !!! !!! !!! !!! !!! A description for what the delete unfocused appearance button does. - Ύёѕ, ďēłěťë кêў ьіиδįŋğ !!! !!! + ¥еš, δέłëţε кéÿ ъіňðīηġ !!! !!! Button label that confirms deletion of a key binding entry. - Ąŗέ уόū şųřε уοΰ ẃªʼnť ťô đёŀéτê ŧħΐš κ℮ý ьīʼnđіŋģ? !!! !!! !!! !!! !!! + Λяз γǿű šůřέ ýόű шåňţ ţο ðēĺĕтё ţнîś кеу вìŋδîлĝ? !!! !!! !!! !!! !!! Confirmation message displayed when the user attempts to delete a key binding entry. - Іňνáłïð κ℮ŷ ¢ђōяď. Рłêάѕê эητзг а νâℓĩδ ĸęÿ сћŏřď. !!! !!! !!! !!! !!! + Īņνãŀίδ κєŷ ¢нòгď. Ρłêªѕ℮ зňŧëř à νāŀίđ ķéÿ ċђόґď. !!! !!! !!! !!! !!! Error message displayed when an invalid key chord is input by the user. - ¥èş + Ỳęš Button label that confirms the deletion of a conflicting key binding to allow the current key binding to be registered. - Ŧђε ρґòνìđéð ќэÿ ĉħθяδ ïš àĺяэáđу ьёĩņĝ ŭšєď вÿ ţħз ƒǿļŀöщîⁿğ à¢τίŏň: !!! !!! !!! !!! !!! !!! !!! + Тђê ρŕόνíđêď ĸęу сĥояð ìś ǻľѓзąδŷ вěϊñğ ùšěð вý ţђě ƒŏŀℓόщíńģ αčŧίôή: !!! !!! !!! !!! !!! !!! !!! Error message displayed when a key chord that is already in use is input by the user. The name of the conflicting key chord is displayed after this message. - Шőűľδ ўоύ ľϊκë τθ ονєřẅřïťė іţ? !!! !!! !!! + Ẁόŭℓð ýôŭ ľїĸê ťô őνêŗẁґìтé їť? !!! !!! !!! Confirmation message displayed when a key chord that is already in use is input by the user. This is intended to ask the user if they wish to delete the conflicting key binding, and assign the current key chord (or binding) instead. This is presented in the context of Actions_RenameConflictConfirmationMessage. The subject of this sentence is the object of that one. - <ϋйпáмĕδ ĉòmмǻňď> !!! !! + <ũήηаmэď čòmмăйđ> !!! !! {Locked="<"} {Locked=">"} The text shown when referring to a command that is unnamed. - Äς¢ёрτ ! + Ăс¢ēрť ! Text label for a button that can be used to accept changes to a key binding entry. - Ĉāпсзļ ! + Ĉάήĉĕľ ! Text label for a button that can be used to cancel changes to a key binding entry - Đεļέтê ! + Ðзľęτе ! Text label for a button that can be used to delete a key binding entry. - Еďïŧ ! + Έđΐŧ ! Text label for a button that can be used to begin making changes to a key binding entry. - Ăđδ ήеẁ !! + Άďð ŋзш !! Button label that creates a new action on the actions page. - Αсτĭòη ! + Åсţїòη ! Label for a control that sets the action of a key binding. - Ίпφůŧ ýσüг đēŝĭŗēď ķеýьôǻѓđ šнóґтċµŧ. !!! !!! !!! !! + Ĩñφΰţ ўθűř ðέšιѓĕđ κ℮ýьόαŗđ śĥōřŧčŭŧ. !!! !!! !!! !! Help text directing users how to use the "KeyChordListener" control. Pressing a keyboard shortcut will be recorded by this control. - ŝħθгŧĉΰτ łΐѕтĕήэя !!! !! + şћσяŧçűт łīѕŧзлëя !!! !! The control type for a control that awaits keyboard input and records it. - Şĥοŗŧċŭţ !! + Śћôŗτċύŧ !! The label for a "key chord listener" control that sets the keys a key binding is bound to. - Τεхţ ₣òяmāтţíⁿğ !!! ! + Τěхţ ₣θŗmάţŧΐŋģ !!! ! Header for a control to how text is formatted - Ìņţęňśз ŧëхŧ ѕţўℓз !!! !! + Íήťêʼnŝē τε×т śтўŀë !!! !! Name for a control to select how "intense" text is formatted (bold, bright, both or none) - Їńтзйѕэ ŧехŧ şŧγļз !!! !! + Ίŋтēņšе ŧєжť ŝτỳłē !!! !! Header for a control to select how "intense" text is formatted (bold, bright, both or none) - Ņòйз ! + Νõńё ! An option to choose from for the "intense text format" setting. When selected, "intense" text will not be rendered differently - Βòℓð ƒõήţ !!! + Бσłđ ƒòʼnŧ !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as bold text - Ъřїģħŧ сοĺøřŝ !!! + ßŗΐğћŧ ĉοĺσŗŝ !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered in a brighter color - Ьōłð ƒøйτ ώįŧĥ ьŕϊģћτ сθľбřŝ !!! !!! !! + Ьоľð ƒθиτ ώίŧћ вгĩĝĥť ¢σĺöřѕ !!! !!! !! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color - ∆ΰťθмąτіčαļłŷ ћίđé ẁϊйδόш !!! !!! ! + Δύτômáťї¢äℓĺŷ ћįďэ ŵîηđøш !!! !!! ! Header for a control to toggle the "Automatically hide window" setting. If enabled, the terminal will be hidden as soon as you switch to another window. - ΃ зŋåьľэδ, ŧħė тéŗmίʼnăľ щїĺŀ ъé ĥіđð℮ή ªŝ śóσň ãŝ ÿθŭ ŝẅĩτčђ ŧô ãпõτħёř ẃĩήđøщ. !!! !!! !!! !!! !!! !!! !!! !!! + Іƒ ėπąьľéđ, ťћë ťëřmíπăŀ ẁїļł ь℮ ћιðð℮ñ āŝ ѕбóň ăŝ γöŭ ѕщĭť¢ĥ τò áйόтĥзґ ẅіňδóш. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Automatically hide window" setting does. - Ŧђĩѕ ςóľǿя ŝçћěме čåπŋóţ ьê δěłĕŧзδ ъέċάυśè ΐτ ϊś ĩʼn¢ℓùδэđ ву ðёƒăџłŧ. !!! !!! !!! !!! !!! !!! !!! + Ŧћϊš ĉбļōг ŝ¢ħęмé ¢ăňņöţ ъє δěłèť℮đ ьєçªúšё īŧ íş ίⁿςłůđėđ ъÿ ðéƒãμℓт. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to delete the selected color scheme when that functionality is disabled. - Ţħіś ςőľσŗ ѕ¢нεмё çдņʼnóт ьз я℮ņàмєď вёĉăϋşэ ìŧ ïѕ ĩñçłџđĕď ьÿ δêƒąϋľť. !!! !!! !!! !!! !!! !!! !!! + Ťћΐš ċôŀöŕ ŝĉħēmê ċαήπǿţ вê ѓёñåmеď ъēсάµšé įţ îŝ īπçℓŭđêδ ьу δěƒаūŀť. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to rename the color scheme when that functionality is disabled. - ðëƒąцłŧ !! + ðεƒáΰŀτ !! Text label displayed adjacent to a "color scheme" entry signifying that it is currently set as the default color scheme to be used. - Şëţ άŝ ðзƒăůŀŧ !!! ! + Śėŧ áŝ ďĕƒâųłτ !!! ! Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use. - Ẃăги ẃнëй ćŀоšΐиġ мõгè ŧĥąπ оńέ тāв !!! !!! !!! ! + Ẅąřп ώнêń сłόšίлģ mŏяé τћăʼn òлэ τªь !!! !!! !!! ! Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open. - Ẃίлðöщš Ťêґmιņâℓ īś гũńйìňğ ìп φôŕŧáьłє мθđ℮. !!! !!! !!! !!! ! + Ẁïηðоẁѕ Тĕřмĭиαľ ίş ѓùņŋїʼnġ їŋ ρоřţäъĺэ mőðе. !!! !!! !!! !!! ! A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder. @@ -1754,15 +1762,15 @@ {Locked} - Łęάяй mθгě. !!! + ₤ěªѓл mŏřе. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - Щāяήϊņģ: !! + Ẃåяņĭηğ: !! Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - Ćћθόśїήġ ã ņŏń-мǿńбѕφàсєđ ƒőηť ẃϊŀŀ ĺĭќєŀу ŕзѕύĺţ îπ νĩšΰάŀ ąŗτїƒąсťŝ. Ųśе дτ ўòϋŕ öẁŋ δĭŝсґęŧїõπ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Čћōбŝїпģ ā ⁿбņ-móήǿŝрąċеď ƒσиŧ ωίľℓ ľїĸєľў ŕēŝџĺţ íñ νіšΰâŀ ªгтΐƒãςτš. Ųѕе ąτ ỳóũѓ όẃň ðĭѕĉѓēτĭõń. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw index 3dfac251171..822917147c7 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw @@ -118,528 +118,528 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - €ŕέдťє Ñēш Бμţťőñ !!! !! + Čяёατé Νêŵ Ъũťτøή !!! !! - Ńëώ ёmρŧỳ ρябƒįĺè !!! !! + Νěω éмρтŷ рřøƒįĺé !!! !! Button label that creates a new profile with default settings. - Ďůрľΐĉăţē ά φяŏƒіℓê !!! !!! + Ðũрļìćατé ª ряσƒīℓ℮ !!! !!! This is the header for a control that lets the user duplicate one of their existing profiles. - Đцφŀťĕ βúŧťбη !!! ! + Ðџφłįĉǻţе Βμтťθñ !!! ! - Ðŭρľïςăŧέ !!! + Ðџρĺĭĉãţε !!! Button label that duplicates the selected profile and navigates to the duplicate's page. - Ţĥїś ćôļőѓ šςћémē īѕ φąŗŧ σƒ тћē вџīłť-īή şεţţіиĝś οř ăи їиśťàŀłзď êхţéπšíõл !!! !!! !!! !!! !!! !!! !!! ! + Τĥìѕ ςõļōř şснэmё їś φäřт оƒ ţђĕ ъϋīľŧ-ìʼn şéŧťΐлğѕ ŏř ай īйѕτáľľэδ ёжťзʼnѕĭŏи !!! !!! !!! !!! !!! !!! !!! ! - Тο мáкё ¢ħαйğэš τö тђïš čøĺŏя śĉĥзmę, ýóџ mûśŧ мäĸë α čσρŷ öƒ їť. !!! !!! !!! !!! !!! !!! ! + Ŧб мǻκë ćнàʼnģěş τθ ţнïś ¢õĺòŕ ščĥêmë, γòũ mŭšť мăκę ą ćοφŷ öƒ īт. !!! !!! !!! !!! !!! !!! ! - Мǻķέ α čóφý !!! + Μåκę α ċθрỳ !!! This is a call to action; the user can click the button to make a copy of the current color scheme. - Ŝέţ çōĺōŕ ŝĉħėm℮ ąş ďėƒªύĺť !!! !!! !! + Šéţ ¢θļǿŗ šĉђєме ąş đёƒдųľţ !!! !!! !! This is the header for a control that allows the user to set the currently selected color scheme as their default. - Àррļу ŧћĭš ćοĺόŕ ѕćнэmз ťο аļℓ γòūř φŕöƒîĺĕş, ъγ đёƒäџŀτ. Їήđíνíďúªľ рґŏƒīļêѕ ćäñ şтīłł şёļĕçт τĥέįґ õẅñ сòĺσя šćĥēм℮ѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ∆ρрĺγ тћїš ċбłøř şĉħēmĕ τō áŀł ỳǿüг ρґôƒíľέś, вŷ ďėƒªüłт. Ĩⁿðĩνϊďùãľ ρŗōƒìľèş сαп ŝтΐľĺ šзℓëĉт тћέįŗ ŏώñ сöłòŗ ščнëмěş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description explaining how this control changes the user's default color scheme. - Ѓēňäмĕ ćŏŀоř ѕćĥèmз !!! !!! + Яэńдmê ¢òłŏŗ śςнęмê !!! !!! This is the header for a control that allows the user to rename the currently selected color scheme. - Ьąςкğѓбŭлδ !!! + Вâċĸģŕòůñδ !!! This is the header for a control that lets the user select the background color for text displayed on the screen. - Бŀāĉк ! + Вľäčќ ! This is the header for a control that lets the user select the black color for text displayed on the screen. - Вŀüė ! + Ьℓů℮ ! This is the header for a control that lets the user select the blue color for text displayed on the screen. - βŗΐğђŧ ьĺªĉĸ !!! + Βѓïĝнť вļàčķ !!! This is the header for a control that lets the user select the bright black color for text displayed on the screen. - βгΐĝнт ъℓũе !!! + Ьяìğћτ ъļΰэ !!! This is the header for a control that lets the user select the bright blue color for text displayed on the screen. - Ьřīğнт ¢ÿãл !!! + Ъѓīğнŧ ĉỳâп !!! This is the header for a control that lets the user select the bright cyan color for text displayed on the screen. - Ьгίģћт ğŕēèη !!! + Βŕίġнť ĝŗèεň !!! This is the header for a control that lets the user select the bright green color for text displayed on the screen. - Бřіġĥŧ ρùгρŀэ !!! + Бґїĝĥť рџŗрŀ℮ !!! This is the header for a control that lets the user select the bright purple color for text displayed on the screen. - Ьґîġђŧ ѓēδ !!! + Бѓíģћт ґёđ !!! This is the header for a control that lets the user select the bright red color for text displayed on the screen. - Ьŗιġħţ ώнίτе !!! + Бяìġђŧ ẃнįτë !!! This is the header for a control that lets the user select the bright white color for text displayed on the screen. - ßŗіĝнτ ŷęłļőщ !!! + Бŗιģнť ŷëľłòщ !!! This is the header for a control that lets the user select the bright yellow color for text displayed on the screen. - Ćύřŝοř ĉσłόг !!! + Ĉúґšŏř ċôľθŗ !!! This is the header for a control that lets the user select the text cursor's color displayed on the screen. - Ćўáñ ! + €ŷαп ! This is the header for a control that lets the user select the cyan color for text displayed on the screen. - ₣őřěĝгǿυńδ !!! + ₣ǿŕέġŕōцņδ !!! This is the header for a control that lets the user select the foreground color for text displayed on the screen. - Ĝřêêй ! + Ğяĕεή ! This is the header for a control that lets the user select the green color for text displayed on the screen. - Рųŗρľ℮ ! + Ρϋгрŀέ ! This is the header for a control that lets the user select the purple color for text displayed on the screen. - Ŝєłëçţΐőп ьãĉкğřоυпδ !!! !!! + Śέľéςτїóи ъāčķĝřøüπđ !!! !!! This is the header for a control that lets the user select the background color for selected text displayed on the screen. - Řéď + Гēð This is the header for a control that lets the user select the red color for text displayed on the screen. - Щнīťě ! + Ŵħΐťě ! This is the header for a control that lets the user select the white color for text displayed on the screen. - Ϋëļℓóẁ ! + Ỳεľĺŏώ ! This is the header for a control that lets the user select the yellow color for text displayed on the screen. - £ăňĝüªġз (ŗēqüіѓėѕ ŗёℓαύйčĥ) !!! !!! !! + Łáпġŭǻğέ (геqúіґēѕ ŗэŀāūńćĥ) !!! !!! !! The header for a control allowing users to choose the app's language. - Ŝĕℓзċťŝ à đïѕρłáγ ľãйğџªĝε ƒθг ŧнė áφρľίćαŧíóņ. Тĥίş ẃιℓĺ õνěґґϊδë ỳòµг δєƒāџĺť Windows ïʼnτèŗƒά¢з ŀªиĝцåğз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ŝëĺё¢ţş ă đΐŝφłдў łąйğűäĝė ƒбř τћз ąφрľíςāťΐöη. Ťĥįš шΐłļ öν℮ŗŗϊδė ỳθϋŗ ď℮ƒдύłт Windows ϊиţěґƒдçē ľăʼnģúåğê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description explaining how this control changes the app's language. {Locked="Windows"} - Ůş℮ ѕўśţėm ď℮ƒäùŀτ !!! !! + Ųšέ ѕŷşτėm ðêƒåųℓτ !!! !! The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead. - Ăłωāŷŝ ѕђσŵ τąъš !!! ! + Ãĺωáÿś şĥοш тáвѕ !!! ! Header for a control to toggle if the app should always show the tabs (similar to a website browser). - Ẅħěʼn δīŝąьłέđ, ţђз ţăь ьäґ ŵïłŀ äррэăя шħëⁿ ā ηěω ţäъ íş ςґéαţεď. Çáńйǿţ ъĕ đĭşǻвļéď ẅнīŀé "Ħĩδз ţћ℮ τïţłė вäř" ϊś Ŏπ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ŵђěη ðΐśàьľėð, ŧћ℮ τаъ ьáг ŵïĺℓ ąρρêαґ щħěи á ñзш ţăв ιѕ сŕёάť℮đ. Çăññбτ вέ đĭѕдьłєδ ẅћιłэ "Ήįđё ŧнĕ тїτĺĕ вǻř" ĩş Óη. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "always show tabs" setting does. Presented near "Globals_AlwaysShowTabs.Header". This setting cannot be disabled while "Globals_ShowTitlebar.Header" is enabled. - Рõѕϊтíóп ǿƒ йĕẁĺŷ ςřεâтėδ ŧãвѕ !!! !!! !!! + Рøšįтíóñ бƒ ňêẁĺў ċŕėâŧêδ τąъѕ !!! !!! !!! Header for a control to select position of newly created tabs. - Ŝр℮сįƒíèѕ ẅнέŕє ñėŵ ťàъś ãрρęàг įñ тћė ŧâъ ŗŏŵ. !!! !!! !!! !!! !! + Ѕφéсίƒįêś ẁнэґε пέώ τàьŝ âρφзаŗ įπ τћē τàь гθω. !!! !!! !!! !!! !! A description for what the "Position of newly created tabs" setting does. - Аƒţеř ŧħĕ ŀåšт τáв !!! !! + Ǻƒŧέŕ ţĥè ŀąŝŧ ταь !!! !! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the last tab. - Ăƒтéя ťнé ċùгŗêйт тάъ !!! !!! + Ńŧеř тћë ¢ϋřřēⁿτ ťāь !!! !!! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the current tab. - Дŭтσmàťí¢āľŀу çőφу ş℮ĺéčŧìοń ŧö çļιρвòάґď !!! !!! !!! !!! + Ãûτθмáτìςãŀļỳ ¢ǿрŷ ѕзĺэçтíοʼn τó сłìρъбäґđ !!! !!! !!! !!! Header for a control to toggle whether selected text should be copied to the clipboard automatically, or not. - Δūтόmãтιςàŀŀу ðĕţесť ЦГ£ѕ ąиδ мαķě тħєм çĺїĉќåьľе !!! !!! !!! !!! !!! + Ăΰťóмªťίčдļĺγ δ℮ťĕčţ ÜЃĻš āηð mâĸέ тћêm ςĺι¢ķáьŀ℮ !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should automatically detect URLs and make them clickable, or not. - Γємöνė ťяâĭłīňġ ώћīŧé-ѕρáçє ΐñ ŗëćţаπğŭĺαя ѕёľё¢τїòń !!! !!! !!! !!! !!! + Ґěmøνë ţяâīľìйġ ẃнιтê-ŝφáĉз ĭŋ геćταņĝűĺдѓ šēĺéçŧĩоñ !!! !!! !!! !!! !!! Header for a control to toggle whether a text selected with block selection should be trimmed of white spaces when copied to the clipboard, or not. - Яęмόνè ŧгâĩŀїпĝ ωĥîťέ-ŝрãċę ώћеń рäşťϊπğ !!! !!! !!! !!! + Гęмőνĕ ťŕąιℓíŋģ шнîŧέ-ѕрăċ℮ ŵђεл φªşţíήĝ !!! !!! !!! !!! Header for a control to toggle whether pasted text should be trimmed of white spaces, or not. - Ьεŀőẅ дґê тħě ćűřґ℮ήťŀў вθûήđ ĸєỳš, ẃħíςћ ĉåņ ье môðϊƒϊęð вÿ ěðΐţιпģ ŧĥ℮ ЈŚФŅ šèŧťіŋğŝ ƒιļě. !!! !!! !!! !!! !!! !!! !!! !!! !!! + βєłōщ ãŕ℮ ŧђė ςũѓґэñτĺý вǿµήď кëỳѕ, ŵђΐĉђ ¢ąή вé mǿđíƒίєď ьў éđīţĩņğ тħз ЈŚÖŅ şêŧŧĩήĝş ƒιłē. !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer located at the top of the actions page that presents the list of keybindings. - Ðëƒǻûĺŧ ρяоƒΐļę !!! ! + Ďєƒαΰľτ φѓŏƒїĺē !!! ! Header for a control to select your default profile from the list of profiles you've created. - Рŕοƒíļē тћáť óρęñŝ ωħзл ćŀĭćкĭńĝ τĥё '+' ĭçøⁿ õѓ вγ тÿφĭňğ ţћé ņëщ тàв ķèў вîņđìйġ. !!! !!! !!! !!! !!! !!! !!! !!! + Ргôƒïļέ ŧћǻţ öφéńş ẃћėи ¢łιĉĸĭπĝ τђё '+' įĉóŋ ôř ьγ ťўφíήġ ŧħε ηзщ τāв ķзÿ ъíňδįňġ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the default profile is and when it's used. - Ďзƒªúłτ ťёřмíйǻℓ āрρŀίčαťîôл !!! !!! !! + Đеƒдûľť ťĕřmìηáł αφрŀіćàţīθñ !!! !!! !! Header for a drop down that permits the user to select which installed Terminal application will launch when command line tools like CMD are run from the Windows Explorer run box or start menu or anywhere else that they do not already have a graphical window assigned. - Τħ℮ тĕŗmїпāļ ǻррŀĩçàţíθŋ ťћдţ ľāμńċћ℮š ŵħėŋ ą čòmmāⁿδ-ŀίйĕ ąφφĺĩčàťїõň ίš ґμη ẃΐтħбũŧ äи ехіŝţĭʼnğ şэŝѕіσπ, ļíќę ƒяöм ŧћê Ѕταřť Мєиΰ ôř Ґμň δîäłσģ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ţнė τěŗmĭηдŀ äρφłïćāτíøи тħąţ ℓдũŋčħèś шћэņ ā çσmмάⁿđ-ľĭйé ǻρрłί¢ąţìŏл īś ґūⁿ ώîτĥöυţ āή ēхîśтίηģ ŝëśѕïοή, ĺікè ƒřбм ŧĥė Ŝтªгт Μëпū σя Ŕùʼn đίдľôġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - Ŕĕďŕαш èпŧīřē ѕςгèέл ẁн℮ň ðïšφĺǻý ũрδатéŝ !!! !!! !!! !!! + Ŕεðŕåŵ эηťĩřё śċяеёπ ωћęñ ðіşрŀâŷ υφđãŧēş !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - Щђέñ δīşâвĺëð, тђε ţéŗmïⁿâĺ щīľļ řěиďєг ōņłў τћę ũрðдţэѕ ťσ τћє ѕсŕéєñ ьęţωёêņ ƒŗămзş. !!! !!! !!! !!! !!! !!! !!! !!! ! + Ẁĥĕŋ ďιśàвłèđ, ŧђ℮ ŧēяmíπäŀ ẁįłļ ѓēʼnďεř ǿиłý ťħё цφđατèѕ ťǿ ŧħє ŝçŗēěń ьеŧщєęⁿ ƒŗăмéş. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". - Ċōŀμmлś !! + €õľümŋѕ !! Header for a control to choose the number of columns in the terminal's text grid. - Яøẁś ! + Ŕőŵš ! Header for a control to choose the number of rows in the terminal's text grid. - İʼnïŧìдľ Čθłùmⁿś !!! ! + Ίʼnįτĩáł Сõľџмⁿŝ !!! ! Name for a control to choose the number of columns in the terminal's text grid. - Íʼnĩτϊàĺ Ґõώŝ !!! + Īиįťϊâľ Ґбώѕ !!! Name for a control to choose the number of rows in the terminal's text grid. - Χ рοšιŧïой !!! + Ж рǿşіŧіση !!! Header for a control to choose the X coordinate of the terminal's starting position. - Υ рθśĩťïοñ !!! + Ỳ рθѕîţїõʼn !!! Header for a control to choose the Y coordinate of the terminal's starting position. - Ж ρóŝіťĩоň !!! + Χ φοśїтіôʼn !!! Name for a control to choose the X coordinate of the terminal's starting position. - Еńţ℮ѓ тħ℮ νάłüê ƒőŗ ŧĥэ ×-ĉŏǿяδīñâтé. !!! !!! !!! !! + Ёʼnŧзг ťĥė ναľŭє ƒбґ ŧħê ж-¢όøŗδїпªтэ. !!! !!! !!! !! Tooltip for the control that allows the user to choose the X coordinate of the terminal's starting position. - Χ + Ж The string to be displayed when there is no current value in the x-coordinate number box. - Υ роşîţϊöñ !!! + Υ роśїтΐóń !!! Name for a control to choose the Y coordinate of the terminal's starting position. - Єήťěř τћέ νáļûė ƒøŗ τнè ÿ-ςбóřđíπãťė. !!! !!! !!! !! + Еňτéг ŧħз νäľűε ƒόŕ ŧнэ ý-ςбοřδϊňªţé. !!! !!! !!! !! Tooltip for the control that allows the user to choose the Y coordinate of the terminal's starting position. - ¥ + The string to be displayed when there is no current value in the y-coordinate number box. - Ł℮т Щїлďοŵѕ ďεčϊδê !!! !! + Ľēŧ Шĩŋďöщş ďėĉįďэ !!! !! A checkbox for the "launch position" setting. Toggling this control sets the launch position to whatever the Windows operating system decides. - Ĩƒ ėήāъĺэδ, ūŝэ тħė ŝÿśŧĕm ðęƒäџℓŧ ŀäŭлċђ φθšїŧíǿñ. !!! !!! !!! !!! !!! + Īƒ éπāъĺеđ, ûşë ţћē šγşтēm ðėƒâúľŧ ŀăúʼnĉћ ρθšιťίôʼn. !!! !!! !!! !!! !!! A description for what the "default launch position" checkbox does. Presented near "Globals_DefaultLaunchPositionCheckbox". - Ẁн℮π Τёřmīńâŀ ѕţдяţş !!! !!! + Ẁĥěи Ţ℮řміŋąĺ šťǻřţŝ !!! !!! Header for a control to select how the terminal should load its first window. - Ẃнäŧ šĥōцļď ьĕ şнőшń ẅĥèⁿ ťħέ ƒіřšť тзямìπäŀ ìş čŕзåτěð. !!! !!! !!! !!! !!! ! + Ẁћáţ śћόũļď ъ℮ ŝħóωη ώћêñ ŧĥè ƒïŗŝт ţëřмїйāł īś сř℮ăтέδ. !!! !!! !!! !!! !!! ! - Õрêⁿ ă ţâв ώĭţћ ŧĥë ďеƒăùľť φŗθƒĩľэ !!! !!! !!! ! + Όρèń α ŧãв ώΐŧĥ ŧђę ďėƒáџľť φѓőƒïł℮ !!! !!! !!! ! An option to choose from for the "First window preference" setting. Open the default profile. - Ōрěи ẃίйδбώš ƒгом á рѓėνįõџś şеšѕΐōи !!! !!! !!! ! + Ôрêή ẃιʼnďøωѕ ƒяőm а ρřëνΐŏũѕ şέśśĭóň !!! !!! !!! ! An option to choose from for the "First window preference" setting. Reopen the layouts from the last session. - Łάµñċћ мσđé !!! + £ăûⁿςн мσðε !!! Header for a control to select what mode to launch the terminal in. - Ηόω τђέ τêŕmΐñдľ ŵїļŀ áрρ℮ăř óή łάūη¢ђ. ₣ōςůš шίĺļ нїďè τћε тǻвś āňđ τîтłĕ ъαг. !!! !!! !!! !!! !!! !!! !!! !!! + Ĥòŵ ŧħз тéґмíйäℓ щιľℓ áρр℮âя οй ļãúήςħ. ₣оςůś ώïłľ ĥϊðε ţħĕ τãвѕ åńδ ťіτłē ьāř. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ľдúⁿċн ρàřдmεтэřŝ !!! !! + Ĺдϋиċђ φāŕąмéτéŕš !!! !! Header for a set of settings that determine how terminal launches. These settings include the launch mode, launch position and whether the terminal should center itself on launch. - Ŝеţтιⁿģŝ ţћàť ςόⁿťяǿľ ħθщ ťĥē τėŕmîŋαℓ ŀäΰηςĥēš !!! !!! !!! !!! !! + Ş℮тτіήĝš ŧђǻτ ċŏñţřóŀ ĥθŵ τħę тэŕмįⁿàł ℓâŭńсĥєѕ !!! !!! !!! !!! !! A description for what the "launch parameters" expander contains. Presented near "Globals_LaunchParameters.Header". - Łâϋπćĥ mόđë !!! + Łăυпċђ мσďë !!! Header for a control to select what mode to launch the terminal in. - Нőẅ ťћė ţеŕмїʼnαℓ ώĭļļ ąрρёář óп ŀǻџήçн. ₣θςūš ώίļľ ĥìďê тħз ţάъš àʼnđ ťĩŧļę ъåґ. !!! !!! !!! !!! !!! !!! !!! !!! + Ήőẅ тнê τēŕmíиаŀ щΐŀļ ăρφèάř óπ ĺªμʼnĉн. ₣øčųѕ ẅιļĺ нìďе тћé τăьŝ àñð ŧϊτℓê вăř. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ďеƒªμℓт !! + Ðéƒăųℓт !! An option to choose from for the "launch mode" setting. Default option. - ₣џłľ ŝćґёëй !!! + ₣υŀļ şċřěеⁿ !!! An option to choose from for the "launch mode" setting. Opens the app in a full screen state. - Мąхîмïźэð !!! + Мд×īмìżěð !!! An option to choose from for the "launch mode" setting. Opens the app maximized (covers the whole screen). - Йèώ ìлŝţāπсе ъęђáνїõґ !!! !!! + Ń℮ŵ íηŝŧâŋçэ вéĥάνιог !!! !!! Header for a control to select how new app instances are handled. - Ċōήŧяõŀŝ ђőŵ πêŵ τĕřmїηªļ іпšŧàήčėš ǻţŧāсħ ŧб ℮хìśтīπġ ẅϊńďôẅѕ. !!! !!! !!! !!! !!! !!! + Ċοηťřοľś ħбẁ иĕŵ ťёŕmίиàℓ ïйŝτдńсěś αťŧаċн ŧõ ę×íśťîлġ ωϊπđôŵѕ. !!! !!! !!! !!! !!! !!! A description for what the "windowing behavior" setting does. Presented near "Globals_WindowingBehavior.Header". - Čŗεáŧ℮ à иέш щĩпđőẅ !!! !!! + Сѓėāţë ą ňĕẁ ẅіňδôẃ !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances create a new window. - Áŧţàсђ ţǿ тћэ mόśţ геćзпţĺу úѕεđ ẁĩиðòш !!! !!! !!! !!! + Âŧтáсђ тθ тĥè мθŝт řєċєпťĺγ џšęδ шīπďθŵ !!! !!! !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window. - Δŧτдςђ τò τħэ мθŝŧ ř℮čэńţłý ųśĕð шīηδőẅ оñ ŧħіѕ ďëŝκтθφ !!! !!! !!! !!! !!! ! + Аťţâςђ τό ŧнэ mǿšт řэ¢ëñτŀý џšēď ωīηđοω όñ ţћιš đèşќŧοр !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - Тħéŝě ŝèťťΐηĝš мąÿ ьë ūşзƒūℓ ƒòř ŧяǿŭвŀёşђōòţįήġ āñ íśšû℮, ђоωěνĕґ тħęγ шįℓľ ίmρâ¢τ ỳőúг рĕґƒогmǻņĉè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ţн℮şę ŝēтťīⁿĝŝ мāγ ьз µŝèƒцℓ ƒøŕ ŧřõϋьľêŝђøǿţîпĝ åń іşŝũê, нõẅěν℮ř τĥĕý ώϊĺĺ ίмрáĉτ ÿőцг рєґƒòґмάпĉэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. - Ĥιðė τђë τίţℓе вäг (ѓέqùìŕëś ґέĺàųήċħ) !!! !!! !!! !! + Нΐðē ťĥĕ ŧΐтŀë ьдŕ (ґēqμįřеś ґĕĺãŭñċĥ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. - Ẃђęπ ďιşαьℓеδ, ŧђэ τįţĺē ьăґ шіĺℓ ăρρēǻŕ äвôνе ţĥê τǻвş. !!! !!! !!! !!! !!! ! + Щħεй đїѕавľ℮ð, тђė ŧïťℓé ъαг ŵĩℓļ άφрёäř αьõνē ťĥ℮ ťαвş. !!! !!! !!! !!! !!! ! A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Üş℮ ăćѓўľĩċ máţέŗιαľ ϊń тђê ŧαь řбẅ !!! !!! !!! ! + Ůśё дċŕýℓĭĉ мăŧєřίªŀ îņ ŧĥè ŧáъ řóẃ !!! !!! !!! ! Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Úš℮ āςŧίνэ ţéґmϊʼnдℓ ţíτľĕ ãš āρρľïсаτίőл τīτĺè !!! !!! !!! !!! ! + Üŝè ǻčτινє τ℮ґmīпαŀ ţįťŀє ǻѕ åрρĺïсăţìôⁿ ţįтℓè !!! !!! !!! !!! ! Header for a control to toggle whether the terminal's title is shown as the application title, or not. - Ẁн℮ή đΐѕàвŀ℮ď, τħě тίтĺę вâг ωîľĺ ъε 'Ţεřmìлαľ'. !!! !!! !!! !!! !! + Шђēñ ďìśάъŀěδ, ŧћē ŧίŧļе вăř ẅíłŀ вê 'Ťĕґmĭйăŀ'. !!! !!! !!! !!! !! A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"} - Šпăр ώíņδőẃ яèŝìżίņģ ťό čħâгдċţèѓ ğґìđ !!! !!! !!! !! + Ѕпåр щīńďоẁ ř℮ѕįźιлġ τσ ćђăяаċŧĕг ġřίđ !!! !!! !!! !! Header for a control to toggle whether the terminal snaps the window to the character grid when resizing, or not. - Щћěή đїśáьŀέđ, τħê ŵιпðоŵ ẃįĺŀ řёšιźë śmøбťĥĺý. !!! !!! !!! !!! !! + Ẅђéʼn διśάъļéď, ťне ωìйδŏẅ ώΐłĺ řèšïżē ѕмôǿτђℓŷ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - Ùśέ ŝσƒţωâřэ гэηðεѓïπģ !!! !!! + Цş℮ şőƒţщâгé ґęлðєґїпģ !!! !!! Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - Ẃĥëή èπåьľĕδ, ţнë ţεѓмїήāŀ щìłľ ûşé τħè ѕöƒтẅàřĕ ŗєŋδèřēг (å.к.à. ŴĀҐΡ) ìηśτεáδ öƒ тђĕ ħάŗđшâѓē σňе. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẅћēη êиåьļέδ, ťĥё τэŕмīпдľ ẃìĺł ϋşέ ťħ℮ ѕóƒťωäяę ŕęпđзřέґ (ä.ķ.ǻ. ẄĄŔР) ĭŋŝťėãδ óƒ ťĥέ ĥâѓđẁãřĕ őйê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - £αûήçн σп мαςħīňĕ ѕţäѓťцφ !!! !!! ! + £âüиçħ οп mąćђīñэ śţáяŧũр !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. - Ǻџтöмǻтіċǻļℓÿ ℓåϋņсђ Τëѓmīʼnаĺ щн℮й γоμ ļõğ їη тŏ Windows. !!! !!! !!! !!! !!! !! + Åůŧǿmдτįčãľłý ĺàüņčħ Ţёřмïйάł ωћêй ўǿũ ℓоġ ĩŋ τσ Windows. !!! !!! !!! !!! !!! !! A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header". {Locked="Windows"} - €ëⁿţęѓėð !! + Сзñŧεř℮δ !! Shorthand, explanatory text displayed when the user has enabled the feature indicated by "Globals_CenterOnLaunch.Text". - Ĉеʼnťëř òń ŀåµńçħ !!! ! + €ěņţēŕ ōņ ļǻυⁿĉћ !!! ! Header for a control to toggle whether the app should launch in the center of the screen, or not. - Ẃнёй ēňâвℓëδ, ťħĕ ŵĩпďσώ ŵîļļ ьэ φŀаĉěď ïη ŧћė ćĕņţéг σƒ тне şćŕєэŋ ώнēη ŀãűņĉнеđ. !!! !!! !!! !!! !!! !!! !!! !!! + Ẁĥёñ єлãъľєđ, ťђè ẃιηδθώ ẃιľŀ ьë ρĺäċëď ïи ťђ℮ čéņτēŕ ǿƒ тĥė šĉŗ℮ĕл ẅђел ľăυлćђεď. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "center on launch" setting does. Presented near "Globals_CenterOnLaunch.Header". - Дℓẅäуś θи ťбρ !!! + Âℓẁдỳš ол τορ !!! Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled). - Τзѓmįиăļ ẁīľļ аľẅάýś ье ŧђе тόрmōśт ẅįпðσщ θʼn ţћ℮ ðзŝкτбр. !!! !!! !!! !!! !!! !! + Τêѓmíⁿаľ ώìĺĺ åĺẁãγś ьэ ťħê τôрмσŝţ ωįпďóŵ оή ŧђė ďēŝķтöр. !!! !!! !!! !!! !!! !! A description for what the "always on top" setting does. Presented near "Globals_AlwaysOnTop.Header". - Ţâъ ẃΐďтћ mσδё !!! ! + Ţăв ωîđťħ мǿδė !!! ! Header for a control to choose how wide the tabs are. - €õmрāст ẃіļℓ ŝĥґїпķ ĭπàсţīνэ ţąвś тö ťĥĕ ѕίžè όƒ τĥê ιćσň. !!! !!! !!! !!! !!! !! + Ĉõmφаĉτ ŵìłľ śняίňќ їņãĉţįν℮ тäъś τò тнë şîżè øƒ ţħє ī¢õň. !!! !!! !!! !!! !!! !! A description for what the "tab width mode" setting does. Presented near "Globals_TabWidthMode.Header". 'Compact' must match the value for <Globals_TabWidthModeCompact.Content>. - Ćōмрαćτ !! + Čòмρàçτ !! An option to choose from for the "tab width mode" setting. When selected, unselected tabs collapse to show only their icon. The selected tab adjusts to display the content within the tab. - Зqυαļ ! + Зqũǻļ ! An option to choose from for the "tab width mode" setting. When selected, each tab has the same width. - Тīţłέ ℓèńĝтĥ !!! + Ŧїţℓè ľеηġťђ !!! An option to choose from for the "tab width mode" setting. When selected, each tab adjusts its width to the content within the tab. - Ãρрłįćąţιοп Ťħемē !!! !! + Ăφрŀì¢ãťîσń Ťћеmĕ !!! !! Header for a control to choose the theme colors used in the app. - Ďªяκ ! + Đάгĸ ! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. - Ùšέ Ŵĩńδσώѕ τнзmê !!! !! + Üşë Ẁĭήðõшѕ ŧћěмε !!! !! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. - Ĺιĝћť ! + Ĺĭġĥŧ ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. - Ðǻŗк (Ľéģà¢ў) !!! + Ďªѓκ (Łеğãċý) !!! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. This is an older version of the "dark" theme - Ŭşё Щΐπδǿшš ŧĥёмє (₤ėĝāćγ) !!! !!! ! + Űѕё Ẅίŋðöŵѕ тĥ℮mé (₤ёģªćỳ) !!! !!! ! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. This is an older version of the "Use Windows theme" theme - Ļĩĝħť (Ŀєġâсу) !!! ! + Ľιğћť (₤еģάςу) !!! ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. This is an older version of the "light" theme - Ẁόŗð đέļϊmіτєřŝ !!! ! + Ẃōŕδ δέĺιmîŧěґş !!! ! Header for a control to determine what delimiters to use to identify separate words during word selection. - Тħэѕё ѕγмвôļѕ ωίłĺ вë ùşėδ ẃћêŋ уоџ đσűвℓė-ĉłіċĸ ťзжţ ΐй ťнэ ťέѓмīʼnдľ øѓ αċτινåτę "Μâřĸ mσδë". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ţĥęѕз ѕÿmвöĺş ώīℓľ ьē ΰśĕď шħéл ỳøú đбúьĺê-ςļìсĸ ŧě×τ ιπ ťћё τєŗмίⁿąŀ ôґ ǻстìνãţ℮ "Μâŕĸ mōðę". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "word delimiters" setting does. Presented near "Globals_WordDelimiters.Header". "Mark" is used in the sense of "choosing something to interact with." - Ąφрεªѓåʼnсê !!! + Λφреǻřдηĉ℮ !!! Header for the "appearance" menu item. This navigates to a page that lets you see and modify settings related to the app's appearance. - Ćǿℓôя śċħемέŝ !!! + Сõĺθя ŝ¢ħêмεş !!! Header for the "color schemes" menu item. This navigates to a page that lets you see and modify schemes of colors that can be used by the terminal. - Īńťеŗäčтíǿʼn !!! + Įπτěŗāčŧīόņ !!! Header for the "interaction" menu item. This navigates to a page that lets you see and modify settings related to the user's mouse and touch interactions with the app. - Śťąґτűρ !! + Śťäřŧџр !! Header for the "startup" menu item. This navigates to a page that lets you see and modify settings related to the app's launch experience (i.e. screen position, mode, etc.) - Õрзл ĴŠΌÑ ƒĩłê !!! ! + Ωρέň ĴЅОŅ ƒϊļè !!! ! Header for a menu item. This opens the JSON file that is used to log the app's settings. - Ðēƒâμĺťŝ !! + Ðёƒåϋļţś !! Header for the "defaults" menu item. This navigates to a page that lets you see and modify settings that affect profiles. This is the lowest layer of profile settings that all other profile settings are based on. If a profile doesn't define a setting, this page is responsible for figuring out what that setting is supposed to be. - Řéлδèřΐņģ !!! + Ґęʼnðęŕίŋġ !!! Header for the "rendering" menu item. This navigates to a page that lets you see and modify settings related to the app's rendering of text in the terminal. - Δćтϊøπѕ !! + Âĉτīøńŝ !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. - βâćκĝŕőũйð ǿρă¢ϊŧŷ !!! !! + Ьαςќġřόџⁿď ōрãςίŧγ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Βд¢кĝŗбцŋð òφдćιтý !!! !! + Ьªĉκğřōûŋđ σφăčΐţў !!! !! Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Šётš ťħέ θрªĉīţў òƒ ŧћė ώіпðøщ. !!! !!! !!! + Ѕзťś ŧђе ǿрàčίτў őƒ ţнē ωίπđőẁ. !!! !!! !!! A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header". - Âδνâлčêđ !! + Δďνǻņčęδ !! Header for a sub-page of profile settings focused on more advanced scenarios. - AltGr ăļìǻşіņġ !!! ! + AltGr ǻļιâŝĩⁿģ !!! ! Header for a control to toggle whether the app treats ctrl+alt as the AltGr (also known as the Alt Graph) modifier key found on keyboards. {Locked="AltGr"} - Бÿ ðěƒαųℓť, Windows ŧґĕăťѕ Čŧяĺ+Āĺτ ąŝ äņ āľìàś ƒσґ АĺтĢŕ. Ťħįś ѕэтţĩηğ ωìłļ őνегŗіďé Windows' δêƒαυℓť ьёћãνíőѓ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ьў ðёƒãџĺŧ, Windows ťŕёдтŝ Ĉтяł+Ǻŀţ àŝ áʼn аľіăš ƒòя АłťĢŕ. Ŧħìś ŝετтϊиğ шіŀł ôνέřѓϊďε Windows' ďзƒαυŀţ ьèĥäνïοř. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header". {Locked="Windows"} - Тėхŧ áŋτΐàĺĩáşīлġ !!! !! + Τехт áпŧîàŀĩâŝíήĝ !!! !! Name for a control to select the graphical anti-aliasing format of text. - Ťэ×ŧ αпţíãĺΐąşιʼnĝ !!! !! + Ťêжτ âʼnтìãŀĭãŝīήğ !!! !! Header for a control to select the graphical anti-aliasing format of text. - Çσňťѓöļś ħôώ τĕхτ îş ªитїаłίãşëð ΐи ŧħĕ ŗêŋđёŕэг. !!! !!! !!! !!! !!! + Ċοňţŕоĺŝ ћόш ţė×τ ĭş άиţίαłΐаşеđ їл τћë řеňďēŗέŕ. !!! !!! !!! !!! !!! A description for what the "antialiasing mode" setting does. Presented near "Profile_AntialiasingMode.Header". - Áľιªşєđ !! + Ăℓįåѕεδ !! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer does not use antialiasing techniques. @@ -647,259 +647,259 @@ An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a "ClearType" antialiasing technique. {Locked="ClearType"} - Ğгªŷѕ¢ãĺз !!! + Ģŕǻўşĉαł℮ !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a grayscale antialiasing technique. - Áφφéªŗâńςė !!! + Δφρêąřąñčз !!! Header for a sub-page of profile settings focused on customizing the appearance of the profile. - ßāсκģřóµʼnđ íмаġě рáтĥ !!! !!! + Вą¢ķğѓŏµлδ ïмâģе ρаŧĥ !!! !!! Name for a control to determine the image presented on the background of the app. - Ьãсķĝѓσцŋδ їmªĝè ρáŧħ !!! !!! + ßά¢ќģѓбűήď ìmăġё φäтђ !!! !!! Name for a control to determine the image presented on the background of the app. - ßåċќġѓǿůⁿđ ίmªĝę φªťн !!! !!! + βªςκġгοüйδ ĭmāĝє ρáŧћ !!! !!! Header for a control to determine the image presented on the background of the app. - ₣ΐļé ļöςåţįǿи øƒ тћє îmãğë ùşēď įʼn тћз ъâċкğŕöūņđ óƒ тħέ ωїлďσŵ. !!! !!! !!! !!! !!! !!! ! + ₣įľé ĺσ¢āτϊŏň öƒ тĥê ĩmåĝĕ üšёδ ĩń ţћĕ ьǻčкğŕθџňđ øƒ ŧнé ωĩηðσω. !!! !!! !!! !!! !!! !!! ! A description for what the "background image path" setting does. Presented near "Profile_BackgroundImage". - Ьª¢κģгǿџňð įmąģє άŀίġήмεņť !!! !!! ! + Ьªċќğяøµпđ īмāģè ăℓίģⁿmεʼnτ !!! !!! ! Name for a control to choose the visual alignment of the image presented on the background of the app. - βá¢кĝѓόŭñδ ïmăĝė ãľīģńмėⁿŧ !!! !!! ! + βάćкģŕóµиð імáģè ąŀїğимэиτ !!! !!! ! Header for a control to choose the visual alignment of the image presented on the background of the app. - Ѕěťş ћõω ţħё вάčķģŗøùπδ ίмâġë äĺĭġйѕ тǿ ŧђέ вôΰňđářізѕ οƒ ŧћë шΐηδôώ. !!! !!! !!! !!! !!! !!! !!! + Śεŧš ħŏẃ ţĥё ьāçкğřŏūŋď ímåģέ ãłĭĝňš ŧо τћê ьôύńďąяīέş őƒ τĥε щìņđσω. !!! !!! !!! !!! !!! !!! !!! A description for what the "background image alignment" setting does. Presented near "Profile_BackgroundImageAlignment". - Ьöŧţоm ! + Вöтτøm ! This is the formal name for a visual alignment. - ßбťтõm ľ℮ƒŧ !!! + βθтτŏm ĺёƒŧ !!! This is the formal name for a visual alignment. - Ъóŧτōм ѓіġђт !!! + βбŧтбm ŕіģђт !!! This is the formal name for a visual alignment. - €ĕñťέг ! + Ćēπţèŕ ! This is the formal name for a visual alignment. - Ĺěƒť ! + ₤êƒт ! This is the formal name for a visual alignment. - Γΐģћт ! + Ґìġĥŧ ! This is the formal name for a visual alignment. - Τόρ + Ŧоφ This is the formal name for a visual alignment. - Ţóр ĺèƒŧ !! + Τőρ ľèƒτ !! This is the formal name for a visual alignment. - Ŧŏр ŗιġнт !!! + Ţόφ ŕïĝђŧ !!! This is the formal name for a visual alignment. - Вřσωşе... !!! + Βŗσщѕę... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Āðď πēŵ !! + Áðđ πеш !! Button label that adds a new font axis for the current font. - Âđð ηэẃ !! + Аδδ ή℮ω !! Button label that adds a new font feature for the current font. - βăсќģřσμиď ĩmáġë ôφåçĭťў !!! !!! ! + Ьªćκģŗσūŋď ϊmăģз őρасΐţγ !!! !!! ! Name for a control to choose the opacity of the image presented on the background of the app. - Ьдċкģřöμήď ïмâģэ őρäċίτý !!! !!! ! + Ъäсќğřǿüŋđ îmâģë ŏρåςĩŧỳ !!! !!! ! Header for a control to choose the opacity of the image presented on the background of the app. - Ѕèťѕ тĥĕ οφāčϊтỳ øƒ ŧħе ваςķğřøúňð імąĝе. !!! !!! !!! !!! + Ŝěтš ŧħė бρа¢ïťу όƒ тħě ъä¢ќġѓòũиď īмαģз. !!! !!! !!! !!! A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". - ßăĉķğřοциđ ïmдĝë śтŗзŧçђ мбďě !!! !!! !!! + Βäçκģяǿџиď ΐмāğё şŧґєтĉħ мöðě !!! !!! !!! Name for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Бăċκġřόûņδ їmаğě śтяéтćĥ мθδε !!! !!! !!! + Βäĉĸģŗöŭŋď ϊmăģ℮ ŝтѓέτċĥ mθδĕ !!! !!! !!! Header for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Ѕęŧŝ ћŏẃ тħè вă¢ĸģгŏŭⁿď ĩmàĝз įš яêšìżěð ťö ƒïĺľ ŧђè шϊпďǿω. !!! !!! !!! !!! !!! !!! + Śêŧѕ ћǿώ ťнě ьάćкġŕōūʼnð ìмªĝĕ ΐѕ ѓэśíżęđ ţô ƒïŀℓ ţħέ ώїⁿďоẅ. !!! !!! !!! !!! !!! !!! A description for what the "background image stretch mode" setting does. Presented near "Profile_BackgroundImageStretchMode". - ₣įĺł ! + ₣ίℓĺ ! An option to choose from for the "background image stretch mode" setting. When selected, the image is resized to fill the destination dimensions. The aspect ratio is not preserved. - Νøⁿė ! + Иöⁿé ! An option to choose from for the "background image stretch mode" setting. When selected, the image preserves its original size. - Üήíƒŏгм !! + Ũпϊƒόřm !! An option to choose from for the "background image stretch mode" setting. When selected,the image is resized to fit in the destination dimensions while it preserves its native aspect ratio. - Цņíƒòѓm ťø ƒΐľľ !!! ! + Ũñĩƒοяm ţо ƒîłļ !!! ! An option to choose from for the "background image stretch mode" setting. When selected, the content is resized to fill the destination dimensions while it preserves its native aspect ratio. But if the aspect ratio of the destination differs, the image is clipped to fit in the space (to fill the space) - Ряőƒìļέ ťзřмιñαŧϊбŋ вêнāνïöг !!! !!! !! + Рŗôƒїŀě ŧëямĩñàŧїǿň ьèĥдνїőŗ !!! !!! !! Name for a control to select the behavior of a terminal session (a profile) when it closes. - Ряøƒїŀэ τєгмїηâťĩóň вėĥąνĭøř !!! !!! !! + Ρřōƒïľě тėŗmìⁿàτΐôη ьęћåνïθŗ !!! !!! !! Header for a control to select the behavior of a terminal session (a profile) when it closes. - Сļòśĕ ώђēń φřó¢ėѕś èхіţŝ, ƒаîľŝ, óř сřαšнэѕ !!! !!! !!! !!! + Сłôѕĕ ωħёл φŕôçėšś ёхιτş, ƒâíłŝ, ŏŕ ċŕαśĥèѕ !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled (exit) or uncontrolled (fail or crash) scenario. - Сłόşε òⁿŀý шĥёň рřο¢εŝş э×їτš ŝџĉčĕśşƒΰℓľŷ !!! !!! !!! !!! + Сłοšэ ǿήℓў ẅĥėи ρѓσçέśѕ έхìтš ѕúç¢ėššƒύĺļý !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully. - Ñëνéř сłøѕĕ âцťόmáťϊĉаℓŀŷ !!! !!! ! + Νёνèř čļоšė āμťσмªťìćǻŀℓÿ !!! !!! ! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal. - Àϋтоmåτíč !!! + ∆ũтõmàťïċ !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal. - Ċöĺōŗ şčнémē !!! + Ĉοłǿŗ ѕćђ℮mэ !!! Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user. - €όмmąňď ŀїήě !!! + Ĉοmмάπδ łϊʼnέ !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Ĉōмmãпδ ľїñê !!! + Ċőмmάήď ļιňê !!! Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Ċōmмаñδ ĺїʼnε !!! + Çοммǻʼnδ łĭʼně !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Ėхĕçϋţąвĺē üšзđ įⁿ ťнέ ряöƒîļé. !!! !!! !!! + Эжê¢ûŧąвŀέ υѕéð ïи тђė ряõƒίł℮. !!! !!! !!! A description for what the "command line" setting does. Presented near "Profile_Commandline". - Βгŏẃšέ... !!! + Ьґóẅšĕ... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Čυŕşόŗ нєϊģђт !!! + Čџѓѕòґ ħэįġħŧ !!! Header for a control to determine the height of the text cursor. - Ŝęťš ťћε ρėŗçèńţдġê ђεîğћť õƒ ţнé сϋřşøг šτаŗŧϊлğ ƒѓőm тнë ъσтťом. Θлĺÿ ώõѓкѕ ωίτћ ţĥё νĩŋţàğё čΰŕšǿř śĥâρĕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ŝєτš ŧћз рéŗςєηŧäğе ĥèίġħţ ôƒ ŧђê ċŭŗšбґ ŝтářţĩиģ ƒѓбм ťнė ъöŧŧσm. Ωņŀÿ ŵǿґκѕ ŵìτђ ťнê νіňŧąĝέ çцŗşоѓ śћāρе. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "cursor height" setting does. Presented near "Profile_CursorHeight". - Ĉųгšóѓ ѕнăρ℮ !!! + Ĉüѓѕôѓ ŝнąρэ !!! Name for a control to select the shape of the text cursor. - Ĉüяśôґ şнǻρê !!! + €ůŗšоѓ ѕћàρ℮ !!! Header for a control to select the shape of the text cursor. - Ŋĕνēŗ ! + Ńęν℮ř ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will never adjust the text colors. - Φňŀý ƒôя сοℓбŗŝ ĭń тħє ćǿŀог şĉђ℮мë !!! !!! !!! ! + Ώʼnŀŷ ƒŏŗ ςôŀбґŝ ïй ŧĥë ςόļǿг şςћëmέ !!! !!! !!! ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table. - Âŀшαÿš ! + Áĺшãÿŝ ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. - Ьàř ( ┃ ) !!! + Ъàŕ ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. - Έmрŧγ вόх ( ▯ ) !!! ! + Ęмφťỳ вο× ( ▯ ) !!! ! {Locked="▯"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an empty box. The character in the parentheses is used to show what it looks like. - ₣ïℓļéđ вŏж ( █ ) !!! ! + ₣їļľéð ьô× ( █ ) !!! ! {Locked="█"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a filled box. The character in the parentheses is used to show what it looks like. - Цñðεѓŝĉσяė ( ▁ ) !!! ! + Úйδęгŝςøŕę ( ▁ ) !!! ! {Locked="▁"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an underscore. The character in the parentheses is used to show what it looks like. - Vίπţăģє ( ▃ ) !!! + Vįπţαğė ( ▃ ) !!! {Locked="▃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a thick underscore. This is the vintage and classic look of a cursor for terminals. The character in the parentheses is used to show what it looks like. - Ðöüъℓĕ űņðєѓśςóřê ( ‗ ) !!! !!! + Đоŭьℓè űñδêřšçòřэ ( ‗ ) !!! !!! {Locked="‗"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a stacked set of two underscores. The character in the parentheses is used to show what it looks like. - ₣őñτ ƒǻςє !!! + ₣òлτ ƒąċē !!! Header for a control to select the font for text in the app. - ₣θит ƒд¢ё !!! + ₣òņτ ƒǻĉè !!! Name for a control to select the font for text in the app. - ₣őиť śĭžέ !!! + ₣θлŧ śϊźé !!! Header for a control to determine the size of the text in the app. - ₣ōήτ ѕĭžė !!! + ₣бητ šїźé !!! Name for a control to determine the size of the text in the app. - Şĭźě οƒ τнĕ ƒοиť ιη φоіиťś. !!! !!! !! + Şįźè σƒ ťħē ƒòʼnť ϊŋ φσĩиťš. !!! !!! !! A description for what the "font size" setting does. Presented near "Profile_FontSize". - Łîŋє ħĕίĝħţ !!! + Ľїπё ĥéíĝђť !!! Header for a control that sets the text line height. - ₤îņę ћэĭĝђτ !!! + Ľїńë нęĭğĥτ !!! Header for a control that sets the text line height. - Θνέгґïδ℮ ťђэ ľĩпε ħêϊĝћţ õƒ ŧћ℮ τēгmïņªł. Μзăśµŕέδ аŝ ª mύľтīрłэ öƒ ťђé ƒθńŧ šížē. Ŧћ℮ đеƒǻùļŧ ναľûè ďęρėηđѕ ôи ýŏύŗ ƒòʼnт ăʼnð íş ùѕυàℓĺý àŕόџʼnď 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ωνэґяϊδê ťĥε ℓїⁿê ћ℮ĩġћţ ôƒ τнэ τěřмïŋаŀ. Мēдѕũřĕð άš ά мџľţΐρĺ℮ ŏƒ τћē ƒŏиť šįžе. Тħĕ δеƒдûľţ νąℓцэ ďėφêйðş óл γόŭř ƒòлť ªйđ ϊś üŝŭáŀĺŷ äŗøūήđ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "line height" setting does. Presented near "Profile_LineHeight". @@ -907,846 +907,854 @@ "1.2" is a decimal number. - Βůιļŧιη Ģłÿφнş !!! ! + Бűīłτĭл Ĝľурĥš !!! ! The main label of a toggle. When enabled, certain characters (glyphs) are replaced with better looking ones. - Ŵнěⁿ εйаъľёđ, ŧђе тэŕміпàℓ ďŗäώş ċϋŝŧōм ğℓγρћś ƒθґ ъłòск эŀємęήť āлď ьǿ× đřдẅïⁿģ ċћåřãċтėяŝ ίйѕτęªδ оƒ џŝіňğ ťђє ƒοňτ. Ŧћїś ƒêåťцѓє òŋłÿ ωόŗĸś ωħēή ĢРÚ Áćсеłёѓªťιőл іş åνåįłаьĺё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Щђëń ℮йãвļэđ, тнė τєřmìήąļ đгäшŝ ċцšťσm ġłγφнş ƒôř ъŀσсķ èľėmέňτ âлď ьõх δґåώίиģ čћãѓαςτēřş íйşťëàď ǿƒ џşįηĝ τħé ƒŏñť. Тћїş ƒêάťųřė θņĺÿ ẁôѓκš щћéπ ĜΡÚ Λċсĕłėŕâŧιǿʼn ΐѕ ăνдìŀαвŀε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + ₣ύℓℓ-сôŀőŗ Ēmθјϊ !!! ! + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Ẁћέń зňªвłεð, Ємóĵί äŕė δΐşрľâýзð ίņ ƒύℓŀ ĉōĺõŕ. !!! !!! !!! !!! !! + A longer description of the "Profile_EnableColorGlyphs" toggle. + - ₣óⁿτ шĕįğħť !!! + ₣οńţ ώєїġнţ !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣øñţ ẃèĩğћт !!! + ₣ǿиŧ ẃєíģĥť !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣õñŧ щєìģђт !!! + ₣ǿŋτ ŵёīĝĥτ !!! Header for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - Śěţѕ τћέ ŵéϊģħţ (ℓіĝнţńέşş òŕ ĥзàνîņēѕş øƒ ŧħĕ ŝťяöĸ℮ś) ƒбŕ τħĕ ģïνěη ƒòήτ. !!! !!! !!! !!! !!! !!! !!! ! + Šεţš τħē шείğнŧ (ℓΐģђťиеŝѕ ǿř ħзάνΐηėšѕ оƒ ţћé šŧѓσķέš) ƒōř τħ℮ ĝìνęⁿ ƒõńτ. !!! !!! !!! !!! !!! !!! !!! ! A description for what the "font weight" setting does. Presented near "Profile_FontWeight". - Vāґіäьĺе ƒσⁿт àжĕś !!! !! + Vāřĭªвľ℮ ƒоñţ αхęś !!! !! Header for a control to allow editing the font axes. - Âďð θř řэmǿνĕ ƒôʼnτ ª×зş ƒόѓ тħé ġíνėη ƒôŋť. !!! !!! !!! !!! + Ǻđď θř я℮mσνë ƒöпт ãхęś ƒôŗ ţħě ġϊνєп ƒоņţ. !!! !!! !!! !!! A description for what the "font axes" setting does. Presented near "Profile_FontAxes". - Ťħĕ śέℓέċтэð ƒőит ħăŝ лσ νąяіαьłě ƒøлţ ąжěş. !!! !!! !!! !!! ! + Тћę ŝеļеçτėð ƒőпт ћåŝ йø νäяΐäьłё ƒøńŧ á×ěś. !!! !!! !!! !!! ! A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". - ₣õпτ ƒêăтûѓèś !!! + ₣öňт ƒзåτũгэŝ !!! Header for a control to allow editing the font features. - Ǻðδ бř ŗèмǿνē ƒôит ƒєªтυѓęş ƒθѓ ŧĥé ğίνĕņ ƒσπţ. !!! !!! !!! !!! !! + Äðδ σґ řéмōνз ƒŏñт ƒєăŧūгёš ƒσŗ тĥз ġινęπ ƒõήт. !!! !!! !!! !!! !! A description for what the "font features" setting does. Presented near "Profile_FontFeatures". - Ţħє ѕεļэçŧêď ƒóлτ ħăś ņő ƒŏʼnт ƒėдтμяéŝ. !!! !!! !!! !!! + Τђê šёľėĉŧěð ƒбņť ћăş пσ ƒŏήт ƒёάťϋŕěѕ. !!! !!! !!! !!! A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". - Ĝεʼnέřάł !! + Ģєʼnеŗαℓ !! Header for a sub-page of profile settings focused on more general scenarios. - Ħĭδэ φгõƒīłэ ƒгбм ðґοрðõωи !!! !!! ! + Ήіðе ρŕθƒΐļ℮ ƒгθм δřōφďòẁñ !!! !!! ! Header for a control to toggle whether the profile is shown in a dropdown menu, or not. - ΃ éйąьľéď, тħĕ φřŏƒìľэ щïℓľ пõť ăφрèāѓ ìή ŧђë łįšţ οƒ φяσƒϊļéš. Тħιѕ ćǻʼn ъĕ цŝêď ťǿ ħίδέ ďēƒдűŀť φřοƒіłєś ǻⁿď đÿлåмï¢ǻľļý ġėⁿзґατèď φŕбƒїļєś, шħĩŀέ ℓзăνїπġ тђëm ìп ÿöцŕ śęτтîηĝş ƒίľè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ίƒ эήªвℓεď, τћз ргоƒїłэ ẃíℓļ ņόţ аρφèåř ϊʼn ŧħę ŀїşţ οƒ φŗòƒíŀėŝ. Τĥїš сâⁿ ъ℮ üŝĕď ťό ĥΐďě đĕƒäΰŀŧ φяόƒіℓзѕ αʼnδ δўиáмìćǻļĺу ğ℮иεŕαтєđ φřöƒіĺεş, шнίľз ļэάνīήģ ţђĕм îñ ỳôûř śєţţίņğš ƒїľз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "hidden" setting does. Presented near "Profile_Hidden". - Ŗùñ ŧĥĭŝ ρřθƒįľė аš Άđmįлīŝтřąτōя !!! !!! !!! + Řύπ ŧћίś рѓŏƒìŀē дѕ Аδmĭⁿĭŝтяǻτöя !!! !!! !!! Header for a control to toggle whether the profile should always open elevated (in an admin window) - Іƒ єиаьĺêđ, ťћэ ρгóƒíŀ℮ ẁĩℓℓ ŏрєŋ ϊй åπ Δδмĭń ŧęѓміňдļ шįņđöẁ αùтόmаŧïçàłļý. ΃ тĥέ ćůґґèит щΐńðōŵ ĩş åŀřзáδў ŗüпήíήġ άş åđmïň, ìţ'ĺł θρзʼn îň тħіѕ щįñδσẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Įƒ έпàьŀêδ, ťħê φяοƒΐŀĕ ẁιĺŀ όреŋ īη ªŋ Дđmìή теямĭńáľ ẅĩñðǿщ åúťσмăŧїĉāłĺў. Ίƒ ťħз ĉùяřéņť ŵіиδοẁ іş ªŀгеàđγ řµпñїпģ åś ąδmίņ, íţ'ĺŀ ōреή íʼn τђίş ωιńďõẅ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Ħĭŝŧбřу ŝîż℮ !!! + Ĥĩšţоřý şΐźε !!! Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Нíѕŧŏřý ŝīžė !!! + Ηïšтōґý ѕΐžě !!! Name for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Ţнę ņυmвзя οƒ ľīńёş аьονε ŧђэ øиēś ðїѕφĺдŷєð ïń тĥē ωïηđøẃ ўǿü čάñ ѕċґŏľł ъàčκ тõ. !!! !!! !!! !!! !!! !!! !!! !!! + Тнë иϋmвĕŕ σƒ łίňêš αьονě тĥé σņёѕ δίşφĺąγěδ іл τħе ŵΐʼnđöŵ ÿоû çäη šςřôļĺ ьāćκ ţο. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "history size" setting does. Presented near "Profile_HistorySize". - Ĭĉõñ ! + Įςøņ ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ĩċбň ! + Ì¢öŋ ! Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ίćøń ! + Ìсθл ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ěмσĵī øґ ìmàĝ℮ ƒιℓэ ĺθċăťĩôŋ οƒ ŧће ìćōл ùśεð īņ ťђэ рřõƒіłё. !!! !!! !!! !!! !!! !!! + Ēmōјĭ ôř ĩмąġě ƒϊľê ĺόćąтïбп όƒ τĥэ įĉõи úšêđ ìи ţĥє рřöƒįļέ. !!! !!! !!! !!! !!! !!! A description for what the "icon" setting does. Presented near "Profile_Icon". - Βґоẃѕē... !!! + Βгøшŝė... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ρăδδίʼnĝ !! + Рâðδїπĝ !! Name for a control to determine the amount of space between text in a terminal and the edge of the window. - Ρåδδιиġ !! + Ρäðďįиģ !! Header for a control to determine the amount of space between text in a terminal and the edge of the window. The space can be any combination of the top, bottom, left, and right side of the window. - Ŝэτŝ ţћė ράđďΐʼnģ ǻяőυπδ тĥë ŧèхт ẁĩŧнίʼn ţнé ẅįηðθщ. !!! !!! !!! !!! !!! + Ŝέţѕ ţнé ρªðδìπġ аґôϋňδ тђě ţêжт шĩτђĩη ŧħě ẁιⁿđöш. !!! !!! !!! !!! !!! A description for what the "padding" setting does. Presented near "Profile_Padding". - Ґéŧґб ţεřmíпâł ëƒƒëċτѕ !!! !!! + Ŗèтгб ŧěѓmϊňâℓ ėƒƒέĉτѕ !!! !!! Header for a control to toggle classic CRT display effects, which gives the terminal a retro look. - Ŝħõẁ ŗєťґŏ-śţÿℓĕ ťêŕmιňªľ ėƒƒεćťŝ ѕũςн áŝ ġℓθщíňģ ŧëжţ ãπδ şçαñ ℓϊŋ℮š. !!! !!! !!! !!! !!! !!! !!! + Ŝħõщ ѓэťřò-şŧýľě τěŗmϊπдł єƒƒēċтş ѕùсĥ ąś ģłøώìлĝ ŧě×τ άήđ şčãņ ļϊηěś. !!! !!! !!! !!! !!! !!! !!! A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect". "Retro" is a common English prefix that suggests a nostalgic, dated appearance. - Åųŧómǻτîсάĺŀÿ àδјµśт ĺīĝħтπєѕş бƒ ĭπδìşтίņġύįšħăьļė ţèхт !!! !!! !!! !!! !!! ! + Àũŧŏмâťί¢αļℓў αδјûšţ ļіģнţⁿ℮śš õƒ їńďιŝţίňĝūíѕћавłз тēжţ !!! !!! !!! !!! !!! ! Header for a control to toggle if we should adjust the foreground color's lightness to make it more visible when necessary, based on the background color. - Αùτŏmãτϊċàļĺŷ вґїĝнт℮пŝ òѓ đαґκéπѕ ŧέхτ ťò мåкè ΐť мøґè νιŝïьļέ. Σνéй ẅħέй εйāъŀєđ, ŧђįś àδĵùŝťм℮ήт ẁîĺļ бŋľÿ бčçúř ώĥēń ª çόmьιпàτïøň оƒ ƒóř℮ģřōΰŋð âŋδ ъдςķģŕσцńđ ċõľǿŗŝ ẁòŭļđ ґéşůĺт îπ φőòґ сøйτŗäѕţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Аŭťömάťî¢áŀľÿ ъґĭģнтéήş оŗ ðãґќέñś ŧēжţ ťõ mâĸē įţ mοřė νĭŝїьļ℮. Еνεй ŵћêή ейдвĺέδ, ŧħίŝ ǻδјůşŧмзņŧ ωìℓļ ōиłў óćĉúг ẃĥëη à сòмвĩиăтϊǿй őƒ ƒŏřęĝŗǿϋηð алð ъą¢ķğřòύиđ ċôłǿѓş ώǿűŀð ѓеѕŭℓţ ιπ ρоóř ¢ôñтґåѕŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors". - Śсяоŀľьâř νîѕιвïℓīŧỳ !!! !!! + Ŝсгŏļľьâř νīŝίвĩℓïτγ !!! !!! Name for a control to select the visibility of the scrollbar in a session. - Śĉяöĺľьǻя νįşιъìŀїτỳ !!! !!! + Ŝćяόℓľвăґ νįѕīъìℓιтŷ !!! !!! Header for a control to select the visibility of the scrollbar in a session. - Нιďδéй ! + Ĥĭđďéй ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is hidden. - Vīŝΐьĺē !! + Vīѕïвłè !! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible but may dynamically shrink. - Åŀщăўѕ ! + Άĺẅäÿš ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is always visible. - Şçřòłĺ ţо ϊήφΰτ ώħεň ţỳρīⁿġ !!! !!! !! + Śςŕбℓŀ ŧό įñρųť ẁĥэή ţўрīηğ !!! !!! !! Header for a control to toggle if keyboard input should automatically scroll to where the input was placed. - Ŝτªřтïŋĝ ðīřęĉŧогŷ !!! !! + Ѕтāřŧΐņğ ďіřěсťôѓý !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Šťäřţĩñģ ðιгĕсţöřγ !!! !! + Šťąяţιʼnĝ διѓ℮čťőѓγ !!! !! Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Śťάґťįŋğ δîгзсţбřŷ !!! !! + Ŝтäгţϊŋġ ðϊŗëçτöяў !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Τĥ℮ δìřéċτőŕỳ ťħε φřőƒíŀě ѕŧǻřţŝ іⁿ ẁђèπ īţ īѕ ℓòąðέδ. !!! !!! !!! !!! !!! ! + Тђэ ðιřесτöŗў τнę рřбƒîļ℮ şτаřтş їņ ẅħėл īŧ ίş ŀόǻδėď. !!! !!! !!! !!! !!! ! A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory". - Бяŏẅśё... !!! + Ьřŏωѕē... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Џşέ φάŗėπţ рřθčέšś δìřęсťőяγ !!! !!! !! + Џѕé φãŕėⁿţ рřóςзŝš ðįŕзćтøяγ !!! !!! !! A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process. - Įƒ ёηâъļèð, ŧĥĩš ρŗоƒįļё ẃĩℓĺ ѕρāẅň ΐń тĥе ðĩґ℮ςтòгў ƒгом шћιсћ Ťεґmΐńαŀ ώâŝ ĺαúлçн℮ð. !!! !!! !!! !!! !!! !!! !!! !!! ! + Ĩƒ éидьłєđ, τħîś рřôƒīℓĕ щìľĺ šрдŵŋ ìй ťћє δίřëçťσřŷ ƒŕόm ẁђісн Ťêřmìñáļ шăš ŀǻцηćђёδ. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox". - Ĥιďё їčǿп !!! + Ħïđе ĩсöи !!! A supplementary setting to the "icon" setting. - Їƒ éⁿàвℓεđ, тђĩš φřοƒįŀĕ щιłĺ ђāνè йθ іċóⁿ. !!! !!! !!! !!! + ̓ ĕпàвłèď, тћįŝ φřоƒīłę ωîłł ĥдνë ņŏ įċόŋ. !!! !!! !!! !!! A description for what the supplementary "Hide icon" setting does. Presented near "Profile_HideIconCheckbox". - Šцφφŗзšş τïŧŀê ¢ħąŋğëş !!! !!! + Śųφφгęśš ţĭţļě çħаńģéš !!! !!! Header for a control to toggle changes in the app title. - Ίğиòřε āрρŀíĉâτîŏп гėqųęśţş ŧø ćнǻπĝě ťнέ ŧіťŀέ (OSC 2). !!! !!! !!! !!! !!! ! + Іģŋŏŗê āρφℓīçάŧΐøη гέqüęŝţŝ τθ ĉнаňģέ ŧђé ţιτĺе (OSC 2). !!! !!! !!! !!! !!! ! A description for what the "suppress application title" setting does. Presented near "Profile_SuppressApplicationTitle". "OSC 2" is a technical term that is understood in the industry. {Locked="OSC 2"} - Ŧªъ ţíтļę !!! + Тăь ţíтľé !!! Name for a control to determine the title of the tab. This is represented using a text box. - Тав ţΐτĺε !!! + Ťаь тīтľę !!! Header for a control to determine the title of the tab. This is represented using a text box. - Řερℓãςëś τнê рŗσƒíĺє лämè ãѕ ţђе τíтŀе ŧθ φâѕѕ ťŏ тђė şђεļļ öл šţāґŧüφ. !!! !!! !!! !!! !!! !!! !!! + Гεрľǻ¢έş ţћё рřǿƒїłз пам℮ áѕ ŧħë тīţļε ŧο φªšѕ тǿ ţђê ŝħěłľ öή śŧāѓτùρ. !!! !!! !!! !!! !!! !!! !!! A description for what the "tab title" setting does. Presented near "Profile_TabTitle". - Џñƒöĉµѕèď âφρėäяāņċē !!! !!! + Ûŋƒǿċûšεð äрρεªŕаήςз !!! !!! The header for the section where the unfocused appearance settings can be changed. - Čгєåτę Ăррęáґåиς℮ !!! !! + Čřęдτє ∆рреářдйċе !!! !! Button label that adds an unfocused appearance for this profile. - Đ℮ļęте ! + Đёĺęτê ! Button label that deletes the unfocused appearance for this profile. - Εпåвł℮ αċґỳļïċ мàŧєгΐàł !!! !!! + Елáъłε āçѓýŀϊĉ мάтεŕіâĺ !!! !!! Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Ǻφρℓīěş ά τѓàήšļű¢εņŧ ţėжŧůŗę ŧо ťĥė ьǻςќģřôůπď оƒ ťĥє ẁĩńďõω. !!! !!! !!! !!! !!! !!! + Дрρłĭèŝ á ťяåйşļüсеиţ ŧє×ţūřё ţθ тĥз ьā¢кĝгöũńð бƒ ŧĥε ẃĩήðбŵ. !!! !!! !!! !!! !!! !!! A description for what the "Profile_UseAcrylic" setting does. - Ũšė δзśктóр щāĺŀрªφěŗ !!! !!! + Űšē δєśκτθρ ώàŀłρåρèŕ !!! !!! A supplementary setting to the "background image" setting. When enabled, the OS desktop wallpaper is used as the background image. Presented near "Profile_BackgroundImage". - Ūśê тће ðĕşкţõр ẃâĺłρáφзѓ įmāĝě áş ţћê вãсќĝŗоúňđ îmªġё ƒǿя тнè ŧēřміпåļ. !!! !!! !!! !!! !!! !!! !!! + Ũѕě ťђе đëѕќτōр ŵαℓŀрαρёг įмάġē ªş тнє ьªċĸĝѓŏûⁿδ імáģě ƒσг тће τĕѓmіηáľ. !!! !!! !!! !!! !!! !!! !!! A description for what the supplementary "use desktop image" setting does. Presented near "Profile_UseDesktopImage". - Đϊŝçдŗď ¢ħаńġèŝ !!! ! + Ďϊѕĉªřđ ċнάиğеś !!! ! Text label for a destructive button that discards the changes made to the settings. - Ðĩŝćаѓδ ãĺĺ ùиѕàνēď šĕтτΐʼnĝś. !!! !!! !!! + Đΐѕċάŗď äŀĺ ϋйšǻνëď śēττìйğś. !!! !!! !!! A description for what the "discard changes" button does. Presented near "Settings_ResetSettingsButton". - Şâνέ ! + Śάνê ! Text label for the confirmation button that saves the changes made to the settings. - ⚠ Υōц ђáνέ ūηşаνеđ ćĥаηğëś. !!! !!! !! + ⚠ ¥θũ ђāνε űпѕªνêð čћалĝεŝ. !!! !!! !! {Locked="⚠"} A disclaimer that appears when the unsaved changes to the settings are present. - Ąδď à ήëш φřŏƒĩłě !!! !! + Āδð а π℮ẅ φяόƒіļę !!! !! Header for the "add new" menu item. This navigates to the page where users can add a new profile. - Рŗóƒìľзš !! + Ρяóƒĩļеś !! Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items. - ₣ŏċùŝ ! + ₣öċΰš ! An option to choose from for the "launch mode" setting. Focus mode is a mode designed to help you focus. - Мд×їмīżéđ ƒθčцś !!! ! + Μǻ×імϊžеδ ƒó¢űѕ !!! ! An option to choose from for the "launch mode" setting. Opens the app maximized and in focus mode. - Μâжίmΐźεδ ƒùľℓ śčязěπ !!! !!! + Μа×ίmїžęð ƒцłľ ѕćґєĕň !!! !!! An option to choose from for the "launch mode" setting. Opens the app maximized and in full screen. - ₣џļĺ şĉřëėи ƒõсúŝ !!! !! + ₣ŭŀĺ ščŕėёп ƒòçϋş !!! !! An option to choose from for the "launch mode" setting. Opens the app in full screen and in focus mode. - Мąхімîźзð ƒύľŀ şĉřέėⁿ ƒóсùŝ !!! !!! !! + Μà×ιmιźèď ƒџĺľ śćŗěёп ƒøčύś !!! !!! !! An option to choose from for the "launch mode" setting. Opens the app maximized in full screen and in focus mode. - ßêłł ńóτïƒίćªтíθⁿ ѕţÿłє !!! !!! + Бēŀĺ лòτιƒϊçâŧíóй šťÿŀэ !!! !!! Name for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Бėłℓ ňõтįƒî¢ăţίŏπ ŝťýłέ !!! !!! + Ъεļľ йόťїƒïсäţιòл śтŷłĕ !!! !!! Header for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Ćθлťřŏľś ώĥąť нäррєŋš ẃћèπ ťн℮ āρрľіçªťίŏń εmϊťş а BEL çнâѓâčŧέя. !!! !!! !!! !!! !!! !!! ! + Çσñţгøłŝ щђāţ ĥäρφεиŝ шћёη ŧђë åφрℓįčаτίбⁿ ēmīτѕ ǻ BEL ćħâŕäćтęг. !!! !!! !!! !!! !!! !!! ! A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"} - £аύлċђ тнΐš âрφľĩćǻťīõπ ωīťĥ ã ʼnēώ ēňνіяóйmęñτ ъĺоĉĸ !!! !!! !!! !!! !!! + £ãűп¢н ťнΐѕ äρρłîĉάτìøň ẅіţĥ ã йεω èņνĭґοńmĕʼnт ъłŏ¢ĸ !!! !!! !!! !!! !!! "environment variables" are user-definable values that can affect the way running processes will behave on a computer - Ẅђэŋ ëηåвľèδ, ŧнè Ťèґmιлαĺ ẃīļℓ ğĕņέяäтé â πзώ éņνĩяōймзπτ ьĺǿ¢ĸ ώђêη čřēàŧіńġ ηęẃ тáвś бř ρąπēѕ ẅїτĥ ŧнïѕ рřöƒίłę. Шђеň đìşăъľеδ, ţħ℮ ţάв/φãπę шïĺļ ϊňşŧ℮ǻð įņћэѓιŧ ŧħё ναґîåъĺěś ŧнè Τěямĩⁿǻĺ ώǻš ѕŧąŗťêđ щϊţћ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŵђέи έńąъĺèð, ŧħë Тĕґmїŋâļ ẁíŀľ ğ℮ⁿėřåτ℮ ª ήέщ εήνĩřσπmёиţ ьļōċķ ŵђêή ĉŗêάŧϊйģ ņěω ťάвѕ ŏг рαñёş щīтћ ţнїѕ рŕöƒíľē. Шћεń đīŝαъłëđ, ţħé ţáъ/φąňз ẃίŀℓ ìňśţедď îηħëяíţ ťнê νагϊåъĺêŝ ţђě Ťэŕmϊπāł ẁдŝ şтǻгтєđ ẅïťћ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - Зήåвļ℮ ĕ×рëґįмéńтåļ νïятцªŀ τеřmĭñάℓ φąšŝţђгоûģћ !!! !!! !!! !!! !! + Еņǻвŀè èжρ℮яιmëлťäł νіѓţυåł τ℮ŗmιлăļ ρäŝŝŧђѓόϋġħ !!! !!! !!! !!! !! An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Дµδíьļë !! + Àũđιьľē !! An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. - Ņбʼnë ! + Ňōπë ! An option to choose from for the "bell style" setting. When selected, notifications are silenced. - Ðίŝāьļė рãпз ăʼnĭмǻťĩбηŝ !!! !!! + Ďΐŝäъĺє ρǻņ℮ äηімдтіøйѕ !!! !!! Header for a control to toggle animations on panes. "Enabled" value disables the animations. - Бĺàċķ ! + Вľåĉĸ ! This is the formal name for a font weight. - Ъøļď ! + ßοℓď ! This is the formal name for a font weight. - Čύśтοm ! + €úšŧǿm ! This is an entry that allows the user to select their own font weight value outside of the simplified list of options. - Єжŧґä-Вŀå¢κ !!! + Ê×ţřа-ßℓąçќ !!! This is the formal name for a font weight. - Єхţяд-Бòļδ !!! + Эжτяå-Вóļď !!! This is the formal name for a font weight. - Éхťřā-Ŀìġĥτ !!! + Єхтгā-₤ïġħт !!! This is the formal name for a font weight. - £ĭģђť ! + Ŀįĝђŧ ! This is the formal name for a font weight. - Мέδĩùм ! + Мέδΐüm ! This is the formal name for a font weight. - Ŋσŕmàℓ ! + Йоřмãľ ! This is the formal name for a font weight. - Ŝёmϊ-Ьоℓď !!! + Ѕěмĭ-Βοℓδ !!! This is the formal name for a font weight. - Šέмΐ-£īĝнт !!! + Ѕēmį-Ļίġћτ !!! This is the formal name for a font weight. - Тĥΐņ ! + Ŧħіʼn ! This is the formal name for a font weight. - Ґεqŭíґëð łіğаťΰґєѕ !!! !! + Řεqцιŕèδ ļіġãťųяéŝ !!! !! This is the formal name for a font feature. - ₤ōčªľïż℮δ ƒόгмš !!! ! + Ļοςâĺϊżêð ƒσгmš !!! ! This is the formal name for a font feature. - Çòmφőśїτìοň/δêćомρσŝϊтįōŋ !!! !!! ! + Сõmρőšíτìοñ/đэčómрôşĩŧіŏņ !!! !!! ! This is the formal name for a font feature. - Ćőⁿŧêхŧūάℓ àℓťėŗņαťėś !!! !!! + Сőиτęхťûâł άĺτèřⁿáτεŝ !!! !!! This is the formal name for a font feature. - Ŝτàйðäřð ŀîģăτüřэš !!! !! + Ŝťάŋðαгđ ļιġдτύґэѕ !!! !! This is the formal name for a font feature. - Ĉŏńţėжťúдļ ℓĭġаτúřεѕ !!! !!! + Ĉöηťёхţџªŀ łîğáтüŕėş !!! !!! This is the formal name for a font feature. - Яêqűіяęδ νдѓïªŧïǿή άłтзґŋаţèš !!! !!! !!! + Ґëqΰίґ℮δ νâгĩдťΐŏň ãĺţěřńаţēś !!! !!! !!! This is the formal name for a font feature. - Ќéřπĭⁿġ !! + Κзяňϊņğ !! This is the formal name for a font feature. - Μăґќ ρóѕίťïοňĩηğ !!! ! + Μâѓк рǿŝϊтїôňίήĝ !!! ! This is the formal name for a font feature. - Мäгĸ ŧό мāŗκ рŏšϊťίóñīиġ !!! !!! ! + Мåѓķ тǿ mářк φοşíţĭòņįñģ !!! !!! ! This is the formal name for a font feature. - Ðιśτãпćе !! + Đìşťάňçе !! This is the formal name for a font feature. - Åćčęŝŝ аŀĺ αℓŧèяπατёš !!! !!! + ∆ćčεśś àļŀ ǻŀтèřйäť℮ѕ !!! !!! This is the formal name for a font feature. - Çǻşє šέηşϊτινę ƒǿřmś !!! !!! + Čåśé śęπšīтΐνé ƒŏѓmѕ !!! !!! This is the formal name for a font feature. - Ðεпōmìйăŧσґ !!! + Đĕиòmĭñдţόґ !!! This is the formal name for a font feature. - Ťĕŕмĭⁿдł ƒǿřмš !!! ! + Тëřмìήâŀ ƒοŗмś !!! ! This is the formal name for a font feature. - ₣гäčŧіõηŝ !!! + ₣řąċŧίοňѕ !!! This is the formal name for a font feature. - Ίηíŧιąľ ƒøřмѕ !!! + Ίпįτιâł ƒóřмŝ !!! This is the formal name for a font feature. - Мĕđίдľ ƒôяmś !!! + Μеðïªł ƒθŗmš !!! This is the formal name for a font feature. - Пŭmêґäţόя !!! + Νμm℮гåŧŏŗ !!! This is the formal name for a font feature. - Öгđįπдŀŝ !! + Фŗďίпåłš !! This is the formal name for a font feature. - Řзqůΐгêđ ċойŧėхťμäľ âĺŧěгпâťěş !!! !!! !!! + Гéqűіŗєð čοⁿťєхтűǻľ ªłťęгņăтěş !!! !!! !!! This is the formal name for a font feature. - Ѕčíëитįƒįс îйƒěґїöѓŝ !!! !!! + Ŝçīэņтïƒìċ ìпƒéŗïôŗѕ !!! !!! This is the formal name for a font feature. - Śцъŝ¢ŗїφт !!! + Ѕùьšçřιρŧ !!! This is the formal name for a font feature. - Şΰφеřş¢гιрŧ !!! + Śŭрзґŝςяîφτ !!! This is the formal name for a font feature. - Şłāŝнēð žэгθ !!! + Šĺαşĥęđ żëřő !!! This is the formal name for a font feature. - Àьθνе-ъāśé mαґĸ рοşіτĭǿⁿīлģ !!! !!! !! + Ąьõνė-ьäŝе мāґќ ροśίτíόйїπģ !!! !!! !! This is the formal name for a font feature. - Ĺäűņсĥ śìžέ !!! + Ŀâûņčн śĩźε !!! Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". - Ŧћє ʼnμmвεг øƒ ŕôшš άпð ĉòŀΰmиś ďîѕφĺªỳёδ ĩŋ τђ℮ ωįńđоω ûφòņ ƒιŗşт ℓбâδ. Мέàşūґĕδ īπ çĥàřáćŧèŕѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Тне иµmвэѓ оƒ ябẃş äйδ ¢őľųmпś ðīѕρĺαÿĕď ϊй ŧћё ẁίйďοщ úφøл ƒīřŝτ ļøåď. Мздŝůязð îņ ¢ħаѓàćтĕřş. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "rows" and "columns" settings do. Presented near "Globals_LaunchSize.Header". - Łªûй¢ħ ρǿŝϊтīόŋ !!! ! + Łдϋйċĥ рοšįţīσи !!! ! Header for a group of settings that control the launch position of the app. Presented near "Globals_InitialPosX" and "Globals_InitialPosY". - Ťħè ΐⁿїŧĩάł рσšĩтïŏŋ бƒ тћė тěŗmĭńąŀ ẃīⁿðǿώ űрôл şťåřŧũφ. Ẅђēŋ ľáμņςĥĭйġ ªѕ má×ïмĩźэδ, ƒύľŀ şςґé℮ⁿ, οґ шιŧħ "Ĉĕŋťéґ őл łªüⁿçћ" єňªвłεδ, ŧħìŝ īŝ ūşèδ ŧσ ţäřğёт ŧħë мōņĭтöя ǿƒ íñτęřéšт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τћє ïņіτīдŀ φоşїţїθŋ бƒ ŧĥê т℮řмĩлąŀ ẁιŋδøщ ΰρőň şтáгτυр. Щĥеⁿ ľǻűņċђĭπĝ ãś mªхíмìžĕď, ƒůļĺ ś¢ŕêέŋ, θŗ ẅīťĥ "Ĉëňτëř ŏή ļąùⁿċĥ" єńάьĺэď, ŧћίš ĩš ûŝèď τø ţâгĝεţ тĥє мøñіŧōŕ øƒ įπťέřέśт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "X position" and "Y position" settings do. Presented near "Globals_LaunchPosition.Header". - Αľļ + Аľļ An option to choose from for the "bell style" setting. When selected, a combination of the other bell styles is used to notify the user. - Vϊѕũǻŀ (ƒļαşħ ŧâŝκьαř) !!! !!! + Vìŝüåℓ (ƒℓдşĥ ťªšкьãґ) !!! !!! - ₣ŀдšн ŧąşќьåг !!! + ₣łäśĥ ταŝķьãř !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the taskbar is flashed. - ₣ℓâśĥ шìйδθẃ !!! + ₣łáşћ ẁįйđǿŵ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the window is flashed. - Àđď πеẁ !! + Ąðδ ⁿëώ !! Button label that creates a new color scheme. - Ď℮ļëŧè çōļõř ѕçћěмэ !!! !!! + Ď℮ļēŧ℮ çòļбґ ş¢ђèме !!! !!! Button label that deletes the selected color scheme. - Èďîţ ! + Εďîŧ ! Button label that edits the currently selected color scheme. - Ðêŀєťз ! + Ðэĺëţê ! Button label that deletes the selected color scheme. - Йдmê ! + Ламē ! The name of the color scheme. Used as a label on the name control. - Тђ℮ πάmе θƒ ţћё çόľоŗ ŝçћėmě. !!! !!! !!! + Ťћè ńǻmè őƒ ţнέ čθłōř śςнēмэ. !!! !!! !!! Supplementary text (tooltip) for the control used to select a color scheme that is under view. - Ďêłεŧê φяŏƒíļе !!! ! + Đēℓëţĕ ρяôƒϊļё !!! ! Button label that deletes the current profile that is being viewed. - Ťħё ńãmέ ôƒ ŧђ℮ φѓόƒϊŀё тнãť ªρφёǻѓś ϊй ŧћέ δѓσρďŏώй. !!! !!! !!! !!! !!! + Ţħĕ ńąmė õƒ ŧħë ρґõƒΐľэ ŧћâť αррëãѓŝ ĩñ τђĕ đґöрðőẃņ. !!! !!! !!! !!! !!! A description for what the "name" setting does. Presented near "Profile_Name". - Иαмê ! + Йămε ! Name for a control to determine the name of the profile. This is a text box. - Иåmё ! + Иåмë ! Header for a control to determine the name of the profile. This is a text box. - Ťяäⁿšφάŕéπсγ !!! + Тґαиšрāгеʼn¢ў !!! Header for a group of settings related to transparency, including the acrylic material background of the app. - ßαсκġŕǿύиď ĭмàĝе !!! ! + Ьãćκġяοùņđ ϊмаġе !!! ! Header for a group of settings that control the image presented on the background of the app. Presented near "Profile_BackgroundImage" and other keys starting with "Profile_BackgroundImage". - Ćύŕśöѓ ! + Ćϋřşθř ! Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". - Ăďđíŧįоʼnąļ šεттϊиğş !!! !!! + Áďďітĭόйāĺ şęţтīпğś !!! !!! Header for the buttons that navigate to additional settings for the profile. - Ŧέ×τ ! + Ţєхŧ ! Header for a group of settings that control the appearance of text in the app. - Ŵΐʼnðош ! + Шΐñðöω ! Header for a group of settings that control the appearance of the window frame of the app. - Όφèⁿ γбцѓ settings.json ƒĩℓę. Áℓť+Çŀїçĸ тô ŏρέň ŷόüг defaults.json ƒĭłė. !!! !!! !!! !!! !!! !!! !!! + Øφĕⁿ ÿόυг settings.json ƒϊłз. Аℓť+Ċľĭčк ťб öφėй ŷοüř defaults.json ƒιĺę. !!! !!! !!! !!! !!! !!! !!! {Locked="settings.json"}, {Locked="defaults.json"} - Řęлămз ! + Ґęʼnámě ! Text label for a button that can be used to begin the renaming process. - Тħΐš ċóľöř śćђзmě ĉǻηňǿτ вē ďéŀеτеδ ǿř řĕπǻměδ ьē¢åûşĕ įť їš ϊņςļũδэđ ъý ďёƒäûľţ. !!! !!! !!! !!! !!! !!! !!! !!! + Ţнïѕ ćöℓσř ѕčнěмз ćάиʼnôŧ вέ δзĺєŧéδ οŗ ŗëήάмέð вëċäůѕĕ îŧ įş ιʼnćĺùðèď ъÿ ďėƒāûľť. !!! !!! !!! !!! !!! !!! !!! !!! Disclaimer presented next to the delete button when it is disabled. - Υèš, ðëŀēŧέ čóℓόŕ ŝсђëmé !!! !!! ! + Ŷêś, đëℓетє čóĺοг ŝςнéмє !!! !!! ! Button label for positive confirmation of deleting a color scheme (presented with "ColorScheme_DeleteConfirmationMessage") - Αŕέ ўοΰ šµѓě ýбù ώдпţ το ðεłёţз тĥΐś ςόļбř ŝċнéмέ? !!! !!! !!! !!! !!! + Âŕĕ ỳõυ ѕΰгè ỳöû ŵаπť тŏ δеĺĕŧэ ťħīŝ ĉòľōґ śçђęmέ? !!! !!! !!! !!! !!! A confirmation message displayed when the user intends to delete a color scheme. - ∆ć¢éρţ гęйªмé !!! + â¢єрτ řέñαмё !!! Text label for a button that can be used to confirm a rename operation during the renaming process. - Čâηςēľ гёήамε !!! + Čдл¢єℓ г℮ήämė !!! Text label for a button that can be used to cancel a rename operation during the renaming process. - Ѕсĥємėš δêƒïпеđ н℮ŕё ςªй ьέ άрρļíĕδ ţǿ ÿōũг φŕőƒîłэѕ џлďεг тћё "Дрρêãŗªη¢ёѕ" ŝзĉţĩøʼn оƒ ťĥ℮ φґбƒίĺе ŝĕţťіпģś ραģэѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Śĉђέмёş δėƒίńéđ нëяё ĉåʼn вє åρφľїеđ ţθ ўσϋя ряοƒïłėѕ ũηδēř ŧнё "Λрρёąяªŋćеś" ѕ℮стīол øƒ ţħë φřóƒΐĺé şêţťΐņĝś рäğёš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A disclaimer presented at the top of a page. "Appearances" should match Profile_Appearance.Header. - Şêττįйĝŝ ðëƒĭʼnéð нėяę шīľļ âρρłŷ ţø аĺļ ρяôƒïļęś ūпĺėšś ťћёý åѓě õν℮яŗΐďđêņ вў â φґбƒíĺэ'ŝ śéţтίηġş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Şέŧтįηġş ðëƒίņєđ ђέřé ẁïĺĺ ąρрļŷ τō дłľ φґŏƒïŀęş űπℓёšŝ тнêÿ ªřэ σνέřяіððєʼn вỳ á ρŗóƒĩłε'ś ѕєţтιπğѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. See "Nav_ProfileDefaults.Content" for a description on what the defaults layer does in the app. - Ύēş, ďёļėťέ рřóƒîŀз !!! !!! + Υêš, ďęłēтė ргõƒϊŀε !!! !!! Button label for positive confirmation of deleting a profile (presented with "Profile_DeleteConfirmationMessage") - Άґє γоũ šūѓê ÿбυ щąήт ţо ďєļěтē τђîѕ рřθƒĩļз? !!! !!! !!! !!! ! + Àґэ ỳŏų šüґє уοū ŵǻπт ŧõ đēľеτ℮ ţĥĭś ρгòƒįℓэ? !!! !!! !!! !!! ! A confirmation message displayed when the user intends to delete a profile. - Ѕдνé ãĺℓ ųлşąνĕδ śĕŧťíπĝš. !!! !!! ! + Şąνè âłł ΰňѕăνêď şзŧтĩņģŝ. !!! !!! ! A description for what the "save" button does. Presented near "Settings_SaveSettingsButton". - Ťàь šщϊţçнėř ιňтēґƒǻĉз ѕтýłє !!! !!! !! + Тãь şωïţςħ℮ŗ ϊŋţêŗƒăċè šţўłē !!! !!! !! Header for a control to choose how the tab switcher operates. - Šеŀěçţš ẃђιčћ ĭηţėřƒãčε ŵïĺł вė џŝèď ŵћэņ ýοϋ ŝωĭтсħ τªьś ũşїŋģ ţĥ℮ κέỳвоаѓδ. !!! !!! !!! !!! !!! !!! !!! !! + Ѕэℓęćţś ẅĥϊςћ ϊηŧεгƒǻçę шïłľ ьє ùšĕđ ẁнëη ÿóŭ śẁΐτčħ ţāьś úŝìŋģ τђë кêýъøàŕδ. !!! !!! !!! !!! !!! !!! !!! !! A description for what the "tab switcher mode" setting does. Presented near "Globals_TabSwitcherMode.Header". - Ŝεφāгàтê щíлđõŵ, ĭň мõѕτ ŗěćěʼnтŀу µŝėđ ōŕδèř !!! !!! !!! !!! ! + Śēφăřâŧę ωΐⁿđόẃ, ίŋ моšτ řєсзпťŀу ūŝėδ όřďзя !!! !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in most recently used order. - Śėφǻґãτз шίπðбш, ίл тǻъ ŝŧґΐρ ǿŕðёѓ !!! !!! !!! ! + Śèрαґąтę ωĭŋđõẃ, įй ŧăв şţŗίр σŕδέř !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in the order of the tabs at the top of the app. - Ťŗªďϊŧіōñåļ ńąνïĝαţїоπ, ηø śёρåяάтε ŵΐпðōώ !!! !!! !!! !!! + Тгăđĭтϊóʼnąĺ ŋāνїġäţįǿň, пø śěφαяáţέ ẃīиðσώ !!! !!! !!! !!! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is hidden and does not appear in a separate window. - Τë×τ ƒθŗмąтŝ τö ¢òρу ţо тђе ćℓĭρвоàґď !!! !!! !!! !! + Тєжτ ƒοřmăτś τò ćбρỳ τσ ŧнê çĺīрьóäяď !!! !!! !!! !! Header for a control to select the format of copied text. - Ρℓαíŋ тèхτ óńĺў !!! ! + Рļãĭп τèхτ ŏŋℓў !!! ! An option to choose from for the "copy formatting" setting. Store only plain text data. - ĤΤМ₤ ! + ΗΤΜ₤ ! An option to choose from for the "copy formatting" setting. Store only HTML data. - ΓΤ₣ + ΓŦ₣ An option to choose from for the "copy formatting" setting. Store only RTF data. - ßōŧн ĤТΜĹ àʼnď ΓТ₣ !!! !! + Вőťђ ΗΤМĻ áиδ ҐŢ₣ !!! !! An option to choose from for the "copy formatting" setting. Store both HTML and RTF data. - Ρℓęаśě снôόŝε ǻ δіƒƒêřεйτ ⁿãmè. !!! !!! !!! + Ρŀёαѕё čђбòşë ă ďϊƒƒěř℮ñť ñāме. !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the subtitle and provides guidance to fix the issue. - Τћįѕ čóĺбř ѕ¢ђěmê ńâмē ĭŝ áľřεãðŷ ĭή ųś℮. !!! !!! !!! !!! + Τћĭѕ ċôℓōѓ śĉĥэмэ ήămē įś ǻŀяĕäδÿ ïň ųŝé. !!! !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the title, and explains the issue. - Áϋţòmàţĭčдĺĺÿ ƒôĉůş ρªńє őп мôūšє нŏνзŕ !!! !!! !!! !!! + Дµţőmäŧïçąłľў ƒõςűš рâлε òи møüŝе ĥǿνêř !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. - Ρåйε αňíмāťĭθπѕ !!! ! + Ρǻŋэ âʼnіmаţїóňš !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. - Àℓŵаÿś ðïŝрľâў âņ ïċǿл íⁿ τħз ņσтιƒĩсąτϊθñ āѓэά !!! !!! !!! !!! !! + Ǻľẁаỳś δīśφŀдý àń ΐсöⁿ ίή ŧђέ ńòŧіƒΐçаτīоп āřεà !!! !!! !!! !!! !! Header for a control to toggle whether the notification icon should always be shown. - Нîðĕ Ţεŕмïⁿąŀ ϊⁿ ťħę йõŧïƒΐςатĭбп άґèа шħéη ìτ ìş mīиιmїżĕđ !!! !!! !!! !!! !!! !!! + Ĥιđз Ŧēґмιñâŀ їй ťĥė йöτίƒι¢ατīόп âѓ℮ą ŵћєŋ íţ īŝ мíňϊмìżèđ !!! !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized. - Яėŝęŧ ťô ĩπħëŕįŧέđ νäℓũє. !!! !!! ! + Гėѕ℮ŧ тб ίиђзřīŧęď νάľųé. !!! !!! ! This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button. - Ťĕŗмïлаļ ċθℓσґŝ !!! ! + Ťэřmїñдļ ćοłόѓŝ !!! ! A header for a grouping of colors in the color scheme. These colors are used for basic parts of the terminal. - Ŝўşтэм çσŀǿѓş !!! + Śÿšτем ćóļŏяŝ !!! A header for a grouping of colors in the color scheme. These colors are used for functional parts of the terminal. - €ŏĺοѓş ! + Čöľσřś ! A header for the grouping of colors in the color scheme. - Ŕ℮śèţ ŧö νâĺύĕ ƒяθm: {} !!! !!! + Яεѕėτ ťο νдľϋē ƒřоm: {} !!! !!! {} is replaced by the name of another profile or generator. This is a text label on a button that is dynamically generated and provides more context in the {}. - Śђöш ªľŀ ƒопŧš !!! ! + Ŝĥóω άłł ƒσήţş !!! ! A supplementary setting to the "font face" setting. Toggling this control updates the font face control to show all of the fonts installed. - ΃ êиäьŀєð, ŝнθщ αŀĺ іⁿşţăłļéď ƒőńťŝ ïʼn ţнз łìѕт αъóν℮. Őťђêґщΐşё, ōⁿŀý śћöẅ тн℮ ĺιšţ θƒ mòⁿоŝρăĉε ƒôптѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ĩƒ ℮ńãъļэđ, ѕнōŵ ǻℓℓ ĩиşţāŀĺĕđ ƒöňтş ϊⁿ ťĥе ļįśт ãвǿνέ. Ǿτĥéгẃїѕè, θйłў şђõщ ŧĥé ĺĩśт òƒ móйôśрªĉ℮ ƒòлтś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts". - Ĉґêǻťė Αрφеагăⁿсє !!! !! + Čŕēäтэ Ǻφφёάѓǻπċë !!! !! Name for a control which creates an the unfocused appearance settings for this profile. Text must match that of "Profile_AddAppearanceButton.Text". - Ċгëаŧз ãñ úʼnƒσĉύśέð άρρěářăл¢э ƒǿя тħιѕ ρяôƒïľę. Ţнīş ŵĭļŀ ьє тђė ãφреǻядņçє бƒ тћĕ ряòƒΐℓė ẁнêл ίτ ìş ίиāćŧíνě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Čŗèâťё āŋ цπƒõčùśĕđ àρрéǻŕàиĉз ƒōѓ ţнΐѕ φřôƒιℓέ. Ťнįŝ ώīĺŀ ьэ τђέ àрφэářαлĉē ōƒ ťĥє φŗθƒίľе ŵнєņ ìт іš ίπąĉŧīνë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the create unfocused appearance button does. - Ď℮ŀёţё Ăрφ℮дгаňć℮ !!! !! + Ďєĺėтē Âφφėãřãйçĕ !!! !! Name for a control which deletes an the unfocused appearance settings for this profile. - Ďзłėŧě τħε υлƒóčúŝēδ ǻρρêäřªπçє ƒõѓ тћιš ρяóƒїĺē. !!! !!! !!! !!! !!! + Ðęłέţέ тĥė űņƒŏсůŝěδ äρρэαгäņċе ƒőґ ŧĥĩŝ φѓöƒΐℓè. !!! !!! !!! !!! !!! A description for what the delete unfocused appearance button does. - Ýĕş, đеľеτέ ќéỳ ьĩηδĩйğ !!! !!! + ¥ėş, ðεļзтє ĸеў ъîńδîήĝ !!! !!! Button label that confirms deletion of a key binding entry. - Àѓė ýøü şцѓє ÿбύ щáиţ ťő δёĺèτε τђïş ķеỳ ъįиδϊлĝ? !!! !!! !!! !!! !!! + Âѓ℮ уôύ śΰяє ÿθů щăñŧ ťô ðēłέτĕ тнįś κεý ьΐʼnđîήğ? !!! !!! !!! !!! !!! Confirmation message displayed when the user attempts to delete a key binding entry. - Ĩлνàℓīδ ĸëÿ ćнŏгď. Рℓěǻşέ ĕлťěŗ å νãľīď ќéÿ ċħòŕď. !!! !!! !!! !!! !!! + Īήνāĺίď ĸéÿ çĥόŕď. Рľэàşĕ ĕńτ℮ŗ á νāłìδ ќєỳ ċћöяð. !!! !!! !!! !!! !!! Error message displayed when an invalid key chord is input by the user. - Ϋěѕ + Ўεş Button label that confirms the deletion of a conflicting key binding to allow the current key binding to be registered. - Ţħέ φřονîďèđ кэу çнǿřδ ΐѕ αĺґęąδŷ ъėĭŋĝ ųŝêδ ъў ŧнĕ ƒõĺľõщíήğ ãčŧìôⁿ: !!! !!! !!! !!! !!! !!! !!! + Ťћĕ рŕöνĭđéδ ķèу ςћбяð ϊŝ âľŗзâδý вέїņģ μŝ℮đ вý тħĕ ƒσĺłоẁιńğ ǻčтíôп: !!! !!! !!! !!! !!! !!! !!! Error message displayed when a key chord that is already in use is input by the user. The name of the conflicting key chord is displayed after this message. - Ẅόüłđ ÿθų łįκє ţο ǿνęѓώгϊŧэ īť? !!! !!! !!! + Шòϋŀð ŷòû ļĩķε ţó оνēяẅгіţê ĭţ? !!! !!! !!! Confirmation message displayed when a key chord that is already in use is input by the user. This is intended to ask the user if they wish to delete the conflicting key binding, and assign the current key chord (or binding) instead. This is presented in the context of Actions_RenameConflictConfirmationMessage. The subject of this sentence is the object of that one. - <üήйámέđ ¢θmmãņδ> !!! !! + <ũήлâmзδ çőmmäήð> !!! !! {Locked="<"} {Locked=">"} The text shown when referring to a command that is unnamed. - Áçčĕрт ! + Дĉĉєρτ ! Text label for a button that can be used to accept changes to a key binding entry. - Сªʼnĉэļ ! + Ćăηčėľ ! Text label for a button that can be used to cancel changes to a key binding entry - Ðеľęťё ! + Ďêľęţ℮ ! Text label for a button that can be used to delete a key binding entry. - Єđĩт ! + Ĕďїт ! Text label for a button that can be used to begin making changes to a key binding entry. - ∆ďđ изẁ !! + Λδð πĕш !! Button label that creates a new action on the actions page. - Âĉτìόņ ! + Ǻčŧΐǿπ ! Label for a control that sets the action of a key binding. - Ĩηφüţ γōüѓ đêšįгеď ќęŷвοâяď ѕђбŕтĉϋт. !!! !!! !!! !! + Їпρũτ ỳθϋř đєѕĭґёð ĸĕýъõàѓď şĥøґţсµŧ. !!! !!! !!! !! Help text directing users how to use the "KeyChordListener" control. Pressing a keyboard shortcut will be recorded by this control. - şħōгť¢ΰť ļìŝţēʼněŕ !!! !! + şĥôŕţсúŧ ŀíšťëņзґ !!! !! The control type for a control that awaits keyboard input and records it. - Śћοŗτčùţ !! + Ѕнŏřтςûт !! The label for a "key chord listener" control that sets the keys a key binding is bound to. - Ţєхť ₣øřmдťťîπģ !!! ! + Τзжτ ₣õґmāťţĭήġ !!! ! Header for a control to how text is formatted - Ìʼnтèŋşэ ŧèжť šţÿłέ !!! !! + Ìηţепśê ťēжţ ѕтγľє !!! !! Name for a control to select how "intense" text is formatted (bold, bright, both or none) - Ĩñťėňşę ťєжτ śţуŀė !!! !! + Įņтēпśè ŧзхŧ šтỳŀę !!! !! Header for a control to select how "intense" text is formatted (bold, bright, both or none) - Йõńє ! + Ņòήз ! An option to choose from for the "intense text format" setting. When selected, "intense" text will not be rendered differently - βοłđ ƒσňτ !!! + Βõŀð ƒŏńт !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as bold text - Бґĩĝħť ¢øℓбŕş !!! + Ъřîĝћτ сøŀôřš !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered in a brighter color - βŏļð ƒθπţ ώΐţћ ьřîġĥτ ćółόяś !!! !!! !! + Вόłδ ƒöπŧ ώιťђ вяιğĥτ ċθŀôřš !!! !!! !! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color - Ąůτσмâŧιçàľℓŷ ћϊðē ẁĭиđőщ !!! !!! ! + Ãũţõmãτíčąľľγ ћĭďè ωįŋđõŵ !!! !!! ! Header for a control to toggle the "Automatically hide window" setting. If enabled, the terminal will be hidden as soon as you switch to another window. - Ĭƒ ĕйâъℓёđ, ţħέ ťëґmìήāŀ шĭļľ ьз ĥіðďėη дš ŝŏοй âś ÿøμ šŵíť¢н τō ăиθţђзŕ щĩηðόώ. !!! !!! !!! !!! !!! !!! !!! !!! + Ĩƒ ęлдьľēď, τће ťëŗmΐñãļ шíļℓ ьē ĥіďđèπ дŝ ѕõσй αş ÿōџ şẁΐţćђ тő дпθŧĥεѓ щιńđǿẃ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Automatically hide window" setting does. - Τћίş ςöľбŗ şĉĥέmē ĉāйпōт ъě ðёļέτěδ ьéčăüşě їţ ïś ĭпċľµďęδ вỳ ďєƒåύļŧ. !!! !!! !!! !!! !!! !!! !!! + Τħιś ĉбłǿŗ š¢ĥêмë ċáʼnñοţ вê ðёŀέţěð ъεċâύŝë ΐт їś ιʼnĉłůðзď вγ δεƒǻυļτ. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to delete the selected color scheme when that functionality is disabled. - Τнιš ĉøℓøя šçђзмè ¢áήηθŧ ъэ ŕ℮ⁿàmĕď вęçдûşέ ĩт ΐş íηĉļμďëď ъý ðĕƒдùŀŧ. !!! !!! !!! !!! !!! !!! !!! + Ŧђīş ćòℓőѓ šςђėmэ çåπⁿσť вě ґзήămэð ьê¢ąųšę įť ϊş ĩŋçĺūđĕδ ьỳ ďęƒäúľτ. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to rename the color scheme when that functionality is disabled. - ðëƒдµĺт !! + đ℮ƒаűľť !! Text label displayed adjacent to a "color scheme" entry signifying that it is currently set as the default color scheme to be used. - Ѕεт åş đ℮ƒăũľţ !!! ! + Ѕ℮τ ąś đзƒāúℓŧ !!! ! Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use. - Ẁαřñ ẅнεл ¢ĺθśíŋĝ mόґε ţнāη öπё тáв !!! !!! !!! ! + Щąѓʼn ώħęπ ćłŏŝīηġ мŏřε τнάⁿ ŏŋе ταъ !!! !!! !!! ! Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open. - Ẁϊñďбŵѕ Ţēřmīñάŀ ϊš řųηήϊʼnġ ĩň φøѓŧáьľз мøδέ. !!! !!! !!! !!! ! + Шїήďόŵś Ţèŗмíиàĺ īş ŗűñиίлġ îŋ ρōŕтąьŀę mоδё. !!! !!! !!! !!! ! A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder. @@ -1754,15 +1762,15 @@ {Locked} - Ĺ℮ãřή möгę. !!! + Ŀěąґή mбґę. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - Ẃαѓηīпġ: !! + Ẃáŕпīлģ: !! Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - Сћθøśïñğ ā ňóņ-мóлöšρáčēð ƒōπŧ ώĩľĺ ĺіĸєĺỳ яέŝμℓт ϊņ νįšūăŀ ǻŗţίƒăĉŧš. Ůšε дť ỳøŭŕ бώʼn ďΐŝςяęτìǿņ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + €ћόбŝіήġ à йőл-mοñöşрă¢ēď ƒòηŧ шїļℓ ŀîĸеŀγ ŕэşцĺт ιπ νϊśцǻℓ ąѓтíƒăĉτş. Ųŝε åţ γøμг òщⁿ ďĩѕ¢řеτĭоп. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw index 5df6b71f7da..c754129df83 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw @@ -118,528 +118,528 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Çґєǻţе Ñєω βϋτťσπ !!! !! + Čяєáť℮ Ñέω Ьϋττøп !!! !! - Ňёẁ êмρţγ рґοƒïĺė !!! !! + Иèẁ ℮мрτỳ φŗòƒіĺє !!! !! Button label that creates a new profile with default settings. - Ðμрĺίčάтě å ρґøƒїℓέ !!! !!! + Ďΰρĺīċãŧё а φѓōƒįℓє !!! !!! This is the header for a control that lets the user duplicate one of their existing profiles. - Đűрĺĭċдţз ßцţŧοʼn !!! ! + Đūφℓĭсåţз Ъųтťоⁿ !!! ! - Đύφℓΐçàτέ !!! + Ďūρĺïčаτë !!! Button label that duplicates the selected profile and navigates to the duplicate's page. - Тħιś соℓòг śçћëmε ĩš рǻѓт ǿƒ тћэ ьūìℓť-ϊŋ ŝēťтіⁿĝŝ ǿř åπ íήşŧāĺļėð ежţзηšïŏи !!! !!! !!! !!! !!! !!! !!! ! + Τђΐѕ ĉöℓσř ŝćнємз їś рāřτ ŏƒ ţћê вŭĭŀţ-іń śēŧтіňġѕ бѓ áή ϊńşτâĺłėδ ě×ŧ℮ʼnŝïŏл !!! !!! !!! !!! !!! !!! !!! ! - Тό mαќé ĉнàņġêś ŧο ŧнĭś сőℓòя ѕċħèмĕ, γθц мŭѕт māκë д ςőру σƒ ίŧ. !!! !!! !!! !!! !!! !!! ! + Τó маке ćĥãйģëѕ тô τĥįş сõľøя ѕċĥэмэ, γøũ мцśт маќē á сσρў όƒ īť. !!! !!! !!! !!! !!! !!! ! - Мãķё ā ςόφý !!! + Мǻкε д ćоφý !!! This is a call to action; the user can click the button to make a copy of the current color scheme. - Şєť ċоļόř śçђęmэ ąş đэƒąύℓт !!! !!! !! + Śέť ¢øŀőř ś¢ĥеmε άş ðеƒаúℓτ !!! !!! !! This is the header for a control that allows the user to set the currently selected color scheme as their default. - Āρφĺў ţħĭś ςõĺοг ŝçħéмę ťо ªłľ γóμř φяõƒΐℓêş, вý ð℮ƒάűĺт. Ìňðіνįðŭăŀ ρѓоƒìľěš ċдη ѕтïłļ śέŀέςŧ τнέĩґ ôŵʼn ¢óľøґ šćђémëš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Άрφłў τћįŝ соłõř śĉĥэме ťö ăłĺ уôμґ φŗбƒіłėś, ъу ðēƒαύļт. Įηðîνΐďúåļ φяόƒįℓзś ċαή ŝťιļļ śěľєĉτ ťħėįř ŏщп ċôľōř ѕ¢ĥеmέѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description explaining how this control changes the user's default color scheme. - Гèпäмэ ¢бŀοг śċђ℮mέ !!! !!! + Ŗęπámĕ ςöℓθґ ѕςħémė !!! !!! This is the header for a control that allows the user to rename the currently selected color scheme. - Ъάćкġřōüήď !!! + Бдĉкğřöµʼnď !!! This is the header for a control that lets the user select the background color for text displayed on the screen. - Ьℓàĉĸ ! + Ъļдсķ ! This is the header for a control that lets the user select the black color for text displayed on the screen. - βĺűё ! + ßłųэ ! This is the header for a control that lets the user select the blue color for text displayed on the screen. - Βŗīġĥţ вŀãćк !!! + Бŗîġнť ьŀåčκ !!! This is the header for a control that lets the user select the bright black color for text displayed on the screen. - Бŕìğħτ ъļüé !!! + Бѓιġћŧ вℓμê !!! This is the header for a control that lets the user select the bright blue color for text displayed on the screen. - Бŕĭġħţ ĉýăņ !!! + Βŕįģħţ сýάπ !!! This is the header for a control that lets the user select the bright cyan color for text displayed on the screen. - Ъříĝħť ĝŗеėπ !!! + Βгîğĥτ ğřę℮ņ !!! This is the header for a control that lets the user select the bright green color for text displayed on the screen. - Βřϊġђт рùѓρłē !!! + Бřïğĥŧ φúѓφłе !!! This is the header for a control that lets the user select the bright purple color for text displayed on the screen. - Ъŗíģħт ŕ℮ď !!! + Ьгíġћť ŗέđ !!! This is the header for a control that lets the user select the bright red color for text displayed on the screen. - Ьґíġђт ŵĥίτέ !!! + Ъřĭĝнť щђìţέ !!! This is the header for a control that lets the user select the bright white color for text displayed on the screen. - Бгіĝћт γëłĺθώ !!! + Βяîģħŧ ÿέŀℓôώ !!! This is the header for a control that lets the user select the bright yellow color for text displayed on the screen. - Сµяšöř сōŀőř !!! + Ćùřѕòŗ сοļöŕ !!! This is the header for a control that lets the user select the text cursor's color displayed on the screen. - Ĉÿąй ! + Ćýαπ ! This is the header for a control that lets the user select the cyan color for text displayed on the screen. - ₣òґэģґöŭŋď !!! + ₣öґëġřöΰπđ !!! This is the header for a control that lets the user select the foreground color for text displayed on the screen. - Ģřĕėŋ ! + Ğřëéη ! This is the header for a control that lets the user select the green color for text displayed on the screen. - Ρūгρļě ! + Рυѓφļè ! This is the header for a control that lets the user select the purple color for text displayed on the screen. - Ş℮łёсťΐøʼn ьа¢ќġŗбùñđ !!! !!! + Śέŀєçŧïоπ ъαĉķġřòϋńď !!! !!! This is the header for a control that lets the user select the background color for selected text displayed on the screen. - Γεđ + Гęδ This is the header for a control that lets the user select the red color for text displayed on the screen. - Ẁħìŧэ ! + Ẅћϊŧě ! This is the header for a control that lets the user select the white color for text displayed on the screen. - Ϋĕŀľбώ ! + Ỳĕľłбщ ! This is the header for a control that lets the user select the yellow color for text displayed on the screen. - Ŀäйĝüáğē (ŕéqцìřěš ґëļаϋňсђ) !!! !!! !! + Ļāňĝūãğэ (řэqϋĭřêş řзℓаϋⁿċĥ) !!! !!! !! The header for a control allowing users to choose the app's language. - Ŝĕłĕćťś â ðΐѕφļªу łάⁿğűāģė ƒбŗ ţĥē āφρłĭсǻŧіòи. Ţђϊś щĩŀŀ öνêяѓĩďě уθũŗ ðзƒăϋļŧ Windows ϊлťēřƒáĉε ĺãⁿġųåģє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ş℮ĺе¢ŧş д ðіšрĺāу ľâʼnĝųдĝē ƒŏŕ ŧħě αφρłĩçâťιōή. Ŧĥįѕ ẅīļł òνеřŗìðė ўôûř ðèƒäųľт Windows іпτèѓƒăçэ łăŋĝϋǻğз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description explaining how this control changes the app's language. {Locked="Windows"} - Ũѕè şýŝτем δзƒаûļť !!! !! + Úŝë ѕýšτëm ďęƒāûĺŧ !!! !! The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead. - Δłшдÿś ŝħǿẃ ŧǻьѕ !!! ! + Āľшãγś şћǿω τдвś !!! ! Header for a control to toggle if the app should always show the tabs (similar to a website browser). - Ẁћéл ðìśªъłέđ, ŧħē ťǻъ ъаř ẃìℓŀ âρрεãř ŵĥзⁿ á йёŵ ťâъ íѕ ĉѓєаţěð. Čǻňňǿτ вε δίşåьℓέđ щĥíļэ "Нιđê τħę тĭŧŀê вãř" ĩś Őή. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Шħеń ðîŝâъŀèδ, ţĥé τåь ьåґ ώĩĺľ àрρ℮аŗ ẁђėⁿ à ňзẅ тáв ϊš ćґэäŧêδ. Ćāʼnήǿţ ьê ðĩšäъľĕδ ẁђΐℓέ "Ήίðě τнё τíŧℓё вдŗ" íš Ōй. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "always show tabs" setting does. Presented near "Globals_AlwaysShowTabs.Header". This setting cannot be disabled while "Globals_ShowTitlebar.Header" is enabled. - Рôśϊτĩσň ǿƒ ⁿєẅļý сřèąţĕđ τåвš !!! !!! !!! + Ρоšίťіθή òƒ ⁿέщļÿ čгεдťεδ ťªъš !!! !!! !!! Header for a control to select position of newly created tabs. - Ŝφεçĩƒίεŝ ẃћэřє ñещ тâъѕ аφρĕâѓ ïй ţне τав яóẁ. !!! !!! !!! !!! !! + Ѕрę¢іƒĭзš ẃћèге ņэẁ ţаъş āрφĕāř íη ťħє тăв ŗòẅ. !!! !!! !!! !!! !! A description for what the "Position of newly created tabs" setting does. - ∆ƒţėг ťĥè ĺαŝτ ţάв !!! !! + Аƒţеř ŧђ℮ łâşτ ţäв !!! !! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the last tab. - ăŧéŗ тĥё ĉůŗřéⁿţ τāь !!! !!! + Δƒţєŗ τħ℮ ćŭѓя℮лť ŧāъ !!! !!! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the current tab. - Дúťοmªтīćàłĺγ ¢σρŷ ѕεľзсτîоŋ τб ¢łíφъóáґδ !!! !!! !!! !!! + Λúŧǿмáŧïĉǻłŀỳ ¢òрý śéľ℮¢ŧίоⁿ тò çłĭрвόãŗđ !!! !!! !!! !!! Header for a control to toggle whether selected text should be copied to the clipboard automatically, or not. - Αμťõмăтΐćªļŀў δĕţéčţ ŨŔŁŝ åŋď мªкĕ ťђèm čłïçĸâъļę !!! !!! !!! !!! !!! + Дυтøмãτíсăľľÿ ðëŧé¢τ ŬҐ£ş äπð мâќě ţђєм ¢ľî¢ќάъľз !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should automatically detect URLs and make them clickable, or not. - Гęmøνё тѓāΐļїŋġ ẅħĩт℮-ŝφǻćε ïй я℮ςŧдńģūľдѓ śеļєčтїоη !!! !!! !!! !!! !!! + Ѓėмбνē ťřǻįłіñĝ щђϊŧę-śρåčέ îй гēςţдńğúłдґ śēℓеċŧïóл !!! !!! !!! !!! !!! Header for a control to toggle whether a text selected with block selection should be trimmed of white spaces when copied to the clipboard, or not. - Ґēmθνē тřåïĺιйģ ώĥιţè-ѕφā¢э ẁħēń ρãšŧіņğ !!! !!! !!! !!! + Ŕĕмôνē ŧяăïłíňğ щĥΐŧέ-śράćέ ωнēή ραšťĭиĝ !!! !!! !!! !!! Header for a control to toggle whether pasted text should be trimmed of white spaces, or not. - ßέĺőẁ ǻřз ţђз ςμŕґèʼnтĺγ ьôūлđ ķėγś, шніĉн ¢āń ьė мóđїƒϊēď ьỳ ēðίτîŋĝ ťђз ЈŜŐП ѕĕţŧīñĝş ƒĭļě. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Веłбẁ àŕε ŧħε ĉцяѓέŋτĺỳ вбµñđ ќèуѕ, ώħίсħ ¢åл ь℮ мōðϊƒįëď вỳ ĕðιťïņğ τђė ЈŚÓ∏ ŝзтτĭπĝŝ ƒіļĕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer located at the top of the actions page that presents the list of keybindings. - Đēƒåūŀт ргōƒîļε !!! ! + Ðëƒåŭŀţ φяôƒïľе !!! ! Header for a control to select your default profile from the list of profiles you've created. - Рřόƒιŀε τĥǻτ σрέήš ẃĥęņ çℓїĉκìηġ ţħė '+' їćои бґ ьỳ ŧурíńģ τћē ⁿэẁ тǻь κêŷ вíņðіňģ. !!! !!! !!! !!! !!! !!! !!! !!! + Ргøƒíℓė ťнåŧ őрεńš ẃђëñ ċļΐćķíиģ ŧĥë '+' ϊçǿʼn ŏŕ вў ťуріⁿģ тћė ήęш τªь ķĕỳ ъîñδĭňğ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the default profile is and when it's used. - Ďёƒáűŀţ ţėгмîňãł άφφŀìčāťіøŋ !!! !!! !! + Ðєƒàцļт ŧëřmϊήǻľ áρρľíčåτίόή !!! !!! !! Header for a drop down that permits the user to select which installed Terminal application will launch when command line tools like CMD are run from the Windows Explorer run box or start menu or anywhere else that they do not already have a graphical window assigned. - Ŧнέ ţěŕmïⁿªľ āррłįĉατïøй τħäт ľǻúʼnċнєѕ ẅћέⁿ ä сθмmǻπδ-ľіⁿê ªρφĺįċάťιοņ įś яůñ ẁîτĥбûŧ дп ℮×ΐšŧīηğ šєśşϊòπ, ℓĩκє ƒřøм ťнé Ŝтáŗт Мéňυ ŏг Řúⁿ ðíáłøġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Τĥè τěѓmįиàľ ąρφŀіċáŧĩθй ŧĥăţ ĺąűňçћĕŝ щђέή ä ċσммåņδ-ĺíηз аρφľίċăţíøη ĭś řџň ώîτћöùτ ªń эхíŝтíŋĝ şзşѕїоñ, ļįĸé ƒŗθm ŧђę Šťαґτ Мéʼnų ôя Ѓüⁿ đĩåľøĝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - Гéďŕąẁ еŋтĭяё şĉŕέēň ẅħĕñ ďїśφľăу üφδатêŝ !!! !!! !!! !!! + Яėδяǻẁ еиťìŕε şĉŗěēñ ŵђέй ðіšрĺαŷ μρδàτèş !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - Ẁĥέл ďîѕâвĺєđ, ţн℮ ŧёŕmΐňāℓ щîĺℓ ґĕήδέř ǿñļÿ τђê üφδªťєŝ ťō τĥє ѕçяêêŋ вêťŵ℮έⁿ ƒřäмеś. !!! !!! !!! !!! !!! !!! !!! !!! ! + Ẁнэи ðìşăвłëδ, ţĥé ŧĕѓмĩлªŀ ẃίℓŀ яέйđеř οñłу ţћέ υρđªτëѕ ťǿ ţћέ ŝčяěзй ьēťẅэèⁿ ƒѓāmĕş. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". - Çôĺϋмńš !! + Ćоĺΰмπş !! Header for a control to choose the number of columns in the terminal's text grid. - Яōẁѕ ! + Ŗōŵŝ ! Header for a control to choose the number of rows in the terminal's text grid. - Ĩηĩţϊáļ Сòļυмñś !!! ! + Ίńīтĭąļ Ċǿℓųмлѕ !!! ! Name for a control to choose the number of columns in the terminal's text grid. - Ĩⁿϊтĭªł Ґοωѕ !!! + Ίńіţιãĺ Γőшѕ !!! Name for a control to choose the number of rows in the terminal's text grid. - Ж ρθşîŧίōń !!! + Χ φθśĭтìõñ !!! Header for a control to choose the X coordinate of the terminal's starting position. - Ϋ φöśįţìǿń !!! + Ý ρσŝìтĩσή !!! Header for a control to choose the Y coordinate of the terminal's starting position. - Ж ρøśįťīôη !!! + Х ρöšїτϊσñ !!! Name for a control to choose the X coordinate of the terminal's starting position. - Єņтěř ţнэ νåļц℮ ƒōř ŧђэ х-ĉøòгðϊʼnãτє. !!! !!! !!! !! + ∑ņťêŕ тĥē νâľūз ƒöŗ ŧħэ х-çόòѓðΐʼnāťê. !!! !!! !!! !! Tooltip for the control that allows the user to choose the X coordinate of the terminal's starting position. - Х + Χ The string to be displayed when there is no current value in the x-coordinate number box. - ¥ φбѕιτîøй !!! + Ŷ рбŝĭτíøņ !!! Name for a control to choose the Y coordinate of the terminal's starting position. - Ёηťεѓ τђē νάĺūę ƒóѓ τĥĕ ÿ-ćóöŕδîʼnαťе. !!! !!! !!! !! + Éʼnťèř ţћé νǻľůє ƒоŕ τћé ý-çσθяðΐńдťє. !!! !!! !!! !! Tooltip for the control that allows the user to choose the Y coordinate of the terminal's starting position. - Υ + Ύ The string to be displayed when there is no current value in the y-coordinate number box. - Ĺεт Ẃїиďøшѕ đėςιðе !!! !! + Ļèť Щīπδõŵŝ đěčϊđέ !!! !! A checkbox for the "launch position" setting. Toggling this control sets the launch position to whatever the Windows operating system decides. - Ĭƒ êиǻъĺєđ, üŝё ťђê šÿśтєм δēƒãυℓŧ łǻũпčн ρбŝιтīŏи. !!! !!! !!! !!! !!! + Ίƒ ĕʼnªъļěď, μšз ťнё šÿŝţęm đęƒǻůĺт ľáüňĉн рόšΐťīǿʼn. !!! !!! !!! !!! !!! A description for what the "default launch position" checkbox does. Presented near "Globals_DefaultLaunchPositionCheckbox". - Ŵнєņ Ţęѓмîňаľ śτąґτś !!! !!! + Ŵнęй Тěѓмĩпǻļ śтάґţş !!! !!! Header for a control to select how the terminal should load its first window. - Ẅћдт šђóύŀð ъέ ŝнøшņ ẁћεņ ŧђê ƒίяŝť τĕѓміηάĺ íѕ сѓëαŧзđ. !!! !!! !!! !!! !!! ! + Ẅħªť ѕĥōцŀδ ъë šнσẁņ ẅĥεη ŧħέ ƒїŕŝť тэŗміηāł ϊѕ ćґéаŧ℮ð. !!! !!! !!! !!! !!! ! - Ŏρéń ª тав ώīтђ ŧħέ δęƒаúŀŧ рѓōƒΐℓэ !!! !!! !!! ! + Ŏφέπ â тāь ŵĩţн ŧђê ðέƒªύļţ φŗбƒîℓз !!! !!! !!! ! An option to choose from for the "First window preference" setting. Open the default profile. - Őρęⁿ ẃîñðоẁŝ ƒгőm а φґĕνϊоџş šéšşįõή !!! !!! !!! ! + Őρёņ ẁĭŋđбẁś ƒřоm å φřэνįőüş ś℮ŝśĭōⁿ !!! !!! !!! ! An option to choose from for the "First window preference" setting. Reopen the layouts from the last session. - ₤ªûŋсн мσðε !!! + Ŀąųηčħ моďē !!! Header for a control to select what mode to launch the terminal in. - Ησω ŧнє тєґmїņąļ ẁιℓľ άφρεăŗ øņ ŀąũπčђ. ₣σ¢úѕ ẅīļℓ ћіðε ťħэ ťàьś аŋð ţíтļє вäя. !!! !!! !!! !!! !!! !!! !!! !!! + Ήοŵ тĥз ťзґмιлαľ ẅīĺŀ άφрēăř őи łãűйċн. ₣ǿ¢μŝ ẁїℓℓ ĥϊđέ ŧћē ŧâвś ªиď ţìτļé вªя. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ĺāцńςĥ ρагªмĕτєяş !!! !! + Ŀáüη¢ħ φâяǻмĕτέгš !!! !! Header for a set of settings that determine how terminal launches. These settings include the launch mode, launch position and whether the terminal should center itself on launch. - Śēτťïʼnġś ťнаţ соńţѓöŀ нόẁ ŧћє ţёŕmίńäŀ ľâűη¢ħęš !!! !!! !!! !!! !! + Śēŧτïŋğş τндт ċōʼnτгόĺ нøẅ τђě ţєяmϊńąļ ℓâύņćĥёš !!! !!! !!! !!! !! A description for what the "launch parameters" expander contains. Presented near "Globals_LaunchParameters.Header". - Łäůⁿĉĥ mŏďє !!! + Łаųпçћ мθðę !!! Header for a control to select what mode to launch the terminal in. - Ħõẅ тħ℮ тέŕmīņąℓ щīľŀ àρρèàґ бп ℓāũиčћ. ₣оçúѕ щїłł нîďě ţћε τªъş äñð ťíťℓэ ъäř. !!! !!! !!! !!! !!! !!! !!! !!! + Ĥŏώ ŧħě ŧëŗmΐпаł ώίłļ дφρεäґ őñ ļǻũπсн. ₣õċúš ώΐļĺ ĥїđ℮ ŧĥє тąъѕ айδ ŧíτℓē ьαř. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ðěƒάцŀŧ !! + Đēƒãΰļţ !! An option to choose from for the "launch mode" setting. Default option. - ₣ϋłľ š¢гєêʼn !!! + ₣ūļł šĉґё℮и !!! An option to choose from for the "launch mode" setting. Opens the app in a full screen state. - Мäжïmíźэđ !!! + Мäжιmìżзď !!! An option to choose from for the "launch mode" setting. Opens the app maximized (covers the whole screen). - ∏èш ϊñšťáлсє ьзђâνìǿř !!! !!! + Ńéώ īňšťâиćė вĕћανįőŗ !!! !!! Header for a control to select how new app instances are handled. - Ćôήťŗóļş нòẃ πęẃ ťёяmìňąľ ΐņšťåňĉέš áťťάĉн τø ехĩѕţīήğ щίⁿδõŵś. !!! !!! !!! !!! !!! !!! + Ćőňťгбļš ĥõώ ⁿзш ťèŕmìηάŀ îņŝŧαńсзѕ дŧťάсн το ėхΐśţιйğ ẁìńδσщś. !!! !!! !!! !!! !!! !!! A description for what the "windowing behavior" setting does. Presented near "Globals_WindowingBehavior.Header". - Ĉґëάťє ą ńéŵ ωίйðòẃ !!! !!! + Ċѓėªтē á ņëω щīňđσщ !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances create a new window. - Äτтäçħ τо ťнè мóśτ ѓêçèиťłŷ ųśèð ẁΐňďöщ !!! !!! !!! !!! + Ǻťτą¢ћ ŧô ţħè mοśţ ŕęĉзⁿťľỳ ũŝєδ ẅĭⁿďбŵ !!! !!! !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window. - Δтţåčђ ţб ŧнз мǿşт řέĉэитŀў ŭŝĕð ŵΐʼnđóẁ õп ţнîş ðэśкťøр !!! !!! !!! !!! !!! ! + Ăţŧαςђ τó ťĥе mòśţ ŕęςēńŧļÿ űşėď ẅįņδθŵ òл ţћîş ďëśķťôφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - Ţнēşę ŝėţτíπġś mαу ъε цšèƒúł ƒøŕ тŕǿцьĺęśћоőťĩňġ ąⁿ іšśùĕ, ђôωėνёѓ ťнёý ωįĺł ïmрάçť ÿōüѓ φěřƒбřmдήçё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ţћёšè ŝéťτîʼnģŝ mąỳ ьε ũѕêƒцℓ ƒõя τяöúъľėŝћõότįʼnġ äи ĩѕšµ℮, нοшèνзŕ тђėỳ ẃĩℓľ ìмρдćţ ўǿџг рęґƒοґmåñ¢ё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. - Ηΐďё τħē тïţļě ъªг (ŕēqμíгёš гęļăůηсћ) !!! !!! !!! !! + Ηіδě ţћĕ ťіτŀє вªř (ŗ℮qϋϊгёş řέľåџⁿсħ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. - Шĥэи ďìŝªьℓзđ, тнэ тíтĺє вªя ώìℓĺ àρφєǻŕ ăъóνє ŧћз ťäвš. !!! !!! !!! !!! !!! ! + Щħзń ďîѕāвļēđ, ţĥĕ τιтļę ьąŗ ωΐļľ ãррєàѓ āьõνę τĥě ťąьş. !!! !!! !!! !!! !!! ! A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Úšę āćяўľΐč mǻτèґіàŀ įл ťнę ταь řōш !!! !!! !!! ! + Ûŝē âςгýℓĩс мáτεŗιäŀ ĭή ţђε τàъ ŗôω !!! !!! !!! ! Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Цšе âĉтїνε ţέŗmíŋăľ тϊтℓё ąš дρρľϊčαţîòи τίŧĺê !!! !!! !!! !!! ! + Цŝé ǻċτіνè ţзґmïňǻĺ ťіŧľé αś ªρφłιçâŧΐόп ŧίŧĺє !!! !!! !!! !!! ! Header for a control to toggle whether the terminal's title is shown as the application title, or not. - Ẃĥěπ δìšāьĺėđ, ŧнę ţîтļє ьåř ẅĩľĺ вè 'Ţзřмίйαļ'. !!! !!! !!! !!! !! + Щнĕπ đіšǻъℓéď, ŧћє тĭтłê ьªѓ ẅїľł ьэ 'Ťéѓmìйаł'. !!! !!! !!! !!! !! A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"} - Ŝŋар ŵΐņðøẁ ґέšίžîпģ ťô ċĥăřąĉŧêя ġřīđ !!! !!! !!! !! + Ŝńâρ щіňðоŵ ŗзşìžіŋğ ťб ĉнáŕåčτєѓ ğяįδ !!! !!! !!! !! Header for a control to toggle whether the terminal snaps the window to the character grid when resizing, or not. - Ẅђэπ ðïşãъĺєď, τĥě ωіήδǿẁ ẁîļℓ ŕεşįžε ѕmбθŧђłÿ. !!! !!! !!! !!! !! + Ŵĥėή đΐŝάъĺēď, ŧнë шϊⁿđǿẅ ώїľĺ ŗзśΐżę ѕмòöтħļŷ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - Ŭš℮ ѕóƒτẅãřэ ѓēйđэŗіņğ !!! !!! + Ũśë şōƒŧẃáяę гéņđзгîŋğ !!! !!! Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - Ŵћзʼn эπªвłēδ, ťĥë ťэгмìηãĺ ώίŀļ ūşε ţћё śøƒтẅªге ґěⁿðэґεŕ (а.κ.á. ШΔŖΡ) ΐпŝťєªď őƒ ŧĥë ћªřðẁářє ǿʼnĕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Щĥéň ęņăъŀēð, ţнз ťєґмĭηăļ ẃιŀŀ ūśэ ţнє şбƒťẅåŕё řєⁿδëяęŗ (ã.ќ.á. Ẅ∆ŘР) îⁿŝť℮äď ǿƒ τнε нàŕδώάґē ǿиз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Ŀаųпčĥ ǿπ мªćħίńє šτãřŧŭр !!! !!! ! + Ľàûлсĥ бη mªçћìńë ŝťαřтùр !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. - Āџţőmäţīĉãĺℓў ľâυňĉђ Ţëŗmіňāℓ ωћěʼn ŷθύ ľοğ îη ŧö Windows. !!! !!! !!! !!! !!! !! + Δџţõmаťì¢дĺľу ĺǻûņсн Ťęѓmĭиâĺ щħęŋ ÿоυ ľôĝ įŋ ţø Windows. !!! !!! !!! !!! !!! !! A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header". {Locked="Windows"} - Čεñţεřėδ !! + Çэпŧεґéđ !! Shorthand, explanatory text displayed when the user has enabled the feature indicated by "Globals_CenterOnLaunch.Text". - €ěητēґ õη ļάџπςћ !!! ! + Ćéлŧ℮ř õʼn ŀáμʼnčћ !!! ! Header for a control to toggle whether the app should launch in the center of the screen, or not. - Ẅнєи ℮ŋąьŀзď, тнέ шιлđοш ẃìļŀ ьë φĺàĉέð ĩŋ τнē ċéήţέř οƒ ŧĥε śċѓė℮ń ẅђêл ľąΰńćĥĕď. !!! !!! !!! !!! !!! !!! !!! !!! + Ẃĥзń ēňāвĺзď, тнε ẅΐπđбẅ ωĩłł ъĕ φľāĉэď ιп ťħε ¢ėпţéř öƒ тђε ščгĕ℮ņ шħĕй łаΰйċћзð. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "center on launch" setting does. Presented near "Globals_CenterOnLaunch.Header". - Ǻłẃдŷş őй ťõφ !!! + Äĺώąŷŝ õń тóρ !!! Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled). - Τĕŗmιŋăł ωïłℓ аļŵáγš ьë τħè тøрmóśт ẁĩņδōẅ õή тћє ďéśќτбρ. !!! !!! !!! !!! !!! !! + Ŧёřmĭήαŀ ẁīℓℓ аℓшäγѕ ъę ţħэ τǿφmöşţ ωϊπðθώ όή ţћє ðëѕĸťōр. !!! !!! !!! !!! !!! !! A description for what the "always on top" setting does. Presented near "Globals_AlwaysOnTop.Header". - Ťåв ώιðťн møδз !!! ! + Тªъ ẅìđţĥ мöđê !!! ! Header for a control to choose how wide the tabs are. - Сōмρāćť ŵїļℓ šђгĭπķ їиąсŧіνє ŧąьŝ тο тћé šĩżё οƒ ŧђё îċóπ. !!! !!! !!! !!! !!! !! + Ćőmρáćŧ ẅìĺŀ śнřιиķ ĩπąςŧīνę ťǻъš το ŧђè ŝïžе ŏƒ τнё ϊĉση. !!! !!! !!! !!! !!! !! A description for what the "tab width mode" setting does. Presented near "Globals_TabWidthMode.Header". 'Compact' must match the value for <Globals_TabWidthModeCompact.Content>. - Сοmφäćţ !! + Čбmрåсţ !! An option to choose from for the "tab width mode" setting. When selected, unselected tabs collapse to show only their icon. The selected tab adjusts to display the content within the tab. - Éqüąļ ! + Ĕqύáł ! An option to choose from for the "tab width mode" setting. When selected, each tab has the same width. - Τìτŀё ℓέиğтђ !!! + Ţΐŧłé ļέлĝŧђ !!! An option to choose from for the "tab width mode" setting. When selected, each tab adjusts its width to the content within the tab. - Áрρļįçдτιòņ Ţђémε !!! !! + Ăφρℓĭςäτιбň Τĥєм℮ !!! !! Header for a control to choose the theme colors used in the app. - Ðăѓќ ! + Ðăгк ! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. - Ůѕė Ŵίňδôẃş ŧђεмě !!! !! + Ŭśê Ẁįπδŏшś ťнèмě !!! !! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. - Ĺїĝћт ! + £ĭġнţ ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. - Ðāřķ (₤эģăćŷ) !!! + Đàяќ (Ļëġдćŷ) !!! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. This is an older version of the "dark" theme - Ũşё Ẅĩπďöẁš ŧђзmέ (₤ęģăçỳ) !!! !!! ! + Ũѕė Ẃĭπδθώś тђēмё (Ĺеğãčŷ) !!! !!! ! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. This is an older version of the "Use Windows theme" theme - £іġђţ (Łëĝăсÿ) !!! ! + Ŀíġђŧ (Ŀĕĝãсу) !!! ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. This is an older version of the "light" theme - Ẃőŗð ðёŀíмίťзяŝ !!! ! + Ŵóґđ ðĕļímίťėŕš !!! ! Header for a control to determine what delimiters to use to identify separate words during word selection. - Τђεśέ šýmьõŀś ŵîłļ вê џŝèď ẃнєņ ŷŏũ ðоμвŀě-сŀí¢ќ ţěжţ ĭñ τħė ŧěŗмîⁿâĺ ôř αĉŧїνåτэ "Мąřк мóδé". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Тђéśě ŝўмвǿŀѕ ẅΐłŀ ъэ ûś℮ð ẃĥêπ уôů δøυьℓε-čļíсκ ťĕ×т ìη ŧħė ŧзřmìйǻĺ θř áćŧïνāтэ "Μäŕĸ мôđє". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "word delimiters" setting does. Presented near "Globals_WordDelimiters.Header". "Mark" is used in the sense of "choosing something to interact with." - Ąρрèаřªисε !!! + Ąφрêąřǻʼnς℮ !!! Header for the "appearance" menu item. This navigates to a page that lets you see and modify settings related to the app's appearance. - Ċθℓõř ѕĉђëmεѕ !!! + Çŏŀоř şćĥеmеś !!! Header for the "color schemes" menu item. This navigates to a page that lets you see and modify schemes of colors that can be used by the terminal. - Ĭńτзŗǻċтįóñ !!! + Іπţέядćţīóń !!! Header for the "interaction" menu item. This navigates to a page that lets you see and modify settings related to the user's mouse and touch interactions with the app. - Śτǻřţŭр !! + Ŝŧǻŗτūφ !! Header for the "startup" menu item. This navigates to a page that lets you see and modify settings related to the app's launch experience (i.e. screen position, mode, etc.) - Ǿρêŋ ЈŠÓŇ ƒïļé !!! ! + Θрėπ ĴŜŌŅ ƒįĺę !!! ! Header for a menu item. This opens the JSON file that is used to log the app's settings. - Đέƒаµļťš !! + Ďėƒåúŀтś !! Header for the "defaults" menu item. This navigates to a page that lets you see and modify settings that affect profiles. This is the lowest layer of profile settings that all other profile settings are based on. If a profile doesn't define a setting, this page is responsible for figuring out what that setting is supposed to be. - Ѓĕηδéяΐʼnğ !!! + Γёηđěŕїπĝ !!! Header for the "rendering" menu item. This navigates to a page that lets you see and modify settings related to the app's rendering of text in the terminal. - Άćťĭöñś !! + Ãćŧϊŏńš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. - Вăčќģřøũńď ôρǻсιţỳ !!! !! + Ъāςќĝŗŏϋиδ οφáçįτỳ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Вáçќğгőμⁿδ орªčιŧÿ !!! !! + ßǻςĸġřōŭñð õφăčìτÿ !!! !! Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Şěŧş ţћз öφǻçĭţў θƒ ŧĥê ẃïлδôẅ. !!! !!! !!! + Ѕеŧś тħë όρăĉιťý όƒ τнё ẁιиđοш. !!! !!! !!! A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header". - Άðνäńсέδ !! + Ãðνăήćěď !! Header for a sub-page of profile settings focused on more advanced scenarios. - AltGr åľîąŝīπĝ !!! ! + AltGr дľіâѕīпġ !!! ! Header for a control to toggle whether the app treats ctrl+alt as the AltGr (also known as the Alt Graph) modifier key found on keyboards. {Locked="AltGr"} - Ъу ðēƒáûłτ, Windows ŧѓěάţŝ Čťяľ+Άľт ǻš ǻη ǻļϊāś ƒόѓ ǺļτĢѓ. Ŧђϊѕ śêţţĩлġ ώіℓł θνĕŕѓіďè Windows' ðëƒдύĺŧ ъзнаνιοŗ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ву ďэƒάμŀŧ, Windows τŗēдŧѕ Çтґł+Ãℓţ áŝ аⁿ âłіåś ƒōŕ ÂĺţĠґ. Ŧħϊś ѕêťŧĭňġ ωїłĺ όνєŕŕĭđē Windows' đêƒдцℓť ьзħªνΐõŗ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header". {Locked="Windows"} - Ŧєжť äñťïãłΐǻѕΐņğ !!! !! + Ŧêжτ άñţîãľïäšïйġ !!! !! Name for a control to select the graphical anti-aliasing format of text. - Τэжτ åňţïªℓîąѕΐʼnĝ !!! !! + Ťзжţ αиţīăļíαšіπĝ !!! !! Header for a control to select the graphical anti-aliasing format of text. - Ĉοņťřŏĺś ђбŵ ťєхτ ìŝ âлţϊаłїąşėđ іń ŧħ℮ гėлðęřēг. !!! !!! !!! !!! !!! + Ċôптяσłŝ нòώ ŧεхŧ ĩš аʼnťìáľĭдŝēđ іп ťнé ŕзņδêŕέŗ. !!! !!! !!! !!! !!! A description for what the "antialiasing mode" setting does. Presented near "Profile_AntialiasingMode.Header". - Дℓīǻšėđ !! + Αľіǻѕёð !! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer does not use antialiasing techniques. @@ -647,259 +647,259 @@ An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a "ClearType" antialiasing technique. {Locked="ClearType"} - Ĝŗåγѕčäľё !!! + Ġгдуś¢áļ℮ !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a grayscale antialiasing technique. - Λρρéąŗáήčė !!! + Арφęáяãņςė !!! Header for a sub-page of profile settings focused on customizing the appearance of the profile. - Бàċќģгθũňδ īmǻĝє ρąťĥ !!! !!! + Вăсκġгõûπđ įмãġë рªťħ !!! !!! Name for a control to determine the image presented on the background of the app. - Ъàĉќğяоμлð ϊмдĝέ ρåŧħ !!! !!! + βªςкģŕóµйδ ïmàģē φåтħ !!! !!! Name for a control to determine the image presented on the background of the app. - Ъąсκġяøυŋď ïmăģě рâŧĥ !!! !!! + ßâсќģѓøûņđ îmäģė ρåŧђ !!! !!! Header for a control to determine the image presented on the background of the app. - ₣īℓэ łó¢ãŧìôń øƒ ťћė імаğé ϋšέδ ìл τнé вдćĸģѓσµпđ ôƒ τћє ẅîⁿďóω. !!! !!! !!! !!! !!! !!! ! + ₣īŀё ĺôçāťїòπ оƒ τĥé įmáĝě ύşěδ ιʼn ťћē ъąċќģґŏυňđ óƒ ťћê шĩņδöẅ. !!! !!! !!! !!! !!! !!! ! A description for what the "background image path" setting does. Presented near "Profile_BackgroundImage". - Вăčķġŕōцйδ įmдģε ªŀīĝņмėпť !!! !!! ! + βåсκġŗõûήď ïмąĝё ãĺϊğńмėήт !!! !!! ! Name for a control to choose the visual alignment of the image presented on the background of the app. - ßàçкģгθŭпδ ϊmăğē ăļîĝňм℮ņт !!! !!! ! + Ъăсĸġяôũπď įmąġė āļĭĝπмēņŧ !!! !!! ! Header for a control to choose the visual alignment of the image presented on the background of the app. - Ŝ℮ŧš ħōώ ţħė ъăскģřθúņδ ímåğê āľιĝηѕ ŧŏ ťнé вòùñđăŗîěѕ σƒ ťђє шîņđòш. !!! !!! !!! !!! !!! !!! !!! + Ŝêťš нσŵ тђ℮ ъªĉќġŕóμⁿď ίмąğė αľīĝйś ťθ ťђė вõϋňðąŗϊеŝ óƒ τĥē ẅìпδǿш. !!! !!! !!! !!! !!! !!! !!! A description for what the "background image alignment" setting does. Presented near "Profile_BackgroundImageAlignment". - ßоттσm ! + Ьοτťόм ! This is the formal name for a visual alignment. - Ьόťţŏm łéƒţ !!! + Ьоŧţôм ļèƒţ !!! This is the formal name for a visual alignment. - Ъøŧţöм яіģħť !!! + Вóтťǿм ŕϊĝнţ !!! This is the formal name for a visual alignment. - Сēⁿτеѓ ! + Сêⁿŧĕŗ ! This is the formal name for a visual alignment. - Ŀéƒť ! + ₤ēƒţ ! This is the formal name for a visual alignment. - Гΐġħŧ ! + Γįģнţ ! This is the formal name for a visual alignment. - Ţοφ + Ţõр This is the formal name for a visual alignment. - Ŧŏр ℓėƒť !! + Ŧŏφ ľєƒť !! This is the formal name for a visual alignment. - Ţбφ řĩĝĥť !!! + Тŏρ ŕįģĥţ !!! This is the formal name for a visual alignment. - Ьřŏŵŝє... !!! + Ьřοώşє... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Αðð ηєẁ !! + Δďđ лéẅ !! Button label that adds a new font axis for the current font. - Åδđ ʼnеẃ !! + Άðδ ʼnеẃ !! Button label that adds a new font feature for the current font. - Ъåςκģгоŭήđ įмдĝ℮ όрå¢íťŷ !!! !!! ! + Ьǻçќğřόũпđ ïmàĝė ōφåĉіτÿ !!! !!! ! Name for a control to choose the opacity of the image presented on the background of the app. - Ьäсĸġґòûпď ιmåġė öφăćΐťý !!! !!! ! + Ъªĉĸġѓσџńδ ĩмâğэ ǿρãċĩŧý !!! !!! ! Header for a control to choose the opacity of the image presented on the background of the app. - Şēŧş тћë öрăċîτў οƒ ťĥė вâçķğгõūήð ĭмãĝε. !!! !!! !!! !!! + Śęţś ŧħ℮ ŏραçίţý ŏƒ τĥε ьªĉќğгòŭпδ îmãġě. !!! !!! !!! !!! A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". - Ьăсķģяòùηð їmаģе ѕťŗέţςħ móδе !!! !!! !!! + Ьдçкģяôúπđ įмãģė ŝτяеţ¢ћ мόðέ !!! !!! !!! Name for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - ßǻ¢ĸģřôùʼnð ιmâģĕ śτřεŧćн мŏðέ !!! !!! !!! + Ьāċĸġгθűⁿđ ΐмáĝē šţяєťčн мŏďє !!! !!! !!! Header for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Ѕèτş ħόώ ťĥĕ вâćκĝгбųйð ìмάģε ïş ŗėѕїżëð ŧó ƒīłℓ тĥє ẅíήđòш. !!! !!! !!! !!! !!! !!! + Ѕέŧś нŏщ ŧћε ьáςкġгοцπδ ïmаĝе íš ř℮ŝįźєδ ťø ƒíĺŀ ťћέ щīŋďóщ. !!! !!! !!! !!! !!! !!! A description for what the "background image stretch mode" setting does. Presented near "Profile_BackgroundImageStretchMode". - ₣ίļľ ! + ₣ΐĺĺ ! An option to choose from for the "background image stretch mode" setting. When selected, the image is resized to fill the destination dimensions. The aspect ratio is not preserved. - ∏őиĕ ! + Лőле ! An option to choose from for the "background image stretch mode" setting. When selected, the image preserves its original size. - Цʼníƒòгm !! + Ũήĩƒοѓm !! An option to choose from for the "background image stretch mode" setting. When selected,the image is resized to fit in the destination dimensions while it preserves its native aspect ratio. - Ūήΐƒòřм ŧǿ ƒíĺℓ !!! ! + Ùπīƒŏřм ŧǿ ƒīŀℓ !!! ! An option to choose from for the "background image stretch mode" setting. When selected, the content is resized to fill the destination dimensions while it preserves its native aspect ratio. But if the aspect ratio of the destination differs, the image is clipped to fit in the space (to fill the space) - Рґôƒĭłз ţеŗmίηαťįσň ъêħăνĭŏŕ !!! !!! !! + Ρґόƒіľз ťęŕmïиąτίόŋ ъêĥăνįöѓ !!! !!! !! Name for a control to select the behavior of a terminal session (a profile) when it closes. - Ρřòƒĩℓέ ţέřмĭňâтĭόп ъέнáνìõř !!! !!! !! + Ρřŏƒîľэ ťęґмιʼnãτíοπ взнąνіóř !!! !!! !! Header for a control to select the behavior of a terminal session (a profile) when it closes. - Ćℓοśè ẁĥęп рřøсėśŝ ē×їţś, ƒâїłŝ, όř ċřάśĥзѕ !!! !!! !!! !!! + Čľоѕе ωħëй ρгôςеŝš ë×їţś, ƒåìℓѕ, õя çѓâѕĥëѕ !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled (exit) or uncontrolled (fail or crash) scenario. - Čŀøŝэ øпℓу шнěπ ρŗθ¢êśŝ ęхϊťѕ šüς¢ëšśƒΰľłу !!! !!! !!! !!! + Ċľόšě øńļý ώнěń ρґō¢зśš ëжітś śμ¢ĉėśšƒµłľγ !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully. - Лэνёг ćľōѕĕ åūŧόмáŧï¢αĺŀу !!! !!! ! + Ñėνέř ĉłŏśέ άűţõмáťĭċąℓℓγ !!! !!! ! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal. - Áūтόmãŧϊ¢ !!! + Āųτσmåтίς !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal. - Ĉбľôŗ ѕċħĕмэ !!! + Ćθℓοř ŝςнémз !!! Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user. - Čоммāйð ľīπэ !!! + Ċôмmáήδ ľįйё !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - €ôммäñđ ℓíлε !!! + Čóмmдńđ ŀìпэ !!! Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Ĉòmmǻñδ łιηè !!! + Сömmàʼnð ŀΐηέ !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Εхĕсüťäвļĕ υŝéð îή ťћє ρгоƒĭŀë. !!! !!! !!! + È×ë¢ūŧąьľé ϋşēδ ιи ťħ℮ ρřőƒĩļè. !!! !!! !!! A description for what the "command line" setting does. Presented near "Profile_Commandline". - ßяòώѕе... !!! + Бřбώšê... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ĉύѓѕŏѓ ћĕίģђť !!! + Ƶŕѕóř ħëїğђт !!! Header for a control to determine the height of the text cursor. - Ѕеťś ŧнё φ℮ŗçєптãĝé ђëїġĥт öƒ ŧђε ςůґśǿґ ѕтаřтįńġ ƒŗσм ťнĕ ъõττóм. Θņĺў ŵőŕĸŝ шîтћ ţнé νîňťªĝё čυŗŝøŗ ŝĥåрé. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Śётŝ ŧђе φêŕćęńтάğε ħεîĝћť õƒ тђė ¢ΰѓšσґ ŝŧªѓтîńġ ƒŗом ţĥê ъοττόм. Őπłγ шθřĸş шĩτђ τħē νīήŧąġë ĉûгѕöŕ śђáрè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "cursor height" setting does. Presented near "Profile_CursorHeight". - Ćûřŝоř şħåрę !!! + Сùřšŏř ŝħāрę !!! Name for a control to select the shape of the text cursor. - €υѓšοŗ şђαρę !!! + Сûѓšόя ŝħâφє !!! Header for a control to select the shape of the text cursor. - ∏ĕνєŗ ! + Ņĕνеŗ ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will never adjust the text colors. - Оиĺŷ ƒôѓ ςõļόřѕ ϊń τĥë сőℓōѓ śčнзmё !!! !!! !!! ! + Òлľγ ƒǿř сôℓôřś îп ţнэ ćοļōя şčђёmè !!! !!! !!! ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table. - Äŀшªỳѕ ! + Λĺщαÿŝ ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. - βäя ( ┃ ) !!! + Βдŗ ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. - Емφŧў ьŏ× ( ▯ ) !!! ! + Ĕмφτў ъö× ( ▯ ) !!! ! {Locked="▯"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an empty box. The character in the parentheses is used to show what it looks like. - ₣íℓŀзð вõ× ( █ ) !!! ! + ₣ìłŀёď ъòж ( █ ) !!! ! {Locked="█"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a filled box. The character in the parentheses is used to show what it looks like. - Ũπðеŗŝçôřε ( ▁ ) !!! ! + Ůʼnδєŗśςǿřз ( ▁ ) !!! ! {Locked="▁"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an underscore. The character in the parentheses is used to show what it looks like. - Vïптªğę ( ▃ ) !!! + Vįйţαĝë ( ▃ ) !!! {Locked="▃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a thick underscore. This is the vintage and classic look of a cursor for terminals. The character in the parentheses is used to show what it looks like. - Ďőµьļё üņđęгş¢øяĕ ( ‗ ) !!! !!! + Ďσūьŀе ΰпď℮ґŝĉôґε ( ‗ ) !!! !!! {Locked="‗"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a stacked set of two underscores. The character in the parentheses is used to show what it looks like. - ₣őпт ƒάĉě !!! + ₣öņť ƒä¢℮ !!! Header for a control to select the font for text in the app. - ₣ōņτ ƒǻçé !!! + ₣ǿπт ƒâ¢ë !!! Name for a control to select the font for text in the app. - ₣õпτ śіžе !!! + ₣ōпτ ѕιźэ !!! Header for a control to determine the size of the text in the app. - ₣ōиτ şιźε !!! + ₣őπŧ şίżê !!! Name for a control to determine the size of the text in the app. - Ŝїżė όƒ ţђé ƒŏŋţ ĭή рöϊňтś. !!! !!! !! + Ŝΐżє õƒ ţĥε ƒòňť іń φоĭʼnţš. !!! !!! !! A description for what the "font size" setting does. Presented near "Profile_FontSize". - Łïñέ ћёїġћţ !!! + £ΐйэ ћêίģћť !!! Header for a control that sets the text line height. - Ļĩйέ ħêϊğħţ !!! + Ŀĭⁿε ћ℮ïģћť !!! Header for a control that sets the text line height. - Ǿνέřŕίđз ťне ℓïńė ћеіĝћţ ôƒ ŧĥέ тэŗмįπåł. Мĕąşůŕèδ āś å mùľţίρℓė όƒ ťĥê ƒοņτ ѕĭźē. Ŧнé ðèƒãџŀţ νãℓϋę ďєρēńðѕ όⁿ ÿŏūř ƒöⁿт àлδ įş ùşŭąľĺỳ ąгõůйð 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ōνéґѓιďэ ťћэ ℓīпê ђēіğћŧ óƒ ţħĕ ţëгmįηáł. Μèаşŭŕ℮đ ǻś ä мùℓŧіρľě θƒ ťĥэ ƒõńţ śΐźě. Τћ℮ ðèƒäŭℓт νаłůë ðєрейδŝ оⁿ убúŗ ƒόπτ дŋð īś üšύªĺℓу αŗõциď 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "line height" setting does. Presented near "Profile_LineHeight". @@ -907,846 +907,854 @@ "1.2" is a decimal number. - Бûïľťіʼn Ģłγрнş !!! ! + βüĩℓŧīŋ Ġļýрĥś !!! ! The main label of a toggle. When enabled, certain characters (glyphs) are replaced with better looking ones. - Шђел єŋавłэδ, тнέ тεяmїʼnàľ ðгǻẅś čųŝţόm ģłỳрнŝ ƒõŗ вļōĉк ëℓēmēʼnτ åñδ ъőж δŗāшїйģ сћáґаςťεřś іпŝť℮аď θƒ цşĩŋģ тĥę ƒбņт. Ťħιš ƒеăŧūѓè őŋĺў ώόřĸś щĥēл ĞРÛ Αсĉęŀēřåţįöп îѕ åνãίłдъľє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Шнеñ ℮ⁿαвŀêδ, ţђē тėгмïлаľ ðядшš ċūśτóм ĝłγφħŝ ƒõř ъĺŏςκ éĺēmĕήт àňď вбх ďřάшīňĝ снãřăĉτèѓѕ ìñśтеåð οƒ υšΐиĝ тħэ ƒǿńť. Τћіѕ ƒєдтμгэ őиļŷ ẅőґќѕ ẅĥéⁿ ĢΡÙ Ăćĉěĺзяäťïôй ίś ąνаїľªвŀē. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + ₣ŭℓℓ-ćόŀőг Ēмőјĭ !!! ! + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Шђέń зйάьłēď, Ĕмõĵī åŕз ðìśφℓàўέđ ĩʼn ƒųĺℓ ċóℓōŕ. !!! !!! !!! !!! !! + A longer description of the "Profile_EnableColorGlyphs" toggle. + - ₣θņŧ ώëìġнτ !!! + ₣őиτ ẁěìĝнť !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣θņт ωėïġћŧ !!! + ₣ǿňτ шěïġћŧ !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣ōńţ шéιģнŧ !!! + ₣őиţ ẃęїĝнτ !!! Header for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - Šęťś τнэ ẁзïĝĥт (łϊģћŧйëşŝ ôг ĥēäνíⁿέśš ǿƒ ţћέ śτяοкęş) ƒοг ŧћĕ ĝїνèη ƒŏиŧ. !!! !!! !!! !!! !!! !!! !!! ! + Ŝěţš ŧнэ ώęĭĝнţ (ľїĝĥтηěŝŝ öг ђзåνΐⁿεšş оƒ тћє śтѓбĸëş) ƒōґ ţћė ģινėŋ ƒôйŧ. !!! !!! !!! !!! !!! !!! !!! ! A description for what the "font weight" setting does. Presented near "Profile_FontWeight". - Váяίǻъĺé ƒбņť áхęѕ !!! !! + Vářīâъļз ƒōñť âжеš !!! !! Header for a control to allow editing the font axes. - Дđđ öŗ ґěmŏνę ƒőʼnť ά×ěѕ ƒòř тћě ġινеπ ƒόήτ. !!! !!! !!! !!! + Äðð øř ѓємôνё ƒŏπţ ǻхеş ƒőґ тħē ģíνëη ƒòñţ. !!! !!! !!! !!! A description for what the "font axes" setting does. Presented near "Profile_FontAxes". - Ţђέ śєℓеćţєð ƒőйτ ђàѕ ňθ νдґΐάьĺе ƒóʼnτ ǻжёŝ. !!! !!! !!! !!! ! + Ţћê ѕêĺėçţėð ƒōŋť ħąş ⁿб νąŗϊàвľέ ƒσňτ á×ēş. !!! !!! !!! !!! ! A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". - ₣бņт ƒеаťűґëş !!! + ₣ǿńŧ ƒęåτüŗěś !!! Header for a control to allow editing the font features. - Áđď ōř ѓ℮môνę ƒǿит ƒэāţűяėš ƒбř ţĥé ğĭνėη ƒόʼnť. !!! !!! !!! !!! !! + Дδδ őг ŕёмőνе ƒοʼnŧ ƒεāтùґ℮ѕ ƒбг τĥë ġĭνέⁿ ƒσňţ. !!! !!! !!! !!! !! A description for what the "font features" setting does. Presented near "Profile_FontFeatures". - Τĥê ѕëŀěĉт℮δ ƒöиŧ ћàѕ ŋô ƒбήτ ƒеáťμřęŝ. !!! !!! !!! !!! + Тĥε ѕзĺëċтέδ ƒбňť нāś лô ƒòπτ ƒèдτűřëš. !!! !!! !!! !!! A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". - Ĝέňэяάĺ !! + Ģęńёяаℓ !! Header for a sub-page of profile settings focused on more general scenarios. - Ħίđэ φřõƒīŀё ƒřом δгόρđőшл !!! !!! ! + Ηίď℮ φŕøƒϊłê ƒяŏм đгόрďθωή !!! !!! ! Header for a control to toggle whether the profile is shown in a dropdown menu, or not. - Įƒ ėńάъļέð, ťнё ρřòƒĭłě ẁϊļℓ ηŏŧ ąрφèàѓ іή ţħ℮ łĭşţ òƒ φřòƒìłέš. Тĥιş ćάŋ вē úŝĕð ťθ ђìδё ďёƒàΰłτ рŗōƒįļеś áńð ðўηдмîćǻłľỳ ğêлêŕáţεδ φґόƒιłèş, ωћìĺĕ łèáνīлĝ τђém íņ ÿóŭŗ şзŧτíпģѕ ƒіļє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Їƒ ėиªьļěδ, τĥě ρŕôƒίℓę ωìľŀ ņōť αрφέåŕ įй ŧħе ĺĩśŧ ôƒ φřθƒįłėŝ. Ŧнїş ċдņ ьё υşěď ťó ђĭđ℮ đέƒåυℓť ρřŏƒĩĺεŝ аńđ đуπªmίćàłłу ĝэήзřǻτ℮ð φѓōƒĩļęŝ, шђìļě ľ℮άνίйĝ ţнëm ίл γǿŭŕ šέţŧīиğš ƒïĺэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "hidden" setting does. Presented near "Profile_Hidden". - Γµп τĥĩś ргоƒįļé ąş ∆ðmїлìśţяąτόŗ !!! !!! !!! + Ѓύň ŧћίѕ φяóƒіℓε åś Λðмįñĩšŧŗатǿř !!! !!! !!! Header for a control to toggle whether the profile should always open elevated (in an admin window) - Įƒ ĕŋдьĺэď, ŧђэ ρřόƒíľэ ẁίℓļ ōφεη іл áη Άďмĩń τеŗмįπãľ щїňđŏω åúτбмąтΐċäℓļỳ. Ĩƒ ťнε ςúŗяēпţ ωίʼnδōẅ іş дłřзåðў ґϋйŋіпġ ªş âďmιп, ΐт'ℓľ оρèй íй ŧĥіš шιπðŏώ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Įƒ єŋáъľεð, ŧђē φґоƒïŀє ẁĭĺŀ ορèп іⁿ äʼn Αδміл т℮гмìπαł ẁĩņđôщ дüţõмāťïćáŀŀŷ. ݃ ťнę сųŕя℮иŧ ŵïŋδöώ ϊѕ ǻľѓěąðŷ řΰηņîήĝ áś āδmϊń, įτ'ľĺ οрëή īи τђїѕ ώíňδøш. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Ήĭŝŧŏгў şīżê !!! + Ηĭšţǿŕý śіżė !!! Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Нíśтояγ ѕϊżέ !!! + Ĥΐśţоŕŷ śīźę !!! Name for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Τĥė ʼnűмьёř ôƒ ľїηеş дъǿνё ŧђе θň℮ѕ ðїşрłаỳĕð ίп тђε ẁìлđõш ýбű čай ѕ¢ŕόℓł ъαċķ ŧǿ. !!! !!! !!! !!! !!! !!! !!! !!! + Ťн℮ пųmъэґ óƒ ļїйєŝ ãьονε ţће ǿʼnέş δīšφļâÿĕď įη τħ℮ щĩⁿđõŵ уθù ċάй šćгõŀł ъãςĸ ťǿ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "history size" setting does. Presented near "Profile_HistorySize". - Ίςоπ ! + Їčбή ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ĭćσñ ! + Ісбŋ ! Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Įçǿñ ! + Ϊсōπ ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Έmőĵί õř їмăĝε ƒĩłέ ℓó¢ąŧĩòⁿ σƒ ŧĥè їċσⁿ ùŝёď įⁿ ťће φŗǿƒíļε. !!! !!! !!! !!! !!! !!! + Ęmοјį óř ïmáğз ƒíℓё ℓθςâţīόη óƒ тħε іćòŋ ŭѕėð ĩň тĥè ρгõƒїłє. !!! !!! !!! !!! !!! !!! A description for what the "icon" setting does. Presented near "Profile_Icon". - Βŕŏŵŝě... !!! + ßřθẃśė... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ρáďđїņĝ !! + Рåðďìйĝ !! Name for a control to determine the amount of space between text in a terminal and the edge of the window. - Рдðďīлĝ !! + Рåδďìйğ !! Header for a control to determine the amount of space between text in a terminal and the edge of the window. The space can be any combination of the top, bottom, left, and right side of the window. - Śēŧš τĥє φаďđίпġ агóűиđ ţнè τėжŧ ẃìţħϊŋ ţћ℮ щίήðόẃ. !!! !!! !!! !!! !!! + Ѕέτś ţħè раďðίлġ äгōúňð тђé тёжţ ώíтћΐň ŧĥε ώϊʼnδøŵ. !!! !!! !!! !!! !!! A description for what the "padding" setting does. Presented near "Profile_Padding". - Ř℮ťřø τэŗмїпąℓ ęƒƒěςŧś !!! !!! + Γеťяθ ŧēѓmïпàŀ еƒƒėćţѕ !!! !!! Header for a control to toggle classic CRT display effects, which gives the terminal a retro look. - Ŝћòẃ ґëтŕõ-şţỳℓē ťêѓmīʼnάℓ 냃ĕçţѕ šùċћ ǻş ġℓŏщíņģ тėжŧ άņð śçåη łïπέş. !!! !!! !!! !!! !!! !!! !!! + Ѕђοŵ гёťгŏ-ѕťỳłе ťėѓmіηªŀ έƒƒёςŧś ѕũċћ ąş ġĺöщîйġ ţёжŧ äʼnď śçªή ŀίйėś. !!! !!! !!! !!! !!! !!! !!! A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect". "Retro" is a common English prefix that suggests a nostalgic, dated appearance. - Āŭţоmãŧįçáłłŷ àđĵùšт łίģнŧиέšѕ òƒ īйδĩśţíñğΰΐŝħăъļё ţз×τ !!! !!! !!! !!! !!! ! + Āúтǿмαŧі¢ªļłÿ âδјŭşŧ łίĝħţлèşŝ òƒ ìⁿďíšŧĩⁿģûĩśħäьŀє тëжť !!! !!! !!! !!! !!! ! Header for a control to toggle if we should adjust the foreground color's lightness to make it more visible when necessary, based on the background color. - Ăŭτθmαŧιςåłŀŷ ъѓіĝĥťêйŝ øг ďąřκёʼnѕ τё×т τõ mąĸέ ïŧ mõг℮ νĩşϊъŀé. Ενèŋ щћ℮ñ эиąвłëδ, тнίş дđјύśτмєлť ωíłĺ οńℓў øссůѓ ώнěл д čбmъíηăŧίöŋ öƒ ƒσґёģгθûńδ âⁿδ ьā¢кĝґŏųйð ςöĺöѓś ẅσύĺđ гєšůŀτ ĩʼn φóõґ ćбиŧřªŝт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Áΰŧōmατιċåĺłý ъŕīĝħŧĕйѕ öя đаřκεⁿş ţêжт τо мακέ ίť мôŗĕ νįśίъłě. Éνĕи ẃħєⁿ èпдвłêð, ťћįş αδјüşťmέņт шίłļ σʼnľŷ όсċύř щĥ℮й ă çömьíⁿдτίõʼn όƒ ƒôřέġŗǿџπđ àñđ ъªċќĝґõϋπď čбľőřš ωöůℓð řεšùļτ ïń роôѓ ćøñţґдśŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors". - Şсяоľℓьãŕ νïşíвіłíţў !!! !!! + Šćгοŀļьăŕ νïşìьїľιту !!! !!! Name for a control to select the visibility of the scrollbar in a session. - Šçгòĺłьǻř νϊšίьĩļΐťÿ !!! !!! + Śςŗοŀŀьàŗ νіşĩвīľιтŷ !!! !!! Header for a control to select the visibility of the scrollbar in a session. - Ĥιðδёή ! + Ήíðđέņ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is hidden. - Vіѕįвĺė !! + Vĭşįъľέ !! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible but may dynamically shrink. - Åℓшãўѕ ! + Ąℓŵàўѕ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is always visible. - Šćŕǿļļ тö ίńрύτ ẃħэй туφìηğ !!! !!! !! + Ѕçřõĺľ ťô ίñφųţ ωћĕπ тýφίŋĝ !!! !!! !! Header for a control to toggle if keyboard input should automatically scroll to where the input was placed. - Šţāŗţīйĝ ďιѓê¢ţõѓỳ !!! !! + Śтãґτīņğ ðіґэċťõřў !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Śŧаŗтϊπğ δīгěĉŧοřý !!! !! + Şŧªѓťΐňĝ δíяë¢ťòгÿ !!! !! Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Śτąŗτїήğ đîŗёćтθяý !!! !! + Šŧαґŧϊñġ ðϊяē¢ţõřγ !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Тне ðìřéсţòяỳ тĥè рřòƒїŀě şŧаřŧŝ ίń щђéⁿ ĭţ ίś ĺŏăďěδ. !!! !!! !!! !!! !!! ! + Τħ℮ đігέĉтõŗý ţне φяøƒîℓè ѕţаŗťŝ ΐŋ ŵħĕņ ίт ΐѕ ĺοâðεđ. !!! !!! !!! !!! !!! ! A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory". - Βґόŵś℮... !!! + Ьѓθώѕé... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ũšę ρāяèлţ ргǿçέşş đίґэćтθřŷ !!! !!! !! + Ůѕę рăґέňτ ρґõċэśš δĭг℮çτöŗу !!! !!! !! A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process. - Ĩƒ ёπâьŀéđ, ťћĭş рŕòƒїľе ẁìℓļ şφàώл ίή тнë ðįřєςţõгγ ƒгòm ẃћΐсĥ Ŧĕѓmΐπάļ шáš ĺǻциĉћěđ. !!! !!! !!! !!! !!! !!! !!! !!! ! + ݃ éⁿäвℓеδ, ťħїş φřõƒìŀэ ŵϊŀľ šрāщʼn їŋ ŧħє ðïяěçťоѓў ƒгбm шђίςћ Τëřмϊňãŀ ẃãš ℓäџńсĥеđ. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox". - Ήΐðę ΐċση !!! + Ηіďê ĭ¢бń !!! A supplementary setting to the "icon" setting. - Įƒ ĕņâъľēð, τђįš φґσƒіľε ẅìŀĺ ĥãνé πǿ ιčøŋ. !!! !!! !!! !!! + Їƒ éňáъļёð, тĥįѕ φѓōƒϊłė ẃīľļ ĥаνз πò ĭсǿй. !!! !!! !!! !!! A description for what the supplementary "Hide icon" setting does. Presented near "Profile_HideIconCheckbox". - Ŝüφρґęŝş тϊτℓє ċħăπĝëś !!! !!! + Ŝúрρґэśŝ ŧітĺэ ĉħаņġėş !!! !!! Header for a control to toggle changes in the app title. - Įġпõѓэ åφрļΐçáţϊōη ѓëqџēşţš ťô ĉћάʼnğέ ŧħĕ τїтℓё (OSC 2). !!! !!! !!! !!! !!! ! + Ϊġиоѓĕ ǻφφĺĭćǻτΐōņ řéqυëşтş ŧó ςћǻηğё ŧħĕ τíтĺĕ (OSC 2). !!! !!! !!! !!! !!! ! A description for what the "suppress application title" setting does. Presented near "Profile_SuppressApplicationTitle". "OSC 2" is a technical term that is understood in the industry. {Locked="OSC 2"} - Тªь ŧіŧłё !!! + Ťдъ ŧīŧľĕ !!! Name for a control to determine the title of the tab. This is represented using a text box. - Τав тїťłê !!! + Ŧαв τιτľē !!! Header for a control to determine the title of the tab. This is represented using a text box. - Яęрľāčěś тħё рґòƒįŀę ñâмέ ªš ťћз тіŧłē ŧô φåśş ťõ ŧнё şђ℮ĺł ŏή ŝŧâѓťцρ. !!! !!! !!! !!! !!! !!! !!! + Ѓęρľåςěѕ τћэ ρřõƒιℓё πâмĕ âš τнē тĩτĺё тó рǻѕѕ тō ŧĥз ѕнэĺļ őη šтâŗτūφ. !!! !!! !!! !!! !!! !!! !!! A description for what the "tab title" setting does. Presented near "Profile_TabTitle". - Циƒоčûŝеď åρрèåŕâňčë !!! !!! + Ūʼnƒσčůśеð ąφφεªřαисě !!! !!! The header for the section where the unfocused appearance settings can be changed. - Сѓèāτë Ăρρēªřαή¢ê !!! !! + Ċѓēǻŧэ Åррêāґāńĉê !!! !! Button label that adds an unfocused appearance for this profile. - Ďěľ℮ťė ! + Đèłєτз ! Button label that deletes the unfocused appearance for this profile. - Єπåьℓę ª¢ŗγłî¢ мдŧèѓïαℓ !!! !!! + Эπãъℓз āċŕÿļîς мáţĕґĩǻļ !!! !!! Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Ăρρłĩэŝ ą τřаʼnşłΰ¢ēⁿţ ťē×ŧúґė ťô тн℮ ьăĉĸģřόΰήđ ŏƒ ţђė ẁíпďόώ. !!! !!! !!! !!! !!! !!! + Ąррłιêš å τřàʼnşŀμĉёñŧ ťêםŭřе τб τћé ьдčκġřóũňđ ôƒ тħĕ ώíпđοẁ. !!! !!! !!! !!! !!! !!! A description for what the "Profile_UseAcrylic" setting does. - Ûşе ď℮şķтоφ щâľļφдρεŗ !!! !!! + Џŝé ďęşκţōр ẃªĺŀраφëŕ !!! !!! A supplementary setting to the "background image" setting. When enabled, the OS desktop wallpaper is used as the background image. Presented near "Profile_BackgroundImage". - Ŭşέ τћέ ďëŝкţǿρ ẁāľĺрдφěѓ ϊмąģë άş ţће ъãçкġгθцηð іmąğé ƒбř ţħė ŧеřмїиаļ. !!! !!! !!! !!! !!! !!! !!! + Ůѕе τђе ďëšĸτσр ωåŀŀρªφ℮ґ ĩmąğĕ αѕ ŧнэ вǻςќĝґóϋлď ĭмǻğє ƒσř ťħé тëřmïлąľ. !!! !!! !!! !!! !!! !!! !!! A description for what the supplementary "use desktop image" setting does. Presented near "Profile_UseDesktopImage". - Ďίś¢åŕð ċћάⁿģêѕ !!! ! + Ďįşсǻѓď ćнäñğēś !!! ! Text label for a destructive button that discards the changes made to the settings. - Ðϊş¢ãгđ άłℓ ůпŝăνзđ šèŧтίņĝś. !!! !!! !!! + Ďïşčãѓđ áŀļ µŋśāνέδ şěŧτіñģš. !!! !!! !!! A description for what the "discard changes" button does. Presented near "Settings_ResetSettingsButton". - Śανë ! + Ѕāνë ! Text label for the confirmation button that saves the changes made to the settings. - ⚠ Ýǿū ħανë ũŋѕàνєδ çћàʼnġėš. !!! !!! !! + ⚠ Ϋöџ ђаνё цńšªνĕδ çђαηģėš. !!! !!! !! {Locked="⚠"} A disclaimer that appears when the unsaved changes to the settings are present. - Àďδ ą ņеŵ ρгôƒιŀě !!! !! + Ăďδ α πеώ рґőƒΐℓè !!! !! Header for the "add new" menu item. This navigates to the page where users can add a new profile. - Ρŕóƒîℓεš !! + Рŕòƒϊĺёŝ !! Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items. - ₣θсùѕ ! + ₣ŏċūš ! An option to choose from for the "launch mode" setting. Focus mode is a mode designed to help you focus. - Μā×ĩmίżëď ƒοçϋś !!! ! + Мª×ïмîźëδ ƒòċùş !!! ! An option to choose from for the "launch mode" setting. Opens the app maximized and in focus mode. - Мªхîмïżêđ ƒůŀľ śçřзєń !!! !!! + Μα×їмīžēđ ƒūĺļ ś¢гęēñ !!! !!! An option to choose from for the "launch mode" setting. Opens the app maximized and in full screen. - ₣υℓł šсŕëĕņ ƒŏçŭš !!! !! + ₣űŀŀ ŝĉгеєй ƒòċΰѕ !!! !! An option to choose from for the "launch mode" setting. Opens the app in full screen and in focus mode. - Мαхϊмїźĕδ ƒцŀļ ś¢ŕеєη ƒōсüѕ !!! !!! !! + Мă×ίмìžęď ƒцŀℓ śçřεёη ƒθċûš !!! !!! !! An option to choose from for the "launch mode" setting. Opens the app maximized in full screen and in focus mode. - βзℓĺ ήòŧîƒįĉäтîθň šťýłз !!! !!! + βêłĺ йότϊƒįċāŧіσñ ѕţŷļę !!! !!! Name for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Β℮ĺļ ņǿτϊƒĭςăтĩòŋ śтуľе !!! !!! + Бєŀŀ лŏťìƒϊ¢äťîŏп šťўŀê !!! !!! Header for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Ċσʼnτŗŏℓѕ ẃĥάŧ ђãφр℮ňś ẃнėй ŧћэ аррłĭçãťįôⁿ εmīτš а BEL ĉħàŕªςтëŕ. !!! !!! !!! !!! !!! !!! ! + Çŏήτяоĺş щђâт нāρφęñŝ щћéʼn ŧĥë ąφρℓіčǻťіøл эмιţş α BEL ςђåгǻçťęř. !!! !!! !!! !!! !!! !!! ! A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"} - Łăûлċħ тħίś дφρŀîċάтιõπ ẁíŧн ą лёẅ ℮ŋνįяōňmėηт ъĺǿčќ !!! !!! !!! !!! !!! + Ļªůиčђ ŧĥîš аρφŀîçǻтїőл щĩţħ а ňёώ зиνĭяöņмёņť ьŀŏćκ !!! !!! !!! !!! !!! "environment variables" are user-definable values that can affect the way running processes will behave on a computer - Ẃђеŋ ęⁿàвĺёð, ŧће Тëŗmιйªŀ ωîľľ ğĕʼněŗаťε ǻ ňěẃ ěńνίѓσπmęňτ ъŀǿ¢κ ẅħёη ĉřзάŧįηĝ лèẅ ţǻьѕ óř ρдήёŝ ŵίτĥ тħіѕ ряòƒīŀе. Ŵĥèл ďìşάьĺēð, ţħě ťãъ/φапè ώίļļ īпşţεдð ĭлħэřĭť τђē νǻŗϊāвļёŝ ţћĕ Ţ℮ямìńàļ ώäŝ šŧàřťэď ŵĩŧћ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŵђĕⁿ еñáьℓëδ, τħё Τëгмïŋăļ щïŀļ ģēņéѓàţε à иęщ ёŋνìѓõήмεит вŀоçĸ ŵђēи čřèατіņġ ņεẁ τάвś øŕ ρǻπєş ẁΐťћ ŧћїѕ ρŗόƒіľ℮. Ŵħёη đïšäвļĕđ, тħě ťав/φǻйě ŵĩℓℓ ìиşţėāđ įñħэгìť τнë νáŕìãвℓ℮ś τћё Ŧēямїлдℓ ŵаš šťäřťèð ẃΐŧħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - Ēŋдьℓê ĕхρєѓímęηťåŀ νįřτŭãļ ťзгмΐлäℓ φăşšŧћřбμģн !!! !!! !!! !!! !! + ∑лάъłз ě×φєŕіmэηţàĺ νιŕŧũáł т℮ґміŋǻļ φāŝšţнŕōμġĥ !!! !!! !!! !!! !! An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Ăůδіъŀз !! + Λűδĩъℓé !! An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. - Ŋоиє ! + Иöŋë ! An option to choose from for the "bell style" setting. When selected, notifications are silenced. - Ďįѕãьļę рåńê âʼnіmäтіοηś !!! !!! + Ðіşªъĺ℮ ρåʼnĕ ąлīmâţίσⁿś !!! !!! Header for a control to toggle animations on panes. "Enabled" value disables the animations. - Ъℓαсĸ ! + Ьļãçќ ! This is the formal name for a font weight. - Βóĺð ! + Вбℓđ ! This is the formal name for a font weight. - Сűšŧòм ! + Ĉŭŝţõm ! This is an entry that allows the user to select their own font weight value outside of the simplified list of options. - Ē×ţґă-ßŀаćĸ !!! + Ĕ×τřă-Вĺâċќ !!! This is the formal name for a font weight. - Ëжτřд-Ъõľδ !!! + Ёжŧґà-Бοŀð !!! This is the formal name for a font weight. - Зхтѓá-£īġнт !!! + Ěхťгά-Ľїğћŧ !!! This is the formal name for a font weight. - Ŀīğђţ ! + ₤ĩĝĥт ! This is the formal name for a font weight. - Μєđїџм ! + Мэδιυм ! This is the formal name for a font weight. - Ńòѓмâĺ ! + Йбгmâŀ ! This is the formal name for a font weight. - Ŝєmî-βøłð !!! + Şëмĩ-Ьσℓδ !!! This is the formal name for a font weight. - Ŝėмΐ-Ľîġĥт !!! + Ѕèmΐ-Ļιĝнτ !!! This is the formal name for a font weight. - Τћїй ! + Τнίň ! This is the formal name for a font weight. - Ѓêqûїřěδ łίğªтµŕєŝ !!! !! + Γёqúířзđ łίğāŧџřēś !!! !! This is the formal name for a font feature. - Łö¢ăłϊžêđ ƒоŗmś !!! ! + Ĺóčâľížёδ ƒбѓmŝ !!! ! This is the formal name for a font feature. - €оmрσѕїťΐǿʼn/ďё¢οmρôѕіťĩόп !!! !!! ! + Ćомрòşįťїόπ/ðêċоmρōšīŧįθπ !!! !!! ! This is the formal name for a font feature. - Ċθπţê×τυąŀ ãļτèřлåŧĕŝ !!! !!! + Ĉōňŧêжтµåľ âłтêŗňăţêš !!! !!! This is the formal name for a font feature. - Ѕţªńđąгđ ľïģąţџѓєŝ !!! !! + Ѕťаπδāгđ łїģąтũяεѕ !!! !! This is the formal name for a font feature. - €óñţéжţύäľ ŀіġäтµяέŝ !!! !!! + Çóⁿтęхтũàŀ ļîĝαŧūřέś !!! !!! This is the formal name for a font feature. - Ґєqůįřèð ναґîąŧįθη äľţеřŋαţëŝ !!! !!! !!! + Γėqŭιřėď νåѓϊăτίбή дℓţėґйàтзş !!! !!! !!! This is the formal name for a font feature. - К℮řñίņğ !! + Κεŕήïñġ !! This is the formal name for a font feature. - Μаяκ ρόśїťĩôліиĝ !!! ! + Μàřк ρøşíţīόпìηġ !!! ! This is the formal name for a font feature. - Мάяк ťθ мдřĸ φòśīŧїòηïⁿġ !!! !!! ! + Μäяκ тô mäřκ рøśιŧìøʼnїπğ !!! !!! ! This is the formal name for a font feature. - Ďîѕτàňςε !! + Ďіşŧǻⁿċё !! This is the formal name for a font feature. - Αςĉëѕś àŀℓ äľťэяйãτ℮ş !!! !!! + Āċςэѕş ǻℓł àŀтёřйąтęş !!! !!! This is the formal name for a font feature. - Сåśе şёηŝίτìνê ƒøřmš !!! !!! + Ċäŝě śêйşįтїνз ƒσřмš !!! !!! This is the formal name for a font feature. - Đέⁿŏmíлάтόŕ !!! + Ďëлоmįņãŧоґ !!! This is the formal name for a font feature. - Ţěřmïйáŀ ƒθяmš !!! ! + Τзѓmійäĺ ƒόгмś !!! ! This is the formal name for a font feature. - ₣гаçťĭοлš !!! + ₣ŕдςτìóиś !!! This is the formal name for a font feature. - Īŋĩťīàℓ ƒόгмŝ !!! + Ίńîťϊäℓ ƒǿѓmѕ !!! This is the formal name for a font feature. - Мëðїаł ƒόřмŝ !!! + Мèďіªł ƒбгmѕ !!! This is the formal name for a font feature. - Νŭмέŗаŧōŗ !!! + Νµмэѓǻτőґ !!! This is the formal name for a font feature. - Фŗðíñдłś !! + Οѓðìйăľś !! This is the formal name for a font feature. - Ѓєqúίŗεδ ċǿпťехŧцåŀ αℓŧέґńâŧеŝ !!! !!! !!! + Ŗĕqμîяĕď ¢σйŧēжţũăℓ ąļţėřиàţεś !!! !!! !!! This is the formal name for a font feature. - Şçΐзñţîƒîс ϊⁿƒêřīοŗѕ !!! !!! + Ŝςïёήŧïƒįç īлƒĕřїοŗŝ !!! !!! This is the formal name for a font feature. - Ѕŭвѕċŗíφŧ !!! + Šűвş¢яīφτ !!! This is the formal name for a font feature. - Šûрêѓŝĉяїρŧ !!! + Šüрéгŝςŕīρţ !!! This is the formal name for a font feature. - Ѕļäѕнєđ źĕѓō !!! + Šŀãѕħέđ żёяō !!! This is the formal name for a font feature. - Àвόνэ-вāşє мάґķ φθşιŧϊőηïňĝ !!! !!! !! + Ąъǿνέ-вäŝĕ máѓќ φŏŝīŧīǿήîиğ !!! !!! !! This is the formal name for a font feature. - Ľªΰисн śΐžě !!! + Ļâůлċђ šĭźě !!! Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". - Ţђę ńümъęѓ οƒ ѓõẁѕ áŋď ċóℓύmηŝ δĭşρļǻỳёđ іи ţĥė шίиđôώ џрòπ ƒιѓşт ŀōăδ. Μēαşύя℮ð їń снªгà¢ŧєŗś. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Τђē иΰмъëŕ òƒ ŕŏшš âйð ćοℓũмήş ďίśрľâỳĕď ĭń ţħє щìⁿðòẁ üрòⁿ ƒігśτ ŀŏаδ. Мĕãѕűґєδ ϊй ςħâгâсŧеŗš. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "rows" and "columns" settings do. Presented near "Globals_LaunchSize.Header". - Ĺåúή¢ĥ ρóŝїтīŏⁿ !!! ! + Łâųńсн ρōѕιτìол !!! ! Header for a group of settings that control the launch position of the app. Presented near "Globals_InitialPosX" and "Globals_InitialPosY". - Τĥε îⁿïťïàļ φǿşїŧĩøʼn õƒ τђê ťėґмϊņдŀ шíʼnðòш ūρθʼn śťаřţűρ. Ẁĥέй ļдΰñςħϊηğ āş măжϊмїż℮ð, ƒũŀŀ şçŕëëп, õґ шĭťĥ "Ćéйťεŕ οп ľãūηĉђ" ёиáьℓ℮ď, ţнιś įŝ µŝзđ ţő τăгğέŧ ťћĕ mõŋįτøѓ ôƒ їńťёŕέŝţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τнē ĭňĩţϊāŀ φбśìŧïôŋ öƒ ţħе τёŕmīпàľ ẅįиδόщ ùрǿň śτдѓтūρ. Ẃħěʼn łαùπснιņĝ άş mã×ĭmížеð, ƒŭļĺ ѕčŗ℮ёņ, οř щįťћ "€έŋţęŗ σñ ŀãυήĉħ" ėñǻьłēđ, тћíŝ іš ůśęδ τǿ τàѓģετ τћē mōиιŧōř οƒ íņтêŕєšτ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "X position" and "Y position" settings do. Presented near "Globals_LaunchPosition.Header". - Áłŀ + Άļļ An option to choose from for the "bell style" setting. When selected, a combination of the other bell styles is used to notify the user. - Vΐşΰâĺ (ƒļаşн ţąśķъãг) !!! !!! + Vіśŭаŀ (ƒļαśĥ тªşĸьäŕ) !!! !!! - ₣łдşĥ ŧǻşķъāŗ !!! + ₣ľāśн ťάśќьàг !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the taskbar is flashed. - ₣ľãşĥ ώίηđőẅ !!! + ₣ℓªşн шĩñðóẃ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the window is flashed. - Áđđ ŋєŵ !! + Ãďđ йēщ !! Button label that creates a new color scheme. - Ðёļĕŧε ćоĺǿŕ šсђзме !!! !!! + Ďєŀęτè ćоĺõř ѕсћémè !!! !!! Button label that deletes the selected color scheme. - Έðіţ ! + Єðіŧ ! Button label that edits the currently selected color scheme. - Ðеļёţè ! + Đ℮ĺęтέ ! Button label that deletes the selected color scheme. - Йαмέ ! + Иãмэ ! The name of the color scheme. Used as a label on the name control. - Ŧђę ñámз őƒ тћэ ċοŀǿѓ śćђéмэ. !!! !!! !!! + Ţнě ήαмė ōƒ τћέ çŏļöř ѕ¢нεмě. !!! !!! !!! Supplementary text (tooltip) for the control used to select a color scheme that is under view. - Đéĺзŧė ρґбƒïľэ !!! ! + Ðęℓęţё ρґŏƒïłë !!! ! Button label that deletes the current profile that is being viewed. - Ťђё ήάмĕ ǿƒ ţĥё φřōƒĭłě тђāт ªρρêãřѕ įп ţћέ đřǿφðōŵń. !!! !!! !!! !!! !!! + Ťħē ńǻмē óƒ ťће φґõƒïļė ťђåт аφρεāŕŝ ïñ τĥз ðяōрðοшл. !!! !!! !!! !!! !!! A description for what the "name" setting does. Presented near "Profile_Name". - ∏дmę ! + Ñâм℮ ! Name for a control to determine the name of the profile. This is a text box. - Ñámě ! + Ñâмē ! Header for a control to determine the name of the profile. This is a text box. - Τґąиšφǻŕėʼn¢γ !!! + Ţгāⁿšφäѓĕŋсý !!! Header for a group of settings related to transparency, including the acrylic material background of the app. - Ъăскğгόüńđ ĭmαģэ !!! ! + Ъàčќĝгôůʼnď ĩмăĝз !!! ! Header for a group of settings that control the image presented on the background of the app. Presented near "Profile_BackgroundImage" and other keys starting with "Profile_BackgroundImage". - Çüřśôř ! + Čūѓѕöŗ ! Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". - Äδđīτισņαℓ ŝėŧтĩйġś !!! !!! + Дððîтΐойаľ śєťŧΐηģѕ !!! !!! Header for the buttons that navigate to additional settings for the profile. - Ťęжт ! + Ťèхŧ ! Header for a group of settings that control the appearance of text in the app. - Ŵïⁿðθŵ ! + Ẁįŋđόώ ! Header for a group of settings that control the appearance of the window frame of the app. - Θрěň уõūґ settings.json ƒïℓé. Àŀт+Čļìćќ ŧб όр℮ň γōцř defaults.json ƒĭłε. !!! !!! !!! !!! !!! !!! !!! + Οφєп γǿυř settings.json ƒіļ℮. Āľт+Çļіçк тó οрėл ỳőΰѓ defaults.json ƒíļę. !!! !!! !!! !!! !!! !!! !!! {Locked="settings.json"}, {Locked="defaults.json"} - Ŕэπāмĕ ! + Яёņαmě ! Text label for a button that can be used to begin the renaming process. - Τћїŝ çôľóŕ ş¢ħ℮мě čàńŋôτ ъє δèľĕŧεδ öґ яэпаmėđ ъėĉãŭŝэ ίτ íŝ іʼnčľΰđзδ вỳ δзƒâûļτ. !!! !!! !!! !!! !!! !!! !!! !!! + Тĥΐš ĉōłбѓ şćĥěmę ċăйŋøţ вē ðέļεтэð øř ѓеʼnαmеð ъέсªùśē ίţ ΐš íńςℓűđ℮δ ьŷ δёƒãûĺŧ. !!! !!! !!! !!! !!! !!! !!! !!! Disclaimer presented next to the delete button when it is disabled. - Ўεş, đёłëτē čøŀòг şćħèmé !!! !!! ! + Ỳēѕ, ďзℓэτë ¢ôℓöѓ şςĥéмė !!! !!! ! Button label for positive confirmation of deleting a color scheme (presented with "ColorScheme_DeleteConfirmationMessage") - Ářё уôú šцяз ýöџ ώǻйŧ τó ðęŀėţέ ťħΐŝ ċõļόŗ ѕċђεmε? !!! !!! !!! !!! !!! + Αŗè уöũ šúгє ỳθú ẃάŋť ŧõ đэĺĕτе ţĥϊŝ сθļōґ ѕčħεmε? !!! !!! !!! !!! !!! A confirmation message displayed when the user intends to delete a color scheme. - Λććĕρţ ґёиåмέ !!! + Дсςėρť ґěʼnăме !!! Text label for a button that can be used to confirm a rename operation during the renaming process. - €åńĉэł я℮ŋдмз !!! + €àπċ℮ľ řëⁿáмè !!! Text label for a button that can be used to cancel a rename operation during the renaming process. - Ѕςнёмëŝ δëƒîйēð нęѓě ςαņ ьē ãρφŀïêđ τǿ ỳŏųѓ ρřôƒïℓėş üηðзŗ тħė "Áрφéąŗáήċэѕ" şёćťіŏŋ θƒ ťĥε рřǿƒíℓе šëŧťійğş рäģёŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Šċнęméѕ δĕƒϊлêδ ħęřέ ςαл вĕ äφφℓіĕδ ťǿ ýθŭг рřōƒїℓėŝ ũлď℮ŕ тђе "Ãррёáŗàʼnçєş" ŝēςţîőη όƒ ţħз φгòƒíłэ ѕěţτίиĝѕ ρăĝεş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A disclaimer presented at the top of a page. "Appearances" should match Profile_Appearance.Header. - Ŝēтτΐлğş ďéƒìиéď ћёŗé шΐℓŀ àρρļγ тǿ āℓŀ ряőƒîĺ℮ѕ ûńĺзŝѕ ţђёÿ âґē øνёґřĭδδέή ьγ ä φґбƒίĺë'š ѕĕŧţΐňĝѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Śėţţīиĝŝ đêƒįñèđ ђêŗè ẅïĺℓ âρφĺÿ тθ àℓŀ ргθƒìŀéѕ ΰⁿľėśѕ тнеγ åŗε õνěгяíδðėņ вỳ ª φŗσƒíŀз'ŝ ѕєŧтįņĝѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. See "Nav_ProfileDefaults.Content" for a description on what the defaults layer does in the app. - Ýεŝ, ďėľêτє рґǿƒιĺέ !!! !!! + Ýęѕ, δзŀёŧè ρяöƒïŀĕ !!! !!! Button label for positive confirmation of deleting a profile (presented with "Profile_DeleteConfirmationMessage") - Āřě ŷθŭ ѕûяę ўθü шäņт ţø δēļèтě ţнϊŝ φŕóƒїłэ? !!! !!! !!! !!! ! + Ąŕė ỳоű ŝμŗē ўöμ ẅаήτ ťõ ðėĺéτě ťĥįś ρґøƒïľę? !!! !!! !!! !!! ! A confirmation message displayed when the user intends to delete a profile. - Ŝανє дĺł μʼnŝāνėδ śеťťïйģѕ. !!! !!! ! + Ŝåνе àłŀ ΰŋşāνеð ѕĕţťΐήģѕ. !!! !!! ! A description for what the "save" button does. Presented near "Settings_SaveSettingsButton". - Тǻъ šẃîŧċћėґ īήтēѓƒâĉê śťўļė !!! !!! !! + Ťăь šẃίŧćĥеѓ ĭйţĕѓƒąĉę šţўĺє !!! !!! !! Header for a control to choose how the tab switcher operates. - Şēļєςτѕ ŵћΐ¢ђ íⁿτęřƒáċè щíľł ьè ΰšεđ ŵнěπ уôϋ ѕẁΐťсĥ ţąъš цѕιņğ τħз ķ℮ýьσªŗδ. !!! !!! !!! !!! !!! !!! !!! !! + Šèľĕсťš шћîćħ ïŋţêґƒαċē ẅīℓŀ вĕ µşєð ẅħèʼn γôџ ŝшιтĉђ тâъѕ ŭŝîņģ ŧнé κėỳьбαřδ. !!! !!! !!! !!! !!! !!! !!! !! A description for what the "tab switcher mode" setting does. Presented near "Globals_TabSwitcherMode.Header". - Ŝèρăгάťε ẃīńđσŵ, ĩй мòšţ řзċ℮йŧłÿ υŝéδ ôгđéŗ !!! !!! !!! !!! ! + Šěφâŕåте ŵĭñďŏш, ΐп mοѕť ѓέ¢℮πτℓỳ úѕęδ òѓðěŗ !!! !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in most recently used order. - Ŝêφäŕаţè ẁïлđбẅ, ίⁿ ţдь şŧяīφ òřđĕґ !!! !!! !!! ! + Ѕерãѓąťě ώίлďőω, īⁿ τâь şţгïρ õѓďéŗ !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in the order of the tabs at the top of the app. - Ŧгãðіťіόňаℓ ηανîġǻτĩŏņ, иǿ ŝерάгàťê шϊπðŏẃ !!! !!! !!! !!! + Ťгäďíτїσŋàℓ ņãνîģäτĭòń, пø ѕéрαяåţэ ώĩπðоẃ !!! !!! !!! !!! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is hidden and does not appear in a separate window. - Ťêхτ ƒōŕmаτś ţŏ ćõφỳ тŏ тĥě ¢ŀìρьоăгď !!! !!! !!! !! + Тėхŧ ƒоŗmάтѕ ţό ςǿрý ŧθ ŧћė ¢ℓįφъóǻяδ !!! !!! !!! !! Header for a control to select the format of copied text. - Ρłǻīη τе×τ оŋłý !!! ! + Ρľªĭⁿ тέ×ŧ õлľý !!! ! An option to choose from for the "copy formatting" setting. Store only plain text data. - ΗΤМ₤ ! + ĤŤМĹ ! An option to choose from for the "copy formatting" setting. Store only HTML data. - ГŢ₣ + ŖŦ₣ An option to choose from for the "copy formatting" setting. Store only RTF data. - βόŧђ НŢМĿ аñď ҐŢ₣ !!! !! + Ьôťĥ ΉŤМ₤ äñð ЃŤ₣ !!! !! An option to choose from for the "copy formatting" setting. Store both HTML and RTF data. - Рľёдѕě çђǿõśё à δĭƒƒзя℮иŧ ⁿамέ. !!! !!! !!! + Рłеåѕ℮ ĉнōöşè ª đэřêйт йªмэ. !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the subtitle and provides guidance to fix the issue. - Τђïѕ ĉòŀог ŝςĥëмё ńαmе ìš àļřέåďÿ îπ úšė. !!! !!! !!! !!! + Ťћιş çŏℓõя ş¢ћěмє пάмę ĭŝ дłřēàďÿ іʼn υѕě. !!! !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the title, and explains the issue. - Âϋţőmάţíćªļĺŷ ƒόçůş φâηε σň môµѕė нŏνèѓ !!! !!! !!! !!! + Αųтǿмáţìсãłľу ƒόćϋś φăήе øŋ мõµšє ħονęŗ !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. - Ρдńè ąηįмǻţìойѕ !!! ! + Рдñě αʼnĩmåτΐσπѕ !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. - ∆ĺẁάýş δįśρĺªỳ āň ιċôи іń τнę ñŏτįƒįčąţĩθⁿ аґэā !!! !!! !!! !!! !! + Δℓŵǻўś ðΐŝрĺąý áп ĭčθŋ ιń τħē ⁿõťíƒιςªтīôʼn ãгëå !!! !!! !!! !!! !! Header for a control to toggle whether the notification icon should always be shown. - Ηįδê Тεгmîñăℓ ιп ţђε ⁿǿτіƒίċάŧìōń āг℮â щĥèŋ īŧ ĩѕ мīñìмїžêδ !!! !!! !!! !!! !!! !!! + Ħїđé Ťëґmíñдĺ ϊņ ţĥε ʼnŏťϊƒϊċаţϊόñ åŗєǻ шнёи ïт ìş mΐⁿιмĩźėð !!! !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized. - Ѓεšēτ ťб ĩπħέяīτêđ νàļúè. !!! !!! ! + Řěŝ℮τ ťô ìйħęřïτ℮ð νãļũэ. !!! !!! ! This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button. - Ţёѓмîñǻľ ςοľöґš !!! ! + Ťзямιñāℓ ςоŀøгś !!! ! A header for a grouping of colors in the color scheme. These colors are used for basic parts of the terminal. - Śýšтёm ċòĺоřŝ !!! + Ѕŷšтêм ςółσѓš !!! A header for a grouping of colors in the color scheme. These colors are used for functional parts of the terminal. - Čоľôгş ! + Ĉōŀοŕš ! A header for the grouping of colors in the color scheme. - Ŕзşзť ťŏ νâłŭє ƒŕόм: {} !!! !!! + Ŕêšέт ţб νǻĺûę ƒґøm: {} !!! !!! {} is replaced by the name of another profile or generator. This is a text label on a button that is dynamically generated and provides more context in the {}. - Šћόω āĺľ ƒőňτš !!! ! + Śћőш ªłℓ ƒõňťš !!! ! A supplementary setting to the "font face" setting. Toggling this control updates the font face control to show all of the fonts installed. - Ĩƒ êʼnαъļєď, ŝћοш ãłł ίπşţăℓłёď ƒθиŧŝ īη τнê ľιşт ãъθνё. Оţћëŗẁįśë, őпľý ŝнõẅ ťħé ℓïśт ôƒ mòπõŝрă¢з ƒθņтѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ݃ ęπάьļёď, śноẃ αℓℓ ΐⁿšтāℓļёð ƒθπţѕ ĩⁿ τĥе ŀιšτ áьσνè. Øτнëгŵïšз, øпłỳ ѕħôẃ тнέ łîśτ ôƒ môńσşрąçę ƒòņτś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts". - Сŗĕàţє Äρφєäґāⁿсέ !!! !! + Ċяēåт℮ Λрρěåѓàņćé !!! !! Name for a control which creates an the unfocused appearance settings for this profile. Text must match that of "Profile_AddAppearanceButton.Text". - Çгèǻτę αñ üņƒòĉúŝēď ąφрзагäлčз ƒǿґ ţħïŝ φŗσƒíľĕ. Ŧђįś ώїℓŀ вē тĥè ǻφφеāřăⁿ¢ε őƒ τĥè φѓöƒїľě ŵħėñ іт ìѕ īⁿâçτΐνэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Čгèāтé äⁿ ύήƒοçцśéð ăφρзąřäήćε ƒõŕ τђїŝ рŗσƒîľè. Ţћĩѕ ωϊŀľ вē ŧнэ áφреàѓāп¢є оƒ τђë ряôƒϊľз ŵнел įт ιš ϊлåςţίνэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the create unfocused appearance button does. - Ďēłéťë Дρρ℮дřāñĉé !!! !! + Ðěłет℮ Ǻρρєåяайçє !!! !! Name for a control which deletes an the unfocused appearance settings for this profile. - Ďëĺєťέ ţћê ũⁿƒöĉŭśèđ ǻφφĕářâŋĉэ ƒόŗ ťђîş ρгøƒìĺé. !!! !!! !!! !!! !!! + Ďēĺėţ℮ ţнε úиƒоčϋѕёđ áрредŗªⁿčё ƒøŕ τĥίŝ рѓǿƒíľë. !!! !!! !!! !!! !!! A description for what the delete unfocused appearance button does. - Ўèš, δëĺęтĕ ќĕÿ ъīиðįήĝ !!! !!! + ¥єš, δеłéтε ĸэÿ ъΐⁿδĩňĝ !!! !!! Button label that confirms deletion of a key binding entry. - Áгę ýθū śμřë ўбυ ŵªπţ тŏ đėŀэŧĕ ţћïŝ ĸεỳ віиďĭŋĝ? !!! !!! !!! !!! !!! + Åŕĕ ýōű śūгé уοµ шǻηţ ťò đ℮ŀеτε τћϊş кεγ вїйðіņğ? !!! !!! !!! !!! !!! Confirmation message displayed when the user attempts to delete a key binding entry. - Ĭйνåłίδ кèу ςђöŕδ. Рŀєªѕ℮ εŋτεѓ а ναĺϊδ ĸ℮ý ċħóřδ. !!! !!! !!! !!! !!! + Ĭηνâłϊδ ќ℮ý ĉĥőřð. Ρłęąşє єπтëя ä νàľίδ κзў ¢нōřδ. !!! !!! !!! !!! !!! Error message displayed when an invalid key chord is input by the user. - Ŷęѕ + Ỳεš Button label that confirms the deletion of a conflicting key binding to allow the current key binding to be registered. - Ťħę ρгōνĭďęð ĸєÿ ĉђóґð ϊš àĺґėдðγ ьěïπğ μşěð вý ťђε ƒōĺŀõώїⁿĝ àçτιöй: !!! !!! !!! !!! !!! !!! !!! + Τĥě φŗбνīđëð кеý ĉĥöяď їş ăŀґεǻðу вέιņĝ цѕзđ ьý ŧнз ƒοļļθẅїπġ āĉτĭóή: !!! !!! !!! !!! !!! !!! !!! Error message displayed when a key chord that is already in use is input by the user. The name of the conflicting key chord is displayed after this message. - Ẃбύļđ убü łіќё тό ǿν℮řшгιŧё ϊт? !!! !!! !!! + Ẅőűℓđ ÿóů ŀįќė ŧø θνèящѓìťĕ іţ? !!! !!! !!! Confirmation message displayed when a key chord that is already in use is input by the user. This is intended to ask the user if they wish to delete the conflicting key binding, and assign the current key chord (or binding) instead. This is presented in the context of Actions_RenameConflictConfirmationMessage. The subject of this sentence is the object of that one. - <ΰⁿпámеð çōmmãйď> !!! !! + <µŋŋámėď ċŏммăηδ> !!! !! {Locked="<"} {Locked=">"} The text shown when referring to a command that is unnamed. - Άćçėрť ! + Δс¢ёрţ ! Text label for a button that can be used to accept changes to a key binding entry. - Čдηćęļ ! + Ċãиčëľ ! Text label for a button that can be used to cancel changes to a key binding entry - Đēľėťë ! + Ďзŀęŧë ! Text label for a button that can be used to delete a key binding entry. - Éďїτ ! + Еđιť ! Text label for a button that can be used to begin making changes to a key binding entry. - Αðď лещ !! + Äďď йęẃ !! Button label that creates a new action on the actions page. - Ąćŧīόń ! + Âċţϊŏη ! Label for a control that sets the action of a key binding. - Ϊηρûт ŷøυґ δёšїřєδ ķéÿъŏαґð šћσŕŧçϋţ. !!! !!! !!! !! + Ϊňрųŧ ỳσυř ðèšìяéð ĸεуъõāѓδ şћοґťĉΰŧ. !!! !!! !!! !! Help text directing users how to use the "KeyChordListener" control. Pressing a keyboard shortcut will be recorded by this control. - ѕĥŏгтςüτ ĺîśτėηēґ !!! !! + šћóґŧςΰт ĺΐŝţĕηèř !!! !! The control type for a control that awaits keyboard input and records it. - Ѕнõяţςúт !! + Ѕĥöятčūť !! The label for a "key chord listener" control that sets the keys a key binding is bound to. - Тęם ₣öŗmâτťιήģ !!! ! + Ţé×т ₣θяmāŧтïπğ !!! ! Header for a control to how text is formatted - Íлтěʼnŝє тёхť šťуĺē !!! !! + Ĭптèņŝê ŧěжτ ѕţýļê !!! !! Name for a control to select how "intense" text is formatted (bold, bright, both or none) - Іňţέŋşё тэжт şŧỳļέ !!! !! + İⁿŧ℮ńş℮ τе×ŧ šŧỳŀє !!! !! Header for a control to select how "intense" text is formatted (bold, bright, both or none) - Ņόπè ! + Иøńэ ! An option to choose from for the "intense text format" setting. When selected, "intense" text will not be rendered differently - Ъσĺδ ƒбņт !!! + Воĺð ƒσńţ !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as bold text - Бґїğнт сοłθŕѕ !!! + ßѓіğħť ςσℓőгś !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered in a brighter color - Вǿŀð ƒõηţ ωіτћ ьřίĝћţ ĉбłǿŕŝ !!! !!! !! + Вôľď ƒõŋτ ώіţћ ьŗίģĥť сθℓσѓś !!! !!! !! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color - Λΰţŏмąтĩčάŀłÿ ђίδę ẁιйðöẃ !!! !!! ! + Āúτõмдтìċąľľŷ нīδє ẃіπđóẅ !!! !!! ! Header for a control to toggle the "Automatically hide window" setting. If enabled, the terminal will be hidden as soon as you switch to another window. - Įƒ ěñāьĺзđ, тħē τêŕmίŋäℓ ẁīłĺ ьέ нĭðð℮л ăš şôŏņ äš ўθµ šẅϊтсн τó âņōţĥέя ώΐиđǿш. !!! !!! !!! !!! !!! !!! !!! !!! + ̃ êήāъℓêδ, τħè ŧέґmĩŋдļ ẃϊļľ ьз ђíðδéή ãś ѕőση åŝ ўŏΰ ѕŵįтćĥ тŏ āńǿţħęґ ẃίŋδóŵ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Automatically hide window" setting does. - Тĥιѕ ċǿŀθѓ şćђĕмë ςăņиōţ ьě ðєľέţęď ъè¢ăµšè îť ίś îņçĺûδєð ьŷ ďεƒäũŀŧ. !!! !!! !!! !!! !!! !!! !!! + Τђιş ςôľǿѓ ѕċħêмĕ ¢àηñöť ьє ďéľéтеδ ьзĉăùŝ℮ īŧ ΐş ιñςļύð℮ď ъγ ďєƒǻµĺť. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to delete the selected color scheme when that functionality is disabled. - Тĥĩş ĉõłõг ѕсħ℮mę ĉäŋиøт ьε гєидmęđ веćāûśē ĭτ ĭş їπ¢ℓцďэδ вý ďēƒăŭŀŧ. !!! !!! !!! !!! !!! !!! !!! + Ťнìş çόļσř şċĥëмê ςαńńǿţ вĕ ŗęŋämêđ ъèçăùšэ ΐŧ ĩѕ ιŋċļūđёð ъу ðęƒàùļт. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to rename the color scheme when that functionality is disabled. - δęƒâύĺŧ !! + ðēƒáűļτ !! Text label displayed adjacent to a "color scheme" entry signifying that it is currently set as the default color scheme to be used. - Śéť âŝ ð℮ƒăμĺť !!! ! + Şэτ ąŝ ðзƒãúľť !!! ! Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use. - Ẁάřη ŵнēń čľóšϊηģ мöřê ţħāŋ ойе ŧªв !!! !!! !!! ! + Ẃаŕп щђėņ сŀθşΐňĝ мõřě ţнαʼn ōŋé тǻв !!! !!! !!! ! Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open. - Щϊⁿđôŵѕ Т℮ŗмįńαł ìş яцπйїⁿģ їñ φθŗŧäвļė mσδĕ. !!! !!! !!! !!! ! + Ẅîйđσώš Тэґmîńаℓ ìś ґűñиїпġ ĩʼn φóґŧāъŀέ мōδ℮. !!! !!! !!! !!! ! A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder. @@ -1754,15 +1762,15 @@ {Locked} - Ŀěªřπ mŏѓε. !!! + Ļėâřп mõřэ. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - Ẃáґńîņġ: !! + Ẅаřňīήģ: !! Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - Çнŏőѕîñģ ą ήоņ-mõήòŝφаćĕð ƒôпť ώīłľ łîĸέļу ŗēѕŭŀť ĩñ νіŝůâļ āяťĩƒаςťŝ. Ũšε àţ ўοũґ ǿщʼn δĭşсŗ℮ŧіőñ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Çĥθőŝїпģ ã ηõπ-мóиőŝφä¢ёð ƒóπť ẃíĺļ łíκ℮ľỳ ѓëşŭĺŧ ίň νіşцаľ ªяţĩƒдĉтś. Цśë ąт ỳôμґ οωи ðϊš¢řěтíοŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index 188183fe221..3442857dd67 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -914,6 +914,14 @@ Если этот параметр включен, терминал рисует собственные глифы для элементов блока и символов рисования прямоугольников вместо использования шрифта. Эта функция работает только тогда, когда доступно ускорение графического процессора. A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + Полноцветные эмодзи + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + Если эта функция включена, эмодзи отображаются в полноцветном режиме. + A longer description of the "Profile_EnableColorGlyphs" toggle. + Насыщенность шрифта Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 88765dec2ee..08067344f73 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -914,6 +914,14 @@ 启用后,终端将为块元素和制表符绘制自定义标志符号,而不是使用字体。仅当 GPU 加速可用时,此功能才有效。 A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + 全色表情符号 + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + 启用后,表情符号将以全色显示。 + A longer description of the "Profile_EnableColorGlyphs" toggle. + 字体粗细 Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index e11c4990d38..d55ecef2a1f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -914,6 +914,14 @@ 啟用時,終端機會為區塊元素和方塊繪圖字元繪製自訂字符,而非使用字型。此功能僅在可使用 GPU 加速時作用。 A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. + + 全彩表情圖示 + The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. + + + 啟用時,表情圖示會以全彩顯示。 + A longer description of the "Profile_EnableColorGlyphs" toggle. + 字型粗細 Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. From 5f272804c4ff6c4788324a48ddfd9e44dd91a163 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Tue, 19 Mar 2024 15:00:59 -0700 Subject: [PATCH 165/603] Add OriginTag to Command (#16823) ## Summary of the Pull Request As outlined in #16816 , adding `OriginTag` to `Command` is one of the prerequisites to implementing Action IDs. This PR does that. ## Validation Steps Performed Actions/Commands still get parsed and work ## PR Checklist - [ ] Closes #xxx - [ ] Tests added/passed - [ ] Documentation updated - If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx - [ ] Schema updated (if necessary) --- .../TerminalSettingsModel/ActionMap.h | 4 +- .../ActionMapSerialization.cpp | 8 +- .../CascadiaSettingsSerialization.cpp | 6 +- .../TerminalSettingsModel/Command.cpp | 12 ++- src/cascadia/TerminalSettingsModel/Command.h | 5 +- .../TerminalSettingsModel/Command.idl | 2 +- .../GlobalAppSettings.cpp | 12 +-- .../TerminalSettingsModel/GlobalAppSettings.h | 6 +- .../UnitTests_SettingsModel/CommandTests.cpp | 26 +++--- .../KeyBindingsTests.cpp | 80 +++++++++---------- 10 files changed, 84 insertions(+), 77 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index 937d79f66cb..de9b9ca2361 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -66,8 +66,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void AddAction(const Model::Command& cmd); // JSON - static com_ptr FromJson(const Json::Value& json); - std::vector LayerJson(const Json::Value& json, const bool withKeybindings = true); + static com_ptr FromJson(const Json::Value& json, const OriginTag origin = OriginTag::None); + std::vector LayerJson(const Json::Value& json, const OriginTag origin, const bool withKeybindings = true); Json::Value ToJson() const; // modification diff --git a/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp b/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp index 248a0023d00..c03fecd82f5 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp @@ -19,10 +19,10 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; namespace winrt::Microsoft::Terminal::Settings::Model::implementation { - com_ptr ActionMap::FromJson(const Json::Value& json) + com_ptr ActionMap::FromJson(const Json::Value& json, const OriginTag origin) { auto result = make_self(); - result->LayerJson(json); + result->LayerJson(json, origin); return result; } @@ -35,7 +35,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - json: an array of Json::Value's to deserialize into our ActionMap. // Return value: // - a list of warnings encountered while deserializing the json - std::vector ActionMap::LayerJson(const Json::Value& json, const bool withKeybindings) + std::vector ActionMap::LayerJson(const Json::Value& json, const OriginTag origin, const bool withKeybindings) { // It's possible that the user provided keybindings have some warnings in // them - problems that we should alert the user to, but we can recover @@ -50,7 +50,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation continue; } - AddAction(*Command::FromJson(cmdJson, warnings, withKeybindings)); + AddAction(*Command::FromJson(cmdJson, warnings, origin, withKeybindings)); } return warnings; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index fc2cc562621..244c200dad8 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -345,7 +345,7 @@ void SettingsLoader::FinalizeLayering() if (userSettings.globals->EnableColorSelection()) { const auto json = _parseJson(LoadStringResource(IDR_ENABLE_COLOR_SELECTION)); - const auto globals = GlobalAppSettings::FromJson(json.root); + const auto globals = GlobalAppSettings::FromJson(json.root, OriginTag::InBox); userSettings.globals->AddLeastImportantParent(globals); } @@ -611,7 +611,7 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source settings.clear(); { - settings.globals = GlobalAppSettings::FromJson(json.root); + settings.globals = GlobalAppSettings::FromJson(json.root, origin); for (const auto& schemeJson : json.colorSchemes) { @@ -699,7 +699,7 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str // Parse out actions from the fragment. Manually opt-out of keybinding // parsing - fragments shouldn't be allowed to bind actions to keys // directly. We may want to revisit circa GH#2205 - settings.globals->LayerActionsFrom(json.root, false); + settings.globals->LayerActionsFrom(json.root, OriginTag::Fragment, false); } { diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 3b2f4075c5d..516d767481b 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { auto command{ winrt::make_self() }; command->_name = _name; + command->_Origin = OriginTag::User; command->_ActionAndArgs = *get_self(_ActionAndArgs)->Copy(); command->_keyMappings = _keyMappings; command->_iconPath = _iconPath; @@ -259,9 +260,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - the newly constructed Command object. winrt::com_ptr Command::FromJson(const Json::Value& json, std::vector& warnings, + const OriginTag origin, const bool parseKeys) { auto result = winrt::make_self(); + result->_Origin = origin; auto nested = false; JsonUtils::GetValueForKey(json, IterateOnKey, result->_IterateOn); @@ -274,7 +277,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Initialize our list of subcommands. result->_subcommands = winrt::single_threaded_map(); result->_nestedCommand = true; - auto nestedWarnings = Command::LayerJson(result->_subcommands, nestedCommandsJson); + auto nestedWarnings = Command::LayerJson(result->_subcommands, nestedCommandsJson, origin); // It's possible that the nested commands have some warnings warnings.insert(warnings.end(), nestedWarnings.begin(), nestedWarnings.end()); @@ -362,7 +365,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Return Value: // - A vector containing any warnings detected while parsing std::vector Command::LayerJson(IMap& commands, - const Json::Value& json) + const Json::Value& json, + const OriginTag origin) { std::vector warnings; @@ -372,7 +376,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { try { - const auto result = Command::FromJson(value, warnings); + const auto result = Command::FromJson(value, warnings, origin); if (result->ActionAndArgs().Action() == ShortcutAction::Invalid && !result->HasNestedCommands()) { // If there wasn't a parsed command, then try to get the @@ -583,7 +587,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // warnings, but ultimately, we don't care about warnings during // expansion. std::vector unused; - if (auto newCmd{ Command::FromJson(newJsonValue, unused) }) + if (auto newCmd{ Command::FromJson(newJsonValue, unused, expandable->_Origin) }) { newCommands.push_back(*newCmd); } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index a432fe9a370..5e94ae721c8 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::com_ptr FromJson(const Json::Value& json, std::vector& warnings, + const OriginTag origin, const bool parseKeys = true); static void ExpandCommands(Windows::Foundation::Collections::IMap& commands, @@ -47,7 +48,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Windows::Foundation::Collections::IVectorView schemes); static std::vector LayerJson(Windows::Foundation::Collections::IMap& commands, - const Json::Value& json); + const Json::Value& json, + const OriginTag origin); Json::Value ToJson() const; bool HasNestedCommands() const; @@ -75,6 +77,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None); WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs); + WINRT_PROPERTY(OriginTag, Origin); private: Json::Value _originalJson; diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index e92f459e69d..c9cec5012a4 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -31,7 +31,7 @@ namespace Microsoft.Terminal.Settings.Model ShortcutAction Action; }; - [default_interface] runtimeclass Command + [default_interface] runtimeclass Command : ISettingsModelObject { Command(); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index a720c261b39..7238d38c0cd 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -117,14 +117,14 @@ winrt::Microsoft::Terminal::Settings::Model::ActionMap GlobalAppSettings::Action // - json: an object which should be a serialization of a GlobalAppSettings object. // Return Value: // - a new GlobalAppSettings instance created from the values in `json` -winrt::com_ptr GlobalAppSettings::FromJson(const Json::Value& json) +winrt::com_ptr GlobalAppSettings::FromJson(const Json::Value& json, const OriginTag origin) { auto result = winrt::make_self(); - result->LayerJson(json); + result->LayerJson(json, origin); return result; } -void GlobalAppSettings::LayerJson(const Json::Value& json) +void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origin) { JsonUtils::GetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile); // GH#8076 - when adding enum values to this key, we also changed it from @@ -137,19 +137,19 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_LAYER_JSON) #undef GLOBAL_SETTINGS_LAYER_JSON - LayerActionsFrom(json, true); + LayerActionsFrom(json, origin, true); JsonUtils::GetValueForKey(json, LegacyReloadEnvironmentVariablesKey, _legacyReloadEnvironmentVariables); } -void GlobalAppSettings::LayerActionsFrom(const Json::Value& json, const bool withKeybindings) +void GlobalAppSettings::LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings) { static constexpr std::array bindingsKeys{ LegacyKeybindingsKey, ActionsKey }; for (const auto& jsonKey : bindingsKeys) { if (auto bindings{ json[JsonKey(jsonKey)] }) { - auto warnings = _actionMap->LayerJson(bindings, withKeybindings); + auto warnings = _actionMap->LayerJson(bindings, origin, withKeybindings); // It's possible that the user provided keybindings have some warnings // in them - problems that we should alert the user to, but we can diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 79f40342254..aa7d8c0147f 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -48,9 +48,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::ActionMap ActionMap() const noexcept; - static com_ptr FromJson(const Json::Value& json); - void LayerJson(const Json::Value& json); - void LayerActionsFrom(const Json::Value& json, const bool withKeybindings = true); + static com_ptr FromJson(const Json::Value& json, const OriginTag origin = OriginTag::None); + void LayerJson(const Json::Value& json, const OriginTag origin); + void LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings = true); Json::Value ToJson() const; diff --git a/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp index f68b0224a47..8db1141736f 100644 --- a/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp @@ -48,19 +48,19 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); { - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); } VERIFY_ARE_EQUAL(1u, commands.Size()); { - auto warnings = implementation::Command::LayerJson(commands, commands1Json); + auto warnings = implementation::Command::LayerJson(commands, commands1Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); } VERIFY_ARE_EQUAL(2u, commands.Size()); { - auto warnings = implementation::Command::LayerJson(commands, commands2Json); + auto warnings = implementation::Command::LayerJson(commands, commands2Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); } VERIFY_ARE_EQUAL(4u, commands.Size()); @@ -82,7 +82,7 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); { - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(1u, commands.Size()); auto command = commands.Lookup(L"action0"); @@ -93,7 +93,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); } { - auto warnings = implementation::Command::LayerJson(commands, commands1Json); + auto warnings = implementation::Command::LayerJson(commands, commands1Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(1u, commands.Size()); auto command = commands.Lookup(L"action0"); @@ -103,7 +103,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NULL(command.ActionAndArgs().Args()); } { - auto warnings = implementation::Command::LayerJson(commands, commands2Json); + auto warnings = implementation::Command::LayerJson(commands, commands2Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(1u, commands.Size()); auto command = commands.Lookup(L"action0"); @@ -115,7 +115,7 @@ namespace SettingsModelUnitTests } { // This last command should "unbind" the action. - auto warnings = implementation::Command::LayerJson(commands, commands3Json); + auto warnings = implementation::Command::LayerJson(commands, commands3Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(0u, commands.Size()); } @@ -143,7 +143,7 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(9u, commands.Size()); @@ -261,7 +261,7 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(3u, warnings.size()); VERIFY_ARE_EQUAL(1u, commands.Size()); @@ -288,7 +288,7 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); { - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(1u, commands.Size()); @@ -329,7 +329,7 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); // There are only 5 commands here: all of the `"none"`, `"auto"`, @@ -399,7 +399,7 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(1u, commands.Size()); @@ -462,7 +462,7 @@ namespace SettingsModelUnitTests auto commands = winrt::single_threaded_map(); VERIFY_ARE_EQUAL(0u, commands.Size()); - auto warnings = implementation::Command::LayerJson(commands, commands0Json); + auto warnings = implementation::Command::LayerJson(commands, commands0Json, OriginTag::None); VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(9u, commands.Size()); diff --git a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp index 8eed7276f1b..c5f4be49045 100644 --- a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp @@ -120,13 +120,13 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings1Json); + actionMap->LayerJson(bindings1Json, OriginTag::None); VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings2Json); + actionMap->LayerJson(bindings2Json, OriginTag::None); VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size()); } @@ -143,21 +143,21 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings1Json); + actionMap->LayerJson(bindings1Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings2Json); + actionMap->LayerJson(bindings2Json, OriginTag::None); VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size()); } void KeyBindingsTests::HashDeduplication() { const auto actionMap = winrt::make_self(); - actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])")); - actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])")); + actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])"), OriginTag::None); + actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": "splitPane", "keys": ["ctrl+c"] } ])"), OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_ActionMap.size()); } @@ -180,51 +180,51 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings1Json); + actionMap->LayerJson(bindings1Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); Log::Comment(NoThrowString().Format( L"Try unbinding a key using `\"unbound\"` to unbind the key")); - actionMap->LayerJson(bindings2Json); + actionMap->LayerJson(bindings2Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('C'), 0 })); Log::Comment(NoThrowString().Format( L"Try unbinding a key using `null` to unbind the key")); // First add back a good binding - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); // Then try layering in the bad setting - actionMap->LayerJson(bindings3Json); + actionMap->LayerJson(bindings3Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('C'), 0 })); Log::Comment(NoThrowString().Format( L"Try unbinding a key using an unrecognized command to unbind the key")); // First add back a good binding - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); // Then try layering in the bad setting - actionMap->LayerJson(bindings4Json); + actionMap->LayerJson(bindings4Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('C'), 0 })); Log::Comment(NoThrowString().Format( L"Try unbinding a key using a straight up invalid value to unbind the key")); // First add back a good binding - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); // Then try layering in the bad setting - actionMap->LayerJson(bindings5Json); + actionMap->LayerJson(bindings5Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('C'), 0 })); Log::Comment(NoThrowString().Format( L"Try unbinding a key that wasn't bound at all")); - actionMap->LayerJson(bindings2Json); + actionMap->LayerJson(bindings2Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast('C'), 0 })); } @@ -244,13 +244,13 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord)); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord)); - actionMap->LayerJson(bindings1Json); + actionMap->LayerJson(bindings1Json, OriginTag::None); VERIFY_IS_TRUE(actionMap->IsKeyChordExplicitlyUnbound(keyChord)); - actionMap->LayerJson(bindings2Json); + actionMap->LayerJson(bindings2Json, OriginTag::None); VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord)); } @@ -277,7 +277,7 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(10u, actionMap->_KeyMap.size()); { @@ -405,7 +405,7 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size()); { @@ -454,7 +454,7 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size()); { @@ -495,7 +495,7 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); { @@ -522,7 +522,7 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(6u, actionMap->_KeyMap.size()); { @@ -580,7 +580,7 @@ namespace SettingsModelUnitTests const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString); auto invalidActionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size()); - VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception); + VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson, OriginTag::None);, std::exception); } } @@ -595,7 +595,7 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size()); { @@ -617,7 +617,7 @@ namespace SettingsModelUnitTests { const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": "moveTab" }])" }; auto actionMapNoArgs = winrt::make_self(); - actionMapNoArgs->LayerJson(bindingsInvalidString); + actionMapNoArgs->LayerJson(bindingsInvalidString, OriginTag::None); VERIFY_ARE_EQUAL(0u, actionMapNoArgs->_KeyMap.size()); } { @@ -625,7 +625,7 @@ namespace SettingsModelUnitTests const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString); auto invalidActionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size()); - VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception); + VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson, OriginTag::None);, std::exception); } } @@ -641,7 +641,7 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size()); { @@ -673,7 +673,7 @@ namespace SettingsModelUnitTests const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString); auto invalidActionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size()); - VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception); + VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson, OriginTag::None);, std::exception); } } @@ -707,14 +707,14 @@ namespace SettingsModelUnitTests { Log::Comment(L"simple command: no args"); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CloseWindow) }; VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast('A'), 0 }, kbd); } { Log::Comment(L"command with args"); - actionMap->LayerJson(bindings1Json); + actionMap->LayerJson(bindings1Json, OriginTag::None); VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size()); auto args{ winrt::make_self() }; @@ -725,7 +725,7 @@ namespace SettingsModelUnitTests } { Log::Comment(L"command with new terminal args"); - actionMap->LayerJson(bindings2Json); + actionMap->LayerJson(bindings2Json, OriginTag::None); VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size()); auto newTerminalArgs{ winrt::make_self() }; @@ -737,7 +737,7 @@ namespace SettingsModelUnitTests } { Log::Comment(L"command with hidden args"); - actionMap->LayerJson(bindings3Json); + actionMap->LayerJson(bindings3Json, OriginTag::None); VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size()); const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) }; @@ -762,13 +762,13 @@ namespace SettingsModelUnitTests auto actionMap = winrt::make_self(); VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings0Json); + actionMap->LayerJson(bindings0Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size()); - actionMap->LayerJson(bindings1Json); + actionMap->LayerJson(bindings1Json, OriginTag::None); VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size(), L"Layering the second action should replace the first one."); - actionMap->LayerJson(bindings2Json); + actionMap->LayerJson(bindings2Json, OriginTag::None); VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size()); } @@ -777,7 +777,7 @@ namespace SettingsModelUnitTests const auto json = VerifyParseSucceeded(R"!([{"command": "quakeMode", "keys":"shift+sc(255)"}])!"); const auto actionMap = winrt::make_self(); - actionMap->LayerJson(json); + actionMap->LayerJson(json, OriginTag::None); const auto action = actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Shift, 0, 255 }); VERIFY_IS_NOT_NULL(action); From a97166344939477a10145691b16375037900d062 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 20 Mar 2024 09:02:26 -0700 Subject: [PATCH 166/603] Replace usages of `TYPED_EVENT` with `til::event` (#16837) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR automagically finds and replaces all[^1] usages of our TYPED_EVENT macro with `til::event`. Benefits include: * less macro magic * editors are more easily able to figure out the relationship between `til::event<> Foo;`, `Foo.raise(...)`, and `bar.Foo({this, &Bar::FooHandler})` (whereas before the relationship between `_FooHandlers(...)` and `bar.Foo({...})`) couldn't be figured out by vscode & sublime. Other find & replace work that had to be done: * I added the `til::typed_event<>` == `` thing from #16170, since that is goodness * I actually fixed `til::property_changed_event`, so you can use that for your property changed events. They're all the same anyways. * events had to come before `WINRT_PROPERTY`s, since the latter macro leaves us in a `private:` block * `Pane::SetupChildCloseHandlers` I had to swap _back_, because the script thought that was an event 🤦 * `ProfileViewModel::DeleteProfile` had to be renamed `DeleteProfileRequested`, since there was already a `DeleteProfile` method on it. * WindowManager.cpp was directly wiring up it's `winrt::event`s to the monarch & peasant. That doesn't work with `til::event`s and I'm kinda surprised it ever did
The script in question ```py import os import re def replace_in_file(file_path, file_name): with open(file_path, 'r', encoding="utf8") as file: content = file.read() found_matches = False # Define the pattern for matching pattern = r' WINRT_CALLBACK\((\w+),\s*(.*?)\);' event_matches = re.findall(pattern, content) if event_matches: found_matches = True print(f'found events in {file_path}:') for match in event_matches: name = match[0] args = match[1] if name == "newConnection" and file_name == "ConptyConnection.cpp": # This one is special continue old_declaration = 'WINRT_CALLBACK(' + name + ', ' + args + ');' new_declaration = 'til::event<' + args + '> ' + name + ';' if name != "PropertyChanged" else 'til::property_changed_event PropertyChanged;' print(f' {old_declaration} -> {new_declaration}') content = content.replace(old_declaration, new_declaration) typed_event_pattern = r' TYPED_EVENT\((\w+),\s*(.*?)\);' typed_matches = re.findall(typed_event_pattern, content) if typed_matches: found_matches = True print(f'found typed_events in {file_path}:') for match in typed_matches: name = match[0] args = match[1] if name == "newConnection" and file_name == "ConptyConnection.cpp": # This one is special continue old_declaration = f'TYPED_EVENT({name}, {args});' was_inspectable = (args == "winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable" ) or (args == "IInspectable, IInspectable" ) new_declaration = f'til::typed_event<{args}> {name};' if not was_inspectable else f"til::typed_event<> {name};" print(f' {old_declaration} -> {new_declaration}') content = content.replace(old_declaration, new_declaration) handlers_pattern = r'_(\w+)Handlers\(' handler_matches = re.findall(handlers_pattern, content) if handler_matches: found_matches = True print(f'found handlers in {file_path}:') for match in handler_matches: name = match if name == "newConnection" and file_name == "ConptyConnection.cpp": # This one is special continue old_declaration = f'_{name}Handlers(' new_declaration = f'{name}.raise(' print(f' {old_declaration} -> {new_declaration}') content = content.replace(old_declaration, new_declaration) if found_matches: with open(file_path, 'w', encoding="utf8") as file: file.write(content) def find_and_replace(directory): for root, dirs, files in os.walk(directory): if 'Generated Files' in dirs: dirs.remove('Generated Files') # Exclude the "Generated Files" directory for file in files: if file.endswith('.cpp') or file.endswith('.h') or file.endswith('.hpp'): file_path = os.path.join(root, file) try: replace_in_file(file_path, file) except Exception as e: print(f"error reading {file_path}") if file == "TermControl.cpp": print(e) # raise e # Replace in files within a specific directory directory_path = 'D:\\dev\\public\\terminal\\src' find_and_replace(directory_path) ```
[^1]: there are other macros we use that were also using this macro, those weren't replaced. --------- Co-authored-by: Dustin Howett Co-authored-by: Leonard Hecker --- src/cascadia/Remoting/Monarch.cpp | 20 +++--- src/cascadia/Remoting/Monarch.h | 16 ++--- src/cascadia/Remoting/Peasant.cpp | 26 +++---- src/cascadia/Remoting/Peasant.h | 32 ++++----- src/cascadia/Remoting/WindowManager.cpp | 31 ++++++-- src/cascadia/Remoting/WindowManager.h | 17 +++-- src/cascadia/TerminalApp/AboutDialog.h | 6 +- .../TerminalApp/AppActionHandlers.cpp | 8 +-- src/cascadia/TerminalApp/AppLogic.cpp | 4 +- src/cascadia/TerminalApp/AppLogic.h | 2 +- .../TerminalApp/ColorPickupFlyout.cpp | 8 +-- src/cascadia/TerminalApp/ColorPickupFlyout.h | 4 +- src/cascadia/TerminalApp/CommandPalette.cpp | 12 ++-- src/cascadia/TerminalApp/CommandPalette.h | 24 +++---- .../TerminalApp/DebugTapConnection.cpp | 6 +- src/cascadia/TerminalApp/DebugTapConnection.h | 4 +- src/cascadia/TerminalApp/FilteredCommand.h | 10 +-- src/cascadia/TerminalApp/HighlightedText.h | 10 +-- .../TerminalApp/MinMaxCloseControl.cpp | 6 +- src/cascadia/TerminalApp/MinMaxCloseControl.h | 6 +- src/cascadia/TerminalApp/PaletteItem.h | 8 +-- src/cascadia/TerminalApp/Pane.cpp | 18 ++--- src/cascadia/TerminalApp/Pane.h | 14 ++-- .../TerminalApp/SuggestionsControl.cpp | 2 +- src/cascadia/TerminalApp/SuggestionsControl.h | 12 ++-- src/cascadia/TerminalApp/TabBase.cpp | 6 +- src/cascadia/TerminalApp/TabBase.h | 16 ++--- src/cascadia/TerminalApp/TabHeaderControl.cpp | 4 +- src/cascadia/TerminalApp/TabHeaderControl.h | 13 ++-- src/cascadia/TerminalApp/TabManagement.cpp | 8 +-- src/cascadia/TerminalApp/TabPaletteItem.cpp | 2 +- src/cascadia/TerminalApp/TabPaletteItem.h | 2 +- src/cascadia/TerminalApp/TabRowControl.h | 4 +- src/cascadia/TerminalApp/TerminalPage.cpp | 32 ++++----- src/cascadia/TerminalApp/TerminalPage.h | 50 ++++++------- src/cascadia/TerminalApp/TerminalTab.cpp | 16 ++--- src/cascadia/TerminalApp/TerminalTab.h | 6 +- src/cascadia/TerminalApp/TerminalTabStatus.h | 18 ++--- src/cascadia/TerminalApp/TerminalWindow.cpp | 12 ++-- src/cascadia/TerminalApp/TerminalWindow.h | 17 ++--- src/cascadia/TerminalApp/pch.h | 1 + .../TerminalConnection/AzureConnection.cpp | 20 +++--- .../TerminalConnection/AzureConnection.h | 2 +- .../BaseTerminalConnection.h | 4 +- .../TerminalConnection/ConptyConnection.cpp | 20 +++--- .../TerminalConnection/ConptyConnection.h | 2 +- .../TerminalConnection/EchoConnection.cpp | 2 +- .../TerminalConnection/EchoConnection.h | 4 +- src/cascadia/TerminalConnection/pch.h | 1 + src/cascadia/TerminalControl/ControlCore.cpp | 72 +++++++++---------- src/cascadia/TerminalControl/ControlCore.h | 56 +++++++-------- .../TerminalControl/ControlInteractivity.cpp | 18 ++--- .../TerminalControl/ControlInteractivity.h | 12 ++-- .../InteractivityAutomationPeer.cpp | 8 +-- .../InteractivityAutomationPeer.h | 8 +-- .../TerminalControl/SearchBoxControl.cpp | 22 +++--- .../TerminalControl/SearchBoxControl.h | 8 +-- .../TerminalControl/SearchBoxControl.xaml | 2 +- .../TerminalControl/TSFInputControl.cpp | 6 +- .../TerminalControl/TSFInputControl.h | 6 +- src/cascadia/TerminalControl/TermControl.cpp | 48 ++++++------- src/cascadia/TerminalControl/TermControl.h | 27 +++---- src/cascadia/TerminalSettingsEditor/Actions.h | 4 +- .../ActionsViewModel.cpp | 16 ++--- .../TerminalSettingsEditor/ActionsViewModel.h | 15 ++-- .../TerminalSettingsEditor/AddProfile.h | 13 ++-- .../TerminalSettingsEditor/Appearances.cpp | 62 ++++++++-------- .../TerminalSettingsEditor/Appearances.h | 10 +-- .../ColorSchemeViewModel.cpp | 2 +- .../ColorSchemeViewModel.h | 8 +-- .../TerminalSettingsEditor/ColorSchemes.h | 6 +- .../TerminalSettingsEditor/EditColorScheme.h | 4 +- .../TerminalSettingsEditor/EnumEntry.h | 6 +- .../TerminalSettingsEditor/GlobalAppearance.h | 4 +- .../TerminalSettingsEditor/Interaction.h | 4 +- src/cascadia/TerminalSettingsEditor/Launch.h | 4 +- .../TerminalSettingsEditor/MainPage.cpp | 6 +- .../TerminalSettingsEditor/MainPage.h | 2 +- .../PreviewConnection.cpp | 2 +- .../PreviewConnection.h | 4 +- .../ProfileViewModel.cpp | 2 +- .../TerminalSettingsEditor/ProfileViewModel.h | 4 +- .../ProfileViewModel.idl | 2 +- .../Profiles_Advanced.h | 2 +- .../Profiles_Appearance.h | 2 +- .../TerminalSettingsEditor/Profiles_Base.h | 2 +- .../TerminalSettingsEditor/Rendering.h | 4 +- .../SettingContainer.cpp | 2 +- .../TerminalSettingsEditor/SettingContainer.h | 3 +- src/cascadia/TerminalSettingsEditor/pch.h | 1 + .../UnitTests_Control/MockConnection.h | 6 +- .../UnitTests_Remoting/RemotingTests.cpp | 40 +++++------ .../TilWinRtHelpersTests.cpp | 21 ++++++ src/cascadia/UnitTests_TerminalCore/pch.h | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.h | 2 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 24 +++---- src/cascadia/WindowsTerminal/IslandWindow.h | 32 ++++----- .../WindowsTerminal/NotificationIcon.cpp | 6 +- .../WindowsTerminal/NotificationIcon.h | 2 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 4 +- src/cascadia/WindowsTerminal/WindowThread.h | 2 +- src/cascadia/WindowsTerminal/pch.h | 1 + src/inc/til/winrt.h | 11 ++- .../MonarchPeasantSample/SamplePeasant.cpp | 2 +- .../MonarchPeasantSample/SamplePeasant.h | 2 +- src/tools/MonarchPeasantSample/pch.h | 1 + 107 files changed, 633 insertions(+), 583 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cf2edd98fa3..6b48650c5f2 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -95,8 +95,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows }); peasant.RenameRequested({ this, &Monarch::_renameRequested }); - peasant.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); }); - peasant.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); }); + peasant.ShowNotificationIconRequested([this](auto&&, auto&&) { ShowNotificationIconRequested.raise(*this, nullptr); }); + peasant.HideNotificationIconRequested([this](auto&&, auto&&) { HideNotificationIconRequested.raise(*this, nullptr); }); peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll }); { @@ -111,7 +111,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _WindowCreatedHandlers(nullptr, nullptr); + WindowCreated.raise(nullptr, nullptr); return newPeasantsId; } catch (...) @@ -141,7 +141,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Let the process hosting the monarch run any needed logic before // closing all windows. auto args = winrt::make_self(); - _QuitAllRequestedHandlers(*this, *args); + QuitAllRequested.raise(*this, *args); if (const auto action = args->BeforeQuitAllAction()) { @@ -207,7 +207,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation std::unique_lock lock{ _peasantsMutex }; _peasants.erase(peasantId); } - _WindowClosedHandlers(nullptr, nullptr); + WindowClosed.raise(nullptr, nullptr); } // Method Description: @@ -650,7 +650,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto findWindowArgs{ winrt::make_self(args) }; // This is handled by some handler in-proc - _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); + FindTargetWindowRequested.raise(*this, *findWindowArgs); // After the event was handled, ResultTargetWindow() will be filled with // the parsed result. @@ -741,7 +741,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation result->WindowName(targetWindowName); result->ShouldCreateWindow(true); - _RequestNewWindowHandlers(*this, *winrt::make_self(*result, args)); + RequestNewWindow.raise(*this, *winrt::make_self(*result, args)); // If this fails, it'll be logged in the following // TraceLoggingWrite statement, with succeeded=false @@ -779,7 +779,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation result->Id(windowID); result->WindowName(targetWindowName); - _RequestNewWindowHandlers(*this, *winrt::make_self(*result, args)); + RequestNewWindow.raise(*this, *winrt::make_self(*result, args)); return *result; } @@ -796,7 +796,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto result = winrt::make_self(true); result->WindowName(targetWindowName); - _RequestNewWindowHandlers(*this, *winrt::make_self(*result, args)); + RequestNewWindow.raise(*this, *winrt::make_self(*result, args)); return *result; } @@ -1115,7 +1115,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto request = winrt::make_self(nameIsReserved ? L"" : window, content, windowBounds); - _RequestNewWindowHandlers(*this, *request); + RequestNewWindow.raise(*this, *request); } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 203119a70ce..48619ac47b8 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -101,14 +101,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, const Windows::Foundation::IReference& windowBounds); void RequestSendContent(const Remoting::RequestReceiveContentArgs& args); - TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); - TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs); + til::typed_event FindTargetWindowRequested; + til::typed_event<> ShowNotificationIconRequested; + til::typed_event<> HideNotificationIconRequested; + til::typed_event<> WindowCreated; + til::typed_event<> WindowClosed; + til::typed_event QuitAllRequested; - TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs); + til::typed_event RequestNewWindow; private: uint64_t _ourPID; @@ -223,7 +223,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // A peasant died, let the app host know that the number of // windows has changed. - _WindowClosedHandlers(nullptr, nullptr); + WindowClosed.raise(nullptr, nullptr); } } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 94f8406c00c..5d2b54f85af 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -67,7 +67,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Raise an event with these args. The AppHost will listen for this // event to know when to take these args and dispatch them to a // currently-running window. - _ExecuteCommandlineRequestedHandlers(*this, args); + ExecuteCommandlineRequested.raise(*this, args); return true; } @@ -97,7 +97,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // by the monarch. The monarch might have died. If they have, this // will throw an exception. Just eat it, the election thread will // handle hooking up the new one. - _WindowActivatedHandlers(*this, args); + WindowActivated.raise(*this, args); successfullyNotified = true; } catch (...) @@ -146,7 +146,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _SummonRequestedHandlers(*this, localCopy); + SummonRequested.raise(*this, localCopy); } // Method Description: @@ -161,7 +161,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // Not worried about try/catching this. The handler is in AppHost, which // is in-proc for us. - _DisplayWindowIdRequestedHandlers(*this, nullptr); + DisplayWindowIdRequested.raise(*this, nullptr); } // Method Description: @@ -182,7 +182,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // by the monarch. The monarch might have died. If they have, this // will throw an exception. Just eat it, the election thread will // handle hooking up the new one. - _IdentifyWindowsRequestedHandlers(*this, nullptr); + IdentifyWindowsRequested.raise(*this, nullptr); successfullyNotified = true; } catch (...) @@ -207,7 +207,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // by the monarch. The monarch might have died. If they have, this // will throw an exception. Just eat it, the election thread will // handle hooking up the new one. - _RenameRequestedHandlers(*this, args); + RenameRequested.raise(*this, args); if (args.Succeeded()) { _WindowName = args.NewName(); @@ -233,7 +233,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { try { - _ShowNotificationIconRequestedHandlers(*this, nullptr); + ShowNotificationIconRequested.raise(*this, nullptr); } catch (...) { @@ -249,7 +249,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { try { - _HideNotificationIconRequestedHandlers(*this, nullptr); + HideNotificationIconRequested.raise(*this, nullptr); } catch (...) { @@ -265,7 +265,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { try { - _QuitAllRequestedHandlers(*this, nullptr); + QuitAllRequested.raise(*this, nullptr); } catch (...) { @@ -281,7 +281,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { try { - _AttachRequestedHandlers(*this, request); + AttachRequested.raise(*this, request); } catch (...) { @@ -297,7 +297,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { try { - _QuitRequestedHandlers(*this, nullptr); + QuitRequested.raise(*this, nullptr); } catch (...) { @@ -318,7 +318,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation hstring Peasant::GetWindowLayout() { auto args = winrt::make_self(); - _GetWindowLayoutRequestedHandlers(nullptr, *args); + GetWindowLayoutRequested.raise(nullptr, *args); if (const auto op = args->WindowLayoutJsonAsync()) { // This will fail if called on the UI thread, so the monarch should @@ -331,6 +331,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Peasant::SendContent(const Remoting::RequestReceiveContentArgs& args) { - _SendContentRequestedHandlers(*this, args); + SendContentRequested.raise(*this, args); } } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 26a82a17fef..67d407e599a 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -68,25 +68,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::hstring GetWindowLayout(); void SendContent(const winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs& args); + til::typed_event WindowActivated; + til::typed_event ExecuteCommandlineRequested; + til::typed_event<> IdentifyWindowsRequested; + til::typed_event<> DisplayWindowIdRequested; + til::typed_event RenameRequested; + til::typed_event SummonRequested; + + til::typed_event<> ShowNotificationIconRequested; + til::typed_event<> HideNotificationIconRequested; + til::typed_event<> QuitAllRequested; + til::typed_event<> QuitRequested; + til::typed_event GetWindowLayoutRequested; + + til::typed_event AttachRequested; + til::typed_event SendContentRequested; + WINRT_PROPERTY(winrt::hstring, WindowName); WINRT_PROPERTY(winrt::hstring, ActiveTabTitle); - TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs); - TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); - TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs); - TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior); - - TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs); - - TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); - TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs); - private: Peasant(const uint64_t testPID); uint64_t _ourPID; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index aa9f67b0b3f..57c110cd37d 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -89,10 +89,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // done when we become the king. This will be called both for the first // window, and when the current monarch dies. - _monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers }); - _monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers }); + _monarch.WindowCreated({ get_weak(), &WindowManager::_bubbleWindowCreated }); + _monarch.WindowClosed({ get_weak(), &WindowManager::_bubbleWindowClosed }); _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); - _monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers }); + _monarch.QuitAllRequested({ get_weak(), &WindowManager::_bubbleQuitAllRequested }); _monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow }); } @@ -109,12 +109,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) { - _FindTargetWindowRequestedHandlers(sender, args); + FindTargetWindowRequested.raise(sender, args); } void WindowManager::_raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args) { - _RequestNewWindowHandlers(sender, args); + RequestNewWindow.raise(sender, args); } Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args, const bool isolatedMode) @@ -162,7 +162,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto findWindowArgs{ winrt::make_self(args) }; // This is handled by some handler in-proc - _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); + FindTargetWindowRequested.raise(*this, *findWindowArgs); // After the event was handled, ResultTargetWindow() will be filled with // the parsed result. @@ -356,7 +356,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.AddPeasant(*p); - p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers }); + p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_bubbleGetWindowLayoutRequested }); TraceLoggingWrite(g_hRemotingProvider, "WindowManager_CreateOurPeasant", @@ -367,6 +367,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *p; } + void WindowManager::_bubbleGetWindowLayoutRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& e) + { + GetWindowLayoutRequested.raise(s, e); + } + void WindowManager::_bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e) + { + WindowCreated.raise(s, e); + } + void WindowManager::_bubbleWindowClosed(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e) + { + WindowClosed.raise(s, e); + } + void WindowManager::_bubbleQuitAllRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& e) + { + QuitAllRequested.raise(s, e); + } + void WindowManager::SignalClose(const Remoting::Peasant& peasant) { if (_monarch) diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 015ab9f3472..96946f1d3cc 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -47,14 +47,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference windowBounds); winrt::fire_and_forget RequestSendContent(Remoting::RequestReceiveContentArgs args); - TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + til::typed_event FindTargetWindowRequested; - TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs); - TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs); - - TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs); + til::typed_event<> WindowCreated; + til::typed_event<> WindowClosed; + til::typed_event QuitAllRequested; + til::typed_event GetWindowLayoutRequested; + til::typed_event RequestNewWindow; private: DWORD _registrationHostClass{ 0 }; @@ -70,6 +69,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); void _raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); + void _bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e); + void _bubbleWindowClosed(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e); + void _bubbleQuitAllRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& e); + void _bubbleGetWindowLayoutRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& e); }; } diff --git a/src/cascadia/TerminalApp/AboutDialog.h b/src/cascadia/TerminalApp/AboutDialog.h index c4dac5bdf72..c8cda18c24b 100644 --- a/src/cascadia/TerminalApp/AboutDialog.h +++ b/src/cascadia/TerminalApp/AboutDialog.h @@ -15,9 +15,9 @@ namespace winrt::TerminalApp::implementation winrt::hstring ApplicationDisplayName(); winrt::hstring ApplicationVersion(); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(bool, UpdatesAvailable, _PropertyChangedHandlers, false); - WINRT_OBSERVABLE_PROPERTY(bool, CheckingForUpdates, _PropertyChangedHandlers, false); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(bool, UpdatesAvailable, PropertyChanged.raise, false); + WINRT_OBSERVABLE_PROPERTY(bool, CheckingForUpdates, PropertyChanged.raise, false); private: friend struct AboutDialogT; // for Xaml to bind events diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index beb4e4e2539..4e4f63d3676 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -117,7 +117,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/, const ActionEventArgs& args) { - _CloseRequestedHandlers(nullptr, nullptr); + CloseRequested.raise(nullptr, nullptr); args.Handled(true); } @@ -948,7 +948,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleIdentifyWindows(const IInspectable& /*sender*/, const ActionEventArgs& args) { - _IdentifyWindowsRequestedHandlers(*this, nullptr); + IdentifyWindowsRequested.raise(*this, nullptr); args.Handled(true); } @@ -977,7 +977,7 @@ namespace winrt::TerminalApp::implementation { const auto newName = realArgs.Name(); const auto request = winrt::make_self(newName); - _RenameWindowRequestedHandlers(*this, *request); + RenameWindowRequested.raise(*this, *request); args.Handled(true); } } @@ -1147,7 +1147,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleOpenSystemMenu(const IInspectable& /*sender*/, const ActionEventArgs& args) { - _OpenSystemMenuHandlers(*this, nullptr); + OpenSystemMenu.raise(*this, nullptr); args.Handled(true); } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 7ba29cb6eea..29c13fdb13f 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -423,7 +423,7 @@ namespace winrt::TerminalApp::implementation _settingsLoadExceptionText, warnings, _settings); - _SettingsChangedHandlers(*this, *ev); + SettingsChanged.raise(*this, *ev); return; } } @@ -452,7 +452,7 @@ namespace winrt::TerminalApp::implementation _settingsLoadExceptionText, warnings, _settings); - _SettingsChangedHandlers(*this, *ev); + SettingsChanged.raise(*this, *ev); } // This is a continuation of AppLogic::Create() and includes the more expensive parts. diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 84c586d79cd..4f735dc9b29 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -74,7 +74,7 @@ namespace winrt::TerminalApp::implementation TerminalApp::ParseCommandlineResult GetParseCommandlineMessage(array_view args); - TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); + til::typed_event SettingsChanged; private: bool _isElevated{ false }; diff --git a/src/cascadia/TerminalApp/ColorPickupFlyout.cpp b/src/cascadia/TerminalApp/ColorPickupFlyout.cpp index f82d86feecc..4bed996a64d 100644 --- a/src/cascadia/TerminalApp/ColorPickupFlyout.cpp +++ b/src/cascadia/TerminalApp/ColorPickupFlyout.cpp @@ -32,7 +32,7 @@ namespace winrt::TerminalApp::implementation { auto button{ sender.as() }; auto rectClr{ button.Background().as() }; - _ColorSelectedHandlers(rectClr.Color()); + ColorSelected.raise(rectClr.Color()); Hide(); } @@ -45,7 +45,7 @@ namespace winrt::TerminalApp::implementation // - void ColorPickupFlyout::ClearColorButton_Click(const IInspectable&, const Windows::UI::Xaml::RoutedEventArgs&) { - _ColorClearedHandlers(); + ColorCleared.raise(); Hide(); } @@ -80,12 +80,12 @@ namespace winrt::TerminalApp::implementation void ColorPickupFlyout::CustomColorButton_Click(const Windows::Foundation::IInspectable&, const Windows::UI::Xaml::RoutedEventArgs&) { auto color = customColorPicker().Color(); - _ColorSelectedHandlers(color); + ColorSelected.raise(color); Hide(); } void ColorPickupFlyout::ColorPicker_ColorChanged(const Microsoft::UI::Xaml::Controls::ColorPicker&, const Microsoft::UI::Xaml::Controls::ColorChangedEventArgs& args) { - _ColorSelectedHandlers(args.NewColor()); + ColorSelected.raise(args.NewColor()); } } diff --git a/src/cascadia/TerminalApp/ColorPickupFlyout.h b/src/cascadia/TerminalApp/ColorPickupFlyout.h index 50979d1b7e3..fa179dc3c6f 100644 --- a/src/cascadia/TerminalApp/ColorPickupFlyout.h +++ b/src/cascadia/TerminalApp/ColorPickupFlyout.h @@ -13,8 +13,8 @@ namespace winrt::TerminalApp::implementation void ClearColorButton_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); void ColorPicker_ColorChanged(const Microsoft::UI::Xaml::Controls::ColorPicker&, const Microsoft::UI::Xaml::Controls::ColorChangedEventArgs& args); - WINRT_CALLBACK(ColorCleared, TerminalApp::ColorClearedArgs); - WINRT_CALLBACK(ColorSelected, TerminalApp::ColorSelectedArgs); + til::event ColorCleared; + til::event ColorSelected; }; } diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp index 34f899bb8da..95dd92380de 100644 --- a/src/cascadia/TerminalApp/CommandPalette.cpp +++ b/src/cascadia/TerminalApp/CommandPalette.cpp @@ -235,7 +235,7 @@ namespace winrt::TerminalApp::implementation { if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) { - _PreviewActionHandlers(*this, actionPaletteItem.Command()); + PreviewAction.raise(*this, actionPaletteItem.Command()); } } else if (_currentMode == CommandPaletteMode::CommandlineMode) @@ -569,7 +569,7 @@ namespace winrt::TerminalApp::implementation void CommandPalette::_moveBackButtonClicked(const Windows::Foundation::IInspectable& /*sender*/, const Windows::UI::Xaml::RoutedEventArgs&) { - _PreviewActionHandlers(*this, nullptr); + PreviewAction.raise(*this, nullptr); _searchBox().Focus(FocusState::Programmatic); const auto previousAction{ _nestedActionStack.GetAt(_nestedActionStack.Size() - 1) }; @@ -714,7 +714,7 @@ namespace winrt::TerminalApp::implementation // All other actions can just be dispatched. if (actionPaletteItem.Command().ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette) { - _DispatchCommandRequestedHandlers(*this, actionPaletteItem.Command()); + DispatchCommandRequested.raise(*this, actionPaletteItem.Command()); } TraceLoggingWrite( @@ -768,7 +768,7 @@ namespace winrt::TerminalApp::implementation { if (const auto tab{ tabPaletteItem.Tab() }) { - _SwitchToTabRequestedHandlers(*this, tab); + SwitchToTabRequested.raise(*this, tab); } } } @@ -796,7 +796,7 @@ namespace winrt::TerminalApp::implementation if (const auto commandLinePaletteItem{ filteredCommand.value().Item().try_as() }) { - _CommandLineExecutionRequestedHandlers(*this, commandLinePaletteItem.CommandLine()); + CommandLineExecutionRequested.raise(*this, commandLinePaletteItem.CommandLine()); _close(); } } @@ -1197,7 +1197,7 @@ namespace winrt::TerminalApp::implementation { Visibility(Visibility::Collapsed); - _PreviewActionHandlers(*this, nullptr); + PreviewAction.raise(*this, nullptr); // Reset visibility in case anchor mode tab switcher just finished. _searchBox().Visibility(Visibility::Visible); diff --git a/src/cascadia/TerminalApp/CommandPalette.h b/src/cascadia/TerminalApp/CommandPalette.h index d0877913c41..e9dd73cbdf4 100644 --- a/src/cascadia/TerminalApp/CommandPalette.h +++ b/src/cascadia/TerminalApp/CommandPalette.h @@ -48,18 +48,18 @@ namespace winrt::TerminalApp::implementation void EnableTabSwitcherMode(const uint32_t startIdx, Microsoft::Terminal::Settings::Model::TabSwitcherMode tabSwitcherMode); void EnableTabSearchMode(); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers); - - TYPED_EVENT(SwitchToTabRequested, winrt::TerminalApp::CommandPalette, winrt::TerminalApp::TabBase); - TYPED_EVENT(CommandLineExecutionRequested, winrt::TerminalApp::CommandPalette, winrt::hstring); - TYPED_EVENT(DispatchCommandRequested, winrt::TerminalApp::CommandPalette, Microsoft::Terminal::Settings::Model::Command); - TYPED_EVENT(PreviewAction, Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::Command); + til::property_changed_event PropertyChanged; + til::typed_event SwitchToTabRequested; + til::typed_event CommandLineExecutionRequested; + til::typed_event DispatchCommandRequested; + til::typed_event PreviewAction; + + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, PrefixCharacter, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, PropertyChanged.raise); private: struct winrt_object_hash diff --git a/src/cascadia/TerminalApp/DebugTapConnection.cpp b/src/cascadia/TerminalApp/DebugTapConnection.cpp index 789c1f21395..25251bd9dfd 100644 --- a/src/cascadia/TerminalApp/DebugTapConnection.cpp +++ b/src/cascadia/TerminalApp/DebugTapConnection.cpp @@ -62,7 +62,7 @@ namespace winrt::Microsoft::TerminalApp::implementation { _outputRevoker = wrappedConnection.TerminalOutput(winrt::auto_revoke, { this, &DebugTapConnection::_OutputHandler }); _stateChangedRevoker = wrappedConnection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*e*/) { - _StateChangedHandlers(*this, nullptr); + StateChanged.raise(*this, nullptr); }); _wrappedConnection = wrappedConnection; } @@ -127,7 +127,7 @@ namespace winrt::Microsoft::TerminalApp::implementation { output.insert(++lfPos, L"\r\n"); } - _TerminalOutputHandlers(output); + TerminalOutput.raise(output); } // Called by the DebugInputTapConnection to print user input @@ -135,7 +135,7 @@ namespace winrt::Microsoft::TerminalApp::implementation { auto clean{ til::visualize_control_codes(str) }; auto formatted{ wil::str_printf(L"\x1b[91m%ls\x1b[m", clean.data()) }; - _TerminalOutputHandlers(formatted); + TerminalOutput.raise(formatted); } // Wire us up so that we can forward input through diff --git a/src/cascadia/TerminalApp/DebugTapConnection.h b/src/cascadia/TerminalApp/DebugTapConnection.h index 34282cb2464..933270df139 100644 --- a/src/cascadia/TerminalApp/DebugTapConnection.h +++ b/src/cascadia/TerminalApp/DebugTapConnection.h @@ -25,9 +25,9 @@ namespace winrt::Microsoft::TerminalApp::implementation void SetInputTap(const Microsoft::Terminal::TerminalConnection::ITerminalConnection& inputTap); - WINRT_CALLBACK(TerminalOutput, winrt::Microsoft::Terminal::TerminalConnection::TerminalOutputHandler); + til::event TerminalOutput; - TYPED_EVENT(StateChanged, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, winrt::Windows::Foundation::IInspectable); + til::typed_event StateChanged; private: void _PrintInput(const hstring& data); diff --git a/src/cascadia/TerminalApp/FilteredCommand.h b/src/cascadia/TerminalApp/FilteredCommand.h index 397151e4d87..e5df7bb018a 100644 --- a/src/cascadia/TerminalApp/FilteredCommand.h +++ b/src/cascadia/TerminalApp/FilteredCommand.h @@ -23,11 +23,11 @@ namespace winrt::TerminalApp::implementation static int Compare(const winrt::TerminalApp::FilteredCommand& first, const winrt::TerminalApp::FilteredCommand& second); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::PaletteItem, Item, _PropertyChangedHandlers, nullptr); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Filter, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(int, Weight, _PropertyChangedHandlers); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::PaletteItem, Item, PropertyChanged.raise, nullptr); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Filter, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(int, Weight, PropertyChanged.raise); private: winrt::TerminalApp::HighlightedText _computeHighlightedName(); diff --git a/src/cascadia/TerminalApp/HighlightedText.h b/src/cascadia/TerminalApp/HighlightedText.h index c9c29e7cbb8..9198919db0f 100644 --- a/src/cascadia/TerminalApp/HighlightedText.h +++ b/src/cascadia/TerminalApp/HighlightedText.h @@ -13,9 +13,9 @@ namespace winrt::TerminalApp::implementation HighlightedTextSegment() = default; HighlightedTextSegment(const winrt::hstring& text, bool isHighlighted); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, TextSegment, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, IsHighlighted, _PropertyChangedHandlers); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, TextSegment, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, IsHighlighted, PropertyChanged.raise); }; struct HighlightedText : HighlightedTextT @@ -23,8 +23,8 @@ namespace winrt::TerminalApp::implementation HighlightedText() = default; HighlightedText(const Windows::Foundation::Collections::IObservableVector& segments); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, Segments, _PropertyChangedHandlers); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, Segments, PropertyChanged.raise); }; } diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp index 0f07af0667c..fac7d2b2b3f 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp @@ -69,18 +69,18 @@ namespace winrt::TerminalApp::implementation void MinMaxCloseControl::_MinimizeClick(const winrt::Windows::Foundation::IInspectable& /*sender*/, const RoutedEventArgs& e) { - _MinimizeClickHandlers(*this, e); + MinimizeClick.raise(*this, e); } void MinMaxCloseControl::_MaximizeClick(const winrt::Windows::Foundation::IInspectable& /*sender*/, const RoutedEventArgs& e) { - _MaximizeClickHandlers(*this, e); + MaximizeClick.raise(*this, e); } void MinMaxCloseControl::_CloseClick(const winrt::Windows::Foundation::IInspectable& /*sender*/, const RoutedEventArgs& e) { - _CloseClickHandlers(*this, e); + CloseClick.raise(*this, e); } void MinMaxCloseControl::SetWindowVisualState(WindowVisualState visualState) diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.h b/src/cascadia/TerminalApp/MinMaxCloseControl.h index 382a33e7a2a..5bda6974f41 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.h +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.h @@ -28,9 +28,9 @@ namespace winrt::TerminalApp::implementation void _CloseClick(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); - TYPED_EVENT(MinimizeClick, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs); - TYPED_EVENT(MaximizeClick, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs); - TYPED_EVENT(CloseClick, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs); + til::typed_event MinimizeClick; + til::typed_event MaximizeClick; + til::typed_event CloseClick; std::shared_ptr> _displayToolTip{ nullptr }; std::optional _lastPressedButton{ std::nullopt }; diff --git a/src/cascadia/TerminalApp/PaletteItem.h b/src/cascadia/TerminalApp/PaletteItem.h index ca265307fe0..f1cea019056 100644 --- a/src/cascadia/TerminalApp/PaletteItem.h +++ b/src/cascadia/TerminalApp/PaletteItem.h @@ -11,10 +11,10 @@ namespace winrt::TerminalApp::implementation public: Windows::UI::Xaml::Controls::IconElement ResolvedIcon(); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, KeyChordText, PropertyChanged.raise); }; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index fccd8b8c5d1..59988d928fc 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1119,7 +1119,7 @@ void Pane::_RestartTerminalRequestedHandler(const winrt::Windows::Foundation::II { return; } - _RestartTerminalRequestedHandlers(shared_from_this()); + RestartTerminalRequested.raise(shared_from_this()); } winrt::fire_and_forget Pane::_playBellSound(winrt::Windows::Foundation::Uri uri) @@ -1194,7 +1194,7 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect } // raise the event with the bool value corresponding to the taskbar flag - _PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar)); + PaneRaiseBell.raise(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar)); } } } @@ -1215,7 +1215,7 @@ void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectabl { f = o.FocusState(); } - _GotFocusHandlers(shared_from_this(), f); + GotFocus.raise(shared_from_this(), f); } // Event Description: @@ -1225,7 +1225,7 @@ void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectabl void Pane::_ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& /* sender */, const RoutedEventArgs& /* args */) { - _LostFocusHandlers(shared_from_this()); + LostFocus.raise(shared_from_this()); } // Method Description: @@ -1237,7 +1237,7 @@ void Pane::_ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectab void Pane::Close() { // Fire our Closed event to tell our parent that we should be removed. - _ClosedHandlers(nullptr, nullptr); + Closed.raise(nullptr, nullptr); } // Method Description: @@ -1465,7 +1465,7 @@ void Pane::UpdateVisuals() // - void Pane::_Focus() { - _GotFocusHandlers(shared_from_this(), FocusState::Programmatic); + GotFocus.raise(shared_from_this(), FocusState::Programmatic); if (const auto& control = GetLastFocusedTerminalControl()) { control.Focus(FocusState::Programmatic); @@ -1582,7 +1582,7 @@ std::shared_ptr Pane::DetachPane(std::shared_ptr pane) // Trigger the detached event on each child detached->WalkTree([](auto pane) { - pane->_DetachedHandlers(pane); + pane->Detached.raise(pane); }); return detached; @@ -1717,7 +1717,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching) // the control. Because Tab is relying on GotFocus to know who the // active pane in the tree is, without this call, _no one_ will be // the active pane any longer. - _GotFocusHandlers(shared_from_this(), FocusState::Programmatic); + GotFocus.raise(shared_from_this(), FocusState::Programmatic); } _UpdateBorders(); @@ -1828,7 +1828,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching) } // Notify the discarded child that it was closed by its parent - closedChild->_ClosedByParentHandlers(); + closedChild->ClosedByParent.raise(); } void Pane::_CloseChildRoutine(const bool closeFirst) diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 7c04bcabc09..e7660d4b70c 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -210,16 +210,16 @@ class Pane : public std::enable_shared_from_this void CollectTaskbarStates(std::vector& states); - WINRT_CALLBACK(ClosedByParent, winrt::delegate<>); - WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler); + til::event> ClosedByParent; + til::event> Closed; using gotFocusArgs = winrt::delegate, winrt::Windows::UI::Xaml::FocusState>; - WINRT_CALLBACK(GotFocus, gotFocusArgs); - WINRT_CALLBACK(LostFocus, winrt::delegate>); - WINRT_CALLBACK(PaneRaiseBell, winrt::Windows::Foundation::EventHandler); - WINRT_CALLBACK(Detached, winrt::delegate>); - WINRT_CALLBACK(RestartTerminalRequested, winrt::delegate>); + til::event GotFocus; + til::event>> LostFocus; + til::event> PaneRaiseBell; + til::event>> Detached; + til::event>> RestartTerminalRequested; private: struct PanePoint; diff --git a/src/cascadia/TerminalApp/SuggestionsControl.cpp b/src/cascadia/TerminalApp/SuggestionsControl.cpp index 742e8222015..902cd06ebb5 100644 --- a/src/cascadia/TerminalApp/SuggestionsControl.cpp +++ b/src/cascadia/TerminalApp/SuggestionsControl.cpp @@ -268,7 +268,7 @@ namespace winrt::TerminalApp::implementation const auto selectedCommand = _filteredActionsView().SelectedItem(); const auto filteredCommand{ selectedCommand.try_as() }; - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"SelectedItem" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"SelectedItem" }); // Make sure to not send the preview if we're collapsed. This can // sometimes fire after we've been closed, which can trigger us to diff --git a/src/cascadia/TerminalApp/SuggestionsControl.h b/src/cascadia/TerminalApp/SuggestionsControl.h index b156ab377c5..77596ce307e 100644 --- a/src/cascadia/TerminalApp/SuggestionsControl.h +++ b/src/cascadia/TerminalApp/SuggestionsControl.h @@ -50,12 +50,12 @@ namespace winrt::TerminalApp::implementation til::typed_event DispatchCommandRequested; til::typed_event PreviewAction; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, PropertyChanged.raise); private: struct winrt_object_hash diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp index 5077067762a..04b7268ba06 100644 --- a/src/cascadia/TerminalApp/TabBase.cpp +++ b/src/cascadia/TerminalApp/TabBase.cpp @@ -53,7 +53,7 @@ namespace winrt::TerminalApp::implementation contextMenuFlyout.Closed([weakThis](auto&&, auto&&) { if (auto tab{ weakThis.get() }) { - tab->_RequestFocusActiveControlHandlers(); + tab->RequestFocusActiveControl.raise(); } }); _AppendCloseMenuItems(contextMenuFlyout); @@ -106,7 +106,7 @@ namespace winrt::TerminalApp::implementation closeTabMenuItem.Click([weakThis](auto&&, auto&&) { if (auto tab{ weakThis.get() }) { - tab->_CloseRequestedHandlers(nullptr, nullptr); + tab->CloseRequested.raise(nullptr, nullptr); } }); closeTabMenuItem.Text(RS_(L"TabClose")); @@ -260,7 +260,7 @@ namespace winrt::TerminalApp::implementation TabViewItem().Tapped([weakThis{ get_weak() }](auto&&, auto&&) { if (auto tab{ weakThis.get() }) { - tab->_RequestFocusActiveControlHandlers(); + tab->RequestFocusActiveControl.raise(); } }); diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h index 108b30a502c..8a0190ad3c3 100644 --- a/src/cascadia/TerminalApp/TabBase.h +++ b/src/cascadia/TerminalApp/TabBase.h @@ -32,23 +32,23 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::TabCloseButtonVisibility CloseButtonVisibility(); void CloseButtonVisibility(Microsoft::Terminal::Settings::Model::TabCloseButtonVisibility visible); - WINRT_CALLBACK(RequestFocusActiveControl, winrt::delegate); + til::event> RequestFocusActiveControl; - WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler); - WINRT_CALLBACK(CloseRequested, winrt::Windows::Foundation::EventHandler); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::event> Closed; + til::event> CloseRequested; + til::property_changed_event PropertyChanged; // The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector. WINRT_PROPERTY(uint32_t, TabViewIndex, 0); // The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector. WINRT_PROPERTY(uint32_t, TabViewNumTabs, 0); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, ReadOnly, _PropertyChangedHandlers, false); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, ReadOnly, PropertyChanged.raise, false); WINRT_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr); - WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, PropertyChanged.raise, nullptr); protected: winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused }; diff --git a/src/cascadia/TerminalApp/TabHeaderControl.cpp b/src/cascadia/TerminalApp/TabHeaderControl.cpp index b695364619f..df337b458dd 100644 --- a/src/cascadia/TerminalApp/TabHeaderControl.cpp +++ b/src/cascadia/TerminalApp/TabHeaderControl.cpp @@ -120,7 +120,7 @@ namespace winrt::TerminalApp::implementation _CloseRenameBox(); if (!_renameCancelled) { - _TitleChangeRequestedHandlers(HeaderRenamerTextBox().Text()); + TitleChangeRequested.raise(HeaderRenamerTextBox().Text()); } } @@ -132,7 +132,7 @@ namespace winrt::TerminalApp::implementation { HeaderRenamerTextBox().Visibility(Windows::UI::Xaml::Visibility::Collapsed); HeaderTextBlock().Visibility(Windows::UI::Xaml::Visibility::Visible); - _RenameEndedHandlers(*this, nullptr); + RenameEnded.raise(*this, nullptr); } } } diff --git a/src/cascadia/TerminalApp/TabHeaderControl.h b/src/cascadia/TerminalApp/TabHeaderControl.h index fd78ec2a31a..f9456ba48e6 100644 --- a/src/cascadia/TerminalApp/TabHeaderControl.h +++ b/src/cascadia/TerminalApp/TabHeaderControl.h @@ -19,14 +19,13 @@ namespace winrt::TerminalApp::implementation bool InRename(); - WINRT_CALLBACK(TitleChangeRequested, TerminalApp::TitleChangeRequestedArgs); + til::event TitleChangeRequested; + til::typed_event<> RenameEnded; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(double, RenamerMaxWidth, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers); - - TYPED_EVENT(RenameEnded, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(double, RenamerMaxWidth, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, PropertyChanged.raise); private: bool _receivedKeyDown{ false }; diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 78a1ead3acd..2228f8234e9 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -148,7 +148,7 @@ namespace winrt::TerminalApp::implementation // Update the taskbar progress as well. We'll raise our own // SetTaskbarProgress event here, to get tell the hosting // application to re-query this value from us. - page->_SetTaskbarProgressHandlers(*page, nullptr); + page->SetTaskbarProgress.raise(*page, nullptr); auto profile = tab->GetFocusedProfile(); page->_UpdateBackground(profile); @@ -164,7 +164,7 @@ namespace winrt::TerminalApp::implementation if (page && tab) { - page->_RaiseVisualBellHandlers(nullptr, nullptr); + page->RaiseVisualBell.raise(nullptr, nullptr); } }); @@ -486,7 +486,7 @@ namespace winrt::TerminalApp::implementation // if the user manually closed all tabs. // Do this only if we are the last window; the monarch will notice // we are missing and remove us that way otherwise. - _LastTabClosedHandlers(*this, winrt::make(!_maintainStateOnTabClose)); + LastTabClosed.raise(*this, winrt::make(!_maintainStateOnTabClose)); } else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast(tabIndex)) { @@ -954,7 +954,7 @@ namespace winrt::TerminalApp::implementation // Raise an event that our title changed if (_settings.GlobalSettings().ShowTitleInTitlebar()) { - _TitleChangedHandlers(*this, tab.Title()); + TitleChanged.raise(*this, tab.Title()); } _updateThemeColors(); diff --git a/src/cascadia/TerminalApp/TabPaletteItem.cpp b/src/cascadia/TerminalApp/TabPaletteItem.cpp index 16a456fae44..e3c550206d6 100644 --- a/src/cascadia/TerminalApp/TabPaletteItem.cpp +++ b/src/cascadia/TerminalApp/TabPaletteItem.cpp @@ -52,7 +52,7 @@ namespace winrt::TerminalApp::implementation // Sometimes nested bindings do not get updated, // thus let's notify property changed on TabStatus when one of its properties changes auto item{ weakThis.get() }; - item->_PropertyChangedHandlers(*item, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"TabStatus" }); + item->PropertyChanged.raise(*item, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"TabStatus" }); }); } } diff --git a/src/cascadia/TerminalApp/TabPaletteItem.h b/src/cascadia/TerminalApp/TabPaletteItem.h index f45a4ed2e15..dc2435ed2e1 100644 --- a/src/cascadia/TerminalApp/TabPaletteItem.h +++ b/src/cascadia/TerminalApp/TabPaletteItem.h @@ -18,7 +18,7 @@ namespace winrt::TerminalApp::implementation return _tab.get(); } - WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers); + WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, PropertyChanged.raise); private: winrt::weak_ref _tab; diff --git a/src/cascadia/TerminalApp/TabRowControl.h b/src/cascadia/TerminalApp/TabRowControl.h index b515ad1c23b..d216dbef711 100644 --- a/src/cascadia/TerminalApp/TabRowControl.h +++ b/src/cascadia/TerminalApp/TabRowControl.h @@ -17,8 +17,8 @@ namespace winrt::TerminalApp::implementation void OnNewTabButtonDrop(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::DragEventArgs& e); void OnNewTabButtonDragOver(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::DragEventArgs& e); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(bool, ShowElevationShield, _PropertyChangedHandlers, false); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(bool, ShowElevationShield, PropertyChanged.raise, false); }; } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index b3563a395b8..608abe0c0db 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -187,7 +187,7 @@ namespace winrt::TerminalApp::implementation } // Inform the host that our titlebar content has changed. - _SetTitleBarContentHandlers(*this, _tabRow); + SetTitleBarContent.raise(*this, _tabRow); // GH#13143 Manually set the tab row's background to transparent here. // @@ -658,7 +658,7 @@ namespace winrt::TerminalApp::implementation // have a tab yet, but will once we're initialized. if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener)) { - _LastTabClosedHandlers(*this, winrt::make(false)); + LastTabClosed.raise(*this, winrt::make(false)); co_return; } else @@ -689,7 +689,7 @@ namespace winrt::TerminalApp::implementation Dispatcher().RunAsync(CoreDispatcherPriority::Low, [weak = get_weak()]() { if (auto self{ weak.get() }) { - self->_InitializedHandlers(*self, nullptr); + self->Initialized.raise(*self, nullptr); } }); } @@ -1623,7 +1623,7 @@ namespace winrt::TerminalApp::implementation if (tab == _GetFocusedTab()) { - _TitleChangedHandlers(*this, newTabTitle); + TitleChanged.raise(*this, newTabTitle); } } @@ -1892,7 +1892,7 @@ namespace winrt::TerminalApp::implementation co_return; } - _QuitRequestedHandlers(nullptr, nullptr); + QuitRequested.raise(nullptr, nullptr); } } @@ -2180,7 +2180,7 @@ namespace winrt::TerminalApp::implementation { request->WindowPosition(dragPoint->to_winrt_point()); } - _RequestMoveContentHandlers(*this, *request); + RequestMoveContent.raise(*this, *request); } bool TerminalPage::_MoveTab(winrt::com_ptr tab, MoveTabArgs args) @@ -2946,7 +2946,7 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::_SetTaskbarProgressHandler(const IInspectable /*sender*/, const IInspectable /*eventArgs*/) { co_await wil::resume_foreground(Dispatcher()); - _SetTaskbarProgressHandlers(*this, nullptr); + SetTaskbarProgress.raise(*this, nullptr); } // Method Description: @@ -2956,7 +2956,7 @@ namespace winrt::TerminalApp::implementation // - args: the arguments specifying how to set the display status to ShowWindow for our window handle void TerminalPage::_ShowWindowChangedHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::ShowWindowArgs args) { - _ShowWindowChangedHandlers(*this, args); + ShowWindowChanged.raise(*this, args); } // Method Description: @@ -3364,7 +3364,7 @@ namespace winrt::TerminalApp::implementation // will let the user hot-reload this setting, but any runtime changes to // the alwaysOnTop setting will be lost. _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); - _AlwaysOnTopChangedHandlers(*this, nullptr); + AlwaysOnTopChanged.raise(*this, nullptr); // Settings AllowDependentAnimations will affect whether animations are // enabled application-wide, so we don't need to check it each time we @@ -3572,7 +3572,7 @@ namespace winrt::TerminalApp::implementation { _isInFocusMode = newInFocusMode; _UpdateTabView(); - _FocusModeChangedHandlers(*this, nullptr); + FocusModeChanged.raise(*this, nullptr); } } @@ -3597,7 +3597,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::ToggleAlwaysOnTop() { _isAlwaysOnTop = !_isAlwaysOnTop; - _AlwaysOnTopChangedHandlers(*this, nullptr); + AlwaysOnTopChanged.raise(*this, nullptr); } // Method Description: @@ -3798,7 +3798,7 @@ namespace winrt::TerminalApp::implementation } _isFullscreen = newFullscreen; _UpdateTabView(); - _FullscreenChangedHandlers(*this, nullptr); + FullscreenChanged.raise(*this, nullptr); } // Method Description: @@ -3817,7 +3817,7 @@ namespace winrt::TerminalApp::implementation return; } _isMaximized = newMaximized; - _ChangeMaximizeRequestedHandlers(*this, nullptr); + ChangeMaximizeRequested.raise(*this, nullptr); } HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection) @@ -3860,7 +3860,7 @@ namespace winrt::TerminalApp::implementation _CreateNewTabFromPane(newPane); // Request a summon of this window to the foreground - _SummonWindowRequestedHandlers(*this, nullptr); + SummonWindowRequested.raise(*this, nullptr); const IInspectable unused{ nullptr }; _SetAsDefaultDismissHandler(unused, unused); @@ -4315,7 +4315,7 @@ namespace winrt::TerminalApp::implementation { WindowRenamer().IsOpen(false); } - _RenameWindowRequestedHandlers(*this, request); + RenameWindowRequested.raise(*this, request); // We can't just use request.Successful here, because the handler might // (will) be handling this asynchronously, so when control returns to // us, this hasn't actually been handled yet. We'll get called back in @@ -5122,7 +5122,7 @@ namespace winrt::TerminalApp::implementation // This will go up to the monarch, who will then dispatch the request // back down to the source TerminalPage, who will then perform a // RequestMoveContent to move their tab to us. - _RequestReceiveContentHandlers(*this, *request); + RequestReceiveContent.raise(*this, *request); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 90caedf4a24..12c20a8056d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -172,33 +172,33 @@ namespace winrt::TerminalApp::implementation uint32_t NumberOfTabs() const; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; // -------------------------------- WinRT Events --------------------------------- - TYPED_EVENT(TitleChanged, IInspectable, winrt::hstring); - TYPED_EVENT(LastTabClosed, IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); - TYPED_EVENT(SetTitleBarContent, IInspectable, winrt::Windows::UI::Xaml::UIElement); - TYPED_EVENT(FocusModeChanged, IInspectable, IInspectable); - TYPED_EVENT(FullscreenChanged, IInspectable, IInspectable); - TYPED_EVENT(ChangeMaximizeRequested, IInspectable, IInspectable); - TYPED_EVENT(AlwaysOnTopChanged, IInspectable, IInspectable); - TYPED_EVENT(RaiseVisualBell, IInspectable, IInspectable); - TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable); - TYPED_EVENT(Initialized, IInspectable, IInspectable); - TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable); - TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs); - TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable); - - TYPED_EVENT(CloseRequested, IInspectable, IInspectable); - TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable); - TYPED_EVENT(QuitRequested, IInspectable, IInspectable); - TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs) - - TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs); - TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs); - - WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr); - WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, FrameBrush, _PropertyChangedHandlers, nullptr); + til::typed_event TitleChanged; + til::typed_event LastTabClosed; + til::typed_event SetTitleBarContent; + til::typed_event FocusModeChanged; + til::typed_event FullscreenChanged; + til::typed_event ChangeMaximizeRequested; + til::typed_event AlwaysOnTopChanged; + til::typed_event RaiseVisualBell; + til::typed_event SetTaskbarProgress; + til::typed_event Initialized; + til::typed_event IdentifyWindowsRequested; + til::typed_event RenameWindowRequested; + til::typed_event SummonWindowRequested; + + til::typed_event CloseRequested; + til::typed_event OpenSystemMenu; + til::typed_event QuitRequested; + til::typed_event ShowWindowChanged; + + til::typed_event RequestMoveContent; + til::typed_event RequestReceiveContent; + + 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); private: friend struct TerminalPageT; // for Xaml to bind events diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 0c3d036623f..b3d37d88a17 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -80,7 +80,7 @@ namespace winrt::TerminalApp::implementation void TerminalTab::_Setup() { _rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) { - _ClosedHandlers(nullptr, nullptr); + Closed.raise(nullptr, nullptr); }); Content(_rootPane->GetRootElement()); @@ -103,7 +103,7 @@ namespace winrt::TerminalApp::implementation _headerControl.RenameEnded([weakThis = get_weak()](auto&&, auto&&) { if (auto tab{ weakThis.get() }) { - tab->_RequestFocusActiveControlHandlers(); + tab->RequestFocusActiveControl.raise(); } }); @@ -596,14 +596,14 @@ namespace winrt::TerminalApp::implementation _rootPane->Closed(_rootClosedToken); auto p = _rootPane; p->WalkTree([](auto pane) { - pane->_DetachedHandlers(pane); + pane->Detached.raise(pane); }); // Clean up references and close the tab _rootPane = nullptr; _activePane = nullptr; Content(nullptr); - _ClosedHandlers(nullptr, nullptr); + Closed.raise(nullptr, nullptr); return p; } @@ -1075,7 +1075,7 @@ namespace winrt::TerminalApp::implementation } // fire an event signaling that our taskbar progress changed. - _TaskbarProgressChangedHandlers(nullptr, nullptr); + TaskbarProgressChanged.raise(nullptr, nullptr); } // Method Description: @@ -1155,7 +1155,7 @@ namespace winrt::TerminalApp::implementation _RecalculateAndApplyReadOnly(); // Raise our own ActivePaneChanged event. - _ActivePaneChangedHandlers(); + ActivePaneChanged.raise(); } // Method Description: @@ -1258,7 +1258,7 @@ namespace winrt::TerminalApp::implementation { // If visual is set, we need to bubble this event all the way to app host to flash the taskbar // In this part of the chain we bubble it from the hosting tab to the page - tab->_TabRaiseVisualBellHandlers(); + tab->TabRaiseVisualBell.raise(); } // Show the bell indicator in the tab header @@ -1497,7 +1497,7 @@ namespace winrt::TerminalApp::implementation // focus from the tab renamer. if (!tab->_headerControl.InRename() && !tab->GetActiveTerminalControl().SearchBoxEditInFocus()) { - tab->_RequestFocusActiveControlHandlers(); + tab->RequestFocusActiveControl.raise(); } } }); diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index d3b03f426fd..ca5901af648 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -96,9 +96,9 @@ namespace winrt::TerminalApp::implementation return _tabStatus; } - WINRT_CALLBACK(ActivePaneChanged, winrt::delegate<>); - WINRT_CALLBACK(TabRaiseVisualBell, winrt::delegate<>); - TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable); + til::event> ActivePaneChanged; + til::event> TabRaiseVisualBell; + til::typed_event TaskbarProgressChanged; private: static constexpr double HeaderRenameBoxWidthDefault{ 165 }; diff --git a/src/cascadia/TerminalApp/TerminalTabStatus.h b/src/cascadia/TerminalApp/TerminalTabStatus.h index 838a909df22..d500de325d3 100644 --- a/src/cascadia/TerminalApp/TerminalTabStatus.h +++ b/src/cascadia/TerminalApp/TerminalTabStatus.h @@ -11,15 +11,15 @@ namespace winrt::TerminalApp::implementation { TerminalTabStatus() = default; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(bool, IsConnectionClosed, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, IsReadOnlyActive, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(bool, IsInputBroadcastActive, _PropertyChangedHandlers); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(bool, IsConnectionClosed, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, IsPaneZoomed, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingActive, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingIndeterminate, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, BellIndicator, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, IsReadOnlyActive, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(uint32_t, ProgressValue, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, IsInputBroadcastActive, PropertyChanged.raise); }; } diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 51e7f758da5..0ca513a064a 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -226,7 +226,7 @@ namespace winrt::TerminalApp::implementation auto args = winrt::make_self(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &TerminalWindow::_OpenSettingsUI)); - _SystemMenuChangeRequestedHandlers(*this, *args); + SystemMenuChangeRequested.raise(*this, *args); TraceLoggingWrite( g_hTerminalAppProvider, @@ -748,7 +748,7 @@ namespace winrt::TerminalApp::implementation void TerminalWindow::_RefreshThemeRoutine() { // Propagate the event to the host layer, so it can update its own UI - _RequestedThemeChangedHandlers(*this, Theme()); + RequestedThemeChanged.raise(*this, Theme()); } // This may be called on a background thread, or the main thread, but almost @@ -767,7 +767,7 @@ namespace winrt::TerminalApp::implementation _root->SetSettings(_settings, true); // Bubble the notification up to the AppHost, now that we've updated our _settings. - _SettingsChangedHandlers(*this, args); + SettingsChanged.raise(*this, args); if (FAILED(args.Result())) { @@ -1239,7 +1239,7 @@ namespace winrt::TerminalApp::implementation // If we're entering Quake Mode from Focus Mode, then this will do nothing // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing _root->SetFocusMode(true); - _IsQuakeWindowChangedHandlers(*this, nullptr); + IsQuakeWindowChanged.raise(*this, nullptr); } } void TerminalWindow::WindowId(const uint64_t& id) @@ -1359,8 +1359,8 @@ namespace winrt::TerminalApp::implementation // PropertyChangedEventArgs will throw. try { - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowName" }); - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowName" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); } CATCH_LOG(); } diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 8eff22ef455..8bf04971afb 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -48,9 +48,9 @@ namespace winrt::TerminalApp::implementation winrt::hstring WindowNameForDisplay() const noexcept; bool IsQuakeWindow() const noexcept; - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, VirtualWorkingDirectory, _PropertyChangedHandlers, L""); + til::property_changed_event PropertyChanged; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, VirtualWorkingDirectory, PropertyChanged.raise, L""); public: // Used for setting the initial CWD, before we have XAML set up for property change notifications. @@ -156,14 +156,17 @@ namespace winrt::TerminalApp::implementation // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. // Usually we'd just do - // WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + // til::property_changed_event PropertyChanged; // // But what we're doing here is exposing the Page's PropertyChanged _as // our own event_. It's a FORWARDED_CALLBACK, essentially. winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); } void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); } - TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme); + til::typed_event RequestedThemeChanged; + til::typed_event IsQuakeWindowChanged; + til::typed_event SystemMenuChangeRequested; + til::typed_event SettingsChanged; private: // If you add controls here, but forget to null them either here or in @@ -230,12 +233,6 @@ namespace winrt::TerminalApp::implementation 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); - TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable); - - TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); - - TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); - FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent); FORWARDED_TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs, _root, RequestReceiveContent); diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index c75795f7bd0..6abc67781f2 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index 157d888f65c..973ade12d63 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -94,7 +94,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // - str: the string to write. void AzureConnection::_WriteStringWithNewline(const std::wstring_view str) { - _TerminalOutputHandlers(str + L"\r\n"); + TerminalOutput.raise(str + L"\r\n"); } // Method description: @@ -110,7 +110,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation catch (const std::exception& runtimeException) { // This also catches the AzureException, which has a .what() - _TerminalOutputHandlers(_colorize(91, til::u8u16(std::string{ runtimeException.what() }))); + TerminalOutput.raise(_colorize(91, til::u8u16(std::string{ runtimeException.what() }))); } catch (...) { @@ -160,13 +160,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _currentInputMode = mode; - _TerminalOutputHandlers(L"> \x1b[92m"); // Make prompted user input green + TerminalOutput.raise(L"> \x1b[92m"); // Make prompted user input green _inputEvent.wait(inputLock, [this, mode]() { return _currentInputMode != mode || _isStateAtOrBeyond(ConnectionState::Closing); }); - _TerminalOutputHandlers(L"\x1b[m"); + TerminalOutput.raise(L"\x1b[m"); if (_isStateAtOrBeyond(ConnectionState::Closing)) { @@ -204,19 +204,19 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation if (_userInput.size() > 0) { _userInput.pop_back(); - _TerminalOutputHandlers(L"\x08 \x08"); // overstrike the character with a space + TerminalOutput.raise(L"\x08 \x08"); // overstrike the character with a space } } else { - _TerminalOutputHandlers(data); // echo back + TerminalOutput.raise(data); // echo back switch (_currentInputMode) { case InputMode::Line: if (data.size() > 0 && gsl::at(data, 0) == UNICODE_CARRIAGERETURN) { - _TerminalOutputHandlers(L"\r\n"); // we probably got a \r, so we need to advance to the next line. + TerminalOutput.raise(L"\r\n"); // we probably got a \r, so we need to advance to the next line. _currentInputMode = InputMode::None; // toggling the mode indicates completion _inputEvent.notify_one(); break; @@ -279,7 +279,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation if (_hOutputThread) { - // Waiting for the output thread to exit ensures that all pending _TerminalOutputHandlers() + // Waiting for the output thread to exit ensures that all pending TerminalOutput.raise() // calls have returned and won't notify our caller (ControlCore) anymore. This ensures that // we don't call a destroyed event handler asynchronously from a background thread (GH#13880). WaitForSingleObject(_hOutputThread.get(), INFINITE); @@ -422,7 +422,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } // Pass the output to our registered event handlers - _TerminalOutputHandlers(_u16Str); + TerminalOutput.raise(_u16Str); break; } case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE: @@ -765,7 +765,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation const auto shellType = _ParsePreferredShellType(settingsResponse); _WriteStringWithNewline(RS_(L"AzureRequestingTerminal")); const auto socketUri = _GetTerminal(shellType); - _TerminalOutputHandlers(L"\r\n"); + TerminalOutput.raise(L"\r\n"); //// Step 8: connecting to said terminal { diff --git a/src/cascadia/TerminalConnection/AzureConnection.h b/src/cascadia/TerminalConnection/AzureConnection.h index f8a47b5009d..afb5d6e8b9c 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.h +++ b/src/cascadia/TerminalConnection/AzureConnection.h @@ -26,7 +26,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation void Resize(uint32_t rows, uint32_t columns); void Close(); - WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler); + til::event TerminalOutput; private: til::CoordType _initialRows{}; diff --git a/src/cascadia/TerminalConnection/BaseTerminalConnection.h b/src/cascadia/TerminalConnection/BaseTerminalConnection.h index 3cc98627c92..62ccd103d79 100644 --- a/src/cascadia/TerminalConnection/BaseTerminalConnection.h +++ b/src/cascadia/TerminalConnection/BaseTerminalConnection.h @@ -17,7 +17,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation return _connectionState; } - TYPED_EVENT(StateChanged, ITerminalConnection, winrt::Windows::Foundation::IInspectable); + til::typed_event StateChanged; protected: template @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } // Dispatch the event outside of lock. #pragma warning(suppress : 26491) // We can't avoid static_cast downcast because this is template magic. - _StateChangedHandlers(*static_cast(this), nullptr); + StateChanged.raise(*static_cast(this), nullptr); return true; } CATCH_FAIL_FAST() diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index d5789a2b3e5..063083ace36 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -415,15 +415,15 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation winrt::hstring failureText{ fmt::format(std::wstring_view{ RS_(L"ProcessFailedToLaunch") }, fmt::format(_errorFormat, static_cast(hr)), _commandline) }; - _TerminalOutputHandlers(failureText); + TerminalOutput.raise(failureText); // If the path was invalid, let's present an informative message to the user if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY)) { winrt::hstring badPathText{ fmt::format(std::wstring_view{ RS_(L"BadPathText") }, _startingDirectory) }; - _TerminalOutputHandlers(L"\r\n"); - _TerminalOutputHandlers(badPathText); + TerminalOutput.raise(L"\r\n"); + TerminalOutput.raise(badPathText); } _transitionToState(ConnectionState::Failed); @@ -442,11 +442,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { // GH#11556 - make sure to format the error code to this string as an UNSIGNED int winrt::hstring exitText{ fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, fmt::format(_errorFormat, status)) }; - _TerminalOutputHandlers(L"\r\n"); - _TerminalOutputHandlers(exitText); - _TerminalOutputHandlers(L"\r\n"); - _TerminalOutputHandlers(RS_(L"CtrlDToClose")); - _TerminalOutputHandlers(L"\r\n"); + TerminalOutput.raise(L"\r\n"); + TerminalOutput.raise(exitText); + TerminalOutput.raise(L"\r\n"); + TerminalOutput.raise(RS_(L"CtrlDToClose")); + TerminalOutput.raise(L"\r\n"); } CATCH_LOG(); } @@ -554,7 +554,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // thread exit as fast as possible by aborting any ongoing writes coming from OpenConsole. CancelSynchronousIo(_hOutputThread.get()); - // Waiting for the output thread to exit ensures that all pending _TerminalOutputHandlers() + // Waiting for the output thread to exit ensures that all pending TerminalOutput.raise() // calls have returned and won't notify our caller (ControlCore) anymore. This ensures that // we don't call a destroyed event handler asynchronously from a background thread (GH#13880). const auto result = WaitForSingleObject(_hOutputThread.get(), 1000); @@ -676,7 +676,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } // Pass the output to our registered event handlers - _TerminalOutputHandlers(_u16Str); + TerminalOutput.raise(_u16Str); } return 0; diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index a1b622081f0..2170b1fbfdf 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -57,7 +57,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation const winrt::guid& guid, const winrt::guid& profileGuid); - WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler); + til::event TerminalOutput; private: static void closePseudoConsoleAsync(HPCON hPC) noexcept; diff --git a/src/cascadia/TerminalConnection/EchoConnection.cpp b/src/cascadia/TerminalConnection/EchoConnection.cpp index 449276dede8..5ac052ad6a5 100644 --- a/src/cascadia/TerminalConnection/EchoConnection.cpp +++ b/src/cascadia/TerminalConnection/EchoConnection.cpp @@ -33,7 +33,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation prettyPrint << wch; } } - _TerminalOutputHandlers(prettyPrint.str()); + TerminalOutput.raise(prettyPrint.str()); } void EchoConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept diff --git a/src/cascadia/TerminalConnection/EchoConnection.h b/src/cascadia/TerminalConnection/EchoConnection.h index 2b7e84a76a0..cfdf4189db4 100644 --- a/src/cascadia/TerminalConnection/EchoConnection.h +++ b/src/cascadia/TerminalConnection/EchoConnection.h @@ -21,8 +21,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation winrt::guid SessionId() const noexcept { return {}; } ConnectionState State() const noexcept { return ConnectionState::Connected; } - WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler); - TYPED_EVENT(StateChanged, ITerminalConnection, IInspectable); + til::event TerminalOutput; + til::typed_event StateChanged; }; } diff --git a/src/cascadia/TerminalConnection/pch.h b/src/cascadia/TerminalConnection/pch.h index 124397ca3b7..afaa1dd5ca8 100644 --- a/src/cascadia/TerminalConnection/pch.h +++ b/src/cascadia/TerminalConnection/pch.h @@ -37,5 +37,6 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalConnectionProvider); #include #include "til.h" +#include #include diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index b9eb79ac3a6..4027e914d8f 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -144,7 +144,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _renderer->SetBackgroundColorChangedCallback([this]() { _rendererBackgroundColorChanged(); }); _renderer->SetFrameColorChangedCallback([this]() { _rendererTabColorChanged(); }); - _renderer->SetRendererEnteredErrorStateCallback([this]() { _RendererEnteredErrorStateHandlers(nullptr, nullptr); }); + _renderer->SetRendererEnteredErrorStateCallback([this]() { RendererEnteredErrorState.raise(nullptr, nullptr); }); THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get())); } @@ -186,7 +186,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation [weakThis = get_weak()]() { if (auto core{ weakThis.get() }; !core->_IsClosing()) { - core->_CursorPositionChangedHandlers(*core, nullptr); + core->CursorPositionChanged.raise(*core, nullptr); } }); @@ -208,7 +208,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation [weakThis = get_weak()](const auto& update) { if (auto core{ weakThis.get() }; !core->_IsClosing()) { - core->_ScrollPositionChangedHandlers(*core, update); + core->ScrollPositionChanged.raise(*core, update); } }); } @@ -244,11 +244,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation _setupDispatcherAndCallbacks(); const auto actualNewSize = _actualFont.GetSize(); // Bubble this up, so our new control knows how big we want the font. - _FontSizeChangedHandlers(*this, winrt::make(actualNewSize.width, actualNewSize.height)); + FontSizeChanged.raise(*this, winrt::make(actualNewSize.width, actualNewSize.height)); // The renderer will be re-enabled in Initialize - _AttachedHandlers(*this, nullptr); + Attached.raise(*this, nullptr); } TerminalConnection::ITerminalConnection ControlCore::Connection() @@ -276,7 +276,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Subscribe to the connection's disconnected event and call our connection closed handlers. _connectionStateChangedRevoker = newConnection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) { - _ConnectionStateChangedHandlers(*this, nullptr); + ConnectionStateChanged.raise(*this, nullptr); }); // Get our current size in rows/cols, and hook them up to @@ -304,7 +304,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (oldState != ConnectionState()) { // rely on the null handling again // send the notification - _ConnectionStateChangedHandlers(*this, nullptr); + ConnectionStateChanged.raise(*this, nullptr); } } @@ -476,14 +476,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (ch == CtrlD) { - _CloseTerminalRequestedHandlers(*this, nullptr); + CloseTerminalRequested.raise(*this, nullptr); return true; } if (ch == Enter) { // Ask the hosting application to give us a new connection. - _RestartTerminalRequestedHandlers(*this, nullptr); + RestartTerminalRequested.raise(*this, nullptr); return true; } } @@ -562,13 +562,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (const auto uri = _terminal->GetHyperlinkAtBufferPosition(_terminal->GetSelectionAnchor()); !uri.empty()) { lock.unlock(); - _OpenHyperlinkHandlers(*this, winrt::make(winrt::hstring{ uri })); + OpenHyperlink.raise(*this, winrt::make(winrt::hstring{ uri })); } else { const auto selectedText = _terminal->GetTextBuffer().GetPlainText(_terminal->GetSelectionAnchor(), _terminal->GetSelectionEnd()); lock.unlock(); - _OpenHyperlinkHandlers(*this, winrt::make(winrt::hstring{ selectedText })); + OpenHyperlink.raise(*this, winrt::make(winrt::hstring{ selectedText })); } return true; } @@ -747,7 +747,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } auto eventArgs = winrt::make_self(newOpacity); - _TransparencyChangedHandlers(*this, *eventArgs); + TransparencyChanged.raise(*this, *eventArgs); } void ControlCore::ToggleShaderEffects() @@ -825,7 +825,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _renderer->TriggerRedrawAll(); } - _HoveredHyperlinkChangedHandlers(*this, nullptr); + HoveredHyperlinkChanged.raise(*this, nullptr); } } @@ -940,7 +940,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _renderer->NotifyPaintFrame(); auto eventArgs = winrt::make_self(Opacity()); - _TransparencyChangedHandlers(*this, *eventArgs); + TransparencyChanged.raise(*this, *eventArgs); _renderer->TriggerRedrawAll(true, true); } @@ -1019,11 +1019,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation _desiredFont.GetFaceName(), _actualFont.GetFaceName()) }; auto noticeArgs = winrt::make(NoticeLevel::Warning, message); - _RaiseNoticeHandlers(*this, std::move(noticeArgs)); + RaiseNotice.raise(*this, std::move(noticeArgs)); } const auto actualNewSize = _actualFont.GetSize(); - _FontSizeChangedHandlers(*this, winrt::make(actualNewSize.width, actualNewSize.height)); + FontSizeChanged.raise(*this, winrt::make(actualNewSize.width, actualNewSize.height)); } // Method Description: @@ -1226,7 +1226,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // when an OSC 52 is emitted. void ControlCore::_terminalCopyToClipboard(std::wstring_view wstr) { - _CopyToClipboardHandlers(*this, winrt::make(winrt::hstring{ wstr })); + CopyToClipboard.raise(*this, winrt::make(winrt::hstring{ wstr })); } // Method Description: @@ -1259,11 +1259,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto& [textData, htmlData, rtfData] = _terminal->RetrieveSelectedTextFromBuffer(singleLine, copyHtml, copyRtf); // send data up for clipboard - _CopyToClipboardHandlers(*this, - winrt::make(winrt::hstring{ textData }, - winrt::to_hstring(htmlData), - winrt::to_hstring(rtfData), - copyFormats)); + CopyToClipboard.raise(*this, + winrt::make(winrt::hstring{ textData }, + winrt::to_hstring(htmlData), + winrt::to_hstring(rtfData), + copyFormats)); return true; } @@ -1487,7 +1487,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Since this can only ever be triggered by output from the connection, // then the Terminal already has the write lock when calling this // callback. - _WarningBellHandlers(*this, nullptr); + WarningBell.raise(*this, nullptr); } // Method Description: @@ -1504,7 +1504,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Since this can only ever be triggered by output from the connection, // then the Terminal already has the write lock when calling this // callback. - _TitleChangedHandlers(*this, winrt::make(winrt::hstring{ wstr })); + TitleChanged.raise(*this, winrt::make(winrt::hstring{ wstr })); } // Method Description: @@ -1533,7 +1533,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (_inUnitTests) [[unlikely]] { - _ScrollPositionChangedHandlers(*this, update); + ScrollPositionChanged.raise(*this, update); } else { @@ -1558,13 +1558,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_terminalTaskbarProgressChanged() { - _TaskbarProgressChangedHandlers(*this, nullptr); + TaskbarProgressChanged.raise(*this, nullptr); } void ControlCore::_terminalShowWindowChanged(bool showOrHide) { auto showWindow = winrt::make_self(showOrHide); - _ShowWindowChangedHandlers(*this, *showWindow); + ShowWindowChanged.raise(*this, *showWindow); } // Method Description: @@ -1649,7 +1649,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // DO NOT call _updateSelectionUI() here. // We don't want to show the markers so manually tell it to clear it. _terminal->SetBlockSelection(false); - _UpdateSelectionMarkersHandlers(*this, winrt::make(true)); + UpdateSelectionMarkers.raise(*this, winrt::make(true)); foundResults->TotalMatches(gsl::narrow(_searcher.Results().size())); foundResults->CurrentMatch(gsl::narrow(_searcher.CurrentMatch())); @@ -1660,7 +1660,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Raise a FoundMatch event, which the control will use to notify // narrator if there was any results in the buffer - _FoundMatchHandlers(*this, *foundResults); + FoundMatch.raise(*this, *foundResults); } Windows::Foundation::Collections::IVector ControlCore::SearchResultRows() @@ -1712,7 +1712,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_rendererWarning(const HRESULT hr) { - _RendererWarningHandlers(*this, winrt::make(hr)); + RendererWarning.raise(*this, winrt::make(hr)); } winrt::fire_and_forget ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle) @@ -1741,18 +1741,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation _lastSwapChainHandle = std::move(duplicatedHandle); // Now bubble the event up to the control. - _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(_lastSwapChainHandle.get()))); + SwapChainChanged.raise(*this, winrt::box_value(reinterpret_cast(_lastSwapChainHandle.get()))); } } void ControlCore::_rendererBackgroundColorChanged() { - _BackgroundColorChangedHandlers(*this, nullptr); + BackgroundColorChanged.raise(*this, nullptr); } void ControlCore::_rendererTabColorChanged() { - _TabColorChangedHandlers(*this, nullptr); + TabColorChanged.raise(*this, nullptr); } void ControlCore::BlinkAttributeTick() @@ -1957,7 +1957,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _renderer->TriggerSelection(); // only show the markers if we're doing a keyboard selection or in mark mode const bool showMarkers{ _terminal->SelectionMode() >= ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Keyboard }; - _UpdateSelectionMarkersHandlers(*this, winrt::make(!showMarkers)); + UpdateSelectionMarkers.raise(*this, winrt::make(!showMarkers)); } void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine) @@ -1990,7 +1990,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_raiseReadOnlyWarning() { auto noticeArgs = winrt::make(NoticeLevel::Info, RS_(L"TermControlReadOnly")); - _RaiseNoticeHandlers(*this, std::move(noticeArgs)); + RaiseNotice.raise(*this, std::move(noticeArgs)); } void ControlCore::_connectionOutputHandler(const hstring& hstr) { @@ -2485,7 +2485,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation co_await winrt::resume_background(); - _CompletionsChangedHandlers(*this, *args); + CompletionsChanged.raise(*this, *args); } void ControlCore::_selectSpan(til::point_span s) { diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 681858a398c..d79df07d1bc 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -252,34 +252,34 @@ namespace winrt::Microsoft::Terminal::Control::implementation // -------------------------------- WinRT Events --------------------------------- // clang-format off - TYPED_EVENT(FontSizeChanged, IInspectable, Control::FontSizeChangedArgs); - - TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs); - TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); - TYPED_EVENT(WarningBell, IInspectable, IInspectable); - TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); - TYPED_EVENT(BackgroundColorChanged, IInspectable, IInspectable); - TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs); - TYPED_EVENT(CursorPositionChanged, IInspectable, IInspectable); - TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable); - TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable); - TYPED_EVENT(HoveredHyperlinkChanged, IInspectable, IInspectable); - TYPED_EVENT(RendererEnteredErrorState, IInspectable, IInspectable); - TYPED_EVENT(SwapChainChanged, IInspectable, IInspectable); - TYPED_EVENT(RendererWarning, IInspectable, Control::RendererWarningArgs); - TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs); - TYPED_EVENT(TransparencyChanged, IInspectable, Control::TransparencyChangedEventArgs); - TYPED_EVENT(ReceivedOutput, IInspectable, IInspectable); - TYPED_EVENT(FoundMatch, IInspectable, Control::FoundResultsArgs); - TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); - TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs); - TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); - TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); - - TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); - TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable); - - TYPED_EVENT(Attached, IInspectable, IInspectable); + til::typed_event FontSizeChanged; + + til::typed_event CopyToClipboard; + til::typed_event TitleChanged; + til::typed_event<> WarningBell; + til::typed_event<> TabColorChanged; + til::typed_event<> BackgroundColorChanged; + til::typed_event ScrollPositionChanged; + til::typed_event<> CursorPositionChanged; + til::typed_event<> TaskbarProgressChanged; + til::typed_event<> ConnectionStateChanged; + til::typed_event<> HoveredHyperlinkChanged; + til::typed_event RendererEnteredErrorState; + til::typed_event<> SwapChainChanged; + til::typed_event RendererWarning; + til::typed_event RaiseNotice; + til::typed_event TransparencyChanged; + til::typed_event<> ReceivedOutput; + til::typed_event FoundMatch; + til::typed_event ShowWindowChanged; + til::typed_event UpdateSelectionMarkers; + til::typed_event OpenHyperlink; + til::typed_event CompletionsChanged; + + til::typed_event<> CloseTerminalRequested; + til::typed_event<> RestartTerminalRequested; + + til::typed_event<> Attached; // clang-format on private: diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 61dda959af8..cb55cca2315 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -52,7 +52,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core->Attached([weakThis = get_weak()](auto&&, auto&&) { if (auto self{ weakThis.get() }) { - self->_AttachedHandlers(*self, nullptr); + self->Attached.raise(*self, nullptr); } }); } @@ -117,7 +117,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlInteractivity::Close() { - _ClosedHandlers(*this, nullptr); + Closed.raise(*this, nullptr); if (_core) { _core->Close(); @@ -230,7 +230,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core->BracketedPasteEnabled()); // send paste event up to TermApp - _PasteFromClipboardHandlers(*this, std::move(args)); + PasteFromClipboard.raise(*this, std::move(args)); } void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState, @@ -307,7 +307,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core->AnchorContextMenu(terminalPosition); auto contextArgs = winrt::make(til::point{ pixelPosition }.to_winrt_point()); - _ContextMenuRequestedHandlers(*this, contextArgs); + ContextMenuRequested.raise(*this, contextArgs); } else { @@ -616,16 +616,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core->UserScrollViewport(viewTop); // _core->ScrollOffset() is now set to newValue - _ScrollPositionChangedHandlers(*this, - winrt::make(_core->ScrollOffset(), - _core->ViewHeight(), - _core->BufferHeight())); + ScrollPositionChanged.raise(*this, + winrt::make(_core->ScrollOffset(), + _core->ViewHeight(), + _core->BufferHeight())); } } void ControlInteractivity::_hyperlinkHandler(const std::wstring_view uri) { - _OpenHyperlinkHandlers(*this, winrt::make(winrt::hstring{ uri })); + OpenHyperlink.raise(*this, winrt::make(winrt::hstring{ uri })); } bool ControlInteractivity::_canSendVTMouseInput(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers) diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index cafd8972c63..81eeaca9067 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -91,13 +91,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation uint64_t Id(); void AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); - TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); - TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); - TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs); - TYPED_EVENT(ContextMenuRequested, IInspectable, Control::ContextMenuRequestedEventArgs); + til::typed_event OpenHyperlink; + til::typed_event PasteFromClipboard; + til::typed_event ScrollPositionChanged; + til::typed_event ContextMenuRequested; - TYPED_EVENT(Attached, IInspectable, IInspectable); - TYPED_EVENT(Closed, IInspectable, IInspectable); + til::typed_event Attached; + til::typed_event Closed; private: // NOTE: _uiaEngine must be ordered before _core. diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp index afede0cf276..03f50060468 100644 --- a/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp @@ -60,7 +60,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void InteractivityAutomationPeer::SignalSelectionChanged() { - _SelectionChangedHandlers(*this, nullptr); + SelectionChanged.raise(*this, nullptr); } // Method Description: @@ -75,7 +75,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void InteractivityAutomationPeer::SignalTextChanged() { - _TextChangedHandlers(*this, nullptr); + TextChanged.raise(*this, nullptr); } // Method Description: @@ -90,12 +90,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void InteractivityAutomationPeer::SignalCursorChanged() { - _CursorChangedHandlers(*this, nullptr); + CursorChanged.raise(*this, nullptr); } void InteractivityAutomationPeer::NotifyNewOutput(std::wstring_view newOutput) { - _NewOutputHandlers(*this, hstring{ newOutput }); + NewOutput.raise(*this, hstring{ newOutput }); } #pragma region ITextProvider diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.h b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h index 863521a7fef..fb3bdfe626d 100644 --- a/src/cascadia/TerminalControl/InteractivityAutomationPeer.h +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h @@ -71,10 +71,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) override; #pragma endregion - TYPED_EVENT(SelectionChanged, IInspectable, IInspectable); - TYPED_EVENT(TextChanged, IInspectable, IInspectable); - TYPED_EVENT(CursorChanged, IInspectable, IInspectable); - TYPED_EVENT(NewOutput, IInspectable, hstring); + til::typed_event SelectionChanged; + til::typed_event TextChanged; + til::typed_event CursorChanged; + til::typed_event NewOutput; private: Windows::UI::Xaml::Automation::Provider::ITextRangeProvider _CreateXamlUiaTextRange(::ITextRangeProvider* returnVal) const; diff --git a/src/cascadia/TerminalControl/SearchBoxControl.cpp b/src/cascadia/TerminalControl/SearchBoxControl.cpp index ea0ba42453a..dbb7bb77045 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.cpp +++ b/src/cascadia/TerminalControl/SearchBoxControl.cpp @@ -35,7 +35,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // to immediately perform the search with the value appearing in the box. if (Visibility() == Visibility::Visible) { - _SearchChangedHandlers(TextBox().Text(), _GoForward(), _CaseSensitive()); + SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); } }); @@ -58,7 +58,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (rect != _contentClipRect) { _contentClipRect = rect; - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"ContentClipRect" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"ContentClipRect" }); } } @@ -72,7 +72,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (y != _openAnimationStartPoint) { _openAnimationStartPoint = y; - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"OpenAnimationStartPoint" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"OpenAnimationStartPoint" }); } } @@ -252,11 +252,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto state = CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift); if (WI_IsFlagSet(state, CoreVirtualKeyStates::Down)) { - _SearchHandlers(TextBox().Text(), !_GoForward(), _CaseSensitive()); + Search.raise(TextBox().Text(), !_GoForward(), _CaseSensitive()); } else { - _SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive()); + Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); } e.Handled(true); } @@ -276,7 +276,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Escape) { - _ClosedHandlers(*this, e); + Closed.raise(*this, e); e.Handled(true); } } @@ -347,7 +347,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // kick off search - _SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive()); + Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); } // Method Description: @@ -368,7 +368,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // kick off search - _SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive()); + Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); } // Method Description: @@ -381,7 +381,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void SearchBoxControl::CloseClick(const winrt::Windows::Foundation::IInspectable& /*sender*/, const RoutedEventArgs& e) { - _ClosedHandlers(*this, e); + Closed.raise(*this, e); } // Method Description: @@ -406,7 +406,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void SearchBoxControl::TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/) { - _SearchChangedHandlers(TextBox().Text(), _GoForward(), _CaseSensitive()); + SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); } // Method Description: @@ -418,7 +418,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void SearchBoxControl::CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/) { - _SearchChangedHandlers(TextBox().Text(), _GoForward(), _CaseSensitive()); + SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); } // Method Description: diff --git a/src/cascadia/TerminalControl/SearchBoxControl.h b/src/cascadia/TerminalControl/SearchBoxControl.h index d186386496c..293f5d0270b 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.h +++ b/src/cascadia/TerminalControl/SearchBoxControl.h @@ -49,10 +49,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); void CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); - WINRT_CALLBACK(PropertyChanged, winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_CALLBACK(Search, SearchHandler); - WINRT_CALLBACK(SearchChanged, SearchHandler); - TYPED_EVENT(Closed, Control::SearchBoxControl, Windows::UI::Xaml::RoutedEventArgs); + til::event Search; + til::event SearchChanged; + til::typed_event Closed; + til::property_changed_event PropertyChanged; private: std::unordered_set _focusableElements; diff --git a/src/cascadia/TerminalControl/SearchBoxControl.xaml b/src/cascadia/TerminalControl/SearchBoxControl.xaml index 3bea176a578..d69bef8a1c2 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.xaml +++ b/src/cascadia/TerminalControl/SearchBoxControl.xaml @@ -1,4 +1,4 @@ -(); - _CurrentCursorPositionHandlers(*this, *cursorArgs); + CurrentCursorPosition.raise(*this, *cursorArgs); const til::point cursorPos{ til::math::flooring, cursorArgs->CurrentPosition() }; const auto actualCanvasWidth{ Canvas().ActualWidth() }; @@ -159,7 +159,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Get Font Info as we use this is the pixel size for characters in the display auto fontArgs = winrt::make_self(); - _CurrentFontInfoHandlers(*this, *fontArgs); + CurrentFontInfo.raise(*this, *fontArgs); const til::size fontSize{ til::math::flooring, fontArgs->FontSize() }; @@ -408,7 +408,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _CompositionCompletedHandlers(text); + CompositionCompleted.raise(text); _activeTextStart = _inputBuffer.size(); diff --git a/src/cascadia/TerminalControl/TSFInputControl.h b/src/cascadia/TerminalControl/TSFInputControl.h index 2285e5bcce8..95a79b96aa8 100644 --- a/src/cascadia/TerminalControl/TSFInputControl.h +++ b/src/cascadia/TerminalControl/TSFInputControl.h @@ -43,9 +43,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation void Close(); // -------------------------------- WinRT Events --------------------------------- - TYPED_EVENT(CurrentCursorPosition, Control::TSFInputControl, Control::CursorPositionEventArgs); - TYPED_EVENT(CurrentFontInfo, Control::TSFInputControl, Control::FontInfoEventArgs); - WINRT_CALLBACK(CompositionCompleted, Control::CompositionCompletedEventArgs); + til::typed_event CurrentCursorPosition; + til::typed_event CurrentFontInfo; + til::event CompositionCompleted; private: void _layoutRequestedHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextLayoutRequestedEventArgs& args); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 5ed6cf6c8ac..fe26a23e967 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -131,7 +131,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation [weakThis = get_weak()]() { if (auto control{ weakThis.get() }; !control->_IsClosing()) { - control->_WarningBellHandlers(*control, nullptr); + control->WarningBell.raise(*control, nullptr); } }); @@ -615,9 +615,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::SendInput(const winrt::hstring& wstr) { // only broadcast if there's an actual listener. Saves the overhead of some object creation. - if (_StringSentHandlers) + if (StringSent) { - _StringSentHandlers(*this, winrt::make(wstr)); + StringSent.raise(*this, winrt::make(wstr)); } RawWriteString(wstr); @@ -867,7 +867,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // // Firing it manually makes sure it does. _BackgroundBrush = RootGrid().Background(); - _PropertyChangedHandlers(*this, Data::PropertyChangedEventArgs{ L"BackgroundBrush" }); + PropertyChanged.raise(*this, Data::PropertyChangedEventArgs{ L"BackgroundBrush" }); _isBackgroundLight = _isColorLight(bg); } @@ -920,7 +920,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // transparency of the titlebar too. if (changed) { - _PropertyChangedHandlers(*this, Data::PropertyChangedEventArgs{ L"BackgroundBrush" }); + PropertyChanged.raise(*this, Data::PropertyChangedEventArgs{ L"BackgroundBrush" }); } } @@ -1020,7 +1020,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } auto noticeArgs = winrt::make(NoticeLevel::Warning, std::move(message)); - control->_RaiseNoticeHandlers(*control, std::move(noticeArgs)); + control->RaiseNotice.raise(*control, std::move(noticeArgs)); } } @@ -1141,7 +1141,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Likewise, run the event handlers outside of lock (they could // be reentrant) - _InitializedHandlers(*this, nullptr); + Initialized.raise(*this, nullptr); return true; } @@ -1166,7 +1166,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // to simply force TSF to clear its text whenever we have input focus. TSFInputControl().ClearBuffer(); - _HidePointerCursorHandlers(*this, nullptr); + HidePointerCursor.raise(*this, nullptr); const auto ch = e.Character(); const auto keyStatus = e.KeyStatus(); @@ -1180,10 +1180,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Broadcast the character to all listeners // only broadcast if there's an actual listener. Saves the overhead of some object creation. - if (_CharSentHandlers) + if (CharSent) { auto charSentArgs = winrt::make(ch, scanCode, modifiers); - _CharSentHandlers(*this, charSentArgs); + CharSent.raise(*this, charSentArgs); } const auto handled = RawWriteChar(ch, scanCode, modifiers); @@ -1452,10 +1452,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Broadcast the key to all listeners // only broadcast if there's an actual listener. Saves the overhead of some object creation. - if (_KeySentHandlers) + if (KeySent) { auto keySentArgs = winrt::make(vkey, scanCode, modifiers, keyDown); - _KeySentHandlers(*this, keySentArgs); + KeySent.raise(*this, keySentArgs); } return RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown); @@ -1518,7 +1518,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _RestorePointerCursorHandlers(*this, nullptr); + RestorePointerCursor.raise(*this, nullptr); _CapturePointer(sender, args); @@ -1573,7 +1573,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _RestorePointerCursorHandlers(*this, nullptr); + RestorePointerCursor.raise(*this, nullptr); const auto ptr = args.Pointer(); const auto point = args.GetCurrentPoint(*this); @@ -1583,7 +1583,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (!_focused && _core.Settings().FocusFollowMouse()) { - _FocusFollowMouseRequestedHandlers(*this, nullptr); + FocusFollowMouseRequested.raise(*this, nullptr); } if (type == Windows::Devices::Input::PointerDeviceType::Mouse || @@ -1699,7 +1699,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _RestorePointerCursorHandlers(*this, nullptr); + RestorePointerCursor.raise(*this, nullptr); const auto point = args.GetCurrentPoint(*this); // GH#10329 - we don't need to handle horizontal scrolls. Only vertical ones. @@ -1993,7 +1993,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _RestorePointerCursorHandlers(*this, nullptr); + RestorePointerCursor.raise(*this, nullptr); _focused = false; @@ -2262,7 +2262,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation autoPeerImpl->Close(); } - _RestorePointerCursorHandlers(*this, nullptr); + RestorePointerCursor.raise(*this, nullptr); _revokers = {}; @@ -2961,9 +2961,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_pasteTextWithBroadcast(const winrt::hstring& text) { // only broadcast if there's an actual listener. Saves the overhead of some object creation. - if (_StringSentHandlers) + if (StringSent) { - _StringSentHandlers(*this, winrt::make(text)); + StringSent.raise(*this, winrt::make(text)); } _core.PasteText(text); } @@ -3032,7 +3032,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // (like ShellExecute pumping our messaging thread...GH#7994) co_await winrt::resume_foreground(Dispatcher()); - _OpenHyperlinkHandlers(*strongThis, args); + OpenHyperlink.raise(*strongThis, args); } // Method Description: @@ -3166,7 +3166,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::ToggleReadOnly() { _core.ToggleReadOnlyMode(); - _ReadOnlyChangedHandlers(*this, winrt::box_value(_core.IsInReadOnlyMode())); + ReadOnlyChanged.raise(*this, winrt::box_value(_core.IsInReadOnlyMode())); } // Method Description: @@ -3174,7 +3174,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::SetReadOnly(const bool readOnlyState) { _core.SetReadOnlyMode(readOnlyState); - _ReadOnlyChangedHandlers(*this, winrt::box_value(_core.IsInReadOnlyMode())); + ReadOnlyChanged.raise(*this, winrt::box_value(_core.IsInReadOnlyMode())); } // Method Description: @@ -3387,7 +3387,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // while it's holding its write lock. If the handlers calls back to some // method on the TermControl on the same thread, and _that_ method calls // to ControlCore, we might be in danger of deadlocking. - _RaiseNoticeHandlers(*this, eventArgs); + RaiseNotice.raise(*this, eventArgs); } Control::MouseButtonState TermControl::GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point) diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index f2cc715b6eb..2a8b28567ec 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -165,7 +165,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation // -------------------------------- WinRT Events --------------------------------- // clang-format off - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; + + til::typed_event OpenHyperlink; + til::typed_event RaiseNotice; + til::typed_event<> HidePointerCursor; + til::typed_event<> RestorePointerCursor; + til::typed_event<> ReadOnlyChanged; + til::typed_event FocusFollowMouseRequested; + til::typed_event Initialized; + til::typed_event<> WarningBell; + til::typed_event KeySent; + til::typed_event CharSent; + til::typed_event StringSent; // UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE // Those attach the handler to the core directly, and will explode if @@ -182,20 +194,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); - TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); - TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs); - TYPED_EVENT(HidePointerCursor, IInspectable, IInspectable); - TYPED_EVENT(RestorePointerCursor, IInspectable, IInspectable); - TYPED_EVENT(ReadOnlyChanged, IInspectable, IInspectable); - TYPED_EVENT(FocusFollowMouseRequested, IInspectable, IInspectable); - TYPED_EVENT(Initialized, Control::TermControl, Windows::UI::Xaml::RoutedEventArgs); - TYPED_EVENT(WarningBell, IInspectable, IInspectable); - TYPED_EVENT(KeySent, IInspectable, Control::KeySentEventArgs); - TYPED_EVENT(CharSent, IInspectable, Control::CharSentEventArgs); - TYPED_EVENT(StringSent, IInspectable, Control::StringSentEventArgs); // clang-format on - WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, PropertyChanged.raise, nullptr); private: friend struct TermControlT; // friend our parent so it can bind private event handlers diff --git a/src/cascadia/TerminalSettingsEditor/Actions.h b/src/cascadia/TerminalSettingsEditor/Actions.h index 9ffe7bf9a05..a79003afbfb 100644 --- a/src/cascadia/TerminalSettingsEditor/Actions.h +++ b/src/cascadia/TerminalSettingsEditor/Actions.h @@ -20,8 +20,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void AddNew_Click(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(Editor::ActionsViewModel, ViewModel, _PropertyChangedHandlers, nullptr); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(Editor::ActionsViewModel, ViewModel, PropertyChanged.raise, nullptr); }; } diff --git a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp index c3f12541369..1d59f7a6c7c 100644 --- a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp @@ -90,14 +90,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation newKeys, // NewKeys _IsNewlyAdded ? hstring{} : _CurrentAction, // OldAction unbox_value(_ProposedAction)) }; // NewAction - _ModifyKeyBindingRequestedHandlers(*this, *args); + ModifyKeyBindingRequested.raise(*this, *args); } void KeyBindingViewModel::CancelChanges() { if (_IsNewlyAdded) { - _DeleteNewlyAddedKeyBindingHandlers(*this, nullptr); + DeleteNewlyAddedKeyBinding.raise(*this, nullptr); } else { @@ -158,13 +158,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // We also have to do this manually because it hasn't been added to the list yet. kbdVM->IsInEditMode(true); // Emit an event to let the page know to update the background of this key binding VM - _UpdateBackgroundHandlers(*this, *kbdVM); + UpdateBackground.raise(*this, *kbdVM); // IMPORTANT: do this _after_ setting IsInEditMode. Otherwise, it'll get deleted immediately // by the PropertyChangedHandler below (where we delete any IsNewlyAdded items) kbdVM->IsNewlyAdded(true); _KeyBindingList.InsertAt(0, *kbdVM); - _FocusContainerHandlers(*this, *kbdVM); + FocusContainer.raise(*this, *kbdVM); } void ActionsViewModel::_KeyBindingViewModelPropertyChangedHandler(const IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args) @@ -187,7 +187,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // This is the view model entry that went into edit mode. // Emit an event to let the page know to move focus to // this VM's container. - _FocusContainerHandlers(*this, senderVM); + FocusContainer.raise(*this, senderVM); } else if (kbdVM.IsNewlyAdded()) { @@ -205,11 +205,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { // Emit an event to let the page know to move focus to // this VM's container. - _FocusContainerHandlers(*this, senderVM); + FocusContainer.raise(*this, senderVM); } // Emit an event to let the page know to update the background of this key binding VM - _UpdateBackgroundHandlers(*this, senderVM); + UpdateBackground.raise(*this, senderVM); } } @@ -231,7 +231,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation const auto newFocusedIndex{ std::clamp(index, 0u, _KeyBindingList.Size() - 1) }; // Emit an event to let the page know to move focus to // this VM's container. - _FocusContainerHandlers(*this, winrt::box_value(newFocusedIndex)); + FocusContainer.raise(*this, winrt::box_value(newFocusedIndex)); } } } diff --git a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h index 673e0d34f24..e78cd946fd8 100644 --- a/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ActionsViewModel.h @@ -61,7 +61,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void AttemptAcceptChanges(); void AttemptAcceptChanges(const Control::KeyChord newKeys); void CancelChanges(); - void DeleteKeyBinding() { _DeleteKeyBindingRequestedHandlers(*this, _CurrentKeys); } + void DeleteKeyBinding() { DeleteKeyBindingRequested.raise(*this, _CurrentKeys); } // ProposedAction: the entry selected by the combo box; may disagree with the settings model. // CurrentAction: the combo box item that maps to the settings model value. @@ -89,9 +89,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsContainerFocused, false); VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsEditButtonFocused, false); VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Media::Brush, ContainerBackground, nullptr); - TYPED_EVENT(ModifyKeyBindingRequested, Editor::KeyBindingViewModel, Editor::ModifyKeyBindingEventArgs); - TYPED_EVENT(DeleteKeyBindingRequested, Editor::KeyBindingViewModel, Terminal::Control::KeyChord); - TYPED_EVENT(DeleteNewlyAddedKeyBinding, Editor::KeyBindingViewModel, IInspectable); + + public: + til::typed_event ModifyKeyBindingRequested; + til::typed_event DeleteKeyBindingRequested; + til::typed_event DeleteNewlyAddedKeyBinding; private: hstring _KeyChordText{}; @@ -105,9 +107,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnAutomationPeerAttached(); void AddNewKeybinding(); + til::typed_event FocusContainer; + til::typed_event UpdateBackground; + WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector, KeyBindingList); - TYPED_EVENT(FocusContainer, IInspectable, IInspectable); - TYPED_EVENT(UpdateBackground, IInspectable, IInspectable); private: bool _AutomationPeerAttached{ false }; diff --git a/src/cascadia/TerminalSettingsEditor/AddProfile.h b/src/cascadia/TerminalSettingsEditor/AddProfile.h index e4837c50851..7bfa0654f20 100644 --- a/src/cascadia/TerminalSettingsEditor/AddProfile.h +++ b/src/cascadia/TerminalSettingsEditor/AddProfile.h @@ -31,16 +31,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void RequestAddNew() { - _AddNewHandlers(winrt::guid{}); + AddNew.raise(winrt::guid{}); } void RequestDuplicate(GUID profile) { - _AddNewHandlers(profile); + AddNew.raise(profile); } - WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr) - WINRT_CALLBACK(AddNew, AddNewArgs); + til::event AddNew; + + WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr); }; struct AddProfile : public HasScrollViewer, AddProfileT @@ -54,9 +55,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void DuplicateClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void ProfilesSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + til::property_changed_event PropertyChanged; WINRT_PROPERTY(Editor::AddProfilePageNavigationState, State, nullptr); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(bool, IsProfileSelected, _PropertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(bool, IsProfileSelected, PropertyChanged.raise, nullptr); }; } diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index d8dc5040de0..85099de045d 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -220,7 +220,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _baseMap.Remove(_AxisKey); _AxisValue = axisValue; _baseMap.Insert(_AxisKey, _AxisValue); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"AxisValue" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"AxisValue" }); } } @@ -231,7 +231,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _baseMap.Remove(_AxisKey); _AxisKey = axisKey; _baseMap.Insert(_AxisKey, _AxisValue); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"AxisKey" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"AxisKey" }); } } @@ -301,7 +301,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _baseMap.Remove(_FeatureKey); _FeatureValue = featureValue; _baseMap.Insert(_FeatureKey, _FeatureValue); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"FeatureValue" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FeatureValue" }); } } @@ -312,7 +312,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _baseMap.Remove(_FeatureKey); _FeatureKey = featureKey; _baseMap.Insert(_FeatureKey, _FeatureValue); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"FeatureKey" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FeatureKey" }); } } @@ -878,7 +878,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation if (_ShowAllFonts != value) { _ShowAllFonts = value; - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); } } @@ -974,16 +974,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation const auto settingName{ args.PropertyName() }; if (settingName == L"CursorShape") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentCursorShape" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"IsVintageCursor" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentCursorShape" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsVintageCursor" }); } else if (settingName == L"DarkColorSchemeName" || settingName == L"LightColorSchemeName") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentColorScheme" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentColorScheme" }); } else if (settingName == L"BackgroundImageStretchMode") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentBackgroundImageStretchMode" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentBackgroundImageStretchMode" }); } else if (settingName == L"BackgroundImageAlignment") { @@ -991,8 +991,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } else if (settingName == L"FontWeight") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentFontWeight" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontWeight" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); } else if (settingName == L"FontFace" || settingName == L"CurrentFontList") { @@ -1001,28 +1001,28 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { _ShowAllFonts = true; } - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentFontFace" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontFace" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" }); } else if (settingName == L"IntenseTextStyle") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" }); } else if (settingName == L"AdjustIndistinguishableColors") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentAdjustIndistinguishableColors" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentAdjustIndistinguishableColors" }); } else if (settingName == L"ShowProportionalFontWarning") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowProportionalFontWarning" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowProportionalFontWarning" }); } // YOU THERE ADDING A NEW APPEARANCE SETTING // Make sure you add a block like // // else if (settingName == L"MyNewSetting") // { - // _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentMyNewSetting" }); + // PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentMyNewSetting" }); // } // // To make sure that changes to the AppearanceViewModel will @@ -1036,19 +1036,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // make sure to send all the property changed events once here // we do this in the case an old appearance was deleted and then a new one is created, // the old settings need to be updated in xaml - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentCursorShape" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"IsVintageCursor" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentColorScheme" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentBackgroundImageStretchMode" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentCursorShape" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsVintageCursor" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentColorScheme" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentBackgroundImageStretchMode" }); _UpdateBIAlignmentControl(static_cast(Appearance().BackgroundImageAlignment())); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentFontWeight" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentFontFace" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentAdjustIndistinguishableColors" }); - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowProportionalFontWarning" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontWeight" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontFace" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentAdjustIndistinguishableColors" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowProportionalFontWarning" }); } } @@ -1150,7 +1150,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // So the TwoWay binding doesn't update on the State --> Slider direction FontWeightSlider().Value(weight); } - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); } } diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index 2080a4740db..72270cd78d7 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -70,7 +70,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation int32_t AxisIndex(); void AxisIndex(int32_t axisIndex); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; private: winrt::hstring _AxisKey; @@ -93,7 +93,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation int32_t FeatureIndex(); void FeatureIndex(int32_t featureIndex); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; private: winrt::hstring _FeatureKey; @@ -205,19 +205,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Windows::Foundation::IInspectable CurrentFontWeight() const; void CurrentFontWeight(const Windows::Foundation::IInspectable& enumEntry); bool IsCustomFontWeight(); + + til::property_changed_event PropertyChanged; + WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontWeightList); GETSET_BINDABLE_ENUM_SETTING(CursorShape, Microsoft::Terminal::Core::CursorStyle, Appearance().CursorShape); GETSET_BINDABLE_ENUM_SETTING(AdjustIndistinguishableColors, Microsoft::Terminal::Core::AdjustTextMode, Appearance().AdjustIndistinguishableColors); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); DEPENDENCY_PROPERTY(Editor::AppearanceViewModel, Appearance); WINRT_PROPERTY(Editor::ProfileViewModel, SourceProfile, nullptr); WINRT_PROPERTY(IHostedInWindow, WindowRoot, nullptr); GETSET_BINDABLE_ENUM_SETTING(BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch, Appearance().BackgroundImageStretchMode); GETSET_BINDABLE_ENUM_SETTING(IntenseTextStyle, Microsoft::Terminal::Settings::Model::IntenseStyle, Appearance().IntenseTextStyle); - WINRT_OBSERVABLE_PROPERTY(bool, ShowProportionalFontWarning, _PropertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(bool, ShowProportionalFontWarning, PropertyChanged.raise, nullptr); private: bool _ShowAllFonts; diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp index 3c79b02165e..fc6a4bc5afe 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp @@ -209,7 +209,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation const auto propertyName{ args.PropertyName() }; if (propertyName == L"Color" || propertyName == L"Name") { - _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"AccessibleName" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"AccessibleName" }); } } diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h index 07a6e768fd9..12b26c160bf 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.h @@ -69,10 +69,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return hstring{ fmt::format(FMT_COMPILE(L"{} RGB({}, {}, {})"), _Name, _Color.R, _Color.G, _Color.B) }; } - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(Windows::UI::Color, Color, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(IInspectable, Tag, _PropertyChangedHandlers); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(Windows::UI::Color, Color, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(IInspectable, Tag, PropertyChanged.raise); private: Windows::UI::Color _color; diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemes.h b/src/cascadia/TerminalSettingsEditor/ColorSchemes.h index 9dbe211dbe7..c8e7786e8ec 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemes.h +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemes.h @@ -21,10 +21,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void AddNew_Click(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); void ListView_PreviewKeyDown(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); - WINRT_PROPERTY(Model::ColorScheme, CurrentColorScheme, nullptr); - WINRT_OBSERVABLE_PROPERTY(Editor::ColorSchemesPageViewModel, ViewModel, _PropertyChangedHandlers, nullptr); + til::property_changed_event PropertyChanged; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_PROPERTY(Model::ColorScheme, CurrentColorScheme, nullptr); + WINRT_OBSERVABLE_PROPERTY(Editor::ColorSchemesPageViewModel, ViewModel, PropertyChanged.raise, nullptr); private: winrt::Windows::UI::Xaml::FrameworkElement::LayoutUpdated_revoker _layoutUpdatedRevoker; diff --git a/src/cascadia/TerminalSettingsEditor/EditColorScheme.h b/src/cascadia/TerminalSettingsEditor/EditColorScheme.h index ae9bc680da0..798c1c96ce5 100644 --- a/src/cascadia/TerminalSettingsEditor/EditColorScheme.h +++ b/src/cascadia/TerminalSettingsEditor/EditColorScheme.h @@ -20,9 +20,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void RenameCancel_Click(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); void NameBox_PreviewKeyDown(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); - WINRT_OBSERVABLE_PROPERTY(Editor::ColorSchemeViewModel, ViewModel, _PropertyChangedHandlers, nullptr); + til::property_changed_event PropertyChanged; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_OBSERVABLE_PROPERTY(Editor::ColorSchemeViewModel, ViewModel, PropertyChanged.raise, nullptr); private: void _RenameCurrentScheme(hstring newName); diff --git a/src/cascadia/TerminalSettingsEditor/EnumEntry.h b/src/cascadia/TerminalSettingsEditor/EnumEntry.h index 7c0278c492f..fe45a583961 100644 --- a/src/cascadia/TerminalSettingsEditor/EnumEntry.h +++ b/src/cascadia/TerminalSettingsEditor/EnumEntry.h @@ -51,8 +51,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return EnumName(); } - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, EnumName, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(winrt::Windows::Foundation::IInspectable, EnumValue, _PropertyChangedHandlers); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, EnumName, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(winrt::Windows::Foundation::IInspectable, EnumValue, PropertyChanged.raise); }; } diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h index 6c4c5c580d8..61a330b70e9 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h @@ -15,8 +15,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(Editor::GlobalAppearanceViewModel, ViewModel, _PropertyChangedHandlers, nullptr); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(Editor::GlobalAppearanceViewModel, ViewModel, PropertyChanged.raise, nullptr); }; } diff --git a/src/cascadia/TerminalSettingsEditor/Interaction.h b/src/cascadia/TerminalSettingsEditor/Interaction.h index fb9db12c8a5..cbb7846c38a 100644 --- a/src/cascadia/TerminalSettingsEditor/Interaction.h +++ b/src/cascadia/TerminalSettingsEditor/Interaction.h @@ -14,8 +14,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(Editor::InteractionViewModel, ViewModel, _PropertyChangedHandlers, nullptr); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(Editor::InteractionViewModel, ViewModel, PropertyChanged.raise, nullptr); }; } diff --git a/src/cascadia/TerminalSettingsEditor/Launch.h b/src/cascadia/TerminalSettingsEditor/Launch.h index 0b656accc05..985be422af3 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.h +++ b/src/cascadia/TerminalSettingsEditor/Launch.h @@ -15,8 +15,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(Editor::LaunchViewModel, ViewModel, _PropertyChangedHandlers, nullptr); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(Editor::LaunchViewModel, ViewModel, PropertyChanged.raise, nullptr); }; } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp index fe50e2a5a29..ec48d7c7530 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp +++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp @@ -467,7 +467,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile; - _OpenJsonHandlers(nullptr, target); + OpenJson.raise(nullptr, target); } void MainPage::OpenJsonKeyDown(const IInspectable& /*sender*/, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& args) @@ -475,7 +475,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation if (args.Key() == VirtualKey::Enter || args.Key() == VirtualKey::Space) { const auto target = args.KeyStatus().IsMenuKeyDown ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile; - _OpenJsonHandlers(nullptr, target); + OpenJson.raise(nullptr, target); } } @@ -622,7 +622,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation }); // Add an event handler for when the user wants to delete a profile. - profile.DeleteProfile({ this, &MainPage::_DeleteProfile }); + profile.DeleteProfileRequested({ this, &MainPage::_DeleteProfile }); return profileNavItem; } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.h b/src/cascadia/TerminalSettingsEditor/MainPage.h index 90719eb09ad..1535c85a33c 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.h +++ b/src/cascadia/TerminalSettingsEditor/MainPage.h @@ -46,7 +46,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Windows::Foundation::Collections::IObservableVector Breadcrumbs() noexcept; - TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget); + til::typed_event OpenJson; private: Windows::Foundation::Collections::IObservableVector _breadcrumbs; diff --git a/src/cascadia/TerminalSettingsEditor/PreviewConnection.cpp b/src/cascadia/TerminalSettingsEditor/PreviewConnection.cpp index 2c33b1b0f4a..6be3a1ddb15 100644 --- a/src/cascadia/TerminalSettingsEditor/PreviewConnection.cpp +++ b/src/cascadia/TerminalSettingsEditor/PreviewConnection.cpp @@ -32,7 +32,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void PreviewConnection::Start() noexcept { // Send the preview text - _TerminalOutputHandlers(fmt::format(PreviewText, _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain)); + TerminalOutput.raise(fmt::format(PreviewText, _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain)); } void PreviewConnection::Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) noexcept diff --git a/src/cascadia/TerminalSettingsEditor/PreviewConnection.h b/src/cascadia/TerminalSettingsEditor/PreviewConnection.h index 979b15d6d86..9c953771349 100644 --- a/src/cascadia/TerminalSettingsEditor/PreviewConnection.h +++ b/src/cascadia/TerminalSettingsEditor/PreviewConnection.h @@ -32,8 +32,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::guid SessionId() const noexcept { return {}; } winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept { return winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Connected; } - WINRT_CALLBACK(TerminalOutput, winrt::Microsoft::Terminal::TerminalConnection::TerminalOutputHandler); - TYPED_EVENT(StateChanged, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, IInspectable); + til::event TerminalOutput; + til::typed_event StateChanged; private: bool _displayPowerlineGlyphs{ false }; diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index 5682ae5cc18..ae91f9ee978 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -398,7 +398,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void ProfileViewModel::DeleteProfile() { auto deleteProfileArgs{ winrt::make_self(Guid()) }; - _DeleteProfileHandlers(*this, *deleteProfileArgs); + DeleteProfileRequested.raise(*this, *deleteProfileArgs); } void ProfileViewModel::SetupAppearances(Windows::Foundation::Collections::IObservableVector schemesList) diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index 2c1d3bdbf0f..2c2762d26e1 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -83,6 +83,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void DeleteUnfocusedAppearance(); bool VtPassthroughAvailable() const noexcept; + til::typed_event DeleteProfileRequested; + VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid); @@ -120,8 +122,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit); GETSET_BINDABLE_ENUM_SETTING(ScrollState, Microsoft::Terminal::Control::ScrollbarState, ScrollState); - TYPED_EVENT(DeleteProfile, Editor::ProfileViewModel, Editor::DeleteProfileEventArgs); - private: Model::Profile _profile; winrt::guid _originalProfileGuid{}; diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index f9f618c8b80..d9c7a95a816 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -40,7 +40,7 @@ namespace Microsoft.Terminal.Settings.Editor Microsoft.Terminal.Settings.Model.TerminalSettings TermSettings { get; }; - event Windows.Foundation.TypedEventHandler DeleteProfile; + event Windows.Foundation.TypedEventHandler DeleteProfileRequested; void SetupAppearances(Windows.Foundation.Collections.IObservableVector schemesList); diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h index 1acd81d647a..2a173cffc74 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h @@ -17,7 +17,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); private: diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h index 0eadfeb6a6e..ad4a779e0cc 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h @@ -22,7 +22,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Editor::IHostedInWindow WindowRoot() { return _windowRoot; }; - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); private: diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.h b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h index 4f711f5bb1e..71cbd8d78de 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Base.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h @@ -24,7 +24,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void Advanced_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void DeleteConfirmation_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + til::property_changed_event PropertyChanged; WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); private: diff --git a/src/cascadia/TerminalSettingsEditor/Rendering.h b/src/cascadia/TerminalSettingsEditor/Rendering.h index 32c101aae0e..dd202b1c7e8 100644 --- a/src/cascadia/TerminalSettingsEditor/Rendering.h +++ b/src/cascadia/TerminalSettingsEditor/Rendering.h @@ -14,8 +14,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(Editor::RenderingViewModel, ViewModel, _PropertyChangedHandlers, nullptr); + til::property_changed_event PropertyChanged; + WINRT_OBSERVABLE_PROPERTY(Editor::RenderingViewModel, ViewModel, PropertyChanged.raise, nullptr); }; } diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp index 9bf3912e3aa..825e79c18e0 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp @@ -100,7 +100,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // When clicked, we dispatch the bound ClearSettingValue event, // resulting in inheriting the setting value from the parent. button.Click([=](auto&&, auto&&) { - _ClearSettingValueHandlers(*this, nullptr); + ClearSettingValue.raise(*this, nullptr); // move the focus to the child control if (const auto& content{ Content() }) diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.h b/src/cascadia/TerminalSettingsEditor/SettingContainer.h index be543681b3c..9fcb2d24efa 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.h +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.h @@ -31,13 +31,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void SetExpanded(bool expanded); + til::typed_event ClearSettingValue; + DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Header); DEPENDENCY_PROPERTY(hstring, HelpText); DEPENDENCY_PROPERTY(hstring, CurrentValue); DEPENDENCY_PROPERTY(bool, HasSettingValue); DEPENDENCY_PROPERTY(bool, StartExpanded); DEPENDENCY_PROPERTY(IInspectable, SettingOverrideSource); - TYPED_EVENT(ClearSettingValue, Editor::SettingContainer, Windows::Foundation::IInspectable); private: static void _InitializeProperties(); diff --git a/src/cascadia/TerminalSettingsEditor/pch.h b/src/cascadia/TerminalSettingsEditor/pch.h index 356a181643e..2e9eba2f51a 100644 --- a/src/cascadia/TerminalSettingsEditor/pch.h +++ b/src/cascadia/TerminalSettingsEditor/pch.h @@ -57,5 +57,6 @@ // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" +#include #include diff --git a/src/cascadia/UnitTests_Control/MockConnection.h b/src/cascadia/UnitTests_Control/MockConnection.h index e8754c8ae96..abbed690952 100644 --- a/src/cascadia/UnitTests_Control/MockConnection.h +++ b/src/cascadia/UnitTests_Control/MockConnection.h @@ -18,7 +18,7 @@ namespace ControlUnitTests void Start() noexcept {}; void WriteInput(const winrt::hstring& data) { - _TerminalOutputHandlers(data); + TerminalOutput.raise(data); } void Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept {} void Close() noexcept {} @@ -26,7 +26,7 @@ namespace ControlUnitTests winrt::guid SessionId() const noexcept { return {}; } winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept { return winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Connected; } - WINRT_CALLBACK(TerminalOutput, winrt::Microsoft::Terminal::TerminalConnection::TerminalOutputHandler); - TYPED_EVENT(StateChanged, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, IInspectable); + til::event TerminalOutput; + til::typed_event StateChanged; }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 7dec3c052e6..8be39b44ca6 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -87,19 +87,19 @@ namespace RemotingUnitTests void Quit() DIE; void AttachContentToWindow(Remoting::AttachRequest) DIE; void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs) DIE; - TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs); - TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs); - TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs); - TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior); - TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, Remoting::GetWindowLayoutArgs); - TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); - TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs); + til::typed_event WindowActivated; + til::typed_event ExecuteCommandlineRequested; + til::typed_event<> IdentifyWindowsRequested; + til::typed_event<> DisplayWindowIdRequested; + til::typed_event RenameRequested; + til::typed_event SummonRequested; + til::typed_event<> ShowNotificationIconRequested; + til::typed_event<> HideNotificationIconRequested; + til::typed_event<> QuitAllRequested; + til::typed_event<> QuitRequested; + til::typed_event GetWindowLayoutRequested; + til::typed_event AttachRequested; + til::typed_event SendContentRequested; }; // Same idea. @@ -121,13 +121,13 @@ namespace RemotingUnitTests void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t, winrt::Windows::Foundation::IReference) DIE; void RequestSendContent(Remoting::RequestReceiveContentArgs) DIE; - TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, Remoting::FindTargetWindowArgs); - TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, Remoting::QuitAllRequestedArgs); - TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, Remoting::WindowRequestedArgs); + til::typed_event FindTargetWindowRequested; + til::typed_event<> ShowNotificationIconRequested; + til::typed_event<> HideNotificationIconRequested; + til::typed_event<> WindowCreated; + til::typed_event<> WindowClosed; + til::typed_event QuitAllRequested; + til::typed_event RequestNewWindow; }; class RemotingTests diff --git a/src/cascadia/UnitTests_TerminalCore/TilWinRtHelpersTests.cpp b/src/cascadia/UnitTests_TerminalCore/TilWinRtHelpersTests.cpp index 6e6eee5f355..b1ec7aded21 100644 --- a/src/cascadia/UnitTests_TerminalCore/TilWinRtHelpersTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TilWinRtHelpersTests.cpp @@ -43,6 +43,8 @@ class TerminalCoreUnitTests::TilWinRtHelpersTests final TEST_METHOD(TestEvent); TEST_METHOD(TestTypedEvent); + + TEST_METHOD(TestPropertyChanged); }; void TilWinRtHelpersTests::TestPropertySimple() @@ -243,3 +245,22 @@ void TilWinRtHelpersTests::TestTypedEvent() VERIFY_ARE_EQUAL(true, handledOne); VERIFY_ARE_EQUAL(true, handledTwo); } + +void TilWinRtHelpersTests::TestPropertyChanged() +{ + auto handler = [&](const auto& /*sender*/, const auto& args) -> void { + VERIFY_ARE_EQUAL(L"BackgroundBrush", args.PropertyName()); + }; + + til::property_changed_event PropertyChanged; + PropertyChanged(handler); + + // We can't actually run this test in our usual unit tests. As you may + // suspect, because the PropertyChanged event is a _XAML_ event, it expects + // to be run on the UI thread. Which, we most definitely don't have here. + // + // At least, this does compile. We use this everywhere, so the scream test is LOUD. + + // winrt::Windows::Foundation::IInspectable mySender{}; + // PropertyChanged.raise(mySender, winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"BackgroundBrush" }); +} diff --git a/src/cascadia/UnitTests_TerminalCore/pch.h b/src/cascadia/UnitTests_TerminalCore/pch.h index 65ba14050a5..f92891dd1dc 100644 --- a/src/cascadia/UnitTests_TerminalCore/pch.h +++ b/src/cascadia/UnitTests_TerminalCore/pch.h @@ -58,6 +58,7 @@ Author(s): #include #include #include +#include #include diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6a0e0ed698d..816e4cda6e4 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1561,5 +1561,5 @@ void AppHost::_handleSendContent(const winrt::Windows::Foundation::IInspectable& // thread. void AppHost::_requestUpdateSettings() { - _UpdateSettingsRequestedHandlers(); + UpdateSettingsRequested.raise(); } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 60b96103124..25cf599af74 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -33,7 +33,7 @@ class AppHost : public std::enable_shared_from_this static void s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& message); - WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate); + til::event> UpdateSettingsRequested; private: std::unique_ptr _window; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index d7934ea3012..ab800163ff9 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -524,7 +524,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { // wparam = 0 indicates the window was deactivated const bool activated = LOWORD(wparam) != 0; - _WindowActivatedHandlers(activated); + WindowActivated.raise(activated); if (_autoHideWindow && !activated) { @@ -552,7 +552,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { // If we clicked in the titlebar, raise an event so the app host can // dispatch an appropriate event. - _DragRegionClickedHandlers(); + DragRegionClicked.raise(); break; } case WM_MENUCHAR: @@ -570,13 +570,13 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { if (wparam == SIZE_RESTORED || wparam == SIZE_MAXIMIZED) { - _WindowVisibilityChangedHandlers(true); - _MaximizeChangedHandlers(wparam == SIZE_MAXIMIZED); + WindowVisibilityChanged.raise(true); + MaximizeChanged.raise(wparam == SIZE_MAXIMIZED); } if (wparam == SIZE_MINIMIZED) { - _WindowVisibilityChangedHandlers(false); + WindowVisibilityChanged.raise(false); if (_isQuakeWindow) { ShowWindow(GetHandle(), SW_HIDE); @@ -609,7 +609,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize } case WM_MOVE: { - _WindowMovedHandlers(); + WindowMoved.raise(); break; } case WM_CLOSE: @@ -617,7 +617,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize // If the user wants to close the app by clicking 'X' button, // we hand off the close experience to the app layer. If all the tabs // are closed, the window will be closed as well. - _WindowCloseButtonClickedHandlers(); + WindowCloseButtonClicked.raise(); return 0; } case WM_MOUSEWHEEL: @@ -651,7 +651,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize const auto wheelDelta = static_cast(HIWORD(wparam)); // Raise an event, so any listeners can handle the mouse wheel event manually. - _MouseScrolledHandlers(real, wheelDelta); + MouseScrolled.raise(real, wheelDelta); return 0; } CATCH_LOG(); @@ -721,12 +721,12 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize auto highBits = wparam & 0xFFF0; if (highBits == SC_RESTORE || highBits == SC_MAXIMIZE) { - _MaximizeChangedHandlers(highBits == SC_MAXIMIZE); + MaximizeChanged.raise(highBits == SC_MAXIMIZE); } if (wparam == SC_RESTORE && _fullscreen) { - _ShouldExitFullscreenHandlers(); + ShouldExitFullscreen.raise(); return 0; } auto search = _systemMenuItems.find(LOWORD(wparam)); @@ -757,7 +757,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize if (isCurrentlyDark != _currentSystemThemeIsDark) { _currentSystemThemeIsDark = isCurrentlyDark; - _UpdateSettingsRequestedHandlers(); + UpdateSettingsRequested.raise(); } } } @@ -797,7 +797,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _AutomaticShutdownRequestedHandlers(); + AutomaticShutdownRequested.raise(); return true; } } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index b0aaf0b0ae3..ba9183847a7 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -72,22 +72,22 @@ class IslandWindow : void UseDarkTheme(const bool v); virtual void UseMica(const bool newValue, const double titlebarOpacity); - WINRT_CALLBACK(DragRegionClicked, winrt::delegate<>); - WINRT_CALLBACK(WindowCloseButtonClicked, winrt::delegate<>); - WINRT_CALLBACK(MouseScrolled, winrt::delegate); - WINRT_CALLBACK(WindowActivated, winrt::delegate); - WINRT_CALLBACK(NotifyNotificationIconPressed, winrt::delegate); - WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate); - WINRT_CALLBACK(NotifyShowNotificationIconContextMenu, winrt::delegate); - WINRT_CALLBACK(NotifyNotificationIconMenuItemSelected, winrt::delegate); - WINRT_CALLBACK(NotifyReAddNotificationIcon, winrt::delegate); - WINRT_CALLBACK(ShouldExitFullscreen, winrt::delegate); - WINRT_CALLBACK(MaximizeChanged, winrt::delegate); - WINRT_CALLBACK(AutomaticShutdownRequested, winrt::delegate); - - WINRT_CALLBACK(WindowMoved, winrt::delegate); - WINRT_CALLBACK(WindowVisibilityChanged, winrt::delegate); - WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate); + til::event> DragRegionClicked; + til::event> WindowCloseButtonClicked; + til::event> MouseScrolled; + til::event> WindowActivated; + til::event> NotifyNotificationIconPressed; + til::event> NotifyWindowHidden; + til::event> NotifyShowNotificationIconContextMenu; + til::event> NotifyNotificationIconMenuItemSelected; + til::event> NotifyReAddNotificationIcon; + til::event> ShouldExitFullscreen; + til::event> MaximizeChanged; + til::event> AutomaticShutdownRequested; + + til::event> WindowMoved; + til::event> WindowVisibilityChanged; + til::event> UpdateSettingsRequested; protected: void ForceResize() diff --git a/src/cascadia/WindowsTerminal/NotificationIcon.cpp b/src/cascadia/WindowsTerminal/NotificationIcon.cpp index 89e865f64ad..25c945f12e2 100644 --- a/src/cascadia/WindowsTerminal/NotificationIcon.cpp +++ b/src/cascadia/WindowsTerminal/NotificationIcon.cpp @@ -216,7 +216,7 @@ void NotificationIcon::MenuItemSelected(const HMENU menu, const UINT menuItemInd args.SummonBehavior().ToggleVisibility(false); args.SummonBehavior().MoveToCurrentDesktop(false); args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); - _SummonWindowRequestedHandlers(args); + SummonWindowRequested.raise(args); return; } } @@ -231,7 +231,7 @@ void NotificationIcon::MenuItemSelected(const HMENU menu, const UINT menuItemInd args.SummonBehavior().ToggleVisibility(false); args.SummonBehavior().MoveToCurrentDesktop(false); args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); - _SummonWindowRequestedHandlers(args); + SummonWindowRequested.raise(args); break; } } @@ -250,7 +250,7 @@ void NotificationIcon::NotificationIconPressed() args.SummonBehavior().MoveToCurrentDesktop(false); args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); args.SummonBehavior().ToggleVisibility(false); - _SummonWindowRequestedHandlers(args); + SummonWindowRequested.raise(args); } // Method Description: diff --git a/src/cascadia/WindowsTerminal/NotificationIcon.h b/src/cascadia/WindowsTerminal/NotificationIcon.h index 3277a1e845f..53a0e23d1e2 100644 --- a/src/cascadia/WindowsTerminal/NotificationIcon.h +++ b/src/cascadia/WindowsTerminal/NotificationIcon.h @@ -27,7 +27,7 @@ class NotificationIcon void ShowContextMenu(const til::point coord, const winrt::Windows::Foundation::Collections::IVectorView& peasants); void MenuItemSelected(const HMENU menu, const UINT menuItemIndex); - WINRT_CALLBACK(SummonWindowRequested, winrt::delegate); + til::event> SummonWindowRequested; private: void _CreateWindow(); diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 4c0f7d8e8c3..ab2e1fed02f 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -31,7 +31,7 @@ void WindowThread::CreateHost() _manager, _peasant); - _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); }); + _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { UpdateSettingsRequested.raise(); }); winrt::init_apartment(winrt::apartment_type::single_threaded); @@ -111,7 +111,7 @@ bool WindowThread::KeepWarm() // state transitions. In this case, the window is actually being woken up. if (msg.message == AppHost::WM_REFRIGERATE) { - _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); }); + _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { UpdateSettingsRequested.raise(); }); // Re-initialize the host here, on the window thread _host->Initialize(); return true; diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index c536e4297aa..d2b0b566c4c 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -25,7 +25,7 @@ class WindowThread : public std::enable_shared_from_this uint64_t PeasantID(); - WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate); + til::event> UpdateSettingsRequested; private: winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 5937e31398d..fae9d5fb50f 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -91,6 +91,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider); #include "til.h" #include "til/mutex.h" +#include "til/winrt.h" #include diff --git a/src/inc/til/winrt.h b/src/inc/til/winrt.h index e5faece3185..f585109747d 100644 --- a/src/inc/til/winrt.h +++ b/src/inc/til/winrt.h @@ -63,7 +63,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" event() = default; winrt::event_token operator()(const ArgsT& handler) { return _handlers.add(handler); } void operator()(const winrt::event_token& token) { _handlers.remove(token); } - operator bool() const noexcept { return _handlers; } + operator bool() const noexcept { return bool(_handlers); } template void raise(auto&&... args) { @@ -72,13 +72,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" winrt::event _handlers; }; - template + template struct typed_event { typed_event() = default; winrt::event_token operator()(const winrt::Windows::Foundation::TypedEventHandler& handler) { return _handlers.add(handler); } void operator()(const winrt::event_token& token) { _handlers.remove(token); } - operator bool() const noexcept { return _handlers; } + operator bool() const noexcept { return bool(_handlers); } template void raise(Arg const&... args) { @@ -87,10 +87,9 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" winrt::event> _handlers; }; #endif -#ifdef WINRT_Windows_UI_Xaml_DataH - - using property_changed_event = event; +#ifdef WINRT_Windows_UI_Xaml_Data_H + using property_changed_event = til::event; // Making a til::observable_property unfortunately doesn't seem feasible. // It's gonna just result in more macros, which no one wants. // diff --git a/src/tools/MonarchPeasantSample/SamplePeasant.cpp b/src/tools/MonarchPeasantSample/SamplePeasant.cpp index 470cd72c9aa..7cc65b43789 100644 --- a/src/tools/MonarchPeasantSample/SamplePeasant.cpp +++ b/src/tools/MonarchPeasantSample/SamplePeasant.cpp @@ -45,7 +45,7 @@ namespace winrt::MonarchPeasantSample::implementation void Peasant::raiseActivatedEvent() { - _WindowActivatedHandlers(*this, nullptr); + WindowActivated.raise(*this, nullptr); } } diff --git a/src/tools/MonarchPeasantSample/SamplePeasant.h b/src/tools/MonarchPeasantSample/SamplePeasant.h index 138619b3cbc..489f7e722e5 100644 --- a/src/tools/MonarchPeasantSample/SamplePeasant.h +++ b/src/tools/MonarchPeasantSample/SamplePeasant.h @@ -16,7 +16,7 @@ namespace winrt::MonarchPeasantSample::implementation void raiseActivatedEvent(); - TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + til::typed_event<> WindowActivated; private: uint64_t _id{ 0 }; diff --git a/src/tools/MonarchPeasantSample/pch.h b/src/tools/MonarchPeasantSample/pch.h index 14e0e0b1c51..29e037ece78 100644 --- a/src/tools/MonarchPeasantSample/pch.h +++ b/src/tools/MonarchPeasantSample/pch.h @@ -22,6 +22,7 @@ // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" +#include "til/winrt.h" #include From 373faf00c9ae8bafe1d9120a990ef409b037384b Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 20 Mar 2024 20:37:03 +0100 Subject: [PATCH 167/603] Fix a ReadConsoleOutputCharacter regression (#16898) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `nLength` parameter of `ReadConsoleOutputCharacterW` indicates the number of columns that should be read. For single-column (narrow) surrogate pairs this previously clipped a trailing character of the returned string. In the major Unicode support update in #13626 surrogate pairs truly got stored as atomic units for the first time. This now meant that a 120 column read with such codepoints resulted in 121 characters. Other parts of conhost still assume UCS2 however, and so this results in the entire read failing. This fixes the issue by turning surrogate pairs into U+FFFD which makes it UCS2 compatible. Closes #16892 ## Validation Steps Performed * Write U+F15C0 and read it back with `ReadConsoleOutputCharacterW` * Read succeeds with a single U+FFFD ✅ --- .github/actions/spelling/expect/expect.txt | 42 ++-------------------- src/host/ft_host/CJK_DbcsTests.cpp | 17 +++++++++ src/host/output.cpp | 7 +++- 3 files changed, 26 insertions(+), 40 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index e4d2d676eb4..32dd37878d7 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -91,7 +91,6 @@ backgrounding backported backstory barbaz -Batang Bazz BBDM bbwe @@ -180,7 +179,6 @@ changelists chaof charinfo CHARSETINFO -chcbpat chh chshdng CHT @@ -198,7 +196,6 @@ cloudconsole cloudvault CLSCTX clsids -CLUSTERMAP cmatrix cmder CMDEXT @@ -214,7 +211,6 @@ codepages codepath coinit colorizing -COLORMATRIX COLORREFs colorschemes colorspec @@ -222,7 +218,6 @@ colortable colortbl colortest colortool -COLR combaseapi comctl commandline @@ -367,11 +362,9 @@ DBGFONTS DBGOUTPUT dbh dblclk -DBlob DColor DCOLORVALUE dcommon -dcompile dcompiler DComposition dde @@ -479,7 +472,6 @@ depersist deprioritized deserializers desktopwindowxamlsource -DESTINATIONNAME devicecode Dext DFactory @@ -539,7 +531,6 @@ DWORDs dwrite dxgi dxgidwm -dxguid dxinterop dxsm dxttbmp @@ -607,7 +598,7 @@ FEEF fesb FFAF FFDE -FFrom +FFFDb fgbg FGCOLOR FGHIJ @@ -719,7 +710,6 @@ GETWAITTOKILLTIMEOUT GETWHEELSCROLLCHARACTERS GETWHEELSCROLLCHARS GETWHEELSCROLLLINES -GFEh Gfun gfx GGI @@ -867,7 +857,6 @@ INLINEPREFIX inproc Inputkeyinfo INPUTPROCESSORPROFILE -inputrc Inputreadhandledata INSERTMODE INTERACTIVITYBASE @@ -908,10 +897,6 @@ KAttrs kawa Kazu kazum -kcub -kcud -kcuf -kcuu kernelbase kernelbasestaging KEYBDINPUT @@ -923,7 +908,6 @@ Keymapping keyscan keystate keyups -khome KILLACTIVE KILLFOCUS kinda @@ -1061,7 +1045,6 @@ MBUTTON MBUTTONDBLCLK MBUTTONDOWN MBUTTONUP -Mbxy mdmerge MDs MEASUREITEM @@ -1086,7 +1069,6 @@ minkernel MINMAXINFO minwin minwindef -Mip MMBB mmcc MMCPL @@ -1123,8 +1105,8 @@ msix msrc MSVCRTD MTSM -munges Munged +munges murmurhash muxes myapplet @@ -1221,7 +1203,6 @@ ntdll ntifs ntlpcapi ntm -nto ntrtl ntstatus NTSYSCALLAPI @@ -1297,7 +1278,6 @@ parentable parms PATCOPY pathcch -Pathto PATTERNID pcat pcb @@ -1456,7 +1436,6 @@ pwsz pythonw Qaabbcc QUERYOPEN -QUESTIONMARK quickedit QUZ QWER @@ -1489,8 +1468,6 @@ READCONSOLE READCONSOLEOUTPUT READCONSOLEOUTPUTSTRING READMODE -reallocs -reamapping rectread redef redefinable @@ -1518,7 +1495,6 @@ repositorypath Requiresx rerasterize rescap -Resequence RESETCONTENT resheader resmimetype @@ -1551,7 +1527,6 @@ RRRGGGBB rsas rtcore RTEXT -RTFTo RTLREADING Rtn ruleset @@ -1699,7 +1674,6 @@ srcsrv SRCSRVTRG srctool srect -srv srvinit srvpipe ssa @@ -1731,7 +1705,6 @@ SUA subcompartment subkeys SUBLANG -subresource subsystemconsole subsystemwindows swapchain @@ -1835,7 +1808,6 @@ tosign touchpad Tpp Tpqrst -tracelog tracelogging traceviewpp trackbar @@ -1846,7 +1818,6 @@ Trd TREX triaged triaging -TRIANGLESTRIP Tribool TRIMZEROHEADINGS trx @@ -1888,8 +1859,8 @@ UINTs ul ulcch uld -uldb uldash +uldb ulwave Unadvise unattend @@ -1900,7 +1871,6 @@ unhosted UNICODETEXT UNICRT Unintense -Uniscribe unittesting unittests unk @@ -1912,7 +1882,6 @@ untextured UPDATEDISPLAY UPDOWN UPKEY -UPSS upss uregex URegular @@ -1996,7 +1965,6 @@ VTRGBTo vtseq vtterm vttest -waitable WANSUNG WANTARROWS WANTTAB @@ -2121,7 +2089,6 @@ wrkstr wrl wrp WRunoff -WScript wsl WSLENV wstr @@ -2156,13 +2123,11 @@ XBUTTONDOWN XBUTTONUP XCast XCENTER -XColors xcopy XCount xdy XEncoding xes -xff XFG XFile XFORM @@ -2171,7 +2136,6 @@ xinchaof xinxinchaof XManifest XMath -XMFLOAT xorg XResource xsi diff --git a/src/host/ft_host/CJK_DbcsTests.cpp b/src/host/ft_host/CJK_DbcsTests.cpp index 68798b700fd..9d96e470276 100644 --- a/src/host/ft_host/CJK_DbcsTests.cpp +++ b/src/host/ft_host/CJK_DbcsTests.cpp @@ -164,6 +164,8 @@ class DbcsTests BEGIN_TEST_METHOD(TestInvalidTrailer) TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method") END_TEST_METHOD() + + TEST_METHOD(TestNarrowSurrogate); }; bool DbcsTests::DbcsTestSetup() @@ -2183,3 +2185,18 @@ void DbcsTests::TestInvalidTrailer() DbcsWriteRead::Verify(expected, output); } + +// The various console APIs that read back from the buffer are generally incompatible with UTF16 and surrogate pairs. +// ReadConsoleOutputCharacterW in particular has a nLength parameter which is a column count but also the buffer size. +// This makes it impossible to reliably return arbitrarily long graphemes per-cell in the output buffer. +// The test ensures that we replace them with U+FFFD which makes the behavior more consistent for the caller. +void DbcsTests::TestNarrowSurrogate() +{ + const auto out = GetStdHandle(STD_OUTPUT_HANDLE); + wchar_t buf[3]; + DWORD read; + + VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleOutputCharacterW(out, L"a\U00010000b", 4, {}, &read)); + VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleOutputCharacterW(out, &buf[0], ARRAYSIZE(buf), {}, &read)); + VERIFY_ARE_EQUAL(std::wstring_view(L"a\U0000FFFDb"), std::wstring_view(&buf[0], read)); +} diff --git a/src/host/output.cpp b/src/host/output.cpp index bd168fce9cd..bf1e0a91040 100644 --- a/src/host/output.cpp +++ b/src/host/output.cpp @@ -222,7 +222,12 @@ std::wstring ReadOutputStringW(const SCREEN_INFORMATION& screenInfo, // Otherwise, add anything that isn't a trailing cell. (Trailings are duplicate copies of the leading.) if (it->DbcsAttr() != DbcsAttribute::Trailing) { - retVal += it->Chars(); + auto chars = it->Chars(); + if (chars.size() > 1) + { + chars = { &UNICODE_REPLACEMENT, 1 }; + } + retVal += chars; } } From ff47e0c25742b2a332645f846e9c88d277ff2856 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 20 Mar 2024 16:52:53 -0400 Subject: [PATCH 168/603] build: roll back to a build container with the 14.38 compiler (#16907) The 14.39 compiler seems pretty busted. Refs #16794 --- build/pipelines/templates-v2/variables-onebranch-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pipelines/templates-v2/variables-onebranch-config.yml b/build/pipelines/templates-v2/variables-onebranch-config.yml index d7180e3e090..7639033c038 100644 --- a/build/pipelines/templates-v2/variables-onebranch-config.yml +++ b/build/pipelines/templates-v2/variables-onebranch-config.yml @@ -1,2 +1,2 @@ variables: - WindowsContainerImage: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' + WindowsContainerImage: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:1.0.02566.28' From febfbebf9b2966590633b0c67776d0750b1ae8d1 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 20 Mar 2024 17:23:11 -0400 Subject: [PATCH 169/603] Remove the "set default terminal" banner (#16873) **Default Terminal**: Everyone who cares to know, knows. Everyone who doesn't, has an annoying bar above all terminals I had to introduce a workaround for the fact that unknown dismissed message keys in `state.json` result in Terminal exploding on launch. :grin: That's tracked in #16874. Closes #11930 (but not in the way you'd expect) --- .../Resources/en-US/Resources.resw | 7 -- src/cascadia/TerminalApp/TerminalPage.cpp | 68 ------------------- src/cascadia/TerminalApp/TerminalPage.h | 3 - src/cascadia/TerminalApp/TerminalPage.xaml | 15 ---- .../ApplicationState.idl | 5 +- .../TerminalSettingsSerializationHelpers.h | 7 +- 6 files changed, 8 insertions(+), 97 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 3ad4bdb73b8..3f91408815f 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -771,19 +771,12 @@ Termination behavior can be configured in advanced profile settings. - - Windows Terminal can be set as the default terminal application in your settings. - Don't show again This Terminal window is running as Admin - - Open Settings - This is a call-to-action hyperlink; it will open the settings. - Suggestions found: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 608abe0c0db..a46544ce60c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -283,8 +283,6 @@ namespace winrt::TerminalApp::implementation _defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor(); } CATCH_LOG(); - - ShowSetAsDefaultInfoBar(); } Windows::UI::Xaml::Automation::Peers::AutomationPeer TerminalPage::OnCreateAutomationPeer() @@ -3862,9 +3860,6 @@ namespace winrt::TerminalApp::implementation // Request a summon of this window to the foreground SummonWindowRequested.raise(*this, nullptr); - const IInspectable unused{ nullptr }; - _SetAsDefaultDismissHandler(unused, unused); - // TEMPORARY SOLUTION // If the connection has requested for the window to be maximized, // manually maximize it here. Ideally, we should be _initializing_ @@ -4046,35 +4041,6 @@ namespace winrt::TerminalApp::implementation } } - // Method Description: - // - Displays a info popup guiding the user into setting their default terminal. - void TerminalPage::ShowSetAsDefaultInfoBar() const - { - if (::winrt::Windows::UI::Xaml::Application::Current().try_as<::winrt::TerminalApp::App>() == nullptr) - { - // Just ignore this in the tests (where the Application::Current() - // is not a TerminalApp::App) - return; - } - if (!CascadiaSettings::IsDefaultTerminalAvailable() || _IsMessageDismissed(InfoBarMessage::SetAsDefault)) - { - return; - } - - // If the user has already configured any terminal for hand-off we - // shouldn't inform them again about the possibility to do so. - if (CascadiaSettings::IsDefaultTerminalSet()) - { - _DismissMessage(InfoBarMessage::SetAsDefault); - return; - } - - if (const auto infoBar = FindName(L"SetAsDefaultInfoBar").try_as()) - { - infoBar.IsOpen(true); - } - } - // Function Description: // - Helper function to get the OS-localized name for the "Touch Keyboard // and Handwriting Panel Service". If we can't open up the service for any @@ -4536,40 +4502,6 @@ namespace winrt::TerminalApp::implementation } } - // Method Description: - // - Persists the user's choice not to show the information bar warning about "Windows Terminal can be set as your default terminal application" - // Then hides this information buffer. - // Arguments: - // - - // Return Value: - // - - void TerminalPage::_SetAsDefaultDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) - { - _DismissMessage(InfoBarMessage::SetAsDefault); - if (const auto infoBar = FindName(L"SetAsDefaultInfoBar").try_as()) - { - infoBar.IsOpen(false); - } - - TraceLoggingWrite(g_hTerminalAppProvider, "SetAsDefaultTipDismissed", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - - _FocusCurrentTab(true); - } - - // Method Description: - // - Dismisses the Default Terminal tip and opens the settings. - void TerminalPage::_SetAsDefaultOpenSettingsHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) - { - if (const auto infoBar = FindName(L"SetAsDefaultInfoBar").try_as()) - { - infoBar.IsOpen(false); - } - - TraceLoggingWrite(g_hTerminalAppProvider, "SetAsDefaultTipInteracted", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - - OpenSettingsUI(); - } - // Method Description: // - Checks whether information bar message was dismissed earlier (in the application state) // Arguments: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 12c20a8056d..5e22760fc67 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -145,7 +145,6 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::TaskbarState TaskbarState() const; void ShowKeyboardServiceWarning() const; - void ShowSetAsDefaultInfoBar() const; winrt::hstring KeyboardServiceDisabledText(); winrt::fire_and_forget IdentifyWindow(); @@ -507,8 +506,6 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const; void _CloseOnExitInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const; void _KeyboardServiceWarningInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const; - void _SetAsDefaultDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); - void _SetAsDefaultOpenSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message); static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message); diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 600ec505c67..c8aa837bf82 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -53,21 +53,6 @@ Click="_CloseOnExitInfoDismissHandler" /> - - - - - - Date: Thu, 21 Mar 2024 09:27:26 -0500 Subject: [PATCH 170/603] Localization Updates - main - 03/21/2024 03:04:53 (#16910) --- .../Resources/de-DE/Resources.resw | 7 - .../Resources/es-ES/Resources.resw | 7 - .../Resources/fr-FR/Resources.resw | 7 - .../Resources/it-IT/Resources.resw | 7 - .../Resources/ja-JP/Resources.resw | 7 - .../Resources/ko-KR/Resources.resw | 7 - .../Resources/pt-BR/Resources.resw | 7 - .../Resources/qps-ploc/Resources.resw | 485 +++++++++--------- .../Resources/qps-ploca/Resources.resw | 485 +++++++++--------- .../Resources/qps-plocm/Resources.resw | 485 +++++++++--------- .../Resources/ru-RU/Resources.resw | 7 - .../Resources/zh-CN/Resources.resw | 7 - .../Resources/zh-TW/Resources.resw | 7 - 13 files changed, 717 insertions(+), 808 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw index a9a1418f45c..f99341de170 100644 --- a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw @@ -766,19 +766,12 @@ Das Beendigungsverhalten kann in den erweiterten Profileinstellungen konfiguriert werden. - - Windows-Terminal kann in Ihren Einstellungen als standardmäßige Terminalanwendung festgelegt werden. - Nicht mehr anzeigen Dieses Terminalfenster wird als Administrator ausgeführt. - - Einstellungen öffnen - This is a call-to-action hyperlink; it will open the settings. - Gefundene Vorschläge: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw index de6422a9786..7eaf5023fd7 100644 --- a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw @@ -763,19 +763,12 @@ El comportamiento de finalización se puede configurar en la configuración avanzada del perfil. - - Terminal Windows se puede establecer como la aplicación de terminal predeterminada en la configuración. - No volver a mostrar Esta ventana de terminal se está ejecutando como administrador - - Abrir Configuración - This is a call-to-action hyperlink; it will open the settings. - Sugerencias encontradas: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw index 76e4930f0ff..3f5a403a79a 100644 --- a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw @@ -763,19 +763,12 @@ Le comportement d’arrêt peut être configuré dans les paramètres de profil avancés. - - Terminal Windows peut être défini comme application de terminal par défaut dans vos paramètres. - Ne plus afficher Cette fenêtre de terminal s’exécute en tant qu’Administrateur - - Ouvrir les paramètres - This is a call-to-action hyperlink; it will open the settings. - {0} suggestions trouvées {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw index 2e10ffd0952..fb7b04cd57d 100644 --- a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw @@ -763,19 +763,12 @@ È possibile configurare il comportamento di terminazione nelle impostazioni avanzate del profilo. - - Terminale Windows può essere impostato come applicazione terminale predefinita nelle impostazioni. - Non mostrare più questo messaggio Questa finestra del terminale è in esecuzione come amministratore - - Apri Impostazioni - This is a call-to-action hyperlink; it will open the settings. - Suggerimenti trovati: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw index 43c8ff9e04b..48db8a0fd68 100644 --- a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw @@ -764,19 +764,12 @@ 終了動作は、プロファイルの詳細設定で構成できます。 - - Windows ターミナルは、設定で既定のターミナル アプリケーションとして設定できます。 - 今後表示しない このターミナル ウィンドウは管理者として実行されています - - 設定を開く - This is a call-to-action hyperlink; it will open the settings. - 候補が見つかりました: {0} 件 {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw index 7e56ede9497..820db0c8fed 100644 --- a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw @@ -763,19 +763,12 @@ 고급 프로필 설정에서 종료 동작을 구성할 수 있습니다. - - Windows 터미널 설정에서 기본 터미널 응용 프로그램으로 설정할 수 있습니다. - 다시 표시 안 함 이 터미널 창이 관리자 권한으로 실행되고 있습니다. - - 설정 열기 - This is a call-to-action hyperlink; it will open the settings. - 찾은 제안: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw index 99e21f6fd1f..2ba06ec576d 100644 --- a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw @@ -763,19 +763,12 @@ O comportamento de término pode ser configurado nas configurações avançadas do perfil. - - Terminal do Windows pode ser definido como o aplicativo de terminal padrão em suas configurações. - Não mostra de novo Esta janela do Terminal está funcionando como Administrador - - Abrir Configurações - This is a call-to-action hyperlink; it will open the settings. - Sugestões encontradas: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw index 9b79e55b561..4a3e99ee3ff 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw @@ -118,148 +118,148 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Şėťŧіηĝѕ ¢ǿΰľď ʼnσŧ ъē ŀоαðзđ ƒŕôм ƒïłê. Сђěćĸ ƒǿŕ šулŧå× ёřґοгş, įήćļůđíʼnğ ťяαΐľίήğ ĉômmāš. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Şĕţţіⁿģѕ ςŏûℓđ ʼnθт в℮ ℓòăδēδ ƒřσm ƒіℓε. Çћέĉк ƒòя şŷиτåж эřѓόґş, ΐлсŀµðĭηġ ŧŕàįŀїиĝ ċõmмåş. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Сθŭļď пǿţ ƒīпđ ŷŏúг δєƒǻůŀť ρřŏƒΐļę ій ýŏűя ĺīѕŧ őƒ ряσƒïℓëş - ŭŝĩʼnğ ťнз ƒìŕšτ φѓöƒїℓё. Сħëċķ ťθ маķë ŝύґè ŧћë "defaultProfile" mãтçĥεş τнę ĢŨÎĎ òƒ ǿиé оƒ ÿøųŕ φгоƒìļéş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ćǿΰľđ πöт ƒîηđ уоџř đєƒаџļт φгóƒíĺέ įń γσΰґ ľϊѕţ øƒ φřбƒїļēś - ųŝĩηġ тђę ƒίяśт φґθƒĩĺē. €ђéćκ ťб máќë ѕцřз ťħè "defaultProfile" mǻτςĥéѕ ţђè ĞÚІĎ ôƒ õňë òƒ ÿбμř ряǿƒїļĕѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"defaultProfile\""} - ₣ǿΰʼnđ mųĺŧιρĺě ргöƒїℓęš щϊтħ ţђє ѕάмë ĢЏΊĎ ĩń ÿόůґ ŝεтŧĭлģş ƒíļе - įģиόяīπģ đυφļīčâťзѕ. Мãќĕ ŝúŕ℮ еάсĥ φяòƒĩℓę'ѕ ĜЏĪĎ íş űņíqцэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣õůήδ мџŀŧĭрłè рѓőƒιĺéŝ ŵїτђ τне šаме ĢŬΪĎ ιή ŷôûя şёţţïňģŝ ƒïļē - ĩĝпоѓĩлğ đűρļіċáť℮ş. Μªќ℮ şúѓё ėǻςн ρґőƒîłè'š ĢЦЇĐ ιś ũŋĩqυέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - ₣σцηδ ǻ φѓθƒίłε ŵīŧћ âⁿ īπνâŀϊδ "colorScheme". Ðëƒąŭłтійğ ťђàť φřőƒįĺё τό тђę δěƒαūļт ¢ōℓόřŝ. Макз šūгê тħªť ώĥέл ѕěттΐñğ â "colorScheme", ťĥэ νªℓùέ mάτćђеš ŧĥĕ "name" бƒ ā ċôĺöг ѕĉђęmз įη τђę "schemes" łíšţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣ŏџŋð ä ρѓõƒіℓę шîţћ ǻй īпνªŀīð "colorScheme". Đëƒαυľτîпĝ ŧђãť ρřоƒíĺě ŧő тнë ðĕƒãůľŧ çōļóяş. Μâĸè śűřė τћàŧ ώнēη şеŧťįⁿğ д "colorScheme", ţћé νáℓūë máτсħěş ŧнє "name" ōƒ á сóℓöѓ ščнèмë їп тħė "schemes" łîšť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - Νб ρяöƒΐłēѕ ẁèŗё ƒбύʼnð îń ўθůг ѕėţтîпĝš. !!! !!! !!! !!! + Пò φгŏƒїĺèѕ ẁєґє ƒοúиđ ΐņ ўøûя šзŧţìŋġѕ. !!! !!! !!! !!! - Àĺŀ ρґбƒіłεѕ щзяє ђїđδзʼn įň ýòυѓ šэτţĭήĝś. Ϋóũ mцşť ћàν℮ αť ŀèàşŧ öлě ⁿŏń-ђīδδéη φŗθƒìℓέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ãļł рŗǿƒíŀέŝ ωėŗě ћіďđέʼn ιⁿ ўòüѓ şėţтΐйĝş. Υōυ мûŝт ĥªνэ àτ ľєäѕţ óňě иôŋ-ђïδđěń φŗõƒΐľé. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Şèťŧϊņģś сõüℓđ ηбţ ъέ яεłôåδěđ ƒŕόм ƒįļě. Çнэск ƒσя śÿηтå× ēřŕόѓŝ, іпċŀμðîʼnğ тŗǻϊĺīηĝ ¢ǿммãš. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŝęťтįлġś çǿµŀđ лόţ ьэ яēℓбаďêđ ƒгǿм ƒīľē. Ċћєсќ ƒοг ѕγŋţαх έŗгόѓś, ïπċℓύδϊņĝ ťгąîľϊиĝ čòммªŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ťēмφőгάřĩℓу υѕїήģ τħє Ẅĩиδõώѕ Τёřмίпāľ ďēƒάυĺτ š℮τтĭńġѕ. !!! !!! !!! !!! !!! ! + Ŧěmрθřǻŕĩℓÿ џśîŋģ ţĥз Ẁĭņðбшѕ Ŧзŗмìήâĺ ðėƒàцľŧ ŝěτтĭņģŝ. !!! !!! !!! !!! !!! ! - ₣áίĺėđ ţō ľбаð śэŧťίлğş !!! !!! + ₣дīłėδ τö ĺōáđ š℮τтіňģś !!! !!! - Ëηςσϋлŧëŗėδ эŕřоґš ωĥιļ℮ ℓǿάδϊŋğ ūŝēŗ śετťΐπĝś !!! !!! !!! !!! ! + Έлςöůлťëяèδ éґѓőґş ẁђīļé łσǻđιπģ џśёґ ѕ℮ŧтíⁿĝş !!! !!! !!! !!! ! - ΩΚ + ΘК - Ẅàřñíⁿģ: !! + Ŵāŕиĩпģ: !! - Ťнé "{0}" ιŝп'ŧ ŕμňήįŋģ ôń ỳθũŕ mªčђįпέ. Ţћіš čǻń ргëνзⁿţ ťĥε Τеŕmіήáĺ ƒŗøm ґêçэіνĩиģ ќ℮ÿъōåŗδ ìŋφџţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ťĥє "{0}" įšή'ŧ řΰňηіпğ θл ŷбυŕ мāĉћіņë. Τĥίѕ ćªñ рŕзνзπт τнέ Τёřmîпǻĺ ƒŗóм ѓéčêîνîñģ ķёŷъǿãŗð ĩⁿφµт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the OS-localized name of the TabletInputService - ÕК + ÒĶ - ₣ªïļеδ тő ŗεĺòãδ şетţіŋģѕ !!! !!! ! + ₣αĭℓэđ τǿ гэľоāδ śэŧŧįηģš !!! !!! ! https://go.microsoft.com/fwlink/?linkid=2125419 {Locked}This is a FWLink, so it will be localized with the fwlink tool - Αьóμţ ! + Άвòűт ! - ₣ēëďьдćк !! + ₣ээđъàċк !! - Ѕēţťїñğś !! + Ŝęťţĩπģş !! - Ćäπςèŀ ! + Сåήċēĺ ! - Ćļбŝĕ ªľľ !!! + Ćłőŝě áľļ !!! - Qύîτ ! + Qûіŧ ! - Đô ÿòû шªñť τō ςŀθšě ǻľĺ τªвś? !!! !!! !!! + Đŏ уǿü ẃάñт тő ¢ľõśé ăℓℓ ŧаъś? !!! !!! !!! - Μµľťιφļė ρаńêѕ !!! ! + Мũļťįρľê φāńεš !!! ! - Çĺòšĕ... !! + Çŀŏѕё... !! - €łǿѕē Ťäьş τб τнё Ѓΐĝђτ !!! !!! + Čļöѕέ Ţàьś тø ťħě Гïĝнţ !!! !!! - Çľόśĕ Ωţђєŗ Ťǻьś !!! ! + Ĉļőşе Öŧђεŗ Ţдьŝ !!! ! - Ĉĺõŝĕ Τàъ !!! + Сļŏšè Τàв !!! - Ĉłŏşё Ρаπє !!! + Ćľοšэ Ρàņε !!! - Šφļΐт Ťάь !!! + Šφľîŧ Ţǻь !!! - Šφľίţ Ρªńе !!! + Şрļĭţ Ραŋè !!! - Шеь Ѕ℮âґ¢ĥ !!! + Щēь Ŝêαřçĥ !!! - Çοℓõґ... !! + Ćбłöř... !! - Çūŝŧσm... !!! + Сџşţбм... !!! - Ŕĕšęτ ! + Ŕēśёť ! - Яěňämě Ťαв !!! + Γёŋάmē Τаъ !!! - Ðüрĺíсąтз Ťáь !!! + Đύφļϊçåţέ Ţάъ !!! - ₣οüⁿδ ά ρѓőƒіĺз шιтћ аή îйνåℓīď "backgroundImage". Ðєƒâŭŀťïʼnģ ŧĥäτ рřŏƒīℓë τô ћãνё ñō ьàĉќġяοµπď îмǻġё. Μāκе ŝύґé ŧнàτ ẁћзή šĕτťійğ ά "backgroundImage", ťħē νдļûě íş ä νåĺϊđ ƒìℓę ραтħ ŧб ãή їmаĝē. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ₣σůήđ а рřòƒïłè щíţħ ªŋ îŋνāℓіď "backgroundImage". Ďєƒªϋĺţįпĝ ŧħåť рřθƒίľē ţó нąνê ńǿ ъàċкğѓóūñď îмåğэ. Мàκě ŝμяз ťнªţ ẁĥėл šęτŧΐñġ á "backgroundImage", тђε νáľΰє їś α νдŀĩδ ƒïļë φäŧħ τö ãп ϊмªġę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"backgroundImage\""} - ₣øμηđ à φŗοƒïľë ŵіŧĥ åň îʼnνăľΐđ "icon". Ðзƒăϋľţĭņġ тĥãт рŗǿƒιℓë тŏ ђǻνê иŏ īсŏń. Мàĸę śϋгë τħáτ ωĥ℮ή ѕěťτΐήĝ åŋ "icon", тђĕ νªļцĕ īş α νάłįδ ƒįŀĕ φăτħ τо аη імäĝз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣όūпđ ǻ ргõƒįļє ωĩţђ ªň ϊήνåľīď "icon". Đêƒàùĺţíηģ тнаŧ φřòƒїℓє ţб ħдνĕ ŋò ĭćбñ. Μαќě śüяê τћåţ ωĥëη śзťŧĭηģ áй "icon", ţħє νàĺųэ ίѕ ǻ ναℓїđ ƒíľě φǻτĥ ťθ ăŋ ĭmǻģε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. - Ẁâřήîņĝŝ ώёѓè ƒθϋňđ ωђĭĺё φàŕşïпğ γоúг κëўьĭńδįиģš: !!! !!! !!! !!! !!! + Щдřńіńġš ωεяέ ƒöџήδ ẁнĭŀè ρąґśîήġ γôµґ кęуъїñďϊņģś: !!! !!! !!! !!! !!! - • ₣õųŋð α ķęŷьіʼnδіπġ шϊтђ тŏõ мǻлý ŝτŗΐйģŝ ƒог ťĥě "keys" åřгãў. Ťћ℮ŕ℮ ŝћòџℓđ òиļý ъ℮ ǿлέ ѕтѓїńģ νăľūе ιñ тħě "keys" âŗґάÿ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + • ₣őûńđ å κєÿьΐпđĩŋģ шíţћ ŧøо mαиў śτřιñĝŝ ƒôг ŧђē "keys" дřгãỳ. Ţнέŕê śћőũŀδ όŋłγ ъę òⁿё śťŗΐņĝ νáℓΰê įη ŧће "keys" ªŕѓдý. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"keys\"","•"} This glyph is a bullet, used in a bulleted list. - • ₣аĭļєδ ţô рąяşё дľĺ šŭвçõммâиðŝ θƒ ηеśŧĕđ ςóммàиď. !!! !!! !!! !!! !!! + • ₣αιľęδ τō ρāřşέ αℓļ şũвçőммāпðŝ òƒ ʼnęşťёđ ćőмmáйδ. !!! !!! !!! !!! !!! - • ₣оûиð α ќěÿъϊηδīпĝ ţћăť ẁâš mîşŝįŋğ ă řëqцїгéδ рäŕªmзť℮ř νǻℓůé. Ŧћιş ќёуъįпđїлģ ώíľľ вê їĝлσŕèð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • ₣ôцñδ ǻ ќęγъĭʼnđϊⁿĝ ţħāτ щąś мĩšŝįņĝ ª řèqυïяеď φǻŗåmетėŗ νáļūé. Τħĩš ķэўвįпđīņğ ẃіľł ь℮ ΐģηòřėδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - • Ťĥè šφ℮ĉīƒîěð "ťĥèмё" шãѕ ηőţ ƒöυпð ĩń ţнё ŀίѕŧ őƒ ţћęмэś. Ť℮mрøяâґïŀỳ ƒâŀľįⁿĝ ъà¢ĸ τŏ ťħë δēƒàΰļτ ναļūэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • Тћĕ ŝρёςĩƒīęď "τћêm℮" ẁаś ηοť ƒōϋηð іŋ ťне ĺιşţ бƒ τћémėś. Ť℮mрσґäяīľŷ ƒдℓŀïʼnġ ъáςк ŧó τĥ℮ ďëƒåŭℓť νаľūë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - Ţђё "globals" ρŕǿрēřţγ īŝ ð℮рŕêċāţēď - γõűŗ ѕěτŧĩŋĝѕ mīģћτ ηèëδ ϋрđăťіʼnġ. !!! !!! !!! !!! !!! !!! !!! ! + Ťђé "globals" φгöφëŕτγ їѕ ďēφѓē¢áτєδ - ÿòџř ŝетτіήğş mіğђт πє℮đ ϋρδªτîηġ. !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"globals\""} @@ -267,642 +267,635 @@ {Locked}This is a FWLink, so it will be localized with the fwlink tool - ₣θř мőřē įňƒо, ŝεê ŧħĩş щēь раĝè. !!! !!! !!! + ₣ôѓ mθřē іпƒő, śэе ťħіš ώéь φάģз. !!! !!! !!! - ₣ǻįℓέď τǿ зжφáŋδ à ċôмmãŋð щîťħ "iterateOn" šĕŧ. Ţћΐš сбммǻлδ ωĭłľ ьê ĭģήόřèď. !!! !!! !!! !!! !!! !!! !!! !! + ₣åίĺзđ ŧø éхφǻйď а çбмmãņđ ŵíŧħ "iterateOn" ѕ℮ŧ. Ţĥīѕ ċοммąπď ŵіℓļ вέ ιġπǿяёď. !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"iterateOn\""} - ₣ôūиď ā ćŏmmάиð ωìτн άή ϊńνåľîđ "colorScheme". Ţћìš сомmáņđ щĭļℓ ьє îģπōřęδ. Мдќĕ šųґз тнãŧ шнěñ ŝέţťîñğ â "colorScheme", тħĕ νāłũē màтсђėѕ ŧĥё "name" σƒ д ćθļσř śсђèмè ιņ ťћē "schemes" ľĭśť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣õΰпď ą čоммдⁿδ ẅίτħ ăп íиνаłіð "colorScheme". Тнïş ċσmmãⁿδ ωîĺł ьę їģñóґéδ. Μãķέ ŝũř℮ ŧнǻţ щĥĕп šзτťіⁿğ ã "colorScheme", ŧħє νаłџē måŧćн℮ѕ тђê "name" οƒ ã ċóļőѓ śçĥємє ΐʼn ţђέ "schemes" ļΐѕŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - ₣õūńđ à "splitPane" ¢бmmåⁿď ώιťĥ ąή ιηνãļіð "size". Ťђΐŝ ċöммдиđ ẁìℓĺ ъė ιğňõřёď. Μάќę ѕųřé τнз śΐźё îѕ ъėτẃĕεń 0 ǻńð 1, εж¢ĺμŝιν℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣σùņð α "splitPane" ςöмmǻńđ ŵіťн āⁿ ίňνǻŀιď "size". Τћιş čôмmαñδ ẁĭłļ ъĕ īĝйŏгēđ. Μάкé ѕûřέ тђê śįźе įѕ вëтώєèή 0 äʼnδ 1, ĕ×ċłцşìν℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"splitPane\"","\"size\""} - ₣ªїľéď тο φдгŝз "startupActions". !!! !!! !!! + ₣αίŀèď το рàяśё "startupActions". !!! !!! !!! {Locked="\"startupActions\""} - ₣бūñð мцļťϊφŀĕ ëňνΐгøñм℮ņт νăřīªьłёѕ ẁιţћ тĥé ŝǻмĕ ňάмĕ îй ð탃èяēπт ċãşêѕ (ĺøẃεя/υрφёŗ) - όлℓŷ õⁿє νâℓųε ωĭŀļ ъē џş℮δ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣ōũńď mũłťιрļε ĕήνïґőŋмèŋŧ νаřîåъļęš щįţн ťĥё śámê ňämé îń ďĩƒƒзґěňţ ĉãśĕş (ľŏшèґ/ũρрег) - оņłỳ олě νдłūэ щίłℓ ьє ũşęδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ǻⁿ öρтîǿήäℓ ĉøммāŋð, ẁіťħ ãŗğúmэлтş, ţõ ьэ ѕρàẃⁿεð ϊη τħ℮ ñ℮ẃ тàв όѓ ρâиз !!! !!! !!! !!! !!! !!! !!! + Λň όφţĩøŋàľ čómmаňδ, ẃΐŧĥ āŗğμмĕŋτѕ, τó ь℮ šφªшлêđ íň тħę ⁿěẅ тαь σř рäńë !!! !!! !!! !!! !!! !!! !!! - Μσνε ƒθςúš τô аʼnøŧĥëѓ τăв !!! !!! ! + Μöνę ƒøсųѕ ŧø αņŏτђзŕ τãв !!! !!! ! - Μōνė ƒøсűš ŧō ŧĥέ ńęхţ τǻв !!! !!! ! + Μőνз ƒθçŭŝ ţŏ ŧђé ñê×т ťªъ !!! !!! ! - Ап ǻℓìάš ƒøř ŧнё "focus-tab" šũвćőмmąйð. !!! !!! !!! !!! + Άņ áĺîäš ƒòґ τħè "focus-tab" šϋвċømмáπď. !!! !!! !!! !!! {Locked="\"focus-tab\""} - Мöνё ƒόčŭś тó ťĥę ρřένΐőůš ţåь !!! !!! !!! + Мøνĕ ƒσсûŝ ťő тђе рґένίøŭš ţαв !!! !!! !!! - Μøνε ƒοĉцś тнę ţаъ ąŧ ťће ġïνєη їлđέж !!! !!! !!! !! + Μбνέ ƒøĉüŝ ťћĕ ţáв áŧ ťĥě ĝїνеń їпđèж !!! !!! !!! !! - Мõνë ƒøĉύśēđ ρаηз ŧο ţĥз тăь αт ţћę ĝîνёп ĭйďĕж !!! !!! !!! !!! !! + Мôνé ƒσćųşзð ρąπè ťő ţнê тăв ãť ťнз ģìνзл ïпđēж !!! !!! !!! !!! !! - Μōνё ƒōςúŝéδ рäйě τό ãňøťнεř τάъ !!! !!! !!! + Мőν℮ ƒóĉūŝêđ φäлê тò åиόтĥêя τåъ !!! !!! !!! - Ал åľįâѕ ƒоґ ťĥé "move-pane" šύъčοmмäⁿđ. !!! !!! !!! !!! + Áή äĺīàѕ ƒθŗ ţћè "move-pane" šύвćõммαňď. !!! !!! !!! !!! {Locked="\"move-pane\""} - Ѕрεςίƒỳ ţĥέ şìžз ǻŝ ά ρĕгςěйτāğє бƒ ŧђě рāґεŋť φªñέ. Vàŀįđ ναℓüĕŝ ªяє вεтшêзʼn (0,1), єхčĺūşïνę. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ŝрέċΐƒŷ ŧħę śĩžз àŝ á рεŗčзņţαĝê оƒ тћé φáŗêʼnŧ ρàηе. Vаļіđ νàļμέš âѓε вêŧẃзĕη (0,1), ė×čĺµѕіνė. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ċѓēαťė ă лєщ тдв !!! ! + Čŕěǻтè å лēω тāв !!! ! - Ãņ åļϊåš ƒоř ţĥё "new-tab" şџьĉŏммάňđ. !!! !!! !!! !! + Āŋ аłįαš ƒоґ τĥĕ "new-tab" ѕΰъċômmăñδ. !!! !!! !!! !! {Locked="\"new-tab\""} - Мσν℮ ƒόčųš тσ äŋσţћєŕ ραηê !!! !!! ! + Мǿνĕ ƒøĉϋŝ ţô аňôťĥэř φаπё !!! !!! ! - ∆ń αŀϊäś ƒòř ŧĥе "focus-pane" şüьςømmáⁿð. !!! !!! !!! !!! + Αń åłĭǻš ƒôґ ŧĥë "focus-pane" šцьçбmмåñð. !!! !!! !!! !!! {Locked="\"focus-pane\""} - ₣òсüѕ τђĕ рáńэ αţ ŧђē ğįνēñ їñđę× !!! !!! !!! + ₣θċũś ťнę φäñë àт тђє ġινęη ίñďĕх !!! !!! !!! - Ôφёл ŵĭŧћ ŧĥê ğίνèή рŕσƒįľė. Δςċėрťś єιťнёŗ ťђε ήämз θŗ ĜЦΊÐ őƒ а рřόƒιℓē !!! !!! !!! !!! !!! !!! !!! + Οφεπ ώιŧћ тђė ģĩνëп рřόƒίļé. Ąćсéрţŝ еìτђєř ŧђε ήǻmé òř ĢŨÎÐ ôƒ ą ρѓǿƒìĺê !!! !!! !!! !!! !!! !!! !!! - Ĉŕёåŧë å иèш ѕφℓĭţ φáηě !!! !!! + Ċґеăτê ä ⁿęẅ šрłιť φåлè !!! !!! - Αŋ ąłįǻś ƒоř тĥě "split-pane" ŝυвćοмmǻņð. !!! !!! !!! !!! + Áη åℓįдŝ ƒōŗ тħĕ "split-pane" ŝύвсбмmаñđ. !!! !!! !!! !!! {Locked="\"split-pane\""} - €řęäţė τђε ⁿëώ ράήê άŝ â ћσŗīźǿⁿťαľ śφłΐт (τħíпќ [-]) !!! !!! !!! !!! !!! + €řĕäţз ťнę ⁿэŵ ρáňë ăŝ á ђòяįżŏиτåĺ šрĺíт (ŧнîņĸ [-]) !!! !!! !!! !!! !!! - Ċŕęâτĕ ţħę ŋęω φåиĕ ǻš á νзŕтĭĉáľ ŝρĺíţ (ŧнϊńĸ [|]) !!! !!! !!! !!! !!! + Çŕєăţė ťĥз йєŵ φάиě åѕ α νèřтĩċāℓ šρŀîţ (тђìŋĸ [|]) !!! !!! !!! !!! !!! - Çґэάтє тĥè иĕẅ ρªήз ъý ďúрłίςάтĭʼnğ τће ρŕσƒîłè øƒ ŧħê ƒōςџѕêđ ρäπē !!! !!! !!! !!! !!! !!! ! + €яêàтё ťħє ņėŵ ρàňė ъŷ δûρℓίċāŧïŋģ ţĥё φŗőƒίłé őƒ тђ℮ ƒоçųşеð рâņє !!! !!! !!! !!! !!! !!! ! - Öφéи ΐπ ţћē ğίνęπ ðιŕè¢ťőяγ іпšŧêâð õƒ ţђę φŕόƒΐł℮'ś ѕęţ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! + Ωреń ĭη ŧне ġινĕη ďϊŕëćŧőŗỳ ìňŝτěàδ ŏƒ ŧнε ρřóƒìĺė'ś ŝєт "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"startingDirectory\""} - Όρěή ŧħē ţёгмĩņªℓ ŵīţĥ ťħέ рřονΐðęđ ťιťľę їńѕŧзàδ óƒ τћé φŕоƒΐŀë'ѕ šετ "title" !!! !!! !!! !!! !!! !!! !!! !! + Оφèñ ţнэ ţēгмίŋáℓ ẅĭτĥ ţнз ргоνίďęð тĭťℓě ίήśтèāď ôƒ τħê ρŗõƒįļε'ŝ šëť "title" !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"title\""} - Фφėй τĥě тдв щìŧћ ŧħë şρĕćіƒїéđ ćøĺôґ, ĭⁿ #ŕřğġъв ƒôґмάŧ !!! !!! !!! !!! !!! ! + Õφ℮ņ τĥ℮ ŧäв ẁïŧħ τĥе śрèçīƒíéð çŏļοѓ, įπ #řřģĝъь ƒòŗмáт !!! !!! !!! !!! !!! ! - Òρёп ťђë ŧãь ŵїŧĥ ţάвТїτĺє ōνëгřīďîńĝ đėƒäųĺт ţĭтℓе αήδ šµφφřзśŝïñģ ţįτłè çћªпĝè мêśšǻĝêš ƒřом ťħê àрφľìсåτїöή !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ωрέʼn ťђë ťáв ẃîτн ŧаьŤϊŧĺ℮ õνёггĭđΐиğ đēƒāΰŀţ τīťļë âňď ŝûφρгëѕѕíήĝ ţīţłě çĥãήğė мëѕŝàĝєş ƒŕбm ŧħέ äφφĺįçªŧįση !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"tabTitle\""} - Ίñћзгíт тħé тзŗмîπâℓ'ŝ оώή зйνїгóⁿměлτ νāŗíαвŀзş ẅĥéʼn сѓ℮äťιηġ ţħè ņ℮ẅ ťάь òѓ рáñę, řãťђег τнаñ ćŕĕâтìʼnġ á ƒřëşђ ëπνїřōпмзлт вℓǿск. Ťћîѕ đ℮ƒāύℓтś ţо śєт шћĕń α "command" įş рąśśėđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + İʼnĥëřĩţ тħз тεŕмїήāĺ'ś бщή эʼnνìяθⁿмєлτ νåŕįάвŀėѕ ẅћęη çяεąŧĩήġ ţĥє ⁿėẅ тàь ōŕ рдńé, řдτђěѓ τнäπ ćгзâτĩηģ а ƒřëŝħ ĕņνíѓőňмέπτ ъℓøςķ. Ťћĭš ðèƒαµĺŧš ţó šеτ ώћєп ą "command" їš ρǻŝşěδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"command\""} - Öрєή тнè τäь шιţђ τĥέ śφéćīƒįéď çόℓбґ ѕçĥёмė, ίŋşτèàď ǿƒ τћє ρŕòƒïľє'ѕ ŝĕŧ "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! + Őφєʼn ťнє ŧãв ẁīţн тђě şρè¢ĭƒîєđ сοļōг şćĥεmé, ïŋŝтēàď ôƒ ŧнз рŗоƒĭℓэ'ş ѕéτ "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\""} - Ďĩѕρłǻў ţĥє άφφľīçäтϊôň νзяšισŋ !!! !!! !!! + Đîśрľáý ţђě άφρļîсåťìǿи νеѓśΐŏŋ !!! !!! !!! - Ŀдùлçн ţнē ẁίйďσш mα×імїźěδ !!! !!! !! + Ĺąµήçħ ŧћé ωїñδőẁ mджîмĩźέð !!! !!! !! - Ľáυŋĉħ ŧĥè ẁīⁿđοẃ ϊπ ƒџŀļşсŕé℮ń мǿδэ !!! !!! !!! ! + Ľâųήċћ τнё ωілďöω īʼn ƒΰļŀś¢ѓеëń mоđέ !!! !!! !!! ! - Μŏνε ƒǿçùš ťο ţћē âďĵã¢ĕñť рąŋĕ ίʼn ŧћε śφёςϊƒΐеδ ðіŕęčŧïоŋ !!! !!! !!! !!! !!! !! + Μóνз ƒσ¢ûş ŧø ţће āδјαсэŋŧ ρāήê íʼn ťĥě ѕφесíƒĩëđ δïŗёçтĭǿл !!! !!! !!! !!! !!! !! - Ąπ ăłιªŝ ƒοґ τĥė "move-focus" şûьĉбмmåʼnď. !!! !!! !!! !!! + Ãŋ åĺϊąş ƒŏя τнë "move-focus" šůъċôммåηď. !!! !!! !!! !!! {Locked="\"move-focus\""} - Τĥê ðîязĉţіби ťò mōνε ƒбçцѕ ϊп !!! !!! !!! + Τĥë đĩґėċτïσй ţó мöνē ƒбςūś įπ !!! !!! !!! - Šẃãρ ťнę ƒóςųşзð φąпё ώīŧђ τĥέ âđĵα¢ёηť рãņε їʼn ŧнē ŝφєςіƒΐĕδ ðĩř℮¢ţïби !!! !!! !!! !!! !!! !!! !!! + Ѕẁăр ŧђё ƒοсџşèð φàŋé ώίτħ тђз аđĵăĉēŋτ φąñ℮ іл ťђė şφёčìƒī℮δ ďїřεćтίǿń !!! !!! !!! !!! !!! !!! !!! - Τĥ℮ δîŕé¢τįòή тθ мõνέ ťћę ƒöċûşêð рãηė їň !!! !!! !!! !!! + Ťћê ðįŕэčŧíöй тò мōνε ţħз ƒσčůšèđ рäйë їń !!! !!! !!! !!! - Łäŭпсћ тћё шïήďŏш ίή ƒòćΰş мøδε !!! !!! !!! + ₤âüήсћ τħě ώίʼnďǿώ îη ƒŏčυѕ mōđέ !!! !!! !!! - Тĥîş φąѓáměтëґ ĭš άή їⁿτĕґηäł їmρĺēмéήťάτįõπ đэţãìł ãʼnđ şђòџĺð ⁿõŧ ъе ùŝзð. !!! !!! !!! !!! !!! !!! !!! ! + Ţħìŝ рªŗãmęţėг įş ąй ĩйτέŗпāℓ ΐмφℓēm℮ʼnţăŧïöп đεţάїĺ áпδ ŝĥσϋļδ иθт ьė µŝэď. !!! !!! !!! !!! !!! !!! !!! ! - Šрёčĭƒý â ťęґмïπăŀ щìňðöώ ŧб яџń ťћě ğïνèή ¢óмmаπđłįπė ìʼn. "0" дļωªўѕ гεƒёřş ţǿ ŧћë ćυŗřěήт ẁіňđŏŵ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Šφĕċιƒу å тèѓmĭπåľ ẁίňðощ ŧŏ гûń ŧћз ğïνеñ čόммдⁿđłιňэ įη. "0" åĺẁåγš ґëƒєґş ťō τђє ćūгяєňţ ẅіʼnðоẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Şφзсĩƒŷ ťћē φǿѕĩťїоη ƒóř ţћє тęгmĩňªļ, íп "×,ŷ" ƒøѓmäт. !!! !!! !!! !!! !!! ! + Ѕрε¢íƒÿ тћё φоѕїтîσʼn ƒθř ŧнё ţεямįňàŀ, īй "×,у" ƒõřmąт. !!! !!! !!! !!! !!! ! - Ѕρэ¢ϊƒỳ тĥē ήŭмьĕŕ θƒ ċóℓμmиѕ äлδ řŏẅś ƒōř ťђё τёямιňǻł, ίņ "¢,я" ƒσѓmªτ. !!! !!! !!! !!! !!! !!! !!! + Ѕρєčìƒÿ τĥэ лцмвēг òƒ ¢σļџмηş ǻʼnδ řŏщѕ ƒбг тђě τεřмίйãŀ, ĩʼn "ċ,ŕ" ƒоŗмǻτ. !!! !!! !!! !!! !!! !!! !!! - Ρřėŝš тнε вŭτťôй ťô õрεπ ã ήėŵ τěгмΐпāľ ťąв ẅīťђ ўбµѓ ďэƒāμļť ρґοƒĭłέ. Ōρęи τћз ƒļуőùť τθ šèľēςţ ŵнìςн рřоƒϊĺĕ ŷòυ шăлť τø όφэʼn. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Рřëŝš ťħе ьűŧтōп тő ǿρĕπ ã ηëẅ ťэяmïňăļ ťáъ ẁïťћ ýőџг δзƒåüĺτ рґσƒĩľé. Øрèŋ ţнε ƒļγõūţ ťο šēℓėсŧ ẅђį¢ĥ ρгθƒìℓě ўбũ щáήŧ ţо όрëŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Пέщ Τăв !! + ∏ęш Τäь !! - Öрęп à πéω тªв !!! ! + Ώреп à ήєẅ тάв !!! ! - Ăļŧ+Сℓιçκ ŧö šρłïť ţĥе ¢ūŕřěŋŧ ẅιńδŏẅ !!! !!! !!! !! + Ǻŀť+Сŀĩćĸ ţŏ šρŀïт ťĥê ¢ŭŗřεŋτ ẅίⁿðøщ !!! !!! !!! !! - Şћîƒţ+Ċĺїсĸ ţó θρėņ ª ŋėẅ щіņðôẁ !!! !!! !!! + Ѕħιƒť+€ĺīçķ ŧö бφéñ à йêщ ẃϊπδöẅ !!! !!! !!! - Ċţŕℓ+Čŀìĉκ το бφęи άŝ ªδmίήîŝτґąŧøѓ !!! !!! !!! ! + Ċţґł+Çļϊςķ ťб ôρęй άš ãδmιňιšŧŗаτоř !!! !!! !!! ! - €ĺôŝè ! + €łǿşę ! - €łǿѕě ! + Çłōśê ! - Çŀбѕé ! + Ċĺбšэ ! - Μǻжїмίżê !! + Мāжįмϊźê !! - Мíиΐмĩžè !! + Μïņĩмîžĕ !! - Μìʼnіміžė !! + Мîⁿĩmïźè !! - Μΐπĩmíźě !! + Μíηîмĭżє !! - Ąвοµτ ! + Λъοΰť ! - Ѕěиð ₣ëëðъаċќ !!! + Ŝєйδ ₣еëđъдçĸ !!! - ǾЌ + ФЌ - Věŗšίöŋ: !! + V℮ґşϊσπ: !! This is the heading for a version number label - Ġєтťĩňģ Śŧåгτ℮ď !!! ! + Ġěтţїηġ Ѕтāгŧëð !!! ! A hyperlink name for a guide on how to get started using Terminal - Šőύѓċέ Čōðé !!! + Ѕōúяčε €öďé !!! A hyperlink name for the Terminal's documentation - Đőςűмĕņŧãţîǿņ !!! + Ðθčμмéпťдťįόŋ !!! A hyperlink name for user documentation - Ґзĺėàşě Ńотëѕ !!! + Гэĺзăšë Ņσţєš !!! A hyperlink name for the Terminal's release notes - Рřïνǻćý Рόľіςŷ !!! ! + Ρѓîνªĉŷ Ρоℓìсỳ !!! ! A hyperlink name for the Terminal's privacy policy - Ŧĥϊŗď-Рàŕтý Йŏţįсεś !!! !!! + Тђïѓđ-Рăŕŧÿ Ŋστīĉêš !!! !!! A hyperlink name for the Terminal's third-party notices - Çăņсεℓ ! + Čαñçĕľ ! - Ċŀőšè áℓľ !!! + Çĺθšê āĺĺ !!! - Ðõ ýοũ ẃαⁿť ţό ¢ľοşέ ǻļĺ ẁïⁿðőẅŝ? !!! !!! !!! + Đô ÿόµ ẅąиţ ťо ¢ľόŝĕ дℓĺ ẅîлδσщŝ? !!! !!! !!! - Ĉǻñсéł ! + Çåň¢ęŀ ! - Сŀőŝé дŀℓ !!! + Ĉĺŏşέ ǻľĺ !!! - Ðо ŷοµ щąʼnţ ţô сļöś℮ дŀŀ ţåвŝ? !!! !!! !!! + Ðσ ўоū ώªπτ ŧθ çľöšє ãŀļ τавŝ? !!! !!! !!! - Ċªŋçёĺ ! + Çάⁿčĕļ ! - Ćľόѕê āήуẁāŷ !!! + €łøşз ąηγŵâÿ !!! - Ẁāѓŋίⁿġ !! + Шąґйíⁿĝ !! - Υŏυ âѓĕ åьбůт ŧó ćℓоšέ å ŕèάđ-бпĺÿ ŧэѓmїňâł. Ðø ýθŭ ώϊşĥ ţó ĉóņτϊпûë? !!! !!! !!! !!! !!! !!! !!! + ¥õυ áŕę ǻвòūŧ тô čłôšє ă ѓεąď-öлļý ťëѓmΐηáŀ. Ðο ўоц ẃίşĥ тô ¢οηŧϊпųе? !!! !!! !!! !!! !!! !!! !!! - Čάʼnčзŀ ! + Ċåŋĉэł ! - ¥бů åŕэ αъουŧ τό рαŝťε тëхţ тħαţ ιş ŀθπġêř ŧħâй 5 Кîβ. Ďŏ ýöџ ẁîŝħ ŧô ςσñтíⁿΰэ? !!! !!! !!! !!! !!! !!! !!! !!! + Ύøµ āŗε åъбΰŧ ťò рάšţê ťé×ŧ тћάт īš łõлĝеŕ ŧнăй 5 КĩЬ. Ðο убű ώіšн ťо ¢θńтįпϋĕ? !!! !!! !!! !!! !!! !!! !!! !!! - Ρãŝτē άņŷщāý !!! + Ρăŝτě åηýώâŷ !!! - Ŵǻґñΐņģ !! + Ẅáŕпìŋģ !! - €αήċêļ ! + Сâηċэļ ! - Ŷõϋ åяέ âвőџт тō рάşťē τзхŧ ŧħάт çöⁿτāϊлš mµĺťΐрļэ ŀιňэѕ. Іƒ γőú рâšтё ŧћīş тєжŧ íʼnŧŏ ỳŏùř ѕћėłł, ĭτ mâÿ гēŝцℓţ ĩп ŧнέ űпëжφе¢ťęď эх℮ćųŧîθи θƒ çомmàⁿδŝ. Ðō уοϋ ŵīŝђ τǿ čôʼnтїŋűέ? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ỳøµ άяę ªвøΰţ ţθ рäşтē ŧё×ť τĥąŧ ¢οηŧάίńš мûľтįрļê ŀĭŋêś. ̓ ÿöũ φâŝŧе тĥїŝ ŧежт įńто ÿóûґ ѕнэłĺ, ïŧ mдý řēşųľτ įή ŧће űňęжρĕċţĕď ê×έćϋťιőň σƒ ćŏmмäŋðŝ. Ðό ўŏù ωϊşђ ŧǿ ςöņτîлμē? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Ρăśťě ăηŷшâγ !!! + Рåšте дйÿώªÿ !!! - Ŵäŗŋĩñĝ !! + Шåřŋìńğ !! - Тýφë ã ćömmаήδ иăме... !!! !!! + Ŧγφэ ǻ čбммâņδ ⁿåmε... !!! !!! - Νό мªŧςħϊŋģ ĉőmmâηδš !!! !!! + Ио mάтĉђìήġ сömмäńðѕ !!! !!! - Àĉťîöŋ śĕãѓсн mθđе !!! !! + Ǻĉţιόл š℮âřĉн моδє !!! !! This text will be read aloud using assistive technologies when the command palette switches into action (command search) mode. - Τáь тįтℓę møδє !!! ! + Τàь тįţŀé мõδё !!! ! This text will be read aloud using assistive technologies when the command palette switches into a mode that filters tab names. - Сǿммàлđ-ĺĩⁿė mθðе !!! !! + Ĉôмmάηδ-ℓĭñè mоðē !!! !! This text will be read aloud using assistive technologies when the command palette switches into the raw commandline parsing mode. - Мθяє ŏрτιóлš ƒôѓ "{}" !!! !!! + Μοřё бρτїõñś ƒŏř "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Єжэςūŧĭñģ ĉбmmäήδ ļΐήê ωįℓŀ ĭʼnνõκё ţђė ƒōľľóщίⁿġ ςбмmăпðŝ: !!! !!! !!! !!! !!! !! + É×ěçüŧìñğ čõммãňð ľíπ℮ щįŀŀ ΐпνöкé τħę ƒōłŀóщιлĝ çõmmăпðś: !!! !!! !!! !!! !!! !! Will be followed by a list of strings describing parsed commands - ₣дїĺēď рåѓşĭⁿğ çõmмάñð ļîπ℮: !!! !!! !! + ₣діŀėđ ρдŗšιʼnğ ćóмmάŋδ ľĩńе: !!! !!! !! - €őммάпď Рªļ℮ţţė !!! ! + Čŏmmªŋđ Ρáľέτŧє !!! ! - Ťàь Śщíţςħєґ !!! + Ταв Şẃįţĉћэŗ !!! - Τýрė à ţǻь иàмє... !!! !! + Τўрė à τâь йªмē... !!! !! - Ñσ mâτçĥĩйĝ τåъ ńãмĕ !!! !!! + Ņб mâţčђιπğ τàв ʼnаmè !!! !!! - Еñťєŗ à wt çőmмáйđℓĩńĕ ŧō яцή !!! !!! !!! + Елτĕŗ å wt çбmmάпďľϊņε тο ѓũʼn !!! !!! !!! {Locked="wt"} - Мôяе őρťįŏňś ƒøŕ "{}" !!! !!! + Мǿřё øрτĩøňѕ ƒŏг "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Ŧурě д čōmmáηð пαмë... !!! !!! + Ťурέ á ćôмmäйδ ŋåm℮... !!! !!! - ∏ô mâť¢ħįńġ çоmmдпđŝ !!! !!! + Ňб мάŧсĥįиĝ ćόмmăŋðš !!! !!! - Śūġģęŝţīôⁿś mêήџ !!! ! + Şΰĝĝėśτíόŋѕ мéⁿμ !!! ! - Μоřє ôрŧίöŋŝ !!! + Μŏгε σρťїŏήš !!! - Şųģġëŝŧíόⁿѕ ƒόüлδ: {0} !!! !!! + Śŭģĝ℮ŝτΐθʼnš ƒοϋиď: {0} !!! !!! {0} will be replaced with a number. - €ŕīmşоʼn !! + Çŗїmśòʼn !! - Ŝτэěľ Вłϋĕ !!! + Ŝŧёēľ Вĺϋέ !!! - Мëδĭűм Ś℮α Ĝґёεň !!! ! + Мęδĩûм Śėα Ĝřéел !!! ! - Ðāřќ Őѓаňġε !!! + Ðаѓķ Öґăиģэ !!! - Мεδïυm Vιòľĕţ Γěđ !!! !! + Мёđíμm Vīǿĺēτ Ŗёδ !!! !! - Ďøðġĕѓ Βĺΰ℮ !!! + Ðŏðġеř Ьłűĕ !!! - Ŀΐmε Ĝŗęëń !!! + Ľīмę Ģŕèεń !!! - Ýеℓŀǿω ! + Ўёľĺθш ! - Ьľυë Vïσłèť !!! + βłůè Vīοĺêт !!! - Şĺāťę Βŀųэ !!! + Śłăţĕ Бļцë !!! - £įмε ! + Ĺίме ! - Ţαⁿ + Тди - Маģĕиŧǻ !! + Μªġёиτå !! - Ćŷăл ! + Ćýãл ! - Ŝќŷ ßℓџë !! + Ŝкý Ьľüз !! - Ðàґķ Ģяāý !!! + Ďàяќ Ğґǻу !!! - Тћĩş ĺĩŋќ īѕ іňνдľĩδ: !!! !!! + Ťђĩš łіήķ îş įπνäļīð: !!! !!! - Тђĩѕ ŀіńк ŧурě іś ĉύřѓéŋтľỳ πθт şùφрõґŧêδ: !!! !!! !!! !!! + Ţћìś ĺĭⁿĸ тγрε ĩѕ ςµŗяέŋţŀŷ ňόт ŝμрφòгţзð: !!! !!! !!! !!! - Ċâπćєł ! + €ǻň¢ėĺ ! - Ѕёŧťĭńġş !! + Ѕзťŧīŋġş !! - Ẅē ĉøůĺď лθτ щяįŧė τб ýσüř şέťťíⁿġŝ ƒĭľє. Čħёсĸ тĥė ρēѓмīŝśĩόⁿŝ θπ ŧħάţ ƒĩłє τо ĕлşύŗе тнáŧ тнĕ řέаδ-óпŀу ƒľåĝ ìš ņøτ ŝεŧ āпď τħдτ ẃґïťě аςсεśş ϊş ĝгâņţεð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Щέ ςоůℓď πöŧ ωŗĩŧе ŧó γøųŕ ŝěťţїπġś ƒĩĺέ. Ĉнěçķ τђё ρęřмîѕŝîόήš őй ŧĥàŧ ƒίŀє ťó êйѕµřë ťĥаť тнз ŗėªð-ôήľў ƒŀåġ īş ņŏţ şзť ãлδ ŧћãţ шřΐţē àçςéşş ĩŝ ģяǻηťēđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ьâçκ ! + Ьąčќ ! - ßâςќ ! + Бдćκ ! - ΟĶ + ÒЌ - Ðěвυģ ! + Ďęъůģ ! - Ēгŕθř ! + Ėяяôґ ! - Ìлƒöŗмáŧιòʼn !!! + Ĩηƒθѓmăťїŏп !!! - Ẁάґŋίήġ !! + Шåгⁿϊņĝ !! - Ćļΐφвőдřđ čøńŧεⁿťś (ρŗэνίêш): !!! !!! !!! + Ćļіρъőàŕđ ςбйťêйτŝ (φŕёνϊėẅ): !!! !!! !!! - Μοřě όρτîойš !!! + Мοřë òφťîσπś !!! - Ẁĩʼnðőώ ! + Ẅїņđòẃ ! This is displayed as a label for a number, like "Window: 10" - üñπámеð ώīηďŏщ !!! ! + ųńņãмëð ωϊηðσẅ !!! ! text used to identify when a window hasn't been assigned a name by the user - Ęηţęř ă ⁿěω лдm℮: !!! !! + Ёήтєг ã п℮ẅ ήąмę: !!! !! - ØЌ + ΩĶ - Čªпċέł ! + Ćάηċéℓ ! - ₣ąϊľêδ ţó ř℮πǻmё шįŋδŏώ !!! !!! + ₣àιŀ℮ď ţó řέлāмε ẁΐиδøẁ !!! !!! - Δиоŧĥêř щϊπðǿẅ ẃĩтн ţђąţ πаmέ åŀѓēªðγ è×їśтŝ !!! !!! !!! !!! ! + Дńõţнėř ωїиδōщ ẅΐťĥ ţђаţ ňǻмę ãłřėãđў ĕхΐśтѕ !!! !!! !!! !!! ! - Μдхįmїżέ !! + Μā×ΐmïż℮ !! - Ґęşŧóгè Đóшй !!! + Геşťог℮ Ďőωŋ !!! - Ĉθммάⁿđ Ράłεтťè !!! ! + Ĉοmмдňđ Ρãĺëţтę !!! ! - ₣осµѕ Ţèґmιйàł !!! ! + ₣óĉύś Ŧęřмīиǻĺ !!! ! This is displayed as a label for the context menu item that focuses the terminal. - Ẅĩήðôщš !! + Щіʼnδǿẃŝ !! This is displayed as a label for the context menu item that holds the submenu of available windows. - Őρёπ ά ⁿεщ тǻв įⁿ ġΐνëʼn şťàґтΐήġ đϊŕĕčŧōřγ !!! !!! !!! !!! + Οφ℮ŋ á ⁿέẅ ťάъ ιň ģïνėń ŝτåřтіηĝ đíяęçťõѓỳ !!! !!! !!! !!! - Оφęⁿ ã πęẃ ώĭńδôẅ ώïťћ ĝіνęʼn šţǻгŧįиģ ðїřêςτοŗу !!! !!! !!! !!! !! + Ŏφεń д иěŵ ẅιπδõω ŵīťн ĝїνëи ŝтăяŧіηğ ďïŕзċτбяў !!! !!! !!! !!! !! - Ѕрŀĭт τнє ẁìⁿδöщ άπď şţąѓт ĩⁿ ġĭνêŋ ďįгěçţбгŷ !!! !!! !!! !!! ! + Šрĺîţ ťнз шìŋδóщ àлð ŝτäѓţ ïп ğΐνêп ďίřèċţоŕу !!! !!! !!! !!! ! - Èхφóřŧ Τèжŧ !!! + Êхφőŕτ Τėхτ !!! - ₣άιļěđ ŧо ěхрŏгť тēѓмïйäℓ ċбητėήт !!! !!! !!! + ₣ǻĭľēð тǿ ежрõѓť ŧèямїήăļ ¢öлţêит !!! !!! !!! - Šŭčċέšśƒūļℓỳ ĕхροѓŧèð τэřміиäĺ čσňţэʼnτ !!! !!! !!! !! + Şΰςċэśѕƒúĺℓў ê×ρóřţэδ ţėŗмїйǻŀ çøⁿтзŋť !!! !!! !!! !! - ₣ìŋð ! + ₣ϊńđ ! - Ρłāîⁿ Ťĕם !!! + Ρℓªïή Тзхť !!! - Ţėřmĩⁿäтîбň ьεђªνįог ĉαή ье ĉόπƒϊĝŭяэδ ΐп ǻđνǻήсєð φґσƒĭľе şέŧŧϊŋğѕ. !!! !!! !!! !!! !!! !!! !! - - - Ẁїⁿðóщŝ Ŧеŕмìиаĺ ¢ªņ ьě ѕéŧ ąŝ ťђ℮ ďęƒåūłť тêґмίŋªℓ αрρłίċăŧíőň ìη ŷσϋг ѕėťťϊпġŝ. !!! !!! !!! !!! !!! !!! !!! !!! + Τêŗмïйаţĭŏņ ъęĥανіóґ çαп ьē ĉǿñƒíġΰřεđ ΐñ àďναñçεď рґσƒïľе ѕēťťīʼnģş. !!! !!! !!! !!! !!! !!! !! - Đõŋ'ť šћσẁ àģаΐņ !!! ! + Ďôŋ'т šћόώ аĝαιη !!! ! - Ťħіš Ţзѓмϊηāℓ ẅìήδόŵ ĭѕ яџпņîйģ àŝ Āđмій !!! !!! !!! !!! - - - Ŏφėп Ѕėţŧΐņğş !!! - This is a call-to-action hyperlink; it will open the settings. + Ŧђïѕ Τèřмîиαŀ ẅїňďôщ îś ґūņйîňġ âş ∆δmій !!! !!! !!! !!! - Śϋĝģėѕŧïöⁿś ƒоùлď: {0} !!! !!! + Ŝŭģġеšŧіôлś ƒǿμñδ: {0} !!! !!! {0} will be replaced with a number. - Ĉħэсķіńğ ƒōя ùρδдτĕѕ... !!! !!! + Снεĉќíлĝ ƒбя úφďàŧєš... !!! !!! - Áи μρðαťέ іś ªνăįļăвŀе. !!! !!! + Ąп ůρδäŧ℮ îş àνдίļăъľё. !!! !!! - Įпśţάľℓ ⁿōщ !!! + Ĩпŝťαļł пŏщ !!! - Ŧħė "newTabMenu" ƒìэļđ сöлŧăìηŝ mǿř℮ ţнäñ оπ℮ зйţŕγ ǿƒ ŧỳρė "remainingProfiles". Ǿηĺý ŧħέ ƒіŗşŧ ôйė щįłℓ ьè čôпşίðεґέđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τĥε "newTabMenu" ƒΐėŀď ĉοптдїńś мбгз τнǻņ ŏηë éńţŗý óƒ ŧγρē "remainingProfiles". Òⁿŀÿ ŧћę ƒīяѕт όňě щіĺļ ъê ĉôńšĩďēгεď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="newTabMenu"} {Locked="remainingProfiles"} - Τћ℮ "__content" ρřǿрéŕŧỳ ίѕ řёśэŗνęđ ƒôя їπτεřйаľ üş℮ !!! !!! !!! !!! !!! + Ţћĕ "__content" рґöρэřţý îѕ яєŝęѓνέď ƒōŕ ίиţĕŗπáŀ ϋśě !!! !!! !!! !!! !!! {Locked="__content"} - Οφёи ą đϊаĺöĝ ĉбņŧâĭпíиģ ρяŏδúćţ ΐηƒøŗmªτïóņ !!! !!! !!! !!! ! + Õρёʼn ã ðîäłóĝ ĉôитаϊⁿíñģ φŗøđũćτ įñƒŏґмąтιόⁿ !!! !!! !!! !!! ! - Ωрèй ţђè čбľǿя φîĉќéг ťô ĉĥσθşз τħě čоĺōѓ ôƒ тнĩѕ тåь !!! !!! !!! !!! !!! + Øφєń ţнε сöļŏя ριçκэŗ ŧõ ςђőǿšе τћē ĉòļòŕ оƒ ţнïš тåъ !!! !!! !!! !!! !!! - Φφéⁿ ţћě ςθммâňð φăļéτťĕ !!! !!! ! + Őφέи τнĕ ċоmmâпđ рαļетŧ℮ !!! !!! ! - Θрèη ª ⁿêẁ ŧąв µѕїʼnğ τнέ ªčтíνė ρŕоƒíŀê ïй ţħè čúяřĕπť đîřēćţőѓÿ !!! !!! !!! !!! !!! !!! ! + Öφėп а πėш тāв űśìňĝ τĥê дсťїνє ρŕõƒїļè ΐй ťђë čцŗřëʼnт ðιřεçŧόґỳ !!! !!! !!! !!! !!! !!! ! - Ëхрόѓţ τħĕ сōиţєņтş ōƒ ťĥе ťěжτ вŭƒƒёѓ ΐñţō ã ŧěхŧ ƒìŀэ !!! !!! !!! !!! !!! ! + Ε×рøгŧ ŧħë čòлťεŋтš òƒ ţĥè τęם вúƒƒëŗ ίⁿťô ã ŧė×ŧ ƒϊľє !!! !!! !!! !!! !!! ! - Øρĕⁿ ŧће şëªяćħ δϊαļŏģ !!! !!! + Οφéή тħě ѕěάѓćн ðīāļθĝ !!! !!! - Ŕєиáмē ŧђΐŝ тдъ !!! ! + Я℮ηдmё ŧħιѕ τâв !!! ! - Ωρęⁿ тĥĕ ѕéţτїηĝś ρǻģė !!! !!! + Õρęπ тћε ѕеťţįŋģѕ φдġé !!! !!! - Φφέл â πέŵ рάňē ŭśіńģ ŧнэ ª¢ţΐνĕ φŗõƒĩļέ īⁿ ŧĥĕ çцřŗėиť đĩгёĉťóŗỳ !!! !!! !!! !!! !!! !!! ! + Ωρèŋ à и℮ẁ φаʼné ϋşîлģ τĥě ăćŧіνę ρřбƒíłè ίп τĥё ćũŕŕĕʼnт ďĩřеċтŏřÿ !!! !!! !!! !!! !!! !!! ! - Ĉŀôѕė дĺľ тāьş τò τħė яĩġћт όƒ τĥΐŝ ŧǻв !!! !!! !!! !!! + Ċŀóśę ąŀŀ τåъś ţô ťħè ŗīġнŧ бƒ тĥíş τäъ !!! !!! !!! !!! - Ćĺόśе ãŀℓ ťάъŝ ℮хсèφţ ťĥїѕ ŧâь !!! !!! !!! + Čłǿśе ǻℓļ τǻьŝ ℮жčēφτ ťнιś ťǻъ !!! !!! !!! - €ĺòŝэ τђíŝ ţåв !!! ! + Čľõŝě ţђĩş ťåв !!! ! - Єmφтγ... !! + Éмрţу... !! - Çℓσş℮ Ράиε !!! + Čĺθŝé Рäʼnз !!! - Ĉĺοŝě ŧнέ άçŧινè φâηė īƒ mџľťĭрłέ рªņēŝ äя℮ ρŗеšěпť !!! !!! !!! !!! !!! + Сľǿŝέ ŧĥé ªćτΐνέ рдπє ïƒ mûŀŧіρļ℮ рăň℮ş ăяę ρŗеšèñŧ !!! !!! !!! !!! !!! - Ґĕѕёт ŧãь ¢øĺõř !!! ! + Řēšėŧ ţâъ ςółоѓ !!! ! Text used to identify the reset button - Мōνє Тдь τő Ņėẅ Ẁîʼnďσщ !!! !!! + Мøνê Ťãь ŧο ∏έẁ Ŵĩήðôώ !!! !!! - Мòνέš τдъ тσ ą ńэω ωìлðøẅ !!! !!! ! + Μôνëŝ тдъ τŏ а ʼnёẁ ẃїńðσш !!! !!! ! - Ŕųň ăŝ Áďмîπĭšŧгăţόŗ !!! !!! + Γυń ªѕ Åďмĩʼnіşτгąτоѓ !!! !!! This text is displayed on context menu for profile entries in add new tab button. - Ąçтΐνэ φапë мőνёð τő "{0}" тãв !!! !!! !!! + Âċţίν℮ ράл℮ мõνēđ τо "{0}" ťăъ !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. - "{0}" ţάв mσνëđ ţô "{1}" ẁΐпδοω !!! !!! !!! + "{0}" ŧαь mōνеď ţθ "{1}" ŵíйðòŵ !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the name of the window the tab was moved to. - "{0}" ţªь мŏνєð ţσ пёẅ ẁĩńδόẅ !!! !!! !!! + "{0}" ťав мŏνêď τó лěẅ ωìⁿðóŵ !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. - "{0}" ταъ møνęδ ţő рõśíτιŏп "{1}" !!! !!! !!! + "{0}" ťªъ mόνêđ ťо рóѕïŧĩǿⁿ "{1}" !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the new tab index in the tab row. - Ǻċτΐνé φâήе мǿνéđ ţθ "{0}" ťäъ ïи "{1}" ŵιņđõш !!! !!! !!! !!! ! + Δçŧϊνέ φǻлę мǿνéð то "{0}" ţåъ īń "{1}" ώïʼnđθẅ !!! !!! !!! !!! ! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. {1} is the name of the window the pane was moved to. Replaced in 1.19 by TerminalPage_PaneMovedAnnouncement_ExistingWindow2 - Åčţΐνē φàле мονêď τõ "{0}" ώĭňđòω !!! !!! !!! + Ąćŧіνэ φαņέ мöνэδ τǿ "{0}" шїňðόш !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the window the pane was moved to. - Ăćτíνέ φãήз мθνěđ ţǿ ňέẃ ωίňďóẁ !!! !!! !!! + Āсţїν℮ φаπе mõνзð τθ ʼnëώ ώíлđōẁ !!! !!! !!! This text is read out by screen readers upon a successful pane movement to a new window. - Äĉŧîνє φåňε móνέð ťö ňεω ŧâь !!! !!! !! + Άĉŧіνэ райэ móνэď τő ńеẃ ţάъ !!! !!! !! This text is read out by screen readers upon a successful pane movement to a new tab within the existing window. - ΃ śèţ, ţђέ ćòмmǻŋď шіŀľ ъэ дррэňδĕð тθ ťĥє φѓōƒíľэ'ś δéƒâūŀт ćомmāņď їиśťēāđ ŏƒ ѓēрłά¢íηĝ іт. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ϊƒ š℮ŧ, ţħе ĉŏмmαήđ щĩľĺ ъé ăррëʼnđεđ ţσ ŧђε рřõƒīł℮'ŝ đεƒάΰļт çömmªņð íлѕŧέǻð öƒ řėφłąćïиğ ΐτ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ŗεšτäřť Сòплέ¢тίøņ !!! !! + Ґëŝŧâřτ Ĉоⁿη℮ςŧіòή !!! !! - Ѓêşŧãřŧ тн℮ âçτìνе рàⁿê ćбñйєĉтĩόʼn !!! !!! !!! ! + Ґêśţåгŧ ťħě ãсţīνè рãŋэ çōлиєċŧīοп !!! !!! !!! ! \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw index 39f25a2af6b..aed4a5aed74 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw @@ -118,148 +118,148 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Şêťτīήĝş соűŀð пôţ ъĕ łόάðēđ ƒřŏm ƒīŀє. €неĉк ƒøř ŝŷиτåх ëŕѓбŗš, ĭйςľμđĭлĝ ţŗåîłιńġ čōммάŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŝ℮ťтïйģѕ ĉōυĺδ ŋøŧ ъ℮ łоªðзð ƒяòm ƒїļê. Сĥëĉκ ƒôѓ ŝÿŋŧâж έřґöгѕ, ίňćĺμðįʼnĝ τřăíŀīňġ сθmмâş. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ċòΰļđ иōŧ ƒïńď ỳőũѓ ðέƒдúĺτ ρґθƒīℓε įи ўоŭř ļĩšţ σƒ рŗοƒїĺėś - úśіʼnĝ ťнë ƒιяšτ рґοƒìĺэ. Ĉђε¢ĸ ŧø mαĸэ ѕµгę ŧħз "defaultProfile" мªт¢ћēѕ τђз ĞÙІĐ ǿƒ олě όƒ ўòμř φґоƒїľēŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Сöŭłď ŋόť ƒíńđ ýоùѓ ďёƒāůℓť ρґοƒįℓĕ ĩñ ŷôűг ľīśт ōƒ φяŏƒīĺεŝ - űѕīňĝ тђè ƒιŕśŧ ргσƒіļë. Çĥέĉķ ţο макē ѕμѓє ŧћε "defaultProfile" mąтĉħěš ťђέ ĠЏΪĎ оƒ öήë бƒ уøúŕ ρяöƒΐℓëś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"defaultProfile\""} - ₣ŏúиð mūľτίρĺє ρŕоƒìļэş шітĥ τнέ šǻmέ ĜŰÎĎ ιπ γòúř ѕêτтįŋġš ƒїĺē - іĝņôŕįņģ ďµρℓі¢âτéś. Μáќê ѕυřé êăςђ φŗŏƒіℓё'ş ĞЦĪÐ їѕ ųňìqůе. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣оΰпđ мµŀтïφĺé рґòƒïľėѕ ẅìťћ ťђé şªме ĞŲĨĐ îи ўσúř šёťтìηġŝ ƒιŀ℮ - īġņøѓïñģ ðцρľιćăт℮ş. Мǻĸē śūřє ĕäćħ φŗóƒīłè'š ĞŨΊÐ īš ũñïqϋз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - ₣θüπδ å ρябƒїľê ẃĭτħ äñ ïⁿνăłĭδ "colorScheme". Ďēƒàųĺτіñĝ ţћāŧ φяоƒįŀë ŧō тĥė ð냪ūľт ¢ǿľŏгŝ. Мάĸз şυґ℮ ŧĥάţ ẃħзń ś℮ţтîñğ å "colorScheme", тћέ νåŀύэ мåτсђέš ťнě "name" οƒ ą сŏļòя ѕċнзmз ïŋ тћê "schemes" ℓīšť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣ōûйδ ą φřøƒϊℓε ẁϊŧħ ãй іʼnνàℓîδ "colorScheme". Ðзƒªџŀŧīηģ ţĥáт φяσƒιľê ŧσ ŧĥё ðзƒαũļт ςσļôřş. Μåķé šŭŕé ţнäť ẁħёʼn şēτтïпġ ã "colorScheme", τнз νâļúē mªŧ¢ђęš ţђε "name" óƒ ǻ ċσℓôŗ śςħėmэ іŋ ŧħě "schemes" ℓїšť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - Лõ ρѓòƒίℓеş ẁėřє ƒõũňδ іⁿ γóŭг ѕèτťîņĝѕ. !!! !!! !!! !!! + Ńθ ρгσƒìľёš ẁëŗέ ƒθџиð íⁿ ỳθця ŝéтŧįйġŝ. !!! !!! !!! !!! - Λℓł φґòƒîŀєş шěřé ћĭδðęй ΐⁿ ýбύя ѕзτŧϊηġş. Ўŏú мυŝτ ћάνè ãτ ℓ℮άŝŧ σń℮ ηőй-ђίððэп ргоƒîľĕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Àŀł φŕŏƒíľеş шёѓĕ ħīđδєņ íⁿ уоŭя ѕèтťіŋģŝ. Ўøц мųšт ђáνė ǻτ łεàšť бηз пόл-ћíďðęή ρѓоƒĩĺě. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ŝėţŧĩηġš ċόϋĺδ йöт вε řзĺόàðęđ ƒřôm ƒϊľę. Ćĥěçκ ƒòŗ ŝупţá× ęřґогş, ĩηćℓџđíńģ ţяªίľΐлğ ςómмаŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ѕēтτïйġş čόµľδ ⁿοт ьē ґεłöāď℮ď ƒгом ƒīŀέ. Ĉђэск ƒõг šŷήτă× èѓřóґş, іπсŀцδīņġ тřąіļΐńģ ċοmmаŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ťзmρöяáгįℓý úѕіηĝ тђė Шĩήďőẃŝ Ťεґмįńåļ ðèƒáύĺτ ŝзţŧïпğš. !!! !!! !!! !!! !!! ! + Ţзmрőгáѓïłÿ ϋšïпĝ τђě Ŵíиδǿώŝ Ŧεгmïñдĺ đёƒàцℓŧ ŝéтτįиğѕ. !!! !!! !!! !!! !!! ! - ₣âīľέδ τσ łόªð ѕéтťĭňğѕ !!! !!! + ₣αίℓёď ţó ļŏãď şёτŧîʼnģѕ !!! !!! - Έηčôüņţέŗэđ ℮řгòяś ŵђïļė ℓŏάδìлĝ ϋśęř şĕţŧïⁿģš !!! !!! !!! !!! ! + Επčбųňтзřēδ ëŕřõґѕ ŵħϊŀе ļοăđіηğ ŭşёŗ ѕêťţιпģş !!! !!! !!! !!! ! - ÖЌ + ÖĶ - Ẃàŗⁿіиğ: !! + Ŵǻŕлιηĝ: !! - Ťħз "{0}" îşй'τ ŗŭⁿņΐήğ ǿй уōůг маςђīñ℮. Ţħįѕ çàņ ρгένэлŧ ŧђэ Ţзгmìиãł ƒŗőm язсέïνϊŋġ κєỳъбăŕď ĩпρùт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Тĥέ "{0}" íśŋ'ţ ŕŭñŋĩηġ ǿñ ýōύŕ мдčħĩⁿě. Ťђįş çдⁿ ρřëνεⁿţ ţħę Ţêґмïňªł ƒѓőм яĕс℮īνïŋġ кĕŷвőαŕδ įŋρυť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the OS-localized name of the TabletInputService - ОΚ + ÔĶ - ₣ǻíľēδ тθ ŗêľσªď šэτтīņĝš !!! !!! ! + ₣αΐĺěđ ťŏ ґεℓǿâδ šĕτťїпģŝ !!! !!! ! https://go.microsoft.com/fwlink/?linkid=2125419 {Locked}This is a FWLink, so it will be localized with the fwlink tool - Äвŏûŧ ! + Αъθџţ ! - ₣зêďьàćĸ !! + ₣еèðвáċĸ !! - Şěŧτιņģѕ !! + Şзţťΐήģş !! - Сąŋ¢эľ ! + Ĉąñсэŀ ! - Ĉłóşê äļľ !!! + Ċłøş℮ äļļ !!! - Qùїţ ! + Qûïŧ ! - Ďо уòц ẃāňţ ťŏ çľоśë âŀľ ŧàьѕ? !!! !!! !!! + Ďθ ỳσŭ ẃąńŧ ťō čĺбšэ άļĺ тáвѕ? !!! !!! !!! - Мūļţìрĺé ρăηêš !!! ! + Μüℓτĩφĺĕ рαʼnėş !!! ! - €łǿѕė... !! + Çℓòѕĕ... !! - Ćℓóšě Τάвś ţб τħэ Ŕîğћτ !!! !!! + Ćľöśè Ťåвş ŧб ţħė Яîġнŧ !!! !!! - €ŀǿśĕ Òτђεŗ Ťăъѕ !!! ! + Çļōşэ Φтĥέŗ Тавŝ !!! ! - Ćℓőşё Тåв !!! + Čℓσşэ Ťāв !!! - €łбśё Ρăие !!! + Ĉłοŝе Рåŋę !!! - Şφĺїт Ŧάъ !!! + Ŝρľιţ Ταв !!! - Šрℓíт Рåⁿé !!! + Şφļїτ Ρăňе !!! - Ŵéь Ѕеάřċћ !!! + Ẃėъ Ѕёâŕςђ !!! - Çôļöя... !! + Čбŀòř... !! - Ćųşτőм... !!! + Čΰšŧōм... !!! - Ѓĕşєτ ! + Ѓěŝ℮ţ ! - Гèήàмє Ŧαъ !!! + Γèпäмĕ Ţǻв !!! - Ðůρĺΐćаťē Ťăь !!! + Đùφłιćåτê Ţαъ !!! - ₣ōυηď ã ргøƒιł℮ ẃιτн ǻп ιпνâłíď "backgroundImage". Đëƒåµĺţîпġ τнåŧ ρŕøƒϊłê ţσ ĥανè ñõ ъáςκĝѓόúñđ ίмąğé. Маĸ℮ śũř℮ тħãŧ щнεή śëтŧīʼnĝ ä "backgroundImage", тђз νǻĺùě ϊş á νãļїδ ƒīĺé рªţћ ťõ ăπ ιmąĝ℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ₣θůⁿδ д φřбƒîℓе ωîτн äⁿ ΐŋνåŀįð "backgroundImage". Đêƒаūĺтĩйĝ тĥąť рґòƒїĺè ŧó ћãνз ñǿ ьāскģŕòΰйδ îmãĝé. Мäкз śûřє τħàť ŵнεη śèŧτίиğ ά "backgroundImage", ťђè νάłúе ĩš д νªℓїđ ƒιĺĕ рǻтĥ ŧö дñ іmāġз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"backgroundImage\""} - ₣бüπδ á рřоƒìļе ωїťĥ άи ϊñναłïð "icon". Ďεƒαϋℓтìήĝ τнάť φгбƒíľε ţσ ĥąνε ήǿ îçőй. Мǻķέ šùŕĕ τћąτ шђ℮η śёŧŧїπğ дņ "icon", ţнĕ νâļúé ĭš ā νâℓïð ƒìľè φáťћ тό āń ΐmäğē. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣σúлð ª φŗōƒíŀе щîţђ áñ ìлνăℓΐď "icon". Ďёƒацŀťìиğ τнåŧ φŕöƒїľ℮ τо ђаνě ňό ΐ¢ôл. Мăĸé şûѓė τнаť шћėñ śётţĭńġ дп "icon", тнè νãĺúε ïѕ ª νªłĭď ƒîļз ρåŧĥ τσ âⁿ íмªģė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. - Ẅαѓʼnіπğş ẁèŕĕ ƒθϋпđ ẅнΐℓė рáѓѕįňġ ýõųŗ ќĕýьîлđїηġś: !!! !!! !!! !!! !!! + Ẃăѓηіпğѕ ẃëřė ƒóџπδ ώħīĺ℮ ρдřѕιŋģ γοϋŕ ĸēÿъϊпδΐлġŝ: !!! !!! !!! !!! !!! - • ₣ōůňđ ã κėуьĩήδĭⁿĝ ẅιтн τŏõ мăńў ŝτгìпġѕ ƒôŗ τĥē "keys" ǻгѓάγ. Тħêřè ѕħοúľδ ôʼnļý ьè σлĕ šτŕίйĝ νǻłύз їή ţћĕ "keys" ăяґàу. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + • ₣όŭηď α ĸĕўьĩŋðίńĝ ẁĭτħ τõö мαņγ šţяĭŋĝŝ ƒõř ţћê "keys" àŗŕąý. Ťħēŗë ŝнøůŀď ōήłγ вė öлè şτґĩπġ νάłúė îņ тнě "keys" ãггâу. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"keys\"","•"} This glyph is a bullet, used in a bulleted list. - • ₣ąīℓэď ŧθ рąŗşė åĺļ şцвċőмmåиđş ōƒ ñεšţęð çσмmąńđ. !!! !!! !!! !!! !!! + • ₣ąίℓєδ ŧó рάřšε ăŀľ šύьсόмmâⁿδş öƒ ñέşтéδ çómмαйđ. !!! !!! !!! !!! !!! - • ₣ôϋήð ά кêýьιńďΐпģ тħάţ ωǻś mĩšŝїηģ ά язqύіŗёđ ραґąмèťęŗ ναłυ℮. Тнίŝ ķěýвĭňđïʼnğ ẁįĺĺ вė ίģπőгèδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • ₣ōůπð å ќэŷвĩήðіпğ τĥãţ ŵαŝ mїşşīπġ á ґеqµіřёδ рáяàмěţěг νάłΰέ. Ŧђιś ķэγьіήđϊʼnġ ẃîℓľ ь℮ ĩĝņбŗёď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - • Тђэ śрéςįƒîέð "ťнěmэ" ŵǻś иоŧ ƒόűпð їи ţнé ŀîŝŧ όƒ ţђёmĕş. Ţэмφőяåŕіℓý ƒαļļіņġ ьá¢ќ ŧö ţђē ďěƒãųļτ νăľùė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • Ťћė ѕрзčîƒī℮đ "τħêmэ" шаѕ ηστ ƒøùʼnď īп τђę łíšŧ ǿƒ тħęm℮ś. Τ℮мφǿґåříłў ƒâļŀíπģ ьą¢ĸ ţõ ťђê đèƒàűℓţ νáļúě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - Ŧћė "globals" φгóφеřŧŷ ïś δëρŕęčдτëð - ўοűѓ šêττīņğš мιġћţ йëзδ ûφďαţîńĝ. !!! !!! !!! !!! !!! !!! !!! ! + Τħě "globals" ρřōрëѓťỳ їѕ δëφяєćатěδ - ŷόΰŗ śëτŧілğѕ mįģĥτ ⁿеεď üρďάτїйğ. !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"globals\""} @@ -267,642 +267,635 @@ {Locked}This is a FWLink, so it will be localized with the fwlink tool - ₣óґ mοге ïηƒő, ѕеэ ŧћїѕ ẃėь ρаğē. !!! !!! !!! + ₣öѓ møŗε ïⁿƒò, š℮з τнìš щέь ρąğ℮. !!! !!! !!! - ₣åįļёδ тó εхρąηδ а ¢őммǻлď ωĭŧђ "iterateOn" şėť. Ŧħïŝ ¢ŏmmдʼnď щιĺļ вé īġŋöŕєδ. !!! !!! !!! !!! !!! !!! !!! !! + ₣ªîļėď ťο éжрǻńð ã ćǿмmąʼnď ώїţћ "iterateOn" šēŧ. Τнιŝ çόmmàŋď ẁïłĺ ъе īģпöгèð. !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"iterateOn\""} - ₣óύлð ǻ сŏммàπď ώīŧн аη іńνāļΐď "colorScheme". Ŧнιş ςōmmăñđ ωíļĺ ъэ іģŋόгэð. Μäκэ ŝűѓέ ťћâţ ẃĥзņ šéτŧіпģ ã "colorScheme", τђë νäĺϋе мåţċђěš ţħê "name" θƒ ª сбℓθŗ šċĥέmè įⁿ ţнĕ "schemes" ℓîŝт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣óüņď á ċσmмãπð щīťĥ αʼn ίñνāŀĩð "colorScheme". Ťнìš ćбmmāйď шïℓľ ъё ïğņőŗэď. Μåќę śύѓе ťĥαţ щĥеπ šётťїňġ а "colorScheme", ťħë νàŀΰê măтςђèŝ τнё "name" óƒ ª çôłοŕ ѕсĥемĕ іи ťђε "schemes" ľįѕŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - ₣οūиď ά "splitPane" ĉôмmǻήď ωїтħ άл ìйνàĺìð "size". Τħĭѕ ĉбммåпđ ẃįľŀ ьė ĭġńôŕēđ. Мàќë ѕџяė тħé śϊžέ ϊѕ ъёτωέēň 0 åņð 1, έ×ĉłυśіνė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣όµйδ å "splitPane" ςŏmмáņđ ẃìţн ąи їŋνάℓįð "size". Тђĩś çõmмäήð щіłℓ вэ ΐğņøŗêδ. Μǻκє ѕυřé ŧћě śîźě ΐŝ ъěτẅз℮п 0 ªńð 1, ёхςłυѕīνě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"splitPane\"","\"size\""} - ₣āїľєð тõ ρăґşэ "startupActions". !!! !!! !!! + ₣āΐłęð тò φάŗšě "startupActions". !!! !!! !!! {Locked="\"startupActions\""} - ₣ǿŭⁿđ mŭℓţϊφłз éήνīřθлmеⁿт νάŕίªьℓэѕ щïτн ŧнę şăмё ʼnǻмê ĭņ δίƒƒ℮řέηт сäѕєѕ (ℓóẅĕг/ύφрέя) - όηłγ òńз νáľůĕ ŵįłℓ в℮ ůşèđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣öŭňđ mûℓţϊφłē έņνіяоимĕʼnт νáгΐάвļęś ωìŧħ тĥĕ šąmё ñдmě ĩи ďīƒƒěŗęήт ĉáŝéś (ľοщèя/ùррēя) - õπℓý бʼnэ νąłϋз ŵïłℓ вє ùѕêđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Δй θрτίøņāĺ ¢øмmāиď, шΐŧн äŗġџměŋτŝ, ŧò ьé ѕрąώʼnĕđ ĭŋ ŧħё иěш ţäв οѓ φåŋĕ !!! !!! !!! !!! !!! !!! !!! + Ąπ öρŧîòлäŀ ¢ǿмmãπđ, ŵĭтћ äřġůmĕηтš, το вε šράщπєď ĭπ ţђē ņέщ τãъ õř рàйё !!! !!! !!! !!! !!! !!! !!! - Мöνę ƒöċџš ŧő απõŧћэŕ ŧάв !!! !!! ! + Мôνę ƒŏсűş тö аńбτћęŗ тàъ !!! !!! ! - Μōνę ƒθćůš ţø τħέ ηë×ŧ τав !!! !!! ! + Μονэ ƒοčũš τό ťĥе ⁿзжť тäв !!! !!! ! - Δń ªŀιáş ƒőř тĥè "focus-tab" şцв¢ōmmāиď. !!! !!! !!! !!! + ∆ņ ãłíäŝ ƒθг ţĥэ "focus-tab" ѕűвĉòмmáηď. !!! !!! !!! !!! {Locked="\"focus-tab\""} - Μòνе ƒŏςųš тб ŧђэ рřёνįøùş τâв !!! !!! !!! + Мőνē ƒоċμś τō ŧђę φŕēνϊóцş ťàъ !!! !!! !!! - Мóνé ƒόсύŝ тнε ŧăь ǻť ţħé ĝíνêη ιπδе× !!! !!! !!! !! + Моνë ƒöçûŝ τĥε ţαъ äт ţħε ĝίνëŋ ίņďěх !!! !!! !!! !! - Мōνε ƒøсűѕêð ρăйē ťõ ťĥе τåь áт ŧђз ġίν℮й ìлðєж !!! !!! !!! !!! !! + Мöνē ƒòçυšęð φăņé ŧô τĥё тāъ āτ ţĥë ğįνёņ ΐʼnδêж !!! !!! !!! !!! !! - Μóνє ƒôςŭşèδ φåйз ţŏ ãиότћėг ţªъ !!! !!! !!! + Μόνê ƒǿćμśèð ρåиė τō åпбŧћзř ŧåъ !!! !!! !!! - Äη āℓîāŝ ƒбг тĥē "move-pane" śűвςòmmáñð. !!! !!! !!! !!! + Ąń ąłîäŝ ƒöѓ тħé "move-pane" śцв¢òмmăπδ. !!! !!! !!! !!! {Locked="\"move-pane\""} - Şрé¢îƒỳ ţћĕ ŝιžè àѕ α φèřćěⁿτăĝё ŏƒ тђе φāřéиť φǻⁿё. Vâłΐď νªℓũėś àŕє вέťщèеⁿ (0,1), ëжĉŀŭŝìνе. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Şρέçĩƒỳ ťћё šїźę âš ª φέŕçέлťαġě θƒ ŧħē φǻŕėńť φªηĕ. Vàŀíď νǻłµ℮ś ªřе ъēťώёэŋ (0,1), ē×ĉłϋşĩνė. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ĉřėǻŧĕ ă ŋéщ τąь !!! ! + Сŕěăтě ă йεŵ тàъ !!! ! - Ăŋ āļιдş ƒǿŕ ţĥέ "new-tab" ŝũв¢ömмăñð. !!! !!! !!! !! + Άп âłìàś ƒбř ţћè "new-tab" šûьĉòmмąйδ. !!! !!! !!! !! {Locked="\"new-tab\""} - Мøνё ƒŏĉűš ŧσ āηóţћéя рăŋĕ !!! !!! ! + Мόνę ƒöčυś τø ăиŏтнëґ φάňę !!! !!! ! - Αñ ªļίàŝ ƒоř ŧђє "focus-pane" šŭвĉömмàήδ. !!! !!! !!! !!! + Дŋ ąľιàš ƒôř ŧħё "focus-pane" śûъĉбmмаʼnđ. !!! !!! !!! !!! {Locked="\"focus-pane\""} - ₣оçμş тнε ρáήе ãτ тђė ğïνеи ϊпðēж !!! !!! !!! + ₣ôςűş ţħέ φâиє дт ťћέ ġίνéи ιņδĕх !!! !!! !!! - Ώрêп ώïţħ ŧнê ġϊνĕи ρřöƒįłę. Áćсεφτś ℮ϊťĥěř ťђê ήámĕ ŏŗ ĠŬĬĎ òƒ ª рŕöƒïĺé !!! !!! !!! !!! !!! !!! !!! + Öφěй щíţħ ťћε ğīνёⁿ φґõƒіĺė. ∆ςćëρťş ēįţнèя тĥě ņάмé бř ĜÛΊĎ ǿƒ ª рŕόƒϊłê !!! !!! !!! !!! !!! !!! !!! - Ċѓзąŧέ å πεẃ ŝрℓîт рâпэ !!! !!! + Ćřεàτę ª пєẃ ŝρℓîŧ ρãйę !!! !!! - Λņ âļіąš ƒоŗ тĥе "split-pane" śϋъсöммаʼnð. !!! !!! !!! !!! + ∆ň àŀîåš ƒоŕ тђе "split-pane" śµвсômmāñδ. !!! !!! !!! !!! {Locked="\"split-pane\""} - €ѓэâť℮ ţĥé ñĕω ρâήé дş ά нôґιžσйŧдŀ şρĺìŧ (ťніήк [-]) !!! !!! !!! !!! !!! + Ċгéåτє ţнė ñĕŵ ρáπė ąš á ħôřїżöñтάľ šрℓϊт (τћíñĸ [-]) !!! !!! !!! !!! !!! - €ґéαťĕ тħę πēẃ ρàлè âš â νêяţĭċāļ śрℓіť (ŧћіŋķ [|]) !!! !!! !!! !!! !!! + Ĉѓέāтė ţнê πёẃ ρäņё āš ā νеřťîĉâľ ŝρŀϊť (τћϊņк [|]) !!! !!! !!! !!! !!! - €яėǻτè ŧнё иэẃ ρªņέ ъў đűρļϊ¢άŧĭηģ ťћĕ φřøƒіℓę ōƒ ţђė ƒøċџśеđ φâπě !!! !!! !!! !!! !!! !!! ! + Ĉřēâťє ŧћê πέŵ φàńε ъў đûρĺϊςάтίńğ ţћε рŕóƒïℓē ôƒ ŧħ℮ ƒôċŭśεδ φâήë !!! !!! !!! !!! !!! !!! ! - Φрĕñ ïη тнě ğίνęņ ðϊřêçтőřŷ ϊπšŧęãđ οƒ тне рябƒїŀε'ŝ śет "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! + Ŏφēή ĭл τĥé ġΐνэñ đīřĕĉťǿґу îņşţěαð оƒ ŧђе φřōƒΐļė'ŝ ŝ℮ţ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"startingDirectory\""} - Óφзñ τђé ŧзřmίηãĺ щíтћ τћэ ряõνΐďēδ тĩţŀ℮ îήŝτεдď őƒ ŧђę ргóƒīļé'ś śĕŧ "title" !!! !!! !!! !!! !!! !!! !!! !! + Όρεⁿ тћ℮ ťėґmíʼnªĺ ώĩтћ τнé φѓŏνįďèď ŧιťŀ℮ ϊŋşťéāď őƒ тђε рřǿƒĩľë'ş şêţ "title" !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"title\""} - Φрèʼn ťĥė тăъ шіţћ ťĥе ŝρеςĩƒìęð ĉőĺбя, ϊŋ #ŕґĝġвв ƒθѓmāŧ !!! !!! !!! !!! !!! ! + Оρêή ţђë ŧαв ωĩŧћ τћê ŝρēčίƒιęð ćôļóŗ, īη #ггģģвв ƒǿřmåτ !!! !!! !!! !!! !!! ! - Òφ℮л ţнє τąь ώîтħ ţąъТįŧłэ òνëѓѓïδϊήģ ðěƒáųľţ ŧιţļέ ãʼnđ ѕμрφґєśšíñģ ţįтŀё сĥäйğè мéşѕдģέѕ ƒґøм τħè ǻрφĺϊćáτĩőņ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ορĕņ тĥë таь ωįŧђ тăвŤιтłё ον℮ґŕìδĭπġ ďэƒάùľť ţΐŧłę ăⁿď śüφрřĕşšΐʼnğ тíтļè ĉђąиġë měşšąģĕś ƒяőm ţћ℮ ąρφĺΐсаτίöй !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"tabTitle\""} - Íпħěřιţ τђ℮ ŧзгmïπāĺ'ѕ οωⁿ ёйνïŕоňмĕлť νǻŗĩдъŀёś ώĥëʼn ćгěаτίņģ ŧђε ⁿëẁ τäв óѓ ρąηě, ŕàŧħèг ţђăŋ ĉřēãтíňġ ä ƒřεśћ éиνìřσʼnm℮пŧ ъļõċķ. Ŧђíŝ δёƒàúℓτš ťό ѕęţ ẁħèή à "command" ίš φâśš℮ð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ϊπħěřïт тĥέ ťэґmιŋąℓ'ŝ оẅή ëʼnνīŕőпmєŋτ νâřϊǻьľėś ẁħĕη ċѓĕаťіпğ ţħë ήэщ ťäь όг ρâлë, гáтнэя τћαή ςгęâŧìñġ â ƒřеŝћ єйνιŕőñmĕπŧ ъļõĉķ. Ŧнĩś δёƒàùŀτš ţо ŝéт ωћ℮π ª "command" ιŝ ράşśеđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"command\""} - Ωρзп ťђě τªв ẁïтħ тĥз śрèčįƒίέď ćõļőґ ŝćĥèmз, ΐпѕтėąδ øƒ ťħэ φřбƒіľę'ş ŝєţ "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! + Ωрêй тħĕ ťάв ώїτħ τĥë şφěςíƒįеď сòŀõŗ ѕĉнзmе, їŋšτέåđ őƒ ŧħέ ρѓбƒîłε'ŝ ѕęŧ "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\""} - Ďĩşρŀãу τħε áррℓíćαтïöй νēŗśιóή !!! !!! !!! + Ďĩѕρłăγ ťне äφρļϊćąτìôñ νęŕѕïǿⁿ !!! !!! !!! - Ŀąϋńçħ ţћэ ωĩиďŏŵ mахîmϊźέđ !!! !!! !! + Ŀâύйĉħ ťнє ẅїñðõẃ мª×ĩмĭżэď !!! !!! !! - £āμη¢ĥ ťђë щìńδőẃ ĭŋ ƒūłŀś¢ґззπ мŏδё !!! !!! !!! ! + Ŀãűисћ тнё ωįлδøẃ ĩʼn ƒůłℓś¢гεέи mбðє !!! !!! !!! ! - Мòνě ƒōċųš τō тћę āđĵāčęⁿτ рдŋě ïņ ţћέ şρєĉįƒìēð ďїѓэçťïõπ !!! !!! !!! !!! !!! !! + Μöνě ƒǿćųś ţø ťђе ǻðјå¢℮йт φąήë іπ ťћė ѕρėćїƒіεď đϊřеċтîóñ !!! !!! !!! !!! !!! !! - Åи ãłīåš ƒõґ ţĥз "move-focus" şµвςŏмmаŋď. !!! !!! !!! !!! + Ǻπ ªℓïǻŝ ƒόŕ ţĥз "move-focus" ŝűъčοмmάπδ. !!! !!! !!! !!! {Locked="\"move-focus\""} - Ťћê ďířёċťìõʼn τõ мòνė ƒøĉúš īή !!! !!! !!! + Тнė ďίѓèċţϊōň ťö мøνè ƒθсųş ίή !!! !!! !!! - Şẁãр ťĥě ƒо¢ùŝéδ φăи℮ ŵιτħ ţħė ąđјăсéňτ рăлė ιñ тћė šφεсïƒì℮ď ďїґ℮ćτíŏņ !!! !!! !!! !!! !!! !!! !!! + Šẅаφ τћë ƒŏçŭšėđ ρąήè ώĭŧħ ŧĥē аďјā¢ēйτ рǻńë ĩп ţħε ѕφĕĉĩƒīēď đîŕэċŧїòň !!! !!! !!! !!! !!! !!! !!! - Ŧђ℮ δìřęçťîôπ тõ мõνз τнĕ ƒőčűѕ℮ð φãήĕ ïп !!! !!! !!! !!! + Ŧħε ďΐřéċтїŏй τö mθνĕ τћέ ƒőćυѕзđ рàиĕ ĩń !!! !!! !!! !!! - Ļάųи¢ħ ťĥē ẅιñďσẁ ιñ ƒŏςũş môďē !!! !!! !!! + Ŀаµп¢ђ ťћė шϊⁿδǿẃ íй ƒôćûş мöđē !!! !!! !!! - Ŧħíš рαŗāмêτēг īŝ âʼn ιиŧēŗπåļ ιмφĺєmęņтάтìöл δєтάįĺ άиð ѕĥσυĺδ иõт ъе µš℮đ. !!! !!! !!! !!! !!! !!! !!! ! + Ŧħιš рáгàмĕтêŗ ϊŝ άń íηŧєŕŋάĺ ϊmφľémёńтăţíőń đęтàĭļ диď šнθυłď ήøŧ ъ℮ ùśєđ. !!! !!! !!! !!! !!! !!! !!! ! - Śрëċΐƒÿ á ŧęŗмĩήαľ щίňđбŵ τö гύη тħέ ĝΐνęņ соmmāπδĺïиê ιň. "0" áļωåÿś ѓэƒзгś ţö тђĕ çϋггéńť щïⁿðбш. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ѕφêċīƒŷ ą ţěřmΐñåľ ŵϊňðбω ŧб řūл ŧħе ğίνéη çômмâηδłîʼnē įπ. "0" дľẃäŷѕ ґėƒëŗś тό ŧħè ćŭгяëπŧ ŵĭńðōω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ŝρêсіƒŷ τћє рσѕїťїŏй ƒõř тħ℮ тéґміŋаľ, ìη "х,ÿ" ƒõямăŧ. !!! !!! !!! !!! !!! ! + Śφ℮сίƒу ţħè ρóşΐťιöʼn ƒōŗ ŧђε тёŕmĩʼnªŀ, ìń "×,ý" ƒõŗмāт. !!! !!! !!! !!! !!! ! - Şрэçĩƒý τћê иϋмьēř ǿƒ ĉσℓцмπѕ âⁿð гõẁŝ ƒøř ŧђє τэґmíňāļ, ΐл "ς,ґ" ƒόямąŧ. !!! !!! !!! !!! !!! !!! !!! + Śρěćϊƒÿ ťħє ŋџmьēŗ оƒ сοłůмʼnś аʼnď яóẁѕ ƒõя ŧĥе тεřмίⁿǻĺ, įη "с,ř" ƒöгмáŧ. !!! !!! !!! !!! !!! !!! !!! - Ρѓęśş тђέ ьϋţťóʼn ŧò бρэʼn ά йзш ťéŗmіñаł τάь ώĩτн ỳôűř đꃪúľť рřбƒіℓε. Òрęň тћě ƒľγøŭţ τö šëłëсţ ώнì¢ћ ρŗθƒїℓě ўøμ ŵªпт тθ оφёň. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ρґëšş ţђê вúţтôʼn ţó ǿφеʼn а ňêŵ ťĕямιπаℓ ţав шĭţĥ уòůŕ đэƒäџℓť ρґöƒιŀě. Ôρēη ťнє ƒĺỳöϋŧ τо śеłęčτ ŵћΐĉћ ρřöƒϊļė уōů ẃåņт ťö θрêⁿ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Ņёш Ţǻв !! + Ňещ Ŧăь !! - Θφęň ã πεŵ ťãъ !!! ! + Öрзи д пéẁ ţαъ !!! ! - Ǻŀť+€ľĩćќ ťø ŝφļįť ťħě čϋгŕєпŧ ώίńđøω !!! !!! !!! !! + Åĺţ+Ĉŀïćκ ťó şρľïτ τħĕ čũгŗëŋŧ ŵīηďøẅ !!! !!! !!! !! - Şнìƒτ+Ĉℓіćķ ťő оφéл á ñêώ щįηðŏẃ !!! !!! !!! + Šнĩƒт+Çℓιĉĸ ţø ŏрεη α πέẃ шīήðõẃ !!! !!! !!! - Çŧŗł+Сĺі¢ќ ŧö øρ℮η ąŝ ăđмìйĩśťѓāţσř !!! !!! !!! ! + Ćтѓℓ+Сℓї¢κ тó σρёп ąŝ ªđмιйіşŧŗаŧοѓ !!! !!! !!! ! - €ℓоšė ! + Çĺøś℮ ! - Ĉłοŝé ! + €ĺοşé ! - Čŀōѕз ! + Ċľõѕě ! - Мä×ϊмîžę !! + Μάжįмîżę !! - Мϊŋímīźε !! + Μíиímĩżé !! - Мĭʼnĭmįžě !! + Μĭиімĩż℮ !! - Μΐňîmΐżе !! + Μĭńįмĩź℮ !! - ∆вőũť ! + Àвōŭţ ! - Ŝеňð ₣зéďвǻçĸ !!! + Ś℮пđ ₣зèδъàćќ !!! - ŐК + ΏĶ - Vëѓśίöη: !! + Vзŗšįоⁿ: !! This is the heading for a version number label - Ģеţŧіñģ Ŝťäгт℮ď !!! ! + Ģêťŧΐňĝ Šţàřтëð !!! ! A hyperlink name for a guide on how to get started using Terminal - Şоùяčе Сòδë !!! + Ŝσϋřćę Ĉòďė !!! A hyperlink name for the Terminal's documentation - Ďθ¢µméńŧâτĭοņ !!! + Ðσčûmейŧąţîǿη !!! A hyperlink name for user documentation - Řëŀэǻŝэ ∏ōŧèş !!! + Ґεℓ℮âѕє Ńōτэš !!! A hyperlink name for the Terminal's release notes - Ρѓίνąсỳ Роℓїςу !!! ! + Ρяίνäсу Рθļĩ¢у !!! ! A hyperlink name for the Terminal's privacy policy - Ťĥιŗđ-Ρдгţỳ Ņóτíс℮ŝ !!! !!! + Ťнΐяð-Ραяţу Йōŧîĉеŝ !!! !!! A hyperlink name for the Terminal's third-party notices - Çāñċёŀ ! + €ªňćêļ ! - Çłοśе άℓľ !!! + Ċľöŝē áłŀ !!! - Ďσ ýŏú ώǻňť ţο ĉłøѕз ǻĺĺ шіπδöщş? !!! !!! !!! + Ðǿ уǿú ẃǻлŧ ţō çŀθѕє ǻľĺ ẁĩⁿďőώŝ? !!! !!! !!! - Čāи¢ēĺ ! + Ċªñсεļ ! - Ćľθşе ąŀŀ !!! + Ĉľōѕε ãļℓ !!! - Ďø ýθµ ώαņт τø čłōşз άłℓ τăвѕ? !!! !!! !!! + Ďό γσџ ẅǻńτ ŧο сĺòśè āľŀ тäьŝ? !!! !!! !!! - Ĉαήçĕľ ! + Сāйсéľ ! - Ĉļòşę аηÿŵãÿ !!! + Çļøśé áņỳщăŷ !!! - Ẁªґпιńğ !! + Ẁαяήîʼnĝ !! - Ϋθџ ǻřё авøųт ţő ¢ľõşě ã ŗēăδ-σňľỳ ţέѓmίлàľ. Ďō ўøū шĭşђ ŧθ čòлτíʼnŭė? !!! !!! !!! !!! !!! !!! !!! + Ύõŭ àŗё āьǿŭτ ţθ çŀôşĕ â ґèαđ-õŋłÿ ťěŗмįņаľ. Đò ýбų ẁίŝħ ţŏ čôñτΐйџê? !!! !!! !!! !!! !!! !!! !!! - Ćáñćзľ ! + Ćăņçëľ ! - Ўθů αŗē аьσùт тô рǻšţé ť℮жτ тћåτ îѕ ℓöήĝєř ťндņ 5 КîБ. Ðō ýбũ ẁĭѕħ ţθ ¢óņťíņŭě? !!! !!! !!! !!! !!! !!! !!! !!! + Ύõµ ąѓê åъоϋţ ŧø ρдşŧĕ ţę×ţ ťђáτ ίş łǿлģ℮ѓ ţнăń 5 ЌїБ. Đō ÿőų ẃĩśћ тō ¢όⁿťΐлůз? !!! !!! !!! !!! !!! !!! !!! !!! - Ρáşťε ǻиÿщáỳ !!! + Раŝтε ãńỳщāу !!! - Шãŕŋĭⁿġ !! + Ẅǻŗήĭňĝ !! - Сàñčéŀ ! + Ĉąŋćėĺ ! - ¥οµ ǻřé āвöüţ тό φāŝťě тěхт τђâť ¢øŋťăīиš mµŀťΐρŀέ ĺìñèѕ. ΃ γоŭ рăśťэ ţнìś ţĕжτ ιʼnτǿ γοџѓ şђэℓŀ, ΐť мαý яέśųľт ΐй ŧђз ūηë×φĕçтĕδ зж℮ćϋţīøй öƒ ćòмmăйďš. Đø ýôΰ ŵĩѕђ ťσ ¢οήťĭņüë? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ўоυ àяє āьôυť ţŏ ρăŝтè тежţ τђãτ čǿŋтдĩηś mцŀŧĩрℓě łìʼnėš. ΃ ýσц ρǻŝтê τђίś τě×τ ìηťŏ ỳσůґ śћēļŀ, íт мäŷ гëśцℓт îň тћё ųпèхφєсτєδ ė×êćџтίőņ бƒ сøмmąлđŝ. Ðò ỳǿџ щīŝћ ťö ¢óńŧĭиџě? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Рάѕτě ăπýшãў !!! + Рдşťë άñỳŵåŷ !!! - Ẃâгиĭήģ !! + Ẃąŗпĭʼnġ !! - Τÿφę å ċòmmдňδ ήάm℮... !!! !!! + Ťÿрε ǻ сõmмǻлδ лàме... !!! !!! - Лó mąŧċħΐʼnğ ćοmmāиδѕ !!! !!! + Пō мάŧ¢ĥιпģ ĉőммάлδš !!! !!! - À¢тïøй ѕéâяćћ мǿďė !!! !! + ∆сτîõņ š℮αŗčħ mõðэ !!! !! This text will be read aloud using assistive technologies when the command palette switches into action (command search) mode. - Ţдв тĩţℓė мбðě !!! ! + Ťаь тíťĺê mθďе !!! ! This text will be read aloud using assistive technologies when the command palette switches into a mode that filters tab names. - €òmмаʼnđ-ļΐήё mбđĕ !!! !! + Сòмmªπď-ℓìņê mǿđē !!! !! This text will be read aloud using assistive technologies when the command palette switches into the raw commandline parsing mode. - Мοŕε ŏφţîοŋś ƒθŕ "{}" !!! !!! + Мòŕε õρτίǿήѕ ƒŏґ "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Έхęсûţïñġ сбmmåńð ℓїʼnє ŵϊľļ іпνǿќ℮ тнє ƒōłļøẃīйģ ςöмmдņđŝ: !!! !!! !!! !!! !!! !! + Έжĕćũţіпġ çǿмmаńđ ŀîπè ώíļļ íлνōķě ťћз ƒőłłǿщίήğ ĉθмmäňðś: !!! !!! !!! !!! !!! !! Will be followed by a list of strings describing parsed commands - ₣àįĺэđ φàŕšîπĝ сθмmаиδ ℓίйė: !!! !!! !! + ₣ąĩľĕď рàŗśìπğ ¢бmмаπδ ℓιńє: !!! !!! !! - Čõмmąήď Ρâŀзτтε !!! ! + Ċǿмmāňδ Ρäℓέτťę !!! ! - Ťǻь Ŝώїťčћëя !!! + Тáъ Ѕẁĭŧ¢ħĕř !!! - Ţуφе α ŧãъ лāмэ... !!! !! + Τŷρė à ţãъ ńаmё... !!! !! - Ñŏ мāт¢нійġ τǻв лǻmë !!! !!! + Ŋο mãτ¢ħįпģ ŧдв ŋámë !!! !!! - Ëŋŧέґ ā wt ċöмmªńδłíηё ŧо гŭⁿ !!! !!! !!! + Ēητėя α wt ćōмmάпđłįήę ŧô ŗúŋ !!! !!! !!! {Locked="wt"} - Мõřĕ οφťĩόňś ƒòѓ "{}" !!! !!! + Мθґέ орŧîõиŝ ƒøř "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Ťŷφэ д çōммâⁿδ ńàмέ... !!! !!! + Тŷφє à ςόммãņð ñāмė... !!! !!! - ∏ŏ мдťçħīŋğ ςõмmăиδş !!! !!! + Ñǿ mäŧčћіиğ соmмªŋďŝ !!! !!! - Ŝūğģєŝтΐόйś мέⁿυ !!! ! + Ѕµğğêŝţïöήš мзиū !!! ! - Μǿѓэ òφţíǿиѕ !!! + Мбяё òрţϊõňş !!! - Ŝúģĝеŝţíθⁿŝ ƒσųйď: {0} !!! !!! + Śμġģëѕτϊǿňš ƒσϋⁿď: {0} !!! !!! {0} will be replaced with a number. - Ċŕīmѕοη !! + Ċŗΐmşǿπ !! - Şŧэéł ßļüę !!! + Ŝτеêľ Вŀüĕ !!! - Мêδιџм Şєǻ Ğяě℮ņ !!! ! + Мέďîųm Ŝéª Ĝŗεзп !!! ! - Đäřќ Όŕāŋģе !!! + Đдгκ Οґâήĝë !!! - Мėδϊùm Vїόℓєť Яеď !!! !! + Мėðįΰm Vïøľέţ Ŗěð !!! !! - Ďбđġęř ßĺυё !!! + Đбδģĕя Ъłūě !!! - ₤ϊмē Ġŗэзⁿ !!! + ₤îмè Ģґéëη !!! - Ýèŀłσẁ ! + ¥еĺľόш ! - βŀυě Vìøľеŧ !!! + Ъℓцê Vĩöŀěτ !!! - Şŀãтę Ъľųё !!! + Ѕŀάťê Ъŀυé !!! - £ϊmε ! + Ľίмё ! - Τàň + Ťāʼn - Мāğęήтâ !! + Мαġęпτă !! - Çỳàň ! + Ćγąή ! - Şκў βľµĕ !! + Ѕку ßļυ℮ !! - Ďªřķ Ğѓάŷ !!! + Ďářк Ĝгāу !!! - Тћїŝ łїńк îš ïⁿνάľïð: !!! !!! + Ţђíš ℓιпĸ іŝ ΐиνªĺίδ: !!! !!! - Ťђїś ĺίпĸ ťýφє іš čΰŕřěлŧľу ŋόŧ şũрρθґτэð: !!! !!! !!! !!! + Ŧĥіś ĺĩпќ тỳφэ ϊš čџяѓёиŧŀỳ πøţ šũρρōŕťэď: !!! !!! !!! !!! - Сαпсêŀ ! + Čǻŋ¢έł ! - Śěтŧΐπģş !! + Śéтţīйġŝ !! - Ẁ℮ сǿµŀδ ибť ẁŕìŧ℮ ţō ýǿύř šёťťίήģš ƒϊĺе. Сĥέçќ τнē р℮řmîşşιолš οи τђàŧ ƒĭĺè ţο εńŝũяĕ тнåŧ ŧħэ гêáð-бⁿļÿ ƒľąĝ ĩş ņøť ŝëτ âπď тħàť ẃѓįţę äсćėŝŝ íš ģяάлτêð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ŵэ ĉøűļδ πōτ ẅяìťė ťõ ўоūŕ ŝěŧţĩηġš ƒΐĺĕ. Çћěςк τћε φĕŗmìѕšιőʼnѕ øл ťћāт ƒïŀĕ ŧò єŋśџѓз ŧндţ τћè řęàđ-όŋľγ ƒłàģ їš ñσŧ śĕт ãπð тћâτ ŵяιťę āč¢ěśѕ ΐş ĝяāⁿţêđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Бãçķ ! + Бåсκ ! - Βäċк ! + Βåск ! - ŎЌ + ΏΚ - Ďёъμğ ! + Ðєьùğ ! - Еřřôґ ! + Éѓяσř ! - Ĭйƒòгmäтїοп !!! + Íŋƒσгmаτіθŋ !!! - Ŵàŕήįπğ !! + Ẅąѓŋιņĝ !! - Çĺіφъσдґð ςŏňŧ℮ήŧş (рřêνїэẁ): !!! !!! !!! + Ćĺίφвõάгď ćǿπтєπτş (φřêνїέω): !!! !!! !!! - Мǿřë σрŧіõñŝ !!! + Мóřė ôрťίōήś !!! - Ŵΐпðοŵ ! + Ẁιлδõẃ ! This is displayed as a label for a number, like "Window: 10" - ûñήåmêď ẁιηđőώ !!! ! + μńηаméð шїñδόщ !!! ! text used to identify when a window hasn't been assigned a name by the user - Элţêř à пёẅ ήåмě: !!! !! + Ěпŧěŕ ª ŋėẁ ηàmè: !!! !! - ǾЌ + ŎЌ - Ĉāⁿčėĺ ! + Сàπçёļ ! - ₣дĭļеđ тő řēŋâмэ ŵìʼnδòώ !!! !!! + ₣âΐļěð ťθ řэπамè ẃíлðоώ !!! !!! - Åлŏţћεґ ẅïлďбω ωїťн тћåŧ лªmέ åŀѓέäđÿ ε×ιšŧş !!! !!! !!! !!! ! + Ăлóŧћėґ щίňđοŵ ώίţн ťћдτ ήåме äļřёǻδŷ ęхіşţş !!! !!! !!! !!! ! - Мà×ΐmϊžз !! + Мàхιmįżє !! - Ŕёśтŏřė Ďоωл !!! + Ґέşτοřę Đόẅň !!! - Ĉŏmmàпδ Рàľéŧтє !!! ! + Ċоmmäňδ Рâĺеŧţз !!! ! - ₣öċūş Тēŗмīʼnåł !!! ! + ₣σċΰš Ţэŗмįņаĺ !!! ! This is displayed as a label for the context menu item that focuses the terminal. - Ẅϊŋδøщѕ !! + Ŵΐŋđσẃš !! This is displayed as a label for the context menu item that holds the submenu of available windows. - Ôрëй д ņĕώ тăъ îл ĝĩν℮п ŝţаŕтΐлġ đìгëĉτбŗў !!! !!! !!! !!! + Θрēŋ д ŋėẁ ţǻв íπ ģίνзл ѕтάřтіηğ δϊřėĉţοŗŷ !!! !!! !!! !!! - Õφęπ а ńèώ ŵīņðόẁ ŵìτĥ ğїνêⁿ šťªřŧιлğ ðìřéċťθřў !!! !!! !!! !!! !! + Õρεņ ª ňēŵ ωĩńđôщ ẁΐŧђ ĝíνзή ŝŧáґτϊⁿĝ ďîѓėςťőґý !!! !!! !!! !!! !! - Šрłίŧ ŧђė ẁíňđοώ ăйδ şţαřŧ ιŋ ĝīνėη ďιгёстōяў !!! !!! !!! !!! ! + Şφłĭт тнë ώīñðőŵ ªпδ ŝţаŕŧ ĩñ ĝíνел δίŕê¢ŧбŗý !!! !!! !!! !!! ! - Ęжφθґŧ Ŧεхτ !!! + Ěхρŏяť Ťэжţ !!! - ₣ªіłєđ ťø èжρóřŧ тĕямΐʼnãļ čóлťεπт !!! !!! !!! + ₣àΐĺėδ тб эжφǿѓт ţěгмìήāĺ ¢θňŧēņţ !!! !!! !!! - Śůĉčěşşƒцℓĺγ ехρõřŧёð ŧéѓміπăļ сσήтзηт !!! !!! !!! !! + Šΰç¢ēśśƒűŀļý ε×ρоѓтеď тεямĭηåℓ ¢øʼnŧēŋţ !!! !!! !!! !! - ₣ìπδ ! + ₣ìňď ! - Рļάįñ Τĕхт !!! + Рℓǻïⁿ Τęжţ !!! - Ţзřмїпäţΐоŋ вэħανĭõѓ çāⁿ вз соňƒįġμѓёδ ïŋ ǻđνąйсėð ρґθƒїļě şёŧťіņĝš. !!! !!! !!! !!! !!! !!! !! - - - Ŵįņďŏẃŝ Ťëřмįňàℓ ¢ąʼn ъė ś℮τ άŝ тħє ďęƒáυłţ ţėямīйäł àррłĭсåτΐоń їл уθüя şέţтίʼnģš. !!! !!! !!! !!! !!! !!! !!! !!! + Τêŗмίⁿàťïöń ъёĥǻνіόг сдπ ъ℮ čθπƒĩĝüŕеď ĩñ âðνªňč℮đ φѓòƒìĺę šεţţïπğš. !!! !!! !!! !!! !!! !!! !! - Ðθń'ť šнöẁ áġαĭл !!! ! + Đǿʼn'ţ śĥőŵ ãģάΐń !!! ! - Тĥіѕ Ťėŕмілāĺ шіηďθщ íš гūŋʼnįйĝ äś Àďmіⁿ !!! !!! !!! !!! - - - Ορēй Ŝęтţĩňĝś !!! - This is a call-to-action hyperlink; it will open the settings. + Τћìŝ Ŧэґmїńâļ ẁïπđōŵ įš řüлñιņġ ăş ∆ðmîň !!! !!! !!! !!! - Şυġğēśτįōηś ƒôüлđ: {0} !!! !!! + Ŝцĝġеşŧіǿⁿѕ ƒōύйđ: {0} !!! !!! {0} will be replaced with a number. - Čћєćĸîлģ ƒоŗ υφđàţеş... !!! !!! + €ћëςĸįńĝ ƒǿř ûρðăтéš... !!! !!! - Αή ΰрδäŧē їś åνдΐļáьľэ. !!! !!! + Àń ŭрďáŧέ īš ãνãїľдъļé. !!! !!! - Ϊņŝŧάľľ ňòш !!! + Îήѕťªĺℓ йøω !!! - Τнз "newTabMenu" ƒιεłð ςòπтàĩйş моřę тĥăⁿ ǿňě эŋťřў ōƒ ţўρз "remainingProfiles". Øπŀý ŧнέ ƒίřşť οņě щįŀℓ ъě ςóņśίđєѓĕð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τће "newTabMenu" ƒįєŀð çōиŧάíňѕ мοяё ŧħãʼn θπĕ ĕπτѓý òƒ τÿρē "remainingProfiles". Ωйℓу ŧђè ƒϊѓşť øⁿе ẁιľľ ъё ¢ŏлѕįδèѓęď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="newTabMenu"} {Locked="remainingProfiles"} - Ťĥз "__content" ρřǿреŕτý îş ѓеѕεŕνëđ ƒǿя īⁿтзřňάļ üѕè !!! !!! !!! !!! !!! + Ŧĥę "__content" рřǿрёřтŷ ιѕ гéŝēяνеď ƒоѓ ιńτëŕидĺ ũśэ !!! !!! !!! !!! !!! {Locked="__content"} - Õρêń ά đїάĺŏġ ċóηтàιņíηġ φѓôđμςτ ĩŋƒбгмåţįŏй !!! !!! !!! !!! ! + Õрęп α δίªĺōğ čøńťąιйіиġ ρгôδúĉτ ĩʼnƒōґмаŧіóń !!! !!! !!! !!! ! - Ǿрèň тћё сōℓσŕ ρîçκèŗ ţò сђôōśє τћέ ¢ǿŀõѓ οƒ ŧнίş тáь !!! !!! !!! !!! !!! + Οрёň ťħэ ćοłōř рĭ¢ĸєŕ ťő čнθόŝé тђě čоłοѓ ôƒ тћίš ταъ !!! !!! !!! !!! !!! - Ωρєй тћé ĉθмmãņđ ρąĺэţťĕ !!! !!! ! + Ωрěп тћέ çōммалδ φāĺĕттз !!! !!! ! - Ôφзи ă ήзщ ŧăв ΰŝīňğ τђè āċтìνĕ рŗбƒĭľ℮ ΐл τн℮ ¢üŗŗєπт ďίřëсťŏŕý !!! !!! !!! !!! !!! !!! ! + Ώρєπ ä ʼnĕω ťåъ цѕΐʼnġ τĥė аċťĩνε рřθƒįŀ℮ ìŋ тħё ςűгřэиţ δíязçтøŗў !!! !!! !!! !!! !!! !!! ! - ∑жφòŗτ τħз сóлťейτŝ őƒ τђē тěם ъųƒƒέř ĩʼnťŏ ä ŧехτ ƒïℓē !!! !!! !!! !!! !!! ! + Зхρøгτ ťнę ¢ǿŋт℮ňţş бƒ ŧне ŧė×ţ ьůƒƒĕŗ ĩлţő ä тэ×ţ ƒіℓê !!! !!! !!! !!! !!! ! - Ωφэл ŧће šęáяςћ δϊдĺöģ !!! !!! + Ôрēň тћє şеªŗсħ ðįãℓòġ !!! !!! - Řêʼnåmе ŧћіŝ тαв !!! ! + Ѓεńǻмë τнìѕ ŧâъ !!! ! - Оφėп τħέ ş℮ţтίňġѕ ρаğε !!! !!! + Όφéп ťћέ śěττîŋġś рåģē !!! !!! - Φρел д ńěщ φаŋε űŝĭйğ ŧнë ąčťīνз рґôƒîℓё ĩл ŧĥé ĉϋŗгěńţ đīřĕĉţòяÿ !!! !!! !!! !!! !!! !!! ! + Ōφĕη ă лěщ φàʼnё üšīпģ тħэ àĉτîνз ρгοƒιŀ℮ îŋ τħё ċùгřĕñτ ðīřěćţбŗу !!! !!! !!! !!! !!! !!! ! - €ℓôŝê ªłℓ ťâъš тô ţђэ яîġђт õƒ ťнįş ťǻъ !!! !!! !!! !!! + Çłōśе ąłŀ тąьŝ тó тћé ŗīĝĥт ôƒ ŧђįś ťâь !!! !!! !!! !!! - Çľőŝë åłℓ ťдьş ĕж¢еφţ ŧнϊš τâв !!! !!! !!! + Сľбśé ãĺĺ тåвŝ ёхĉëφŧ тħΐş тǻв !!! !!! !!! - Сľθѕě ťнιş τāв !!! ! + Čłόšé ŧħϊŝ τάъ !!! ! - Ємρţγ... !! + Éмρтÿ... !! - Сłοŝ℮ Рãʼnė !!! + Ċłóşę Ρдņë !!! - Ĉℓõšë τħë åςтîνє рαʼnē įƒ múĺťîφĺе раņέš àгε ρѓęšèńŧ !!! !!! !!! !!! !!! + Сŀθşë ŧħє äċťïνз рąήз ιƒ mΰľťϊφļε рãήёś αгє ргęŝέпŧ !!! !!! !!! !!! !!! - Яëŝзт τāв ĉоľбř !!! ! + Ґεšεт ťаъ ćöľòŕ !!! ! Text used to identify the reset button - Мθνé Τąь ŧö Ňĕẃ Ẅïπðǿẃ !!! !!! + Μονê Ťǻв ţô Иéẅ Ŵìʼnđŏώ !!! !!! - Μǿνέś тáв ţø ά лєш ωįňđǿш !!! !!! ! + Мбνеѕ тåь ťо ä лěẃ ẃιņðóώ !!! !!! ! - Ŗŭл дś Аδmΐπíšτŕáţσř !!! !!! + Ŕцⁿ ãѕ Àδмιⁿíŝтřăţθя !!! !!! This text is displayed on context menu for profile entries in add new tab button. - Δстĭνé рàηз мōν℮ď ŧő "{0}" тąь !!! !!! !!! + Дсτϊνē рдйë mŏνεđ ţö "{0}" тǻъ !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. - "{0}" ŧáъ môνëδ ŧǿ "{1}" ωìñďόŵ !!! !!! !!! + "{0}" ţąъ моνеð ťο "{1}" ώіňďõω !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the name of the window the tab was moved to. - "{0}" ŧάъ môνėð ŧο ŋēш ωĭⁿđбẅ !!! !!! !!! + "{0}" ŧäь мőνзđ ŧõ ñěщ ẁιňďòш !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. - "{0}" τаъ mòν℮đ τό ρŏśîťїой "{1}" !!! !!! !!! + "{0}" ťаъ mõνеđ ŧб φόśĩťίθп "{1}" !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the new tab index in the tab row. - Ąĉťіνę ράñę møνěδ ŧó "{0}" ţǻь ϊⁿ "{1}" ώĭņďòω !!! !!! !!! !!! ! + ∆ćŧινë ρáлě мбνęđ тθ "{0}" ťăв ĩπ "{1}" ẃΐπđõщ !!! !!! !!! !!! ! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. {1} is the name of the window the pane was moved to. Replaced in 1.19 by TerminalPage_PaneMovedAnnouncement_ExistingWindow2 - Δ¢ţĩνê φāηє mōνēδ ŧø "{0}" ώϊиďőŵ !!! !!! !!! + Λçťìνę ρǻñе моνéď ţò "{0}" щĭŋδõш !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the window the pane was moved to. - Äçťĩνê рàηę mбνėδ τо ήęẅ ώíⁿđőώ !!! !!! !!! + Á¢ŧìνě ρãńè möνēđ ťó ʼnèẅ ώιňδõẅ !!! !!! !!! This text is read out by screen readers upon a successful pane movement to a new window. - А¢ťίνе φªʼné mǿνĕđ τŏ ñęώ ţªь !!! !!! !! + Αςтïνέ рάйз mōνëď ťб ňėẃ ŧаь !!! !!! !! This text is read out by screen readers upon a successful pane movement to a new tab within the existing window. - ݃ şèŧ, τĥэ čǿмmαňδ шιłł ь℮ åрρêпđεď ťö ťђе рґōƒίļε'ś đ胪ύℓŧ ĉόmmāńð ìŋšťëăđ őƒ ѓęφĺã¢ìлġ їτ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Īƒ śэť, ţħĕ čоmмăήď ẁįŀļ ве ªрρєπδёð ŧō ŧћę ρřõƒīŀє'ѕ ďεƒåμℓτ ¢ǿmmάňď ιηśтēąď õƒ řéφĺāĉΐⁿġ īţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ґэѕţăґţ €ŏňйĕ¢ŧίŏň !!! !! + Γèѕťāřţ €οллзčţïσπ !!! !! - Яèśŧªяŧ τћє âćτĩνε рäиē сόηήё¢τĭóи !!! !!! !!! ! + Яēśťâŗт ťћз áċтΐνę рǻń℮ сøⁿηėčťϊôή !!! !!! !!! ! \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw index 4a269ba1f5f..13302a27a5c 100644 --- a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw @@ -118,148 +118,148 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ѕеτťϊńģś сőúľδ ʼnǿŧ ъę ľоâðέð ƒґŏм ƒìŀè. Ċћêċк ƒőŕ ѕỳńŧà× ℮ґґôŗŝ, ΐпсŀúďīйĝ ţřάіĺΐлġ ćоmмåš. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Śěţťĩπĝš čǿùĺδ пθť ьє ℓбãđėð ƒŕθм ƒίłє. Ĉнέ¢ķ ƒòŕ šÿńťá× ℮ŕřõřŝ, їηćľúδїⁿğ ťřªíļϊпġ ςømмаś. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ċŏµĺđ ŋǿţ ƒϊпð ÿθųя ďэƒдΰľτ рřøƒìľė іπ γøũŗ ĺïšť öƒ рŕőƒіļéš - ŭşĭπğ ţћĕ ƒïŗšτ φřбƒіŀę. Ĉĥêçк ŧό мáќέ ŝμѓэ ťђê "defaultProfile" mãŧсĥέš ŧнę ĜŬĮÐ оƒ ŏпē õƒ ỳøцŗ φґθƒìļεś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ćòџℓð лθт ƒĩήδ ỳôùř ðеƒăųłŧ φřőƒĩℓ℮ ïή ýŏúя ĺìѕţ οƒ рґοƒΐĺεś - ũŝΐпġ ťнё ƒίґşт ρŕбƒĩℓε. Сђэčќ ţθ мąκε şυřě тнё "defaultProfile" мαţĉĥéŝ ţнĕ ĠŮİĎ ôƒ öпė öƒ ŷŏΰŕ ρŕõƒΐĺêş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"defaultProfile\""} - ₣оüñđ мΰļťìφłε ρѓőƒíŀéѕ шîтн τĥє śămë ĞŬÍÐ įņ уōΰř ş℮ţţįņĝŝ ƒΐŀέ - ĩģηöяіŋģ ðυρĺĩ¢ãťèѕ. Μáĸе šūřę êãċћ ряőƒïļе'ŝ ĞÛĮĐ іѕ üñіqűė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣ōûńď мцľтϊρļè рřõƒιļέŝ шίťħ ťнé śámē ĞÚĮÐ ΐπ уōůř ŝέтŧìňĝš ƒïℓε - ïġʼnǿŗìήģ ðϋφľϊςãτēŝ. Мαķĕ śùѓэ ēǻ¢ĥ φŗōƒїℓę'ś ĜЏЇĐ ιş üηϊqúě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - ₣öµŋď á ρřõƒΐŀε ωīτĥ āή ϊйνάľΐđ "colorScheme". Ď℮ƒàμŀτΐņğ тћªτ ρŗŏƒĩŀз τŏ тĥę ďёƒãűłт сσłǿŕş. Μдкě šμѓě тĥдţ ẅĥęň šėťтĭπģ ǻ "colorScheme", ťђê νâĺцė māţċħêş ţћэ "name" ŏƒ ã çοŀŏŕ ѕĉћëмз ìñ ţĥё "schemes" łïѕť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣όϋήð ă рѓόƒïłё ẃìτĥ дŋ ìпνáļїδ "colorScheme". Ďεƒαµļţïʼnğ ťћäť ряόƒіĺë тσ тĥέ δêƒαųŀŧ ¢ôļòřŝ. Μǻκэ ѕůяέ τĥдτ ώн℮ň şэττĭπģ à "colorScheme", тђё νãĺυĕ мāтćћэś ŧћэ "name" õƒ á çöĺόя ѕćнêмε ιπ тћё "schemes" ľĩşť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - Ио ρŕθƒϊℓêŝ шзяę ƒøűлð ĩπ ỳôűŕ şēţŧĩʼnġѕ. !!! !!! !!! !!! + Йθ рѓŏƒįℓëś шëřэ ƒοųπδ īπ уσυŗ ѕēтťιⁿġѕ. !!! !!! !!! !!! - Äłľ φґοƒìŀęš шèґέ ђιðđéή їʼn ўθμя ŝеťтĭňĝś. Ϋǿú мųşť ђãνé àт łêαśť σлĕ ŋόп-ћïďďēń ρґоƒіĺέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + ∆ŀļ ρѓõƒîľëś шėгę ĥíðδėл ΐň ÿóŭг ѕέŧŧіňġś. Ύőú múşť ђǻνē άт łєáşť öήє ňòπ-ĥĭđđ℮ņ φґǿƒìłè. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ŝèŧťìήğѕ сőůŀď ʼnõţ вє гêłóäðзđ ƒгøm ƒїĺē. Čĥêςĸ ƒöѓ šўʼnτάж ēґґόŕš, ϊⁿčļüďϊńğ ŧŗăįļíиģ ¢őmмãś. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ś℮ттΐиġš čбμłď ñοŧ вė ґęłθǻδεð ƒяσм ƒіĺë. Ċнęĉќ ƒōѓ şγņтåж εŗґõřŝ, ïйçℓūδìⁿĝ τґàιļĭйĝ čοмmáş. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Τėmрθѓάѓίℓÿ űşįŋġ τће Ŵįήďöщѕ Ťеґmϊйåĺ δзƒąύĺτ šετťιńğś. !!! !!! !!! !!! !!! ! + Ťĕмрöґāŗìŀỳ μŝïηĝ тћê Щϊʼnđôωŝ Ŧêŕмīлªł δέƒáυŀτ şεŧтĩйģş. !!! !!! !!! !!! !!! ! - ₣άίĺëđ ťó ℓöâď śзŧţíйğѕ !!! !!! + ₣ąîℓ℮đ ŧо ŀŏªð ŝêŧţιŋġŝ !!! !!! - Эńçθųπτ℮ѓзδ éґѓøřś ẅħĭłє ļōăδίñĝ ΰŝεŗ šéττιńģš !!! !!! !!! !!! ! + Èηсòųņťêřĕδ êґґοřś ώħΐŀё ℓόǻďīπĝ ůśęя şęтτîпĝş !!! !!! !!! !!! ! - ŐЌ + ÖК - Ŵåŕńĭηĝ: !! + Шāгńιηğ: !! - Тĥ℮ "{0}" ΐšл'ŧ ґũňňιňģ οⁿ уǿűѓ мåćħĭⁿë. Ţђįś çåи рŗєνэŋť тħє Ţĕґмīлáł ƒřøм гęċеìνϊʼnġ кēўвŏāґδ ίⁿрùŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Тħε "{0}" ιšⁿ'τ ŕūппїñğ ôη ỳóüѓ mªςĥįⁿз. Ŧħіѕ čαň ρгєνёлť τђè Тέřmîñàľ ƒřöм яέçěĩνїηġ кèÿъøǻŕď ìⁿρûт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the OS-localized name of the TabletInputService - ŌĶ + ÕК - ₣āϊłĕď ťŏ ґêļöǻď šèţŧïήĝś !!! !!! ! + ₣ăīļёð ťθ яеľõăð šĕτťīⁿġś !!! !!! ! https://go.microsoft.com/fwlink/?linkid=2125419 {Locked}This is a FWLink, so it will be localized with the fwlink tool - Ąъόűт ! + Ąъõµţ ! - ₣еέďвàĉķ !! + ₣еёđьäçκ !! - Şέтŧîπģś !! + Ѕεττīπĝѕ !! - Çáлςεł ! + Ćªήċëļ ! - Ćłŏŝз àŀĺ !!! + €łθşз åℓĺ !!! - Qύíт ! + Qųìτ ! - Ďб ỳóυ ẅáпţ ťŏ çĺóśē дľł ŧăвş? !!! !!! !!! + Ðǿ ÿоџ ẃαπτ ţό çŀòšė äļł τăъś? !!! !!! !!! - Μύℓţïφŀě раņεş !!! ! + Мџłтĭрŀё φăйĕş !!! ! - Çℓöśэ... !! + €ℓöśε... !! - Čŀōšέ Ťâъš ŧő ţнē Ŗīğħτ !!! !!! + Çŀõśέ Ťāвś ŧǿ τħê Ŗϊğћţ !!! !!! - €ℓбş℮ Οťнęґ Ťªвş !!! ! + €ľσśĕ Φτнеř Ŧåъѕ !!! ! - Ċℓоśε Ťăъ !!! + Çľōšέ Ŧåь !!! - Ĉŀóšё Ρåήэ !!! + Ĉłôšē Ρдйє !!! - Ѕрŀîţ Τªь !!! + Ŝφĺïť Ŧāъ !!! - Śρℓіţ Рăпě !!! + Şрℓіτ Ρàŋé !!! - Ẅėь Ŝєªŗçн !!! + Шёъ Šзäґĉĥ !!! - Ċøℓθг... !! + €θľŏř... !! - Çџѕτøм... !!! + Ċűşţöм... !!! - Řέŝёτ ! + Я℮š℮т ! - Язńдmэ Ŧąв !!! + Ŗěήąмë Ţдв !!! - ϵφłїčåţę Ţǻь !!! + Ðûρℓĭсåτë Ŧąь !!! - ₣ŏџʼnď д φґôƒíŀе ώíŧĥ âη ϊⁿνåℓΐδ "backgroundImage". Đеƒãùĺţΐⁿğ τĥαт φřôƒїľэ ŧő ђανе ñø ьª¢ќğŗőųʼnď ìмǻĝе. Мªκέ şŭѓę ţнªţ ωħěŋ ŝéττīńğ ä "backgroundImage", тћĕ νǻℓϋĕ įš д νάĺĭδ ƒίℓĕ ρªťħ ťō ǻń ìmãğє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ₣őůⁿð ã рŗοƒіℓє ẃіťђ ãň іήνäļîđ "backgroundImage". Đęƒąΰľťįлģ тħăт φгοƒĩľê τò ħдνє ʼnò ъâçķġѓøύñđ įмãġ℮. Μåкз şųґе тћåţ шĥеņ şęŧŧϊňğ ª "backgroundImage", ťђè νдℓϋ℮ ìś д νáℓïď ƒįļĕ φăтћ ťò аⁿ іmáġë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"backgroundImage\""} - ₣όυйð ά φгόƒĩľё ŵïŧћ άʼn ιŋναľíð "icon". Ðëƒâûŀτìňğ тђάт рѓόƒìℓέ ťō ĥãνę ńǿ ìςöи. Μåќε şůгε τħаτ ẅђèŋ ŝëŧťîήģ άл "icon", тћè νáłůě іş ä νаļїð ƒїℓě φäŧн τŏ αή їмǻğë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣σΰńð ǻ φřοƒĩĺê щϊťĥ åл ĭñνąĺïď "icon". Đзƒªцℓťĩήĝ ŧђат φřőƒįŀê ŧõ ђανэ йõ і¢σп. Мαķę şùřё тĥăτ ωнĕⁿ ѕėţŧιлğ ǻη "icon", ťĥè νáłúз įś ª νаĺįð ƒĩľе ρąţћ тő åи îмąğě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. - Щāгňĭηġş ωéяε ƒŏμńð ẃћΐļз ρàѓşϊʼnĝ ўбμř κęỳвĭʼnďīйģŝ: !!! !!! !!! !!! !!! + Ẅäřпĭņğş ẁеѓĕ ƒоūηð ẃђΐĺë ρªяŝίⁿĝ ỳθůѓ κèўьΐπđìⁿġš: !!! !!! !!! !!! !!! - • ₣ǿųñδ α кэýьїńδιлĝ ẃϊťћ ţŏό мàйγ ѕţřίлģŝ ƒоř τђê "keys" αřгåў. Тђėřє şћόūļđ óиℓỳ вé бňз ѕτґϊπĝ ναļцê ιп тħë "keys" áяŗάу. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + • ₣øυňð ā ќєỳьīńđΐñĝ ŵîťĥ тòό мάņу ŝŧřΐʼnġѕ ƒòґ тĥε "keys" άŕґáý. Ťђεřē şђθųĺđ οиĺŷ ье øήę ѕţŕįŋğ νªłüё ĩη ŧħё "keys" äřŗάý. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"keys\"","•"} This glyph is a bullet, used in a bulleted list. - • ₣ăíļеδ тó рǻŕśě аĺļ şцъĉоmmªиδś σƒ лёѕţэď ¢őммãⁿδ. !!! !!! !!! !!! !!! + • ₣ªίłęđ τо φªґŝё āľℓ śúъçǿmmäⁿðş σƒ лęśτĕđ ςŏмmάηð. !!! !!! !!! !!! !!! - • ₣ôúńδ д ķэўьįⁿðìйĝ ťђǻţ шąś mìşѕìņĝ á řēqµìř℮ď рáѓǻмëţёг νªľü℮. Τĥĩѕ ķзγвĩиďįпĝ ŵíℓł в℮ ìğńόŕєð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • ₣õџиδ д ќ℮ŷвïηďϊňĝ ŧħàţ ωаѕ мίşšīήğ ǻ ŗеqΰįŕėð φăґάмēтêŕ νâļûé. Τнìѕ κėŷьîηďìήĝ ẃίļŀ ъĕ ìġπōŕėđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - • Ťћė śφ℮ćϊƒϊēð "тђέмέ" шàš ñότ ƒöûπδ îή ŧћĕ łĩѕт бƒ τĥęмĕѕ. Ŧĕмρσŕãŕΐĺγ ƒáłĺīηġ ъáċќ ţο тђε ďėƒāμľŧ νаľŭě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • Ŧħ℮ ŝφéċĭƒίёđ "тђэmê" ώάѕ ňöŧ ƒőůŋð íи τћë łїšť øƒ тђемèş. Ţєmφôŗàřιℓÿ ƒàłłïńğ вд¢ĸ ťθ тĥє ð℮ƒªυľť νąľμэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - Ŧħе "globals" ρґόрєŗţÿ įš ðëφѓêĉåτėδ - ўóųґ şεтţΐйĝŝ мΐġнт ⁿêėď úφđαťĭⁿģ. !!! !!! !!! !!! !!! !!! !!! ! + Тнė "globals" ρґόφëґŧў ĩŝ ďéρгέċàţęð - γоμѓ şёťţїńğş mιģнť ŋ℮êđ úρđãťΐņĝ. !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"globals\""} @@ -267,642 +267,635 @@ {Locked}This is a FWLink, so it will be localized with the fwlink tool - ₣бг møŕē ìиƒô, ŝзĕ ŧћϊŝ ŵèь ρâġè. !!! !!! !!! + ₣ǿř mőřё ілƒŏ, şęє τђΐѕ ẁэъ ρâĝē. !!! !!! !!! - ₣āіℓ℮ď ţσ ĕжφãⁿđ а çŏмmдиđ ẃїтĥ "iterateOn" šĕť. Ţнιŝ ¢ǿмmàлδ ẅιŀľ вě įġлоŗєð. !!! !!! !!! !!! !!! !!! !!! !! + ₣äïļēð το э×рåиδ ά ¢όmmäňð ώїτħ "iterateOn" ѕēτ. Ţħίѕ ¢óммăŋď ẁіľł ъě їġŋθřĕδ. !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"iterateOn\""} - ₣бџņδ à сöмmáήď ŵïτħ άп ίйνäℓīđ "colorScheme". Ťнïś ĉбмmąйδ ẅįĺľ ьê ïğñǿяēð. Мªќë śųѓé ťħάť ŵћèй ѕэŧţіήĝ ã "colorScheme", ţнз νåľύэ máťĉĥέŝ ţнє "name" οƒ ã çőľõř ŝсђêmĕ іń ţће "schemes" ľίšŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣őüиď å ςőммαňð ẃΐтħ āй îйνаłίð "colorScheme". Ťнΐŝ сǿmмαηδ щïĺℓ ъë їġñσґзδ. Мäķë ѕûґε ŧħâŧ щĥéñ ѕęтŧΐņğ ª "colorScheme", ţħě νªŀŭě мàτĉћ℮ś τђē "name" óƒ а ĉôĺоґ ŝçћěмэ ìй тħе "schemes" ŀįѕт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - ₣бцπđ ª "splitPane" çοmмªŋδ ẅΐтн ай îñνáℓΐđ "size". Ŧнιş ¢ôммāⁿđ ŵίļĺ вέ įĝήθѓéδ. Μāκе şύгз ŧĥę ѕΐžέ їś вěτωěėʼn 0 àņď 1, εжċłџşіνε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣бůήđ á "splitPane" ςōmмǻńď ŵιťн áп īⁿνªļιď "size". Τђιš çόmmâηđ щíľľ ъę їĝπσŕĕð. Μāĸĕ ѕџřē τħě śϊżė ìŝ в℮ţщ℮éʼn 0 àήď 1, ê×ćłΰşįνё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"splitPane\"","\"size\""} - ₣ªïłęδ ŧő рåřşэ "startupActions". !!! !!! !!! + ₣дĭļέď то рãřśě "startupActions". !!! !!! !!! {Locked="\"startupActions\""} - ₣øϋйď мυĺτίрļê éŋνĭŗóňмëит νâяїαъŀĕš ẁįţђ тĥē şåмę πämё ιл ďэŕēŋť çášĕѕ (ℓőшěѓ/ϋρρêг) - õηļў ŏπз νâłűę ŵíłľ ъé ųšéđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣óŭлð мŭľťïφŀє ēπνїяöņмέпт νăŕΐâъłěŝ ẁїťħ ŧнз ŝãmе пąмë ĭл ðіƒƒ℮ѓёηŧ ċāşêş (ŀοшêř/úρρéř) - ŏπŀў őñє νăĺϋз ẃіłļ ьё џšėδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Âⁿ ŏртіõŋάℓ ¢ōмmаņď, ẅïŧн ąŗġůmёйтŝ, тõ ъě ŝрáщήéď īñ τħε ήëщ ţǻъ σѓ φāиэ !!! !!! !!! !!! !!! !!! !!! + Âη ôрţίǿпαł сøмmªйð, щϊτђ ªгġύмėиτş, ŧõ ъε ŝрåẃʼnëð ïⁿ ţĥę ŋĕω ťάь óг рàňέ !!! !!! !!! !!! !!! !!! !!! - Μονé ƒōсμѕ ţô аηоŧĥэŗ τāв !!! !!! ! + Мőνё ƒõćųŝ ťö àņôτнěґ τάь !!! !!! ! - Мǿνě ƒσćűś ťǿ тĥē ʼnèхť ŧαь !!! !!! ! + Μоν℮ ƒǿ¢úś τó ťђэ пэ×ŧ ţǻь !!! !!! ! - Дπ àłιаś ƒοř тнє "focus-tab" şϋьċöммäηδ. !!! !!! !!! !!! + Áη àļιăş ƒöř ťĥè "focus-tab" şϋвςømmåлđ. !!! !!! !!! !!! {Locked="\"focus-tab\""} - Μбνě ƒôċύś τό тнę рŕёνìőūš ťåъ !!! !!! !!! + Μǿνë ƒőçùѕ ţб ťĥэ ρґēνįθūѕ ťаь !!! !!! !!! - Μŏνê ƒø¢ŭѕ τћĕ ťăь дţ τђè ġĩνєⁿ íπďэх !!! !!! !!! !! + Моνë ƒŏ¢μš тħз ťàв àť ŧĥè ĝįνēň íʼnδë× !!! !!! !!! !! - Μον℮ ƒσсΰşĕđ ράņê τö ŧћέ ţãь åţ τħэ ģινёπ ïήðęх !!! !!! !!! !!! !! + Μσνё ƒосцśέδ ρãиĕ τθ ťн℮ ţáв аť тнê ġίνëņ ιηđèж !!! !!! !!! !!! !! - Мσνέ ƒőćûşęđ ράʼnе ŧõ ªйőτћēг τάь !!! !!! !!! + Μõνė ƒōçμŝëδ рáήĕ ťó âπбŧђēґ ťåъ !!! !!! !!! - Ди āļìãѕ ƒòя τĥэ "move-pane" śцвςømмàиδ. !!! !!! !!! !!! + Αņ ąŀίǻŝ ƒöѓ τћє "move-pane" ѕũъćôмmâʼnď. !!! !!! !!! !!! {Locked="\"move-pane\""} - Śρэςϊƒŷ ťђę śīźэ áş â φēґ¢έñţåģě øƒ ťђĕ рªя℮йť рäńé. Vαŀϊđ νãłύëš αяэ ьęтш℮еи (0,1), єхċℓűśįνě. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Şφē¢îƒу τĥé şĭźε ǻŝ ã рēŕсĕητâĝē θƒ τĥё рãŕëпŧ φªпз. Vάŀĩď νªŀύëş дгě вёτẅэзй (0,1), ê×¢łΰšįνэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Сŕзªтё ã π℮ω ťāъ !!! ! + Сřēάтέ д πзω ťâь !!! ! - Âи åŀíªş ƒθŗ τнē "new-tab" ŝúъ¢όмmãπδ. !!! !!! !!! !! + Ǻń άĺіαş ƒόŕ ţħє "new-tab" şűьčóмmαήđ. !!! !!! !!! !! {Locked="\"new-tab\""} - Мøνё ƒŏċυş ŧó ãήōţђěř φаŋē !!! !!! ! + Мóνе ƒöςύѕ тŏ ǻήòτĥёř φªʼnê !!! !!! ! - Ăņ äļĭάş ƒòя τħέ "focus-pane" śúвćоmmâиð. !!! !!! !!! !!! + Ąи àľіâś ƒòг ţĥè "focus-pane" šûвčōмmäπď. !!! !!! !!! !!! {Locked="\"focus-pane\""} - ₣σċùѕ ťнè ρаņз ãť тђ℮ ĝΐνеη îŋδє× !!! !!! !!! + ₣òĉµś τħέ ρǻńέ ăŧ ťнё ģΐνеń īñđзх !!! !!! !!! - Όρέή ŵīŧħ ŧђе ġîνęп ρгöƒΐľє. Ąçćέρŧş εïтĥèѓ тнε ηαмĕ òŗ ĠŲÌÐ õƒ ª ρřõƒιĺë !!! !!! !!! !!! !!! !!! !!! + Øрēń ẃíτħ ŧħë ĝїνēń ρяōƒįľè. Àĉĉεрţѕ ěīţĥэґ ţĥє παm℮ óѓ ĢÙЇĎ òƒ ă φřòƒїļê !!! !!! !!! !!! !!! !!! !!! - Сгэαţĕ ä ņéш šρℓïτ φªйє !!! !!! + Ĉяéâт℮ α ñёẃ ѕρļίт рãηз !!! !!! - Äņ āŀĩãş ƒσŕ тђз "split-pane" ѕûъčǿmmǻʼnδ. !!! !!! !!! !!! + Ǻņ ãŀĩàş ƒôґ ţĥ℮ "split-pane" šџьςоmmâņδ. !!! !!! !!! !!! {Locked="\"split-pane\""} - Čґėάтє тђë ñ℮ẃ ρãпе ăş ǻ ћσŗīžσňťªĺ šрĺĭτ (тђїηк [-]) !!! !!! !!! !!! !!! + Ĉŕєăтз ťнё ⁿзẁ ρàʼnё àŝ а ћõѓϊžőⁿţªℓ ŝρłіт (тнįηк [-]) !!! !!! !!! !!! !!! - Сгéǻţè тћέ πëщ ρªйē åŝ ā νéятіćдℓ ŝрľĭτ (ţђїŋκ [|]) !!! !!! !!! !!! !!! + Çгêåтз ťнė йèẃ рãиέ άŝ д νέґťίĉάļ šρĺĩť (τћїʼnķ [|]) !!! !!! !!! !!! !!! - Ċя℮ǻţє τђĕ пèώ ρåńę ьў ďџφľìçăтîñğ ŧђê φґǿƒίľз σƒ тĥе ƒòçџšĕđ φāñĕ !!! !!! !!! !!! !!! !!! ! + Ćřзãτ℮ тђę ʼnėẃ φâйέ ьý ďΰρℓíčäтïňģ ťĥė φяοƒíłє θƒ τђэ ƒòćцśеð ρąлέ !!! !!! !!! !!! !!! !!! ! - Öρей ĩń τнє ğїν℮ń δіřёčτøřŷ īηѕţêàδ σƒ ťħε ρřøƒìŀę'ŝ ŝэт "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! + Õрëñ їń ţĥë ġìνєń đĭґĕĉťθŕý іⁿѕťεàð οƒ ţђę ρřǿƒĩĺę'š ŝєŧ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"startingDirectory\""} - Ōρэη τнє ţэřmïηąľ щĩţћ ťђě φяǿνìđēδ ţíŧłĕ ійѕţëάð øƒ ťћє φѓбƒîľе'š šėτ "title" !!! !!! !!! !!! !!! !!! !!! !! + Ǿφěⁿ ŧĥē τεŗмīņâļ щíţĥ ťн℮ φřőνīðĕď ţїţłê íйšŧęāď øƒ тнë ρŕőƒιļέ'š ѕéŧ "title" !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"title\""} - Οφėл тĥє тαъ ώίŧћ тнě šφĕςîƒìéď čθŀог, įή #ѓřģģьв ƒθямдτ !!! !!! !!! !!! !!! ! + Φрєл ŧĥэ тãъ ŵíŧħ ŧћё ŝρэ¢ìƒϊэδ ¢őłŏř, įи #ѓŗģġьъ ƒöŗmäŧ !!! !!! !!! !!! !!! ! - Ώρęⁿ тĥэ τªь щĭťĥ τåъŤíţłê θνêггіδĭηğ đėƒάũłτ ŧітľз åпď şŭррŗёššіņġ ţíťĺе ċĥáňģè мéŝşāĝэѕ ƒяоm ťнê åρρłįćàτίσⁿ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Òφéл ŧђέ ţàь ẁíţħ τªьŢітŀè ǿνёŕřĭďιŋģ ďēƒάџľţ τīťℓé åňđ śûφрŕėşšΐⁿĝ ŧįŧĺє ĉћαʼnģĕ мéśšáġēś ƒґбm ťħë арρℓĩςāтїσⁿ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"tabTitle\""} - Ίлћ℮řίŧ ŧне τεřмїиǻļ'ŝ ǿŵņ ěлνįřōňмęńт νāгΐáвļёѕ ẅнеň ćřεáŧīпģ ťђе ńêẁ τáъ οř φάñĕ, ŗàŧĥêŕ ťћàη ςŗèάŧĭиğ à ƒяéŝĥ ęηνìřõήmĕńŧ вℓöçк. Тħίś ðěƒäûļťš τθ šéτ щћεη â "command" īš φаšşēď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ĩηнěѓїт тħè τèŗмιŋāļ'ş óωʼn εήνīřōπméлť νâґįâвĺέş ẅĥēʼn ćřеàţιήġ ţнэ йеω тåь òř ρǻⁿе, řãťħĕѓ τђåл ¢ŕéάťĩήġ α ƒгёşн злνΐřõʼnmèⁿτ ъłōçќ. Ťћĭš đéƒąúłŧѕ тö šēт ẁħéή α "command" ΐŝ ρаśšéđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"command\""} - Όрèπ ţħę ťαъ ẅîтĥ тнĕ şрęćīƒίêδ сόĺöѓ ś¢ћеm℮, ïʼnѕŧέǻδ σƒ ťħз ρѓŏƒіļê'ŝ şēť "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! + Όφέŋ τĥē ŧаъ шĭтĥ τĥë ѕφęсìƒìέď čõŀόŕ śċћêmë, ìńѕţĕαđ όƒ ţћε φґöƒιłě'š şĕť "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\""} - Ďϊśρľάý ţнě ąφρĺіčǻτΐǿπ νєѓѕíōп !!! !!! !!! + Ďìśφłäу ţђê аφφŀīċâŧĩοπ νεŗśіőŋ !!! !!! !!! - Ľāüή¢н ťђę ωĭŋðöẁ mαхїмΐżěď !!! !!! !! + ₤ąűņčħ ťĥĕ ẁіπðǿẃ måхíмïźĕď !!! !!! !! - Ľäџňςн тħė ẃίиďõŵ ìʼn ƒüľļśċŕзęň mõđĕ !!! !!! !!! ! + Ĺάцńĉђ ţђє ŵïпđôщ іⁿ ƒüŀłšċѓéëň мσđе !!! !!! !!! ! - Μòνэ ƒőĉμš ťõ ŧне ǻðјāс℮иť рàπє іŋ ŧћё šρěċΐƒïėđ ďìřê¢ŧîŏл !!! !!! !!! !!! !!! !! + Мøνέ ƒŏčΰş ŧθ ŧђέ åδĵаćёʼnт рάлë ïη ţĥě ŝφėċΐƒίεď đіґэсŧįóń !!! !!! !!! !!! !!! !! - Åл ąľїãѕ ƒόѓ тћε "move-focus" şûьćǿмmǻņď. !!! !!! !!! !!! + Āη ǻľїαš ƒöř τħê "move-focus" şůвčøмmåⁿđ. !!! !!! !!! !!! {Locked="\"move-focus\""} - Ţħě δїгèсŧīσл ŧó моν℮ ƒоčűѕ ϊņ !!! !!! !!! + Ťђê ðîгèсτîбň ŧǿ мøνё ƒöćμś īπ !!! !!! !!! - Śщαр ťħę ƒǿĉùşĕδ φǻņĕ ώΐťн ŧћè ǻďјаċεητ φάпë íп ţħέ ѕρęćїƒϊéð ðīгęċтíόη !!! !!! !!! !!! !!! !!! !!! + Ŝẅáφ тĥê ƒθĉŭѕєđ рâńé ŵîτђ ťћэ ªďĵдćėʼnτ ρдŋз ιņ ţђε ѕρęċϊƒίèđ δíŗеćτīóŋ !!! !!! !!! !!! !!! !!! !!! - Ţћє ðіѓε¢ţїőń ţø мσνз ťћ℮ ƒбсųşéð рâпè īň !!! !!! !!! !!! + Τĥë đΐґэçτїøŋ ŧθ mòνє ťħĕ ƒŏςΰşеđ рąⁿě ίň !!! !!! !!! !!! - ₤ăϋηсђ τђз ẅįπðóŵ ϊň ƒθςυš мбđė !!! !!! !!! + Ļǻŭйĉђ τнę ẁιʼnďõẃ ιи ƒôčųş мøδė !!! !!! !!! - Ŧћîš ρåŗãmëтēŕ ĩš ªп ìⁿţēŕñàľ ïmρĺèм℮ŋŧåţιòπ ðëтąіℓ åñđ śћőΰłď лōτ вė ũŝ℮ð. !!! !!! !!! !!! !!! !!! !!! ! + Тнîś раřǻмеτêř ĭѕ άň ϊлтëяņãŀ ĩmрļєméⁿţаţīøи δěτąîĺ аηđ šђоцĺð лŏτ ъě ūşęď. !!! !!! !!! !!! !!! !!! !!! ! - Şφéĉϊƒу ä ťеŕмĩήàļ ŵïńďōщ ťô яüи ťне ĝіνел ċоmmǻйďļΐńę іñ. "0" ăłшªўş гзƒзřş тő тћε ćΰѓŗέήт ŵїⁿðøώ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ѕρέςïƒγ ä ŧёяmįʼnāļ ẁϊņδōẅ ţб řцή ŧн℮ ġïνëŋ ċŏмmàπďľíňε ίń. "0" ãŀẃăуş ŕєƒεґş ţô тнé ¢ūřřзňτ щįήδοŵ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Şφę¢іƒÿ ţĥ℮ рöşїţϊθń ƒοř ťћë τèяmΐňâł, įň "х,у" ƒόґmäŧ. !!! !!! !!! !!! !!! ! + Śρēсíƒý ţнę φóśїťіòⁿ ƒоŕ τħē τėřmìпàĺ, îπ "х,ý" ƒøŕmăţ. !!! !!! !!! !!! !!! ! - Śрёςíƒỳ ťђе ņυмвëŗ øƒ ĉǿĺūмйѕ ǻήð гõẅş ƒǿŗ ţнє тєґмϊηàℓ, ϊй "č,г" ƒǿґmäτ. !!! !!! !!! !!! !!! !!! !!! + Ѕрëсιƒŷ τнë ήύmьėř óƒ ςθĺùmиš αήď ґσωş ƒòř ťĥз τèґmіŋąľ, ĩŋ "č,ŗ" ƒöŗмªτ. !!! !!! !!! !!! !!! !!! !!! - Ρґëŝś ŧнє ъüŧţóл το θρëπ ą йèẅ тĕѓмійάł ŧåъ ẃίŧћ ýõџг δėƒàűĺτ рŕöƒíĺε. Ōρєи ŧђе ƒłўοũт тŏ šêļεćť шħï¢ђ φŗσƒĭļз ÿöµ ẅªńţ ţö όрęл. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Рŗзšş ţђ℮ ъџŧţοи тô òрēń ã ήėщ ŧεґmīñãŀ ţãъ ẅΐтн γōμř đ℮ƒâųļţ φŗōƒĩĺє. Ŏρ℮ή ŧнè ƒĺýöũţ ţô ŝёļĕċτ ẁĥī¢ħ ρґóƒίℓε ýοŭ ẁªñŧ тó ǿφзп. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Ńєш Ţàъ !! + Ņ℮ω Тǻв !! - Ǿрėʼn á ηэẁ тāъ !!! ! + Òρêň â ήęώ ţäь !!! ! - Ãļť+Ĉłϊĉќ ţο ѕφℓϊť тђё ćυяřеʼnť ẃĭʼnďòẅ !!! !!! !!! !! + Λľť+Čℓίčк τö šρŀіт ŧћз ĉűŗяęñт ẅілðοẁ !!! !!! !!! !! - Śћĭƒτ+Ćłī¢ķ ťò õрěή ª ʼnеω ẅίʼnďǿщ !!! !!! !!! + Şнįƒт+Čŀĩćκ ťò θφэη а πèẁ ώίŋďōẅ !!! !!! !!! - Ċťřļ+Ċŀìčκ ŧő õреи ãŝ ąďmĭñϊŝťŕǻтθѓ !!! !!! !!! ! + Ĉτřł+Ċľìċκ ţò θр℮й ăś àδmίʼnìşтгáтöг !!! !!! !!! ! - Сĺσśе ! + Čļőšë ! - Ċľоşε ! + €ľőš℮ ! - €ŀōšз ! + Çļоšě ! - Мà×íмīźè !! + Μå×ίмīžĕ !! - Мíηĭмїż℮ !! + Μĩńιміžë !! - Μιñіmįżэ !! + Μĩηĭмĩžе !! - Мїήîмīžě !! + Μîņïmīźè !! - Λьőџτ ! + Âъθŭт ! - Ѕēлđ ₣ëèðвасќ !!! + Ŝέήδ ₣ěеđвαсķ !!! - ΩК + ΘК - Vзяśĭöή: !! + Vэŗşισή: !! This is the heading for a version number label - Ĝέτтìπĝ Śţąŕťěð !!! ! + Ġėŧτįлġ Śŧăѓтĕð !!! ! A hyperlink name for a guide on how to get started using Terminal - Ŝōϋŗĉė Čоďè !!! + Šоúѓс℮ Ĉóðĕ !!! A hyperlink name for the Terminal's documentation - Ďôςųmèňтãŧįõή !!! + Đóċűm℮ņťâтĭθñ !!! A hyperlink name for user documentation - Řεŀèαśė Лőŧěş !!! + Ѓęłєąśε Ņθтėš !!! A hyperlink name for the Terminal's release notes - Ρґїνасŷ Ρоļіĉў !!! ! + Ρŕΐναçγ Ρõℓĭčý !!! ! A hyperlink name for the Terminal's privacy policy - Ţђїŗð-Рǻŗтγ Иоťϊċéŝ !!! !!! + Τћїřđ-Ράŕтŷ Ńôţι¢ęś !!! !!! A hyperlink name for the Terminal's third-party notices - Çǻπçέļ ! + €áйċêł ! - Сℓōѕë àłℓ !!! + Сľŏѕè āłℓ !!! - Đô γσû шąήŧ тõ ĉļöŝě àłĺ щïйðōщѕ? !!! !!! !!! + Đò ўθü ẁаⁿŧ ŧб ćŀǿѕё āľℓ ωіⁿđőщš? !!! !!! !!! - Çâńćěĺ ! + Ćдлčєľ ! - Ĉŀǿşз àľŀ !!! + Çŀóśė άłł !!! - Ďο ÿοџ ŵàηŧ тο ĉĺôѕε àĺĺ ŧåьš? !!! !!! !!! + Ðό ỳőΰ ẃàŋτ τõ čŀőśё åĺļ ťāъś? !!! !!! !!! - Ćãйçеľ ! + Сάήćêł ! - Сŀбŝę аńушåγ !!! + Čľόşë дйγŵаỳ !!! - Ẁāґņϊйĝ !! + Ẅãŗήĭйģ !! - Ýǿû åгè авõϋţ ťǿ ςľóѕē ă ŕεâð-оņŀγ тзґmīⁿªĺ. Đö ŷоμ шïѕђ тõ ċσйŧїπûэ? !!! !!! !!! !!! !!! !!! !!! + Υőū ąŗę āвõųт τô ĉℓôśĕ ā ŕęăδ-õήļγ ŧēгмĩņął. Ďø уôü ŵΐѕн ŧö čöʼnţìйџè? !!! !!! !!! !!! !!! !!! !!! - Ċаňĉєļ ! + Ċдņçëℓ ! - Υбũ āѓе ªъǿΰт ŧŏ ρąŝŧë тęжт τнατ ïŝ ŀôиģέѓ ŧндй 5 Κîß. Đǿ γοŭ ẅįŝĥ ťο ĉøⁿτïňυє? !!! !!! !!! !!! !!! !!! !!! !!! + Ўòџ āřε àьоŭт ťô φªşтĕ τêхť τĥǻŧ ĩѕ ļоñĝёŗ ťĥдⁿ 5 Ќΐß. Ðő γöύ ẅιšђ ţø ċòπτīŋϋė? !!! !!! !!! !!! !!! !!! !!! !!! - Ραŝτε ªŋуωáŷ !!! + Ρåŝтę ąηýẅªγ !!! - Щагⁿîиğ !! + Ẁǻґñīņĝ !! - €åʼnč℮ľ ! + Саńčеļ ! - Ỳŏũ âřё âвŏůτ ťο φαşŧэ ťêжт τћªţ ċθʼnťдϊñѕ мûłτίρℓë ℓīйзş. Іƒ ŷбц φǻѕţё τĥîś ťė×ŧ ΐήтō ўǿύř šħέℓļ, ίţ мάγ яēśŭļţ ϊή ţнę ūņєжрêĉτěđ έ×ěĉůţíŏи òƒ ćоммäήðѕ. Ďõ ỳõμ шįѕĥ ŧŏ čθⁿτїйυэ? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ϋσų ąгε ąвőûτ τо ρдѕťз τзхŧ ŧђàţ ¢õитαίņŝ mύĺтïρľė ĺĭñêѕ. ̓ ÿσΰ рάѕţè τнϊş ŧèжŧ ìйŧõ ÿοŭґ şнéľĺ, ΐт mаý řëśϋℓŧ îň ŧħě ϋñĕхφ℮¢ţзð ε×эĉûτιбņ οƒ čømмãпðś. Ðō ÿôû ẁίѕĥ ţó сöñţïñûĕ? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Рдѕţè āŋуẃаỳ !!! + Раşтё ǻлýщáỳ !!! - Ẁąřŋїηġ !! + Ẃăřиїńğ !! - Тўрз ä ςòмmǻйð иαмέ... !!! !!! + Ţỳрε а ċθммªňď ŋămэ... !!! !!! - Νǿ мâτčĥĭņģ ćǿмmªπďś !!! !!! + Ŋô măŧĉђįñġ ćõммâиďš !!! !!! - âтϊοņ šěáŗςђ мбδê !!! !! + Аĉŧíòи šêдŗćħ möđε !!! !! This text will be read aloud using assistive technologies when the command palette switches into action (command search) mode. - Τàъ ťΐťŀě mőđê !!! ! + Ŧаь ţìτłē мöďё !!! ! This text will be read aloud using assistive technologies when the command palette switches into a mode that filters tab names. - Сòммǻŋđ-ŀįņë möďέ !!! !! + Çоммаиđ-łĭпê mσďę !!! !! This text will be read aloud using assistive technologies when the command palette switches into the raw commandline parsing mode. - Мοгэ ōφτίθήš ƒбѓ "{}" !!! !!! + Мõґē бρтīøńŝ ƒõѓ "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Έхé¢ύτíήğ çбmmăйδ łìņе ẁїľŀ îйνŏкê τћε ƒóļĺôшįπğ ćσmmάйďś: !!! !!! !!! !!! !!! !! + Ĕхёсûţîπġ ćõмmáⁿδ ℓīňë ŵìℓĺ ĩⁿνōκè ŧħę ƒôłľόшîпğ ċømmåņðş: !!! !!! !!! !!! !!! !! Will be followed by a list of strings describing parsed commands - ₣дįļ℮đ φăřѕιņġ ¢ōmмâηδ łįńê: !!! !!! !! + ₣дìĺέď ρǻяšΐňģ čǿмmдиď ľïñέ: !!! !!! !! - Ćøммāлď Рдłĕτţĕ !!! ! + €οmмдиδ Ρãℓėŧţė !!! ! - Ţāв Şωϊтčћëř !!! + Ŧав Ѕώįťċнзя !!! - Τŷρε ă ťãь пªмэ... !!! !! + Ţýρе ǻ ťåв ήáмє... !!! !! - Ňó мãτčĥϊńĝ ťàъ ňám℮ !!! !!! + Ñό màŧĉĥĩпģ тåв лąмę !!! !!! - Ėητėѓ α wt сθmmãиďℓïńέ тŏ яůи !!! !!! !!! + Ęⁿţέŕ а wt ςōммǻŋđłïηé ťò гůņ !!! !!! !!! {Locked="wt"} - Мοяε őрťίоⁿš ƒбѓ "{}" !!! !!! + Μŏѓê øφţįőńş ƒóř "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Тўрē д ćŏmмǻⁿď йāmэ... !!! !!! + Тÿрє ā čомmäʼnð ńаmэ... !!! !!! - Йǿ mäŧ¢ђїⁿģ ċσмmàŋδѕ !!! !!! + Иô мǻţčђїńġ ćõmmåŋđŝ !!! !!! - Ѕџĝĝěśŧίǿňş m℮ŋú !!! ! + Ѕцğĝēѕтĩóⁿŝ mεйű !!! ! - Мοŗë σрτιθñś !!! + Мòřε ōрťïóⁿš !!! - Šύģġεŝŧīоήš ƒöüʼnδ: {0} !!! !!! + Ŝűģĝзšŧïõлѕ ƒǿûňδ: {0} !!! !!! {0} will be replaced with a number. - Ćŗĭmśση !! + Ċґįmśõņ !! - Ѕŧзеļ Ьℓũê !!! + Şţêēļ Вľΰë !!! - Μęďϊϋm Śéά Ğŕезл !!! ! + Мěďīųм Ŝęά Ğřĕ℮ņ !!! ! - Đдгķ Ǿѓåñĝë !!! + Ďåґķ Φѓăŋġé !!! - Μêðιυm Vίоļєť Ґэð !!! !! + Мēðіΰm Vϊόŀěť Яєď !!! !! - Ðòδğēѓ Вℓϋė !!! + Ðοďĝέґ βľυę !!! - £ιmё Ġгеēπ !!! + Ŀімě Ġŕеéň !!! - ¥ęŀĺőώ ! + Ŷέļłőẃ ! - Вłũě Vîбĺэτ !!! + Вŀûє Vĩöĺеť !!! - Šľăţе Вŀŭė !!! + Šĺàŧĕ Бłůέ !!! - Łίm℮ ! + Ŀíмё ! - Тãπ + Ťдπ - Μаġэńŧд !! + Мâġėňтα !! - Ċýǻň ! + €ÿàй ! - Ŝĸў βĺϋε !! + Śκŷ ßłúë !! - Đàřк Ġřäý !!! + Đąřķ Ģґăý !!! - Ťħΐŝ łΐиķ íś ĭиνãľíð: !!! !!! + Τĥīś łīлĸ ĭś īⁿνдļîď: !!! !!! - Тћĭś łĩηĸ ťуφê ĭŝ čųґřεήτĺỳ лöŧ ŝųрρσѓţέđ: !!! !!! !!! !!! + Ťђιš ļĭπк ŧγρę ιş ςύґŗёηťľỳ ŋôт śũрρόятзđ: !!! !!! !!! !!! - Ćдñĉèľ ! + €дⁿĉεℓ ! - Śęттΐńġš !! + Ŝêттΐʼnġş !! - Щě ςθΰľδ ŋöτ ώřΐŧє ťο уθϋя ѕетŧíηġš ƒĩℓе. Čħзčк ŧĥе φеŗmìşşίσⁿś όñ ŧђαţ ƒįℓĕ ţθ ęπŝüř℮ τђåт ţĥэ ѓεăđ-óиļý ƒļάğ īś ηøţ ŝėť άňδ τĥáτ ẅřίтз âςćέѕš іŝ ģгàлŧэδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Щз сőυĺδ йότ шříтз тθ ўоџŕ šėτţįŋĝš ƒïĺε. €ħесќ ţђε рëѓmĩšşîбñŝ òη ťћăţ ƒíļе ţό ěήѕüяē тĥāт ťħе гεάđ-øñℓý ƒℓªğ ϊś ņōт ѕзτ āиđ ŧнàт ẅřіţê áςčεѕş їѕ ĝѓăήτєđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ьáċκ ! + Βãčκ ! - Вąςķ ! + βāċķ ! - ΩЌ + ФК - Đęъũĝ ! + Ðéвµģ ! - Єґřǿŕ ! + Ēŕřθř ! - Įňƒοŕmàťїσņ !!! + Ϊήƒόѓмâŧїōл !!! - Шâŗñіňĝ !! + Ẅάѓʼnιⁿğ !! - Ĉļïрьǿдяδ ςσиťзņтŝ (рŗэνíέщ): !!! !!! !!! + Сłįръοåřđ čôπŧέʼnŧš (ρŗзνįēŵ): !!! !!! !!! - Μôŕě õрťīóńš !!! + Μōгę øрţïбʼnś !!! - Ẁϊʼnðóщ ! + Шϊиđŏώ ! This is displayed as a label for a number, like "Window: 10" - ΰηлάмēδ щïʼnδοẁ !!! ! + ûйηãмзđ ŵĩņðőώ !!! ! text used to identify when a window hasn't been assigned a name by the user - Злτея å ňěω лąmэ: !!! !! + Ėⁿŧęѓ ä ňèŵ ñāмė: !!! !! - ΩК + ΦΚ - Ćáηсéľ ! + Čǻʼnςёŀ ! - ₣ăīℓėδ ťθ řєπαmё ẃійďоώ !!! !!! + ₣åϊłеď ťō ŕэñάмę ẅϊпďöẁ !!! !!! - Áπøŧнзя ẃĭйďôш ώîτħ тĥăŧ ńãmë äŀŕêäðý эжïŝťš !!! !!! !!! !!! ! + Äлôţħêř ωīńδõẃ ẁίтћ τђаŧ ńªмě áłŗěаďŷ ё×ĩşťѕ !!! !!! !!! !!! ! - Μāхĭmіżë !! + Μā×ΐmĭźё !! - Řэşţōŗĕ Ðθщй !!! + Г℮šţòґě Ďôẁл !!! - €ømmåņð Ρáļёţтė !!! ! + Ćőmmªňď Ρăľёŧţε !!! ! - ₣őċúš Ţęřмìņāĺ !!! ! + ₣õĉΰѕ Ŧέгмîŋäľ !!! ! This is displayed as a label for the context menu item that focuses the terminal. - Ẅіʼnđǿẃŝ !! + Ẅϊʼnďθẁş !! This is displayed as a label for the context menu item that holds the submenu of available windows. - Øρëл д ʼnėẁ ťдь їň ģΐνёй šťąŕťíńģ đîŗėċтøґý !!! !!! !!! !!! + Óφêй â лέώ ŧâв ĭʼn ğΐνéņ şţăřτíήġ ďįяе¢тоѓγ !!! !!! !!! !!! - Ώφėл á ňёщ ŵīʼnδöẅ ŵìŧђ ğίνëй şţáŗŧΐήģ đíґэĉťθґў !!! !!! !!! !!! !! + Фφēή ä ñēώ щїήďθẅ ώϊτħ ġіνėй ѕŧαŕтїйğ đïřéĉтõŗý !!! !!! !!! !!! !! - Šρļϊт ţħë ωĩñδбŵ аňð šţдѓť ίń ġïνεи đιґēсτσřŷ !!! !!! !!! !!! ! + Ŝрļîţ ťнε ωΐήđõẃ άήδ ѕтăřŧ ĭπ ġіνэŋ δΐяëĉŧоŗу !!! !!! !!! !!! ! - Ёжрбřŧ Ŧ℮хţ !!! + Е×ρōґт Τ℮хŧ !!! - ₣âîŀєđ тσ зжφőяτ ţėґmιйäļ сǿйťêńт !!! !!! !!! + ₣ăīļēđ ţǿ έжρõřŧ τëřмïňãĺ ćóñτëητ !!! !!! !!! - Śùсčеšѕƒúℓŀỳ èжφöŗŧéδ тзŗmĭņāľ ćōńŧėňт !!! !!! !!! !! + Şυčć℮ѕŝƒϋĺŀŷ ε×ρøяťзđ ŧэґмϊйáļ ćõņťεлţ !!! !!! !!! !! - ₣ĩŋδ ! + ₣їŋð ! - Рľαįπ Ţĕ×ŧ !!! + Рľάïʼn Τėхт !!! - Τεřмíʼnаţїőⁿ ъėħâνĩθя ćαη ъĕ čõπƒīģùŗėδ ϊņ āδνăŋсєδ ρгŏƒίłé ŝ℮ţτîņġś. !!! !!! !!! !!! !!! !!! !! - - - Ẅīʼnđóшś Ŧėґмîήåℓ ĉªň ве šеť åş τђē ďĕƒªμℓτ ţèŗмĩńâĺ äφρŀϊ¢ǻτīοñ îл ýøùř ѕєťţíηğş. !!! !!! !!! !!! !!! !!! !!! !!! + Тёѓmĩŋáŧΐôń ъεђàνĭöґ ĉáл ьέ ćôлƒιģџřěď ĩń ąďνάиĉëđ φяóƒίłĕ ŝеţτϊпĝś. !!! !!! !!! !!! !!! !!! !! - Đόπ'т śђóщ ăĝäΐи !!! ! + Ðόπ'т śħöω ãġāїй !!! ! - Ţħίѕ Ţėřмīйąĺ ώιňδòώ ίŝ ŕџⁿпϊπģ αş Άδmíⁿ !!! !!! !!! !!! - - - Όρеπ Šéťтīńġš !!! - This is a call-to-action hyperlink; it will open the settings. + Ţђīѕ Тёřмíņàĺ щίńďόщ ĩѕ яџиńίйğ дś Δδmϊⁿ !!! !!! !!! !!! - Ѕúğġέšτιбйš ƒθύńð: {0} !!! !!! + Śυģģєşτîòиŝ ƒοųņδ: {0} !!! !!! {0} will be replaced with a number. - Çћêčќíйĝ ƒσř ůφðăτεš... !!! !!! + Ċĥēćкîñģ ƒθř ūρďάţēŝ... !!! !!! - Αń ųрðáťě ïş ăνāíľáъŀę. !!! !!! + Ăŋ űрδάтē ΐś ãνāїļâвł℮. !!! !!! - Ìñşţªľℓ ήöщ !!! + Ĭńšτдℓℓ пøш !!! - Тнĕ "newTabMenu" ƒΐéℓđ ćόиŧåіńѕ мôřэ тнаⁿ őñê έηŧŗў бƒ ŧуφе "remainingProfiles". Όñŀў ŧђė ƒįґşť őňê ώιľŀ вë ςбήśіđĕяέď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τħē "newTabMenu" ƒĩêłď сöήťåïŋş mōřé ŧĥдл оņě êηтřγ őƒ ţỳφë "remainingProfiles". Õňľỳ ŧнė ƒîґšŧ οήз ώìłļ в℮ čσņšїďèгеð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="newTabMenu"} {Locked="remainingProfiles"} - Τнë "__content" ρřŏφзřтỳ ĭş ŕêšęѓνëð ƒõя įñţěґπàļ џŝё !!! !!! !!! !!! !!! + Ţнé "__content" φѓορεяτў ĭѕ řêŝęѓνеď ƒǿř įйтзѓйäļ űşе !!! !!! !!! !!! !!! {Locked="__content"} - Ωφêŋ α ďįǻĺøġ ċøŋтªϊʼnĩπġ φгòďŭсť ίňƒŏŕmâŧĩσʼn !!! !!! !!! !!! ! + Ωρēп ǻ ďîаľôĝ сōňŧдįńΐņģ ρгόďϋčţ ĩήƒбŗмâтîõń !!! !!! !!! !!! ! - Фρέй ţђę čοŀøř ριςķеѓ ţő ĉнõóŝё ţнэ čōłóŕ ŏƒ тĥιś тąв !!! !!! !!! !!! !!! + Öφέη ťħē ĉôℓôг ρįςќēґ ŧǿ ςћóőšе ţћє ċόłöŕ όƒ ţĥįŝ ťãь !!! !!! !!! !!! !!! - Θφęⁿ тĥë ċòmmäпď ρǻľзтτз !!! !!! ! + Óφεʼn ŧħе ςóмmǻηð φąŀëŧτε !!! !!! ! - Øρēп â иêш тªь ϋŝίŋģ ŧħĕ ά¢ťĭνе φґőƒĩĺз їп ťнë ςųŗŕéйť ďίŕеċтθґў !!! !!! !!! !!! !!! !!! ! + Öρěń α ηёω ţâь ųśìπğ тћė áċτíνĕ φŗôƒìĺе ιŋ тћé çΰгřєŋŧ đířéċťσѓÿ !!! !!! !!! !!! !!! !!! ! - Èжрôřτ ţħε ¢οŋτзиťŝ όƒ τĥё тē×ŧ вµƒƒεя ĭптō ā ŧ℮×ŧ ƒīĺέ !!! !!! !!! !!! !!! ! + Έ×ρøѓť τħέ čôлтзⁿťѕ õƒ ţћë τехť вцƒƒêя įήţǿ â ťέжţ ƒīĺє !!! !!! !!! !!! !!! ! - Ōреи ŧħé ŝèāřςћ δіăĺŏġ !!! !!! + Øрëй тђê šěāřćђ đïªłôģ !!! !!! - Яêπåmε τђіŝ ţäв !!! ! + Γёńāмę ţħιѕ тáъ !!! ! - Ωрзņ ťĥę ѕέţŧϊήġš ρªġė !!! !!! + Θрėń ţћē ѕèŧţΐŋğŝ рãģĕ !!! !!! - Ōрεπ д пεш рãⁿè цśìñģ тĥê ª¢тіνз ρѓòƒīłέ ϊи ŧħĕ ςūгѓёπť ðίѓęċтοřγ !!! !!! !!! !!! !!! !!! ! + Ŏρеи ǻ лěẁ рăηë űŝĭňģ ťћэ ªсťįνέ ρгбƒίļ℮ ιň ťђ℮ čŭѓŕėйŧ ďįѓęčťōґу !!! !!! !!! !!! !!! !!! ! - Ĉļǿѕē ªłĺ ťαъş ŧó ţне ѓΐģнŧ öƒ ŧĥĩś ţåъ !!! !!! !!! !!! + Ĉŀôśё àłł τáъś ťǿ ŧĥє ŕįĝнт ŏƒ ţĥìš ťâь !!! !!! !!! !!! - Сℓòŝе ąŀĺ тǻъŝ зхςέρŧ тђĩѕ ţáъ !!! !!! !!! + Ćŀǿŝē âłĺ ŧǻъş ë×ćзρţ ťђïš ŧăь !!! !!! !!! - Çľõŝе τђΐš ţāь !!! ! + Çℓôѕé τнĭš τăь !!! ! - ∑mрţγ... !! + Έmрту... !! - €ĺσŝė Рąή℮ !!! + €ŀοşē Ρǻйė !!! - Čļőѕê тђě ąćťїνé рãиĕ ĭƒ мµĺτіρĺę ρąπεѕ αŕё ρŗèѕέⁿτ !!! !!! !!! !!! !!! + €ℓöśė ţħэ âćτīνз ρªπę ϊƒ mυŀтïрℓэ рάñęş ǻřě φґĕŝëηţ !!! !!! !!! !!! !!! - Γзѕеţ тдь čôľóѓ !!! ! + Γĕşęŧ ţåъ ċθłõŗ !!! ! Text used to identify the reset button - Μоνě Тåъ ţǿ Иêш Ŵíŋδöẃ !!! !!! + Μøνέ Ťãъ ŧø Ņ℮ẃ Ŵїŋđóш !!! !!! - Мőνěš ťдв ŧό ã ňěώ ẃįņđōщ !!! !!! ! + Μονèѕ τäь το д пěẃ шϊлδǿẃ !!! !!! ! - Ґúπ άѕ Äďmΐʼníşťřαŧöѓ !!! !!! + Ŗμń ąś Áđмįпįśŧѓăτőř !!! !!! This text is displayed on context menu for profile entries in add new tab button. - Λċŧϊνê рàлє мőνëď τб "{0}" ŧàь !!! !!! !!! + ∆çţіνė ρªňè möνéδ ťõ "{0}" ťªъ !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. - "{0}" τąъ мòνεδ ťó "{1}" щîⁿðоẃ !!! !!! !!! + "{0}" тăъ мǿνèδ тб "{1}" ώілδòẁ !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the name of the window the tab was moved to. - "{0}" тªв mòνéđ ŧŏ п℮ẅ щīπďоẃ !!! !!! !!! + "{0}" τäь môνėđ ťö ηęω ωĩⁿđοш !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. - "{0}" ťάъ мòνзđ ţö ρöŝĭτіθй "{1}" !!! !!! !!! + "{0}" ťáь мòνέð τό ρôšìťϊǿñ "{1}" !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the new tab index in the tab row. - Δĉţįνê ρåň℮ моνєď ťŏ "{0}" ţàв ïʼn "{1}" ωìйďòω !!! !!! !!! !!! ! + Δсτîνę ρдηё мσνëđ ŧǿ "{0}" ŧâь ĩʼn "{1}" щΐńδоω !!! !!! !!! !!! ! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. {1} is the name of the window the pane was moved to. Replaced in 1.19 by TerminalPage_PaneMovedAnnouncement_ExistingWindow2 - Àĉťϊνē ρäņё мσνέδ ţǿ "{0}" ώîňδòẅ !!! !!! !!! + Αĉтīνĕ рάňè мбνéδ тŏ "{0}" ŵιⁿđŏω !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the window the pane was moved to. - Ǻčτїνĕ φåňê mŏνěð ťθ лéщ ẁīήðσŵ !!! !!! !!! + Δċŧίνë рăпε möν℮ď ţό ή℮ẁ ẃίⁿδõŵ !!! !!! !!! This text is read out by screen readers upon a successful pane movement to a new window. - Áçτîνĕ рåʼné мøνèđ ťǿ η℮ω ťăъ !!! !!! !! + ∆ĉŧïνε φâй℮ мσνěð ŧő пèẃ τдв !!! !!! !! This text is read out by screen readers upon a successful pane movement to a new tab within the existing window. - Їƒ şєŧ, ťĥē ĉõмmдńđ ẃΐľℓ ъэ āρρéиďέδ ťő τнĕ рřоƒįĺε'š ďêƒάΰłť čǿмmаʼnď ιиѕŧ℮ăď õƒ řєρłą¢īñģ ìţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Іƒ šεт, ťĥē ċōмmáиð ŵіĺℓ ъė áφρėηďéδ тō ťћĕ ρřσƒĩℓз'ş ďēƒªцľŧ çõммâʼnđ їⁿŝťèàď σƒ яёрľάĉіʼnĝ іт. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ŗėšťäґť Ćōŋⁿεĉťïоŋ !!! !! + Ѓёѕťąřτ Čόņńěċтīōņ !!! !! - Ŕĕśŧàѓť тĥз ąčŧιν℮ рãпé ĉóлпêсτíõл !!! !!! !!! ! + Гєšťάřŧ ťђé ǻсťїνę рдйε ςσлήеςťїõπ !!! !!! !!! ! \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw index 3fd4f2230d9..a66c47aabd6 100644 --- a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw @@ -763,19 +763,12 @@ Поведение завершения можно настроить в дополнительных параметрах профиля. - - Терминал Windows можно настроить в параметрах как приложение терминала по умолчанию. - Больше не показывать Это окно терминала запущено от имени администратора - - Открыть параметры - This is a call-to-action hyperlink; it will open the settings. - Найдено рекомендаций: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw index 45fc2c0a53f..7cee543bce9 100644 --- a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw @@ -763,19 +763,12 @@ 可以在高级配置文件设置中配置终止行为。 - - Windows 终端可在设置中设置为默认终端应用程序。 - 不再显示 此终端窗口正在以管理员身份运行 - - 打开设置 - This is a call-to-action hyperlink; it will open the settings. - 找到的建议: {0} {0} will be replaced with a number. diff --git a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw index 6ddf9dbdae1..33e548f77ad 100644 --- a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw @@ -763,19 +763,12 @@ 您可以在進階設定檔設定中設定終止行為。 - - Windows 終端機可在您的設定中設定為預設終端機應用程式。 - 不要再顯示 此終端機視窗目前以系統管理員身分執行 - - 開啟設定 - This is a call-to-action hyperlink; it will open the settings. - 找到的建議: {0} {0} will be replaced with a number. From 0a83946214277ef8c773ef094f19cc7ba6357f10 Mon Sep 17 00:00:00 2001 From: Craig Loewen Date: Thu, 21 Mar 2024 12:07:32 -0400 Subject: [PATCH 171/603] Update similarIssues.yml to include issue bodies (#16915) Improved the GitGudSimilarIssues bot to include issue body info now. --- .github/workflows/similarIssues.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/similarIssues.yml b/.github/workflows/similarIssues.yml index fa93ad8597d..3e377e2ff9d 100644 --- a/.github/workflows/similarIssues.yml +++ b/.github/workflows/similarIssues.yml @@ -13,7 +13,8 @@ jobs: - id: getBody uses: craigloewen-msft/GitGudSimilarIssues@main with: - issuetitle: ${{ github.event.issue.title }} + issueTitle: ${{ github.event.issue.title }} + issueBody: ${{ github.event.issue.body }} repo: ${{ github.repository }} similaritytolerance: "0.75" add-comment: From b9a0cae01076d50a066ab26d65f5439f0d0a246e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 21 Mar 2024 10:07:57 -0700 Subject: [PATCH 172/603] Add a pile of 'experimental' settings to the profile SUI (#16809) As noted in #3337, we never actually added this menu to the settings. Since we're planning on taking this out of "experimental" in 1.21, we should have a visible setting for it too. --- src/cascadia/TerminalControl/TermControl.cpp | 1 - .../ProfileViewModel.cpp | 12 ++++++ .../TerminalSettingsEditor/ProfileViewModel.h | 8 ++++ .../ProfileViewModel.idl | 8 ++++ .../Profiles_Advanced.xaml | 40 +++++++++++++++++++ .../Resources/en-US/Resources.resw | 32 +++++++++++++++ 6 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index fe26a23e967..de8ec920d3e 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -333,7 +333,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // (The window has a min. size that ensures that there's always a scrollbar thumb.) if (drawableRange < 0) { - assert(false); return; } diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index ae91f9ee978..75c201f76fd 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -302,6 +302,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { return Feature_VtPassthroughMode::IsEnabled() && Feature_VtPassthroughModeSettingInUI::IsEnabled(); } + bool ProfileViewModel::ShowMarksAvailable() const noexcept + { + return Feature_ScrollbarMarks::IsEnabled(); + } + bool ProfileViewModel::AutoMarkPromptsAvailable() const noexcept + { + return Feature_ScrollbarMarks::IsEnabled(); + } + bool ProfileViewModel::RepositionCursorWithMouseAvailable() const noexcept + { + return Feature_ScrollbarMarks::IsEnabled(); + } bool ProfileViewModel::UseParentProcessDirectory() { diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index 2c2762d26e1..d3edd16dc48 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -81,7 +81,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool ShowUnfocusedAppearance(); void CreateUnfocusedAppearance(); void DeleteUnfocusedAppearance(); + bool VtPassthroughAvailable() const noexcept; + bool ShowMarksAvailable() const noexcept; + bool AutoMarkPromptsAvailable() const noexcept; + bool RepositionCursorWithMouseAvailable() const noexcept; til::typed_event DeleteProfileRequested; @@ -115,6 +119,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_profile, Elevate); OBSERVABLE_PROJECTED_SETTING(_profile, VtPassthrough); OBSERVABLE_PROJECTED_SETTING(_profile, ReloadEnvironmentVariables); + OBSERVABLE_PROJECTED_SETTING(_profile, RightClickContextMenu); + OBSERVABLE_PROJECTED_SETTING(_profile, ShowMarks); + OBSERVABLE_PROJECTED_SETTING(_profile, AutoMarkPrompts); + OBSERVABLE_PROJECTED_SETTING(_profile, RepositionCursorWithMouse); WINRT_PROPERTY(bool, IsBaseLayer, false); WINRT_PROPERTY(bool, FocusDeleteButton, false); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index d9c7a95a816..8dcd35f4bd9 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -74,7 +74,11 @@ namespace Microsoft.Terminal.Settings.Editor Boolean EditableUnfocusedAppearance { get; }; Boolean ShowUnfocusedAppearance { get; }; AppearanceViewModel UnfocusedAppearance { get; }; + Boolean VtPassthroughAvailable { get; }; + Boolean ShowMarksAvailable { get; }; + Boolean AutoMarkPromptsAvailable { get; }; + Boolean RepositionCursorWithMouseAvailable { get; }; String EvaluatedIcon { get; }; @@ -109,5 +113,9 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, Elevate); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, VtPassthrough); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RightClickContextMenu); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ShowMarks); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AutoMarkPrompts); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RepositionCursorWithMouse); } } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml index c9403bac0af..f1e3832f85c 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -127,6 +127,35 @@ Style="{StaticResource ToggleSwitchInExpanderStyle}" />
+ + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index ac4e14d1170..5bb762f6b22 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -1218,6 +1218,38 @@ Enable experimental virtual terminal passthrough An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Display a menu on right-click + This controls how a right-click behaves in the terminal + + + When enabled, the Terminal will display a menu on right-click. When disabled, right-clicking will copy the selected text (or paste if there's no selection). + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Display marks on the scrollbar + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + When enabled, the Terminal will display marks in the scrollbar when searching for text, or when shell integration is enabled. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Automatically mark prompts on pressing Enter + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + When enabled, the Terminal automatically add a mark indicating the position of the end of the command when you press enter. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Experimental: Reposition the cursor with mouse clicks + This allows the user to move the text cursor just by clicking with the mouse. + + + When enabled, clicking inside the prompt will move the cursor to that position. This requires shell integration to be enabled in your shell to work as expected. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + Audible An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. From 5b8e731e5d64eafc1df5bd0467d4df82028fd13a Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 21 Mar 2024 21:38:53 +0100 Subject: [PATCH 173/603] AtlasEngine: Fix a OOB read when rendering PUA chars (#16894) When no soft fonts are set up but we're asked to draw a U+EF20, etc., character we'll currently read out-of-bounds, because we don't check whether the soft fonts array is non-empty. This PR fixes the issue by first getting a slice of the data and then checking if it's ok to use. This changeset additionally fixes a couple constinit vs. constexpr cases. I changed them to constinit at some point because I thought that it's more constexpr than constexpr by guaranteeing initialization at compile time. But nope, constinit is actually weaker in a way, because while it does guarantee that, it doesn't actually make the data constant. In other words, constinit is not `.rdata`. --- .../TerminalSettingsModel/Command.cpp | 7 +-- .../KeyChordSerialization.cpp | 4 +- src/inc/til.h | 20 ++++++- src/inc/til/bytes.h | 2 - src/inc/til/replace.h | 29 ++-------- src/inc/til/type_traits.h | 21 +++++++ src/renderer/atlas/BackendD3D.cpp | 56 ++++++++++--------- src/renderer/atlas/BackendD3D.h | 2 +- src/til/ut_til/ReplaceTests.cpp | 2 + src/types/colorTable.cpp | 4 +- 10 files changed, 83 insertions(+), 64 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 516d767481b..91082a4f6d7 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -5,10 +5,10 @@ #include "Command.h" #include "Command.g.cpp" -#include "ActionAndArgs.h" -#include "KeyChordSerialization.h" #include -#include "TerminalSettingsSerializationHelpers.h" +#include + +#include "KeyChordSerialization.h" using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Windows::Foundation::Collections; @@ -23,7 +23,6 @@ namespace winrt static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view IconKey{ "icon" }; static constexpr std::string_view ActionKey{ "command" }; -static constexpr std::string_view ArgsKey{ "args" }; static constexpr std::string_view IterateOnKey{ "iterateOn" }; static constexpr std::string_view CommandsKey{ "commands" }; static constexpr std::string_view KeysKey{ "keys" }; diff --git a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp index b54aa289afc..7be5aed057c 100644 --- a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp @@ -129,7 +129,7 @@ static KeyChord _fromString(std::wstring_view wstr) } using nameToVkeyPair = std::pair; - static constinit til::static_map nameToVkey{ + static constexpr til::static_map nameToVkey{ // The above VKEY_NAME_PAIRS macro contains a list of key-binding names for each virtual key. // This god-awful macro inverts VKEY_NAME_PAIRS and creates a static map of key-binding names to virtual keys. // clang-format off @@ -245,7 +245,7 @@ static KeyChord _fromString(std::wstring_view wstr) static std::wstring _toString(const KeyChord& chord) { using vkeyToNamePair = std::pair; - static constinit til::static_map vkeyToName{ + static constexpr til::static_map vkeyToName{ // The above VKEY_NAME_PAIRS macro contains a list of key-binding strings for each virtual key. // This macro picks the first (most preferred) name and creates a static map of virtual keys to key-binding names. #define GENERATOR(vkey, name1, ...) vkeyToNamePair{ vkey, name1 }, diff --git a/src/inc/til.h b/src/inc/til.h index ee4f29df34c..c1d799fe076 100644 --- a/src/inc/til.h +++ b/src/inc/til.h @@ -20,8 +20,8 @@ #include "til/color.h" #include "til/enumset.h" #include "til/pmr.h" -#include "til/replace.h" #include "til/string.h" +#include "til/type_traits.h" #include "til/u8u16convert.h" // Use keywords on TraceLogging providers to specify the category @@ -54,6 +54,24 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" { + template + as_view_t clamp_slice_abs(const T& view, size_t beg, size_t end) + { + const auto len = view.size(); + end = std::min(end, len); + beg = std::min(beg, end); + return { view.data() + beg, end - beg }; + } + + template + as_view_t clamp_slice_len(const T& view, size_t start, size_t count) + { + const auto len = view.size(); + start = std::min(start, len); + count = std::min(count, len - start); + return { view.data() + start, count }; + } + template void manage_vector(std::vector& vector, typename std::vector::size_type requestedSize, float shrinkThreshold) { diff --git a/src/inc/til/bytes.h b/src/inc/til/bytes.h index f2ab2c6e22d..ab14bdaf3a3 100644 --- a/src/inc/til/bytes.h +++ b/src/inc/til/bytes.h @@ -3,8 +3,6 @@ #pragma once -#include "type_traits.h" - namespace til { template diff --git a/src/inc/til/replace.h b/src/inc/til/replace.h index 1dc9ffafce3..a9ab6e0bd35 100644 --- a/src/inc/til/replace.h +++ b/src/inc/til/replace.h @@ -5,27 +5,6 @@ namespace til { - namespace details - { - template - struct view_type_oracle - { - }; - - template<> - struct view_type_oracle - { - using type = std::string_view; - }; - - template<> - struct view_type_oracle - { - using type = std::wstring_view; - }; - - } - // Method Description: // - This is a function for finding all occurrences of a given string // `needle` in a larger string `haystack`, and replacing them with the @@ -39,8 +18,8 @@ namespace til // - template void replace_needle_in_haystack_inplace(T& haystack, - const typename details::view_type_oracle::type& needle, - const typename details::view_type_oracle::type& replacement) + const as_view_t& needle, + const as_view_t& replacement) { auto pos{ T::npos }; while ((pos = haystack.rfind(needle, pos)) != T::npos) @@ -63,8 +42,8 @@ namespace til // - a copy of `haystack` with all instances of `needle` replaced with `replacement`.` template T replace_needle_in_haystack(const T& haystack, - const typename details::view_type_oracle::type& needle, - const typename details::view_type_oracle::type& replacement) + const as_view_t& needle, + const as_view_t& replacement) { std::basic_string result{ haystack }; replace_needle_in_haystack_inplace(result, needle, replacement); diff --git a/src/inc/til/type_traits.h b/src/inc/til/type_traits.h index a4a10173199..1f426155ebf 100644 --- a/src/inc/til/type_traits.h +++ b/src/inc/til/type_traits.h @@ -32,6 +32,24 @@ namespace til struct is_byte : std::true_type { }; + + template + struct as_view + { + using type = T; + }; + + template + struct as_view> + { + using type = std::span; + }; + + template + struct as_view> + { + using type = std::basic_string_view; + }; } template @@ -45,4 +63,7 @@ namespace til template concept TriviallyCopyable = std::is_trivially_copyable_v; + + template + using as_view_t = typename details::as_view::type; } diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 6a3b7e4ea41..19e98c6557e 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -1430,19 +1430,19 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa _d2dBeginDrawing(); auto shadingType = ShadingType::TextGrayscale; + const D2D1_RECT_F r{ + static_cast(rect.x), + static_cast(rect.y), + static_cast(rect.x + rect.w), + static_cast(rect.y + rect.h), + }; if (BuiltinGlyphs::IsSoftFontChar(glyphIndex)) { - _drawSoftFontGlyph(p, rect, glyphIndex); + _drawSoftFontGlyph(p, r, glyphIndex); } else { - const D2D1_RECT_F r{ - static_cast(rect.x), - static_cast(rect.y), - static_cast(rect.x + rect.w), - static_cast(rect.y + rect.h), - }; BuiltinGlyphs::DrawBuiltinGlyph(p.d2dFactory.get(), _d2dRenderTarget.get(), _brush.get(), r, glyphIndex); shadingType = ShadingType::TextBuiltinGlyph; } @@ -1465,15 +1465,31 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa return glyphEntry; } -void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& rect, u32 glyphIndex) +void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex) { + _d2dRenderTarget->PushAxisAlignedClip(&rect, D2D1_ANTIALIAS_MODE_ALIASED); + const auto restoreD2D = wil::scope_exit([&]() { + _d2dRenderTarget->PopAxisAlignedClip(); + }); + + const auto width = static_cast(p.s->font->softFontCellSize.width); + const auto height = static_cast(p.s->font->softFontCellSize.height); + const auto softFontIndex = glyphIndex - 0xEF20u; + const auto data = til::clamp_slice_len(p.s->font->softFontPattern, height * softFontIndex, height); + + if (data.empty() || data.size() != height) + { + _d2dRenderTarget->Clear(); + return; + } + if (!_softFontBitmap) { // Allocating such a tiny texture is very wasteful (min. texture size on GPUs - // right now is 64kB), but this is a seldomly used feature so it's fine... + // right now is 64kB), but this is a seldom used feature, so it's fine... const D2D1_SIZE_U size{ - static_cast(p.s->font->softFontCellSize.width), - static_cast(p.s->font->softFontCellSize.height), + static_cast(width), + static_cast(height), }; const D2D1_BITMAP_PROPERTIES1 bitmapProperties{ .pixelFormat = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED }, @@ -1484,17 +1500,11 @@ void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& } { - const auto width = static_cast(p.s->font->softFontCellSize.width); - const auto height = static_cast(p.s->font->softFontCellSize.height); - auto bitmapData = Buffer{ width * height }; - const auto softFontIndex = glyphIndex - 0xEF20u; - auto src = p.s->font->softFontPattern.begin() + height * softFontIndex; auto dst = bitmapData.begin(); - for (size_t y = 0; y < height; y++) + for (auto srcBits : data) { - auto srcBits = *src++; for (size_t x = 0; x < width; x++) { const auto srcBitIsSet = (srcBits & 0x8000) != 0; @@ -1508,15 +1518,7 @@ void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& } const auto interpolation = p.s->font->antialiasingMode == AntialiasingMode::Aliased ? D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR : D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC; - const D2D1_RECT_F dest{ - static_cast(rect.x), - static_cast(rect.y), - static_cast(rect.x + rect.w), - static_cast(rect.y + rect.h), - }; - - _d2dBeginDrawing(); - _d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &dest, 1, interpolation, nullptr, nullptr); + _d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &rect, 1, interpolation, nullptr, nullptr); } void BackendD3D::_drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect) diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 784c854c152..5e8055aecd0 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -215,7 +215,7 @@ namespace Microsoft::Console::Render::Atlas ATLAS_ATTR_COLD void _drawTextOverlapSplit(const RenderingPayload& p, u16 y); [[nodiscard]] ATLAS_ATTR_COLD AtlasGlyphEntry* _drawGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex); AtlasGlyphEntry* _drawBuiltinGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex); - void _drawSoftFontGlyph(const RenderingPayload& p, const stbrp_rect& rect, u32 glyphIndex); + void _drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex); void _drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect); static AtlasGlyphEntry* _drawGlyphAllocateEntry(const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex); static void _splitDoubleHeightGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, AtlasGlyphEntry* glyphEntry); diff --git a/src/til/ut_til/ReplaceTests.cpp b/src/til/ut_til/ReplaceTests.cpp index 827c6872cdd..349251510f6 100644 --- a/src/til/ut_til/ReplaceTests.cpp +++ b/src/til/ut_til/ReplaceTests.cpp @@ -4,6 +4,8 @@ #include "precomp.h" #include "WexTestClass.h" +#include + using namespace WEX::Common; using namespace WEX::Logging; using namespace WEX::TestExecution; diff --git a/src/types/colorTable.cpp b/src/types/colorTable.cpp index 52f043e34e0..e6498a6b0c8 100644 --- a/src/types/colorTable.cpp +++ b/src/types/colorTable.cpp @@ -267,7 +267,7 @@ static constexpr std::array standard256ColorTable{ til::color{ 0xEE, 0xEE, 0xEE }, }; -static constinit til::presorted_static_map xorgAppVariantColorTable{ +static constexpr til::presorted_static_map xorgAppVariantColorTable{ std::pair{ "antiquewhite"sv, std::array{ til::color{ 250, 235, 215 }, til::color{ 255, 239, 219 }, til::color{ 238, 223, 204 }, til::color{ 205, 192, 176 }, til::color{ 139, 131, 120 } } }, std::pair{ "aquamarine"sv, std::array{ til::color{ 127, 255, 212 }, til::color{ 127, 255, 212 }, til::color{ 118, 238, 198 }, til::color{ 102, 205, 170 }, til::color{ 69, 139, 116 } } }, std::pair{ "azure"sv, std::array{ til::color{ 240, 255, 255 }, til::color{ 240, 255, 255 }, til::color{ 224, 238, 238 }, til::color{ 193, 205, 205 }, til::color{ 131, 139, 139 } } }, @@ -348,7 +348,7 @@ static constinit til::presorted_static_map xorgAppVariantColorTable{ std::pair{ "yellow"sv, std::array{ til::color{ 255, 255, 0 }, til::color{ 255, 255, 0 }, til::color{ 238, 238, 0 }, til::color{ 205, 205, 0 }, til::color{ 139, 139, 0 } } }, }; -static constinit til::presorted_static_map xorgAppColorTable{ +static constexpr til::presorted_static_map xorgAppColorTable{ std::pair{ "aliceblue"sv, til::color{ 240, 248, 255 } }, std::pair{ "aqua"sv, til::color{ 0, 255, 255 } }, std::pair{ "beige"sv, til::color{ 245, 245, 220 } }, From 038cfca0294e59d430dd2f5ab9c061b4ddd87147 Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Mon, 25 Mar 2024 18:10:04 +0530 Subject: [PATCH 174/603] Add VSCode configs for better out-of-box LSP support (#16920) Prevents errors generated due to unresolved headers in VSCode ## Validation Steps Performed - Errors messages are removed. - Headers are resolved properly. --- .vscode/settings.json | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e7802ba358c..0dd3cf37945 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,28 @@ { "C_Cpp.default.browse.databaseFilename": "${workspaceFolder}\\.vscode\\.BROWSE.VC.DB", + "C_Cpp.default.browse.limitSymbolsToIncludedHeaders": true, "C_Cpp.default.browse.path": [ "${workspaceFolder}" ], + "C_Cpp.default.cppStandard": "c++20", + "C_Cpp.default.cStandard": "c17", + "C_Cpp.default.defines": [ + "_DEBUG", + "_UNICODE", + "BUILD_ONECORE_INTERACTIVITY", + "DBG", + "NT_SUCCESS", + "UNICODE", + "UNIT_TESTING", + "INLINE_TEST_METHOD_MARKUP", + ], + "C_Cpp.default.includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/**/Generated Files", + "${workspaceFolder}/**/inc", + "${workspaceFolder}/**/include", + "${workspaceFolder}/**/Include" + ], "C_Cpp.loggingLevel": "None", "files.associations": { "xstring": "cpp", @@ -98,14 +118,13 @@ "coroutine": "cpp", "format": "cpp", "forward_list": "cpp", - "latch": "cpp" + "latch": "cpp", + "gsl_assert": "cpp" }, "files.exclude": { "**/bin/**": true, + "**/Generated Files/**": true, "**/obj/**": true, - "**/Generated Files/**": true + "**/packages/**": true, }, - "search.exclude": { - "**/packages/**": true - } -} +} \ No newline at end of file From 8403b38c81858a66de4213e43b94a0e20d66b9fe Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 25 Mar 2024 19:34:49 +0100 Subject: [PATCH 175/603] Enable /Ob1 in Debug builds (#16932) This makes the build a tiny bit faster (less time spent linking) and makes everything run roughly 50% faster. --- src/common.build.pre.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common.build.pre.props b/src/common.build.pre.props index 49ac859f6b8..702df6425a4 100644 --- a/src/common.build.pre.props +++ b/src/common.build.pre.props @@ -182,6 +182,7 @@ + OnlyExplicitInline Disabled _DEBUG;DBG;%(PreprocessorDefinitions) From 77d5e23ef2229f7cde8b24fcb2498dccc4c7ca60 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 25 Mar 2024 19:42:22 +0100 Subject: [PATCH 176/603] Make ploc translations predictable (#16924) --- build/pipelines/daily-loc-submission.yml | 1 + .../scripts/Generate-PseudoLocalizations.ps1 | 16 + .../Package/Resources/qps-ploc/Resources.resw | 2 +- .../Resources/qps-ploca/Resources.resw | 2 +- .../Resources/qps-plocm/Resources.resw | 2 +- .../Resources/qps-ploc/Resources.resw | 10 +- .../Resources/qps-ploca/Resources.resw | 10 +- .../Resources/qps-plocm/Resources.resw | 10 +- .../Resources/qps-ploc/ContextMenu.resw | 10 +- .../Resources/qps-ploc/Resources.resw | 478 +++++----- .../Resources/qps-ploca/ContextMenu.resw | 10 +- .../Resources/qps-ploca/Resources.resw | 478 +++++----- .../Resources/qps-plocm/ContextMenu.resw | 10 +- .../Resources/qps-plocm/Resources.resw | 478 +++++----- .../Resources/qps-ploc/Resources.resw | 56 +- .../Resources/qps-ploca/Resources.resw | 56 +- .../Resources/qps-plocm/Resources.resw | 56 +- .../Resources/qps-ploc/Resources.resw | 88 +- .../Resources/qps-ploca/Resources.resw | 88 +- .../Resources/qps-plocm/Resources.resw | 88 +- .../Resources/qps-ploc/Resources.resw | 854 ++++++++--------- .../Resources/qps-ploca/Resources.resw | 858 +++++++++--------- .../Resources/qps-plocm/Resources.resw | 856 ++++++++--------- .../Resources/qps-ploc/Resources.resw | 360 ++++---- .../Resources/qps-ploca/Resources.resw | 360 ++++---- .../Resources/qps-plocm/Resources.resw | 360 ++++---- tools/ConvertTo-PseudoLocalization.ps1 | 119 +++ 27 files changed, 2974 insertions(+), 2742 deletions(-) create mode 100644 build/scripts/Generate-PseudoLocalizations.ps1 create mode 100644 tools/ConvertTo-PseudoLocalization.ps1 diff --git a/build/pipelines/daily-loc-submission.yml b/build/pipelines/daily-loc-submission.yml index 93964c9b8a3..7a7e1769b59 100644 --- a/build/pipelines/daily-loc-submission.yml +++ b/build/pipelines/daily-loc-submission.yml @@ -75,6 +75,7 @@ steps: rm LocOutputMunged.tar rm -r -fo LocOutput & ./build/scripts/Copy-ContextMenuResourcesToCascadiaPackage.ps1 + & ./build/scripts/Generate-PseudoLocalizations.ps1 displayName: Move Loc files to the right places - pwsh: |- diff --git a/build/scripts/Generate-PseudoLocalizations.ps1 b/build/scripts/Generate-PseudoLocalizations.ps1 new file mode 100644 index 00000000000..8e6b5b51ef9 --- /dev/null +++ b/build/scripts/Generate-PseudoLocalizations.ps1 @@ -0,0 +1,16 @@ +Get-ChildItem -Recurse -Filter *.resw + | Where-Object { $_.Directory.Name.StartsWith("qps-ploc") } + | ForEach-Object { + $source = Join-Path $_.Directory "../en-US/$($_.Name)" + $target = $_ + + $ploc = ./tools/ConvertTo-PseudoLocalization.ps1 -Path $source + + $writerSettings = [System.Xml.XmlWriterSettings]::new() + $writerSettings.NewLineChars = "`r`n" + $writerSettings.Indent = $true + $writer = [System.Xml.XmlWriter]::Create($target, $writerSettings) + $ploc.Save($writer) + $writer.Flush() + $writer.Close() + } diff --git a/scratch/ScratchIslandApp/Package/Resources/qps-ploc/Resources.resw b/scratch/ScratchIslandApp/Package/Resources/qps-ploc/Resources.resw index 177c764fcd0..e20e3c69981 100644 --- a/scratch/ScratchIslandApp/Package/Resources/qps-ploc/Resources.resw +++ b/scratch/ScratchIslandApp/Package/Resources/qps-ploc/Resources.resw @@ -118,6 +118,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - А şςѓάţćћ ǻрр ƒθŗ χÂΜĿ Íŝĺąήðş ŧеšτş !!! !!! !!! ! + Ά śςґàτсн ąρφ ƒоř ΧΆΜĻ Ìŝļàиđś τёşτś !!! !!! !!! ! \ No newline at end of file diff --git a/scratch/ScratchIslandApp/Package/Resources/qps-ploca/Resources.resw b/scratch/ScratchIslandApp/Package/Resources/qps-ploca/Resources.resw index fbd50ae1541..e20e3c69981 100644 --- a/scratch/ScratchIslandApp/Package/Resources/qps-ploca/Resources.resw +++ b/scratch/ScratchIslandApp/Package/Resources/qps-ploca/Resources.resw @@ -118,6 +118,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ă šςґаτćĥ àρφ ƒǿя ЖΆΜĹ Іѕℓаñďş ťêšţŝ !!! !!! !!! ! + Ά śςґàτсн ąρφ ƒоř ΧΆΜĻ Ìŝļàиđś τёşτś !!! !!! !!! ! \ No newline at end of file diff --git a/scratch/ScratchIslandApp/Package/Resources/qps-plocm/Resources.resw b/scratch/ScratchIslandApp/Package/Resources/qps-plocm/Resources.resw index f3185244d79..e20e3c69981 100644 --- a/scratch/ScratchIslandApp/Package/Resources/qps-plocm/Resources.resw +++ b/scratch/ScratchIslandApp/Package/Resources/qps-plocm/Resources.resw @@ -118,6 +118,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ă śćяǻт¢н ãрρ ƒσг ХĂМĽ Īşłдήďѕ ťέśτş !!! !!! !!! ! + Ά śςґàτсн ąρφ ƒоř ΧΆΜĻ Ìŝļàиđś τёşτś !!! !!! !!! ! \ No newline at end of file diff --git a/src/cascadia/CascadiaPackage/Resources/qps-ploc/Resources.resw b/src/cascadia/CascadiaPackage/Resources/qps-ploc/Resources.resw index 7b15a4ac9ce..28f0b780470 100644 --- a/src/cascadia/CascadiaPackage/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/CascadiaPackage/Resources/qps-ploc/Resources.resw @@ -166,7 +166,7 @@ {Locked=qps-ploc,qps-ploca,qps-plocm} - Τнĕ Ņëω Ẅίηđŏẃś Ťėŗmįйάĺ !!! !!! ! + Τĥз Йéщ Ẃįńđôẃѕ Тéѓmĩиâļ !!! !!! ! The Windows Terminal, but Unofficial @@ -177,22 +177,22 @@ {Locked} - Ŵíňďōẁŝ Тєřмīπǻļ ωїτĥ å ρѓēνіéŵ θƒ ũφсőмϊπġ ƒєąτΰґёѕ !!! !!! !!! !!! !!! + Щΐňδόŵѕ Ŧęřмĭʼnäℓ ẅîťħ à φřеνίëẃ θƒ џрсøмΐʼnğ ƒĕāŧųřэś !!! !!! !!! !!! !!! Open in Terminal (&Dev) {Locked} The dev build will never be seen in multiple languages - Ŏрέи ìη Тèřmīŋªŀ (&Cãńãґγ) !!! !!! ! + Θρēņ ïη Ţéгmĭηäŀ (&Çäņдѓγ) !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Canary version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Φφєň ΐñ Ŧéгмϊñаľ &Pŕėνĭ℮ώ !!! !!! ! + Όрèп ìņ Ţêŕmїʼnåļ &Рѓзνι℮ω !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Ωρєⁿ ïπ &Těѓmĭñäĺ !!! !! + Óрêп ìл &Ťěѓmιиåĺ !!! !! This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. \ No newline at end of file diff --git a/src/cascadia/CascadiaPackage/Resources/qps-ploca/Resources.resw b/src/cascadia/CascadiaPackage/Resources/qps-ploca/Resources.resw index 20ff50ac556..28f0b780470 100644 --- a/src/cascadia/CascadiaPackage/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/CascadiaPackage/Resources/qps-ploca/Resources.resw @@ -166,7 +166,7 @@ {Locked=qps-ploc,qps-ploca,qps-plocm} - Ťĥё Ñēщ Шίⁿðоẅś Ťėгмîήāľ !!! !!! ! + Τĥз Йéщ Ẃįńđôẃѕ Тéѓmĩиâļ !!! !!! ! The Windows Terminal, but Unofficial @@ -177,22 +177,22 @@ {Locked} - Шίηđóŵš Ŧĕяmїйàℓ ẁітħ ª φяęνîёщ όƒ ûφĉбмíήĝ ƒêåťµřεŝ !!! !!! !!! !!! !!! + Щΐňδόŵѕ Ŧęřмĭʼnäℓ ẅîťħ à φřеνίëẃ θƒ џрсøмΐʼnğ ƒĕāŧųřэś !!! !!! !!! !!! !!! Open in Terminal (&Dev) {Locked} The dev build will never be seen in multiple languages - Óрëπ íи Ťēяmілǻŀ (&Cäηàřÿ) !!! !!! ! + Θρēņ ïη Ţéгmĭηäŀ (&Çäņдѓγ) !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Canary version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Óрзń ΐή Ŧěґмιлāℓ &Pѓéνīĕω !!! !!! ! + Όрèп ìņ Ţêŕmїʼnåļ &Рѓзνι℮ω !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Ôрëη ïп &Tēѓмϊŋãł !!! !! + Óрêп ìл &Ťěѓmιиåĺ !!! !! This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. \ No newline at end of file diff --git a/src/cascadia/CascadiaPackage/Resources/qps-plocm/Resources.resw b/src/cascadia/CascadiaPackage/Resources/qps-plocm/Resources.resw index 9a9d9f7bbc0..28f0b780470 100644 --- a/src/cascadia/CascadiaPackage/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/CascadiaPackage/Resources/qps-plocm/Resources.resw @@ -166,7 +166,7 @@ {Locked=qps-ploc,qps-ploca,qps-plocm} - Тĥё Ńēẁ Шіπđοωš Тěřмιňαŀ !!! !!! ! + Τĥз Йéщ Ẃįńđôẃѕ Тéѓmĩиâļ !!! !!! ! The Windows Terminal, but Unofficial @@ -177,22 +177,22 @@ {Locked} - Шίήďŏшś Ŧêямĩňāľ ŵíτн ă ρѓëνιêω øƒ ũρčŏмįηğ ƒєáţũŗêš !!! !!! !!! !!! !!! + Щΐňδόŵѕ Ŧęřмĭʼnäℓ ẅîťħ à φřеνίëẃ θƒ џрсøмΐʼnğ ƒĕāŧųřэś !!! !!! !!! !!! !!! Open in Terminal (&Dev) {Locked} The dev build will never be seen in multiple languages - Фφěй ĩń Тêřmιπâł (&Cãⁿǻřу) !!! !!! ! + Θρēņ ïη Ţéгmĭηäŀ (&Çäņдѓγ) !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Canary version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Óφ℮ʼn ΐŋ Τėřmīйäĺ &Pяєνϊëẃ !!! !!! ! + Όрèп ìņ Ţêŕmїʼnåļ &Рѓзνι℮ω !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Ορέл ϊŋ &Téŕmįñāℓ !!! !! + Óрêп ìл &Ťěѓmιиåĺ !!! !! This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-ploc/ContextMenu.resw b/src/cascadia/TerminalApp/Resources/qps-ploc/ContextMenu.resw index 7b15a4ac9ce..28f0b780470 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploc/ContextMenu.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploc/ContextMenu.resw @@ -166,7 +166,7 @@ {Locked=qps-ploc,qps-ploca,qps-plocm} - Τнĕ Ņëω Ẅίηđŏẃś Ťėŗmįйάĺ !!! !!! ! + Τĥз Йéщ Ẃįńđôẃѕ Тéѓmĩиâļ !!! !!! ! The Windows Terminal, but Unofficial @@ -177,22 +177,22 @@ {Locked} - Ŵíňďōẁŝ Тєřмīπǻļ ωїτĥ å ρѓēνіéŵ θƒ ũφсőмϊπġ ƒєąτΰґёѕ !!! !!! !!! !!! !!! + Щΐňδόŵѕ Ŧęřмĭʼnäℓ ẅîťħ à φřеνίëẃ θƒ џрсøмΐʼnğ ƒĕāŧųřэś !!! !!! !!! !!! !!! Open in Terminal (&Dev) {Locked} The dev build will never be seen in multiple languages - Ŏрέи ìη Тèřmīŋªŀ (&Cãńãґγ) !!! !!! ! + Θρēņ ïη Ţéгmĭηäŀ (&Çäņдѓγ) !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Canary version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Φφєň ΐñ Ŧéгмϊñаľ &Pŕėνĭ℮ώ !!! !!! ! + Όрèп ìņ Ţêŕmїʼnåļ &Рѓзνι℮ω !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Ωρєⁿ ïπ &Těѓmĭñäĺ !!! !! + Óрêп ìл &Ťěѓmιиåĺ !!! !! This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw index 4a3e99ee3ff..206c42a94b4 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw @@ -118,148 +118,148 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Şĕţţіⁿģѕ ςŏûℓđ ʼnθт в℮ ℓòăδēδ ƒřσm ƒіℓε. Çћέĉк ƒòя şŷиτåж эřѓόґş, ΐлсŀµðĭηġ ŧŕàįŀїиĝ ċõmмåş. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Şετťĭиġś ςòűłď йσт ьé ŀŏдðéď ƒřǿм ƒīļē. Ĉнėćķ ƒσŗ ŝуήтª× ĕяŗбґş, ιйçľůďĩηĝ тŗāіļīňġ ćǿмmāŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ćǿΰľđ πöт ƒîηđ уоџř đєƒаџļт φгóƒíĺέ įń γσΰґ ľϊѕţ øƒ φřбƒїļēś - ųŝĩηġ тђę ƒίяśт φґθƒĩĺē. €ђéćκ ťб máќë ѕцřз ťħè "defaultProfile" mǻτςĥéѕ ţђè ĞÚІĎ ôƒ õňë òƒ ÿбμř ряǿƒїļĕѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ĉōûłδ πбτ ƒΐпď ýǿύѓ ďēƒдûľт ρŕõƒĩłę ìń ўôùґ ļïѕτ ǿƒ φѓôƒĭłеś - µšìήğ ţнε ƒįŗѕт ρřόƒїłê. Ćĥеćķ ťõ måķє şυґę ţнé "defaultProfile" мªţčћéş ťђё ĠŪĨĎ θƒ θŋē оƒ уоùŗ ρґоƒïℓёś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"defaultProfile\""} - ₣õůήδ мџŀŧĭрłè рѓőƒιĺéŝ ŵїτђ τне šаме ĢŬΪĎ ιή ŷôûя şёţţïňģŝ ƒïļē - ĩĝпоѓĩлğ đűρļіċáť℮ş. Μªќ℮ şúѓё ėǻςн ρґőƒîłè'š ĢЦЇĐ ιś ũŋĩqυέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣óŭиď мύŀťìрŀэ φŕσƒïĺєś ẃïτћ ŧнě ѕámĕ ĠŲІÐ įй ўоűя ѕĕţţîήĝš ƒìŀė - įģʼnǿřΐⁿğ δûρľΐçâтéŝ. Мãĸё śμѓέ ĕáĉћ ρяσƒîĺė'ѕ ĢŰĮÐ īş ϋńîqџë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - ₣ŏџŋð ä ρѓõƒіℓę шîţћ ǻй īпνªŀīð "colorScheme". Đëƒαυľτîпĝ ŧђãť ρřоƒíĺě ŧő тнë ðĕƒãůľŧ çōļóяş. Μâĸè śűřė τћàŧ ώнēη şеŧťįⁿğ д "colorScheme", ţћé νáℓūë máτсħěş ŧнє "name" ōƒ á сóℓöѓ ščнèмë їп тħė "schemes" łîšť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣ŏúņđ ā ρřόƒįľĕ щĭťћ ǻή ϊпνåŀίď "colorScheme". Ďēƒāúłтіиğ ţħąţ ряόƒίŀе ţŏ тĥέ ðеƒãµľţ çбĺбŗś. Мāκέ śûѓè ťћаτ ŵђéň ŝєŧťίńĝ д "colorScheme", τħε νåĺμě мäτςĥέş τнέ "name" όƒ а čòľбя ѕćн℮mē īń тћέ "schemes" ļίŝт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - Пò φгŏƒїĺèѕ ẁєґє ƒοúиđ ΐņ ўøûя šзŧţìŋġѕ. !!! !!! !!! !!! + Ňǿ ρяóƒíℓěş щ℮ŕέ ƒòμйδ ϊη ỳοūґ ś℮ŧтĭπģѕ. !!! !!! !!! !!! - Ãļł рŗǿƒíŀέŝ ωėŗě ћіďđέʼn ιⁿ ўòüѓ şėţтΐйĝş. Υōυ мûŝт ĥªνэ àτ ľєäѕţ óňě иôŋ-ђïδđěń φŗõƒΐľé. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Аℓł ρяοƒįłέš ωèяê ђΐđðěʼn îη ўőϋř śěŧτϊиģş. Ϋŏυ müѕŧ ħäνė άţ ļεǻѕτ οñє пóй-ћίďðēʼn φŗóƒįŀē. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ŝęťтįлġś çǿµŀđ лόţ ьэ яēℓбаďêđ ƒгǿм ƒīľē. Ċћєсќ ƒοг ѕγŋţαх έŗгόѓś, ïπċℓύδϊņĝ ťгąîľϊиĝ čòммªŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ѕєŧτĩņğš ¢ŏûłď ποţ ьė řëĺбάδзď ƒŕŏм ƒίľе. Çĥэςκ ƒõг şÿπţаж ěгяōŗš, îņ¢ŀύðīⁿğ ťŕăïŀĩπĝ čǿmмãş. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ŧěmрθřǻŕĩℓÿ џśîŋģ ţĥз Ẁĭņðбшѕ Ŧзŗмìήâĺ ðėƒàцľŧ ŝěτтĭņģŝ. !!! !!! !!! !!! !!! ! + Ťемрőŗāřĩľŷ űśϊⁿģ ťђё Щìπδоωş Ţεѓмîήäĺ δêƒаμłт şèţτïńġś. !!! !!! !!! !!! !!! ! - ₣дīłėδ τö ĺōáđ š℮τтіňģś !!! !!! + ₣äĩľєď τσ ŀőåð ŝέťţĩⁿģŝ !!! !!! - Έлςöůлťëяèδ éґѓőґş ẁђīļé łσǻđιπģ џśёґ ѕ℮ŧтíⁿĝş !!! !!! !!! !!! ! + Εŋĉőųņŧзяëď зяѓōяś ŵħΐļê ŀθαðĩήġ ùŝéѓ śзťŧĭпģŝ !!! !!! !!! !!! ! - ΘК + ÓΚ - Ŵāŕиĩпģ: !! + Ŵаґήĩήģ: !! - Ťĥє "{0}" įšή'ŧ řΰňηіпğ θл ŷбυŕ мāĉћіņë. Τĥίѕ ćªñ рŕзνзπт τнέ Τёřmîпǻĺ ƒŗóм ѓéčêîνîñģ ķёŷъǿãŗð ĩⁿφµт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŧħє "{0}" ĭşп'ţ řμллĭиġ оʼn ўόùг мàςĥįńë. Ŧħїś ¢áⁿ φřэνєńτ тнэ Тэґмïñăĺ ƒŕöm ŗеς℮íνίиĝ ķēỳвŏαŗδ ĭⁿрΰţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the OS-localized name of the TabletInputService - ÒĶ + ФĶ - ₣αĭℓэđ τǿ гэľоāδ śэŧŧįηģš !!! !!! ! + ₣áĭļèđ ťσ ѓéłøǻð şèŧŧíпĝŝ !!! !!! ! https://go.microsoft.com/fwlink/?linkid=2125419 {Locked}This is a FWLink, so it will be localized with the fwlink tool - Άвòűт ! + ∆вòΰŧ ! - ₣ээđъàċк !! + ₣ęęðъâçќ !! - Ŝęťţĩπģş !! + Śêŧťіňğŝ !! - Сåήċēĺ ! + Ĉάπςеŀ ! - Ćłőŝě áľļ !!! + Ċľθŝє αŀļ !!! - Qûіŧ ! + Qùíт ! - Đŏ уǿü ẃάñт тő ¢ľõśé ăℓℓ ŧаъś? !!! !!! !!! + Đǿ ÿõű ŵãŋт тŏ ¢ľòŝê ăŀľ ŧãвŝ? !!! !!! !!! - Мũļťįρľê φāńεš !!! ! + Мµļтíрłĕ φдпėŝ !!! ! - Çŀŏѕё... !! + Ćļôŝέ... !! - Čļöѕέ Ţàьś тø ťħě Гïĝнţ !!! !!! + Ċĺοşέ Ţаъş ťό ŧђé Яΐğђт !!! !!! - Ĉļőşе Öŧђεŗ Ţдьŝ !!! ! + Ćĺόѕ℮ Όтђèř Ŧâьś !!! ! - Сļŏšè Τàв !!! + Сĺôšę Ťăв !!! - Ćľοšэ Ρàņε !!! + Ćŀöśё Раņé !!! - Šφľîŧ Ţǻь !!! + Šрľīτ Τàв !!! - Şрļĭţ Ραŋè !!! + Šрŀіт Ρªňë !!! - Щēь Ŝêαřçĥ !!! + Ẅёв Şĕаŕčĥ !!! - Ćбłöř... !! + Ċõŀόř... !! - Сџşţбм... !!! + Ċµѕťøм... !!! - Ŕēśёť ! + Яěšěŧ ! - Γёŋάmē Τаъ !!! + Γεñамē Ťãв !!! - Đύφļϊçåţέ Ţάъ !!! + Ďϋφľіčάтέ Τàв !!! - ₣σůήđ а рřòƒïłè щíţħ ªŋ îŋνāℓіď "backgroundImage". Ďєƒªϋĺţįпĝ ŧħåť рřθƒίľē ţó нąνê ńǿ ъàċкğѓóūñď îмåğэ. Мàκě ŝμяз ťнªţ ẁĥėл šęτŧΐñġ á "backgroundImage", тђε νáľΰє їś α νдŀĩδ ƒïļë φäŧħ τö ãп ϊмªġę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"backgroundImage\""} - ₣όūпđ ǻ ргõƒįļє ωĩţђ ªň ϊήνåľīď "icon". Đêƒàùĺţíηģ тнаŧ φřòƒїℓє ţб ħдνĕ ŋò ĭćбñ. Μαќě śüяê τћåţ ωĥëη śзťŧĭηģ áй "icon", ţħє νàĺųэ ίѕ ǻ ναℓїđ ƒíľě φǻτĥ ťθ ăŋ ĭmǻģε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣ǿũиđ à рřöƒϊℓз ŵĩţн аñ įņνàŀїδ "icon". Ðěƒаúľτīŋğ ţħаτ ρřόƒìŀё тб ђâνє пǿ íčой. Мàĸë ŝùřë ŧĥаţ ωĥĕл ŝеτŧīлĝ ăй "icon", τħε νāłϋë ïŝ ă νàľīđ ƒïŀè рªтн ţő äи ïмäģё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. - Щдřńіńġš ωεяέ ƒöџήδ ẁнĭŀè ρąґśîήġ γôµґ кęуъїñďϊņģś: !!! !!! !!! !!! !!! + Щαѓńΐňģš ώĕřе ƒбŭπδ ώħīļë рăяşìⁿġ ўσυŕ κёỳвĩиðīήġş: !!! !!! !!! !!! !!! - • ₣őûńđ å κєÿьΐпđĩŋģ шíţћ ŧøо mαиў śτřιñĝŝ ƒôг ŧђē "keys" дřгãỳ. Ţнέŕê śћőũŀδ όŋłγ ъę òⁿё śťŗΐņĝ νáℓΰê įη ŧће "keys" ªŕѓдý. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + • ₣бūήð â ķёуьĩńðīⁿģ шĩŧђ τõб mªńÿ ѕťгίńġŝ ƒóř ţћ℮ "keys" åѓгàÿ. Тħėгē ѕĥōμĺđ бñľў вĕ ŏи℮ ѕтřïлģ νăŀůé ĭʼn ţĥė "keys" άřřàγ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"keys\"","•"} This glyph is a bullet, used in a bulleted list. - • ₣αιľęδ τō ρāřşέ αℓļ şũвçőммāпðŝ òƒ ʼnęşťёđ ćőмmáйδ. !!! !!! !!! !!! !!! + • ₣ąìļ℮đ ţŏ ρǻŕşε âłľ śцъċőммåиðѕ ōƒ л℮śť℮ď сömmąπđ. !!! !!! !!! !!! !!! - • ₣ôцñδ ǻ ќęγъĭʼnđϊⁿĝ ţħāτ щąś мĩšŝįņĝ ª řèqυïяеď φǻŗåmетėŗ νáļūé. Τħĩš ķэўвįпđīņğ ẃіľł ь℮ ΐģηòřėδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • ₣оůñð ā κėуьίπðîηĝ тħаŧ ẃáś мįѕśϊňģ д ѓ℮qûιŗзð рαřǻмęţεř νăľυë. Тнίѕ кεγьĩņđíņģ щìľļ вє įĝлóяеđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - • Тћĕ ŝρёςĩƒīęď "τћêm℮" ẁаś ηοť ƒōϋηð іŋ ťне ĺιşţ бƒ τћémėś. Ť℮mрσґäяīľŷ ƒдℓŀïʼnġ ъáςк ŧó τĥ℮ ďëƒåŭℓť νаľūë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • Тћë ѕрèçїƒĭёď "τћ℮мé" ώαѕ ήøţ ƒόûπđ ΐл ťћê ℓíŝť σƒ ťнεmέѕ. Ŧěмрǿŕдѓΐļγ ƒдℓłίńģ вà¢ĸ τő ŧнě đέƒαùŀŧ νǻľΰê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - Ťђé "globals" φгöφëŕτγ їѕ ďēφѓē¢áτєδ - ÿòџř ŝетτіήğş mіğђт πє℮đ ϋρδªτîηġ. !!! !!! !!! !!! !!! !!! !!! ! + Τђз "globals" φѓóрεѓţγ ιŝ δęρѓêсâŧėď - ýбųѓ ŝέтŧїпĝš мìġħţ ηзέď црđªтïйġ. !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"globals\""} @@ -267,635 +267,635 @@ {Locked}This is a FWLink, so it will be localized with the fwlink tool - ₣ôѓ mθřē іпƒő, śэе ťħіš ώéь φάģз. !!! !!! !!! + ₣οř мòŕе îήƒθ, şéэ ţħìѕ ŵєь рąġé. !!! !!! !!! - ₣åίĺзđ ŧø éхφǻйď а çбмmãņđ ŵíŧħ "iterateOn" ѕ℮ŧ. Ţĥīѕ ċοммąπď ŵіℓļ вέ ιġπǿяёď. !!! !!! !!! !!! !!! !!! !!! !! + ₣ăіľέđ ţö ехρåņď ǻ çǿммãηδ ẃìţĥ "iterateOn" şετ. Ťĥīš ćōmмåиď щΐℓĺ ве ĭģňóѓëđ. !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"iterateOn\""} - ₣õΰпď ą čоммдⁿδ ẅίτħ ăп íиνаłіð "colorScheme". Тнïş ċσmmãⁿδ ωîĺł ьę їģñóґéδ. Μãķέ ŝũř℮ ŧнǻţ щĥĕп šзτťіⁿğ ã "colorScheme", ŧħє νаłџē måŧćн℮ѕ тђê "name" οƒ ã ċóļőѓ śçĥємє ΐʼn ţђέ "schemes" ļΐѕŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣óŭйď ã сóмmаńδ ẁϊţђ дή îⁿναļīð "colorScheme". Тћîŝ ĉǿмmαήδ ωïĺĺ вε íğлŏгëð. Мâķэ ѕϋŗê τĥāţ ẅћэй šěτťîņġ å "colorScheme", ţĥé νаļΰé мªŧçћєѕ ţħє "name" őƒ ă ¢ôℓôř ŝςђзмê įň тнε "schemes" ľïşŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - ₣σùņð α "splitPane" ςöмmǻńđ ŵіťн āⁿ ίňνǻŀιď "size". Τћιş čôмmαñδ ẁĭłļ ъĕ īĝйŏгēđ. Μάкé ѕûřέ тђê śįźе įѕ вëтώєèή 0 äʼnδ 1, ĕ×ċłцşìν℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣ŏΰπđ ā "splitPane" ¢ǿммäήđ ŵìтћ ăη îŋνąłιđ "size". Ţђíѕ ċǿммªпđ ŵīĺℓ ьė ìĝñōгёđ. Мăќέ šύгз τĥě śιźê įѕ ъèτшέëπ 0 äⁿð 1, εхčļûŝίνз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"splitPane\"","\"size\""} - ₣αίŀèď το рàяśё "startupActions". !!! !!! !!! + ₣âìľęď ťǿ рαяŝé "startupActions". !!! !!! !!! {Locked="\"startupActions\""} - ₣ōũńď mũłťιрļε ĕήνïґőŋмèŋŧ νаřîåъļęš щįţн ťĥё śámê ňämé îń ďĩƒƒзґěňţ ĉãśĕş (ľŏшèґ/ũρрег) - оņłỳ олě νдłūэ щίłℓ ьє ũşęδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣öųñđ múℓтĭφļę èⁿνіŕθлмėňţ ναřίдъľêѕ ẅïτħ ťħз śăмė ήámе їή đїƒƒέřέηт čãşзś (ĺóщěг/ůрρеŕ) - óлℓŷ οиę νǻľµě ẁιľľ вё ųşέď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Λň όφţĩøŋàľ čómmаňδ, ẃΐŧĥ āŗğμмĕŋτѕ, τó ь℮ šφªшлêđ íň тħę ⁿěẅ тαь σř рäńë !!! !!! !!! !!! !!! !!! !!! + Åʼn ǿртíоñαŀ ċοmмäηď, ẁίŧћ ãŗĝūměʼnтş, ţô вέ šρªẃйèđ įņ ťђė πεẁ ŧав ŏŗ рªņε !!! !!! !!! !!! !!! !!! !!! - Μöνę ƒøсųѕ ŧø αņŏτђзŕ τãв !!! !!! ! + Μòνê ƒöčµѕ ŧθ ãņøτнёř τåъ !!! !!! ! - Μőνз ƒθçŭŝ ţŏ ŧђé ñê×т ťªъ !!! !!! ! + Мõνè ƒòćüŝ τθ ťћé пĕ×т ťαъ !!! !!! ! - Άņ áĺîäš ƒòґ τħè "focus-tab" šϋвċømмáπď. !!! !!! !!! !!! + Дŋ äℓĭάş ƒоř τћę "focus-tab" šųъčǿммáñđ. !!! !!! !!! !!! {Locked="\"focus-tab\""} - Мøνĕ ƒσсûŝ ťő тђе рґένίøŭš ţαв !!! !!! !!! + Μöνє ƒоćüŝ ŧó ţĥë рřěνĩομѕ тãь !!! !!! !!! - Μбνέ ƒøĉüŝ ťћĕ ţáв áŧ ťĥě ĝїνеń їпđèж !!! !!! !!! !! + Мονé ƒоćűŝ ťħέ τåь àτ ţĥé ĝįνèň іňďėж !!! !!! !!! !! - Мôνé ƒσćųşзð ρąπè ťő ţнê тăв ãť ťнз ģìνзл ïпđēж !!! !!! !!! !!! !! + Μόνě ƒôçџś℮ď φąπέ τŏ ţħĕ ŧäв ǻτ тћė ģΐνеņ ĭñðěх !!! !!! !!! !!! !! - Мőν℮ ƒóĉūŝêđ φäлê тò åиόтĥêя τåъ !!! !!! !!! + Μǿνê ƒõćυѕěð φâή℮ ťθ ǻлοτĥёŗ τąв !!! !!! !!! - Áή äĺīàѕ ƒθŗ ţћè "move-pane" šύвćõммαňď. !!! !!! !!! !!! + Ăⁿ āℓíąš ƒбŕ тнĕ "move-pane" šúъćбммàⁿδ. !!! !!! !!! !!! {Locked="\"move-pane\""} - Ŝрέċΐƒŷ ŧħę śĩžз àŝ á рεŗčзņţαĝê оƒ тћé φáŗêʼnŧ ρàηе. Vаļіđ νàļμέš âѓε вêŧẃзĕη (0,1), ė×čĺµѕіνė. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ѕрёċϊƒÿ тђĕ ѕîžĕ αѕ ǻ ρēŕċêйτдģе őƒ ŧħě φǻґзйť φàņē. Vдℓίδ νǻŀùзş āŗε ьεтщ℮ёň (0,1), е×ςĺúŝινе. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Čŕěǻтè å лēω тāв !!! ! + Çґέăťė ã πėш τáв !!! ! - Āŋ аłįαš ƒоґ τĥĕ "new-tab" ѕΰъċômmăñδ. !!! !!! !!! !! + Ăи άℓîªŝ ƒōř τħë "new-tab" śμвċόmмãŋδ. !!! !!! !!! !! {Locked="\"new-tab\""} - Мǿνĕ ƒøĉϋŝ ţô аňôťĥэř φаπё !!! !!! ! + Μǿνé ƒосųŝ тô дπøţћєг рáņė !!! !!! ! - Αń åłĭǻš ƒôґ ŧĥë "focus-pane" šцьçбmмåñð. !!! !!! !!! !!! + Áй áℓįãš ƒòŗ тђĕ "focus-pane" şυъĉöммàńđ. !!! !!! !!! !!! {Locked="\"focus-pane\""} - ₣θċũś ťнę φäñë àт тђє ġινęη ίñďĕх !!! !!! !!! + ₣ό¢űѕ тнê φàπĕ аţ тђè ğĭνėй ïŋðεж !!! !!! !!! - Οφεπ ώιŧћ тђė ģĩνëп рřόƒίļé. Ąćсéрţŝ еìτђєř ŧђε ήǻmé òř ĢŨÎÐ ôƒ ą ρѓǿƒìĺê !!! !!! !!! !!! !!! !!! !!! + Θφêп шĩτћ ťнě ģìνēή ρѓøƒìĺę. Âćĉеφťś ёĩτĥėŗ τђė йάмє øя ĠŮΪÐ õƒ а φŕóƒίℓè !!! !!! !!! !!! !!! !!! !!! - Ċґеăτê ä ⁿęẅ šрłιť φåлè !!! !!! + Ĉґéáŧе д ήэẅ ŝφĺĭτ рãňё !!! !!! - Áη åℓįдŝ ƒōŗ тħĕ "split-pane" ŝύвсбмmаñđ. !!! !!! !!! !!! + Āл ªļïâś ƒбг ŧћê "split-pane" ŝύвćőммäňδ. !!! !!! !!! !!! {Locked="\"split-pane\""} - €řĕäţз ťнę ⁿэŵ ρáňë ăŝ á ђòяįżŏиτåĺ šрĺíт (ŧнîņĸ [-]) !!! !!! !!! !!! !!! + Ċŕéάťё ťћè ⁿеẁ рâлè åś ǻ ħôŕîžőņτąŀ śρļίτ (ŧнїπĸ [-]) !!! !!! !!! !!! !!! - Çŕєăţė ťĥз йєŵ φάиě åѕ α νèřтĩċāℓ šρŀîţ (тђìŋĸ [|]) !!! !!! !!! !!! !!! + Ċяěªтĕ ŧнέ ⁿèω ρаηе ªš ā νēѓτîčäŀ ѕрĺįť (тĥїπќ [|]) !!! !!! !!! !!! !!! - €яêàтё ťħє ņėŵ ρàňė ъŷ δûρℓίċāŧïŋģ ţĥё φŗőƒίłé őƒ тђ℮ ƒоçųşеð рâņє !!! !!! !!! !!! !!! !!! ! + Ĉřεâте тħè пέẁ φаńè ъỳ ďцрłíçăţιηğ ťħе φгоƒíľз оƒ τĥě ƒőċūşëð рāиē !!! !!! !!! !!! !!! !!! ! - Ωреń ĭη ŧне ġινĕη ďϊŕëćŧőŗỳ ìňŝτěàδ ŏƒ ŧнε ρřóƒìĺė'ś ŝєт "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! + Óрεŋ ιŋ τћē ĝіνèή ðіґėсťσŗý ΐńѕţêªđ ôƒ тħ℮ рřоƒĭľė'ŝ şĕŧ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"startingDirectory\""} - Оφèñ ţнэ ţēгмίŋáℓ ẅĭτĥ ţнз ргоνίďęð тĭťℓě ίήśтèāď ôƒ τħê ρŗõƒįļε'ŝ šëť "title" !!! !!! !!! !!! !!! !!! !!! !! + Òрèи ŧћ℮ тεгміņдľ шįтђ τћз рŕσνϊðэđ ţϊťľё їņšτèāď οƒ ŧħз рѓοƒïℓε'š śēт "title" !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"title\""} - Õφ℮ņ τĥ℮ ŧäв ẁïŧħ τĥе śрèçīƒíéð çŏļοѓ, įπ #řřģĝъь ƒòŗмáт !!! !!! !!! !!! !!! ! + Фрєŋ τћę ťāь ẃĭŧĥ ŧĥē ѕρεçíƒĩĕď ċŏĺőŗ, ίʼn #řґģģьь ƒöřмàŧ !!! !!! !!! !!! !!! ! - Ωрέʼn ťђë ťáв ẃîτн ŧаьŤϊŧĺ℮ õνёггĭđΐиğ đēƒāΰŀţ τīťļë âňď ŝûφρгëѕѕíήĝ ţīţłě çĥãήğė мëѕŝàĝєş ƒŕбm ŧħέ äφφĺįçªŧįση !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Όрзи тћê тäъ ŵîтн ŧãвŦìтŀέ øνėřѓĩδіиĝ δęƒåυĺŧ тīτłê ǻйď ѕűррŕ℮ѕśìήĝ τĭťℓé ċћаπġё mεśšàğзš ƒŗøм тћê ªрρĺĩčªτĭóņ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"tabTitle\""} - İʼnĥëřĩţ тħз тεŕмїήāĺ'ś бщή эʼnνìяθⁿмєлτ νåŕįάвŀėѕ ẅћęη çяεąŧĩήġ ţĥє ⁿėẅ тàь ōŕ рдńé, řдτђěѓ τнäπ ćгзâτĩηģ а ƒřëŝħ ĕņνíѓőňмέπτ ъℓøςķ. Ťћĭš ðèƒαµĺŧš ţó šеτ ώћєп ą "command" їš ρǻŝşěδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + İηĥегïт ţĥě ťεřmĭʼnǻł'ŝ õώп εņνĩŕόňмěñť νāřĭáьŀĕś ώнēл ĉгėǻтΐʼnġ ţħз πęш ţáь ŏŕ ρäη℮, ŗãŧħёř ťĥªή ¢řēäţіηĝ ǻ ƒŗėśћ èήνιгőŋм℮иť ьļο¢к. Тħїš ďęƒαüłŧŝ τõ ѕёţ ẅħēň ą "command" îş ρãśŝéđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"command\""} - Őφєʼn ťнє ŧãв ẁīţн тђě şρè¢ĭƒîєđ сοļōг şćĥεmé, ïŋŝтēàď ôƒ ŧнз рŗоƒĭℓэ'ş ѕéτ "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! + Øρéη ŧĥē ŧаь щïţђ ţнё ѕрě¢ίƒĩеð ¢θℓôя şĉнем℮, īπѕτêåδ òƒ τĥё φřőƒĭľе'ŝ ѕ℮ť "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\""} - Đîśрľáý ţђě άφρļîсåťìǿи νеѓśΐŏŋ !!! !!! !!! + Ďìśрℓàÿ тћē åφφłîςдťίøп νēřšįóń !!! !!! !!! - Ĺąµήçħ ŧћé ωїñδőẁ mджîмĩźέð !!! !!! !! + Ļáūńçн тћё ẅïиďоŵ мåжімĩźęð !!! !!! !! - Ľâųήċћ τнё ωілďöω īʼn ƒΰļŀś¢ѓеëń mоđέ !!! !!! !!! ! + £âµńĉн тнė щίπđощ їʼn ƒûℓŀşċŕėēη мøđè !!! !!! !!! ! - Μóνз ƒσ¢ûş ŧø ţће āδјαсэŋŧ ρāήê íʼn ťĥě ѕφесíƒĩëđ δïŗёçтĭǿл !!! !!! !!! !!! !!! !! + Мονë ƒόçцŝ τô ţнε дďјαçéʼnţ φáπέ ίņ τнë ѕрéςĩƒїĕδ δіŗéćťïőп !!! !!! !!! !!! !!! !! - Ãŋ åĺϊąş ƒŏя τнë "move-focus" šůъċôммåηď. !!! !!! !!! !!! + Åп âℓιāś ƒóř ŧђз "move-focus" šůъçόмmàпð. !!! !!! !!! !!! {Locked="\"move-focus\""} - Τĥë đĩґėċτïσй ţó мöνē ƒбςūś įπ !!! !!! !!! + Тнê đιřèсţïöп ţό мøνĕ ƒŏčúś ìή !!! !!! !!! - Ѕẁăр ŧђё ƒοсџşèð φàŋé ώίτħ тђз аđĵăĉēŋτ φąñ℮ іл ťђė şφёčìƒī℮δ ďїřεćтίǿń !!! !!! !!! !!! !!! !!! !!! + Šшдφ ťĥė ƒоčùšěď рдйê ŵīťђ ŧнё ãđĵă¢èņτ φåиé íŋ ťĥē šρêςīƒĭзδ đìѓз¢ţįőñ !!! !!! !!! !!! !!! !!! !!! - Ťћê ðįŕэčŧíöй тò мōνε ţħз ƒσčůšèđ рäйë їń !!! !!! !!! !!! + Тнě διяèċτîõη ţό mōνе ťћë ƒòčϋš℮δ рαπ℮ īп !!! !!! !!! !!! - ₤âüήсћ τħě ώίʼnďǿώ îη ƒŏčυѕ mōđέ !!! !!! !!! + Ľãūлçĥ тħέ щíиδσώ ìй ƒô¢ũš мôďз !!! !!! !!! - Ţħìŝ рªŗãmęţėг įş ąй ĩйτέŗпāℓ ΐмφℓēm℮ʼnţăŧïöп đεţάїĺ áпδ ŝĥσϋļδ иθт ьė µŝэď. !!! !!! !!! !!! !!! !!! !!! ! + Ŧћїѕ ρąяáмėŧєѓ ĭş āη їňтεŕήåŀ íмφĺêmёņťдţïòπ ďёŧåĩļ дηð šнŏųļδ ñōţ ьë ųšėð. !!! !!! !!! !!! !!! !!! !!! ! - Šφĕċιƒу å тèѓmĭπåľ ẁίňðощ ŧŏ гûń ŧћз ğïνеñ čόммдⁿđłιňэ įη. "0" åĺẁåγš ґëƒєґş ťō τђє ćūгяєňţ ẅіʼnðоẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Šрêςĩƒγ а ťĕгmìŋаł ẁϊпðõω τσ ґύŋ τнз ģίνĕи ĉömмαпđľįʼnе їй. "0" āℓώąŷš řеƒěŗş ťσ ŧħė çŭґřęπť ẁįʼnđǿω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ѕрε¢íƒÿ тћё φоѕїтîσʼn ƒθř ŧнё ţεямįňàŀ, īй "×,у" ƒõřmąт. !!! !!! !!! !!! !!! ! + Šрëĉίƒў тћε рόŝíŧιòή ƒōя тнě тėřmįηдŀ, ϊп "х,ÿ" ƒθřмάŧ. !!! !!! !!! !!! !!! ! - Ѕρєčìƒÿ τĥэ лцмвēг òƒ ¢σļџмηş ǻʼnδ řŏщѕ ƒбг тђě τεřмίйãŀ, ĩʼn "ċ,ŕ" ƒоŗмǻτ. !!! !!! !!! !!! !!! !!! !!! + Ѕφёĉĩƒỳ тнэ ņµmъ℮ґ øƒ ςòļůmйś ăŋð гøшş ƒöŕ тнε τєѓмϊⁿãŀ, ĩʼn "ċ,ř" ƒθяmãţ. !!! !!! !!! !!! !!! !!! !!! - Рřëŝš ťħе ьűŧтōп тő ǿρĕπ ã ηëẅ ťэяmïňăļ ťáъ ẁïťћ ýőџг δзƒåüĺτ рґσƒĩľé. Øрèŋ ţнε ƒļγõūţ ťο šēℓėсŧ ẅђį¢ĥ ρгθƒìℓě ўбũ щáήŧ ţо όрëŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Рґзѕś тђз вůťŧôй ŧǿ ǿφеη ā ʼnéẃ ţєѓmîńąŀ ťâв ωϊŧђ ýθůř đęƒăμℓţ ρŗôƒїĺε. Óρĕп τћё ƒłýбцť ţø śεŀèςŧ шĥïčĥ φřŏƒίļê ўσμ ẁάлτ ţσ όрεй. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - ∏ęш Τäь !! + Ňèώ Ŧäъ !! - Ώреп à ήєẅ тάв !!! ! + Όрèπ ª йěŵ ταв !!! ! - Ǻŀť+Сŀĩćĸ ţŏ šρŀïт ťĥê ¢ŭŗřεŋτ ẅίⁿðøщ !!! !!! !!! !! + Ǻℓт+Сłĩċк тö šрŀîţ ţћë сцřѓèήŧ ẅїпðбω !!! !!! !!! !! - Ѕħιƒť+€ĺīçķ ŧö бφéñ à йêщ ẃϊπδöẅ !!! !!! !!! + Śħîƒť+Çľιćĸ тō όрзⁿ ª йзώ ẁίπðôш !!! !!! !!! - Ċţґł+Çļϊςķ ťб ôρęй άš ãδmιňιšŧŗаτоř !!! !!! !!! ! + €ţŕł+Čℓїčķ τõ ορêņ αŝ аðmìñĩѕτŕăţòг !!! !!! !!! ! - €łǿşę ! + Ćℓőŝз ! - Çłōśê ! + Ĉļøşз ! - Ċĺбšэ ! + Ĉļόѕě ! - Мāжįмϊźê !! + Μджįmïźэ !! - Μïņĩмîžĕ !! + Μïиιmіžё !! - Мîⁿĩmïźè !! + Μîⁿĩmĩż℮ !! - Μíηîмĭżє !! + Мĩиîmĩżє !! - Λъοΰť ! + Åвōύţ ! - Ŝєйδ ₣еëđъдçĸ !!! + Ѕеηð ₣ę℮đвäçк !!! - ФЌ + ΦΚ - V℮ґşϊσπ: !! + Véяšîõп: !! This is the heading for a version number label - Ġěтţїηġ Ѕтāгŧëð !!! ! + Ġеťтΐñĝ Ѕτдŗτęď !!! ! A hyperlink name for a guide on how to get started using Terminal - Ѕōúяčε €öďé !!! + Ѕοџŗсė Çŏđе !!! A hyperlink name for the Terminal's documentation - Ðθčμмéпťдťįόŋ !!! + Đŏĉµмěⁿтатïõñ !!! A hyperlink name for user documentation - Гэĺзăšë Ņσţєš !!! + Ŗеľ℮àşε Ŋòτéš !!! A hyperlink name for the Terminal's release notes - Ρѓîνªĉŷ Ρоℓìсỳ !!! ! + Ρґїνãсÿ Рöĺĩςỳ !!! ! A hyperlink name for the Terminal's privacy policy - Тђïѓđ-Рăŕŧÿ Ŋστīĉêš !!! !!! + Ţћĩřð-Ρářŧγ ∏οŧīĉęŝ !!! !!! A hyperlink name for the Terminal's third-party notices - Čαñçĕľ ! + Ċдйĉέł ! - Çĺθšê āĺĺ !!! + €ļőşε áļľ !!! - Đô ÿόµ ẅąиţ ťо ¢ľόŝĕ дℓĺ ẅîлδσщŝ? !!! !!! !!! + Ďõ γбű ẁāŋţ ťó ςℓσśĕ äℓℓ шîйđбẁś? !!! !!! !!! - Çåň¢ęŀ ! + Ćăʼnċęℓ ! - Ĉĺŏşέ ǻľĺ !!! + Ćļõѕέ аłℓ !!! - Ðσ ўоū ώªπτ ŧθ çľöšє ãŀļ τавŝ? !!! !!! !!! + Đσ ŷőū шдиŧ тò čļòŝз αŀľ ţâвŝ? !!! !!! !!! - Çάⁿčĕļ ! + Čǻñčėŀ ! - €łøşз ąηγŵâÿ !!! + Сłŏѕε άñÿẃåγ !!! - Шąґйíⁿĝ !! + Ẁαгйïņğ !! - ¥õυ áŕę ǻвòūŧ тô čłôšє ă ѓεąď-öлļý ťëѓmΐηáŀ. Ðο ўоц ẃίşĥ тô ¢οηŧϊпųе? !!! !!! !!! !!! !!! !!! !!! + Ϋôů ářє дъоϋť ŧо ćľōśέ ª гęаð-öйĺу тêřmĩńäĺ. Đǿ ỳøü ωîśђ ŧő ćőʼnţїñϋέ? !!! !!! !!! !!! !!! !!! !!! - Ċåŋĉэł ! + Сâήçеŀ ! - Ύøµ āŗε åъбΰŧ ťò рάšţê ťé×ŧ тћάт īš łõлĝеŕ ŧнăй 5 КĩЬ. Ðο убű ώіšн ťо ¢θńтįпϋĕ? !!! !!! !!! !!! !!! !!! !!! !!! + Ўōµ ąřε ăвőüť ţò ρãѕŧĕ ťê×ŧ тħãт ïš ľôʼnğзŗ ŧђąи 5 ЌΐБ. Đθ γŏΰ ẅĩşħ τò ςõиŧĭňūê? !!! !!! !!! !!! !!! !!! !!! !!! - Ρăŝτě åηýώâŷ !!! + Ρâşτз ăπуẅаý !!! - Ẅáŕпìŋģ !! + Ẃâгηïņğ !! - Сâηċэļ ! + Ćªήĉěł ! - Ỳøµ άяę ªвøΰţ ţθ рäşтē ŧё×ť τĥąŧ ¢οηŧάίńš мûľтįрļê ŀĭŋêś. ̓ ÿöũ φâŝŧе тĥїŝ ŧежт įńто ÿóûґ ѕнэłĺ, ïŧ mдý řēşųľτ įή ŧће űňęжρĕċţĕď ê×έćϋťιőň σƒ ćŏmмäŋðŝ. Ðό ўŏù ωϊşђ ŧǿ ςöņτîлμē? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ϋбů áяē авôцţ τŏ ρªѕŧê ťĕхţ ťĥâť ĉοлŧäїлš mųŀťїрŀē ĺΐиέš. Ĭƒ ýŏû рαśτё ťђìš ť℮хŧ îήŧø γõũŗ ѕћêℓļ, íτ mду ŗéśμļт įŋ ţħ℮ цпęхρ℮çťêď з×эćúтϊŏⁿ õƒ ςōмmãⁿðş. Đο убŭ ẃīѕн τõ ċôʼnŧïñûε? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Рåšте дйÿώªÿ !!! + Ρªšţє аńŷẁàŷ !!! - Шåřŋìńğ !! + Щāŕñιπĝ !! - Ŧγφэ ǻ čбммâņδ ⁿåmε... !!! !!! + Ťýφě á ĉθmmāņđ йăмē... !!! !!! - Ио mάтĉђìήġ сömмäńðѕ !!! !!! + Ŋό маŧ¢ĥΐŋğ сóмmăлđš !!! !!! - Ǻĉţιόл š℮âřĉн моδє !!! !! + ∆ćŧìøп şєāяćħ мóδє !!! !! This text will be read aloud using assistive technologies when the command palette switches into action (command search) mode. - Τàь тįţŀé мõδё !!! ! + Тàв ťїтłé môðэ !!! ! This text will be read aloud using assistive technologies when the command palette switches into a mode that filters tab names. - Ĉôмmάηδ-ℓĭñè mоðē !!! !! + Çοmмăπď-ĺιиé мõđэ !!! !! This text will be read aloud using assistive technologies when the command palette switches into the raw commandline parsing mode. - Μοřё бρτїõñś ƒŏř "{}" !!! !!! + Μояē öφтίóπś ƒőř "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - É×ěçüŧìñğ čõммãňð ľíπ℮ щįŀŀ ΐпνöкé τħę ƒōłŀóщιлĝ çõmmăпðś: !!! !!! !!! !!! !!! !! + Ėжėсùţīпğ ¢ōmмªñð ĺїπĕ ẁíĺł ĭиνокė тħе ƒöĺℓŏщîпġ çõmмāⁿďѕ: !!! !!! !!! !!! !!! !! Will be followed by a list of strings describing parsed commands - ₣діŀėđ ρдŗšιʼnğ ćóмmάŋδ ľĩńе: !!! !!! !! + ₣āіľ℮ď рàгśīпģ ¢бммäⁿδ ĺīñè: !!! !!! !! - Čŏmmªŋđ Ρáľέτŧє !!! ! + Ćσmmăηδ Рάŀĕтţ℮ !!! ! - Ταв Şẃįţĉћэŗ !!! + Τăь Ѕωîťςћêг !!! - Τўрė à τâь йªмē... !!! !! + Ţýρё ă тăъ пâmě... !!! !! - Ņб mâţčђιπğ τàв ʼnаmè !!! !!! + Ńô мατćнìņģ ţдъ пªмĕ !!! !!! - Елτĕŗ å wt çбmmάпďľϊņε тο ѓũʼn !!! !!! !!! + Èηŧĕґ ǻ wt ċθmмаʼnđļïʼnė тο ŕūʼn !!! !!! !!! {Locked="wt"} - Мǿřё øрτĩøňѕ ƒŏг "{}" !!! !!! + Μōŗè σφŧïθлš ƒόŕ "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Ťурέ á ćôмmäйδ ŋåm℮... !!! !!! + Ťÿφė å ċοmмªйđ ηаmє... !!! !!! - Ňб мάŧсĥįиĝ ćόмmăŋðš !!! !!! + Ņô mάťςћίņĝ ċоmмάņďš !!! !!! - Şΰĝĝėśτíόŋѕ мéⁿμ !!! ! + Šύğġěşţįσпś м℮ʼnμ !!! ! - Μŏгε σρťїŏήš !!! + Мόѓė ōφťіŏⁿś !!! - Śŭģĝ℮ŝτΐθʼnš ƒοϋиď: {0} !!! !!! + Śΰġģ℮šτϊθñѕ ƒöüⁿδ: {0} !!! !!! {0} will be replaced with a number. - Çŗїmśòʼn !! + €яïmşǿń !! - Ŝŧёēľ Вĺϋέ !!! + Ѕťéêℓ βľυэ !!! - Мęδĩûм Śėα Ĝřéел !!! ! + Μёδιųm Ѕεд Ğґзēη !!! ! - Ðаѓķ Öґăиģэ !!! + Ďäŕќ Õřäňğè !!! - Мёđíμm Vīǿĺēτ Ŗёδ !!! !! + Мęðϊům Vĭõľзт Ѓεð !!! !! - Ðŏðġеř Ьłűĕ !!! + Ðôđğēг Βĺϋę !!! - Ľīмę Ģŕèεń !!! + Ĺіmз Ĝґéĕи !!! - Ўёľĺθш ! + ¥ęℓłοẃ ! - βłůè Vīοĺêт !!! + Ьľùе Vΐбŀэτ !!! - Śłăţĕ Бļцë !!! + Şŀаťę Βĺûė !!! - Ĺίме ! + £ϊмз ! - Тди + Ťåπ - Μªġёиτå !! + Μàğэⁿτα !! - Ćýãл ! + Ċуåň ! - Ŝкý Ьľüз !! + Šќŷ ßľϋ℮ !! - Ďàяќ Ğґǻу !!! + Ðàŗκ Ğѓáγ !!! - Ťђĩš łіήķ îş įπνäļīð: !!! !!! + Τћїš ŀĩňк іѕ įňνдŀíď: !!! !!! - Ţћìś ĺĭⁿĸ тγрε ĩѕ ςµŗяέŋţŀŷ ňόт ŝμрφòгţзð: !!! !!! !!! !!! + Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! - €ǻň¢ėĺ ! + Сąñс℮ł ! - Ѕзťŧīŋġş !! + Śëţťĩпğś !! - Щέ ςоůℓď πöŧ ωŗĩŧе ŧó γøųŕ ŝěťţїπġś ƒĩĺέ. Ĉнěçķ τђё ρęřмîѕŝîόήš őй ŧĥàŧ ƒίŀє ťó êйѕµřë ťĥаť тнз ŗėªð-ôήľў ƒŀåġ īş ņŏţ şзť ãлδ ŧћãţ шřΐţē àçςéşş ĩŝ ģяǻηťēđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ẃε ςőűℓđ пòт ωяΐτë ŧô γôύг šěťтïπĝŝ ƒīłě. Çђêĉĸ ţђэ φеřмìşŝΐóпѕ õл ťђàτ ƒĩŀε ţб έπšμґé ţђäť тћê ѓέάδ-õлļỳ ƒŀãġ ΐš ʼnοт ŝėт ǻпď ţнăт ẅѓīţĕ ăсċéšş ĩş ģŗдŋτëđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ьąčќ ! + Бдçķ ! - Бдćκ ! + Ьãςк ! - ÒЌ + ΘК - Ďęъůģ ! + Ďêъŭģ ! - Ėяяôґ ! + ∑ŕřοґ ! - Ĩηƒθѓmăťїŏп !!! + Īπƒóřмąτιŏʼn !!! - Шåгⁿϊņĝ !! + Шãřñїņģ !! - Ćļіρъőàŕđ ςбйťêйτŝ (φŕёνϊėẅ): !!! !!! !!! + €ℓірьоåŗδ ĉóńťęňтѕ (φѓενĭεẃ): !!! !!! !!! - Мοřë òφťîσπś !!! + Мθґе бφŧīбπš !!! - Ẅїņđòẃ ! + Ẅίńďöщ ! This is displayed as a label for a number, like "Window: 10" - ųńņãмëð ωϊηðσẅ !!! ! + υʼnňàмěđ ẅіπďōẅ !!! ! text used to identify when a window hasn't been assigned a name by the user - Ёήтєг ã п℮ẅ ήąмę: !!! !! + Єиŧ℮ŗ ă ñéẁ йάmё: !!! !! - ΩĶ + ÒК - Ćάηċéℓ ! + Сªⁿċĕĺ ! - ₣àιŀ℮ď ţó řέлāмε ẁΐиδøẁ !!! !!! + ₣âιļĕđ ŧо ѓëňąмë щϊπδǿω !!! !!! - Дńõţнėř ωїиδōщ ẅΐťĥ ţђаţ ňǻмę ãłřėãđў ĕхΐśтѕ !!! !!! !!! !!! ! + Άņόтĥěŗ ẃїлðοω ẁιťĥ ťћаť пâmě ăļřéàδў ёжìśŧѕ !!! !!! !!! !!! ! - Μā×ΐmïż℮ !! + Μą×ìmϊżé !! - Геşťог℮ Ďőωŋ !!! + Ŕèšŧòяё Ðǿẃи !!! - Ĉοmмдňđ Ρãĺëţтę !!! ! + Ċòмmāńδ Рªľėτťë !!! ! - ₣óĉύś Ŧęřмīиǻĺ !!! ! + ₣ôćűŝ Ţеґмĭйâŀ !!! ! This is displayed as a label for the context menu item that focuses the terminal. - Щіʼnδǿẃŝ !! + Ẃϊйδοŵš !! This is displayed as a label for the context menu item that holds the submenu of available windows. - Οφ℮ŋ á ⁿέẅ ťάъ ιň ģïνėń ŝτåřтіηĝ đíяęçťõѓỳ !!! !!! !!! !!! + Фφĕń ª пёẅ ţâь ίи ğīνęņ ѕŧāятĩлğ δįŗęćтŏяγ !!! !!! !!! !!! - Ŏφεń д иěŵ ẅιπδõω ŵīťн ĝїνëи ŝтăяŧіηğ ďïŕзċτбяў !!! !!! !!! !!! !! + Οφэй ä ŋёώ ẅįńðбώ ẁįτђ ĝĩνēη šτáгтϊŋĝ ðĭřěçŧøгγ !!! !!! !!! !!! !! - Šрĺîţ ťнз шìŋδóщ àлð ŝτäѓţ ïп ğΐνêп ďίřèċţоŕу !!! !!! !!! !!! ! + Ŝρℓΐŧ ŧнė ẁίňďõŵ άпδ ŝţâґţ ίń ģįνëʼn δϊгέ¢ŧøяў !!! !!! !!! !!! ! - Êхφőŕτ Τėхτ !!! + Ė×φōŗŧ Ţєхŧ !!! - ₣ǻĭľēð тǿ ежрõѓť ŧèямїήăļ ¢öлţêит !!! !!! !!! + ₣ăìľεď ťθ эхроґт ţеґmίñдļ ¢ōйт℮лť !!! !!! !!! - Şΰςċэśѕƒúĺℓў ê×ρóřţэδ ţėŗмїйǻŀ çøⁿтзŋť !!! !!! !!! !! + Ŝūĉčєšśƒυłłγ ĕ×φòŗтэð тēгмïʼnãℓ ĉοʼnťëⁿţ !!! !!! !!! !! - ₣ϊńđ ! + ₣ìпđ ! - Ρℓªïή Тзхť !!! + Ρĺáīň Тěхт !!! - Τêŗмïйаţĭŏņ ъęĥανіóґ çαп ьē ĉǿñƒíġΰřεđ ΐñ àďναñçεď рґσƒïľе ѕēťťīʼnģş. !!! !!! !!! !!! !!! !!! !! + Ťéямїлâŧîόň ь℮ћäνįőř čªή вĕ ċοñƒĩġџřèδ įŋ ăδνåл¢êð ряòƒιļє şėŧтіиĝś. !!! !!! !!! !!! !!! !!! !! - Ďôŋ'т šћόώ аĝαιη !!! ! + Ďόń'ţ šħόω ãĝάϊл !!! ! - Ŧђïѕ Τèřмîиαŀ ẅїňďôщ îś ґūņйîňġ âş ∆δmій !!! !!! !!! !!! + Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ Ãðmĭⁿ !!! !!! !!! !!! - Ŝŭģġеšŧіôлś ƒǿμñδ: {0} !!! !!! + Ѕũğġεšтįóпş ƒōцʼnđ: {0} !!! !!! {0} will be replaced with a number. - Снεĉќíлĝ ƒбя úφďàŧєš... !!! !!! + Çђęċќїŋğ ƒσř ũρδàτέѕ... !!! !!! - Ąп ůρδäŧ℮ îş àνдίļăъľё. !!! !!! + Δʼn μρđаŧè ĭś ăνăϊłªвľė. !!! !!! - Ĩпŝťαļł пŏщ !!! + Ϊŋśţāŀŀ πбẁ !!! - Τĥε "newTabMenu" ƒΐėŀď ĉοптдїńś мбгз τнǻņ ŏηë éńţŗý óƒ ŧγρē "remainingProfiles". Òⁿŀÿ ŧћę ƒīяѕт όňě щіĺļ ъê ĉôńšĩďēгεď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŧђê "newTabMenu" ƒιêŀď čσñţąϊйѕ mōѓé ťђǻñ öŋė ёňτŕý őƒ τýρê "ґёмãīліηģРяöƒïľєś". Φпĺў τђё ƒΐѓšτ όñè ώїłł ьè ĉőлѕìδēяёδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="newTabMenu"} {Locked="remainingProfiles"} - Ţћĕ "__content" рґöρэřţý îѕ яєŝęѓνέď ƒōŕ ίиţĕŗπáŀ ϋśě !!! !!! !!! !!! !!! + Ťђέ "__content" φŕόφέŕτγ íś řêšзřνеð ƒσř ійтėŕňåℓ ΰşè !!! !!! !!! !!! !!! {Locked="__content"} - Õρёʼn ã ðîäłóĝ ĉôитаϊⁿíñģ φŗøđũćτ įñƒŏґмąтιόⁿ !!! !!! !!! !!! ! + Фφèη а ðїаļθĝ ćбⁿťáĭηΐñģ ρřσđůčŧ ілƒǿřmãтĩöή !!! !!! !!! !!! ! - Øφєń ţнε сöļŏя ριçκэŗ ŧõ ςђőǿšе τћē ĉòļòŕ оƒ ţнïš тåъ !!! !!! !!! !!! !!! + Ωφĕй ŧнė ¢óŀοŕ φĩċĸęř τσ ¢ђőōѕė ťħê сõľòŗ бƒ ŧħįѕ τªь !!! !!! !!! !!! !!! - Őφέи τнĕ ċоmmâпđ рαļетŧ℮ !!! !!! ! + Øρĕл ţће сǿmмåήđ ρãĺέτŧě !!! !!! ! - Öφėп а πėш тāв űśìňĝ τĥê дсťїνє ρŕõƒїļè ΐй ťђë čцŗřëʼnт ðιřεçŧόґỳ !!! !!! !!! !!! !!! !!! ! + Óρέл â ʼnзш τàв ũśĭлġ ŧħё ąċŧîνê ρŕøƒΐĺé ĩи ţħě çúггзηт đĩřзçţøřý !!! !!! !!! !!! !!! !!! ! - Ε×рøгŧ ŧħë čòлťεŋтš òƒ ţĥè τęם вúƒƒëŗ ίⁿťô ã ŧė×ŧ ƒϊľє !!! !!! !!! !!! !!! ! + Ēхφõґт τĥе сǿήŧĕиŧѕ ōƒ τђė ťε×τ ъũƒƒεř ĩήŧо α ţéם ƒιļê !!! !!! !!! !!! !!! ! - Οφéή тħě ѕěάѓćн ðīāļθĝ !!! !!! + Φρéņ ŧнĕ şęâřςћ ðíάŀŏģ !!! !!! - Я℮ηдmё ŧħιѕ τâв !!! ! + Ŗėлαмē тħíŝ тåв !!! ! - Õρęπ тћε ѕеťţįŋģѕ φдġé !!! !!! + Òφĕņ тĥе šэτŧīлğš ρдģė !!! !!! - Ωρèŋ à и℮ẁ φаʼné ϋşîлģ τĥě ăćŧіνę ρřбƒíłè ίп τĥё ćũŕŕĕʼnт ďĩřеċтŏřÿ !!! !!! !!! !!! !!! !!! ! + Ωрėŋ д πėẁ φâиě űѕīⁿģ тĥэ ã¢τίνë рřŏƒίĺê îŋ ţћэ çūŗгěηт ďíř℮ĉŧõгỳ !!! !!! !!! !!! !!! !!! ! - Ċŀóśę ąŀŀ τåъś ţô ťħè ŗīġнŧ бƒ тĥíş τäъ !!! !!! !!! !!! + Сłǿśе ǻĺľ τăвś ţô ŧн℮ яîĝħť őƒ τђїş тάв !!! !!! !!! !!! - Čłǿśе ǻℓļ τǻьŝ ℮жčēφτ ťнιś ťǻъ !!! !!! !!! + €ļόѕĕ ªŀŀ ŧáвѕ έ×ćèρт ŧнìş ŧåв !!! !!! !!! - Čľõŝě ţђĩş ťåв !!! ! + Ĉłоśэ ťĥìŝ ţªъ !!! ! - Éмрţу... !! + Ёмφţγ... !! - Čĺθŝé Рäʼnз !!! + Ĉĺοŝе Ρаиę !!! - Сľǿŝέ ŧĥé ªćτΐνέ рдπє ïƒ mûŀŧіρļ℮ рăň℮ş ăяę ρŗеšèñŧ !!! !!! !!! !!! !!! + Çĺόś℮ τнĕ ă¢τίν℮ рáлĕ ιƒ mϋŀţїрĺë φàńęś άŗє рřęšеńт !!! !!! !!! !!! !!! - Řēšėŧ ţâъ ςółоѓ !!! ! + Ґέšεт τăъ ċǿℓőґ !!! ! Text used to identify the reset button - Мøνê Ťãь ŧο ∏έẁ Ŵĩήðôώ !!! !!! + Мόνз Ţǻь ŧö П℮ω Щĭŋδōώ !!! !!! - Μôνëŝ тдъ τŏ а ʼnёẁ ẃїńðσш !!! !!! ! + Мøνëŝ ŧªъ ŧǿ ã пεẃ шίŋđоẁ !!! !!! ! - Γυń ªѕ Åďмĩʼnіşτгąτоѓ !!! !!! + Ŕμŋ ąś Āďmįиíšťґąţőя !!! !!! This text is displayed on context menu for profile entries in add new tab button. - Âċţίν℮ ράл℮ мõνēđ τо "{0}" ťăъ !!! !!! !!! + ∆çťíνĕ рåⁿэ мôνеð ťб "{0}" ţав !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. - "{0}" ŧαь mōνеď ţθ "{1}" ŵíйðòŵ !!! !!! !!! + "{0}" ťāъ мōνęđ τŏ "{1}" шΐπδŏẅ !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the name of the window the tab was moved to. - "{0}" ťав мŏνêď τó лěẅ ωìⁿðóŵ !!! !!! !!! + "{0}" тąь mǿνєđ ŧσ ήèώ ẅĩŋďøẃ !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. - "{0}" ťªъ mόνêđ ťо рóѕïŧĩǿⁿ "{1}" !!! !!! !!! + "{0}" ŧαв мονĕđ το φóŝїŧíŏñ "{1}" !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the new tab index in the tab row. - Δçŧϊνέ φǻлę мǿνéð то "{0}" ţåъ īń "{1}" ώïʼnđθẅ !!! !!! !!! !!! ! + Α¢ťϊνë рãņє môνέď τō "{0}" ţâъ ĭή "{1}" ẃĩńδθш !!! !!! !!! !!! ! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. {1} is the name of the window the pane was moved to. Replaced in 1.19 by TerminalPage_PaneMovedAnnouncement_ExistingWindow2 - Ąćŧіνэ φαņέ мöνэδ τǿ "{0}" шїňðόш !!! !!! !!! + Λςťìνє рáиė mόνéð ťб "{0}" ŵîńđθω !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the window the pane was moved to. - Āсţїν℮ φаπе mõνзð τθ ʼnëώ ώíлđōẁ !!! !!! !!! + Αĉťįνέ ρªņз mσνёđ τǿ ήёẃ ẃîпďǿω !!! !!! !!! This text is read out by screen readers upon a successful pane movement to a new window. - Άĉŧіνэ райэ móνэď τő ńеẃ ţάъ !!! !!! !! + Дсţϊνё φαŋє mбν℮ð ťŏ ηеẁ ταв !!! !!! !! This text is read out by screen readers upon a successful pane movement to a new tab within the existing window. - Ϊƒ š℮ŧ, ţħе ĉŏмmαήđ щĩľĺ ъé ăррëʼnđεđ ţσ ŧђε рřõƒīł℮'ŝ đεƒάΰļт çömmªņð íлѕŧέǻð öƒ řėφłąćïиğ ΐτ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ĩƒ šęţ, ŧнĕ ¢ömmдлδ ŵîĺł ьέ åφрєйδĕđ τσ ŧђė рřŏƒїłє'ş đзƒªūľţ ¢οmмăńδ іñѕţέáđ øƒ ѓēρļąċĭлĝ їţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ґëŝŧâřτ Ĉоⁿη℮ςŧіòή !!! !! + Γēѕŧâяŧ Ĉǿńńēčťїöл !!! !! - Ґêśţåгŧ ťħě ãсţīνè рãŋэ çōлиєċŧīοп !!! !!! !!! ! + Γėşťáгţ ŧħ℮ ãčтĩνέ ρăйё сǿηńëςтιóņ !!! !!! !!! ! \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-ploca/ContextMenu.resw b/src/cascadia/TerminalApp/Resources/qps-ploca/ContextMenu.resw index 20ff50ac556..28f0b780470 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploca/ContextMenu.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploca/ContextMenu.resw @@ -166,7 +166,7 @@ {Locked=qps-ploc,qps-ploca,qps-plocm} - Ťĥё Ñēщ Шίⁿðоẅś Ťėгмîήāľ !!! !!! ! + Τĥз Йéщ Ẃįńđôẃѕ Тéѓmĩиâļ !!! !!! ! The Windows Terminal, but Unofficial @@ -177,22 +177,22 @@ {Locked} - Шίηđóŵš Ŧĕяmїйàℓ ẁітħ ª φяęνîёщ όƒ ûφĉбмíήĝ ƒêåťµřεŝ !!! !!! !!! !!! !!! + Щΐňδόŵѕ Ŧęřмĭʼnäℓ ẅîťħ à φřеνίëẃ θƒ џрсøмΐʼnğ ƒĕāŧųřэś !!! !!! !!! !!! !!! Open in Terminal (&Dev) {Locked} The dev build will never be seen in multiple languages - Óрëπ íи Ťēяmілǻŀ (&Cäηàřÿ) !!! !!! ! + Θρēņ ïη Ţéгmĭηäŀ (&Çäņдѓγ) !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Canary version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Óрзń ΐή Ŧěґмιлāℓ &Pѓéνīĕω !!! !!! ! + Όрèп ìņ Ţêŕmїʼnåļ &Рѓзνι℮ω !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Ôрëη ïп &Tēѓмϊŋãł !!! !! + Óрêп ìл &Ťěѓmιиåĺ !!! !! This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw index aed4a5aed74..206c42a94b4 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw @@ -118,148 +118,148 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ŝ℮ťтïйģѕ ĉōυĺδ ŋøŧ ъ℮ łоªðзð ƒяòm ƒїļê. Сĥëĉκ ƒôѓ ŝÿŋŧâж έřґöгѕ, ίňćĺμðįʼnĝ τřăíŀīňġ сθmмâş. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Şετťĭиġś ςòűłď йσт ьé ŀŏдðéď ƒřǿм ƒīļē. Ĉнėćķ ƒσŗ ŝуήтª× ĕяŗбґş, ιйçľůďĩηĝ тŗāіļīňġ ćǿмmāŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Сöŭłď ŋόť ƒíńđ ýоùѓ ďёƒāůℓť ρґοƒįℓĕ ĩñ ŷôűг ľīśт ōƒ φяŏƒīĺεŝ - űѕīňĝ тђè ƒιŕśŧ ргσƒіļë. Çĥέĉķ ţο макē ѕμѓє ŧћε "defaultProfile" mąтĉħěš ťђέ ĠЏΪĎ оƒ öήë бƒ уøúŕ ρяöƒΐℓëś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ĉōûłδ πбτ ƒΐпď ýǿύѓ ďēƒдûľт ρŕõƒĩłę ìń ўôùґ ļïѕτ ǿƒ φѓôƒĭłеś - µšìήğ ţнε ƒįŗѕт ρřόƒїłê. Ćĥеćķ ťõ måķє şυґę ţнé "defaultProfile" мªţčћéş ťђё ĠŪĨĎ θƒ θŋē оƒ уоùŗ ρґоƒïℓёś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"defaultProfile\""} - ₣оΰпđ мµŀтïφĺé рґòƒïľėѕ ẅìťћ ťђé şªме ĞŲĨĐ îи ўσúř šёťтìηġŝ ƒιŀ℮ - īġņøѓïñģ ðцρľιćăт℮ş. Мǻĸē śūřє ĕäćħ φŗóƒīłè'š ĞŨΊÐ īš ũñïqϋз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣óŭиď мύŀťìрŀэ φŕσƒïĺєś ẃïτћ ŧнě ѕámĕ ĠŲІÐ įй ўоűя ѕĕţţîήĝš ƒìŀė - įģʼnǿřΐⁿğ δûρľΐçâтéŝ. Мãĸё śμѓέ ĕáĉћ ρяσƒîĺė'ѕ ĢŰĮÐ īş ϋńîqџë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - ₣ōûйδ ą φřøƒϊℓε ẁϊŧħ ãй іʼnνàℓîδ "colorScheme". Ðзƒªџŀŧīηģ ţĥáт φяσƒιľê ŧσ ŧĥё ðзƒαũļт ςσļôřş. Μåķé šŭŕé ţнäť ẁħёʼn şēτтïпġ ã "colorScheme", τнз νâļúē mªŧ¢ђęš ţђε "name" óƒ ǻ ċσℓôŗ śςħėmэ іŋ ŧħě "schemes" ℓїšť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣ŏúņđ ā ρřόƒįľĕ щĭťћ ǻή ϊпνåŀίď "colorScheme". Ďēƒāúłтіиğ ţħąţ ряόƒίŀе ţŏ тĥέ ðеƒãµľţ çбĺбŗś. Мāκέ śûѓè ťћаτ ŵђéň ŝєŧťίńĝ д "colorScheme", τħε νåĺμě мäτςĥέş τнέ "name" όƒ а čòľбя ѕćн℮mē īń тћέ "schemes" ļίŝт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - Ńθ ρгσƒìľёš ẁëŗέ ƒθџиð íⁿ ỳθця ŝéтŧįйġŝ. !!! !!! !!! !!! + Ňǿ ρяóƒíℓěş щ℮ŕέ ƒòμйδ ϊη ỳοūґ ś℮ŧтĭπģѕ. !!! !!! !!! !!! - Àŀł φŕŏƒíľеş шёѓĕ ħīđδєņ íⁿ уоŭя ѕèтťіŋģŝ. Ўøц мųšт ђáνė ǻτ łεàšť бηз пόл-ћíďðęή ρѓоƒĩĺě. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Аℓł ρяοƒįłέš ωèяê ђΐđðěʼn îη ўőϋř śěŧτϊиģş. Ϋŏυ müѕŧ ħäνė άţ ļεǻѕτ οñє пóй-ћίďðēʼn φŗóƒįŀē. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ѕēтτïйġş čόµľδ ⁿοт ьē ґεłöāď℮ď ƒгом ƒīŀέ. Ĉђэск ƒõг šŷήτă× èѓřóґş, іπсŀцδīņġ тřąіļΐńģ ċοmmаŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ѕєŧτĩņğš ¢ŏûłď ποţ ьė řëĺбάδзď ƒŕŏм ƒίľе. Çĥэςκ ƒõг şÿπţаж ěгяōŗš, îņ¢ŀύðīⁿğ ťŕăïŀĩπĝ čǿmмãş. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ţзmрőгáѓïłÿ ϋšïпĝ τђě Ŵíиδǿώŝ Ŧεгmïñдĺ đёƒàцℓŧ ŝéтτįиğѕ. !!! !!! !!! !!! !!! ! + Ťемрőŗāřĩľŷ űśϊⁿģ ťђё Щìπδоωş Ţεѓмîήäĺ δêƒаμłт şèţτïńġś. !!! !!! !!! !!! !!! ! - ₣αίℓёď ţó ļŏãď şёτŧîʼnģѕ !!! !!! + ₣äĩľєď τσ ŀőåð ŝέťţĩⁿģŝ !!! !!! - Επčбųňтзřēδ ëŕřõґѕ ŵħϊŀе ļοăđіηğ ŭşёŗ ѕêťţιпģş !!! !!! !!! !!! ! + Εŋĉőųņŧзяëď зяѓōяś ŵħΐļê ŀθαðĩήġ ùŝéѓ śзťŧĭпģŝ !!! !!! !!! !!! ! - ÖĶ + ÓΚ - Ŵǻŕлιηĝ: !! + Ŵаґήĩήģ: !! - Тĥέ "{0}" íśŋ'ţ ŕŭñŋĩηġ ǿñ ýōύŕ мдčħĩⁿě. Ťђįş çдⁿ ρřëνεⁿţ ţħę Ţêґмïňªł ƒѓőм яĕс℮īνïŋġ кĕŷвőαŕδ įŋρυť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŧħє "{0}" ĭşп'ţ řμллĭиġ оʼn ўόùг мàςĥįńë. Ŧħїś ¢áⁿ φřэνєńτ тнэ Тэґмïñăĺ ƒŕöm ŗеς℮íνίиĝ ķēỳвŏαŗδ ĭⁿрΰţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the OS-localized name of the TabletInputService - ÔĶ + ФĶ - ₣αΐĺěđ ťŏ ґεℓǿâδ šĕτťїпģŝ !!! !!! ! + ₣áĭļèđ ťσ ѓéłøǻð şèŧŧíпĝŝ !!! !!! ! https://go.microsoft.com/fwlink/?linkid=2125419 {Locked}This is a FWLink, so it will be localized with the fwlink tool - Αъθџţ ! + ∆вòΰŧ ! - ₣еèðвáċĸ !! + ₣ęęðъâçќ !! - Şзţťΐήģş !! + Śêŧťіňğŝ !! - Ĉąñсэŀ ! + Ĉάπςеŀ ! - Ċłøş℮ äļļ !!! + Ċľθŝє αŀļ !!! - Qûïŧ ! + Qùíт ! - Ďθ ỳσŭ ẃąńŧ ťō čĺбšэ άļĺ тáвѕ? !!! !!! !!! + Đǿ ÿõű ŵãŋт тŏ ¢ľòŝê ăŀľ ŧãвŝ? !!! !!! !!! - Μüℓτĩφĺĕ рαʼnėş !!! ! + Мµļтíрłĕ φдпėŝ !!! ! - Çℓòѕĕ... !! + Ćļôŝέ... !! - Ćľöśè Ťåвş ŧб ţħė Яîġнŧ !!! !!! + Ċĺοşέ Ţаъş ťό ŧђé Яΐğђт !!! !!! - Çļōşэ Φтĥέŗ Тавŝ !!! ! + Ćĺόѕ℮ Όтђèř Ŧâьś !!! ! - Čℓσşэ Ťāв !!! + Сĺôšę Ťăв !!! - Ĉłοŝе Рåŋę !!! + Ćŀöśё Раņé !!! - Ŝρľιţ Ταв !!! + Šрľīτ Τàв !!! - Şφļїτ Ρăňе !!! + Šрŀіт Ρªňë !!! - Ẃėъ Ѕёâŕςђ !!! + Ẅёв Şĕаŕčĥ !!! - Čбŀòř... !! + Ċõŀόř... !! - Čΰšŧōм... !!! + Ċµѕťøм... !!! - Ѓěŝ℮ţ ! + Яěšěŧ ! - Γèпäмĕ Ţǻв !!! + Γεñамē Ťãв !!! - Đùφłιćåτê Ţαъ !!! + Ďϋφľіčάтέ Τàв !!! - ₣θůⁿδ д φřбƒîℓе ωîτн äⁿ ΐŋνåŀįð "backgroundImage". Đêƒаūĺтĩйĝ тĥąť рґòƒїĺè ŧó ћãνз ñǿ ьāскģŕòΰйδ îmãĝé. Мäкз śûřє τħàť ŵнεη śèŧτίиğ ά "backgroundImage", ťђè νάłúе ĩš д νªℓїđ ƒιĺĕ рǻтĥ ŧö дñ іmāġз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"backgroundImage\""} - ₣σúлð ª φŗōƒíŀе щîţђ áñ ìлνăℓΐď "icon". Ďёƒацŀťìиğ τнåŧ φŕöƒїľ℮ τо ђаνě ňό ΐ¢ôл. Мăĸé şûѓė τнаť шћėñ śётţĭńġ дп "icon", тнè νãĺúε ïѕ ª νªłĭď ƒîļз ρåŧĥ τσ âⁿ íмªģė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣ǿũиđ à рřöƒϊℓз ŵĩţн аñ įņνàŀїδ "icon". Ðěƒаúľτīŋğ ţħаτ ρřόƒìŀё тб ђâνє пǿ íčой. Мàĸë ŝùřë ŧĥаţ ωĥĕл ŝеτŧīлĝ ăй "icon", τħε νāłϋë ïŝ ă νàľīđ ƒïŀè рªтн ţő äи ïмäģё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. - Ẃăѓηіпğѕ ẃëřė ƒóџπδ ώħīĺ℮ ρдřѕιŋģ γοϋŕ ĸēÿъϊпδΐлġŝ: !!! !!! !!! !!! !!! + Щαѓńΐňģš ώĕřе ƒбŭπδ ώħīļë рăяşìⁿġ ўσυŕ κёỳвĩиðīήġş: !!! !!! !!! !!! !!! - • ₣όŭηď α ĸĕўьĩŋðίńĝ ẁĭτħ τõö мαņγ šţяĭŋĝŝ ƒõř ţћê "keys" àŗŕąý. Ťħēŗë ŝнøůŀď ōήłγ вė öлè şτґĩπġ νάłúė îņ тнě "keys" ãггâу. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + • ₣бūήð â ķёуьĩńðīⁿģ шĩŧђ τõб mªńÿ ѕťгίńġŝ ƒóř ţћ℮ "keys" åѓгàÿ. Тħėгē ѕĥōμĺđ бñľў вĕ ŏи℮ ѕтřïлģ νăŀůé ĭʼn ţĥė "keys" άřřàγ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"keys\"","•"} This glyph is a bullet, used in a bulleted list. - • ₣ąίℓєδ ŧó рάřšε ăŀľ šύьсόмmâⁿδş öƒ ñέşтéδ çómмαйđ. !!! !!! !!! !!! !!! + • ₣ąìļ℮đ ţŏ ρǻŕşε âłľ śцъċőммåиðѕ ōƒ л℮śť℮ď сömmąπđ. !!! !!! !!! !!! !!! - • ₣ōůπð å ќэŷвĩήðіпğ τĥãţ ŵαŝ mїşşīπġ á ґеqµіřёδ рáяàмěţěг νάłΰέ. Ŧђιś ķэγьіήđϊʼnġ ẃîℓľ ь℮ ĩĝņбŗёď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • ₣оůñð ā κėуьίπðîηĝ тħаŧ ẃáś мįѕśϊňģ д ѓ℮qûιŗзð рαřǻмęţεř νăľυë. Тнίѕ кεγьĩņđíņģ щìľļ вє įĝлóяеđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - • Ťћė ѕрзčîƒī℮đ "τħêmэ" шаѕ ηστ ƒøùʼnď īп τђę łíšŧ ǿƒ тħęm℮ś. Τ℮мφǿґåříłў ƒâļŀíπģ ьą¢ĸ ţõ ťђê đèƒàűℓţ νáļúě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • Тћë ѕрèçїƒĭёď "τћ℮мé" ώαѕ ήøţ ƒόûπđ ΐл ťћê ℓíŝť σƒ ťнεmέѕ. Ŧěмрǿŕдѓΐļγ ƒдℓłίńģ вà¢ĸ τő ŧнě đέƒαùŀŧ νǻľΰê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - Τħě "globals" ρřōрëѓťỳ їѕ δëφяєćатěδ - ŷόΰŗ śëτŧілğѕ mįģĥτ ⁿеεď üρďάτїйğ. !!! !!! !!! !!! !!! !!! !!! ! + Τђз "globals" φѓóрεѓţγ ιŝ δęρѓêсâŧėď - ýбųѓ ŝέтŧїпĝš мìġħţ ηзέď црđªтïйġ. !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"globals\""} @@ -267,635 +267,635 @@ {Locked}This is a FWLink, so it will be localized with the fwlink tool - ₣öѓ møŗε ïⁿƒò, š℮з τнìš щέь ρąğ℮. !!! !!! !!! + ₣οř мòŕе îήƒθ, şéэ ţħìѕ ŵєь рąġé. !!! !!! !!! - ₣ªîļėď ťο éжрǻńð ã ćǿмmąʼnď ώїţћ "iterateOn" šēŧ. Τнιŝ çόmmàŋď ẁïłĺ ъе īģпöгèð. !!! !!! !!! !!! !!! !!! !!! !! + ₣ăіľέđ ţö ехρåņď ǻ çǿммãηδ ẃìţĥ "iterateOn" şετ. Ťĥīš ćōmмåиď щΐℓĺ ве ĭģňóѓëđ. !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"iterateOn\""} - ₣óüņď á ċσmмãπð щīťĥ αʼn ίñνāŀĩð "colorScheme". Ťнìš ćбmmāйď шïℓľ ъё ïğņőŗэď. Μåќę śύѓе ťĥαţ щĥеπ šётťїňġ а "colorScheme", ťħë νàŀΰê măтςђèŝ τнё "name" óƒ ª çôłοŕ ѕсĥемĕ іи ťђε "schemes" ľįѕŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣óŭйď ã сóмmаńδ ẁϊţђ дή îⁿναļīð "colorScheme". Тћîŝ ĉǿмmαήδ ωïĺĺ вε íğлŏгëð. Мâķэ ѕϋŗê τĥāţ ẅћэй šěτťîņġ å "colorScheme", ţĥé νаļΰé мªŧçћєѕ ţħє "name" őƒ ă ¢ôℓôř ŝςђзмê įň тнε "schemes" ľïşŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - ₣όµйδ å "splitPane" ςŏmмáņđ ẃìţн ąи їŋνάℓįð "size". Тђĩś çõmмäήð щіłℓ вэ ΐğņøŗêδ. Μǻκє ѕυřé ŧћě śîźě ΐŝ ъěτẅз℮п 0 ªńð 1, ёхςłυѕīνě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣ŏΰπđ ā "splitPane" ¢ǿммäήđ ŵìтћ ăη îŋνąłιđ "size". Ţђíѕ ċǿммªпđ ŵīĺℓ ьė ìĝñōгёđ. Мăќέ šύгз τĥě śιźê įѕ ъèτшέëπ 0 äⁿð 1, εхčļûŝίνз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"splitPane\"","\"size\""} - ₣āΐłęð тò φάŗšě "startupActions". !!! !!! !!! + ₣âìľęď ťǿ рαяŝé "startupActions". !!! !!! !!! {Locked="\"startupActions\""} - ₣öŭňđ mûℓţϊφłē έņνіяоимĕʼnт νáгΐάвļęś ωìŧħ тĥĕ šąmё ñдmě ĩи ďīƒƒěŗęήт ĉáŝéś (ľοщèя/ùррēя) - õπℓý бʼnэ νąłϋз ŵïłℓ вє ùѕêđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣öųñđ múℓтĭφļę èⁿνіŕθлмėňţ ναřίдъľêѕ ẅïτħ ťħз śăмė ήámе їή đїƒƒέřέηт čãşзś (ĺóщěг/ůрρеŕ) - óлℓŷ οиę νǻľµě ẁιľľ вё ųşέď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ąπ öρŧîòлäŀ ¢ǿмmãπđ, ŵĭтћ äřġůmĕηтš, το вε šράщπєď ĭπ ţђē ņέщ τãъ õř рàйё !!! !!! !!! !!! !!! !!! !!! + Åʼn ǿртíоñαŀ ċοmмäηď, ẁίŧћ ãŗĝūměʼnтş, ţô вέ šρªẃйèđ įņ ťђė πεẁ ŧав ŏŗ рªņε !!! !!! !!! !!! !!! !!! !!! - Мôνę ƒŏсűş тö аńбτћęŗ тàъ !!! !!! ! + Μòνê ƒöčµѕ ŧθ ãņøτнёř τåъ !!! !!! ! - Μονэ ƒοčũš τό ťĥе ⁿзжť тäв !!! !!! ! + Мõνè ƒòćüŝ τθ ťћé пĕ×т ťαъ !!! !!! ! - ∆ņ ãłíäŝ ƒθг ţĥэ "focus-tab" ѕűвĉòмmáηď. !!! !!! !!! !!! + Дŋ äℓĭάş ƒоř τћę "focus-tab" šųъčǿммáñđ. !!! !!! !!! !!! {Locked="\"focus-tab\""} - Мőνē ƒоċμś τō ŧђę φŕēνϊóцş ťàъ !!! !!! !!! + Μöνє ƒоćüŝ ŧó ţĥë рřěνĩομѕ тãь !!! !!! !!! - Моνë ƒöçûŝ τĥε ţαъ äт ţħε ĝίνëŋ ίņďěх !!! !!! !!! !! + Мονé ƒоćűŝ ťħέ τåь àτ ţĥé ĝįνèň іňďėж !!! !!! !!! !! - Мöνē ƒòçυšęð φăņé ŧô τĥё тāъ āτ ţĥë ğįνёņ ΐʼnδêж !!! !!! !!! !!! !! + Μόνě ƒôçџś℮ď φąπέ τŏ ţħĕ ŧäв ǻτ тћė ģΐνеņ ĭñðěх !!! !!! !!! !!! !! - Μόνê ƒǿćμśèð ρåиė τō åпбŧћзř ŧåъ !!! !!! !!! + Μǿνê ƒõćυѕěð φâή℮ ťθ ǻлοτĥёŗ τąв !!! !!! !!! - Ąń ąłîäŝ ƒöѓ тħé "move-pane" śцв¢òмmăπδ. !!! !!! !!! !!! + Ăⁿ āℓíąš ƒбŕ тнĕ "move-pane" šúъćбммàⁿδ. !!! !!! !!! !!! {Locked="\"move-pane\""} - Şρέçĩƒỳ ťћё šїźę âš ª φέŕçέлťαġě θƒ ŧħē φǻŕėńť φªηĕ. Vàŀíď νǻłµ℮ś ªřе ъēťώёэŋ (0,1), ē×ĉłϋşĩνė. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ѕрёċϊƒÿ тђĕ ѕîžĕ αѕ ǻ ρēŕċêйτдģе őƒ ŧħě φǻґзйť φàņē. Vдℓίδ νǻŀùзş āŗε ьεтщ℮ёň (0,1), е×ςĺúŝινе. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Сŕěăтě ă йεŵ тàъ !!! ! + Çґέăťė ã πėш τáв !!! ! - Άп âłìàś ƒбř ţћè "new-tab" šûьĉòmмąйδ. !!! !!! !!! !! + Ăи άℓîªŝ ƒōř τħë "new-tab" śμвċόmмãŋδ. !!! !!! !!! !! {Locked="\"new-tab\""} - Мόνę ƒöčυś τø ăиŏтнëґ φάňę !!! !!! ! + Μǿνé ƒосųŝ тô дπøţћєг рáņė !!! !!! ! - Дŋ ąľιàš ƒôř ŧħё "focus-pane" śûъĉбmмаʼnđ. !!! !!! !!! !!! + Áй áℓįãš ƒòŗ тђĕ "focus-pane" şυъĉöммàńđ. !!! !!! !!! !!! {Locked="\"focus-pane\""} - ₣ôςűş ţħέ φâиє дт ťћέ ġίνéи ιņδĕх !!! !!! !!! + ₣ό¢űѕ тнê φàπĕ аţ тђè ğĭνėй ïŋðεж !!! !!! !!! - Öφěй щíţħ ťћε ğīνёⁿ φґõƒіĺė. ∆ςćëρťş ēįţнèя тĥě ņάмé бř ĜÛΊĎ ǿƒ ª рŕόƒϊłê !!! !!! !!! !!! !!! !!! !!! + Θφêп шĩτћ ťнě ģìνēή ρѓøƒìĺę. Âćĉеφťś ёĩτĥėŗ τђė йάмє øя ĠŮΪÐ õƒ а φŕóƒίℓè !!! !!! !!! !!! !!! !!! !!! - Ćřεàτę ª пєẃ ŝρℓîŧ ρãйę !!! !!! + Ĉґéáŧе д ήэẅ ŝφĺĭτ рãňё !!! !!! - ∆ň àŀîåš ƒоŕ тђе "split-pane" śµвсômmāñδ. !!! !!! !!! !!! + Āл ªļïâś ƒбг ŧћê "split-pane" ŝύвćőммäňδ. !!! !!! !!! !!! {Locked="\"split-pane\""} - Ċгéåτє ţнė ñĕŵ ρáπė ąš á ħôřїżöñтάľ šрℓϊт (τћíñĸ [-]) !!! !!! !!! !!! !!! + Ċŕéάťё ťћè ⁿеẁ рâлè åś ǻ ħôŕîžőņτąŀ śρļίτ (ŧнїπĸ [-]) !!! !!! !!! !!! !!! - Ĉѓέāтė ţнê πёẃ ρäņё āš ā νеřťîĉâľ ŝρŀϊť (τћϊņк [|]) !!! !!! !!! !!! !!! + Ċяěªтĕ ŧнέ ⁿèω ρаηе ªš ā νēѓτîčäŀ ѕрĺįť (тĥїπќ [|]) !!! !!! !!! !!! !!! - Ĉřēâťє ŧћê πέŵ φàńε ъў đûρĺϊςάтίńğ ţћε рŕóƒïℓē ôƒ ŧħ℮ ƒôċŭśεδ φâήë !!! !!! !!! !!! !!! !!! ! + Ĉřεâте тħè пέẁ φаńè ъỳ ďцрłíçăţιηğ ťħе φгоƒíľз оƒ τĥě ƒőċūşëð рāиē !!! !!! !!! !!! !!! !!! ! - Ŏφēή ĭл τĥé ġΐνэñ đīřĕĉťǿґу îņşţěαð оƒ ŧђе φřōƒΐļė'ŝ ŝ℮ţ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! + Óрεŋ ιŋ τћē ĝіνèή ðіґėсťσŗý ΐńѕţêªđ ôƒ тħ℮ рřоƒĭľė'ŝ şĕŧ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"startingDirectory\""} - Όρεⁿ тћ℮ ťėґmíʼnªĺ ώĩтћ τнé φѓŏνįďèď ŧιťŀ℮ ϊŋşťéāď őƒ тђε рřǿƒĩľë'ş şêţ "title" !!! !!! !!! !!! !!! !!! !!! !! + Òрèи ŧћ℮ тεгміņдľ шįтђ τћз рŕσνϊðэđ ţϊťľё їņšτèāď οƒ ŧħз рѓοƒïℓε'š śēт "title" !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"title\""} - Оρêή ţђë ŧαв ωĩŧћ τћê ŝρēčίƒιęð ćôļóŗ, īη #ггģģвв ƒǿřmåτ !!! !!! !!! !!! !!! ! + Фрєŋ τћę ťāь ẃĭŧĥ ŧĥē ѕρεçíƒĩĕď ċŏĺőŗ, ίʼn #řґģģьь ƒöřмàŧ !!! !!! !!! !!! !!! ! - Ορĕņ тĥë таь ωįŧђ тăвŤιтłё ον℮ґŕìδĭπġ ďэƒάùľť ţΐŧłę ăⁿď śüφрřĕşšΐʼnğ тíтļè ĉђąиġë měşšąģĕś ƒяőm ţћ℮ ąρφĺΐсаτίöй !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Όрзи тћê тäъ ŵîтн ŧãвŦìтŀέ øνėřѓĩδіиĝ δęƒåυĺŧ тīτłê ǻйď ѕűррŕ℮ѕśìήĝ τĭťℓé ċћаπġё mεśšàğзš ƒŗøм тћê ªрρĺĩčªτĭóņ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"tabTitle\""} - Ϊπħěřïт тĥέ ťэґmιŋąℓ'ŝ оẅή ëʼnνīŕőпmєŋτ νâřϊǻьľėś ẁħĕη ċѓĕаťіпğ ţħë ήэщ ťäь όг ρâлë, гáтнэя τћαή ςгęâŧìñġ â ƒřеŝћ єйνιŕőñmĕπŧ ъļõĉķ. Ŧнĩś δёƒàùŀτš ţо ŝéт ωћ℮π ª "command" ιŝ ράşśеđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + İηĥегïт ţĥě ťεřmĭʼnǻł'ŝ õώп εņνĩŕόňмěñť νāřĭáьŀĕś ώнēл ĉгėǻтΐʼnġ ţħз πęш ţáь ŏŕ ρäη℮, ŗãŧħёř ťĥªή ¢řēäţіηĝ ǻ ƒŗėśћ èήνιгőŋм℮иť ьļο¢к. Тħїš ďęƒαüłŧŝ τõ ѕёţ ẅħēň ą "command" îş ρãśŝéđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"command\""} - Ωрêй тħĕ ťάв ώїτħ τĥë şφěςíƒįеď сòŀõŗ ѕĉнзmе, їŋšτέåđ őƒ ŧħέ ρѓбƒîłε'ŝ ѕęŧ "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! + Øρéη ŧĥē ŧаь щïţђ ţнё ѕрě¢ίƒĩеð ¢θℓôя şĉнем℮, īπѕτêåδ òƒ τĥё φřőƒĭľе'ŝ ѕ℮ť "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\""} - Ďĩѕρłăγ ťне äφρļϊćąτìôñ νęŕѕïǿⁿ !!! !!! !!! + Ďìśрℓàÿ тћē åφφłîςдťίøп νēřšįóń !!! !!! !!! - Ŀâύйĉħ ťнє ẅїñðõẃ мª×ĩмĭżэď !!! !!! !! + Ļáūńçн тћё ẅïиďоŵ мåжімĩźęð !!! !!! !! - Ŀãűисћ тнё ωįлδøẃ ĩʼn ƒůłℓś¢гεέи mбðє !!! !!! !!! ! + £âµńĉн тнė щίπđощ їʼn ƒûℓŀşċŕėēη мøđè !!! !!! !!! ! - Μöνě ƒǿćųś ţø ťђе ǻðјå¢℮йт φąήë іπ ťћė ѕρėćїƒіεď đϊřеċтîóñ !!! !!! !!! !!! !!! !! + Мονë ƒόçцŝ τô ţнε дďјαçéʼnţ φáπέ ίņ τнë ѕрéςĩƒїĕδ δіŗéćťïőп !!! !!! !!! !!! !!! !! - Ǻπ ªℓïǻŝ ƒόŕ ţĥз "move-focus" ŝűъčοмmάπδ. !!! !!! !!! !!! + Åп âℓιāś ƒóř ŧђз "move-focus" šůъçόмmàпð. !!! !!! !!! !!! {Locked="\"move-focus\""} - Тнė ďίѓèċţϊōň ťö мøνè ƒθсųş ίή !!! !!! !!! + Тнê đιřèсţïöп ţό мøνĕ ƒŏčúś ìή !!! !!! !!! - Šẅаφ τћë ƒŏçŭšėđ ρąήè ώĭŧħ ŧĥē аďјā¢ēйτ рǻńë ĩп ţħε ѕφĕĉĩƒīēď đîŕэċŧїòň !!! !!! !!! !!! !!! !!! !!! + Šшдφ ťĥė ƒоčùšěď рдйê ŵīťђ ŧнё ãđĵă¢èņτ φåиé íŋ ťĥē šρêςīƒĭзδ đìѓз¢ţįőñ !!! !!! !!! !!! !!! !!! !!! - Ŧħε ďΐřéċтїŏй τö mθνĕ τћέ ƒőćυѕзđ рàиĕ ĩń !!! !!! !!! !!! + Тнě διяèċτîõη ţό mōνе ťћë ƒòčϋš℮δ рαπ℮ īп !!! !!! !!! !!! - Ŀаµп¢ђ ťћė шϊⁿδǿẃ íй ƒôćûş мöđē !!! !!! !!! + Ľãūлçĥ тħέ щíиδσώ ìй ƒô¢ũš мôďз !!! !!! !!! - Ŧħιš рáгàмĕтêŗ ϊŝ άń íηŧєŕŋάĺ ϊmφľémёńтăţíőń đęтàĭļ диď šнθυłď ήøŧ ъ℮ ùśєđ. !!! !!! !!! !!! !!! !!! !!! ! + Ŧћїѕ ρąяáмėŧєѓ ĭş āη їňтεŕήåŀ íмφĺêmёņťдţïòπ ďёŧåĩļ дηð šнŏųļδ ñōţ ьë ųšėð. !!! !!! !!! !!! !!! !!! !!! ! - Ѕφêċīƒŷ ą ţěřmΐñåľ ŵϊňðбω ŧб řūл ŧħе ğίνéη çômмâηδłîʼnē įπ. "0" дľẃäŷѕ ґėƒëŗś тό ŧħè ćŭгяëπŧ ŵĭńðōω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Šрêςĩƒγ а ťĕгmìŋаł ẁϊпðõω τσ ґύŋ τнз ģίνĕи ĉömмαпđľįʼnе їй. "0" āℓώąŷš řеƒěŗş ťσ ŧħė çŭґřęπť ẁįʼnđǿω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Śφ℮сίƒу ţħè ρóşΐťιöʼn ƒōŗ ŧђε тёŕmĩʼnªŀ, ìń "×,ý" ƒõŗмāт. !!! !!! !!! !!! !!! ! + Šрëĉίƒў тћε рόŝíŧιòή ƒōя тнě тėřmįηдŀ, ϊп "х,ÿ" ƒθřмάŧ. !!! !!! !!! !!! !!! ! - Śρěćϊƒÿ ťħє ŋџmьēŗ оƒ сοłůмʼnś аʼnď яóẁѕ ƒõя ŧĥе тεřмίⁿǻĺ, įη "с,ř" ƒöгмáŧ. !!! !!! !!! !!! !!! !!! !!! + Ѕφёĉĩƒỳ тнэ ņµmъ℮ґ øƒ ςòļůmйś ăŋð гøшş ƒöŕ тнε τєѓмϊⁿãŀ, ĩʼn "ċ,ř" ƒθяmãţ. !!! !!! !!! !!! !!! !!! !!! - Ρґëšş ţђê вúţтôʼn ţó ǿφеʼn а ňêŵ ťĕямιπаℓ ţав шĭţĥ уòůŕ đэƒäџℓť ρґöƒιŀě. Ôρēη ťнє ƒĺỳöϋŧ τо śеłęčτ ŵћΐĉћ ρřöƒϊļė уōů ẃåņт ťö θрêⁿ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Рґзѕś тђз вůťŧôй ŧǿ ǿφеη ā ʼnéẃ ţєѓmîńąŀ ťâв ωϊŧђ ýθůř đęƒăμℓţ ρŗôƒїĺε. Óρĕп τћё ƒłýбцť ţø śεŀèςŧ шĥïčĥ φřŏƒίļê ўσμ ẁάлτ ţσ όрεй. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Ňещ Ŧăь !! + Ňèώ Ŧäъ !! - Öрзи д пéẁ ţαъ !!! ! + Όрèπ ª йěŵ ταв !!! ! - Åĺţ+Ĉŀïćκ ťó şρľïτ τħĕ čũгŗëŋŧ ŵīηďøẅ !!! !!! !!! !! + Ǻℓт+Сłĩċк тö šрŀîţ ţћë сцřѓèήŧ ẅїпðбω !!! !!! !!! !! - Šнĩƒт+Çℓιĉĸ ţø ŏрεη α πέẃ шīήðõẃ !!! !!! !!! + Śħîƒť+Çľιćĸ тō όрзⁿ ª йзώ ẁίπðôш !!! !!! !!! - Ćтѓℓ+Сℓї¢κ тó σρёп ąŝ ªđмιйіşŧŗаŧοѓ !!! !!! !!! ! + €ţŕł+Čℓїčķ τõ ορêņ αŝ аðmìñĩѕτŕăţòг !!! !!! !!! ! - Çĺøś℮ ! + Ćℓőŝз ! - €ĺοşé ! + Ĉļøşз ! - Ċľõѕě ! + Ĉļόѕě ! - Μάжįмîżę !! + Μджįmïźэ !! - Μíиímĩżé !! + Μïиιmіžё !! - Μĭиімĩż℮ !! + Μîⁿĩmĩż℮ !! - Μĭńįмĩź℮ !! + Мĩиîmĩżє !! - Àвōŭţ ! + Åвōύţ ! - Ś℮пđ ₣зèδъàćќ !!! + Ѕеηð ₣ę℮đвäçк !!! - ΏĶ + ΦΚ - Vзŗšįоⁿ: !! + Véяšîõп: !! This is the heading for a version number label - Ģêťŧΐňĝ Šţàřтëð !!! ! + Ġеťтΐñĝ Ѕτдŗτęď !!! ! A hyperlink name for a guide on how to get started using Terminal - Ŝσϋřćę Ĉòďė !!! + Ѕοџŗсė Çŏđе !!! A hyperlink name for the Terminal's documentation - Ðσčûmейŧąţîǿη !!! + Đŏĉµмěⁿтатïõñ !!! A hyperlink name for user documentation - Ґεℓ℮âѕє Ńōτэš !!! + Ŗеľ℮àşε Ŋòτéš !!! A hyperlink name for the Terminal's release notes - Ρяίνäсу Рθļĩ¢у !!! ! + Ρґїνãсÿ Рöĺĩςỳ !!! ! A hyperlink name for the Terminal's privacy policy - Ťнΐяð-Ραяţу Йōŧîĉеŝ !!! !!! + Ţћĩřð-Ρářŧγ ∏οŧīĉęŝ !!! !!! A hyperlink name for the Terminal's third-party notices - €ªňćêļ ! + Ċдйĉέł ! - Ċľöŝē áłŀ !!! + €ļőşε áļľ !!! - Ðǿ уǿú ẃǻлŧ ţō çŀθѕє ǻľĺ ẁĩⁿďőώŝ? !!! !!! !!! + Ďõ γбű ẁāŋţ ťó ςℓσśĕ äℓℓ шîйđбẁś? !!! !!! !!! - Ċªñсεļ ! + Ćăʼnċęℓ ! - Ĉľōѕε ãļℓ !!! + Ćļõѕέ аłℓ !!! - Ďό γσџ ẅǻńτ ŧο сĺòśè āľŀ тäьŝ? !!! !!! !!! + Đσ ŷőū шдиŧ тò čļòŝз αŀľ ţâвŝ? !!! !!! !!! - Сāйсéľ ! + Čǻñčėŀ ! - Çļøśé áņỳщăŷ !!! + Сłŏѕε άñÿẃåγ !!! - Ẁαяήîʼnĝ !! + Ẁαгйïņğ !! - Ύõŭ àŗё āьǿŭτ ţθ çŀôşĕ â ґèαđ-õŋłÿ ťěŗмįņаľ. Đò ýбų ẁίŝħ ţŏ čôñτΐйџê? !!! !!! !!! !!! !!! !!! !!! + Ϋôů ářє дъоϋť ŧо ćľōśέ ª гęаð-öйĺу тêřmĩńäĺ. Đǿ ỳøü ωîśђ ŧő ćőʼnţїñϋέ? !!! !!! !!! !!! !!! !!! !!! - Ćăņçëľ ! + Сâήçеŀ ! - Ύõµ ąѓê åъоϋţ ŧø ρдşŧĕ ţę×ţ ťђáτ ίş łǿлģ℮ѓ ţнăń 5 ЌїБ. Đō ÿőų ẃĩśћ тō ¢όⁿťΐлůз? !!! !!! !!! !!! !!! !!! !!! !!! + Ўōµ ąřε ăвőüť ţò ρãѕŧĕ ťê×ŧ тħãт ïš ľôʼnğзŗ ŧђąи 5 ЌΐБ. Đθ γŏΰ ẅĩşħ τò ςõиŧĭňūê? !!! !!! !!! !!! !!! !!! !!! !!! - Раŝтε ãńỳщāу !!! + Ρâşτз ăπуẅаý !!! - Ẅǻŗήĭňĝ !! + Ẃâгηïņğ !! - Ĉąŋćėĺ ! + Ćªήĉěł ! - Ўоυ àяє āьôυť ţŏ ρăŝтè тежţ τђãτ čǿŋтдĩηś mцŀŧĩрℓě łìʼnėš. ΃ ýσц ρǻŝтê τђίś τě×τ ìηťŏ ỳσůґ śћēļŀ, íт мäŷ гëśцℓт îň тћё ųпèхφєсτєδ ė×êćџтίőņ бƒ сøмmąлđŝ. Ðò ỳǿџ щīŝћ ťö ¢óńŧĭиџě? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ϋбů áяē авôцţ τŏ ρªѕŧê ťĕхţ ťĥâť ĉοлŧäїлš mųŀťїрŀē ĺΐиέš. Ĭƒ ýŏû рαśτё ťђìš ť℮хŧ îήŧø γõũŗ ѕћêℓļ, íτ mду ŗéśμļт įŋ ţħ℮ цпęхρ℮çťêď з×эćúтϊŏⁿ õƒ ςōмmãⁿðş. Đο убŭ ẃīѕн τõ ċôʼnŧïñûε? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Рдşťë άñỳŵåŷ !!! + Ρªšţє аńŷẁàŷ !!! - Ẃąŗпĭʼnġ !! + Щāŕñιπĝ !! - Ťÿрε ǻ сõmмǻлδ лàме... !!! !!! + Ťýφě á ĉθmmāņđ йăмē... !!! !!! - Пō мάŧ¢ĥιпģ ĉőммάлδš !!! !!! + Ŋό маŧ¢ĥΐŋğ сóмmăлđš !!! !!! - ∆сτîõņ š℮αŗčħ mõðэ !!! !! + ∆ćŧìøп şєāяćħ мóδє !!! !! This text will be read aloud using assistive technologies when the command palette switches into action (command search) mode. - Ťаь тíťĺê mθďе !!! ! + Тàв ťїтłé môðэ !!! ! This text will be read aloud using assistive technologies when the command palette switches into a mode that filters tab names. - Сòмmªπď-ℓìņê mǿđē !!! !! + Çοmмăπď-ĺιиé мõđэ !!! !! This text will be read aloud using assistive technologies when the command palette switches into the raw commandline parsing mode. - Мòŕε õρτίǿήѕ ƒŏґ "{}" !!! !!! + Μояē öφтίóπś ƒőř "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Έжĕćũţіпġ çǿмmаńđ ŀîπè ώíļļ íлνōķě ťћз ƒőłłǿщίήğ ĉθмmäňðś: !!! !!! !!! !!! !!! !! + Ėжėсùţīпğ ¢ōmмªñð ĺїπĕ ẁíĺł ĭиνокė тħе ƒöĺℓŏщîпġ çõmмāⁿďѕ: !!! !!! !!! !!! !!! !! Will be followed by a list of strings describing parsed commands - ₣ąĩľĕď рàŗśìπğ ¢бmмаπδ ℓιńє: !!! !!! !! + ₣āіľ℮ď рàгśīпģ ¢бммäⁿδ ĺīñè: !!! !!! !! - Ċǿмmāňδ Ρäℓέτťę !!! ! + Ćσmmăηδ Рάŀĕтţ℮ !!! ! - Тáъ Ѕẁĭŧ¢ħĕř !!! + Τăь Ѕωîťςћêг !!! - Τŷρė à ţãъ ńаmё... !!! !! + Ţýρё ă тăъ пâmě... !!! !! - Ŋο mãτ¢ħįпģ ŧдв ŋámë !!! !!! + Ńô мατćнìņģ ţдъ пªмĕ !!! !!! - Ēητėя α wt ćōмmάпđłįήę ŧô ŗúŋ !!! !!! !!! + Èηŧĕґ ǻ wt ċθmмаʼnđļïʼnė тο ŕūʼn !!! !!! !!! {Locked="wt"} - Мθґέ орŧîõиŝ ƒøř "{}" !!! !!! + Μōŗè σφŧïθлš ƒόŕ "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Тŷφє à ςόммãņð ñāмė... !!! !!! + Ťÿφė å ċοmмªйđ ηаmє... !!! !!! - Ñǿ mäŧčћіиğ соmмªŋďŝ !!! !!! + Ņô mάťςћίņĝ ċоmмάņďš !!! !!! - Ѕµğğêŝţïöήš мзиū !!! ! + Šύğġěşţįσпś м℮ʼnμ !!! ! - Мбяё òрţϊõňş !!! + Мόѓė ōφťіŏⁿś !!! - Śμġģëѕτϊǿňš ƒσϋⁿď: {0} !!! !!! + Śΰġģ℮šτϊθñѕ ƒöüⁿδ: {0} !!! !!! {0} will be replaced with a number. - Ċŗΐmşǿπ !! + €яïmşǿń !! - Ŝτеêľ Вŀüĕ !!! + Ѕťéêℓ βľυэ !!! - Мέďîųm Ŝéª Ĝŗεзп !!! ! + Μёδιųm Ѕεд Ğґзēη !!! ! - Đдгκ Οґâήĝë !!! + Ďäŕќ Õřäňğè !!! - Мėðįΰm Vïøľέţ Ŗěð !!! !! + Мęðϊům Vĭõľзт Ѓεð !!! !! - Đбδģĕя Ъłūě !!! + Ðôđğēг Βĺϋę !!! - ₤îмè Ģґéëη !!! + Ĺіmз Ĝґéĕи !!! - ¥еĺľόш ! + ¥ęℓłοẃ ! - Ъℓцê Vĩöŀěτ !!! + Ьľùе Vΐбŀэτ !!! - Ѕŀάťê Ъŀυé !!! + Şŀаťę Βĺûė !!! - Ľίмё ! + £ϊмз ! - Ťāʼn + Ťåπ - Мαġęпτă !! + Μàğэⁿτα !! - Ćγąή ! + Ċуåň ! - Ѕку ßļυ℮ !! + Šќŷ ßľϋ℮ !! - Ďářк Ĝгāу !!! + Ðàŗκ Ğѓáγ !!! - Ţђíš ℓιпĸ іŝ ΐиνªĺίδ: !!! !!! + Τћїš ŀĩňк іѕ įňνдŀíď: !!! !!! - Ŧĥіś ĺĩпќ тỳφэ ϊš čџяѓёиŧŀỳ πøţ šũρρōŕťэď: !!! !!! !!! !!! + Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! - Čǻŋ¢έł ! + Сąñс℮ł ! - Śéтţīйġŝ !! + Śëţťĩпğś !! - Ŵэ ĉøűļδ πōτ ẅяìťė ťõ ўоūŕ ŝěŧţĩηġš ƒΐĺĕ. Çћěςк τћε φĕŗmìѕšιőʼnѕ øл ťћāт ƒïŀĕ ŧò єŋśџѓз ŧндţ τћè řęàđ-όŋľγ ƒłàģ їš ñσŧ śĕт ãπð тћâτ ŵяιťę āč¢ěśѕ ΐş ĝяāⁿţêđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ẃε ςőűℓđ пòт ωяΐτë ŧô γôύг šěťтïπĝŝ ƒīłě. Çђêĉĸ ţђэ φеřмìşŝΐóпѕ õл ťђàτ ƒĩŀε ţб έπšμґé ţђäť тћê ѓέάδ-õлļỳ ƒŀãġ ΐš ʼnοт ŝėт ǻпď ţнăт ẅѓīţĕ ăсċéšş ĩş ģŗдŋτëđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Бåсκ ! + Бдçķ ! - Βåск ! + Ьãςк ! - ΏΚ + ΘК - Ðєьùğ ! + Ďêъŭģ ! - Éѓяσř ! + ∑ŕřοґ ! - Íŋƒσгmаτіθŋ !!! + Īπƒóřмąτιŏʼn !!! - Ẅąѓŋιņĝ !! + Шãřñїņģ !! - Ćĺίφвõάгď ćǿπтєπτş (φřêνїέω): !!! !!! !!! + €ℓірьоåŗδ ĉóńťęňтѕ (φѓενĭεẃ): !!! !!! !!! - Мóřė ôрťίōήś !!! + Мθґе бφŧīбπš !!! - Ẁιлδõẃ ! + Ẅίńďöщ ! This is displayed as a label for a number, like "Window: 10" - μńηаméð шїñδόщ !!! ! + υʼnňàмěđ ẅіπďōẅ !!! ! text used to identify when a window hasn't been assigned a name by the user - Ěпŧěŕ ª ŋėẁ ηàmè: !!! !! + Єиŧ℮ŗ ă ñéẁ йάmё: !!! !! - ŎЌ + ÒК - Сàπçёļ ! + Сªⁿċĕĺ ! - ₣âΐļěð ťθ řэπамè ẃíлðоώ !!! !!! + ₣âιļĕđ ŧо ѓëňąмë щϊπδǿω !!! !!! - Ăлóŧћėґ щίňđοŵ ώίţн ťћдτ ήåме äļřёǻδŷ ęхіşţş !!! !!! !!! !!! ! + Άņόтĥěŗ ẃїлðοω ẁιťĥ ťћаť пâmě ăļřéàδў ёжìśŧѕ !!! !!! !!! !!! ! - Мàхιmįżє !! + Μą×ìmϊżé !! - Ґέşτοřę Đόẅň !!! + Ŕèšŧòяё Ðǿẃи !!! - Ċоmmäňδ Рâĺеŧţз !!! ! + Ċòмmāńδ Рªľėτťë !!! ! - ₣σċΰš Ţэŗмįņаĺ !!! ! + ₣ôćűŝ Ţеґмĭйâŀ !!! ! This is displayed as a label for the context menu item that focuses the terminal. - Ŵΐŋđσẃš !! + Ẃϊйδοŵš !! This is displayed as a label for the context menu item that holds the submenu of available windows. - Θрēŋ д ŋėẁ ţǻв íπ ģίνзл ѕтάřтіηğ δϊřėĉţοŗŷ !!! !!! !!! !!! + Фφĕń ª пёẅ ţâь ίи ğīνęņ ѕŧāятĩлğ δįŗęćтŏяγ !!! !!! !!! !!! - Õρεņ ª ňēŵ ωĩńđôщ ẁΐŧђ ĝíνзή ŝŧáґτϊⁿĝ ďîѓėςťőґý !!! !!! !!! !!! !! + Οφэй ä ŋёώ ẅįńðбώ ẁįτђ ĝĩνēη šτáгтϊŋĝ ðĭřěçŧøгγ !!! !!! !!! !!! !! - Şφłĭт тнë ώīñðőŵ ªпδ ŝţаŕŧ ĩñ ĝíνел δίŕê¢ŧбŗý !!! !!! !!! !!! ! + Ŝρℓΐŧ ŧнė ẁίňďõŵ άпδ ŝţâґţ ίń ģįνëʼn δϊгέ¢ŧøяў !!! !!! !!! !!! ! - Ěхρŏяť Ťэжţ !!! + Ė×φōŗŧ Ţєхŧ !!! - ₣àΐĺėδ тб эжφǿѓт ţěгмìήāĺ ¢θňŧēņţ !!! !!! !!! + ₣ăìľεď ťθ эхроґт ţеґmίñдļ ¢ōйт℮лť !!! !!! !!! - Šΰç¢ēśśƒűŀļý ε×ρоѓтеď тεямĭηåℓ ¢øʼnŧēŋţ !!! !!! !!! !! + Ŝūĉčєšśƒυłłγ ĕ×φòŗтэð тēгмïʼnãℓ ĉοʼnťëⁿţ !!! !!! !!! !! - ₣ìňď ! + ₣ìпđ ! - Рℓǻïⁿ Τęжţ !!! + Ρĺáīň Тěхт !!! - Τêŗмίⁿàťïöń ъёĥǻνіόг сдπ ъ℮ čθπƒĩĝüŕеď ĩñ âðνªňč℮đ φѓòƒìĺę šεţţïπğš. !!! !!! !!! !!! !!! !!! !! + Ťéямїлâŧîόň ь℮ћäνįőř čªή вĕ ċοñƒĩġџřèδ įŋ ăδνåл¢êð ряòƒιļє şėŧтіиĝś. !!! !!! !!! !!! !!! !!! !! - Đǿʼn'ţ śĥőŵ ãģάΐń !!! ! + Ďόń'ţ šħόω ãĝάϊл !!! ! - Τћìŝ Ŧэґmїńâļ ẁïπđōŵ įš řüлñιņġ ăş ∆ðmîň !!! !!! !!! !!! + Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ Ãðmĭⁿ !!! !!! !!! !!! - Ŝцĝġеşŧіǿⁿѕ ƒōύйđ: {0} !!! !!! + Ѕũğġεšтįóпş ƒōцʼnđ: {0} !!! !!! {0} will be replaced with a number. - €ћëςĸįńĝ ƒǿř ûρðăтéš... !!! !!! + Çђęċќїŋğ ƒσř ũρδàτέѕ... !!! !!! - Àń ŭрďáŧέ īš ãνãїľдъļé. !!! !!! + Δʼn μρđаŧè ĭś ăνăϊłªвľė. !!! !!! - Îήѕťªĺℓ йøω !!! + Ϊŋśţāŀŀ πбẁ !!! - Τће "newTabMenu" ƒįєŀð çōиŧάíňѕ мοяё ŧħãʼn θπĕ ĕπτѓý òƒ τÿρē "remainingProfiles". Ωйℓу ŧђè ƒϊѓşť øⁿе ẁιľľ ъё ¢ŏлѕįδèѓęď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŧђê "newTabMenu" ƒιêŀď čσñţąϊйѕ mōѓé ťђǻñ öŋė ёňτŕý őƒ τýρê "ґёмãīліηģРяöƒïľєś". Φпĺў τђё ƒΐѓšτ όñè ώїłł ьè ĉőлѕìδēяёδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="newTabMenu"} {Locked="remainingProfiles"} - Ŧĥę "__content" рřǿрёřтŷ ιѕ гéŝēяνеď ƒоѓ ιńτëŕидĺ ũśэ !!! !!! !!! !!! !!! + Ťђέ "__content" φŕόφέŕτγ íś řêšзřνеð ƒσř ійтėŕňåℓ ΰşè !!! !!! !!! !!! !!! {Locked="__content"} - Õрęп α δίªĺōğ čøńťąιйіиġ ρгôδúĉτ ĩʼnƒōґмаŧіóń !!! !!! !!! !!! ! + Фφèη а ðїаļθĝ ćбⁿťáĭηΐñģ ρřσđůčŧ ілƒǿřmãтĩöή !!! !!! !!! !!! ! - Οрёň ťħэ ćοłōř рĭ¢ĸєŕ ťő čнθόŝé тђě čоłοѓ ôƒ тћίš ταъ !!! !!! !!! !!! !!! + Ωφĕй ŧнė ¢óŀοŕ φĩċĸęř τσ ¢ђőōѕė ťħê сõľòŗ бƒ ŧħįѕ τªь !!! !!! !!! !!! !!! - Ωрěп тћέ çōммалδ φāĺĕттз !!! !!! ! + Øρĕл ţће сǿmмåήđ ρãĺέτŧě !!! !!! ! - Ώρєπ ä ʼnĕω ťåъ цѕΐʼnġ τĥė аċťĩνε рřθƒįŀ℮ ìŋ тħё ςűгřэиţ δíязçтøŗў !!! !!! !!! !!! !!! !!! ! + Óρέл â ʼnзш τàв ũśĭлġ ŧħё ąċŧîνê ρŕøƒΐĺé ĩи ţħě çúггзηт đĩřзçţøřý !!! !!! !!! !!! !!! !!! ! - Зхρøгτ ťнę ¢ǿŋт℮ňţş бƒ ŧне ŧė×ţ ьůƒƒĕŗ ĩлţő ä тэ×ţ ƒіℓê !!! !!! !!! !!! !!! ! + Ēхφõґт τĥе сǿήŧĕиŧѕ ōƒ τђė ťε×τ ъũƒƒεř ĩήŧо α ţéם ƒιļê !!! !!! !!! !!! !!! ! - Ôрēň тћє şеªŗсħ ðįãℓòġ !!! !!! + Φρéņ ŧнĕ şęâřςћ ðíάŀŏģ !!! !!! - Ѓεńǻмë τнìѕ ŧâъ !!! ! + Ŗėлαмē тħíŝ тåв !!! ! - Όφéп ťћέ śěττîŋġś рåģē !!! !!! + Òφĕņ тĥе šэτŧīлğš ρдģė !!! !!! - Ōφĕη ă лěщ φàʼnё üšīпģ тħэ àĉτîνз ρгοƒιŀ℮ îŋ τħё ċùгřĕñτ ðīřěćţбŗу !!! !!! !!! !!! !!! !!! ! + Ωрėŋ д πėẁ φâиě űѕīⁿģ тĥэ ã¢τίνë рřŏƒίĺê îŋ ţћэ çūŗгěηт ďíř℮ĉŧõгỳ !!! !!! !!! !!! !!! !!! ! - Çłōśе ąłŀ тąьŝ тó тћé ŗīĝĥт ôƒ ŧђįś ťâь !!! !!! !!! !!! + Сłǿśе ǻĺľ τăвś ţô ŧн℮ яîĝħť őƒ τђїş тάв !!! !!! !!! !!! - Сľбśé ãĺĺ тåвŝ ёхĉëφŧ тħΐş тǻв !!! !!! !!! + €ļόѕĕ ªŀŀ ŧáвѕ έ×ćèρт ŧнìş ŧåв !!! !!! !!! - Čłόšé ŧħϊŝ τάъ !!! ! + Ĉłоśэ ťĥìŝ ţªъ !!! ! - Éмρтÿ... !! + Ёмφţγ... !! - Ċłóşę Ρдņë !!! + Ĉĺοŝе Ρаиę !!! - Сŀθşë ŧħє äċťïνз рąήз ιƒ mΰľťϊφļε рãήёś αгє ргęŝέпŧ !!! !!! !!! !!! !!! + Çĺόś℮ τнĕ ă¢τίν℮ рáлĕ ιƒ mϋŀţїрĺë φàńęś άŗє рřęšеńт !!! !!! !!! !!! !!! - Ґεšεт ťаъ ćöľòŕ !!! ! + Ґέšεт τăъ ċǿℓőґ !!! ! Text used to identify the reset button - Μονê Ťǻв ţô Иéẅ Ŵìʼnđŏώ !!! !!! + Мόνз Ţǻь ŧö П℮ω Щĭŋδōώ !!! !!! - Мбνеѕ тåь ťо ä лěẃ ẃιņðóώ !!! !!! ! + Мøνëŝ ŧªъ ŧǿ ã пεẃ шίŋđоẁ !!! !!! ! - Ŕцⁿ ãѕ Àδмιⁿíŝтřăţθя !!! !!! + Ŕμŋ ąś Āďmįиíšťґąţőя !!! !!! This text is displayed on context menu for profile entries in add new tab button. - Дсτϊνē рдйë mŏνεđ ţö "{0}" тǻъ !!! !!! !!! + ∆çťíνĕ рåⁿэ мôνеð ťб "{0}" ţав !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. - "{0}" ţąъ моνеð ťο "{1}" ώіňďõω !!! !!! !!! + "{0}" ťāъ мōνęđ τŏ "{1}" шΐπδŏẅ !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the name of the window the tab was moved to. - "{0}" ŧäь мőνзđ ŧõ ñěщ ẁιňďòш !!! !!! !!! + "{0}" тąь mǿνєđ ŧσ ήèώ ẅĩŋďøẃ !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. - "{0}" ťаъ mõνеđ ŧб φόśĩťίθп "{1}" !!! !!! !!! + "{0}" ŧαв мονĕđ το φóŝїŧíŏñ "{1}" !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the new tab index in the tab row. - ∆ćŧινë ρáлě мбνęđ тθ "{0}" ťăв ĩπ "{1}" ẃΐπđõщ !!! !!! !!! !!! ! + Α¢ťϊνë рãņє môνέď τō "{0}" ţâъ ĭή "{1}" ẃĩńδθш !!! !!! !!! !!! ! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. {1} is the name of the window the pane was moved to. Replaced in 1.19 by TerminalPage_PaneMovedAnnouncement_ExistingWindow2 - Λçťìνę ρǻñе моνéď ţò "{0}" щĭŋδõш !!! !!! !!! + Λςťìνє рáиė mόνéð ťб "{0}" ŵîńđθω !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the window the pane was moved to. - Á¢ŧìνě ρãńè möνēđ ťó ʼnèẅ ώιňδõẅ !!! !!! !!! + Αĉťįνέ ρªņз mσνёđ τǿ ήёẃ ẃîпďǿω !!! !!! !!! This text is read out by screen readers upon a successful pane movement to a new window. - Αςтïνέ рάйз mōνëď ťб ňėẃ ŧаь !!! !!! !! + Дсţϊνё φαŋє mбν℮ð ťŏ ηеẁ ταв !!! !!! !! This text is read out by screen readers upon a successful pane movement to a new tab within the existing window. - Īƒ śэť, ţħĕ čоmмăήď ẁįŀļ ве ªрρєπδёð ŧō ŧћę ρřõƒīŀє'ѕ ďεƒåμℓτ ¢ǿmmάňď ιηśтēąď õƒ řéφĺāĉΐⁿġ īţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ĩƒ šęţ, ŧнĕ ¢ömmдлδ ŵîĺł ьέ åφрєйδĕđ τσ ŧђė рřŏƒїłє'ş đзƒªūľţ ¢οmмăńδ іñѕţέáđ øƒ ѓēρļąċĭлĝ їţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Γèѕťāřţ €οллзčţïσπ !!! !! + Γēѕŧâяŧ Ĉǿńńēčťїöл !!! !! - Яēśťâŗт ťћз áċтΐνę рǻń℮ сøⁿηėčťϊôή !!! !!! !!! ! + Γėşťáгţ ŧħ℮ ãčтĩνέ ρăйё сǿηńëςтιóņ !!! !!! !!! ! \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-plocm/ContextMenu.resw b/src/cascadia/TerminalApp/Resources/qps-plocm/ContextMenu.resw index 9a9d9f7bbc0..28f0b780470 100644 --- a/src/cascadia/TerminalApp/Resources/qps-plocm/ContextMenu.resw +++ b/src/cascadia/TerminalApp/Resources/qps-plocm/ContextMenu.resw @@ -166,7 +166,7 @@ {Locked=qps-ploc,qps-ploca,qps-plocm} - Тĥё Ńēẁ Шіπđοωš Тěřмιňαŀ !!! !!! ! + Τĥз Йéщ Ẃįńđôẃѕ Тéѓmĩиâļ !!! !!! ! The Windows Terminal, but Unofficial @@ -177,22 +177,22 @@ {Locked} - Шίήďŏшś Ŧêямĩňāľ ŵíτн ă ρѓëνιêω øƒ ũρčŏмįηğ ƒєáţũŗêš !!! !!! !!! !!! !!! + Щΐňδόŵѕ Ŧęřмĭʼnäℓ ẅîťħ à φřеνίëẃ θƒ џрсøмΐʼnğ ƒĕāŧųřэś !!! !!! !!! !!! !!! Open in Terminal (&Dev) {Locked} The dev build will never be seen in multiple languages - Фφěй ĩń Тêřmιπâł (&Cãⁿǻřу) !!! !!! ! + Θρēņ ïη Ţéгmĭηäŀ (&Çäņдѓγ) !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Canary version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Óφ℮ʼn ΐŋ Τėřmīйäĺ &Pяєνϊëẃ !!! !!! ! + Όрèп ìņ Ţêŕmїʼnåļ &Рѓзνι℮ω !!! !!! ! This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. - Ορέл ϊŋ &Téŕmįñāℓ !!! !! + Óрêп ìл &Ťěѓmιиåĺ !!! !! This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key. \ No newline at end of file diff --git a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw index 13302a27a5c..206c42a94b4 100644 --- a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw @@ -118,148 +118,148 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Śěţťĩπĝš čǿùĺδ пθť ьє ℓбãđėð ƒŕθм ƒίłє. Ĉнέ¢ķ ƒòŕ šÿńťá× ℮ŕřõřŝ, їηćľúδїⁿğ ťřªíļϊпġ ςømмаś. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Şετťĭиġś ςòűłď йσт ьé ŀŏдðéď ƒřǿм ƒīļē. Ĉнėćķ ƒσŗ ŝуήтª× ĕяŗбґş, ιйçľůďĩηĝ тŗāіļīňġ ćǿмmāŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ćòџℓð лθт ƒĩήδ ỳôùř ðеƒăųłŧ φřőƒĩℓ℮ ïή ýŏúя ĺìѕţ οƒ рґοƒΐĺεś - ũŝΐпġ ťнё ƒίґşт ρŕбƒĩℓε. Сђэčќ ţθ мąκε şυřě тнё "defaultProfile" мαţĉĥéŝ ţнĕ ĠŮİĎ ôƒ öпė öƒ ŷŏΰŕ ρŕõƒΐĺêş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ĉōûłδ πбτ ƒΐпď ýǿύѓ ďēƒдûľт ρŕõƒĩłę ìń ўôùґ ļïѕτ ǿƒ φѓôƒĭłеś - µšìήğ ţнε ƒįŗѕт ρřόƒїłê. Ćĥеćķ ťõ måķє şυґę ţнé "defaultProfile" мªţčћéş ťђё ĠŪĨĎ θƒ θŋē оƒ уоùŗ ρґоƒïℓёś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"defaultProfile\""} - ₣ōûńď мцľтϊρļè рřõƒιļέŝ шίťħ ťнé śámē ĞÚĮÐ ΐπ уōůř ŝέтŧìňĝš ƒïℓε - ïġʼnǿŗìήģ ðϋφľϊςãτēŝ. Мαķĕ śùѓэ ēǻ¢ĥ φŗōƒїℓę'ś ĜЏЇĐ ιş üηϊqúě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣óŭиď мύŀťìрŀэ φŕσƒïĺєś ẃïτћ ŧнě ѕámĕ ĠŲІÐ įй ўоűя ѕĕţţîήĝš ƒìŀė - įģʼnǿřΐⁿğ δûρľΐçâтéŝ. Мãĸё śμѓέ ĕáĉћ ρяσƒîĺė'ѕ ĢŰĮÐ īş ϋńîqџë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - ₣όϋήð ă рѓόƒïłё ẃìτĥ дŋ ìпνáļїδ "colorScheme". Ďεƒαµļţïʼnğ ťћäť ряόƒіĺë тσ тĥέ δêƒαųŀŧ ¢ôļòřŝ. Μǻκэ ѕůяέ τĥдτ ώн℮ň şэττĭπģ à "colorScheme", тђё νãĺυĕ мāтćћэś ŧћэ "name" õƒ á çöĺόя ѕćнêмε ιπ тћё "schemes" ľĩşť. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + ₣ŏúņđ ā ρřόƒįľĕ щĭťћ ǻή ϊпνåŀίď "colorScheme". Ďēƒāúłтіиğ ţħąţ ряόƒίŀе ţŏ тĥέ ðеƒãµľţ çбĺбŗś. Мāκέ śûѓè ťћаτ ŵђéň ŝєŧťίńĝ д "colorScheme", τħε νåĺμě мäτςĥέş τнέ "name" όƒ а čòľбя ѕćн℮mē īń тћέ "schemes" ļίŝт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - Йθ рѓŏƒįℓëś шëřэ ƒοųπδ īπ уσυŗ ѕēтťιⁿġѕ. !!! !!! !!! !!! + Ňǿ ρяóƒíℓěş щ℮ŕέ ƒòμйδ ϊη ỳοūґ ś℮ŧтĭπģѕ. !!! !!! !!! !!! - ∆ŀļ ρѓõƒîľëś шėгę ĥíðδėл ΐň ÿóŭг ѕέŧŧіňġś. Ύőú múşť ђǻνē άт łєáşť öήє ňòπ-ĥĭđđ℮ņ φґǿƒìłè. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Аℓł ρяοƒįłέš ωèяê ђΐđðěʼn îη ўőϋř śěŧτϊиģş. Ϋŏυ müѕŧ ħäνė άţ ļεǻѕτ οñє пóй-ћίďðēʼn φŗóƒįŀē. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ś℮ттΐиġš čбμłď ñοŧ вė ґęłθǻδεð ƒяσм ƒіĺë. Ċнęĉќ ƒōѓ şγņтåж εŗґõřŝ, ïйçℓūδìⁿĝ τґàιļĭйĝ čοмmáş. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ѕєŧτĩņğš ¢ŏûłď ποţ ьė řëĺбάδзď ƒŕŏм ƒίľе. Çĥэςκ ƒõг şÿπţаж ěгяōŗš, îņ¢ŀύðīⁿğ ťŕăïŀĩπĝ čǿmмãş. !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ťĕмрöґāŗìŀỳ μŝïηĝ тћê Щϊʼnđôωŝ Ŧêŕмīлªł δέƒáυŀτ şεŧтĩйģş. !!! !!! !!! !!! !!! ! + Ťемрőŗāřĩľŷ űśϊⁿģ ťђё Щìπδоωş Ţεѓмîήäĺ δêƒаμłт şèţτïńġś. !!! !!! !!! !!! !!! ! - ₣ąîℓ℮đ ŧо ŀŏªð ŝêŧţιŋġŝ !!! !!! + ₣äĩľєď τσ ŀőåð ŝέťţĩⁿģŝ !!! !!! - Èηсòųņťêřĕδ êґґοřś ώħΐŀё ℓόǻďīπĝ ůśęя şęтτîпĝş !!! !!! !!! !!! ! + Εŋĉőųņŧзяëď зяѓōяś ŵħΐļê ŀθαðĩήġ ùŝéѓ śзťŧĭпģŝ !!! !!! !!! !!! ! - ÖК + ÓΚ - Шāгńιηğ: !! + Ŵаґήĩήģ: !! - Тħε "{0}" ιšⁿ'τ ŕūппїñğ ôη ỳóüѓ mªςĥįⁿз. Ŧħіѕ čαň ρгєνёлť τђè Тέřmîñàľ ƒřöм яέçěĩνїηġ кèÿъøǻŕď ìⁿρûт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŧħє "{0}" ĭşп'ţ řμллĭиġ оʼn ўόùг мàςĥįńë. Ŧħїś ¢áⁿ φřэνєńτ тнэ Тэґмïñăĺ ƒŕöm ŗеς℮íνίиĝ ķēỳвŏαŗδ ĭⁿрΰţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the OS-localized name of the TabletInputService - ÕК + ФĶ - ₣ăīļёð ťθ яеľõăð šĕτťīⁿġś !!! !!! ! + ₣áĭļèđ ťσ ѓéłøǻð şèŧŧíпĝŝ !!! !!! ! https://go.microsoft.com/fwlink/?linkid=2125419 {Locked}This is a FWLink, so it will be localized with the fwlink tool - Ąъõµţ ! + ∆вòΰŧ ! - ₣еёđьäçκ !! + ₣ęęðъâçќ !! - Ѕεττīπĝѕ !! + Śêŧťіňğŝ !! - Ćªήċëļ ! + Ĉάπςеŀ ! - €łθşз åℓĺ !!! + Ċľθŝє αŀļ !!! - Qųìτ ! + Qùíт ! - Ðǿ ÿоџ ẃαπτ ţό çŀòšė äļł τăъś? !!! !!! !!! + Đǿ ÿõű ŵãŋт тŏ ¢ľòŝê ăŀľ ŧãвŝ? !!! !!! !!! - Мџłтĭрŀё φăйĕş !!! ! + Мµļтíрłĕ φдпėŝ !!! ! - €ℓöśε... !! + Ćļôŝέ... !! - Çŀõśέ Ťāвś ŧǿ τħê Ŗϊğћţ !!! !!! + Ċĺοşέ Ţаъş ťό ŧђé Яΐğђт !!! !!! - €ľσśĕ Φτнеř Ŧåъѕ !!! ! + Ćĺόѕ℮ Όтђèř Ŧâьś !!! ! - Çľōšέ Ŧåь !!! + Сĺôšę Ťăв !!! - Ĉłôšē Ρдйє !!! + Ćŀöśё Раņé !!! - Ŝφĺïť Ŧāъ !!! + Šрľīτ Τàв !!! - Şрℓіτ Ρàŋé !!! + Šрŀіт Ρªňë !!! - Шёъ Šзäґĉĥ !!! + Ẅёв Şĕаŕčĥ !!! - €θľŏř... !! + Ċõŀόř... !! - Ċűşţöм... !!! + Ċµѕťøм... !!! - Я℮š℮т ! + Яěšěŧ ! - Ŗěήąмë Ţдв !!! + Γεñамē Ťãв !!! - Ðûρℓĭсåτë Ŧąь !!! + Ďϋφľіčάтέ Τàв !!! - ₣őůⁿð ã рŗοƒіℓє ẃіťђ ãň іήνäļîđ "backgroundImage". Đęƒąΰľťįлģ тħăт φгοƒĩľê τò ħдνє ʼnò ъâçķġѓøύñđ įмãġ℮. Μåкз şųґе тћåţ шĥеņ şęŧŧϊňğ ª "backgroundImage", ťђè νдℓϋ℮ ìś д νáℓïď ƒįļĕ φăтћ ťò аⁿ іmáġë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"backgroundImage\""} - ₣σΰńð ǻ φřοƒĩĺê щϊťĥ åл ĭñνąĺïď "icon". Đзƒªцℓťĩήĝ ŧђат φřőƒįŀê ŧõ ђανэ йõ і¢σп. Мαķę şùřё тĥăτ ωнĕⁿ ѕėţŧιлğ ǻη "icon", ťĥè νáłúз įś ª νаĺįð ƒĩľе ρąţћ тő åи îмąğě. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣ǿũиđ à рřöƒϊℓз ŵĩţн аñ įņνàŀїδ "icon". Ðěƒаúľτīŋğ ţħаτ ρřόƒìŀё тб ђâνє пǿ íčой. Мàĸë ŝùřë ŧĥаţ ωĥĕл ŝеτŧīлĝ ăй "icon", τħε νāłϋë ïŝ ă νàľīđ ƒïŀè рªтн ţő äи ïмäģё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. - Ẅäřпĭņğş ẁеѓĕ ƒоūηð ẃђΐĺë ρªяŝίⁿĝ ỳθůѓ κèўьΐπđìⁿġš: !!! !!! !!! !!! !!! + Щαѓńΐňģš ώĕřе ƒбŭπδ ώħīļë рăяşìⁿġ ўσυŕ κёỳвĩиðīήġş: !!! !!! !!! !!! !!! - • ₣øυňð ā ќєỳьīńđΐñĝ ŵîťĥ тòό мάņу ŝŧřΐʼnġѕ ƒòґ тĥε "keys" άŕґáý. Ťђεřē şђθųĺđ οиĺŷ ье øήę ѕţŕįŋğ νªłüё ĩη ŧħё "keys" äřŗάý. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + • ₣бūήð â ķёуьĩńðīⁿģ шĩŧђ τõб mªńÿ ѕťгίńġŝ ƒóř ţћ℮ "keys" åѓгàÿ. Тħėгē ѕĥōμĺđ бñľў вĕ ŏи℮ ѕтřïлģ νăŀůé ĭʼn ţĥė "keys" άřřàγ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"keys\"","•"} This glyph is a bullet, used in a bulleted list. - • ₣ªίłęđ τо φªґŝё āľℓ śúъçǿmmäⁿðş σƒ лęśτĕđ ςŏмmάηð. !!! !!! !!! !!! !!! + • ₣ąìļ℮đ ţŏ ρǻŕşε âłľ śцъċőммåиðѕ ōƒ л℮śť℮ď сömmąπđ. !!! !!! !!! !!! !!! - • ₣õџиδ д ќ℮ŷвïηďϊňĝ ŧħàţ ωаѕ мίşšīήğ ǻ ŗеqΰįŕėð φăґάмēтêŕ νâļûé. Τнìѕ κėŷьîηďìήĝ ẃίļŀ ъĕ ìġπōŕėđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • ₣оůñð ā κėуьίπðîηĝ тħаŧ ẃáś мįѕśϊňģ д ѓ℮qûιŗзð рαřǻмęţεř νăľυë. Тнίѕ кεγьĩņđíņģ щìľļ вє įĝлóяеđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - • Ŧħ℮ ŝφéċĭƒίёđ "тђэmê" ώάѕ ňöŧ ƒőůŋð íи τћë łїšť øƒ тђемèş. Ţєmφôŗàřιℓÿ ƒàłłïńğ вд¢ĸ ťθ тĥє ð℮ƒªυľť νąľμэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + • Тћë ѕрèçїƒĭёď "τћ℮мé" ώαѕ ήøţ ƒόûπđ ΐл ťћê ℓíŝť σƒ ťнεmέѕ. Ŧěмрǿŕдѓΐļγ ƒдℓłίńģ вà¢ĸ τő ŧнě đέƒαùŀŧ νǻľΰê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="•"} This glyph is a bullet, used in a bulleted list. - Тнė "globals" ρґόφëґŧў ĩŝ ďéρгέċàţęð - γоμѓ şёťţїńğş mιģнť ŋ℮êđ úρđãťΐņĝ. !!! !!! !!! !!! !!! !!! !!! ! + Τђз "globals" φѓóрεѓţγ ιŝ δęρѓêсâŧėď - ýбųѓ ŝέтŧїпĝš мìġħţ ηзέď црđªтïйġ. !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"globals\""} @@ -267,635 +267,635 @@ {Locked}This is a FWLink, so it will be localized with the fwlink tool - ₣ǿř mőřё ілƒŏ, şęє τђΐѕ ẁэъ ρâĝē. !!! !!! !!! + ₣οř мòŕе îήƒθ, şéэ ţħìѕ ŵєь рąġé. !!! !!! !!! - ₣äïļēð το э×рåиδ ά ¢όmmäňð ώїτħ "iterateOn" ѕēτ. Ţħίѕ ¢óммăŋď ẁіľł ъě їġŋθřĕδ. !!! !!! !!! !!! !!! !!! !!! !! + ₣ăіľέđ ţö ехρåņď ǻ çǿммãηδ ẃìţĥ "iterateOn" şετ. Ťĥīš ćōmмåиď щΐℓĺ ве ĭģňóѓëđ. !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"iterateOn\""} - ₣őüиď å ςőммαňð ẃΐтħ āй îйνаłίð "colorScheme". Ťнΐŝ сǿmмαηδ щïĺℓ ъë їġñσґзδ. Мäķë ѕûґε ŧħâŧ щĥéñ ѕęтŧΐņğ ª "colorScheme", ţħě νªŀŭě мàτĉћ℮ś τђē "name" óƒ а ĉôĺоґ ŝçћěмэ ìй тħе "schemes" ŀįѕт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣óŭйď ã сóмmаńδ ẁϊţђ дή îⁿναļīð "colorScheme". Тћîŝ ĉǿмmαήδ ωïĺĺ вε íğлŏгëð. Мâķэ ѕϋŗê τĥāţ ẅћэй šěτťîņġ å "colorScheme", ţĥé νаļΰé мªŧçћєѕ ţħє "name" őƒ ă ¢ôℓôř ŝςђзмê įň тнε "schemes" ľïşŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"colorScheme\"","\"name\"","\"schemes\""} - ₣бůήđ á "splitPane" ςōmмǻńď ŵιťн áп īⁿνªļιď "size". Τђιš çόmmâηđ щíľľ ъę їĝπσŕĕð. Μāĸĕ ѕџřē τħě śϊżė ìŝ в℮ţщ℮éʼn 0 àήď 1, ê×ćłΰşįνё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣ŏΰπđ ā "splitPane" ¢ǿммäήđ ŵìтћ ăη îŋνąłιđ "size". Ţђíѕ ċǿммªпđ ŵīĺℓ ьė ìĝñōгёđ. Мăќέ šύгз τĥě śιźê įѕ ъèτшέëπ 0 äⁿð 1, εхčļûŝίνз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"splitPane\"","\"size\""} - ₣дĭļέď то рãřśě "startupActions". !!! !!! !!! + ₣âìľęď ťǿ рαяŝé "startupActions". !!! !!! !!! {Locked="\"startupActions\""} - ₣óŭлð мŭľťïφŀє ēπνїяöņмέпт νăŕΐâъłěŝ ẁїťħ ŧнз ŝãmе пąмë ĭл ðіƒƒ℮ѓёηŧ ċāşêş (ŀοшêř/úρρéř) - ŏπŀў őñє νăĺϋз ẃіłļ ьё џšėδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ₣öųñđ múℓтĭφļę èⁿνіŕθлмėňţ ναřίдъľêѕ ẅïτħ ťħз śăмė ήámе їή đїƒƒέřέηт čãşзś (ĺóщěг/ůрρеŕ) - óлℓŷ οиę νǻľµě ẁιľľ вё ųşέď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Âη ôрţίǿпαł сøмmªйð, щϊτђ ªгġύмėиτş, ŧõ ъε ŝрåẃʼnëð ïⁿ ţĥę ŋĕω ťάь óг рàňέ !!! !!! !!! !!! !!! !!! !!! + Åʼn ǿртíоñαŀ ċοmмäηď, ẁίŧћ ãŗĝūměʼnтş, ţô вέ šρªẃйèđ įņ ťђė πεẁ ŧав ŏŗ рªņε !!! !!! !!! !!! !!! !!! !!! - Мőνё ƒõćųŝ ťö àņôτнěґ τάь !!! !!! ! + Μòνê ƒöčµѕ ŧθ ãņøτнёř τåъ !!! !!! ! - Μоν℮ ƒǿ¢úś τó ťђэ пэ×ŧ ţǻь !!! !!! ! + Мõνè ƒòćüŝ τθ ťћé пĕ×т ťαъ !!! !!! ! - Áη àļιăş ƒöř ťĥè "focus-tab" şϋвςømmåлđ. !!! !!! !!! !!! + Дŋ äℓĭάş ƒоř τћę "focus-tab" šųъčǿммáñđ. !!! !!! !!! !!! {Locked="\"focus-tab\""} - Μǿνë ƒőçùѕ ţб ťĥэ ρґēνįθūѕ ťаь !!! !!! !!! + Μöνє ƒоćüŝ ŧó ţĥë рřěνĩομѕ тãь !!! !!! !!! - Моνë ƒŏ¢μš тħз ťàв àť ŧĥè ĝįνēň íʼnδë× !!! !!! !!! !! + Мονé ƒоćűŝ ťħέ τåь àτ ţĥé ĝįνèň іňďėж !!! !!! !!! !! - Μσνё ƒосцśέδ ρãиĕ τθ ťн℮ ţáв аť тнê ġίνëņ ιηđèж !!! !!! !!! !!! !! + Μόνě ƒôçџś℮ď φąπέ τŏ ţħĕ ŧäв ǻτ тћė ģΐνеņ ĭñðěх !!! !!! !!! !!! !! - Μõνė ƒōçμŝëδ рáήĕ ťó âπбŧђēґ ťåъ !!! !!! !!! + Μǿνê ƒõćυѕěð φâή℮ ťθ ǻлοτĥёŗ τąв !!! !!! !!! - Αņ ąŀίǻŝ ƒöѓ τћє "move-pane" ѕũъćôмmâʼnď. !!! !!! !!! !!! + Ăⁿ āℓíąš ƒбŕ тнĕ "move-pane" šúъćбммàⁿδ. !!! !!! !!! !!! {Locked="\"move-pane\""} - Şφē¢îƒу τĥé şĭźε ǻŝ ã рēŕсĕητâĝē θƒ τĥё рãŕëпŧ φªпз. Vάŀĩď νªŀύëş дгě вёτẅэзй (0,1), ê×¢łΰšįνэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ѕрёċϊƒÿ тђĕ ѕîžĕ αѕ ǻ ρēŕċêйτдģе őƒ ŧħě φǻґзйť φàņē. Vдℓίδ νǻŀùзş āŗε ьεтщ℮ёň (0,1), е×ςĺúŝινе. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Сřēάтέ д πзω ťâь !!! ! + Çґέăťė ã πėш τáв !!! ! - Ǻń άĺіαş ƒόŕ ţħє "new-tab" şűьčóмmαήđ. !!! !!! !!! !! + Ăи άℓîªŝ ƒōř τħë "new-tab" śμвċόmмãŋδ. !!! !!! !!! !! {Locked="\"new-tab\""} - Мóνе ƒöςύѕ тŏ ǻήòτĥёř φªʼnê !!! !!! ! + Μǿνé ƒосųŝ тô дπøţћєг рáņė !!! !!! ! - Ąи àľіâś ƒòг ţĥè "focus-pane" šûвčōмmäπď. !!! !!! !!! !!! + Áй áℓįãš ƒòŗ тђĕ "focus-pane" şυъĉöммàńđ. !!! !!! !!! !!! {Locked="\"focus-pane\""} - ₣òĉµś τħέ ρǻńέ ăŧ ťнё ģΐνеń īñđзх !!! !!! !!! + ₣ό¢űѕ тнê φàπĕ аţ тђè ğĭνėй ïŋðεж !!! !!! !!! - Øрēń ẃíτħ ŧħë ĝїνēń ρяōƒįľè. Àĉĉεрţѕ ěīţĥэґ ţĥє παm℮ óѓ ĢÙЇĎ òƒ ă φřòƒїļê !!! !!! !!! !!! !!! !!! !!! + Θφêп шĩτћ ťнě ģìνēή ρѓøƒìĺę. Âćĉеφťś ёĩτĥėŗ τђė йάмє øя ĠŮΪÐ õƒ а φŕóƒίℓè !!! !!! !!! !!! !!! !!! !!! - Ĉяéâт℮ α ñёẃ ѕρļίт рãηз !!! !!! + Ĉґéáŧе д ήэẅ ŝφĺĭτ рãňё !!! !!! - Ǻņ ãŀĩàş ƒôґ ţĥ℮ "split-pane" šџьςоmmâņδ. !!! !!! !!! !!! + Āл ªļïâś ƒбг ŧћê "split-pane" ŝύвćőммäňδ. !!! !!! !!! !!! {Locked="\"split-pane\""} - Ĉŕєăтз ťнё ⁿзẁ ρàʼnё àŝ а ћõѓϊžőⁿţªℓ ŝρłіт (тнįηк [-]) !!! !!! !!! !!! !!! + Ċŕéάťё ťћè ⁿеẁ рâлè åś ǻ ħôŕîžőņτąŀ śρļίτ (ŧнїπĸ [-]) !!! !!! !!! !!! !!! - Çгêåтз ťнė йèẃ рãиέ άŝ д νέґťίĉάļ šρĺĩť (τћїʼnķ [|]) !!! !!! !!! !!! !!! + Ċяěªтĕ ŧнέ ⁿèω ρаηе ªš ā νēѓτîčäŀ ѕрĺįť (тĥїπќ [|]) !!! !!! !!! !!! !!! - Ćřзãτ℮ тђę ʼnėẃ φâйέ ьý ďΰρℓíčäтïňģ ťĥė φяοƒíłє θƒ τђэ ƒòćцśеð ρąлέ !!! !!! !!! !!! !!! !!! ! + Ĉřεâте тħè пέẁ φаńè ъỳ ďцрłíçăţιηğ ťħе φгоƒíľз оƒ τĥě ƒőċūşëð рāиē !!! !!! !!! !!! !!! !!! ! - Õрëñ їń ţĥë ġìνєń đĭґĕĉťθŕý іⁿѕťεàð οƒ ţђę ρřǿƒĩĺę'š ŝєŧ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! + Óрεŋ ιŋ τћē ĝіνèή ðіґėсťσŗý ΐńѕţêªđ ôƒ тħ℮ рřоƒĭľė'ŝ şĕŧ "startingDirectory" !!! !!! !!! !!! !!! !!! !!! ! {Locked="\"startingDirectory\""} - Ǿφěⁿ ŧĥē τεŗмīņâļ щíţĥ ťн℮ φřőνīðĕď ţїţłê íйšŧęāď øƒ тнë ρŕőƒιļέ'š ѕéŧ "title" !!! !!! !!! !!! !!! !!! !!! !! + Òрèи ŧћ℮ тεгміņдľ шįтђ τћз рŕσνϊðэđ ţϊťľё їņšτèāď οƒ ŧħз рѓοƒïℓε'š śēт "title" !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"title\""} - Φрєл ŧĥэ тãъ ŵíŧħ ŧћё ŝρэ¢ìƒϊэδ ¢őłŏř, įи #ѓŗģġьъ ƒöŗmäŧ !!! !!! !!! !!! !!! ! + Фрєŋ τћę ťāь ẃĭŧĥ ŧĥē ѕρεçíƒĩĕď ċŏĺőŗ, ίʼn #řґģģьь ƒöřмàŧ !!! !!! !!! !!! !!! ! - Òφéл ŧђέ ţàь ẁíţħ τªьŢітŀè ǿνёŕřĭďιŋģ ďēƒάџľţ τīťℓé åňđ śûφрŕėşšΐⁿĝ ŧįŧĺє ĉћαʼnģĕ мéśšáġēś ƒґбm ťħë арρℓĩςāтїσⁿ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Όрзи тћê тäъ ŵîтн ŧãвŦìтŀέ øνėřѓĩδіиĝ δęƒåυĺŧ тīτłê ǻйď ѕűррŕ℮ѕśìήĝ τĭťℓé ċћаπġё mεśšàğзš ƒŗøм тћê ªрρĺĩčªτĭóņ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"tabTitle\""} - Ĩηнěѓїт тħè τèŗмιŋāļ'ş óωʼn εήνīřōπméлť νâґįâвĺέş ẅĥēʼn ćřеàţιήġ ţнэ йеω тåь òř ρǻⁿе, řãťħĕѓ τђåл ¢ŕéάťĩήġ α ƒгёşн злνΐřõʼnmèⁿτ ъłōçќ. Ťћĭš đéƒąúłŧѕ тö šēт ẁħéή α "command" ΐŝ ρаśšéđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + İηĥегïт ţĥě ťεřmĭʼnǻł'ŝ õώп εņνĩŕόňмěñť νāřĭáьŀĕś ώнēл ĉгėǻтΐʼnġ ţħз πęш ţáь ŏŕ ρäη℮, ŗãŧħёř ťĥªή ¢řēäţіηĝ ǻ ƒŗėśћ èήνιгőŋм℮иť ьļο¢к. Тħїš ďęƒαüłŧŝ τõ ѕёţ ẅħēň ą "command" îş ρãśŝéđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="\"command\""} - Όφέŋ τĥē ŧаъ шĭтĥ τĥë ѕφęсìƒìέď čõŀόŕ śċћêmë, ìńѕţĕαđ όƒ ţћε φґöƒιłě'š şĕť "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! + Øρéη ŧĥē ŧаь щïţђ ţнё ѕрě¢ίƒĩеð ¢θℓôя şĉнем℮, īπѕτêåδ òƒ τĥё φřőƒĭľе'ŝ ѕ℮ť "colorScheme" !!! !!! !!! !!! !!! !!! !!! !!! !! {Locked="\"colorScheme\""} - Ďìśφłäу ţђê аφφŀīċâŧĩοπ νεŗśіőŋ !!! !!! !!! + Ďìśрℓàÿ тћē åφφłîςдťίøп νēřšįóń !!! !!! !!! - ₤ąűņčħ ťĥĕ ẁіπðǿẃ måхíмïźĕď !!! !!! !! + Ļáūńçн тћё ẅïиďоŵ мåжімĩźęð !!! !!! !! - Ĺάцńĉђ ţђє ŵïпđôщ іⁿ ƒüŀłšċѓéëň мσđе !!! !!! !!! ! + £âµńĉн тнė щίπđощ їʼn ƒûℓŀşċŕėēη мøđè !!! !!! !!! ! - Мøνέ ƒŏčΰş ŧθ ŧђέ åδĵаćёʼnт рάлë ïη ţĥě ŝφėċΐƒίεď đіґэсŧįóń !!! !!! !!! !!! !!! !! + Мονë ƒόçцŝ τô ţнε дďјαçéʼnţ φáπέ ίņ τнë ѕрéςĩƒїĕδ δіŗéćťïőп !!! !!! !!! !!! !!! !! - Āη ǻľїαš ƒöř τħê "move-focus" şůвčøмmåⁿđ. !!! !!! !!! !!! + Åп âℓιāś ƒóř ŧђз "move-focus" šůъçόмmàпð. !!! !!! !!! !!! {Locked="\"move-focus\""} - Ťђê ðîгèсτîбň ŧǿ мøνё ƒöćμś īπ !!! !!! !!! + Тнê đιřèсţïöп ţό мøνĕ ƒŏčúś ìή !!! !!! !!! - Ŝẅáφ тĥê ƒθĉŭѕєđ рâńé ŵîτђ ťћэ ªďĵдćėʼnτ ρдŋз ιņ ţђε ѕρęċϊƒίèđ δíŗеćτīóŋ !!! !!! !!! !!! !!! !!! !!! + Šшдφ ťĥė ƒоčùšěď рдйê ŵīťђ ŧнё ãđĵă¢èņτ φåиé íŋ ťĥē šρêςīƒĭзδ đìѓз¢ţįőñ !!! !!! !!! !!! !!! !!! !!! - Τĥë đΐґэçτїøŋ ŧθ mòνє ťħĕ ƒŏςΰşеđ рąⁿě ίň !!! !!! !!! !!! + Тнě διяèċτîõη ţό mōνе ťћë ƒòčϋš℮δ рαπ℮ īп !!! !!! !!! !!! - Ļǻŭйĉђ τнę ẁιʼnďõẃ ιи ƒôčųş мøδė !!! !!! !!! + Ľãūлçĥ тħέ щíиδσώ ìй ƒô¢ũš мôďз !!! !!! !!! - Тнîś раřǻмеτêř ĭѕ άň ϊлтëяņãŀ ĩmрļєméⁿţаţīøи δěτąîĺ аηđ šђоцĺð лŏτ ъě ūşęď. !!! !!! !!! !!! !!! !!! !!! ! + Ŧћїѕ ρąяáмėŧєѓ ĭş āη їňтεŕήåŀ íмφĺêmёņťдţïòπ ďёŧåĩļ дηð šнŏųļδ ñōţ ьë ųšėð. !!! !!! !!! !!! !!! !!! !!! ! - Ѕρέςïƒγ ä ŧёяmįʼnāļ ẁϊņδōẅ ţб řцή ŧн℮ ġïνëŋ ċŏмmàπďľíňε ίń. "0" ãŀẃăуş ŕєƒεґş ţô тнé ¢ūřřзňτ щįήδοŵ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Šрêςĩƒγ а ťĕгmìŋаł ẁϊпðõω τσ ґύŋ τнз ģίνĕи ĉömмαпđľįʼnе їй. "0" āℓώąŷš řеƒěŗş ťσ ŧħė çŭґřęπť ẁįʼnđǿω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Śρēсíƒý ţнę φóśїťіòⁿ ƒоŕ τħē τėřmìпàĺ, îπ "х,ý" ƒøŕmăţ. !!! !!! !!! !!! !!! ! + Šрëĉίƒў тћε рόŝíŧιòή ƒōя тнě тėřmįηдŀ, ϊп "х,ÿ" ƒθřмάŧ. !!! !!! !!! !!! !!! ! - Ѕрëсιƒŷ τнë ήύmьėř óƒ ςθĺùmиš αήď ґσωş ƒòř ťĥз τèґmіŋąľ, ĩŋ "č,ŗ" ƒöŗмªτ. !!! !!! !!! !!! !!! !!! !!! + Ѕφёĉĩƒỳ тнэ ņµmъ℮ґ øƒ ςòļůmйś ăŋð гøшş ƒöŕ тнε τєѓмϊⁿãŀ, ĩʼn "ċ,ř" ƒθяmãţ. !!! !!! !!! !!! !!! !!! !!! - Рŗзšş ţђ℮ ъџŧţοи тô òрēń ã ήėщ ŧεґmīñãŀ ţãъ ẅΐтн γōμř đ℮ƒâųļţ φŗōƒĩĺє. Ŏρ℮ή ŧнè ƒĺýöũţ ţô ŝёļĕċτ ẁĥī¢ħ ρґóƒίℓε ýοŭ ẁªñŧ тó ǿφзп. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Рґзѕś тђз вůťŧôй ŧǿ ǿφеη ā ʼnéẃ ţєѓmîńąŀ ťâв ωϊŧђ ýθůř đęƒăμℓţ ρŗôƒїĺε. Óρĕп τћё ƒłýбцť ţø śεŀèςŧ шĥïčĥ φřŏƒίļê ўσμ ẁάлτ ţσ όрεй. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Ņ℮ω Тǻв !! + Ňèώ Ŧäъ !! - Òρêň â ήęώ ţäь !!! ! + Όрèπ ª йěŵ ταв !!! ! - Λľť+Čℓίčк τö šρŀіт ŧћз ĉűŗяęñт ẅілðοẁ !!! !!! !!! !! + Ǻℓт+Сłĩċк тö šрŀîţ ţћë сцřѓèήŧ ẅїпðбω !!! !!! !!! !! - Şнįƒт+Čŀĩćκ ťò θφэη а πèẁ ώίŋďōẅ !!! !!! !!! + Śħîƒť+Çľιćĸ тō όрзⁿ ª йзώ ẁίπðôш !!! !!! !!! - Ĉτřł+Ċľìċκ ţò θр℮й ăś àδmίʼnìşтгáтöг !!! !!! !!! ! + €ţŕł+Čℓїčķ τõ ορêņ αŝ аðmìñĩѕτŕăţòг !!! !!! !!! ! - Čļőšë ! + Ćℓőŝз ! - €ľőš℮ ! + Ĉļøşз ! - Çļоšě ! + Ĉļόѕě ! - Μå×ίмīžĕ !! + Μджįmïźэ !! - Μĩńιміžë !! + Μïиιmіžё !! - Μĩηĭмĩžе !! + Μîⁿĩmĩż℮ !! - Μîņïmīźè !! + Мĩиîmĩżє !! - Âъθŭт ! + Åвōύţ ! - Ŝέήδ ₣ěеđвαсķ !!! + Ѕеηð ₣ę℮đвäçк !!! - ΘК + ΦΚ - Vэŗşισή: !! + Véяšîõп: !! This is the heading for a version number label - Ġėŧτįлġ Śŧăѓтĕð !!! ! + Ġеťтΐñĝ Ѕτдŗτęď !!! ! A hyperlink name for a guide on how to get started using Terminal - Šоúѓс℮ Ĉóðĕ !!! + Ѕοџŗсė Çŏđе !!! A hyperlink name for the Terminal's documentation - Đóċűm℮ņťâтĭθñ !!! + Đŏĉµмěⁿтатïõñ !!! A hyperlink name for user documentation - Ѓęłєąśε Ņθтėš !!! + Ŗеľ℮àşε Ŋòτéš !!! A hyperlink name for the Terminal's release notes - Ρŕΐναçγ Ρõℓĭčý !!! ! + Ρґїνãсÿ Рöĺĩςỳ !!! ! A hyperlink name for the Terminal's privacy policy - Τћїřđ-Ράŕтŷ Ńôţι¢ęś !!! !!! + Ţћĩřð-Ρářŧγ ∏οŧīĉęŝ !!! !!! A hyperlink name for the Terminal's third-party notices - €áйċêł ! + Ċдйĉέł ! - Сľŏѕè āłℓ !!! + €ļőşε áļľ !!! - Đò ўθü ẁаⁿŧ ŧб ćŀǿѕё āľℓ ωіⁿđőщš? !!! !!! !!! + Ďõ γбű ẁāŋţ ťó ςℓσśĕ äℓℓ шîйđбẁś? !!! !!! !!! - Ćдлčєľ ! + Ćăʼnċęℓ ! - Çŀóśė άłł !!! + Ćļõѕέ аłℓ !!! - Ðό ỳőΰ ẃàŋτ τõ čŀőśё åĺļ ťāъś? !!! !!! !!! + Đσ ŷőū шдиŧ тò čļòŝз αŀľ ţâвŝ? !!! !!! !!! - Сάήćêł ! + Čǻñčėŀ ! - Čľόşë дйγŵаỳ !!! + Сłŏѕε άñÿẃåγ !!! - Ẅãŗήĭйģ !! + Ẁαгйïņğ !! - Υőū ąŗę āвõųт τô ĉℓôśĕ ā ŕęăδ-õήļγ ŧēгмĩņął. Ďø уôü ŵΐѕн ŧö čöʼnţìйџè? !!! !!! !!! !!! !!! !!! !!! + Ϋôů ářє дъоϋť ŧо ćľōśέ ª гęаð-öйĺу тêřmĩńäĺ. Đǿ ỳøü ωîśђ ŧő ćőʼnţїñϋέ? !!! !!! !!! !!! !!! !!! !!! - Ċдņçëℓ ! + Сâήçеŀ ! - Ўòџ āřε àьоŭт ťô φªşтĕ τêхť τĥǻŧ ĩѕ ļоñĝёŗ ťĥдⁿ 5 Ќΐß. Ðő γöύ ẅιšђ ţø ċòπτīŋϋė? !!! !!! !!! !!! !!! !!! !!! !!! + Ўōµ ąřε ăвőüť ţò ρãѕŧĕ ťê×ŧ тħãт ïš ľôʼnğзŗ ŧђąи 5 ЌΐБ. Đθ γŏΰ ẅĩşħ τò ςõиŧĭňūê? !!! !!! !!! !!! !!! !!! !!! !!! - Ρåŝтę ąηýẅªγ !!! + Ρâşτз ăπуẅаý !!! - Ẁǻґñīņĝ !! + Ẃâгηïņğ !! - Саńčеļ ! + Ćªήĉěł ! - Ϋσų ąгε ąвőûτ τо ρдѕťз τзхŧ ŧђàţ ¢õитαίņŝ mύĺтïρľė ĺĭñêѕ. ̓ ÿσΰ рάѕţè τнϊş ŧèжŧ ìйŧõ ÿοŭґ şнéľĺ, ΐт mаý řëśϋℓŧ îň ŧħě ϋñĕхφ℮¢ţзð ε×эĉûτιбņ οƒ čømмãпðś. Ðō ÿôû ẁίѕĥ ţó сöñţïñûĕ? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ϋбů áяē авôцţ τŏ ρªѕŧê ťĕхţ ťĥâť ĉοлŧäїлš mųŀťїрŀē ĺΐиέš. Ĭƒ ýŏû рαśτё ťђìš ť℮хŧ îήŧø γõũŗ ѕћêℓļ, íτ mду ŗéśμļт įŋ ţħ℮ цпęхρ℮çťêď з×эćúтϊŏⁿ õƒ ςōмmãⁿðş. Đο убŭ ẃīѕн τõ ċôʼnŧïñûε? !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Раşтё ǻлýщáỳ !!! + Ρªšţє аńŷẁàŷ !!! - Ẃăřиїńğ !! + Щāŕñιπĝ !! - Ţỳрε а ċθммªňď ŋămэ... !!! !!! + Ťýφě á ĉθmmāņđ йăмē... !!! !!! - Ŋô măŧĉђįñġ ćõммâиďš !!! !!! + Ŋό маŧ¢ĥΐŋğ сóмmăлđš !!! !!! - Аĉŧíòи šêдŗćħ möđε !!! !! + ∆ćŧìøп şєāяćħ мóδє !!! !! This text will be read aloud using assistive technologies when the command palette switches into action (command search) mode. - Ŧаь ţìτłē мöďё !!! ! + Тàв ťїтłé môðэ !!! ! This text will be read aloud using assistive technologies when the command palette switches into a mode that filters tab names. - Çоммаиđ-łĭпê mσďę !!! !! + Çοmмăπď-ĺιиé мõđэ !!! !! This text will be read aloud using assistive technologies when the command palette switches into the raw commandline parsing mode. - Мõґē бρтīøńŝ ƒõѓ "{}" !!! !!! + Μояē öφтίóπś ƒőř "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Ĕхёсûţîπġ ćõмmáⁿδ ℓīňë ŵìℓĺ ĩⁿνōκè ŧħę ƒôłľόшîпğ ċømmåņðş: !!! !!! !!! !!! !!! !! + Ėжėсùţīпğ ¢ōmмªñð ĺїπĕ ẁíĺł ĭиνокė тħе ƒöĺℓŏщîпġ çõmмāⁿďѕ: !!! !!! !!! !!! !!! !! Will be followed by a list of strings describing parsed commands - ₣дìĺέď ρǻяšΐňģ čǿмmдиď ľïñέ: !!! !!! !! + ₣āіľ℮ď рàгśīпģ ¢бммäⁿδ ĺīñè: !!! !!! !! - €οmмдиδ Ρãℓėŧţė !!! ! + Ćσmmăηδ Рάŀĕтţ℮ !!! ! - Ŧав Ѕώįťċнзя !!! + Τăь Ѕωîťςћêг !!! - Ţýρе ǻ ťåв ήáмє... !!! !! + Ţýρё ă тăъ пâmě... !!! !! - Ñό màŧĉĥĩпģ тåв лąмę !!! !!! + Ńô мατćнìņģ ţдъ пªмĕ !!! !!! - Ęⁿţέŕ а wt ςōммǻŋđłïηé ťò гůņ !!! !!! !!! + Èηŧĕґ ǻ wt ċθmмаʼnđļïʼnė тο ŕūʼn !!! !!! !!! {Locked="wt"} - Μŏѓê øφţįőńş ƒóř "{}" !!! !!! + Μōŗè σφŧïθлš ƒόŕ "{}" !!! !!! This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options. - Тÿрє ā čомmäʼnð ńаmэ... !!! !!! + Ťÿφė å ċοmмªйđ ηаmє... !!! !!! - Иô мǻţčђїńġ ćõmmåŋđŝ !!! !!! + Ņô mάťςћίņĝ ċоmмάņďš !!! !!! - Ѕцğĝēѕтĩóⁿŝ mεйű !!! ! + Šύğġěşţįσпś м℮ʼnμ !!! ! - Мòřε ōрťïóⁿš !!! + Мόѓė ōφťіŏⁿś !!! - Ŝűģĝзšŧïõлѕ ƒǿûňδ: {0} !!! !!! + Śΰġģ℮šτϊθñѕ ƒöüⁿδ: {0} !!! !!! {0} will be replaced with a number. - Ċґįmśõņ !! + €яïmşǿń !! - Şţêēļ Вľΰë !!! + Ѕťéêℓ βľυэ !!! - Мěďīųм Ŝęά Ğřĕ℮ņ !!! ! + Μёδιųm Ѕεд Ğґзēη !!! ! - Ďåґķ Φѓăŋġé !!! + Ďäŕќ Õřäňğè !!! - Мēðіΰm Vϊόŀěť Яєď !!! !! + Мęðϊům Vĭõľзт Ѓεð !!! !! - Ðοďĝέґ βľυę !!! + Ðôđğēг Βĺϋę !!! - Ŀімě Ġŕеéň !!! + Ĺіmз Ĝґéĕи !!! - Ŷέļłőẃ ! + ¥ęℓłοẃ ! - Вŀûє Vĩöĺеť !!! + Ьľùе Vΐбŀэτ !!! - Šĺàŧĕ Бłůέ !!! + Şŀаťę Βĺûė !!! - Ŀíмё ! + £ϊмз ! - Ťдπ + Ťåπ - Мâġėňтα !! + Μàğэⁿτα !! - €ÿàй ! + Ċуåň ! - Śκŷ ßłúë !! + Šќŷ ßľϋ℮ !! - Đąřķ Ģґăý !!! + Ðàŗκ Ğѓáγ !!! - Τĥīś łīлĸ ĭś īⁿνдļîď: !!! !!! + Τћїš ŀĩňк іѕ įňνдŀíď: !!! !!! - Ťђιš ļĭπк ŧγρę ιş ςύґŗёηťľỳ ŋôт śũрρόятзđ: !!! !!! !!! !!! + Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! - €дⁿĉεℓ ! + Сąñс℮ł ! - Ŝêттΐʼnġş !! + Śëţťĩпğś !! - Щз сőυĺδ йότ шříтз тθ ўоџŕ šėτţįŋĝš ƒïĺε. €ħесќ ţђε рëѓmĩšşîбñŝ òη ťћăţ ƒíļе ţό ěήѕüяē тĥāт ťħе гεάđ-øñℓý ƒℓªğ ϊś ņōт ѕзτ āиđ ŧнàт ẅřіţê áςčεѕş їѕ ĝѓăήτєđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ẃε ςőűℓđ пòт ωяΐτë ŧô γôύг šěťтïπĝŝ ƒīłě. Çђêĉĸ ţђэ φеřмìşŝΐóпѕ õл ťђàτ ƒĩŀε ţб έπšμґé ţђäť тћê ѓέάδ-õлļỳ ƒŀãġ ΐš ʼnοт ŝėт ǻпď ţнăт ẅѓīţĕ ăсċéšş ĩş ģŗдŋτëđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Βãčκ ! + Бдçķ ! - βāċķ ! + Ьãςк ! - ФК + ΘК - Ðéвµģ ! + Ďêъŭģ ! - Ēŕřθř ! + ∑ŕřοґ ! - Ϊήƒόѓмâŧїōл !!! + Īπƒóřмąτιŏʼn !!! - Ẅάѓʼnιⁿğ !! + Шãřñїņģ !! - Сłįръοåřđ čôπŧέʼnŧš (ρŗзνįēŵ): !!! !!! !!! + €ℓірьоåŗδ ĉóńťęňтѕ (φѓενĭεẃ): !!! !!! !!! - Μōгę øрţïбʼnś !!! + Мθґе бφŧīбπš !!! - Шϊиđŏώ ! + Ẅίńďöщ ! This is displayed as a label for a number, like "Window: 10" - ûйηãмзđ ŵĩņðőώ !!! ! + υʼnňàмěđ ẅіπďōẅ !!! ! text used to identify when a window hasn't been assigned a name by the user - Ėⁿŧęѓ ä ňèŵ ñāмė: !!! !! + Єиŧ℮ŗ ă ñéẁ йάmё: !!! !! - ΦΚ + ÒК - Čǻʼnςёŀ ! + Сªⁿċĕĺ ! - ₣åϊłеď ťō ŕэñάмę ẅϊпďöẁ !!! !!! + ₣âιļĕđ ŧо ѓëňąмë щϊπδǿω !!! !!! - Äлôţħêř ωīńδõẃ ẁίтћ τђаŧ ńªмě áłŗěаďŷ ё×ĩşťѕ !!! !!! !!! !!! ! + Άņόтĥěŗ ẃїлðοω ẁιťĥ ťћаť пâmě ăļřéàδў ёжìśŧѕ !!! !!! !!! !!! ! - Μā×ΐmĭźё !! + Μą×ìmϊżé !! - Г℮šţòґě Ďôẁл !!! + Ŕèšŧòяё Ðǿẃи !!! - Ćőmmªňď Ρăľёŧţε !!! ! + Ċòмmāńδ Рªľėτťë !!! ! - ₣õĉΰѕ Ŧέгмîŋäľ !!! ! + ₣ôćűŝ Ţеґмĭйâŀ !!! ! This is displayed as a label for the context menu item that focuses the terminal. - Ẅϊʼnďθẁş !! + Ẃϊйδοŵš !! This is displayed as a label for the context menu item that holds the submenu of available windows. - Óφêй â лέώ ŧâв ĭʼn ğΐνéņ şţăřτíήġ ďįяе¢тоѓγ !!! !!! !!! !!! + Фφĕń ª пёẅ ţâь ίи ğīνęņ ѕŧāятĩлğ δįŗęćтŏяγ !!! !!! !!! !!! - Фφēή ä ñēώ щїήďθẅ ώϊτħ ġіνėй ѕŧαŕтїйğ đïřéĉтõŗý !!! !!! !!! !!! !! + Οφэй ä ŋёώ ẅįńðбώ ẁįτђ ĝĩνēη šτáгтϊŋĝ ðĭřěçŧøгγ !!! !!! !!! !!! !! - Ŝрļîţ ťнε ωΐήđõẃ άήδ ѕтăřŧ ĭπ ġіνэŋ δΐяëĉŧоŗу !!! !!! !!! !!! ! + Ŝρℓΐŧ ŧнė ẁίňďõŵ άпδ ŝţâґţ ίń ģįνëʼn δϊгέ¢ŧøяў !!! !!! !!! !!! ! - Е×ρōґт Τ℮хŧ !!! + Ė×φōŗŧ Ţєхŧ !!! - ₣ăīļēđ ţǿ έжρõřŧ τëřмïňãĺ ćóñτëητ !!! !!! !!! + ₣ăìľεď ťθ эхроґт ţеґmίñдļ ¢ōйт℮лť !!! !!! !!! - Şυčć℮ѕŝƒϋĺŀŷ ε×ρøяťзđ ŧэґмϊйáļ ćõņťεлţ !!! !!! !!! !! + Ŝūĉčєšśƒυłłγ ĕ×φòŗтэð тēгмïʼnãℓ ĉοʼnťëⁿţ !!! !!! !!! !! - ₣їŋð ! + ₣ìпđ ! - Рľάïʼn Τėхт !!! + Ρĺáīň Тěхт !!! - Тёѓmĩŋáŧΐôń ъεђàνĭöґ ĉáл ьέ ćôлƒιģџřěď ĩń ąďνάиĉëđ φяóƒίłĕ ŝеţτϊпĝś. !!! !!! !!! !!! !!! !!! !! + Ťéямїлâŧîόň ь℮ћäνįőř čªή вĕ ċοñƒĩġџřèδ įŋ ăδνåл¢êð ряòƒιļє şėŧтіиĝś. !!! !!! !!! !!! !!! !!! !! - Ðόπ'т śħöω ãġāїй !!! ! + Ďόń'ţ šħόω ãĝάϊл !!! ! - Ţђīѕ Тёřмíņàĺ щίńďόщ ĩѕ яџиńίйğ дś Δδmϊⁿ !!! !!! !!! !!! + Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ Ãðmĭⁿ !!! !!! !!! !!! - Śυģģєşτîòиŝ ƒοųņδ: {0} !!! !!! + Ѕũğġεšтįóпş ƒōцʼnđ: {0} !!! !!! {0} will be replaced with a number. - Ċĥēćкîñģ ƒθř ūρďάţēŝ... !!! !!! + Çђęċќїŋğ ƒσř ũρδàτέѕ... !!! !!! - Ăŋ űрδάтē ΐś ãνāїļâвł℮. !!! !!! + Δʼn μρđаŧè ĭś ăνăϊłªвľė. !!! !!! - Ĭńšτдℓℓ пøш !!! + Ϊŋśţāŀŀ πбẁ !!! - Τħē "newTabMenu" ƒĩêłď сöήťåïŋş mōřé ŧĥдл оņě êηтřγ őƒ ţỳφë "remainingProfiles". Õňľỳ ŧнė ƒîґšŧ οήз ώìłļ в℮ čσņšїďèгеð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ŧђê "newTabMenu" ƒιêŀď čσñţąϊйѕ mōѓé ťђǻñ öŋė ёňτŕý őƒ τýρê "ґёмãīліηģРяöƒïľєś". Φпĺў τђё ƒΐѓšτ όñè ώїłł ьè ĉőлѕìδēяёδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="newTabMenu"} {Locked="remainingProfiles"} - Ţнé "__content" φѓορεяτў ĭѕ řêŝęѓνеď ƒǿř įйтзѓйäļ űşе !!! !!! !!! !!! !!! + Ťђέ "__content" φŕόφέŕτγ íś řêšзřνеð ƒσř ійтėŕňåℓ ΰşè !!! !!! !!! !!! !!! {Locked="__content"} - Ωρēп ǻ ďîаľôĝ сōňŧдįńΐņģ ρгόďϋčţ ĩήƒбŗмâтîõń !!! !!! !!! !!! ! + Фφèη а ðїаļθĝ ćбⁿťáĭηΐñģ ρřσđůčŧ ілƒǿřmãтĩöή !!! !!! !!! !!! ! - Öφέη ťħē ĉôℓôг ρįςќēґ ŧǿ ςћóőšе ţћє ċόłöŕ όƒ ţĥįŝ ťãь !!! !!! !!! !!! !!! + Ωφĕй ŧнė ¢óŀοŕ φĩċĸęř τσ ¢ђőōѕė ťħê сõľòŗ бƒ ŧħįѕ τªь !!! !!! !!! !!! !!! - Óφεʼn ŧħе ςóмmǻηð φąŀëŧτε !!! !!! ! + Øρĕл ţће сǿmмåήđ ρãĺέτŧě !!! !!! ! - Öρěń α ηёω ţâь ųśìπğ тћė áċτíνĕ φŗôƒìĺе ιŋ тћé çΰгřєŋŧ đířéċťσѓÿ !!! !!! !!! !!! !!! !!! ! + Óρέл â ʼnзш τàв ũśĭлġ ŧħё ąċŧîνê ρŕøƒΐĺé ĩи ţħě çúггзηт đĩřзçţøřý !!! !!! !!! !!! !!! !!! ! - Έ×ρøѓť τħέ čôлтзⁿťѕ õƒ ţћë τехť вцƒƒêя įήţǿ â ťέжţ ƒīĺє !!! !!! !!! !!! !!! ! + Ēхφõґт τĥе сǿήŧĕиŧѕ ōƒ τђė ťε×τ ъũƒƒεř ĩήŧо α ţéם ƒιļê !!! !!! !!! !!! !!! ! - Øрëй тђê šěāřćђ đïªłôģ !!! !!! + Φρéņ ŧнĕ şęâřςћ ðíάŀŏģ !!! !!! - Γёńāмę ţħιѕ тáъ !!! ! + Ŗėлαмē тħíŝ тåв !!! ! - Θрėń ţћē ѕèŧţΐŋğŝ рãģĕ !!! !!! + Òφĕņ тĥе šэτŧīлğš ρдģė !!! !!! - Ŏρеи ǻ лěẁ рăηë űŝĭňģ ťћэ ªсťįνέ ρгбƒίļ℮ ιň ťђ℮ čŭѓŕėйŧ ďįѓęčťōґу !!! !!! !!! !!! !!! !!! ! + Ωрėŋ д πėẁ φâиě űѕīⁿģ тĥэ ã¢τίνë рřŏƒίĺê îŋ ţћэ çūŗгěηт ďíř℮ĉŧõгỳ !!! !!! !!! !!! !!! !!! ! - Ĉŀôśё àłł τáъś ťǿ ŧĥє ŕįĝнт ŏƒ ţĥìš ťâь !!! !!! !!! !!! + Сłǿśе ǻĺľ τăвś ţô ŧн℮ яîĝħť őƒ τђїş тάв !!! !!! !!! !!! - Ćŀǿŝē âłĺ ŧǻъş ë×ćзρţ ťђïš ŧăь !!! !!! !!! + €ļόѕĕ ªŀŀ ŧáвѕ έ×ćèρт ŧнìş ŧåв !!! !!! !!! - Çℓôѕé τнĭš τăь !!! ! + Ĉłоśэ ťĥìŝ ţªъ !!! ! - Έmрту... !! + Ёмφţγ... !! - €ŀοşē Ρǻйė !!! + Ĉĺοŝе Ρаиę !!! - €ℓöśė ţħэ âćτīνз ρªπę ϊƒ mυŀтïрℓэ рάñęş ǻřě φґĕŝëηţ !!! !!! !!! !!! !!! + Çĺόś℮ τнĕ ă¢τίν℮ рáлĕ ιƒ mϋŀţїрĺë φàńęś άŗє рřęšеńт !!! !!! !!! !!! !!! - Γĕşęŧ ţåъ ċθłõŗ !!! ! + Ґέšεт τăъ ċǿℓőґ !!! ! Text used to identify the reset button - Μøνέ Ťãъ ŧø Ņ℮ẃ Ŵїŋđóш !!! !!! + Мόνз Ţǻь ŧö П℮ω Щĭŋδōώ !!! !!! - Μονèѕ τäь το д пěẃ шϊлδǿẃ !!! !!! ! + Мøνëŝ ŧªъ ŧǿ ã пεẃ шίŋđоẁ !!! !!! ! - Ŗμń ąś Áđмįпįśŧѓăτőř !!! !!! + Ŕμŋ ąś Āďmįиíšťґąţőя !!! !!! This text is displayed on context menu for profile entries in add new tab button. - ∆çţіνė ρªňè möνéδ ťõ "{0}" ťªъ !!! !!! !!! + ∆çťíνĕ рåⁿэ мôνеð ťб "{0}" ţав !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. - "{0}" тăъ мǿνèδ тб "{1}" ώілδòẁ !!! !!! !!! + "{0}" ťāъ мōνęđ τŏ "{1}" шΐπδŏẅ !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the name of the window the tab was moved to. - "{0}" τäь môνėđ ťö ηęω ωĩⁿđοш !!! !!! !!! + "{0}" тąь mǿνєđ ŧσ ήèώ ẅĩŋďøẃ !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. - "{0}" ťáь мòνέð τό ρôšìťϊǿñ "{1}" !!! !!! !!! + "{0}" ŧαв мονĕđ το φóŝїŧíŏñ "{1}" !!! !!! !!! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful tab movement. {0} is the name of the tab. {1} is the new tab index in the tab row. - Δсτîνę ρдηё мσνëđ ŧǿ "{0}" ŧâь ĩʼn "{1}" щΐńδоω !!! !!! !!! !!! ! + Α¢ťϊνë рãņє môνέď τō "{0}" ţâъ ĭή "{1}" ẃĩńδθш !!! !!! !!! !!! ! {Locked="{0}"}{Locked="{1}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the tab the pane was moved to. {1} is the name of the window the pane was moved to. Replaced in 1.19 by TerminalPage_PaneMovedAnnouncement_ExistingWindow2 - Αĉтīνĕ рάňè мбνéδ тŏ "{0}" ŵιⁿđŏω !!! !!! !!! + Λςťìνє рáиė mόνéð ťб "{0}" ŵîńđθω !!! !!! !!! {Locked="{0}"}This text is read out by screen readers upon a successful pane movement. {0} is the name of the window the pane was moved to. - Δċŧίνë рăпε möν℮ď ţό ή℮ẁ ẃίⁿδõŵ !!! !!! !!! + Αĉťįνέ ρªņз mσνёđ τǿ ήёẃ ẃîпďǿω !!! !!! !!! This text is read out by screen readers upon a successful pane movement to a new window. - ∆ĉŧïνε φâй℮ мσνěð ŧő пèẃ τдв !!! !!! !! + Дсţϊνё φαŋє mбν℮ð ťŏ ηеẁ ταв !!! !!! !! This text is read out by screen readers upon a successful pane movement to a new tab within the existing window. - Іƒ šεт, ťĥē ċōмmáиð ŵіĺℓ ъė áφρėηďéδ тō ťћĕ ρřσƒĩℓз'ş ďēƒªцľŧ çõммâʼnđ їⁿŝťèàď σƒ яёрľάĉіʼnĝ іт. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ĩƒ šęţ, ŧнĕ ¢ömmдлδ ŵîĺł ьέ åφрєйδĕđ τσ ŧђė рřŏƒїłє'ş đзƒªūľţ ¢οmмăńδ іñѕţέáđ øƒ ѓēρļąċĭлĝ їţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Ѓёѕťąřτ Čόņńěċтīōņ !!! !! + Γēѕŧâяŧ Ĉǿńńēčťїöл !!! !! - Гєšťάřŧ ťђé ǻсťїνę рдйε ςσлήеςťїõπ !!! !!! !!! ! + Γėşťáгţ ŧħ℮ ãčтĩνέ ρăйё сǿηńëςтιóņ !!! !!! !!! ! \ No newline at end of file diff --git a/src/cascadia/TerminalConnection/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalConnection/Resources/qps-ploc/Resources.resw index 3eff4e0a615..53e853a925f 100644 --- a/src/cascadia/TerminalConnection/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalConnection/Resources/qps-ploc/Resources.resw @@ -118,81 +118,81 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Τђîš çόδέ ẅĩℓľ ехρĭяę ϊи 15 mїήμτėš. !!! !!! !!! ! + Ŧнιŝ ςøδё ẅіℓŀ зхρîґέ íи 15 mїπųŧęś. !!! !!! !!! ! - Рŀзаѕ℮ éñтзя ŧħέ ďеšΐř℮ð теńąпŧ ⁿμmвêř. !!! !!! !!! !!! + Рĺέåŝē еňţéŗ τĥĕ ďєśįřєđ τëņåńŧ иџмьэŗ. !!! !!! !!! !!! - Єитэя {0} ťŏ ŀσġįñ ẅīŧћ â ńзш āč¢бũⁿť !!! !!! !!! !! + Ĕńťèѓ {0} ťō łøģΐŋ ώіŧĥ ā ńèώ άčĉøŭпţ !!! !!! !!! !! {0} will be replaced with the resource from AzureUserEntry_NewLogin; it is intended to be a single-character shorthand for "new account" - Ęʼnţέґ {0} ťο яěмσνе τħě äвσν℮ šăνёď čøⁿŋэčţΐóʼn şэтτĩñģś. !!! !!! !!! !!! !!! ! + Σлтзґ {0} ŧø ŕэмôνз τђε авóνе şǻνеð сσñńзčťιôⁿ ѕĕťťїňġš. !!! !!! !!! !!! !!! ! {0} will be replaced with the resource from AzureUserEntry_RemoveStored; it is intended to be a single-character shorthand for "remove stored" - Рĺēǻŝê ёʼnτεя å νäℓĩđ ⁿüмьэř τό åςĉëśŝ тĥė śτóřèð çôñηěčŧîóń šéτťίńģѕ, {0} ţǿ ļöġ ιп щīтħ â ⁿёω áĉĉбυйţ, θґ {1} ťο ŕëmόνέ τнз ŝανέď çбⁿņэςťîǿη šëτţĭñģş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Рľεдşê ěπτэг ă νàĺįδ йΰmьēя ŧó àсĉĕŝѕ тђέ ŝтøяěð çøⁿñέĉтïǿŋ šєţτĭηģś, {0} тø ľóĝ īп ẅϊτђ â лęщ āς¢òŭñŧ, ôѓ {1} ŧŏ язмôνё тħέ śάνēδ čσňпęċτīôη šėŧŧíпĝѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the resource from AzureUserEntry_NewLogin, and {1} will be replaced with AzureUserEntry_RemoveStored. This is an error message, used after AzureNewLogin/AzureRemoveStored if the user enters an invalid value. - Ρĺεàŝе éņŧєг ª пűмьěŗ. !!! !!! + Ρŀєáśе ёňŧεг ª ňύмъēг. !!! !!! - Лũмьëг øΰт οƒ ьбμⁿďš. Рłêąѕέ ēπτĕг â νäℓιď ⁿΰмь℮ґ. !!! !!! !!! !!! !!! + Пϋмъęґ óΰť õƒ воµήďş. Ρŀεäŝé эⁿťєг â νãŀϊδ пцмъёř. !!! !!! !!! !!! !!! - Сόύŀđ ŋот ƒìņď дπγ ţёηªηŧş. !!! !!! !! + Сôµłď ηǿť ƒіπđ ãñŷ ţ℮ηáńŧş. !!! !!! !! - Ύŏû ĥдνе ņőť ѕэť μр ýőűг ĉĺôυð śħёĺℓ âċćõüпť ŷёт. Ρļéāŝέ ģò τø https://shell.azure.com τб şеţ īť ϋφ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ϋσΰ ђáνě ņоť śéт ūφ ỳθϋг сŀоůδ šђęļĺ áćçθùηт ўеτ. Рĺėåŝê ĝο ŧό https://shell.azure.com το šёť ΐт úр. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="https://shell.azure.com"} This URL should not be localized. Everything else should. - Ðό уοϋ ώάπτ ţο şάνε τħèѕĕ ĉòⁿйєçťìοй šèτţϊлĝѕ ƒöѓ ƒüтµřэ ĺθģіпѕ? [{0}/{1}] !!! !!! !!! !!! !!! !!! !!! ! + Đò ÿóΰ ẁâňт ŧŏ ŝàνè ťђėšē сôиⁿе¢ŧιŏñ şετтΐńģѕ ƒøѓ ƒϋŧûґē ĺоġΐñş? [{0}/{1}] !!! !!! !!! !!! !!! !!! !!! ! {0} and {1} will be replaced with AzureUserEntry_Yes and AzureUserEntry_No. They are single-character shorthands for "yes" and "no" in this language. - Ρŀэåšє ěñτ℮ѓ {0} ôѓ {1} !!! !!! + Ρĺèªѕê εňţēŕ {0} оŗ {1} !!! !!! {0} and {1} will be replaced with AzureUserEntry_Yes and AzureUserEntry_No. This resource will be used as an error response after AzureStorePrompt. - Ґεqùęşŧιπġ ã сľóμď śĥεļŀ īⁿşτάπсє... !!! !!! !!! ! + Ґĕqμ℮šŧїŋĝ ã ćŀòμð śнёĺŀ íņşťąňċз... !!! !!! !!! ! - Ŝцćċёęðĕð. !!! + Śűċćêёđέď. !!! - Яėqüεŝτĩлĝ ά тēгмïпąļ (ŧĥĩѕ mϊģĥт ţáκε α ẅĥίļè)... !!! !!! !!! !!! !!! + Яεqůęŝτίñĝ д тėґmΐπаľ (ťнĭś мìģĥť ţªќĕ ā ẁĥΐĺé)... !!! !!! !!! !!! !!! - Ўôųŗ ċόńñέĉťíöй ŝěťτїлġѕ ĥάνэ ьéèп ѕдνĕđ ƒбř ƒμŧϋřе ŀθģĭπŝ. !!! !!! !!! !!! !!! !!! + Ỳσûř čθñňĕ¢ţįõʼn ѕεтţĭňğš наνē вёэʼn śǻνēδ ƒόг ƒμţüŗè ĺõģìйś. !!! !!! !!! !!! !!! !!! - Йø τòκëñš тο ŗěмõνē. !!! !!! + Ňŏ ŧōкèйś ţő яэmονе. !!! !!! - Śдνêď ćбñńёĉŧíōй śеττĭлģš яémòνёð. !!! !!! !!! ! + Śανеđ ċσйñз¢ťìόń şєŧтιŋğѕ яёмǿνёđ. !!! !!! !!! ! - Δúţħєŋтïςãŧіōň рāґåmêтĕѓś ¢ĥáиĝĕď. Ỳõų'ℓļ πєεð ţō łøĝ īη àġąĭń. !!! !!! !!! !!! !!! !!! + Āΰτнėиŧΐċâтīбŋ рářáмęŧéгŝ сħåⁿģëď. Ϋοц'ļľ ⁿêэð τô ĺσĝ ϊņ àġαĭπ. !!! !!! !!! !!! !!! !!! - <υηĸήοẃⁿ τёñâńţ ņąмє> !!! !!! + <ϋпкйôẁи ŧēņàлτ ňămё> !!! !!! - Ŧèŋαпţ {0}: {1} ({2}) !!! !!! + Ťėηªŋт {0}: {1} ({2}) !!! !!! {0} is the tenant's number, which the user will enter to connect to the tenant. {1} is the tenant's display name, which will be meaningful for the user. {2} is the tenant's internal ID number. - Āųţĥёņţīĉατèđ. !!! ! + Ąűŧħēπτįсáτèđ. !!! ! - п + ņ This is shorthand for "new login". The user must enter this character to activate the New Login feature (AzureInvalidAccessInput, AzureNewLogin) - ѓ + ґ This is shorthand for "remove saved connections". The user must enter this character to activate the Remove Saved Logins feature (AzureRemoveStored, AzureInvalidAccessInput) @@ -200,24 +200,24 @@ This is shorthand for "yes". The user must enter this character to CONFIRM a prompt. - η + ń This is shorthand for "no". The user must enter this character to DECLINE a prompt. - [ρřō¢ëśŝ ėжιťéđ щϊťн сθďё {0}] !!! !!! !!! + [рѓос℮śš ёхіţεð ẃιţĥ ćōďë {0}] !!! !!! !!! The first argument {0} is the error code of the process. When there is no error, the number ZERO will be displayed. - Υбΰ ċαň пοẅ ċļǿşę тђìś ťєгмìʼnâł ωїτħ Ċτřℓ+Đ, бг ρяęśš Зйţëŕ ţб ѓęѕŧąřŧ. !!! !!! !!! !!! !!! !!! !!! + Ỳóŭ ćǻή иòω сĺøѕè ťнįş тёѓмîήªŀ ωīťђ Çťѓℓ+Ď, όг ргéšѕ Σñтèř το ґèšтªят. !!! !!! !!! !!! !!! !!! !!! "Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter). - [эгřбѓ {0} шђεñ łăΰήсĥįʼnģ `{1}'] !!! !!! !!! + [℮ѓѓŏŕ {0} ωĥєй łåύñćђίηğ `{1}'] !!! !!! !!! The first argument {0} is the error code. The second argument {1} is the user-specified path to a program. If this string is broken to multiple lines, it will not be displayed properly. - Сбùℓδ иоţ дçсзśѕ šťªřţīŋġ δîŗĕčŧσŕγ "{0}" !!! !!! !!! !!! + Ċőŭľđ йōť ª¢čеѕş şτāŗťΐиğ ðιѓεςтоŗγ "{0}" !!! !!! !!! !!! The first argument {0} is a path to a directory on the filesystem, as provided by the user. \ No newline at end of file diff --git a/src/cascadia/TerminalConnection/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalConnection/Resources/qps-ploca/Resources.resw index 4701f5b2660..53e853a925f 100644 --- a/src/cascadia/TerminalConnection/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalConnection/Resources/qps-ploca/Resources.resw @@ -118,77 +118,77 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ťђĩš ¢бðĕ щΐľĺ é×φΐŗè ίń 15 міņūťèś. !!! !!! !!! ! + Ŧнιŝ ςøδё ẅіℓŀ зхρîґέ íи 15 mїπųŧęś. !!! !!! !!! ! - Ρℓēάśě єñτęŕ τђĕ đĕśìŗéđ ŧ℮йάит ήůmъεř. !!! !!! !!! !!! + Рĺέåŝē еňţéŗ τĥĕ ďєśįřєđ τëņåńŧ иџмьэŗ. !!! !!! !!! !!! - Єŋťĕř {0} ťο ļòġįи ώιťĥ ā ņĕŵ аċсôúñţ !!! !!! !!! !! + Ĕńťèѓ {0} ťō łøģΐŋ ώіŧĥ ā ńèώ άčĉøŭпţ !!! !!! !!! !! {0} will be replaced with the resource from AzureUserEntry_NewLogin; it is intended to be a single-character shorthand for "new account" - Епŧěя {0} ŧό ŗęmбνε τђέ αвòνė şāνέđ ċóпñêçťĩбñ şéţŧįήģѕ. !!! !!! !!! !!! !!! ! + Σлтзґ {0} ŧø ŕэмôνз τђε авóνе şǻνеð сσñńзčťιôⁿ ѕĕťťїňġš. !!! !!! !!! !!! !!! ! {0} will be replaced with the resource from AzureUserEntry_RemoveStored; it is intended to be a single-character shorthand for "remove stored" - Рℓёäśэ èŋţęґ à νăℓіδ ńцmвєя ťό α¢čëşš тће şτθŗэď ςöйʼnесţїôⁿ ѕзτťϊňğş, {0} τö ℓöġ ίη ẃĭţĥ ã ñеẁ åċčσųńт, οŗ {1} ŧθ ґèmöνе ťнě ѕāνεđ ćǿπйёсťìöл šèţтіʼnġѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Рľεдşê ěπτэг ă νàĺįδ йΰmьēя ŧó àсĉĕŝѕ тђέ ŝтøяěð çøⁿñέĉтïǿŋ šєţτĭηģś, {0} тø ľóĝ īп ẅϊτђ â лęщ āς¢òŭñŧ, ôѓ {1} ŧŏ язмôνё тħέ śάνēδ čσňпęċτīôη šėŧŧíпĝѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the resource from AzureUserEntry_NewLogin, and {1} will be replaced with AzureUserEntry_RemoveStored. This is an error message, used after AzureNewLogin/AzureRemoveStored if the user enters an invalid value. - Рł℮āѕê êηţěŗ а ñцmвēг. !!! !!! + Ρŀєáśе ёňŧεг ª ňύмъēг. !!! !!! - Νűmъεґ όµŧ бƒ ьσΰиδš. Рŀ℮âŝэ ëʼnтзř â νаℓįð πúmъêŗ. !!! !!! !!! !!! !!! + Пϋмъęґ óΰť õƒ воµήďş. Ρŀεäŝé эⁿťєг â νãŀϊδ пцмъёř. !!! !!! !!! !!! !!! - Сουļδ ńǿŧ ƒïŋď āπγ ţέⁿаʼnτŝ. !!! !!! !! + Сôµłď ηǿť ƒіπđ ãñŷ ţ℮ηáńŧş. !!! !!! !! - Ỳōù ћανē ŋŏт ѕęţ ΰρ ỳõûř ćľòùđ şĥěļŀ à¢ćōŭʼnť ýėτ. Ρļèάšέ ĝø тσ https://shell.azure.com τò ѕ℮ť їτ ũρ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ϋσΰ ђáνě ņоť śéт ūφ ỳθϋг сŀоůδ šђęļĺ áćçθùηт ўеτ. Рĺėåŝê ĝο ŧό https://shell.azure.com το šёť ΐт úр. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="https://shell.azure.com"} This URL should not be localized. Everything else should. - Ðό ýóü шаήт τǿ ŝąνę τĥęŝê ¢øʼnńέсţĩōи şéтτîиĝś ƒōŕ ƒùτµřê ļøĝĩπş? [{0}/{1}] !!! !!! !!! !!! !!! !!! !!! ! + Đò ÿóΰ ẁâňт ŧŏ ŝàνè ťђėšē сôиⁿе¢ŧιŏñ şετтΐńģѕ ƒøѓ ƒϋŧûґē ĺоġΐñş? [{0}/{1}] !!! !!! !!! !!! !!! !!! !!! ! {0} and {1} will be replaced with AzureUserEntry_Yes and AzureUserEntry_No. They are single-character shorthands for "yes" and "no" in this language. - Ρľ℮аşě ęпτэř {0} óг {1} !!! !!! + Ρĺèªѕê εňţēŕ {0} оŗ {1} !!! !!! {0} and {1} will be replaced with AzureUserEntry_Yes and AzureUserEntry_No. This resource will be used as an error response after AzureStorePrompt. - Řèqυėşтϊʼnĝ д ćĺοûđ šĥēłĺ įʼnŝťàňčĕ... !!! !!! !!! ! + Ґĕqμ℮šŧїŋĝ ã ćŀòμð śнёĺŀ íņşťąňċз... !!! !!! !!! ! - Śŭ¢¢εēðéď. !!! + Śűċćêёđέď. !!! - Геqūеѕтĭńģ α ťёямϊⁿдļ (ŧђīŝ мïğћτ тáκē á щħìŀė)... !!! !!! !!! !!! !!! + Яεqůęŝτίñĝ д тėґmΐπаľ (ťнĭś мìģĥť ţªќĕ ā ẁĥΐĺé)... !!! !!! !!! !!! !!! - Ύθμг čöŋπεċτĩθņ śεťţιŋĝŝ ђáνę ьέêй śăν℮ď ƒőŕ ƒџтцгē ℓőĝϊлş. !!! !!! !!! !!! !!! !!! + Ỳσûř čθñňĕ¢ţįõʼn ѕεтţĭňğš наνē вёэʼn śǻνēδ ƒόг ƒμţüŗè ĺõģìйś. !!! !!! !!! !!! !!! !!! - Ňö ťσĸëиѕ ţö гĕmŏνē. !!! !!! + Ňŏ ŧōкèйś ţő яэmονе. !!! !!! - Śªνёð ćōňиēĉτîóл ѕėτтілġş řĕмǿνêđ. !!! !!! !!! ! + Śανеđ ċσйñз¢ťìόń şєŧтιŋğѕ яёмǿνёđ. !!! !!! !!! ! - Άūтћĕлŧĭĉªтίοñ рäѓàmëтεґş ¢нåñğēď. Ўοů'ŀĺ ʼnęёð ţθ ĺоģ ίñ ąġаĩň. !!! !!! !!! !!! !!! !!! + Āΰτнėиŧΐċâтīбŋ рářáмęŧéгŝ сħåⁿģëď. Ϋοц'ļľ ⁿêэð τô ĺσĝ ϊņ àġαĭπ. !!! !!! !!! !!! !!! !!! - <ūŋкиόώň τёńдⁿτ ñªmĕ> !!! !!! + <ϋпкйôẁи ŧēņàлτ ňămё> !!! !!! - Τεиäиţ {0}: {1} ({2}) !!! !!! + Ťėηªŋт {0}: {1} ({2}) !!! !!! {0} is the tenant's number, which the user will enter to connect to the tenant. {1} is the tenant's display name, which will be meaningful for the user. {2} is the tenant's internal ID number. - Àůтнęńтīċаτέđ. !!! ! + Ąűŧħēπτįсáτèđ. !!! ! - ʼn + ņ This is shorthand for "new login". The user must enter this character to activate the New Login feature (AzureInvalidAccessInput, AzureNewLogin) @@ -196,28 +196,28 @@ This is shorthand for "remove saved connections". The user must enter this character to activate the Remove Saved Logins feature (AzureRemoveStored, AzureInvalidAccessInput) - ŷ + ў This is shorthand for "yes". The user must enter this character to CONFIRM a prompt. - η + ń This is shorthand for "no". The user must enter this character to DECLINE a prompt. - [рřøĉзšŝ èхіťзď ώīťћ ċőδе {0}] !!! !!! !!! + [рѓос℮śš ёхіţεð ẃιţĥ ćōďë {0}] !!! !!! !!! The first argument {0} is the error code of the process. When there is no error, the number ZERO will be displayed. - Ŷóц çåп ñøώ ćŀõśĕ ťћίś ŧεѓміήāļ ẁĩţђ Ćţѓℓ+Ď, ǿя φяεŝѕ Ēήτеř тб ґêşţâŕť. !!! !!! !!! !!! !!! !!! !!! + Ỳóŭ ćǻή иòω сĺøѕè ťнįş тёѓмîήªŀ ωīťђ Çťѓℓ+Ď, όг ргéšѕ Σñтèř το ґèšтªят. !!! !!! !!! !!! !!! !!! !!! "Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter). - [ĕяґöґ {0} ẁĥęл ŀǻûŋċħīņģ `{1}'] !!! !!! !!! + [℮ѓѓŏŕ {0} ωĥєй łåύñćђίηğ `{1}'] !!! !!! !!! The first argument {0} is the error code. The second argument {1} is the user-specified path to a program. If this string is broken to multiple lines, it will not be displayed properly. - Ĉǿΰļδ ʼnбτ ãċсёśŝ ѕţăŕŧîñġ đіřêςţóяγ "{0}" !!! !!! !!! !!! + Ċőŭľđ йōť ª¢čеѕş şτāŗťΐиğ ðιѓεςтоŗγ "{0}" !!! !!! !!! !!! The first argument {0} is a path to a directory on the filesystem, as provided by the user. \ No newline at end of file diff --git a/src/cascadia/TerminalConnection/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalConnection/Resources/qps-plocm/Resources.resw index 83825f3ac89..53e853a925f 100644 --- a/src/cascadia/TerminalConnection/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalConnection/Resources/qps-plocm/Resources.resw @@ -118,77 +118,77 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Тђįš ςŏðè ώïľľ єжφίřё îʼn 15 mīπµţéš. !!! !!! !!! ! + Ŧнιŝ ςøδё ẅіℓŀ зхρîґέ íи 15 mїπųŧęś. !!! !!! !!! ! - Рľėãş℮ еñţěг тђз δ℮śįŗéď ţёņǻηť пűmьёř. !!! !!! !!! !!! + Рĺέåŝē еňţéŗ τĥĕ ďєśįřєđ τëņåńŧ иџмьэŗ. !!! !!! !!! !!! - Σпŧзř {0} тο ĺǿģíй ẃĩтħ ă ⁿěẅ ªċčόυńт !!! !!! !!! !! + Ĕńťèѓ {0} ťō łøģΐŋ ώіŧĥ ā ńèώ άčĉøŭпţ !!! !!! !!! !! {0} will be replaced with the resource from AzureUserEntry_NewLogin; it is intended to be a single-character shorthand for "new account" - Σπŧеř {0} ţб řέmθνě τђέ àъονë ŝàνëð сθйņ℮çťíбň şēţтїиğš. !!! !!! !!! !!! !!! ! + Σлтзґ {0} ŧø ŕэмôνз τђε авóνе şǻνеð сσñńзčťιôⁿ ѕĕťťїňġš. !!! !!! !!! !!! !!! ! {0} will be replaced with the resource from AzureUserEntry_RemoveStored; it is intended to be a single-character shorthand for "remove stored" - Рℓèãśэ έиţєґ ã νåŀΐď иûмьеř ŧσ â¢сέѕš ţђë ŝťòя℮ð ¢ǿлήęčŧΐόл ѕєтŧīŋģѕ, {0} τǿ ℓόğ ĭʼn шĩţĥ â ŋзω àсčőűñт, оŗ {1} ţō řêmŏνę ťнё śâνėđ ċбππ℮ĉťϊοň ѕєťŧιňĝѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Рľεдşê ěπτэг ă νàĺįδ йΰmьēя ŧó àсĉĕŝѕ тђέ ŝтøяěð çøⁿñέĉтïǿŋ šєţτĭηģś, {0} тø ľóĝ īп ẅϊτђ â лęщ āς¢òŭñŧ, ôѓ {1} ŧŏ язмôνё тħέ śάνēδ čσňпęċτīôη šėŧŧíпĝѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {0} will be replaced with the resource from AzureUserEntry_NewLogin, and {1} will be replaced with AzureUserEntry_RemoveStored. This is an error message, used after AzureNewLogin/AzureRemoveStored if the user enters an invalid value. - Рĺěǻśє ēņтęŗ á ʼnŭmъ℮ř. !!! !!! + Ρŀєáśе ёňŧεг ª ňύмъēг. !!! !!! - Иūmьéŗ őųτ òƒ ъоµñďѕ. Ρļзαşé êήтεя а νāľίđ лυmъèř. !!! !!! !!! !!! !!! + Пϋмъęґ óΰť õƒ воµήďş. Ρŀεäŝé эⁿťєг â νãŀϊδ пцмъёř. !!! !!! !!! !!! !!! - Ćοµļđ лσţ ƒīńð ǻñý тēйáⁿτś. !!! !!! !! + Сôµłď ηǿť ƒіπđ ãñŷ ţ℮ηáńŧş. !!! !!! !! - Ϋŏű нâνє пòт ŝ℮τ ũρ ỳőũŗ ĉļőυď ѕћέļľ äсčόúŋŧ ỳèт. Рľ℮äѕє ģõ тō https://shell.azure.com ŧő šêţ ϊţ ύφ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ϋσΰ ђáνě ņоť śéт ūφ ỳθϋг сŀоůδ šђęļĺ áćçθùηт ўеτ. Рĺėåŝê ĝο ŧό https://shell.azure.com το šёť ΐт úр. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! {Locked="https://shell.azure.com"} This URL should not be localized. Everything else should. - Đο ўομ ẁдηţ τø šàνĕ ŧĥęśз čбπņєςтΐοη ŝєττîπģѕ ƒόг ƒΰτůяė ĺőġìⁿš? [{0}/{1}] !!! !!! !!! !!! !!! !!! !!! ! + Đò ÿóΰ ẁâňт ŧŏ ŝàνè ťђėšē сôиⁿе¢ŧιŏñ şετтΐńģѕ ƒøѓ ƒϋŧûґē ĺоġΐñş? [{0}/{1}] !!! !!! !!! !!! !!! !!! !!! ! {0} and {1} will be replaced with AzureUserEntry_Yes and AzureUserEntry_No. They are single-character shorthands for "yes" and "no" in this language. - Рļзâšє зʼnţэґ {0} õѓ {1} !!! !!! + Ρĺèªѕê εňţēŕ {0} оŗ {1} !!! !!! {0} and {1} will be replaced with AzureUserEntry_Yes and AzureUserEntry_No. This resource will be used as an error response after AzureStorePrompt. - Ŗĕqūзşťįήĝ ā ćłôūđ ѕнэŀĺ іŋşťáйςè... !!! !!! !!! ! + Ґĕqμ℮šŧїŋĝ ã ćŀòμð śнёĺŀ íņşťąňċз... !!! !!! !!! ! - Šϋć¢ėзðèđ. !!! + Śűċćêёđέď. !!! - Γèqΰёşτіńğ ǻ ŧĕŕмΐйáł (ťђìѕ mīġнť ţãĸē à щĥіℓ℮)... !!! !!! !!! !!! !!! + Яεqůęŝτίñĝ д тėґmΐπаľ (ťнĭś мìģĥť ţªќĕ ā ẁĥΐĺé)... !!! !!! !!! !!! !!! - Ŷöùѓ čόпήёсťĩθⁿ ś℮ţτїηĝŝ ĥåνē вęĕņ šǻνēδ ƒöř ƒџţμге ĺǿğîлŝ. !!! !!! !!! !!! !!! !!! + Ỳσûř čθñňĕ¢ţįõʼn ѕεтţĭňğš наνē вёэʼn śǻνēδ ƒόг ƒμţüŗè ĺõģìйś. !!! !!! !!! !!! !!! !!! - Νő тŏкéпѕ тŏ ґзmòνě. !!! !!! + Ňŏ ŧōкèйś ţő яэmονе. !!! !!! - Ŝąνеđ čöŋπĕĉťĩоŋ šĕţţīňġѕ гêмōνĕď. !!! !!! !!! ! + Śανеđ ċσйñз¢ťìόń şєŧтιŋğѕ яёмǿνёđ. !!! !!! !!! ! - Δϋτћęňτĭčαťíóп ρдřàмёŧèřş сђªńğ℮ď. Ύőú'ľł ņёĕð ţǿ ľöĝ ίπ ãĝâĭп. !!! !!! !!! !!! !!! !!! + Āΰτнėиŧΐċâтīбŋ рářáмęŧéгŝ сħåⁿģëď. Ϋοц'ļľ ⁿêэð τô ĺσĝ ϊņ àġαĭπ. !!! !!! !!! !!! !!! !!! - <ũńкňǿŵŋ τелдŋŧ ʼnăмє> !!! !!! + <ϋпкйôẁи ŧēņàлτ ňămё> !!! !!! - Тĕηάπŧ {0}: {1} ({2}) !!! !!! + Ťėηªŋт {0}: {1} ({2}) !!! !!! {0} is the tenant's number, which the user will enter to connect to the tenant. {1} is the tenant's display name, which will be meaningful for the user. {2} is the tenant's internal ID number. - ∆υτнêήτĭćåтéδ. !!! ! + Ąűŧħēπτįсáτèđ. !!! ! - ή + ņ This is shorthand for "new login". The user must enter this character to activate the New Login feature (AzureInvalidAccessInput, AzureNewLogin) @@ -196,28 +196,28 @@ This is shorthand for "remove saved connections". The user must enter this character to activate the Remove Saved Logins feature (AzureRemoveStored, AzureInvalidAccessInput) - у + ў This is shorthand for "yes". The user must enter this character to CONFIRM a prompt. - η + ń This is shorthand for "no". The user must enter this character to DECLINE a prompt. - [рґŏ¢℮şѕ ĕ×ĭŧēδ ẅįтĥ ςòðе {0}] !!! !!! !!! + [рѓос℮śš ёхіţεð ẃιţĥ ćōďë {0}] !!! !!! !!! The first argument {0} is the error code of the process. When there is no error, the number ZERO will be displayed. - Ϋôμ čǻή ńόщ ċĺбѕё ţђįš ť℮ŗmΐⁿáľ щīţђ Ċτґℓ+Ð, οѓ φŗэśş Ěŋťĕŗ ţő řěśτäѓŧ. !!! !!! !!! !!! !!! !!! !!! + Ỳóŭ ćǻή иòω сĺøѕè ťнįş тёѓмîήªŀ ωīťђ Çťѓℓ+Ď, όг ргéšѕ Σñтèř το ґèšтªят. !!! !!! !!! !!! !!! !!! !!! "Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter). - [ëґѓøř {0} ωнеи ľãμńĉħïňğ `{1}'] !!! !!! !!! + [℮ѓѓŏŕ {0} ωĥєй łåύñćђίηğ `{1}'] !!! !!! !!! The first argument {0} is the error code. The second argument {1} is the user-specified path to a program. If this string is broken to multiple lines, it will not be displayed properly. - Čőυĺδ ŋõť аçĉěšŝ ŝŧäřтϊиģ ðϊřèςťοŕў "{0}" !!! !!! !!! !!! + Ċőŭľđ йōť ª¢čеѕş şτāŗťΐиğ ðιѓεςтоŗγ "{0}" !!! !!! !!! !!! The first argument {0} is a path to a directory on the filesystem, as provided by the user. \ No newline at end of file diff --git a/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw index f58bf6dfb58..62835cf4c86 100644 --- a/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw @@ -118,72 +118,72 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Μåťĉĥ Ĉãѕё !!! + Мåţ¢н Čдŝë !!! The tooltip text for the case sensitivity button on the search box control. - Ćĺσśė ! + Çŀŏŝė ! The tooltip text for the close button on the search box control. - ₣іňď Цφ !! + ₣ϊņδ Ûρ !! The tooltip text for the search backward button. - ₣īŋđ Đбẁņ !!! + ₣îńð Đόщπ !!! The tooltip text for the search forward button. - ₣ĭлđ ! + ₣įńď ! The placeholder text in the search box control. - Рåѕťĕ рǻťћ тó ƒíļз !!! !! + Рáşтĕ ρāτн ŧб ƒїŀè !!! !! The displayed caption for dragging a file onto a terminal. - Ρªѕţē ŧэ×ţ !!! + Рāśŧë τ℮жţ !!! The displayed caption for dragging text onto a terminal. - Ċăѕë Ѕεŋŝíŧіνιτγ !!! ! + Čαŝė Śèňśίтīνίτγ !!! ! The name of the case sensitivity button on the search box control for accessibility. - Šёăґċħ ₣οѓŵāґð !!! ! + Şêǻгсĥ ₣őгώдřδ !!! ! The name of the search forward button for accessibility. - Ŝзàřçћ Ьά¢κẁàŕδ !!! ! + Šеąŕсђ βáćкщâřð !!! ! The name of the search backward button for accessibility. - ₣ίήď ! + ₣īηď ! The name of the text box on the search box control for accessibility. This should match "SearchBox_TextBox.PlaceholderText" - ŧĕŗmįŋãł !! + тêŕmіʼnāŀ !! The type of control that the terminal adheres to. Used to identify how a user can interact with this kind of control. - Сļбŝê Ŝэαřçн Бŏ× !!! ! + €ŀοşэ Şеàґćĥ βǿх !!! ! The explanation of the search box close button used by Narrator and other screen readers. - Тћϊŝ тέяmϊиàł ђãš ëпçőůʼnŧєřėď àⁿ ΐŝśűє шĩŧħ ťђє ĝřăрĥīćѕ δґіνĕŕ âηδ їт ċōüľď ŋőт яэĉоνĕг ïή тїмэ. Íŧ ħãş ъêєл šųşρęпðзð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ťђіş ţēгmϊⁿàŀ ħáѕ εńçøüπτèґеδ ªņ ΐѕşϋё ẁíţђ ŧћέ ĝřāрħіςš δřїνëґ аπδ ïт çõúļð ňøτ ѓèċоνėг ìή ťĩm℮. Ìţ ђǻѕ ъέêʼn ѕũşрέпðєď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ŕέşцмę ! + Ѓèşυмè ! - €тřŀ+Сłί¢ķ тō ƒǿļℓбш ļïлќ !!! !!! ! + Сŧŗļ+Čŀĩčķ тô ƒőĺľόш łîņĸ !!! !!! ! - Ňő řęŝųłтś !!! + Νθ ѓéšцℓťş !!! Will be presented near the search box when Find operation returned no results. - Şēαřĉћìńĝ... !!! + Ѕěαя¢ђìпğ... !!! Will be presented near the search box when Find operation is running. @@ -191,101 +191,101 @@ Will be displayed to indicate what result the user has selected, of how many total results. {0} will be replaced with the index of the current result. {1} will be replaced with the total number of results. - Ĭлνäŀìď ÚЃĨ !!! + Íņνãľîđ ÚŔİ !!! Whenever we encounter an invalid URI or URL we show this string as a warning. - Ùлâьľě τö ƒíńð тħз ŝéĺзсţэδ ƒоñť "{0}". !!! !!! !!! !!! + Ůʼnäвľė ťö ƒΐńđ τħé şèℓ℮ĉťēδ ƒòñŧ "{0}". -"{1}" ђдš ъè℮π ѕèľεĉŧєď ΐήşťēãď. !!! !!! !!! +"{1}" ђàś ъëęñ šеŀесťĕđ ιñśтёąδ. -Ρℓėãšé éīţђěŗ ϊⁿŝţăľŀ ŧђэ mιşśϊñğ ƒοňţ ŏґ ςћобŝε ąпǿтħèŗ оп℮. !!! !!! !!! !!! !!! !!! +Ρℓēάŝę ėїţħěř ĭлşţäļł ťнз mîşśĭηģ ƒóⁿт òґ ¢ћōóşę άηŏťнєѓ ôŋє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! 0 and 1 are names of fonts provided by the user and system respectively. - Џňαьļě τǿ ƒĩлð ťħё φřőνįδėð šħąđёŗ "{0}". !!! !!! !!! !!! + Üидьŀэ τθ ƒìņď ťнė ρяóνίðěď ŝђάðèѓ "{0}". !!! !!! !!! !!! {0} is a file name - Цńáвℓё ţõ ςōmрїĺэ ŧнè şφëсίƒϊéđ ρì×еł šћαđэř. !!! !!! !!! !!! ! + Ūňåъļë ťб ćóмφïĺє ťћз şрęςíƒĩêð ριх℮ĺ şħâđеŕ. !!! !!! !!! !!! ! - Řέňδĕŗзѓ ℮ⁿçøϋńţέřëð άⁿ ύлę×рёċтëď ēѓѓσŕ: {0} !!! !!! !!! !!! ! + Řєʼnδέгęř зйĉóűňтëř℮δ âη ûⁿėхрєĉţēð ℮ѓґоŗ: {0} !!! !!! !!! !!! ! {0} is an error code. - Яĕåď-őлļŷ mόďé ϊŝ єņǻвłέď. !!! !!! ! + Гėāδ-σπļỳ mőðê īŝ ėńäвļεδ. !!! !!! ! - Řёşŭļτѕ ƒбύлδ !!! + Γĕšµľţş ƒбŭⁿð !!! Announced to a screen reader when the user searches for some text and there are matches for that text in the terminal. - Ñó ѓёşûĺτš ƒōüńδ !!! ! + Ńō гēśџĺŧѕ ƒσúπđ !!! ! Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal. - Рåѕŧĕ ! + Рăşţε ! The label of a button for pasting the contents of the clipboard. - Рáѕţε ! + Ρåśŧē ! The tooltip for a paste button - €óφγ ! + €ǿру ! The label of a button for copying the selected text to the clipboard. - Ċøρу ! + Ćŏφỳ ! The tooltip for a copy button - Ρâѕťë ! + Рāŝтę ! The label of a button for pasting the contents of the clipboard. - Рáśτз ! + Рáşţê ! The tooltip for a paste button - ₣îпď... !! + ₣їņď... !! The label of a button for searching for the selected text - ₣ĭиď ! + ₣ΐņđ ! The tooltip for a button for searching for the selected text - Ŝėľĕсť ςòмmдηð !!! ! + Šéļэςţ çǿммàñδ !!! ! The label of a button for selecting all of the text of a command - Şёľэĉţ ćõмmãлð !!! ! + Ѕеłєçŧ ċοмmàлđ !!! ! The tooltip for a button for selecting all of the text of a command - Ŝèļέćτ őüţрůţ !!! + Ѕεŀэċт σцτφūţ !!! The label of a button for selecting all of a command's output - Şėľë¢τ óúтрŭт !!! + Şèℓëċτ ōϋťρŭţ !!! The tooltip for a button for selecting all of a command's output - Ŝėļе¢τ ¢õmмàŋđ !!! ! + Šěľēĉť ćǿмmăŋð !!! ! The label of a button for selecting all of the text of a command - Ŝëļэсŧ čбммάñð !!! ! + Şēľεςŧ сõmmäηδ !!! ! The tooltip for a button for selecting all of the text of a command - Ѕеļèĉť οūťрůт !!! + Ŝęļëćť σùτρύť !!! The label of a button for selecting all of a command's output - Ŝėℓēςт öůŧрűт !!! + Ŝ℮ĺèčť οùτφųţ !!! The tooltip for a button for selecting all of a command's output \ No newline at end of file diff --git a/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw index db007341965..62835cf4c86 100644 --- a/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw @@ -118,72 +118,72 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Μаŧçĥ Сàšę !!! + Мåţ¢н Čдŝë !!! The tooltip text for the case sensitivity button on the search box control. - Ċļôšê ! + Çŀŏŝė ! The tooltip text for the close button on the search box control. - ₣ΐñð Ŭр !! + ₣ϊņδ Ûρ !! The tooltip text for the search backward button. - ₣ĭñđ Ďόщи !!! + ₣îńð Đόщπ !!! The tooltip text for the search forward button. - ₣ìлď ! + ₣įńď ! The placeholder text in the search box control. - Ρªšŧĕ φàтн ţô ƒїℓę !!! !! + Рáşтĕ ρāτн ŧб ƒїŀè !!! !! The displayed caption for dragging a file onto a terminal. - Ρášŧę ţэжт !!! + Рāśŧë τ℮жţ !!! The displayed caption for dragging text onto a terminal. - €дѕē Ŝèлşĩţϊνĩτў !!! ! + Čαŝė Śèňśίтīνίτγ !!! ! The name of the case sensitivity button on the search box control for accessibility. - Ѕèăřсħ ₣σґώářδ !!! ! + Şêǻгсĥ ₣őгώдřδ !!! ! The name of the search forward button for accessibility. - Ѕёàяçĥ Вäċќẁāřđ !!! ! + Šеąŕсђ βáćкщâřð !!! ! The name of the search backward button for accessibility. - ₣їйđ ! + ₣īηď ! The name of the text box on the search box control for accessibility. This should match "SearchBox_TextBox.PlaceholderText" - ţέŗмīηăĺ !! + тêŕmіʼnāŀ !! The type of control that the terminal adheres to. Used to identify how a user can interact with this kind of control. - Ċłöšè Śêãгċђ βσ× !!! ! + €ŀοşэ Şеàґćĥ βǿх !!! ! The explanation of the search box close button used by Narrator and other screen readers. - Ţħíś т℮гмїňął ĥåѕ зпċǿûήтéѓ℮đ äņ ΐѕšϋз ẁĭтћ ťћё ĝřαρħïςѕ đřïνęя αⁿð ĭť ćŏúŀð ňǿт яэčöν℮ŕ ΐη тįmέ. Ίτ ĥªş ьēęŋ śϋśрęⁿðėδ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ťђіş ţēгmϊⁿàŀ ħáѕ εńçøüπτèґеδ ªņ ΐѕşϋё ẁíţђ ŧћέ ĝřāрħіςš δřїνëґ аπδ ïт çõúļð ňøτ ѓèċоνėг ìή ťĩm℮. Ìţ ђǻѕ ъέêʼn ѕũşрέпðєď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ґěşůмз ! + Ѓèşυмè ! - Čţяľ+Ċļĩčĸ τő ƒôŀŀôщ ľįňќ !!! !!! ! + Сŧŗļ+Čŀĩčķ тô ƒőĺľόш łîņĸ !!! !!! ! - Ńô я℮šúĺτѕ !!! + Νθ ѓéšцℓťş !!! Will be presented near the search box when Find operation returned no results. - Śēаŕĉħîŋģ... !!! + Ѕěαя¢ђìпğ... !!! Will be presented near the search box when Find operation is running. @@ -191,101 +191,101 @@ Will be displayed to indicate what result the user has selected, of how many total results. {0} will be replaced with the index of the current result. {1} will be replaced with the total number of results. - İŋναĺїď ЦҐĨ !!! + Íņνãľîđ ÚŔİ !!! Whenever we encounter an invalid URI or URL we show this string as a warning. - Ůŋªъĺэ ŧò ƒįņð ţђέ ѕĕℓéçţэđ ƒбŋт "{0}". !!! !!! !!! !!! + Ůʼnäвľė ťö ƒΐńđ τħé şèℓ℮ĉťēδ ƒòñŧ "{0}". -"{1}" ħªş ь℮ėŋ śєĺĕсţзď íπŝŧёáď. !!! !!! !!! +"{1}" ђàś ъëęñ šеŀесťĕđ ιñśтёąδ. -Ρŀέаşé еïŧђêŗ îñşţαłļ τћέ mìšŝіʼnğ ƒŏлτ бѓ ςђбǿšę ăηøτђёř οⁿė. !!! !!! !!! !!! !!! !!! +Ρℓēάŝę ėїţħěř ĭлşţäļł ťнз mîşśĭηģ ƒóⁿт òґ ¢ћōóşę άηŏťнєѓ ôŋє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! 0 and 1 are names of fonts provided by the user and system respectively. - Űήåьℓέ ŧθ ƒïʼnδ ťнэ ρŗōνĩδзď ŝĥªđĕŕ "{0}". !!! !!! !!! !!! + Üидьŀэ τθ ƒìņď ťнė ρяóνίðěď ŝђάðèѓ "{0}". !!! !!! !!! !!! {0} is a file name - Ŭήâвℓє τõ ĉσmφìŀέ ŧнĕ šрěċїƒїěδ φíх℮ℓ śђąδεř. !!! !!! !!! !!! ! + Ūňåъļë ťб ćóмφïĺє ťћз şрęςíƒĩêð ριх℮ĺ şħâđеŕ. !!! !!! !!! !!! ! - Řёпðєгéŕ ℮ⁿсόΰňтεѓèđ àй ŭñε×φєċţêδ єřѓбŕ: {0} !!! !!! !!! !!! ! + Řєʼnδέгęř зйĉóűňтëř℮δ âη ûⁿėхрєĉţēð ℮ѓґоŗ: {0} !!! !!! !!! !!! ! {0} is an error code. - Яēάð-òŋŀγ møđé ìş ĕńâьℓзδ. !!! !!! ! + Гėāδ-σπļỳ mőðê īŝ ėńäвļεδ. !!! !!! ! - Ѓéѕџŀтѕ ƒóϋйδ !!! + Γĕšµľţş ƒбŭⁿð !!! Announced to a screen reader when the user searches for some text and there are matches for that text in the terminal. - Йô řēşùĺτŝ ƒоũņď !!! ! + Ńō гēśџĺŧѕ ƒσúπđ !!! ! Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal. - Рαŝŧę ! + Рăşţε ! The label of a button for pasting the contents of the clipboard. - Рàŝτê ! + Ρåśŧē ! The tooltip for a paste button - Çôρý ! + €ǿру ! The label of a button for copying the selected text to the clipboard. - €õφў ! + Ćŏφỳ ! The tooltip for a copy button - Рäŝťè ! + Рāŝтę ! The label of a button for pasting the contents of the clipboard. - Ραşτé ! + Рáşţê ! The tooltip for a paste button - ₣ĩⁿď... !! + ₣їņď... !! The label of a button for searching for the selected text - ₣ìⁿδ ! + ₣ΐņđ ! The tooltip for a button for searching for the selected text - Śěļęčť ĉómmăиδ !!! ! + Šéļэςţ çǿммàñδ !!! ! The label of a button for selecting all of the text of a command - Śēļεćţ çоммªŋδ !!! ! + Ѕеłєçŧ ċοмmàлđ !!! ! The tooltip for a button for selecting all of the text of a command - Šėℓέςŧ ǿµťρüť !!! + Ѕεŀэċт σцτφūţ !!! The label of a button for selecting all of a command's output - Ѕэℓз¢τ ǿυťφùţ !!! + Şèℓëċτ ōϋťρŭţ !!! The tooltip for a button for selecting all of a command's output - Šзℓèçţ сöммāйð !!! ! + Šěľēĉť ćǿмmăŋð !!! ! The label of a button for selecting all of the text of a command - Ѕĕĺëст ċбmмâпδ !!! ! + Şēľεςŧ сõmmäηδ !!! ! The tooltip for a button for selecting all of the text of a command - Šêļêčτ øύτрųт !!! + Ŝęļëćť σùτρύť !!! The label of a button for selecting all of a command's output - Šëℓэčт ŏцτρũτ !!! + Ŝ℮ĺèčť οùτφųţ !!! The tooltip for a button for selecting all of a command's output \ No newline at end of file diff --git a/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw index b12e6e3dbf2..62835cf4c86 100644 --- a/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw @@ -118,72 +118,72 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Μдŧčђ Ĉåşё !!! + Мåţ¢н Čдŝë !!! The tooltip text for the case sensitivity button on the search box control. - Çℓøśé ! + Çŀŏŝė ! The tooltip text for the close button on the search box control. - ₣іⁿď Úφ !! + ₣ϊņδ Ûρ !! The tooltip text for the search backward button. - ₣íńð Đσŵи !!! + ₣îńð Đόщπ !!! The tooltip text for the search forward button. - ₣įηδ ! + ₣įńď ! The placeholder text in the search box control. - Ρãšŧ℮ рåтн тó ƒīŀē !!! !! + Рáşтĕ ρāτн ŧб ƒїŀè !!! !! The displayed caption for dragging a file onto a terminal. - Рάşţé ŧē×ţ !!! + Рāśŧë τ℮жţ !!! The displayed caption for dragging text onto a terminal. - Ĉâšē Ŝėиŝĭтïνîτу !!! ! + Čαŝė Śèňśίтīνίτγ !!! ! The name of the case sensitivity button on the search box control for accessibility. - Ŝзãгсĥ ₣όŗшãřð !!! ! + Şêǻгсĥ ₣őгώдřδ !!! ! The name of the search forward button for accessibility. - Ŝěäřçђ Βą¢ĸшàгδ !!! ! + Šеąŕсђ βáćкщâřð !!! ! The name of the search backward button for accessibility. - ₣ĭπđ ! + ₣īηď ! The name of the text box on the search box control for accessibility. This should match "SearchBox_TextBox.PlaceholderText" - ŧéямîⁿåļ !! + тêŕmіʼnāŀ !! The type of control that the terminal adheres to. Used to identify how a user can interact with this kind of control. - €ℓőѕє Śéдгςн βŏх !!! ! + €ŀοşэ Şеàґćĥ βǿх !!! ! The explanation of the search box close button used by Narrator and other screen readers. - Ŧħĩѕ тёřmίⁿάℓ ћáѕ ěп¢ŏџŋţéŗėď дŋ ìššµé ωįţн ŧне ğгäφĥΐćš ďřίνзѓ āήď íť ĉоùĺδ πότ яеĉòνег ιñ τіmę. Íţ нǻŝ ьêέň śцŝφèŋðєđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ťђіş ţēгmϊⁿàŀ ħáѕ εńçøüπτèґеδ ªņ ΐѕşϋё ẁíţђ ŧћέ ĝřāрħіςš δřїνëґ аπδ ïт çõúļð ňøτ ѓèċоνėг ìή ťĩm℮. Ìţ ђǻѕ ъέêʼn ѕũşрέпðєď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - Ґēŝцm℮ ! + Ѓèşυмè ! - Ĉτгĺ+Сľιĉќ тö ƒŏľŀòщ ļιńк !!! !!! ! + Сŧŗļ+Čŀĩčķ тô ƒőĺľόш łîņĸ !!! !!! ! - Йо ґ℮şûļтš !!! + Νθ ѓéšцℓťş !!! Will be presented near the search box when Find operation returned no results. - Ŝēάґćнίñġ... !!! + Ѕěαя¢ђìпğ... !!! Will be presented near the search box when Find operation is running. @@ -191,101 +191,101 @@ Will be displayed to indicate what result the user has selected, of how many total results. {0} will be replaced with the index of the current result. {1} will be replaced with the total number of results. - Ĩπνāļϊð ŰŘЇ !!! + Íņνãľîđ ÚŔİ !!! Whenever we encounter an invalid URI or URL we show this string as a warning. - Џņªвŀë τθ ƒĩпď ŧнэ ѕèℓє¢ţęđ ƒōлť "{0}". !!! !!! !!! !!! + Ůʼnäвľė ťö ƒΐńđ τħé şèℓ℮ĉťēδ ƒòñŧ "{0}". -"{1}" ħăš ьє℮ⁿ śēļéçŧёđ ĭņşţэαď. !!! !!! !!! +"{1}" ђàś ъëęñ šеŀесťĕđ ιñśтёąδ. -Рłëаŝз ėìτћęř īпŝτăľļ ŧћě міŝŝįиģ ƒōлŧ όŕ çнǿőŝё áńøτнĕŗ ôи℮. !!! !!! !!! !!! !!! !!! +Ρℓēάŝę ėїţħěř ĭлşţäļł ťнз mîşśĭηģ ƒóⁿт òґ ¢ћōóşę άηŏťнєѓ ôŋє. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! 0 and 1 are names of fonts provided by the user and system respectively. - Ůʼnäъĺє ŧσ ƒīπð ŧħë ρґøνïđзδ ѕнаđêґ "{0}". !!! !!! !!! !!! + Üидьŀэ τθ ƒìņď ťнė ρяóνίðěď ŝђάðèѓ "{0}". !!! !!! !!! !!! {0} is a file name - Ùⁿαъļє ŧó ςбmφîŀ℮ ťĥè šρêċїƒіêđ рι×ëĺ ŝђдðέŗ. !!! !!! !!! !!! ! + Ūňåъļë ťб ćóмφïĺє ťћз şрęςíƒĩêð ριх℮ĺ şħâđеŕ. !!! !!! !!! !!! ! - Яέʼnďёřёŗ ěп¢ōŭŋτеѓęď άй ùйěхрєçτëđ ёгřσя: {0} !!! !!! !!! !!! ! + Řєʼnδέгęř зйĉóűňтëř℮δ âη ûⁿėхрєĉţēð ℮ѓґоŗ: {0} !!! !!! !!! !!! ! {0} is an error code. - Ѓєαđ-øńŀŷ мöδĕ ĭş ęηάъĺĕď. !!! !!! ! + Гėāδ-σπļỳ mőðê īŝ ėńäвļεδ. !!! !!! ! - Řзšųľŧŝ ƒöųήð !!! + Γĕšµľţş ƒбŭⁿð !!! Announced to a screen reader when the user searches for some text and there are matches for that text in the terminal. - Ņò гέšůľŧѕ ƒôџήδ !!! ! + Ńō гēśџĺŧѕ ƒσúπđ !!! ! Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal. - Ράśтé ! + Рăşţε ! The label of a button for pasting the contents of the clipboard. - Рãšť℮ ! + Ρåśŧē ! The tooltip for a paste button - Сбрÿ ! + €ǿру ! The label of a button for copying the selected text to the clipboard. - Ĉορў ! + Ćŏφỳ ! The tooltip for a copy button - Ρàŝŧе ! + Рāŝтę ! The label of a button for pasting the contents of the clipboard. - Раŝŧ℮ ! + Рáşţê ! The tooltip for a paste button - ₣ϊňð... !! + ₣їņď... !! The label of a button for searching for the selected text - ₣ϊпđ ! + ₣ΐņđ ! The tooltip for a button for searching for the selected text - Ѕĕŀεςť čθммąпð !!! ! + Šéļэςţ çǿммàñδ !!! ! The label of a button for selecting all of the text of a command - Śэľεćŧ ċómmάⁿď !!! ! + Ѕеłєçŧ ċοмmàлđ !!! ! The tooltip for a button for selecting all of the text of a command - Ŝέĺєсţ ǿϋтφúτ !!! + Ѕεŀэċт σцτφūţ !!! The label of a button for selecting all of a command's output - Śэℓєçţ σũτρύť !!! + Şèℓëċτ ōϋťρŭţ !!! The tooltip for a button for selecting all of a command's output - Ŝєļёĉτ ćόmmалδ !!! ! + Šěľēĉť ćǿмmăŋð !!! ! The label of a button for selecting all of the text of a command - Šéľęĉť čøмmāňď !!! ! + Şēľεςŧ сõmmäηδ !!! ! The tooltip for a button for selecting all of the text of a command - Šèĺэст оцŧφμţ !!! + Ŝęļëćť σùτρύť !!! The label of a button for selecting all of a command's output - Ŝêĺėćτ óυţφμţ !!! + Ŝ℮ĺèčť οùτφųţ !!! The tooltip for a button for selecting all of a command's output \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw index d345810049d..f7193ce8d63 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw @@ -118,237 +118,237 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - €ŗёãťè Ńєщ βυţτøʼn !!! !! + Čгéªţë Ŋещ Бϋτţбⁿ !!! !! - Лεẅ ℮мрťў ρřόƒïłě !!! !! + Ŋ℮ω ĕmρŧý рŗőƒîļё !!! !! Button label that creates a new profile with default settings. - Ďϋρĺíςаτέ à ргòƒĩĺé !!! !!! + Ďύφľίċåте ã φґǿƒïłē !!! !!! This is the header for a control that lets the user duplicate one of their existing profiles. - Ďüρℓїçάţε Ъūťŧǿŋ !!! ! + Ðùρłīĉāţз Вµτŧσň !!! ! - ерℓίċâт℮ !!! + Đŭρŀī¢ατê !!! Button label that duplicates the selected profile and navigates to the duplicate's page. - Тħϊś ¢öłóř ѕċĥëме įѕ φāяŧ öƒ ťђё вµīľŧ-ïπ ѕэŧŧιñğѕ öя дⁿ їŋşţåłŀєď ĕхŧęлŝïоņ !!! !!! !!! !!! !!! !!! !!! ! + Ţħīѕ ċøłǿґ ŝςħèм℮ îś рăŗţ õƒ тнè ъûĩľт-íπ śéţťΐлĝś õř дŋ íńşťªłĺèð èжťęлşίοη !!! !!! !!! !!! !!! !!! !!! ! - Ŧô mąκε çĥªиĝεŝ тò ţĥїş ςθŀθя šĉħémέ, ўôџ мџѕт måќз å сŏрỳ òƒ īŧ. !!! !!! !!! !!! !!! !!! ! + Ţό màќз ¢ћдņĝзş тő ťђїš ĉǿℓöř śсђзmė, ÿŏú мũŝт măĸę α сòφў σƒ ΐť. !!! !!! !!! !!! !!! !!! ! - Μåк℮ ά сõрỳ !!! + Маĸέ α ċōρý !!! This is a call to action; the user can click the button to make a copy of the current color scheme. - Śĕţ ςőľбґ ѕсħêмę ǻś ď℮ƒάùľŧ !!! !!! !! + Śèţ ćŏŀθґ šςħëmĕ âŝ δĕƒдϋŀţ !!! !!! !! This is the header for a control that allows the user to set the currently selected color scheme as their default. - Αρρļŷ ţђîş ¢őľŏг şćĥёmė тô âļℓ ýǿũř рŕσƒіľĕś, вγ đ℮ƒǻùļţ. Îⁿðίνìδµāŀ ρŕøƒïŀêѕ ¢аʼn ѕťíļĺ śέľĕĉţ ţђєίř ōшñ čοľǿř ščĥĕměš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Áφρļу τђìš čǿℓóѓ ѕςĥеmё ţò âļℓ ўσùґ φѓôƒίļеš, ъÿ ðεƒăŭļŧ. Īиðїνιďμäĺ φяöƒíłĕš çǻп şťíĺļ ŝзĺëĉτ тнĕιř ǿшŋ сόļöѓ şčĥêмέѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description explaining how this control changes the user's default color scheme. - Ŕėⁿăмз ĉøℓøѓ ś¢ĥèmε !!! !!! + Ŕêпªmè сθłόя ŝçħémë !!! !!! This is the header for a control that allows the user to rename the currently selected color scheme. - Ъâсĸĝŗõũήð !!! + Ъāċкġřőůņð !!! This is the header for a control that lets the user select the background color for text displayed on the screen. - ßļªçķ ! + Вľд¢κ ! This is the header for a control that lets the user select the black color for text displayed on the screen. - Ьŀúε ! + Ьĺųε ! This is the header for a control that lets the user select the blue color for text displayed on the screen. - Ъŗιğђţ вľªçк !!! + Ьŗίĝħť ьľā¢к !!! This is the header for a control that lets the user select the bright black color for text displayed on the screen. - Ьřĩġћţ ъŀµē !!! + βґίġнť вŀµё !!! This is the header for a control that lets the user select the bright blue color for text displayed on the screen. - Βřīġħţ сýåπ !!! + Вяίģћŧ ĉуǻл !!! This is the header for a control that lets the user select the bright cyan color for text displayed on the screen. - Вŕīģħт ģгēзň !!! + ßѓΐĝђŧ ğґęêп !!! This is the header for a control that lets the user select the bright green color for text displayed on the screen. - ßŗϊğħт ρűřφĺé !!! + Бяīğнŧ ρùŗрľĕ !!! This is the header for a control that lets the user select the bright purple color for text displayed on the screen. - βѓΐğћτ ŕĕď !!! + Ъŕίğћт гėđ !!! This is the header for a control that lets the user select the bright red color for text displayed on the screen. - Ъгîġнŧ ωћĩтē !!! + Ьгīģђţ ωђĭτê !!! This is the header for a control that lets the user select the bright white color for text displayed on the screen. - ßѓїĝћť ýèļŀōώ !!! + Вříĝħŧ ÿéĺĺóẃ !!! This is the header for a control that lets the user select the bright yellow color for text displayed on the screen. - Ċџŕŝøґ çóĺőѓ !!! + Ćμŕšσґ ćθŀθя !!! This is the header for a control that lets the user select the text cursor's color displayed on the screen. - Ċŷăń ! + Сÿāⁿ ! This is the header for a control that lets the user select the cyan color for text displayed on the screen. - ₣őř℮ġŕоμŋđ !!! + ₣οŕėĝřσűлđ !!! This is the header for a control that lets the user select the foreground color for text displayed on the screen. - Ģѓēёи ! + Ġязêή ! This is the header for a control that lets the user select the green color for text displayed on the screen. - Ρυŗρŀє ! + Рµгρļě ! This is the header for a control that lets the user select the purple color for text displayed on the screen. - Şēłэ¢тíοñ ьά¢кğѓбυňď !!! !!! + Š℮ŀзςŧίθл ьâĉĸġřōŭńδ !!! !!! This is the header for a control that lets the user select the background color for selected text displayed on the screen. - Ѓèð + Ŗ℮ď This is the header for a control that lets the user select the red color for text displayed on the screen. - Ẁнíтė ! + Щнϊтè ! This is the header for a control that lets the user select the white color for text displayed on the screen. - ¥ěłļбẅ ! + Υéĺľоŵ ! This is the header for a control that lets the user select the yellow color for text displayed on the screen. - Łâⁿğμąĝе (řзqũìѓěş ѓéℓāúŋčħ) !!! !!! !! + Ŀąʼnģüǻğз (řêqџĭŗэś яёłдũⁿ¢ħ) !!! !!! !! The header for a control allowing users to choose the app's language. - Ѕēłêĉţś á ðіśрĺãγ łàиĝυâğę ƒŏř ŧн℮ άррĺіċâţĭоņ. Ťћîŝ шįľļ ονėŕřìðě ỳõυѓ ďеƒãџĺт Windows ïňťеґƒäĉέ ĺáлģΰâģε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ѕęļéćŧѕ ā ďįŝρℓду łàйģцâğέ ƒǿř ťћз ǻρрľïčдŧíòʼn. Ťħϊş ωіŀľ όνëґŗίďē уǿüř ďèƒăμľţ Ẃïñďбŵś įñτéŗƒαсэ ŀåŋġύдğé. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description explaining how this control changes the app's language. {Locked="Windows"} - Ũѕé śỳśť℮м ďëƒäμļť !!! !! + Ůŝê ѕŷŝτėм đєƒāùľŧ !!! !! The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead. - ∆ĺщãγѕ ѕнοщ ŧąвš !!! ! + Άľώāŷš şђǿŵ тãвŝ !!! ! Header for a control to toggle if the app should always show the tabs (similar to a website browser). - Ẅħєň δîśάьł℮δ, тĥê тáв ъдŗ щϊŀĺ ǻрρęáѓ ẃћэπ д ňεŵ ţáъ ίѕ ĉґєάťєď. Ćάйņōŧ ве ðîşäьĺéð ẅћîĺε "Ĥϊδê тђé ťїťļе ъãŕ" ιŝ Ώη. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Шħεń ďίѕªъľеð, ţĥё ŧāъ ъаŕ ώіļℓ аρρëâř ẃн℮ŋ ª ŋëẁ ťаъ ιš сгęǻţеď. Ćâиňбŧ ьε ďîśαъŀ℮đ ωħϊŀē "Ĥίδē τђę ţĭτľέ ъάѓ" įś Ωń. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "always show tabs" setting does. Presented near "Globals_AlwaysShowTabs.Header". This setting cannot be disabled while "Globals_ShowTitlebar.Header" is enabled. - Ρθšΐτιöņ øƒ ŋéωłỳ çřĕāŧěð ťãъŝ !!! !!! !!! + Ρόšîŧϊøņ őƒ йĕŵℓу ĉřēάţεđ τдьš !!! !!! !!! Header for a control to select position of newly created tabs. - Ѕρє¢ĩƒιεѕ ẁħєŕě ņĕẃ τªвš дφφèåг ĭи ťћě тåъ ґǿω. !!! !!! !!! !!! !! + Śφзćїƒíеş шĥëřē ⁿêŵ τâъś ªρρёáґ ĭп тћé τåв яøш. !!! !!! !!! !!! !! A description for what the "Position of newly created tabs" setting does. - Ńťεŕ ţђê ļªšţ ŧąв !!! !! + ăτёř ţнė ĺąśţ τаъ !!! !! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the last tab. - Λƒτěґ ţђз çųřŕёŋŧ ŧдь !!! !!! + Αƒŧëѓ ťнė сúѓŗėπτ τăъ !!! !!! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the current tab. - Ąũтоmатïсáłŀу сθφý śēļëçŧïôи τó çļιρъόдřď !!! !!! !!! !!! + Āúтǿmаţιĉąľľў сοφý şèℓэċтïøñ ţō ĉļĩρьóąґď !!! !!! !!! !!! Header for a control to toggle whether selected text should be copied to the clipboard automatically, or not. - Áμτοmáτîċǻŀĺγ đęťёçť ЏΓĿŝ áʼnð мāķё ţђέм ¢ļĭċќдьĺέ !!! !!! !!! !!! !!! + Λυτοмдτìćăŀŀÿ đзťēćτ ЦŖĿš аňđ máĸë ţнэм сŀĭçќάвłĕ !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should automatically detect URLs and make them clickable, or not. - Řëмσνē ťŗåίľїήġ ẁћĩτē-śрāčέ ϊņ ŗёçŧäⁿğùĺдř ŝэļзċτïóŋ !!! !!! !!! !!! !!! + Ѓëmονĕ тŕáìĺїʼnġ ẃђϊτë-ѕρá¢ę ĭл ѓеċτåηğцĺăŗ ş℮ŀěčŧĭõň !!! !!! !!! !!! !!! Header for a control to toggle whether a text selected with block selection should be trimmed of white spaces when copied to the clipboard, or not. - Γемоνė ŧŕǻіŀīñģ ŵнΐţē-ŝρäċе ẅђеñ φâšŧĭʼnĝ !!! !!! !!! !!! + Ґεmθνе ťѓªįŀíʼnĝ шĥіťе-šрäčė ẃнзň φâśťίņġ !!! !!! !!! !!! Header for a control to toggle whether pasted text should be trimmed of white spaces, or not. - Взľбẅ αяė ţнě ĉùŗřеņтℓγ вóûŋď кęуş, ẅĥΐçђ ċąη вĕ mбδīƒīέđ ъỳ έðĭŧĩňğ τнě ĴŜǾ∏ ŝĕŧťîńġś ƒιľέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Вêļσώ áяэ τĥє сûѓгεπтľý ъόůηð κєÿş, ώĥīćн čāñ ъз мοðïƒīěð ьý зðїŧįńģ τћė ЈŜÖΝ şзťŧίñğѕ ƒΐłέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer located at the top of the actions page that presents the list of keybindings. - Ðёƒāμĺτ φгǿƒīłé !!! ! + Đėƒªûľť рґøƒϊľê !!! ! Header for a control to select your default profile from the list of profiles you've created. - Ρřòƒìĺę тĥат öρëйş шћêʼn ĉŀϊςκîʼnğ ţħе '+' íċóп όŗ вў ťýφįиġ τћė иęш тâъ кέÿ вΐŋďíʼnģ. !!! !!! !!! !!! !!! !!! !!! !!! + Ρŗσƒιℓê тнǻт ōреŋś ώнèи ¢ļíčкïñğ ťћέ '+' ίçôи ŏґ ьў ţýрϊńğ тĥє ņěẁ тªв ќèý ъϊήðїńġ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the default profile is and when it's used. - Ðёƒâцľť ťêгмîйάļ аρρľϊςαтįőʼn !!! !!! !! + Ðêƒάϋℓτ ŧέřmìпάℓ аρφĺΐςàţїōŋ !!! !!! !! Header for a drop down that permits the user to select which installed Terminal application will launch when command line tools like CMD are run from the Windows Explorer run box or start menu or anywhere else that they do not already have a graphical window assigned. - Ŧĥê τéŕmϊʼnдℓ āρρĺìčåťĭŏή τĥåт łàцπсħёś ẃĥëπ ǻ čσmмäñδ-ℓιń℮ áρφĺĩ¢åţĩöⁿ ĩš яúņ ẁīτĥθũτ ąʼn èхĩśŧΐиġ śēŝśιόň, łíķę ƒřőм ťħë Şтăřт Мεⁿũ οŕ Ŕύп ðіαļоġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - Ŕеδřαω ēлŧįřê ѕćяéэи ώнęή ďîѕφļáÿ űрďåτêš !!! !!! !!! !!! + Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - Ẅħëņ ďīѕάьℓĕď, тĥ℮ тєŗmίⁿάł ẃїĺĺ ř℮ńđèŗ σйłỳ τĥè ϋрđǻτєŝ ţσ тнë şċѓëεπ ъĕťẃээⁿ ƒřámêś. !!! !!! !!! !!! !!! !!! !!! !!! ! + Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". - Çóļцмлš !! + Ċσŀùмñѕ !! Header for a control to choose the number of columns in the terminal's text grid. - Ŕóẃś ! + Ѓõшŝ ! Header for a control to choose the number of rows in the terminal's text grid. - Ĩñíŧĭąĺ €óľμмńš !!! ! + Îйϊŧìãľ Çòłųмπş !!! ! Name for a control to choose the number of columns in the terminal's text grid. - Ίņĭŧįáℓ Γǿшś !!! + İńìťîáℓ Ŕőώś !!! Name for a control to choose the number of rows in the terminal's text grid. - χ рòśίťιŏπ !!! + χ φόśΐтįóñ !!! Header for a control to choose the X coordinate of the terminal's starting position. - Ύ рǿѕіτìõи !!! + Ỳ φôѕΐтĩσп !!! Header for a control to choose the Y coordinate of the terminal's starting position. - Х φõşїтĭőη !!! + χ φŏѕĭтϊόŋ !!! Name for a control to choose the X coordinate of the terminal's starting position. - Εņţєŕ τħê νàłůè ƒøŗ ŧћє ж-čǿóґďïⁿат℮. !!! !!! !!! !! + Ëʼnŧëř ţнę νáľυé ƒóŗ τђę х-čōóѓđΐήâтë. !!! !!! !!! !! Tooltip for the control that allows the user to choose the X coordinate of the terminal's starting position. @@ -356,11 +356,11 @@ The string to be displayed when there is no current value in the x-coordinate number box. - Υ φǿśіťίǿл !!! + Ỳ ρθŝìŧιοπ !!! Name for a control to choose the Y coordinate of the terminal's starting position. - Ĕņтэř ŧнэ νãļùе ƒθґ ŧħĕ ỳ-ĉőöŕδïπáţέ. !!! !!! !!! !! + Ёйŧëŕ ţћε νàłüέ ƒòŕ ťĥє ỳ-čоθґδĩπāтė. !!! !!! !!! !! Tooltip for the control that allows the user to choose the Y coordinate of the terminal's starting position. @@ -368,538 +368,538 @@ The string to be displayed when there is no current value in the y-coordinate number box. - ₤зτ Щΐиðøẃѕ ðèċįðз !!! !! + ₤êτ Ẃĩиðōẁŝ ðē¢îðε !!! !! A checkbox for the "launch position" setting. Toggling this control sets the launch position to whatever the Windows operating system decides. - Īƒ ėлªвĺёδ, ūѕè ţħė šŷşŧëm ďэƒāџľţ ļāύήсħ φöѕīтίθŋ. !!! !!! !!! !!! !!! + Іƒ éлдъłëđ, úśз тђé ѕýŝт℮m δέƒăµłŧ łäúηćћ φöšïţϊóň. !!! !!! !!! !!! !!! A description for what the "default launch position" checkbox does. Presented near "Globals_DefaultLaunchPositionCheckbox". - Ẃнēи Ťεřmιйàℓ šţдґτś !!! !!! + Ẃђéñ Ţëгмїńªĺ ѕťářтš !!! !!! Header for a control to select how the terminal should load its first window. - Ẃђăť şħõύļð вэ şћöωń ώнėņ тћ℮ ƒϊяšŧ тĕřміʼnªľ īš ςгęåťêđ. !!! !!! !!! !!! !!! ! + Ẁђăт şĥõцĺð вε ѕнбшп ẁћёй ťђё ƒîŗŝт тěřмΐйåł їś сѓéąţзď. !!! !!! !!! !!! !!! ! - Όρεň å τâв ώΐţћ ŧђз ďĕƒάüļτ рřоƒīŀέ !!! !!! !!! ! + Οрзη à ťáь ώΐţĥ ţђè δεƒāűĺţ ρяθƒίłё !!! !!! !!! ! An option to choose from for the "First window preference" setting. Open the default profile. - Öρęņ ŵîиďόшѕ ƒřŏм а ргéνįóûş šęšŝĩбⁿ !!! !!! !!! ! + Орéŋ ẅϊηďóωš ƒřōm ă φяèνιôùś şęŝśîőň !!! !!! !!! ! An option to choose from for the "First window preference" setting. Reopen the layouts from the last session. - Ĺǻϋйċћ мōđ℮ !!! + ₤дΰⁿçћ mοďè !!! Header for a control to select what mode to launch the terminal in. - Ĥοẃ ţнэ ťéяmīⁿåĺ ωïℓł ąφρзάř ŏñ ŀäũлčн. ₣õċűş шīľŀ ħΐδ℮ тħё тãъŝ άπð ţіŧŀě ьаг. !!! !!! !!! !!! !!! !!! !!! !!! + Ηóώ ţĥè ţěѓmϊиаľ ẁίĺĺ àρρ℮åг σⁿ łåŭⁿ¢ĥ. ₣οċύŝ ẁīłł ђїδė ţђê ťªьş āńð тιŧľ℮ вăŕ. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ĺåμʼnćн ραѓämёťэřŝ !!! !! + £ăŭʼnсн рąґăмěţеґŝ !!! !! Header for a set of settings that determine how terminal launches. These settings include the launch mode, launch position and whether the terminal should center itself on launch. - Şётťΐήğѕ ţĥåτ ςоŋţґőł нθŵ ţĥė ŧëгмίʼnàℓ ĺàύńċћёš !!! !!! !!! !!! !! + Ѕèттîŋĝś ŧħªŧ ςôňťѓσŀ ĥõщ ţнè ţĕŗmîйåℓ ℓăµňĉнэś !!! !!! !!! !!! !! A description for what the "launch parameters" expander contains. Presented near "Globals_LaunchParameters.Header". - Ĺαüйςђ мŏðé !!! + Ŀäцñсн mőď℮ !!! Header for a control to select what mode to launch the terminal in. - Ηöш ŧнё τэяmίйäļ ώιłł àρφєдя σⁿ łàŭηĉħ. ₣ő¢цş ẁιĺℓ нïďĕ ţнé тâвś äʼnδ ţïţłê ъăѓ. !!! !!! !!! !!! !!! !!! !!! !!! + Ηōŵ ţнέ тěřmіηªĺ ẃΐŀľ ąρρëäґ öñ ļǻûй¢ћ. ₣όςµŝ ẅїļł ħĭďē τћε ŧªъš āηď ŧìťℓέ ьář. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Đєƒάцĺŧ !! + Ďěƒάυŀţ !! An option to choose from for the "launch mode" setting. Default option. - ₣ùĺĺ şċяěêή !!! + ₣ųľľ ѕ¢řέей !!! An option to choose from for the "launch mode" setting. Opens the app in a full screen state. - Мą×ímĭźέđ !!! + Μâ×įміźέď !!! An option to choose from for the "launch mode" setting. Opens the app maximized (covers the whole screen). - Ñέш їηśťдпčé ьέђäνíòř !!! !!! + Νēщ ίńѕţãиςé ьęĥâνΐог !!! !!! Header for a control to select how new app instances are handled. - Ĉŏηтřöļŝ ħŏщ пëω ťéřмïήдĺ įлѕţªⁿčзś ãŧτâ¢н το έ×ϊśŧϊπģ ωĩŋďöẃş. !!! !!! !!! !!! !!! !!! + Ĉöñтŕбℓš ћøш иēŵ τêгmîⁿàļ ĩπšţáπςεŝ άťţдčн тö є×ĭšτіņğ ώілđøẃş. !!! !!! !!! !!! !!! !!! A description for what the "windowing behavior" setting does. Presented near "Globals_WindowingBehavior.Header". - Ĉŗзäţ℮ â иεш ώĩŋđбω !!! !!! + Ćяèàťέ д ήéẃ ẃĭлďőω !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances create a new window. - Άţţǻçħ ţò ťħê мбšŧ гęсėñţĺỳ ϋŝēð ẁîήďøώ !!! !!! !!! !!! + Ăţţăčĥ ŧó тħέ mõŝŧ řěçèпŧļý ŭśëď ẃįňδσω !!! !!! !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window. - Âťťàçħ ţõ тĥе мōśτ řěсèйтĺγ ŭşėδ ŵīŋδθẃ őʼn ŧĥϊŝ ďēѕĸţóφ !!! !!! !!! !!! !!! ! + Äţŧαĉђ τǿ τħе mοѕт ѓěςęńτļγ ûѕєď ŵíñðοω øи тћϊѕ ďёšкτǿφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - Тħĕѕе ѕęťťíňģѕ мāý ъè ùѕзƒüļ ƒόѓ ŧгôύьļėşћόόťĩηġ âņ іśśűэ, ĥбωэνêř тĥєу ŵіľĺ ĩmρâćτ ŷôцг φĕґƒōґмªńčë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τћέŝė ѕëτтίņģš мâу ьê üѕēƒµľ ƒόґ τřóųьŀėѕĥбοτілġ ǻл іŝśü℮, нσώéνєŕ τĥêў ŵīŀł ΐmρåςť убŭя ρěгƒόґмåñĉę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. - Ήĭδë ŧћέ тïťłë вäѓ (ѓзqüïгєŝ ŗéℓâūň¢ħ) !!! !!! !!! !! + Ĥìđε тħê τīţĺё ъªř (ŗėqūΐŗêś яеľаϋʼnčћ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. - Ẅħеñ δіѕªъĺєð, ŧнέ ţιťłê ъαя ẃīļļ аρφėαг авŏνé ŧћэ тāвś. !!! !!! !!! !!! !!! ! + Ẃћèп ðϊŝαъłėð, тнė ťĭτłê вąѓ ẁιĺł ąφφёǻŕ äвöνė ŧħė ťãьś. !!! !!! !!! !!! !!! ! A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Ůѕε ăčѓÿℓїč mαтέѓįάł ĭń тħε ťàъ ѓσŵ !!! !!! !!! ! + Ūŝē àςѓýłīć мãτęѓįάł ϊή ťнę ťāъ гоω !!! !!! !!! ! Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Ûšĕ áсτĭνĕ τёŗмΐⁿàŀ τįţļë áś ăφφĺïĉªţįθň тіτĺє !!! !!! !!! !!! ! + Ůŝě å¢ťįνέ τèґmĭʼnαĺ τįτľé äš дρφℓīĉатĭǿʼn ţιţľē !!! !!! !!! !!! ! Header for a control to toggle whether the terminal's title is shown as the application title, or not. - Ŵђęñ đīśåьļěð, ţнє тιťŀє ьãя ẃιłł вë 'Ťēřмϊиåł'. !!! !!! !!! !!! !! + Ŵћéή đíѕǻьļεď, тħè ťįτłе ьǻŗ ẁїℓł вє 'Ťěѓмĩⁿăĺ'. !!! !!! !!! !!! !! A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"} - Śпâφ ẃϊиδбẅ řéѕíżíⁿĝ τō ċħªґªçţèѓ ğřįď !!! !!! !!! !! + Ŝиάρ щίйðõщ яēşΐžíпĝ тõ čħáѓä¢ťέѓ ģѓĩδ !!! !!! !!! !! Header for a control to toggle whether the terminal snaps the window to the character grid when resizing, or not. - Ŵнэπ δϊşåъłёδ, тнē ẁїπđõŵ ωіļŀ řεѕìżέ ѕmόοτнłγ. !!! !!! !!! !!! !! + Ẅн℮ñ ðìşāьļĕð, τĥė ŵΐņďòώ ẃíļĺ řėŝîżē šмõόтнłγ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - Ūŝэ ѕοƒтŵάгē ґèňďĕřιňģ !!! !!! + Ųѕε ŝόƒťщаřė яĕŋðεříлğ !!! !!! Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - Ẃĥ℮π ěлàвļ℮đ, ťĥз ţèŕмïñąℓ ẃΐłł ůšê ŧћз ѕθƒťшăŗę ŗέŋδêŕěŗ (ǻ.ĸ.â. ШĂЃΡ) ΐпšτёǻð õƒ ţћė ħâгđщåг℮ òиэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ŧнé šбƒţẅāяē ґэпδėѓεŕ (д.к.ă. ẀÅЃΡ) ĭñŝτéāđ оƒ тħё ĥάґđẃаяē οлε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Łąüπçђ ôπ mªčћΐηз şτаяţυρ !!! !!! ! + ₤áϋпςħ ôл мαςĥĭñę ŝťąřťûρ !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. - Αũтθмǻтίςàℓľý ĺāũиćћ Тēřмįʼnàŀ ŵĥэņ уõŭ ℓбğ ĩⁿ ŧő Windows. !!! !!! !!! !!! !!! !! + Āũŧοмªτĩčäĺļỳ ŀªūⁿ¢ћ Ťëŗmįŋāℓ ẃнęņ ўоű ŀóġ ìπ τσ Шĩņðоẁѕ. !!! !!! !!! !!! !!! !! A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header". {Locked="Windows"} - €êńтέя℮δ !! + Ćêŋťєŕеδ !! Shorthand, explanatory text displayed when the user has enabled the feature indicated by "Globals_CenterOnLaunch.Text". - Ċ℮йť℮ŗ øň ľªűйсн !!! ! + Ćēňτєѓ ŏŋ ľäūńçђ !!! ! Header for a control to toggle whether the app should launch in the center of the screen, or not. - Ẅнэй εйăъłèð, ťħê ẅιⁿđоŵ ẁíŀŀ ьē ρļàćёď ιп ťĥ℮ ĉ℮ňťέŕ òƒ ŧћê ščг℮ĕň ẅђзп łäůπснĕδ. !!! !!! !!! !!! !!! !!! !!! !!! + Ẁђēи ėⁿąьĺëđ, ţħė ωīņďбώ ώїłľ ъė рℓªςέð ïⁿ ŧĥε сęⁿтєř ǿƒ ŧħè ŝćŕėęή ẁћêʼn ĺдűňĉĥεđ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "center on launch" setting does. Presented near "Globals_CenterOnLaunch.Header". - Αĺẅάỳš ои τòр !!! + Аŀẅâγš ōп ŧбφ !!! Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled). - Ŧęгмĭпåľ ẅΐℓľ äļщąγŝ в℮ τћē тόφmоšţ ώìʼnδøẃ бʼn ţĥĕ ðęšкτőρ. !!! !!! !!! !!! !!! !! + Ťèřmĭήäľ ŵϊłļ àļщāÿş ъĕ ŧħє ţóрmόѕŧ шíñđθώ όи ŧħě δεşĸţбφ. !!! !!! !!! !!! !!! !! A description for what the "always on top" setting does. Presented near "Globals_AlwaysOnTop.Header". - Ťáв ẁιðťђ mθδë !!! ! + Ťâв ώΐđťн mбðě !!! ! Header for a control to choose how wide the tabs are. - Ćοмрǻсŧ щїŀł ѕħřĭńĸ īйªčŧĭνз τäьş ŧό ţĥє šϊżе õƒ ţĥе ιĉőπ. !!! !!! !!! !!! !!! !! + Ćôмрáçτ ώĭĺļ ŝнгíñк ïŋãċţïνе тâъѕ ťö тĥε ѕіźë ŏƒ ŧн℮ ĩćóň. !!! !!! !!! !!! !!! !! A description for what the "tab width mode" setting does. Presented near "Globals_TabWidthMode.Header". 'Compact' must match the value for <Globals_TabWidthModeCompact.Content>. - Čόмράсτ !! + Ċøмрªĉť !! An option to choose from for the "tab width mode" setting. When selected, unselected tabs collapse to show only their icon. The selected tab adjusts to display the content within the tab. - Εqυàł ! + Εqüáł ! An option to choose from for the "tab width mode" setting. When selected, each tab has the same width. - Тìŧℓэ ľэņġŧĥ !!! + Тīτľе ľεňĝτђ !!! An option to choose from for the "tab width mode" setting. When selected, each tab adjusts its width to the content within the tab. - ∆φρĺîςąтіøπ Ţћ℮мè !!! !! + Αрρłΐćàтīòñ Ŧнèм℮ !!! !! Header for a control to choose the theme colors used in the app. - Đâřĸ ! + Đăгќ ! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. - Úѕё Ẃîήðòώѕ τђзмэ !!! !! + Űŝ℮ Щįņđоẃś тћěmе !!! !! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. - Ľіġћт ! + Ĺιģћť ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. - Đάřκ (Ľзġªçŷ) !!! + Ďāґќ (₤єĝåçŷ) !!! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. This is an older version of the "dark" theme - Ùşė Шìлďθŵѕ τћεmě (Ľēĝаçÿ) !!! !!! ! + Ùśê Щΐʼnδøщŝ ťħéмě (Łèĝąçу) !!! !!! ! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. This is an older version of the "Use Windows theme" theme - Ĺίğħť (Ļĕğâčÿ) !!! ! + Ŀїģнţ (Łεĝäċÿ) !!! ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. This is an older version of the "light" theme - Ẅθґď ďεľіmΐτзŗŝ !!! ! + Щοґδ đэļιмϊŧеѓş !!! ! Header for a control to determine what delimiters to use to identify separate words during word selection. - Ţĥзśε şÿмвоłś ẅιℓĺ ьё џşєđ шĥèņ ỳòú ðσυвĺë-¢ľïčķ тзжт įπ τђэ тėгмίηåł όř дсţіνáţ℮ "Мåґк móδέ". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ţћзŝ℮ ѕумвöŀѕ шιŀℓ вê ùŝеď ẃнёñ ỳõµ ďбùвŀэ-ċľϊčк ţεם ïⁿ ţћě τєґmїиаℓ ŏѓ αсτϊνãŧě "Мάřк mŏđë". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "word delimiters" setting does. Presented near "Globals_WordDelimiters.Header". "Mark" is used in the sense of "choosing something to interact with." - Λрρеąŕаиçє !!! + Λρρёªгąńсё !!! Header for the "appearance" menu item. This navigates to a page that lets you see and modify settings related to the app's appearance. - Соŀőг ŝčħеmзś !!! + Ċōľøя š¢нēměѕ !!! Header for the "color schemes" menu item. This navigates to a page that lets you see and modify schemes of colors that can be used by the terminal. - Ίņŧεґäćťìόл !!! + Íŋт℮řâçτîöй !!! Header for the "interaction" menu item. This navigates to a page that lets you see and modify settings related to the user's mouse and touch interactions with the app. - Śτāŕţūр !! + Ѕţâŕτűр !! Header for the "startup" menu item. This navigates to a page that lets you see and modify settings related to the app's launch experience (i.e. screen position, mode, etc.) - Ωφел ĴŜÖŊ ƒīĺé !!! ! + Фφëņ ЈŞŐŅ ƒϊĺэ !!! ! Header for a menu item. This opens the JSON file that is used to log the app's settings. - Ďėƒάüℓťś !! + Đэƒαűŀţş !! Header for the "defaults" menu item. This navigates to a page that lets you see and modify settings that affect profiles. This is the lowest layer of profile settings that all other profile settings are based on. If a profile doesn't define a setting, this page is responsible for figuring out what that setting is supposed to be. - Řěʼnδєŕιńģ !!! + Ŕэⁿðэŕίńğ !!! Header for the "rendering" menu item. This navigates to a page that lets you see and modify settings related to the app's rendering of text in the terminal. - Äćŧΐоήš !! + Ăςтιòñš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. - βāçķğřόцлδ øрãсïţỳ !!! !! + βά¢ĸĝŕõúήđ ǿрãĉĩŧÿ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - βăĉĸģяōΰńď ōφā¢ίту !!! !! + βāĉκğřôμηď óρăĉїŧỳ !!! !! Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Şёτś τђє θрαčїťý òƒ τћë ŵίηðǿш. !!! !!! !!! + Ŝεŧŝ ţђé ǿφąċϊτŷ ôƒ ťђé ẃіŋďοẁ. !!! !!! !!! A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header". - ∆δνάņсеδ !! + Дδναиċēδ !! Header for a sub-page of profile settings focused on more advanced scenarios. - AltGr åľįäѕιńġ !!! ! + ΛŀţĢř ąĺîàśιⁿģ !!! ! Header for a control to toggle whether the app treats ctrl+alt as the AltGr (also known as the Alt Graph) modifier key found on keyboards. {Locked="AltGr"} - βý đеƒäůľт, Windows τяęаťš Čтґℓ+Âŀţ ªś дй αŀįαś ƒοя ÄľτĜґ. Ŧħΐš šĕťтîηģ шìłľ óνèяґίđ℮ Windows' δéƒāцľт ьэћдνϊőř. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Βу ďёƒдџĺţ, Ŵϊńďŏшš τřεåτš Ċťґℓ+Аłτ åѕ αń àłĩάš ƒŏг ДŀŧĢř. Τħїś śёťтїлğ ŵíℓĺ õνёяřΐďё Ŵìʼnďôщѕ' ďэƒâüŀт ьĕђāνïŏř. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header". {Locked="Windows"} - Ţежţ ąйŧĩдļíåśīйğ !!! !! + Ŧεжτ āпŧїåℓїášĩňģ !!! !! Name for a control to select the graphical anti-aliasing format of text. - Ţ℮хţ άлтīāļíдѕíпģ !!! !! + Ŧėжţ âлтίāℓïãšìņĝ !!! !! Header for a control to select the graphical anti-aliasing format of text. - Ċθŋтřŏℓѕ ħоẃ ťєжт ìš ãŋτîāŀìαŝеď ΐń τђз ř℮лđĕяεř. !!! !!! !!! !!! !!! + €σŋťгǿĺś ноώ τë×ţ ĭŝ āηтįāĺįäŝзđ ïⁿ тнê ŕëⁿðęг℮ř. !!! !!! !!! !!! !!! A description for what the "antialiasing mode" setting does. Presented near "Profile_AntialiasingMode.Header". - Δłΐäşеđ !! + Ǻłìдšèδ !! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer does not use antialiasing techniques. - ClearType !!! + ĊłėāřŦўрε !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a "ClearType" antialiasing technique. {Locked="ClearType"} - Ğŕáŷŝčăĺĕ !!! + Ġѓǻỳşćãľ℮ !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a grayscale antialiasing technique. - Λррєāгǻйςę !!! + Äррéâřăйςё !!! Header for a sub-page of profile settings focused on customizing the appearance of the profile. - Вàčκġřöυπđ ίмãġê φдŧħ !!! !!! + Вãćκĝґõцńð ιмăġέ ратĥ !!! !!! Name for a control to determine the image presented on the background of the app. - Ъáčќģґøϋŋď ìmαğé φåτн !!! !!! + Βãĉķğѓθцňď ΐмάģ℮ φªţĥ !!! !!! Name for a control to determine the image presented on the background of the app. - Ъāςķĝřόûņð ίmαğ℮ φâτħ !!! !!! + βäċκġяǿúпđ ĩмåĝё φατн !!! !!! Header for a control to determine the image presented on the background of the app. - ₣іļê ľõćąτïõή όƒ ťђё ιmāġê űѕеð ìⁿ тђé ъāçќġяōűňδ σƒ тнε ẃїπďóщ. !!! !!! !!! !!! !!! !!! ! + ₣įĺė łóςąтιǿŋ òƒ ŧħê íмάğз űšéδ їʼn ťĥ℮ вǻ¢κĝяőųиď őƒ ţĥė ώĭŋδбŵ. !!! !!! !!! !!! !!! !!! ! A description for what the "background image path" setting does. Presented near "Profile_BackgroundImage". - Βáскğѓōúⁿð імáğэ ąłιğπmėńт !!! !!! ! + ßάсĸģŗǿџηδ ιmãģê αĺĭġήmēлť !!! !!! ! Name for a control to choose the visual alignment of the image presented on the background of the app. - Βăçķğŗоūŋď іmäĝė аℓīġʼnмēņţ !!! !!! ! + Бαčĸğяōΰηđ їмåĝэ åłĭğʼnмεлŧ !!! !!! ! Header for a control to choose the visual alignment of the image presented on the background of the app. - Ѕēţѕ ĥош τнз ьǻćĸģŕöůŋđ ĭмâġē āľіģʼnš тθ ţнê ъòūйðáґĭέѕ σƒ τнέ шīиđòŵ. !!! !!! !!! !!! !!! !!! !!! + Ś℮ţŝ ћöẅ τĥę ва¢κğŗθŭйδ ìmдğē αŀíģņś то τħę вóµʼnďăгĩěѕ öƒ τћз щĭńðǿẃ. !!! !!! !!! !!! !!! !!! !!! A description for what the "background image alignment" setting does. Presented near "Profile_BackgroundImageAlignment". - Ъőŧťσм ! + Βŏτŧøм ! This is the formal name for a visual alignment. - Ьбτтõм ŀêƒţ !!! + Βòττōм łéƒŧ !!! This is the formal name for a visual alignment. - Ьσťтом ѓіģĥť !!! + Вбŧŧŏм ŗίģнť !!! This is the formal name for a visual alignment. - Ćέήтęя ! + Ĉêñť℮ř ! This is the formal name for a visual alignment. - £ĕƒт ! + Ŀèƒţ ! This is the formal name for a visual alignment. - Ґīğђŧ ! + Ŗĭģħŧ ! This is the formal name for a visual alignment. - Тǿρ + Ťθφ This is the formal name for a visual alignment. - Τбφ ļĕƒť !! + Ţθр ļέƒτ !! This is the formal name for a visual alignment. - Ťóφ ґΐġнŧ !!! + Тøρ ŕĩġћт !!! This is the formal name for a visual alignment. - Вяøωšє... !!! + Ьřоẅşé... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ăđδ ņēẅ !! + Λďð ňéẁ !! Button label that adds a new font axis for the current font. - Äδδ пéẁ !! + Άðδ ⁿэщ !! Button label that adds a new font feature for the current font. - βªçķģřǿűиď їмăĝë брāçίтў !!! !!! ! + Βαċĸġґθũηď ϊmдġэ орάсιτу !!! !!! ! Name for a control to choose the opacity of the image presented on the background of the app. - βåĉкĝґóŭйδ įmäġē ōρàĉīτý !!! !!! ! + Βàĉĸġгòΰлð ϊmąġє бφáĉΐţÿ !!! !!! ! Header for a control to choose the opacity of the image presented on the background of the app. - Şëţś τħè őφåсιτỳ őƒ ťĥę ьâ¢ķğгσυήδ ìmāģë. !!! !!! !!! !!! + Ŝέтŝ тĥě ōφâĉіţŷ θƒ ŧħë ъâćќğŕоŭńð ĩмαĝę. !!! !!! !!! !!! A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". - Бâċќģгøųňð īмαğ℮ śтŗèţсн mόďé !!! !!! !!! + Вάĉĸġřόϋⁿð ĩмăğе şťŗ℮ŧĉђ мòδ℮ !!! !!! !!! Name for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Ъăċķĝřόůŋď іmäġē ѕťřэţсħ моðè !!! !!! !!! + ßäскġŗǿůπď īмâġэ şтŗĕтсħ мőďè !!! !!! !!! Header for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Şзťŝ ĥощ τĥє вąсķģřοųпđ їmάĝĕ îѕ ŗēŝΐžзð тǿ ƒίĺĺ τнę шιņðθω. !!! !!! !!! !!! !!! !!! + Šëτş ħσω ŧħё ьãćķĝгøµπð īмαğę ϊš řĕŝіżèď ţθ ƒĭŀł ťћέ ωїηđôẁ. !!! !!! !!! !!! !!! !!! A description for what the "background image stretch mode" setting does. Presented near "Profile_BackgroundImageStretchMode". - ₣ίℓľ ! + ₣ίľļ ! An option to choose from for the "background image stretch mode" setting. When selected, the image is resized to fill the destination dimensions. The aspect ratio is not preserved. - Йôйé ! + Ŋŏⁿé ! An option to choose from for the "background image stretch mode" setting. When selected, the image preserves its original size. - Ûñΐƒøям !! + Üⁿĭƒòгm !! An option to choose from for the "background image stretch mode" setting. When selected,the image is resized to fit in the destination dimensions while it preserves its native aspect ratio. - Ųⁿιƒõŕm тō ƒϊļℓ !!! ! + Úⁿîƒŏŕм τо ƒіļļ !!! ! An option to choose from for the "background image stretch mode" setting. When selected, the content is resized to fill the destination dimensions while it preserves its native aspect ratio. But if the aspect ratio of the destination differs, the image is clipped to fit in the space (to fill the space) - Ρѓσƒїℓє τэřmíňάтίõи ьêћǻνΐőґ !!! !!! !! + Рѓǿƒïłė ţέřmįпąťїоп ьэндνіõя !!! !!! !! Name for a control to select the behavior of a terminal session (a profile) when it closes. - Ρŗοƒîℓ℮ ţєґmїйãτìоñ ьěнãνīõř !!! !!! !! + Рŗσƒīļє τеŗmíŋªťιοñ вěђâνίøя !!! !!! !! Header for a control to select the behavior of a terminal session (a profile) when it closes. - Ĉĺõѕê ẁнëň ρŗō¢ĕŝş э×ïťš, ƒāιℓŝ, οŕ ćřαŝħзѕ !!! !!! !!! !!! + Сĺöѕę ẃђèи φяô¢єśş êжîţš, ƒäιļš, оř ¢řąŝнéś !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled (exit) or uncontrolled (fail or crash) scenario. - Čℓôşё øŋŀý ẃħзи φгöćêšŝ êхįτѕ śűςсєšśƒϋŀŀỳ !!! !!! !!! !!! + Сłбşê бńļγ ẅнēή рřόс℮śŝ ęжιτś ŝџçсĕšѕƒŭŀłў !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully. - Иένèѓ ĉłőşě άŭτőmåŧĭ¢äľłў !!! !!! ! + Ŋ℮νĕŗ ćļǿѕê äμτőмâťĩċªļŀў !!! !!! ! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal. - Δџτöмãτĩč !!! + õťøмäтïč !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal. - Ćŏłοř ŝċħёmē !!! + Çŏłőг ŝćнĕмε !!! Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user. - Ċомmâʼnđ ŀïηë !!! + €οмmǻпδ łįʼnε !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Ĉόмmªŋð ℓïňê !!! + Соммаňð ℓιπë !!! Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Çõmмåηď ℓϊņė !!! + Çŏммǻиδ ŀϊňè !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Ε×èĉűţàъļé úšëδ ϊň ťћê рřοƒίļè. !!! !!! !!! + Эхĕçΰтдьłё ūśëď įñ ţħè рŕòƒĭŀз. !!! !!! !!! A description for what the "command line" setting does. Presented near "Profile_Commandline". - βяóώŝё... !!! + ßгθшşє... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Čμŗšôѓ ħèіġнţ !!! + Сµгŝőґ ђēîĝħт !!! Header for a control to determine the height of the text cursor. - Šēтš τћэ φεгĉэņťáğе ћεїģнŧ öƒ тће ćúŕŝòř ѕţàѓτĩпĝ ƒгŏm ţћę ъøţтσm. Όʼnłý ẃôřķş ẁîтħ τĥė νΐпŧǻğε сųгšöř šђäρè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Šеτѕ тĥέ φегсеŋтåĝě ћēíġĥт öƒ ťĥέ ¢úґšόг ŝŧάřťīʼnģ ƒŕõм τћ℮ воţŧōм. Φņľγ шбŕķş ωітĥ τђĕ νĭŋťάġз ¢ūґşσř ŝнāφē. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "cursor height" setting does. Presented near "Profile_CursorHeight". - Çûřşθŗ śĥąφĕ !!! + Ćũяšòѓ ŝђàρĕ !!! Name for a control to select the shape of the text cursor. - Ĉϋŕŝőŗ śћàρē !!! + Ĉϋřšǿг śнâρε !!! Header for a control to select the shape of the text cursor. - Ņзνэř ! + Ñèνêř ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will never adjust the text colors. - Òņłỳ ƒőř čθĺοŕś ίņ τнę çóℓбг ѕсћĕmє !!! !!! !!! ! + Òňľỳ ƒθг çöłőґš ΐл ťħé сøłθґ ŝĉћ℮мё !!! !!! !!! ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table. - Дľŵдỳš ! + Åļωάỳš ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. - βаř ( ┃ ) !!! + Ъâг ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. - Εмρťŷ ьο× ( ▯ ) !!! ! + Емρţý ьǿ× ( ▯ ) !!! ! {Locked="▯"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an empty box. The character in the parentheses is used to show what it looks like. - ₣ĭℓŀеď вǿх ( █ ) !!! ! + ₣ιŀłęđ вσж ( █ ) !!! ! {Locked="█"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a filled box. The character in the parentheses is used to show what it looks like. - Ùŋðэŕśςǿřε ( ▁ ) !!! ! + Ùήđĕřŝċогё ( ▁ ) !!! ! {Locked="▁"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an underscore. The character in the parentheses is used to show what it looks like. - Vįйťąğё ( ▃ ) !!! + Vίпτâĝĕ ( ▃ ) !!! {Locked="▃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a thick underscore. This is the vintage and classic look of a cursor for terminals. The character in the parentheses is used to show what it looks like. - Ðθŭъľе ũиđēгşçöř℮ ( ‗ ) !!! !!! + Ďòûьĺē ύηđεґşćбяĕ ( ‗ ) !!! !!! {Locked="‗"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a stacked set of two underscores. The character in the parentheses is used to show what it looks like. - ₣øŋŧ ƒäĉε !!! + ₣øñт ƒâςę !!! Header for a control to select the font for text in the app. - ₣ŏπт ƒª¢з !!! + ₣øñŧ ƒдсέ !!! Name for a control to select the font for text in the app. - ₣ǿηŧ ѕîžė !!! + ₣ŏňτ şίźε !!! Header for a control to determine the size of the text in the app. - ₣óⁿτ ŝīżе !!! + ₣оňť şίżє !!! Name for a control to determine the size of the text in the app. - Ѕιžè σƒ тђę ƒôñť ĭŋ рôΐŋťѕ. !!! !!! !! + Šіžё ǿƒ тнé ƒõņτ їņ φσîйτş. !!! !!! !! A description for what the "font size" setting does. Presented near "Profile_FontSize". - Ļíŋĕ н℮іĝћť !!! + Ŀīйĕ ĥĕΐĝђŧ !!! Header for a control that sets the text line height. - £іňê нèìĝћт !!! + ₤ìŋè ħέίģћť !!! Header for a control that sets the text line height. - Ονэŕŕíðê ŧћę ľïŋē нéιĝĥţ оƒ ŧĥê ŧĕямíňâľ. Μĕàšũяёđ āś α мųłтїрļè όƒ тħė ƒǿηт ѕїżе. Ťђё δęƒäϋļŧ νǻłůέ đęρеʼnđş οп уόμя ƒбŋŧ ăлď ϊś μŝϋдļľŷ āґσůпδ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ονëřŗíďĕ тђэ ℓìʼnё ћëîğнτ оƒ ŧнè τёґмįņªļ. Мēáśυґ℮đ аś ª mϋłţìρℓè õƒ тне ƒõήτ ѕΐżë. Тђê ďέƒāųŀţ νâŀüē δέρěņđš ǿň ỳőυя ƒôʼnт ąʼnď ΐş υşūäľŀÿ àŗôüŋđ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "line height" setting does. Presented near "Profile_LineHeight". @@ -907,854 +907,886 @@ "1.2" is a decimal number. - βúïłтіⁿ Ğĺỳφђš !!! ! + Ьùïľŧïň Ģłуφĥś !!! ! The main label of a toggle. When enabled, certain characters (glyphs) are replaced with better looking ones. - Шнєй εйαъľëδ, тħè ťєřmĭлªŀ δřаωş ĉΰšτőm ğļỳрнś ƒŏŗ ъℓøск ёℓεмεňτ αňδ ьõх đяªщįπģ ςђāŕά¢ţёřѕ іиśŧєāδ õƒ ϋŝίήģ ťħέ ƒóйť. Ŧђìѕ ƒěăтűґè биĺγ ωбгќś ώнеη ĞΡŬ Δĉċėŀєřäţìоŋ ΐş âνàïłåъℓê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ẅħĕň еņªъℓёð, тћз τēгмïπдℓ ðѓáшş çύšτом ģļўφħѕ ƒǿя ъĺôсĸ éℓêm℮ήŧ àñð ъο× ðгдωΐπĝ čћдŗąςŧέѓś ìπŝţėãđ ŏƒ ũŝіⁿğ ťћę ƒбηŧ. Ţћιş ƒзаτџŕè οʼnĺŷ ωóгκŝ ẁĥéŋ ĢΡŬ Àςĉеŀèѓдτισñ ĩѕ ǻνãîļåвłę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. - ₣μĺℓ-ςбļŏя Εмојì !!! ! + ₣џłŀ-сθℓøґ Έmόјї !!! ! The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. - Шħêņ επáвℓзđ, Éмőĵί дґэ ðїşρłåγєđ ïп ƒΰłŀ сθŀόř. !!! !!! !!! !!! !! + Ẅнзп έňåвłёδ, Эмöĵī ãŕě ďíşρľǻỳęð ίñ ƒμŀℓ ćоļöŕ. !!! !!! !!! !!! !! A longer description of the "Profile_EnableColorGlyphs" toggle. - ₣бήт ωĕїĝћť !!! + ₣οиτ ẅэíğħť !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣οⁿт ώêϊģħť !!! + ₣õиτ ŵėíğĥŧ !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣őņт щ℮ìğнτ !!! + ₣оňτ ŵειġнт !!! Header for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - Ŝèťŝ ŧħę шēĭġħţ (ļīģћтπėśś õя ĥзäνīпēşѕ бƒ тħз ŝţґöκ℮ѕ) ƒōř ŧђé ĝìνеп ƒόⁿŧ. !!! !!! !!! !!! !!! !!! !!! ! + Ŝéŧѕ ţђέ ẁèϊģнť (łϊğħţⁿéşś όř ђзâνĩηзśş óƒ ŧĥė śťгόќęŝ) ƒθґ тħέ ġįνэи ƒőňţ. !!! !!! !!! !!! !!! !!! !!! ! A description for what the "font weight" setting does. Presented near "Profile_FontWeight". - Vагíдвĺё ƒŏηŧ ахĕѕ !!! !! + Vαгįдвłз ƒôηŧ á×ęś !!! !! Header for a control to allow editing the font axes. - Δďď оř яèmőνé ƒοпŧ ªхеѕ ƒǿŗ τћε ĝĩνэπ ƒόņť. !!! !!! !!! !!! + Āðδ θѓ ŗēмôνε ƒŏŋτ āжеŝ ƒóř ťћè ġīνęŋ ƒóήτ. !!! !!! !!! !!! A description for what the "font axes" setting does. Presented near "Profile_FontAxes". - Тђę šзĺзсŧеð ƒóňт нαŝ ňô νăгϊàвļê ƒôйť ά×ěѕ. !!! !!! !!! !!! ! + Тђέ šěļεċťєď ƒŏηŧ ћάś πø νäѓїдвłз ƒōπт αжęš. !!! !!! !!! !!! ! A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". - ₣σňτ ƒèąтűřзş !!! + ₣оʼnτ ƒзåŧŭŕέś !!! Header for a control to allow editing the font features. - Аđđ θř ѓέmбν℮ ƒθňτ ƒέάтцřēš ƒŏŕ тћэ ģïνėń ƒőηţ. !!! !!! !!! !!! !! + Ǻďð όŗ řěmòνě ƒóńт ƒєãťüгęś ƒθг тнĕ ĝĩνёŋ ƒόŋţ. !!! !!! !!! !!! !! A description for what the "font features" setting does. Presented near "Profile_FontFeatures". - Ťħе śёℓєçтěď ƒōňţ ĥâѕ ņō ƒбńт ƒéåτüŗěŝ. !!! !!! !!! !!! + Τћē ŝэŀëсŧзð ƒőйť нâŝ иο ƒòиŧ ƒėäţцŕэś. !!! !!! !!! !!! A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". - Ģęпеŗàľ !! + Ġеʼnėяâĺ !! Header for a sub-page of profile settings focused on more general scenarios. - Нīďë ρяоƒĭĺĕ ƒґθm đяσрδόщи !!! !!! ! + Ήîď℮ φябƒίļĕ ƒѓòm ďřóφδόшπ !!! !!! ! Header for a control to toggle whether the profile is shown in a dropdown menu, or not. - Іƒ эиäъľėδ, ţĥє φŕόƒīŀέ ẅιℓℓ ʼnøτ ǻрφėâг ĩп ŧћę ľīşţ σƒ φяóƒίłěş. Ťĥίŝ ςǻл ъε üşĕď тō нιđé đэƒдџĺŧ ρяõƒíļέš дńδ δγñãміćāļℓŷ ĝеήэѓаŧêð ρяōƒϊŀèś, ώђįℓё łёàνĩпğ τнем ïп γôùѓ ѕęтŧîηĝš ƒîłë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Їƒ ĕηάъℓěđ, ťĥэ φѓóƒїłę щíĺŀ иǿτ āφрĕдґ ΐŋ ŧћ℮ ŀīѕţ ǿƒ ρřŏƒīłėѕ. Ţђіş ςǻⁿ ъê ūšέð ťŏ ђĩðė δëƒǻūļт φґøƒíļέş аņð ďγñăмïĉâļℓÿ ģěиēяāŧεð ρřòƒίļзŝ, ẁĥιľє łэăνīⁿĝ ťĥèм ϊʼn ỳôцг śэťŧιйğŝ ƒιľë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "hidden" setting does. Presented near "Profile_Hidden". - Ŗűπ ŧħìѕ ряőƒіŀê ǻš Λďміиϊśţŕãŧõř !!! !!! !!! + Гµл тђįş φѓöƒιļэ åş Λđmįňίšтŕàтоя !!! !!! !!! Header for a control to toggle whether the profile should always open elevated (in an admin window) - Ĩƒ êηāвļêδ, тнě φŗóƒΐℓэ ẃïℓĺ ôрêή īⁿ äи Ąďмîʼn тēŕмįňàľ шīʼnđòẃ áũŧōмãτΐċãℓłу. Īƒ ţђĕ ςũřґэйť шїņđοώ їŝ åļґзàðỳ ŗµņņìňģ ǻş åďmίņ, įŧ'ľľ óφěŋ ïи тђĭş ωîйδõω. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ'ŀℓ σрèń ĩй тħїŝ ẅīйδõẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Ĥĩšťòѓў śïźê !!! + Нíşŧôŗỳ ѕιźë !!! Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Нïѕтояγ śіżē !!! + Ĥіśţόѓÿ šιžё !!! Name for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Τћё иümьēř őƒ ľįⁿěś äьόνе тђé όŋέş δίšρłаγĕđ ìⁿ ţћэ ŵίņδòẁ ÿöù ĉàи şсгσļľ ъàĉк τо. !!! !!! !!! !!! !!! !!! !!! !!! + Тђě ņůмвέґ όƒ ļïлèѕ àьόνэ τнέ ôиεŝ ďίşрℓâýèď ΐη ŧћз ẃΐлđóẃ ŷòц сåп şçřбŀľ вªćķ ťσ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "history size" setting does. Presented near "Profile_HistorySize". - İčόл ! + Îčόŋ ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - İĉöη ! + İ¢оń ! Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ίčθл ! + Į¢òп ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ęмǿјî őѓ īmάğё ƒіł℮ ļőĉäтΐôπ θƒ τћê ίćǿñ ΰŝęđ ΐñ тĥĕ ρгōƒϊłе. !!! !!! !!! !!! !!! !!! + Ęmőĵì õř îmаĝė ƒìĺĕ ļöĉǻтïòπ ǿƒ ŧђē ісǿⁿ ùšεð іń τĥε φřôƒιľê. !!! !!! !!! !!! !!! !!! A description for what the "icon" setting does. Presented near "Profile_Icon". - βřõώşē... !!! + Ьřòẁśз... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ρǻďδíŋģ !! + Рáðđĩñġ !! Name for a control to determine the amount of space between text in a terminal and the edge of the window. - Ράďďϊʼnġ !! + Ρáððìʼnğ !! Header for a control to determine the amount of space between text in a terminal and the edge of the window. The space can be any combination of the top, bottom, left, and right side of the window. - Ŝĕťş ŧħе ρãδδĩŋĝ άřóųπď тћê т℮жť ŵίťђïŋ ťнé ẅίņďοẅ. !!! !!! !!! !!! !!! + Śěтš тђ℮ ρªđδїлğ ăѓоūńđ ŧћέ τεжт ẃĭтħϊп τħέ ẃίņδθẁ. !!! !!! !!! !!! !!! A description for what the "padding" setting does. Presented near "Profile_Padding". - Яěţґò τęяmіпåł έƒƒёċтş !!! !!! + Яęтѓō тéѓmιйăĺ 냃έсŧŝ !!! !!! Header for a control to toggle classic CRT display effects, which gives the terminal a retro look. - Šђбẁ яεŧґø-şтýłэ ťεřmїηąļ 胃ęċтš ѕμ¢н ªš ğŀøώīлģ ţ℮жť ǻňð şĉăņ ļΐňзš. !!! !!! !!! !!! !!! !!! !!! + Śĥбẅ гзτřō-şтγłе τëѓmīⁿдĺ еƒƒέсτś şúćђ αş ğℓōώĩиġ ţέхť άηδ šςаи ľìиėš. !!! !!! !!! !!! !!! !!! !!! A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect". "Retro" is a common English prefix that suggests a nostalgic, dated appearance. - Λџţόмǻŧіčǻℓℓý ăδјūѕţ ĺïġћтņėŝŝ ŏƒ ìⁿđιşťĩηĝŭïśндъľę тё×т !!! !!! !!! !!! !!! ! + Аŭťοмäтĩčāĺłỳ ăδјΰѕť ļϊģђτπ℮şѕ ǿƒ їиδιšťΐņğűīşћāвłэ тêжť !!! !!! !!! !!! !!! ! Header for a control to toggle if we should adjust the foreground color's lightness to make it more visible when necessary, based on the background color. - Ãΰτοmáťìĉåļľγ ьґīğĥŧëŋѕ σг ðàгķēŋş ŧĕ×т τό mãķè ĭτ мòгè νìšìъļэ. Éνэŋ щĥėл ёήªъĺĕδ, ţћīѕ äðĵµѕтmèηţ ώіŀľ øлľў оċçύг ẁĥзп ά čőмвιйαтιŏⁿ òƒ ƒǿŕęġяöυйď åŋδ ьąćкģгőųпð çбļŏѓş шбŭłď ŕэѕüŀŧ ìи ρôŏя сοⁿŧŕąŝŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ∆ųţοmäţιċãℓľў ьŗϊġĥťëñś ǿŗ δåřķēиѕ ťěжŧ ťò mäќε ĭŧ mόґз νίşìъŀĕ. Зνёŋ ẃћéŋ ęηāьℓёδ, τћιѕ äđјυšŧměйŧ ẃìℓŀ õñℓў ôćčцг ŵђěи ǻ çøмъĩⁿãτĩθñ øƒ ƒбřεġґòϋňδ áηď вāсκģřŏŭпδ čółοřş ẁóΰłđ я℮şŭľτ ìñ ρőόŗ сõʼnтгáѕт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors". - Ѕсгбľľъάŗ νīśîьĭŀίŧÿ !!! !!! + Šсґσļľвąя νĩѕìъįłіťу !!! !!! Name for a control to select the visibility of the scrollbar in a session. - Şċѓόℓłьäř νìşĩвîłĩţỳ !!! !!! + Ščяòľŀвåŕ νίšιъįŀιτÿ !!! !!! Header for a control to select the visibility of the scrollbar in a session. - Ηíďď℮ñ ! + Ĥΐδđēⁿ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is hidden. - Vιşĩвĺέ !! + Vїşівłε !! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible but may dynamically shrink. - Λŀώдуś ! + Âℓώãýѕ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is always visible. - Śςŗθĺĺ ŧó îирμт ωнзń ţуφιňĝ !!! !!! !! + Śсгόľĺ ťσ їŋрυт ẃħęπ ţỳφįⁿģ !!! !!! !! Header for a control to toggle if keyboard input should automatically scroll to where the input was placed. - Ŝťàгτïŋĝ ðïґëçŧǿŕγ !!! !! + Ŝταяτìлĝ ðĩѓ℮ćτоŗỳ !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Šταґŧĩήģ ðΐřê¢τōřỳ !!! !! + Ѕťåřťіπġ đíгëĉţöґγ !!! !! Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Šτдŗτїⁿĝ διŗэćťόŗγ !!! !! + Šтдřтíⁿģ đϊŕёςŧôŗў !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Ŧђз đїřесτöřу ţнē ряόƒíļë šŧăѓτŝ ìň ẁħèń ïŧ ĭś ℓоãďзđ. !!! !!! !!! !!! !!! ! + Ţћé đĭŗéçťòŕу τħе рѓøƒĩŀе śťªřťş ïή ẅћĕл îţ ίѕ ŀбдδėď. !!! !!! !!! !!! !!! ! A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory". - βŗθωšé... !!! + Ьŕōωŝē... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ųѕέ φáŗéπт ρŗōсэѕѕ δīř℮ĉťояỳ !!! !!! !! + Üŝè φαŗèńŧ рřоĉěѕѕ đĭřё¢τоґý !!! !!! !! A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process. - Іƒ ëⁿåьľĕδ, тĥιś ρŕøƒïŀέ ẅιℓĺ ѕρāẃй ιη тĥë ďΐřêċτόŕỳ ƒѓòм ẅħįćĥ Ť℮řmĩлăĺ ŵąŝ ļåûηсĥèď. !!! !!! !!! !!! !!! !!! !!! !!! ! + ΃ έпãьłëď, тђìš ρѓòƒìĺè ωιĺℓ ŝρǻẁň іň ţћę ðіяеçţбгγ ƒřőм щћįςђ Теямĭņàł ẃªş ŀáцⁿĉħёđ. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox". - Нιđє ісθή !!! + Ηϊðё îçǿй !!! A supplementary setting to the "icon" setting. - ݃ εηăьľέď, ťĥίѕ ρяόƒϊŀέ ώįĺł ħàνė ηò íćōň. !!! !!! !!! !!! + ̓ ёηдъļэδ, ŧĥϊŝ φгōƒιℓз шіℓŀ ђдνê ňö ΐĉôй. !!! !!! !!! !!! A description for what the supplementary "Hide icon" setting does. Presented near "Profile_HideIconCheckbox". - Šűρφґзšѕ ţįтľε ĉħªņĝēŝ !!! !!! + Ѕΰφφřэśš τíťļė čħªʼnĝèѕ !!! !!! Header for a control to toggle changes in the app title. - Ìĝηоřę дφрļîсâţίбл ŕéqυέѕťş тǿ ĉĥáηğè ţĥе τΐτłë (OSC 2). !!! !!! !!! !!! !!! ! + Íġπόŗэ áρρĺіćâţìǿŋ ŗέqúёśţś ťò çђâηğě ťђê тįтŀé (ΘЅĆ 2). !!! !!! !!! !!! !!! ! A description for what the "suppress application title" setting does. Presented near "Profile_SuppressApplicationTitle". "OSC 2" is a technical term that is understood in the industry. {Locked="OSC 2"} - Таъ тĩţļε !!! + Τάъ ŧїŧļê !!! Name for a control to determine the title of the tab. This is represented using a text box. - Τâъ τΐŧłė !!! + Ťǻв τįťłэ !!! Header for a control to determine the title of the tab. This is represented using a text box. - Ґèрľдςэş ţĥе φřǿƒĭļ℮ ňǻmě áѕ τђē тιτĺέ ťö рдѕŝ ťò тĥ℮ śнéļℓ õп şţαŕťũр. !!! !!! !!! !!! !!! !!! !!! + Ŕèφłǻčèş ţће рřöƒîℓέ иάмз äş τне тíŧŀë ŧŏ φāѕş ŧό ţĥë şђëℓł öņ śťâгŧûρ. !!! !!! !!! !!! !!! !!! !!! A description for what the "tab title" setting does. Presented near "Profile_TabTitle". - Ũʼnƒσ¢ύŝéð αφрèάřąňсє !!! !!! + Ûηƒŏċμşεď äррėāŕαńćё !!! !!! The header for the section where the unfocused appearance settings can be changed. - Čŕéăτė Αρφзάŗаņçė !!! !! + Ĉřêάŧэ Δрφзάŕãñςε !!! !! Button label that adds an unfocused appearance for this profile. - Ďéľ℮ŧě ! + Ðéľėŧê ! Button label that deletes the unfocused appearance for this profile. - Èήâвℓë ąĉѓýļįč мâťěŗīåļ !!! !!! + Єňάьłē дсѓŷĺĭс mąτěřìªŀ !!! !!! Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Ãρρļïзŝ ā ŧřаπśℓΰćęπŧ τёхτμřэ ťô ţћê ьдćќĝґбύпď бƒ τнê шіńðòẃ. !!! !!! !!! !!! !!! !!! + Àφρļįëś ã ţґάήŝℓűсэиŧ тēжτυŗэ ťό тĥ℮ ъάćĸĝґõυήď õƒ ţћ℮ щϊηðǿщ. !!! !!! !!! !!! !!! !!! A description for what the "Profile_UseAcrylic" setting does. - Џѕę đёѕќťőφ ώаļľρаφέѓ !!! !!! + Ŭŝē đεśķτòр ẃąℓłραрĕя !!! !!! A supplementary setting to the "background image" setting. When enabled, the OS desktop wallpaper is used as the background image. Presented near "Profile_BackgroundImage". - Ūѕê ŧћę δěŝκŧőφ щãŀļрàρèŗ įмàġé åś τће вåċķġřόũňð ίмªġē ƒбг ťĥè ţεřmīπáļ. !!! !!! !!! !!! !!! !!! !!! + Ůšê тнè ďєśķťōρ ŵªĺļраφēř ímǻğё αś тħэ ъąĉκġřōůлđ іmãġè ƒόґ ŧн℮ ţэґмϊлªļ. !!! !!! !!! !!! !!! !!! !!! A description for what the supplementary "use desktop image" setting does. Presented near "Profile_UseDesktopImage". - Ďĭşςαяđ ςĥαñĝєѕ !!! ! + Ď욢αŗð ĉђăʼnġęѕ !!! ! Text label for a destructive button that discards the changes made to the settings. - Đίšсäřδ āļľ űņšανêď šέтťιйģś. !!! !!! !!! + Ðĭŝĉäŕð дĺľ цŋşåνёď ѕεťŧīńġś. !!! !!! !!! A description for what the "discard changes" button does. Presented near "Settings_ResetSettingsButton". - Ŝăν℮ ! + Śāνэ ! Text label for the confirmation button that saves the changes made to the settings. - ⚠ Ўöџ ħăνе ûńšάνэð ¢ħáńģεš. !!! !!! !! + ⚠ Υŏű нâνě цπśąνêδ ¢ђãпġēš. !!! !!! !! {Locked="⚠"} A disclaimer that appears when the unsaved changes to the settings are present. - Ąðđ α пêщ φѓôƒīŀė !!! !! + Ãđδ α ηèŵ ряоƒіľэ !!! !! Header for the "add new" menu item. This navigates to the page where users can add a new profile. - Ρѓŏƒíŀзѕ !! + Ρґőƒϊłеѕ !! Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items. - ₣оĉũş ! + ₣őćџŝ ! An option to choose from for the "launch mode" setting. Focus mode is a mode designed to help you focus. - Μā×ίмϊžęď ƒōçųś !!! ! + Μãхîмížęď ƒòćύŝ !!! ! An option to choose from for the "launch mode" setting. Opens the app maximized and in focus mode. - Μªжΐmĩžзđ ƒũłℓ śčŕёėи !!! !!! + Мª×įmίźėδ ƒύļĺ šсґèэη !!! !!! An option to choose from for the "launch mode" setting. Opens the app maximized and in full screen. - ₣űŀŀ şçґєзŋ ƒó¢ůş !!! !! + ₣üĺℓ śčřèéη ƒŏćŭś !!! !! An option to choose from for the "launch mode" setting. Opens the app in full screen and in focus mode. - Мǻ×іmΐžéđ ƒūℓł ѕčŗеèⁿ ƒσčůŝ !!! !!! !! + Μąжįмīźēď ƒµĺļ śсяé℮ⁿ ƒθčϋš !!! !!! !! An option to choose from for the "launch mode" setting. Opens the app maximized in full screen and in focus mode. - Ьέŀℓ иοŧïƒĭćªţїóи šŧỳļë !!! !!! + Бэĺł иőťíƒΐćªŧίøл ѕţуĺз !!! !!! Name for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - ß℮ĺļ ņŏţіƒї¢âтΐόή šţÿłé !!! !!! + Веĺł йθŧϊƒīçǻťΐθп ŝťỳℓé !!! !!! Header for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Ċöήτŗоℓś ωħαť ĥăрρепś шħєń ťђê дρρℓĩčäţιοń έмιţѕ ä BEL čħагä¢тëг. !!! !!! !!! !!! !!! !!! ! + Ĉøήτѓōℓѕ ẅнǻτ ђдρφéпš шђĕñ тнē ąρрĺιčάτĩоň емìťś а ЬΕ£ ćђãяд¢ţĕř. !!! !!! !!! !!! !!! !!! ! A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"} - £ãϋňčĥ тĥіş ąφрļΐςаţїóň шĩτн д иэώ έпνіѓσиmэŋŧ ъŀǿčķ !!! !!! !!! !!! !!! + £αüņ¢ħ ŧћΐš аρφℓîсäŧïōп ŵìţн α йéω ёňνΐѓõлmĕπţ ъłосќ !!! !!! !!! !!! !!! "environment variables" are user-definable values that can affect the way running processes will behave on a computer - Шћéŋ зήдвĺэđ, тĥĕ Τêгмĩлàľ ώįℓŀ ģêηėřąτё â лэŵ ęйνĭґοиmėņť вℓǿċκ ẅђęη ςřέαŧϊņģ пēŵ ŧáъŝ õг ρąņěš ωĩтħ ŧнíŝ рřόƒîĺέ. Ẁђэŋ ðϊśăъĺêđ, ťћē ťάв/φáⁿз ώïļł ìήѕτёąð ιпћεгīţ ţћë νãгіǻьĺэş ţђĕ Τεґмïňął ẅäş şτãѓτэđ ẃìŧћ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẁћέл εņаьŀèď, τħё Ŧëгmĩňªľ ẅīŀℓ ğεлěяαте å ʼnэщ еńνîřόńмęñт ьŀøĉĸ ŵħ℮л čґēãťîņğ ʼnεώ τâвѕ όя рªлёѕ щĭτћ тĥĭѕ ρŗòƒΐℓė. Ẅђêй ðìšąвļėđ, τћë ťąь/рáňε шîŀĺ íñśτėáđ īήђзŕίτ τђė νăяϊаьℓĕŝ ţђε Ťęřmíŋаŀ ẁαŝ śťäřťέδ ωįтħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - Êñåвľ℮ ёжφęŗīмéŋţàľ νΐřтϋåĺ ťéяміпаł рāѕśťħгòùĝĥ !!! !!! !!! !!! !! + Зńăьℓē эхφзґímėпτªŀ νіѓтűǻļ τ℮ґмϊñāļ φаśşтнґöúģн !!! !!! !!! !!! !! An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Ðįşφłąÿ ǻ мèηΰ όή řïĝĥť-ćľїçķ !!! !!! !!! + This controls how a right-click behaves in the terminal + + + Ẁђéŋ êπäвĺεð, ţĥё Τéŕмįⁿął ẃìļŀ ďïśρĺąỳ ã мέиц öη ŕìğĥţ-сℓĩ¢ķ. Ŵћëʼn đϊśªьŀèð, řιġћţ-čℓî¢ķιñĝ ẁĩłℓ ĉσрÿ тħē şèłэĉţĕđ ŧэжτ (ŏя рâŝтé ΐƒ ťћзяę'ś йб śзľĕćţîбñ). !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Ďιşрĺąŷ мǻŕĸş θй ţĥě ŝćřбŀŀъáŕ !!! !!! !!! + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Ẅĥέň ёпдъļêď, τĥ℮ Τëгmįиªł ẃĩľļ ðΐśρℓăỳ мāѓκś ιñ ţħє şĉѓòℓľьªŗ ŵħëⁿ ŝēãѓçђιŋĝ ƒθř ťèжт, σѓ ŵћзп šђêļŀ ίπтέğґãťîόи іś εŋäьłёđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + ∆űŧòмåťĩċǻłļγ маѓķ ρřǿmρŧş óй φґěѕşіиğ Еŋŧєř !!! !!! !!! !!! ! + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Ẃћеⁿ эņáвļēď, тħè Тěřмíиãŀ ǻµтοмăťìςǻℓľу åδď ã мâŕĸ іиđιςåŧïⁿġ тħé рσŝĩţίол öƒ ţђé ęņð øƒ τĥę ςбmмǻйδ ẃћéи ýθů φŕэѕѕ ēиŧęѓ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Ê×рэґїmзʼnτâļ: Гэрŏśΐťĩőⁿ τћέ ćũŕšσŕ ẁіτн mσůśё ċℓīċĸѕ !!! !!! !!! !!! !!! + This allows the user to move the text cursor just by clicking with the mouse. + + + Ẅћзή ℮йăъļэð, ĉľīćкїηġ ιňśīðè ŧĥё ρŕσmρŧ ωïŀļ мσνэ ţћё čúřšŏѓ тő ţħάŧ рōŝΐτíôⁿ. Ŧђΐŝ ŕéqűїřέѕ šћėľľ іñţεģřãţīǿň тø ъē зŋāвłêð ιπ убųѓ ѕћєľĺ τó ẃθѓκ ăѕ зжφĕčτēď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + - Ãüđīъłě !! + ∆ůðĭъĺę !! An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. - Ňőηė ! + Ñбʼnĕ ! An option to choose from for the "bell style" setting. When selected, notifications are silenced. - Ðïśαъłĕ рåйĕ ăʼnίmдтíόŋŝ !!! !!! + Đїŝâвŀè рăŋě ǻйįmąтіǿňŝ !!! !!! Header for a control to toggle animations on panes. "Enabled" value disables the animations. - Ьłáςќ ! + Вĺåčќ ! This is the formal name for a font weight. - Ъσĺď ! + Ъθĺď ! This is the formal name for a font weight. - €ūŝŧôм ! + Ćцѕτσm ! This is an entry that allows the user to select their own font weight value outside of the simplified list of options. - Êхŧřǻ-ßŀдςк !!! + Ĕжťŗā-Βļаċк !!! This is the formal name for a font weight. - Ёжŧгą-ßǿŀð !!! + Ёжţгд-Боļδ !!! This is the formal name for a font weight. - Σхтřà-£ιġĥţ !!! + Ę×тяā-£ïĝћţ !!! This is the formal name for a font weight. - ₤īğнτ ! + Ŀιğĥŧ ! This is the formal name for a font weight. - Мęđϊüм ! + Μєđιϋм ! This is the formal name for a font weight. - Ñόřmàℓ ! + Ňŏŗmªĺ ! This is the formal name for a font weight. - Śēмι-Вοľđ !!! + Şèмï-Вōłđ !!! This is the formal name for a font weight. - Šĕmĩ-₤ìġнт !!! + Ѕėmį-₤іģħŧ !!! This is the formal name for a font weight. - Тħĭπ ! + Ŧħīň ! This is the formal name for a font weight. - Ґèqûίŕęð ŀіġάţüѓęş !!! !! + Ŗéqùїяëď ľîģąτüřеŝ !!! !! This is the formal name for a font feature. - Ľōсаℓϊżèď ƒõямš !!! ! + £ǿčãłϊźэď ƒŏгмş !!! ! This is the formal name for a font feature. - Ĉǿмρøŝĭţĭôй/ðêčσmρőśíťįőη !!! !!! ! + Ćóмρőśĭťϊοņ/ðέċömφóšίτìøη !!! !!! ! This is the formal name for a font feature. - Ċóήťēםύдℓ ãℓŧёŗлдťэѕ !!! !!! + Сθⁿте×тüáŀ ǻℓţėŗпαтēŝ !!! !!! This is the formal name for a font feature. - Šтăʼnďàŗđ ľĭġάťùřëѕ !!! !! + Ѕτάлďάґð ℓíġąтŭѓεѕ !!! !! This is the formal name for a font feature. - Ćǿⁿтĕםŭάℓ ℓĩģàţűřёѕ !!! !!! + Ċоňťêжţũāł łіġăŧµѓèś !!! !!! This is the formal name for a font feature. - Γеqцíŕěď νäѓίãτΐōň āłτęґпåŧēŝ !!! !!! !!! + Ŕëqџìґêð νãяìâтïöп ªļтĕгñąţëš !!! !!! !!! This is the formal name for a font feature. - Κęřήïήğ !! + Ќεřпĩńģ !! This is the formal name for a font feature. - Μǻŕĸ рõşїτіθлįйğ !!! ! + Мăґĸ φбśїτìõйίήģ !!! ! This is the formal name for a font feature. - Μäѓк ţö мâŗĸ рσśìţїöйΐήğ !!! !!! ! + Мªяκ ŧθ máгķ φбşίţīōпîņĝ !!! !!! ! This is the formal name for a font feature. - Ðĩѕŧãń¢ě !! + Ďïşťªпčє !! This is the formal name for a font feature. - Ǻĉčëśš дℓľ ãĺťęгйαŧēš !!! !!! + Âĉċēśş αłł ǻĺтэяńáťėş !!! !!! This is the formal name for a font feature. - Ċаşè şейśìтîνе ƒόґмś !!! !!! + €αŝē śεⁿŝιţіνé ƒǿŗmŝ !!! !!! This is the formal name for a font feature. - Ďêиómіŋαţǿя !!! + Đēñômĭņąţόя !!! This is the formal name for a font feature. - Ťзŕmιηăℓ ƒôѓмѕ !!! ! + Ŧēгмιπáļ ƒόŕmś !!! ! This is the formal name for a font feature. - ₣ŕãĉŧįόлŝ !!! + ₣ŕãçŧїõήŝ !!! This is the formal name for a font feature. - Ίйíтīаł ƒόѓmѕ !!! + Īήíťĭàľ ƒõяmŝ !!! This is the formal name for a font feature. - Мзðідļ ƒόямš !!! + Μеďìαℓ ƒбřмš !!! This is the formal name for a font feature. - Ņümзѓåŧŏŕ !!! + Ňϋмēгªţőř !!! This is the formal name for a font feature. - Øřðΐŋаĺѕ !! + Οřďĩŋäℓѕ !! This is the formal name for a font feature. - Ŗēqůїґéð çóńŧĕхŧύǻĺ дŀťэŗňãţêş !!! !!! !!! + Ŗèqŭīŗеð ĉοйτęхťüàĺ áľţėřʼnãŧεŝ !!! !!! !!! This is the formal name for a font feature. - Šсïзйţĭƒîċ ìηƒéгĭǿřś !!! !!! + Śсϊèⁿťίƒιč ΐñƒєřīōŗŝ !!! !!! This is the formal name for a font feature. - Şцвşсřĩрτ !!! + Śûвś¢řîρţ !!! This is the formal name for a font feature. - Šűφєѓѕćяїрť !!! + Ŝùρεřśčřîφŧ !!! This is the formal name for a font feature. - Śĺаѕĥêď żêŗо !!! + Ѕĺáѕħëď żêґø !!! This is the formal name for a font feature. - Авθνέ-ъãşэ мâгκ рōŝΐŧіōńíńĝ !!! !!! !! + Λъŏνє-ъåѕέ мǻяќ φōśīτΐóņīлĝ !!! !!! !! This is the formal name for a font feature. - Ļдűⁿčн šϊżë !!! + Łáцηсħ šіžē !!! Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". - Ŧђ℮ ŋúмвęґ οƒ řøẅš аήδ ćǿļύмŋŝ đîśрľаγĕď íη ťĥĕ ωĩйďòŵ µροή ƒířŝτ ĺоàδ. Мέåšυřёď ìи сħăřąçŧèяѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Τĥé ήυмвēŕ θƒ гόωş äйđ ςôľũмņś đΐşφĺąŷęđ îй ŧĥê ẃįňďŏώ ųрбŋ ƒїяšţ ļŏăď. Μêάşµřēď īй ςħαŕāçτέřś. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "rows" and "columns" settings do. Presented near "Globals_LaunchSize.Header". - Łâµηčĥ ρθśιťĩōπ !!! ! + Ľâűπçћ φσѕíţįбʼn !!! ! Header for a group of settings that control the launch position of the app. Presented near "Globals_InitialPosX" and "Globals_InitialPosY". - Ŧћё īήíťιдŀ рôśϊťîôñ ōƒ ţħέ тĕґміпäł щїлδθш ùρöπ śťåгŧύρ. Ẁђěπ ℓªüйçђìňĝ āŝ ма×ĩmіżєð, ƒúℓĺ ś¢ŕ℮℮п, ôř ẅĩτђ "€ёņŧèг ôñ łąűηçћ" єлáьĺєð, ťĥíѕ ïš ύŝэδ ŧσ ŧàŕģēţ ŧђè mŏηίτóѓ òƒ įⁿт℮ґеşτ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ťнέ īηĭţϊąĺ φǿšĩтīòл őƒ ŧђę τĕѓмīηäł ωĭиđŏщ ϋρőʼn śτåřťϋр. Ŵнέη ľãцпçђìŋģ άŝ mã×ĩмιż℮ď, ƒµļľ ŝ¢ґэ℮ň, øř ẃĩτħ "Çĕпτеґ ои łåúŋ¢ђ" ĕņǻвℓėδ, ŧĥíŝ íś џşĕď ţö ţάřĝēŧ тħē моŋітòя σƒ įпťēґēśŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "X position" and "Y position" settings do. Presented near "Globals_LaunchPosition.Header". - Δℓľ + Äŀℓ An option to choose from for the "bell style" setting. When selected, a combination of the other bell styles is used to notify the user. - Vίѕцдℓ (ƒŀǻşђ ťāšќъăѓ) !!! !!! + Vĭśũάŀ (ƒĺāşђ ţąşκвдѓ) !!! !!! - ₣ŀαŝħ τâŝќьåя !!! + ₣ŀªśћ τâśќъäѓ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the taskbar is flashed. - ₣ľãşћ шįńđоẅ !!! + ₣łāŝћ ẅїňďŏщ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the window is flashed. - Ăδđ пëẁ !! + Äδď ⁿĕш !! Button label that creates a new color scheme. - Ð℮łéтε çóĺøŕ ѕċĥêmĕ !!! !!! + Ðєĺèŧĕ ¢оľōѓ ѕснεмė !!! !!! Button label that deletes the selected color scheme. - Ëďìт ! + Σδϊт ! Button label that edits the currently selected color scheme. - Ðέľèтė ! + Đеļėŧę ! Button label that deletes the selected color scheme. - Йåме ! + Ŋām℮ ! The name of the color scheme. Used as a label on the name control. - Ŧĥε ʼnάмē öƒ ţнέ ¢őℓòґ ѕçнęмê. !!! !!! !!! + Ŧħё ŋąмэ őƒ τћě ¢ǿℓôѓ śçђéмē. !!! !!! !!! Supplementary text (tooltip) for the control used to select a color scheme that is under view. - Đєļĕţè φѓσƒιŀέ !!! ! + Đёľĕŧз рŗõƒιŀê !!! ! Button label that deletes the current profile that is being viewed. - Ţħė ηªмέ бƒ τнз φřσƒîĺē ŧђαţ åρφēąѓš ìπ тħэ ďяόρđōωп. !!! !!! !!! !!! !!! + Ťћє ηǻмэ бƒ тнέ рřоƒïľé ţĥāτ άрρеαяѕ ĭπ ťћê đŕøφđóẃи. !!! !!! !!! !!! !!! A description for what the "name" setting does. Presented near "Profile_Name". - Ńάmè ! + ∏ãmê ! Name for a control to determine the name of the profile. This is a text box. - Ñăмě ! + Ņàmě ! Header for a control to determine the name of the profile. This is a text box. - Ŧяáņśφаŕ℮ńςŷ !!! + Ţřąηѕρªґěňçÿ !!! Header for a group of settings related to transparency, including the acrylic material background of the app. - Ъą¢ķġřõűήđ ιmąğз !!! ! + Βдĉкġřöûⁿď ìmąĝέ !!! ! Header for a group of settings that control the image presented on the background of the app. Presented near "Profile_BackgroundImage" and other keys starting with "Profile_BackgroundImage". - ȵŗśóř ! + Čυřŝóґ ! Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". - Åđðϊτіõήąŀ šěтτįņğŝ !!! !!! + ∆ďδΐţіõпаľ śєτťΐņĝŝ !!! !!! Header for the buttons that navigate to additional settings for the profile. - Ţĕ×ţ ! + Ţέхţ ! Header for a group of settings that control the appearance of text in the app. - Ẃιйðόώ ! + Щіňďош ! Header for a group of settings that control the appearance of the window frame of the app. - Öφёή ỳŏüŕ settings.json ƒïℓê. Дĺт+Çłįск ťò όрèň ỳøύř defaults.json ƒιĺз. !!! !!! !!! !!! !!! !!! !!! + Ώр℮п ỳõμѓ settings.json ƒιĺ℮. Áŀţ+Ċĺι¢ќ тθ ôφ℮л уοųѓ δěƒåџĺţş.јśŏл ƒïłе. !!! !!! !!! !!! !!! !!! !!! {Locked="settings.json"}, {Locked="defaults.json"} - Ŗέʼnåмĕ ! + Γ℮иάмê ! Text label for a button that can be used to begin the renaming process. - Ŧнįś ĉбľòŗ ѕςнëmэ çäñπòτ ъē δεļéţ℮ď öř гěⁿаmεď вėсâùŝè ĭţ їš ïŋ¢ŀΰđēđ ьý δĕƒαŭŀτ. !!! !!! !!! !!! !!! !!! !!! !!! + Τћîŝ čŏļòя şċĥěмε ćâňñǿτ ьё đĕļ℮ťèδ ōŕ ґĕňãmėð ьēĉаūśё ĭţ îś їńċľůðзď ъγ đэƒāûŀŧ. !!! !!! !!! !!! !!! !!! !!! !!! Disclaimer presented next to the delete button when it is disabled. - Ϋєś, đёŀĕτз čôľőг śçђèmē !!! !!! ! + Ýëѕ, ðěĺέŧε çółöг śċћэмέ !!! !!! ! Button label for positive confirmation of deleting a color scheme (presented with "ColorScheme_DeleteConfirmationMessage") - Âřе ўόù ŝûŕέ ŷσϋ ώάⁿτ ťŏ ďёℓетé ťђĩš ¢øłòř ѕсĥěмε? !!! !!! !!! !!! !!! + Аŕë уоû şύŗê γǿџ ẃаηť ŧθ ðέłēтε тнíѕ çσľòř ѕćĥεмέ? !!! !!! !!! !!! !!! A confirmation message displayed when the user intends to delete a color scheme. - Āçċėρŧ ґēиάmε !!! + Àćĉêрŧ ŕεηáмĕ !!! Text label for a button that can be used to confirm a rename operation during the renaming process. - Čàηçëł ѓęлаmэ !!! + Čªⁿςęℓ гëήдмέ !!! Text label for a button that can be used to cancel a rename operation during the renaming process. - Ŝċћęмэś ďēƒїńėď нεґе ćàň ъē арρŀïэđ ŧŏ ỳσûг рřσƒίĺĕѕ ûŋδ℮ř тђě "Άφφěãґăлć℮ŝ" ѕĕĉτϊôń оƒ ţħě ρяŏƒїŀё šèτтїńğѕ ρàġęѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Šсћěмěš đèƒιñèđ ћėřė ċдŋ ъę αрφŀĩ℮ð ŧö ỳóцř ρґοƒįℓэŝ цйðĕř ŧћ℮ "Άρφêäгăήςεѕ" šєĉťΐόη θƒ τћę φґôƒіℓē şĕŧŧιⁿĝѕ рǻĝêѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A disclaimer presented at the top of a page. "Appearances" should match Profile_Appearance.Header. - Ѕєτťϊйĝѕ δêƒĭπеđ ĥєřе щίℓļ ªρρĺŷ ŧò ąłŀ ρгǿƒíĺєş µйℓēşş тђεỳ àř℮ óνέѓŕíδđĕň ьỳ а ргόƒιℓ℮'š şєťτїйĝş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Śéţţĩñģš ďėƒįňеδ ђеŗ℮ ẃίľĺ áφρℓý ŧō ªļł ряöƒíŀєš ūлĺèŝŝ ťħęу àгë σνэřřїδδέй ьγ а рŗőƒįŀé'ś ŝëтţіиġš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. See "Nav_ProfileDefaults.Content" for a description on what the defaults layer does in the app. - Ỳēş, đёľèťê φřǿƒιľė !!! !!! + Ỳěŝ, ďзľетè ρřöƒīľé !!! !!! Button label for positive confirmation of deleting a profile (presented with "Profile_DeleteConfirmationMessage") - Ǻѓе ýòũ śúŕ℮ γбû ẅáиτ ţő ďэℓêť℮ ŧĥίś φŕǿƒΐļē? !!! !!! !!! !!! ! + Дгè уøџ ѕŭřз ỳθū шдŋт ťö đěĺэţè ţћιś φгòƒίŀ℮? !!! !!! !!! !!! ! A confirmation message displayed when the user intends to delete a profile. - Ŝªνě áľŀ цŋśàνзδ śёţтīηğŝ. !!! !!! ! + Ѕąνε āľŀ ΰʼnşаν℮ð šέťτϊŋģŝ. !!! !!! ! A description for what the "save" button does. Presented near "Settings_SaveSettingsButton". - Ťąв şŵìτčнêґ їņτеřƒăćĕ şтŷℓе !!! !!! !! + Τãь ŝẃιťςђęř įⁿтëяƒā¢ĕ şтγℓε !!! !!! !! Header for a control to choose how the tab switcher operates. - Ŝèľеċτѕ ẃĥісн ϊпŧêŗƒдċè ωĩĺļ ьє ûśзď ώħёʼn ýøµ śщìтċђ тăьŝ ύśîпğ ťћè кĕγъǿåґð. !!! !!! !!! !!! !!! !!! !!! !! + Śėℓĕčţś ẃħĩçћ īиŧēгƒаςэ ωīℓℓ вè цŝëδ шħёл ŷόџ şщīťĉђ ŧáвš ùśīлġ τнз ќèŷьóâяð. !!! !!! !!! !!! !!! !!! !!! !! A description for what the "tab switcher mode" setting does. Presented near "Globals_TabSwitcherMode.Header". - Şέφдяāτë щīηðοш, ïń мőşт ґêς℮йтľý űśёđ öяďēя !!! !!! !!! !!! ! + Şéράŗâт℮ ŵĩñðōω, ïл mθŝť ŗĕčєπťℓу ūŝěď оŗδêґ !!! !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in most recently used order. - Ѕєφåřάтє щīпðǿẅ, ĩи тāв ѕťгιφ όґďеř !!! !!! !!! ! + Šєρãѓªτе ẅійđòẁ, îń ťāъ śŧґір σяðєŗ !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in the order of the tabs at the top of the app. - Тŗаðîťϊôηǻļ ñàνĭĝдτîōη, ήб ŝεφάŗàτĕ ώĩʼnδõш !!! !!! !!! !!! + Ţřàðίţίöлàľ ήдνΐĝãťįθⁿ, ⁿô šэράŕăť℮ ẃĩηďøω !!! !!! !!! !!! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is hidden and does not appear in a separate window. - Ţехτ ƒöгмǻťŝ ťó сбφÿ ŧø ťћę ¢ŀįφъøдґď !!! !!! !!! !! + Ţęхţ ƒόřмâтš ťσ ċορў ţő ťнё ĉĺїρвσагδ !!! !!! !!! !! Header for a control to select the format of copied text. - Рļǻїи ŧєхţ ôⁿℓỳ !!! ! + Ρĺαΐñ ŧëжť όиĺÿ !!! ! An option to choose from for the "copy formatting" setting. Store only plain text data. - ĦТΜ₤ ! + ΉТМ£ ! An option to choose from for the "copy formatting" setting. Store only HTML data. - ŘŢ₣ + ЯŢ₣ An option to choose from for the "copy formatting" setting. Store only RTF data. - βŏţћ ΗŦМ£ аήď ГŢ₣ !!! !! + βôτђ ΉŢΜŁ àήδ ГТ₣ !!! !! An option to choose from for the "copy formatting" setting. Store both HTML and RTF data. - Рĺėаšе ĉћôòşέ ã ðίƒƒэřέʼnť лªmě. !!! !!! !!! + Ρłęāѕë ċнǿőŝê å ð탃℮яēйţ ņãмέ. !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the subtitle and provides guidance to fix the issue. - Τћιѕ čöłǿґ ščĥęmε ńâмē íŝ ǻľřέåðγ îπ ύŝε. !!! !!! !!! !!! + Τħīѕ çôŀóř ѕčћємє ŋдmë ĩѕ άĺŕėãďý ĭп ύšє. !!! !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the title, and explains the issue. - õτómãţιĉāłļỳ ƒŏςûš φǻйë бп мôüśĕ ћöνēя !!! !!! !!! !!! + Áϋτомàţīćąłłý ƒόćŭś рåπę öń мõùšě ђöνëŗ !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. - Рäŋě αņîmαţϊσňš !!! ! + Ρǻйě ǻйϊмäţіőηş !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. - Άŀẅåγѕ đîśφľãý дπ ιćőñ īη ţĥе лοŧιƒϊċªŧĩοʼn άгέą !!! !!! !!! !!! !! + Äľŵαŷś ďιśφĺαу дⁿ ìčǿи їň ţнē ŋöťϊƒĭćåţιøй αřεд !!! !!! !!! !!! !! Header for a control to toggle whether the notification icon should always be shown. - Нϊďε Ţέѓmìлаļ ιπ ťнę ⁿöтíƒіćǻťіόπ ăřęд ώнєŋ ĭŧ ίš mιлїmīźэð !!! !!! !!! !!! !!! !!! + Ήïδė Ŧěŗмìπàł ïň τħз ⁿøтíƒĩçąтіοй âŕéă ẁћēή ΐť ίś mìлїmïźєđ !!! !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized. - Яěşεŧ ţθ ΐŋђеřіŧēď νãℓųε. !!! !!! ! + Яėşĕт ťо ίлћėŕίťеď νãľΰę. !!! !!! ! This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button. - Тёґмιйąľ ćθłόřś !!! ! + Тêѓmíņäľ ¢бŀòгŝ !!! ! A header for a grouping of colors in the color scheme. These colors are used for basic parts of the terminal. - Şỳŝтĕм ςοŀбŕş !!! + Ŝўѕţèm čøĺøгѕ !!! A header for a grouping of colors in the color scheme. These colors are used for functional parts of the terminal. - Ćθľöŕś ! + Ċôŀŏŕŝ ! A header for the grouping of colors in the color scheme. - Яęŝёт τǿ νãŀΰз ƒŗőm: {} !!! !!! + Γ℮šĕţ τǿ νăļϋè ƒяŏm: {} !!! !!! {} is replaced by the name of another profile or generator. This is a text label on a button that is dynamically generated and provides more context in the {}. - Šћощ ăłł ƒóņтś !!! ! + Şħбω ąłļ ƒőňŧś !!! ! A supplementary setting to the "font face" setting. Toggling this control updates the font face control to show all of the fonts installed. - Їƒ èñаьļ℮δ, ѕђоẅ äŀľ ΐņśтªℓĺёď ƒőņτš ίⁿ τћè ŀĭѕţ âъσνê. Φţнëŗшĭşе, óйĺў ŝнøŵ τĥє ĺιśť øƒ мöńоšρäςè ƒòпŧŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Įƒ ěлåвℓεď, šħöω ăℓŀ їήšτаļľêð ƒõņŧŝ ίл τне ľíşť ªвσνє. Öťĥέŕωíŝě, ôπľγ ŝĥσẃ ţĥэ ľíѕτ θƒ mōйǿѕφâčз ƒоņτŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts". - Ċŗέăτэ Λφφёàřāņ¢è !!! !! + Çřëаţë Ăφрзαѓăʼn¢έ !!! !! Name for a control which creates an the unfocused appearance settings for this profile. Text must match that of "Profile_AddAppearanceButton.Text". - €ř℮àŧë άŋ úņƒоćùŝєð αφрéāґäлčê ƒσґ тћіš ρгŏƒϊļę. Τнΐŝ ŵїĺľ вê τħë дρρěаřªŋçě όƒ тħě ρřоƒΐľė ŵћéл ϊţ ĭŝ įпãċţîνэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ċґêâŧέ āл ûηƒοćüšεð аφрěąґªŋčέ ƒòř тнìş φřòƒîľέ. Ţнīѕ ωïĺľ вé ťнē αррêãřªήç℮ οƒ ŧћê ρґőƒίļė ŵħėņ īŧ ίš ïńαčτîν℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the create unfocused appearance button does. - Ď℮ļέŧэ Äφφέăřдπĉę !!! !! + Ďєľéтę Āρρєαяâп¢ė !!! !! Name for a control which deletes an the unfocused appearance settings for this profile. - Đέļ℮ťё ţђę цñƒόćũŝĕď ãφрзαŗåʼn¢ē ƒőř ţђїš ρгøƒϊłē. !!! !!! !!! !!! !!! + Ðěłêţэ ŧђέ џпƒоćΰѕēď ǻρрέāяāηĉé ƒŏѓ τђįś φřōƒĭŀĕ. !!! !!! !!! !!! !!! A description for what the delete unfocused appearance button does. - ¥еš, δέłëţε кéÿ ъіňðīηġ !!! !!! + Ýęѕ, ďэļěŧέ ќêý ьïⁿďїňĝ !!! !!! Button label that confirms deletion of a key binding entry. - Λяз γǿű šůřέ ýόű шåňţ ţο ðēĺĕтё ţнîś кеу вìŋδîлĝ? !!! !!! !!! !!! !!! + Άѓє ŷøü şΰŗė ŷóц ẁаńт ťб ðеℓ℮тè ŧђįś κëÿ вΐπðϊńġ? !!! !!! !!! !!! !!! Confirmation message displayed when the user attempts to delete a key binding entry. - Īņνãŀίδ κєŷ ¢нòгď. Ρłêªѕ℮ зňŧëř à νāŀίđ ķéÿ ċђόґď. !!! !!! !!! !!! !!! + Іпνáŀįδ κέу ¢ђòřď. Ρľеαşέ ëπŧзŕ ā νāŀϊđ ķэў čђθґð. !!! !!! !!! !!! !!! Error message displayed when an invalid key chord is input by the user. - Ỳęš + Υэŝ Button label that confirms the deletion of a conflicting key binding to allow the current key binding to be registered. - Тђê ρŕόνíđêď ĸęу сĥояð ìś ǻľѓзąδŷ вěϊñğ ùšěð вý ţђě ƒŏŀℓόщíńģ αčŧίôή: !!! !!! !!! !!! !!! !!! !!! + Ţнз рŕǿνĭđêδ κęγ ĉноřď ΐś аŀгèαďý ьеїńġ úśέð ьу ţĥē ƒбľĺõẅΐňĝ ăćŧìôň: !!! !!! !!! !!! !!! !!! !!! Error message displayed when a key chord that is already in use is input by the user. The name of the conflicting key chord is displayed after this message. - Ẁόŭℓð ýôŭ ľїĸê ťô őνêŗẁґìтé їť? !!! !!! !!! + Шőűľđ ỳŏυ ĺïķэ тō όνēѓщřΐтê ιţ? !!! !!! !!! Confirmation message displayed when a key chord that is already in use is input by the user. This is intended to ask the user if they wish to delete the conflicting key binding, and assign the current key chord (or binding) instead. This is presented in the context of Actions_RenameConflictConfirmationMessage. The subject of this sentence is the object of that one. - <ũήηаmэď čòmмăйđ> !!! !! + <цʼnňαmеδ ĉŏmмάŋð> !!! !! {Locked="<"} {Locked=">"} The text shown when referring to a command that is unnamed. - Ăс¢ēрť ! + Ăç¢еρт ! Text label for a button that can be used to accept changes to a key binding entry. - Ĉάήĉĕľ ! + Çäňçèĺ ! Text label for a button that can be used to cancel changes to a key binding entry - Ðзľęτе ! + Đēŀéт℮ ! Text label for a button that can be used to delete a key binding entry. - Έđΐŧ ! + Σδĩţ ! Text label for a button that can be used to begin making changes to a key binding entry. - Άďð ŋзш !! + Дδđ ñėώ !! Button label that creates a new action on the actions page. - Åсţїòη ! + Α¢тίōй ! Label for a control that sets the action of a key binding. - Ĩñφΰţ ўθűř ðέšιѓĕđ κ℮ýьόαŗđ śĥōřŧčŭŧ. !!! !!! !!! !! + Ійрûť ýőџѓ ďеśîґеδ кĕÿьôǻґđ ŝнόґŧĉüτ. !!! !!! !!! !! Help text directing users how to use the "KeyChordListener" control. Pressing a keyboard shortcut will be recorded by this control. - şћσяŧçűт łīѕŧзлëя !!! !! + ŝћøŕтςųţ ℓįŝтèʼnêŗ !!! !! The control type for a control that awaits keyboard input and records it. - Śћôŗτċύŧ !! + Şħōяŧ¢цτ !! The label for a "key chord listener" control that sets the keys a key binding is bound to. - Τěхţ ₣θŗmάţŧΐŋģ !!! ! + Ťĕхτ ₣ôямåτţίʼnğ !!! ! Header for a control to how text is formatted - Íήťêʼnŝē τε×т śтўŀë !!! !! + Ϊηŧэйšè ŧзжт şťÿĺë !!! !! Name for a control to select how "intense" text is formatted (bold, bright, both or none) - Ίŋтēņšе ŧєжť ŝτỳłē !!! !! + Ĭиτзиśě τёжт şτўĺє !!! !! Header for a control to select how "intense" text is formatted (bold, bright, both or none) - Νõńё ! + ∏őйě ! An option to choose from for the "intense text format" setting. When selected, "intense" text will not be rendered differently - Бσłđ ƒòʼnŧ !!! + Ьθℓď ƒσʼnŧ !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as bold text - ßŗΐğћŧ ĉοĺσŗŝ !!! + βгïĝĥт çőĺǿгş !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered in a brighter color - Ьоľð ƒθиτ ώίŧћ вгĩĝĥť ¢σĺöřѕ !!! !!! !! + Βοŀð ƒόπť ωіŧĥ вгϊģћţ čõĺόřş !!! !!! !! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color - Δύτômáťї¢äℓĺŷ ћįďэ ŵîηđøш !!! !!! ! + Ǻùţόmǻŧіĉąĺĺỳ ħíďз ώìñδǿẅ !!! !!! ! Header for a control to toggle the "Automatically hide window" setting. If enabled, the terminal will be hidden as soon as you switch to another window. - Іƒ ėπąьľéđ, ťћë ťëřmíπăŀ ẁїļł ь℮ ћιðð℮ñ āŝ ѕбóň ăŝ γöŭ ѕщĭť¢ĥ τò áйόтĥзґ ẅіňδóш. !!! !!! !!! !!! !!! !!! !!! !!! + Įƒ εⁿàъļêď, тђĕ ťέґmιпäļ щìℓŀ ъэ нîďδзñ ąś ѕõθŋ âş γôù śшίτċн ťø àпøťнзř шíήðόщ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Automatically hide window" setting does. - Ŧћϊš ĉбļōг ŝ¢ħęмé ¢ăňņöţ ъє δěłèť℮đ ьєçªúšё īŧ íş ίⁿςłůđėđ ъÿ ðéƒãμℓт. !!! !!! !!! !!! !!! !!! !!! + Ťĥíŝ ćσľõѓ şсħéмэ ĉãйпόŧ в℮ δεĺёт℮ð ьèċàцśě îţ ìš іŋçļųďĕđ вý đ℮ƒдџļť. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to delete the selected color scheme when that functionality is disabled. - Ťћΐš ċôŀöŕ ŝĉħēmê ċαήπǿţ вê ѓёñåmеď ъēсάµšé įţ îŝ īπçℓŭđêδ ьу δěƒаūŀť. !!! !!! !!! !!! !!! !!! !!! + Ŧĥιѕ сбĺθѓ ś¢ĥěмэ ĉαлήôť вė ŕ℮ňąmëđ ь℮ςăΰśĕ ĩт ìš ϊņċĺμďĕđ ъỳ đέƒãüłτ. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to rename the color scheme when that functionality is disabled. - ðεƒáΰŀτ !! + ðéƒαųŀť !! Text label displayed adjacent to a "color scheme" entry signifying that it is currently set as the default color scheme to be used. - Śėŧ áŝ ďĕƒâųłτ !!! ! + Şëť åѕ ďзƒªџŀτ !!! ! Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use. - Ẅąřп ώнêń сłόšίлģ mŏяé τћăʼn òлэ τªь !!! !!! !!! ! + Ẃářй ẅђëπ ĉľôśĩήĝ mǿѓĕ тĥąπ òπё τάь !!! !!! !!! ! Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open. - Ẁïηðоẁѕ Тĕřмĭиαľ ίş ѓùņŋїʼnġ їŋ ρоřţäъĺэ mőðе. !!! !!! !!! !!! ! + Ẅїηðσшѕ Ţеřmíńăľ īѕ ґϋййïʼnģ ĭņ рόѓťáъℓė mōďе. !!! !!! !!! !!! ! A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder. @@ -1762,15 +1794,15 @@ {Locked} - ₤ěªѓл mŏřе. !!! + Ļĕдŗⁿ мõгĕ. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - Ẃåяņĭηğ: !! + Ẃãялíʼnĝ: !! Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - Čћōбŝїпģ ā ⁿбņ-móήǿŝрąċеď ƒσиŧ ωίľℓ ľїĸєľў ŕēŝџĺţ íñ νіšΰâŀ ªгтΐƒãςτš. Ųѕе ąτ ỳóũѓ όẃň ðĭѕĉѓēτĭõń. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + €ħσõśïʼnğ ą ñой-мõποşφãĉєδ ƒŏŋţ ẅіľľ łîķέℓў ŕєŝùłţ íй νΐşūαℓ āѓťīƒдςţŝ. Џŝè àţ ÿοùŗ øẁη δíśčґ℮ťîбņ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw index 822917147c7..f7193ce8d63 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw @@ -118,788 +118,788 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Čяёατé Νêŵ Ъũťτøή !!! !! + Čгéªţë Ŋещ Бϋτţбⁿ !!! !! - Νěω éмρтŷ рřøƒįĺé !!! !! + Ŋ℮ω ĕmρŧý рŗőƒîļё !!! !! Button label that creates a new profile with default settings. - Ðũрļìćατé ª ряσƒīℓ℮ !!! !!! + Ďύφľίċåте ã φґǿƒïłē !!! !!! This is the header for a control that lets the user duplicate one of their existing profiles. - Ðџφłįĉǻţе Βμтťθñ !!! ! + Ðùρłīĉāţз Вµτŧσň !!! ! - Ðџρĺĭĉãţε !!! + Đŭρŀī¢ατê !!! Button label that duplicates the selected profile and navigates to the duplicate's page. - Τĥìѕ ςõļōř şснэmё їś φäřт оƒ ţђĕ ъϋīľŧ-ìʼn şéŧťΐлğѕ ŏř ай īйѕτáľľэδ ёжťзʼnѕĭŏи !!! !!! !!! !!! !!! !!! !!! ! + Ţħīѕ ċøłǿґ ŝςħèм℮ îś рăŗţ õƒ тнè ъûĩľт-íπ śéţťΐлĝś õř дŋ íńşťªłĺèð èжťęлşίοη !!! !!! !!! !!! !!! !!! !!! ! - Ŧб мǻκë ćнàʼnģěş τθ ţнïś ¢õĺòŕ ščĥêmë, γòũ mŭšť мăκę ą ćοφŷ öƒ īт. !!! !!! !!! !!! !!! !!! ! + Ţό màќз ¢ћдņĝзş тő ťђїš ĉǿℓöř śсђзmė, ÿŏú мũŝт măĸę α сòφў σƒ ΐť. !!! !!! !!! !!! !!! !!! ! - Μåκę α ċθрỳ !!! + Маĸέ α ċōρý !!! This is a call to action; the user can click the button to make a copy of the current color scheme. - Šéţ ¢θļǿŗ šĉђєме ąş đёƒдųľţ !!! !!! !! + Śèţ ćŏŀθґ šςħëmĕ âŝ δĕƒдϋŀţ !!! !!! !! This is the header for a control that allows the user to set the currently selected color scheme as their default. - ∆ρрĺγ тћїš ċбłøř şĉħēmĕ τō áŀł ỳǿüг ρґôƒíľέś, вŷ ďėƒªüłт. Ĩⁿðĩνϊďùãľ ρŗōƒìľèş сαп ŝтΐľĺ šзℓëĉт тћέįŗ ŏώñ сöłòŗ ščнëмěş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Áφρļу τђìš čǿℓóѓ ѕςĥеmё ţò âļℓ ўσùґ φѓôƒίļеš, ъÿ ðεƒăŭļŧ. Īиðїνιďμäĺ φяöƒíłĕš çǻп şťíĺļ ŝзĺëĉτ тнĕιř ǿшŋ сόļöѓ şčĥêмέѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description explaining how this control changes the user's default color scheme. - Яэńдmê ¢òłŏŗ śςнęмê !!! !!! + Ŕêпªmè сθłόя ŝçħémë !!! !!! This is the header for a control that allows the user to rename the currently selected color scheme. - Вâċĸģŕòůñδ !!! + Ъāċкġřőůņð !!! This is the header for a control that lets the user select the background color for text displayed on the screen. - Вľäčќ ! + Вľд¢κ ! This is the header for a control that lets the user select the black color for text displayed on the screen. - Ьℓů℮ ! + Ьĺųε ! This is the header for a control that lets the user select the blue color for text displayed on the screen. - Βѓïĝнť вļàčķ !!! + Ьŗίĝħť ьľā¢к !!! This is the header for a control that lets the user select the bright black color for text displayed on the screen. - Ьяìğћτ ъļΰэ !!! + βґίġнť вŀµё !!! This is the header for a control that lets the user select the bright blue color for text displayed on the screen. - Ъѓīğнŧ ĉỳâп !!! + Вяίģћŧ ĉуǻл !!! This is the header for a control that lets the user select the bright cyan color for text displayed on the screen. - Βŕίġнť ĝŗèεň !!! + ßѓΐĝђŧ ğґęêп !!! This is the header for a control that lets the user select the bright green color for text displayed on the screen. - Бґїĝĥť рџŗрŀ℮ !!! + Бяīğнŧ ρùŗрľĕ !!! This is the header for a control that lets the user select the bright purple color for text displayed on the screen. - Бѓíģћт ґёđ !!! + Ъŕίğћт гėđ !!! This is the header for a control that lets the user select the bright red color for text displayed on the screen. - Бяìġђŧ ẃнįτë !!! + Ьгīģђţ ωђĭτê !!! This is the header for a control that lets the user select the bright white color for text displayed on the screen. - Бŗιģнť ŷëľłòщ !!! + Вříĝħŧ ÿéĺĺóẃ !!! This is the header for a control that lets the user select the bright yellow color for text displayed on the screen. - Ĉúґšŏř ċôľθŗ !!! + Ćμŕšσґ ćθŀθя !!! This is the header for a control that lets the user select the text cursor's color displayed on the screen. - €ŷαп ! + Сÿāⁿ ! This is the header for a control that lets the user select the cyan color for text displayed on the screen. - ₣ǿŕέġŕōцņδ !!! + ₣οŕėĝřσűлđ !!! This is the header for a control that lets the user select the foreground color for text displayed on the screen. - Ğяĕεή ! + Ġязêή ! This is the header for a control that lets the user select the green color for text displayed on the screen. - Ρϋгрŀέ ! + Рµгρļě ! This is the header for a control that lets the user select the purple color for text displayed on the screen. - Śέľéςτїóи ъāčķĝřøüπđ !!! !!! + Š℮ŀзςŧίθл ьâĉĸġřōŭńδ !!! !!! This is the header for a control that lets the user select the background color for selected text displayed on the screen. - Гēð + Ŗ℮ď This is the header for a control that lets the user select the red color for text displayed on the screen. - Ŵħΐťě ! + Щнϊтè ! This is the header for a control that lets the user select the white color for text displayed on the screen. - Ỳεľĺŏώ ! + Υéĺľоŵ ! This is the header for a control that lets the user select the yellow color for text displayed on the screen. - Łáпġŭǻğέ (геqúіґēѕ ŗэŀāūńćĥ) !!! !!! !! + Ŀąʼnģüǻğз (řêqџĭŗэś яёłдũⁿ¢ħ) !!! !!! !! The header for a control allowing users to choose the app's language. - Ŝëĺё¢ţş ă đΐŝφłдў łąйğűäĝė ƒбř τћз ąφрľíςāťΐöη. Ťĥįš шΐłļ öν℮ŗŗϊδė ỳθϋŗ ď℮ƒдύłт Windows ϊиţěґƒдçē ľăʼnģúåğê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ѕęļéćŧѕ ā ďįŝρℓду łàйģцâğέ ƒǿř ťћз ǻρрľïčдŧíòʼn. Ťħϊş ωіŀľ όνëґŗίďē уǿüř ďèƒăμľţ Ẃïñďбŵś įñτéŗƒαсэ ŀåŋġύдğé. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description explaining how this control changes the app's language. {Locked="Windows"} - Ųšέ ѕŷşτėm ðêƒåųℓτ !!! !! + Ůŝê ѕŷŝτėм đєƒāùľŧ !!! !! The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead. - Ãĺωáÿś şĥοш тáвѕ !!! ! + Άľώāŷš şђǿŵ тãвŝ !!! ! Header for a control to toggle if the app should always show the tabs (similar to a website browser). - Ŵђěη ðΐśàьľėð, ŧћ℮ τаъ ьáг ŵïĺℓ ąρρêαґ щħěи á ñзш ţăв ιѕ сŕёάť℮đ. Çăññбτ вέ đĭѕдьłєδ ẅћιłэ "Ήįđё ŧнĕ тїτĺĕ вǻř" ĩş Óη. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Шħεń ďίѕªъľеð, ţĥё ŧāъ ъаŕ ώіļℓ аρρëâř ẃн℮ŋ ª ŋëẁ ťаъ ιš сгęǻţеď. Ćâиňбŧ ьε ďîśαъŀ℮đ ωħϊŀē "Ĥίδē τђę ţĭτľέ ъάѓ" įś Ωń. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "always show tabs" setting does. Presented near "Globals_AlwaysShowTabs.Header". This setting cannot be disabled while "Globals_ShowTitlebar.Header" is enabled. - Рøšįтíóñ бƒ ňêẁĺў ċŕėâŧêδ τąъѕ !!! !!! !!! + Ρόšîŧϊøņ őƒ йĕŵℓу ĉřēάţεđ τдьš !!! !!! !!! Header for a control to select position of newly created tabs. - Ѕφéсίƒįêś ẁнэґε пέώ τàьŝ âρφзаŗ įπ τћē τàь гθω. !!! !!! !!! !!! !! + Śφзćїƒíеş шĥëřē ⁿêŵ τâъś ªρρёáґ ĭп тћé τåв яøш. !!! !!! !!! !!! !! A description for what the "Position of newly created tabs" setting does. - Ǻƒŧέŕ ţĥè ŀąŝŧ ταь !!! !! + ăτёř ţнė ĺąśţ τаъ !!! !! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the last tab. - Ńŧеř тћë ¢ϋřřēⁿτ ťāь !!! !!! + Αƒŧëѓ ťнė сúѓŗėπτ τăъ !!! !!! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the current tab. - Ãûτθмáτìςãŀļỳ ¢ǿрŷ ѕзĺэçтíοʼn τó сłìρъбäґđ !!! !!! !!! !!! + Āúтǿmаţιĉąľľў сοφý şèℓэċтïøñ ţō ĉļĩρьóąґď !!! !!! !!! !!! Header for a control to toggle whether selected text should be copied to the clipboard automatically, or not. - Ăΰťóмªťίčдļĺγ δ℮ťĕčţ ÜЃĻš āηð mâĸέ тћêm ςĺι¢ķáьŀ℮ !!! !!! !!! !!! !!! + Λυτοмдτìćăŀŀÿ đзťēćτ ЦŖĿš аňđ máĸë ţнэм сŀĭçќάвłĕ !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should automatically detect URLs and make them clickable, or not. - Ґěmøνë ţяâīľìйġ ẃнιтê-ŝφáĉз ĭŋ геćταņĝűĺдѓ šēĺéçŧĩоñ !!! !!! !!! !!! !!! + Ѓëmονĕ тŕáìĺїʼnġ ẃђϊτë-ѕρá¢ę ĭл ѓеċτåηğцĺăŗ ş℮ŀěčŧĭõň !!! !!! !!! !!! !!! Header for a control to toggle whether a text selected with block selection should be trimmed of white spaces when copied to the clipboard, or not. - Гęмőνĕ ťŕąιℓíŋģ шнîŧέ-ѕрăċ℮ ŵђεл φªşţíήĝ !!! !!! !!! !!! + Ґεmθνе ťѓªįŀíʼnĝ шĥіťе-šрäčė ẃнзň φâśťίņġ !!! !!! !!! !!! Header for a control to toggle whether pasted text should be trimmed of white spaces, or not. - βєłōщ ãŕ℮ ŧђė ςũѓґэñτĺý вǿµήď кëỳѕ, ŵђΐĉђ ¢ąή вé mǿđíƒίєď ьў éđīţĩņğ тħз ЈŚÖŅ şêŧŧĩήĝş ƒιłē. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Вêļσώ áяэ τĥє сûѓгεπтľý ъόůηð κєÿş, ώĥīćн čāñ ъз мοðïƒīěð ьý зðїŧįńģ τћė ЈŜÖΝ şзťŧίñğѕ ƒΐłέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer located at the top of the actions page that presents the list of keybindings. - Ďєƒαΰľτ φѓŏƒїĺē !!! ! + Đėƒªûľť рґøƒϊľê !!! ! Header for a control to select your default profile from the list of profiles you've created. - Ргôƒïļέ ŧћǻţ öφéńş ẃћėи ¢łιĉĸĭπĝ τђё '+' įĉóŋ ôř ьγ ťўφíήġ ŧħε ηзщ τāв ķзÿ ъíňδįňġ. !!! !!! !!! !!! !!! !!! !!! !!! + Ρŗσƒιℓê тнǻт ōреŋś ώнèи ¢ļíčкïñğ ťћέ '+' ίçôи ŏґ ьў ţýрϊńğ тĥє ņěẁ тªв ќèý ъϊήðїńġ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the default profile is and when it's used. - Đеƒдûľť ťĕřmìηáł αφрŀіćàţīθñ !!! !!! !! + Ðêƒάϋℓτ ŧέřmìпάℓ аρφĺΐςàţїōŋ !!! !!! !! Header for a drop down that permits the user to select which installed Terminal application will launch when command line tools like CMD are run from the Windows Explorer run box or start menu or anywhere else that they do not already have a graphical window assigned. - Ţнė τěŗmĭηдŀ äρφłïćāτíøи тħąţ ℓдũŋčħèś шћэņ ā çσmмάⁿđ-ľĭйé ǻρрłί¢ąţìŏл īś ґūⁿ ώîτĥöυţ āή ēхîśтίηģ ŝëśѕïοή, ĺікè ƒřбм ŧĥė Ŝтªгт Μëпū σя Ŕùʼn đίдľôġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - Ŕεðŕåŵ эηťĩřё śċяеёπ ωћęñ ðіşрŀâŷ υφđãŧēş !!! !!! !!! !!! + Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - Ẁĥĕŋ ďιśàвłèđ, ŧђ℮ ŧēяmíπäŀ ẁįłļ ѓēʼnďεř ǿиłý ťħё цφđατèѕ ťǿ ŧħє ŝçŗēěń ьеŧщєęⁿ ƒŗăмéş. !!! !!! !!! !!! !!! !!! !!! !!! ! + Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". - €õľümŋѕ !! + Ċσŀùмñѕ !! Header for a control to choose the number of columns in the terminal's text grid. - Ŕőŵš ! + Ѓõшŝ ! Header for a control to choose the number of rows in the terminal's text grid. - Ίʼnįτĩáł Сõľџмⁿŝ !!! ! + Îйϊŧìãľ Çòłųмπş !!! ! Name for a control to choose the number of columns in the terminal's text grid. - Īиįťϊâľ Ґбώѕ !!! + İńìťîáℓ Ŕőώś !!! Name for a control to choose the number of rows in the terminal's text grid. - Ж рǿşіŧіση !!! + χ φόśΐтįóñ !!! Header for a control to choose the X coordinate of the terminal's starting position. - Ỳ рθѕîţїõʼn !!! + Ỳ φôѕΐтĩσп !!! Header for a control to choose the Y coordinate of the terminal's starting position. - Χ φοśїтіôʼn !!! + χ φŏѕĭтϊόŋ !!! Name for a control to choose the X coordinate of the terminal's starting position. - Ёʼnŧзг ťĥė ναľŭє ƒбґ ŧħê ж-¢όøŗδїпªтэ. !!! !!! !!! !! + Ëʼnŧëř ţнę νáľυé ƒóŗ τђę х-čōóѓđΐήâтë. !!! !!! !!! !! Tooltip for the control that allows the user to choose the X coordinate of the terminal's starting position. - Ж + Χ The string to be displayed when there is no current value in the x-coordinate number box. - Υ роśїтΐóń !!! + Ỳ ρθŝìŧιοπ !!! Name for a control to choose the Y coordinate of the terminal's starting position. - Еňτéг ŧħз νäľűε ƒόŕ ŧнэ ý-ςбοřδϊňªţé. !!! !!! !!! !! + Ёйŧëŕ ţћε νàłüέ ƒòŕ ťĥє ỳ-čоθґδĩπāтė. !!! !!! !!! !! Tooltip for the control that allows the user to choose the Y coordinate of the terminal's starting position. - + Ў The string to be displayed when there is no current value in the y-coordinate number box. - Ľēŧ Шĩŋďöщş ďėĉįďэ !!! !! + ₤êτ Ẃĩиðōẁŝ ðē¢îðε !!! !! A checkbox for the "launch position" setting. Toggling this control sets the launch position to whatever the Windows operating system decides. - Īƒ éπāъĺеđ, ûşë ţћē šγşтēm ðėƒâúľŧ ŀăúʼnĉћ ρθšιťίôʼn. !!! !!! !!! !!! !!! + Іƒ éлдъłëđ, úśз тђé ѕýŝт℮m δέƒăµłŧ łäúηćћ φöšïţϊóň. !!! !!! !!! !!! !!! A description for what the "default launch position" checkbox does. Presented near "Globals_DefaultLaunchPositionCheckbox". - Ẁĥěи Ţ℮řміŋąĺ šťǻřţŝ !!! !!! + Ẃђéñ Ţëгмїńªĺ ѕťářтš !!! !!! Header for a control to select how the terminal should load its first window. - Ẁћáţ śћόũļď ъ℮ ŝħóωη ώћêñ ŧĥè ƒïŗŝт ţëřмїйāł īś сř℮ăтέδ. !!! !!! !!! !!! !!! ! + Ẁђăт şĥõцĺð вε ѕнбшп ẁћёй ťђё ƒîŗŝт тěřмΐйåł їś сѓéąţзď. !!! !!! !!! !!! !!! ! - Όρèń α ŧãв ώΐŧĥ ŧђę ďėƒáџľť φѓőƒïł℮ !!! !!! !!! ! + Οрзη à ťáь ώΐţĥ ţђè δεƒāűĺţ ρяθƒίłё !!! !!! !!! ! An option to choose from for the "First window preference" setting. Open the default profile. - Ôрêή ẃιʼnďøωѕ ƒяőm а ρřëνΐŏũѕ şέśśĭóň !!! !!! !!! ! + Орéŋ ẅϊηďóωš ƒřōm ă φяèνιôùś şęŝśîőň !!! !!! !!! ! An option to choose from for the "First window preference" setting. Reopen the layouts from the last session. - £ăûⁿςн мσðε !!! + ₤дΰⁿçћ mοďè !!! Header for a control to select what mode to launch the terminal in. - Ĥòŵ ŧħз тéґмíйäℓ щιľℓ áρр℮âя οй ļãúήςħ. ₣оςůś ώïłľ ĥϊðε ţħĕ τãвѕ åńδ ťіτłē ьāř. !!! !!! !!! !!! !!! !!! !!! !!! + Ηóώ ţĥè ţěѓmϊиаľ ẁίĺĺ àρρ℮åг σⁿ łåŭⁿ¢ĥ. ₣οċύŝ ẁīłł ђїδė ţђê ťªьş āńð тιŧľ℮ вăŕ. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ĺдϋиċђ φāŕąмéτéŕš !!! !! + £ăŭʼnсн рąґăмěţеґŝ !!! !! Header for a set of settings that determine how terminal launches. These settings include the launch mode, launch position and whether the terminal should center itself on launch. - Ş℮тτіήĝš ŧђǻτ ċŏñţřóŀ ĥθŵ τħę тэŕмįⁿàł ℓâŭńсĥєѕ !!! !!! !!! !!! !! + Ѕèттîŋĝś ŧħªŧ ςôňťѓσŀ ĥõщ ţнè ţĕŗmîйåℓ ℓăµňĉнэś !!! !!! !!! !!! !! A description for what the "launch parameters" expander contains. Presented near "Globals_LaunchParameters.Header". - Łăυпċђ мσďë !!! + Ŀäцñсн mőď℮ !!! Header for a control to select what mode to launch the terminal in. - Ήőẅ тнê τēŕmíиаŀ щΐŀļ ăρφèάř óπ ĺªμʼnĉн. ₣øčųѕ ẅιļĺ нìďе тћé τăьŝ àñð ŧϊτℓê вăř. !!! !!! !!! !!! !!! !!! !!! !!! + Ηōŵ ţнέ тěřmіηªĺ ẃΐŀľ ąρρëäґ öñ ļǻûй¢ћ. ₣όςµŝ ẅїļł ħĭďē τћε ŧªъš āηď ŧìťℓέ ьář. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ðéƒăųℓт !! + Ďěƒάυŀţ !! An option to choose from for the "launch mode" setting. Default option. - ₣υŀļ şċřěеⁿ !!! + ₣ųľľ ѕ¢řέей !!! An option to choose from for the "launch mode" setting. Opens the app in a full screen state. - Мд×īмìżěð !!! + Μâ×įміźέď !!! An option to choose from for the "launch mode" setting. Opens the app maximized (covers the whole screen). - Ń℮ŵ íηŝŧâŋçэ вéĥάνιог !!! !!! + Νēщ ίńѕţãиςé ьęĥâνΐог !!! !!! Header for a control to select how new app instances are handled. - Ċοηťřοľś ħбẁ иĕŵ ťёŕmίиàℓ ïйŝτдńсěś αťŧаċн ŧõ ę×íśťîлġ ωϊπđôŵѕ. !!! !!! !!! !!! !!! !!! + Ĉöñтŕбℓš ћøш иēŵ τêгmîⁿàļ ĩπšţáπςεŝ άťţдčн тö є×ĭšτіņğ ώілđøẃş. !!! !!! !!! !!! !!! !!! A description for what the "windowing behavior" setting does. Presented near "Globals_WindowingBehavior.Header". - Сѓėāţë ą ňĕẁ ẅіňδôẃ !!! !!! + Ćяèàťέ д ήéẃ ẃĭлďőω !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances create a new window. - Âŧтáсђ тθ тĥè мθŝт řєċєпťĺγ џšęδ шīπďθŵ !!! !!! !!! !!! + Ăţţăčĥ ŧó тħέ mõŝŧ řěçèпŧļý ŭśëď ẃįňδσω !!! !!! !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window. - Аťţâςђ τό ŧнэ mǿšт řэ¢ëñτŀý џšēď ωīηđοω όñ ţћιš đèşќŧοр !!! !!! !!! !!! !!! ! + Äţŧαĉђ τǿ τħе mοѕт ѓěςęńτļγ ûѕєď ŵíñðοω øи тћϊѕ ďёšкτǿφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - Ţн℮şę ŝēтťīⁿĝŝ мāγ ьз µŝèƒцℓ ƒøŕ ŧřõϋьľêŝђøǿţîпĝ åń іşŝũê, нõẅěν℮ř τĥĕý ώϊĺĺ ίмрáĉτ ÿőцг рєґƒòґмάпĉэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τћέŝė ѕëτтίņģš мâу ьê üѕēƒµľ ƒόґ τřóųьŀėѕĥбοτілġ ǻл іŝśü℮, нσώéνєŕ τĥêў ŵīŀł ΐmρåςť убŭя ρěгƒόґмåñĉę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. - Нΐðē ťĥĕ ŧΐтŀë ьдŕ (ґēqμįřеś ґĕĺãŭñċĥ) !!! !!! !!! !! + Ĥìđε тħê τīţĺё ъªř (ŗėqūΐŗêś яеľаϋʼnčћ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. - Щħεй đїѕавľ℮ð, тђė ŧïťℓé ъαг ŵĩℓļ άφрёäř αьõνē ťĥ℮ ťαвş. !!! !!! !!! !!! !!! ! + Ẃћèп ðϊŝαъłėð, тнė ťĭτłê вąѓ ẁιĺł ąφφёǻŕ äвöνė ŧħė ťãьś. !!! !!! !!! !!! !!! ! A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Ůśё дċŕýℓĭĉ мăŧєřίªŀ îņ ŧĥè ŧáъ řóẃ !!! !!! !!! ! + Ūŝē àςѓýłīć мãτęѓįάł ϊή ťнę ťāъ гоω !!! !!! !!! ! Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Üŝè ǻčτινє τ℮ґmīпαŀ ţįťŀє ǻѕ åрρĺïсăţìôⁿ ţįтℓè !!! !!! !!! !!! ! + Ůŝě å¢ťįνέ τèґmĭʼnαĺ τįτľé äš дρφℓīĉатĭǿʼn ţιţľē !!! !!! !!! !!! ! Header for a control to toggle whether the terminal's title is shown as the application title, or not. - Шђēñ ďìśάъŀěδ, ŧћē ŧίŧļе вăř ẅíłŀ вê 'Ťĕґmĭйăŀ'. !!! !!! !!! !!! !! + Ŵћéή đíѕǻьļεď, тħè ťįτłе ьǻŗ ẁїℓł вє 'Ťěѓмĩⁿăĺ'. !!! !!! !!! !!! !! A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"} - Ѕпåр щīńďоẁ ř℮ѕįźιлġ τσ ćђăяаċŧĕг ġřίđ !!! !!! !!! !! + Ŝиάρ щίйðõщ яēşΐžíпĝ тõ čħáѓä¢ťέѓ ģѓĩδ !!! !!! !!! !! Header for a control to toggle whether the terminal snaps the window to the character grid when resizing, or not. - Ẅђéʼn διśάъļéď, ťне ωìйδŏẅ ώΐłĺ řèšïżē ѕмôǿτђℓŷ. !!! !!! !!! !!! !! + Ẅн℮ñ ðìşāьļĕð, τĥė ŵΐņďòώ ẃíļĺ řėŝîżē šмõόтнłγ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - Цş℮ şőƒţщâгé ґęлðєґїпģ !!! !!! + Ųѕε ŝόƒťщаřė яĕŋðεříлğ !!! !!! Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - Ẅћēη êиåьļέδ, ťĥё τэŕмīпдľ ẃìĺł ϋşέ ťħ℮ ѕóƒťωäяę ŕęпđзřέґ (ä.ķ.ǻ. ẄĄŔР) ĭŋŝťėãδ óƒ ťĥέ ĥâѓđẁãřĕ őйê. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ŧнé šбƒţẅāяē ґэпδėѓεŕ (д.к.ă. ẀÅЃΡ) ĭñŝτéāđ оƒ тħё ĥάґđẃаяē οлε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - £âüиçħ οп mąćђīñэ śţáяŧũр !!! !!! ! + ₤áϋпςħ ôл мαςĥĭñę ŝťąřťûρ !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. - Åůŧǿmдτįčãľłý ĺàüņčħ Ţёřмïйάł ωћêй ўǿũ ℓоġ ĩŋ τσ Windows. !!! !!! !!! !!! !!! !! + Āũŧοмªτĩčäĺļỳ ŀªūⁿ¢ћ Ťëŗmįŋāℓ ẃнęņ ўоű ŀóġ ìπ τσ Шĩņðоẁѕ. !!! !!! !!! !!! !!! !! A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header". {Locked="Windows"} - Сзñŧεř℮δ !! + Ćêŋťєŕеδ !! Shorthand, explanatory text displayed when the user has enabled the feature indicated by "Globals_CenterOnLaunch.Text". - €ěņţēŕ ōņ ļǻυⁿĉћ !!! ! + Ćēňτєѓ ŏŋ ľäūńçђ !!! ! Header for a control to toggle whether the app should launch in the center of the screen, or not. - Ẁĥёñ єлãъľєđ, ťђè ẃιηδθώ ẃιľŀ ьë ρĺäċëď ïи ťђ℮ čéņτēŕ ǿƒ тĥė šĉŗ℮ĕл ẅђел ľăυлćђεď. !!! !!! !!! !!! !!! !!! !!! !!! + Ẁђēи ėⁿąьĺëđ, ţħė ωīņďбώ ώїłľ ъė рℓªςέð ïⁿ ŧĥε сęⁿтєř ǿƒ ŧħè ŝćŕėęή ẁћêʼn ĺдűňĉĥεđ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "center on launch" setting does. Presented near "Globals_CenterOnLaunch.Header". - Âℓẁдỳš ол τορ !!! + Аŀẅâγš ōп ŧбφ !!! Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled). - Τêѓmíⁿаľ ώìĺĺ åĺẁãγś ьэ ťħê τôрмσŝţ ωįпďóŵ оή ŧђė ďēŝķтöр. !!! !!! !!! !!! !!! !! + Ťèřmĭήäľ ŵϊłļ àļщāÿş ъĕ ŧħє ţóрmόѕŧ шíñđθώ όи ŧħě δεşĸţбφ. !!! !!! !!! !!! !!! !! A description for what the "always on top" setting does. Presented near "Globals_AlwaysOnTop.Header". - Ţăв ωîđťħ мǿδė !!! ! + Ťâв ώΐđťн mбðě !!! ! Header for a control to choose how wide the tabs are. - Ĉõmφаĉτ ŵìłľ śняίňќ їņãĉţįν℮ тäъś τò тнë şîżè øƒ ţħє ī¢õň. !!! !!! !!! !!! !!! !! + Ćôмрáçτ ώĭĺļ ŝнгíñк ïŋãċţïνе тâъѕ ťö тĥε ѕіźë ŏƒ ŧн℮ ĩćóň. !!! !!! !!! !!! !!! !! A description for what the "tab width mode" setting does. Presented near "Globals_TabWidthMode.Header". 'Compact' must match the value for <Globals_TabWidthModeCompact.Content>. - Čòмρàçτ !! + Ċøмрªĉť !! An option to choose from for the "tab width mode" setting. When selected, unselected tabs collapse to show only their icon. The selected tab adjusts to display the content within the tab. - Зqũǻļ ! + Εqüáł ! An option to choose from for the "tab width mode" setting. When selected, each tab has the same width. - Ŧїţℓè ľеηġťђ !!! + Тīτľе ľεňĝτђ !!! An option to choose from for the "tab width mode" setting. When selected, each tab adjusts its width to the content within the tab. - Ăφрŀì¢ãťîσń Ťћеmĕ !!! !! + Αрρłΐćàтīòñ Ŧнèм℮ !!! !! Header for a control to choose the theme colors used in the app. - Đάгĸ ! + Đăгќ ! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. - Üşë Ẁĭήðõшѕ ŧћěмε !!! !! + Űŝ℮ Щįņđоẃś тћěmе !!! !! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. - Ĺĭġĥŧ ! + Ĺιģћť ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. - Ďªѓκ (Łеğãċý) !!! + Ďāґќ (₤єĝåçŷ) !!! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. This is an older version of the "dark" theme - Űѕё Ẅίŋðöŵѕ тĥ℮mé (₤ёģªćỳ) !!! !!! ! + Ùśê Щΐʼnδøщŝ ťħéмě (Łèĝąçу) !!! !!! ! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. This is an older version of the "Use Windows theme" theme - Ľιğћť (₤еģάςу) !!! ! + Ŀїģнţ (Łεĝäċÿ) !!! ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. This is an older version of the "light" theme - Ẃōŕδ δέĺιmîŧěґş !!! ! + Щοґδ đэļιмϊŧеѓş !!! ! Header for a control to determine what delimiters to use to identify separate words during word selection. - Ţĥęѕз ѕÿmвöĺş ώīℓľ ьē ΰśĕď шħéл ỳøú đбúьĺê-ςļìсĸ ŧě×τ ιπ ťћё τєŗмίⁿąŀ ôґ ǻстìνãţ℮ "Μâŕĸ mōðę". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ţћзŝ℮ ѕумвöŀѕ шιŀℓ вê ùŝеď ẃнёñ ỳõµ ďбùвŀэ-ċľϊčк ţεם ïⁿ ţћě τєґmїиаℓ ŏѓ αсτϊνãŧě "Мάřк mŏđë". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "word delimiters" setting does. Presented near "Globals_WordDelimiters.Header". "Mark" is used in the sense of "choosing something to interact with." - Λφреǻřдηĉ℮ !!! + Λρρёªгąńсё !!! Header for the "appearance" menu item. This navigates to a page that lets you see and modify settings related to the app's appearance. - Сõĺθя ŝ¢ħêмεş !!! + Ċōľøя š¢нēměѕ !!! Header for the "color schemes" menu item. This navigates to a page that lets you see and modify schemes of colors that can be used by the terminal. - Įπτěŗāčŧīόņ !!! + Íŋт℮řâçτîöй !!! Header for the "interaction" menu item. This navigates to a page that lets you see and modify settings related to the user's mouse and touch interactions with the app. - Śťäřŧџр !! + Ѕţâŕτűр !! Header for the "startup" menu item. This navigates to a page that lets you see and modify settings related to the app's launch experience (i.e. screen position, mode, etc.) - Ωρέň ĴЅОŅ ƒϊļè !!! ! + Фφëņ ЈŞŐŅ ƒϊĺэ !!! ! Header for a menu item. This opens the JSON file that is used to log the app's settings. - Ðёƒåϋļţś !! + Đэƒαűŀţş !! Header for the "defaults" menu item. This navigates to a page that lets you see and modify settings that affect profiles. This is the lowest layer of profile settings that all other profile settings are based on. If a profile doesn't define a setting, this page is responsible for figuring out what that setting is supposed to be. - Ґęʼnðęŕίŋġ !!! + Ŕэⁿðэŕίńğ !!! Header for the "rendering" menu item. This navigates to a page that lets you see and modify settings related to the app's rendering of text in the terminal. - Âĉτīøńŝ !! + Ăςтιòñš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. - Ьαςќġřόџⁿď ōрãςίŧγ !!! !! + βά¢ĸĝŕõúήđ ǿрãĉĩŧÿ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Ьªĉκğřōûŋđ σφăčΐţў !!! !! + βāĉκğřôμηď óρăĉїŧỳ !!! !! Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Ѕзťś ŧђе ǿрàčίτў őƒ ţнē ωίπđőẁ. !!! !!! !!! + Ŝεŧŝ ţђé ǿφąċϊτŷ ôƒ ťђé ẃіŋďοẁ. !!! !!! !!! A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header". - Δďνǻņčęδ !! + Дδναиċēδ !! Header for a sub-page of profile settings focused on more advanced scenarios. - AltGr ǻļιâŝĩⁿģ !!! ! + ΛŀţĢř ąĺîàśιⁿģ !!! ! Header for a control to toggle whether the app treats ctrl+alt as the AltGr (also known as the Alt Graph) modifier key found on keyboards. {Locked="AltGr"} - Ьў ðёƒãџĺŧ, Windows ťŕёдтŝ Ĉтяł+Ǻŀţ àŝ áʼn аľіăš ƒòя АłťĢŕ. Ŧħìś ŝετтϊиğ шіŀł ôνέřѓϊďε Windows' ďзƒαυŀţ ьèĥäνïοř. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Βу ďёƒдџĺţ, Ŵϊńďŏшš τřεåτš Ċťґℓ+Аłτ åѕ αń àłĩάš ƒŏг ДŀŧĢř. Τħїś śёťтїлğ ŵíℓĺ õνёяřΐďё Ŵìʼnďôщѕ' ďэƒâüŀт ьĕђāνïŏř. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header". {Locked="Windows"} - Τехт áпŧîàŀĩâŝíήĝ !!! !! + Ŧεжτ āпŧїåℓїášĩňģ !!! !! Name for a control to select the graphical anti-aliasing format of text. - Ťêжτ âʼnтìãŀĭãŝīήğ !!! !! + Ŧėжţ âлтίāℓïãšìņĝ !!! !! Header for a control to select the graphical anti-aliasing format of text. - Ċοňţŕоĺŝ ћόш ţė×τ ĭş άиţίαłΐаşеđ їл τћë řеňďēŗέŕ. !!! !!! !!! !!! !!! + €σŋťгǿĺś ноώ τë×ţ ĭŝ āηтįāĺįäŝзđ ïⁿ тнê ŕëⁿðęг℮ř. !!! !!! !!! !!! !!! A description for what the "antialiasing mode" setting does. Presented near "Profile_AntialiasingMode.Header". - Ăℓįåѕεδ !! + Ǻłìдšèδ !! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer does not use antialiasing techniques. - ClearType !!! + ĊłėāřŦўрε !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a "ClearType" antialiasing technique. {Locked="ClearType"} - Ģŕǻўşĉαł℮ !!! + Ġѓǻỳşćãľ℮ !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a grayscale antialiasing technique. - Δφρêąřąñčз !!! + Äррéâřăйςё !!! Header for a sub-page of profile settings focused on customizing the appearance of the profile. - Вą¢ķğѓŏµлδ ïмâģе ρаŧĥ !!! !!! + Вãćκĝґõцńð ιмăġέ ратĥ !!! !!! Name for a control to determine the image presented on the background of the app. - ßά¢ќģѓбűήď ìmăġё φäтђ !!! !!! + Βãĉķğѓθцňď ΐмάģ℮ φªţĥ !!! !!! Name for a control to determine the image presented on the background of the app. - βªςκġгοüйδ ĭmāĝє ρáŧћ !!! !!! + βäċκġяǿúпđ ĩмåĝё φατн !!! !!! Header for a control to determine the image presented on the background of the app. - ₣įľé ĺσ¢āτϊŏň öƒ тĥê ĩmåĝĕ üšёδ ĩń ţћĕ ьǻčкğŕθџňđ øƒ ŧнé ωĩηðσω. !!! !!! !!! !!! !!! !!! ! + ₣įĺė łóςąтιǿŋ òƒ ŧħê íмάğз űšéδ їʼn ťĥ℮ вǻ¢κĝяőųиď őƒ ţĥė ώĭŋδбŵ. !!! !!! !!! !!! !!! !!! ! A description for what the "background image path" setting does. Presented near "Profile_BackgroundImage". - Ьªċќğяøµпđ īмāģè ăℓίģⁿmεʼnτ !!! !!! ! + ßάсĸģŗǿџηδ ιmãģê αĺĭġήmēлť !!! !!! ! Name for a control to choose the visual alignment of the image presented on the background of the app. - βάćкģŕóµиð імáģè ąŀїğимэиτ !!! !!! ! + Бαčĸğяōΰηđ їмåĝэ åłĭğʼnмεлŧ !!! !!! ! Header for a control to choose the visual alignment of the image presented on the background of the app. - Śεŧš ħŏẃ ţĥё ьāçкğřŏūŋď ímåģέ ãłĭĝňš ŧо τћê ьôύńďąяīέş őƒ τĥε щìņđσω. !!! !!! !!! !!! !!! !!! !!! + Ś℮ţŝ ћöẅ τĥę ва¢κğŗθŭйδ ìmдğē αŀíģņś то τħę вóµʼnďăгĩěѕ öƒ τћз щĭńðǿẃ. !!! !!! !!! !!! !!! !!! !!! A description for what the "background image alignment" setting does. Presented near "Profile_BackgroundImageAlignment". - Вöтτøm ! + Βŏτŧøм ! This is the formal name for a visual alignment. - βθтτŏm ĺёƒŧ !!! + Βòττōм łéƒŧ !!! This is the formal name for a visual alignment. - βбŧтбm ŕіģђт !!! + Вбŧŧŏм ŗίģнť !!! This is the formal name for a visual alignment. - Ćēπţèŕ ! + Ĉêñť℮ř ! This is the formal name for a visual alignment. - ₤êƒт ! + Ŀèƒţ ! This is the formal name for a visual alignment. - Ґìġĥŧ ! + Ŗĭģħŧ ! This is the formal name for a visual alignment. - Ŧоφ + Ťθφ This is the formal name for a visual alignment. - Τőρ ľèƒτ !! + Ţθр ļέƒτ !! This is the formal name for a visual alignment. - Ţόφ ŕïĝђŧ !!! + Тøρ ŕĩġћт !!! This is the formal name for a visual alignment. - Βŗσщѕę... !!! + Ьřоẅşé... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Áðđ πеш !! + Λďð ňéẁ !! Button label that adds a new font axis for the current font. - Аδδ ή℮ω !! + Άðδ ⁿэщ !! Button label that adds a new font feature for the current font. - Ьªćκģŗσūŋď ϊmăģз őρасΐţγ !!! !!! ! + Βαċĸġґθũηď ϊmдġэ орάсιτу !!! !!! ! Name for a control to choose the opacity of the image presented on the background of the app. - Ъäсќğřǿüŋđ îmâģë ŏρåςĩŧỳ !!! !!! ! + Βàĉĸġгòΰлð ϊmąġє бφáĉΐţÿ !!! !!! ! Header for a control to choose the opacity of the image presented on the background of the app. - Ŝěтš ŧħė бρа¢ïťу όƒ тħě ъä¢ќġѓòũиď īмαģз. !!! !!! !!! !!! + Ŝέтŝ тĥě ōφâĉіţŷ θƒ ŧħë ъâćќğŕоŭńð ĩмαĝę. !!! !!! !!! !!! A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". - Βäçκģяǿџиď ΐмāğё şŧґєтĉħ мöðě !!! !!! !!! + Вάĉĸġřόϋⁿð ĩмăğе şťŗ℮ŧĉђ мòδ℮ !!! !!! !!! Name for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Βäĉĸģŗöŭŋď ϊmăģ℮ ŝтѓέτċĥ mθδĕ !!! !!! !!! + ßäскġŗǿůπď īмâġэ şтŗĕтсħ мőďè !!! !!! !!! Header for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Śêŧѕ ћǿώ ťнě ьάćкġŕōūʼnð ìмªĝĕ ΐѕ ѓэśíżęđ ţô ƒïŀℓ ţħέ ώїⁿďоẅ. !!! !!! !!! !!! !!! !!! + Šëτş ħσω ŧħё ьãćķĝгøµπð īмαğę ϊš řĕŝіżèď ţθ ƒĭŀł ťћέ ωїηđôẁ. !!! !!! !!! !!! !!! !!! A description for what the "background image stretch mode" setting does. Presented near "Profile_BackgroundImageStretchMode". - ₣ίℓĺ ! + ₣ίľļ ! An option to choose from for the "background image stretch mode" setting. When selected, the image is resized to fill the destination dimensions. The aspect ratio is not preserved. - Иöⁿé ! + Ŋŏⁿé ! An option to choose from for the "background image stretch mode" setting. When selected, the image preserves its original size. - Ũпϊƒόřm !! + Üⁿĭƒòгm !! An option to choose from for the "background image stretch mode" setting. When selected,the image is resized to fit in the destination dimensions while it preserves its native aspect ratio. - Ũñĩƒοяm ţо ƒîłļ !!! ! + Úⁿîƒŏŕм τо ƒіļļ !!! ! An option to choose from for the "background image stretch mode" setting. When selected, the content is resized to fill the destination dimensions while it preserves its native aspect ratio. But if the aspect ratio of the destination differs, the image is clipped to fit in the space (to fill the space) - Рŗôƒїŀě ŧëямĩñàŧїǿň ьèĥдνїőŗ !!! !!! !! + Рѓǿƒïłė ţέřmįпąťїоп ьэндνіõя !!! !!! !! Name for a control to select the behavior of a terminal session (a profile) when it closes. - Ρřōƒïľě тėŗmìⁿàτΐôη ьęћåνïθŗ !!! !!! !! + Рŗσƒīļє τеŗmíŋªťιοñ вěђâνίøя !!! !!! !! Header for a control to select the behavior of a terminal session (a profile) when it closes. - Сłôѕĕ ωħёл φŕôçėšś ёхιτş, ƒâíłŝ, ŏŕ ċŕαśĥèѕ !!! !!! !!! !!! + Сĺöѕę ẃђèи φяô¢єśş êжîţš, ƒäιļš, оř ¢řąŝнéś !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled (exit) or uncontrolled (fail or crash) scenario. - Сłοšэ ǿήℓў ẅĥėи ρѓσçέśѕ έхìтš ѕúç¢ėššƒύĺļý !!! !!! !!! !!! + Сłбşê бńļγ ẅнēή рřόс℮śŝ ęжιτś ŝџçсĕšѕƒŭŀłў !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully. - Νёνèř čļоšė āμťσмªťìćǻŀℓÿ !!! !!! ! + Ŋ℮νĕŗ ćļǿѕê äμτőмâťĩċªļŀў !!! !!! ! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal. - ∆ũтõmàťïċ !!! + õťøмäтïč !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal. - Ĉοłǿŗ ѕćђ℮mэ !!! + Çŏłőг ŝćнĕмε !!! Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user. - Ĉοmмάπδ łϊʼnέ !!! + €οмmǻпδ łįʼnε !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Ċőмmάήď ļιňê !!! + Соммаňð ℓιπë !!! Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Çοммǻʼnδ łĭʼně !!! + Çŏммǻиδ ŀϊňè !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Эжê¢ûŧąвŀέ υѕéð ïи тђė ряõƒίł℮. !!! !!! !!! + Эхĕçΰтдьłё ūśëď įñ ţħè рŕòƒĭŀз. !!! !!! !!! A description for what the "command line" setting does. Presented near "Profile_Commandline". - Ьґóẅšĕ... !!! + ßгθшşє... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Čџѓѕòґ ħэįġħŧ !!! + Сµгŝőґ ђēîĝħт !!! Header for a control to determine the height of the text cursor. - Ŝєτš ŧћз рéŗςєηŧäğе ĥèίġħţ ôƒ ŧђê ċŭŗšбґ ŝтářţĩиģ ƒѓбм ťнė ъöŧŧσm. Ωņŀÿ ŵǿґκѕ ŵìτђ ťнê νіňŧąĝέ çцŗşоѓ śћāρе. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Šеτѕ тĥέ φегсеŋтåĝě ћēíġĥт öƒ ťĥέ ¢úґšόг ŝŧάřťīʼnģ ƒŕõм τћ℮ воţŧōм. Φņľγ шбŕķş ωітĥ τђĕ νĭŋťάġз ¢ūґşσř ŝнāφē. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "cursor height" setting does. Presented near "Profile_CursorHeight". - Ĉüѓѕôѓ ŝнąρэ !!! + Ćũяšòѓ ŝђàρĕ !!! Name for a control to select the shape of the text cursor. - €ůŗšоѓ ѕћàρ℮ !!! + Ĉϋřšǿг śнâρε !!! Header for a control to select the shape of the text cursor. - Ńęν℮ř ! + Ñèνêř ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will never adjust the text colors. - Ώʼnŀŷ ƒŏŗ ςôŀбґŝ ïй ŧĥë ςόļǿг şςћëmέ !!! !!! !!! ! + Òňľỳ ƒθг çöłőґš ΐл ťħé сøłθґ ŝĉћ℮мё !!! !!! !!! ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table. - Áĺшãÿŝ ! + Åļωάỳš ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. - Ъàŕ ( ┃ ) !!! + Ъâг ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. - Ęмφťỳ вο× ( ▯ ) !!! ! + Емρţý ьǿ× ( ▯ ) !!! ! {Locked="▯"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an empty box. The character in the parentheses is used to show what it looks like. - ₣їļľéð ьô× ( █ ) !!! ! + ₣ιŀłęđ вσж ( █ ) !!! ! {Locked="█"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a filled box. The character in the parentheses is used to show what it looks like. - Úйδęгŝςøŕę ( ▁ ) !!! ! + Ùήđĕřŝċогё ( ▁ ) !!! ! {Locked="▁"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an underscore. The character in the parentheses is used to show what it looks like. - Vįπţαğė ( ▃ ) !!! + Vίпτâĝĕ ( ▃ ) !!! {Locked="▃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a thick underscore. This is the vintage and classic look of a cursor for terminals. The character in the parentheses is used to show what it looks like. - Đоŭьℓè űñδêřšçòřэ ( ‗ ) !!! !!! + Ďòûьĺē ύηđεґşćбяĕ ( ‗ ) !!! !!! {Locked="‗"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a stacked set of two underscores. The character in the parentheses is used to show what it looks like. - ₣òлτ ƒąċē !!! + ₣øñт ƒâςę !!! Header for a control to select the font for text in the app. - ₣òņτ ƒǻĉè !!! + ₣øñŧ ƒдсέ !!! Name for a control to select the font for text in the app. - ₣θлŧ śϊźé !!! + ₣ŏňτ şίźε !!! Header for a control to determine the size of the text in the app. - ₣бητ šїźé !!! + ₣оňť şίżє !!! Name for a control to determine the size of the text in the app. - Şįźè σƒ ťħē ƒòʼnť ϊŋ φσĩиťš. !!! !!! !! + Šіžё ǿƒ тнé ƒõņτ їņ φσîйτş. !!! !!! !! A description for what the "font size" setting does. Presented near "Profile_FontSize". - Ľїπё ĥéíĝђť !!! + Ŀīйĕ ĥĕΐĝђŧ !!! Header for a control that sets the text line height. - Ľїńë нęĭğĥτ !!! + ₤ìŋè ħέίģћť !!! Header for a control that sets the text line height. - Ωνэґяϊδê ťĥε ℓїⁿê ћ℮ĩġћţ ôƒ τнэ τěřмïŋаŀ. Мēдѕũřĕð άš ά мџľţΐρĺ℮ ŏƒ τћē ƒŏиť šįžе. Тħĕ δеƒдûľţ νąℓцэ ďėφêйðş óл γόŭř ƒòлť ªйđ ϊś üŝŭáŀĺŷ äŗøūήđ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ονëřŗíďĕ тђэ ℓìʼnё ћëîğнτ оƒ ŧнè τёґмįņªļ. Мēáśυґ℮đ аś ª mϋłţìρℓè õƒ тне ƒõήτ ѕΐżë. Тђê ďέƒāųŀţ νâŀüē δέρěņđš ǿň ỳőυя ƒôʼnт ąʼnď ΐş υşūäľŀÿ àŗôüŋđ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "line height" setting does. Presented near "Profile_LineHeight". @@ -907,854 +907,886 @@ "1.2" is a decimal number. - Бűīłτĭл Ĝľурĥš !!! ! + Ьùïľŧïň Ģłуφĥś !!! ! The main label of a toggle. When enabled, certain characters (glyphs) are replaced with better looking ones. - Щђëń ℮йãвļэđ, тнė τєřmìήąļ đгäшŝ ċцšťσm ġłγφнş ƒôř ъŀσсķ èľėmέňτ âлď ьõх δґåώίиģ čћãѓαςτēřş íйşťëàď ǿƒ џşįηĝ τħé ƒŏñť. Тћїş ƒêάťųřė θņĺÿ ẁôѓκš щћéπ ĜΡÚ Λċсĕłėŕâŧιǿʼn ΐѕ ăνдìŀαвŀε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ẅħĕň еņªъℓёð, тћз τēгмïπдℓ ðѓáшş çύšτом ģļўφħѕ ƒǿя ъĺôсĸ éℓêm℮ήŧ àñð ъο× ðгдωΐπĝ čћдŗąςŧέѓś ìπŝţėãđ ŏƒ ũŝіⁿğ ťћę ƒбηŧ. Ţћιş ƒзаτџŕè οʼnĺŷ ωóгκŝ ẁĥéŋ ĢΡŬ Àςĉеŀèѓдτισñ ĩѕ ǻνãîļåвłę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. - ₣ύℓℓ-сôŀőŗ Ēmθјϊ !!! ! + ₣џłŀ-сθℓøґ Έmόјї !!! ! The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. - Ẁћέń зňªвłεð, Ємóĵί äŕė δΐşрľâýзð ίņ ƒύℓŀ ĉōĺõŕ. !!! !!! !!! !!! !! + Ẅнзп έňåвłёδ, Эмöĵī ãŕě ďíşρľǻỳęð ίñ ƒμŀℓ ćоļöŕ. !!! !!! !!! !!! !! A longer description of the "Profile_EnableColorGlyphs" toggle. - ₣οńţ ώєїġнţ !!! + ₣οиτ ẅэíğħť !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣ǿиŧ ẃєíģĥť !!! + ₣õиτ ŵėíğĥŧ !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣ǿŋτ ŵёīĝĥτ !!! + ₣оňτ ŵειġнт !!! Header for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - Šεţš τħē шείğнŧ (ℓΐģђťиеŝѕ ǿř ħзάνΐηėšѕ оƒ ţћé šŧѓσķέš) ƒōř τħ℮ ĝìνęⁿ ƒõńτ. !!! !!! !!! !!! !!! !!! !!! ! + Ŝéŧѕ ţђέ ẁèϊģнť (łϊğħţⁿéşś όř ђзâνĩηзśş óƒ ŧĥė śťгόќęŝ) ƒθґ тħέ ġįνэи ƒőňţ. !!! !!! !!! !!! !!! !!! !!! ! A description for what the "font weight" setting does. Presented near "Profile_FontWeight". - Vāřĭªвľ℮ ƒоñţ αхęś !!! !! + Vαгįдвłз ƒôηŧ á×ęś !!! !! Header for a control to allow editing the font axes. - Ǻđď θř я℮mσνë ƒöпт ãхęś ƒôŗ ţħě ġϊνєп ƒоņţ. !!! !!! !!! !!! + Āðδ θѓ ŗēмôνε ƒŏŋτ āжеŝ ƒóř ťћè ġīνęŋ ƒóήτ. !!! !!! !!! !!! A description for what the "font axes" setting does. Presented near "Profile_FontAxes". - Тћę ŝеļеçτėð ƒőпт ћåŝ йø νäяΐäьłё ƒøńŧ á×ěś. !!! !!! !!! !!! ! + Тђέ šěļεċťєď ƒŏηŧ ћάś πø νäѓїдвłз ƒōπт αжęš. !!! !!! !!! !!! ! A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". - ₣öňт ƒзåτũгэŝ !!! + ₣оʼnτ ƒзåŧŭŕέś !!! Header for a control to allow editing the font features. - Äðδ σґ řéмōνз ƒŏñт ƒєăŧūгёš ƒσŗ тĥз ġινęπ ƒõήт. !!! !!! !!! !!! !! + Ǻďð όŗ řěmòνě ƒóńт ƒєãťüгęś ƒθг тнĕ ĝĩνёŋ ƒόŋţ. !!! !!! !!! !!! !! A description for what the "font features" setting does. Presented near "Profile_FontFeatures". - Τђê šёľėĉŧěð ƒбņť ћăş пσ ƒŏήт ƒёάťϋŕěѕ. !!! !!! !!! !!! + Τћē ŝэŀëсŧзð ƒőйť нâŝ иο ƒòиŧ ƒėäţцŕэś. !!! !!! !!! !!! A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". - Ģєʼnеŗαℓ !! + Ġеʼnėяâĺ !! Header for a sub-page of profile settings focused on more general scenarios. - Ήіðе ρŕθƒΐļ℮ ƒгθм δřōφďòẁñ !!! !!! ! + Ήîď℮ φябƒίļĕ ƒѓòm ďřóφδόшπ !!! !!! ! Header for a control to toggle whether the profile is shown in a dropdown menu, or not. - Ίƒ эήªвℓεď, τћз ргоƒїłэ ẃíℓļ ņόţ аρφèåř ϊʼn ŧħę ŀїşţ οƒ φŗòƒíŀėŝ. Τĥїš сâⁿ ъ℮ üŝĕď ťό ĥΐďě đĕƒäΰŀŧ φяόƒіℓзѕ αʼnδ δўиáмìćǻļĺу ğ℮иεŕαтєđ φřöƒіĺεş, шнίľз ļэάνīήģ ţђĕм îñ ỳôûř śєţţίņğš ƒїľз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Їƒ ĕηάъℓěđ, ťĥэ φѓóƒїłę щíĺŀ иǿτ āφрĕдґ ΐŋ ŧћ℮ ŀīѕţ ǿƒ ρřŏƒīłėѕ. Ţђіş ςǻⁿ ъê ūšέð ťŏ ђĩðė δëƒǻūļт φґøƒíļέş аņð ďγñăмïĉâļℓÿ ģěиēяāŧεð ρřòƒίļзŝ, ẁĥιľє łэăνīⁿĝ ťĥèм ϊʼn ỳôцг śэťŧιйğŝ ƒιľë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "hidden" setting does. Presented near "Profile_Hidden". - Řύπ ŧћίś рѓŏƒìŀē дѕ Аδmĭⁿĭŝтяǻτöя !!! !!! !!! + Гµл тђįş φѓöƒιļэ åş Λđmįňίšтŕàтоя !!! !!! !!! Header for a control to toggle whether the profile should always open elevated (in an admin window) - Įƒ έпàьŀêδ, ťħê φяοƒΐŀĕ ẁιĺŀ όреŋ īη ªŋ Дđmìή теямĭńáľ ẅĩñðǿщ åúťσмăŧїĉāłĺў. Ίƒ ťħз ĉùяřéņť ŵіиδοẁ іş ªŀгеàđγ řµпñїпģ åś ąδmίņ, íţ'ĺŀ ōреή íʼn τђίş ωιńďõẅ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ'ŀℓ σрèń ĩй тħїŝ ẅīйδõẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Ĥĩšţоřý şΐźε !!! + Нíşŧôŗỳ ѕιźë !!! Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Ηïšтōґý ѕΐžě !!! + Ĥіśţόѓÿ šιžё !!! Name for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Тнë иϋmвĕŕ σƒ łίňêš αьονě тĥé σņёѕ δίşφĺąγěδ іл τħе ŵΐʼnđöŵ ÿоû çäη šςřôļĺ ьāćκ ţο. !!! !!! !!! !!! !!! !!! !!! !!! + Тђě ņůмвέґ όƒ ļïлèѕ àьόνэ τнέ ôиεŝ ďίşрℓâýèď ΐη ŧћз ẃΐлđóẃ ŷòц сåп şçřбŀľ вªćķ ťσ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "history size" setting does. Presented near "Profile_HistorySize". - Įςøņ ! + Îčόŋ ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ì¢öŋ ! + İ¢оń ! Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ìсθл ! + Į¢òп ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ēmōјĭ ôř ĩмąġě ƒϊľê ĺόćąтïбп όƒ τĥэ įĉõи úšêđ ìи ţĥє рřöƒįļέ. !!! !!! !!! !!! !!! !!! + Ęmőĵì õř îmаĝė ƒìĺĕ ļöĉǻтïòπ ǿƒ ŧђē ісǿⁿ ùšεð іń τĥε φřôƒιľê. !!! !!! !!! !!! !!! !!! A description for what the "icon" setting does. Presented near "Profile_Icon". - Βгøшŝė... !!! + Ьřòẁśз... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Рâðδїπĝ !! + Рáðđĩñġ !! Name for a control to determine the amount of space between text in a terminal and the edge of the window. - Ρäðďįиģ !! + Ρáððìʼnğ !! Header for a control to determine the amount of space between text in a terminal and the edge of the window. The space can be any combination of the top, bottom, left, and right side of the window. - Ŝέţѕ ţнé ρªðδìπġ аґôϋňδ тђě ţêжт шĩτђĩη ŧħě ẁιⁿđöш. !!! !!! !!! !!! !!! + Śěтš тђ℮ ρªđδїлğ ăѓоūńđ ŧћέ τεжт ẃĭтħϊп τħέ ẃίņδθẁ. !!! !!! !!! !!! !!! A description for what the "padding" setting does. Presented near "Profile_Padding". - Ŗèтгб ŧěѓmϊňâℓ ėƒƒέĉτѕ !!! !!! + Яęтѓō тéѓmιйăĺ 냃έсŧŝ !!! !!! Header for a control to toggle classic CRT display effects, which gives the terminal a retro look. - Ŝħõщ ѓэťřò-şŧýľě τěŗmϊπдł єƒƒēċтş ѕùсĥ ąś ģłøώìлĝ ŧě×τ άήđ şčãņ ļϊηěś. !!! !!! !!! !!! !!! !!! !!! + Śĥбẅ гзτřō-şтγłе τëѓmīⁿдĺ еƒƒέсτś şúćђ αş ğℓōώĩиġ ţέхť άηδ šςаи ľìиėš. !!! !!! !!! !!! !!! !!! !!! A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect". "Retro" is a common English prefix that suggests a nostalgic, dated appearance. - Àũŧŏмâťί¢αļℓў αδјûšţ ļіģнţⁿ℮śš õƒ їńďιŝţίňĝūíѕћавłз тēжţ !!! !!! !!! !!! !!! ! + Аŭťοмäтĩčāĺłỳ ăδјΰѕť ļϊģђτπ℮şѕ ǿƒ їиδιšťΐņğűīşћāвłэ тêжť !!! !!! !!! !!! !!! ! Header for a control to toggle if we should adjust the foreground color's lightness to make it more visible when necessary, based on the background color. - Аŭťömάťî¢áŀľÿ ъґĭģнтéήş оŗ ðãґќέñś ŧēжţ ťõ mâĸē įţ mοřė νĭŝїьļ℮. Еνεй ŵћêή ейдвĺέδ, ŧħίŝ ǻδјůşŧмзņŧ ωìℓļ ōиłў óćĉúг ẃĥëη à сòмвĩиăтϊǿй őƒ ƒŏřęĝŗǿϋηð алð ъą¢ķğřòύиđ ċôłǿѓş ώǿűŀð ѓеѕŭℓţ ιπ ρоóř ¢ôñтґåѕŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ∆ųţοmäţιċãℓľў ьŗϊġĥťëñś ǿŗ δåřķēиѕ ťěжŧ ťò mäќε ĭŧ mόґз νίşìъŀĕ. Зνёŋ ẃћéŋ ęηāьℓёδ, τћιѕ äđјυšŧměйŧ ẃìℓŀ õñℓў ôćčцг ŵђěи ǻ çøмъĩⁿãτĩθñ øƒ ƒбřεġґòϋňδ áηď вāсκģřŏŭпδ čółοřş ẁóΰłđ я℮şŭľτ ìñ ρőόŗ сõʼnтгáѕт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors". - Ŝсгŏļľьâř νīŝίвĩℓïτγ !!! !!! + Šсґσļľвąя νĩѕìъįłіťу !!! !!! Name for a control to select the visibility of the scrollbar in a session. - Ŝćяόℓľвăґ νįѕīъìℓιтŷ !!! !!! + Ščяòľŀвåŕ νίšιъįŀιτÿ !!! !!! Header for a control to select the visibility of the scrollbar in a session. - Ĥĭđďéй ! + Ĥΐδđēⁿ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is hidden. - Vīѕïвłè !! + Vїşівłε !! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible but may dynamically shrink. - Άĺẅäÿš ! + Âℓώãýѕ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is always visible. - Śςŕбℓŀ ŧό įñρųť ẁĥэή ţўрīηğ !!! !!! !! + Śсгόľĺ ťσ їŋрυт ẃħęπ ţỳφįⁿģ !!! !!! !! Header for a control to toggle if keyboard input should automatically scroll to where the input was placed. - Ѕтāřŧΐņğ ďіřěсťôѓý !!! !! + Ŝταяτìлĝ ðĩѓ℮ćτоŗỳ !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Šťąяţιʼnĝ διѓ℮čťőѓγ !!! !! + Ѕťåřťіπġ đíгëĉţöґγ !!! !! Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Ŝтäгţϊŋġ ðϊŗëçτöяў !!! !! + Šтдřтíⁿģ đϊŕёςŧôŗў !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Тђэ ðιřесτöŗў τнę рřбƒîļ℮ şτаřтş їņ ẅħėл īŧ ίş ŀόǻδėď. !!! !!! !!! !!! !!! ! + Ţћé đĭŗéçťòŕу τħе рѓøƒĩŀе śťªřťş ïή ẅћĕл îţ ίѕ ŀбдδėď. !!! !!! !!! !!! !!! ! A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory". - Ьřŏωѕē... !!! + Ьŕōωŝē... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Џѕé φãŕėⁿţ рřóςзŝš ðįŕзćтøяγ !!! !!! !! + Üŝè φαŗèńŧ рřоĉěѕѕ đĭřё¢τоґý !!! !!! !! A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process. - Ĩƒ éидьłєđ, τħîś рřôƒīℓĕ щìľĺ šрдŵŋ ìй ťћє δίřëçťσřŷ ƒŕόm ẁђісн Ťêřmìñáļ шăš ŀǻцηćђёδ. !!! !!! !!! !!! !!! !!! !!! !!! ! + ΃ έпãьłëď, тђìš ρѓòƒìĺè ωιĺℓ ŝρǻẁň іň ţћę ðіяеçţбгγ ƒřőм щћįςђ Теямĭņàł ẃªş ŀáцⁿĉħёđ. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox". - Ħïđе ĩсöи !!! + Ηϊðё îçǿй !!! A supplementary setting to the "icon" setting. - ̓ ĕпàвłèď, тћįŝ φřоƒīłę ωîłł ĥдνë ņŏ įċόŋ. !!! !!! !!! !!! + ̓ ёηдъļэδ, ŧĥϊŝ φгōƒιℓз шіℓŀ ђдνê ňö ΐĉôй. !!! !!! !!! !!! A description for what the supplementary "Hide icon" setting does. Presented near "Profile_HideIconCheckbox". - Śųφφгęśš ţĭţļě çħаńģéš !!! !!! + Ѕΰφφřэśš τíťļė čħªʼnĝèѕ !!! !!! Header for a control to toggle changes in the app title. - Іģŋŏŗê āρφℓīçάŧΐøη гέqüęŝţŝ τθ ĉнаňģέ ŧђé ţιτĺе (OSC 2). !!! !!! !!! !!! !!! ! + Íġπόŗэ áρρĺіćâţìǿŋ ŗέqúёśţś ťò çђâηğě ťђê тįтŀé (ΘЅĆ 2). !!! !!! !!! !!! !!! ! A description for what the "suppress application title" setting does. Presented near "Profile_SuppressApplicationTitle". "OSC 2" is a technical term that is understood in the industry. {Locked="OSC 2"} - Тăь ţíтľé !!! + Τάъ ŧїŧļê !!! Name for a control to determine the title of the tab. This is represented using a text box. - Ťаь тīтľę !!! + Ťǻв τįťłэ !!! Header for a control to determine the title of the tab. This is represented using a text box. - Гεрľǻ¢έş ţћё рřǿƒїłз пам℮ áѕ ŧħë тīţļε ŧο φªšѕ тǿ ţђê ŝħěłľ öή śŧāѓτùρ. !!! !!! !!! !!! !!! !!! !!! + Ŕèφłǻčèş ţће рřöƒîℓέ иάмз äş τне тíŧŀë ŧŏ φāѕş ŧό ţĥë şђëℓł öņ śťâгŧûρ. !!! !!! !!! !!! !!! !!! !!! A description for what the "tab title" setting does. Presented near "Profile_TabTitle". - Ûŋƒǿċûšεð äрρεªŕаήςз !!! !!! + Ûηƒŏċμşεď äррėāŕαńćё !!! !!! The header for the section where the unfocused appearance settings can be changed. - Čřęдτє ∆рреářдйċе !!! !! + Ĉřêάŧэ Δрφзάŕãñςε !!! !! Button label that adds an unfocused appearance for this profile. - Đёĺęτê ! + Ðéľėŧê ! Button label that deletes the unfocused appearance for this profile. - Елáъłε āçѓýŀϊĉ мάтεŕіâĺ !!! !!! + Єňάьłē дсѓŷĺĭс mąτěřìªŀ !!! !!! Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Дрρłĭèŝ á ťяåйşļüсеиţ ŧє×ţūřё ţθ тĥз ьā¢кĝгöũńð бƒ ŧĥε ẃĩήðбŵ. !!! !!! !!! !!! !!! !!! + Àφρļįëś ã ţґάήŝℓűсэиŧ тēжτυŗэ ťό тĥ℮ ъάćĸĝґõυήď õƒ ţћ℮ щϊηðǿщ. !!! !!! !!! !!! !!! !!! A description for what the "Profile_UseAcrylic" setting does. - Űšē δєśκτθρ ώàŀłρåρèŕ !!! !!! + Ŭŝē đεśķτòр ẃąℓłραрĕя !!! !!! A supplementary setting to the "background image" setting. When enabled, the OS desktop wallpaper is used as the background image. Presented near "Profile_BackgroundImage". - Ũѕě ťђе đëѕќτōр ŵαℓŀрαρёг įмάġē ªş тнє ьªċĸĝѓŏûⁿδ імáģě ƒσг тће τĕѓmіηáľ. !!! !!! !!! !!! !!! !!! !!! + Ůšê тнè ďєśķťōρ ŵªĺļраφēř ímǻğё αś тħэ ъąĉκġřōůлđ іmãġè ƒόґ ŧн℮ ţэґмϊлªļ. !!! !!! !!! !!! !!! !!! !!! A description for what the supplementary "use desktop image" setting does. Presented near "Profile_UseDesktopImage". - Ďϊѕĉªřđ ċнάиğеś !!! ! + Ď욢αŗð ĉђăʼnġęѕ !!! ! Text label for a destructive button that discards the changes made to the settings. - Đΐѕċάŗď äŀĺ ϋйšǻνëď śēττìйğś. !!! !!! !!! + Ðĭŝĉäŕð дĺľ цŋşåνёď ѕεťŧīńġś. !!! !!! !!! A description for what the "discard changes" button does. Presented near "Settings_ResetSettingsButton". - Śάνê ! + Śāνэ ! Text label for the confirmation button that saves the changes made to the settings. - ⚠ ¥θũ ђāνε űпѕªνêð čћалĝεŝ. !!! !!! !! + ⚠ Υŏű нâνě цπśąνêδ ¢ђãпġēš. !!! !!! !! {Locked="⚠"} A disclaimer that appears when the unsaved changes to the settings are present. - Āδð а π℮ẅ φяόƒіļę !!! !! + Ãđδ α ηèŵ ряоƒіľэ !!! !! Header for the "add new" menu item. This navigates to the page where users can add a new profile. - Ρяóƒĩļеś !! + Ρґőƒϊłеѕ !! Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items. - ₣öċΰš ! + ₣őćџŝ ! An option to choose from for the "launch mode" setting. Focus mode is a mode designed to help you focus. - Μǻ×імϊžеδ ƒó¢űѕ !!! ! + Μãхîмížęď ƒòćύŝ !!! ! An option to choose from for the "launch mode" setting. Opens the app maximized and in focus mode. - Μа×ίmїžęð ƒцłľ ѕćґєĕň !!! !!! + Мª×įmίźėδ ƒύļĺ šсґèэη !!! !!! An option to choose from for the "launch mode" setting. Opens the app maximized and in full screen. - ₣ŭŀĺ ščŕėёп ƒòçϋş !!! !! + ₣üĺℓ śčřèéη ƒŏćŭś !!! !! An option to choose from for the "launch mode" setting. Opens the app in full screen and in focus mode. - Μà×ιmιźèď ƒџĺľ śćŗěёп ƒøčύś !!! !!! !! + Μąжįмīźēď ƒµĺļ śсяé℮ⁿ ƒθčϋš !!! !!! !! An option to choose from for the "launch mode" setting. Opens the app maximized in full screen and in focus mode. - Бēŀĺ лòτιƒϊçâŧíóй šťÿŀэ !!! !!! + Бэĺł иőťíƒΐćªŧίøл ѕţуĺз !!! !!! Name for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Ъεļľ йόťїƒïсäţιòл śтŷłĕ !!! !!! + Веĺł йθŧϊƒīçǻťΐθп ŝťỳℓé !!! !!! Header for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Çσñţгøłŝ щђāţ ĥäρφεиŝ шћёη ŧђë åφрℓįčаτίбⁿ ēmīτѕ ǻ BEL ćħâŕäćтęг. !!! !!! !!! !!! !!! !!! ! + Ĉøήτѓōℓѕ ẅнǻτ ђдρφéпš шђĕñ тнē ąρрĺιčάτĩоň емìťś а ЬΕ£ ćђãяд¢ţĕř. !!! !!! !!! !!! !!! !!! ! A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"} - £ãűп¢н ťнΐѕ äρρłîĉάτìøň ẅіţĥ ã йεω èņνĭґοńmĕʼnт ъłŏ¢ĸ !!! !!! !!! !!! !!! + £αüņ¢ħ ŧћΐš аρφℓîсäŧïōп ŵìţн α йéω ёňνΐѓõлmĕπţ ъłосќ !!! !!! !!! !!! !!! "environment variables" are user-definable values that can affect the way running processes will behave on a computer - Ŵђέи έńąъĺèð, ŧħë Тĕґmїŋâļ ẁíŀľ ğ℮ⁿėřåτ℮ ª ήέщ εήνĩřσπmёиţ ьļōċķ ŵђêή ĉŗêάŧϊйģ ņěω ťάвѕ ŏг рαñёş щīтћ ţнїѕ рŕöƒíľē. Шћεń đīŝαъłëđ, ţħé ţáъ/φąňз ẃίŀℓ ìňśţедď îηħëяíţ ťнê νагϊåъĺêŝ ţђě Ťэŕmϊπāł ẁдŝ şтǻгтєđ ẅïťћ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẁћέл εņаьŀèď, τħё Ŧëгmĩňªľ ẅīŀℓ ğεлěяαте å ʼnэщ еńνîřόńмęñт ьŀøĉĸ ŵħ℮л čґēãťîņğ ʼnεώ τâвѕ όя рªлёѕ щĭτћ тĥĭѕ ρŗòƒΐℓė. Ẅђêй ðìšąвļėđ, τћë ťąь/рáňε шîŀĺ íñśτėáđ īήђзŕίτ τђė νăяϊаьℓĕŝ ţђε Ťęřmíŋаŀ ẁαŝ śťäřťέδ ωįтħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - Еņǻвŀè èжρ℮яιmëлťäł νіѓţυåł τ℮ŗmιлăļ ρäŝŝŧђѓόϋġħ !!! !!! !!! !!! !! + Зńăьℓē эхφзґímėпτªŀ νіѓтűǻļ τ℮ґмϊñāļ φаśşтнґöúģн !!! !!! !!! !!! !! An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Ðįşφłąÿ ǻ мèηΰ όή řïĝĥť-ćľїçķ !!! !!! !!! + This controls how a right-click behaves in the terminal + + + Ẁђéŋ êπäвĺεð, ţĥё Τéŕмįⁿął ẃìļŀ ďïśρĺąỳ ã мέиц öη ŕìğĥţ-сℓĩ¢ķ. Ŵћëʼn đϊśªьŀèð, řιġћţ-čℓî¢ķιñĝ ẁĩłℓ ĉσрÿ тħē şèłэĉţĕđ ŧэжτ (ŏя рâŝтé ΐƒ ťћзяę'ś йб śзľĕćţîбñ). !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Ďιşрĺąŷ мǻŕĸş θй ţĥě ŝćřбŀŀъáŕ !!! !!! !!! + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Ẅĥέň ёпдъļêď, τĥ℮ Τëгmįиªł ẃĩľļ ðΐśρℓăỳ мāѓκś ιñ ţħє şĉѓòℓľьªŗ ŵħëⁿ ŝēãѓçђιŋĝ ƒθř ťèжт, σѓ ŵћзп šђêļŀ ίπтέğґãťîόи іś εŋäьłёđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + ∆űŧòмåťĩċǻłļγ маѓķ ρřǿmρŧş óй φґěѕşіиğ Еŋŧєř !!! !!! !!! !!! ! + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Ẃћеⁿ эņáвļēď, тħè Тěřмíиãŀ ǻµтοмăťìςǻℓľу åδď ã мâŕĸ іиđιςåŧïⁿġ тħé рσŝĩţίол öƒ ţђé ęņð øƒ τĥę ςбmмǻйδ ẃћéи ýθů φŕэѕѕ ēиŧęѓ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Ê×рэґїmзʼnτâļ: Гэрŏśΐťĩőⁿ τћέ ćũŕšσŕ ẁіτн mσůśё ċℓīċĸѕ !!! !!! !!! !!! !!! + This allows the user to move the text cursor just by clicking with the mouse. + + + Ẅћзή ℮йăъļэð, ĉľīćкїηġ ιňśīðè ŧĥё ρŕσmρŧ ωïŀļ мσνэ ţћё čúřšŏѓ тő ţħάŧ рōŝΐτíôⁿ. Ŧђΐŝ ŕéqűїřέѕ šћėľľ іñţεģřãţīǿň тø ъē зŋāвłêð ιπ убųѓ ѕћєľĺ τó ẃθѓκ ăѕ зжφĕčτēď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + - Àũđιьľē !! + ∆ůðĭъĺę !! An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. - Ňōπë ! + Ñбʼnĕ ! An option to choose from for the "bell style" setting. When selected, notifications are silenced. - Ďΐŝäъĺє ρǻņ℮ äηімдтіøйѕ !!! !!! + Đїŝâвŀè рăŋě ǻйįmąтіǿňŝ !!! !!! Header for a control to toggle animations on panes. "Enabled" value disables the animations. - Вľåĉĸ ! + Вĺåčќ ! This is the formal name for a font weight. - ßοℓď ! + Ъθĺď ! This is the formal name for a font weight. - €úšŧǿm ! + Ćцѕτσm ! This is an entry that allows the user to select their own font weight value outside of the simplified list of options. - Ê×ţřа-ßℓąçќ !!! + Ĕжťŗā-Βļаċк !!! This is the formal name for a font weight. - Эжτяå-Вóļď !!! + Ёжţгд-Боļδ !!! This is the formal name for a font weight. - Єхтгā-₤ïġħт !!! + Ę×тяā-£ïĝћţ !!! This is the formal name for a font weight. - Ŀįĝђŧ ! + Ŀιğĥŧ ! This is the formal name for a font weight. - Мέδΐüm ! + Μєđιϋм ! This is the formal name for a font weight. - Йоřмãľ ! + Ňŏŗmªĺ ! This is the formal name for a font weight. - Ѕěмĭ-Βοℓδ !!! + Şèмï-Вōłđ !!! This is the formal name for a font weight. - Ѕēmį-Ļίġћτ !!! + Ѕėmį-₤іģħŧ !!! This is the formal name for a font weight. - Ŧħіʼn ! + Ŧħīň ! This is the formal name for a font weight. - Řεqцιŕèδ ļіġãťųяéŝ !!! !! + Ŗéqùїяëď ľîģąτüřеŝ !!! !! This is the formal name for a font feature. - Ļοςâĺϊżêð ƒσгmš !!! ! + £ǿčãłϊźэď ƒŏгмş !!! ! This is the formal name for a font feature. - Сõmρőšíτìοñ/đэčómрôşĩŧіŏņ !!! !!! ! + Ćóмρőśĭťϊοņ/ðέċömφóšίτìøη !!! !!! ! This is the formal name for a font feature. - Сőиτęхťûâł άĺτèřⁿáτεŝ !!! !!! + Сθⁿте×тüáŀ ǻℓţėŗпαтēŝ !!! !!! This is the formal name for a font feature. - Ŝťάŋðαгđ ļιġдτύґэѕ !!! !! + Ѕτάлďάґð ℓíġąтŭѓεѕ !!! !! This is the formal name for a font feature. - Ĉöηťёхţџªŀ łîğáтüŕėş !!! !!! + Ċоňťêжţũāł łіġăŧµѓèś !!! !!! This is the formal name for a font feature. - Ґëqΰίґ℮δ νâгĩдťΐŏň ãĺţěřńаţēś !!! !!! !!! + Ŕëqџìґêð νãяìâтïöп ªļтĕгñąţëš !!! !!! !!! This is the formal name for a font feature. - Κзяňϊņğ !! + Ќεřпĩńģ !! This is the formal name for a font feature. - Μâѓк рǿŝϊтїôňίήĝ !!! ! + Мăґĸ φбśїτìõйίήģ !!! ! This is the formal name for a font feature. - Мåѓķ тǿ mářк φοşíţĭòņįñģ !!! !!! ! + Мªяκ ŧθ máгķ φбşίţīōпîņĝ !!! !!! ! This is the formal name for a font feature. - Đìşťάňçе !! + Ďïşťªпčє !! This is the formal name for a font feature. - ∆ćčεśś àļŀ ǻŀтèřйäť℮ѕ !!! !!! + Âĉċēśş αłł ǻĺтэяńáťėş !!! !!! This is the formal name for a font feature. - Čåśé śęπšīтΐνé ƒŏѓmѕ !!! !!! + €αŝē śεⁿŝιţіνé ƒǿŗmŝ !!! !!! This is the formal name for a font feature. - Đĕиòmĭñдţόґ !!! + Đēñômĭņąţόя !!! This is the formal name for a font feature. - Тëřмìήâŀ ƒοŗмś !!! ! + Ŧēгмιπáļ ƒόŕmś !!! ! This is the formal name for a font feature. - ₣řąċŧίοňѕ !!! + ₣ŕãçŧїõήŝ !!! This is the formal name for a font feature. - Ίпįτιâł ƒóřмŝ !!! + Īήíťĭàľ ƒõяmŝ !!! This is the formal name for a font feature. - Μеðïªł ƒθŗmš !!! + Μеďìαℓ ƒбřмš !!! This is the formal name for a font feature. - Νμm℮гåŧŏŗ !!! + Ňϋмēгªţőř !!! This is the formal name for a font feature. - Фŗďίпåłš !! + Οřďĩŋäℓѕ !! This is the formal name for a font feature. - Гéqűіŗєð čοⁿťєхтűǻľ ªłťęгņăтěş !!! !!! !!! + Ŗèqŭīŗеð ĉοйτęхťüàĺ áľţėřʼnãŧεŝ !!! !!! !!! This is the formal name for a font feature. - Ŝçīэņтïƒìċ ìпƒéŗïôŗѕ !!! !!! + Śсϊèⁿťίƒιč ΐñƒєřīōŗŝ !!! !!! This is the formal name for a font feature. - Ѕùьšçřιρŧ !!! + Śûвś¢řîρţ !!! This is the formal name for a font feature. - Śŭрзґŝςяîφτ !!! + Ŝùρεřśčřîφŧ !!! This is the formal name for a font feature. - Šĺαşĥęđ żëřő !!! + Ѕĺáѕħëď żêґø !!! This is the formal name for a font feature. - Ąьõνė-ьäŝе мāґќ ροśίτíόйїπģ !!! !!! !! + Λъŏνє-ъåѕέ мǻяќ φōśīτΐóņīлĝ !!! !!! !! This is the formal name for a font feature. - Ŀâûņčн śĩźε !!! + Łáцηсħ šіžē !!! Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". - Тне иµmвэѓ оƒ ябẃş äйδ ¢őľųmпś ðīѕρĺαÿĕď ϊй ŧћё ẁίйďοщ úφøл ƒīřŝτ ļøåď. Мздŝůязð îņ ¢ħаѓàćтĕřş. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Τĥé ήυмвēŕ θƒ гόωş äйđ ςôľũмņś đΐşφĺąŷęđ îй ŧĥê ẃįňďŏώ ųрбŋ ƒїяšţ ļŏăď. Μêάşµřēď īй ςħαŕāçτέřś. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "rows" and "columns" settings do. Presented near "Globals_LaunchSize.Header". - Łдϋйċĥ рοšįţīσи !!! ! + Ľâűπçћ φσѕíţįбʼn !!! ! Header for a group of settings that control the launch position of the app. Presented near "Globals_InitialPosX" and "Globals_InitialPosY". - Τћє ïņіτīдŀ φоşїţїθŋ бƒ ŧĥê т℮řмĩлąŀ ẁιŋδøщ ΰρőň şтáгτυр. Щĥеⁿ ľǻűņċђĭπĝ ãś mªхíмìžĕď, ƒůļĺ ś¢ŕêέŋ, θŗ ẅīťĥ "Ĉëňτëř ŏή ļąùⁿċĥ" єńάьĺэď, ŧћίš ĩš ûŝèď τø ţâгĝεţ тĥє мøñіŧōŕ øƒ įπťέřέśт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ťнέ īηĭţϊąĺ φǿšĩтīòл őƒ ŧђę τĕѓмīηäł ωĭиđŏщ ϋρőʼn śτåřťϋр. Ŵнέη ľãцпçђìŋģ άŝ mã×ĩмιż℮ď, ƒµļľ ŝ¢ґэ℮ň, øř ẃĩτħ "Çĕпτеґ ои łåúŋ¢ђ" ĕņǻвℓėδ, ŧĥíŝ íś џşĕď ţö ţάřĝēŧ тħē моŋітòя σƒ įпťēґēśŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "X position" and "Y position" settings do. Presented near "Globals_LaunchPosition.Header". - Аľļ + Äŀℓ An option to choose from for the "bell style" setting. When selected, a combination of the other bell styles is used to notify the user. - Vìŝüåℓ (ƒℓдşĥ ťªšкьãґ) !!! !!! + Vĭśũάŀ (ƒĺāşђ ţąşκвдѓ) !!! !!! - ₣łäśĥ ταŝķьãř !!! + ₣ŀªśћ τâśќъäѓ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the taskbar is flashed. - ₣łáşћ ẁįйđǿŵ !!! + ₣łāŝћ ẅїňďŏщ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the window is flashed. - Ąðδ ⁿëώ !! + Äδď ⁿĕш !! Button label that creates a new color scheme. - Ď℮ļēŧ℮ çòļбґ ş¢ђèме !!! !!! + Ðєĺèŧĕ ¢оľōѓ ѕснεмė !!! !!! Button label that deletes the selected color scheme. - Εďîŧ ! + Σδϊт ! Button label that edits the currently selected color scheme. - Ðэĺëţê ! + Đеļėŧę ! Button label that deletes the selected color scheme. - Ламē ! + Ŋām℮ ! The name of the color scheme. Used as a label on the name control. - Ťћè ńǻmè őƒ ţнέ čθłōř śςнēмэ. !!! !!! !!! + Ŧħё ŋąмэ őƒ τћě ¢ǿℓôѓ śçђéмē. !!! !!! !!! Supplementary text (tooltip) for the control used to select a color scheme that is under view. - Đēℓëţĕ ρяôƒϊļё !!! ! + Đёľĕŧз рŗõƒιŀê !!! ! Button label that deletes the current profile that is being viewed. - Ţħĕ ńąmė õƒ ŧħë ρґõƒΐľэ ŧћâť αррëãѓŝ ĩñ τђĕ đґöрðőẃņ. !!! !!! !!! !!! !!! + Ťћє ηǻмэ бƒ тнέ рřоƒïľé ţĥāτ άрρеαяѕ ĭπ ťћê đŕøφđóẃи. !!! !!! !!! !!! !!! A description for what the "name" setting does. Presented near "Profile_Name". - Йămε ! + ∏ãmê ! Name for a control to determine the name of the profile. This is a text box. - Иåмë ! + Ņàmě ! Header for a control to determine the name of the profile. This is a text box. - Тґαиšрāгеʼn¢ў !!! + Ţřąηѕρªґěňçÿ !!! Header for a group of settings related to transparency, including the acrylic material background of the app. - Ьãćκġяοùņđ ϊмаġе !!! ! + Βдĉкġřöûⁿď ìmąĝέ !!! ! Header for a group of settings that control the image presented on the background of the app. Presented near "Profile_BackgroundImage" and other keys starting with "Profile_BackgroundImage". - Ćϋřşθř ! + Čυřŝóґ ! Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". - Áďďітĭόйāĺ şęţтīпğś !!! !!! + ∆ďδΐţіõпаľ śєτťΐņĝŝ !!! !!! Header for the buttons that navigate to additional settings for the profile. - Ţєхŧ ! + Ţέхţ ! Header for a group of settings that control the appearance of text in the app. - Шΐñðöω ! + Щіňďош ! Header for a group of settings that control the appearance of the window frame of the app. - Øφĕⁿ ÿόυг settings.json ƒϊłз. Аℓť+Ċľĭčк ťб öφėй ŷοüř defaults.json ƒιĺę. !!! !!! !!! !!! !!! !!! !!! + Ώр℮п ỳõμѓ settings.json ƒιĺ℮. Áŀţ+Ċĺι¢ќ тθ ôφ℮л уοųѓ δěƒåџĺţş.јśŏл ƒïłе. !!! !!! !!! !!! !!! !!! !!! {Locked="settings.json"}, {Locked="defaults.json"} - Ґęʼnámě ! + Γ℮иάмê ! Text label for a button that can be used to begin the renaming process. - Ţнïѕ ćöℓσř ѕčнěмз ćάиʼnôŧ вέ δзĺєŧéδ οŗ ŗëήάмέð вëċäůѕĕ îŧ įş ιʼnćĺùðèď ъÿ ďėƒāûľť. !!! !!! !!! !!! !!! !!! !!! !!! + Τћîŝ čŏļòя şċĥěмε ćâňñǿτ ьё đĕļ℮ťèδ ōŕ ґĕňãmėð ьēĉаūśё ĭţ îś їńċľůðзď ъγ đэƒāûŀŧ. !!! !!! !!! !!! !!! !!! !!! !!! Disclaimer presented next to the delete button when it is disabled. - Ŷêś, đëℓетє čóĺοг ŝςнéмє !!! !!! ! + Ýëѕ, ðěĺέŧε çółöг śċћэмέ !!! !!! ! Button label for positive confirmation of deleting a color scheme (presented with "ColorScheme_DeleteConfirmationMessage") - Âŕĕ ỳõυ ѕΰгè ỳöû ŵаπť тŏ δеĺĕŧэ ťħīŝ ĉòľōґ śçђęmέ? !!! !!! !!! !!! !!! + Аŕë уоû şύŗê γǿџ ẃаηť ŧθ ðέłēтε тнíѕ çσľòř ѕćĥεмέ? !!! !!! !!! !!! !!! A confirmation message displayed when the user intends to delete a color scheme. - â¢єрτ řέñαмё !!! + Àćĉêрŧ ŕεηáмĕ !!! Text label for a button that can be used to confirm a rename operation during the renaming process. - Čдл¢єℓ г℮ήämė !!! + Čªⁿςęℓ гëήдмέ !!! Text label for a button that can be used to cancel a rename operation during the renaming process. - Śĉђέмёş δėƒίńéđ нëяё ĉåʼn вє åρφľїеđ ţθ ўσϋя ряοƒïłėѕ ũηδēř ŧнё "Λрρёąяªŋćеś" ѕ℮стīол øƒ ţħë φřóƒΐĺé şêţťΐņĝś рäğёš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Šсћěмěš đèƒιñèđ ћėřė ċдŋ ъę αрφŀĩ℮ð ŧö ỳóцř ρґοƒįℓэŝ цйðĕř ŧћ℮ "Άρφêäгăήςεѕ" šєĉťΐόη θƒ τћę φґôƒіℓē şĕŧŧιⁿĝѕ рǻĝêѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A disclaimer presented at the top of a page. "Appearances" should match Profile_Appearance.Header. - Şέŧтįηġş ðëƒίņєđ ђέřé ẁïĺĺ ąρрļŷ τō дłľ φґŏƒïŀęş űπℓёšŝ тнêÿ ªřэ σνέřяіððєʼn вỳ á ρŗóƒĩłε'ś ѕєţтιπğѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Śéţţĩñģš ďėƒįňеδ ђеŗ℮ ẃίľĺ áφρℓý ŧō ªļł ряöƒíŀєš ūлĺèŝŝ ťħęу àгë σνэřřїδδέй ьγ а рŗőƒįŀé'ś ŝëтţіиġš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. See "Nav_ProfileDefaults.Content" for a description on what the defaults layer does in the app. - Υêš, ďęłēтė ргõƒϊŀε !!! !!! + Ỳěŝ, ďзľетè ρřöƒīľé !!! !!! Button label for positive confirmation of deleting a profile (presented with "Profile_DeleteConfirmationMessage") - Àґэ ỳŏų šüґє уοū ŵǻπт ŧõ đēľеτ℮ ţĥĭś ρгòƒįℓэ? !!! !!! !!! !!! ! + Дгè уøџ ѕŭřз ỳθū шдŋт ťö đěĺэţè ţћιś φгòƒίŀ℮? !!! !!! !!! !!! ! A confirmation message displayed when the user intends to delete a profile. - Şąνè âłł ΰňѕăνêď şзŧтĩņģŝ. !!! !!! ! + Ѕąνε āľŀ ΰʼnşаν℮ð šέťτϊŋģŝ. !!! !!! ! A description for what the "save" button does. Presented near "Settings_SaveSettingsButton". - Тãь şωïţςħ℮ŗ ϊŋţêŗƒăċè šţўłē !!! !!! !! + Τãь ŝẃιťςђęř įⁿтëяƒā¢ĕ şтγℓε !!! !!! !! Header for a control to choose how the tab switcher operates. - Ѕэℓęćţś ẅĥϊςћ ϊηŧεгƒǻçę шïłľ ьє ùšĕđ ẁнëη ÿóŭ śẁΐτčħ ţāьś úŝìŋģ τђë кêýъøàŕδ. !!! !!! !!! !!! !!! !!! !!! !! + Śėℓĕčţś ẃħĩçћ īиŧēгƒаςэ ωīℓℓ вè цŝëδ шħёл ŷόџ şщīťĉђ ŧáвš ùśīлġ τнз ќèŷьóâяð. !!! !!! !!! !!! !!! !!! !!! !! A description for what the "tab switcher mode" setting does. Presented near "Globals_TabSwitcherMode.Header". - Śēφăřâŧę ωΐⁿđόẃ, ίŋ моšτ řєсзпťŀу ūŝėδ όřďзя !!! !!! !!! !!! ! + Şéράŗâт℮ ŵĩñðōω, ïл mθŝť ŗĕčєπťℓу ūŝěď оŗδêґ !!! !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in most recently used order. - Śèрαґąтę ωĭŋđõẃ, įй ŧăв şţŗίр σŕδέř !!! !!! !!! ! + Šєρãѓªτе ẅійđòẁ, îń ťāъ śŧґір σяðєŗ !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in the order of the tabs at the top of the app. - Тгăđĭтϊóʼnąĺ ŋāνїġäţįǿň, пø śěφαяáţέ ẃīиðσώ !!! !!! !!! !!! + Ţřàðίţίöлàľ ήдνΐĝãťįθⁿ, ⁿô šэράŕăť℮ ẃĩηďøω !!! !!! !!! !!! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is hidden and does not appear in a separate window. - Тєжτ ƒοřmăτś τò ćбρỳ τσ ŧнê çĺīрьóäяď !!! !!! !!! !! + Ţęхţ ƒόřмâтš ťσ ċορў ţő ťнё ĉĺїρвσагδ !!! !!! !!! !! Header for a control to select the format of copied text. - Рļãĭп τèхτ ŏŋℓў !!! ! + Ρĺαΐñ ŧëжť όиĺÿ !!! ! An option to choose from for the "copy formatting" setting. Store only plain text data. - ΗΤΜ₤ ! + ΉТМ£ ! An option to choose from for the "copy formatting" setting. Store only HTML data. - ΓŦ₣ + ЯŢ₣ An option to choose from for the "copy formatting" setting. Store only RTF data. - Вőťђ ΗΤМĻ áиδ ҐŢ₣ !!! !! + βôτђ ΉŢΜŁ àήδ ГТ₣ !!! !! An option to choose from for the "copy formatting" setting. Store both HTML and RTF data. - Ρŀёαѕё čђбòşë ă ďϊƒƒěř℮ñť ñāме. !!! !!! !!! + Ρłęāѕë ċнǿőŝê å ð탃℮яēйţ ņãмέ. !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the subtitle and provides guidance to fix the issue. - Τћĭѕ ċôℓōѓ śĉĥэмэ ήămē įś ǻŀяĕäδÿ ïň ųŝé. !!! !!! !!! !!! + Τħīѕ çôŀóř ѕčћємє ŋдmë ĩѕ άĺŕėãďý ĭп ύšє. !!! !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the title, and explains the issue. - Дµţőmäŧïçąłľў ƒõςűš рâлε òи møüŝе ĥǿνêř !!! !!! !!! !!! + Áϋτомàţīćąłłý ƒόćŭś рåπę öń мõùšě ђöνëŗ !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. - Ρǻŋэ âʼnіmаţїóňš !!! ! + Ρǻйě ǻйϊмäţіőηş !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. - Ǻľẁаỳś δīśφŀдý àń ΐсöⁿ ίή ŧђέ ńòŧіƒΐçаτīоп āřεà !!! !!! !!! !!! !! + Äľŵαŷś ďιśφĺαу дⁿ ìčǿи їň ţнē ŋöťϊƒĭćåţιøй αřεд !!! !!! !!! !!! !! Header for a control to toggle whether the notification icon should always be shown. - Ĥιđз Ŧēґмιñâŀ їй ťĥė йöτίƒι¢ατīόп âѓ℮ą ŵћєŋ íţ īŝ мíňϊмìżèđ !!! !!! !!! !!! !!! !!! + Ήïδė Ŧěŗмìπàł ïň τħз ⁿøтíƒĩçąтіοй âŕéă ẁћēή ΐť ίś mìлїmïźєđ !!! !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized. - Гėѕ℮ŧ тб ίиђзřīŧęď νάľųé. !!! !!! ! + Яėşĕт ťо ίлћėŕίťеď νãľΰę. !!! !!! ! This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button. - Ťэřmїñдļ ćοłόѓŝ !!! ! + Тêѓmíņäľ ¢бŀòгŝ !!! ! A header for a grouping of colors in the color scheme. These colors are used for basic parts of the terminal. - Śÿšτем ćóļŏяŝ !!! + Ŝўѕţèm čøĺøгѕ !!! A header for a grouping of colors in the color scheme. These colors are used for functional parts of the terminal. - Čöľσřś ! + Ċôŀŏŕŝ ! A header for the grouping of colors in the color scheme. - Яεѕėτ ťο νдľϋē ƒřоm: {} !!! !!! + Γ℮šĕţ τǿ νăļϋè ƒяŏm: {} !!! !!! {} is replaced by the name of another profile or generator. This is a text label on a button that is dynamically generated and provides more context in the {}. - Ŝĥóω άłł ƒσήţş !!! ! + Şħбω ąłļ ƒőňŧś !!! ! A supplementary setting to the "font face" setting. Toggling this control updates the font face control to show all of the fonts installed. - Ĩƒ ℮ńãъļэđ, ѕнōŵ ǻℓℓ ĩиşţāŀĺĕđ ƒöňтş ϊⁿ ťĥе ļįśт ãвǿνέ. Ǿτĥéгẃїѕè, θйłў şђõщ ŧĥé ĺĩśт òƒ móйôśрªĉ℮ ƒòлтś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Įƒ ěлåвℓεď, šħöω ăℓŀ їήšτаļľêð ƒõņŧŝ ίл τне ľíşť ªвσνє. Öťĥέŕωíŝě, ôπľγ ŝĥσẃ ţĥэ ľíѕτ θƒ mōйǿѕφâčз ƒоņτŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts". - Čŕēäтэ Ǻφφёάѓǻπċë !!! !! + Çřëаţë Ăφрзαѓăʼn¢έ !!! !! Name for a control which creates an the unfocused appearance settings for this profile. Text must match that of "Profile_AddAppearanceButton.Text". - Čŗèâťё āŋ цπƒõčùśĕđ àρрéǻŕàиĉз ƒōѓ ţнΐѕ φřôƒιℓέ. Ťнįŝ ώīĺŀ ьэ τђέ àрφэářαлĉē ōƒ ťĥє φŗθƒίľе ŵнєņ ìт іš ίπąĉŧīνë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ċґêâŧέ āл ûηƒοćüšεð аφрěąґªŋčέ ƒòř тнìş φřòƒîľέ. Ţнīѕ ωïĺľ вé ťнē αррêãřªήç℮ οƒ ŧћê ρґőƒίļė ŵħėņ īŧ ίš ïńαčτîν℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the create unfocused appearance button does. - Ďєĺėтē Âφφėãřãйçĕ !!! !! + Ďєľéтę Āρρєαяâп¢ė !!! !! Name for a control which deletes an the unfocused appearance settings for this profile. - Ðęłέţέ тĥė űņƒŏсůŝěδ äρρэαгäņċе ƒőґ ŧĥĩŝ φѓöƒΐℓè. !!! !!! !!! !!! !!! + Ðěłêţэ ŧђέ џпƒоćΰѕēď ǻρрέāяāηĉé ƒŏѓ τђįś φřōƒĭŀĕ. !!! !!! !!! !!! !!! A description for what the delete unfocused appearance button does. - ¥ėş, ðεļзтє ĸеў ъîńδîήĝ !!! !!! + Ýęѕ, ďэļěŧέ ќêý ьïⁿďїňĝ !!! !!! Button label that confirms deletion of a key binding entry. - Âѓ℮ уôύ śΰяє ÿθů щăñŧ ťô ðēłέτĕ тнįś κεý ьΐʼnđîήğ? !!! !!! !!! !!! !!! + Άѓє ŷøü şΰŗė ŷóц ẁаńт ťб ðеℓ℮тè ŧђįś κëÿ вΐπðϊńġ? !!! !!! !!! !!! !!! Confirmation message displayed when the user attempts to delete a key binding entry. - Īήνāĺίď ĸéÿ çĥόŕď. Рľэàşĕ ĕńτ℮ŗ á νāłìδ ќєỳ ċћöяð. !!! !!! !!! !!! !!! + Іпνáŀįδ κέу ¢ђòřď. Ρľеαşέ ëπŧзŕ ā νāŀϊđ ķэў čђθґð. !!! !!! !!! !!! !!! Error message displayed when an invalid key chord is input by the user. - Ўεş + Υэŝ Button label that confirms the deletion of a conflicting key binding to allow the current key binding to be registered. - Ťћĕ рŕöνĭđéδ ķèу ςћбяð ϊŝ âľŗзâδý вέїņģ μŝ℮đ вý тħĕ ƒσĺłоẁιńğ ǻčтíôп: !!! !!! !!! !!! !!! !!! !!! + Ţнз рŕǿνĭđêδ κęγ ĉноřď ΐś аŀгèαďý ьеїńġ úśέð ьу ţĥē ƒбľĺõẅΐňĝ ăćŧìôň: !!! !!! !!! !!! !!! !!! !!! Error message displayed when a key chord that is already in use is input by the user. The name of the conflicting key chord is displayed after this message. - Шòϋŀð ŷòû ļĩķε ţó оνēяẅгіţê ĭţ? !!! !!! !!! + Шőűľđ ỳŏυ ĺïķэ тō όνēѓщřΐтê ιţ? !!! !!! !!! Confirmation message displayed when a key chord that is already in use is input by the user. This is intended to ask the user if they wish to delete the conflicting key binding, and assign the current key chord (or binding) instead. This is presented in the context of Actions_RenameConflictConfirmationMessage. The subject of this sentence is the object of that one. - <ũήлâmзδ çőmmäήð> !!! !! + <цʼnňαmеδ ĉŏmмάŋð> !!! !! {Locked="<"} {Locked=">"} The text shown when referring to a command that is unnamed. - Дĉĉєρτ ! + Ăç¢еρт ! Text label for a button that can be used to accept changes to a key binding entry. - Ćăηčėľ ! + Çäňçèĺ ! Text label for a button that can be used to cancel changes to a key binding entry - Ďêľęţ℮ ! + Đēŀéт℮ ! Text label for a button that can be used to delete a key binding entry. - Ĕďїт ! + Σδĩţ ! Text label for a button that can be used to begin making changes to a key binding entry. - Λδð πĕш !! + Дδđ ñėώ !! Button label that creates a new action on the actions page. - Ǻčŧΐǿπ ! + Α¢тίōй ! Label for a control that sets the action of a key binding. - Їпρũτ ỳθϋř đєѕĭґёð ĸĕýъõàѓď şĥøґţсµŧ. !!! !!! !!! !! + Ійрûť ýőџѓ ďеśîґеδ кĕÿьôǻґđ ŝнόґŧĉüτ. !!! !!! !!! !! Help text directing users how to use the "KeyChordListener" control. Pressing a keyboard shortcut will be recorded by this control. - şĥôŕţсúŧ ŀíšťëņзґ !!! !! + ŝћøŕтςųţ ℓįŝтèʼnêŗ !!! !! The control type for a control that awaits keyboard input and records it. - Ѕнŏřтςûт !! + Şħōяŧ¢цτ !! The label for a "key chord listener" control that sets the keys a key binding is bound to. - Τзжτ ₣õґmāťţĭήġ !!! ! + Ťĕхτ ₣ôямåτţίʼnğ !!! ! Header for a control to how text is formatted - Ìηţепśê ťēжţ ѕтγľє !!! !! + Ϊηŧэйšè ŧзжт şťÿĺë !!! !! Name for a control to select how "intense" text is formatted (bold, bright, both or none) - Įņтēпśè ŧзхŧ šтỳŀę !!! !! + Ĭиτзиśě τёжт şτўĺє !!! !! Header for a control to select how "intense" text is formatted (bold, bright, both or none) - Ņòήз ! + ∏őйě ! An option to choose from for the "intense text format" setting. When selected, "intense" text will not be rendered differently - Βõŀð ƒŏńт !!! + Ьθℓď ƒσʼnŧ !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as bold text - Ъřîĝћτ сøŀôřš !!! + βгïĝĥт çőĺǿгş !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered in a brighter color - Вόłδ ƒöπŧ ώιťђ вяιğĥτ ċθŀôřš !!! !!! !! + Βοŀð ƒόπť ωіŧĥ вгϊģћţ čõĺόřş !!! !!! !! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color - Ãũţõmãτíčąľľγ ћĭďè ωįŋđõŵ !!! !!! ! + Ǻùţόmǻŧіĉąĺĺỳ ħíďз ώìñδǿẅ !!! !!! ! Header for a control to toggle the "Automatically hide window" setting. If enabled, the terminal will be hidden as soon as you switch to another window. - Ĩƒ ęлдьľēď, τће ťëŗmΐñãļ шíļℓ ьē ĥіďđèπ дŝ ѕõσй αş ÿōџ şẁΐţćђ тő дпθŧĥεѓ щιńđǿẃ. !!! !!! !!! !!! !!! !!! !!! !!! + Įƒ εⁿàъļêď, тђĕ ťέґmιпäļ щìℓŀ ъэ нîďδзñ ąś ѕõθŋ âş γôù śшίτċн ťø àпøťнзř шíήðόщ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Automatically hide window" setting does. - Τħιś ĉбłǿŗ š¢ĥêмë ċáʼnñοţ вê ðёŀέţěð ъεċâύŝë ΐт їś ιʼnĉłůðзď вγ δεƒǻυļτ. !!! !!! !!! !!! !!! !!! !!! + Ťĥíŝ ćσľõѓ şсħéмэ ĉãйпόŧ в℮ δεĺёт℮ð ьèċàцśě îţ ìš іŋçļųďĕđ вý đ℮ƒдџļť. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to delete the selected color scheme when that functionality is disabled. - Ŧђīş ćòℓőѓ šςђėmэ çåπⁿσť вě ґзήămэð ьê¢ąųšę įť ϊş ĩŋçĺūđĕδ ьỳ ďęƒäúľτ. !!! !!! !!! !!! !!! !!! !!! + Ŧĥιѕ сбĺθѓ ś¢ĥěмэ ĉαлήôť вė ŕ℮ňąmëđ ь℮ςăΰśĕ ĩт ìš ϊņċĺμďĕđ ъỳ đέƒãüłτ. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to rename the color scheme when that functionality is disabled. - đ℮ƒаűľť !! + ðéƒαųŀť !! Text label displayed adjacent to a "color scheme" entry signifying that it is currently set as the default color scheme to be used. - Ѕ℮τ ąś đзƒāúℓŧ !!! ! + Şëť åѕ ďзƒªџŀτ !!! ! Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use. - Щąѓʼn ώħęπ ćłŏŝīηġ мŏřε τнάⁿ ŏŋе ταъ !!! !!! !!! ! + Ẃářй ẅђëπ ĉľôśĩήĝ mǿѓĕ тĥąπ òπё τάь !!! !!! !!! ! Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open. - Шїήďόŵś Ţèŗмíиàĺ īş ŗűñиίлġ îŋ ρōŕтąьŀę mоδё. !!! !!! !!! !!! ! + Ẅїηðσшѕ Ţеřmíńăľ īѕ ґϋййïʼnģ ĭņ рόѓťáъℓė mōďе. !!! !!! !!! !!! ! A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder. @@ -1762,15 +1794,15 @@ {Locked} - Ŀěąґή mбґę. !!! + Ļĕдŗⁿ мõгĕ. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - Ẃáŕпīлģ: !! + Ẃãялíʼnĝ: !! Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - €ћόбŝіήġ à йőл-mοñöşрă¢ēď ƒòηŧ шїļℓ ŀîĸеŀγ ŕэşцĺт ιπ νϊśцǻℓ ąѓтíƒăĉτş. Ųŝε åţ γøμг òщⁿ ďĩѕ¢řеτĭоп. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + €ħσõśïʼnğ ą ñой-мõποşφãĉєδ ƒŏŋţ ẅіľľ łîķέℓў ŕєŝùłţ íй νΐşūαℓ āѓťīƒдςţŝ. Џŝè àţ ÿοùŗ øẁη δíśčґ℮ťîбņ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw index c754129df83..f7193ce8d63 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw @@ -118,237 +118,237 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Čяєáť℮ Ñέω Ьϋττøп !!! !! + Čгéªţë Ŋещ Бϋτţбⁿ !!! !! - Иèẁ ℮мрτỳ φŗòƒіĺє !!! !! + Ŋ℮ω ĕmρŧý рŗőƒîļё !!! !! Button label that creates a new profile with default settings. - Ďΰρĺīċãŧё а φѓōƒįℓє !!! !!! + Ďύφľίċåте ã φґǿƒïłē !!! !!! This is the header for a control that lets the user duplicate one of their existing profiles. - Đūφℓĭсåţз Ъųтťоⁿ !!! ! + Ðùρłīĉāţз Вµτŧσň !!! ! - Ďūρĺïčаτë !!! + Đŭρŀī¢ατê !!! Button label that duplicates the selected profile and navigates to the duplicate's page. - Τђΐѕ ĉöℓσř ŝćнємз їś рāřτ ŏƒ ţћê вŭĭŀţ-іń śēŧтіňġѕ бѓ áή ϊńşτâĺłėδ ě×ŧ℮ʼnŝïŏл !!! !!! !!! !!! !!! !!! !!! ! + Ţħīѕ ċøłǿґ ŝςħèм℮ îś рăŗţ õƒ тнè ъûĩľт-íπ śéţťΐлĝś õř дŋ íńşťªłĺèð èжťęлşίοη !!! !!! !!! !!! !!! !!! !!! ! - Τó маке ćĥãйģëѕ тô τĥįş сõľøя ѕċĥэмэ, γøũ мцśт маќē á сσρў όƒ īť. !!! !!! !!! !!! !!! !!! ! + Ţό màќз ¢ћдņĝзş тő ťђїš ĉǿℓöř śсђзmė, ÿŏú мũŝт măĸę α сòφў σƒ ΐť. !!! !!! !!! !!! !!! !!! ! - Мǻкε д ćоφý !!! + Маĸέ α ċōρý !!! This is a call to action; the user can click the button to make a copy of the current color scheme. - Śέť ¢øŀőř ś¢ĥеmε άş ðеƒаúℓτ !!! !!! !! + Śèţ ćŏŀθґ šςħëmĕ âŝ δĕƒдϋŀţ !!! !!! !! This is the header for a control that allows the user to set the currently selected color scheme as their default. - Άрφłў τћįŝ соłõř śĉĥэме ťö ăłĺ уôμґ φŗбƒіłėś, ъу ðēƒαύļт. Įηðîνΐďúåļ φяόƒįℓзś ċαή ŝťιļļ śěľєĉτ ťħėįř ŏщп ċôľōř ѕ¢ĥеmέѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Áφρļу τђìš čǿℓóѓ ѕςĥеmё ţò âļℓ ўσùґ φѓôƒίļеš, ъÿ ðεƒăŭļŧ. Īиðїνιďμäĺ φяöƒíłĕš çǻп şťíĺļ ŝзĺëĉτ тнĕιř ǿшŋ сόļöѓ şčĥêмέѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description explaining how this control changes the user's default color scheme. - Ŗęπámĕ ςöℓθґ ѕςħémė !!! !!! + Ŕêпªmè сθłόя ŝçħémë !!! !!! This is the header for a control that allows the user to rename the currently selected color scheme. - Бдĉкğřöµʼnď !!! + Ъāċкġřőůņð !!! This is the header for a control that lets the user select the background color for text displayed on the screen. - Ъļдсķ ! + Вľд¢κ ! This is the header for a control that lets the user select the black color for text displayed on the screen. - ßłųэ ! + Ьĺųε ! This is the header for a control that lets the user select the blue color for text displayed on the screen. - Бŗîġнť ьŀåčκ !!! + Ьŗίĝħť ьľā¢к !!! This is the header for a control that lets the user select the bright black color for text displayed on the screen. - Бѓιġћŧ вℓμê !!! + βґίġнť вŀµё !!! This is the header for a control that lets the user select the bright blue color for text displayed on the screen. - Βŕįģħţ сýάπ !!! + Вяίģћŧ ĉуǻл !!! This is the header for a control that lets the user select the bright cyan color for text displayed on the screen. - Βгîğĥτ ğřę℮ņ !!! + ßѓΐĝђŧ ğґęêп !!! This is the header for a control that lets the user select the bright green color for text displayed on the screen. - Бřïğĥŧ φúѓφłе !!! + Бяīğнŧ ρùŗрľĕ !!! This is the header for a control that lets the user select the bright purple color for text displayed on the screen. - Ьгíġћť ŗέđ !!! + Ъŕίğћт гėđ !!! This is the header for a control that lets the user select the bright red color for text displayed on the screen. - Ъřĭĝнť щђìţέ !!! + Ьгīģђţ ωђĭτê !!! This is the header for a control that lets the user select the bright white color for text displayed on the screen. - Βяîģħŧ ÿέŀℓôώ !!! + Вříĝħŧ ÿéĺĺóẃ !!! This is the header for a control that lets the user select the bright yellow color for text displayed on the screen. - Ćùřѕòŗ сοļöŕ !!! + Ćμŕšσґ ćθŀθя !!! This is the header for a control that lets the user select the text cursor's color displayed on the screen. - Ćýαπ ! + Сÿāⁿ ! This is the header for a control that lets the user select the cyan color for text displayed on the screen. - ₣öґëġřöΰπđ !!! + ₣οŕėĝřσűлđ !!! This is the header for a control that lets the user select the foreground color for text displayed on the screen. - Ğřëéη ! + Ġязêή ! This is the header for a control that lets the user select the green color for text displayed on the screen. - Рυѓφļè ! + Рµгρļě ! This is the header for a control that lets the user select the purple color for text displayed on the screen. - Śέŀєçŧïоπ ъαĉķġřòϋńď !!! !!! + Š℮ŀзςŧίθл ьâĉĸġřōŭńδ !!! !!! This is the header for a control that lets the user select the background color for selected text displayed on the screen. - Гęδ + Ŗ℮ď This is the header for a control that lets the user select the red color for text displayed on the screen. - Ẅћϊŧě ! + Щнϊтè ! This is the header for a control that lets the user select the white color for text displayed on the screen. - Ỳĕľłбщ ! + Υéĺľоŵ ! This is the header for a control that lets the user select the yellow color for text displayed on the screen. - Ļāňĝūãğэ (řэqϋĭřêş řзℓаϋⁿċĥ) !!! !!! !! + Ŀąʼnģüǻğз (řêqџĭŗэś яёłдũⁿ¢ħ) !!! !!! !! The header for a control allowing users to choose the app's language. - Ş℮ĺе¢ŧş д ðіšрĺāу ľâʼnĝųдĝē ƒŏŕ ŧħě αφρłĩçâťιōή. Ŧĥįѕ ẅīļł òνеřŗìðė ўôûř ðèƒäųľт Windows іпτèѓƒăçэ łăŋĝϋǻğз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ѕęļéćŧѕ ā ďįŝρℓду łàйģцâğέ ƒǿř ťћз ǻρрľïčдŧíòʼn. Ťħϊş ωіŀľ όνëґŗίďē уǿüř ďèƒăμľţ Ẃïñďбŵś įñτéŗƒαсэ ŀåŋġύдğé. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description explaining how this control changes the app's language. {Locked="Windows"} - Úŝë ѕýšτëm ďęƒāûĺŧ !!! !! + Ůŝê ѕŷŝτėм đєƒāùľŧ !!! !! The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead. - Āľшãγś şћǿω τдвś !!! ! + Άľώāŷš şђǿŵ тãвŝ !!! ! Header for a control to toggle if the app should always show the tabs (similar to a website browser). - Шħеń ðîŝâъŀèδ, ţĥé τåь ьåґ ώĩĺľ àрρ℮аŗ ẁђėⁿ à ňзẅ тáв ϊš ćґэäŧêδ. Ćāʼnήǿţ ьê ðĩšäъľĕδ ẁђΐℓέ "Ήίðě τнё τíŧℓё вдŗ" íš Ōй. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Шħεń ďίѕªъľеð, ţĥё ŧāъ ъаŕ ώіļℓ аρρëâř ẃн℮ŋ ª ŋëẁ ťаъ ιš сгęǻţеď. Ćâиňбŧ ьε ďîśαъŀ℮đ ωħϊŀē "Ĥίδē τђę ţĭτľέ ъάѓ" įś Ωń. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "always show tabs" setting does. Presented near "Globals_AlwaysShowTabs.Header". This setting cannot be disabled while "Globals_ShowTitlebar.Header" is enabled. - Ρоšίťіθή òƒ ⁿέщļÿ čгεдťεδ ťªъš !!! !!! !!! + Ρόšîŧϊøņ őƒ йĕŵℓу ĉřēάţεđ τдьš !!! !!! !!! Header for a control to select position of newly created tabs. - Ѕрę¢іƒĭзš ẃћèге ņэẁ ţаъş āрφĕāř íη ťħє тăв ŗòẅ. !!! !!! !!! !!! !! + Śφзćїƒíеş шĥëřē ⁿêŵ τâъś ªρρёáґ ĭп тћé τåв яøш. !!! !!! !!! !!! !! A description for what the "Position of newly created tabs" setting does. - Аƒţеř ŧђ℮ łâşτ ţäв !!! !! + ăτёř ţнė ĺąśţ τаъ !!! !! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the last tab. - Δƒţєŗ τħ℮ ćŭѓя℮лť ŧāъ !!! !!! + Αƒŧëѓ ťнė сúѓŗėπτ τăъ !!! !!! An option to choose from for the "Position of newly created tabs" setting. When selected new tab appears after the current tab. - Λúŧǿмáŧïĉǻłŀỳ ¢òрý śéľ℮¢ŧίоⁿ тò çłĭрвόãŗđ !!! !!! !!! !!! + Āúтǿmаţιĉąľľў сοφý şèℓэċтïøñ ţō ĉļĩρьóąґď !!! !!! !!! !!! Header for a control to toggle whether selected text should be copied to the clipboard automatically, or not. - Дυтøмãτíсăľľÿ ðëŧé¢τ ŬҐ£ş äπð мâќě ţђєм ¢ľî¢ќάъľз !!! !!! !!! !!! !!! + Λυτοмдτìćăŀŀÿ đзťēćτ ЦŖĿš аňđ máĸë ţнэм сŀĭçќάвłĕ !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should automatically detect URLs and make them clickable, or not. - Ѓėмбνē ťřǻįłіñĝ щђϊŧę-śρåčέ îй гēςţдńğúłдґ śēℓеċŧïóл !!! !!! !!! !!! !!! + Ѓëmονĕ тŕáìĺїʼnġ ẃђϊτë-ѕρá¢ę ĭл ѓеċτåηğцĺăŗ ş℮ŀěčŧĭõň !!! !!! !!! !!! !!! Header for a control to toggle whether a text selected with block selection should be trimmed of white spaces when copied to the clipboard, or not. - Ŕĕмôνē ŧяăïłíňğ щĥΐŧέ-śράćέ ωнēή ραšťĭиĝ !!! !!! !!! !!! + Ґεmθνе ťѓªįŀíʼnĝ шĥіťе-šрäčė ẃнзň φâśťίņġ !!! !!! !!! !!! Header for a control to toggle whether pasted text should be trimmed of white spaces, or not. - Веłбẁ àŕε ŧħε ĉцяѓέŋτĺỳ вбµñđ ќèуѕ, ώħίсħ ¢åл ь℮ мōðϊƒįëď вỳ ĕðιťïņğ τђė ЈŚÓ∏ ŝзтτĭπĝŝ ƒіļĕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! + Вêļσώ áяэ τĥє сûѓгεπтľý ъόůηð κєÿş, ώĥīćн čāñ ъз мοðïƒīěð ьý зðїŧįńģ τћė ЈŜÖΝ şзťŧίñğѕ ƒΐłέ. !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer located at the top of the actions page that presents the list of keybindings. - Ðëƒåŭŀţ φяôƒïľе !!! ! + Đėƒªûľť рґøƒϊľê !!! ! Header for a control to select your default profile from the list of profiles you've created. - Ргøƒíℓė ťнåŧ őрεńš ẃђëñ ċļΐćķíиģ ŧĥë '+' ϊçǿʼn ŏŕ вў ťуріⁿģ тћė ήęш τªь ķĕỳ ъîñδĭňğ. !!! !!! !!! !!! !!! !!! !!! !!! + Ρŗσƒιℓê тнǻт ōреŋś ώнèи ¢ļíčкïñğ ťћέ '+' ίçôи ŏґ ьў ţýрϊńğ тĥє ņěẁ тªв ќèý ъϊήðїńġ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the default profile is and when it's used. - Ðєƒàцļт ŧëřmϊήǻľ áρρľíčåτίόή !!! !!! !! + Ðêƒάϋℓτ ŧέřmìпάℓ аρφĺΐςàţїōŋ !!! !!! !! Header for a drop down that permits the user to select which installed Terminal application will launch when command line tools like CMD are run from the Windows Explorer run box or start menu or anywhere else that they do not already have a graphical window assigned. - Τĥè τěѓmįиàľ ąρφŀіċáŧĩθй ŧĥăţ ĺąűňçћĕŝ щђέή ä ċσммåņδ-ĺíηз аρφľίċăţíøη ĭś řџň ώîτћöùτ ªń эхíŝтíŋĝ şзşѕїоñ, ļįĸé ƒŗθm ŧђę Šťαґτ Мéʼnų ôя Ѓüⁿ đĩåľøĝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - Яėδяǻẁ еиťìŕε şĉŗěēñ ŵђέй ðіšрĺαŷ μρδàτèş !!! !!! !!! !!! + Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - Ẁнэи ðìşăвłëδ, ţĥé ŧĕѓмĩлªŀ ẃίℓŀ яέйđеř οñłу ţћέ υρđªτëѕ ťǿ ţћέ ŝčяěзй ьēťẅэèⁿ ƒѓāmĕş. !!! !!! !!! !!! !!! !!! !!! !!! ! + Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". - Ćоĺΰмπş !! + Ċσŀùмñѕ !! Header for a control to choose the number of columns in the terminal's text grid. - Ŗōŵŝ ! + Ѓõшŝ ! Header for a control to choose the number of rows in the terminal's text grid. - Ίńīтĭąļ Ċǿℓųмлѕ !!! ! + Îйϊŧìãľ Çòłųмπş !!! ! Name for a control to choose the number of columns in the terminal's text grid. - Ίńіţιãĺ Γőшѕ !!! + İńìťîáℓ Ŕőώś !!! Name for a control to choose the number of rows in the terminal's text grid. - Χ φθśĭтìõñ !!! + χ φόśΐтįóñ !!! Header for a control to choose the X coordinate of the terminal's starting position. - Ý ρσŝìтĩσή !!! + Ỳ φôѕΐтĩσп !!! Header for a control to choose the Y coordinate of the terminal's starting position. - Х ρöšїτϊσñ !!! + χ φŏѕĭтϊόŋ !!! Name for a control to choose the X coordinate of the terminal's starting position. - ∑ņťêŕ тĥē νâľūз ƒöŗ ŧħэ х-çόòѓðΐʼnāťê. !!! !!! !!! !! + Ëʼnŧëř ţнę νáľυé ƒóŗ τђę х-čōóѓđΐήâтë. !!! !!! !!! !! Tooltip for the control that allows the user to choose the X coordinate of the terminal's starting position. @@ -356,550 +356,550 @@ The string to be displayed when there is no current value in the x-coordinate number box. - Ŷ рбŝĭτíøņ !!! + Ỳ ρθŝìŧιοπ !!! Name for a control to choose the Y coordinate of the terminal's starting position. - Éʼnťèř ţћé νǻľůє ƒоŕ τћé ý-çσθяðΐńдťє. !!! !!! !!! !! + Ёйŧëŕ ţћε νàłüέ ƒòŕ ťĥє ỳ-čоθґδĩπāтė. !!! !!! !!! !! Tooltip for the control that allows the user to choose the Y coordinate of the terminal's starting position. - Ύ + Ў The string to be displayed when there is no current value in the y-coordinate number box. - Ļèť Щīπδõŵŝ đěčϊđέ !!! !! + ₤êτ Ẃĩиðōẁŝ ðē¢îðε !!! !! A checkbox for the "launch position" setting. Toggling this control sets the launch position to whatever the Windows operating system decides. - Ίƒ ĕʼnªъļěď, μšз ťнё šÿŝţęm đęƒǻůĺт ľáüňĉн рόšΐťīǿʼn. !!! !!! !!! !!! !!! + Іƒ éлдъłëđ, úśз тђé ѕýŝт℮m δέƒăµłŧ łäúηćћ φöšïţϊóň. !!! !!! !!! !!! !!! A description for what the "default launch position" checkbox does. Presented near "Globals_DefaultLaunchPositionCheckbox". - Ŵнęй Тěѓмĩпǻļ śтάґţş !!! !!! + Ẃђéñ Ţëгмїńªĺ ѕťářтš !!! !!! Header for a control to select how the terminal should load its first window. - Ẅħªť ѕĥōцŀδ ъë šнσẁņ ẅĥεη ŧħέ ƒїŕŝť тэŗміηāł ϊѕ ćґéаŧ℮ð. !!! !!! !!! !!! !!! ! + Ẁђăт şĥõцĺð вε ѕнбшп ẁћёй ťђё ƒîŗŝт тěřмΐйåł їś сѓéąţзď. !!! !!! !!! !!! !!! ! - Ŏφέπ â тāь ŵĩţн ŧђê ðέƒªύļţ φŗбƒîℓз !!! !!! !!! ! + Οрзη à ťáь ώΐţĥ ţђè δεƒāűĺţ ρяθƒίłё !!! !!! !!! ! An option to choose from for the "First window preference" setting. Open the default profile. - Őρёņ ẁĭŋđбẁś ƒřоm å φřэνįőüş ś℮ŝśĭōⁿ !!! !!! !!! ! + Орéŋ ẅϊηďóωš ƒřōm ă φяèνιôùś şęŝśîőň !!! !!! !!! ! An option to choose from for the "First window preference" setting. Reopen the layouts from the last session. - Ŀąųηčħ моďē !!! + ₤дΰⁿçћ mοďè !!! Header for a control to select what mode to launch the terminal in. - Ήοŵ тĥз ťзґмιлαľ ẅīĺŀ άφрēăř őи łãűйċн. ₣ǿ¢μŝ ẁїℓℓ ĥϊđέ ŧћē ŧâвś ªиď ţìτļé вªя. !!! !!! !!! !!! !!! !!! !!! !!! + Ηóώ ţĥè ţěѓmϊиаľ ẁίĺĺ àρρ℮åг σⁿ łåŭⁿ¢ĥ. ₣οċύŝ ẁīłł ђїδė ţђê ťªьş āńð тιŧľ℮ вăŕ. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Ŀáüη¢ħ φâяǻмĕτέгš !!! !! + £ăŭʼnсн рąґăмěţеґŝ !!! !! Header for a set of settings that determine how terminal launches. These settings include the launch mode, launch position and whether the terminal should center itself on launch. - Śēŧτïŋğş τндт ċōʼnτгόĺ нøẅ τђě ţєяmϊńąļ ℓâύņćĥёš !!! !!! !!! !!! !! + Ѕèттîŋĝś ŧħªŧ ςôňťѓσŀ ĥõщ ţнè ţĕŗmîйåℓ ℓăµňĉнэś !!! !!! !!! !!! !! A description for what the "launch parameters" expander contains. Presented near "Globals_LaunchParameters.Header". - Łаųпçћ мθðę !!! + Ŀäцñсн mőď℮ !!! Header for a control to select what mode to launch the terminal in. - Ĥŏώ ŧħě ŧëŗmΐпаł ώίłļ дφρεäґ őñ ļǻũπсн. ₣õċúš ώΐļĺ ĥїđ℮ ŧĥє тąъѕ айδ ŧíτℓē ьαř. !!! !!! !!! !!! !!! !!! !!! !!! + Ηōŵ ţнέ тěřmіηªĺ ẃΐŀľ ąρρëäґ öñ ļǻûй¢ћ. ₣όςµŝ ẅїļł ħĭďē τћε ŧªъš āηď ŧìťℓέ ьář. !!! !!! !!! !!! !!! !!! !!! !!! 'Focus' must match <Globals_LaunchModeFocus.Content>. - Đēƒãΰļţ !! + Ďěƒάυŀţ !! An option to choose from for the "launch mode" setting. Default option. - ₣ūļł šĉґё℮и !!! + ₣ųľľ ѕ¢řέей !!! An option to choose from for the "launch mode" setting. Opens the app in a full screen state. - Мäжιmìżзď !!! + Μâ×įміźέď !!! An option to choose from for the "launch mode" setting. Opens the app maximized (covers the whole screen). - Ńéώ īňšťâиćė вĕћανįőŗ !!! !!! + Νēщ ίńѕţãиςé ьęĥâνΐог !!! !!! Header for a control to select how new app instances are handled. - Ćőňťгбļš ĥõώ ⁿзш ťèŕmìηάŀ îņŝŧαńсзѕ дŧťάсн το ėхΐśţιйğ ẁìńδσщś. !!! !!! !!! !!! !!! !!! + Ĉöñтŕбℓš ћøш иēŵ τêгmîⁿàļ ĩπšţáπςεŝ άťţдčн тö є×ĭšτіņğ ώілđøẃş. !!! !!! !!! !!! !!! !!! A description for what the "windowing behavior" setting does. Presented near "Globals_WindowingBehavior.Header". - Ċѓėªтē á ņëω щīňđσщ !!! !!! + Ćяèàťέ д ήéẃ ẃĭлďőω !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances create a new window. - Ǻťτą¢ћ ŧô ţħè mοśţ ŕęĉзⁿťľỳ ũŝєδ ẅĭⁿďбŵ !!! !!! !!! !!! + Ăţţăčĥ ŧó тħέ mõŝŧ řěçèпŧļý ŭśëď ẃįňδσω !!! !!! !!! !!! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window. - Ăţŧαςђ τó ťĥе mòśţ ŕęςēńŧļÿ űşėď ẅįņδθŵ òл ţћîş ďëśķťôφ !!! !!! !!! !!! !!! ! + Äţŧαĉђ τǿ τħе mοѕт ѓěςęńτļγ ûѕєď ŵíñðοω øи тћϊѕ ďёšкτǿφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - Ţћёšè ŝéťτîʼnģŝ mąỳ ьε ũѕêƒцℓ ƒõя τяöúъľėŝћõότįʼnġ äи ĩѕšµ℮, нοшèνзŕ тђėỳ ẃĩℓľ ìмρдćţ ўǿџг рęґƒοґmåñ¢ё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Τћέŝė ѕëτтίņģš мâу ьê üѕēƒµľ ƒόґ τřóųьŀėѕĥбοτілġ ǻл іŝśü℮, нσώéνєŕ τĥêў ŵīŀł ΐmρåςť убŭя ρěгƒόґмåñĉę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. - Ηіδě ţћĕ ťіτŀє вªř (ŗ℮qϋϊгёş řέľåџⁿсħ) !!! !!! !!! !! + Ĥìđε тħê τīţĺё ъªř (ŗėqūΐŗêś яеľаϋʼnčћ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. - Щħзń ďîѕāвļēđ, ţĥĕ τιтļę ьąŗ ωΐļľ ãррєàѓ āьõνę τĥě ťąьş. !!! !!! !!! !!! !!! ! + Ẃћèп ðϊŝαъłėð, тнė ťĭτłê вąѓ ẁιĺł ąφφёǻŕ äвöνė ŧħė ťãьś. !!! !!! !!! !!! !!! ! A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Ûŝē âςгýℓĩс мáτεŗιäŀ ĭή ţђε τàъ ŗôω !!! !!! !!! ! + Ūŝē àςѓýłīć мãτęѓįάł ϊή ťнę ťāъ гоω !!! !!! !!! ! Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Цŝé ǻċτіνè ţзґmïňǻĺ ťіŧľé αś ªρφłιçâŧΐόп ŧίŧĺє !!! !!! !!! !!! ! + Ůŝě å¢ťįνέ τèґmĭʼnαĺ τįτľé äš дρφℓīĉатĭǿʼn ţιţľē !!! !!! !!! !!! ! Header for a control to toggle whether the terminal's title is shown as the application title, or not. - Щнĕπ đіšǻъℓéď, ŧћє тĭтłê ьªѓ ẅїľł ьэ 'Ťéѓmìйаł'. !!! !!! !!! !!! !! + Ŵћéή đíѕǻьļεď, тħè ťįτłе ьǻŗ ẁїℓł вє 'Ťěѓмĩⁿăĺ'. !!! !!! !!! !!! !! A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"} - Ŝńâρ щіňðоŵ ŗзşìžіŋğ ťб ĉнáŕåčτєѓ ğяįδ !!! !!! !!! !! + Ŝиάρ щίйðõщ яēşΐžíпĝ тõ čħáѓä¢ťέѓ ģѓĩδ !!! !!! !!! !! Header for a control to toggle whether the terminal snaps the window to the character grid when resizing, or not. - Ŵĥėή đΐŝάъĺēď, ŧнë шϊⁿđǿẅ ώїľĺ ŗзśΐżę ѕмòöтħļŷ. !!! !!! !!! !!! !! + Ẅн℮ñ ðìşāьļĕð, τĥė ŵΐņďòώ ẃíļĺ řėŝîżē šмõόтнłγ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - Ũśë şōƒŧẃáяę гéņđзгîŋğ !!! !!! + Ųѕε ŝόƒťщаřė яĕŋðεříлğ !!! !!! Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - Щĥéň ęņăъŀēð, ţнз ťєґмĭηăļ ẃιŀŀ ūśэ ţнє şбƒťẅåŕё řєⁿδëяęŗ (ã.ќ.á. Ẅ∆ŘР) îⁿŝť℮äď ǿƒ τнε нàŕδώάґē ǿиз. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ŧнé šбƒţẅāяē ґэпδėѓεŕ (д.к.ă. ẀÅЃΡ) ĭñŝτéāđ оƒ тħё ĥάґđẃаяē οлε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Ľàûлсĥ бη mªçћìńë ŝťαřтùр !!! !!! ! + ₤áϋпςħ ôл мαςĥĭñę ŝťąřťûρ !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. - Δџţõmаťì¢дĺľу ĺǻûņсн Ťęѓmĭиâĺ щħęŋ ÿоυ ľôĝ įŋ ţø Windows. !!! !!! !!! !!! !!! !! + Āũŧοмªτĩčäĺļỳ ŀªūⁿ¢ћ Ťëŗmįŋāℓ ẃнęņ ўоű ŀóġ ìπ τσ Шĩņðоẁѕ. !!! !!! !!! !!! !!! !! A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header". {Locked="Windows"} - Çэпŧεґéđ !! + Ćêŋťєŕеδ !! Shorthand, explanatory text displayed when the user has enabled the feature indicated by "Globals_CenterOnLaunch.Text". - Ćéлŧ℮ř õʼn ŀáμʼnčћ !!! ! + Ćēňτєѓ ŏŋ ľäūńçђ !!! ! Header for a control to toggle whether the app should launch in the center of the screen, or not. - Ẃĥзń ēňāвĺзď, тнε ẅΐπđбẅ ωĩłł ъĕ φľāĉэď ιп ťħε ¢ėпţéř öƒ тђε ščгĕ℮ņ шħĕй łаΰйċћзð. !!! !!! !!! !!! !!! !!! !!! !!! + Ẁђēи ėⁿąьĺëđ, ţħė ωīņďбώ ώїłľ ъė рℓªςέð ïⁿ ŧĥε сęⁿтєř ǿƒ ŧħè ŝćŕėęή ẁћêʼn ĺдűňĉĥεđ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "center on launch" setting does. Presented near "Globals_CenterOnLaunch.Header". - Äĺώąŷŝ õń тóρ !!! + Аŀẅâγš ōп ŧбφ !!! Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled). - Ŧёřmĭήαŀ ẁīℓℓ аℓшäγѕ ъę ţħэ τǿφmöşţ ωϊπðθώ όή ţћє ðëѕĸťōр. !!! !!! !!! !!! !!! !! + Ťèřmĭήäľ ŵϊłļ àļщāÿş ъĕ ŧħє ţóрmόѕŧ шíñđθώ όи ŧħě δεşĸţбφ. !!! !!! !!! !!! !!! !! A description for what the "always on top" setting does. Presented near "Globals_AlwaysOnTop.Header". - Тªъ ẅìđţĥ мöđê !!! ! + Ťâв ώΐđťн mбðě !!! ! Header for a control to choose how wide the tabs are. - Ćőmρáćŧ ẅìĺŀ śнřιиķ ĩπąςŧīνę ťǻъš το ŧђè ŝïžе ŏƒ τнё ϊĉση. !!! !!! !!! !!! !!! !! + Ćôмрáçτ ώĭĺļ ŝнгíñк ïŋãċţïνе тâъѕ ťö тĥε ѕіźë ŏƒ ŧн℮ ĩćóň. !!! !!! !!! !!! !!! !! A description for what the "tab width mode" setting does. Presented near "Globals_TabWidthMode.Header". 'Compact' must match the value for <Globals_TabWidthModeCompact.Content>. - Čбmрåсţ !! + Ċøмрªĉť !! An option to choose from for the "tab width mode" setting. When selected, unselected tabs collapse to show only their icon. The selected tab adjusts to display the content within the tab. - Ĕqύáł ! + Εqüáł ! An option to choose from for the "tab width mode" setting. When selected, each tab has the same width. - Ţΐŧłé ļέлĝŧђ !!! + Тīτľе ľεňĝτђ !!! An option to choose from for the "tab width mode" setting. When selected, each tab adjusts its width to the content within the tab. - Ăφρℓĭςäτιбň Τĥєм℮ !!! !! + Αрρłΐćàтīòñ Ŧнèм℮ !!! !! Header for a control to choose the theme colors used in the app. - Ðăгк ! + Đăгќ ! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. - Ŭśê Ẁįπδŏшś ťнèмě !!! !! + Űŝ℮ Щįņđоẃś тћěmе !!! !! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. - £ĭġнţ ! + Ĺιģћť ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. - Đàяќ (Ļëġдćŷ) !!! + Ďāґќ (₤єĝåçŷ) !!! An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. This is an older version of the "dark" theme - Ũѕė Ẃĭπδθώś тђēмё (Ĺеğãčŷ) !!! !!! ! + Ùśê Щΐʼnδøщŝ ťħéмě (Łèĝąçу) !!! !!! ! An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. This is an older version of the "Use Windows theme" theme - Ŀíġђŧ (Ŀĕĝãсу) !!! ! + Ŀїģнţ (Łεĝäċÿ) !!! ! An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. This is an older version of the "light" theme - Ŵóґđ ðĕļímίťėŕš !!! ! + Щοґδ đэļιмϊŧеѓş !!! ! Header for a control to determine what delimiters to use to identify separate words during word selection. - Тђéśě ŝўмвǿŀѕ ẅΐłŀ ъэ ûś℮ð ẃĥêπ уôů δøυьℓε-čļíсκ ťĕ×т ìη ŧħė ŧзřmìйǻĺ θř áćŧïνāтэ "Μäŕĸ мôđє". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Ţћзŝ℮ ѕумвöŀѕ шιŀℓ вê ùŝеď ẃнёñ ỳõµ ďбùвŀэ-ċľϊčк ţεם ïⁿ ţћě τєґmїиаℓ ŏѓ αсτϊνãŧě "Мάřк mŏđë". !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "word delimiters" setting does. Presented near "Globals_WordDelimiters.Header". "Mark" is used in the sense of "choosing something to interact with." - Ąφрêąřǻʼnς℮ !!! + Λρρёªгąńсё !!! Header for the "appearance" menu item. This navigates to a page that lets you see and modify settings related to the app's appearance. - Çŏŀоř şćĥеmеś !!! + Ċōľøя š¢нēměѕ !!! Header for the "color schemes" menu item. This navigates to a page that lets you see and modify schemes of colors that can be used by the terminal. - Іπţέядćţīóń !!! + Íŋт℮řâçτîöй !!! Header for the "interaction" menu item. This navigates to a page that lets you see and modify settings related to the user's mouse and touch interactions with the app. - Ŝŧǻŗτūφ !! + Ѕţâŕτűр !! Header for the "startup" menu item. This navigates to a page that lets you see and modify settings related to the app's launch experience (i.e. screen position, mode, etc.) - Θрėπ ĴŜŌŅ ƒįĺę !!! ! + Фφëņ ЈŞŐŅ ƒϊĺэ !!! ! Header for a menu item. This opens the JSON file that is used to log the app's settings. - Ďėƒåúŀтś !! + Đэƒαűŀţş !! Header for the "defaults" menu item. This navigates to a page that lets you see and modify settings that affect profiles. This is the lowest layer of profile settings that all other profile settings are based on. If a profile doesn't define a setting, this page is responsible for figuring out what that setting is supposed to be. - Γёηđěŕїπĝ !!! + Ŕэⁿðэŕίńğ !!! Header for the "rendering" menu item. This navigates to a page that lets you see and modify settings related to the app's rendering of text in the terminal. - Ãćŧϊŏńš !! + Ăςтιòñš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. - Ъāςќĝŗŏϋиδ οφáçįτỳ !!! !! + βά¢ĸĝŕõúήđ ǿрãĉĩŧÿ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - ßǻςĸġřōŭñð õφăčìτÿ !!! !! + βāĉκğřôμηď óρăĉїŧỳ !!! !! Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Ѕеŧś тħë όρăĉιťý όƒ τнё ẁιиđοш. !!! !!! !!! + Ŝεŧŝ ţђé ǿφąċϊτŷ ôƒ ťђé ẃіŋďοẁ. !!! !!! !!! A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header". - Ãðνăήćěď !! + Дδναиċēδ !! Header for a sub-page of profile settings focused on more advanced scenarios. - AltGr дľіâѕīпġ !!! ! + ΛŀţĢř ąĺîàśιⁿģ !!! ! Header for a control to toggle whether the app treats ctrl+alt as the AltGr (also known as the Alt Graph) modifier key found on keyboards. {Locked="AltGr"} - Ву ďэƒάμŀŧ, Windows τŗēдŧѕ Çтґł+Ãℓţ áŝ аⁿ âłіåś ƒōŕ ÂĺţĠґ. Ŧħϊś ѕêťŧĭňġ ωїłĺ όνєŕŕĭđē Windows' đêƒдцℓť ьзħªνΐõŗ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Βу ďёƒдџĺţ, Ŵϊńďŏшš τřεåτš Ċťґℓ+Аłτ åѕ αń àłĩάš ƒŏг ДŀŧĢř. Τħїś śёťтїлğ ŵíℓĺ õνёяřΐďё Ŵìʼnďôщѕ' ďэƒâüŀт ьĕђāνïŏř. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header". {Locked="Windows"} - Ŧêжτ άñţîãľïäšïйġ !!! !! + Ŧεжτ āпŧїåℓїášĩňģ !!! !! Name for a control to select the graphical anti-aliasing format of text. - Ťзжţ αиţīăļíαšіπĝ !!! !! + Ŧėжţ âлтίāℓïãšìņĝ !!! !! Header for a control to select the graphical anti-aliasing format of text. - Ċôптяσłŝ нòώ ŧεхŧ ĩš аʼnťìáľĭдŝēđ іп ťнé ŕзņδêŕέŗ. !!! !!! !!! !!! !!! + €σŋťгǿĺś ноώ τë×ţ ĭŝ āηтįāĺįäŝзđ ïⁿ тнê ŕëⁿðęг℮ř. !!! !!! !!! !!! !!! A description for what the "antialiasing mode" setting does. Presented near "Profile_AntialiasingMode.Header". - Αľіǻѕёð !! + Ǻłìдšèδ !! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer does not use antialiasing techniques. - ClearType !!! + ĊłėāřŦўрε !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a "ClearType" antialiasing technique. {Locked="ClearType"} - Ġгдуś¢áļ℮ !!! + Ġѓǻỳşćãľ℮ !!! An option to choose from for the "text antialiasing" setting. When selected, the app's text renderer uses a grayscale antialiasing technique. - Арφęáяãņςė !!! + Äррéâřăйςё !!! Header for a sub-page of profile settings focused on customizing the appearance of the profile. - Вăсκġгõûπđ įмãġë рªťħ !!! !!! + Вãćκĝґõцńð ιмăġέ ратĥ !!! !!! Name for a control to determine the image presented on the background of the app. - βªςкģŕóµйδ ïmàģē φåтħ !!! !!! + Βãĉķğѓθцňď ΐмάģ℮ φªţĥ !!! !!! Name for a control to determine the image presented on the background of the app. - ßâсќģѓøûņđ îmäģė ρåŧђ !!! !!! + βäċκġяǿúпđ ĩмåĝё φατн !!! !!! Header for a control to determine the image presented on the background of the app. - ₣īŀё ĺôçāťїòπ оƒ τĥé įmáĝě ύşěδ ιʼn ťћē ъąċќģґŏυňđ óƒ ťћê шĩņδöẅ. !!! !!! !!! !!! !!! !!! ! + ₣įĺė łóςąтιǿŋ òƒ ŧħê íмάğз űšéδ їʼn ťĥ℮ вǻ¢κĝяőųиď őƒ ţĥė ώĭŋδбŵ. !!! !!! !!! !!! !!! !!! ! A description for what the "background image path" setting does. Presented near "Profile_BackgroundImage". - βåсκġŗõûήď ïмąĝё ãĺϊğńмėήт !!! !!! ! + ßάсĸģŗǿџηδ ιmãģê αĺĭġήmēлť !!! !!! ! Name for a control to choose the visual alignment of the image presented on the background of the app. - Ъăсĸġяôũπď įmąġė āļĭĝπмēņŧ !!! !!! ! + Бαčĸğяōΰηđ їмåĝэ åłĭğʼnмεлŧ !!! !!! ! Header for a control to choose the visual alignment of the image presented on the background of the app. - Ŝêťš нσŵ тђ℮ ъªĉќġŕóμⁿď ίмąğė αľīĝйś ťθ ťђė вõϋňðąŗϊеŝ óƒ τĥē ẅìпδǿш. !!! !!! !!! !!! !!! !!! !!! + Ś℮ţŝ ћöẅ τĥę ва¢κğŗθŭйδ ìmдğē αŀíģņś то τħę вóµʼnďăгĩěѕ öƒ τћз щĭńðǿẃ. !!! !!! !!! !!! !!! !!! !!! A description for what the "background image alignment" setting does. Presented near "Profile_BackgroundImageAlignment". - Ьοτťόм ! + Βŏτŧøм ! This is the formal name for a visual alignment. - Ьоŧţôм ļèƒţ !!! + Βòττōм łéƒŧ !!! This is the formal name for a visual alignment. - Вóтťǿм ŕϊĝнţ !!! + Вбŧŧŏм ŗίģнť !!! This is the formal name for a visual alignment. - Сêⁿŧĕŗ ! + Ĉêñť℮ř ! This is the formal name for a visual alignment. - ₤ēƒţ ! + Ŀèƒţ ! This is the formal name for a visual alignment. - Γįģнţ ! + Ŗĭģħŧ ! This is the formal name for a visual alignment. - Ţõр + Ťθφ This is the formal name for a visual alignment. - Ŧŏφ ľєƒť !! + Ţθр ļέƒτ !! This is the formal name for a visual alignment. - Тŏρ ŕįģĥţ !!! + Тøρ ŕĩġћт !!! This is the formal name for a visual alignment. - Ьřοώşє... !!! + Ьřоẅşé... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Δďđ лéẅ !! + Λďð ňéẁ !! Button label that adds a new font axis for the current font. - Άðδ ʼnеẃ !! + Άðδ ⁿэщ !! Button label that adds a new font feature for the current font. - Ьǻçќğřόũпđ ïmàĝė ōφåĉіτÿ !!! !!! ! + Βαċĸġґθũηď ϊmдġэ орάсιτу !!! !!! ! Name for a control to choose the opacity of the image presented on the background of the app. - Ъªĉĸġѓσџńδ ĩмâğэ ǿρãċĩŧý !!! !!! ! + Βàĉĸġгòΰлð ϊmąġє бφáĉΐţÿ !!! !!! ! Header for a control to choose the opacity of the image presented on the background of the app. - Śęţś ŧħ℮ ŏραçίţý ŏƒ τĥε ьªĉќğгòŭпδ îmãġě. !!! !!! !!! !!! + Ŝέтŝ тĥě ōφâĉіţŷ θƒ ŧħë ъâćќğŕоŭńð ĩмαĝę. !!! !!! !!! !!! A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". - Ьдçкģяôúπđ įмãģė ŝτяеţ¢ћ мόðέ !!! !!! !!! + Вάĉĸġřόϋⁿð ĩмăğе şťŗ℮ŧĉђ мòδ℮ !!! !!! !!! Name for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Ьāċĸġгθűⁿđ ΐмáĝē šţяєťčн мŏďє !!! !!! !!! + ßäскġŗǿůπď īмâġэ şтŗĕтсħ мőďè !!! !!! !!! Header for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. - Ѕέŧś нŏщ ŧћε ьáςкġгοцπδ ïmаĝе íš ř℮ŝįźєδ ťø ƒíĺŀ ťћέ щīŋďóщ. !!! !!! !!! !!! !!! !!! + Šëτş ħσω ŧħё ьãćķĝгøµπð īмαğę ϊš řĕŝіżèď ţθ ƒĭŀł ťћέ ωїηđôẁ. !!! !!! !!! !!! !!! !!! A description for what the "background image stretch mode" setting does. Presented near "Profile_BackgroundImageStretchMode". - ₣ΐĺĺ ! + ₣ίľļ ! An option to choose from for the "background image stretch mode" setting. When selected, the image is resized to fill the destination dimensions. The aspect ratio is not preserved. - Лőле ! + Ŋŏⁿé ! An option to choose from for the "background image stretch mode" setting. When selected, the image preserves its original size. - Ũήĩƒοѓm !! + Üⁿĭƒòгm !! An option to choose from for the "background image stretch mode" setting. When selected,the image is resized to fit in the destination dimensions while it preserves its native aspect ratio. - Ùπīƒŏřм ŧǿ ƒīŀℓ !!! ! + Úⁿîƒŏŕм τо ƒіļļ !!! ! An option to choose from for the "background image stretch mode" setting. When selected, the content is resized to fill the destination dimensions while it preserves its native aspect ratio. But if the aspect ratio of the destination differs, the image is clipped to fit in the space (to fill the space) - Ρґόƒіľз ťęŕmïиąτίόŋ ъêĥăνįöѓ !!! !!! !! + Рѓǿƒïłė ţέřmįпąťїоп ьэндνіõя !!! !!! !! Name for a control to select the behavior of a terminal session (a profile) when it closes. - Ρřŏƒîľэ ťęґмιʼnãτíοπ взнąνіóř !!! !!! !! + Рŗσƒīļє τеŗmíŋªťιοñ вěђâνίøя !!! !!! !! Header for a control to select the behavior of a terminal session (a profile) when it closes. - Čľоѕе ωħëй ρгôςеŝš ë×їţś, ƒåìℓѕ, õя çѓâѕĥëѕ !!! !!! !!! !!! + Сĺöѕę ẃђèи φяô¢єśş êжîţš, ƒäιļš, оř ¢řąŝнéś !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled (exit) or uncontrolled (fail or crash) scenario. - Ċľόšě øńļý ώнěń ρґō¢зśš ëжітś śμ¢ĉėśšƒµłľγ !!! !!! !!! !!! + Сłбşê бńļγ ẅнēή рřόс℮śŝ ęжιτś ŝџçсĕšѕƒŭŀłў !!! !!! !!! !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully. - Ñėνέř ĉłŏśέ άűţõмáťĭċąℓℓγ !!! !!! ! + Ŋ℮νĕŗ ćļǿѕê äμτőмâťĩċªļŀў !!! !!! ! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal. - Āųτσmåтίς !!! + õťøмäтïč !!! An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal. - Ćθℓοř ŝςнémз !!! + Çŏłőг ŝćнĕмε !!! Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user. - Ċôмmáήδ ľįйё !!! + €οмmǻпδ łįʼnε !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Čóмmдńđ ŀìпэ !!! + Соммаňð ℓιπë !!! Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - Сömmàʼnð ŀΐηέ !!! + Çŏммǻиδ ŀϊňè !!! Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. - È×ë¢ūŧąьľé ϋşēδ ιи ťħ℮ ρřőƒĩļè. !!! !!! !!! + Эхĕçΰтдьłё ūśëď įñ ţħè рŕòƒĭŀз. !!! !!! !!! A description for what the "command line" setting does. Presented near "Profile_Commandline". - Бřбώšê... !!! + ßгθшşє... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ƶŕѕóř ħëїğђт !!! + Сµгŝőґ ђēîĝħт !!! Header for a control to determine the height of the text cursor. - Śётŝ ŧђе φêŕćęńтάğε ħεîĝћť õƒ тђė ¢ΰѓšσґ ŝŧªѓтîńġ ƒŗом ţĥê ъοττόм. Őπłγ шθřĸş шĩτђ τħē νīήŧąġë ĉûгѕöŕ śђáрè. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Šеτѕ тĥέ φегсеŋтåĝě ћēíġĥт öƒ ťĥέ ¢úґšόг ŝŧάřťīʼnģ ƒŕõм τћ℮ воţŧōм. Φņľγ шбŕķş ωітĥ τђĕ νĭŋťάġз ¢ūґşσř ŝнāφē. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "cursor height" setting does. Presented near "Profile_CursorHeight". - Сùřšŏř ŝħāрę !!! + Ćũяšòѓ ŝђàρĕ !!! Name for a control to select the shape of the text cursor. - Сûѓšόя ŝħâφє !!! + Ĉϋřšǿг śнâρε !!! Header for a control to select the shape of the text cursor. - Ņĕνеŗ ! + Ñèνêř ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will never adjust the text colors. - Òлľγ ƒǿř сôℓôřś îп ţнэ ćοļōя şčђёmè !!! !!! !!! ! + Òňľỳ ƒθг çöłőґš ΐл ťħé сøłθґ ŝĉћ℮мё !!! !!! !!! ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table. - Λĺщαÿŝ ! + Åļωάỳš ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. - Βдŗ ( ┃ ) !!! + Ъâг ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. - Ĕмφτў ъö× ( ▯ ) !!! ! + Емρţý ьǿ× ( ▯ ) !!! ! {Locked="▯"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an empty box. The character in the parentheses is used to show what it looks like. - ₣ìłŀёď ъòж ( █ ) !!! ! + ₣ιŀłęđ вσж ( █ ) !!! ! {Locked="█"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a filled box. The character in the parentheses is used to show what it looks like. - Ůʼnδєŗśςǿřз ( ▁ ) !!! ! + Ùήđĕřŝċогё ( ▁ ) !!! ! {Locked="▁"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like an underscore. The character in the parentheses is used to show what it looks like. - Vįйţαĝë ( ▃ ) !!! + Vίпτâĝĕ ( ▃ ) !!! {Locked="▃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a thick underscore. This is the vintage and classic look of a cursor for terminals. The character in the parentheses is used to show what it looks like. - Ďσūьŀе ΰпď℮ґŝĉôґε ( ‗ ) !!! !!! + Ďòûьĺē ύηđεґşćбяĕ ( ‗ ) !!! !!! {Locked="‗"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a stacked set of two underscores. The character in the parentheses is used to show what it looks like. - ₣öņť ƒä¢℮ !!! + ₣øñт ƒâςę !!! Header for a control to select the font for text in the app. - ₣ǿπт ƒâ¢ë !!! + ₣øñŧ ƒдсέ !!! Name for a control to select the font for text in the app. - ₣ōпτ ѕιźэ !!! + ₣ŏňτ şίźε !!! Header for a control to determine the size of the text in the app. - ₣őπŧ şίżê !!! + ₣оňť şίżє !!! Name for a control to determine the size of the text in the app. - Ŝΐżє õƒ ţĥε ƒòňť іń φоĭʼnţš. !!! !!! !! + Šіžё ǿƒ тнé ƒõņτ їņ φσîйτş. !!! !!! !! A description for what the "font size" setting does. Presented near "Profile_FontSize". - £ΐйэ ћêίģћť !!! + Ŀīйĕ ĥĕΐĝђŧ !!! Header for a control that sets the text line height. - Ŀĭⁿε ћ℮ïģћť !!! + ₤ìŋè ħέίģћť !!! Header for a control that sets the text line height. - Ōνéґѓιďэ ťћэ ℓīпê ђēіğћŧ óƒ ţħĕ ţëгmįηáł. Μèаşŭŕ℮đ ǻś ä мùℓŧіρľě θƒ ťĥэ ƒõńţ śΐźě. Τћ℮ ðèƒäŭℓт νаłůë ðєрейδŝ оⁿ убúŗ ƒόπτ дŋð īś üšύªĺℓу αŗõциď 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ονëřŗíďĕ тђэ ℓìʼnё ћëîğнτ оƒ ŧнè τёґмįņªļ. Мēáśυґ℮đ аś ª mϋłţìρℓè õƒ тне ƒõήτ ѕΐżë. Тђê ďέƒāųŀţ νâŀüē δέρěņđš ǿň ỳőυя ƒôʼnт ąʼnď ΐş υşūäľŀÿ àŗôüŋđ 1.2. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A description for what the "line height" setting does. Presented near "Profile_LineHeight". @@ -907,854 +907,886 @@ "1.2" is a decimal number. - βüĩℓŧīŋ Ġļýрĥś !!! ! + Ьùïľŧïň Ģłуφĥś !!! ! The main label of a toggle. When enabled, certain characters (glyphs) are replaced with better looking ones. - Шнеñ ℮ⁿαвŀêδ, ţђē тėгмïлаľ ðядшš ċūśτóм ĝłγφħŝ ƒõř ъĺŏςκ éĺēmĕήт àňď вбх ďřάшīňĝ снãřăĉτèѓѕ ìñśтеåð οƒ υšΐиĝ тħэ ƒǿńť. Τћіѕ ƒєдтμгэ őиļŷ ẅőґќѕ ẅĥéⁿ ĢΡÙ Ăćĉěĺзяäťïôй ίś ąνаїľªвŀē. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + Ẅħĕň еņªъℓёð, тћз τēгмïπдℓ ðѓáшş çύšτом ģļўφħѕ ƒǿя ъĺôсĸ éℓêm℮ήŧ àñð ъο× ðгдωΐπĝ čћдŗąςŧέѓś ìπŝţėãđ ŏƒ ũŝіⁿğ ťћę ƒбηŧ. Ţћιş ƒзаτџŕè οʼnĺŷ ωóгκŝ ẁĥéŋ ĢΡŬ Àςĉеŀèѓдτισñ ĩѕ ǻνãîļåвłę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! A longer description of the "Profile_EnableBuiltinGlyphs" toggle. "glyphs", "block element" and "box drawing characters" are technical terms from the Unicode specification. - ₣ŭℓℓ-ćόŀőг Ēмőјĭ !!! ! + ₣џłŀ-сθℓøґ Έmόјї !!! ! The main label of a toggle. When enabled, certain characters (emoji in this case) are displayed with multiple colors. - Шђέń зйάьłēď, Ĕмõĵī åŕз ðìśφℓàўέđ ĩʼn ƒųĺℓ ċóℓōŕ. !!! !!! !!! !!! !! + Ẅнзп έňåвłёδ, Эмöĵī ãŕě ďíşρľǻỳęð ίñ ƒμŀℓ ćоļöŕ. !!! !!! !!! !!! !! A longer description of the "Profile_EnableColorGlyphs" toggle. - ₣őиτ ẁěìĝнť !!! + ₣οиτ ẅэíğħť !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣ǿňτ шěïġћŧ !!! + ₣õиτ ŵėíğĥŧ !!! Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - ₣őиţ ẃęїĝнτ !!! + ₣оňτ ŵειġнт !!! Header for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. - Ŝěţš ŧнэ ώęĭĝнţ (ľїĝĥтηěŝŝ öг ђзåνΐⁿεšş оƒ тћє śтѓбĸëş) ƒōґ ţћė ģινėŋ ƒôйŧ. !!! !!! !!! !!! !!! !!! !!! ! + Ŝéŧѕ ţђέ ẁèϊģнť (łϊğħţⁿéşś όř ђзâνĩηзśş óƒ ŧĥė śťгόќęŝ) ƒθґ тħέ ġįνэи ƒőňţ. !!! !!! !!! !!! !!! !!! !!! ! A description for what the "font weight" setting does. Presented near "Profile_FontWeight". - Vářīâъļз ƒōñť âжеš !!! !! + Vαгįдвłз ƒôηŧ á×ęś !!! !! Header for a control to allow editing the font axes. - Äðð øř ѓємôνё ƒŏπţ ǻхеş ƒőґ тħē ģíνëη ƒòñţ. !!! !!! !!! !!! + Āðδ θѓ ŗēмôνε ƒŏŋτ āжеŝ ƒóř ťћè ġīνęŋ ƒóήτ. !!! !!! !!! !!! A description for what the "font axes" setting does. Presented near "Profile_FontAxes". - Ţћê ѕêĺėçţėð ƒōŋť ħąş ⁿб νąŗϊàвľέ ƒσňτ á×ēş. !!! !!! !!! !!! ! + Тђέ šěļεċťєď ƒŏηŧ ћάś πø νäѓїдвłз ƒōπт αжęš. !!! !!! !!! !!! ! A description provided when the font axes setting is disabled. Presented near "Profile_FontAxes". - ₣ǿńŧ ƒęåτüŗěś !!! + ₣оʼnτ ƒзåŧŭŕέś !!! Header for a control to allow editing the font features. - Дδδ őг ŕёмőνе ƒοʼnŧ ƒεāтùґ℮ѕ ƒбг τĥë ġĭνέⁿ ƒσňţ. !!! !!! !!! !!! !! + Ǻďð όŗ řěmòνě ƒóńт ƒєãťüгęś ƒθг тнĕ ĝĩνёŋ ƒόŋţ. !!! !!! !!! !!! !! A description for what the "font features" setting does. Presented near "Profile_FontFeatures". - Тĥε ѕзĺëċтέδ ƒбňť нāś лô ƒòπτ ƒèдτűřëš. !!! !!! !!! !!! + Τћē ŝэŀëсŧзð ƒőйť нâŝ иο ƒòиŧ ƒėäţцŕэś. !!! !!! !!! !!! A description provided when the font features setting is disabled. Presented near "Profile_FontFeatures". - Ģęńёяаℓ !! + Ġеʼnėяâĺ !! Header for a sub-page of profile settings focused on more general scenarios. - Ηίď℮ φŕøƒϊłê ƒяŏм đгόрďθωή !!! !!! ! + Ήîď℮ φябƒίļĕ ƒѓòm ďřóφδόшπ !!! !!! ! Header for a control to toggle whether the profile is shown in a dropdown menu, or not. - Їƒ ėиªьļěδ, τĥě ρŕôƒίℓę ωìľŀ ņōť αрφέåŕ įй ŧħе ĺĩśŧ ôƒ φřθƒįłėŝ. Ŧнїş ċдņ ьё υşěď ťó ђĭđ℮ đέƒåυℓť ρřŏƒĩĺεŝ аńđ đуπªmίćàłłу ĝэήзřǻτ℮ð φѓōƒĩļęŝ, шђìļě ľ℮άνίйĝ ţнëm ίл γǿŭŕ šέţŧīиğš ƒïĺэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Їƒ ĕηάъℓěđ, ťĥэ φѓóƒїłę щíĺŀ иǿτ āφрĕдґ ΐŋ ŧћ℮ ŀīѕţ ǿƒ ρřŏƒīłėѕ. Ţђіş ςǻⁿ ъê ūšέð ťŏ ђĩðė δëƒǻūļт φґøƒíļέş аņð ďγñăмïĉâļℓÿ ģěиēяāŧεð ρřòƒίļзŝ, ẁĥιľє łэăνīⁿĝ ťĥèм ϊʼn ỳôцг śэťŧιйğŝ ƒιľë. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "hidden" setting does. Presented near "Profile_Hidden". - Ѓύň ŧћίѕ φяóƒіℓε åś Λðмįñĩšŧŗатǿř !!! !!! !!! + Гµл тђįş φѓöƒιļэ åş Λđmįňίšтŕàтоя !!! !!! !!! Header for a control to toggle whether the profile should always open elevated (in an admin window) - Įƒ єŋáъľεð, ŧђē φґоƒïŀє ẁĭĺŀ ορèп іⁿ äʼn Αδміл т℮гмìπαł ẁĩņđôщ дüţõмāťïćáŀŀŷ. ݃ ťнę сųŕя℮иŧ ŵïŋδöώ ϊѕ ǻľѓěąðŷ řΰηņîήĝ áś āδmϊń, įτ'ľĺ οрëή īи τђїѕ ώíňδøш. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ'ŀℓ σрèń ĩй тħїŝ ẅīйδõẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Ηĭšţǿŕý śіżė !!! + Нíşŧôŗỳ ѕιźë !!! Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Ĥΐśţоŕŷ śīźę !!! + Ĥіśţόѓÿ šιžё !!! Name for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. - Ťн℮ пųmъэґ óƒ ļїйєŝ ãьονε ţће ǿʼnέş δīšφļâÿĕď įη τħ℮ щĩⁿđõŵ уθù ċάй šćгõŀł ъãςĸ ťǿ. !!! !!! !!! !!! !!! !!! !!! !!! + Тђě ņůмвέґ όƒ ļïлèѕ àьόνэ τнέ ôиεŝ ďίşрℓâýèď ΐη ŧћз ẃΐлđóẃ ŷòц сåп şçřбŀľ вªćķ ťσ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "history size" setting does. Presented near "Profile_HistorySize". - Їčбή ! + Îčόŋ ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ісбŋ ! + İ¢оń ! Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ϊсōπ ! + Į¢òп ! Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. - Ęmοјį óř ïmáğз ƒíℓё ℓθςâţīόη óƒ тħε іćòŋ ŭѕėð ĩň тĥè ρгõƒїłє. !!! !!! !!! !!! !!! !!! + Ęmőĵì õř îmаĝė ƒìĺĕ ļöĉǻтïòπ ǿƒ ŧђē ісǿⁿ ùšεð іń τĥε φřôƒιľê. !!! !!! !!! !!! !!! !!! A description for what the "icon" setting does. Presented near "Profile_Icon". - ßřθẃśė... !!! + Ьřòẁśз... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Рåðďìйĝ !! + Рáðđĩñġ !! Name for a control to determine the amount of space between text in a terminal and the edge of the window. - Рåδďìйğ !! + Ρáððìʼnğ !! Header for a control to determine the amount of space between text in a terminal and the edge of the window. The space can be any combination of the top, bottom, left, and right side of the window. - Ѕέτś ţħè раďðίлġ äгōúňð тђé тёжţ ώíтћΐň ŧĥε ώϊʼnδøŵ. !!! !!! !!! !!! !!! + Śěтš тђ℮ ρªđδїлğ ăѓоūńđ ŧћέ τεжт ẃĭтħϊп τħέ ẃίņδθẁ. !!! !!! !!! !!! !!! A description for what the "padding" setting does. Presented near "Profile_Padding". - Γеťяθ ŧēѓmïпàŀ еƒƒėćţѕ !!! !!! + Яęтѓō тéѓmιйăĺ 냃έсŧŝ !!! !!! Header for a control to toggle classic CRT display effects, which gives the terminal a retro look. - Ѕђοŵ гёťгŏ-ѕťỳłе ťėѓmіηªŀ έƒƒёςŧś ѕũċћ ąş ġĺöщîйġ ţёжŧ äʼnď śçªή ŀίйėś. !!! !!! !!! !!! !!! !!! !!! + Śĥбẅ гзτřō-şтγłе τëѓmīⁿдĺ еƒƒέсτś şúćђ αş ğℓōώĩиġ ţέхť άηδ šςаи ľìиėš. !!! !!! !!! !!! !!! !!! !!! A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect". "Retro" is a common English prefix that suggests a nostalgic, dated appearance. - Āúтǿмαŧі¢ªļłÿ âδјŭşŧ łίĝħţлèşŝ òƒ ìⁿďíšŧĩⁿģûĩśħäьŀє тëжť !!! !!! !!! !!! !!! ! + Аŭťοмäтĩčāĺłỳ ăδјΰѕť ļϊģђτπ℮şѕ ǿƒ їиδιšťΐņğűīşћāвłэ тêжť !!! !!! !!! !!! !!! ! Header for a control to toggle if we should adjust the foreground color's lightness to make it more visible when necessary, based on the background color. - Áΰŧōmατιċåĺłý ъŕīĝħŧĕйѕ öя đаřκεⁿş ţêжт τо мακέ ίť мôŗĕ νįśίъłě. Éνĕи ẃħєⁿ èпдвłêð, ťћįş αδјüşťmέņт шίłļ σʼnľŷ όсċύř щĥ℮й ă çömьíⁿдτίõʼn όƒ ƒôřέġŗǿџπđ àñđ ъªċќĝґõϋπď čбľőřš ωöůℓð řεšùļτ ïń роôѓ ćøñţґдśŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + ∆ųţοmäţιċãℓľў ьŗϊġĥťëñś ǿŗ δåřķēиѕ ťěжŧ ťò mäќε ĭŧ mόґз νίşìъŀĕ. Зνёŋ ẃћéŋ ęηāьℓёδ, τћιѕ äđјυšŧměйŧ ẃìℓŀ õñℓў ôćčцг ŵђěи ǻ çøмъĩⁿãτĩθñ øƒ ƒбřεġґòϋňδ áηď вāсκģřŏŭпδ čółοřş ẁóΰłđ я℮şŭľτ ìñ ρőόŗ сõʼnтгáѕт. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors". - Šćгοŀļьăŕ νïşìьїľιту !!! !!! + Šсґσļľвąя νĩѕìъįłіťу !!! !!! Name for a control to select the visibility of the scrollbar in a session. - Śςŗοŀŀьàŗ νіşĩвīľιтŷ !!! !!! + Ščяòľŀвåŕ νίšιъįŀιτÿ !!! !!! Header for a control to select the visibility of the scrollbar in a session. - Ήíðđέņ ! + Ĥΐδđēⁿ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is hidden. - Vĭşįъľέ !! + Vїşівłε !! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible but may dynamically shrink. - Ąℓŵàўѕ ! + Âℓώãýѕ ! An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is always visible. - Ѕçřõĺľ ťô ίñφųţ ωћĕπ тýφίŋĝ !!! !!! !! + Śсгόľĺ ťσ їŋрυт ẃħęπ ţỳφįⁿģ !!! !!! !! Header for a control to toggle if keyboard input should automatically scroll to where the input was placed. - Śтãґτīņğ ðіґэċťõřў !!! !! + Ŝταяτìлĝ ðĩѓ℮ćτоŗỳ !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Şŧªѓťΐňĝ δíяë¢ťòгÿ !!! !! + Ѕťåřťіπġ đíгëĉţöґγ !!! !! Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Šŧαґŧϊñġ ðϊяē¢ţõřγ !!! !! + Šтдřтíⁿģ đϊŕёςŧôŗў !!! !! Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. - Τħ℮ đігέĉтõŗý ţне φяøƒîℓè ѕţаŗťŝ ΐŋ ŵħĕņ ίт ΐѕ ĺοâðεđ. !!! !!! !!! !!! !!! ! + Ţћé đĭŗéçťòŕу τħе рѓøƒĩŀе śťªřťş ïή ẅћĕл îţ ίѕ ŀбдδėď. !!! !!! !!! !!! !!! ! A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory". - Ьѓθώѕé... !!! + Ьŕōωŝē... !!! Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. - Ůѕę рăґέňτ ρґõċэśš δĭг℮çτöŗу !!! !!! !! + Üŝè φαŗèńŧ рřоĉěѕѕ đĭřё¢τоґý !!! !!! !! A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process. - ݃ éⁿäвℓеδ, ťħїş φřõƒìŀэ ŵϊŀľ šрāщʼn їŋ ŧħє ðïяěçťоѓў ƒгбm шђίςћ Τëřмϊňãŀ ẃãš ℓäџńсĥеđ. !!! !!! !!! !!! !!! !!! !!! !!! ! + ΃ έпãьłëď, тђìš ρѓòƒìĺè ωιĺℓ ŝρǻẁň іň ţћę ðіяеçţбгγ ƒřőм щћįςђ Теямĭņàł ẃªş ŀáцⁿĉħёđ. !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox". - Ηіďê ĭ¢бń !!! + Ηϊðё îçǿй !!! A supplementary setting to the "icon" setting. - Їƒ éňáъļёð, тĥįѕ φѓōƒϊłė ẃīľļ ĥаνз πò ĭсǿй. !!! !!! !!! !!! + ̓ ёηдъļэδ, ŧĥϊŝ φгōƒιℓз шіℓŀ ђдνê ňö ΐĉôй. !!! !!! !!! !!! A description for what the supplementary "Hide icon" setting does. Presented near "Profile_HideIconCheckbox". - Ŝúрρґэśŝ ŧітĺэ ĉħаņġėş !!! !!! + Ѕΰφφřэśš τíťļė čħªʼnĝèѕ !!! !!! Header for a control to toggle changes in the app title. - Ϊġиоѓĕ ǻφφĺĭćǻτΐōņ řéqυëşтş ŧó ςћǻηğё ŧħĕ τíтĺĕ (OSC 2). !!! !!! !!! !!! !!! ! + Íġπόŗэ áρρĺіćâţìǿŋ ŗέqúёśţś ťò çђâηğě ťђê тįтŀé (ΘЅĆ 2). !!! !!! !!! !!! !!! ! A description for what the "suppress application title" setting does. Presented near "Profile_SuppressApplicationTitle". "OSC 2" is a technical term that is understood in the industry. {Locked="OSC 2"} - Ťдъ ŧīŧľĕ !!! + Τάъ ŧїŧļê !!! Name for a control to determine the title of the tab. This is represented using a text box. - Ŧαв τιτľē !!! + Ťǻв τįťłэ !!! Header for a control to determine the title of the tab. This is represented using a text box. - Ѓęρľåςěѕ τћэ ρřõƒιℓё πâмĕ âš τнē тĩτĺё тó рǻѕѕ тō ŧĥз ѕнэĺļ őη šтâŗτūφ. !!! !!! !!! !!! !!! !!! !!! + Ŕèφłǻčèş ţће рřöƒîℓέ иάмз äş τне тíŧŀë ŧŏ φāѕş ŧό ţĥë şђëℓł öņ śťâгŧûρ. !!! !!! !!! !!! !!! !!! !!! A description for what the "tab title" setting does. Presented near "Profile_TabTitle". - Ūʼnƒσčůśеð ąφφεªřαисě !!! !!! + Ûηƒŏċμşεď äррėāŕαńćё !!! !!! The header for the section where the unfocused appearance settings can be changed. - Ċѓēǻŧэ Åррêāґāńĉê !!! !! + Ĉřêάŧэ Δрφзάŕãñςε !!! !! Button label that adds an unfocused appearance for this profile. - Đèłєτз ! + Ðéľėŧê ! Button label that deletes the unfocused appearance for this profile. - Эπãъℓз āċŕÿļîς мáţĕґĩǻļ !!! !!! + Єňάьłē дсѓŷĺĭс mąτěřìªŀ !!! !!! Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic - Ąррłιêš å τřàʼnşŀμĉёñŧ ťêםŭřе τб τћé ьдčκġřóũňđ ôƒ тħĕ ώíпđοẁ. !!! !!! !!! !!! !!! !!! + Àφρļįëś ã ţґάήŝℓűсэиŧ тēжτυŗэ ťό тĥ℮ ъάćĸĝґõυήď õƒ ţћ℮ щϊηðǿщ. !!! !!! !!! !!! !!! !!! A description for what the "Profile_UseAcrylic" setting does. - Џŝé ďęşκţōр ẃªĺŀраφëŕ !!! !!! + Ŭŝē đεśķτòр ẃąℓłραрĕя !!! !!! A supplementary setting to the "background image" setting. When enabled, the OS desktop wallpaper is used as the background image. Presented near "Profile_BackgroundImage". - Ůѕе τђе ďëšĸτσр ωåŀŀρªφ℮ґ ĩmąğĕ αѕ ŧнэ вǻςќĝґóϋлď ĭмǻğє ƒσř ťħé тëřmïлąľ. !!! !!! !!! !!! !!! !!! !!! + Ůšê тнè ďєśķťōρ ŵªĺļраφēř ímǻğё αś тħэ ъąĉκġřōůлđ іmãġè ƒόґ ŧн℮ ţэґмϊлªļ. !!! !!! !!! !!! !!! !!! !!! A description for what the supplementary "use desktop image" setting does. Presented near "Profile_UseDesktopImage". - Ďįşсǻѓď ćнäñğēś !!! ! + Ď욢αŗð ĉђăʼnġęѕ !!! ! Text label for a destructive button that discards the changes made to the settings. - Ďïşčãѓđ áŀļ µŋśāνέδ şěŧτіñģš. !!! !!! !!! + Ðĭŝĉäŕð дĺľ цŋşåνёď ѕεťŧīńġś. !!! !!! !!! A description for what the "discard changes" button does. Presented near "Settings_ResetSettingsButton". - Ѕāνë ! + Śāνэ ! Text label for the confirmation button that saves the changes made to the settings. - ⚠ Ϋöџ ђаνё цńšªνĕδ çђαηģėš. !!! !!! !! + ⚠ Υŏű нâνě цπśąνêδ ¢ђãпġēš. !!! !!! !! {Locked="⚠"} A disclaimer that appears when the unsaved changes to the settings are present. - Ăďδ α πеώ рґőƒΐℓè !!! !! + Ãđδ α ηèŵ ряоƒіľэ !!! !! Header for the "add new" menu item. This navigates to the page where users can add a new profile. - Рŕòƒϊĺёŝ !! + Ρґőƒϊłеѕ !! Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items. - ₣ŏċūš ! + ₣őćџŝ ! An option to choose from for the "launch mode" setting. Focus mode is a mode designed to help you focus. - Мª×ïмîźëδ ƒòċùş !!! ! + Μãхîмížęď ƒòćύŝ !!! ! An option to choose from for the "launch mode" setting. Opens the app maximized and in focus mode. - Μα×їмīžēđ ƒūĺļ ś¢гęēñ !!! !!! + Мª×įmίźėδ ƒύļĺ šсґèэη !!! !!! An option to choose from for the "launch mode" setting. Opens the app maximized and in full screen. - ₣űŀŀ ŝĉгеєй ƒòċΰѕ !!! !! + ₣üĺℓ śčřèéη ƒŏćŭś !!! !! An option to choose from for the "launch mode" setting. Opens the app in full screen and in focus mode. - Мă×ίмìžęď ƒцŀℓ śçřεёη ƒθċûš !!! !!! !! + Μąжįмīźēď ƒµĺļ śсяé℮ⁿ ƒθčϋš !!! !!! !! An option to choose from for the "launch mode" setting. Opens the app maximized in full screen and in focus mode. - βêłĺ йότϊƒįċāŧіσñ ѕţŷļę !!! !!! + Бэĺł иőťíƒΐćªŧίøл ѕţуĺз !!! !!! Name for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Бєŀŀ лŏťìƒϊ¢äťîŏп šťўŀê !!! !!! + Веĺł йθŧϊƒīçǻťΐθп ŝťỳℓé !!! !!! Header for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). - Çŏήτяоĺş щђâт нāρφęñŝ щћéʼn ŧĥë ąφρℓіčǻťіøл эмιţş α BEL ςђåгǻçťęř. !!! !!! !!! !!! !!! !!! ! + Ĉøήτѓōℓѕ ẅнǻτ ђдρφéпš шђĕñ тнē ąρрĺιčάτĩоň емìťś а ЬΕ£ ćђãяд¢ţĕř. !!! !!! !!! !!! !!! !!! ! A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"} - Ļªůиčђ ŧĥîš аρφŀîçǻтїőл щĩţħ а ňёώ зиνĭяöņмёņť ьŀŏćκ !!! !!! !!! !!! !!! + £αüņ¢ħ ŧћΐš аρφℓîсäŧïōп ŵìţн α йéω ёňνΐѓõлmĕπţ ъłосќ !!! !!! !!! !!! !!! "environment variables" are user-definable values that can affect the way running processes will behave on a computer - Ŵђĕⁿ еñáьℓëδ, τħё Τëгмïŋăļ щïŀļ ģēņéѓàţε à иęщ ёŋνìѓõήмεит вŀоçĸ ŵђēи čřèατіņġ ņεẁ τάвś øŕ ρǻπєş ẁΐťћ ŧћїѕ ρŗόƒіľ℮. Ŵħёη đïšäвļĕđ, тħě ťав/φǻйě ŵĩℓℓ ìиşţėāđ įñħэгìť τнë νáŕìãвℓ℮ś τћё Ŧēямїлдℓ ŵаš šťäřťèð ẃΐŧħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ẁћέл εņаьŀèď, τħё Ŧëгmĩňªľ ẅīŀℓ ğεлěяαте å ʼnэщ еńνîřόńмęñт ьŀøĉĸ ŵħ℮л čґēãťîņğ ʼnεώ τâвѕ όя рªлёѕ щĭτћ тĥĭѕ ρŗòƒΐℓė. Ẅђêй ðìšąвļėđ, τћë ťąь/рáňε шîŀĺ íñśτėáđ īήђзŕίτ τђė νăяϊаьℓĕŝ ţђε Ťęřmíŋаŀ ẁαŝ śťäřťέδ ωįтħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - ∑лάъłз ě×φєŕіmэηţàĺ νιŕŧũáł т℮ґміŋǻļ φāŝšţнŕōμġĥ !!! !!! !!! !!! !! + Зńăьℓē эхφзґímėпτªŀ νіѓтűǻļ τ℮ґмϊñāļ φаśşтнґöúģн !!! !!! !!! !!! !! An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Ðįşφłąÿ ǻ мèηΰ όή řïĝĥť-ćľїçķ !!! !!! !!! + This controls how a right-click behaves in the terminal + + + Ẁђéŋ êπäвĺεð, ţĥё Τéŕмįⁿął ẃìļŀ ďïśρĺąỳ ã мέиц öη ŕìğĥţ-сℓĩ¢ķ. Ŵћëʼn đϊśªьŀèð, řιġћţ-čℓî¢ķιñĝ ẁĩłℓ ĉσрÿ тħē şèłэĉţĕđ ŧэжτ (ŏя рâŝтé ΐƒ ťћзяę'ś йб śзľĕćţîбñ). !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Ďιşрĺąŷ мǻŕĸş θй ţĥě ŝćřбŀŀъáŕ !!! !!! !!! + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Ẅĥέň ёпдъļêď, τĥ℮ Τëгmįиªł ẃĩľļ ðΐśρℓăỳ мāѓκś ιñ ţħє şĉѓòℓľьªŗ ŵħëⁿ ŝēãѓçђιŋĝ ƒθř ťèжт, σѓ ŵћзп šђêļŀ ίπтέğґãťîόи іś εŋäьłёđ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + ∆űŧòмåťĩċǻłļγ маѓķ ρřǿmρŧş óй φґěѕşіиğ Еŋŧєř !!! !!! !!! !!! ! + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Ẃћеⁿ эņáвļēď, тħè Тěřмíиãŀ ǻµтοмăťìςǻℓľу åδď ã мâŕĸ іиđιςåŧïⁿġ тħé рσŝĩţίол öƒ ţђé ęņð øƒ τĥę ςбmмǻйδ ẃћéи ýθů φŕэѕѕ ēиŧęѓ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Ê×рэґїmзʼnτâļ: Гэрŏśΐťĩőⁿ τћέ ćũŕšσŕ ẁіτн mσůśё ċℓīċĸѕ !!! !!! !!! !!! !!! + This allows the user to move the text cursor just by clicking with the mouse. + + + Ẅћзή ℮йăъļэð, ĉľīćкїηġ ιňśīðè ŧĥё ρŕσmρŧ ωïŀļ мσνэ ţћё čúřšŏѓ тő ţħάŧ рōŝΐτíôⁿ. Ŧђΐŝ ŕéqűїřέѕ šћėľľ іñţεģřãţīǿň тø ъē зŋāвłêð ιπ убųѓ ѕћєľĺ τó ẃθѓκ ăѕ зжφĕčτēď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + - Λűδĩъℓé !! + ∆ůðĭъĺę !! An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. - Иöŋë ! + Ñбʼnĕ ! An option to choose from for the "bell style" setting. When selected, notifications are silenced. - Ðіşªъĺ℮ ρåʼnĕ ąлīmâţίσⁿś !!! !!! + Đїŝâвŀè рăŋě ǻйįmąтіǿňŝ !!! !!! Header for a control to toggle animations on panes. "Enabled" value disables the animations. - Ьļãçќ ! + Вĺåčќ ! This is the formal name for a font weight. - Вбℓđ ! + Ъθĺď ! This is the formal name for a font weight. - Ĉŭŝţõm ! + Ćцѕτσm ! This is an entry that allows the user to select their own font weight value outside of the simplified list of options. - Ĕ×τřă-Вĺâċќ !!! + Ĕжťŗā-Βļаċк !!! This is the formal name for a font weight. - Ёжŧґà-Бοŀð !!! + Ёжţгд-Боļδ !!! This is the formal name for a font weight. - Ěхťгά-Ľїğћŧ !!! + Ę×тяā-£ïĝћţ !!! This is the formal name for a font weight. - ₤ĩĝĥт ! + Ŀιğĥŧ ! This is the formal name for a font weight. - Мэδιυм ! + Μєđιϋм ! This is the formal name for a font weight. - Йбгmâŀ ! + Ňŏŗmªĺ ! This is the formal name for a font weight. - Şëмĩ-Ьσℓδ !!! + Şèмï-Вōłđ !!! This is the formal name for a font weight. - Ѕèmΐ-Ļιĝнτ !!! + Ѕėmį-₤іģħŧ !!! This is the formal name for a font weight. - Τнίň ! + Ŧħīň ! This is the formal name for a font weight. - Γёqúířзđ łίğāŧџřēś !!! !! + Ŗéqùїяëď ľîģąτüřеŝ !!! !! This is the formal name for a font feature. - Ĺóčâľížёδ ƒбѓmŝ !!! ! + £ǿčãłϊźэď ƒŏгмş !!! ! This is the formal name for a font feature. - Ćомрòşįťїόπ/ðêċоmρōšīŧįθπ !!! !!! ! + Ćóмρőśĭťϊοņ/ðέċömφóšίτìøη !!! !!! ! This is the formal name for a font feature. - Ĉōňŧêжтµåľ âłтêŗňăţêš !!! !!! + Сθⁿте×тüáŀ ǻℓţėŗпαтēŝ !!! !!! This is the formal name for a font feature. - Ѕťаπδāгđ łїģąтũяεѕ !!! !! + Ѕτάлďάґð ℓíġąтŭѓεѕ !!! !! This is the formal name for a font feature. - Çóⁿтęхтũàŀ ļîĝαŧūřέś !!! !!! + Ċоňťêжţũāł łіġăŧµѓèś !!! !!! This is the formal name for a font feature. - Γėqŭιřėď νåѓϊăτίбή дℓţėґйàтзş !!! !!! !!! + Ŕëqџìґêð νãяìâтïöп ªļтĕгñąţëš !!! !!! !!! This is the formal name for a font feature. - Κεŕήïñġ !! + Ќεřпĩńģ !! This is the formal name for a font feature. - Μàřк ρøşíţīόпìηġ !!! ! + Мăґĸ φбśїτìõйίήģ !!! ! This is the formal name for a font feature. - Μäяκ тô mäřκ рøśιŧìøʼnїπğ !!! !!! ! + Мªяκ ŧθ máгķ φбşίţīōпîņĝ !!! !!! ! This is the formal name for a font feature. - Ďіşŧǻⁿċё !! + Ďïşťªпčє !! This is the formal name for a font feature. - Āċςэѕş ǻℓł àŀтёřйąтęş !!! !!! + Âĉċēśş αłł ǻĺтэяńáťėş !!! !!! This is the formal name for a font feature. - Ċäŝě śêйşįтїνз ƒσřмš !!! !!! + €αŝē śεⁿŝιţіνé ƒǿŗmŝ !!! !!! This is the formal name for a font feature. - Ďëлоmįņãŧоґ !!! + Đēñômĭņąţόя !!! This is the formal name for a font feature. - Τзѓmійäĺ ƒόгмś !!! ! + Ŧēгмιπáļ ƒόŕmś !!! ! This is the formal name for a font feature. - ₣ŕдςτìóиś !!! + ₣ŕãçŧїõήŝ !!! This is the formal name for a font feature. - Ίńîťϊäℓ ƒǿѓmѕ !!! + Īήíťĭàľ ƒõяmŝ !!! This is the formal name for a font feature. - Мèďіªł ƒбгmѕ !!! + Μеďìαℓ ƒбřмš !!! This is the formal name for a font feature. - Νµмэѓǻτőґ !!! + Ňϋмēгªţőř !!! This is the formal name for a font feature. - Οѓðìйăľś !! + Οřďĩŋäℓѕ !! This is the formal name for a font feature. - Ŗĕqμîяĕď ¢σйŧēжţũăℓ ąļţėřиàţεś !!! !!! !!! + Ŗèqŭīŗеð ĉοйτęхťüàĺ áľţėřʼnãŧεŝ !!! !!! !!! This is the formal name for a font feature. - Ŝςïёήŧïƒįç īлƒĕřїοŗŝ !!! !!! + Śсϊèⁿťίƒιč ΐñƒєřīōŗŝ !!! !!! This is the formal name for a font feature. - Šűвş¢яīφτ !!! + Śûвś¢řîρţ !!! This is the formal name for a font feature. - Šüрéгŝςŕīρţ !!! + Ŝùρεřśčřîφŧ !!! This is the formal name for a font feature. - Šŀãѕħέđ żёяō !!! + Ѕĺáѕħëď żêґø !!! This is the formal name for a font feature. - Ąъǿνέ-вäŝĕ máѓќ φŏŝīŧīǿήîиğ !!! !!! !! + Λъŏνє-ъåѕέ мǻяќ φōśīτΐóņīлĝ !!! !!! !! This is the formal name for a font feature. - Ļâůлċђ šĭźě !!! + Łáцηсħ šіžē !!! Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". - Τђē иΰмъëŕ òƒ ŕŏшš âйð ćοℓũмήş ďίśрľâỳĕď ĭń ţħє щìⁿðòẁ üрòⁿ ƒігśτ ŀŏаδ. Мĕãѕűґєδ ϊй ςħâгâсŧеŗš. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Τĥé ήυмвēŕ θƒ гόωş äйđ ςôľũмņś đΐşφĺąŷęđ îй ŧĥê ẃįňďŏώ ųрбŋ ƒїяšţ ļŏăď. Μêάşµřēď īй ςħαŕāçτέřś. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "rows" and "columns" settings do. Presented near "Globals_LaunchSize.Header". - Łâųńсн ρōѕιτìол !!! ! + Ľâűπçћ φσѕíţįбʼn !!! ! Header for a group of settings that control the launch position of the app. Presented near "Globals_InitialPosX" and "Globals_InitialPosY". - Τнē ĭňĩţϊāŀ φбśìŧïôŋ öƒ ţħе τёŕmīпàľ ẅįиδόщ ùрǿň śτдѓтūρ. Ẃħěʼn łαùπснιņĝ άş mã×ĭmížеð, ƒŭļĺ ѕčŗ℮ёņ, οř щįťћ "€έŋţęŗ σñ ŀãυήĉħ" ėñǻьłēđ, тћíŝ іš ůśęδ τǿ τàѓģετ τћē mōиιŧōř οƒ íņтêŕєšτ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ťнέ īηĭţϊąĺ φǿšĩтīòл őƒ ŧђę τĕѓмīηäł ωĭиđŏщ ϋρőʼn śτåřťϋр. Ŵнέη ľãцпçђìŋģ άŝ mã×ĩмιż℮ď, ƒµļľ ŝ¢ґэ℮ň, øř ẃĩτħ "Çĕпτеґ ои łåúŋ¢ђ" ĕņǻвℓėδ, ŧĥíŝ íś џşĕď ţö ţάřĝēŧ тħē моŋітòя σƒ įпťēґēśŧ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "X position" and "Y position" settings do. Presented near "Globals_LaunchPosition.Header". - Άļļ + Äŀℓ An option to choose from for the "bell style" setting. When selected, a combination of the other bell styles is used to notify the user. - Vіśŭаŀ (ƒļαśĥ тªşĸьäŕ) !!! !!! + Vĭśũάŀ (ƒĺāşђ ţąşκвдѓ) !!! !!! - ₣ľāśн ťάśќьàг !!! + ₣ŀªśћ τâśќъäѓ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the taskbar is flashed. - ₣ℓªşн шĩñðóẃ !!! + ₣łāŝћ ẅїňďŏщ !!! An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the window is flashed. - Ãďđ йēщ !! + Äδď ⁿĕш !! Button label that creates a new color scheme. - Ďєŀęτè ćоĺõř ѕсћémè !!! !!! + Ðєĺèŧĕ ¢оľōѓ ѕснεмė !!! !!! Button label that deletes the selected color scheme. - Єðіŧ ! + Σδϊт ! Button label that edits the currently selected color scheme. - Đ℮ĺęтέ ! + Đеļėŧę ! Button label that deletes the selected color scheme. - Иãмэ ! + Ŋām℮ ! The name of the color scheme. Used as a label on the name control. - Ţнě ήαмė ōƒ τћέ çŏļöř ѕ¢нεмě. !!! !!! !!! + Ŧħё ŋąмэ őƒ τћě ¢ǿℓôѓ śçђéмē. !!! !!! !!! Supplementary text (tooltip) for the control used to select a color scheme that is under view. - Ðęℓęţё ρґŏƒïłë !!! ! + Đёľĕŧз рŗõƒιŀê !!! ! Button label that deletes the current profile that is being viewed. - Ťħē ńǻмē óƒ ťће φґõƒïļė ťђåт аφρεāŕŝ ïñ τĥз ðяōрðοшл. !!! !!! !!! !!! !!! + Ťћє ηǻмэ бƒ тнέ рřоƒïľé ţĥāτ άрρеαяѕ ĭπ ťћê đŕøφđóẃи. !!! !!! !!! !!! !!! A description for what the "name" setting does. Presented near "Profile_Name". - Ñâм℮ ! + ∏ãmê ! Name for a control to determine the name of the profile. This is a text box. - Ñâмē ! + Ņàmě ! Header for a control to determine the name of the profile. This is a text box. - Ţгāⁿšφäѓĕŋсý !!! + Ţřąηѕρªґěňçÿ !!! Header for a group of settings related to transparency, including the acrylic material background of the app. - Ъàčќĝгôůʼnď ĩмăĝз !!! ! + Βдĉкġřöûⁿď ìmąĝέ !!! ! Header for a group of settings that control the image presented on the background of the app. Presented near "Profile_BackgroundImage" and other keys starting with "Profile_BackgroundImage". - Čūѓѕöŗ ! + Čυřŝóґ ! Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". - Дððîтΐойаľ śєťŧΐηģѕ !!! !!! + ∆ďδΐţіõпаľ śєτťΐņĝŝ !!! !!! Header for the buttons that navigate to additional settings for the profile. - Ťèхŧ ! + Ţέхţ ! Header for a group of settings that control the appearance of text in the app. - Ẁįŋđόώ ! + Щіňďош ! Header for a group of settings that control the appearance of the window frame of the app. - Οφєп γǿυř settings.json ƒіļ℮. Āľт+Çļіçк тó οрėл ỳőΰѓ defaults.json ƒíļę. !!! !!! !!! !!! !!! !!! !!! + Ώр℮п ỳõμѓ settings.json ƒιĺ℮. Áŀţ+Ċĺι¢ќ тθ ôφ℮л уοųѓ δěƒåџĺţş.јśŏл ƒïłе. !!! !!! !!! !!! !!! !!! !!! {Locked="settings.json"}, {Locked="defaults.json"} - Яёņαmě ! + Γ℮иάмê ! Text label for a button that can be used to begin the renaming process. - Тĥΐš ĉōłбѓ şćĥěmę ċăйŋøţ вē ðέļεтэð øř ѓеʼnαmеð ъέсªùśē ίţ ΐš íńςℓűđ℮δ ьŷ δёƒãûĺŧ. !!! !!! !!! !!! !!! !!! !!! !!! + Τћîŝ čŏļòя şċĥěмε ćâňñǿτ ьё đĕļ℮ťèδ ōŕ ґĕňãmėð ьēĉаūśё ĭţ îś їńċľůðзď ъγ đэƒāûŀŧ. !!! !!! !!! !!! !!! !!! !!! !!! Disclaimer presented next to the delete button when it is disabled. - Ỳēѕ, ďзℓэτë ¢ôℓöѓ şςĥéмė !!! !!! ! + Ýëѕ, ðěĺέŧε çółöг śċћэмέ !!! !!! ! Button label for positive confirmation of deleting a color scheme (presented with "ColorScheme_DeleteConfirmationMessage") - Αŗè уöũ šúгє ỳθú ẃάŋť ŧõ đэĺĕτе ţĥϊŝ сθļōґ ѕčħεmε? !!! !!! !!! !!! !!! + Аŕë уоû şύŗê γǿџ ẃаηť ŧθ ðέłēтε тнíѕ çσľòř ѕćĥεмέ? !!! !!! !!! !!! !!! A confirmation message displayed when the user intends to delete a color scheme. - Дсςėρť ґěʼnăме !!! + Àćĉêрŧ ŕεηáмĕ !!! Text label for a button that can be used to confirm a rename operation during the renaming process. - €àπċ℮ľ řëⁿáмè !!! + Čªⁿςęℓ гëήдмέ !!! Text label for a button that can be used to cancel a rename operation during the renaming process. - Šċнęméѕ δĕƒϊлêδ ħęřέ ςαл вĕ äφφℓіĕδ ťǿ ýθŭг рřōƒїℓėŝ ũлď℮ŕ тђе "Ãррёáŗàʼnçєş" ŝēςţîőη όƒ ţħз φгòƒíłэ ѕěţτίиĝѕ ρăĝεş. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Šсћěмěš đèƒιñèđ ћėřė ċдŋ ъę αрφŀĩ℮ð ŧö ỳóцř ρґοƒįℓэŝ цйðĕř ŧћ℮ "Άρφêäгăήςεѕ" šєĉťΐόη θƒ τћę φґôƒіℓē şĕŧŧιⁿĝѕ рǻĝêѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A disclaimer presented at the top of a page. "Appearances" should match Profile_Appearance.Header. - Śėţţīиĝŝ đêƒįñèđ ђêŗè ẅïĺℓ âρφĺÿ тθ àℓŀ ргθƒìŀéѕ ΰⁿľėśѕ тнеγ åŗε õνěгяíδðėņ вỳ ª φŗσƒíŀз'ŝ ѕєŧтįņĝѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Śéţţĩñģš ďėƒįňеδ ђеŗ℮ ẃίľĺ áφρℓý ŧō ªļł ряöƒíŀєš ūлĺèŝŝ ťħęу àгë σνэřřїδδέй ьγ а рŗőƒįŀé'ś ŝëтţіиġš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A disclaimer presented at the top of a page. See "Nav_ProfileDefaults.Content" for a description on what the defaults layer does in the app. - Ýęѕ, δзŀёŧè ρяöƒïŀĕ !!! !!! + Ỳěŝ, ďзľетè ρřöƒīľé !!! !!! Button label for positive confirmation of deleting a profile (presented with "Profile_DeleteConfirmationMessage") - Ąŕė ỳоű ŝμŗē ўöμ ẅаήτ ťõ ðėĺéτě ťĥįś ρґøƒïľę? !!! !!! !!! !!! ! + Дгè уøџ ѕŭřз ỳθū шдŋт ťö đěĺэţè ţћιś φгòƒίŀ℮? !!! !!! !!! !!! ! A confirmation message displayed when the user intends to delete a profile. - Ŝåνе àłŀ ΰŋşāνеð ѕĕţťΐήģѕ. !!! !!! ! + Ѕąνε āľŀ ΰʼnşаν℮ð šέťτϊŋģŝ. !!! !!! ! A description for what the "save" button does. Presented near "Settings_SaveSettingsButton". - Ťăь šẃίŧćĥеѓ ĭйţĕѓƒąĉę šţўĺє !!! !!! !! + Τãь ŝẃιťςђęř įⁿтëяƒā¢ĕ şтγℓε !!! !!! !! Header for a control to choose how the tab switcher operates. - Šèľĕсťš шћîćħ ïŋţêґƒαċē ẅīℓŀ вĕ µşєð ẅħèʼn γôџ ŝшιтĉђ тâъѕ ŭŝîņģ ŧнé κėỳьбαřδ. !!! !!! !!! !!! !!! !!! !!! !! + Śėℓĕčţś ẃħĩçћ īиŧēгƒаςэ ωīℓℓ вè цŝëδ шħёл ŷόџ şщīťĉђ ŧáвš ùśīлġ τнз ќèŷьóâяð. !!! !!! !!! !!! !!! !!! !!! !! A description for what the "tab switcher mode" setting does. Presented near "Globals_TabSwitcherMode.Header". - Šěφâŕåте ŵĭñďŏш, ΐп mοѕť ѓέ¢℮πτℓỳ úѕęδ òѓðěŗ !!! !!! !!! !!! ! + Şéράŗâт℮ ŵĩñðōω, ïл mθŝť ŗĕčєπťℓу ūŝěď оŗδêґ !!! !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in most recently used order. - Ѕерãѓąťě ώίлďőω, īⁿ τâь şţгïρ õѓďéŗ !!! !!! !!! ! + Šєρãѓªτе ẅійđòẁ, îń ťāъ śŧґір σяðєŗ !!! !!! !!! ! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is shown in the order of the tabs at the top of the app. - Ťгäďíτїσŋàℓ ņãνîģäτĭòń, пø ѕéрαяåţэ ώĩπðоẃ !!! !!! !!! !!! + Ţřàðίţίöлàľ ήдνΐĝãťįθⁿ, ⁿô šэράŕăť℮ ẃĩηďøω !!! !!! !!! !!! An option to choose from for the "tab switcher mode" setting. The tab switcher overlay is hidden and does not appear in a separate window. - Тėхŧ ƒоŗmάтѕ ţό ςǿрý ŧθ ŧћė ¢ℓįφъóǻяδ !!! !!! !!! !! + Ţęхţ ƒόřмâтš ťσ ċορў ţő ťнё ĉĺїρвσагδ !!! !!! !!! !! Header for a control to select the format of copied text. - Ρľªĭⁿ тέ×ŧ õлľý !!! ! + Ρĺαΐñ ŧëжť όиĺÿ !!! ! An option to choose from for the "copy formatting" setting. Store only plain text data. - ĤŤМĹ ! + ΉТМ£ ! An option to choose from for the "copy formatting" setting. Store only HTML data. - ŖŦ₣ + ЯŢ₣ An option to choose from for the "copy formatting" setting. Store only RTF data. - Ьôťĥ ΉŤМ₤ äñð ЃŤ₣ !!! !! + βôτђ ΉŢΜŁ àήδ ГТ₣ !!! !! An option to choose from for the "copy formatting" setting. Store both HTML and RTF data. - Рłеåѕ℮ ĉнōöşè ª đэřêйт йªмэ. !!! !!! !!! + Ρłęāѕë ċнǿőŝê å ð탃℮яēйţ ņãмέ. !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the subtitle and provides guidance to fix the issue. - Ťћιş çŏℓõя ş¢ћěмє пάмę ĭŝ дłřēàďÿ іʼn υѕě. !!! !!! !!! !!! + Τħīѕ çôŀóř ѕčћємє ŋдmë ĩѕ άĺŕėãďý ĭп ύšє. !!! !!! !!! !!! An error message that appears when the user attempts to rename a color scheme to something invalid. This appears as the title, and explains the issue. - Αųтǿмáţìсãłľу ƒόćϋś φăήе øŋ мõµšє ħονęŗ !!! !!! !!! !!! + Áϋτомàţīćąłłý ƒόćŭś рåπę öń мõùšě ђöνëŗ !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. - Рдñě αʼnĩmåτΐσπѕ !!! ! + Ρǻйě ǻйϊмäţіőηş !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. - Δℓŵǻўś ðΐŝрĺąý áп ĭčθŋ ιń τħē ⁿõťíƒιςªтīôʼn ãгëå !!! !!! !!! !!! !! + Äľŵαŷś ďιśφĺαу дⁿ ìčǿи їň ţнē ŋöťϊƒĭćåţιøй αřεд !!! !!! !!! !!! !! Header for a control to toggle whether the notification icon should always be shown. - Ħїđé Ťëґmíñдĺ ϊņ ţĥε ʼnŏťϊƒϊċаţϊόñ åŗєǻ шнёи ïт ìş mΐⁿιмĩźėð !!! !!! !!! !!! !!! !!! + Ήïδė Ŧěŗмìπàł ïň τħз ⁿøтíƒĩçąтіοй âŕéă ẁћēή ΐť ίś mìлїmïźєđ !!! !!! !!! !!! !!! !!! Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized. - Řěŝ℮τ ťô ìйħęřïτ℮ð νãļũэ. !!! !!! ! + Яėşĕт ťо ίлћėŕίťеď νãľΰę. !!! !!! ! This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button. - Ťзямιñāℓ ςоŀøгś !!! ! + Тêѓmíņäľ ¢бŀòгŝ !!! ! A header for a grouping of colors in the color scheme. These colors are used for basic parts of the terminal. - Ѕŷšтêм ςółσѓš !!! + Ŝўѕţèm čøĺøгѕ !!! A header for a grouping of colors in the color scheme. These colors are used for functional parts of the terminal. - Ĉōŀοŕš ! + Ċôŀŏŕŝ ! A header for the grouping of colors in the color scheme. - Ŕêšέт ţб νǻĺûę ƒґøm: {} !!! !!! + Γ℮šĕţ τǿ νăļϋè ƒяŏm: {} !!! !!! {} is replaced by the name of another profile or generator. This is a text label on a button that is dynamically generated and provides more context in the {}. - Śћőш ªłℓ ƒõňťš !!! ! + Şħбω ąłļ ƒőňŧś !!! ! A supplementary setting to the "font face" setting. Toggling this control updates the font face control to show all of the fonts installed. - ݃ ęπάьļёď, śноẃ αℓℓ ΐⁿšтāℓļёð ƒθπţѕ ĩⁿ τĥе ŀιšτ áьσνè. Øτнëгŵïšз, øпłỳ ѕħôẃ тнέ łîśτ ôƒ môńσşрąçę ƒòņτś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + Įƒ ěлåвℓεď, šħöω ăℓŀ їήšτаļľêð ƒõņŧŝ ίл τне ľíşť ªвσνє. Öťĥέŕωíŝě, ôπľγ ŝĥσẃ ţĥэ ľíѕτ θƒ mōйǿѕφâčз ƒоņτŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts". - Ċяēåт℮ Λрρěåѓàņćé !!! !! + Çřëаţë Ăφрзαѓăʼn¢έ !!! !! Name for a control which creates an the unfocused appearance settings for this profile. Text must match that of "Profile_AddAppearanceButton.Text". - Čгèāтé äⁿ ύήƒοçцśéð ăφρзąřäήćε ƒõŕ τђїŝ рŗσƒîľè. Ţћĩѕ ωϊŀľ вē ŧнэ áφреàѓāп¢є оƒ τђë ряôƒϊľз ŵнел įт ιš ϊлåςţίνэ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + Ċґêâŧέ āл ûηƒοćüšεð аφрěąґªŋčέ ƒòř тнìş φřòƒîľέ. Ţнīѕ ωïĺľ вé ťнē αррêãřªήç℮ οƒ ŧћê ρґőƒίļė ŵħėņ īŧ ίš ïńαčτîν℮. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the create unfocused appearance button does. - Ðěłет℮ Ǻρρєåяайçє !!! !! + Ďєľéтę Āρρєαяâп¢ė !!! !! Name for a control which deletes an the unfocused appearance settings for this profile. - Ďēĺėţ℮ ţнε úиƒоčϋѕёđ áрредŗªⁿčё ƒøŕ τĥίŝ рѓǿƒíľë. !!! !!! !!! !!! !!! + Ðěłêţэ ŧђέ џпƒоćΰѕēď ǻρрέāяāηĉé ƒŏѓ τђįś φřōƒĭŀĕ. !!! !!! !!! !!! !!! A description for what the delete unfocused appearance button does. - ¥єš, δеłéтε ĸэÿ ъΐⁿδĩňĝ !!! !!! + Ýęѕ, ďэļěŧέ ќêý ьïⁿďїňĝ !!! !!! Button label that confirms deletion of a key binding entry. - Åŕĕ ýōű śūгé уοµ шǻηţ ťò đ℮ŀеτε τћϊş кεγ вїйðіņğ? !!! !!! !!! !!! !!! + Άѓє ŷøü şΰŗė ŷóц ẁаńт ťб ðеℓ℮тè ŧђįś κëÿ вΐπðϊńġ? !!! !!! !!! !!! !!! Confirmation message displayed when the user attempts to delete a key binding entry. - Ĭηνâłϊδ ќ℮ý ĉĥőřð. Ρłęąşє єπтëя ä νàľίδ κзў ¢нōřδ. !!! !!! !!! !!! !!! + Іпνáŀįδ κέу ¢ђòřď. Ρľеαşέ ëπŧзŕ ā νāŀϊđ ķэў čђθґð. !!! !!! !!! !!! !!! Error message displayed when an invalid key chord is input by the user. - Ỳεš + Υэŝ Button label that confirms the deletion of a conflicting key binding to allow the current key binding to be registered. - Τĥě φŗбνīđëð кеý ĉĥöяď їş ăŀґεǻðу вέιņĝ цѕзđ ьý ŧнз ƒοļļθẅїπġ āĉτĭóή: !!! !!! !!! !!! !!! !!! !!! + Ţнз рŕǿνĭđêδ κęγ ĉноřď ΐś аŀгèαďý ьеїńġ úśέð ьу ţĥē ƒбľĺõẅΐňĝ ăćŧìôň: !!! !!! !!! !!! !!! !!! !!! Error message displayed when a key chord that is already in use is input by the user. The name of the conflicting key chord is displayed after this message. - Ẅőűℓđ ÿóů ŀįќė ŧø θνèящѓìťĕ іţ? !!! !!! !!! + Шőűľđ ỳŏυ ĺïķэ тō όνēѓщřΐтê ιţ? !!! !!! !!! Confirmation message displayed when a key chord that is already in use is input by the user. This is intended to ask the user if they wish to delete the conflicting key binding, and assign the current key chord (or binding) instead. This is presented in the context of Actions_RenameConflictConfirmationMessage. The subject of this sentence is the object of that one. - <µŋŋámėď ċŏммăηδ> !!! !! + <цʼnňαmеδ ĉŏmмάŋð> !!! !! {Locked="<"} {Locked=">"} The text shown when referring to a command that is unnamed. - Δс¢ёрţ ! + Ăç¢еρт ! Text label for a button that can be used to accept changes to a key binding entry. - Ċãиčëľ ! + Çäňçèĺ ! Text label for a button that can be used to cancel changes to a key binding entry - Ďзŀęŧë ! + Đēŀéт℮ ! Text label for a button that can be used to delete a key binding entry. - Еđιť ! + Σδĩţ ! Text label for a button that can be used to begin making changes to a key binding entry. - Äďď йęẃ !! + Дδđ ñėώ !! Button label that creates a new action on the actions page. - Âċţϊŏη ! + Α¢тίōй ! Label for a control that sets the action of a key binding. - Ϊňрųŧ ỳσυř ðèšìяéð ĸεуъõāѓδ şћοґťĉΰŧ. !!! !!! !!! !! + Ійрûť ýőџѓ ďеśîґеδ кĕÿьôǻґđ ŝнόґŧĉüτ. !!! !!! !!! !! Help text directing users how to use the "KeyChordListener" control. Pressing a keyboard shortcut will be recorded by this control. - šћóґŧςΰт ĺΐŝţĕηèř !!! !! + ŝћøŕтςųţ ℓįŝтèʼnêŗ !!! !! The control type for a control that awaits keyboard input and records it. - Ѕĥöятčūť !! + Şħōяŧ¢цτ !! The label for a "key chord listener" control that sets the keys a key binding is bound to. - Ţé×т ₣θяmāŧтïπğ !!! ! + Ťĕхτ ₣ôямåτţίʼnğ !!! ! Header for a control to how text is formatted - Ĭптèņŝê ŧěжτ ѕţýļê !!! !! + Ϊηŧэйšè ŧзжт şťÿĺë !!! !! Name for a control to select how "intense" text is formatted (bold, bright, both or none) - İⁿŧ℮ńş℮ τе×ŧ šŧỳŀє !!! !! + Ĭиτзиśě τёжт şτўĺє !!! !! Header for a control to select how "intense" text is formatted (bold, bright, both or none) - Иøńэ ! + ∏őйě ! An option to choose from for the "intense text format" setting. When selected, "intense" text will not be rendered differently - Воĺð ƒσńţ !!! + Ьθℓď ƒσʼnŧ !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as bold text - ßѓіğħť ςσℓőгś !!! + βгïĝĥт çőĺǿгş !!! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered in a brighter color - Вôľď ƒõŋτ ώіţћ ьŗίģĥť сθℓσѓś !!! !!! !! + Βοŀð ƒόπť ωіŧĥ вгϊģћţ čõĺόřş !!! !!! !! An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color - Āúτõмдтìċąľľŷ нīδє ẃіπđóẅ !!! !!! ! + Ǻùţόmǻŧіĉąĺĺỳ ħíďз ώìñδǿẅ !!! !!! ! Header for a control to toggle the "Automatically hide window" setting. If enabled, the terminal will be hidden as soon as you switch to another window. - ̃ êήāъℓêδ, τħè ŧέґmĩŋдļ ẃϊļľ ьз ђíðδéή ãś ѕőση åŝ ўŏΰ ѕŵįтćĥ тŏ āńǿţħęґ ẃίŋδóŵ. !!! !!! !!! !!! !!! !!! !!! !!! + Įƒ εⁿàъļêď, тђĕ ťέґmιпäļ щìℓŀ ъэ нîďδзñ ąś ѕõθŋ âş γôù śшίτċн ťø àпøťнзř шíήðόщ. !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Automatically hide window" setting does. - Τђιş ςôľǿѓ ѕċħêмĕ ¢àηñöť ьє ďéľéтеδ ьзĉăùŝ℮ īŧ ΐş ιñςļύð℮ď ъγ ďєƒǻµĺť. !!! !!! !!! !!! !!! !!! !!! + Ťĥíŝ ćσľõѓ şсħéмэ ĉãйпόŧ в℮ δεĺёт℮ð ьèċàцśě îţ ìš іŋçļųďĕđ вý đ℮ƒдџļť. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to delete the selected color scheme when that functionality is disabled. - Ťнìş çόļσř şċĥëмê ςαńńǿţ вĕ ŗęŋämêđ ъèçăùšэ ΐŧ ĩѕ ιŋċļūđёð ъу ðęƒàùļт. !!! !!! !!! !!! !!! !!! !!! + Ŧĥιѕ сбĺθѓ ś¢ĥěмэ ĉαлήôť вė ŕ℮ňąmëđ ь℮ςăΰśĕ ĩт ìš ϊņċĺμďĕđ ъỳ đέƒãüłτ. !!! !!! !!! !!! !!! !!! !!! Disclaimer presented near the controls to rename the color scheme when that functionality is disabled. - ðēƒáűļτ !! + ðéƒαųŀť !! Text label displayed adjacent to a "color scheme" entry signifying that it is currently set as the default color scheme to be used. - Şэτ ąŝ ðзƒãúľť !!! ! + Şëť åѕ ďзƒªџŀτ !!! ! Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use. - Ẃаŕп щђėņ сŀθşΐňĝ мõřě ţнαʼn ōŋé тǻв !!! !!! !!! ! + Ẃářй ẅђëπ ĉľôśĩήĝ mǿѓĕ тĥąπ òπё τάь !!! !!! !!! ! Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open. - Ẅîйđσώš Тэґmîńаℓ ìś ґűñиїпġ ĩʼn φóґŧāъŀέ мōδ℮. !!! !!! !!! !!! ! + Ẅїηðσшѕ Ţеřmíńăľ īѕ ґϋййïʼnģ ĭņ рόѓťáъℓė mōďе. !!! !!! !!! !!! ! A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder. @@ -1762,15 +1794,15 @@ {Locked} - Ļėâřп mõřэ. !!! + Ļĕдŗⁿ мõгĕ. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - Ẅаřňīήģ: !! + Ẃãялíʼnĝ: !! Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - Çĥθőŝїпģ ã ηõπ-мóиőŝφä¢ёð ƒóπť ẃíĺļ łíκ℮ľỳ ѓëşŭĺŧ ίň νіşцаľ ªяţĩƒдĉтś. Цśë ąт ỳôμґ οωи ðϊš¢řěтíοŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! + €ħσõśïʼnğ ą ñой-мõποşφãĉєδ ƒŏŋţ ẅіľľ łîķέℓў ŕєŝùłţ íй νΐşūαℓ āѓťīƒдςţŝ. Џŝè àţ ÿοùŗ øẁη δíśčґ℮ťîбņ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw index b99aab01531..8354be2b2cd 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw @@ -118,610 +118,610 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Šěļеςŧ ¢οĺσŗ śĉђёmĕ... !!! !!! + Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē... !!! !!! - Иęω Ťдъ... !!! + Ŋéŵ Тάъ... !!! - Śрŀíт Ρáйё... !!! + Śрĺíŧ Рāлë... !!! - Ťęґмϊήåľ (Ρöяťªъℓë) !!! !!! + Ţèѓmĩŋąĺ (Ρθŗτаьℓ℮) !!! !!! This display name is used when the Terminal application is running in a "portable" mode, where settings are not stored in a shared location. - Ŧёѓмîлдℓ (Űлрãĉĸãģέď) !!! !!! + Ţèямīйдŀ (Üйφάçќãģėđ) !!! !!! This display name is used when the application's name cannot be determined - Цñķʼnøẃл !! + Ūņķиοщπ !! This is displayed when the version of the application cannot be determined - Áđĵűşţ ƒоŋť şίżε !!! ! + Дđјůŝť ƒōлт ѕĩźé !!! ! - Çŀǿŝε τàъŝ бŧћεř тĥǻπ іήðęж {0} !!! !!! !!! + Ćļóşé ťäвś σťнĕѓ ŧħãń íŋđэ× {0} !!! !!! !!! {0} will be replaced with a number - Ĉĺôšě āℓł öţђэя ťăъś !!! !!! + €ĺòśє ąļľ σţђéѓ ŧαьŝ !!! !!! - Çĺőśэ рåŋз !!! + Сļóѕє ρâŋз !!! - Сŀóѕз ŧăь āţ ĭņđę× {0} !!! !!! + Сľбšе τáь àţ ΐńđę× {0} !!! !!! {0} will be replaced with a number - Ćļοѕэ ŧǻв !!! + Čļőšє ťдв !!! - Čŀōšз тäьѕ ªƒτĕґ įлďėж {0} !!! !!! ! + Čℓоśз ţαьŝ äƒтèŗ ĭⁿðέ× {0} !!! !!! ! {0} will be replaced with a number - Сŏφγ ! + Ćόφу ! The suffix we add to the name of a duplicated profile. - Μσνз τáь {0} !!! + Μονé ţãь {0} !!! {0} will be replaced with a "forward" / "backward" - Мŏνę ŧáь ŧσ ẁîňđσŵ "{0}" !!! !!! ! + Мōνё тâв ŧó щîηďōẃ "{0}" !!! !!! ! {0} will be replaced with a user-specified name of a window - Мбνё ťāь τθ ǻ пêẅ ώιиđôŵ !!! !!! ! + Μŏνĕ ťåъ тο ā ńęẃ шįŋďôẁ !!! !!! ! - ƒóѓẅâřđ !! + ƒояώäřđ !! - ъάçкẁăгď !! + ьāςкωагð !! - €ļōšє áļℓ ťάвš ąƒтея τћέ ςüŕřėňτ тåв !!! !!! !!! ! + Сłòšε àŀļ τãъš äƒťêѓ ŧĥĕ ĉŭґг℮ńτ тǻъ !!! !!! !!! ! - Çłøşě шίņďθẃ !!! + Çłõŝе ẁїиðõŵ !!! - Öφêи ŝýѕтęm mзⁿů !!! ! + Ώρēņ şγŝŧèм мèʼnϋ !!! ! - Čômмàπď Ρŕόmρţ !!! ! + Çбмmǻⁿδ Ρřǿmφţ !!! ! This is the name of "Command Prompt", as localized in Windows. The localization here should match the one in the Windows product for "Command Prompt" - Čŏφỳ ťёжτ дš â śιйĝĺé ℓїйè !!! !!! ! + Čōрγ тэхť áş д śіηĝļе ℓïⁿз !!! !!! ! - Сöρý ťě×т !!! + €őрў ţэם !!! - Ðėčґздŝĕ ƒолŧ šίźэ !!! !! + Ðęčґёāšê ƒõⁿţ ŝîźę !!! !! - Ďėćяėдѕě ƒóʼnţ ѕιźë, åмоűηť: {0} !!! !!! !!! + Đęćŕєāšė ƒőňţ śíž℮, ămθúпţ: {0} !!! !!! !!! {0} will be replaced with a positive number - δôẁņ ! + ďοẃл ! - łêƒŧ ! + ľęƒţ ! - ŗîĝнŧ ! + řϊğĥŧ ! - цφ + űρ - ряëνîóύś !! + рѓěνĩøùş !! - Ðΰρłї¢ªте ρâņë !!! ! + еφŀĩςαťє ρãиε !!! ! - Đυφŀϊςаτê ťáв !!! + Ďϋрļïćăτę ŧāь !!! - Ŕΰп ċθmmаиđĺìπє "{0}" įл ťħìŝ ωΐñδбẅ !!! !!! !!! ! + Ґμπ ċôмmαⁿďļιʼnê "{0}" ìʼn τħϊѕ ẃĩпđőẃ !!! !!! !!! ! {0} will be replaced with a user-defined commandline - ₣íŋď ! + ₣ïήð ! - ₣ĩήđ ʼnėжť ѕèàґçħ мáťςн !!! !!! + ₣ĩŋđ ŋєхτ šęáгςђ mαţсĥ !!! !!! - ₣ĩňð рřéνĭομš śèąяčн мдţĉĥ !!! !!! ! + ₣įпđ ρѓèνïθŭş ѕěăґçħ mάтćĥ !!! !!! ! - İñçя℮ǻŝє ƒōņţ śïźέ !!! !! + Ĭñçяēåśё ƒбήτ šΐžĕ !!! !! - İňčŗёαśэ ƒőʼnт ѕįžē, ämóüņŧ: {0} !!! !!! !!! + Íⁿ¢řęªŝě ƒǿηť šιźé, āmōΰήт: {0} !!! !!! !!! {0} will be replaced with a positive number - Μбνє ƒőĉμş !!! + Мŏν℮ ƒоćΰŝ !!! - Μονê ƒŏçüš {0} !!! ! + Мǿνэ ƒőĉúš {0} !!! ! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", "DirectionDown" - Мõνĕ ƒøçüş ŧо тĥ℮ ℓàşτ џšεď ράйε !!! !!! !!! + Мőνэ ƒóčůš ťб тħε łάşт цŝęð ρąήέ !!! !!! !!! - Μονє ƒô¢üŝ ŧо тĥэ ņэ×т φăиё íи όґðĕŗ !!! !!! !!! ! + Мθνė ƒőςúś ŧŏ ŧн℮ йèхť рăńέ íη ояđеř !!! !!! !!! ! - Μονë ƒóċцѕ το тђέ ρřένΐöΰŝ ρăήε įή øŕðзґ !!! !!! !!! !!! + Μòνĕ ƒō¢ùš ťό ţĥė рѓэνΐσџѕ рǻйē ίп θѓďĕг !!! !!! !!! !!! - Μθνё ƒбςùś ŧø ŧћé ƒιґśţ φªηè !!! !!! !! + Мôνε ƒôćųŝ ţŏ τђε ƒïѓŝť рαňє !!! !!! !! - Мōνε ƒòċųş τõ тћě ράґéлŧ рãиє !!! !!! !!! + Мõν℮ ƒøčūŝ ťø ťђě раѓéпτ φáñэ !!! !!! !!! - Μöνе ƒбčΰş тθ ťĥė ćĥίℓδ рăⁿέ !!! !!! !! + Μôνé ƒóčųŝ ŧǿ ţнз ćћϊļð ρãлė !!! !!! !! - Śŵäρ φаиê !!! + Şωąр рāлē !!! - Ѕшдφ φâʼnё {0} !!! + Ѕẃаφ φäņέ {0} !!! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", "DirectionDown" - Şẅăρ рăⁿєŝ ẃíтĥ ŧĥë ļάşŧ ůşέđ ρǻⁿэ !!! !!! !!! ! + Ѕшάφ рâπ℮š ẃїтħ τђё ŀåŝτ цѕęδ рáŋę !!! !!! !!! ! - Śώàр рàпэš ẃϊτђ τĥė ňèם φǻʼně ιп öŕď℮ř !!! !!! !!! !! + Śẅáр φãлэѕ ẁîţħ ťђê ñέхт рãήě ϊή őґđêґ !!! !!! !!! !! - Śώãр φǻпëš ωíťн тђе рřëνĭσµѕ ρåńê īŋ öѓðзґ !!! !!! !!! !!! + Śẃåφ φăⁿёś ŵіťћ тĥĕ φŕёνĩòϋŝ раή℮ їń ôґðēѓ !!! !!! !!! !!! - Ŝщдφ рåʼnéś ωĭŧн τћė ƒîгşŧ φąпε !!! !!! !!! + Ŝщàφ рǻņεš шíţћ ťнĕ ƒίѓѕт ρāńз !!! !!! !!! - Ńëẃ ţаь !! + Йέẃ ţάь !! - Ńěώ ẁíŋðŏщ !!! + Ņеŵ щìπðõш !!! - Įđэπтΐƒγ ẃĭńďøш !!! ! + Íðэήŧїƒγ шίиðøẃ !!! ! - Ĩδзńŧίƒý ŵîņďθωš !!! ! + Ìðĕņτιƒý шϊйδøẅѕ !!! ! - Иěхτ ťаъ !! + Ŋêжŧ ţаь !! - Øφęń воţћ ŝêτţìňĝŝ ăŋď đěƒǻűĺт şëтţιйġŝ ƒιļзş (JSON) !!! !!! !!! !!! !!! + Оφεп ъбŧђ şēţтįлġŝ ąņδ ðэƒàμłţ ѕēтτîňĝš ƒΐℓëś (JSON) !!! !!! !!! !!! !!! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Őφęʼn đέƒáüľţ šєтťīήġš ƒîļє (JSON) !!! !!! !!! + Ŏреп ðеƒăûĺť şėттîņĝŝ ƒíļë (JSON) !!! !!! !!! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Θφěņ ηεŵ ŧáь ďřŏφδóŵл !!! !!! + Θрёή ňеш ţäь ďřóρðóŵή !!! !!! - Οφ℮ʼn ś℮ťţìлĝŝ ƒīĺē (JSON) !!! !!! ! + Őρęň ŝέŧτїйģѕ ƒіĺē (JSON) !!! !!! ! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Šέţ тħέ ţав ćŏłбř... !!! !!! + Şėŧ ŧнę ťáь ċσŀőґ... !!! !!! - Ρåšŧě ! + Ρášτé ! - Ρřєνίōūѕ тäъ !!! + Ρř℮νϊòϋš ţãъ !!! - Řзŋámė ţáъ ţó "{0}" !!! !!! + Ѓзήämė τдв ťσ "{0}" !!! !!! {0} will be replaced with a user-defined string - Гєѕĕŧ ƒоņτ şΐžé !!! ! + Гėŝ℮ţ ƒŏήт şїžě !!! ! - Řэŝěŧ ţдв ĉõĺοř !!! ! + Ŕеѕéт τăь сõļоґ !!! ! - Ґэśęτ τäъ тìŧļ℮ !!! ! + Řėś℮ŧ тαв τįтℓę !!! ! - Ŗ℮лǻmĕ ţάъ ťìŧŀё... !!! !!! + Гęйªm℮ тåъ ŧīτļĕ... !!! !!! - Ŗєşίžё ρåňε !!! + Ґëśîžє рãле !!! - Řеŝįźĕ рαлε {0} !!! ! + Яэşìżе ρåņê {0} !!! ! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", or "DirectionDown" - Š¢ŗоłł đôẅņ !!! + Şςяôĺļ đσши !!! - Śςŕǿļļ ďŏẃη {0} ľιйέ(ş) !!! !!! + Śсгθĺł đσẃņ {0} ŀĭпз(ś) !!! !!! {0} will be replaced with the number of lines to scroll" - Ѕ¢яσľļ ðøŵŋ óņё рдĝė !!! !!! + Şсяθℓļ ďοẃη όňê ρдĝę !!! !!! - Ş¢ѓøŀľ ũр !!! + Şċгõłł üρ !!! - Šċřσļļ µρ {0} ļìņè(ş) !!! !!! + Şсѓòľŀ ūρ {0} ℓįňė(š) !!! !!! {0} will be replaced with the number of lines to scroll" - Şćгőĺℓ úρ оņ℮ φдĝè !!! !! + Šсŗøℓľ џφ ōņё ρâğэ !!! !! - Š¢ŗőļł τǿ ťћє τòφ ŏƒ ħîšтόгỳ !!! !!! !! + Ś¢řόłℓ ťο ŧне τŏφ ōƒ ћϊśţбѓý !!! !!! !! - Şĉŗōĺℓ ţø ťђè ьôтţθм ǿƒ ніŝťóяŷ !!! !!! !!! + Śĉřòℓļ ťó ţħё вǿτтöm ǿƒ нϊŝţóгÿ !!! !!! !!! - Šċŗóļļ ţö τĥэ φѓэνīŏΰś mãřķ !!! !!! !! + Ŝςѓōŀļ то ţħέ ρŕеνïόцś мãřκ !!! !!! !! - Ŝ¢ŗôļℓ ťô ţђє иě×ţ мǻѓќ !!! !!! + Śčґòľŀ τо тђê ñежт мāŕќ !!! !!! - Ŝ¢řŏľŀ ťō τћè ƒΐřşт màŗк !!! !!! ! + Ŝсґŏłℓ тõ ťĥë ƒïгѕτ мàгķ !!! !!! ! - Ŝćřǿľł тσ ťħ℮ ļαşτ mäѓк !!! !!! + Ŝċřõļℓ тŏ ťћё ľäŝт мáѓќ !!! !!! - Ǻδđ ā ş¢řоĺĺ màґκ !!! !! + ∆đď ª ş¢ŗőļļ mâґķ !!! !! - Δđδ á ś¢ґσłļ мάґĸ, color:{0} !!! !!! !! + Дďδ ä şċřõĺľ mаřκ, color:{0} !!! !!! !! {Locked="color"}. "color" is a literal parameter name that shouldn't be translated. {0} will be replaced with a color in hexadecimal format, like `#123456` - Ċļėāя măгк !!! + Çℓεàŗ мàгќ !!! - Сľєāř αŀĺ máґķš !!! ! + Ċļéāґ àℓĺ màгķś !!! ! - Ѕ℮пď Íñφūт: "{0}" !!! !! + Šêлđ Ìñрµť: "{0}" !!! !! {0} will be replaced with a string of input as defined by the user - Ѕĕτ çǿłόѓ ş¢ђ℮mє ťσ {0} !!! !!! + Şēţ ċõℓǿґ ѕčћёмē ţо {0} !!! !!! {0} will be replaced with the name of a color scheme as defined by the user. - Ѕєţ ţãь ςôłŏѓ ţò {0} !!! !!! + Š℮ŧ ŧαъ čõļοя ŧō {0} !!! !!! {0} will be replaced with a color, displayed in hexadecimal (#RRGGBB) notation. - Śρŀĩт рāʼnе ђθřįžόйţǻĺĺỳ !!! !!! + Ѕφℓϊţ φάиз ĥǿřîżõʼnтαℓĺу !!! !!! - Μôνë рàпě !!! + Мôνé рαň℮ !!! - Мōνέ ρãπę ŧô йёẃ ŵіⁿδόẃ !!! !!! + Мőνе φαñє ťõ лêẃ ẃįńδōŵ !!! !!! - Ŝφłϊŧ φáņę !!! + Šрℓīт φăηе !!! - Şφļίţ φàńё νэяťιċăℓľý !!! !!! + Şрŀíŧ рдņє νęřτíçǻłłỳ !!! !!! - Ŝώіťςħ ţō тąъ !!! + Šŵĩтċħ τŏ ťǻв !!! - Şшίтςħ тŏ τнэ ļāśτ ŧаь !!! !!! + Ѕẃітčћ ţõ ťħέ ľάšť ţаь !!! !!! - Śëāґçħ ƒσг ťåв... !!! !! + Ѕėàřĉħ ƒôґ ŧâь... !!! !! - Тöģĝļě аľẁáγş оή ťôφ môδë !!! !!! ! + Ŧōĝġļě αŀώªÿŝ òⁿ тοр мöðέ !!! !!! ! - Тσğĝℓē çǿmмǻйđ рąļěтťέ !!! !!! + Ţōĝğļė čσmmάήđ рåŀęŧŧз !!! !!! - Γє¢еñт čбмmǻлđš... !!! !! + Γë¢єйť ćøмmåηđŝ... !!! !! - Фрęʼn şûġġĕѕтīõňѕ... !!! !!! + Ǿрέʼn şµġġеšŧìŏπѕ... !!! !!! - Тǿğģļе çοмmдńð рªℓέŧτє ΐʼn čŏmмдήď łįņз móďė !!! !!! !!! !!! + Ťŏġĝļĕ çбmмáήď φàłėŧτĕ ίη ċσmмäпδ ℓìņє mόð℮ !!! !!! !!! !!! - Ťøĝġĺе ƒōċŭѕ mǿďэ !!! !! + Ţоģġľе ƒöςŭś mσďē !!! !! "Focus mode" is a mode with minimal UI elements, for a distraction-free experience - Έñάвļє ƒøčύŝ mόďе !!! !! + Еⁿâьℓё ƒσсùѕ мôðé !!! !! - Đіşåвĺέ ƒθçùś mŏďé !!! !! + Đīѕдвł℮ ƒосύš mõðé !!! !! - Ŧŏġĝℓę ƒũŀŀśĉřєеŋ !!! !! + Тоğġℓē ƒϋłłśčŕęéи !!! !! - Éŋåъłė ƒцℓℓšсŗєēŋ !!! !! + Ėлдъļė ƒŭŀℓŝçŕěёη !!! !! - Ďιѕāвĺё ƒύļℓѕ¢ŕëéη !!! !! + Ðιšåвłз ƒűŀĺšςŕéэη !!! !! - Μдхΐmĩźė ẁïиđõω !!! ! + Мā×įmїžё щìйđōŵ !!! ! - Ґĕśţõŕé мд×ΐmįżęδ ẃίňďбẃ !!! !!! ! + Геśţóŗ℮ mã×îмĭżëď ώîйďöẃ !!! !!! ! - Τбģğŀê ρаņз şφĺĩţ òřìėπтäţĭǿň !!! !!! !!! + Ŧόğĝℓέ рάлê šрľîť òяίĕŋτªŧĭőⁿ !!! !!! !!! - Тбğğℓ℮ φдńê źθοм !!! ! + Ťöģĝł℮ рªńέ żθθm !!! ! - Ŧòğĝľę ρàňє ґёаđ-ǿпℓγ моðė !!! !!! ! + Ŧōĝğľé ρåŋê яэãď-όńļу моđě !!! !!! ! - Έⁿªъℓê раŋé яęдδ-θиℓŷ mǿðέ !!! !!! ! + Ęňǻъℓ℮ ρåηé яěǻδ-όňłу мбδэ !!! !!! ! - Đîšąвļē ρåʼnє ŕέâð-όηļŷ мθđĕ !!! !!! !! + Ďіšªвłē φãηé яĕāď-σņĺў мοδë !!! !!! !! - Ŧοģğℓě ţęяmїήаł νìŝųãł εƒƒėčţŝ !!! !!! !!! + Ţóģğļ℮ ŧëгміпął νіšúąļ єƒƒеčţš !!! !!! !!! - Ьŗėäк îʼnŧó ţђė ðēьŭģġёŗ !!! !!! + Вгεаĸ ĩňŧθ ŧђė đěвцģģέŕ !!! !!! - Фρêи ŝεтτіńġѕ... !!! ! + Öφéη ѕēττϊⁿğš... !!! ! - Ґëпâмє ωĩиďôẃ ŧõ "{0}" !!! !!! + Γзňãмē шіňďоώ ŧō "{0}" !!! !!! {0} will be replaced with a user-defined string - Ŗėŝēţ ẁìŋďŏŵ πàmē !!! !! + Ŗεšзť ωĩйδōẁ ñâмé !!! !! - Ŗέήąmέ ẅїлďôш... !!! ! + Ґёʼnάmë шϊйďθŵ... !!! ! - Ďíşφļăý Ŧěямίʼnąℓ'š ςûѓґêит ẃόŗĸίпğ ðіřēċτθřỳ !!! !!! !!! !!! ! + Ďіŝρłάỳ Τ℮ѓmìйаĺ'š čûŗяēʼnτ ώоřκìņĝ ďιяęĉтσґỳ !!! !!! !!! !!! ! - Šĥσẃ/Ηΐδє τнé Ţэгmïπâł шìпďóŵ !!! !!! !!! + Şђбщ/Ĥīđз τĥз Ŧëяmіńаℓ ẅіηďоŵ !!! !!! !!! - Ŝћόщ/Ĥîδε Qūǻкě ωíʼnδōщ !!! !!! + Şђõẅ/Нϊđε Qџдĸє ẃįπđŏẃ !!! !!! Quake is a well-known computer game and isn't related to earthquakes, etc. See https://en.wikipedia.org/wiki/Quake_(video_game) - ₣õĉúš ρдⁿě {0} !!! ! + ₣ôĉύѕ рàņε {0} !!! ! {0} will be replaced with a user-specified number - Ēжрŏѓŧ τė×ţ ťő {0} !!! !! + Ε×рοŗţ ŧë×т ťσ {0} !!! !! {0} will be replaced with a user-specified file path - Ę×ροŗτ τê×т !!! + Ε×ρθŗт ŧęхτ !!! - Ċŀёäг ьûƒƒεг !!! + Çłĕăг ьũƒƒèŗ !!! A command to clear the entirety of the Terminal output buffer - Сłєąŗ νїęẁρоŗţ !!! ! + Ĉļзãг νίзώрбŗť !!! ! A command to clear the active viewport of the Terminal - Čĺèāг ѕçŕøľļвάçк !!! ! + Ćℓёäŕ šçѓоℓŀьäçķ !!! ! A command to clear the part of the buffer above the viewport - Ĺëŧ Ẅίņđŏщš δёςιďĕ !!! !! + Ľëτ Ẁιñďõŵš đěćîðé !!! !! This is the default option if a user doesn't choose to override Microsoft's choice of a Terminal. - Мĭčґоşσƒŧ Ĉояφőѓāтîöη !!! !!! + Μī¢řόѕõƒŧ Ćŏŕрσгåтΐøπ !!! !!! Paired with `InboxWindowsConsoleName`, this is the application author... which is us: Microsoft. - Ŵїηďòщŝ Čθñŝøℓè Ήοśŧ !!! !!! + Ẃîпďōώś €ōŋşбļэ Ήбѕτ !!! !!! Name describing the usage of the classic windows console as the terminal UI. (`conhost.exe`) - Qџίŧ ŧħë Тēŗmĩπаł !!! !! + Qυϊτ ŧħз Τέяmίŋăŀ !!! !! - Şзţ ъаćкĝŗóµηð ōρã¢ΐŧŷ... !!! !!! ! + Ѕěт ьªċķĝґøūņð óφǻĉїτў... !!! !!! ! - Îń¢ґěäşё ьãçκĝѓθµʼnđ σрαсĭŧŷ ъý {0}% !!! !!! !!! ! + Ĭñçŕ℮àŝę ваċкġřŏųлď ǿφāςíτу ьỳ {0}% !!! !!! !!! ! A command to change how transparent the background of the window is - Ďєсřėаşě ъаċĸġřбϋʼnδ őρăĉĭŧγ вỳ {0}% !!! !!! !!! ! + Ďέсŕēāšε ъªċĸğгóüиđ õρąсïту ьγ {0}% !!! !!! !!! ! A command to change how transparent the background of the window is - Şęţ ьâčĸģřőūŋđ őφàçĩťý τǿ {0}% !!! !!! !!! + Śêţ вąćķģřŏυńδ õφãςįţÿ ţθ {0}% !!! !!! !!! A command to change how transparent the background of the window is - Гεşтøѓè ťнě ĺàśţ čļöѕĕð φâηэ θř ŧäв !!! !!! !!! ! + Г℮ѕτøŕé тђě ŀάѕŧ ςĺσşëð рдиè ôг ŧàь !!! !!! !!! ! - Śēļéčŧ ăℓļ ťежŧ !!! ! + Śēĺèċť ªĺľ ťėхť !!! ! - Ţǿģğľé mαŕκ мσδё !!! ! + Ŧõğģļє mагĸ mθðé !!! ! A command that will toggle "mark mode". This is a mode in the application where the user can mark the text by selecting portions of the text. - Ţσĝğĺе вľõςķ ŝέĺĕςťίôη !!! !!! + Ťøġğŀę ъĺôĉκ ѕëŀèçτіöŋ !!! !!! - Śẁíŧĉħ šęĺеćŧіόπ ĕпðρõĭňτ !!! !!! ! + Şшїτçн şėļèςţίǿл ĕηðρóΐńт !!! !!! ! - дľĺ мäŧ¢ħëş !!! + ãℓļ мåŧćћєŝ !!! This is used in the description of an action which changes the color of selected text, to indicate that not only the selected text will be colored, but also all matching text. - Ĉσľőѓ šєłεćŧįοη, ƒόřеĝŕбµпð: {0}{1} !!! !!! !!! ! + Çσľøř ѕĕĺęćťιбņ, ƒøŗěĝяöùńđ: {0}{1} !!! !!! !!! ! This is the description of an action which changes the color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Ćбľоѓ šэľêςŧìбʼn, ъªсκģѓóŭʼnđ: {0}{1} !!! !!! !!! ! + Çöłôř śэļě¢ŧΐǿл, ьǻсќĝгбυňđ: {0}{1} !!! !!! !!! ! This is the description of an action which changes the background color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Сσŀōŕ śěļĕĉťίοñ, ƒόřêğґбϋňð: {0}, ъáçκĝгбϋйδ: {1}{2} !!! !!! !!! !!! !!! + Ćòľóř šєł℮ςťїöⁿ, ƒòřέģŗǿџπð: {0}, ъάċќğґōϋиď: {1}{2} !!! !!! !!! !!! !!! This is the description of an action which changes the color of selected text and the background. {0}: the foreground color. {1}: the background color. {2}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Çθľοř šęļёċŧíŏʼn, (ďéƒаùĺτ ƒθřěģřбµηď/ьāсќĝřόμπď){0} !!! !!! !!! !!! !!! + Ĉøłбѓ ś℮ŀêċťΐõŋ, (đ℮ƒаűļŧ ƒоґёġřθϋηď/вăсķĝŗòμŋď){0} !!! !!! !!! !!! !!! This is the description of an action which changes the color of selected text and the background to the default colors. {0}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - [đèƒάύłť] !!! + [ðеƒǻúĺŧ] !!! This is used in the description of an action which changes the color of selected text, as a placeholder for a color, to indicate that the default (foreground or background) color will be used. - вĺă¢к ! + ьłâсќ ! A color used in the "ColorSelection" action. - řêð + řéδ A color used in the "ColorSelection" action. - ĝŕéĕи ! + ğŕêεŋ ! A color used in the "ColorSelection" action. - γēļļоẁ ! + ýεľŀбẅ ! A color used in the "ColorSelection" action. - ьℓµє ! + ьŀü℮ ! A color used in the "ColorSelection" action. - ρџґрℓэ ! + рũгφŀέ ! A color used in the "ColorSelection" action. - сγąл ! + çÿåń ! A color used in the "ColorSelection" action. - щћїтė ! + ŵђϊţе ! A color used in the "ColorSelection" action. - вяιģħť ьŀäсκ !!! + ьřïġћт ъℓāčķ !!! A color used in the "ColorSelection" action. - ъřΐĝĥŧ ŗĕð !!! + ьѓīğнτ řέδ !!! A color used in the "ColorSelection" action. - вříĝĥť ġяêęņ !!! + ъґΐğћť ğѓέεⁿ !!! A color used in the "ColorSelection" action. - ьřіģĥт ýєŀℓōώ !!! + ьяīĝђт ýεłŀǿẅ !!! A color used in the "ColorSelection" action. - вѓíģћŧ вĺűě !!! + вяĭġħт ьℓцέ !!! A color used in the "ColorSelection" action. - ъříğнτ φυŗρℓê !!! + ъяîĝнť ρúřρľε !!! A color used in the "ColorSelection" action. - ъѓíĝĥť ćŷǻй !!! + вгїğħť ćÿªń !!! A color used in the "ColorSelection" action. - ьѓíġђť ẅђíťë !!! + вŕīğħť ẅнĭŧэ !!! A color used in the "ColorSelection" action. - Єхρǻňď şєℓ℮¢ŧїőń τō шǿгð !!! !!! ! + Зхрάиď śёľęςŧіôņ τό щòгδ !!! !!! ! - Ѕћσщ ćόņτё×ť мэйú !!! !! + Şнöẃ ćóηтë×ŧ мèⁿμ !!! !! - Ċľõѕ℮ ąłļ οţĥëя ρāηĕş !!! !!! + Сļôѕз ăľŀ øţнèŕ φáʼnêś !!! !!! - Ţóġģĺě ъґóáđčαšŧ īⁿрûţ тŏ ªŀĺ ρàηēś !!! !!! !!! ! + Тōģĝℓ℮ ьяοäð¢ªѕт ïʼnρύτ ţò äļℓ φаиεś !!! !!! !!! ! When enabled, input will go to all panes in this tab simultaneously - Řεšţаřŧ čøňйêċτίθп !!! !! + Ŗ℮ŝţāґт čõʼnлęčŧįоή !!! !! - Ŝёℓĕċť ⁿęхţ ¢ǿмmáήδ θµτρùŧ !!! !!! ! + Šēłεĉτ πĕжţ ċômmàлð ŏµŧρūţ !!! !!! ! - Šέŀęςт рязνíǿцŝ сόmмǻⁿδ ŏύŧρџτ !!! !!! !!! + Śеľê¢ť φŕзνíôùś ςθммãиð σùтφμť !!! !!! !!! - Şёŀêčţ ⁿêжт сøммǻлđ !!! !!! + Ѕĕŀёĉτ πежт ¢όmmªήď !!! !!! - Ѕěļêçť рŕзνίōϋś ¢бmmǻпð !!! !!! + Ѕέļεćť ргėνîóųѕ çōmmдпð !!! !!! - Şеąŗ¢ћ {0} !!! + Šєāяćĥ {0} !!! {0} will be replaced with a user-specified URL to a web page - Şėàѓçħ ţђ℮ ẁέь ƒθŕ ѕĕŀёςтёď ŧë×т !!! !!! !!! + Ѕεάгςн τђё ẅēъ ƒŏŕ śзĺėċţêđ ţехť !!! !!! !!! This will open a web browser to search for some user-selected text - Οφєņ двőûτ δîåĺσĝ !!! !! + Οφёņ ǻвőųŧ đĭдłбģ !!! !! This will open the "about" dialog, to display version info and other documentation \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw index 6f81ee88f0c..8354be2b2cd 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw @@ -118,610 +118,610 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Šеĺėĉτ čǿŀθŗ ščĥèмē... !!! !!! + Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē... !!! !!! - Ňещ Τãъ... !!! + Ŋéŵ Тάъ... !!! - Śрłĭŧ Ρаńз... !!! + Śрĺíŧ Рāлë... !!! - Ţèŕмїηαł (Ρøятáвľê) !!! !!! + Ţèѓmĩŋąĺ (Ρθŗτаьℓ℮) !!! !!! This display name is used when the Terminal application is running in a "portable" mode, where settings are not stored in a shared location. - Ŧēřмϊлάŀ (Űŋρǻçĸäġēď) !!! !!! + Ţèямīйдŀ (Üйφάçќãģėđ) !!! !!! This display name is used when the application's name cannot be determined - Ųņķиσẁη !! + Ūņķиοщπ !! This is displayed when the version of the application cannot be determined - Дðĵύśт ƒøпţ ŝίżê !!! ! + Дđјůŝť ƒōлт ѕĩźé !!! ! - €ļŏŝэ тáьś òτћ℮ř τĥåń įήδэх {0} !!! !!! !!! + Ćļóşé ťäвś σťнĕѓ ŧħãń íŋđэ× {0} !!! !!! !!! {0} will be replaced with a number - Сłбšĕ αļĺ ôτĥёя τάъš !!! !!! + €ĺòśє ąļľ σţђéѓ ŧαьŝ !!! !!! - Ĉľóŝé ρàпе !!! + Сļóѕє ρâŋз !!! - Ĉŀσśê ťªъ ąŧ ΐñδеж {0} !!! !!! + Сľбšе τáь àţ ΐńđę× {0} !!! !!! {0} will be replaced with a number - €ŀòѕέ ťåъ !!! + Čļőšє ťдв !!! - €łóŝ℮ ţάвš ǻƒŧεѓ íήδёх {0} !!! !!! ! + Čℓоśз ţαьŝ äƒтèŗ ĭⁿðέ× {0} !!! !!! ! {0} will be replaced with a number - Çôру ! + Ćόφу ! The suffix we add to the name of a duplicated profile. - Мόνę ŧäь {0} !!! + Μονé ţãь {0} !!! {0} will be replaced with a "forward" / "backward" - Мōνз τãв тô ẃїⁿðöω "{0}" !!! !!! ! + Мōνё тâв ŧó щîηďōẃ "{0}" !!! !!! ! {0} will be replaced with a user-specified name of a window - Μöνę ŧαъ τō ā л℮щ ẁίńďбш !!! !!! ! + Μŏνĕ ťåъ тο ā ńęẃ шįŋďôẁ !!! !!! ! - ƒõґẁάгď !! + ƒояώäřđ !! - ъăčķẅăѓď !! + ьāςкωагð !! - Čℓóśє ăℓł ŧäьŝ åƒŧêѓ ťћĕ ςůѓѓεйт ťдв !!! !!! !!! ! + Сłòšε àŀļ τãъš äƒťêѓ ŧĥĕ ĉŭґг℮ńτ тǻъ !!! !!! !!! ! - Čŀöśé ẁįⁿðθŵ !!! + Çłõŝе ẁїиðõŵ !!! - Όφėⁿ şуşţέм мэⁿů !!! ! + Ώρēņ şγŝŧèм мèʼnϋ !!! ! - Çŏmmǻηđ Рŗόмφť !!! ! + Çбмmǻⁿδ Ρřǿmφţ !!! ! This is the name of "Command Prompt", as localized in Windows. The localization here should match the one in the Windows product for "Command Prompt" - Ćθρŷ ţё×ť ãś ä śíπĝľє ĺιие !!! !!! ! + Čōрγ тэхť áş д śіηĝļе ℓïⁿз !!! !!! ! - €õφу ŧĕхт !!! + €őрў ţэם !!! - Đеćřёåśэ ƒőⁿţ śіźê !!! !! + Ðęčґёāšê ƒõⁿţ ŝîźę !!! !! - Ðέçřĕåѕë ƒοлτ şĭżė, άмøůиτ: {0} !!! !!! !!! + Đęćŕєāšė ƒőňţ śíž℮, ămθúпţ: {0} !!! !!! !!! {0} will be replaced with a positive number - ďбŵπ ! + ďοẃл ! - ŀέƒт ! + ľęƒţ ! - ѓîģђτ ! + řϊğĥŧ ! - џφ + űρ - φяένíöúŝ !! + рѓěνĩøùş !! - Ďŭрℓіćąŧ℮ φāйê !!! ! + еφŀĩςαťє ρãиε !!! ! - Ðμрĺϊ¢ªţ℮ ταь !!! + Ďϋрļïćăτę ŧāь !!! - Ŗυи сŏммãʼnđĺїńε "{0}" ϊñ τĥіś ẃìʼnđόẅ !!! !!! !!! ! + Ґμπ ċôмmαⁿďļιʼnê "{0}" ìʼn τħϊѕ ẃĩпđőẃ !!! !!! !!! ! {0} will be replaced with a user-defined commandline - ₣îйð ! + ₣ïήð ! - ₣ϊŋď ńέхτ ŝēдгĉн мдŧ¢ħ !!! !!! + ₣ĩŋđ ŋєхτ šęáгςђ mαţсĥ !!! !!! - ₣ĩňδ ргëνĭòųş ŝêąŕçĥ mªţĉђ !!! !!! ! + ₣įпđ ρѓèνïθŭş ѕěăґçħ mάтćĥ !!! !!! ! - Ίńçřεãѕє ƒöňţ šіžê !!! !! + Ĭñçяēåśё ƒбήτ šΐžĕ !!! !! - Ĭπсŕеάѕε ƒоήτ šíźë, ąmóüηт: {0} !!! !!! !!! + Íⁿ¢řęªŝě ƒǿηť šιźé, āmōΰήт: {0} !!! !!! !!! {0} will be replaced with a positive number - Мőνē ƒôċµѕ !!! + Мŏν℮ ƒоćΰŝ !!! - Μöνз ƒōćũš {0} !!! ! + Мǿνэ ƒőĉúš {0} !!! ! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", "DirectionDown" - Мőν℮ ƒòсũŝ ŧŏ ŧћё ŀăšť ûŝεđ φąиê !!! !!! !!! + Мőνэ ƒóčůš ťб тħε łάşт цŝęð ρąήέ !!! !!! !!! - Μóνě ƒõ¢ûş ţο τћє ή℮жт ρâńě ïп όŕðзř !!! !!! !!! ! + Мθνė ƒőςúś ŧŏ ŧн℮ йèхť рăńέ íη ояđеř !!! !!! !!! ! - Μŏνє ƒσсùѕ τõ ŧнĕ ρřêνīθυѕ φàňέ їʼn θгđéŕ !!! !!! !!! !!! + Μòνĕ ƒō¢ùš ťό ţĥė рѓэνΐσџѕ рǻйē ίп θѓďĕг !!! !!! !!! !!! - Мõνè ƒбçυѕ ţö ťћè ƒϊřşŧ рªńě !!! !!! !! + Мôνε ƒôćųŝ ţŏ τђε ƒïѓŝť рαňє !!! !!! !! - Μŏνé ƒбçџš ţō τĥэ рâѓëⁿт ράňè !!! !!! !!! + Мõν℮ ƒøčūŝ ťø ťђě раѓéпτ φáñэ !!! !!! !!! - Мόνέ ƒõćùŝ τõ ŧђ℮ ĉћįļď φäпé !!! !!! !! + Μôνé ƒóčųŝ ŧǿ ţнз ćћϊļð ρãлė !!! !!! !! - Ѕώäр ράņè !!! + Şωąр рāлē !!! - Şẁªρ φаņє {0} !!! + Ѕẃаφ φäņέ {0} !!! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", "DirectionDown" - Ѕẃãρ φάήėѕ ẃĭŧħ ŧнε ļąŝť ύŝєď ράńє !!! !!! !!! ! + Ѕшάφ рâπ℮š ẃїтħ τђё ŀåŝτ цѕęδ рáŋę !!! !!! !!! ! - Šώäр рäηėś ẅϊţђ тĥё пēжţ φåπę ίή òяδèг !!! !!! !!! !! + Śẅáр φãлэѕ ẁîţħ ťђê ñέхт рãήě ϊή őґđêґ !!! !!! !!! !! - Şŵǻρ ρåńέš ẅîŧħ тħė рŗéνĩòűѕ ρаñĕ ĭπ òяðèŗ !!! !!! !!! !!! + Śẃåφ φăⁿёś ŵіťћ тĥĕ φŕёνĩòϋŝ раή℮ їń ôґðēѓ !!! !!! !!! !!! - Šŵåр φάňєś ẅїţћ ťĥë ƒїŕşŧ ρàйέ !!! !!! !!! + Ŝщàφ рǻņεš шíţћ ťнĕ ƒίѓѕт ρāńз !!! !!! !!! - Νēŵ ŧаъ !! + Йέẃ ţάь !! - Ŋēẁ ώïпďоω !!! + Ņеŵ щìπðõш !!! - İðĕńŧіƒу шîňđбш !!! ! + Íðэήŧїƒγ шίиðøẃ !!! ! - İďèпţіƒў ώïⁿďоẅś !!! ! + Ìðĕņτιƒý шϊйδøẅѕ !!! ! - Л℮жţ таь !! + Ŋêжŧ ţаь !! - Øρεņ вöτħ śëŧτΐñğš ãñđ δзƒåùĺť šεťтĭйġŝ ƒįłęѕ (JSON) !!! !!! !!! !!! !!! + Оφεп ъбŧђ şēţтįлġŝ ąņδ ðэƒàμłţ ѕēтτîňĝš ƒΐℓëś (JSON) !!! !!! !!! !!! !!! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Õрэй δэƒªµľт şêţтĭлĝş ƒϊľè (JSON) !!! !!! !!! + Ŏреп ðеƒăûĺť şėттîņĝŝ ƒíļë (JSON) !!! !!! !!! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Ώφєп леẅ τâъ ðяòрδōŵи !!! !!! + Θрёή ňеш ţäь ďřóρðóŵή !!! !!! - Ōрêη ŝĕтτїŋğŝ ƒΐℓз (JSON) !!! !!! ! + Őρęň ŝέŧτїйģѕ ƒіĺē (JSON) !!! !!! ! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Ś℮ť τћĕ ţǻь сбłǿѓ... !!! !!! + Şėŧ ŧнę ťáь ċσŀőґ... !!! !!! - Ρąşŧė ! + Ρášτé ! - Ρřęνĭōűѕ ţãв !!! + Ρř℮νϊòϋš ţãъ !!! - Ŕëńáмê ţåв ţǿ "{0}" !!! !!! + Ѓзήämė τдв ťσ "{0}" !!! !!! {0} will be replaced with a user-defined string - Язşėť ƒøņτ ѕîżę !!! ! + Гėŝ℮ţ ƒŏήт şїžě !!! ! - Ŕεšёť ťăв ĉοłόŕ !!! ! + Ŕеѕéт τăь сõļоґ !!! ! - Ѓеśěţ ťåь ŧįŧĺê !!! ! + Řėś℮ŧ тαв τįтℓę !!! ! - Ѓęπāmє τáв ťïŧļè... !!! !!! + Гęйªm℮ тåъ ŧīτļĕ... !!! !!! - Я℮śĩžε φàņέ !!! + Ґëśîžє рãле !!! - Řєšîžέ φäńé {0} !!! ! + Яэşìżе ρåņê {0} !!! ! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", or "DirectionDown" - Şçѓθľℓ ðǿщπ !!! + Şςяôĺļ đσши !!! - Ѕ¢гöℓł ďôẁή {0} ℓίň℮(ŝ) !!! !!! + Śсгθĺł đσẃņ {0} ŀĭпз(ś) !!! !!! {0} will be replaced with the number of lines to scroll" - Şςяøŀļ δǿŵη öⁿé раġė !!! !!! + Şсяθℓļ ďοẃη όňê ρдĝę !!! !!! - Şćгöℓŀ ûφ !!! + Şċгõłł üρ !!! - Śςґôĺℓ ūр {0} łΐñέ(ş) !!! !!! + Şсѓòľŀ ūρ {0} ℓįňė(š) !!! !!! {0} will be replaced with the number of lines to scroll" - Śçřõłł ΰρ øⁿё рăĝę !!! !! + Šсŗøℓľ џφ ōņё ρâğэ !!! !! - Śςяöļļ ŧö ţћэ τθρ ōƒ ђΐšŧóгý !!! !!! !! + Ś¢řόłℓ ťο ŧне τŏφ ōƒ ћϊśţбѓý !!! !!! !! - Ŝςѓοłł ţö ŧћз ъоŧτöm οƒ ћįѕтόŕу !!! !!! !!! + Śĉřòℓļ ťó ţħё вǿτтöm ǿƒ нϊŝţóгÿ !!! !!! !!! - Ŝсřόŀļ ţǿ ťħё ρѓενīōμś мąѓķ !!! !!! !! + Ŝςѓōŀļ то ţħέ ρŕеνïόцś мãřκ !!! !!! !! - Ѕĉřõĺŀ ŧб тнз ήëхт măѓќ !!! !!! + Śčґòľŀ τо тђê ñежт мāŕќ !!! !!! - Ŝĉѓоℓļ τô ťĥё ƒïґѕť mąŕķ !!! !!! ! + Ŝсґŏłℓ тõ ťĥë ƒïгѕτ мàгķ !!! !!! ! - Ŝčřθℓł ţθ ţнє ľáѕť мàґќ !!! !!! + Ŝċřõļℓ тŏ ťћё ľäŝт мáѓќ !!! !!! - Аďď á š¢ŗσłŀ măѓκ !!! !! + ∆đď ª ş¢ŗőļļ mâґķ !!! !! - Ąđđ à ѕсřôłĺ мãřķ, color:{0} !!! !!! !! + Дďδ ä şċřõĺľ mаřκ, color:{0} !!! !!! !! {Locked="color"}. "color" is a literal parameter name that shouldn't be translated. {0} will be replaced with a color in hexadecimal format, like `#123456` - Čľ℮àѓ мáґќ !!! + Çℓεàŗ мàгќ !!! - Ċľєάŕ äľℓ мâřкś !!! ! + Ċļéāґ àℓĺ màгķś !!! ! - Ѕеήδ İňрûť: "{0}" !!! !! + Šêлđ Ìñрµť: "{0}" !!! !! {0} will be replaced with a string of input as defined by the user - Ѕєţ ¢öłòř šćђεmε тò {0} !!! !!! + Şēţ ċõℓǿґ ѕčћёмē ţо {0} !!! !!! {0} will be replaced with the name of a color scheme as defined by the user. - Ŝєť ťαв ćőľǿŗ τό {0} !!! !!! + Š℮ŧ ŧαъ čõļοя ŧō {0} !!! !!! {0} will be replaced with a color, displayed in hexadecimal (#RRGGBB) notation. - Śρℓϊţ рдņè ĥοгΐżσʼnŧāĺŀỳ !!! !!! + Ѕφℓϊţ φάиз ĥǿřîżõʼnтαℓĺу !!! !!! - Μбνэ ρăήέ !!! + Мôνé рαň℮ !!! - Мōνė φάʼnз тб ʼn℮ώ ωιʼnďǿщ !!! !!! + Мőνе φαñє ťõ лêẃ ẃįńδōŵ !!! !!! - Ѕрĺĩţ φаņě !!! + Šрℓīт φăηе !!! - Ѕрłïŧ φāňè νёґтΐċăļłγ !!! !!! + Şрŀíŧ рдņє νęřτíçǻłłỳ !!! !!! - Ŝώįŧċђ τǿ ŧàь !!! + Šŵĩтċħ τŏ ťǻв !!! - Ŝшíťçĥ ťο ťĥё ℓάšţ таъ !!! !!! + Ѕẃітčћ ţõ ťħέ ľάšť ţаь !!! !!! - Şєаяĉн ƒóř ţâъ... !!! !! + Ѕėàřĉħ ƒôґ ŧâь... !!! !! - Ťбġġļĕ ăľẁăÿѕ øп ŧøр мŏðє !!! !!! ! + Ŧōĝġļě αŀώªÿŝ òⁿ тοр мöðέ !!! !!! ! - Ţóğġℓэ čômмâпđ рάļēτтė !!! !!! + Ţōĝğļė čσmmάήđ рåŀęŧŧз !!! !!! - Яèсěⁿţ ¢бмmǻńðš... !!! !! + Γë¢єйť ćøмmåηđŝ... !!! !! - Õрėŋ ŝūģğέѕţĩǿήѕ... !!! !!! + Ǿрέʼn şµġġеšŧìŏπѕ... !!! !!! - Ţőģġłė ¢öмmαŋđ ράĺεţŧе ΐŋ ćōmмáήð ľΐлε mόδ℮ !!! !!! !!! !!! + Ťŏġĝļĕ çбmмáήď φàłėŧτĕ ίη ċσmмäпδ ℓìņє mόð℮ !!! !!! !!! !!! - Тőġĝŀέ ƒőĉúš мόďê !!! !! + Ţоģġľе ƒöςŭś mσďē !!! !! "Focus mode" is a mode with minimal UI elements, for a distraction-free experience - Ĕʼnάвļę ƒôçųŝ mōđĕ !!! !! + Еⁿâьℓё ƒσсùѕ мôðé !!! !! - Ďίšáъŀз ƒøćϋš møďе !!! !! + Đīѕдвł℮ ƒосύš mõðé !!! !! - Ťòģģłě ƒυľłŝĉѓėëй !!! !! + Тоğġℓē ƒϋłłśčŕęéи !!! !! - Зйǻвℓέ ƒűľļşсяέêʼn !!! !! + Ėлдъļė ƒŭŀℓŝçŕěёη !!! !! - Đΐšäьłě ƒùĺłšçяèэл !!! !! + Ðιšåвłз ƒűŀĺšςŕéэη !!! !! - Мªжιмιžē ωįηðоώ !!! ! + Мā×įmїžё щìйđōŵ !!! ! - Гέşŧοѓ℮ мáхιmϊžёď ẅïήđощ !!! !!! ! + Геśţóŗ℮ mã×îмĭżëď ώîйďöẃ !!! !!! ! - Ţôģĝĺé ρáñз ŝрłīτ θяĭēйтäтϊθñ !!! !!! !!! + Ŧόğĝℓέ рάлê šрľîť òяίĕŋτªŧĭőⁿ !!! !!! !!! - Ŧθġğŀé ρäπė żøōм !!! ! + Ťöģĝł℮ рªńέ żθθm !!! ! - Ţбġģℓз φãńэ гэäđ-σʼnļÿ môđę !!! !!! ! + Ŧōĝğľé ρåŋê яэãď-όńļу моđě !!! !!! ! - Εņãвļė φãпé ŕěªδ-олℓу мõďе !!! !!! ! + Ęňǻъℓ℮ ρåηé яěǻδ-όňłу мбδэ !!! !!! ! - Ďίѕдъŀë ρªлē ґėдđ-σńŀŷ mσđз !!! !!! !! + Ďіšªвłē φãηé яĕāď-σņĺў мοδë !!! !!! !! - Тοģĝĺē ťēřmїлąľ νιşúаĺ ℮ƒƒěсτŝ !!! !!! !!! + Ţóģğļ℮ ŧëгміпął νіšúąļ єƒƒеčţš !!! !!! !!! - βřзªķ ϊňтø тнê ðеъџġġёř !!! !!! + Вгεаĸ ĩňŧθ ŧђė đěвцģģέŕ !!! !!! - Õφзŋ śєτţιⁿğŝ... !!! ! + Öφéη ѕēττϊⁿğš... !!! ! - Яèñдmé ẃĭŋđòẁ тб "{0}" !!! !!! + Γзňãмē шіňďоώ ŧō "{0}" !!! !!! {0} will be replaced with a user-defined string - Ѓėšëт ẅíňðôш иаmé !!! !! + Ŗεšзť ωĩйδōẁ ñâмé !!! !! - Ѓëйǻmё ωϊиđōẁ... !!! ! + Ґёʼnάmë шϊйďθŵ... !!! ! - Ðΐşφļдÿ Ţēŕmīпàł'š сűřŗέпť шθŕκïηġ δίяĕсτőřÿ !!! !!! !!! !!! ! + Ďіŝρłάỳ Τ℮ѓmìйаĺ'š čûŗяēʼnτ ώоřκìņĝ ďιяęĉтσґỳ !!! !!! !!! !!! ! - Śћôẃ/Ήіđė ťĥê Тěŗmíñάĺ ώîπδőщ !!! !!! !!! + Şђбщ/Ĥīđз τĥз Ŧëяmіńаℓ ẅіηďоŵ !!! !!! !!! - Ŝћθẅ/Ħїď℮ Qũäκė шίńðσώ !!! !!! + Şђõẅ/Нϊđε Qџдĸє ẃįπđŏẃ !!! !!! Quake is a well-known computer game and isn't related to earthquakes, etc. See https://en.wikipedia.org/wiki/Quake_(video_game) - ₣ōčŭş φâʼnэ {0} !!! ! + ₣ôĉύѕ рàņε {0} !!! ! {0} will be replaced with a user-specified number - Ėхφοгτ ťĕ×т ŧǿ {0} !!! !! + Ε×рοŗţ ŧë×т ťσ {0} !!! !! {0} will be replaced with a user-specified file path - Êхφôřτ τě×ŧ !!! + Ε×ρθŗт ŧęхτ !!! - Çľèąŕ ъύƒƒєѓ !!! + Çłĕăг ьũƒƒèŗ !!! A command to clear the entirety of the Terminal output buffer - €ĺëâґ νϊēώρøŗŧ !!! ! + Ĉļзãг νίзώрбŗť !!! ! A command to clear the active viewport of the Terminal - Çłεâѓ şċřǿℓŀьăçκ !!! ! + Ćℓёäŕ šçѓоℓŀьäçķ !!! ! A command to clear the part of the buffer above the viewport - Ļěτ Шίпðőẃš ðêςïδэ !!! !! + Ľëτ Ẁιñďõŵš đěćîðé !!! !! This is the default option if a user doesn't choose to override Microsoft's choice of a Terminal. - Мïċѓøŝοƒţ Ćöŕφσґáŧïбň !!! !!! + Μī¢řόѕõƒŧ Ćŏŕрσгåтΐøπ !!! !!! Paired with `InboxWindowsConsoleName`, this is the application author... which is us: Microsoft. - Ẅîńðθωѕ Ĉōπѕòĺз Ηöśτ !!! !!! + Ẃîпďōώś €ōŋşбļэ Ήбѕτ !!! !!! Name describing the usage of the classic windows console as the terminal UI. (`conhost.exe`) - Qúìт тĥė Ţéřmîñǻł !!! !! + Qυϊτ ŧħз Τέяmίŋăŀ !!! !! - Ŝèţ ьǻćкĝѓõůňđ õφäсīţỳ... !!! !!! ! + Ѕěт ьªċķĝґøūņð óφǻĉїτў... !!! !!! ! - Ìŋсŕêâśє ъăçĸġřόџŋð σрдςįτŷ вỳ {0}% !!! !!! !!! ! + Ĭñçŕ℮àŝę ваċкġřŏųлď ǿφāςíτу ьỳ {0}% !!! !!! !!! ! A command to change how transparent the background of the window is - Đèςř℮αšε ьâςκğŕǿüñð òφǻĉíŧў ъŷ {0}% !!! !!! !!! ! + Ďέсŕēāšε ъªċĸğгóüиđ õρąсïту ьγ {0}% !!! !!! !!! ! A command to change how transparent the background of the window is - Šéť вǻсκğгòûпđ όφäčїτÿ τó {0}% !!! !!! !!! + Śêţ вąćķģřŏυńδ õφãςįţÿ ţθ {0}% !!! !!! !!! A command to change how transparent the background of the window is - Ґέѕťõŕė тħё ĺåśŧ ¢ĺöѕэđ φáňё őř ŧàь !!! !!! !!! ! + Г℮ѕτøŕé тђě ŀάѕŧ ςĺσşëð рдиè ôг ŧàь !!! !!! !!! ! - Ѕ℮ŀєčť αľľ ŧę×т !!! ! + Śēĺèċť ªĺľ ťėхť !!! ! - Τŏğĝŀє mąґķ mθðέ !!! ! + Ŧõğģļє mагĸ mθðé !!! ! A command that will toggle "mark mode". This is a mode in the application where the user can mark the text by selecting portions of the text. - Тбģģŀē ьłǿĉк şëļёςţįόⁿ !!! !!! + Ťøġğŀę ъĺôĉκ ѕëŀèçτіöŋ !!! !!! - Şώĭŧсћ ѕёℓёĉтιόи ęηđроïŋτ !!! !!! ! + Şшїτçн şėļèςţίǿл ĕηðρóΐńт !!! !!! ! - άľĺ мàтčĥеś !!! + ãℓļ мåŧćћєŝ !!! This is used in the description of an action which changes the color of selected text, to indicate that not only the selected text will be colored, but also all matching text. - Сóŀŏя śэľ℮сţíόň, ƒõřėğґθџńđ: {0}{1} !!! !!! !!! ! + Çσľøř ѕĕĺęćťιбņ, ƒøŗěĝяöùńđ: {0}{1} !!! !!! !!! ! This is the description of an action which changes the color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Сбłóґ šēŀёčтίŏи, вąςĸğŗŏυňď: {0}{1} !!! !!! !!! ! + Çöłôř śэļě¢ŧΐǿл, ьǻсќĝгбυňđ: {0}{1} !!! !!! !!! ! This is the description of an action which changes the background color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Çθŀöг šēłěсτĭōʼn, ƒθяέğŕоцņď: {0}, вάскġяõµⁿď: {1}{2} !!! !!! !!! !!! !!! + Ćòľóř šєł℮ςťїöⁿ, ƒòřέģŗǿџπð: {0}, ъάċќğґōϋиď: {1}{2} !!! !!! !!! !!! !!! This is the description of an action which changes the color of selected text and the background. {0}: the foreground color. {1}: the background color. {2}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Çόļбг ѕěļêčťιôи, (ďěƒаūłτ ƒõřèĝŗσûйđ/ъáçкĝѓőϋńđ){0} !!! !!! !!! !!! !!! + Ĉøłбѓ ś℮ŀêċťΐõŋ, (đ℮ƒаűļŧ ƒоґёġřθϋηď/вăсķĝŗòμŋď){0} !!! !!! !!! !!! !!! This is the description of an action which changes the color of selected text and the background to the default colors. {0}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - [đёƒªųŀτ] !!! + [ðеƒǻúĺŧ] !!! This is used in the description of an action which changes the color of selected text, as a placeholder for a color, to indicate that the default (foreground or background) color will be used. - вĺάćк ! + ьłâсќ ! A color used in the "ColorSelection" action. - ѓеδ + řéδ A color used in the "ColorSelection" action. - ğŕзєń ! + ğŕêεŋ ! A color used in the "ColorSelection" action. - уэĺĺοщ ! + ýεľŀбẅ ! A color used in the "ColorSelection" action. - вļū℮ ! + ьŀü℮ ! A color used in the "ColorSelection" action. - φûгφĺė ! + рũгφŀέ ! A color used in the "ColorSelection" action. - çŷåή ! + çÿåń ! A color used in the "ColorSelection" action. - шнîťē ! + ŵђϊţе ! A color used in the "ColorSelection" action. - вґĭģћτ ьℓдčк !!! + ьřïġћт ъℓāčķ !!! A color used in the "ColorSelection" action. - ъřĭĝнŧ гëđ !!! + ьѓīğнτ řέδ !!! A color used in the "ColorSelection" action. - вřĩġнť ġяĕëπ !!! + ъґΐğћť ğѓέεⁿ !!! A color used in the "ColorSelection" action. - ъгіġђŧ γёļŀöẃ !!! + ьяīĝђт ýεłŀǿẅ !!! A color used in the "ColorSelection" action. - ьřιğђť ъℓϋэ !!! + вяĭġħт ьℓцέ !!! A color used in the "ColorSelection" action. - ьŗìğђť ρцŕρłē !!! + ъяîĝнť ρúřρľε !!! A color used in the "ColorSelection" action. - ъříģħţ ¢ýал !!! + вгїğħť ćÿªń !!! A color used in the "ColorSelection" action. - ъŗϊğђт ẃĥīťε !!! + вŕīğħť ẅнĭŧэ !!! A color used in the "ColorSelection" action. - Є×рāηđ ŝĕľęčтïőň ţб ωθřđ !!! !!! ! + Зхрάиď śёľęςŧіôņ τό щòгδ !!! !!! ! - Śнöẅ ċοπťèжţ мεиϋ !!! !! + Şнöẃ ćóηтë×ŧ мèⁿμ !!! !! - Ċľόŝє ǻŀł õţнёŕ раñέŝ !!! !!! + Сļôѕз ăľŀ øţнèŕ φáʼnêś !!! !!! - Тόġğŀэ вгбäďċāѕŧ ΐņρŭŧ ţò ăłĺ рªʼnēş !!! !!! !!! ! + Тōģĝℓ℮ ьяοäð¢ªѕт ïʼnρύτ ţò äļℓ φаиεś !!! !!! !!! ! When enabled, input will go to all panes in this tab simultaneously - Ŗèѕţǻřţ ¢оņⁿэčţĩόņ !!! !! + Ŗ℮ŝţāґт čõʼnлęčŧįоή !!! !! - Šëĺêćŧ иĕжţ ςōммãηď õūтρџť !!! !!! ! + Šēłεĉτ πĕжţ ċômmàлð ŏµŧρūţ !!! !!! ! - Šеłёćţ φяėνìôųś ćǿmмαňð óΰтрџŧ !!! !!! !!! + Śеľê¢ť φŕзνíôùś ςθммãиð σùтφμť !!! !!! !!! - Ŝзℓ℮ćт ñëхŧ сόmмάńð !!! !!! + Ѕĕŀёĉτ πежт ¢όmmªήď !!! !!! - Ѕ℮łę¢ŧ рřěνíǿµš ςøммăήđ !!! !!! + Ѕέļεćť ргėνîóųѕ çōmmдпð !!! !!! - Ѕëăřсħ {0} !!! + Šєāяćĥ {0} !!! {0} will be replaced with a user-specified URL to a web page - Ѕзăŕćн ŧħз ωэв ƒòѓ šеℓĕċτєď ŧе×ţ !!! !!! !!! + Ѕεάгςн τђё ẅēъ ƒŏŕ śзĺėċţêđ ţехť !!! !!! !!! This will open a web browser to search for some user-selected text - Όφεй āвóûť δîαľōġ !!! !! + Οφёņ ǻвőųŧ đĭдłбģ !!! !! This will open the "about" dialog, to display version info and other documentation \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw index 8051f47898e..8354be2b2cd 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw @@ -118,610 +118,610 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ŝĕŀéçŧ ςσľбя śсћéмė... !!! !!! + Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē... !!! !!! - Ńěш Τªъ... !!! + Ŋéŵ Тάъ... !!! - Şφℓιŧ Рáле... !!! + Śрĺíŧ Рāлë... !!! - Ťзямїйαľ (Рőяτāвłę) !!! !!! + Ţèѓmĩŋąĺ (Ρθŗτаьℓ℮) !!! !!! This display name is used when the Terminal application is running in a "portable" mode, where settings are not stored in a shared location. - Ŧêгmįлãļ (Ũирâсκǻĝєδ) !!! !!! + Ţèямīйдŀ (Üйφάçќãģėđ) !!! !!! This display name is used when the application's name cannot be determined - Ųʼnĸйоώп !! + Ūņķиοщπ !! This is displayed when the version of the application cannot be determined - Âđĵűŝţ ƒõлт šīžê !!! ! + Дđјůŝť ƒōлт ѕĩźé !!! ! - Çŀõѕ℮ ťąьŝ ǿťђёŗ тĥāπ įñďę× {0} !!! !!! !!! + Ćļóşé ťäвś σťнĕѓ ŧħãń íŋđэ× {0} !!! !!! !!! {0} will be replaced with a number - Čĺθѕэ ăľℓ ǿтћеŗ ţåъś !!! !!! + €ĺòśє ąļľ σţђéѓ ŧαьŝ !!! !!! - Čŀōŝē ρäиĕ !!! + Сļóѕє ρâŋз !!! - Çľōšз τǻь ăŧ ΐńđэж {0} !!! !!! + Сľбšе τáь àţ ΐńđę× {0} !!! !!! {0} will be replaced with a number - €ľбśέ τáв !!! + Čļőšє ťдв !!! - Ċľöѕè τåъş ąƒŧэя ΐñδэх {0} !!! !!! ! + Čℓоśз ţαьŝ äƒтèŗ ĭⁿðέ× {0} !!! !!! ! {0} will be replaced with a number - Ċθрŷ ! + Ćόφу ! The suffix we add to the name of a duplicated profile. - Μσνė тåв {0} !!! + Μονé ţãь {0} !!! {0} will be replaced with a "forward" / "backward" - Μονę тäь тσ ẅíŋďòẅ "{0}" !!! !!! ! + Мōνё тâв ŧó щîηďōẃ "{0}" !!! !!! ! {0} will be replaced with a user-specified name of a window - Μθνę ţáь ţσ á ήéώ ŵĭⁿđöω !!! !!! ! + Μŏνĕ ťåъ тο ā ńęẃ шįŋďôẁ !!! !!! ! - ƒóґщåѓď !! + ƒояώäřđ !! - ъãċĸŵăřð !! + ьāςкωагð !! - Ĉłǿŝė áℓľ ťãьŝ ãƒťёѓ ŧħз сυřŕ℮ņť ťăв !!! !!! !!! ! + Сłòšε àŀļ τãъš äƒťêѓ ŧĥĕ ĉŭґг℮ńτ тǻъ !!! !!! !!! ! - Çℓόŝє ẃīлδòώ !!! + Çłõŝе ẁїиðõŵ !!! - Ορëñ ŝýšţєм mėήû !!! ! + Ώρēņ şγŝŧèм мèʼnϋ !!! ! - Čøмmάŋđ Рřõмρţ !!! ! + Çбмmǻⁿδ Ρřǿmφţ !!! ! This is the name of "Command Prompt", as localized in Windows. The localization here should match the one in the Windows product for "Command Prompt" - Ćŏρŷ τêхť âś á ѕίⁿĝłę łįⁿè !!! !!! ! + Čōрγ тэхť áş д śіηĝļе ℓïⁿз !!! !!! ! - Ćŏрў ţêжт !!! + €őрў ţэם !!! - Ďèćґзαѕę ƒоⁿт šīźέ !!! !! + Ðęčґёāšê ƒõⁿţ ŝîźę !!! !! - Đêċřèăşê ƒóñţ ѕίżэ, аmόùʼnт: {0} !!! !!! !!! + Đęćŕєāšė ƒőňţ śíž℮, ămθúпţ: {0} !!! !!! !!! {0} will be replaced with a positive number - đŏщп ! + ďοẃл ! - ℓзƒť ! + ľęƒţ ! - ŕϊĝĥт ! + řϊğĥŧ ! - µρ + űρ - ρгэνîöцš !! + рѓěνĩøùş !! - Ďύрļïςąτé раήê !!! ! + еφŀĩςαťє ρãиε !!! ! - Đυρŀі¢àťё ŧάь !!! + Ďϋрļïćăτę ŧāь !!! - Яųη čοммαńδŀΐňè "{0}" ĭπ ţĥĭš щīńðøẅ !!! !!! !!! ! + Ґμπ ċôмmαⁿďļιʼnê "{0}" ìʼn τħϊѕ ẃĩпđőẃ !!! !!! !!! ! {0} will be replaced with a user-defined commandline - ₣ïňđ ! + ₣ïήð ! - ₣ĩлđ ʼnė×т şéáґċћ mǻтсн !!! !!! + ₣ĩŋđ ŋєхτ šęáгςђ mαţсĥ !!! !!! - ₣îńð рŕэνīōΰŝ şéдŕçћ máţćĥ !!! !!! ! + ₣įпđ ρѓèνïθŭş ѕěăґçħ mάтćĥ !!! !!! ! - İπčг℮άśε ƒοņţ şïźĕ !!! !! + Ĭñçяēåśё ƒбήτ šΐžĕ !!! !! - İʼnĉґεαšё ƒοņт šіżέ, ämбΰйţ: {0} !!! !!! !!! + Íⁿ¢řęªŝě ƒǿηť šιźé, āmōΰήт: {0} !!! !!! !!! {0} will be replaced with a positive number - Μôνє ƒόсџş !!! + Мŏν℮ ƒоćΰŝ !!! - Мбνė ƒσċůѕ {0} !!! ! + Мǿνэ ƒőĉúš {0} !!! ! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", "DirectionDown" - Мòνē ƒóčüš ţσ ťħε ļāѕť üšĕð рăй℮ !!! !!! !!! + Мőνэ ƒóčůš ťб тħε łάşт цŝęð ρąήέ !!! !!! !!! - Мŏνē ƒоċûś ţθ тĥĕ лēжτ φàηĕ ïŋ οяďея !!! !!! !!! ! + Мθνė ƒőςúś ŧŏ ŧн℮ йèхť рăńέ íη ояđеř !!! !!! !!! ! - Μóνë ƒôçцѕ ťθ τĥέ φѓзνΐσµş φãñĕ ίⁿ ŏґďęř !!! !!! !!! !!! + Μòνĕ ƒō¢ùš ťό ţĥė рѓэνΐσџѕ рǻйē ίп θѓďĕг !!! !!! !!! !!! - Мονë ƒǿ絺 тŏ ŧħę ƒîřѕť ρªήé !!! !!! !! + Мôνε ƒôćųŝ ţŏ τђε ƒïѓŝť рαňє !!! !!! !! - Мōνз ƒθćùѕ ŧо ŧђé ρãřєńŧ ράŋε !!! !!! !!! + Мõν℮ ƒøčūŝ ťø ťђě раѓéпτ φáñэ !!! !!! !!! - Мθνë ƒô¢џŝ ŧô тħз čĥìℓđ рăñё !!! !!! !! + Μôνé ƒóčųŝ ŧǿ ţнз ćћϊļð ρãлė !!! !!! !! - Śẅäφ ρäŋε !!! + Şωąр рāлē !!! - Śщăφ ραлé {0} !!! + Ѕẃаφ φäņέ {0} !!! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", "DirectionDown" - Ѕшαφ ρаńзŝ ωįŧн ţђé ℓášť ųśěđ ρаиĕ !!! !!! !!! ! + Ѕшάφ рâπ℮š ẃїтħ τђё ŀåŝτ цѕęδ рáŋę !!! !!! !!! ! - Šẃªφ рάиěѕ ẃïťħ ŧћė ʼnė×т ρāлę ΐñ őяδēг !!! !!! !!! !! + Śẅáр φãлэѕ ẁîţħ ťђê ñέхт рãήě ϊή őґđêґ !!! !!! !!! !! - Şẅãφ ρàⁿεѕ ŵĭŧĥ тћз φяéνΐоùѕ φǻńĕ ΐⁿ οѓďзґ !!! !!! !!! !!! + Śẃåφ φăⁿёś ŵіťћ тĥĕ φŕёνĩòϋŝ раή℮ їń ôґðēѓ !!! !!! !!! !!! - Ŝŵäφ φăņėš ωìţн ťĥę ƒіѓšт рāňє !!! !!! !!! + Ŝщàφ рǻņεš шíţћ ťнĕ ƒίѓѕт ρāńз !!! !!! !!! - Νэẁ ŧāъ !! + Йέẃ ţάь !! - ∏ëω шіņďоω !!! + Ņеŵ щìπðõш !!! - Ĩďëńťιƒý ẅіńđθŵ !!! ! + Íðэήŧїƒγ шίиðøẃ !!! ! - Іđέńťіƒý ẃīⁿðǿώš !!! ! + Ìðĕņτιƒý шϊйδøẅѕ !!! ! - Ņёжţ ťąь !! + Ŋêжŧ ţаь !! - Θрěй ьǿτћ šéţťĩŋğś аñđ ďěƒäûℓт ŝёţтιηğş ƒίĺēş (JSON) !!! !!! !!! !!! !!! + Оφεп ъбŧђ şēţтįлġŝ ąņδ ðэƒàμłţ ѕēтτîňĝš ƒΐℓëś (JSON) !!! !!! !!! !!! !!! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Ωφêη ðэƒаџℓτ ѕéтťїʼnġŝ ƒįŀè (JSON) !!! !!! !!! + Ŏреп ðеƒăûĺť şėттîņĝŝ ƒíļë (JSON) !!! !!! !!! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Οφęη ʼnзẅ τдъ đгòρďôωⁿ !!! !!! + Θрёή ňеш ţäь ďřóρðóŵή !!! !!! - Ωφ℮π śέτţίйĝѕ ƒїĺë (JSON) !!! !!! ! + Őρęň ŝέŧτїйģѕ ƒіĺē (JSON) !!! !!! ! {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Şěτ ŧђè ţâъ ¢οĺøѓ... !!! !!! + Şėŧ ŧнę ťáь ċσŀőґ... !!! !!! - Рåśţε ! + Ρášτé ! - Ρŗĕνїоϋŝ тăв !!! + Ρř℮νϊòϋš ţãъ !!! - Ѓεńαмє ŧǻь тö "{0}" !!! !!! + Ѓзήämė τдв ťσ "{0}" !!! !!! {0} will be replaced with a user-defined string - Γеśэτ ƒóητ ѕїź℮ !!! ! + Гėŝ℮ţ ƒŏήт şїžě !!! ! - Ŕëѕ℮τ ţάв čőľбя !!! ! + Ŕеѕéт τăь сõļоґ !!! ! - Γεѕэŧ τάв ŧιтℓé !!! ! + Řėś℮ŧ тαв τįтℓę !!! ! - Ŕëпам℮ ťαъ τîτĺė... !!! !!! + Гęйªm℮ тåъ ŧīτļĕ... !!! !!! - Ŕêšíżè φáήє !!! + Ґëśîžє рãле !!! - Ѓëŝĭžę ρǻñέ {0} !!! ! + Яэşìżе ρåņê {0} !!! ! {0} will be replaced with one of the four directions "DirectionLeft", "DirectionRight", "DirectionUp", or "DirectionDown" - Šċгόℓŀ đοẁñ !!! + Şςяôĺļ đσши !!! - Şćřõłℓ đőωŋ {0} łΐʼnέ(ŝ) !!! !!! + Śсгθĺł đσẃņ {0} ŀĭпз(ś) !!! !!! {0} will be replaced with the number of lines to scroll" - Ŝċгбľļ ďσщή οπē φàğё !!! !!! + Şсяθℓļ ďοẃη όňê ρдĝę !!! !!! - Şĉŗøℓĺ ύр !!! + Şċгõłł üρ !!! - Šςяòłĺ ϋφ {0} ľίŋё(ş) !!! !!! + Şсѓòľŀ ūρ {0} ℓįňė(š) !!! !!! {0} will be replaced with the number of lines to scroll" - Ŝсŕσłł ùρ øпê ρàġĕ !!! !! + Šсŗøℓľ џφ ōņё ρâğэ !!! !! - Ŝсяοĺĺ ŧô тћє ŧǿφ оƒ ĥϊśţόŕў !!! !!! !! + Ś¢řόłℓ ťο ŧне τŏφ ōƒ ћϊśţбѓý !!! !!! !! - Śçґōŀŀ ťσ τнę ъοţťσм ǿƒ ĥíśťöяγ !!! !!! !!! + Śĉřòℓļ ťó ţħё вǿτтöm ǿƒ нϊŝţóгÿ !!! !!! !!! - Šςґбľŀ ťõ ŧħĕ ρŕένΐôџŝ máяќ !!! !!! !! + Ŝςѓōŀļ то ţħέ ρŕеνïόцś мãřκ !!! !!! !! - Śċŗôℓŀ тθ ŧĥэ ʼnėжτ mαґκ !!! !!! + Śčґòľŀ τо тђê ñежт мāŕќ !!! !!! - Ѕςřθℓĺ ŧó тħз ƒīяśτ mãяĸ !!! !!! ! + Ŝсґŏłℓ тõ ťĥë ƒïгѕτ мàгķ !!! !!! ! - Ѕĉяθŀĺ τõ ţнэ ŀăѕτ мάґĸ !!! !!! + Ŝċřõļℓ тŏ ťћё ľäŝт мáѓќ !!! !!! - Áďď à şċгőľľ mãґĸ !!! !! + ∆đď ª ş¢ŗőļļ mâґķ !!! !! - Ăδð å ŝćгöļĺ mäřк, color:{0} !!! !!! !! + Дďδ ä şċřõĺľ mаřκ, color:{0} !!! !!! !! {Locked="color"}. "color" is a literal parameter name that shouldn't be translated. {0} will be replaced with a color in hexadecimal format, like `#123456` - Čłéаґ мäгķ !!! + Çℓεàŗ мàгќ !!! - Ĉℓėάѓ ãℓł mâŗķš !!! ! + Ċļéāґ àℓĺ màгķś !!! ! - Şěπď Įʼnφŭτ: "{0}" !!! !! + Šêлđ Ìñрµť: "{0}" !!! !! {0} will be replaced with a string of input as defined by the user - Śêţ ςόļōґ ѕ¢ћемέ ţő {0} !!! !!! + Şēţ ċõℓǿґ ѕčћёмē ţо {0} !!! !!! {0} will be replaced with the name of a color scheme as defined by the user. - Ŝèŧ ťãв çőłôѓ ŧõ {0} !!! !!! + Š℮ŧ ŧαъ čõļοя ŧō {0} !!! !!! {0} will be replaced with a color, displayed in hexadecimal (#RRGGBB) notation. - Şρŀιτ рâηε нοřĭżóήťǻℓľу !!! !!! + Ѕφℓϊţ φάиз ĥǿřîżõʼnтαℓĺу !!! !!! - Μоνé ρªñе !!! + Мôνé рαň℮ !!! - Μονз ρãήé тó ŋєẅ щĭʼnðощ !!! !!! + Мőνе φαñє ťõ лêẃ ẃįńδōŵ !!! !!! - Śφľīτ φäлє !!! + Šрℓīт φăηе !!! - Ѕρļίť рåñє νèřτīćąĺŀу !!! !!! + Şрŀíŧ рдņє νęřτíçǻłłỳ !!! !!! - Ѕώΐţċн τσ тáв !!! + Šŵĩтċħ τŏ ťǻв !!! - Ѕŵíτćħ тö ŧĥë ľªѕτ ťāь !!! !!! + Ѕẃітčћ ţõ ťħέ ľάšť ţаь !!! !!! - Śєąѓĉн ƒоѓ τáь... !!! !! + Ѕėàřĉħ ƒôґ ŧâь... !!! !! - Ťοģġŀè áℓŵдỳѕ òʼn ŧθр mǿðē !!! !!! ! + Ŧōĝġļě αŀώªÿŝ òⁿ тοр мöðέ !!! !!! ! - Тоğğļε ċõммàйð рąłĕţτê !!! !!! + Ţōĝğļė čσmmάήđ рåŀęŧŧз !!! !!! - Γέ¢èʼnŧ ċòммªʼnδŝ... !!! !! + Γë¢єйť ćøмmåηđŝ... !!! !! - Øрęⁿ şŭġġěśτιòиš... !!! !!! + Ǿрέʼn şµġġеšŧìŏπѕ... !!! !!! - Ťσğĝĺе ĉоmмąñđ рάłēţτě іⁿ ¢οmмайδ ľîηε møðε !!! !!! !!! !!! + Ťŏġĝļĕ çбmмáήď φàłėŧτĕ ίη ċσmмäпδ ℓìņє mόð℮ !!! !!! !!! !!! - Τбġģĺê ƒоĉùś мøďέ !!! !! + Ţоģġľе ƒöςŭś mσďē !!! !! "Focus mode" is a mode with minimal UI elements, for a distraction-free experience - Ēηªъŀє ƒŏсũš мőðė !!! !! + Еⁿâьℓё ƒσсùѕ мôðé !!! !! - Ðīşáьĺë ƒо¢űš мŏδĕ !!! !! + Đīѕдвł℮ ƒосύš mõðé !!! !! - Ŧбġğĺë ƒųłℓşсѓêëи !!! !! + Тоğġℓē ƒϋłłśčŕęéи !!! !! - Σйάьĺë ƒůĺℓŝςŕэзи !!! !! + Ėлдъļė ƒŭŀℓŝçŕěёη !!! !! - Đΐŝдъℓē ƒµĺľѕčяèеŋ !!! !! + Ðιšåвłз ƒűŀĺšςŕéэη !!! !! - Μâжιmîźê ŵîⁿδōщ !!! ! + Мā×įmїžё щìйđōŵ !!! ! - Ґèşтοгë mάжιмížëδ ẁіηδοẁ !!! !!! ! + Геśţóŗ℮ mã×îмĭżëď ώîйďöẃ !!! !!! ! - Ţøġģĺ℮ рǻиê şρŀίţ ŏяїеπŧªţίòή !!! !!! !!! + Ŧόğĝℓέ рάлê šрľîť òяίĕŋτªŧĭőⁿ !!! !!! !!! - Тöğğľę рàŋê źǿόm !!! ! + Ťöģĝł℮ рªńέ żθθm !!! ! - Ťóġġĺε ρąņë řεãð-õйļý мοδέ !!! !!! ! + Ŧōĝğľé ρåŋê яэãď-όńļу моđě !!! !!! ! - Ëńǻвŀė рдⁿε ŗēáď-öήŀγ möđē !!! !!! ! + Ęňǻъℓ℮ ρåηé яěǻδ-όňłу мбδэ !!! !!! ! - Ðіśάъł℮ φàлě ґ℮àδ-ǿиℓу мбδě !!! !!! !! + Ďіšªвłē φãηé яĕāď-σņĺў мοδë !!! !!! !! - Ţòģģłė ťěґмїńаĺ νĭšúªŀ 胃έçτѕ !!! !!! !!! + Ţóģğļ℮ ŧëгміпął νіšúąļ єƒƒеčţš !!! !!! !!! - Вŕĕаĸ ĩпţό ţђé δ℮ьцģģěř !!! !!! + Вгεаĸ ĩňŧθ ŧђė đěвцģģέŕ !!! !!! - Оφěй şэťŧïńĝѕ... !!! ! + Öφéη ѕēττϊⁿğš... !!! ! - Ŗέʼnämè ẁĩńδóώ тò "{0}" !!! !!! + Γзňãмē шіňďоώ ŧō "{0}" !!! !!! {0} will be replaced with a user-defined string - Ŗеŝэτ щιņδõщ лãmè !!! !! + Ŗεšзť ωĩйδōẁ ñâмé !!! !! - Ґ℮йаm℮ ώΐⁿďόώ... !!! ! + Ґёʼnάmë шϊйďθŵ... !!! ! - Ðīšφļâγ Τέŕmΐńªŀ'ѕ ċύŗґĕⁿτ ŵòяκîñġ ðĭřĕĉťóŗŷ !!! !!! !!! !!! ! + Ďіŝρłάỳ Τ℮ѓmìйаĺ'š čûŗяēʼnτ ώоřκìņĝ ďιяęĉтσґỳ !!! !!! !!! !!! ! - Šћőш/Ηίðē ťħε Ťěřмΐñął ŵϊņďǿẅ !!! !!! !!! + Şђбщ/Ĥīđз τĥз Ŧëяmіńаℓ ẅіηďоŵ !!! !!! !!! - Śћóώ/Ήĩđę Qŭâќё ẃΐήδõω !!! !!! + Şђõẅ/Нϊđε Qџдĸє ẃįπđŏẃ !!! !!! Quake is a well-known computer game and isn't related to earthquakes, etc. See https://en.wikipedia.org/wiki/Quake_(video_game) - ₣öćųś рãήέ {0} !!! ! + ₣ôĉύѕ рàņε {0} !!! ! {0} will be replaced with a user-specified number - Ĕ×рõяτ ŧёжţ тό {0} !!! !! + Ε×рοŗţ ŧë×т ťσ {0} !!! !! {0} will be replaced with a user-specified file path - È×рóŕţ ťêжť !!! + Ε×ρθŗт ŧęхτ !!! - Ćŀěāѓ ьŭƒƒэŕ !!! + Çłĕăг ьũƒƒèŗ !!! A command to clear the entirety of the Terminal output buffer - Čļĕǻř νìēώρоŗŧ !!! ! + Ĉļзãг νίзώрбŗť !!! ! A command to clear the active viewport of the Terminal - Сĺėāŕ š¢řθℓℓвάсķ !!! ! + Ćℓёäŕ šçѓоℓŀьäçķ !!! ! A command to clear the part of the buffer above the viewport - £ĕţ Ẅΐņďθщŝ ðęçїďе !!! !! + Ľëτ Ẁιñďõŵš đěćîðé !!! !! This is the default option if a user doesn't choose to override Microsoft's choice of a Terminal. - Μĩ¢ґōśθƒť €όгφōѓâτїõñ !!! !!! + Μī¢řόѕõƒŧ Ćŏŕрσгåтΐøπ !!! !!! Paired with `InboxWindowsConsoleName`, this is the application author... which is us: Microsoft. - Щїⁿδŏωѕ Çбήšŏŀē Ήǿѕţ !!! !!! + Ẃîпďōώś €ōŋşбļэ Ήбѕτ !!! !!! Name describing the usage of the classic windows console as the terminal UI. (`conhost.exe`) - Qϋіť τнé Тέѓmіŋάļ !!! !! + Qυϊτ ŧħз Τέяmίŋăŀ !!! !! - Ѕęţ ьàçĸĝяоųŋδ õφªςϊŧγ... !!! !!! ! + Ѕěт ьªċķĝґøūņð óφǻĉїτў... !!! !!! ! - Íπċгĕàš℮ ьăćķġѓǿùиð όραċĭŧý ъý {0}% !!! !!! !!! ! + Ĭñçŕ℮àŝę ваċкġřŏųлď ǿφāςíτу ьỳ {0}% !!! !!! !!! ! A command to change how transparent the background of the window is - Đĕċřèäśê вαćķģřōűʼnď őφäĉîту ву {0}% !!! !!! !!! ! + Ďέсŕēāšε ъªċĸğгóüиđ õρąсïту ьγ {0}% !!! !!! !!! ! A command to change how transparent the background of the window is - Ѕέт вαċķĝѓòũⁿđ őρăćϊтỳ ťŏ {0}% !!! !!! !!! + Śêţ вąćķģřŏυńδ õφãςįţÿ ţθ {0}% !!! !!! !!! A command to change how transparent the background of the window is - Ŕεśŧοґĕ ţћę ĺåşŧ ćłŏşзð рāηē оґ тαъ !!! !!! !!! ! + Г℮ѕτøŕé тђě ŀάѕŧ ςĺσşëð рдиè ôг ŧàь !!! !!! !!! ! - Ŝэļеćŧ ãℓľ ţĕжŧ !!! ! + Śēĺèċť ªĺľ ťėхť !!! ! - Ŧθĝğĺ℮ måřĸ мóđέ !!! ! + Ŧõğģļє mагĸ mθðé !!! ! A command that will toggle "mark mode". This is a mode in the application where the user can mark the text by selecting portions of the text. - Ţǿġģłě вľŏск ŝéĺ℮¢ŧίōπ !!! !!! + Ťøġğŀę ъĺôĉκ ѕëŀèçτіöŋ !!! !!! - Śщìŧċĥ ѕěℓεĉτιбл éńðρöïñŧ !!! !!! ! + Şшїτçн şėļèςţίǿл ĕηðρóΐńт !!! !!! ! - åŀℓ мàŧçћēѕ !!! + ãℓļ мåŧćћєŝ !!! This is used in the description of an action which changes the color of selected text, to indicate that not only the selected text will be colored, but also all matching text. - Ćŏŀǿř ŝеℓёċτīôη, ƒθгěğŕóùйð: {0}{1} !!! !!! !!! ! + Çσľøř ѕĕĺęćťιбņ, ƒøŗěĝяöùńđ: {0}{1} !!! !!! !!! ! This is the description of an action which changes the color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Čбłøѓ šёℓêςŧĭσⁿ, ьάςκġяσµπď: {0}{1} !!! !!! !!! ! + Çöłôř śэļě¢ŧΐǿл, ьǻсќĝгбυňđ: {0}{1} !!! !!! !!! ! This is the description of an action which changes the background color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Čôłσř ŝέĺėćťĩőп, ƒǿгзĝŗóΰⁿđ: {0}, вǻ¢кġѓőųηď: {1}{2} !!! !!! !!! !!! !!! + Ćòľóř šєł℮ςťїöⁿ, ƒòřέģŗǿџπð: {0}, ъάċќğґōϋиď: {1}{2} !!! !!! !!! !!! !!! This is the description of an action which changes the color of selected text and the background. {0}: the foreground color. {1}: the background color. {2}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - Čøĺóѓ šėļéćţìõń, (ðёƒǻύļţ ƒθřęġŕθųʼnδ/ьά¢кĝґσūⁿδ){0} !!! !!! !!! !!! !!! + Ĉøłбѓ ś℮ŀêċťΐõŋ, (đ℮ƒаűļŧ ƒоґёġřθϋηď/вăсķĝŗòμŋď){0} !!! !!! !!! !!! !!! This is the description of an action which changes the color of selected text and the background to the default colors. {0}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). - [δεƒāúľŧ] !!! + [ðеƒǻúĺŧ] !!! This is used in the description of an action which changes the color of selected text, as a placeholder for a color, to indicate that the default (foreground or background) color will be used. - ьℓαск ! + ьłâсќ ! A color used in the "ColorSelection" action. - řēđ + řéδ A color used in the "ColorSelection" action. - ğгεэŋ ! + ğŕêεŋ ! A color used in the "ColorSelection" action. - γёļŀōώ ! + ýεľŀбẅ ! A color used in the "ColorSelection" action. - вℓµè ! + ьŀü℮ ! A color used in the "ColorSelection" action. - φūѓρľė ! + рũгφŀέ ! A color used in the "ColorSelection" action. - ςỳãл ! + çÿåń ! A color used in the "ColorSelection" action. - щħϊтэ ! + ŵђϊţе ! A color used in the "ColorSelection" action. - ъяϊģнт вľäĉķ !!! + ьřïġћт ъℓāčķ !!! A color used in the "ColorSelection" action. - ьяìģħŧ ѓєδ !!! + ьѓīğнτ řέδ !!! A color used in the "ColorSelection" action. - ьŗΐģħŧ ĝřєĕл !!! + ъґΐğћť ğѓέεⁿ !!! A color used in the "ColorSelection" action. - вřįġђŧ ýěĺĺòω !!! + ьяīĝђт ýεłŀǿẅ !!! A color used in the "ColorSelection" action. - вřìğћŧ ъĺϋз !!! + вяĭġħт ьℓцέ !!! A color used in the "ColorSelection" action. - вѓίğђŧ φύґρĺë !!! + ъяîĝнť ρúřρľε !!! A color used in the "ColorSelection" action. - вŕїġĥт ĉýąņ !!! + вгїğħť ćÿªń !!! A color used in the "ColorSelection" action. - ьŕΐġħţ ώђїťę !!! + вŕīğħť ẅнĭŧэ !!! A color used in the "ColorSelection" action. - É×рдпđ şęľėĉţϊõñ ŧõ ώóѓđ !!! !!! ! + Зхрάиď śёľęςŧіôņ τό щòгδ !!! !!! ! - Ŝнóщ ċõⁿţěжτ мєñµ !!! !! + Şнöẃ ćóηтë×ŧ мèⁿμ !!! !! - €ŀόŝэ ąļļ бţĥėř ρªń℮ѕ !!! !!! + Сļôѕз ăľŀ øţнèŕ φáʼnêś !!! !!! - Τǿġģľє ьřøαđсâšť ΐņрΰт ŧò аļĺ ρåŋ℮ѕ !!! !!! !!! ! + Тōģĝℓ℮ ьяοäð¢ªѕт ïʼnρύτ ţò äļℓ φаиεś !!! !!! !!! ! When enabled, input will go to all panes in this tab simultaneously - Яêšţªŕŧ ¢ôŋŋёćŧίõʼn !!! !! + Ŗ℮ŝţāґт čõʼnлęčŧįоή !!! !! - Śéľёĉт иëжт ĉόмmåπđ øűтφџţ !!! !!! ! + Šēłεĉτ πĕжţ ċômmàлð ŏµŧρūţ !!! !!! ! - Şεŀёςт ряένιõύş ςòmмаⁿð бūţρůť !!! !!! !!! + Śеľê¢ť φŕзνíôùś ςθммãиð σùтφμť !!! !!! !!! - Şêĺëċţ ñз×т çómmăŋδ !!! !!! + Ѕĕŀёĉτ πежт ¢όmmªήď !!! !!! - Ŝêłзсť φŗĕνΐőüŝ çŏmmâήđ !!! !!! + Ѕέļεćť ргėνîóųѕ çōmmдпð !!! !!! - Śèαѓсħ {0} !!! + Šєāяćĥ {0} !!! {0} will be replaced with a user-specified URL to a web page - Śеāŗčĥ ŧђė ẃęъ ƒоŕ ŝęℓĕсŧзđ тз×ŧ !!! !!! !!! + Ѕεάгςн τђё ẅēъ ƒŏŕ śзĺėċţêđ ţехť !!! !!! !!! This will open a web browser to search for some user-selected text - Òφéʼn äвóüŧ δįäľόğ !!! !! + Οφёņ ǻвőųŧ đĭдłбģ !!! !! This will open the "about" dialog, to display version info and other documentation \ No newline at end of file diff --git a/tools/ConvertTo-PseudoLocalization.ps1 b/tools/ConvertTo-PseudoLocalization.ps1 new file mode 100644 index 00000000000..93e292a6a86 --- /dev/null +++ b/tools/ConvertTo-PseudoLocalization.ps1 @@ -0,0 +1,119 @@ +param( + [Parameter(Mandatory=$True)] + [string]$Path +) + +# As per PseudoReplacementDefs.xml in LocStudio +$mapping = [hashtable]::new() +$mapping['a'[0]] = 'ªàáâãäåāăąǻάαад' +$mapping['A'[0]] = 'ÀÁÂÃÄÅĀĂĄǺΆΑ∆ΔΛАД' +$mapping['b'[0]] = 'вьъ' +$mapping['B'[0]] = 'ΒßβВЪЬБ' +$mapping['c'[0]] = '¢çćĉċčсς' +$mapping['C'[0]] = 'ÇĆĈĊČС€' +$mapping['d'[0]] = 'ðďđδ' +$mapping['D'[0]] = 'ÐĎĐ' +$mapping['e'[0]] = 'èéêëēĕėęěέεеёє℮зэ' +$mapping['E'[0]] = 'ÈÉÊËĒĔĖĘĚΈΕΣЕ∑ЁЄЗЄЭ' +$mapping['f'[0]] = 'ƒ' +$mapping['F'[0]] = '₣' +$mapping['g'[0]] = 'ĝğġģ' +$mapping['G'[0]] = 'ĜĞĠĢ' +$mapping['h'[0]] = 'ĥħнћђ' +$mapping['H'[0]] = 'ĤĦΉΗН' +$mapping['i'[0]] = 'ìíîïĩīĭįίιϊіїΐ' +$mapping['I'[0]] = 'ÌÍÎĨĪĬĮİΊΪІЇ' +$mapping['j'[0]] = 'ĵј' +$mapping['J'[0]] = 'ĴЈ' +$mapping['k'[0]] = 'ķĸκкќ' +$mapping['K'[0]] = 'ĶΚЌК' +$mapping['l'[0]] = 'ĺļľŀłℓ' +$mapping['L'[0]] = '£ĹĻĽĿŁ₤' +$mapping['m'[0]] = 'mм' +$mapping['M'[0]] = 'ΜМ' +$mapping['n'[0]] = 'ийлⁿпπήηńņňʼnŋñ' +$mapping['N'[0]] = 'ÑŃŅŇŊΝИЙЛП∏' +$mapping['o'[0]] = 'òóôõöøōŏőοσόоǿθб' +$mapping['O'[0]] = 'ÒÓÔÕÖØŌŎŐǾΌΘΟΦΩОФΩΏ' +$mapping['p'[0]] = 'φρр' +$mapping['P'[0]] = 'ΡР' +$mapping['r'[0]] = 'ŕŗřяѓґгř' +$mapping['R'[0]] = 'ŔŖŘЯΓЃҐГ' +$mapping['s'[0]] = 'śŝşѕš' +$mapping['S'[0]] = 'ŚŜŞЅŠ' +$mapping['t'[0]] = 'ţťŧτт' +$mapping['T'[0]] = 'ŢŤŦΤТ' +$mapping['u'[0]] = 'µùúûüũūŭůűųΰυϋύцμџ' +$mapping['U'[0]] = 'ÙÚÛÜŨŪŬŮŰŲЏЦ' +$mapping['v'[0]] = 'ν' +$mapping['w'[0]] = 'ŵωώшщẁẃẅ' +$mapping['W'[0]] = 'ŴШЩẀẂẄ' +$mapping['x'[0]] = '×хж' +$mapping['X'[0]] = 'ΧχХЖ' +$mapping['y'[0]] = 'ýÿŷγўỳу' +$mapping['Y'[0]] = '¥ÝŶΎΥΫỲЎ' +$mapping['z'[0]] = 'źżž' +$mapping['Z'[0]] = 'ŹŻΖŽ' + +[xml]$content = Get-Content $Path + +foreach ($entry in $content.root.data) { + $value = $entry.value + $comment = $entry.comment ?? '' + $placeholders = @{} + + if ($comment.StartsWith('{Locked')) { + # Skip {Locked} and {Locked=qps-ploc} entries + if ($comment -match '\{Locked(\}|=[^}]*qps-ploc).*') { + continue + } + + $lockedList = ($comment -replace '\{Locked=(.*?)\}.*','$1') -split ',' + $placeholderChar = 0xE000 + + # Replaced all locked words with placeholders from the Unicode Private Use Area + foreach ($locked in $lockedList) { + if ($locked.StartsWith('"') -and $locked.EndsWith('"')) { + $locked = $locked.Substring(1, $locked.Length - 2) + $locked = $locked.Replace('\"', '"') + } + + $placeholder = "$([char]$placeholderChar)" + $placeholderChar++ + + $placeholders[$placeholder] = $locked + $value = $value.Replace($locked, $placeholder) + } + } + + # We can't rely on $entry.name.GetHashCode() to be consistent across different runs, + # because in the future PowerShell may enable UseRandomizedStringHashAlgorithm. + $hash = [System.Text.Encoding]::UTF8.GetBytes($entry.name) + $hash = [System.Security.Cryptography.SHA1]::Create().ComputeHash($hash) + $hash = [System.BitConverter]::ToInt32($hash) + + # Replace all characters with pseudo-localized characters + $rng = [System.Random]::new($hash) + $newValue = '' + foreach ($char in $value.ToCharArray()) { + if ($m = $mapping[$char]) { + $newValue += $m[$rng.Next(0, $mapping[$char].Length)] + } else { + $newValue += $char + } + } + + # Replace all placeholders with their original values + foreach ($kv in $placeholders.GetEnumerator()) { + $newValue = $newValue.Replace($kv.Key, $kv.Value) + } + + # Add 40% padding to the end of the string + $paddingLength = [System.Math]::Round(0.4 * $newValue.Length) + $padding = ' !!!' * ($paddingLength / 4 + 1) + $newValue += $padding.Substring(0, $paddingLength) + + $entry.value = $newValue +} + +return $content From 2ee7783f235ae335c19dd089b8fe10fe8cf878ab Mon Sep 17 00:00:00 2001 From: Windows Console Service Bot <14666831+consvc@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:01:42 -0500 Subject: [PATCH 177/603] Localization Updates - main - 03/25/2024 19:34:25 (#16934) --- .../Resources/de-DE/Resources.resw | 32 +++++++++++++++++++ .../Resources/es-ES/Resources.resw | 32 +++++++++++++++++++ .../Resources/fr-FR/Resources.resw | 32 +++++++++++++++++++ .../Resources/it-IT/Resources.resw | 32 +++++++++++++++++++ .../Resources/ja-JP/Resources.resw | 32 +++++++++++++++++++ .../Resources/ko-KR/Resources.resw | 32 +++++++++++++++++++ .../Resources/pt-BR/Resources.resw | 32 +++++++++++++++++++ .../Resources/ru-RU/Resources.resw | 32 +++++++++++++++++++ .../Resources/zh-CN/Resources.resw | 32 +++++++++++++++++++ .../Resources/zh-TW/Resources.resw | 32 +++++++++++++++++++ 10 files changed, 320 insertions(+) diff --git a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw index c29152a2373..5c27fe81845 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw @@ -1218,6 +1218,38 @@ Experimentellen Passthrough für virtuelles Terminal aktivieren An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Menü bei Rechtsklick anzeigen + This controls how a right-click behaves in the terminal + + + Wenn diese Option aktiviert ist, zeigt das Terminal ein Menü an, wenn Sie mit der rechten Maustaste klicken. Wenn diese Option deaktiviert ist, wird durch Klicken mit der rechten Maustaste der markierte Text kopiert (oder eingefügt, wenn keine Auswahl erfolgt). + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Anzeigen von Markierungen auf der Scrollleiste + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Wenn diese Option aktiviert ist, zeigt das Terminal Markierungen in der Scrollleiste an, wenn nach Text gesucht oder die Shellintegration aktiviert ist. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Prompts beim Drücken der EINGABETASTE automatisch markieren + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Wenn diese Option aktiviert ist, fügt das Terminal automatisch eine Markierung hinzu, die die Position des Befehlsendes angibt, wenn Sie die EINGABETASTE drücken. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Experimentell: Positionieren des Cursors mit Mausklicks + This allows the user to move the text cursor just by clicking with the mouse. + + + Wenn diese Option aktiviert ist, wird der Cursor durch Klicken innerhalb des Prompts an diese Position verschoben. Dies erfordert, dass die Shellintegration in Ihrer Shell aktiviert ist, damit sie erwartungsgemäß funktioniert. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + Hörbar An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index fd1ac2ce47b..ef08ff08fe1 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -1218,6 +1218,38 @@ Habilitar paso a través de terminal virtual experimental An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Mostrar un menú al hacer clic con el botón derecho + This controls how a right-click behaves in the terminal + + + Cuando se habilite, el terminal mostrará un menú al hacer clic con el botón derecho. Cuando se deshabilite, al hacer clic con el botón derecho se copiará el texto seleccionado (o se pegará si no hay ninguna selección). + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Mostrar marcas en la barra de desplazamiento + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Cuando está habilitado, el terminal mostrará marcas en la barra de desplazamiento al buscar texto o cuando la integración de shell esté habilitada. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Marcar automáticamente los avisos al presionar Entrar + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Cuando se habilita, el terminal agrega automáticamente una marca que indica la posición del final del comando al presionar Entrar. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Experimental: cambiar la posición del cursor con clics del mouse + This allows the user to move the text cursor just by clicking with the mouse. + + + Cuando se habilita, al hacer clic dentro del aviso se moverá el cursor a esa posición. Esto requiere que la integración de shell esté habilitada en el shell para funcionar de la forma esperada. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + Audible An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index 18484d3a24f..37caf840fca 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -1218,6 +1218,38 @@ Activer le relais de terminal virtuel expérimental An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Afficher un menu en cliquant avec le bouton droit + This controls how a right-click behaves in the terminal + + + Lorsque cette option est activée, le terminal affiche un menu en cliquant avec le bouton droit. Si cette option est désactivée, le fait de cliquer avec le bouton droit copie le texte sélectionné (ou le coller en l’absence de sélection). + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Afficher les marques sur la barre de défilement + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Quand cette option est activée, le terminal affiche les marques dans la barre de défilement lors de la recherche de texte ou lorsque l’intégration de l’interpréteur de commandes est activée. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Marquer automatiquement les invites en appuyant sur Entrée + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Lorsque cette option est activée, le terminal ajoute automatiquement une marque indiquant la position de la fin de la commande lorsque vous appuyez sur Entrée. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Expérimental : repositionner le curseur avec des clics de souris + This allows the user to move the text cursor just by clicking with the mouse. + + + Lorsque cette option est activée, le fait de cliquer à l’intérieur de l’invite déplace le curseur vers cette position. Cela nécessite que l’intégration de l’interpréteur de commandes soit activée dans votre interpréteur de commandes pour fonctionner comme prévu. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + Sonore An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 14e06c19d18..cd06973a50e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -1218,6 +1218,38 @@ Abilitare il pass-through sperimentale del terminale virtuale An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Visualizzare un menu al clic con il pulsante destro del mouse + This controls how a right-click behaves in the terminal + + + Se questa opzione è abilitata, il terminale visualizzerà un menu al clic con il pulsante destro del mouse. Se questa opzione è disabilitata, facendo clic con il pulsante destro del mouse verrà copiato il testo selezionato o incollato se non è presente alcuna selezione. + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Visualizzare indicatori sulla barra di scorrimento + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Se questa opzione è abilitata, il terminale visualizzerà i segni nella barra di scorrimento durante la ricerca di testo o quando è abilitata l'integrazione della shell. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Contrassegnare automaticamente i prompt premendo INVIO + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Se questa opzione è abilitata, il terminale aggiunge automaticamente un indicatore che indica la posizione della fine del comando quando si preme INVIO. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Sperimentale: riposizionare il cursore con i clic del mouse + This allows the user to move the text cursor just by clicking with the mouse. + + + Se questa opzione è abilitata, facendo clic all'interno del prompt il cursore verrà spostato in tale posizione. Ciò richiede che l'integrazione della shell sia abilitata nella shell dell’utente per funzionare come previsto. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + Udibili An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index b3b67ed772f..a8935f14470 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -1218,6 +1218,38 @@ 試験的な仮想ターミナル パススルーを有効にする An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + 右クリックでメニューを表示する + This controls how a right-click behaves in the terminal + + + 有効にすると、ターミナルは右クリックでメニューを表示します。無効にすると、右クリックすると選択したテキストがコピーされます (選択されていない場合は貼り付けます)。 + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + スクロール バーにマークを表示する + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + 有効にすると、ターミナルでテキストを検索するとき、またはシェル統合が有効になっているときに、スクロール バーにマークが表示されます。 + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Enter キーを押したときにプロンプトを自動的にマークする + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + 有効にすると、ターミナルは Enter キーを押したときにコマンドの終了位置を示すマークを自動的に追加します。 + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + 試験段階: マウスのクリックでカーソルの位置を変更する + This allows the user to move the text cursor just by clicking with the mouse. + + + 有効にすると、プロンプト内をクリック時にカーソルがその位置に移動します。これを行うには、シェルでシェル統合を有効にし、期待どおりに動作させる必要があります。 + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + 音によるチャイム An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index d3a2cf1d262..9c8f8e9663f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -1218,6 +1218,38 @@ 실험적 가상 터미널 통과 사용 An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + 마우스 오른쪽 단추를 클릭할 때 메뉴 표시 + This controls how a right-click behaves in the terminal + + + 사용하도록 설정하면 터미널에서 마우스 오른쪽 단추를 클릭할 때 메뉴가 표시됩니다. 사용하지 않도록 설정하면 마우스 오른쪽 단추를 클릭하면 선택한 텍스트가 복사됩니다(또는 선택 항목이 없는 경우 붙여넣기). + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + 스크롤 막대에 마크 표시 + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + 사용하도록 설정하면 터미널은 텍스트를 검색하거나 셸 통합을 사용할 때 스크롤 막대에 표시를 표시합니다. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + <Enter> 키를 누를 때 자동으로 프롬프트 표시 + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + 사용하도록 설정하면 Enter 키를 누를 때 터미널에서 명령의 끝 위치를 나타내는 표시를 자동으로 추가합니다. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + 실험적: 마우스 클릭으로 커서 위치 변경 + This allows the user to move the text cursor just by clicking with the mouse. + + + 활성화된 경우 프롬프트 내부를 클릭하면 커서가 해당 위치로 이동합니다. 예상대로 작동하려면 셸에서 셸 통합이 활성화되어 있어야 합니다. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + 들을 수 있음 An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index 86193e7bf62..543cb2ec43d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -1218,6 +1218,38 @@ Habilitar passagem de terminal virtual experimental An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Exibir um menu ao clicar com o botão direito do mouse + This controls how a right-click behaves in the terminal + + + Quando habilitado, o Terminal exibirá um menu ao clicar com o botão direito do mouse. Quando desabilitado, clicar com o botão direito do mouse copiará o texto selecionado (ou colará se não houver seleção). + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Exibir marcas na barra de rolagem + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Quando habilitado, o Terminal exibirá marcas na barra de rolagem ao procurar texto ou quando a integração do shell estiver habilitada. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Marcar automaticamente os prompts ao pressionar Enter + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Quando habilitado, o Terminal adiciona automaticamente uma marca indicando a posição do final do comando quando você pressiona Enter. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Experimental: reposicionar o cursor com cliques do mouse + This allows the user to move the text cursor just by clicking with the mouse. + + + Quando habilitado, clicar dentro do prompt moverá o cursor para essa posição. Isso exige que a integração do shell seja habilitada no shell para funcionar conforme o esperado. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + Audível An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index 3442857dd67..3af0c8f8f6b 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -1218,6 +1218,38 @@ Включить экспериментальную сквозную проверку на виртуальном терминале An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + Отображать меню при щелчке правой кнопкой мыши + This controls how a right-click behaves in the terminal + + + Если этот параметр включен, терминал отображает меню правой кнопкой мыши. Если этот параметр отключен, при нажатии правой кнопкой мыши будет скопироваться выделенный текст (или вставить, если нет выделенного фрагмента). + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + Отображать метки на полосе прокрутки + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + Если этот параметр включен, Терминал будет отображать метки на полосе прокрутки при поиске текста или при включенной интеграции оболочки. + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + Автоматически помечать запросы при нажатии клавиши ВВОД + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + Если этот параметр включен, Терминал автоматически добавляет метку, указывающую позицию конца команды при нажатии клавиши ВВОД. + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + Экспериментальная функция: изменение положения курсора щелчками мыши + This allows the user to move the text cursor just by clicking with the mouse. + + + Если этот параметр включен, при щелчке внутри запроса курсор перемещается в эту позицию. Для правильного выполнения этой операции необходимо включить интеграцию оболочки в вашей оболочке. + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + Звук An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 08067344f73..0d602e9adc0 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -1218,6 +1218,38 @@ 启用实验性虚拟终端传递 An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + 右键单击时显示菜单 + This controls how a right-click behaves in the terminal + + + 启用后,终端将在右键单击时显示菜单。禁用后,右键单击将复制所选文本 (,如果没有选择) 则粘贴。 + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + 在滚动条上显示标记 + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + 启用后,终端将在搜索文本或启用 shell 集成时在滚动条中显示标记。 + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + 按 Enter 时自动标记提示 + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + 启用后,终端在按 Enter 时自动添加一个指示命令结尾位置的标记。 + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + 实验性: 通过鼠标单击重新定位光标 + This allows the user to move the text cursor just by clicking with the mouse. + + + 启用后,在提示中单击会将光标移动到该位置。这需要在 shell 中启用 shell 集成才能按预期工作。 + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + 声音 An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index d55ecef2a1f..1b1ffb8bbf2 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -1218,6 +1218,38 @@ 啟用實驗性虛擬終端機通道 An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + + 在以滑鼠右鍵按一下時顯示選單 + This controls how a right-click behaves in the terminal + + + 啟用時,終端機會在單擊滑鼠右鍵時顯示功能表。停用時,若沒有選取專案),按鼠右鍵將會複製選取的文字 (或貼上。 + A description for what the "Display a menu on right-click" setting does. Presented near "Profile_RightClickContextMenu". + + + 在捲軸上顯示標記 + "Marks" are small visual indicators that can help the user identify the position of useful info in the scrollbar + + + 啟用時,當搜尋文字或啟用殼層整合時,終端機會在滾動條中顯示標記。 + A description for what the "Display marks on the scrollbar" setting does. Presented near "Profile_ShowMarks". + + + 按下 Enter 鍵時自動標記提示 + "Enter" is the enter/return key on the keyboard. This will add a mark indicating the position of a shell prompt when the user presses enter. + + + 啟用時,當您按下 Enter 時,終端機會自動新增指示命令結尾位置的標記。 + A description for what the "Automatically mark prompts on pressing Enter" setting does. Presented near "Profile_AutoMarkPrompts". + + + 實驗性: 使用滑鼠點擊來調整游標位置 + This allows the user to move the text cursor just by clicking with the mouse. + + + 啟用時,按一下提示內部就會將游標移到該位置。這需要啟用您殼層中的殼層整合,才能如預期地運作。 + A description for what the "Reload environment variables" setting does. Presented near "Profile_RepositionCursorWithMouse". + 有聲 An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. From 08dc34612058355b4e07b8e59ba33d09d43ee656 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Mar 2024 09:03:05 -0700 Subject: [PATCH 178/603] Refactor Pane to be able to host non-terminal content (#16170) Instead of `Pane` hosting a `TermControl` directly, it now hosts an `IPaneContent`. This is an abstraction between the TermControl and the pane itself, to allow for arbitrary implementations of `IPaneContent`, with things that might not be terminals. ## References and Relevant Issues * #997 * #1000 ## Detailed Description of the Pull Request / Additional comments This PR by itself doesn't do much. It's just a refactoring. - It doesn't actually add any other types of pane content. - It overall just tries to move code whenever possible, with as little refactoring as possible. There are some patterns from before that don't super scale well to other types of pane content (think: the `xyzChanged` events on `IPaneContent`). - There's a few remaining places where Pane is explicitly checking if its content is a terminal. We probably shouldn't, but meh There are two follow-up PRs to this PR: * #16171 * #16172 In addition, there's more work to be done after these merge: * TODO! issue number for "Replace `IPaneContent::xyzChanged` with `PropertyChanged` events" * TODO! issue number for "Re-write state restoration so panes don't produce `NewTerminalArgs`" ## Validation Steps Performed * It still launches * It still works * Broadcasting still works * The weird restart connection thing from #16001 still works ## PR Checklist - [x] Closes #997 ## other PRs * #16170 <-- you are here * #16171 * #16172 --- .../LocalTests_TerminalApp/TabTests.cpp | 20 +- src/cascadia/LocalTests_TerminalApp/pch.h | 2 + .../TerminalApp/AppActionHandlers.cpp | 2 +- src/cascadia/TerminalApp/IPaneContent.idl | 52 ++ src/cascadia/TerminalApp/Pane.cpp | 492 +++++------------- src/cascadia/TerminalApp/Pane.h | 55 +- src/cascadia/TerminalApp/TabManagement.cpp | 4 +- .../TerminalApp/TerminalAppLib.vcxproj | 8 + src/cascadia/TerminalApp/TerminalPage.cpp | 48 +- src/cascadia/TerminalApp/TerminalPage.h | 7 +- .../TerminalApp/TerminalPaneContent.cpp | 332 ++++++++++++ .../TerminalApp/TerminalPaneContent.h | 102 ++++ .../TerminalApp/TerminalPaneContent.idl | 21 + src/cascadia/TerminalApp/TerminalTab.cpp | 326 +++++++----- src/cascadia/TerminalApp/TerminalTab.h | 34 +- src/cascadia/TerminalApp/pch.h | 1 + 16 files changed, 925 insertions(+), 581 deletions(-) create mode 100644 src/cascadia/TerminalApp/IPaneContent.idl create mode 100644 src/cascadia/TerminalApp/TerminalPaneContent.cpp create mode 100644 src/cascadia/TerminalApp/TerminalPaneContent.h create mode 100644 src/cascadia/TerminalApp/TerminalPaneContent.idl diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index 4a09828c680..d21497a51d9 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -1326,7 +1326,7 @@ namespace TerminalAppLocalTests const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); }); TestOnUIThread([&page]() { @@ -1344,7 +1344,7 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(controlSettings); Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); // And we should have stored a function to revert the change. VERIFY_ARE_EQUAL(1u, page->_restorePreviewFuncs.size()); @@ -1366,7 +1366,7 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(controlSettings); Log::Comment(L"Color should be changed"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); // After preview there should be no more restore functions to execute. VERIFY_ARE_EQUAL(0u, page->_restorePreviewFuncs.size()); @@ -1394,7 +1394,7 @@ namespace TerminalAppLocalTests const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); }); TestOnUIThread([&page]() { @@ -1412,7 +1412,7 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(controlSettings); Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); }); TestOnUIThread([&page]() { @@ -1428,7 +1428,7 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(controlSettings); Log::Comment(L"Color should be the same as it originally was"); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); }); Log::Comment(L"Sleep to let events propagate"); Sleep(250); @@ -1450,7 +1450,7 @@ namespace TerminalAppLocalTests const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); }); TestOnUIThread([&page]() { @@ -1467,7 +1467,7 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(controlSettings); Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); }); TestOnUIThread([&page]() { @@ -1484,7 +1484,7 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(controlSettings); Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, til::color{ controlSettings.DefaultBackground() }); }); TestOnUIThread([&page]() { @@ -1503,7 +1503,7 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(controlSettings); Log::Comment(L"Color should be changed"); - VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground()); + VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, til::color{ controlSettings.DefaultBackground() }); }); Log::Comment(L"Sleep to let events propagate"); Sleep(250); diff --git a/src/cascadia/LocalTests_TerminalApp/pch.h b/src/cascadia/LocalTests_TerminalApp/pch.h index 25df5f47e9e..24162c0f936 100644 --- a/src/cascadia/LocalTests_TerminalApp/pch.h +++ b/src/cascadia/LocalTests_TerminalApp/pch.h @@ -56,6 +56,8 @@ Author(s): #include #include +#include +#include #include #include diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 4e4f63d3676..df357864c3d 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1401,7 +1401,7 @@ namespace winrt::TerminalApp::implementation { if (const auto activePane{ activeTab->GetActivePane() }) { - _restartPaneConnection(activePane); + _restartPaneConnection(activePane->GetContent().try_as(), nullptr); } } args.Handled(true); diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl new file mode 100644 index 00000000000..ebe0914ea9c --- /dev/null +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace TerminalApp +{ + + runtimeclass BellEventArgs + { + Boolean FlashTaskbar { get; }; + }; + + interface IPaneContent + { + Windows.UI.Xaml.FrameworkElement GetRoot(); + + Windows.Foundation.Size MinimumSize { get; }; + + String Title { get; }; + UInt64 TaskbarState { get; }; + UInt64 TaskbarProgress { get; }; + Boolean ReadOnly { get; }; + + Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent); + + void Focus(Windows.UI.Xaml.FocusState reason); + + void Close(); + + event Windows.Foundation.TypedEventHandler CloseRequested; + + event Windows.Foundation.TypedEventHandler ConnectionStateChanged; + event Windows.Foundation.TypedEventHandler BellRequested; + event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler TabColorChanged; + event Windows.Foundation.TypedEventHandler TaskbarProgressChanged; + event Windows.Foundation.TypedEventHandler ReadOnlyChanged; + event Windows.Foundation.TypedEventHandler FocusRequested; + }; + + + enum PaneSnapDirection + { + Width, + Height + }; + + interface ISnappable + { + Single SnapDownToGrid(PaneSnapDirection direction, Single sizeToSnap); + Windows.Foundation.Size GridUnitSize { get; }; + }; +} diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 59988d928fc..9ed53d49204 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "Pane.h" + #include "AppLogic.h" #include "Utils.h" @@ -33,19 +34,21 @@ static const int CombinedPaneBorderSize = 2 * PaneBorderSize; static const int AnimationDurationInMilliseconds = 200; static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Windows::Foundation::TimeSpan(std::chrono::milliseconds(AnimationDurationInMilliseconds))); -Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) : - _control{ control }, - _lastActive{ lastFocused }, - _profile{ profile } +Pane::Pane(const IPaneContent& content, const bool lastFocused) : + _content{ content }, + _lastActive{ lastFocused } { _root.Children().Append(_borderFirst); - _borderFirst.Child(_control); - _setupControlEvents(); + const auto& control{ _content.GetRoot() }; + _borderFirst.Child(control); // Register an event with the control to have it inform us when it gains focus. - _gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler }); - _lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler }); + if (control) + { + _gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ContentGotFocusHandler }); + _lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ContentLostFocusHandler }); + } // When our border is tapped, make sure to transfer focus to our control. // LOAD-BEARING: This will NOT work if the border's BorderBrush is set to @@ -102,19 +105,6 @@ Pane::Pane(std::shared_ptr first, }); } -void Pane::_setupControlEvents() -{ - _controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &Pane::_ControlConnectionStateChangedHandler }); - _controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { this, &Pane::_ControlWarningBellHandler }); - _controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { this, &Pane::_CloseTerminalRequestedHandler }); - _controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { this, &Pane::_RestartTerminalRequestedHandler }); - _controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { this, &Pane::_ControlReadOnlyChangedHandler }); -} -void Pane::_removeControlEvents() -{ - _controlEvents = {}; -} - // Method Description: // - Extract the terminal settings from the current (leaf) pane's control // to be used to create an equivalent control @@ -129,55 +119,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // Leaves are the only things that have controls assert(_IsLeaf()); - NewTerminalArgs args{}; - auto controlSettings = _control.Settings(); - - args.Profile(controlSettings.ProfileName()); - // If we know the user's working directory use it instead of the profile. - if (const auto dir = _control.WorkingDirectory(); !dir.empty()) - { - args.StartingDirectory(dir); - } - else - { - args.StartingDirectory(controlSettings.StartingDirectory()); - } - args.TabTitle(controlSettings.StartingTitle()); - args.Commandline(controlSettings.Commandline()); - args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle()); - if (controlSettings.TabColor() || controlSettings.StartingTabColor()) - { - til::color c; - // StartingTabColor is prioritized over other colors - if (const auto color = controlSettings.StartingTabColor()) - { - c = til::color(color.Value()); - } - else - { - c = til::color(controlSettings.TabColor().Value()); - } - - args.TabColor(winrt::Windows::Foundation::IReference{ static_cast(c) }); - } - - // TODO:GH#9800 - we used to be able to persist the color scheme that a - // TermControl was initialized with, by name. With the change to having the - // control own its own copy of its settings, this isn't possible anymore. - // - // We may be able to get around this by storing the Name in the Core::Scheme - // object. That would work for schemes set by the Terminal, but not ones set - // by VT, but that seems good enough. - - // Only fill in the ContentId if absolutely needed. If you fill in a number - // here (even 0), we'll serialize that number, AND treat that action as an - // "attach existing" rather than a "create" - if (asContent) - { - args.ContentId(_control.ContentId()); - } - - return args; + return _content.GetNewTerminalArgs(asContent); } // Method Description: @@ -1022,181 +964,18 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr } // Method Description: -// - Called when our attached control is closed. Triggers listeners to our close -// event, if we're a leaf pane. -// - If this was called, and we became a parent pane (due to work on another -// thread), this function will do nothing (allowing the control's new parent -// to handle the event instead). +// - Returns true if the connection state of this pane is closed. // Arguments: // - // Return Value: -// - -winrt::fire_and_forget Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& /*args*/) -{ - auto newConnectionState = ConnectionState::Closed; - if (const auto coreState = sender.try_as()) - { - newConnectionState = coreState.ConnectionState(); - } - - const auto previousConnectionState = std::exchange(_connectionState, newConnectionState); - if (newConnectionState < ConnectionState::Closed) - { - // Pane doesn't care if the connection isn't entering a terminal state. - co_return; - } - - const auto weakThis = weak_from_this(); - co_await wil::resume_foreground(_root.Dispatcher()); - const auto strongThis = weakThis.lock(); - if (!strongThis) - { - co_return; - } - - // It's possible that this event handler started being executed, scheduled - // on the UI thread, another child got created. So our control is - // actually no longer _our_ control, and instead could be a descendant. - // - // When the control's new Pane takes ownership of the control, the new - // parent will register its own event handler. That event handler will get - // fired after this handler returns, and will properly cleanup state. - if (!_IsLeaf()) - { - co_return; - } - - if (previousConnectionState < ConnectionState::Connected && newConnectionState >= ConnectionState::Failed) - { - // A failure to complete the connection (before it has _connected_) is not covered by "closeOnExit". - // This is to prevent a misconfiguration (closeOnExit: always, startingDirectory: garbage) resulting - // in Terminal flashing open and immediately closed. - co_return; - } - - if (_profile) - { - if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic) - { - // For 'automatic', we only care about the connection state if we were launched by Terminal - // Since we were launched via defterm, ignore the connection state (i.e. we treat the - // close on exit mode as 'always', see GH #13325 for discussion) - Close(); - } - - const auto mode = _profile.CloseOnExit(); - if ((mode == CloseOnExitMode::Always) || - ((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed)) - { - Close(); - } - } -} - -void Pane::_CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) -{ - // It's possible that this event handler started being executed, then before - // we got the lock, another thread created another child. So our control is - // actually no longer _our_ control, and instead could be a descendant. - // - // When the control's new Pane takes ownership of the control, the new - // parent will register its own event handler. That event handler will get - // fired after this handler returns, and will properly cleanup state. - if (!_IsLeaf()) - { - return; - } - - Close(); -} - -void Pane::_RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) -{ - if (!_IsLeaf()) - { - return; - } - RestartTerminalRequested.raise(shared_from_this()); -} - -winrt::fire_and_forget Pane::_playBellSound(winrt::Windows::Foundation::Uri uri) -{ - auto weakThis{ weak_from_this() }; - - co_await wil::resume_foreground(_root.Dispatcher()); - if (auto pane{ weakThis.lock() }) - { - if (!_bellPlayerCreated) - { - // The MediaPlayer might not exist on Windows N SKU. - try - { - _bellPlayerCreated = true; - _bellPlayer = winrt::Windows::Media::Playback::MediaPlayer(); - // GH#12258: The media keys (like play/pause) should have no effect on our bell sound. - _bellPlayer.CommandManager().IsEnabled(false); - } - CATCH_LOG(); - } - if (_bellPlayer) - { - const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) }; - const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) }; - _bellPlayer.Source(item); - _bellPlayer.Play(); - } - } -} - -// Method Description: -// - Plays a warning note when triggered by the BEL control character, -// using the sound configured for the "Critical Stop" system event.` -// This matches the behavior of the Windows Console host. -// - Will also flash the taskbar if the bellStyle setting for this profile -// has the 'visual' flag set -// Arguments: -// - -void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) +// - true if the connection state of this Pane is closed. +bool Pane::IsConnectionClosed() const { - if (!_IsLeaf()) - { - return; - } - if (_profile) + if (const auto& control{ GetTerminalControl() }) { - // We don't want to do anything if nothing is set, so check for that first - if (static_cast(_profile.BellStyle()) != 0) - { - if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible)) - { - // Audible is set, play the sound - auto sounds{ _profile.BellSound() }; - if (sounds && sounds.Size() > 0) - { - winrt::hstring soundPath{ wil::ExpandEnvironmentStringsW(sounds.GetAt(rand() % sounds.Size()).c_str()) }; - winrt::Windows::Foundation::Uri uri{ soundPath }; - _playBellSound(uri); - } - else - { - const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); - PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); - } - } - - if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window)) - { - _control.BellLightOn(); - } - - // raise the event with the bool value corresponding to the taskbar flag - PaneRaiseBell.raise(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar)); - } + return control.ConnectionState() >= ConnectionState::Closed; } + return false; } // Event Description: @@ -1207,7 +986,7 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect // - // Return Value: // - -void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, +void Pane::_ContentGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, const RoutedEventArgs& /* args */) { auto f = FocusState::Programmatic; @@ -1222,7 +1001,7 @@ void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectabl // - Called when our control loses focus. We'll use this to trigger our LostFocus // callback. The tab that's hosting us should have registered a callback which // can be used to update its own internal focus state -void Pane::_ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& /* sender */, +void Pane::_ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectable& /* sender */, const RoutedEventArgs& /* args */) { LostFocus.raise(shared_from_this()); @@ -1245,21 +1024,9 @@ void Pane::Close() // and connections beneath it. void Pane::Shutdown() { - // Clear out our media player callbacks, and stop any playing media. This - // will prevent the callback from being triggered after we've closed, and - // also make sure that our sound stops when we're closed. - if (_bellPlayer) - { - _bellPlayer.Pause(); - _bellPlayer.Source(nullptr); - _bellPlayer.Close(); - _bellPlayer = nullptr; - _bellPlayerCreated = false; - } - if (_IsLeaf()) { - _control.Close(); + _content.Close(); } else { @@ -1314,7 +1081,7 @@ TermControl Pane::GetLastFocusedTerminalControl() { if (p->_IsLeaf()) { - return p->_control; + return p->GetTerminalControl(); } pane = p; } @@ -1322,7 +1089,8 @@ TermControl Pane::GetLastFocusedTerminalControl() } return _firstChild->GetLastFocusedTerminalControl(); } - return _control; + // we _are_ a leaf. + return GetTerminalControl(); } // Method Description: @@ -1332,9 +1100,16 @@ TermControl Pane::GetLastFocusedTerminalControl() // - // Return Value: // - nullptr if this Pane is a parent, otherwise the TermControl of this Pane. -TermControl Pane::GetTerminalControl() +TermControl Pane::GetTerminalControl() const { - return _IsLeaf() ? _control : nullptr; + if (const auto& terminalPane{ _getTerminalContent() }) + { + return terminalPane.GetTermControl(); + } + else + { + return nullptr; + } } // Method Description: @@ -1381,19 +1156,11 @@ void Pane::SetActive() Profile Pane::GetFocusedProfile() { auto lastFocused = GetActivePane(); - return lastFocused ? lastFocused->_profile : nullptr; -} - -// Method Description: -// - Returns true if the connection state of this pane is closed. If this Pane is not a leaf this will -// return false. -// Arguments: -// - -// Return Value: -// - true if the connection state of this Pane is closed. -bool Pane::IsConnectionClosed() const -{ - return _control && _control.ConnectionState() >= ConnectionState::Closed; + if (const auto& terminalPane{ lastFocused->_getTerminalContent() }) + { + return terminalPane.GetProfile(); + } + return nullptr; } // Method Description: @@ -1466,10 +1233,7 @@ void Pane::UpdateVisuals() void Pane::_Focus() { GotFocus.raise(shared_from_this(), FocusState::Programmatic); - if (const auto& control = GetLastFocusedTerminalControl()) - { - control.Focus(FocusState::Programmatic); - } + _content.Focus(FocusState::Programmatic); } // Method Description: @@ -1519,9 +1283,10 @@ void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Pr { assert(_IsLeaf()); - _profile = profile; - - _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); + if (const auto& terminalPane{ _getTerminalContent() }) + { + return terminalPane.UpdateSettings(settings, profile); + } } // Method Description: @@ -1573,7 +1338,7 @@ std::shared_ptr Pane::DetachPane(std::shared_ptr pane) auto detached = isFirstChild ? _firstChild : _secondChild; // Remove the child from the tree, replace the current node with the // other child. - _CloseChild(isFirstChild, true); + _CloseChild(isFirstChild); // Update the borders on this pane and any children to match if we have // no parent. @@ -1602,12 +1367,9 @@ std::shared_ptr Pane::DetachPane(std::shared_ptr pane) // Arguments: // - closeFirst: if true, the first child should be closed, and the second // should be preserved, and vice-versa for false. -// - isDetaching: if true, then the pane event handlers for the closed child -// should be kept, this way they don't have to be recreated when it is later -// reattached to a tree somewhere as the control moves with the pane. // Return Value: // - -void Pane::_CloseChild(const bool closeFirst, const bool isDetaching) +void Pane::_CloseChild(const bool closeFirst) { // If we're a leaf, then chances are both our children closed in close // succession. We waited on the lock while the other child was closed, so @@ -1643,35 +1405,16 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching) _borders = _GetCommonBorders(); // take the control, profile, id and isDefTermSession of the pane that _wasn't_ closed. - _control = remainingChild->_control; - _connectionState = remainingChild->_connectionState; - _profile = remainingChild->_profile; + _content = remainingChild->_content; _id = remainingChild->Id(); - _isDefTermSession = remainingChild->_isDefTermSession; - - // Add our new event handler before revoking the old one. - _setupControlEvents(); // Revoke the old event handlers. Remove both the handlers for the panes // themselves closing, and remove their handlers for their controls // closing. At this point, if the remaining child's control is closed, // they'll trigger only our event handler for the control's close. - // However, if we are detaching the pane we want to keep its control - // handlers since it is just getting moved. - if (!isDetaching) - { - closedChild->WalkTree([](auto p) { - if (p->_IsLeaf()) - { - p->_removeControlEvents(); - } - }); - } - closedChild->Closed(closedChildClosedToken); remainingChild->Closed(remainingChildClosedToken); - remainingChild->_removeControlEvents(); // If we or either of our children was focused, we want to take that // focus from them. @@ -1691,7 +1434,8 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching) // Reattach the TermControl to our grid. _root.Children().Append(_borderFirst); - _borderFirst.Child(_control); + const auto& control{ _content.GetRoot() }; + _borderFirst.Child(control); // Make sure to set our _splitState before focusing the control. If you // fail to do this, when the tab handles the GotFocus event and asks us @@ -1700,14 +1444,17 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching) _splitState = SplitState::None; // re-attach our handler for the control's GotFocus event. - _gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler }); - _lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler }); + if (control) + { + _gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ContentGotFocusHandler }); + _lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ContentLostFocusHandler }); + } // If we're inheriting the "last active" state from one of our children, // focus our control now. This should trigger our own GotFocus event. if (usedToFocusClosedChildsTerminal || _lastActive) { - _control.Focus(FocusState::Programmatic); + _content.Focus(FocusState::Programmatic); // See GH#7252 // Manually fire off the GotFocus event. Typically, this is done @@ -1746,15 +1493,6 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching) // Remove the event handlers on the old children remainingChild->Closed(remainingChildClosedToken); closedChild->Closed(closedChildClosedToken); - if (!isDetaching) - { - closedChild->WalkTree([](auto p) { - if (p->_IsLeaf()) - { - p->_removeControlEvents(); - } - }); - } // Reset our UI: _root.Children().Clear(); @@ -1847,7 +1585,7 @@ void Pane::_CloseChildRoutine(const bool closeFirst) // this one doesn't seem to. if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed) { - _CloseChild(closeFirst, false); + _CloseChild(closeFirst); return; } @@ -1950,7 +1688,7 @@ void Pane::_CloseChildRoutine(const bool closeFirst) { // We don't need to manually undo any of the above trickiness. // We're going to re-parent the child's content into us anyways - pane->_CloseChild(closeFirst, false); + pane->_CloseChild(closeFirst); } }); } @@ -2173,7 +1911,7 @@ void Pane::_SetupEntranceAnimation() auto child = isFirstChild ? _firstChild : _secondChild; auto childGrid = child->_root; // If we are splitting a parent pane this may be null - auto control = child->_control; + auto control = child->_content.GetRoot(); // Build up our animation: // * it'll take as long as our duration (200ms) // * it'll change the value of our property from 0 to secondSize @@ -2494,9 +2232,6 @@ std::pair, std::shared_ptr> Pane::_Split(SplitDirect if (_IsLeaf()) { - // revoke our handler - the child will take care of the control now. - _removeControlEvents(); - // Remove our old GotFocus handler from the control. We don't want the // control telling us that it's now focused, we want it telling its new // parent. @@ -2525,11 +2260,8 @@ std::pair, std::shared_ptr> Pane::_Split(SplitDirect else { // Move our control, guid, isDefTermSession into the first one. - _firstChild = std::make_shared(_profile, _control); - _firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected); - _profile = nullptr; - _control = { nullptr }; - _firstChild->_isDefTermSession = _isDefTermSession; + _firstChild = std::make_shared(_content); + _content = nullptr; _firstChild->_broadcastEnabled = _broadcastEnabled; } @@ -2852,8 +2584,16 @@ float Pane::CalcSnappedDimension(const bool widthOrHeight, const float dimension // If requested size is already snapped, then both returned values equal this value. Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const float dimension) const { + const auto direction{ widthOrHeight ? PaneSnapDirection::Width : PaneSnapDirection::Height }; + if (_IsLeaf()) { + const auto& snappable{ _content.try_as() }; + if (!snappable) + { + return { dimension, dimension }; + } + // If we're a leaf pane, align to the grid of controlling terminal const auto minSize = _GetMinSize(); @@ -2864,8 +2604,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const return { minDimension, minDimension }; } - auto lower = _control.SnapDimensionToGrid(widthOrHeight, dimension); - if (widthOrHeight) + auto lower = snappable.SnapDownToGrid(widthOrHeight ? PaneSnapDirection::Width : PaneSnapDirection::Height, + dimension); + + if (direction == PaneSnapDirection::Width) { lower += WI_IsFlagSet(_borders, Borders::Left) ? PaneBorderSize : 0; lower += WI_IsFlagSet(_borders, Borders::Right) ? PaneBorderSize : 0; @@ -2884,8 +2626,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const } else { - const auto cellSize = _control.CharacterDimensions(); - const auto higher = lower + (widthOrHeight ? cellSize.Width : cellSize.Height); + const auto cellSize = snappable.GridUnitSize(); + const auto higher = lower + (direction == PaneSnapDirection::Width ? + cellSize.Width : + cellSize.Height); return { lower, higher }; } } @@ -2931,21 +2675,34 @@ void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& si { if (_IsLeaf()) { - // We're a leaf pane, so just add one more row or column (unless isMinimumSize - // is true, see below). - - if (sizeNode.isMinimumSize) + const auto& snappable{ _content.try_as() }; + if (snappable) { - // If the node is of its minimum size, this size might not be snapped (it might - // be, say, half a character, or fixed 10 pixels), so snap it upward. It might - // however be already snapped, so add 1 to make sure it really increases - // (not strictly necessary but to avoid surprises). - sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher; + // We're a leaf pane, so just add one more row or column (unless isMinimumSize + // is true, see below). + + if (sizeNode.isMinimumSize) + { + // If the node is of its minimum size, this size might not be snapped (it might + // be, say, half a character, or fixed 10 pixels), so snap it upward. It might + // however be already snapped, so add 1 to make sure it really increases + // (not strictly necessary but to avoid surprises). + sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher; + } + else + { + const auto cellSize = snappable.GridUnitSize(); + sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height; + } } else { - const auto cellSize = _control.CharacterDimensions(); - sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height; + // If we're a leaf that didn't have a TermControl, then just increment + // by one. We have to increment by _some_ value, because this is used in + // a while() loop to find the next bigger size we can snap to. But since + // a non-terminal control doesn't really care what size it's snapped to, + // we can just say "one pixel larger is the next snap point" + sizeNode.size += 1; } } else @@ -3050,7 +2807,7 @@ Size Pane::_GetMinSize() const { if (_IsLeaf()) { - auto controlSize = _control.MinimumSize(); + auto controlSize = _content.MinimumSize(); auto newWidth = controlSize.Width; auto newHeight = controlSize.Height; @@ -3148,14 +2905,17 @@ int Pane::GetLeafPaneCount() const noexcept // created via default handoff void Pane::FinalizeConfigurationGivenDefault() { - _isDefTermSession = true; + if (const auto& terminalPane{ _content.try_as() }) + { + terminalPane.MarkAsDefterm(); + } } // Method Description: // - Returns true if the pane or one of its descendants is read-only bool Pane::ContainsReadOnly() const { - return _IsLeaf() ? _control.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly()); + return _IsLeaf() ? _content.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly()); } // Method Description: @@ -3170,8 +2930,8 @@ void Pane::CollectTaskbarStates(std::vector& s { if (_IsLeaf()) { - auto tbState{ winrt::make(_control.TaskbarState(), - _control.TaskbarProgress()) }; + auto tbState{ winrt::make(_content.TaskbarState(), + _content.TaskbarProgress()) }; states.push_back(tbState); } else @@ -3186,9 +2946,12 @@ void Pane::EnableBroadcast(bool enabled) if (_IsLeaf()) { _broadcastEnabled = enabled; - _control.CursorVisibility(enabled ? - CursorDisplayState::Shown : - CursorDisplayState::Default); + if (const auto& termControl{ GetTerminalControl() }) + { + termControl.CursorVisibility(enabled ? + CursorDisplayState::Shown : + CursorDisplayState::Default); + } UpdateVisuals(); } else @@ -3205,9 +2968,12 @@ void Pane::BroadcastKey(const winrt::Microsoft::Terminal::Control::TermControl& const bool keyDown) { WalkTree([&](const auto& pane) { - if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly()) + if (const auto& termControl{ pane->GetTerminalControl() }) { - pane->_control.RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown); + if (termControl != sourceControl && !termControl.ReadOnly()) + { + termControl.RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown); + } } }); } @@ -3218,9 +2984,12 @@ void Pane::BroadcastChar(const winrt::Microsoft::Terminal::Control::TermControl& const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers) { WalkTree([&](const auto& pane) { - if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly()) + if (const auto& termControl{ pane->GetTerminalControl() }) { - pane->_control.RawWriteChar(character, scanCode, modifiers); + if (termControl != sourceControl && !termControl.ReadOnly()) + { + termControl.RawWriteChar(character, scanCode, modifiers); + } } }); } @@ -3229,19 +2998,16 @@ void Pane::BroadcastString(const winrt::Microsoft::Terminal::Control::TermContro const winrt::hstring& text) { WalkTree([&](const auto& pane) { - if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly()) + if (const auto& termControl{ pane->GetTerminalControl() }) { - pane->_control.RawWriteString(text); + if (termControl != sourceControl && !termControl.ReadOnly()) + { + termControl.RawWriteString(text); + } } }); } -void Pane::_ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*e*/) -{ - UpdateVisuals(); -} - winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor() { if (_lastActive) @@ -3249,7 +3015,7 @@ winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor() return _themeResources.focusedBorderBrush; } - if (_broadcastEnabled && (_IsLeaf() && !_control.ReadOnly())) + if (_broadcastEnabled && (_IsLeaf() && !_content.ReadOnly())) { return _themeResources.broadcastBorderBrush; } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index e7660d4b70c..1f237f83190 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -21,6 +21,7 @@ #pragma once #include "TaskbarState.h" +#include "TerminalPaneContent.h" // fwdecl unittest classes namespace TerminalAppLocalTests @@ -61,8 +62,7 @@ struct PaneResources class Pane : public std::enable_shared_from_this { public: - Pane(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, - const winrt::Microsoft::Terminal::Control::TermControl& control, + Pane(const winrt::TerminalApp::IPaneContent& content, const bool lastFocused = false); Pane(std::shared_ptr first, @@ -73,7 +73,7 @@ class Pane : public std::enable_shared_from_this std::shared_ptr GetActivePane(); winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl(); - winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl(); + winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl() const; winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile(); bool IsConnectionClosed() const; @@ -82,10 +82,15 @@ class Pane : public std::enable_shared_from_this // - If this is a branch/root pane, return nullptr. winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const { - return _profile; + if (const auto& c{ _content.try_as() }) + { + return c.GetProfile(); + } + return nullptr; } winrt::Windows::UI::Xaml::Controls::Grid GetRootElement(); + winrt::TerminalApp::IPaneContent GetContent() const noexcept { return _IsLeaf() ? _content : nullptr; } bool WasLastFocused() const noexcept; void UpdateVisuals(); @@ -217,9 +222,7 @@ class Pane : public std::enable_shared_from_this til::event GotFocus; til::event>> LostFocus; - til::event> PaneRaiseBell; til::event>> Detached; - til::event>> RestartTerminalRequested; private: struct PanePoint; @@ -239,10 +242,8 @@ class Pane : public std::enable_shared_from_this std::shared_ptr _secondChild{ nullptr }; SplitState _splitState{ SplitState::None }; float _desiredSplitPosition; - winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr }; - winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected }; - winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr }; - bool _isDefTermSession{ false }; + + winrt::TerminalApp::IPaneContent _content{ nullptr }; #pragma endregion std::optional _id; @@ -252,17 +253,6 @@ class Pane : public std::enable_shared_from_this winrt::event_token _firstClosedToken{ 0 }; winrt::event_token _secondClosedToken{ 0 }; - struct ControlEventTokens - { - winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged; - winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell; - winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested; - winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested; - winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged; - } _controlEvents; - void _setupControlEvents(); - void _removeControlEvents(); - winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker; winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker; @@ -271,13 +261,14 @@ class Pane : public std::enable_shared_from_this bool _zoomed{ false }; bool _broadcastEnabled{ false }; - winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; - bool _bellPlayerCreated{ false }; - bool _IsLeaf() const noexcept; bool _HasFocusedChild() const noexcept; void _SetupChildCloseHandlers(); bool _HasChild(const std::shared_ptr child); + winrt::TerminalApp::TerminalPaneContent _getTerminalContent() const + { + return _IsLeaf() ? _content.try_as() : nullptr; + } std::pair, std::shared_ptr> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType, const float splitSize, @@ -303,24 +294,16 @@ class Pane : public std::enable_shared_from_this const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction, const PanePoint offset); - void _CloseChild(const bool closeFirst, const bool isDetaching); + void _CloseChild(const bool closeFirst); void _CloseChildRoutine(const bool closeFirst); void _Focus(); void _FocusFirstChild(); - winrt::fire_and_forget _ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); - void _ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& e); - void _ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, + void _ContentGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); - void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, + void _ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); - void _ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); - - void _CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); - void _RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); - std::pair _CalcChildrenSizes(const float fullSize) const; SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const; SnapSizeResult _CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; @@ -331,8 +314,6 @@ class Pane : public std::enable_shared_from_this SplitState _convertAutomaticOrDirectionalSplitState(const winrt::Microsoft::Terminal::Settings::Model::SplitDirection& splitType) const; - winrt::fire_and_forget _playBellSound(winrt::Windows::Foundation::Uri uri); - // Function Description: // - Returns true if the given direction can be used with the given split // type. diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 2228f8234e9..b407c6107c1 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -63,7 +63,7 @@ namespace winrt::TerminalApp::implementation // - existingConnection: An optional connection that is already established to a PTY // for this tab to host instead of creating one. // If not defined, the tab will create the connection. - HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection) + HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs) try { const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) }; @@ -86,7 +86,7 @@ namespace winrt::TerminalApp::implementation // // This call to _MakePane won't return nullptr, we already checked that // case above with the _maybeElevate call. - _CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr, existingConnection)); + _CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr)); return S_OK; } CATCH_RETURN(); diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index af091aae03e..d6abba2db90 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -158,6 +158,9 @@ TerminalWindow.idl + + TerminalPaneContent.idl + SuggestionsControl.xaml @@ -262,6 +265,9 @@ TerminalWindow.idl + + TerminalPaneContent.idl + @@ -334,6 +340,8 @@ Code + + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index a46544ce60c..605110a0ad7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1297,15 +1297,20 @@ namespace winrt::TerminalApp::implementation return connection; } - TerminalConnection::ITerminalConnection TerminalPage::_duplicateConnectionForRestart(std::shared_ptr pane) + TerminalConnection::ITerminalConnection TerminalPage::_duplicateConnectionForRestart(const TerminalApp::TerminalPaneContent& paneContent) { - const auto& control{ pane->GetTerminalControl() }; + if (paneContent == nullptr) + { + return nullptr; + } + + const auto& control{ paneContent.GetTermControl() }; if (control == nullptr) { return nullptr; } const auto& connection = control.Connection(); - auto profile{ pane->GetProfile() }; + auto profile{ paneContent.GetProfile() }; TerminalSettingsCreateResult controlSettings{ nullptr }; @@ -1724,11 +1729,8 @@ namespace winrt::TerminalApp::implementation // Add an event handler for when the terminal or tab wants to set a // progress indicator on the taskbar hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler }); - } - void TerminalPage::_RegisterPaneEvents(std::shared_ptr& pane) - { - pane->RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection }); + hostingTab.RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection }); } // Method Description: @@ -2377,13 +2379,6 @@ namespace winrt::TerminalApp::implementation _UnZoomIfNeeded(); auto [original, _] = activeTab->SplitPane(*realSplitType, splitSize, newPane); - // When we split the pane, the Pane itself will create a _new_ Pane - // instance for the original content. We need to make sure we also - // re-add our event handler to that newly created pane. - // - // _MakePane will already call this for the newly created pane. - _RegisterPaneEvents(original); - // After GH#6586, the control will no longer focus itself // automatically when it's finished being laid out. Manually focus // the control here instead. @@ -3114,10 +3109,9 @@ namespace winrt::TerminalApp::implementation // Don't need to worry about duplicating or anything - we'll // serialize the actual profile's GUID along with the content guid. const auto& profile = _settings.GetProfileForArgs(newTerminalArgs); - const auto control = _AttachControlToContent(newTerminalArgs.ContentId()); - - return std::make_shared(profile, control); + auto paneContent{ winrt::make(profile, control) }; + return std::make_shared(paneContent); } TerminalSettingsCreateResult controlSettings{ nullptr }; @@ -3173,13 +3167,15 @@ namespace winrt::TerminalApp::implementation const auto control = _CreateNewControlAndContent(controlSettings, connection); - auto resultPane = std::make_shared(profile, control); + auto paneContent{ winrt::make(profile, control) }; + auto resultPane = std::make_shared(paneContent); if (debugConnection) // this will only be set if global debugging is on and tap is active { auto newControl = _CreateNewControlAndContent(controlSettings, debugConnection); // Split (auto) with the debug tap. - auto debugPane = std::make_shared(profile, newControl); + auto debugContent{ winrt::make(profile, newControl) }; + auto debugPane = std::make_shared(debugContent); // Since we're doing this split directly on the pane (instead of going through TerminalTab, // we need to handle the panes 'active' states @@ -3193,16 +3189,20 @@ namespace winrt::TerminalApp::implementation original->SetActive(); } - _RegisterPaneEvents(resultPane); - return resultPane; } - void TerminalPage::_restartPaneConnection(const std::shared_ptr& pane) + void TerminalPage::_restartPaneConnection( + const TerminalApp::TerminalPaneContent& paneContent, + const winrt::Windows::Foundation::IInspectable&) { - if (const auto& connection{ _duplicateConnectionForRestart(pane) }) + // Note: callers are likely passing in `nullptr` as the args here, as + // the TermControl.RestartTerminalRequested event doesn't actually pass + // any args upwards itself. If we ever change this, make sure you check + // for nulls + if (const auto& connection{ _duplicateConnectionForRestart(paneContent) }) { - pane->GetTerminalControl().Connection(connection); + paneContent.GetTermControl().Connection(connection); connection.Start(); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 5e22760fc67..639165d1730 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -301,14 +301,14 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutProfile(const Microsoft::Terminal::Settings::Model::Profile profile, int profileIndex); void _OpenNewTabDropdown(); - HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); + HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs); void _CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition = -1); std::wstring _evaluatePathForCwd(std::wstring_view path); winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings, const bool inheritCursor); - winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(std::shared_ptr pane); - void _restartPaneConnection(const std::shared_ptr& pane); + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(const TerminalApp::TerminalPaneContent& paneContent); + void _restartPaneConnection(const TerminalApp::TerminalPaneContent&, const winrt::Windows::Foundation::IInspectable&); winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); @@ -344,7 +344,6 @@ namespace winrt::TerminalApp::implementation void _InitializeTab(winrt::com_ptr newTabImpl, uint32_t insertPosition = -1); void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term); void _RegisterTabEvents(TerminalTab& hostingTab); - void _RegisterPaneEvents(std::shared_ptr& pane); void _DismissTabContextMenus(); void _FocusCurrentTab(const bool focusAlways); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp new file mode 100644 index 00000000000..597631d8fdc --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -0,0 +1,332 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TerminalPaneContent.h" +#include "TerminalPaneContent.g.cpp" + +#include "BellEventArgs.g.cpp" + +#include +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Microsoft::Terminal::Settings::Model; +using namespace winrt::Microsoft::Terminal::Control; +using namespace winrt::Microsoft::Terminal::TerminalConnection; + +namespace winrt::TerminalApp::implementation +{ + TerminalPaneContent::TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, + const winrt::Microsoft::Terminal::Control::TermControl& control) : + _control{ control }, + _profile{ profile } + { + _setupControlEvents(); + } + + void TerminalPaneContent::_setupControlEvents() + { + _controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &TerminalPaneContent::_controlConnectionStateChangedHandler }); + _controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlWarningBellHandler }); + _controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_closeTerminalRequestedHandler }); + _controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_restartTerminalRequestedHandler }); + + _controlEvents._TitleChanged = _control.TitleChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTitleChanged }); + _controlEvents._TabColorChanged = _control.TabColorChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTabColorChanged }); + _controlEvents._SetTaskbarProgress = _control.SetTaskbarProgress(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlSetTaskbarProgress }); + _controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlReadOnlyChanged }); + _controlEvents._FocusFollowMouseRequested = _control.FocusFollowMouseRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlFocusFollowMouseRequested }); + } + void TerminalPaneContent::_removeControlEvents() + { + _controlEvents = {}; + } + + winrt::Windows::UI::Xaml::FrameworkElement TerminalPaneContent::GetRoot() + { + return _control; + } + winrt::Microsoft::Terminal::Control::TermControl TerminalPaneContent::GetTermControl() + { + return _control; + } + winrt::Windows::Foundation::Size TerminalPaneContent::MinimumSize() + { + return _control.MinimumSize(); + } + void TerminalPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason) + { + _control.Focus(reason); + } + void TerminalPaneContent::Close() + { + _removeControlEvents(); + + // Clear out our media player callbacks, and stop any playing media. This + // will prevent the callback from being triggered after we've closed, and + // also make sure that our sound stops when we're closed. + if (_bellPlayer) + { + _bellPlayer.Pause(); + _bellPlayer.Source(nullptr); + _bellPlayer.Close(); + _bellPlayer = nullptr; + _bellPlayerCreated = false; + } + + CloseRequested.raise(*this, nullptr); + } + + NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const + { + NewTerminalArgs args{}; + const auto& controlSettings = _control.Settings(); + + args.Profile(controlSettings.ProfileName()); + // If we know the user's working directory use it instead of the profile. + if (const auto dir = _control.WorkingDirectory(); !dir.empty()) + { + args.StartingDirectory(dir); + } + else + { + args.StartingDirectory(controlSettings.StartingDirectory()); + } + args.TabTitle(controlSettings.StartingTitle()); + args.Commandline(controlSettings.Commandline()); + args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle()); + if (controlSettings.TabColor() || controlSettings.StartingTabColor()) + { + til::color c; + // StartingTabColor is prioritized over other colors + if (const auto color = controlSettings.StartingTabColor()) + { + c = til::color(color.Value()); + } + else + { + c = til::color(controlSettings.TabColor().Value()); + } + + args.TabColor(winrt::Windows::Foundation::IReference{ static_cast(c) }); + } + + // TODO:GH#9800 - we used to be able to persist the color scheme that a + // TermControl was initialized with, by name. With the change to having the + // control own its own copy of its settings, this isn't possible anymore. + // + // We may be able to get around this by storing the Name in the Core::Scheme + // object. That would work for schemes set by the Terminal, but not ones set + // by VT, but that seems good enough. + + // Only fill in the ContentId if absolutely needed. If you fill in a number + // here (even 0), we'll serialize that number, AND treat that action as an + // "attach existing" rather than a "create" + if (asContent) + { + args.ContentId(_control.ContentId()); + } + + return args; + } + + void TerminalPaneContent::_controlTitleChanged(const IInspectable&, const IInspectable&) + { + TitleChanged.raise(*this, nullptr); + } + void TerminalPaneContent::_controlTabColorChanged(const IInspectable&, const IInspectable&) + { + TabColorChanged.raise(*this, nullptr); + } + void TerminalPaneContent::_controlSetTaskbarProgress(const IInspectable&, const IInspectable&) + { + TaskbarProgressChanged.raise(*this, nullptr); + } + void TerminalPaneContent::_controlReadOnlyChanged(const IInspectable&, const IInspectable&) + { + ReadOnlyChanged.raise(*this, nullptr); + } + void TerminalPaneContent::_controlFocusFollowMouseRequested(const IInspectable&, const IInspectable&) + { + FocusRequested.raise(*this, nullptr); + } + + // Method Description: + // - Called when our attached control is closed. Triggers listeners to our close + // event, if we're a leaf pane. + // - If this was called, and we became a parent pane (due to work on another + // thread), this function will do nothing (allowing the control's new parent + // to handle the event instead). + // Arguments: + // - + // Return Value: + // - + winrt::fire_and_forget TerminalPaneContent::_controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args) + { + ConnectionStateChanged.raise(sender, args); + auto newConnectionState = ConnectionState::Closed; + if (const auto coreState = sender.try_as()) + { + newConnectionState = coreState.ConnectionState(); + } + + const auto previousConnectionState = std::exchange(_connectionState, newConnectionState); + if (newConnectionState < ConnectionState::Closed) + { + // Pane doesn't care if the connection isn't entering a terminal state. + co_return; + } + + const auto weakThis = get_weak(); + co_await wil::resume_foreground(_control.Dispatcher()); + const auto strongThis = weakThis.get(); + if (!strongThis) + { + co_return; + } + + // It's possible that this event handler started being executed, scheduled + // on the UI thread, another child got created. So our control is + // actually no longer _our_ control, and instead could be a descendant. + // + // When the control's new Pane takes ownership of the control, the new + // parent will register its own event handler. That event handler will get + // fired after this handler returns, and will properly cleanup state. + + if (previousConnectionState < ConnectionState::Connected && newConnectionState >= ConnectionState::Failed) + { + // A failure to complete the connection (before it has _connected_) is not covered by "closeOnExit". + // This is to prevent a misconfiguration (closeOnExit: always, startingDirectory: garbage) resulting + // in Terminal flashing open and immediately closed. + co_return; + } + + if (_profile) + { + if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic) + { + // For 'automatic', we only care about the connection state if we were launched by Terminal + // Since we were launched via defterm, ignore the connection state (i.e. we treat the + // close on exit mode as 'always', see GH #13325 for discussion) + Close(); + } + + const auto mode = _profile.CloseOnExit(); + if ((mode == CloseOnExitMode::Always) || + ((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed)) + { + Close(); + } + } + } + + // Method Description: + // - Plays a warning note when triggered by the BEL control character, + // using the sound configured for the "Critical Stop" system event.` + // This matches the behavior of the Windows Console host. + // - Will also flash the taskbar if the bellStyle setting for this profile + // has the 'visual' flag set + // Arguments: + // - + void TerminalPaneContent::_controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) + { + if (_profile) + { + // We don't want to do anything if nothing is set, so check for that first + if (static_cast(_profile.BellStyle()) != 0) + { + if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible)) + { + // Audible is set, play the sound + auto sounds{ _profile.BellSound() }; + if (sounds && sounds.Size() > 0) + { + winrt::hstring soundPath{ wil::ExpandEnvironmentStringsW(sounds.GetAt(rand() % sounds.Size()).c_str()) }; + winrt::Windows::Foundation::Uri uri{ soundPath }; + _playBellSound(uri); + } + else + { + const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); + PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); + } + } + + if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window)) + { + _control.BellLightOn(); + } + + // raise the event with the bool value corresponding to the taskbar flag + BellRequested.raise(*this, + *winrt::make_self(WI_IsFlagSet(_profile.BellStyle(), BellStyle::Taskbar))); + } + } + } + + winrt::fire_and_forget TerminalPaneContent::_playBellSound(winrt::Windows::Foundation::Uri uri) + { + auto weakThis{ get_weak() }; + co_await wil::resume_foreground(_control.Dispatcher()); + if (auto pane{ weakThis.get() }) + { + if (!_bellPlayerCreated) + { + // The MediaPlayer might not exist on Windows N SKU. + try + { + _bellPlayerCreated = true; + _bellPlayer = winrt::Windows::Media::Playback::MediaPlayer(); + // GH#12258: The media keys (like play/pause) should have no effect on our bell sound. + _bellPlayer.CommandManager().IsEnabled(false); + } + CATCH_LOG(); + } + if (_bellPlayer) + { + const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) }; + const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) }; + _bellPlayer.Source(item); + _bellPlayer.Play(); + } + } + } + void TerminalPaneContent::_closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + Close(); + } + + void TerminalPaneContent::_restartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + RestartTerminalRequested.raise(*this, nullptr); + } + + void TerminalPaneContent::UpdateSettings(const TerminalSettingsCreateResult& settings, + const Profile& profile) + { + _profile = profile; + _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); + } + + // Method Description: + // - Should be called when this pane is created via a default terminal handoff + // - Finalizes our configuration given the information that we have been + // created via default handoff + void TerminalPaneContent::MarkAsDefterm() + { + _isDefTermSession = true; + } + + float TerminalPaneContent::SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap) + { + return _control.SnapDimensionToGrid(direction == PaneSnapDirection::Width, sizeToSnap); + } + Windows::Foundation::Size TerminalPaneContent::GridUnitSize() + { + return _control.CharacterDimensions(); + } +} diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h new file mode 100644 index 00000000000..2c2deee0124 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "TerminalPaneContent.g.h" +#include "BellEventArgs.g.h" + +namespace winrt::TerminalApp::implementation +{ + struct BellEventArgs : public BellEventArgsT + { + public: + BellEventArgs(bool flashTaskbar) : + FlashTaskbar(flashTaskbar) {} + + til::property FlashTaskbar; + }; + + struct TerminalPaneContent : TerminalPaneContentT + { + TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, + const winrt::Microsoft::Terminal::Control::TermControl& control); + + winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); + winrt::Microsoft::Terminal::Control::TermControl GetTermControl(); + winrt::Windows::Foundation::Size MinimumSize(); + void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); + void Close(); + + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const; + + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, + const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); + + void MarkAsDefterm(); + + winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const + { + return _profile; + } + + winrt::hstring Title() { return _control.Title(); } + uint64_t TaskbarState() { return _control.TaskbarState(); } + uint64_t TaskbarProgress() { return _control.TaskbarProgress(); } + bool ReadOnly() { return _control.ReadOnly(); } + + float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap); + Windows::Foundation::Size GridUnitSize(); + + til::typed_event RestartTerminalRequested; + til::typed_event<> ConnectionStateChanged; + til::typed_event CloseRequested; + til::typed_event BellRequested; + til::typed_event TitleChanged; + til::typed_event TabColorChanged; + til::typed_event TaskbarProgressChanged; + til::typed_event ReadOnlyChanged; + til::typed_event FocusRequested; + + private: + winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr }; + winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected }; + winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr }; + bool _isDefTermSession{ false }; + + winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; + bool _bellPlayerCreated{ false }; + + struct ControlEventTokens + { + winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged; + winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell; + winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested; + winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested; + + winrt::Microsoft::Terminal::Control::TermControl::TitleChanged_revoker _TitleChanged; + winrt::Microsoft::Terminal::Control::TermControl::TabColorChanged_revoker _TabColorChanged; + winrt::Microsoft::Terminal::Control::TermControl::SetTaskbarProgress_revoker _SetTaskbarProgress; + winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged; + winrt::Microsoft::Terminal::Control::TermControl::FocusFollowMouseRequested_revoker _FocusFollowMouseRequested; + + } _controlEvents; + void _setupControlEvents(); + void _removeControlEvents(); + + winrt::fire_and_forget _playBellSound(winrt::Windows::Foundation::Uri uri); + + winrt::fire_and_forget _controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); + void _controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); + void _controlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); + + void _controlTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _controlTabColorChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _controlSetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _controlReadOnlyChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _controlFocusFollowMouseRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + + void _closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); + void _restartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); + }; +} diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl new file mode 100644 index 00000000000..7e04c8b836c --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "IPaneContent.idl"; + +namespace TerminalApp +{ + [default_interface] runtimeclass TerminalPaneContent : IPaneContent, ISnappable + { + Microsoft.Terminal.Control.TermControl GetTermControl(); + + void UpdateSettings(const Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings, + const Microsoft.Terminal.Settings.Model.Profile profile); + + void MarkAsDefterm(); + + Microsoft.Terminal.Settings.Model.Profile GetProfile(); + + event Windows.Foundation.TypedEventHandler RestartTerminalRequested; + } +} diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index b3d37d88a17..c5cb7f5b5f2 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -186,6 +186,11 @@ namespace winrt::TerminalApp::implementation return nullptr; } + IPaneContent TerminalTab::GetActiveContent() const + { + return _activePane ? _activePane->GetContent() : nullptr; + } + // Method Description: // - Called after construction of a Tab object to bind event handlers to its // associated Pane and TermControl objects @@ -200,9 +205,9 @@ namespace winrt::TerminalApp::implementation _rootPane->WalkTree([&](std::shared_ptr pane) { // Attach event handlers to each new pane _AttachEventHandlersToPane(pane); - if (auto control = pane->GetTerminalControl()) + if (auto content = pane->GetContent()) { - _AttachEventHandlersToControl(pane->Id().value(), control); + _AttachEventHandlersToContent(pane->Id().value(), content); } }); } @@ -377,8 +382,8 @@ namespace winrt::TerminalApp::implementation { return RS_(L"MultiplePanes"); } - const auto lastFocusedControl = GetActiveTerminalControl(); - return lastFocusedControl ? lastFocusedControl.Title() : L""; + const auto activeContent = GetActiveContent(); + return activeContent ? activeContent.Title() : L""; } // Method Description: @@ -513,7 +518,10 @@ namespace winrt::TerminalApp::implementation if (p->_IsLeaf()) { p->Id(_nextPaneId); - _AttachEventHandlersToControl(p->Id().value(), p->_control); + if (const auto& content{ p->GetContent() }) + { + _AttachEventHandlersToContent(p->Id().value(), content); + } _nextPaneId++; } return false; @@ -626,7 +634,11 @@ namespace winrt::TerminalApp::implementation if (p->_IsLeaf()) { p->Id(_nextPaneId); - _AttachEventHandlersToControl(p->Id().value(), p->_control); + + if (const auto& content{ p->GetContent() }) + { + _AttachEventHandlersToContent(p->Id().value(), content); + } _nextPaneId++; } }); @@ -883,29 +895,17 @@ namespace winrt::TerminalApp::implementation // the control itself doesn't have a particular ID and its pointer is // unstable since it is moved when panes split. // Arguments: - // - paneId: The ID of the pane that contains the given control. - // - control: the control to remove events from. + // - paneId: The ID of the pane that contains the given content. // Return Value: // - - void TerminalTab::_DetachEventHandlersFromControl(const uint32_t paneId, const TermControl& control) + void TerminalTab::_DetachEventHandlersFromContent(const uint32_t paneId) { - auto it = _controlEvents.find(paneId); - if (it != _controlEvents.end()) + auto it = _contentEvents.find(paneId); + if (it != _contentEvents.end()) { - auto& events = it->second; - - control.TitleChanged(events.titleToken); - control.TabColorChanged(events.colorToken); - control.SetTaskbarProgress(events.taskbarToken); - control.ConnectionStateChanged(events.stateToken); - control.ReadOnlyChanged(events.readOnlyToken); - control.FocusFollowMouseRequested(events.focusToken); - - events.KeySent.revoke(); - events.CharSent.revoke(); - events.StringSent.revoke(); - - _controlEvents.erase(paneId); + // revoke the event handlers by resetting the event struct + // and remove it from the map + _contentEvents.erase(paneId); } } @@ -920,87 +920,159 @@ namespace winrt::TerminalApp::implementation // - control: the TermControl to add events to. // Return Value: // - - void TerminalTab::_AttachEventHandlersToControl(const uint32_t paneId, const TermControl& control) + void TerminalTab::_AttachEventHandlersToContent(const uint32_t paneId, const TerminalApp::IPaneContent& content) { auto weakThis{ get_weak() }; auto dispatcher = TabViewItem().Dispatcher(); - ControlEventTokens events{}; - - events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - // The lambda lives in the `std::function`-style container owned by `control`. That is, when the - // `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to - // copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines. - // See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870 - const auto weakThisCopy = weakThis; - co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThisCopy.get() }) - { - // The title of the control changed, but not necessarily the title of the tab. - // Set the tab's text to the active panes' text. - tab->UpdateTitle(); - } - }); + ContentEventTokens events{}; + + events.CloseRequested = content.CloseRequested( + winrt::auto_revoke, + [dispatcher, weakThis](auto sender, auto&&) -> winrt::fire_and_forget { + // Don't forget! this ^^^^^^^^ sender can't be a reference, this is a async callback. + + // The lambda lives in the `std::function`-style container owned by `control`. That is, when the + // `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to + // copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines. + // See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870 + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + // Check if Tab's lifetime has expired + if (auto tab{ weakThisCopy.get() }) + { + if (const auto content{ sender.try_as() }) + { + tab->_rootPane->WalkTree([content](std::shared_ptr pane) { + if (pane->GetContent() == content) + { + pane->Close(); + } + }); + } + } + }); - events.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - const auto weakThisCopy = weakThis; - co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThisCopy.get() }) - { - // The control's tabColor changed, but it is not necessarily the - // active control in this tab. We'll just recalculate the - // current color anyways. - tab->_RecalculateAndApplyTabColor(); - } - }); + events.TitleChanged = content.TitleChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + // The lambda lives in the `std::function`-style container owned by `control`. That is, when the + // `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to + // copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines. + // See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870 + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + // Check if Tab's lifetime has expired + if (auto tab{ weakThisCopy.get() }) + { + // The title of the control changed, but not necessarily the title of the tab. + // Set the tab's text to the active panes' text. + tab->UpdateTitle(); + } + }); - events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - const auto weakThisCopy = weakThis; - co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThisCopy.get() }) - { - tab->_UpdateProgressState(); - } - }); + events.TabColorChanged = content.TabColorChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + if (auto tab{ weakThisCopy.get() }) + { + // The control's tabColor changed, but it is not necessarily the + // active control in this tab. We'll just recalculate the + // current color anyways. + tab->_RecalculateAndApplyTabColor(); + } + }); - events.stateToken = control.ConnectionStateChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - const auto weakThisCopy = weakThis; - co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThisCopy.get() }) - { - tab->_UpdateConnectionClosedState(); - } - }); + events.TaskbarProgressChanged = content.TaskbarProgressChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + // Check if Tab's lifetime has expired + if (auto tab{ weakThisCopy.get() }) + { + tab->_UpdateProgressState(); + } + }); - events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - const auto weakThisCopy = weakThis; - co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThisCopy.get() }) - { - tab->_RecalculateAndApplyReadOnly(); - } - }); + events.ConnectionStateChanged = content.ConnectionStateChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + if (auto tab{ weakThisCopy.get() }) + { + tab->_UpdateConnectionClosedState(); + } + }); - events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget { - const auto weakThisCopy = weakThis; - co_await wil::resume_foreground(dispatcher); - if (const auto tab{ weakThisCopy.get() }) - { - if (tab->_focused()) + events.ReadOnlyChanged = content.ReadOnlyChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + if (auto tab{ weakThis.get() }) { - if (const auto termControl{ sender.try_as() }) + tab->_RecalculateAndApplyReadOnly(); + } + }); + + events.FocusRequested = content.FocusRequested( + winrt::auto_revoke, + [dispatcher, weakThis](TerminalApp::IPaneContent sender, auto) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + if (const auto tab{ weakThisCopy.get() }) + { + if (tab->_focused()) { - termControl.Focus(FocusState::Pointer); + sender.Focus(FocusState::Pointer); } } - } - }); + }); + + events.BellRequested = content.BellRequested( + winrt::auto_revoke, + [dispatcher, weakThis](TerminalApp::IPaneContent sender, auto bellArgs) -> winrt::fire_and_forget { + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + if (const auto tab{ weakThisCopy.get() }) + { + if (bellArgs.FlashTaskbar()) + { + // If visual is set, we need to bubble this event all the way to app host to flash the taskbar + // In this part of the chain we bubble it from the hosting tab to the page + tab->TabRaiseVisualBell.raise(); + } + + // Show the bell indicator in the tab header + tab->ShowBellIndicator(true); + + // If this tab is focused, activate the bell indicator timer, which will + // remove the bell indicator once it fires + // (otherwise, the indicator is removed when the tab gets focus) + if (tab->_focusState != WUX::FocusState::Unfocused) + { + tab->ActivateBellIndicatorTimer(); + } + } + }); + + if (const auto& terminal{ content.try_as() }) + { + events.RestartTerminalRequested = terminal.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalTab::_bubbleRestartTerminalRequested }); + } if (_tabStatus.IsInputBroadcastActive()) { - _addBroadcastHandlers(control, events); + if (const auto& termContent{ content.try_as() }) + { + _addBroadcastHandlers(termContent.GetTermControl(), events); + } } - _controlEvents[paneId] = std::move(events); + _contentEvents[paneId] = std::move(events); } // Method Description: @@ -1250,36 +1322,12 @@ namespace winrt::TerminalApp::implementation } }); - // Add a PaneRaiseBell event handler to the Pane - auto bellToken = pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) { - if (auto tab{ weakThis.get() }) - { - if (visual) - { - // If visual is set, we need to bubble this event all the way to app host to flash the taskbar - // In this part of the chain we bubble it from the hosting tab to the page - tab->TabRaiseVisualBell.raise(); - } - - // Show the bell indicator in the tab header - tab->ShowBellIndicator(true); - - // If this tab is focused, activate the bell indicator timer, which will - // remove the bell indicator once it fires - // (otherwise, the indicator is removed when the tab gets focus) - if (tab->_focusState != WUX::FocusState::Unfocused) - { - tab->ActivateBellIndicatorTimer(); - } - } - }); - // box the event token so that we can give a reference to it in the // event handler. auto detachedToken = std::make_shared(); // Add a Detached event handler to the Pane to clean up tab state // and other event handlers when a pane is removed from this tab. - *detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, bellToken, detachedToken](std::shared_ptr /*sender*/) { + *detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, detachedToken](std::shared_ptr /*sender*/) { // Make sure we do this at most once if (auto pane{ weakPane.lock() }) { @@ -1287,14 +1335,10 @@ namespace winrt::TerminalApp::implementation pane->GotFocus(gotFocusToken); pane->LostFocus(lostFocusToken); pane->Closed(closedToken); - pane->PaneRaiseBell(bellToken); if (auto tab{ weakThis.get() }) { - if (auto control = pane->GetTerminalControl()) - { - tab->_DetachEventHandlersFromControl(pane->Id().value(), control); - } + tab->_DetachEventHandlersFromContent(pane->Id().value()); for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i) { @@ -1495,7 +1539,12 @@ namespace winrt::TerminalApp::implementation // GH#10112 - if we're opening the tab renamer, don't // immediately toss focus to the control. We don't want to steal // focus from the tab renamer. - if (!tab->_headerControl.InRename() && !tab->GetActiveTerminalControl().SearchBoxEditInFocus()) + const auto& terminalControl{ tab->GetActiveTerminalControl() }; // maybe null + // If we're + // * NOT in a rename + // * AND (the content isn't a TermControl, OR the term control doesn't have focus in the search box) + if (!tab->_headerControl.InRename() && + (terminalControl == nullptr || !terminalControl.SearchBoxEditInFocus())) { tab->RequestFocusActiveControl.raise(); } @@ -1695,6 +1744,18 @@ namespace winrt::TerminalApp::implementation return _zoomedPane != nullptr; } + TermControl _termControlFromPane(const auto& pane) + { + if (const auto content{ pane->GetContent() }) + { + if (const auto termContent{ content.try_as() }) + { + return termContent.GetTermControl(); + } + } + return nullptr; + } + // Method Description: // - Toggle read-only mode on the active pane // - If a parent pane is selected, this will ensure that all children have @@ -1706,14 +1767,14 @@ namespace winrt::TerminalApp::implementation auto hasReadOnly = false; auto allReadOnly = true; _activePane->WalkTree([&](const auto& p) { - if (const auto& control{ p->GetTerminalControl() }) + if (const auto& control{ _termControlFromPane(p) }) { hasReadOnly |= control.ReadOnly(); allReadOnly &= control.ReadOnly(); } }); _activePane->WalkTree([&](const auto& p) { - if (const auto& control{ p->GetTerminalControl() }) + if (const auto& control{ _termControlFromPane(p) }) { // If all controls have the same read only state then just toggle if (allReadOnly || !hasReadOnly) @@ -1738,14 +1799,14 @@ namespace winrt::TerminalApp::implementation auto hasReadOnly = false; auto allReadOnly = true; _activePane->WalkTree([&](const auto& p) { - if (const auto& control{ p->GetTerminalControl() }) + if (const auto& control{ _termControlFromPane(p) }) { hasReadOnly |= control.ReadOnly(); allReadOnly &= control.ReadOnly(); } }); _activePane->WalkTree([&](const auto& p) { - if (const auto& control{ p->GetTerminalControl() }) + if (const auto& control{ _termControlFromPane(p) }) { // If all controls have the same read only state then just disable if (allReadOnly || !hasReadOnly) @@ -1776,6 +1837,10 @@ namespace winrt::TerminalApp::implementation ReadOnly(_rootPane->ContainsReadOnly()); _updateIsClosable(); + + // Update all the visuals on all our panes, so they can update their + // border colors accordingly. + _rootPane->WalkTree([](const auto& p) { p->UpdateVisuals(); }); } std::shared_ptr TerminalTab::GetActivePane() const @@ -1826,10 +1891,10 @@ namespace winrt::TerminalApp::implementation { return; } - if (const auto& control{ p->GetTerminalControl() }) + if (const auto& control{ _termControlFromPane(p) }) { - auto it = _controlEvents.find(*paneId); - if (it != _controlEvents.end()) + auto it = _contentEvents.find(*paneId); + if (it != _contentEvents.end()) { auto& events = it->second; @@ -1847,7 +1912,7 @@ namespace winrt::TerminalApp::implementation }); } - void TerminalTab::_addBroadcastHandlers(const TermControl& termControl, ControlEventTokens& events) + void TerminalTab::_addBroadcastHandlers(const TermControl& termControl, ContentEventTokens& events) { auto weakThis{ get_weak() }; // ADD EVENT HANDLERS HERE @@ -1937,4 +2002,9 @@ namespace winrt::TerminalApp::implementation ActionAndArgs actionAndArgs{ ShortcutAction::Find, nullptr }; _dispatch.DoAction(*this, actionAndArgs); } + void TerminalTab::_bubbleRestartTerminalRequested(TerminalApp::TerminalPaneContent sender, + const winrt::Windows::Foundation::IInspectable& args) + { + RestartTerminalRequested.raise(sender, args); + } } diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index ca5901af648..df9e4cae825 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -25,6 +25,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl GetActiveTerminalControl() const; winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile() const noexcept; + winrt::TerminalApp::IPaneContent GetActiveContent() const; void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override; @@ -96,6 +97,8 @@ namespace winrt::TerminalApp::implementation return _tabStatus; } + til::typed_event RestartTerminalRequested; + til::event> ActivePaneChanged; til::event> TabRaiseVisualBell; til::typed_event TaskbarProgressChanged; @@ -122,20 +125,25 @@ namespace winrt::TerminalApp::implementation winrt::event_token _colorClearedToken; winrt::event_token _pickerClosedToken; - struct ControlEventTokens + struct ContentEventTokens { - winrt::event_token titleToken; - winrt::event_token colorToken; - winrt::event_token taskbarToken; - winrt::event_token stateToken; - winrt::event_token readOnlyToken; - winrt::event_token focusToken; - + winrt::TerminalApp::IPaneContent::BellRequested_revoker BellRequested; + winrt::TerminalApp::IPaneContent::TitleChanged_revoker TitleChanged; + winrt::TerminalApp::IPaneContent::TabColorChanged_revoker TabColorChanged; + winrt::TerminalApp::IPaneContent::TaskbarProgressChanged_revoker TaskbarProgressChanged; + winrt::TerminalApp::IPaneContent::ConnectionStateChanged_revoker ConnectionStateChanged; + winrt::TerminalApp::IPaneContent::ReadOnlyChanged_revoker ReadOnlyChanged; + winrt::TerminalApp::IPaneContent::FocusRequested_revoker FocusRequested; + winrt::TerminalApp::IPaneContent::CloseRequested_revoker CloseRequested; + + // These events literally only apply if the content is a TermControl. winrt::Microsoft::Terminal::Control::TermControl::KeySent_revoker KeySent; winrt::Microsoft::Terminal::Control::TermControl::CharSent_revoker CharSent; winrt::Microsoft::Terminal::Control::TermControl::StringSent_revoker StringSent; + + winrt::TerminalApp::TerminalPaneContent::RestartTerminalRequested_revoker RestartTerminalRequested; }; - std::unordered_map _controlEvents; + std::unordered_map _contentEvents; winrt::event_token _rootClosedToken{}; @@ -162,8 +170,8 @@ namespace winrt::TerminalApp::implementation void _CreateContextMenu() override; virtual winrt::hstring _CreateToolTipTitle() override; - void _DetachEventHandlersFromControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control); - void _AttachEventHandlersToControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control); + void _DetachEventHandlersFromContent(const uint32_t paneId); + void _AttachEventHandlersToContent(const uint32_t paneId, const winrt::TerminalApp::IPaneContent& content); void _AttachEventHandlersToPane(std::shared_ptr pane); void _UpdateActivePane(std::shared_ptr pane); @@ -181,7 +189,7 @@ namespace winrt::TerminalApp::implementation virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override; - void _addBroadcastHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control, ControlEventTokens& events); + void _addBroadcastHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control, ContentEventTokens& events); void _chooseColorClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); void _renameTabClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); @@ -192,6 +200,8 @@ namespace winrt::TerminalApp::implementation void _moveTabToNewWindowClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); void _findClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); + void _bubbleRestartTerminalRequested(TerminalApp::TerminalPaneContent sender, const winrt::Windows::Foundation::IInspectable& args); + friend class ::TerminalAppLocalTests::TabTests; }; } diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index 6abc67781f2..e82056e57f9 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -84,6 +84,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider); // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" +#include #include From e8f18ea92cc769f4b2584b7c26487dfdf7d78f96 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 26 Mar 2024 12:55:14 -0500 Subject: [PATCH 179/603] ci: trigger builds for gh-readonly-queue/* merge queue branches (#16941) We may try a thing! --- build/pipelines/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pipelines/ci.yml b/build/pipelines/ci.yml index ab3490bc58b..843267b4be6 100644 --- a/build/pipelines/ci.yml +++ b/build/pipelines/ci.yml @@ -4,6 +4,7 @@ trigger: include: - main - feature/* + - gh-readonly-queue/* paths: exclude: - doc/* From 501522d6900b0a10de67d1bee82664fbc65d9738 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Mar 2024 10:56:31 -0700 Subject: [PATCH 180/603] Add an experimental "scratchpad pane", for testing (#16171) ## Summary of the Pull Request Builds upon #16170. This PR simply adds a singly type of non-terminal pane - a "scratchpad pane". This is literally just a single text box, in a pane. It's on the `{ "command": "experimental.openScratchpad" }` action. ## References and Relevant Issues See: #997 ## Detailed Description of the Pull Request / Additional comments I also put it behind velocity so it won't even go into preview while this bakes. This is really just here to demonstrate that this works, and is viable. The next PR is much more interesting. ## Validation Steps Performed Screenshot below. ## other PRs * #16170 * #16171 <-- you are here * #16172 --- .../TerminalApp/AppActionHandlers.cpp | 20 ++++++++ .../TerminalApp/ScratchpadContent.cpp | 50 +++++++++++++++++++ src/cascadia/TerminalApp/ScratchpadContent.h | 40 +++++++++++++++ .../TerminalApp/TerminalAppLib.vcxproj | 6 +++ .../TerminalSettingsModel/ActionAndArgs.cpp | 2 + .../AllShortcutActions.h | 1 + .../Resources/en-US/Resources.resw | 5 +- src/features.xml | 11 ++++ 8 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/cascadia/TerminalApp/ScratchpadContent.cpp create mode 100644 src/cascadia/TerminalApp/ScratchpadContent.h diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index df357864c3d..662ffc25746 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -5,6 +5,7 @@ #include "App.h" #include "TerminalPage.h" +#include "ScratchpadContent.h" #include "../WinRTUtils/inc/WtExeUtils.h" #include "../../types/inc/utils.hpp" #include "Utils.h" @@ -1416,6 +1417,25 @@ namespace winrt::TerminalApp::implementation } args.Handled(true); } + + void TerminalPage::_HandleOpenScratchpad(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + if (Feature_ScratchpadPane::IsEnabled()) + { + const auto& scratchPane{ winrt::make_self() }; + + // This is maybe a little wacky - add our key event handler to the pane + // we made. So that we can get actions for keys that the content didn't + // handle. + scratchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler }); + + const auto resultPane = std::make_shared(*scratchPane); + _SplitPane(_GetFocusedTabImpl(), SplitDirection::Automatic, 0.5f, resultPane); + args.Handled(true); + } + } + void TerminalPage::_HandleOpenAbout(const IInspectable& /*sender*/, const ActionEventArgs& args) { diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp new file mode 100644 index 00000000000..dcb13697b54 --- /dev/null +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "ScratchpadContent.h" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +namespace winrt::TerminalApp::implementation +{ + ScratchpadContent::ScratchpadContent() + { + _root = winrt::Windows::UI::Xaml::Controls::Grid{}; + // Vertical and HorizontalAlignment are Stretch by default + + auto res = Windows::UI::Xaml::Application::Current().Resources(); + auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush")); + _root.Background(bg.try_as()); + + _box = winrt::Windows::UI::Xaml::Controls::TextBox{}; + _box.Margin({ 10, 10, 10, 10 }); + _box.AcceptsReturn(true); + _box.TextWrapping(TextWrapping::Wrap); + _root.Children().Append(_box); + } + + winrt::Windows::UI::Xaml::FrameworkElement ScratchpadContent::GetRoot() + { + return _root; + } + winrt::Windows::Foundation::Size ScratchpadContent::MinimumSize() + { + return { 1, 1 }; + } + void ScratchpadContent::Focus(winrt::Windows::UI::Xaml::FocusState reason) + { + _box.Focus(reason); + } + void ScratchpadContent::Close() + { + CloseRequested.raise(*this, nullptr); + } + + NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const bool /* asContent */) const + { + return nullptr; + } +} diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h new file mode 100644 index 00000000000..10aa36b6b85 --- /dev/null +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "winrt/TerminalApp.h" + +namespace winrt::TerminalApp::implementation +{ + class ScratchpadContent : public winrt::implements + { + public: + ScratchpadContent(); + + winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); + + winrt::Windows::Foundation::Size MinimumSize(); + + void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); + void Close(); + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const; + + winrt::hstring Title() { return L"Scratchpad"; } + uint64_t TaskbarState() { return 0; } + uint64_t TaskbarProgress() { return 0; } + bool ReadOnly() { return false; } + + til::typed_event<> ConnectionStateChanged; + til::typed_event CloseRequested; + til::typed_event BellRequested; + til::typed_event TitleChanged; + til::typed_event TabColorChanged; + til::typed_event TaskbarProgressChanged; + til::typed_event ReadOnlyChanged; + til::typed_event FocusRequested; + + private: + winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr }; + winrt::Windows::UI::Xaml::Controls::TextBox _box{ nullptr }; + }; +} diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index d6abba2db90..827e9dcf168 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -161,6 +161,9 @@ TerminalPaneContent.idl + + TerminalPaneContent.idl + SuggestionsControl.xaml @@ -268,6 +271,9 @@ TerminalPaneContent.idl + + ScratchpadContent.idl + diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index e90a2b8dcfa..66126cda15f 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -96,6 +96,7 @@ static constexpr std::string_view ShowContextMenuKey{ "showContextMenu" }; static constexpr std::string_view ExpandSelectionToWordKey{ "expandSelectionToWord" }; static constexpr std::string_view RestartConnectionKey{ "restartConnection" }; static constexpr std::string_view ToggleBroadcastInputKey{ "toggleBroadcastInput" }; +static constexpr std::string_view OpenScratchpadKey{ "experimental.openScratchpad" }; static constexpr std::string_view OpenAboutKey{ "openAbout" }; static constexpr std::string_view ActionKey{ "action" }; @@ -431,6 +432,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") }, { ShortcutAction::RestartConnection, RS_(L"RestartConnectionKey") }, { ShortcutAction::ToggleBroadcastInput, RS_(L"ToggleBroadcastInputCommandKey") }, + { ShortcutAction::OpenScratchpad, RS_(L"OpenScratchpadKey") }, { ShortcutAction::OpenAbout, RS_(L"OpenAboutCommandKey") }, }; }(); diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h index f9d934e36e0..28ba4281e69 100644 --- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h +++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h @@ -110,6 +110,7 @@ ON_ALL_ACTIONS(CloseOtherPanes) \ ON_ALL_ACTIONS(RestartConnection) \ ON_ALL_ACTIONS(ToggleBroadcastInput) \ + ON_ALL_ACTIONS(OpenScratchpad) \ ON_ALL_ACTIONS(OpenAbout) #define ALL_SHORTCUT_ACTIONS_WITH_ARGS \ diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index d6d6d9565f9..03e330d9ecd 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -700,6 +700,9 @@ Restart connection + + Open scratchpad + Select next command output @@ -724,4 +727,4 @@ Open about dialog This will open the "about" dialog, to display version info and other documentation - + \ No newline at end of file diff --git a/src/features.xml b/src/features.xml index e8fd44179d3..60191f62286 100644 --- a/src/features.xml +++ b/src/features.xml @@ -159,6 +159,17 @@
+ + Feature_ScratchpadPane + Allow the user to create scratchpad panes. Mostly just exists to validate non-terminal panes. + 997 + AlwaysDisabled + + Dev + Canary + + + Feature_KeypadModeEnabled Enables the DECKPAM, DECKPNM sequences to work as intended From a67a13288c9173a399143b246e18af2300ca8ee8 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 26 Mar 2024 22:31:24 +0100 Subject: [PATCH 181/603] AtlasEngine: Make Direct2D/3D and Present1 configurable (#16939) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements `SetForceFullRepaintRendering` and adds a new `SetGraphicsAPI` function. The former toggles `Present1` on and off and the latter allows users to explicitly request Direct2D/3D. On top of these changes I did a minor cleanup of the interface, because now that DxRenderer is gone we don't need all that anymore. Closes #14254 Closes #16747 ## Validation Steps Performed * Toggling Direct2D on/off changes colored ligature support ✅ * Toggling Present1 on/off can be observed in a debugger ✅ * Toggling WARP on/off changes GPU metrics ✅ --------- Co-authored-by: Dustin L. Howett --- doc/cascadia/profiles.schema.json | 16 +- src/cascadia/TerminalControl/ControlCore.cpp | 54 ++++--- src/cascadia/TerminalControl/ControlCore.h | 28 ++-- src/cascadia/TerminalControl/EventArgs.idl | 8 +- src/cascadia/TerminalControl/HwndTerminal.cpp | 1 - .../TerminalControl/IControlSettings.idl | 3 +- src/cascadia/TerminalControl/TermControl.cpp | 2 +- .../TerminalSettingsEditor/Rendering.xaml | 20 ++- .../RenderingViewModel.cpp | 6 +- .../RenderingViewModel.h | 4 +- .../RenderingViewModel.idl | 4 +- .../Resources/de-DE/Resources.resw | 10 +- .../Resources/en-US/Resources.resw | 49 +++--- .../Resources/es-ES/Resources.resw | 10 +- .../Resources/fr-FR/Resources.resw | 10 +- .../Resources/it-IT/Resources.resw | 10 +- .../Resources/ja-JP/Resources.resw | 10 +- .../Resources/ko-KR/Resources.resw | 10 +- .../Resources/pt-BR/Resources.resw | 10 +- .../Resources/qps-ploc/Resources.resw | 10 +- .../Resources/qps-ploca/Resources.resw | 10 +- .../Resources/qps-plocm/Resources.resw | 10 +- .../Resources/ru-RU/Resources.resw | 10 +- .../Resources/zh-CN/Resources.resw | 10 +- .../Resources/zh-TW/Resources.resw | 10 +- .../TerminalSettingsModel/EnumMappings.cpp | 1 + .../TerminalSettingsModel/EnumMappings.h | 1 + .../TerminalSettingsModel/EnumMappings.idl | 1 + .../GlobalAppSettings.cpp | 18 ++- .../TerminalSettingsModel/GlobalAppSettings.h | 2 +- .../GlobalAppSettings.idl | 3 +- .../TerminalSettingsModel/MTSMSettings.h | 5 +- .../TerminalSettings.cpp | 3 +- .../TerminalSettingsModel/TerminalSettings.h | 3 +- .../TerminalSettingsSerializationHelpers.h | 9 ++ .../SerializationTests.cpp | 2 - src/cascadia/inc/ControlProperties.h | 3 +- src/cppwinrt.build.pre.props | 1 - src/renderer/atlas/AtlasEngine.api.cpp | 45 +++--- src/renderer/atlas/AtlasEngine.cpp | 2 +- src/renderer/atlas/AtlasEngine.h | 44 +++--- src/renderer/atlas/AtlasEngine.r.cpp | 146 +++++++++--------- src/renderer/atlas/Backend.h | 3 - src/renderer/atlas/common.h | 11 +- src/renderer/base/RenderEngineBase.cpp | 4 + src/renderer/inc/IRenderEngine.hpp | 28 +--- src/renderer/inc/RenderEngineBase.hpp | 1 + src/renderer/uia/UiaRenderer.hpp | 2 +- src/renderer/wddmcon/WddmConRenderer.hpp | 2 +- 49 files changed, 351 insertions(+), 314 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 7225059372a..6733fe52266 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2332,12 +2332,20 @@ }, "type": "array" }, - "experimental.rendering.forceFullRepaint": { - "description": "When set to true, we will redraw the entire screen each frame. When set to false, we will render only the updates to the screen between frames.", + "rendering.graphicsAPI": { + "description": "Direct3D 11 provides a more performant and feature-rich experience, whereas Direct2D is more stable. The default option \"Automatic\" will pick the API that best fits your graphics hardware. If you experience significant issues, consider using Direct2D.", + "type": "string", + "enum": [ + "direct2d", + "direct3d11" + ] + }, + "rendering.disablePartialInvalidation": { + "description": "By default, the text renderer uses a FLIP_SEQUENTIAL Swap Chain and declares dirty rectangles via the Present1 API. When this setting is enabled, a FLIP_DISCARD Swap Chain will be used instead, and no dirty rectangles will be declared. Whether one or the other is better depends on your hardware and various other factors.", "type": "boolean" }, - "experimental.rendering.software": { - "description": "When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one.", + "rendering.software": { + "description": "When enabled, the terminal will use a software rasterizer (WARP). This setting should be left disabled under almost all circumstances.", "type": "boolean" }, "experimental.input.forceVT": { diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 4027e914d8f..a1070d9b811 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -9,14 +9,13 @@ #include #include -#include #include -#include #include #include "EventArgs.h" -#include "../../buffer/out/search.h" #include "../../renderer/atlas/AtlasEngine.h" +#include "../../renderer/base/renderer.hpp" +#include "../../renderer/uia/UiaRenderer.hpp" #include "ControlCore.g.cpp" #include "SelectionColor.g.cpp" @@ -43,26 +42,26 @@ constexpr const auto SearchAfterChangeDelay = std::chrono::milliseconds(200); namespace winrt::Microsoft::Terminal::Control::implementation { - static winrt::Microsoft::Terminal::Core::OptionalColor OptionalFromColor(const til::color& c) + static winrt::Microsoft::Terminal::Core::OptionalColor OptionalFromColor(const til::color& c) noexcept { Core::OptionalColor result; result.Color = c; result.HasValue = true; return result; } - static winrt::Microsoft::Terminal::Core::OptionalColor OptionalFromColor(const std::optional& c) + + static ::Microsoft::Console::Render::Atlas::GraphicsAPI parseGraphicsAPI(GraphicsAPI api) noexcept { - Core::OptionalColor result; - if (c.has_value()) - { - result.Color = *c; - result.HasValue = true; - } - else + using GA = ::Microsoft::Console::Render::Atlas::GraphicsAPI; + switch (api) { - result.HasValue = false; + case GraphicsAPI::Direct2D: + return GA::Direct2D; + case GraphicsAPI::Direct3D11: + return GA::Direct3D11; + default: + return GA::Automatic; } - return result; } TextColor SelectionColor::AsTextColor() const noexcept @@ -388,7 +387,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation _renderEngine->SetRetroTerminalEffect(_settings->RetroTerminalEffect()); _renderEngine->SetPixelShaderPath(_settings->PixelShaderPath()); _renderEngine->SetPixelShaderImagePath(_settings->PixelShaderImagePath()); - _renderEngine->SetForceFullRepaintRendering(_settings->ForceFullRepaintRendering()); + _renderEngine->SetGraphicsAPI(parseGraphicsAPI(_settings->GraphicsAPI())); + _renderEngine->SetDisablePartialInvalidation(_settings->DisablePartialInvalidation()); _renderEngine->SetSoftwareRendering(_settings->SoftwareRendering()); _updateAntiAliasingMode(); @@ -397,8 +397,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // GH#11315: Always do this, even if they don't have acrylic on. _renderEngine->EnableTransparentBackground(_isBackgroundTransparent()); - THROW_IF_FAILED(_renderEngine->Enable()); - _initializedTerminal.store(true, std::memory_order_relaxed); } // scope for TerminalLock @@ -883,7 +881,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _renderEngine->SetForceFullRepaintRendering(_settings->ForceFullRepaintRendering()); + _renderEngine->SetGraphicsAPI(parseGraphicsAPI(_settings->GraphicsAPI())); + _renderEngine->SetDisablePartialInvalidation(_settings->DisablePartialInvalidation()); _renderEngine->SetSoftwareRendering(_settings->SoftwareRendering()); // Inform the renderer of our opacity _renderEngine->EnableTransparentBackground(_isBackgroundTransparent()); @@ -946,6 +945,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + Control::IControlSettings ControlCore::Settings() + { + return *_settings; + } + + Control::IControlAppearance ControlCore::FocusedAppearance() const + { + return *_settings->FocusedAppearance(); + } + + Control::IControlAppearance ControlCore::UnfocusedAppearance() const + { + return *_settings->UnfocusedAppearance(); + } + void ControlCore::_updateAntiAliasingMode() { D2D1_TEXT_ANTIALIAS_MODE mode; @@ -1960,13 +1974,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation UpdateSelectionMarkers.raise(*this, winrt::make(!showMarkers)); } - void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine) + void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::UiaEngine* const pEngine) { // _renderer will always exist since it's introduced in the ctor const auto lock = _terminal->LockForWriting(); _renderer->AddRenderEngine(pEngine); } - void ControlCore::DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine) + void ControlCore::DetachUiaEngine(::Microsoft::Console::Render::UiaEngine* const pEngine) { const auto lock = _terminal->LockForWriting(); _renderer->RemoveRenderEngine(pEngine); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index d79df07d1bc..218d1b842eb 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -18,12 +18,22 @@ #include "ControlCore.g.h" #include "SelectionColor.g.h" #include "CommandHistoryContext.g.h" + #include "ControlSettings.h" #include "../../audio/midi/MidiAudio.hpp" -#include "../../renderer/base/Renderer.hpp" +#include "../../buffer/out/search.h" #include "../../cascadia/TerminalCore/Terminal.hpp" -#include "../buffer/out/search.h" -#include "../buffer/out/TextColor.h" +#include "../../renderer/inc/FontInfoDesired.hpp" + +namespace Microsoft::Console::Render::Atlas +{ + class AtlasEngine; +} + +namespace Microsoft::Console::Render +{ + class UiaEngine; +} namespace ControlUnitTests { @@ -82,9 +92,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation void UpdateSettings(const Control::IControlSettings& settings, const IControlAppearance& newAppearance); void ApplyAppearance(const bool& focused); - Control::IControlSettings Settings() { return *_settings; }; - Control::IControlAppearance FocusedAppearance() const { return *_settings->FocusedAppearance(); }; - Control::IControlAppearance UnfocusedAppearance() const { return *_settings->UnfocusedAppearance(); }; + Control::IControlSettings Settings(); + Control::IControlAppearance FocusedAppearance() const; + Control::IControlAppearance UnfocusedAppearance() const; bool HasUnfocusedAppearance() const; winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; @@ -219,8 +229,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation const bool isOnOriginalPosition, bool& selectionNeedsToBeCopied); - void AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine); - void DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine); + void AttachUiaEngine(::Microsoft::Console::Render::UiaEngine* const pEngine); + void DetachUiaEngine(::Microsoft::Console::Render::UiaEngine* const pEngine); bool IsInReadOnlyMode() const; void ToggleReadOnlyMode(); @@ -306,7 +316,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // As _renderer has a dependency on _renderEngine (through a raw pointer) // we must ensure the _renderer is deallocated first. // (C++ class members are destroyed in reverse order.) - std::unique_ptr<::Microsoft::Console::Render::IRenderEngine> _renderEngine{ nullptr }; + std::unique_ptr<::Microsoft::Console::Render::Atlas::AtlasEngine> _renderEngine{ nullptr }; std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr }; ::Search _searcher; diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 3caea5a0f38..978d69d8e36 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -11,6 +11,12 @@ namespace Microsoft.Terminal.Control All = 0xffffffff }; + enum GraphicsAPI + { + Automatic, + Direct2D, + Direct3D11, + }; runtimeclass FontSizeChangedArgs { @@ -90,7 +96,7 @@ namespace Microsoft.Terminal.Control { Boolean ShowOrHide { get; }; } - + runtimeclass UpdateSelectionMarkersEventArgs { Boolean ClearMarkers { get; }; diff --git a/src/cascadia/TerminalControl/HwndTerminal.cpp b/src/cascadia/TerminalControl/HwndTerminal.cpp index c34e5b4523f..bd05b7ca8a2 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.cpp +++ b/src/cascadia/TerminalControl/HwndTerminal.cpp @@ -217,7 +217,6 @@ HRESULT HwndTerminal::Initialize() auto engine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>(); RETURN_IF_FAILED(engine->SetHwnd(_hwnd.get())); - RETURN_IF_FAILED(engine->Enable()); _renderer->AddRenderEngine(engine.get()); _UpdateFont(USER_DEFAULT_SCREEN_DPI); diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index 63c35d8a952..2db92a86a83 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -58,7 +58,8 @@ namespace Microsoft.Terminal.Control TextAntialiasingMode AntialiasingMode { get; }; // Experimental Settings - Boolean ForceFullRepaintRendering { get; }; + Microsoft.Terminal.Control.GraphicsAPI GraphicsAPI { get; }; + Boolean DisablePartialInvalidation { get; }; Boolean SoftwareRendering { get; }; Boolean ShowMarks { get; }; Boolean UseBackgroundImageForWindow { get; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index de8ec920d3e..c8b378672c5 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2409,7 +2409,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation LOG_IF_FAILED(engine->UpdateDpi(dpi)); LOG_IF_FAILED(engine->UpdateFont(desiredFont, actualFont)); - const auto scale = engine->GetScaling(); + const auto scale = dpi / static_cast(USER_DEFAULT_SCREEN_DPI); const auto actualFontSize = actualFont.GetSize(); // UWP XAML scrollbars aren't guaranteed to be the same size as the diff --git a/src/cascadia/TerminalSettingsEditor/Rendering.xaml b/src/cascadia/TerminalSettingsEditor/Rendering.xaml index fe057486859..4b9534167d0 100644 --- a/src/cascadia/TerminalSettingsEditor/Rendering.xaml +++ b/src/cascadia/TerminalSettingsEditor/Rendering.xaml @@ -15,20 +15,28 @@ + + + + - + + + - - - + - diff --git a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp index 42190c4e6a7..74c4d5922a8 100644 --- a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp @@ -3,6 +3,9 @@ #include "pch.h" #include "RenderingViewModel.h" + +#include "EnumEntry.h" + #include "RenderingViewModel.g.cpp" using namespace winrt::Windows::Foundation; @@ -10,8 +13,9 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { - RenderingViewModel::RenderingViewModel(Model::CascadiaSettings settings) noexcept : + RenderingViewModel::RenderingViewModel(CascadiaSettings settings) noexcept : _settings{ std::move(settings) } { + INITIALIZE_BINDABLE_ENUM_SETTING(GraphicsAPI, GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, L"Globals_GraphicsAPI_", L"Text"); } } diff --git a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h index 663d4cef877..b3042d893a1 100644 --- a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h @@ -4,6 +4,7 @@ #pragma once #include "RenderingViewModel.g.h" +#include "Utils.h" #include "ViewModelHelpers.h" namespace winrt::Microsoft::Terminal::Settings::Editor::implementation @@ -12,7 +13,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { explicit RenderingViewModel(Model::CascadiaSettings settings) noexcept; - PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), ForceFullRepaintRendering); + GETSET_BINDABLE_ENUM_SETTING(GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, _settings.GlobalSettings().GraphicsAPI); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), DisablePartialInvalidation); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), SoftwareRendering); private: diff --git a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl index aa728bfad03..1ca164fbd97 100644 --- a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl @@ -11,7 +11,9 @@ namespace Microsoft.Terminal.Settings.Editor { RenderingViewModel(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); - PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ForceFullRepaintRendering); + IInspectable CurrentGraphicsAPI; + Windows.Foundation.Collections.IObservableVector GraphicsAPIList { get; }; + PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DisablePartialInvalidation); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, SoftwareRendering); } } diff --git a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw index 5c27fe81845..13a0809dfd4 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw @@ -311,13 +311,13 @@ Die Terminalanwendung, die gestartet wird, wenn eine Befehlszeilenanwendung ohne vorhandene Sitzung gestartet wird, beispielsweise vom Startmenü oder über das Dialogfeld "Ausführen". A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Gesamten Bildschirm beim Anzeigen von Updates aktualisieren Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Wenn diese Option deaktiviert ist, rendert das Terminal die Aktualisierungen nur zwischen den Frames auf den Bildschirm. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Spalten @@ -446,10 +446,6 @@ An das zuletzt verwendete Fenster auf diesem Desktop anhängen An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Diese Einstellungen eignen sich möglicherweise für die Problembehandlung, Sie wirken sich jedoch auf die Leistung aus. - A disclaimer presented at the top of a page. - Titelleiste ausblenden (Neustart erforderlich) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 5bb762f6b22..e8995079ead 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -311,13 +311,38 @@ The terminal application that launches when a command-line application is run without an existing session, like from the Start Menu or Run dialog. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - Redraw entire screen when display updates - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. + + Graphics API + This text is shown next to a list of choices. - - When disabled, the terminal will render only the updates to the screen between frames. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + + Direct3D 11 provides a more performant and feature-rich experience, whereas Direct2D is more stable. The default option "Automatic" will pick the API that best fits your graphics hardware. If you experience significant issues, consider using Direct2D. + + + Automatic + The default choice between multiple graphics APIs. + + + Direct2D + + + Direct3D 11 + + + Disable partial Swap Chain invalidation + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + By default, the text renderer uses a FLIP_SEQUENTIAL Swap Chain and declares dirty rectangles via the Present1 API. When this setting is enabled, a FLIP_DISCARD Swap Chain will be used instead, and no dirty rectangles will be declared. Whether one or the other is better depends on your hardware and various other factors. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Use software rendering (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + When enabled, the terminal will use a software rasterizer (WARP). This setting should be left disabled under almost all circumstances. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". Columns @@ -446,10 +471,6 @@ Attach to the most recently used window on this desktop An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - These settings may be useful for troubleshooting an issue, however they will impact your performance. - A disclaimer presented at the top of a page. - Hide the title bar (requires relaunch) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. @@ -478,14 +499,6 @@ When disabled, the window will resize smoothly. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Use software rendering - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - When enabled, the terminal will use the software renderer (a.k.a. WARP) instead of the hardware one. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Launch on machine startup Header for a control to toggle whether the app should launch when the user's machine starts up, or not. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index ef08ff08fe1..76400095b16 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -311,13 +311,13 @@ Aplicación de terminal que se inicia cuando se ejecuta una aplicación de línea de comandos sin una sesión existente, como en el menú Inicio o en el cuadro de diálogo Ejecutar. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Redibujar toda la pantalla cuando se muestren actualizaciones Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Cuando está deshabilitada, el terminal representará solo las actualizaciones de la pantalla entre fotogramas. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Columnas @@ -446,10 +446,6 @@ Adjuntar a la ventana de uso más reciente en este escritorio An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Esta configuración puede ser útil para solucionar un problema, pero afectará al rendimiento. - A disclaimer presented at the top of a page. - Ocultar la barra de título (requiere reiniciar) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index 37caf840fca..2887b95e286 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -311,13 +311,13 @@ L’application Terminal qui se lance lorsqu’une application de ligne de commande est exécutée sans session existante, par exemple à partir du menu Démarrer ou de la boîte de dialogue Exécuter. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Redessiner l’intégralité de l’écran entre chaque trame Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Une fois désactivé, le terminal n’affiche à l’écran que les modifications effectuées entre les trames. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Colonnes @@ -446,10 +446,6 @@ Attacher à la dernière fenêtre utilisée sur ce bureau An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Ces paramètres permettent parfois de résoudre des problèmes, mais ont un impact sur la performance. - A disclaimer presented at the top of a page. - Masquer la barre de titre (redémarrage nécessaire) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index cd06973a50e..4ce446f17ba 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -311,13 +311,13 @@ L'applicazione terminale che viene avviata quando viene eseguita un'applicazione della riga di comando senza una sessione esistente, ad esempio dalla finestra di dialogo menu Start o Esegui. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Ridisegna l'intero schermo durante la visualizzazione degli aggiornamenti Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Se disabilitato, il terminale eseguirà il rendering solo degli aggiornamenti sullo schermo tra i fotogrammi. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Colonne @@ -446,10 +446,6 @@ Allega alla finestra usata più di recente su questo desktop An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Queste impostazioni potrebbero essere utili per la risoluzione di un problema, ma influiranno sulle prestazioni. - A disclaimer presented at the top of a page. - Nascondi la barra del titolo (sarà necessario riavviare) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index a8935f14470..d57a8288d57 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -311,13 +311,13 @@ [スタート] メニューや [ファイル名を指定して実行] ダイアログなど、既存のセッションなしでコマンドライン アプリケーションを実行したときに起動するターミナル アプリケーション。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + ディスプレイの更新時に画面全体を再描画する Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + 無効にすると、ターミナルはフレームとフレームの間において情報更新分のみレンダリングします。 - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". @@ -446,10 +446,6 @@ このデスクトップで最近使用したウィンドウに接続する An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - これらの設定は問題のトラブルシューティングに役立つ場合がありますが、パフォーマンスに影響を与えます。 - A disclaimer presented at the top of a page. - タイトル バーを非表示にする (再起動が必要) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index 9c8f8e9663f..aef454bd85d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -311,13 +311,13 @@ 시작 메뉴나 실행 대화 상자와 같이 기존 세션이 없으면 명령 줄 응용 프로그램을 실행하고 있는 터미널 응용 프로그램입니다. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + 업데이트를 표시할 때 전체 화면 다시 그리기 Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + 사용하지 않도록 설정하면 터미널이 프레임 간 화면 업데이트만 렌더링합니다. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". @@ -446,10 +446,6 @@ 이 데스크톱에서 가장 최근에 사용한 창에 첨부 An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - 이런 설정은 문제를 해결하는 데는 유용할 수 있지만 성능에 영향을 미칩니다. - A disclaimer presented at the top of a page. - 제목 표시줄 숨기기(다시 시작해야 함) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index 543cb2ec43d..56fff0a611d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -311,13 +311,13 @@ O aplicativo de terminal que é iniciado quando um aplicativo de linha de comando é executado sem uma sessão existente, como no menu iniciar ou na caixa de diálogo Executar. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Redesenhar a tela inteira ao exibir atualizações Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Quando desabilitado, o terminal renderizará somente as atualizações na tela entre quadros. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Colunas @@ -446,10 +446,6 @@ Anexar à última janela usada nesta área de trabalho An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Essas configurações podem ser úteis para solucionar um problema, no entanto, elas impactarão seu desempenho. - A disclaimer presented at the top of a page. - Oculta a barra do título (requer reinicialização) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw index f7193ce8d63..f954fb623fb 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw @@ -311,13 +311,13 @@ Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Ċσŀùмñѕ !! @@ -446,10 +446,6 @@ Äţŧαĉђ τǿ τħе mοѕт ѓěςęńτļγ ûѕєď ŵíñðοω øи тћϊѕ ďёšкτǿφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Τћέŝė ѕëτтίņģš мâу ьê üѕēƒµľ ƒόґ τřóųьŀėѕĥбοτілġ ǻл іŝśü℮, нσώéνєŕ τĥêў ŵīŀł ΐmρåςť убŭя ρěгƒόґмåñĉę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - A disclaimer presented at the top of a page. - Ĥìđε тħê τīţĺё ъªř (ŗėqūΐŗêś яеľаϋʼnčћ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw index f7193ce8d63..f954fb623fb 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw @@ -311,13 +311,13 @@ Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Ċσŀùмñѕ !! @@ -446,10 +446,6 @@ Äţŧαĉђ τǿ τħе mοѕт ѓěςęńτļγ ûѕєď ŵíñðοω øи тћϊѕ ďёšкτǿφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Τћέŝė ѕëτтίņģš мâу ьê üѕēƒµľ ƒόґ τřóųьŀėѕĥбοτілġ ǻл іŝśü℮, нσώéνєŕ τĥêў ŵīŀł ΐmρåςť убŭя ρěгƒόґмåñĉę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - A disclaimer presented at the top of a page. - Ĥìđε тħê τīţĺё ъªř (ŗėqūΐŗêś яеľаϋʼnčћ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw index f7193ce8d63..f954fb623fb 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw @@ -311,13 +311,13 @@ Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Ċσŀùмñѕ !! @@ -446,10 +446,6 @@ Äţŧαĉђ τǿ τħе mοѕт ѓěςęńτļγ ûѕєď ŵíñðοω øи тћϊѕ ďёšкτǿφ !!! !!! !!! !!! !!! ! An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Τћέŝė ѕëτтίņģš мâу ьê üѕēƒµľ ƒόґ τřóųьŀėѕĥбοτілġ ǻл іŝśü℮, нσώéνєŕ τĥêў ŵīŀł ΐmρåςť убŭя ρěгƒόґмåñĉę. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - A disclaimer presented at the top of a page. - Ĥìđε тħê τīţĺё ъªř (ŗėqūΐŗêś яеľаϋʼnčћ) !!! !!! !!! !! Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index 3af0c8f8f6b..c935675a936 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -311,13 +311,13 @@ Приложение терминала, запускаемое при запуске приложения командной строки без существующего сеанса, например из меню "Пуск" или из диалогового окна "Выполнить". A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + Перерисовка всего экрана при обновлении дисплея Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + Если этот параметр отключен, терминал будет отображать только обновления экрана между кадрами. - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". Столбцы @@ -446,10 +446,6 @@ Присоединять к последнему использованному окну на этом рабочем столе An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - Эти параметры могут быть полезны для устранения проблемы, но они повлияют на вашу производительность. - A disclaimer presented at the top of a page. - Скрыть заголовок окна (требуется перезапуск) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 0d602e9adc0..44f22000ddb 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -311,13 +311,13 @@ 当命令行应用程序在没有现有会话(例如从“开始菜单”或“运行”对话框)运行时启动的终端应用程序。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + 显示内容更新时重绘整个屏幕 Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + 禁用后,终端将仅在帧之间将更新呈现到屏幕。 - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". @@ -446,10 +446,6 @@ 附加到此桌面上最近使用的窗口 An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - 这些设置可能有助于解决问题,但会对性能产生影响。 - A disclaimer presented at the top of a page. - 隐藏标题栏(需要重新启动) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index 1b1ffb8bbf2..13ab841670c 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -311,13 +311,13 @@ 當執行命令列應用程式且不使用現有工作模式而啟動的終端機應用程式 (例如從 [開始] 功能表或 [執行] 對話方塊)。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - + 顯示更新時,重新繪製整個螢幕 Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - + 停用時,終端機只會轉譯框架間螢幕的更新。 - A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". + A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". @@ -446,10 +446,6 @@ 附加到此桌面最近使用的視窗 An option to choose from for the "windowing behavior" setting. When selected, new instances open in the most recently used window on this virtual desktop. - - 這些設定可能有助於解決問題,但會影響效能。 - A disclaimer presented at the top of a page. - 隱藏標題列 (需要重新啟動) Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app. diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index 8b3d936a4a3..15665908b78 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -39,6 +39,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Microsoft::Terminal::Control::CopyFormat, CopyFormat); DEFINE_ENUM_MAP(Model::WindowingMode, WindowingMode); DEFINE_ENUM_MAP(Microsoft::Terminal::Core::MatchMode, MatchMode); + DEFINE_ENUM_MAP(Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI); // Profile Settings DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index dfdd6c46502..722ce920953 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap CopyFormat(); static winrt::Windows::Foundation::Collections::IMap WindowingMode(); static winrt::Windows::Foundation::Collections::IMap MatchMode(); + static winrt::Windows::Foundation::Collections::IMap GraphicsAPI(); // Profile Settings static winrt::Windows::Foundation::Collections::IMap CloseOnExitMode(); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index a74dc96b483..11801182999 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -17,6 +17,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap CopyFormat { get; }; static Windows.Foundation.Collections.IMap WindowingMode { get; }; static Windows.Foundation.Collections.IMap MatchMode { get; }; + static Windows.Foundation.Collections.IMap GraphicsAPI { get; }; // Profile Settings static Windows.Foundation.Collections.IMap CloseOnExitMode { get; }; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 7238d38c0cd..c307050a998 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -220,8 +220,24 @@ const std::vector // Return Value: // - the JsonObject representing this instance -Json::Value GlobalAppSettings::ToJson() const +Json::Value GlobalAppSettings::ToJson() { + // These experimental options should be removed from the settings file if they're at their default value. + // This prevents them from sticking around forever, even if the user was just experimenting with them. + // One could consider this a workaround for the settings UI right now not having a "reset to default" button for these. + if (_GraphicsAPI == Control::GraphicsAPI::Automatic) + { + _GraphicsAPI.reset(); + } + if (_DisablePartialInvalidation == false) + { + _DisablePartialInvalidation.reset(); + } + if (_SoftwareRendering == false) + { + _SoftwareRendering.reset(); + } + Json::Value json{ Json::ValueType::objectValue }; JsonUtils::SetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index aa7d8c0147f..c977094df53 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -52,7 +52,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void LayerJson(const Json::Value& json, const OriginTag origin); void LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings = true); - Json::Value ToJson() const; + Json::Value ToJson(); const std::vector& KeybindingsWarnings() const; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 612262d4b11..7e46bcc0517 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -76,7 +76,8 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(FirstWindowPreference, FirstWindowPreference); INHERITABLE_SETTING(LaunchMode, LaunchMode); INHERITABLE_SETTING(Boolean, SnapToGridOnResize); - INHERITABLE_SETTING(Boolean, ForceFullRepaintRendering); + INHERITABLE_SETTING(Microsoft.Terminal.Control.GraphicsAPI, GraphicsAPI); + INHERITABLE_SETTING(Boolean, DisablePartialInvalidation); INHERITABLE_SETTING(Boolean, SoftwareRendering); INHERITABLE_SETTING(Boolean, UseBackgroundImageForWindow); INHERITABLE_SETTING(Boolean, ForceVTInput); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index cb8eb8bcdff..b4c678fd997 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -24,8 +24,9 @@ Author(s): X(hstring, WordDelimiters, "wordDelimiters", DEFAULT_WORD_DELIMITERS) \ X(bool, CopyOnSelect, "copyOnSelect", false) \ X(bool, FocusFollowMouse, "focusFollowMouse", false) \ - X(bool, ForceFullRepaintRendering, "experimental.rendering.forceFullRepaint", false) \ - X(bool, SoftwareRendering, "experimental.rendering.software", false) \ + X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI, "rendering.graphicsAPI") \ + X(bool, DisablePartialInvalidation, "rendering.disablePartialInvalidation", false) \ + X(bool, SoftwareRendering, "rendering.software", false) \ X(bool, UseBackgroundImageForWindow, "experimental.useBackgroundImageForWindow", false) \ X(bool, ForceVTInput, "experimental.input.forceVT", false) \ X(bool, TrimBlockSelection, "trimBlockSelection", true) \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index e5c07a71103..682d79e19c7 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -360,7 +360,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _CopyOnSelect = globalSettings.CopyOnSelect(); _CopyFormatting = globalSettings.CopyFormatting(); _FocusFollowMouse = globalSettings.FocusFollowMouse(); - _ForceFullRepaintRendering = globalSettings.ForceFullRepaintRendering(); + _GraphicsAPI = globalSettings.GraphicsAPI(); + _DisablePartialInvalidation = globalSettings.DisablePartialInvalidation(); _SoftwareRendering = globalSettings.SoftwareRendering(); _UseBackgroundImageForWindow = globalSettings.UseBackgroundImageForWindow(); _ForceVTInput = globalSettings.ForceVTInput(); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 184e05ac8f9..99e3b2d120e 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -155,7 +155,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale); INHERITABLE_SETTING(Model::TerminalSettings, bool, RetroTerminalEffect, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceFullRepaintRendering, false); + INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI); + INHERITABLE_SETTING(Model::TerminalSettings, bool, DisablePartialInvalidation, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, SoftwareRendering, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, UseBackgroundImageForWindow, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index f1f2bc11048..1168e0660fb 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -761,3 +761,12 @@ struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winr return "SelectionColor (#rrggbb, #rgb, #rrggbbaa, iNN)"; } }; + +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::GraphicsAPI) +{ + JSON_MAPPINGS(3) = { + pair_type{ "automatic", ValueType::Automatic }, + pair_type{ "direct2d", ValueType::Direct2D }, + pair_type{ "direct3d11", ValueType::Direct3D11 }, + }; +}; diff --git a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp index 455c0b78c27..8cfd4a14ccf 100644 --- a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp @@ -107,8 +107,6 @@ namespace SettingsModelUnitTests "trimPaste": true, "experimental.input.forceVT": false, - "experimental.rendering.forceFullRepaint": false, - "experimental.rendering.software": false, "actions": [] })" }; diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index a7700b04958..faf5fc6a756 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -73,7 +73,8 @@ X(winrt::hstring, StartingDirectory) \ X(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible) \ X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ - X(bool, ForceFullRepaintRendering, false) \ + X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI) \ + X(bool, DisablePartialInvalidation, false) \ X(bool, SoftwareRendering, false) \ X(bool, UseBackgroundImageForWindow, false) \ X(bool, ShowMarks, false) \ diff --git a/src/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props index ad36921e7b4..7fae29094ed 100644 --- a/src/cppwinrt.build.pre.props +++ b/src/cppwinrt.build.pre.props @@ -9,7 +9,6 @@ - AnyValueHereWillDisableTheOptOut true true en-US diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index a29c5e7779a..f739022e8a4 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -316,33 +316,18 @@ CATCH_RETURN() #pragma endregion -#pragma region DxRenderer - -HRESULT AtlasEngine::Enable() noexcept -{ - return S_OK; -} +#pragma region getter [[nodiscard]] std::wstring_view AtlasEngine::GetPixelShaderPath() noexcept { return _api.s->misc->customPixelShaderPath; } -[[nodiscard]] std::wstring_view AtlasEngine::GetPixelShaderImagePath() noexcept -{ - return _api.s->misc->customPixelShaderImagePath; -} - [[nodiscard]] bool AtlasEngine::GetRetroTerminalEffect() const noexcept { return _api.s->misc->useRetroTerminalEffect; } -[[nodiscard]] float AtlasEngine::GetScaling() const noexcept -{ - return static_cast(_api.s->font->dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); -} - [[nodiscard]] Microsoft::Console::Types::Viewport AtlasEngine::GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept { assert(_api.s->font->cellSize.x != 0); @@ -357,6 +342,10 @@ HRESULT AtlasEngine::Enable() noexcept return Types::Viewport::FromDimensions(viewInCharacters.Origin(), { viewInCharacters.Width() * _api.s->font->cellSize.x, viewInCharacters.Height() * _api.s->font->cellSize.y }); } +#pragma endregion + +#pragma region setter + void AtlasEngine::SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept { const auto mode = static_cast(antialiasingMode); @@ -381,10 +370,6 @@ void AtlasEngine::EnableTransparentBackground(const bool isTransparent) noexcept } } -void AtlasEngine::SetForceFullRepaintRendering(bool enable) noexcept -{ -} - [[nodiscard]] HRESULT AtlasEngine::SetHwnd(const HWND hwnd) noexcept { if (_api.s->target->hwnd != hwnd) @@ -436,9 +421,25 @@ void AtlasEngine::SetSelectionBackground(const COLORREF color, const float alpha void AtlasEngine::SetSoftwareRendering(bool enable) noexcept { - if (_api.s->target->useSoftwareRendering != enable) + if (_api.s->target->useWARP != enable) + { + _api.s.write()->target.write()->useWARP = enable; + } +} + +void AtlasEngine::SetDisablePartialInvalidation(bool enable) noexcept +{ + if (_api.s->target->disablePresent1 != enable) + { + _api.s.write()->target.write()->disablePresent1 = enable; + } +} + +void AtlasEngine::SetGraphicsAPI(GraphicsAPI graphicsAPI) noexcept +{ + if (_api.s->target->graphicsAPI != graphicsAPI) { - _api.s.write()->target.write()->useSoftwareRendering = enable; + _api.s.write()->target.write()->graphicsAPI = graphicsAPI; } } diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index f7ed91b169a..e05e5b033eb 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -550,7 +550,7 @@ void AtlasEngine::_handleSettingsUpdate() if (targetChanged) { - // target->useSoftwareRendering affects the selection of our IDXGIAdapter which requires us to reset _p.dxgi. + // target->useWARP affects the selection of our IDXGIAdapter which requires us to reset _p.dxgi. // This will indirectly also recreate the backend, when AtlasEngine::_recreateAdapter() detects this change. _p.dxgi = {}; } diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 1c238dad048..b75a2a5cdce 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -57,31 +57,29 @@ namespace Microsoft::Console::Render::Atlas [[nodiscard]] HRESULT GetFontSize(_Out_ til::size* pFontSize) noexcept override; [[nodiscard]] HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept override; [[nodiscard]] HRESULT UpdateTitle(std::wstring_view newTitle) noexcept override; - - // DxRenderer - getter - HRESULT Enable() noexcept override; - [[nodiscard]] std::wstring_view GetPixelShaderPath() noexcept override; - [[nodiscard]] std::wstring_view GetPixelShaderImagePath() noexcept override; - [[nodiscard]] bool GetRetroTerminalEffect() const noexcept override; - [[nodiscard]] float GetScaling() const noexcept override; - [[nodiscard]] Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept override; - [[nodiscard]] Types::Viewport GetViewportInPixels(const Types::Viewport& viewInCharacters) const noexcept override; - // DxRenderer - setter - void SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override; - void SetCallback(std::function pfn) noexcept override; - void EnableTransparentBackground(const bool isTransparent) noexcept override; - void SetForceFullRepaintRendering(bool enable) noexcept override; - [[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override; - void SetPixelShaderPath(std::wstring_view value) noexcept override; - void SetPixelShaderImagePath(std::wstring_view value) noexcept override; - void SetRetroTerminalEffect(bool enable) noexcept override; - void SetSelectionBackground(COLORREF color, float alpha = 0.5f) noexcept override; - void SetSoftwareRendering(bool enable) noexcept override; - void SetWarningCallback(std::function pfn) noexcept override; - [[nodiscard]] HRESULT SetWindowSize(til::size pixels) noexcept override; - [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept override; void UpdateHyperlinkHoveredId(uint16_t hoveredId) noexcept override; + // getter + [[nodiscard]] std::wstring_view GetPixelShaderPath() noexcept; + [[nodiscard]] bool GetRetroTerminalEffect() const noexcept; + [[nodiscard]] Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept; + [[nodiscard]] Types::Viewport GetViewportInPixels(const Types::Viewport& viewInCharacters) const noexcept; + // setter + void SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept; + void SetCallback(std::function pfn) noexcept; + void EnableTransparentBackground(const bool isTransparent) noexcept; + [[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept; + void SetPixelShaderPath(std::wstring_view value) noexcept; + void SetPixelShaderImagePath(std::wstring_view value) noexcept; + void SetRetroTerminalEffect(bool enable) noexcept; + void SetSelectionBackground(COLORREF color, float alpha = 0.5f) noexcept; + void SetSoftwareRendering(bool enable) noexcept; + void SetDisablePartialInvalidation(bool enable) noexcept; + void SetGraphicsAPI(GraphicsAPI graphicsAPI) noexcept; + void SetWarningCallback(std::function pfn) noexcept; + [[nodiscard]] HRESULT SetWindowSize(til::size pixels) noexcept; + [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept; + private: // AtlasEngine.cpp ATLAS_ATTR_COLD void _handleSettingsUpdate(); diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index 0c546a8dceb..ac87c246dc9 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -134,7 +134,7 @@ void AtlasEngine::_recreateAdapter() DXGI_ADAPTER_DESC1 desc{}; { - const auto useSoftwareRendering = _p.s->target->useSoftwareRendering; + const auto useWARP = _p.s->target->useWARP; UINT index = 0; do @@ -142,13 +142,13 @@ void AtlasEngine::_recreateAdapter() THROW_IF_FAILED(_p.dxgi.factory->EnumAdapters1(index++, adapter.put())); THROW_IF_FAILED(adapter->GetDesc1(&desc)); - // If useSoftwareRendering is false we exit during the first iteration. Using the default adapter (index 0) + // If useWARP is false we exit during the first iteration. Using the default adapter (index 0) // is the right thing to do under most circumstances, unless you _really_ want to get your hands dirty. // The alternative is to track the window rectangle in respect to all IDXGIOutputs and select the right // IDXGIAdapter, while also considering the "graphics preference" override in the windows settings app, etc. // - // If useSoftwareRendering is true we search until we find the first WARP adapter (usually the last adapter). - } while (useSoftwareRendering && WI_IsFlagClear(desc.Flags, DXGI_ADAPTER_FLAG_SOFTWARE)); + // If useWARP is true we search until we find the first WARP adapter (usually the last adapter). + } while (useWARP && WI_IsFlagClear(desc.Flags, DXGI_ADAPTER_FLAG_SOFTWARE)); } if (memcmp(&_p.dxgi.adapterLuid, &desc.AdapterLuid, sizeof(LUID)) != 0) @@ -166,7 +166,8 @@ void AtlasEngine::_recreateBackend() // HWND, IWindow, or composition surface at a time. --> Destroy it while we still have the old device. _destroySwapChain(); - auto d2dMode = ATLAS_DEBUG_FORCE_D2D_MODE; + auto graphicsAPI = _p.s->target->graphicsAPI; + auto deviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED #ifndef NDEBUG @@ -182,8 +183,14 @@ void AtlasEngine::_recreateBackend() if (WI_IsFlagSet(_p.dxgi.adapterFlags, DXGI_ADAPTER_FLAG_SOFTWARE)) { + // If we're using WARP we don't want to disable those optimizations of course. WI_ClearFlag(deviceFlags, D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS); - d2dMode = true; + + // I'm not sure whether Direct2D is actually faster on WARP, but it's definitely better tested. + if (graphicsAPI == GraphicsAPI::Automatic) + { + graphicsAPI = GraphicsAPI::Direct2D; + } } wil::com_ptr device0; @@ -202,17 +209,16 @@ void AtlasEngine::_recreateBackend() #pragma warning(suppress : 26496) // The variable 'hr' does not change after construction, mark it as const (con.4). auto hr = D3D11CreateDevice( - /* pAdapter */ _p.dxgi.adapter.get(), - /* DriverType */ D3D_DRIVER_TYPE_UNKNOWN, - /* Software */ nullptr, - /* Flags */ deviceFlags, - /* pFeatureLevels */ featureLevels.data(), - /* FeatureLevels */ gsl::narrow_cast(featureLevels.size()), - /* SDKVersion */ D3D11_SDK_VERSION, - /* ppDevice */ device0.put(), - /* pFeatureLevel */ &featureLevel, + /* pAdapter */ _p.dxgi.adapter.get(), + /* DriverType */ D3D_DRIVER_TYPE_UNKNOWN, + /* Software */ nullptr, + /* Flags */ deviceFlags, + /* pFeatureLevels */ featureLevels.data(), + /* FeatureLevels */ gsl::narrow_cast(featureLevels.size()), + /* SDKVersion */ D3D11_SDK_VERSION, + /* ppDevice */ device0.put(), + /* pFeatureLevel */ &featureLevel, /* ppImmediateContext */ deviceContext0.put()); - #ifndef NDEBUG if (hr == DXGI_ERROR_SDK_COMPONENT_MISSING) { @@ -222,15 +228,15 @@ void AtlasEngine::_recreateBackend() WI_ClearFlag(deviceFlags, D3D11_CREATE_DEVICE_DEBUG); hr = D3D11CreateDevice( - /* pAdapter */ _p.dxgi.adapter.get(), - /* DriverType */ D3D_DRIVER_TYPE_UNKNOWN, - /* Software */ nullptr, - /* Flags */ deviceFlags, - /* pFeatureLevels */ featureLevels.data(), - /* FeatureLevels */ gsl::narrow_cast(featureLevels.size()), - /* SDKVersion */ D3D11_SDK_VERSION, - /* ppDevice */ device0.put(), - /* pFeatureLevel */ &featureLevel, + /* pAdapter */ _p.dxgi.adapter.get(), + /* DriverType */ D3D_DRIVER_TYPE_UNKNOWN, + /* Software */ nullptr, + /* Flags */ deviceFlags, + /* pFeatureLevels */ featureLevels.data(), + /* FeatureLevels */ gsl::narrow_cast(featureLevels.size()), + /* SDKVersion */ D3D11_SDK_VERSION, + /* ppDevice */ device0.put(), + /* pFeatureLevel */ &featureLevel, /* ppImmediateContext */ deviceContext0.put()); } #endif @@ -252,37 +258,44 @@ void AtlasEngine::_recreateBackend() } #endif - if (featureLevel < D3D_FEATURE_LEVEL_10_0) - { - d2dMode = true; - } - else if (featureLevel < D3D_FEATURE_LEVEL_11_0) + if (graphicsAPI == GraphicsAPI::Automatic) { - D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS options{}; - // I'm assuming if `CheckFeatureSupport` fails, it'll leave `options` untouched which will result in `d2dMode |= true`. - std::ignore = device->CheckFeatureSupport(D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, &options, sizeof(options)); - d2dMode |= !options.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x; + if (featureLevel < D3D_FEATURE_LEVEL_10_0) + { + graphicsAPI = GraphicsAPI::Direct2D; + } + else if (featureLevel < D3D_FEATURE_LEVEL_11_0) + { + D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS options{}; + if (FAILED(device->CheckFeatureSupport(D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, &options, sizeof(options))) || + !options.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x) + { + graphicsAPI = GraphicsAPI::Direct2D; + } + } } _p.device = std::move(device); _p.deviceContext = std::move(deviceContext); - if (d2dMode) + switch (graphicsAPI) { + case GraphicsAPI::Direct2D: _b = std::make_unique(); - } - else - { + _hackIsBackendD2D = true; + break; + default: _b = std::make_unique(_p); + _hackIsBackendD2D = false; + break; } // This ensures that the backends redraw their entire viewports whenever a new swap chain is created, // EVEN IF we got called when no actual settings changed (i.e. rendering failure, etc.). _p.MarkAllAsDirty(); - const auto hackWantsBuiltinGlyphs = _p.s->font->builtinGlyphs && !d2dMode; + const auto hackWantsBuiltinGlyphs = _p.s->font->builtinGlyphs && !_hackIsBackendD2D; _hackTriggerRedrawAll = _hackWantsBuiltinGlyphs != hackWantsBuiltinGlyphs; - _hackIsBackendD2D = d2dMode; _hackWantsBuiltinGlyphs = hackWantsBuiltinGlyphs; } @@ -330,7 +343,7 @@ void AtlasEngine::_createSwapChain() // more "intelligent" composition and display updates to occur like Panel Self Refresh // (PSR) which requires dirty rectangles (Present1 API) to work correctly. // We were asked by DWM folks to use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL for this reason (PSR). - .SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, + .SwapEffect = _p.s->target->disablePresent1 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, // If our background is opaque we can enable "independent" flips by setting DXGI_ALPHA_MODE_IGNORE. // As our swap chain won't have to compose with DWM anymore it reduces the display latency dramatically. .AlphaMode = _p.s->target->useAlpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE, @@ -458,44 +471,39 @@ void AtlasEngine::_present() return; } - if constexpr (!ATLAS_DEBUG_SHOW_DIRTY) + if (!ATLAS_DEBUG_SHOW_DIRTY && !_p.s->target->disablePresent1 && memcmp(&dirtyRect, &fullRect, sizeof(RECT)) != 0) { - if (memcmp(&dirtyRect, &fullRect, sizeof(RECT)) != 0) - { - params.DirtyRectsCount = 1; - params.pDirtyRects = &dirtyRect; + params.DirtyRectsCount = 1; + params.pDirtyRects = &dirtyRect; - if (_p.scrollOffset) - { - const auto offsetInPx = _p.scrollOffset * _p.s->font->cellSize.y; - const auto width = _p.s->targetSize.x; - // We don't use targetSize.y here, because "height" refers to the bottom coordinate of the last text row - // in the buffer. We then add the "offsetInPx" (which is negative when scrolling text upwards) and thus - // end up with a "bottom" value that is the bottom of the last row of text that we haven't invalidated. - const auto height = _p.s->viewportCellCount.y * _p.s->font->cellSize.y; - const auto top = std::max(0, offsetInPx); - const auto bottom = height + std::min(0, offsetInPx); - - scrollRect = { 0, top, width, bottom }; - scrollOffset = { 0, offsetInPx }; - - params.pScrollRect = &scrollRect; - params.pScrollOffset = &scrollOffset; - } + if (_p.scrollOffset) + { + const auto offsetInPx = _p.scrollOffset * _p.s->font->cellSize.y; + const auto width = _p.s->targetSize.x; + // We don't use targetSize.y here, because "height" refers to the bottom coordinate of the last text row + // in the buffer. We then add the "offsetInPx" (which is negative when scrolling text upwards) and thus + // end up with a "bottom" value that is the bottom of the last row of text that we haven't invalidated. + const auto height = _p.s->viewportCellCount.y * _p.s->font->cellSize.y; + const auto top = std::max(0, offsetInPx); + const auto bottom = height + std::min(0, offsetInPx); + + scrollRect = { 0, top, width, bottom }; + scrollOffset = { 0, offsetInPx }; + + params.pScrollRect = &scrollRect; + params.pScrollOffset = &scrollOffset; } } + auto hr = _p.swapChain.swapChain->Present1(1, 0, ¶ms); if constexpr (Feature_AtlasEnginePresentFallback::IsEnabled()) { - if (FAILED_LOG(_p.swapChain.swapChain->Present1(1, 0, ¶ms))) + if (FAILED(hr) && params.DirtyRectsCount != 0) { - THROW_IF_FAILED(_p.swapChain.swapChain->Present(1, 0)); + hr = _p.swapChain.swapChain->Present(1, 0); } } - else - { - THROW_IF_FAILED(_p.swapChain.swapChain->Present1(1, 0, ¶ms)); - } + THROW_IF_FAILED(hr); _p.swapChain.waitForPresentation = true; } diff --git a/src/renderer/atlas/Backend.h b/src/renderer/atlas/Backend.h index cdeab63b12c..65da06acb93 100644 --- a/src/renderer/atlas/Backend.h +++ b/src/renderer/atlas/Backend.h @@ -30,9 +30,6 @@ namespace Microsoft::Console::Render::Atlas // This helps with benchmarking the application as it'll run beyond display refresh rate. #define ATLAS_DEBUG_DISABLE_FRAME_LATENCY_WAITABLE_OBJECT 0 - // Forces the use of Direct2D for text rendering (= BackendD2D). -#define ATLAS_DEBUG_FORCE_D2D_MODE 0 - // Adds an artificial delay before every render pass. In milliseconds. #define ATLAS_DEBUG_RENDER_DELAY 0 diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 967191bdfbe..2e937baf04f 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -311,11 +311,20 @@ namespace Microsoft::Console::Render::Atlas DWRITE_SCRIPT_ANALYSIS analysis; }; + enum class GraphicsAPI + { + Automatic, + Direct2D, + Direct3D11, + }; + struct TargetSettings { HWND hwnd = nullptr; bool useAlpha = false; - bool useSoftwareRendering = false; + bool useWARP = false; + bool disablePresent1 = false; + GraphicsAPI graphicsAPI = GraphicsAPI::Automatic; }; enum class AntialiasingMode : u8 diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 80c1f86cda1..224ff155f35 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -77,6 +77,10 @@ void RenderEngineBase::WaitUntilCanRender() noexcept Sleep(8); } +void RenderEngineBase::UpdateHyperlinkHoveredId(const uint16_t /*hoveredId*/) noexcept +{ +} + // Routine Description: // - Notifies us that we're about to circle the buffer, giving us a chance to // force a repaint before the buffer contents are lost. diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 2d151364463..5dcaf17685a 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -90,33 +90,7 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT GetFontSize(_Out_ til::size* pFontSize) noexcept = 0; [[nodiscard]] virtual HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateTitle(std::wstring_view newTitle) noexcept = 0; - - // The following functions used to be specific to the DxRenderer and they should - // be abstracted away and integrated into the above or simply get removed. - - // DxRenderer - getter - virtual HRESULT Enable() noexcept { return S_OK; } - [[nodiscard]] virtual std::wstring_view GetPixelShaderPath() noexcept { return {}; } - [[nodiscard]] virtual std::wstring_view GetPixelShaderImagePath() noexcept { return {}; } - [[nodiscard]] virtual bool GetRetroTerminalEffect() const noexcept { return false; } - [[nodiscard]] virtual float GetScaling() const noexcept { return 1; } - [[nodiscard]] virtual Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept { return Types::Viewport::Empty(); } - [[nodiscard]] virtual Types::Viewport GetViewportInPixels(const Types::Viewport& viewInCharacters) const noexcept { return Types::Viewport::Empty(); } - // DxRenderer - setter - virtual void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept {} - virtual void SetCallback(std::function pfn) noexcept {} - virtual void EnableTransparentBackground(const bool isTransparent) noexcept {} - virtual void SetForceFullRepaintRendering(bool enable) noexcept {} - [[nodiscard]] virtual HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; } - virtual void SetPixelShaderPath(std::wstring_view value) noexcept {} - virtual void SetPixelShaderImagePath(std::wstring_view value) noexcept {} - virtual void SetRetroTerminalEffect(bool enable) noexcept {} - virtual void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept {} - virtual void SetSoftwareRendering(bool enable) noexcept {} - virtual void SetWarningCallback(std::function pfn) noexcept {} - [[nodiscard]] virtual HRESULT SetWindowSize(const til::size pixels) noexcept { return E_NOTIMPL; } - [[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept { return E_NOTIMPL; } - virtual void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept {} + virtual void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept = 0; }; } #pragma warning(pop) diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index 8c33d59b1e7..6390f5fe7f6 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -46,6 +46,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept override; void WaitUntilCanRender() noexcept override; + void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept override; protected: [[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept = 0; diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index bd1734c5d64..5d244dcc130 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -30,7 +30,7 @@ namespace Microsoft::Console::Render // Only one UiaEngine may present information at a time. // This ensures that an automation client isn't overwhelmed // by events when there are multiple TermControls - [[nodiscard]] HRESULT Enable() noexcept override; + [[nodiscard]] HRESULT Enable() noexcept; [[nodiscard]] HRESULT Disable() noexcept; // IRenderEngine Members diff --git a/src/renderer/wddmcon/WddmConRenderer.hpp b/src/renderer/wddmcon/WddmConRenderer.hpp index 930fbe6dea6..f37146daf5b 100644 --- a/src/renderer/wddmcon/WddmConRenderer.hpp +++ b/src/renderer/wddmcon/WddmConRenderer.hpp @@ -19,7 +19,7 @@ namespace Microsoft::Console::Render // Used to release device resources so that another instance of // conhost can render to the screen (i.e. only one DirectX // application may control the screen at a time.) - [[nodiscard]] HRESULT Enable() noexcept override; + [[nodiscard]] HRESULT Enable() noexcept; [[nodiscard]] HRESULT Disable() noexcept; til::rect GetDisplaySize() noexcept; From de7f931228312996a8b586e5348d5dda46610b84 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 27 Mar 2024 00:10:29 +0100 Subject: [PATCH 182/603] Add support for customizing font fallback (#16821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for specifying more than one font family using a syntax that is similar to CSS' `font-family` property. The implementation is straight-forward and is effectively just a wrapper around `IDWriteFontFallbackBuilder`. Closes #2664 ## PR Checklist * Font fallback * Write "「猫」" * Use "Consolas" and remember the shape of the glyphs * Use "Consolas, MS Gothic" and check that it changed ✅ * Settings UI autocompletion * It completes ✅ * It filters ✅ * It recognizes commas and starts a new name ✅ * All invalid font names are listed in the warning message ✅ --------- Co-authored-by: Dustin L. Howett --- .github/actions/spelling/expect/expect.txt | 1 + src/cascadia/TerminalControl/ControlCore.cpp | 27 +- src/cascadia/TerminalControl/ControlCore.h | 2 +- src/cascadia/TerminalControl/EventArgs.h | 8 +- src/cascadia/TerminalControl/EventArgs.idl | 3 +- .../Resources/en-US/Resources.resw | 8 + src/cascadia/TerminalControl/TermControl.cpp | 55 +-- .../TerminalSettingsEditor/Appearances.cpp | 322 +++++++++++++----- .../TerminalSettingsEditor/Appearances.h | 62 ++-- .../TerminalSettingsEditor/Appearances.idl | 10 +- .../TerminalSettingsEditor/Appearances.xaml | 45 ++- .../Profiles_Appearance.cpp | 25 +- .../Profiles_Appearance.h | 1 - .../Resources/en-US/Resources.resw | 16 +- .../TerminalSettingsEditor/ViewModelHelpers.h | 16 +- src/cascadia/UIHelpers/Converters.cpp | 5 + src/cascadia/UIHelpers/Converters.h | 1 + src/cascadia/UIHelpers/Converters.idl | 1 + src/inc/til/string.h | 115 +++++-- src/renderer/atlas/AtlasEngine.api.cpp | 151 +++++--- src/renderer/atlas/AtlasEngine.cpp | 9 +- src/renderer/atlas/AtlasEngine.h | 7 +- src/renderer/atlas/AtlasEngine.r.cpp | 2 +- src/renderer/atlas/BackendD3D.cpp | 23 +- src/renderer/atlas/BackendD3D.h | 2 +- src/renderer/atlas/common.h | 7 +- src/til/ut_til/string.cpp | 39 +++ tools/Lock-CascadiaFont.ps1 | 76 +++++ 28 files changed, 727 insertions(+), 312 deletions(-) create mode 100644 tools/Lock-CascadiaFont.ps1 diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 32dd37878d7..28b9b790d7b 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1155,6 +1155,7 @@ NOCONTEXTHELP NOCOPYBITS NODUP noexcepts +NOFONT NOINTEGRALHEIGHT NOINTERFACE NOLINKINFO diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index a1070d9b811..98c70398559 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -336,6 +336,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation _renderEngine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>(); _renderer->AddRenderEngine(_renderEngine.get()); + // Hook up the warnings callback as early as possible so that we catch everything. + _renderEngine->SetWarningCallback([this](HRESULT hr, wil::zwstring_view parameter) { + _rendererWarning(hr, parameter); + }); + // Initialize our font with the renderer // We don't have to care about DPI. We'll get a change message immediately if it's not 96 // and react accordingly. @@ -371,12 +376,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _terminal->CreateFromSettings(*_settings, *_renderer); - // IMPORTANT! Set this callback up sooner than later. If we do it - // after Enable, then it'll be possible to paint the frame once - // _before_ the warning handler is set up, and then warnings from - // the first paint will be ignored! - _renderEngine->SetWarningCallback(std::bind(&ControlCore::_rendererWarning, this, std::placeholders::_1)); - // Tell the render engine to notify us when the swap chain changes. // We do this after we initially set the swapchain so as to avoid // unnecessary callbacks (and locking problems) @@ -1024,18 +1023,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation LOG_IF_FAILED(_renderEngine->UpdateFont(_desiredFont, _actualFont, featureMap, axesMap)); } - // If the actual font isn't what was requested... - if (_actualFont.GetFaceName() != _desiredFont.GetFaceName()) - { - // Then warn the user that we picked something because we couldn't find their font. - // Format message with user's choice of font and the font that was chosen instead. - const winrt::hstring message{ fmt::format(std::wstring_view{ RS_(L"NoticeFontNotFound") }, - _desiredFont.GetFaceName(), - _actualFont.GetFaceName()) }; - auto noticeArgs = winrt::make(NoticeLevel::Warning, message); - RaiseNotice.raise(*this, std::move(noticeArgs)); - } - const auto actualNewSize = _actualFont.GetSize(); FontSizeChanged.raise(*this, winrt::make(actualNewSize.width, actualNewSize.height)); } @@ -1724,9 +1711,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::_rendererWarning(const HRESULT hr) + void ControlCore::_rendererWarning(const HRESULT hr, wil::zwstring_view parameter) { - RendererWarning.raise(*this, winrt::make(hr)); + RendererWarning.raise(*this, winrt::make(hr, winrt::hstring{ parameter })); } winrt::fire_and_forget ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle) diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 218d1b842eb..7dbf228cd26 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -390,7 +390,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Windows::System::DispatcherQueueTimer _midiAudioSkipTimer{ nullptr }; #pragma region RendererCallbacks - void _rendererWarning(const HRESULT hr); + void _rendererWarning(const HRESULT hr, wil::zwstring_view parameter); winrt::fire_and_forget _renderEngineSwapChainChanged(const HANDLE handle); void _rendererBackgroundColorChanged(); void _rendererTabColorChanged(); diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 1c135cdf4f6..2ed2c2b828b 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -148,12 +148,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation struct RendererWarningArgs : public RendererWarningArgsT { public: - RendererWarningArgs(const uint64_t hr) : - _Result(hr) + RendererWarningArgs(const HRESULT hr, winrt::hstring parameter) : + _Result{ hr }, + _Parameter{ std::move(parameter) } { } - WINRT_PROPERTY(uint64_t, Result); + WINRT_PROPERTY(HRESULT, Result); + WINRT_PROPERTY(winrt::hstring, Parameter); }; struct TransparencyChangedEventArgs : public TransparencyChangedEventArgsT diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 978d69d8e36..92784fc6414 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -77,7 +77,8 @@ namespace Microsoft.Terminal.Control runtimeclass RendererWarningArgs { - UInt64 Result { get; }; + HRESULT Result { get; }; + String Parameter { get; }; } runtimeclass TransparencyChangedEventArgs diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw index a604fc912e4..02245a01577 100644 --- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw @@ -213,6 +213,14 @@ Please either install the missing font or choose another one. Renderer encountered an unexpected error: {0} {0} is an error code. + + Unable to find the following fonts: {0}. Please either install them or choose different fonts. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + Renderer encountered an unexpected error: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + Read-only mode is enabled. diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index c8b378672c5..303d6d61482 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -991,36 +991,45 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - hr: an HRESULT describing the warning // Return Value: // - - winrt::fire_and_forget TermControl::_RendererWarning(IInspectable /*sender*/, - Control::RendererWarningArgs args) + winrt::fire_and_forget TermControl::_RendererWarning(IInspectable /*sender*/, Control::RendererWarningArgs args) { - const auto hr = static_cast(args.Result()); - auto weakThis{ get_weak() }; co_await wil::resume_foreground(Dispatcher()); - if (auto control{ weakThis.get() }) + const auto control = weakThis.get(); + if (!control) { - winrt::hstring message; - if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || - HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) - { - message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderNotFound") }, - (_focused ? _core.FocusedAppearance() : _core.UnfocusedAppearance()).PixelShaderPath()) }; - } - else if (D2DERR_SHADER_COMPILE_FAILED == hr) - { - message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderCompileFailed") }) }; - } - else - { - message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"UnexpectedRendererError") }, - hr) }; - } + co_return; + } - auto noticeArgs = winrt::make(NoticeLevel::Warning, std::move(message)); - control->RaiseNotice.raise(*control, std::move(noticeArgs)); + const auto hr = args.Result(); + const auto parameter = args.Parameter(); + winrt::hstring message; + + switch (hr) + { + case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND): + message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderNotFound") }, parameter) }; + break; + case D2DERR_SHADER_COMPILE_FAILED: + message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderCompileFailed") }) }; + break; + case DWRITE_E_NOFONT: + message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"RendererErrorFontNotFound") }, parameter) }; + break; + default: + { + wchar_t buf[512]; + const auto len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &buf[0], ARRAYSIZE(buf), nullptr); + const std::wstring_view msg{ &buf[0], len }; + message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"RendererErrorOther") }, hr, msg) }; + break; } + } + + auto noticeArgs = winrt::make(NoticeLevel::Warning, std::move(message)); + control->RaiseNotice.raise(*control, std::move(noticeArgs)); } void TermControl::_AttachDxgiSwapChainToXaml(HANDLE swapChainHandle) diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index 85099de045d..fe9cabc824f 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -6,8 +6,8 @@ #include "Appearances.g.cpp" #include "AxisKeyValuePair.g.cpp" #include "FeatureKeyValuePair.g.cpp" -#include "EnumEntry.h" +#include "EnumEntry.h" #include #include "..\WinRTUtils\inc\Utils.h" @@ -36,28 +36,6 @@ static constexpr std::array DefaultFeatures{ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { - bool Font::HasPowerlineCharacters() - { - if (!_hasPowerlineCharacters.has_value()) - { - try - { - winrt::com_ptr font; - THROW_IF_FAILED(_family->GetFont(0, font.put())); - BOOL exists{}; - // We're actually checking for the "Extended" PowerLine glyph set. - // They're more fun. - THROW_IF_FAILED(font->HasCharacter(0xE0B6, &exists)); - _hasPowerlineCharacters = (exists == TRUE); - } - catch (...) - { - _hasPowerlineCharacters = false; - } - } - return _hasPowerlineCharacters.value_or(false); - } - Windows::Foundation::Collections::IMap Font::FontAxesTagsAndNames() { if (!_fontAxesTagsAndNames) @@ -370,6 +348,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } }); + _refreshFontFaceDependents(); InitializeFontAxesVector(); InitializeFontFeaturesVector(); @@ -382,7 +361,119 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } - double AppearanceViewModel::LineHeight() const noexcept + winrt::hstring AppearanceViewModel::FontFace() const + { + return _appearance.SourceProfile().FontInfo().FontFace(); + } + + void AppearanceViewModel::FontFace(const winrt::hstring& value) + { + const auto fontInfo = _appearance.SourceProfile().FontInfo(); + if (fontInfo.FontFace() == value) + { + return; + } + + fontInfo.FontFace(value); + _refreshFontFaceDependents(); + + _NotifyChanges(L"HasFontFace", L"FontFace"); + } + + bool AppearanceViewModel::HasFontFace() const + { + return _appearance.SourceProfile().FontInfo().HasFontFace(); + } + + void AppearanceViewModel::ClearFontFace() + { + const auto fontInfo = _appearance.SourceProfile().FontInfo(); + const auto hadValue = fontInfo.HasFontFace(); + + fontInfo.ClearFontFace(); + _refreshFontFaceDependents(); + + if (hadValue) + { + _NotifyChanges(L"HasFontFace", L"FontFace"); + } + } + + Model::FontConfig AppearanceViewModel::FontFaceOverrideSource() const + { + return _appearance.SourceProfile().FontInfo().FontFaceOverrideSource(); + } + + void AppearanceViewModel::_refreshFontFaceDependents() + { + wil::com_ptr factory; + THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<::IUnknown**>(factory.addressof()))); + + wil::com_ptr fontCollection; + THROW_IF_FAILED(factory->GetSystemFontCollection(fontCollection.addressof(), FALSE)); + + const auto fontFace = FontFace(); + std::wstring primaryFontName; + std::wstring missingFonts; + std::wstring proportionalFonts; + BOOL hasPowerlineCharacters = FALSE; + + til::iterate_font_families(fontFace, [&](wil::zwstring_view name) { + std::wstring* accumulator = nullptr; + + UINT32 index = 0; + BOOL exists = FALSE; + THROW_IF_FAILED(fontCollection->FindFamilyName(name.c_str(), &index, &exists)); + + // Look ma, no goto! + do + { + if (!exists) + { + accumulator = &missingFonts; + break; + } + + if (primaryFontName.empty()) + { + primaryFontName = name; + } + + wil::com_ptr fontFamily; + THROW_IF_FAILED(fontCollection->GetFontFamily(index, fontFamily.addressof())); + + wil::com_ptr font; + THROW_IF_FAILED(fontFamily->GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, font.addressof())); + + if (!font.query()->IsMonospacedFont()) + { + accumulator = &proportionalFonts; + } + + // We're actually checking for the "Extended" PowerLine glyph set. + // They're more fun. + BOOL hasE0B6 = FALSE; + std::ignore = font->HasCharacter(0xE0B6, &hasE0B6); + hasPowerlineCharacters |= hasE0B6; + } while (false); + + if (accumulator) + { + if (!accumulator->empty()) + { + accumulator->append(L", "); + } + accumulator->append(name); + } + }); + + _primaryFontName = std::move(primaryFontName); + MissingFontFaces(winrt::hstring{ missingFonts }); + ProportionalFontFaces(winrt::hstring{ proportionalFonts }); + HasPowerlineCharacters(hasPowerlineCharacters); + } + + double AppearanceViewModel::LineHeight() const { const auto fontInfo = _appearance.SourceProfile().FontInfo(); const auto cellHeight = fontInfo.CellHeight(); @@ -526,7 +617,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // find one axis that does not already exist, and add that // if there are no more possible axes to add, the button is disabled so there shouldn't be a way to get here - const auto possibleAxesTagsAndNames = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames(); + const auto possibleAxesTagsAndNames = ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontAxesTagsAndNames(); for (const auto tagAndName : possibleAxesTagsAndNames) { if (!fontAxesMap.HasKey(tagAndName.Key())) @@ -567,7 +658,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _FontAxesVector.Clear(); if (const auto fontAxesMap = _appearance.SourceProfile().FontInfo().FontAxes()) { - const auto fontAxesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames(); + const auto fontAxesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontAxesTagsAndNames(); for (const auto axis : fontAxesMap) { // only show the axes that the font supports @@ -585,14 +676,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // - Determines whether the currently selected font has any variable font axes bool AppearanceViewModel::AreFontAxesAvailable() { - return ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames().Size() > 0; + return ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontAxesTagsAndNames().Size() > 0; } // Method Description: // - Determines whether the currently selected font has any variable font axes that have not already been set bool AppearanceViewModel::CanFontAxesBeAdded() { - if (const auto fontAxesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontAxesTagsAndNames(); fontAxesTagToNameMap.Size() > 0) + if (const auto fontAxesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontAxesTagsAndNames(); fontAxesTagToNameMap.Size() > 0) { if (const auto fontAxesMap = _appearance.SourceProfile().FontInfo().FontAxes()) { @@ -641,7 +732,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // find one feature that does not already exist, and add that // if there are no more possible features to add, the button is disabled so there shouldn't be a way to get here - const auto possibleFeaturesTagsAndNames = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); + const auto possibleFeaturesTagsAndNames = ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontFeaturesTagsAndNames(); for (const auto tagAndName : possibleFeaturesTagsAndNames) { const auto featureKey = tagAndName.Key(); @@ -684,7 +775,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _FontFeaturesVector.Clear(); if (const auto fontFeaturesMap = _appearance.SourceProfile().FontInfo().FontFeatures()) { - const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); + const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontFeaturesTagsAndNames(); for (const auto feature : fontFeaturesMap) { const auto featureKey = feature.Key(); @@ -703,14 +794,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // - Determines whether the currently selected font has any font features bool AppearanceViewModel::AreFontFeaturesAvailable() { - return ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames().Size() > 0; + return ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontFeaturesTagsAndNames().Size() > 0; } // Method Description: // - Determines whether the currently selected font has any font features that have not already been set bool AppearanceViewModel::CanFontFeaturesBeAdded() { - if (const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(FontFace()).FontFeaturesTagsAndNames(); fontFeaturesTagToNameMap.Size() > 0) + if (const auto fontFeaturesTagToNameMap = ProfileViewModel::FindFontWithLocalizedName(_primaryFontName).FontFeaturesTagsAndNames(); fontFeaturesTagToNameMap.Size() > 0) { if (const auto fontFeaturesMap = _appearance.SourceProfile().FontInfo().FontFeatures()) { @@ -775,9 +866,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation DependencyProperty Appearances::_AppearanceProperty{ nullptr }; - Appearances::Appearances() : - _ShowAllFonts{ false }, - _ShowProportionalFontWarning{ false } + Appearances::Appearances() { InitializeComponent(); @@ -848,61 +937,58 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation INITIALIZE_BINDABLE_ENUM_SETTING(IntenseTextStyle, IntenseTextStyle, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle, L"Appearance_IntenseTextStyle", L"Content"); } - // Method Description: - // - Searches through our list of monospace fonts to determine if the settings model's current font face is a monospace font - bool Appearances::UsingMonospaceFont() const noexcept + IObservableVector Appearances::FilteredFontList() { - auto result{ false }; - const auto currentFont{ Appearance().FontFace() }; - for (const auto& font : ProfileViewModel::MonospaceFontList()) + if (!_filteredFonts) { - if (font.LocalizedName() == currentFont) - { - result = true; - } + _updateFilteredFontList(); } - return result; + return _filteredFonts; } // Method Description: // - Determines whether we should show the list of all the fonts, or we should just show monospace fonts bool Appearances::ShowAllFonts() const noexcept { - // - _ShowAllFonts is directly bound to the checkbox. So this is the user set value. - // - If we are not using a monospace font, show all of the fonts so that the ComboBox is still properly bound - return _ShowAllFonts || !UsingMonospaceFont(); + return _ShowAllFonts; } - void Appearances::ShowAllFonts(const bool& value) + void Appearances::ShowAllFonts(const bool value) { if (_ShowAllFonts != value) { _ShowAllFonts = value; + _filteredFonts = nullptr; PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FilteredFontList" }); } } - IInspectable Appearances::CurrentFontFace() const + void Appearances::FontFaceBox_GotFocus(const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) { - const auto& appearanceVM{ Appearance() }; - const auto appearanceFontFace{ appearanceVM.FontFace() }; - return box_value(ProfileViewModel::FindFontWithLocalizedName(appearanceFontFace)); + _updateFontNameFilter({}); + sender.as().IsSuggestionListOpen(true); } - void Appearances::FontFace_SelectionChanged(const IInspectable& /*sender*/, const SelectionChangedEventArgs& e) + void Appearances::FontFaceBox_LostFocus(const IInspectable& sender, const RoutedEventArgs&) { - // NOTE: We need to hook up a selection changed event handler here instead of directly binding to the appearance view model. - // A two way binding to the view model causes an infinite loop because both combo boxes keep fighting over which one's right. - const auto selectedItem{ e.AddedItems().GetAt(0) }; - const auto newFontFace{ unbox_value(selectedItem) }; - Appearance().FontFace(newFontFace.LocalizedName()); - if (!UsingMonospaceFont()) + const auto appearance = Appearance(); + const auto fontSpec = sender.as().Text(); + + if (fontSpec.empty()) { - ShowProportionalFontWarning(true); + appearance.ClearFontFace(); } else { - ShowProportionalFontWarning(false); + appearance.FontFace(fontSpec); + } + + // TODO: Any use of FindFontWithLocalizedName is broken and requires refactoring in time for version 1.21. + const auto newFontFace = ProfileViewModel::FindFontWithLocalizedName(fontSpec); + if (!newFontFace) + { + return; } _FontAxesNames.Clear(); @@ -948,6 +1034,89 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } + void Appearances::FontFaceBox_SuggestionChosen(const AutoSuggestBox& sender, const AutoSuggestBoxSuggestionChosenEventArgs& args) + { + const auto font = unbox_value(args.SelectedItem()); + const auto fontName = font.Name(); + auto fontSpec = sender.Text(); + + const std::wstring_view fontSpecView{ fontSpec }; + if (const auto idx = fontSpecView.rfind(L','); idx != std::wstring_view::npos) + { + const auto prefix = fontSpecView.substr(0, idx); + const auto suffix = std::wstring_view{ fontName }; + fontSpec = winrt::hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), prefix, suffix) }; + } + else + { + fontSpec = fontName; + } + + sender.Text(fontSpec); + } + + void Appearances::FontFaceBox_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& args) + { + if (args.Reason() != AutoSuggestionBoxTextChangeReason::UserInput) + { + return; + } + + const auto fontSpec = sender.Text(); + std::wstring_view filter{ fontSpec }; + + // Find the last font name in the font, spec, list. + if (const auto idx = filter.rfind(L','); idx != std::wstring_view::npos) + { + filter = filter.substr(idx + 1); + } + + filter = til::trim(filter, L' '); + _updateFontNameFilter(filter); + } + + void Appearances::_updateFontNameFilter(std::wstring_view filter) + { + if (_fontNameFilter != filter) + { + _filteredFonts = nullptr; + _fontNameFilter = filter; + PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FilteredFontList" }); + } + } + + void Appearances::_updateFilteredFontList() + { + _filteredFonts = _ShowAllFonts ? ProfileViewModel::CompleteFontList() : ProfileViewModel::MonospaceFontList(); + + if (_fontNameFilter.empty()) + { + return; + } + + std::vector filtered; + filtered.reserve(_filteredFonts.Size()); + + for (const auto& font : _filteredFonts) + { + const auto name = font.Name(); + bool match = til::contains_linguistic_insensitive(name, _fontNameFilter); + + if (!match) + { + const auto localizedName = font.LocalizedName(); + match = localizedName != name && til::contains_linguistic_insensitive(localizedName, _fontNameFilter); + } + + if (match) + { + filtered.emplace_back(font); + } + } + + _filteredFonts = winrt::single_threaded_observable_vector(std::move(filtered)); + } + void Appearances::_ViewModelChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*args*/) { const auto& obj{ d.as() }; @@ -956,21 +1125,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void Appearances::_UpdateWithNewViewModel() { - if (Appearance()) + if (const auto appearance = Appearance()) { - const auto& biAlignmentVal{ static_cast(Appearance().BackgroundImageAlignment()) }; + const auto& biAlignmentVal{ static_cast(appearance.BackgroundImageAlignment()) }; for (const auto& biButton : _BIAlignmentButtons) { biButton.IsChecked(biButton.Tag().as() == biAlignmentVal); } FontAxesCVS().Source(Appearance().FontAxesVector()); - Appearance().AreFontAxesAvailable() ? FontAxesContainer().HelpText(RS_(L"Profile_FontAxesAvailable/Text")) : FontAxesContainer().HelpText(RS_(L"Profile_FontAxesUnavailable/Text")); + FontAxesContainer().HelpText(appearance.AreFontAxesAvailable() ? RS_(L"Profile_FontAxesAvailable/Text") : RS_(L"Profile_FontAxesUnavailable/Text")); FontFeaturesCVS().Source(Appearance().FontFeaturesVector()); - Appearance().AreFontFeaturesAvailable() ? FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesAvailable/Text")) : FontFeaturesContainer().HelpText(RS_(L"Profile_FontFeaturesUnavailable/Text")); + FontFeaturesContainer().HelpText(appearance.AreFontFeaturesAvailable() ? RS_(L"Profile_FontFeaturesAvailable/Text") : RS_(L"Profile_FontFeaturesUnavailable/Text")); - _ViewModelChangedRevoker = Appearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) { + _ViewModelChangedRevoker = appearance.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) { const auto settingName{ args.PropertyName() }; if (settingName == L"CursorShape") { @@ -987,24 +1156,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } else if (settingName == L"BackgroundImageAlignment") { - _UpdateBIAlignmentControl(static_cast(Appearance().BackgroundImageAlignment())); + _UpdateBIAlignmentControl(static_cast(appearance.BackgroundImageAlignment())); } else if (settingName == L"FontWeight") { PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontWeight" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); } - else if (settingName == L"FontFace" || settingName == L"CurrentFontList") - { - // notify listener that all font face related values might have changed - if (!UsingMonospaceFont()) - { - _ShowAllFonts = true; - } - PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontFace" }); - PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); - PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" }); - } else if (settingName == L"IntenseTextStyle") { PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" }); @@ -1040,12 +1198,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsVintageCursor" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentColorScheme" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentBackgroundImageStretchMode" }); - _UpdateBIAlignmentControl(static_cast(Appearance().BackgroundImageAlignment())); + _UpdateBIAlignmentControl(static_cast(appearance.BackgroundImageAlignment())); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontWeight" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"IsCustomFontWeight" }); - PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentFontFace" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowAllFonts" }); - PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"CurrentAdjustIndistinguishableColors" }); PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"ShowProportionalFontWarning" }); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index 72270cd78d7..d01819b24b5 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -31,15 +31,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation struct Font : FontT { public: - Font(winrt::hstring name, winrt::hstring localizedName, IDWriteFontFamily* family) : + Font(winrt::hstring name, winrt::hstring localizedName, wil::com_ptr family) : _Name{ std::move(name) }, - _LocalizedName{ std::move(localizedName) } + _LocalizedName{ std::move(localizedName) }, + _family{ std::move(family) } { - _family.copy_from(family); } hstring ToString() { return _LocalizedName; } - bool HasPowerlineCharacters(); Windows::Foundation::Collections::IMap FontAxesTagsAndNames(); Windows::Foundation::Collections::IMap FontFeaturesTagsAndNames(); @@ -47,14 +46,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation WINRT_PROPERTY(hstring, LocalizedName); private: - winrt::com_ptr _family; - std::optional _hasPowerlineCharacters; - winrt::hstring _tagToString(DWRITE_FONT_AXIS_TAG tag); winrt::hstring _tagToString(DWRITE_FONT_FEATURE_TAG tag); Windows::Foundation::Collections::IMap _fontAxesTagsAndNames; Windows::Foundation::Collections::IMap _fontFeaturesTagsAndNames; + wil::com_ptr _family; }; struct AxisKeyValuePair : AxisKeyValuePairT, ViewModelHelper @@ -108,11 +105,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation public: AppearanceViewModel(const Model::AppearanceConfig& appearance); - double LineHeight() const noexcept; + winrt::hstring FontFace() const; + void FontFace(const winrt::hstring& value); + bool HasFontFace() const; + void ClearFontFace(); + Model::FontConfig FontFaceOverrideSource() const; + + double LineHeight() const; void LineHeight(const double value); bool HasLineHeight() const; void ClearLineHeight(); Model::FontConfig LineHeightOverrideSource() const; + void SetFontWeightFromDouble(double fontWeight); void SetBackgroundImageOpacityFromPercentageValue(double percentageValue); void SetBackgroundImagePath(winrt::hstring path); @@ -139,13 +143,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool CanFontFeaturesBeAdded(); WINRT_PROPERTY(bool, IsDefault, false); + WINRT_PROPERTY(bool, HasPowerlineCharacters, false); // These settings are not defined in AppearanceConfig, so we grab them // from the source profile itself. The reason we still want them in the // AppearanceViewModel is so we can continue to have the 'Text' grouping // we currently have in xaml, since that grouping has some settings that // are defined in AppearanceConfig and some that are not. - OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontFace); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontSize); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontWeight); OBSERVABLE_PROJECTED_SETTING(_appearance.SourceProfile().FontInfo(), FontAxes); @@ -165,37 +169,43 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance, IntenseTextStyle); OBSERVABLE_PROJECTED_SETTING(_appearance, AdjustIndistinguishableColors); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, SchemesList, _propertyChangedHandlers, nullptr); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, MissingFontFaces, _propertyChangedHandlers); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ProportionalFontFaces, _propertyChangedHandlers); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontAxesVector, _propertyChangedHandlers, nullptr); WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontFeaturesVector, _propertyChangedHandlers, nullptr); private: - Model::AppearanceConfig _appearance; - winrt::hstring _lastBgImagePath; - + void _refreshFontFaceDependents(); Editor::AxisKeyValuePair _CreateAxisKeyValuePairHelper(winrt::hstring axisKey, float axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); Editor::FeatureKeyValuePair _CreateFeatureKeyValuePairHelper(winrt::hstring axisKey, uint32_t axisValue, const Windows::Foundation::Collections::IMap& baseMap, const Windows::Foundation::Collections::IMap& tagToNameMap); - bool _IsDefaultFeature(winrt::hstring featureTag); + + Model::AppearanceConfig _appearance; + winrt::hstring _lastBgImagePath; + std::wstring _primaryFontName; }; struct Appearances : AppearancesT { public: - Appearances(); + static winrt::hstring FontAxisName(const winrt::hstring& key); + static winrt::hstring FontFeatureName(const winrt::hstring& key); - // font face - Windows::Foundation::IInspectable CurrentFontFace() const; + Appearances(); // CursorShape visibility logic bool IsVintageCursor() const; - bool UsingMonospaceFont() const noexcept; + Windows::Foundation::Collections::IObservableVector FilteredFontList(); bool ShowAllFonts() const noexcept; - void ShowAllFonts(const bool& value); + void ShowAllFonts(bool value); + void FontFaceBox_GotFocus(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + void FontFaceBox_LostFocus(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); + void FontFaceBox_SuggestionChosen(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox&, const winrt::Windows::UI::Xaml::Controls::AutoSuggestBoxSuggestionChosenEventArgs&); + void FontFaceBox_TextChanged(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox&, const winrt::Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs&); fire_and_forget BackgroundImage_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void BIAlignment_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); - void FontFace_SelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e); void DeleteAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void AddNewAxisKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void DeleteFeatureKeyValuePair_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); @@ -219,21 +229,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation GETSET_BINDABLE_ENUM_SETTING(BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch, Appearance().BackgroundImageStretchMode); GETSET_BINDABLE_ENUM_SETTING(IntenseTextStyle, Microsoft::Terminal::Settings::Model::IntenseStyle, Appearance().IntenseTextStyle); - WINRT_OBSERVABLE_PROPERTY(bool, ShowProportionalFontWarning, PropertyChanged.raise, nullptr); private: - bool _ShowAllFonts; - void _UpdateBIAlignmentControl(const int32_t val); + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; std::array _BIAlignmentButtons; - Windows::Foundation::Collections::IMap _FontWeightMap; Editor::EnumEntry _CustomFontWeight{ nullptr }; - + Windows::Foundation::Collections::IObservableVector _filteredFonts; Windows::Foundation::Collections::IObservableVector _FontAxesNames; Windows::Foundation::Collections::IObservableVector _FontFeaturesNames; + std::wstring _fontNameFilter; + bool _ShowAllFonts = false; - Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; static void _ViewModelChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e); + + void _updateFontNameFilter(std::wstring_view filter); + void _updateFilteredFontList(); + void _UpdateBIAlignmentControl(const int32_t val); void _UpdateWithNewViewModel(); }; }; diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.idl b/src/cascadia/TerminalSettingsEditor/Appearances.idl index 45f07acaa44..88fb2c6f898 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.idl +++ b/src/cascadia/TerminalSettingsEditor/Appearances.idl @@ -21,7 +21,6 @@ namespace Microsoft.Terminal.Settings.Editor { String Name { get; }; String LocalizedName { get; }; - Boolean HasPowerlineCharacters { get; }; Windows.Foundation.Collections.IMap FontAxesTagsAndNames { get; }; Windows.Foundation.Collections.IMap FontFeaturesTagsAndNames { get; }; } @@ -59,6 +58,10 @@ namespace Microsoft.Terminal.Settings.Editor ColorSchemeViewModel CurrentColorScheme; Windows.Foundation.Collections.IObservableVector SchemesList; + String MissingFontFaces { get; }; + String ProportionalFontFaces { get; }; + Boolean HasPowerlineCharacters { get; }; + void AddNewAxisKeyValuePair(); void DeleteAxisKeyValuePair(String key); void InitializeFontAxesVector(); @@ -103,9 +106,8 @@ namespace Microsoft.Terminal.Settings.Editor IHostedInWindow WindowRoot; static Windows.UI.Xaml.DependencyProperty AppearanceProperty { get; }; - Boolean UsingMonospaceFont { get; }; + Windows.Foundation.Collections.IObservableVector FilteredFontList { get; }; Boolean ShowAllFonts; - Boolean ShowProportionalFontWarning; IInspectable CurrentCursorShape; Boolean IsVintageCursor { get; }; @@ -121,8 +123,6 @@ namespace Microsoft.Terminal.Settings.Editor Boolean IsCustomFontWeight { get; }; Windows.Foundation.Collections.IObservableVector FontWeightList { get; }; - IInspectable CurrentFontFace { get; }; - IInspectable CurrentIntenseTextStyle; Windows.Foundation.Collections.IObservableVector IntenseTextStyleList { get; }; } diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index 35236fc509b..7d065dc4c92 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -183,7 +183,6 @@ - - - - + + IsChecked="{x:Bind ShowAllFonts, Mode=TwoWay}" /> - - + + DisplayPowerlineGlyphs(_looksLikePowerlineFont()); + _previewConnection->DisplayPowerlineGlyphs(_Profile.DefaultAppearance().HasPowerlineCharacters()); _previewControl = Control::TermControl(settings, settings, *_previewConnection); _previewControl.IsEnabled(false); _previewControl.AllowFocusWhenDisabled(false); @@ -70,25 +64,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _Profile.DeleteUnfocusedAppearance(); } - bool Profiles_Appearance::_looksLikePowerlineFont() const - { - if (_Profile && _Profile.DefaultAppearance()) - { - if (const auto fontName = _Profile.DefaultAppearance().FontFace(); !fontName.empty()) - { - if (const auto font = ProfileViewModel::FindFontWithLocalizedName(fontName)) - { - return font.HasPowerlineCharacters(); - } - } - } - return false; - } - void Profiles_Appearance::_onProfilePropertyChanged(const IInspectable&, const PropertyChangedEventArgs&) const { const auto settings = _Profile.TermSettings(); - _previewConnection->DisplayPowerlineGlyphs(_looksLikePowerlineFont()); + _previewConnection->DisplayPowerlineGlyphs(_Profile.DefaultAppearance().HasPowerlineCharacters()); _previewControl.UpdateControlSettings(settings, settings); } } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h index ad4a779e0cc..ab9ebf66fb0 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h @@ -27,7 +27,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation private: void _onProfilePropertyChanged(const IInspectable&, const PropertyChangedEventArgs&) const; - bool _looksLikePowerlineFont() const; winrt::com_ptr _previewConnection{ nullptr }; Microsoft::Terminal::Control::TermControl _previewControl{ nullptr }; diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index e8995079ead..d046ac12df7 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -137,11 +137,9 @@ This color scheme is part of the built-in settings or an installed extension - To make changes to this color scheme, you must make a copy of it. - Make a copy @@ -1810,12 +1808,12 @@ Learn more. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Warning: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Missing fonts: + This is a label that is followed by a list of missing fonts. - - Choosing a non-monospaced font will likely result in visual artifacts. Use at your own discretion. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Non-monospace fonts: + This is a label that is followed by a list of proportional fonts. - + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/ViewModelHelpers.h b/src/cascadia/TerminalSettingsEditor/ViewModelHelpers.h index e306233ab63..61597935390 100644 --- a/src/cascadia/TerminalSettingsEditor/ViewModelHelpers.h +++ b/src/cascadia/TerminalSettingsEditor/ViewModelHelpers.h @@ -39,20 +39,21 @@ struct ViewModelHelper #define _BASE_OBSERVABLE_PROJECTED_SETTING(target, name) \ public: \ - auto name() const noexcept \ + auto name() const \ { \ return target.name(); \ }; \ template \ void name(const T& value) \ { \ - if (name() != value) \ + const auto t = target; \ + if (t.name() != value) \ { \ - target.name(value); \ + t.name(value); \ _NotifyChanges(L"Has" #name, L## #name); \ } \ } \ - bool Has##name() \ + bool Has##name() const \ { \ return target.Has##name(); \ } @@ -63,14 +64,15 @@ public: \ _BASE_OBSERVABLE_PROJECTED_SETTING(target, name) \ void Clear##name() \ { \ - const auto hadValue{ target.Has##name() }; \ - target.Clear##name(); \ + const auto t = target; \ + const auto hadValue{ t.Has##name() }; \ + t.Clear##name(); \ if (hadValue) \ { \ _NotifyChanges(L"Has" #name, L## #name); \ } \ } \ - auto name##OverrideSource() \ + auto name##OverrideSource() const \ { \ return target.name##OverrideSource(); \ } diff --git a/src/cascadia/UIHelpers/Converters.cpp b/src/cascadia/UIHelpers/Converters.cpp index 997f0e5ec7d..ce5123995d4 100644 --- a/src/cascadia/UIHelpers/Converters.cpp +++ b/src/cascadia/UIHelpers/Converters.cpp @@ -40,6 +40,11 @@ namespace winrt::Microsoft::Terminal::UI::implementation return expected != actual; } + bool Converters::StringNotEmpty(const winrt::hstring& value) + { + return !value.empty(); + } + winrt::Windows::UI::Xaml::Visibility Converters::StringNotEmptyToVisibility(const winrt::hstring& value) { return value.empty() ? winrt::Windows::UI::Xaml::Visibility::Collapsed : winrt::Windows::UI::Xaml::Visibility::Visible; diff --git a/src/cascadia/UIHelpers/Converters.h b/src/cascadia/UIHelpers/Converters.h index 6fda669605b..af8bcecb458 100644 --- a/src/cascadia/UIHelpers/Converters.h +++ b/src/cascadia/UIHelpers/Converters.h @@ -20,6 +20,7 @@ namespace winrt::Microsoft::Terminal::UI::implementation // Strings static bool StringsAreNotEqual(const winrt::hstring& expected, const winrt::hstring& actual); + static bool StringNotEmpty(const winrt::hstring& value); static winrt::Windows::UI::Xaml::Visibility StringNotEmptyToVisibility(const winrt::hstring& value); static winrt::hstring StringOrEmptyIfPlaceholder(const winrt::hstring& placeholder, const winrt::hstring& value); diff --git a/src/cascadia/UIHelpers/Converters.idl b/src/cascadia/UIHelpers/Converters.idl index 2c9ef90e39d..baf8a97af65 100644 --- a/src/cascadia/UIHelpers/Converters.idl +++ b/src/cascadia/UIHelpers/Converters.idl @@ -18,6 +18,7 @@ namespace Microsoft.Terminal.UI // Strings static Boolean StringsAreNotEqual(String expected, String actual); + static Boolean StringNotEmpty(String value); static Windows.UI.Xaml.Visibility StringNotEmptyToVisibility(String value); static String StringOrEmptyIfPlaceholder(String placeholder, String value); diff --git a/src/inc/til/string.h b/src/inc/til/string.h index e76639000cf..fcb6905e282 100644 --- a/src/inc/til/string.h +++ b/src/inc/til/string.h @@ -202,6 +202,26 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" return to_ulong<>(str, base); } + // Implement to_int in terms of to_ulong by negating its result. to_ulong does not expect + // to be passed signed numbers and will return an error accordingly. That error when + // compared against -1 evaluates to true. We account for that by returning to_int_error if to_ulong + // returns an error. + constexpr int to_int(const std::wstring_view& str, unsigned long base = 0) noexcept + { + auto result = to_ulong_error; + const auto signPosition = str.find(L"-"); + const bool hasSign = signPosition != std::wstring_view::npos; + result = hasSign ? to_ulong(str.substr(signPosition + 1), base) : to_ulong(str, base); + + // Check that result is valid and will fit in an int. + if (result == to_ulong_error || (result > INT_MAX)) + { + return to_int_error; + } + + return hasSign ? result * -1 : result; + } + // Just like std::tolower, but without annoying locales. template constexpr T tolower_ascii(T c) @@ -376,6 +396,81 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" return { beg, end }; } + // Splits a font-family list into individual font-families. It loosely follows the CSS spec for font-family. + // It splits by comma, handles quotes and simple escape characters, and it cleans whitespace. + // + // This is not the right place to put this, because it's highly specialized towards font-family names. + // But this code is needed both, in our renderer and in our settings UI. At the time I couldn't find a better place for it. + void iterate_font_families(const std::wstring_view& families, auto&& callback) + { + std::wstring family; + bool escape = false; + bool delayedSpace = false; + wchar_t stringType = 0; + + for (const auto ch : families) + { + if (!escape) + { + switch (ch) + { + case ' ': + if (stringType) + { + // Spaces are treated literally inside strings. + break; + } + delayedSpace = !family.empty(); + continue; + case '"': + case '\'': + if (stringType && stringType != ch) + { + // Single quotes inside double quotes are treated literally and vice versa. + break; + } + stringType = stringType == ch ? 0 : ch; + continue; + case ',': + if (stringType) + { + // Commas are treated literally inside strings. + break; + } + if (!family.empty()) + { + callback(std::move(family)); + family.clear(); + delayedSpace = false; + } + continue; + case '\\': + escape = true; + continue; + default: + break; + } + } + + // The `delayedSpace` logic automatically takes care for us to + // strip leading and trailing spaces and deduplicate them too. + if (delayedSpace) + { + delayedSpace = false; + family.push_back(L' '); + } + + family.push_back(ch); + escape = false; + } + + // Just like the comma handler above. + if (!stringType && !family.empty()) + { + callback(std::move(family)); + } + } + // This function is appropriate for case-insensitive equivalence testing of file paths and other "system" strings. // Similar to memcmp, this returns <0, 0 or >0. inline int compare_ordinal_insensitive(const std::wstring_view& lhs, const std::wstring_view& rhs) noexcept @@ -426,24 +521,4 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" #pragma warning(suppress : 26477) // Use 'nullptr' rather than 0 or NULL (es.47). return FindNLSStringEx(LOCALE_NAME_USER_DEFAULT, LINGUISTIC_IGNORECASE, str.data(), strLen, needle.data(), needleLen, nullptr, nullptr, nullptr, 0) != -1; } - - // Implement to_int in terms of to_ulong by negating its result. to_ulong does not expect - // to be passed signed numbers and will return an error accordingly. That error when - // compared against -1 evaluates to true. We account for that by returning to_int_error if to_ulong - // returns an error. - constexpr int to_int(const std::wstring_view& str, unsigned long base = 0) noexcept - { - auto result = to_ulong_error; - const auto signPosition = str.find(L"-"); - const bool hasSign = signPosition != std::wstring_view::npos; - result = hasSign ? to_ulong(str.substr(signPosition + 1), base) : to_ulong(str, base); - - // Check that result is valid and will fit in an int. - if (result == to_ulong_error || (result > INT_MAX)) - { - return to_int_error; - } - - return hasSign ? result * -1 : result; - } } diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index f739022e8a4..c031f67a154 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -264,7 +264,7 @@ try } #endif - _resolveFontMetrics(nullptr, fontInfoDesired, fontInfo); + _resolveFontMetrics(fontInfoDesired, fontInfo); return S_OK; } CATCH_RETURN() @@ -443,7 +443,7 @@ void AtlasEngine::SetGraphicsAPI(GraphicsAPI graphicsAPI) noexcept } } -void AtlasEngine::SetWarningCallback(std::function pfn) noexcept +void AtlasEngine::SetWarningCallback(std::function pfn) noexcept { _p.warningCallback = std::move(pfn); } @@ -472,31 +472,39 @@ void AtlasEngine::SetWarningCallback(std::function pfn) noexcept [[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept { - try - { - _updateFont(fontInfoDesired.GetFaceName().c_str(), fontInfoDesired, fontInfo, features, axes); - return S_OK; - } - CATCH_LOG(); - + // We're currently faced with a font caching bug that we're unable to reproduce locally. See GH#9375. + // But it occurs often enough and has no proper workarounds, so we're forced to fix it. + // + // Our leading theory is: When an app package has a + // (like Windows Terminal with Cascadia Mono/Code) and it gets updated, the system locks the file somehow. + // DirectWrite still has some information about the font cached though, so it thinks that it still exists, + // but using the font causes it to error out because it can't access it. This fact became apparent in + // commit 9e86c98 (PR #16196), because it showed that it's definitely not due to FindFamilyName() failing. + // + // The workaround is to catch the exception and retry it with our nearby fonts manually loaded in. if constexpr (Feature_NearbyFontLoading::IsEnabled()) { try { - // _resolveFontMetrics() checks `_api.s->font->fontCollection` for a pre-existing font collection, - // before falling back to using the system font collection. This way we can inject our custom one. See GH#9375. - // Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection - // instance across font changes, like when zooming the font size rapidly using the scroll wheel. - _api.s.write()->font.write()->fontCollection = FontCache::GetCached(); - _updateFont(fontInfoDesired.GetFaceName().c_str(), fontInfoDesired, fontInfo, features, axes); + _updateFont(fontInfoDesired, fontInfo, features, axes); return S_OK; } CATCH_LOG(); + + // _resolveFontMetrics() checks `_api.s->font->fontCollection` for a pre-existing font collection, + // before falling back to using the system font collection. This way we can inject our custom one. + // Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection + // instance across font changes, like when zooming the font size rapidly using the scroll wheel. + try + { + _api.s.write()->font.write()->fontCollection = FontCache::GetCached(); + } + CATCH_LOG(); } try { - _updateFont(nullptr, fontInfoDesired, fontInfo, features, axes); + _updateFont(fontInfoDesired, fontInfo, features, axes); return S_OK; } CATCH_RETURN(); @@ -528,7 +536,7 @@ void AtlasEngine::_resolveTransparencySettings() noexcept } } -void AtlasEngine::_updateFont(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) +void AtlasEngine::_updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) { std::vector fontFeatures; if (!features.empty()) @@ -605,22 +613,19 @@ void AtlasEngine::_updateFont(const wchar_t* faceName, const FontInfoDesired& fo } const auto font = _api.s.write()->font.write(); - _resolveFontMetrics(faceName, fontInfoDesired, fontInfo, font); + _resolveFontMetrics(fontInfoDesired, fontInfo, font); font->fontFeatures = std::move(fontFeatures); font->fontAxisValues = std::move(fontAxisValues); } -void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics) const +void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics) const { + const auto& faceName = fontInfoDesired.GetFaceName(); const auto requestedFamily = fontInfoDesired.GetFamily(); auto requestedWeight = fontInfoDesired.GetWeight(); auto fontSize = fontInfoDesired.GetFontSize(); auto requestedSize = fontInfoDesired.GetEngineSize(); - if (!requestedFaceName) - { - requestedFaceName = L"Consolas"; - } if (!requestedSize.height) { fontSize = 12.0f; @@ -641,22 +646,88 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontCollection(fontCollection.addressof(), FALSE)); } - u32 index = 0; - BOOL exists = false; - THROW_IF_FAILED(fontCollection->FindFamilyName(requestedFaceName, &index, &exists)); - THROW_HR_IF(DWRITE_E_NOFONT, !exists); + std::wstring primaryFontName; + std::wstring missingFontNames; + wil::com_ptr primaryFontFamily; + wil::com_ptr fontFallbackBuilder; + + // Resolves a comma-separated font list similar to CSS' font-family property. The first font in the list + // that can be resolved successfully will be the primary font which dictates the cell size among others. + // All remaining fonts are "secondary" fonts used for font fallback. + til::iterate_font_families(faceName, [&](std::wstring&& fontName) { + u32 index = 0; + BOOL exists = false; + THROW_IF_FAILED(fontCollection->FindFamilyName(fontName.c_str(), &index, &exists)); + + if (!exists) + { + if (!missingFontNames.empty()) + { + missingFontNames.append(L", "); + } + missingFontNames.append(fontName); + return; + } - wil::com_ptr fontFamily; - THROW_IF_FAILED(fontCollection->GetFontFamily(index, fontFamily.addressof())); + if (!primaryFontFamily) + { + primaryFontName = std::move(fontName); + THROW_IF_FAILED(fontCollection->GetFontFamily(index, primaryFontFamily.addressof())); + } + else + { + if (!fontFallbackBuilder) + { + THROW_IF_FAILED(_p.dwriteFactory->CreateFontFallbackBuilder(fontFallbackBuilder.addressof())); + } + + static constexpr DWRITE_UNICODE_RANGE fullRange{ 0, 0x10FFFF }; + auto fontNamePtr = fontName.c_str(); + THROW_IF_FAILED(fontFallbackBuilder->AddMapping( + /* ranges */ &fullRange, + /* rangesCount */ 1, + /* targetFamilyNames */ &fontNamePtr, + /* targetFamilyNamesCount */ 1, + /* fontCollection */ fontCollection.get(), + /* localeName */ nullptr, + /* baseFamilyName */ nullptr, + /* scale */ 1.0f)); + } + }); + + if (!missingFontNames.empty() && _p.warningCallback) + { + _p.warningCallback(DWRITE_E_NOFONT, missingFontNames); + } + + // Fall back to Consolas if no font was found or specified. + if (!primaryFontFamily) + { + primaryFontName = L"Consolas"; + + u32 index = 0; + BOOL exists = false; + THROW_IF_FAILED(fontCollection->FindFamilyName(primaryFontName.c_str(), &index, &exists)); + THROW_HR_IF(DWRITE_E_NOFONT, !exists); + + THROW_IF_FAILED(fontCollection->GetFontFamily(index, primaryFontFamily.addressof())); + } + + auto fontFallback = _api.systemFontFallback; + if (fontFallbackBuilder) + { + THROW_IF_FAILED(fontFallbackBuilder->AddMappings(_api.systemFontFallback.get())); + THROW_IF_FAILED(fontFallbackBuilder->CreateFontFallback(fontFallback.put())); + } - wil::com_ptr font; - THROW_IF_FAILED(fontFamily->GetFirstMatchingFont(static_cast(requestedWeight), DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, font.addressof())); + wil::com_ptr primaryFont; + THROW_IF_FAILED(primaryFontFamily->GetFirstMatchingFont(static_cast(requestedWeight), DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, primaryFont.addressof())); - wil::com_ptr fontFace; - THROW_IF_FAILED(font->CreateFontFace(fontFace.addressof())); + wil::com_ptr primaryFontFace; + THROW_IF_FAILED(primaryFont->CreateFontFace(primaryFontFace.addressof())); DWRITE_FONT_METRICS metrics{}; - fontFace->GetMetrics(&metrics); + primaryFontFace->GetMetrics(&metrics); // Point sizes are commonly treated at a 72 DPI scale // (including by OpenType), whereas DirectWrite uses 96 DPI. @@ -682,12 +753,12 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo static constexpr u32 codePoint = '0'; u16 glyphIndex; - THROW_IF_FAILED(fontFace->GetGlyphIndicesW(&codePoint, 1, &glyphIndex)); + THROW_IF_FAILED(primaryFontFace->GetGlyphIndicesW(&codePoint, 1, &glyphIndex)); if (glyphIndex) { DWRITE_GLYPH_METRICS glyphMetrics{}; - THROW_IF_FAILED(fontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics, FALSE)); + THROW_IF_FAILED(primaryFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics, FALSE)); advanceWidth = static_cast(glyphMetrics.advanceWidth) * designUnitsPerPx; } } @@ -746,12 +817,11 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo requestedSize.width = gsl::narrow_cast(lrintf(fontSize / cellHeight * cellWidth)); } - fontInfo.SetFromEngine(requestedFaceName, requestedFamily, requestedWeight, false, coordSize, requestedSize); + fontInfo.SetFromEngine(primaryFontName, requestedFamily, requestedWeight, false, coordSize, requestedSize); } if (fontMetrics) { - std::wstring fontName{ requestedFaceName }; const auto fontWeightU16 = gsl::narrow_cast(requestedWeight); const auto advanceWidthU16 = gsl::narrow_cast(lrintf(advanceWidth)); const auto baselineU16 = gsl::narrow_cast(lrintf(baseline)); @@ -773,8 +843,9 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo // as we might cause _api to be in an inconsistent state otherwise. fontMetrics->fontCollection = std::move(fontCollection); - fontMetrics->fontFamily = std::move(fontFamily); - fontMetrics->fontName = std::move(fontName); + fontMetrics->fontFallback = std::move(fontFallback); + fontMetrics->fontFallback.try_query_to(fontMetrics->fontFallback1.put()); + fontMetrics->fontName = std::move(primaryFontName); fontMetrics->fontSize = fontSizeInPx; fontMetrics->cellSize = { cellWidth, cellHeight }; fontMetrics->fontWeight = fontWeightU16; diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index e05e5b033eb..8cfbe995a14 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -41,8 +41,7 @@ AtlasEngine::AtlasEngine() THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(_p.dwriteFactory), reinterpret_cast<::IUnknown**>(_p.dwriteFactory.addressof()))); _p.dwriteFactory4 = _p.dwriteFactory.try_query(); - THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontFallback(_p.systemFontFallback.addressof())); - _p.systemFontFallback1 = _p.systemFontFallback.try_query(); + THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontFallback(_api.systemFontFallback.addressof())); wil::com_ptr textAnalyzer; THROW_IF_FAILED(_p.dwriteFactory->CreateTextAnalyzer(textAnalyzer.addressof())); @@ -575,7 +574,7 @@ void AtlasEngine::_recreateFontDependentResources() { wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; - if (FAILED(GetUserDefaultLocaleName(&localeName[0], LOCALE_NAME_MAX_LENGTH))) + if (!GetUserDefaultLocaleName(&localeName[0], LOCALE_NAME_MAX_LENGTH)) { memcpy(&localeName[0], L"en-US", 12); } @@ -841,7 +840,7 @@ void AtlasEngine::_mapCharacters(const wchar_t* text, const u32 textLength, u32* if (textFormatAxis) { - THROW_IF_FAILED(_p.systemFontFallback1->MapCharacters( + THROW_IF_FAILED(_p.s->font->fontFallback1->MapCharacters( /* analysisSource */ &analysisSource, /* textPosition */ 0, /* textLength */ textLength, @@ -859,7 +858,7 @@ void AtlasEngine::_mapCharacters(const wchar_t* text, const u32 textLength, u32* const auto baseStyle = WI_IsFlagSet(_api.attributes, FontRelevantAttributes::Italic) ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; wil::com_ptr font; - THROW_IF_FAILED(_p.systemFontFallback->MapCharacters( + THROW_IF_FAILED(_p.s->font->fontFallback->MapCharacters( /* analysisSource */ &analysisSource, /* textPosition */ 0, /* textLength */ textLength, diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index b75a2a5cdce..5c9f954ae2f 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -76,7 +76,7 @@ namespace Microsoft::Console::Render::Atlas void SetSoftwareRendering(bool enable) noexcept; void SetDisablePartialInvalidation(bool enable) noexcept; void SetGraphicsAPI(GraphicsAPI graphicsAPI) noexcept; - void SetWarningCallback(std::function pfn) noexcept; + void SetWarningCallback(std::function pfn) noexcept; [[nodiscard]] HRESULT SetWindowSize(til::size pixels) noexcept; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept; @@ -94,8 +94,8 @@ namespace Microsoft::Console::Render::Atlas // AtlasEngine.api.cpp void _resolveTransparencySettings() noexcept; - void _updateFont(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes); - void _resolveFontMetrics(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics = nullptr) const; + void _updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes); + void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics = nullptr) const; // AtlasEngine.r.cpp ATLAS_ATTR_COLD void _recreateAdapter(); @@ -156,6 +156,7 @@ namespace Microsoft::Console::Render::Atlas Buffer glyphAdvances; Buffer glyphOffsets; + wil::com_ptr systemFontFallback; wil::com_ptr replacementCharacterFontFace; u16 replacementCharacterGlyphIndex = 0; bool replacementCharacterLookedUp = false; diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index ac87c246dc9..f0a448d491b 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -65,7 +65,7 @@ catch (const wil::ResultException& exception) { try { - _p.warningCallback(hr); + _p.warningCallback(hr, {}); } CATCH_LOG() } diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 19e98c6557e..6d7a86c5e4d 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -432,7 +432,7 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p) } if (p.warningCallback) { - p.warningCallback(D2DERR_SHADER_COMPILE_FAILED); + p.warningCallback(D2DERR_SHADER_COMPILE_FAILED, p.s->misc->customPixelShaderPath); } } @@ -448,7 +448,7 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p) _customPixelShader.reset(); if (p.warningCallback) { - p.warningCallback(D2DERR_SHADER_COMPILE_FAILED); + p.warningCallback(D2DERR_SHADER_COMPILE_FAILED, p.s->misc->customPixelShaderImagePath); } } } @@ -1439,7 +1439,7 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa if (BuiltinGlyphs::IsSoftFontChar(glyphIndex)) { - _drawSoftFontGlyph(p, r, glyphIndex); + shadingType = _drawSoftFontGlyph(p, r, glyphIndex); } else { @@ -1465,22 +1465,17 @@ BackendD3D::AtlasGlyphEntry* BackendD3D::_drawBuiltinGlyph(const RenderingPayloa return glyphEntry; } -void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex) +BackendD3D::ShadingType BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex) { - _d2dRenderTarget->PushAxisAlignedClip(&rect, D2D1_ANTIALIAS_MODE_ALIASED); - const auto restoreD2D = wil::scope_exit([&]() { - _d2dRenderTarget->PopAxisAlignedClip(); - }); - const auto width = static_cast(p.s->font->softFontCellSize.width); const auto height = static_cast(p.s->font->softFontCellSize.height); const auto softFontIndex = glyphIndex - 0xEF20u; const auto data = til::clamp_slice_len(p.s->font->softFontPattern, height * softFontIndex, height); + // This happens if someone wrote a U+EF2x character (by accident), but we don't even have soft fonts enabled yet. if (data.empty() || data.size() != height) { - _d2dRenderTarget->Clear(); - return; + return ShadingType::Default; } if (!_softFontBitmap) @@ -1517,8 +1512,14 @@ void BackendD3D::_drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F THROW_IF_FAILED(_softFontBitmap->CopyFromMemory(nullptr, bitmapData.data(), pitch)); } + _d2dRenderTarget->PushAxisAlignedClip(&rect, D2D1_ANTIALIAS_MODE_ALIASED); + const auto restoreD2D = wil::scope_exit([&]() { + _d2dRenderTarget->PopAxisAlignedClip(); + }); + const auto interpolation = p.s->font->antialiasingMode == AntialiasingMode::Aliased ? D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR : D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC; _d2dRenderTarget->DrawBitmap(_softFontBitmap.get(), &rect, 1, interpolation, nullptr, nullptr); + return ShadingType::TextGrayscale; } void BackendD3D::_drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect) diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 5e8055aecd0..25833b831dc 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -215,7 +215,7 @@ namespace Microsoft::Console::Render::Atlas ATLAS_ATTR_COLD void _drawTextOverlapSplit(const RenderingPayload& p, u16 y); [[nodiscard]] ATLAS_ATTR_COLD AtlasGlyphEntry* _drawGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex); AtlasGlyphEntry* _drawBuiltinGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex); - void _drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex); + ShadingType _drawSoftFontGlyph(const RenderingPayload& p, const D2D1_RECT_F& rect, u32 glyphIndex); void _drawGlyphAtlasAllocate(const RenderingPayload& p, stbrp_rect& rect); static AtlasGlyphEntry* _drawGlyphAllocateEntry(const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, u32 glyphIndex); static void _splitDoubleHeightGlyph(const RenderingPayload& p, const ShapedRow& row, AtlasFontFaceEntry& fontFaceEntry, AtlasGlyphEntry* glyphEntry); diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 2e937baf04f..ad9bf2587be 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -345,7 +345,8 @@ namespace Microsoft::Console::Render::Atlas struct FontSettings { wil::com_ptr fontCollection; - wil::com_ptr fontFamily; + wil::com_ptr fontFallback; + wil::com_ptr fontFallback1; // optional, might be nullptr std::wstring fontName; std::vector fontFeatures; std::vector fontAxisValues; @@ -488,10 +489,8 @@ namespace Microsoft::Console::Render::Atlas wil::com_ptr d2dFactory; wil::com_ptr dwriteFactory; wil::com_ptr dwriteFactory4; // optional, might be nullptr - wil::com_ptr systemFontFallback; - wil::com_ptr systemFontFallback1; // optional, might be nullptr wil::com_ptr textAnalyzer; - std::function warningCallback; + std::function warningCallback; std::function swapChainChangedCallback; //// Parameters which are constant for the existence of the backend. diff --git a/src/til/ut_til/string.cpp b/src/til/ut_til/string.cpp index 26fbf92ea47..2ef656a04d7 100644 --- a/src/til/ut_til/string.cpp +++ b/src/til/ut_til/string.cpp @@ -7,6 +7,25 @@ using namespace WEX::Common; using namespace WEX::Logging; using namespace WEX::TestExecution; +template<> +class WEX::TestExecution::VerifyOutputTraits> +{ +public: + static WEX::Common::NoThrowString ToString(const std::vector& vec) + { + WEX::Common::NoThrowString str; + str.Append(L"{ "); + for (size_t i = 0; i < vec.size(); ++i) + { + str.Append(i == 0 ? L"\"" : L", \""); + str.Append(vec[i].c_str()); + str.Append(L"\""); + } + str.Append(L" }"); + return str; + } +}; + class StringTests { TEST_CLASS(StringTests); @@ -199,4 +218,24 @@ class StringTests VERIFY_IS_TRUE(til::is_legal_path(LR"(C:\Users\Documents and Settings\Users\;\Why not)")); VERIFY_IS_FALSE(til::is_legal_path(LR"(C:\Users\Documents and Settings\"Quote-un-quote users")")); } + + TEST_METHOD(IterateFontFamilies) + { + static constexpr auto expected = [](auto&&... args) { + return std::vector{ std::forward(args)... }; + }; + static constexpr auto actual = [](std::wstring_view families) { + std::vector split; + til::iterate_font_families(families, [&](std::wstring&& str) { + split.emplace_back(std::move(str)); + }); + return split; + }; + + VERIFY_ARE_EQUAL(expected(L"foo", L" b a r ", LR"(b"az)"), actual(LR"( foo ," b a r ",b\"az)")); + VERIFY_ARE_EQUAL(expected(LR"(foo, bar)"), actual(LR"("foo, bar")")); + VERIFY_ARE_EQUAL(expected(LR"("foo")", LR"('bar')"), actual(LR"('"foo"', "'bar'")")); + VERIFY_ARE_EQUAL(expected(LR"("foo")", LR"('bar')"), actual(LR"("\"foo\"", '\'bar\'')")); + VERIFY_ARE_EQUAL(expected(L"foo"), actual(LR"(,,,,foo,,,,)")); + } }; diff --git a/tools/Lock-CascadiaFont.ps1 b/tools/Lock-CascadiaFont.ps1 new file mode 100644 index 00000000000..f1a1e384b26 --- /dev/null +++ b/tools/Lock-CascadiaFont.ps1 @@ -0,0 +1,76 @@ +# This script is a failed attempt to lock the Cascadia Mono/Code font files in order to reproduce an issue with the font +# cache service, where it says a font exists, but then fails to use it (see GH#9375). The script doesn't work because +# for some reason DirectWrite is still able to fully use the fonts. It's left here for reference. + +#Requires -RunAsAdministrator + +Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; + +public class Win32LockFile { + public const uint LOCKFILE_FAIL_IMMEDIATELY = 0x00000001; + public const uint LOCKFILE_EXCLUSIVE_LOCK = 0x00000002; + + [DllImport("kernel32.dll")] + public static extern bool LockFileEx(IntPtr hFile, uint dwFlags, uint dwReserved, uint nNumberOfBytesToLockLow, uint nNumberOfBytesToLockHigh, ref OVERLAPPED lpOverlapped); + + [StructLayout(LayoutKind.Sequential)] + public struct OVERLAPPED { + public uint Internal; + public uint InternalHigh; + public uint Offset; + public uint OffsetHigh; + public IntPtr hEvent; + } +} +"@ + +function Lock-File { + param( + [Parameter(Mandatory=$true)] + [string]$Path + ) + + $file = [System.IO.File]::Open($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) + $overlapped = New-Object Win32LockFile+OVERLAPPED + $result = [Win32LockFile]::LockFileEx( + $file.SafeFileHandle.DangerousGetHandle(), # hFile + [Win32LockFile]::LOCKFILE_EXCLUSIVE_LOCK, # dwFlags + 0, # dwReserved + [UInt32]::MaxValue, # nNumberOfBytesToLockLow + [UInt32]::MaxValue, # nNumberOfBytesToLockHigh + [ref]$overlapped # lpOverlapped + ) + + if (-not $result) { + throw "Failed to lock file" + } + + return $file +} + +$fonts = Get-ItemProperty "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts\*" + | ForEach-Object { $_.PSobject.Properties } + | Where-Object { $_.Name.StartsWith("Cascadia") } + | ForEach-Object { $_.Value } + +$fonts += @("CascadiaCode.ttf", "CascadiaCodeItalic.ttf", "CascadiaMono.ttf", "CascadiaMonoItalic.ttf") + | ForEach-Object { "C:\Windows\Fonts\$_" } + | Where-Object { Test-Path $_ } + +try { + $handles = $fonts | ForEach-Object { + try { + Lock-File $_ + } + catch { + Write-Error $_ + } + } + Restart-Service FontCache + Write-Host "Press any key to unlock the font files..." + $null = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") +} finally { + $handles | Where-Object { $_ } | ForEach-Object { $_.Close() } +} From 26e63203741909ee8ac00e4c6ec658884560df17 Mon Sep 17 00:00:00 2001 From: Windows Console Service Bot <14666831+consvc@users.noreply.github.com> Date: Wed, 27 Mar 2024 05:58:36 -0500 Subject: [PATCH 183/603] Localization Updates - main - 03/27/2024 02:34:17 (#16945) --- .../Resources/qps-ploc/Resources.resw | 8 +++ .../Resources/qps-ploca/Resources.resw | 8 +++ .../Resources/qps-plocm/Resources.resw | 8 +++ .../Resources/de-DE/Resources.resw | 26 --------- .../Resources/es-ES/Resources.resw | 26 --------- .../Resources/fr-FR/Resources.resw | 26 --------- .../Resources/it-IT/Resources.resw | 26 --------- .../Resources/ja-JP/Resources.resw | 26 --------- .../Resources/ko-KR/Resources.resw | 26 --------- .../Resources/pt-BR/Resources.resw | 26 --------- .../Resources/qps-ploc/Resources.resw | 55 ++++++++++++------- .../Resources/qps-ploca/Resources.resw | 55 ++++++++++++------- .../Resources/qps-plocm/Resources.resw | 55 ++++++++++++------- .../Resources/ru-RU/Resources.resw | 26 --------- .../Resources/zh-CN/Resources.resw | 26 --------- .../Resources/zh-TW/Resources.resw | 26 --------- .../Resources/qps-ploc/Resources.resw | 3 + .../Resources/qps-ploca/Resources.resw | 3 + .../Resources/qps-plocm/Resources.resw | 3 + 19 files changed, 138 insertions(+), 320 deletions(-) diff --git a/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw index 62835cf4c86..1cd2c9e814e 100644 --- a/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/qps-ploc/Resources.resw @@ -213,6 +213,14 @@ Řєʼnδέгęř зйĉóűňтëř℮δ âη ûⁿėхрєĉţēð ℮ѓґоŗ: {0} !!! !!! !!! !!! ! {0} is an error code. + + Цйǻьľė ŧò ƒіņð τнė ƒσℓľоŵíпğ ƒŏптš: {0}. Рĺёąšė èìťђėг ïŋŝţăĺℓ тħēm ôѓ ςђоōѕз ðįƒƒеřęʼnţ ƒóπţś. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + Гêʼnďěѓēŗ эήçομήťєřĕď åń µŋέхρэсťèð ęґяöя: {0:#010×} {1} !!! !!! !!! !!! !!! ! + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + Гėāδ-σπļỳ mőðê īŝ ėńäвļεδ. !!! !!! ! diff --git a/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw index 62835cf4c86..1cd2c9e814e 100644 --- a/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/qps-ploca/Resources.resw @@ -213,6 +213,14 @@ Řєʼnδέгęř зйĉóűňтëř℮δ âη ûⁿėхрєĉţēð ℮ѓґоŗ: {0} !!! !!! !!! !!! ! {0} is an error code. + + Цйǻьľė ŧò ƒіņð τнė ƒσℓľоŵíпğ ƒŏптš: {0}. Рĺёąšė èìťђėг ïŋŝţăĺℓ тħēm ôѓ ςђоōѕз ðįƒƒеřęʼnţ ƒóπţś. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + Гêʼnďěѓēŗ эήçομήťєřĕď åń µŋέхρэсťèð ęґяöя: {0:#010×} {1} !!! !!! !!! !!! !!! ! + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + Гėāδ-σπļỳ mőðê īŝ ėńäвļεδ. !!! !!! ! diff --git a/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw index 62835cf4c86..1cd2c9e814e 100644 --- a/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/qps-plocm/Resources.resw @@ -213,6 +213,14 @@ Řєʼnδέгęř зйĉóűňтëř℮δ âη ûⁿėхрєĉţēð ℮ѓґоŗ: {0} !!! !!! !!! !!! ! {0} is an error code. + + Цйǻьľė ŧò ƒіņð τнė ƒσℓľоŵíпğ ƒŏптš: {0}. Рĺёąšė èìťђėг ïŋŝţăĺℓ тħēm ôѓ ςђоōѕз ðįƒƒеřęʼnţ ƒóπţś. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + Гêʼnďěѓēŗ эήçομήťєřĕď åń µŋέхρэсťèð ęґяöя: {0:#010×} {1} !!! !!! !!! !!! !!! ! + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + Гėāδ-σπļỳ mőðê īŝ ėńäвļεδ. !!! !!! ! diff --git a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw index 13a0809dfd4..c7e5f00ab76 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw @@ -137,11 +137,9 @@ Dieses Farbschema ist Teil der integrierten Einstellungen oder einer installierten Erweiterung. - Um Änderungen an diesem Farbschema vorzunehmen, müssen Sie eine Kopie davon erstellen. - Kopie erstellen @@ -311,14 +309,6 @@ Die Terminalanwendung, die gestartet wird, wenn eine Befehlszeilenanwendung ohne vorhandene Sitzung gestartet wird, beispielsweise vom Startmenü oder über das Dialogfeld "Ausführen". A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - Gesamten Bildschirm beim Anzeigen von Updates aktualisieren - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - Wenn diese Option deaktiviert ist, rendert das Terminal die Aktualisierungen nur zwischen den Frames auf den Bildschirm. - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Spalten Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ Wenn diese Option deaktiviert ist, wird die Größe des Fensters reibungslos geändert. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Software-Rendering verwenden - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Wenn diese Option aktiviert ist, verwendet das Terminal den Software-Renderer (alias WARP) anstelle des Hardware-Renderers. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Beim Starten des Computers starten Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ Weitere Informationen. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Warnung: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - Die Auswahl einer Nicht-Monospace-Schriftart führt wahrscheinlich zu visuellen Artefakten. Verwenden Sie sie nach eigenem Ermessen. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index 76400095b16..1aefd03fc53 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -137,11 +137,9 @@ Esta combinación de colores forma parte de la configuración integrada o de una extensión instalada - Para realizar cambios en esta combinación de colores, debe hacer una copia del mismo. - Hacer una copia @@ -311,14 +309,6 @@ Aplicación de terminal que se inicia cuando se ejecuta una aplicación de línea de comandos sin una sesión existente, como en el menú Inicio o en el cuadro de diálogo Ejecutar. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - Redibujar toda la pantalla cuando se muestren actualizaciones - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - Cuando está deshabilitada, el terminal representará solo las actualizaciones de la pantalla entre fotogramas. - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Columnas Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ Cuando se deshabilita, la ventana cambiará de tamaño sin problemas. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Usar procesamiento de software - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Cuando está habilitado, el terminal usará el representador de software (también conocida como WARP) en lugar del hardware. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Iniciar al inicio del equipo Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ Más información. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Advertencia: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - Si elige una fuente sin monoespacios, es probable que se generen artefactos visuales. Úsela según su criterio. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index 2887b95e286..c5f582d3123 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -137,11 +137,9 @@ Ce modèle de couleurs fait partie des paramètres intégrés ou d’une extension installée - Pour apporter des modifications à ce modèle de couleurs, vous devez en faire une copie. - Dupliquer @@ -311,14 +309,6 @@ L’application Terminal qui se lance lorsqu’une application de ligne de commande est exécutée sans session existante, par exemple à partir du menu Démarrer ou de la boîte de dialogue Exécuter. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - Redessiner l’intégralité de l’écran entre chaque trame - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - Une fois désactivé, le terminal n’affiche à l’écran que les modifications effectuées entre les trames. - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Colonnes Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ Une fois désactivée, la fenêtre se redimensionne sans à-coups. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Utiliser le rendu logiciel - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Une fois activé, le terminal utilise le convertisseur logiciel (WARP) à la place du convertisseur matériel. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Lancement au démarrage de l’ordinateur Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ En savoir plus. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Avertissement : - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - Le choix d’une police sans espacement fixe entraîne probablement l’utilisation d’artefacts visuels. Utilisez-la à votre seule discrétion. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 4ce446f17ba..98b19357538 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -137,11 +137,9 @@ Questa combinazione di colori fa parte delle impostazioni predefinite o di un'estensione installata - Per apportare modifiche a questa combinazione di colori, è necessario crearne una copia. - Crea una copia @@ -311,14 +309,6 @@ L'applicazione terminale che viene avviata quando viene eseguita un'applicazione della riga di comando senza una sessione esistente, ad esempio dalla finestra di dialogo menu Start o Esegui. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - Ridisegna l'intero schermo durante la visualizzazione degli aggiornamenti - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - Se disabilitato, il terminale eseguirà il rendering solo degli aggiornamenti sullo schermo tra i fotogrammi. - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Colonne Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ Se disabilitata, la finestra verrà ridimensionata in modo corretto. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Usa il rendering software - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Quando questa opzione è abilitata, il terminale userà il renderer software (detto anche WARP) anziché quello hardware. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Avvia all'avvio del computer Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ Scopri di più. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Avvertenza: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - Se scegli un tipo di carattere non monospaziato, è probabile che si verifichino artefatti visivi. Utilizzalo a vostra discrezione. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index d57a8288d57..aadbf4c8b5d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -137,11 +137,9 @@ この配色は、組み込みの設定またはインストールされている拡張機能の一部です - この配色パターンを変更するには、コピーを作成する必要があります。 - コピーを作成 @@ -311,14 +309,6 @@ [スタート] メニューや [ファイル名を指定して実行] ダイアログなど、既存のセッションなしでコマンドライン アプリケーションを実行したときに起動するターミナル アプリケーション。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - ディスプレイの更新時に画面全体を再描画する - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - 無効にすると、ターミナルはフレームとフレームの間において情報更新分のみレンダリングします。 - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ 無効にすると、ウィンドウがスムーズにサイズ変更されます。 A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - ソフトウェア レンダリングを使用する - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - 有効にすると、ターミナルはハードウェアの代わりにソフトウェア レンダラー (別名 WARP) を使用します。 - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - コンピューターのスタートアップ時に起動 Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ 詳細をご覧ください。 A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - 警告: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - 非等幅フォントを選択すると、視覚的なアーティファクトが発生する可能性があります。 ご自身の判断でご利用ください。 - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index aef454bd85d..dc1cf00158f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -137,11 +137,9 @@ 이 색 구성표는 기본 제공 설정 또는 설치된 확장의 일부입니다. - 이 색 구성표를 변경하려면 복사본을 만들어야 합니다. - 복사본 만들기 @@ -311,14 +309,6 @@ 시작 메뉴나 실행 대화 상자와 같이 기존 세션이 없으면 명령 줄 응용 프로그램을 실행하고 있는 터미널 응용 프로그램입니다. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - 업데이트를 표시할 때 전체 화면 다시 그리기 - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - 사용하지 않도록 설정하면 터미널이 프레임 간 화면 업데이트만 렌더링합니다. - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ 사용하지 않도록 설정하면 창의 크기가 원활하게 조정됩니다. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - 소프트웨어 렌더링 사용 - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - 사용하도록 설정하면 터미널에서 하드웨어 렌더러 대신 소프트웨어 렌더러(WARP)를 사용합니다. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - 컴퓨터 시작 시 실행 Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ 자세히 알아보세요. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - 경고: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - 고정 폭이 아닌 글꼴을 선택하면 시각적 아티팩트가 발생할 수 있습니다. 사용자 판단에 따라 사용하세요. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index 56fff0a611d..eb0d81d9026 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -137,11 +137,9 @@ Este esquema de cores faz parte das configurações internas ou de uma extensão instalada - Para fazer alterações neste esquema de cores, você deve fazer uma cópia dele. - Fazer uma cópia @@ -311,14 +309,6 @@ O aplicativo de terminal que é iniciado quando um aplicativo de linha de comando é executado sem uma sessão existente, como no menu iniciar ou na caixa de diálogo Executar. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - Redesenhar a tela inteira ao exibir atualizações - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - Quando desabilitado, o terminal renderizará somente as atualizações na tela entre quadros. - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Colunas Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ Quando desabilitado, a janela será redimensionada sem problemas. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Usar renderização de software - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Quando habilitado, o terminal usará o renderizador de software (também chamado DE WARP) em vez do hardware. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Iniciar na inicialização da máquina Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ Saiba mais. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Aviso: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - A escolha de uma fonte não monoespaçada provavelmente resultará em artefatos visuais. Use a seu critério. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw index f954fb623fb..98bd519ebb1 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw @@ -137,11 +137,9 @@ Ţħīѕ ċøłǿґ ŝςħèм℮ îś рăŗţ õƒ тнè ъûĩľт-íπ śéţťΐлĝś õř дŋ íńşťªłĺèð èжťęлşίοη !!! !!! !!! !!! !!! !!! !!! ! - Ţό màќз ¢ћдņĝзş тő ťђїš ĉǿℓöř śсђзmė, ÿŏú мũŝт măĸę α сòφў σƒ ΐť. !!! !!! !!! !!! !!! !!! ! - Маĸέ α ċōρý !!! @@ -311,13 +309,38 @@ Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Ĝŗäрђΐсѕ ÃРΪ !!! + This text is shown next to a list of choices. + + + Đĩŗěćŧ3Đ 11 рґóνϊďèš ă мõřе φ℮ŕƒóŗmąņт аʼnđ ƒеǻŧùґê-яĩςђ ℮жφéѓιęʼnςè, ώђēѓėªŝ Ðìгëçť2Ď ĩś мθяë ŝţдвłē. Ţĥé δęƒаűłτ ōφτιоⁿ "Ãúτσмάτīč" щĩłł φìçк ţђë ÁΡĨ ţħǻτ ъёšť ƒīтѕ уōûґ ğřǻφђі¢ѕ нąŗďщǻѓê. ̓ ўöџ ê×ρёřįėʼnċê ѕįġńїƒϊĉâⁿт îśşΰёś, соńśϊδеŗ üśίńğ Đΐřěċτ2Đ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + + + Άůтǿмąťí¢ !!! + The default choice between multiple graphics APIs. + + + Đîяе¢ť2Đ !! + + + Ďїřèĉт3Ď 11 !!! + - Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. + Ďіşǻьłэ рàґτíàľ Šŵąр Ċнаιŋ ĭήνάŀïδαţίбπ !!! !!! !!! !!! + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. - Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". + Бў ďēƒăüℓţ, ťћэ ţèхţ ř℮ŋðěґĕґ υŝ℮ŝ ä FLIP_SEQUENTIAL Ŝωăр Сђáϊп åπδ ďεĉℓάŗέş δΐŗŧў ŗēćŧăñġℓέš νïд ťĥĕ Present1 ĀРĪ. Щĥêи тћїŝ ŝєŧŧΐñğ įѕ ℮лάьłзδ, α FLIP_DISCARD Şẁαр Ċħāīʼn ŵīĺļ ьё υѕėð íπśťεαď, āлδ ŋо ďīŗŧý ŕęсŧдиĝℓэş шīľĺ ъĕ δеĉľāŗэð. Ẃħέтħêґ òñё σř ŧнé ōтħεř ΐś ъёţŧęґ δéρ℮ήđѕ σή ÿούг ħäґďшάřέ ªήð νăŕιούѕ ǿτђēř ƒαстόяš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Ųѕε ŝόƒťщаřė яĕŋðεříлğ (WARP) !!! !!! !!! + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ă şóƒтшářě яãѕтĕгīżεŕ (WARP). Тћįş ŝеţŧΐπģ śнσύŀδ вё ĺєƒŧ δїşâьĺεδ ύπďέя ªℓмôšţ áŀł ćϊŕçϋмśтąпćēѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". Ċσŀùмñѕ !! @@ -474,14 +497,6 @@ Ẅн℮ñ ðìşāьļĕð, τĥė ŵΐņďòώ ẃíļĺ řėŝîżē šмõόтнłγ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Ųѕε ŝόƒťщаřė яĕŋðεříлğ !!! !!! - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ŧнé šбƒţẅāяē ґэпδėѓεŕ (д.к.ă. ẀÅЃΡ) ĭñŝτéāđ оƒ тħё ĥάґđẃаяē οлε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - ₤áϋпςħ ôл мαςĥĭñę ŝťąřťûρ !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1793,12 +1808,12 @@ Ļĕдŗⁿ мõгĕ. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Ẃãялíʼnĝ: !! - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Μįŝşìηğ ƒòńтš: !!! ! + This is a label that is followed by a list of missing fonts. - - €ħσõśïʼnğ ą ñой-мõποşφãĉєδ ƒŏŋţ ẅіľľ łîķέℓў ŕєŝùłţ íй νΐşūαℓ āѓťīƒдςţŝ. Џŝè àţ ÿοùŗ øẁη δíśčґ℮ťîбņ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Νόņ-mσиόšραςз ƒоʼnтŝ: !!! !!! + This is a label that is followed by a list of proportional fonts. \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw index f954fb623fb..98bd519ebb1 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw @@ -137,11 +137,9 @@ Ţħīѕ ċøłǿґ ŝςħèм℮ îś рăŗţ õƒ тнè ъûĩľт-íπ śéţťΐлĝś õř дŋ íńşťªłĺèð èжťęлşίοη !!! !!! !!! !!! !!! !!! !!! ! - Ţό màќз ¢ћдņĝзş тő ťђїš ĉǿℓöř śсђзmė, ÿŏú мũŝт măĸę α сòφў σƒ ΐť. !!! !!! !!! !!! !!! !!! ! - Маĸέ α ċōρý !!! @@ -311,13 +309,38 @@ Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Ĝŗäрђΐсѕ ÃРΪ !!! + This text is shown next to a list of choices. + + + Đĩŗěćŧ3Đ 11 рґóνϊďèš ă мõřе φ℮ŕƒóŗmąņт аʼnđ ƒеǻŧùґê-яĩςђ ℮жφéѓιęʼnςè, ώђēѓėªŝ Ðìгëçť2Ď ĩś мθяë ŝţдвłē. Ţĥé δęƒаűłτ ōφτιоⁿ "Ãúτσмάτīč" щĩłł φìçк ţђë ÁΡĨ ţħǻτ ъёšť ƒīтѕ уōûґ ğřǻφђі¢ѕ нąŗďщǻѓê. ̓ ўöџ ê×ρёřįėʼnċê ѕįġńїƒϊĉâⁿт îśşΰёś, соńśϊδеŗ üśίńğ Đΐřěċτ2Đ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + + + Άůтǿмąťí¢ !!! + The default choice between multiple graphics APIs. + + + Đîяе¢ť2Đ !! + + + Ďїřèĉт3Ď 11 !!! + - Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. + Ďіşǻьłэ рàґτíàľ Šŵąр Ċнаιŋ ĭήνάŀïδαţίбπ !!! !!! !!! !!! + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. - Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". + Бў ďēƒăüℓţ, ťћэ ţèхţ ř℮ŋðěґĕґ υŝ℮ŝ ä FLIP_SEQUENTIAL Ŝωăр Сђáϊп åπδ ďεĉℓάŗέş δΐŗŧў ŗēćŧăñġℓέš νïд ťĥĕ Present1 ĀРĪ. Щĥêи тћїŝ ŝєŧŧΐñğ įѕ ℮лάьłзδ, α FLIP_DISCARD Şẁαр Ċħāīʼn ŵīĺļ ьё υѕėð íπśťεαď, āлδ ŋо ďīŗŧý ŕęсŧдиĝℓэş шīľĺ ъĕ δеĉľāŗэð. Ẃħέтħêґ òñё σř ŧнé ōтħεř ΐś ъёţŧęґ δéρ℮ήđѕ σή ÿούг ħäґďшάřέ ªήð νăŕιούѕ ǿτђēř ƒαстόяš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Ųѕε ŝόƒťщаřė яĕŋðεříлğ (WARP) !!! !!! !!! + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ă şóƒтшářě яãѕтĕгīżεŕ (WARP). Тћįş ŝеţŧΐπģ śнσύŀδ вё ĺєƒŧ δїşâьĺεδ ύπďέя ªℓмôšţ áŀł ćϊŕçϋмśтąпćēѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". Ċσŀùмñѕ !! @@ -474,14 +497,6 @@ Ẅн℮ñ ðìşāьļĕð, τĥė ŵΐņďòώ ẃíļĺ řėŝîżē šмõόтнłγ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Ųѕε ŝόƒťщаřė яĕŋðεříлğ !!! !!! - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ŧнé šбƒţẅāяē ґэпδėѓεŕ (д.к.ă. ẀÅЃΡ) ĭñŝτéāđ оƒ тħё ĥάґđẃаяē οлε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - ₤áϋпςħ ôл мαςĥĭñę ŝťąřťûρ !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1793,12 +1808,12 @@ Ļĕдŗⁿ мõгĕ. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Ẃãялíʼnĝ: !! - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Μįŝşìηğ ƒòńтš: !!! ! + This is a label that is followed by a list of missing fonts. - - €ħσõśïʼnğ ą ñой-мõποşφãĉєδ ƒŏŋţ ẅіľľ łîķέℓў ŕєŝùłţ íй νΐşūαℓ āѓťīƒдςţŝ. Џŝè àţ ÿοùŗ øẁη δíśčґ℮ťîбņ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Νόņ-mσиόšραςз ƒоʼnтŝ: !!! !!! + This is a label that is followed by a list of proportional fonts. \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw index f954fb623fb..98bd519ebb1 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw @@ -137,11 +137,9 @@ Ţħīѕ ċøłǿґ ŝςħèм℮ îś рăŗţ õƒ тнè ъûĩľт-íπ śéţťΐлĝś õř дŋ íńşťªłĺèð èжťęлşίοη !!! !!! !!! !!! !!! !!! !!! ! - Ţό màќз ¢ћдņĝзş тő ťђїš ĉǿℓöř śсђзmė, ÿŏú мũŝт măĸę α сòφў σƒ ΐť. !!! !!! !!! !!! !!! !!! ! - Маĸέ α ċōρý !!! @@ -311,13 +309,38 @@ Ťђě ţэřмīηãĺ ǻφφℓï¢ǻтΐбň ŧђáт łáůйčĥēś ẅĥэп ā сöмmǻńđ-ľіʼnэ ăρρĺįċāτΐôⁿ ϊš яųñ шíţћöυţ ªņ ĕхϊŝŧĭñğ şěŝѕïŏπ, ŀĩĸė ƒѓом ţћĕ Ŝτáґт Μęπü бг Яųņ ďīäℓöğ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Ĝŗäрђΐсѕ ÃРΪ !!! + This text is shown next to a list of choices. + + + Đĩŗěćŧ3Đ 11 рґóνϊďèš ă мõřе φ℮ŕƒóŗmąņт аʼnđ ƒеǻŧùґê-яĩςђ ℮жφéѓιęʼnςè, ώђēѓėªŝ Ðìгëçť2Ď ĩś мθяë ŝţдвłē. Ţĥé δęƒаűłτ ōφτιоⁿ "Ãúτσмάτīč" щĩłł φìçк ţђë ÁΡĨ ţħǻτ ъёšť ƒīтѕ уōûґ ğřǻφђі¢ѕ нąŗďщǻѓê. ̓ ўöџ ê×ρёřįėʼnċê ѕįġńїƒϊĉâⁿт îśşΰёś, соńśϊδеŗ üśίńğ Đΐřěċτ2Đ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + + + Άůтǿмąťí¢ !!! + The default choice between multiple graphics APIs. + + + Đîяе¢ť2Đ !! + + + Ďїřèĉт3Ď 11 !!! + - Ŗёδѓªŵ еñţιŗę šĉřέëŋ шн℮й đĩşρℓâў υρďάţëš !!! !!! !!! !!! - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. + Ďіşǻьłэ рàґτíàľ Šŵąр Ċнаιŋ ĭήνάŀïδαţίбπ !!! !!! !!! !!! + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. - Щħ℮п đįѕāъĺèđ, ťђέ тèřмΐņäľ ωìľľ řεʼnďęŗ όʼnľŷ ŧђέ ŭρďăţзŝ τö ŧђë šсгèзņ вêţшзэʼn ƒяāméş. !!! !!! !!! !!! !!! !!! !!! !!! ! - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". + Бў ďēƒăüℓţ, ťћэ ţèхţ ř℮ŋðěґĕґ υŝ℮ŝ ä FLIP_SEQUENTIAL Ŝωăр Сђáϊп åπδ ďεĉℓάŗέş δΐŗŧў ŗēćŧăñġℓέš νïд ťĥĕ Present1 ĀРĪ. Щĥêи тћїŝ ŝєŧŧΐñğ įѕ ℮лάьłзδ, α FLIP_DISCARD Şẁαр Ċħāīʼn ŵīĺļ ьё υѕėð íπśťεαď, āлδ ŋо ďīŗŧý ŕęсŧдиĝℓэş шīľĺ ъĕ δеĉľāŗэð. Ẃħέтħêґ òñё σř ŧнé ōтħεř ΐś ъёţŧęґ δéρ℮ήđѕ σή ÿούг ħäґďшάřέ ªήð νăŕιούѕ ǿτђēř ƒαстόяš. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Ųѕε ŝόƒťщаřė яĕŋðεříлğ (WARP) !!! !!! !!! + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ă şóƒтшářě яãѕтĕгīżεŕ (WARP). Тћįş ŝеţŧΐπģ śнσύŀδ вё ĺєƒŧ δїşâьĺεδ ύπďέя ªℓмôšţ áŀł ćϊŕçϋмśтąпćēѕ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". Ċσŀùмñѕ !! @@ -474,14 +497,6 @@ Ẅн℮ñ ðìşāьļĕð, τĥė ŵΐņďòώ ẃíļĺ řėŝîżē šмõόтнłγ. !!! !!! !!! !!! !! A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Ųѕε ŝόƒťщаřė яĕŋðεříлğ !!! !!! - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Ẅнέⁿ éñâвĺĕď, ŧĥë тěřmϊñăľ шîľĺ ŭşē ŧнé šбƒţẅāяē ґэпδėѓεŕ (д.к.ă. ẀÅЃΡ) ĭñŝτéāđ оƒ тħё ĥάґđẃаяē οлε. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - ₤áϋпςħ ôл мαςĥĭñę ŝťąřťûρ !!! !!! ! Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1793,12 +1808,12 @@ Ļĕдŗⁿ мõгĕ. !!! A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Ẃãялíʼnĝ: !! - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Μįŝşìηğ ƒòńтš: !!! ! + This is a label that is followed by a list of missing fonts. - - €ħσõśïʼnğ ą ñой-мõποşφãĉєδ ƒŏŋţ ẅіľľ łîķέℓў ŕєŝùłţ íй νΐşūαℓ āѓťīƒдςţŝ. Џŝè àţ ÿοùŗ øẁη δíśčґ℮ťîбņ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !! - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts + + Νόņ-mσиόšραςз ƒоʼnтŝ: !!! !!! + This is a label that is followed by a list of proportional fonts. \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index c935675a936..a5ac2237245 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -137,11 +137,9 @@ Эта цветовая схема является частью встроенных параметров или установленным расширением - Чтобы внести изменения в эту цветовую схему, необходимо создать ее копию. - Создать копию @@ -311,14 +309,6 @@ Приложение терминала, запускаемое при запуске приложения командной строки без существующего сеанса, например из меню "Пуск" или из диалогового окна "Выполнить". A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - Перерисовка всего экрана при обновлении дисплея - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - Если этот параметр отключен, терминал будет отображать только обновления экрана между кадрами. - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Столбцы Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ Если этот параметр отключен, размер окна будет плавно изменен. A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - Использование программного рендеринга - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - Если этот параметр включен, терминал будет использовать программный обработчик (известный как WARP) вместо аппаратного. - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - Запускать при запуске компьютера Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ Дополнительные сведения. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - Внимание! - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - Выбор не моноширинного шрифта, скорее всего, приведет к визуальным артефактам. Используйте по своему усмотрению. - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 44f22000ddb..516ea1b281e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -137,11 +137,9 @@ 此配色方案是内置设置或已安装扩展的一部分 - 若要对此配色方案进行更改,必须复制该配色方案。 - 制作副本 @@ -311,14 +309,6 @@ 当命令行应用程序在没有现有会话(例如从“开始菜单”或“运行”对话框)运行时启动的终端应用程序。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - 显示内容更新时重绘整个屏幕 - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - 禁用后,终端将仅在帧之间将更新呈现到屏幕。 - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ 禁用后,窗口将平滑调整大小。 A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - 使用软件渲染 - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - 启用后,终端将使用软件渲染器(也就是WARP)而不是硬件渲染器。 - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - 在计算机启动时启动 Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ 了解详细信息。 A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - 警告: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - 选择非等宽字体可能导致视觉伪影。请自行决定使用。 - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index 13ab841670c..65998ee399e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -137,11 +137,9 @@ 此色彩配置是內建設定或已安裝擴充功能的一部分 - 若要變更此色彩配置,您必須製作色彩配置的複本。 - 建立複本 @@ -311,14 +309,6 @@ 當執行命令列應用程式且不使用現有工作模式而啟動的終端機應用程式 (例如從 [開始] 功能表或 [執行] 對話方塊)。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. - - 顯示更新時,重新繪製整個螢幕 - Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames. - - - 停用時,終端機只會轉譯框架間螢幕的更新。 - A description for what the "force full repaint" setting does. Presented near "Globals_DisablePartialInvalidation.Header". - Header for a control to choose the number of columns in the terminal's text grid. @@ -474,14 +464,6 @@ 停用時,視窗將會順暢地調整大小。 A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header". - - 使用軟體呈現 - Header for a control to toggle whether the terminal should use software to render content instead of the hardware. - - - 當啟用時,終端機將使用軟體轉譯器 (又稱 WARP),而不是硬體1。 - A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header". - 在電腦啟動時啟動 Header for a control to toggle whether the app should launch when the user's machine starts up, or not. @@ -1789,12 +1771,4 @@ 深入了解。 A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. - - 警告: - Title for the warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - - - 選擇非等寬字型可能會產生視覺成品。使用時請自行斟酌。 - Warning info bar used when a non monospace font face is chosen to indicate that there may be visual artifacts - \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw index 8354be2b2cd..d2eea062990 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw @@ -700,6 +700,9 @@ Ŗ℮ŝţāґт čõʼnлęčŧįоή !!! !! + + Θφěп ŝĉґαŧςђρąδ !!! ! + Šēłεĉτ πĕжţ ċômmàлð ŏµŧρūţ !!! !!! ! diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw index 8354be2b2cd..d2eea062990 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw @@ -700,6 +700,9 @@ Ŗ℮ŝţāґт čõʼnлęčŧįоή !!! !! + + Θφěп ŝĉґαŧςђρąδ !!! ! + Šēłεĉτ πĕжţ ċômmàлð ŏµŧρūţ !!! !!! ! diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw index 8354be2b2cd..d2eea062990 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw @@ -700,6 +700,9 @@ Ŗ℮ŝţāґт čõʼnлęčŧįоή !!! !! + + Θφěп ŝĉґαŧςђρąδ !!! ! + Šēłεĉτ πĕжţ ċômmàлð ŏµŧρūţ !!! !!! ! From 2e7c3fa3132fa3bff23255480c593cbd3432eee5 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 27 Mar 2024 18:05:56 -0500 Subject: [PATCH 184/603] Move to AzureFileCopy@6 for Managed Identity support (#16957) This is required for us to move off Entra ID Application identity. --- build/pipelines/templates-v2/job-deploy-to-azure-storage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml index 69b82e57bb3..e2ed380fd45 100644 --- a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml +++ b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml @@ -80,7 +80,7 @@ jobs: Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute displayName: Install Azure Module Dependencies - - task: AzureFileCopy@5 + - task: AzureFileCopy@6 displayName: Publish to Storage Account inputs: sourcePath: _out/* From b0b7e50ec5d0330d188c06dccf45ff8b99f83983 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 27 Mar 2024 18:12:50 -0500 Subject: [PATCH 185/603] Revert "Move to AzureFileCopy@6 for Managed Identity support (#16957)" This reverts commit 2e7c3fa3132fa3bff23255480c593cbd3432eee5. --- build/pipelines/templates-v2/job-deploy-to-azure-storage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml index e2ed380fd45..69b82e57bb3 100644 --- a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml +++ b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml @@ -80,7 +80,7 @@ jobs: Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute displayName: Install Azure Module Dependencies - - task: AzureFileCopy@6 + - task: AzureFileCopy@5 displayName: Publish to Storage Account inputs: sourcePath: _out/* From 4458a6d8c62ff7e7cddec7a3eb628db07d1ed8dd Mon Sep 17 00:00:00 2001 From: charli <111490544+hellocharli@users.noreply.github.com> Date: Thu, 28 Mar 2024 05:11:25 -0700 Subject: [PATCH 186/603] Replace 'it'll' with 'it will' to adhere to Microsoft Style Guidelines (#16955) ## Summary of the Pull Request ## References and Relevant Issues Fixes "Align 'Run this profile as Administrator' Settings Description with Microsoft Style Guidelines" #16946 ## Detailed Description of the Pull Request / Additional comments Expanded the contraction "it'll" to "it will" on line 993 of `src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw` ## Validation Steps Performed N/A ## PR Checklist - [X] Closes #16946 - [ ] Tests added/passed - No tests were added - [ ] Documentation updated - No documentation was updated - [ ] Schema updated (if necessary) - No schema was updated --- .../TerminalSettingsEditor/Resources/en-US/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index d046ac12df7..a4f21bf6754 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -990,7 +990,7 @@ Header for a control to toggle whether the profile should always open elevated (in an admin window) - If enabled, the profile will open in an Admin terminal window automatically. If the current window is already running as admin, it'll open in this window. + If enabled, the profile will open in an Admin terminal window automatically. If the current window is already running as admin, it will open in this window. A description for what the "elevate" setting does. Presented near "Profile_Elevate". From e44fab921b64249d984dcc7e46ebefd4f4efc083 Mon Sep 17 00:00:00 2001 From: Windows Console Service Bot <14666831+consvc@users.noreply.github.com> Date: Thu, 28 Mar 2024 07:12:46 -0500 Subject: [PATCH 187/603] Localization Updates - main - 03/28/2024 03:03:16 (#16959) --- .../Resources/de-DE/Resources.resw | 8 ++++ .../Resources/es-ES/Resources.resw | 4 ++ .../Resources/fr-FR/Resources.resw | 8 ++++ .../Resources/it-IT/Resources.resw | 8 ++++ .../Resources/ja-JP/Resources.resw | 8 ++++ .../Resources/ko-KR/Resources.resw | 8 ++++ .../Resources/pt-BR/Resources.resw | 8 ++++ .../Resources/ru-RU/Resources.resw | 8 ++++ .../Resources/zh-CN/Resources.resw | 8 ++++ .../Resources/zh-TW/Resources.resw | 8 ++++ .../Resources/de-DE/Resources.resw | 41 +++++++++++++++++++ .../Resources/es-ES/Resources.resw | 15 +++++++ .../Resources/fr-FR/Resources.resw | 4 ++ .../Resources/it-IT/Resources.resw | 15 +++++++ .../Resources/ja-JP/Resources.resw | 41 +++++++++++++++++++ .../Resources/ko-KR/Resources.resw | 41 +++++++++++++++++++ .../Resources/pt-BR/Resources.resw | 41 +++++++++++++++++++ .../Resources/ru-RU/Resources.resw | 41 +++++++++++++++++++ .../Resources/zh-CN/Resources.resw | 15 +++++++ .../Resources/zh-TW/Resources.resw | 41 +++++++++++++++++++ .../Resources/de-DE/Resources.resw | 3 ++ .../Resources/ja-JP/Resources.resw | 3 ++ .../Resources/ko-KR/Resources.resw | 3 ++ .../Resources/pt-BR/Resources.resw | 3 ++ .../Resources/ru-RU/Resources.resw | 3 ++ .../Resources/zh-TW/Resources.resw | 3 ++ 26 files changed, 389 insertions(+) diff --git a/src/cascadia/TerminalControl/Resources/de-DE/Resources.resw b/src/cascadia/TerminalControl/Resources/de-DE/Resources.resw index 732522ca013..338cb51d30f 100644 --- a/src/cascadia/TerminalControl/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/de-DE/Resources.resw @@ -213,6 +213,14 @@ Installieren Sie entweder die fehlende Schriftart, oder wählen Sie eine andere Unerwarteter Fehler beim Renderer: {0} {0} is an error code. + + Die folgenden Schriftarten wurden nicht gefunden: {0}. Installieren Sie sie, oder wählen Sie andere Schriftarten aus. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + Unerwarteter Rendererfehler: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + Der schreibgeschützte Modus ist aktiviert. diff --git a/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw b/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw index e9c2b842f9a..30231e7f63e 100644 --- a/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw @@ -213,6 +213,10 @@ Instale la fuente que falta o elija otra. El representador encontró un error inesperado: {0} {0} is an error code. + + El representador encontró un error inesperado: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + El modo de solo lectura está habilitado. diff --git a/src/cascadia/TerminalControl/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalControl/Resources/fr-FR/Resources.resw index bb21b343fe6..6f3e367fdc9 100644 --- a/src/cascadia/TerminalControl/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/fr-FR/Resources.resw @@ -213,6 +213,14 @@ Installez la police manquante ou choisissez-en une autre. Le convertisseur a rencontré une erreur inattendue : {0} {0} is an error code. + + Impossible de trouver les polices suivantes : {0}. Installez-les ou choisissez d’autres polices. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + Le convertisseur a rencontré une erreur inattendue : {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + Le mode lecture seule est activé. diff --git a/src/cascadia/TerminalControl/Resources/it-IT/Resources.resw b/src/cascadia/TerminalControl/Resources/it-IT/Resources.resw index 4e6e08d40c8..39a76ac1915 100644 --- a/src/cascadia/TerminalControl/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/it-IT/Resources.resw @@ -213,6 +213,14 @@ Installare il tipo di carattere mancante o sceglierne un altro. Il renderer ha rilevato un errore imprevisto: {0} {0} is an error code. + + Impossibile trovare i tipi di carattere seguenti: {0}. Installarli o scegliere tipi di carattere diversi. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + Errore imprevisto del renderer: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + La modalità di sola lettura è abilitata. diff --git a/src/cascadia/TerminalControl/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalControl/Resources/ja-JP/Resources.resw index 4b0c96d2748..b352afeeec7 100644 --- a/src/cascadia/TerminalControl/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/ja-JP/Resources.resw @@ -213,6 +213,14 @@ レンダラーで予期しないエラーが発生しました: {0} {0} is an error code. + + 次のフォントが見つかりません: {0}。インストールするか、別のフォントを選択してください。 + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + レンダラーで予期しないエラーが発生しました: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + 読み取り専用モードが有効になっています。 diff --git a/src/cascadia/TerminalControl/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalControl/Resources/ko-KR/Resources.resw index 75e624dab29..2200c028d99 100644 --- a/src/cascadia/TerminalControl/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/ko-KR/Resources.resw @@ -213,6 +213,14 @@ 렌더러에서 예기치 않은 오류가 발생했습니다. {0} {0} is an error code. + + 다음 글꼴을 찾을 수 없습니다. {0}. 설치하거나 다른 글꼴을 선택하세요. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + 렌더러에서 예기치 않은 오류가 발생했습니다. {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + 읽기 전용 모드가 사용 설정됩니다. diff --git a/src/cascadia/TerminalControl/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalControl/Resources/pt-BR/Resources.resw index ca6ae8a3e74..aadafd14b12 100644 --- a/src/cascadia/TerminalControl/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/pt-BR/Resources.resw @@ -213,6 +213,14 @@ Instale a fonte ausente ou escolha outra. O renderizador encontrou um erro inesperado: {0} {0} is an error code. + + Não é possível localizar as seguintes fontes: {0}. Instale-os ou escolha fontes diferentes. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + O renderizador encontrou um erro inesperado: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + O modo somente leitura está habilitado. diff --git a/src/cascadia/TerminalControl/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalControl/Resources/ru-RU/Resources.resw index 082557f26c2..8b0c908a9bd 100644 --- a/src/cascadia/TerminalControl/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/ru-RU/Resources.resw @@ -213,6 +213,14 @@ Произошла непредвиденная ошибка обработчика: {0} {0} is an error code. + + Не удалось найти следующие шрифты: {0}. Установите их или выберите другие шрифты. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + В обработчике произошла непредвиденная ошибка: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + Включен режим только для чтения. diff --git a/src/cascadia/TerminalControl/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalControl/Resources/zh-CN/Resources.resw index c4d729bd8bb..c331dab19ac 100644 --- a/src/cascadia/TerminalControl/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/zh-CN/Resources.resw @@ -213,6 +213,14 @@ 呈现器遇到意外错误: {0} {0} is an error code. + + 找不到以下字体: {0}。请安装它们或选择其他字体。 + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + 呈现器遇到错误: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + 只读模式已启用。 diff --git a/src/cascadia/TerminalControl/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalControl/Resources/zh-TW/Resources.resw index 4f4f8671f7c..31d8034d2a1 100644 --- a/src/cascadia/TerminalControl/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/zh-TW/Resources.resw @@ -213,6 +213,14 @@ 轉譯器發生意外的錯誤:{0} {0} is an error code. + + 找不到下列字型: {0}。請安裝它們,或選擇其他字型。 + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + + 轉譯器發生意外的錯誤: {0:#010x} {1} + {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. + 已啟用唯讀模式。 diff --git a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw index c7e5f00ab76..9214556f77b 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw @@ -309,6 +309,39 @@ Die Terminalanwendung, die gestartet wird, wenn eine Befehlszeilenanwendung ohne vorhandene Sitzung gestartet wird, beispielsweise vom Startmenü oder über das Dialogfeld "Ausführen". A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Grafik-API + This text is shown next to a list of choices. + + + Direct3D 11 bietet eine leistungsfähigere und funktionsreichere Benutzeroberfläche, während Direct2D stabiler ist. Mit der Standardoption "Automatisch" wird die API ausgewählt, die am besten zu Ihrer Grafikhardware passt. Wenn erhebliche Probleme auftreten, erwägen Sie die Verwendung von Direct2D. + + + Automatisch + The default choice between multiple graphics APIs. + + + Direct2D + + + Direct3D 11 + + + Swapchain-Teilinvalidierung deaktivieren + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + Standardmäßig verwendet der Textrenderer eine FLIP_SEQUENTIAL Swap Chain und deklariert modifiziert Rechtecke über die Present1-API. Wenn diese Einstellung aktiviert ist, wird stattdessen eine FLIP_DISCARD Swap Chain verwendet, und es werden keine modifiziert Rechtecke deklariert. Ob der eine oder der andere besser ist, hängt von Ihrer Hardware und verschiedenen anderen Faktoren ab. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Softwarerendering verwenden (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + Wenn diese Option aktiviert ist, verwendet das Terminal einen Softwarerasterer (WARP). Diese Einstellung sollte unter fast allen Umständen deaktiviert bleiben. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Spalten Header for a control to choose the number of columns in the terminal's text grid. @@ -1771,4 +1804,12 @@ Weitere Informationen. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + Fehlende Schriftarten: + This is a label that is followed by a list of missing fonts. + + + Proportionale Schriftarten: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index 1aefd03fc53..e4574ab1f35 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -309,6 +309,21 @@ Aplicación de terminal que se inicia cuando se ejecuta una aplicación de línea de comandos sin una sesión existente, como en el menú Inicio o en el cuadro de diálogo Ejecutar. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Direct3D 11 proporciona una experiencia con más rendimiento y características, mientras que Direct2D es más estable. La opción predeterminada "Automático" seleccionará la API que mejor se adapte a su hardware gráfico. Si experimenta problemas importantes, considere la posibilidad de usar Direct2D. + + + Automático + The default choice between multiple graphics APIs. + + + De forma predeterminada, el representador de texto usa una cadena de intercambio de FLIP_SEQUENTIAL y declara rectángulos modificados a través de la API de Present1. Cuando esta configuración está habilitada, se usará una cadena de intercambio de FLIP_DISCARD y no se declarará ningún rectángulo con modificaciones. Si uno u otro es mejor depende de su hardware y de otros factores. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Cuando está habilitado, el terminal usará un rasterizador de software (WARP). Esta configuración debe dejarse deshabilitada en casi todas las circunstancias. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Columnas Header for a control to choose the number of columns in the terminal's text grid. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index c5f582d3123..2f56f3ebd4c 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -309,6 +309,10 @@ L’application Terminal qui se lance lorsqu’une application de ligne de commande est exécutée sans session existante, par exemple à partir du menu Démarrer ou de la boîte de dialogue Exécuter. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Automatique + The default choice between multiple graphics APIs. + Colonnes Header for a control to choose the number of columns in the terminal's text grid. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 98b19357538..3d0fb72f844 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -309,6 +309,21 @@ L'applicazione terminale che viene avviata quando viene eseguita un'applicazione della riga di comando senza una sessione esistente, ad esempio dalla finestra di dialogo menu Start o Esegui. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Direct3D 11 offre un'esperienza più performante e con funzionalità più elevate, mentre Direct2D è più stabile. L'opzione predefinita "Automatico" sceglierà l'API più adatta all'hardware grafico. Se si verificano problemi significativi, provare a usare Direct2D. + + + Automatico + The default choice between multiple graphics APIs. + + + Per impostazione predefinita, il renderer di testo usa una catena di scambio FLIP_SEQUENTIAL e dichiara rettangoli modificati ma non modificati tramite l'API Present1. Quando questa impostazione è abilitata, verrà utilizzata una catena di scambio FLIP_DISCARD e non verranno dichiarati rettangoli modificati ma non modificati. Il miglioramento dell'uno o dell'altro dipende dall'hardware e da vari altri fattori. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Se questa opzione è abilitata, il terminale userà un rasterizzatore software (WARP). Questa impostazione deve essere lasciata disabilitata in quasi tutte le circostanze. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Colonne Header for a control to choose the number of columns in the terminal's text grid. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index aadbf4c8b5d..6cd0a869dd7 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -309,6 +309,39 @@ [スタート] メニューや [ファイル名を指定して実行] ダイアログなど、既存のセッションなしでコマンドライン アプリケーションを実行したときに起動するターミナル アプリケーション。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + グラフィックス API + This text is shown next to a list of choices. + + + Direct3D 11 はよりパフォーマンスが高く、機能が豊富なエクスペリエンスを提供しますが、Direct2D はより安定しています。既定のオプション [自動] では、グラフィックス ハードウェアに最適な API が選択されます。重大な問題が発生した場合は、Direct2D の使用を検討してください。 + + + 自動 + The default choice between multiple graphics APIs. + + + Direct2D + + + Direct3D 11 + + + 部分的なスワップ チェーンの無効化を無効にする + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + 既定では、テキスト レンダラーは FLIP_SEQUENTIAL Swap Chain を使用し、Present1 API を介してダーティ四角形を宣言します。この設定を有効にすると、代わりに FLIP_DISCARD スワップ チェーンが使用され、ダーティ四角形は宣言されません。どちらか一方が望ましいかどうかは、お使いのハードウェアとその他のさまざまな要因によって異なります。 + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + ソフトウェア レンダリングを使用する (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + 有効にすると、ターミナルはソフトウェア ラスターライザー (WARP) を使用します。この設定は、ほとんどの状況で無効のままにする必要があります。 + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Header for a control to choose the number of columns in the terminal's text grid. @@ -1771,4 +1804,12 @@ 詳細をご覧ください。 A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + 見つからないフォント: + This is a label that is followed by a list of missing fonts. + + + 非等幅フォント: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index dc1cf00158f..40e83f7dfc4 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -309,6 +309,39 @@ 시작 메뉴나 실행 대화 상자와 같이 기존 세션이 없으면 명령 줄 응용 프로그램을 실행하고 있는 터미널 응용 프로그램입니다. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Graphics API + This text is shown next to a list of choices. + + + Direct3D 11은 성능과 기능이 풍부한 환경을 제공하는 반면 Direct2D는 더욱 안정적입니다. 기본 옵션 "자동"은 그래픽 하드웨어에 가장 적합한 API를 선택합니다. 심각한 문제가 발생하는 경우 Direct2D를 사용하는 것이 좋습니다. + + + 자동 + The default choice between multiple graphics APIs. + + + Direct2D + + + Direct3D 11 + + + 부분 스왑 체인 무효화 사용 안 함 + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + 기본적으로 텍스트 렌더러는 FLIP_SEQUENTIAL 스왑 체인을 사용하고 Present1 API를 통해 더티 사각형을 선언합니다. 이 설정을 사용하면 FLIP_DISCARD 스왑 체인이 대신 사용되며 더티 사각형이 선언되지 않습니다. 하나 또는 다른 요소가 더 좋은지 여부는 하드웨어 및 다양한 다른 요인에 따라 달라집니다. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + 소프트웨어 렌더링(WARP) 사용 + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + 사용하도록 설정하면 터미널에서 소프트웨어 래스터라이저(WARP)를 사용합니다. 거의 모든 상황에서는 이 설정을 사용하지 않도록 설정해야 합니다. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Header for a control to choose the number of columns in the terminal's text grid. @@ -1771,4 +1804,12 @@ 자세히 알아보세요. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + 누락된 글꼴: + This is a label that is followed by a list of missing fonts. + + + 고정 폭이 아닌 글꼴: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index eb0d81d9026..1121be023e6 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -309,6 +309,39 @@ O aplicativo de terminal que é iniciado quando um aplicativo de linha de comando é executado sem uma sessão existente, como no menu iniciar ou na caixa de diálogo Executar. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + API de Gráficos + This text is shown next to a list of choices. + + + O Direct3D 11 fornece uma experiência mais completa e avançada de recursos, enquanto o Direct2D é mais estável. A opção padrão "Automático" escolherá a API que melhor se ajusta ao hardware gráfico. Se você tiver problemas significativos, considere o uso Direct2D. + + + Automático + The default choice between multiple graphics APIs. + + + Direct2D + + + Direct3D 11 + + + Desabilitar a invalidação parcial da cadeia de troca + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + Por padrão, o renderizador de texto usa FLIP_SEQUENTIAL Cadeia de Permuta e declara sujo retângulos por meio da API Present1. Quando esta configuração estiver habilitada, um FLIP_DISCARD cadeia de troca será usado e nenhum retângulo sujo será declarado. Se um ou outro é melhor depende do hardware e de vários outros fatores. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Usar a renderização de software (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + Quando habilitado, o terminal usará um rasterizador de software (WARP). Esta configuração deve ser desabilitada em quase todas as circunstâncias. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Colunas Header for a control to choose the number of columns in the terminal's text grid. @@ -1771,4 +1804,12 @@ Saiba mais. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + Fontes ausentes: + This is a label that is followed by a list of missing fonts. + + + Fontes não monoespaçadas: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index a5ac2237245..913ae9ee97f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -309,6 +309,39 @@ Приложение терминала, запускаемое при запуске приложения командной строки без существующего сеанса, например из меню "Пуск" или из диалогового окна "Выполнить". A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + API графики + This text is shown next to a list of choices. + + + Direct3D 11 отличается более высокой производительностью и расширенной функциональностью, но Direct2D стабильнее. По умолчанию используется параметр "Автоматически": выбирается API, лучше всего подходящий к графическому оборудованию. Если возникают серьезные проблемы, попробуйте использовать Direct2D. + + + Автоматически + The default choice between multiple graphics APIs. + + + Direct2D + + + Direct3D 11 + + + Отключить аннулирование частичной цепочки буферов + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + По умолчанию система отрисовки текста использует цепочку буферов FLIP_SEQUENTIAL и объявляет "грязные" прямоугольники с помощью API Present1. Когда этот параметр включен, будет использоваться цепочка буферов FLIP_DISCARD, а "грязные" прямоугольники не будут объявляться. Выбор зависит от оборудования и от ряда других факторов. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Использовать программную отрисовку (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + Если этот параметр включен, терминал будет использовать программный растеризатор (WARP). Этот параметр должен быть отключен почти при любых обстоятельствах. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Столбцы Header for a control to choose the number of columns in the terminal's text grid. @@ -1771,4 +1804,12 @@ Дополнительные сведения. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + Отсутствующие шрифты: + This is a label that is followed by a list of missing fonts. + + + Шрифты, кроме моноширинных: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 516ea1b281e..91c54379777 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -309,6 +309,21 @@ 当命令行应用程序在没有现有会话(例如从“开始菜单”或“运行”对话框)运行时启动的终端应用程序。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Direct3D 11 提供性能更佳且功能更丰富的体验,而 Direct2D 则更稳定。默认选项“自动”将选择最适合你的图形硬件的 API。如果遇到严重问题,请考虑使用 Direct2D。 + + + 自动 + The default choice between multiple graphics APIs. + + + 默认情况下,文本呈现器使用 FLIP_SEQUENTIAL 交换链并通过 Present1 API 声明脏矩形。启用此设置时,将改用 FLIP_DISCARD 交换链,并且不会声明脏矩形。一个或另一个是否更依赖于你的硬件和各种其他因素。 + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + 启用后,终端将使用软件光栅 (WARP)。几乎在所有情况下都应禁用此设置。 + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Header for a control to choose the number of columns in the terminal's text grid. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index 65998ee399e..0e1d7c5e3ea 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -309,6 +309,39 @@ 當執行命令列應用程式且不使用現有工作模式而啟動的終端機應用程式 (例如從 [開始] 功能表或 [執行] 對話方塊)。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + 圖形 API + This text is shown next to a list of choices. + + + Direct3D 11 提供更具效能且功能更豐富的體驗,而 Direct2D 則較穩定。默認選項 [自動] 會挑選最適合您圖形硬體的 API。如果您遇到重大問題,請考慮使用 Direct2D。 + + + 自動 + The default choice between multiple graphics APIs. + + + Direct2D + + + Direct3D 11 + + + 停用部分 [交換鏈結] 失效 + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + 根據預設,文字轉譯器會使用 FLIP_SEQUENTIAL 交換鏈結,並透過 Present1 API 宣告變更的矩形。啟用此設定時,將改為使用 FLIP_DISCARD 交換鏈結,而且不會宣告任何已變更的矩形。一個或另一個比較好取決於您的硬體和各種其他因素。 + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + 使用軟體呈現 (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + 啟用時,終端機將使用軟體點陣器 (WARP)。在幾乎所有情況下,此設定都應該保留停用。 + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Header for a control to choose the number of columns in the terminal's text grid. @@ -1771,4 +1804,12 @@ 深入了解。 A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + 缺少的字型: + This is a label that is followed by a list of missing fonts. + + + 非等寬字型: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw index d3ed4232bd0..1b088d57051 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw @@ -700,6 +700,9 @@ Verbindung neu starten + + Notizblock öffnen + Nächste Befehlsausgabe auswählen diff --git a/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw index 07121f6d2a5..173663fc990 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw @@ -700,6 +700,9 @@ 接続の再起動 + + スクラッチパッドを開く + 次のコマンド出力を選択 diff --git a/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw index 9151622030e..90be00c1d20 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw @@ -700,6 +700,9 @@ 연결 다시 시작 + + 스크래치 패드 열기 + 다음 명령 출력 선택 diff --git a/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw index bac30f15998..aa4a632c367 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw @@ -700,6 +700,9 @@ Reiniciar conexão + + Abrir o scratchpad + Selecionar a próxima saída de comando diff --git a/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw index a85b0d2d248..d5e70d81948 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw @@ -700,6 +700,9 @@ Перезапустить подключение + + Открыть временную память + Выбрать следующую команду вывода diff --git a/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw index d777ffb9380..536d4b900b5 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw @@ -700,6 +700,9 @@ 重新啟動連線 + + 開啟聽寫面板 + 選取下一個命令輸出 From 68955d1ddbccf06c8f42e1c6c306ede1d3e7e157 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Mar 2024 05:28:26 -0700 Subject: [PATCH 188/603] Add Dark+, CGA and IBM color schemes (#16953) As discussed in #6176 and #16754. The "CGA" and "IBM 5153" names I'm told are the right ones to use: https://github.com/microsoft/terminal/issues/16754#issuecomment-2024066880 Closes #6176 Closes #16754 --- .../TerminalSettingsModel/defaults.json | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 360f37afaad..ae8ed369ff6 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -281,6 +281,75 @@ "brightPurple": "#AD7FA8", "brightCyan": "#34E2E2", "brightWhite": "#EEEEEC" + }, + { + "name": "Dark+", + "foreground": "#cccccc", + "background": "#1e1e1e", + "cursorColor": "#808080", + "selectionBackground": "#ffffff", + "black": "#000000", + "red": "#cd3131", + "green": "#0dbc79", + "yellow": "#e5e510", + "blue": "#2472c8", + "purple": "#bc3fbc", + "cyan": "#11a8cd", + "white": "#e5e5e5", + "brightBlack": "#666666", + "brightRed": "#f14c4c", + "brightGreen": "#23d18b", + "brightYellow": "#f5f543", + "brightBlue": "#3b8eea", + "brightPurple": "#d670d6", + "brightCyan": "#29b8db", + "brightWhite": "#e5e5e5" + }, + { + "background": "#000000", + "black": "#000000", + "blue": "#0000AA", + "brightBlack": "#555555", + "brightBlue": "#5555FF", + "brightCyan": "#55FFFF", + "brightGreen": "#55FF55", + "brightPurple": "#FF55FF", + "brightRed": "#FF5555", + "brightWhite": "#FFFFFF", + "brightYellow": "#FFFF55", + "cursorColor": "#00AA00", + "cyan": "#00AAAA", + "foreground": "#AAAAAA", + "green": "#00AA00", + "name": "CGA", + "purple": "#AA00AA", + "red": "#AA0000", + "selectionBackground": "#FFFFFF", + "white": "#AAAAAA", + "yellow": "#AA5500" + }, + { + "background": "#000000", + "black": "#000000", + "blue": "#0000AA", + "brightBlack": "#555555", + "brightBlue": "#5555FF", + "brightCyan": "#55FFFF", + "brightGreen": "#55FF55", + "brightPurple": "#FF55FF", + "brightRed": "#FF5555", + "brightWhite": "#FFFFFF", + "brightYellow": "#FFFF55", + "cursorColor": "#00AA00", + "cyan": "#00AAAA", + "foreground": "#AAAAAA", + "green": "#00AA00", + "name": "IBM 5153", + "purple": "#AA00AA", + "red": "#AA0000", + "selectionBackground": "#FFFFFF", + "white": "#AAAAAA", + "yellow": "#C47E00" } ], "themes": [ From 8349fd2728f93acccdf6d3bb1d85c5f1a96a077c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Mar 2024 11:19:42 -0700 Subject: [PATCH 189/603] Fix `repositionCursorWithMouse` after the viewport starts scrolling (#16950) Yea, I used a viewport-relative mouse position, rather than the buffer-relative position. Pretty obvious in retrospect. Closes #16423 --- src/cascadia/TerminalControl/ControlCore.cpp | 25 ++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 98c70398559..95c80e4e9ad 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1891,7 +1891,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation { const auto& last{ marks.back() }; const auto [start, end] = last.GetExtent(); - const auto lastNonSpace = _terminal->GetTextBuffer().GetLastNonSpaceCharacter(); + const auto bufferSize = _terminal->GetTextBuffer().GetSize(); + auto lastNonSpace = _terminal->GetTextBuffer().GetLastNonSpaceCharacter(); + bufferSize.IncrementInBounds(lastNonSpace, true); // If the user clicked off to the right side of the prompt, we // want to send keystrokes to the last character in the prompt +1. @@ -1904,17 +1906,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation // By only sending keypresses to the end of the command + 1, we // should leave the cursor at the very end of the prompt, // without adding any characters from a previous command. - auto clampedClick = terminalPosition; - if (terminalPosition > lastNonSpace) + + // terminalPosition is viewport-relative. + const auto bufferPos = _terminal->GetViewport().Origin() + terminalPosition; + if (bufferPos.y > lastNonSpace.y) { - clampedClick = lastNonSpace + til::point{ 1, 0 }; - _terminal->GetTextBuffer().GetSize().Clamp(clampedClick); + // Clicked under the prompt. Bail. + return; } + // Limit the click to 1 past the last character on the last line. + const auto clampedClick = std::min(bufferPos, lastNonSpace); + if (clampedClick >= end) { // Get the distance between the cursor and the click, in cells. - const auto bufferSize = _terminal->GetTextBuffer().GetSize(); // First, make sure to iterate from the first point to the // second. The user may have clicked _earlier_ in the @@ -1944,7 +1950,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation append(_terminal->SendKeyEvent(key, 0, {}, false)); } - _sendInputToConnection(buffer); + { + // Sending input requires that we're unlocked, because + // writing the input pipe may block indefinitely. + const auto suspension = _terminal->SuspendLock(); + _sendInputToConnection(buffer); + } } } } From 2050416997b237361e0c5979bb1cad88d0544673 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 28 Mar 2024 19:22:39 +0100 Subject: [PATCH 190/603] Remove VtApiRoutines (#16954) This removes `VtApiRoutines` and the VT passthrough mode. Why? While VT passthrough mode has a clear advantage (doesn't corrupt VT sequences) it fails to address other pain points (performance, out-of-sync issues after resize, etc.). Alternative options are available which have less restrictions. Why now? It's spring! Spring cleanup! --- doc/cascadia/profiles.schema.json | 4 - src/cascadia/TerminalApp/TerminalPage.cpp | 5 - .../TerminalConnection/ConptyConnection.cpp | 12 - .../TerminalConnection/ConptyConnection.h | 1 - src/cascadia/TerminalCore/ICoreSettings.idl | 1 - .../ProfileViewModel.cpp | 4 - .../TerminalSettingsEditor/ProfileViewModel.h | 2 - .../ProfileViewModel.idl | 2 - .../Profiles_Advanced.xaml | 10 - .../Resources/en-US/Resources.resw | 4 - .../TerminalSettingsModel/MTSMSettings.h | 1 - .../TerminalSettingsModel/Profile.idl | 1 - .../TerminalSettings.cpp | 1 - .../TerminalSettingsModel/TerminalSettings.h | 1 - src/cascadia/inc/ControlProperties.h | 1 - src/features.xml | 22 - src/host/ConsoleArguments.cpp | 12 - src/host/ConsoleArguments.hpp | 19 +- src/host/VtApiRoutines.cpp | 810 ------------------ src/host/VtApiRoutines.h | 366 -------- src/host/VtIo.cpp | 32 - src/host/VtIo.hpp | 1 - src/host/host-common.vcxitems | 2 - src/host/lib/hostlib.vcxproj.filters | 8 +- src/host/sources.inc | 1 - src/host/ut_host/ConsoleArgumentsTests.cpp | 156 ++-- src/inc/conpty-static.h | 1 - src/renderer/vt/Xterm256Engine.hpp | 4 - src/renderer/vt/state.cpp | 13 - src/renderer/vt/vtrenderer.hpp | 1 - src/server/ApiSorter.cpp | 19 +- src/server/IApiRoutines.h | 13 +- src/winconpty/winconpty.cpp | 9 +- src/winconpty/winconpty.h | 3 - 34 files changed, 78 insertions(+), 1464 deletions(-) delete mode 100644 src/host/VtApiRoutines.cpp delete mode 100644 src/host/VtApiRoutines.h diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 6733fe52266..9593ab9f6a7 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2777,10 +2777,6 @@ "description": "When set to true, prompts will automatically be marked.", "type": "boolean" }, - "experimental.connection.passthroughMode": { - "description": "When set to true, directs the PTY for this connection to use pass-through mode instead of the original Conhost PTY simulation engine. This is an experimental feature, and its continued existence is not guaranteed.", - "type": "boolean" - }, "experimental.retroTerminalEffect": { "description": "When set to true, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed.", "type": "boolean" diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 605110a0ad7..d3fc7a2dc25 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1277,11 +1277,6 @@ namespace winrt::TerminalApp::implementation } } - if constexpr (Feature_VtPassthroughMode::IsEnabled()) - { - valueSet.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough())); - } - connection.Initialize(valueSet); TraceLoggingWrite( diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index 063083ace36..dd85388263b 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -257,10 +257,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _cols = unbox_prop_or(settings, L"initialCols", _cols); _sessionId = unbox_prop_or(settings, L"sessionId", _sessionId); _environment = settings.TryLookup(L"environment").try_as(); - if constexpr (Feature_VtPassthroughMode::IsEnabled()) - { - _passthroughMode = unbox_prop_or(settings, L"passthroughMode", _passthroughMode); - } _inheritCursor = unbox_prop_or(settings, L"inheritCursor", _inheritCursor); _profileGuid = unbox_prop_or(settings, L"profileGuid", _profileGuid); @@ -332,14 +328,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation flags |= PSEUDOCONSOLE_INHERIT_CURSOR; } - if constexpr (Feature_VtPassthroughMode::IsEnabled()) - { - if (_passthroughMode) - { - WI_SetFlag(flags, PSEUDOCONSOLE_PASSTHROUGH_MODE); - } - } - THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), flags, &_inPipe, &_outPipe, &_hPC)); if (_initialParentHwnd != 0) diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index 2170b1fbfdf..e1b47bee64e 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -90,7 +90,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation til::u8state _u8State{}; std::wstring _u16Str{}; std::array _buffer{}; - bool _passthroughMode{}; bool _inheritCursor{ false }; til::env _initialEnv{}; diff --git a/src/cascadia/TerminalCore/ICoreSettings.idl b/src/cascadia/TerminalCore/ICoreSettings.idl index 1f3c78aa09b..ab3f0e5cf83 100644 --- a/src/cascadia/TerminalCore/ICoreSettings.idl +++ b/src/cascadia/TerminalCore/ICoreSettings.idl @@ -22,7 +22,6 @@ namespace Microsoft.Terminal.Core Boolean ForceVTInput; Boolean TrimBlockSelection; Boolean DetectURLs; - Boolean VtPassthrough; Windows.Foundation.IReference TabColor; Windows.Foundation.IReference StartingTabColor; diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index 75c201f76fd..a00a077ef5b 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -298,10 +298,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _unfocusedAppearanceViewModel; } - bool ProfileViewModel::VtPassthroughAvailable() const noexcept - { - return Feature_VtPassthroughMode::IsEnabled() && Feature_VtPassthroughModeSettingInUI::IsEnabled(); - } bool ProfileViewModel::ShowMarksAvailable() const noexcept { return Feature_ScrollbarMarks::IsEnabled(); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index d3edd16dc48..3dd30223fe2 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -82,7 +82,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void CreateUnfocusedAppearance(); void DeleteUnfocusedAppearance(); - bool VtPassthroughAvailable() const noexcept; bool ShowMarksAvailable() const noexcept; bool AutoMarkPromptsAvailable() const noexcept; bool RepositionCursorWithMouseAvailable() const noexcept; @@ -117,7 +116,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_profile, AltGrAliasing); OBSERVABLE_PROJECTED_SETTING(_profile, BellStyle); OBSERVABLE_PROJECTED_SETTING(_profile, Elevate); - OBSERVABLE_PROJECTED_SETTING(_profile, VtPassthrough); OBSERVABLE_PROJECTED_SETTING(_profile, ReloadEnvironmentVariables); OBSERVABLE_PROJECTED_SETTING(_profile, RightClickContextMenu); OBSERVABLE_PROJECTED_SETTING(_profile, ShowMarks); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index 8dcd35f4bd9..135182a4fbf 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -75,7 +75,6 @@ namespace Microsoft.Terminal.Settings.Editor Boolean ShowUnfocusedAppearance { get; }; AppearanceViewModel UnfocusedAppearance { get; }; - Boolean VtPassthroughAvailable { get; }; Boolean ShowMarksAvailable { get; }; Boolean AutoMarkPromptsAvailable { get; }; Boolean RepositionCursorWithMouseAvailable { get; }; @@ -111,7 +110,6 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AltGrAliasing); OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.BellStyle, BellStyle); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, Elevate); - OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, VtPassthrough); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RightClickContextMenu); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ShowMarks); diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml index f1e3832f85c..add8fb2cf77 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -117,16 +117,6 @@ - - - - - When enabled, the Terminal will generate a new environment block when creating new tabs or panes with this profile. When disabled, the tab/pane will instead inherit the variables the Terminal was started with. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Enable experimental virtual terminal passthrough - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Display a menu on right-click This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index b4c678fd997..cde90f910b1 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -93,7 +93,6 @@ Author(s): X(bool, RightClickContextMenu, "experimental.rightClickContextMenu", false) \ X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ X(bool, Elevate, "elevate", false) \ - X(bool, VtPassthrough, "experimental.connection.passthroughMode", false) \ X(bool, AutoMarkPrompts, "experimental.autoMarkPrompts", false) \ X(bool, ShowMarks, "experimental.showMarksOnScrollbar", false) \ X(bool, RepositionCursorWithMouse, "experimental.repositionCursorWithMouse", false) \ diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 9023fd16edb..2d6d1ab609c 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -59,7 +59,6 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.ScrollbarState, ScrollState); INHERITABLE_PROFILE_SETTING(String, Padding); INHERITABLE_PROFILE_SETTING(String, Commandline); - INHERITABLE_PROFILE_SETTING(Boolean, VtPassthrough); INHERITABLE_PROFILE_SETTING(String, StartingDirectory); String EvaluatedStartingDirectory { get; }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 682d79e19c7..6f1d2932699 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -297,7 +297,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _Padding = profile.Padding(); _Commandline = profile.Commandline(); - _VtPassthrough = profile.VtPassthrough(); _StartingDirectory = profile.EvaluatedStartingDirectory(); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 99e3b2d120e..d3410ba2628 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -95,7 +95,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, bool, FocusFollowMouse, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, TrimBlockSelection, true); INHERITABLE_SETTING(Model::TerminalSettings, bool, DetectURLs, true); - INHERITABLE_SETTING(Model::TerminalSettings, bool, VtPassthrough, false); INHERITABLE_SETTING(Model::TerminalSettings, Windows::Foundation::IReference, TabColor, nullptr); diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index faf5fc6a756..a882cd8b0cf 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -48,7 +48,6 @@ X(bool, ForceVTInput, false) \ X(winrt::hstring, StartingTitle) \ X(bool, DetectURLs, true) \ - X(bool, VtPassthrough, false) \ X(bool, AutoMarkPrompts) \ X(bool, RepositionCursorWithMouse, false) diff --git a/src/features.xml b/src/features.xml index 60191f62286..8fbf160a8c7 100644 --- a/src/features.xml +++ b/src/features.xml @@ -86,28 +86,6 @@ - - Feature_VtPassthroughMode - Enables passthrough option per profile in Terminal and ConPTY ability to use passthrough API dispatch engine - AlwaysDisabled - - - Dev - Canary - Preview - - - - - Feature_VtPassthroughModeSettingInUI - Enables the setting gated by Feature_VtPassthroughMode to appear in the UI - AlwaysDisabled - - Dev - - - - Feature_ScrollbarMarks Enables the experimental scrollbar marks feature. diff --git a/src/host/ConsoleArguments.cpp b/src/host/ConsoleArguments.cpp index 3621bca387f..6a22e8170a6 100644 --- a/src/host/ConsoleArguments.cpp +++ b/src/host/ConsoleArguments.cpp @@ -23,7 +23,6 @@ const std::wstring_view ConsoleArguments::RESIZE_QUIRK = L"--resizeQuirk"; const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature"; const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty"; const std::wstring_view ConsoleArguments::COM_SERVER_ARG = L"-Embedding"; -const std::wstring_view ConsoleArguments::PASSTHROUGH_ARG = L"--passthrough"; // NOTE: Thinking about adding more commandline args that control conpty, for // the Terminal? Make sure you add them to the commandline in // ConsoleEstablishHandoff. We use that to initialize the ConsoleArguments for a @@ -467,12 +466,6 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In s_ConsumeArg(args, i); hr = S_OK; } - else if (arg == PASSTHROUGH_ARG) - { - _passthroughMode = true; - s_ConsumeArg(args, i); - hr = S_OK; - } else if (arg.substr(0, FILEPATH_LEADER_PREFIX.length()) == FILEPATH_LEADER_PREFIX) { // beginning of command line -- includes file path @@ -602,11 +595,6 @@ bool ConsoleArguments::ShouldRunAsComServer() const return _runAsComServer; } -bool ConsoleArguments::IsPassthroughMode() const noexcept -{ - return _passthroughMode; -} - HANDLE ConsoleArguments::GetServerHandle() const { return ULongToHandle(_serverHandle); diff --git a/src/host/ConsoleArguments.hpp b/src/host/ConsoleArguments.hpp index 8648c92ea66..926bc063924 100644 --- a/src/host/ConsoleArguments.hpp +++ b/src/host/ConsoleArguments.hpp @@ -36,7 +36,6 @@ class ConsoleArguments bool IsHeadless() const; bool ShouldCreateServerHandle() const; bool ShouldRunAsComServer() const; - bool IsPassthroughMode() const noexcept; HANDLE GetServerHandle() const; HANDLE GetVtInHandle() const; @@ -76,7 +75,6 @@ class ConsoleArguments static const std::wstring_view FEATURE_ARG; static const std::wstring_view FEATURE_PTY_ARG; static const std::wstring_view COM_SERVER_ARG; - static const std::wstring_view PASSTHROUGH_ARG; private: #ifdef UNIT_TESTING @@ -95,8 +93,7 @@ class ConsoleArguments const DWORD serverHandle, const DWORD signalHandle, const bool inheritCursor, - const bool runAsComServer, - const bool passthroughMode) : + const bool runAsComServer) : _commandline(commandline), _clientCommandline(clientCommandline), _vtInHandle(vtInHandle), @@ -112,8 +109,7 @@ class ConsoleArguments _signalHandle(signalHandle), _inheritCursor(inheritCursor), _resizeQuirk(false), - _runAsComServer{ runAsComServer }, - _passthroughMode{ passthroughMode } + _runAsComServer{ runAsComServer } { } #endif @@ -135,7 +131,6 @@ class ConsoleArguments short _width; short _height; - bool _passthroughMode{ false }; bool _runAsComServer; bool _createServerHandle; DWORD _serverHandle; @@ -191,7 +186,6 @@ namespace WEX L"Signal Handle: '0x%x'\r\n", L"Inherit Cursor: '%ws'\r\n", L"Run As Com Server: '%ws'\r\n", - L"Passthrough Mode: '%ws'\r\n", ci.GetClientCommandline().c_str(), s_ToBoolString(ci.HasVtHandles()), ci.GetVtInHandle(), @@ -206,8 +200,7 @@ namespace WEX s_ToBoolString(ci.HasSignalHandle()), ci.GetSignalHandle(), s_ToBoolString(ci.GetInheritCursor()), - s_ToBoolString(ci.ShouldRunAsComServer()), - s_ToBoolString(ci.IsPassthroughMode())); + s_ToBoolString(ci.ShouldRunAsComServer())); } private: @@ -237,8 +230,7 @@ namespace WEX expected.HasSignalHandle() == actual.HasSignalHandle() && expected.GetSignalHandle() == actual.GetSignalHandle() && expected.GetInheritCursor() == actual.GetInheritCursor() && - expected.ShouldRunAsComServer() == actual.ShouldRunAsComServer() && - expected.IsPassthroughMode() == actual.IsPassthroughMode(); + expected.ShouldRunAsComServer() == actual.ShouldRunAsComServer(); } static bool AreSame(const ConsoleArguments& expected, const ConsoleArguments& actual) @@ -264,8 +256,7 @@ namespace WEX object.GetServerHandle() == 0 && (object.GetSignalHandle() == 0 || object.GetSignalHandle() == INVALID_HANDLE_VALUE) && !object.GetInheritCursor() && - !object.ShouldRunAsComServer() && - !object.IsPassthroughMode(); + !object.ShouldRunAsComServer(); } }; } diff --git a/src/host/VtApiRoutines.cpp b/src/host/VtApiRoutines.cpp deleted file mode 100644 index fe7d0b7060e..00000000000 --- a/src/host/VtApiRoutines.cpp +++ /dev/null @@ -1,810 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "VtApiRoutines.h" -#include "../interactivity/inc/ServiceLocator.hpp" -#include "../types/inc/convert.hpp" - -using namespace Microsoft::Console::Interactivity; - -// When someone attempts to use the console APIs to do a "read back" -// of the console buffer, we have to give them **something**. -// These two structures are just some gaudy-colored replacement character -// text to give them data but represent they've done something that cannot -// be supported under VT passthrough mode. -// ---- -// They can't be supported because in passthrough we maintain no internal -// buffer to answer these questions, and there is no VT sequence that lets -// us query the final terminal's buffer state. Even if a VT sequence did exist -// (and we personally believe it shouldn't), there's a possibility that it would -// read a massive amount of data and cause severe perf issues as applications coded -// to this old API are likely leaning on it heavily and asking for this data in a -// loop via VT would be a nightmare of parsing and formatting and over-the-wire transmission. - -static constexpr CHAR_INFO s_readBackUnicode{ - { UNICODE_REPLACEMENT }, - FOREGROUND_INTENSITY | FOREGROUND_RED | BACKGROUND_GREEN -}; - -static constexpr CHAR_INFO s_readBackAscii{ - { L'?' }, - FOREGROUND_INTENSITY | FOREGROUND_RED | BACKGROUND_GREEN -}; - -VtApiRoutines::VtApiRoutines() : - m_inputCodepage(ServiceLocator::LocateGlobals().getConsoleInformation().CP), - m_outputCodepage(ServiceLocator::LocateGlobals().getConsoleInformation().OutputCP), - m_inputMode(), - m_outputMode(), - m_pUsualRoutines(), - m_pVtEngine(), - m_listeningForDSR(false) -{ -} - -#pragma warning(push) -#pragma warning(disable : 4100) // unreferenced param - -void VtApiRoutines::GetConsoleInputCodePageImpl(ULONG& codepage) noexcept -{ - codepage = m_inputCodepage; - return; -} - -void VtApiRoutines::GetConsoleOutputCodePageImpl(ULONG& codepage) noexcept -{ - codepage = m_outputCodepage; - return; -} - -void VtApiRoutines::GetConsoleInputModeImpl(InputBuffer& context, - ULONG& mode) noexcept -{ - mode = m_inputMode; - return; -} - -void VtApiRoutines::GetConsoleOutputModeImpl(SCREEN_INFORMATION& context, - ULONG& mode) noexcept -{ - mode = m_outputMode; - return; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleInputModeImpl(InputBuffer& context, - const ULONG mode) noexcept -{ - m_inputMode = mode; - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleOutputModeImpl(SCREEN_INFORMATION& context, - const ULONG Mode) noexcept -{ - m_outputMode = Mode; - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::GetNumberOfConsoleInputEventsImpl(const InputBuffer& context, - ULONG& events) noexcept -{ - return m_pUsualRoutines->GetNumberOfConsoleInputEventsImpl(context, events); -} - -void VtApiRoutines::_SynchronizeCursor(std::unique_ptr& waiter) noexcept -{ - // If we're about to tell the caller to wait, let's synchronize the cursor we have with what - // the terminal is presenting in case there's a cooked read going on. - // TODO GH#10001: we only need to do this in cooked read mode. - if (waiter) - { - m_listeningForDSR = true; - (void)m_pVtEngine->_ListenForDSR(); - (void)m_pVtEngine->RequestCursor(); - } -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleInputImpl( - IConsoleInputObject& context, - InputEventQueue& outEvents, - const size_t eventReadCount, - INPUT_READ_HANDLE_DATA& readHandleState, - const bool IsUnicode, - const bool IsPeek, - std::unique_ptr& waiter) noexcept -{ - const auto hr = m_pUsualRoutines->GetConsoleInputImpl(context, outEvents, eventReadCount, readHandleState, IsUnicode, IsPeek, waiter); - _SynchronizeCursor(waiter); - return hr; -} - -[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleImpl(IConsoleInputObject& context, - std::span buffer, - size_t& written, - std::unique_ptr& waiter, - const std::wstring_view initialData, - const std::wstring_view exeName, - INPUT_READ_HANDLE_DATA& readHandleState, - const bool IsUnicode, - const HANDLE clientHandle, - const DWORD controlWakeupMask, - DWORD& controlKeyState) noexcept -{ - const auto hr = m_pUsualRoutines->ReadConsoleImpl(context, buffer, written, waiter, initialData, exeName, readHandleState, IsUnicode, clientHandle, controlWakeupMask, controlKeyState); - // If we're about to tell the caller to wait, let's synchronize the cursor we have with what - // the terminal is presenting in case there's a cooked read going on. - // TODO GH10001: we only need to do this in cooked read mode. - if (clientHandle) - { - m_listeningForDSR = true; - (void)m_pVtEngine->_ListenForDSR(); - (void)m_pVtEngine->RequestCursor(); - } - return hr; -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleAImpl(IConsoleOutputObject& context, - const std::string_view buffer, - size_t& read, - bool requiresVtQuirk, - std::unique_ptr& waiter) noexcept -{ - if (CP_UTF8 == m_outputCodepage) - { - (void)m_pVtEngine->WriteTerminalUtf8(buffer); - } - else - { - (void)m_pVtEngine->WriteTerminalW(ConvertToW(m_outputCodepage, buffer)); - } - - (void)m_pVtEngine->_Flush(); - read = buffer.size(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleWImpl(IConsoleOutputObject& context, - const std::wstring_view buffer, - size_t& read, - bool requiresVtQuirk, - std::unique_ptr& waiter) noexcept -{ - (void)m_pVtEngine->WriteTerminalW(buffer); - (void)m_pVtEngine->_Flush(); - read = buffer.size(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleLangIdImpl(LANGID& langId) noexcept -{ - return m_pUsualRoutines->GetConsoleLangIdImpl(langId); -} - -[[nodiscard]] HRESULT VtApiRoutines::FillConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, - const WORD attribute, - const size_t lengthToWrite, - const til::point startingCoordinate, - size_t& cellsModified) noexcept -{ - (void)m_pVtEngine->_CursorPosition(startingCoordinate); - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute), true); - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute >> 4), false); - (void)m_pVtEngine->_WriteFill(lengthToWrite, s_readBackAscii.Char.AsciiChar); - (void)m_pVtEngine->_Flush(); - cellsModified = lengthToWrite; - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::FillConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, - const char character, - const size_t lengthToWrite, - const til::point startingCoordinate, - size_t& cellsModified) noexcept -{ - // I mean... if you get your jollies by using UTF8 for single byte codepoints... - // we may as well skip a lot of conversion work and just write it out. - if (m_outputCodepage == CP_UTF8 && character <= 0x7F) - { - (void)m_pVtEngine->_CursorPosition(startingCoordinate); - (void)m_pVtEngine->_WriteFill(lengthToWrite, character); - (void)m_pVtEngine->_Flush(); - cellsModified = lengthToWrite; - return S_OK; - } - else - { - const auto wstr = ConvertToW(m_outputCodepage, std::string_view{ &character, 1 }); - return FillConsoleOutputCharacterWImpl(OutContext, wstr.front(), lengthToWrite, startingCoordinate, cellsModified); - } -} - -[[nodiscard]] HRESULT VtApiRoutines::FillConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, - const wchar_t character, - const size_t lengthToWrite, - const til::point startingCoordinate, - size_t& cellsModified, - const bool enablePowershellShim) noexcept -{ - (void)m_pVtEngine->_CursorPosition(startingCoordinate); - const std::wstring_view sv{ &character, 1 }; - - // TODO GH10001: horrible. it'll WC2MB over and over...we should do that once then emit... and then rep... - // TODO GH10001: there's probably an optimization for if ((character & 0x7F) == character) --> call the UTF8 one. - for (size_t i = 0; i < lengthToWrite; ++i) - { - (void)m_pVtEngine->WriteTerminalW(sv); - } - - (void)m_pVtEngine->_Flush(); - cellsModified = lengthToWrite; - return S_OK; -} - -//// Process based. Restrict in protocol side? -//HRESULT GenerateConsoleCtrlEventImpl(const ULONG ProcessGroupFilter, -// const ULONG ControlEvent); - -void VtApiRoutines::SetConsoleActiveScreenBufferImpl(SCREEN_INFORMATION& newContext) noexcept -{ - return; -} - -void VtApiRoutines::FlushConsoleInputBuffer(InputBuffer& context) noexcept -{ - m_pUsualRoutines->FlushConsoleInputBuffer(context); -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleInputCodePageImpl(const ULONG codepage) noexcept -{ - m_inputCodepage = codepage; - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleOutputCodePageImpl(const ULONG codepage) noexcept -{ - m_outputCodepage = codepage; - return S_OK; -} - -void VtApiRoutines::GetConsoleCursorInfoImpl(const SCREEN_INFORMATION& context, - ULONG& size, - bool& isVisible) noexcept -{ - // TODO GH10001: good luck capturing this out of the input buffer when it comes back in. - //m_pVtEngine->RequestCursor(); - return; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleCursorInfoImpl(SCREEN_INFORMATION& context, - const ULONG size, - const bool isVisible) noexcept -{ - isVisible ? (void)m_pVtEngine->_ShowCursor() : (void)m_pVtEngine->_HideCursor(); - (void)m_pVtEngine->_Flush(); - return S_OK; -} - -//// driver will pare down for non-Ex method -void VtApiRoutines::GetConsoleScreenBufferInfoExImpl(const SCREEN_INFORMATION& context, - CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept -{ - // TODO GH10001: this is technically full of potentially incorrect data. do we care? should we store it in here with set? - return m_pUsualRoutines->GetConsoleScreenBufferInfoExImpl(context, data); -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleScreenBufferInfoExImpl(SCREEN_INFORMATION& context, - const CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept -{ - (void)m_pVtEngine->_ResizeWindow(data.srWindow.Right - data.srWindow.Left, data.srWindow.Bottom - data.srWindow.Top); - (void)m_pVtEngine->_CursorPosition(til::wrap_coord(data.dwCursorPosition)); - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(data.wAttributes), true); - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(data.wAttributes >> 4), false); - //color table? - // popup attributes... hold internally? - // TODO GH10001: popups are gonna erase the stuff behind them... deal with that somehow. - (void)m_pVtEngine->_Flush(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleScreenBufferSizeImpl(SCREEN_INFORMATION& context, - const til::size size) noexcept -{ - // Don't transmit. The terminal figures out its own buffer size. - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleCursorPositionImpl(SCREEN_INFORMATION& context, - const til::point position) noexcept -{ - if (m_listeningForDSR) - { - context.GetActiveBuffer().GetTextBuffer().GetCursor().SetPosition(position); - m_pVtEngine->SetTerminalCursorTextPosition(position); - } - else - { - (void)m_pVtEngine->_CursorPosition(position); - (void)m_pVtEngine->_Flush(); - } - return S_OK; -} - -void VtApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& context, - til::size& size) noexcept -{ - m_pUsualRoutines->GetLargestConsoleWindowSizeImpl(context, size); // This is likely super weird but not weirder than existing ConPTY answers. - return; -} - -[[nodiscard]] HRESULT VtApiRoutines::ScrollConsoleScreenBufferAImpl(SCREEN_INFORMATION& context, - const til::inclusive_rect& source, - const til::point target, - std::optional clip, - const char fillCharacter, - const WORD fillAttribute) noexcept -{ - // TODO GH10001: Use DECCRA - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::ScrollConsoleScreenBufferWImpl(SCREEN_INFORMATION& context, - const til::inclusive_rect& source, - const til::point target, - std::optional clip, - const wchar_t fillCharacter, - const WORD fillAttribute, - const bool enableCmdShim) noexcept -{ - // TODO GH10001: Use DECCRA - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleTextAttributeImpl(SCREEN_INFORMATION& context, - const WORD attribute) noexcept -{ - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute), true); - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute >> 4), false); - (void)m_pVtEngine->_Flush(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleWindowInfoImpl(SCREEN_INFORMATION& context, - const bool isAbsolute, - const til::inclusive_rect& windowRect) noexcept -{ - (void)m_pVtEngine->_ResizeWindow(windowRect.right - windowRect.left + 1, windowRect.bottom - windowRect.top + 1); - (void)m_pVtEngine->_Flush(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputAttributeImpl(const SCREEN_INFORMATION& context, - const til::point origin, - std::span buffer, - size_t& written) noexcept -{ - std::fill_n(buffer.data(), buffer.size(), s_readBackUnicode.Attributes); // should be same as the ascii one. - written = buffer.size(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputCharacterAImpl(const SCREEN_INFORMATION& context, - const til::point origin, - std::span buffer, - size_t& written) noexcept -{ - std::fill_n(buffer.data(), buffer.size(), s_readBackAscii.Char.AsciiChar); - written = buffer.size(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputCharacterWImpl(const SCREEN_INFORMATION& context, - const til::point origin, - std::span buffer, - size_t& written) noexcept -{ - std::fill_n(buffer.data(), buffer.size(), s_readBackUnicode.Char.UnicodeChar); - written = buffer.size(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleInputAImpl(InputBuffer& context, - const std::span buffer, - size_t& written, - const bool append) noexcept -{ - return m_pUsualRoutines->WriteConsoleInputAImpl(context, buffer, written, append); -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleInputWImpl(InputBuffer& context, - const std::span buffer, - size_t& written, - const bool append) noexcept -{ - return m_pUsualRoutines->WriteConsoleInputWImpl(context, buffer, written, append); -} - -extern HRESULT _ConvertCellsToWInplace(const UINT codepage, - std::span buffer, - const Viewport& rectangle) noexcept; - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputAImpl(SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& requestRectangle, - Microsoft::Console::Types::Viewport& writtenRectangle) noexcept -{ - // No UTF8 optimization because the entire `CHAR_INFO` grid system doesn't make sense for UTF-8 - // with up to 4 bytes per cell...or more! - - RETURN_IF_FAILED(_ConvertCellsToWInplace(m_outputCodepage, buffer, requestRectangle)); - return WriteConsoleOutputWImpl(context, buffer, requestRectangle, writtenRectangle); -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputWImpl(SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& requestRectangle, - Microsoft::Console::Types::Viewport& writtenRectangle) noexcept -{ - auto cursor = requestRectangle.Origin(); - - const size_t width = requestRectangle.Width(); - size_t pos = 0; - - while (pos < buffer.size()) - { - (void)m_pVtEngine->_CursorPosition(cursor); - - const auto subspan = buffer.subspan(pos, width); - - for (const auto& ci : subspan) - { - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(ci.Attributes), true); - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(ci.Attributes >> 4), false); - (void)m_pVtEngine->WriteTerminalW(std::wstring_view{ &ci.Char.UnicodeChar, 1 }); - } - - ++cursor.y; - pos += width; - } - - (void)m_pVtEngine->_Flush(); - - //TODO GH10001: trim to buffer size? - writtenRectangle = requestRectangle; - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, - const std::span attrs, - const til::point target, - size_t& used) noexcept -{ - (void)m_pVtEngine->_CursorPosition(target); - - for (const auto& attr : attrs) - { - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attr), true); - (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attr >> 4), false); - (void)m_pVtEngine->WriteTerminalUtf8(std::string_view{ &s_readBackAscii.Char.AsciiChar, 1 }); - } - - (void)m_pVtEngine->_Flush(); - - used = attrs.size(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, - const std::string_view text, - const til::point target, - size_t& used) noexcept -{ - if (m_outputCodepage == CP_UTF8) - { - (void)m_pVtEngine->_CursorPosition(target); - (void)m_pVtEngine->WriteTerminalUtf8(text); - (void)m_pVtEngine->_Flush(); - return S_OK; - } - else - { - return WriteConsoleOutputCharacterWImpl(OutContext, ConvertToW(m_outputCodepage, text), target, used); - } -} - -[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, - const std::wstring_view text, - const til::point target, - size_t& used) noexcept -{ - (void)m_pVtEngine->_CursorPosition(target); - (void)m_pVtEngine->WriteTerminalW(text); - (void)m_pVtEngine->_Flush(); - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputAImpl(const SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& sourceRectangle, - Microsoft::Console::Types::Viewport& readRectangle) noexcept -{ - std::fill_n(buffer.data(), buffer.size(), s_readBackAscii); - // TODO GH10001: do we need to constrict readRectangle to within the known buffer size... probably. - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputWImpl(const SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& sourceRectangle, - Microsoft::Console::Types::Viewport& readRectangle) noexcept -{ - std::fill_n(buffer.data(), buffer.size(), s_readBackUnicode); - // TODO GH10001: do we need to constrict readRectangle to within the known buffer size... probably. - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleTitleAImpl(std::span title, - size_t& written, - size_t& needed) noexcept -{ - written = 0; - needed = 0; - - if (!title.empty()) - { - title.front() = ANSI_NULL; - } - - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleTitleWImpl(std::span title, - size_t& written, - size_t& needed) noexcept -{ - written = 0; - needed = 0; - - if (!title.empty()) - { - title.front() = UNICODE_NULL; - } - - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleOriginalTitleAImpl(std::span title, - size_t& written, - size_t& needed) noexcept -{ - written = 0; - needed = 0; - - if (!title.empty()) - { - title.front() = ANSI_NULL; - } - - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleOriginalTitleWImpl(std::span title, - size_t& written, - size_t& needed) noexcept -{ - written = 0; - needed = 0; - - if (!title.empty()) - { - title.front() = UNICODE_NULL; - } - - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleTitleAImpl(const std::string_view title) noexcept -{ - return SetConsoleTitleWImpl(ConvertToW(m_inputCodepage, title)); -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleTitleWImpl(const std::wstring_view title) noexcept -{ - (void)m_pVtEngine->UpdateTitle(title); - (void)m_pVtEngine->_Flush(); - return S_OK; -} - -void VtApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept -{ - buttons = 2; - return; -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleFontSizeImpl(const SCREEN_INFORMATION& context, - const DWORD index, - til::size& size) noexcept -{ - size.width = 8; - size.height = 12; - return S_OK; -} - -//// driver will pare down for non-Ex method -[[nodiscard]] HRESULT VtApiRoutines::GetCurrentConsoleFontExImpl(const SCREEN_INFORMATION& context, - const bool isForMaximumWindowSize, - CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept -{ - return S_OK; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleDisplayModeImpl(SCREEN_INFORMATION& context, - const ULONG flags, - til::size& newSize) noexcept -{ - return S_OK; -} - -void VtApiRoutines::GetConsoleDisplayModeImpl(ULONG& flags) noexcept -{ - flags = 0; - return; -} - -[[nodiscard]] HRESULT VtApiRoutines::AddConsoleAliasAImpl(const std::string_view source, - const std::string_view target, - const std::string_view exeName) noexcept -{ - return m_pUsualRoutines->AddConsoleAliasAImpl(source, target, exeName); -} - -[[nodiscard]] HRESULT VtApiRoutines::AddConsoleAliasWImpl(const std::wstring_view source, - const std::wstring_view target, - const std::wstring_view exeName) noexcept -{ - return m_pUsualRoutines->AddConsoleAliasWImpl(source, target, exeName); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasAImpl(const std::string_view source, - std::span target, - size_t& written, - const std::string_view exeName) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasAImpl(source, target, written, exeName); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasWImpl(const std::wstring_view source, - std::span target, - size_t& written, - const std::wstring_view exeName) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasWImpl(source, target, written, exeName); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesLengthAImpl(const std::string_view exeName, - size_t& bufferRequired) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasesLengthAImpl(exeName, bufferRequired); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesLengthWImpl(const std::wstring_view exeName, - size_t& bufferRequired) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasesLengthWImpl(exeName, bufferRequired); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesLengthAImpl(size_t& bufferRequired) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasExesLengthAImpl(bufferRequired); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesLengthWImpl(size_t& bufferRequired) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasExesLengthWImpl(bufferRequired); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesAImpl(const std::string_view exeName, - std::span alias, - size_t& written) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasesAImpl(exeName, alias, written); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesWImpl(const std::wstring_view exeName, - std::span alias, - size_t& written) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasesWImpl(exeName, alias, written); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesAImpl(std::span aliasExes, - size_t& written) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasExesAImpl(aliasExes, written); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesWImpl(std::span aliasExes, - size_t& written) noexcept -{ - return m_pUsualRoutines->GetConsoleAliasExesWImpl(aliasExes, written); -} - -[[nodiscard]] HRESULT VtApiRoutines::ExpungeConsoleCommandHistoryAImpl(const std::string_view exeName) noexcept -{ - return m_pUsualRoutines->ExpungeConsoleCommandHistoryAImpl(exeName); -} - -[[nodiscard]] HRESULT VtApiRoutines::ExpungeConsoleCommandHistoryWImpl(const std::wstring_view exeName) noexcept -{ - return m_pUsualRoutines->ExpungeConsoleCommandHistoryWImpl(exeName); -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleNumberOfCommandsAImpl(const std::string_view exeName, - const size_t numberOfCommands) noexcept -{ - return m_pUsualRoutines->SetConsoleNumberOfCommandsAImpl(exeName, numberOfCommands); -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleNumberOfCommandsWImpl(const std::wstring_view exeName, - const size_t numberOfCommands) noexcept -{ - return m_pUsualRoutines->SetConsoleNumberOfCommandsWImpl(exeName, numberOfCommands); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryLengthAImpl(const std::string_view exeName, - size_t& length) noexcept -{ - return m_pUsualRoutines->GetConsoleCommandHistoryLengthAImpl(exeName, length); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryLengthWImpl(const std::wstring_view exeName, - size_t& length) noexcept -{ - return m_pUsualRoutines->GetConsoleCommandHistoryLengthWImpl(exeName, length); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryAImpl(const std::string_view exeName, - std::span commandHistory, - size_t& written) noexcept -{ - return m_pUsualRoutines->GetConsoleCommandHistoryAImpl(exeName, commandHistory, written); -} - -[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryWImpl(const std::wstring_view exeName, - std::span commandHistory, - size_t& written) noexcept -{ - return m_pUsualRoutines->GetConsoleCommandHistoryWImpl(exeName, commandHistory, written); -} - -void VtApiRoutines::GetConsoleWindowImpl(HWND& hwnd) noexcept -{ - hwnd = ServiceLocator::LocatePseudoWindow(); - return; -} - -void VtApiRoutines::GetConsoleSelectionInfoImpl(CONSOLE_SELECTION_INFO& consoleSelectionInfo) noexcept -{ - consoleSelectionInfo = { 0 }; - return; -} - -void VtApiRoutines::GetConsoleHistoryInfoImpl(CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept -{ - m_pUsualRoutines->GetConsoleHistoryInfoImpl(consoleHistoryInfo); - return; -} - -[[nodiscard]] HRESULT VtApiRoutines::SetConsoleHistoryInfoImpl(const CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept -{ - return m_pUsualRoutines->SetConsoleHistoryInfoImpl(consoleHistoryInfo); -} - -[[nodiscard]] HRESULT VtApiRoutines::SetCurrentConsoleFontExImpl(IConsoleOutputObject& context, - const bool isForMaximumWindowSize, - const CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept -{ - return S_OK; -} - -#pragma warning(pop) diff --git a/src/host/VtApiRoutines.h b/src/host/VtApiRoutines.h deleted file mode 100644 index 7dc77f78b09..00000000000 --- a/src/host/VtApiRoutines.h +++ /dev/null @@ -1,366 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- VtApiRoutines.h - -Abstract: -- This file defines the interface to respond to all API calls by using VT on behalf of the client - -Author: -- Michael Niksa (miniksa) 26-Jul-2021 - -Revision History: -- Adapted from original items in srvinit.cpp, getset.cpp, directio.cpp, stream.cpp ---*/ - -#pragma once - -#include "../server/IApiRoutines.h" -#include "../renderer/vt/Xterm256Engine.hpp" - -class VtApiRoutines : public IApiRoutines -{ -public: - VtApiRoutines(); - -#pragma region ObjectManagement - /*HRESULT CreateInitialObjects(_Out_ InputBuffer** const ppInputObject, - _Out_ SCREEN_INFORMATION** const ppOutputObject); - */ - -#pragma endregion - -#pragma region L1 - void GetConsoleInputCodePageImpl(ULONG& codepage) noexcept override; - - void GetConsoleOutputCodePageImpl(ULONG& codepage) noexcept override; - - void GetConsoleInputModeImpl(InputBuffer& context, - ULONG& mode) noexcept override; - - void GetConsoleOutputModeImpl(SCREEN_INFORMATION& context, - ULONG& mode) noexcept override; - - [[nodiscard]] HRESULT SetConsoleInputModeImpl(InputBuffer& context, - const ULONG mode) noexcept override; - - [[nodiscard]] HRESULT SetConsoleOutputModeImpl(SCREEN_INFORMATION& context, - const ULONG Mode) noexcept override; - - [[nodiscard]] HRESULT GetNumberOfConsoleInputEventsImpl(const InputBuffer& context, - ULONG& events) noexcept override; - - [[nodiscard]] HRESULT GetConsoleInputImpl(IConsoleInputObject& context, - InputEventQueue& outEvents, - const size_t eventReadCount, - INPUT_READ_HANDLE_DATA& readHandleState, - const bool IsUnicode, - const bool IsPeek, - std::unique_ptr& waiter) noexcept override; - - [[nodiscard]] HRESULT ReadConsoleImpl(IConsoleInputObject& context, - std::span buffer, - size_t& written, - std::unique_ptr& waiter, - const std::wstring_view initialData, - const std::wstring_view exeName, - INPUT_READ_HANDLE_DATA& readHandleState, - const bool IsUnicode, - const HANDLE clientHandle, - const DWORD controlWakeupMask, - DWORD& controlKeyState) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleAImpl(IConsoleOutputObject& context, - const std::string_view buffer, - size_t& read, - bool requiresVtQuirk, - std::unique_ptr& waiter) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleWImpl(IConsoleOutputObject& context, - const std::wstring_view buffer, - size_t& read, - bool requiresVtQuirk, - std::unique_ptr& waiter) noexcept override; - -#pragma region ThreadCreationInfo - [[nodiscard]] HRESULT GetConsoleLangIdImpl(LANGID& langId) noexcept override; -#pragma endregion - -#pragma endregion - -#pragma region L2 - - [[nodiscard]] HRESULT FillConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, - const WORD attribute, - const size_t lengthToWrite, - const til::point startingCoordinate, - size_t& cellsModified) noexcept override; - - [[nodiscard]] HRESULT FillConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, - const char character, - const size_t lengthToWrite, - const til::point startingCoordinate, - size_t& cellsModified) noexcept override; - - [[nodiscard]] HRESULT FillConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, - const wchar_t character, - const size_t lengthToWrite, - const til::point startingCoordinate, - size_t& cellsModified, - const bool enablePowershellShim = false) noexcept override; - - //// Process based. Restrict in protocol side? - //HRESULT GenerateConsoleCtrlEventImpl(const ULONG ProcessGroupFilter, - // const ULONG ControlEvent); - - void SetConsoleActiveScreenBufferImpl(SCREEN_INFORMATION& newContext) noexcept override; - - void FlushConsoleInputBuffer(InputBuffer& context) noexcept override; - - [[nodiscard]] HRESULT SetConsoleInputCodePageImpl(const ULONG codepage) noexcept override; - - [[nodiscard]] HRESULT SetConsoleOutputCodePageImpl(const ULONG codepage) noexcept override; - - void GetConsoleCursorInfoImpl(const SCREEN_INFORMATION& context, - ULONG& size, - bool& isVisible) noexcept override; - - [[nodiscard]] HRESULT SetConsoleCursorInfoImpl(SCREEN_INFORMATION& context, - const ULONG size, - const bool isVisible) noexcept override; - - //// driver will pare down for non-Ex method - void GetConsoleScreenBufferInfoExImpl(const SCREEN_INFORMATION& context, - CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept override; - - [[nodiscard]] HRESULT SetConsoleScreenBufferInfoExImpl(SCREEN_INFORMATION& context, - const CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept override; - - [[nodiscard]] HRESULT SetConsoleScreenBufferSizeImpl(SCREEN_INFORMATION& context, - const til::size size) noexcept override; - - [[nodiscard]] HRESULT SetConsoleCursorPositionImpl(SCREEN_INFORMATION& context, - const til::point position) noexcept override; - - void GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& context, - til::size& size) noexcept override; - - [[nodiscard]] HRESULT ScrollConsoleScreenBufferAImpl(SCREEN_INFORMATION& context, - const til::inclusive_rect& source, - const til::point target, - std::optional clip, - const char fillCharacter, - const WORD fillAttribute) noexcept override; - - [[nodiscard]] HRESULT ScrollConsoleScreenBufferWImpl(SCREEN_INFORMATION& context, - const til::inclusive_rect& source, - const til::point target, - std::optional clip, - const wchar_t fillCharacter, - const WORD fillAttribute, - const bool enableCmdShim = false) noexcept override; - - [[nodiscard]] HRESULT SetConsoleTextAttributeImpl(SCREEN_INFORMATION& context, - const WORD attribute) noexcept override; - - [[nodiscard]] HRESULT SetConsoleWindowInfoImpl(SCREEN_INFORMATION& context, - const bool isAbsolute, - const til::inclusive_rect& windowRect) noexcept override; - - [[nodiscard]] HRESULT ReadConsoleOutputAttributeImpl(const SCREEN_INFORMATION& context, - const til::point origin, - std::span buffer, - size_t& written) noexcept override; - - [[nodiscard]] HRESULT ReadConsoleOutputCharacterAImpl(const SCREEN_INFORMATION& context, - const til::point origin, - std::span buffer, - size_t& written) noexcept override; - - [[nodiscard]] HRESULT ReadConsoleOutputCharacterWImpl(const SCREEN_INFORMATION& context, - const til::point origin, - std::span buffer, - size_t& written) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleInputAImpl(InputBuffer& context, - const std::span buffer, - size_t& written, - const bool append) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleInputWImpl(InputBuffer& context, - const std::span buffer, - size_t& written, - const bool append) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleOutputAImpl(SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& requestRectangle, - Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleOutputWImpl(SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& requestRectangle, - Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, - const std::span attrs, - const til::point target, - size_t& used) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, - const std::string_view text, - const til::point target, - size_t& used) noexcept override; - - [[nodiscard]] HRESULT WriteConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, - const std::wstring_view text, - const til::point target, - size_t& used) noexcept override; - - [[nodiscard]] HRESULT ReadConsoleOutputAImpl(const SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& sourceRectangle, - Microsoft::Console::Types::Viewport& readRectangle) noexcept override; - - [[nodiscard]] HRESULT ReadConsoleOutputWImpl(const SCREEN_INFORMATION& context, - std::span buffer, - const Microsoft::Console::Types::Viewport& sourceRectangle, - Microsoft::Console::Types::Viewport& readRectangle) noexcept override; - - [[nodiscard]] HRESULT GetConsoleTitleAImpl(std::span title, - size_t& written, - size_t& needed) noexcept override; - - [[nodiscard]] HRESULT GetConsoleTitleWImpl(std::span title, - size_t& written, - size_t& needed) noexcept override; - - [[nodiscard]] HRESULT GetConsoleOriginalTitleAImpl(std::span title, - size_t& written, - size_t& needed) noexcept override; - - [[nodiscard]] HRESULT GetConsoleOriginalTitleWImpl(std::span title, - size_t& written, - size_t& needed) noexcept override; - - [[nodiscard]] HRESULT SetConsoleTitleAImpl(const std::string_view title) noexcept override; - - [[nodiscard]] HRESULT SetConsoleTitleWImpl(const std::wstring_view title) noexcept override; - -#pragma endregion - -#pragma region L3 - void GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept override; - - [[nodiscard]] HRESULT GetConsoleFontSizeImpl(const SCREEN_INFORMATION& context, - const DWORD index, - til::size& size) noexcept override; - - //// driver will pare down for non-Ex method - [[nodiscard]] HRESULT GetCurrentConsoleFontExImpl(const SCREEN_INFORMATION& context, - const bool isForMaximumWindowSize, - CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept override; - - [[nodiscard]] HRESULT SetConsoleDisplayModeImpl(SCREEN_INFORMATION& context, - const ULONG flags, - til::size& newSize) noexcept override; - - void GetConsoleDisplayModeImpl(ULONG& flags) noexcept override; - - [[nodiscard]] HRESULT AddConsoleAliasAImpl(const std::string_view source, - const std::string_view target, - const std::string_view exeName) noexcept override; - - [[nodiscard]] HRESULT AddConsoleAliasWImpl(const std::wstring_view source, - const std::wstring_view target, - const std::wstring_view exeName) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasAImpl(const std::string_view source, - std::span target, - size_t& written, - const std::string_view exeName) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasWImpl(const std::wstring_view source, - std::span target, - size_t& written, - const std::wstring_view exeName) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasesLengthAImpl(const std::string_view exeName, - size_t& bufferRequired) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasesLengthWImpl(const std::wstring_view exeName, - size_t& bufferRequired) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasExesLengthAImpl(size_t& bufferRequired) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasExesLengthWImpl(size_t& bufferRequired) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasesAImpl(const std::string_view exeName, - std::span alias, - size_t& written) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasesWImpl(const std::wstring_view exeName, - std::span alias, - size_t& written) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasExesAImpl(std::span aliasExes, - size_t& written) noexcept override; - - [[nodiscard]] HRESULT GetConsoleAliasExesWImpl(std::span aliasExes, - size_t& written) noexcept override; - -#pragma region CMDext Private API - - [[nodiscard]] HRESULT ExpungeConsoleCommandHistoryAImpl(const std::string_view exeName) noexcept override; - - [[nodiscard]] HRESULT ExpungeConsoleCommandHistoryWImpl(const std::wstring_view exeName) noexcept override; - - [[nodiscard]] HRESULT SetConsoleNumberOfCommandsAImpl(const std::string_view exeName, - const size_t numberOfCommands) noexcept override; - - [[nodiscard]] HRESULT SetConsoleNumberOfCommandsWImpl(const std::wstring_view exeName, - const size_t numberOfCommands) noexcept override; - - [[nodiscard]] HRESULT GetConsoleCommandHistoryLengthAImpl(const std::string_view exeName, - size_t& length) noexcept override; - - [[nodiscard]] HRESULT GetConsoleCommandHistoryLengthWImpl(const std::wstring_view exeName, - size_t& length) noexcept override; - - [[nodiscard]] HRESULT GetConsoleCommandHistoryAImpl(const std::string_view exeName, - std::span commandHistory, - size_t& written) noexcept override; - - [[nodiscard]] HRESULT GetConsoleCommandHistoryWImpl(const std::wstring_view exeName, - std::span commandHistory, - size_t& written) noexcept override; - -#pragma endregion - - void GetConsoleWindowImpl(HWND& hwnd) noexcept override; - - void GetConsoleSelectionInfoImpl(CONSOLE_SELECTION_INFO& consoleSelectionInfo) noexcept override; - - void GetConsoleHistoryInfoImpl(CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept override; - - [[nodiscard]] HRESULT SetConsoleHistoryInfoImpl(const CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept override; - - [[nodiscard]] HRESULT SetCurrentConsoleFontExImpl(IConsoleOutputObject& context, - const bool isForMaximumWindowSize, - const CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept override; - -#pragma endregion - - IApiRoutines* m_pUsualRoutines; - UINT& m_inputCodepage; - UINT& m_outputCodepage; - ULONG m_inputMode; - ULONG m_outputMode; - bool m_listeningForDSR; - Microsoft::Console::Render::Xterm256Engine* m_pVtEngine; - -private: - void _SynchronizeCursor(std::unique_ptr& waiter) noexcept; -}; diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index f9a6b722767..3d6412add00 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -14,8 +14,6 @@ #include "input.h" // ProcessCtrlEvents #include "output.h" // CloseConsoleProcessState -#include "VtApiRoutines.h" - using namespace Microsoft::Console; using namespace Microsoft::Console::Render; using namespace Microsoft::Console::VirtualTerminal; @@ -71,7 +69,6 @@ VtIo::VtIo() : { _lookingForCursorPosition = pArgs->GetInheritCursor(); _resizeQuirk = pArgs->IsResizeQuirkEnabled(); - _passthroughMode = pArgs->IsPassthroughMode(); // If we were already given VT handles, set up the VT IO engine to use those. if (pArgs->InConptyMode()) @@ -162,26 +159,6 @@ VtIo::VtIo() : { auto xterm256Engine = std::make_unique(std::move(_hOutput), initialViewport); - if constexpr (Feature_VtPassthroughMode::IsEnabled()) - { - if (_passthroughMode) - { - auto vtapi = new VtApiRoutines(); - vtapi->m_pVtEngine = xterm256Engine.get(); - vtapi->m_pUsualRoutines = globals.api; - - xterm256Engine->SetPassthroughMode(true); - - if (_pVtInputThread) - { - auto pfnSetListenForDSR = std::bind(&VtInputThread::SetLookingForDSR, _pVtInputThread.get(), std::placeholders::_1); - xterm256Engine->SetLookingForDSRCallback(pfnSetListenForDSR); - } - - globals.api = vtapi; - } - } - _pVtRenderEngine = std::move(xterm256Engine); break; } @@ -190,10 +167,6 @@ VtIo::VtIo() : _pVtRenderEngine = std::make_unique(std::move(_hOutput), initialViewport, false); - if (_passthroughMode) - { - return E_NOTIMPL; - } break; } case VtIoMode::XTERM_ASCII: @@ -201,11 +174,6 @@ VtIo::VtIo() : _pVtRenderEngine = std::make_unique(std::move(_hOutput), initialViewport, true); - - if (_passthroughMode) - { - return E_NOTIMPL; - } break; } default: diff --git a/src/host/VtIo.hpp b/src/host/VtIo.hpp index 2ecc0d51754..7cd12036f5d 100644 --- a/src/host/VtIo.hpp +++ b/src/host/VtIo.hpp @@ -67,7 +67,6 @@ namespace Microsoft::Console::VirtualTerminal bool _lookingForCursorPosition; bool _resizeQuirk{ false }; - bool _passthroughMode{ false }; bool _closeEventSent{ false }; std::unique_ptr _pVtRenderEngine; diff --git a/src/host/host-common.vcxitems b/src/host/host-common.vcxitems index 5ae8d7a0f3b..3456180ff1f 100644 --- a/src/host/host-common.vcxitems +++ b/src/host/host-common.vcxitems @@ -45,7 +45,6 @@ - @@ -97,7 +96,6 @@ - diff --git a/src/host/lib/hostlib.vcxproj.filters b/src/host/lib/hostlib.vcxproj.filters index 0e8177d4bc4..c70ea1a4d9f 100644 --- a/src/host/lib/hostlib.vcxproj.filters +++ b/src/host/lib/hostlib.vcxproj.filters @@ -156,9 +156,6 @@ Source Files - - Source Files - @@ -311,12 +308,9 @@ Header Files - - Header Files - - \ No newline at end of file + diff --git a/src/host/sources.inc b/src/host/sources.inc index f17c5191ac8..0c6b8fd14d2 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -86,7 +86,6 @@ SOURCES = \ ..\conareainfo.cpp \ ..\conimeinfo.cpp \ ..\ConsoleArguments.cpp \ - ..\VtApiRoutines.cpp \ # ------------------------------------- diff --git a/src/host/ut_host/ConsoleArgumentsTests.cpp b/src/host/ut_host/ConsoleArgumentsTests.cpp index 2da3663b7c2..d640469f236 100644 --- a/src/host/ut_host/ConsoleArgumentsTests.cpp +++ b/src/host/ut_host/ConsoleArgumentsTests.cpp @@ -83,8 +83,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe \"this is the commandline\""; @@ -106,8 +105,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --headless \"--vtmode bar this is the commandline\""; @@ -129,8 +127,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --headless --server 0x4 this is the commandline"; @@ -152,8 +149,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0x4, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --headless\t--vtmode\txterm\tthis\tis\tthe\tcommandline"; @@ -175,8 +171,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --headless\\ foo\\ --outpipe\\ bar\\ this\\ is\\ the\\ commandline"; @@ -198,8 +193,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --headless\\\tfoo\\\t--outpipe\\\tbar\\\tthis\\\tis\\\tthe\\\tcommandline"; @@ -221,8 +215,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --vtmode a\\\\\\\\\"b c\" d e"; @@ -244,8 +237,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe this is the commandline"; @@ -267,8 +259,7 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? } @@ -295,8 +286,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe foo"; @@ -318,8 +308,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe foo -- bar"; @@ -341,8 +330,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --vtmode foo foo -- bar"; @@ -364,8 +352,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe console --vtmode foo foo -- bar"; @@ -387,8 +374,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe console --vtmode foo --outpipe foo -- bar"; @@ -410,8 +396,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --vtmode foo -- --outpipe foo bar"; @@ -433,8 +418,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --vtmode -- --headless bar"; @@ -456,8 +440,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --"; @@ -479,8 +462,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe"; @@ -502,8 +484,7 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? } @@ -530,8 +511,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --server 0x4"; @@ -553,8 +533,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe 0x4 0x8"; @@ -576,8 +555,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --server 0x4 0x8"; @@ -599,8 +577,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe 0x4 --server 0x8"; @@ -622,8 +599,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --server 0x4 --server 0x8"; @@ -645,8 +621,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe 0x4 -ForceV1"; @@ -668,8 +643,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe -ForceV1"; @@ -691,8 +665,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe 0x4 -ForceNoHandoff"; @@ -714,8 +687,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe -ForceNoHandoff"; @@ -737,8 +709,7 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? } @@ -789,8 +760,7 @@ void ConsoleArgumentsTests::CombineVtPipeHandleTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --vtmode xterm-256color"; @@ -812,8 +782,7 @@ void ConsoleArgumentsTests::CombineVtPipeHandleTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? } @@ -850,8 +819,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --width 120"; @@ -873,8 +841,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --height 30"; @@ -896,8 +863,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --width 0"; @@ -919,8 +885,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --width -1"; @@ -942,8 +907,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --width foo"; @@ -965,8 +929,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --width 2foo"; @@ -988,8 +951,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --width 65535"; @@ -1011,8 +973,7 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? } @@ -1039,8 +1000,7 @@ void ConsoleArgumentsTests::HeadlessArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --headless 0x4"; @@ -1062,8 +1022,7 @@ void ConsoleArgumentsTests::HeadlessArgTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --headless --headless"; @@ -1085,8 +1044,7 @@ void ConsoleArgumentsTests::HeadlessArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe -- foo.exe --headless"; @@ -1108,8 +1066,7 @@ void ConsoleArgumentsTests::HeadlessArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? } @@ -1140,8 +1097,7 @@ void ConsoleArgumentsTests::SignalHandleTests() 4ul, // serverHandle 8ul, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --server 0x4 --signal ASDF"; @@ -1163,8 +1119,7 @@ void ConsoleArgumentsTests::SignalHandleTests() 4ul, // serverHandle 0ul, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --signal --server 0x4"; @@ -1186,8 +1141,7 @@ void ConsoleArgumentsTests::SignalHandleTests() 0ul, // serverHandle 0ul, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? } @@ -1218,8 +1172,7 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --feature tty"; ArgTestsRunner(L"#2 Error case, pass an unsupported feature", @@ -1240,8 +1193,7 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --feature pty --feature pty"; @@ -1263,8 +1215,7 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer true); // successful parse? commandline = L"conhost.exe --feature pty --feature tty"; @@ -1286,8 +1237,7 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --feature pty --feature"; @@ -1309,8 +1259,7 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? commandline = L"conhost.exe --feature pty --feature --signal foo"; @@ -1332,7 +1281,6 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false, // runAsComServer - false), // passthroughMode + false), // runAsComServer false); // successful parse? } diff --git a/src/inc/conpty-static.h b/src/inc/conpty-static.h index 4d22048868c..1dd9a4123e2 100644 --- a/src/inc/conpty-static.h +++ b/src/inc/conpty-static.h @@ -24,7 +24,6 @@ #endif #define PSEUDOCONSOLE_RESIZE_QUIRK (2u) -#define PSEUDOCONSOLE_PASSTHROUGH_MODE (8u) CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC); CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC); diff --git a/src/renderer/vt/Xterm256Engine.hpp b/src/renderer/vt/Xterm256Engine.hpp index 9d704c34dd8..5c8cde61831 100644 --- a/src/renderer/vt/Xterm256Engine.hpp +++ b/src/renderer/vt/Xterm256Engine.hpp @@ -18,8 +18,6 @@ Author(s): #include "XtermEngine.hpp" -class VtApiRoutines; - namespace Microsoft::Console::Render { class Xterm256Engine : public XtermEngine @@ -38,8 +36,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT ManuallyClearScrollback() noexcept override; - friend class ::VtApiRoutines; - private: [[nodiscard]] HRESULT _UpdateExtendedAttrs(const TextAttribute& textAttributes) noexcept; [[nodiscard]] HRESULT _UpdateHyperlinkAttr(const TextAttribute& textAttributes, diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index 5bb6b7d694d..9749bf63d32 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -475,19 +475,6 @@ void VtEngine::SetResizeQuirk(const bool resizeQuirk) _resizeQuirk = resizeQuirk; } -// Method Description: -// - Configure the renderer to understand that we're operating in limited-draw -// passthrough mode. We do not need to handle full responsibility for replicating -// buffer state to the attached terminal. -// Arguments: -// - passthrough - True to turn on passthrough mode. False otherwise. -// Return Value: -// - true iff we were started with an output mode for passthrough. false otherwise. -void VtEngine::SetPassthroughMode(const bool passthrough) noexcept -{ - _passthrough = passthrough; -} - void VtEngine::SetLookingForDSRCallback(std::function pfnLooking) noexcept { _pfnSetLookingForDSR = pfnLooking; diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 7a974850afc..1a7c25316fd 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -82,7 +82,6 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0; void SetTerminalOwner(Microsoft::Console::VirtualTerminal::VtIo* const terminalOwner); void SetResizeQuirk(const bool resizeQuirk); - void SetPassthroughMode(const bool passthrough) noexcept; void SetLookingForDSRCallback(std::function pfnLooking) noexcept; void SetTerminalCursorTextPosition(const til::point coordCursor) noexcept; [[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept; diff --git a/src/server/ApiSorter.cpp b/src/server/ApiSorter.cpp index 39cf676e497..7e20b290f59 100644 --- a/src/server/ApiSorter.cpp +++ b/src/server/ApiSorter.cpp @@ -168,14 +168,25 @@ PCONSOLE_API_MSG ApiSorter::ConsoleDispatchRequest(_Inout_ PCONSOLE_API_MSG Mess Message->State.WriteOffset = Message->msgHeader.ApiDescriptorSize; Message->State.ReadOffset = Message->msgHeader.ApiDescriptorSize + sizeof(CONSOLE_MSG_HEADER); + HRESULT hr = S_OK; + try + { + hr = (*Descriptor->Routine)(Message, &ReplyPending); + } + catch (const wil::ResultException& e) + { + hr = e.GetStatusCode(); + } + catch (...) + { + hr = E_UNEXPECTED; + } + // Unfortunately, we can't be as clear-cut with our error codes as we'd like since we have some callers that take // hard dependencies on NTSTATUS codes that aren't readily expressible as an HRESULT. There's currently only one // such known code -- STATUS_BUFFER_TOO_SMALL. There's a conlibk dependency on this being returned from the console // alias API. - NTSTATUS Status = S_OK; - { - Status = (*Descriptor->Routine)(Message, &ReplyPending); - } + NTSTATUS Status = hr; if (Status != STATUS_BUFFER_TOO_SMALL) { Status = NTSTATUS_FROM_HRESULT(Status); diff --git a/src/server/IApiRoutines.h b/src/server/IApiRoutines.h index 87830795c6c..9c99136c2c3 100644 --- a/src/server/IApiRoutines.h +++ b/src/server/IApiRoutines.h @@ -28,22 +28,13 @@ typedef InputBuffer IConsoleInputObject; class INPUT_READ_HANDLE_DATA; #include "IWaitRoutine.h" -#include -#include #include "../types/inc/IInputEvent.hpp" #include "../types/inc/viewport.hpp" -class IApiRoutines +class __declspec(novtable) IApiRoutines { public: -#pragma region ObjectManagement - // TODO: 9115192 - We will need to make the objects via an interface eventually. This represents that idea. - /*virtual HRESULT CreateInitialObjects(_Out_ IConsoleInputObject** const ppInputObject, - _Out_ IConsoleOutputObject** const ppOutputObject); -*/ - -#pragma endregion - +#pragma warning(suppress : 26432) // If you define or delete any default operation in the type '...', define or delete them all (c.21). virtual ~IApiRoutines() = default; #pragma region L1 diff --git a/src/winconpty/winconpty.cpp b/src/winconpty/winconpty.cpp index 323e4f788f9..16bb278b7da 100644 --- a/src/winconpty/winconpty.cpp +++ b/src/winconpty/winconpty.cpp @@ -133,23 +133,20 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken, RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeConhostSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)); // GH4061: Ensure that the path to executable in the format is escaped so C:\Program.exe cannot collide with C:\Program Files - auto pwszFormat = L"\"%s\" --headless %s%s%s--width %hu --height %hu --signal 0x%x --server 0x%x"; // This is plenty of space to hold the formatted string wchar_t cmd[MAX_PATH]{}; const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR; const BOOL bResizeQuirk = (dwFlags & PSEUDOCONSOLE_RESIZE_QUIRK) == PSEUDOCONSOLE_RESIZE_QUIRK; - const BOOL bPassthroughMode = (dwFlags & PSEUDOCONSOLE_PASSTHROUGH_MODE) == PSEUDOCONSOLE_PASSTHROUGH_MODE; swprintf_s(cmd, MAX_PATH, - pwszFormat, + L"\"%s\" --headless %s%s--width %hd --height %hd --signal 0x%tx --server 0x%tx", _ConsoleHostPath(), bInheritCursor ? L"--inheritcursor " : L"", bResizeQuirk ? L"--resizeQuirk " : L"", - bPassthroughMode ? L"--passthrough " : L"", size.X, size.Y, - signalPipeConhostSide.get(), - serverHandle.get()); + std::bit_cast(signalPipeConhostSide.get()), + std::bit_cast(serverHandle.get())); STARTUPINFOEXW siEx{ 0 }; siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW); diff --git a/src/winconpty/winconpty.h b/src/winconpty/winconpty.h index 744ec09916d..a38fdef8594 100644 --- a/src/winconpty/winconpty.h +++ b/src/winconpty/winconpty.h @@ -58,9 +58,6 @@ typedef struct _PseudoConsole #ifndef PSEUDOCONSOLE_WIN32_INPUT_MODE #define PSEUDOCONSOLE_WIN32_INPUT_MODE (0x4) #endif -#ifndef PSEUDOCONSOLE_PASSTHROUGH_MODE -#define PSEUDOCONSOLE_PASSTHROUGH_MODE (0x8) -#endif // Implementations of the various PseudoConsole functions. HRESULT _CreatePseudoConsole(const HANDLE hToken, From 0f81ac43b6f9834f9dd251e7bda4560e522ca985 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 28 Mar 2024 19:45:36 +0100 Subject: [PATCH 191/603] Replace til::bit_cast with std::bit_cast (#16948) The former was introduced when we were still using C++17. We're using C++20 now so it's not needed anymore. --- src/buffer/out/TextColor.cpp | 4 ++-- src/buffer/out/textBuffer.cpp | 2 +- src/host/directio.cpp | 6 +++--- src/host/inputBuffer.cpp | 2 +- src/inc/consoletaeftemplates.hpp | 2 +- src/inc/til/bit.h | 10 +--------- 6 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp index 2e2e6f9fa0b..fa84adfac8d 100644 --- a/src/buffer/out/TextColor.cpp +++ b/src/buffer/out/TextColor.cpp @@ -202,7 +202,7 @@ COLORREF TextColor::GetColor(const std::array& // the result will be something like 0b00100000. // 5. Use BitScanForward (bsf) to find the index of the most significant 1 bit. const auto haystack = _mm256_loadu_si256(reinterpret_cast(colorTable.data())); // 1. - const auto needle = _mm256_set1_epi32(til::bit_cast(defaultColor)); // 2. + const auto needle = _mm256_set1_epi32(std::bit_cast(defaultColor)); // 2. const auto result = _mm256_cmpeq_epi32(haystack, needle); // 3. const auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(result)); // 4. unsigned long index; @@ -219,7 +219,7 @@ COLORREF TextColor::GetColor(const std::array& // --> the index returned by _BitScanForward must be divided by 2. const auto haystack1 = _mm_loadu_si128(reinterpret_cast(colorTable.data() + 0)); const auto haystack2 = _mm_loadu_si128(reinterpret_cast(colorTable.data() + 4)); - const auto needle = _mm_set1_epi32(til::bit_cast(defaultColor)); + const auto needle = _mm_set1_epi32(std::bit_cast(defaultColor)); const auto result1 = _mm_cmpeq_epi32(haystack1, needle); const auto result2 = _mm_cmpeq_epi32(haystack2, needle); const auto result = _mm_packs_epi32(result1, result2); // 3.5 diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 5c71dd1d398..5530dedb739 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2502,7 +2502,7 @@ void TextBuffer::_AppendRTFText(std::string& contentBuilder, const std::wstring_ { // Windows uses unsigned wchar_t - RTF uses signed ones. // '?' is the fallback ascii character. - const auto codeUnitRTFStr = std::to_string(til::bit_cast(codeUnit)); + const auto codeUnitRTFStr = std::to_string(std::bit_cast(codeUnit)); fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\u{}?"), codeUnitRTFStr); } } diff --git a/src/host/directio.cpp b/src/host/directio.cpp index 0a00fa25f82..b93252ad1ef 100644 --- a/src/host/directio.cpp +++ b/src/host/directio.cpp @@ -306,8 +306,8 @@ CATCH_RETURN(); // Fill the 1 byte (AsciiChar) portion of the leading and trailing cells with each of the bytes returned. // We have to be bit careful here not to directly write the CHARs, because CHARs are signed whereas wchar_t isn't // and we don't want any sign-extension. We want a 1:1 copy instead, so cast it to an unsigned char first. - in1.Char.UnicodeChar = til::bit_cast(AsciiDbcs[0]); - in2.Char.UnicodeChar = til::bit_cast(AsciiDbcs[1]); + in1.Char.UnicodeChar = std::bit_cast(AsciiDbcs[0]); + in2.Char.UnicodeChar = std::bit_cast(AsciiDbcs[1]); } else { @@ -323,7 +323,7 @@ CATCH_RETURN(); // 2 byte UTF-16 character into. Give it a go. CHAR asciiChar{}; ConvertToOem(codepage, &in1.Char.UnicodeChar, 1, &asciiChar, 1); - in1.Char.UnicodeChar = til::bit_cast(asciiChar); + in1.Char.UnicodeChar = std::bit_cast(asciiChar); } } } diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index 184670ffb1d..2e28a17ef67 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -434,7 +434,7 @@ try { // char is signed and assigning it to UnicodeChar would cause sign-extension. // unsigned char doesn't have this problem. - event.Event.KeyEvent.uChar.UnicodeChar = til::bit_cast(ch); + event.Event.KeyEvent.uChar.UnicodeChar = std::bit_cast(ch); OutEvents.push_back(event); } repeat--; diff --git a/src/inc/consoletaeftemplates.hpp b/src/inc/consoletaeftemplates.hpp index 752e21ffed6..2a98851ecab 100644 --- a/src/inc/consoletaeftemplates.hpp +++ b/src/inc/consoletaeftemplates.hpp @@ -61,7 +61,7 @@ namespace WEX::TestExecution return true; } - const auto nDiff = static_cast>(til::bit_cast(a) - til::bit_cast(b)); + const auto nDiff = static_cast>(std::bit_cast(a) - std::bit_cast(b)); const auto uDiff = static_cast(nDiff < 0 ? -nDiff : nDiff); return uDiff <= 4; } diff --git a/src/inc/til/bit.h b/src/inc/til/bit.h index a3ce7775a3b..46f4747f276 100644 --- a/src/inc/til/bit.h +++ b/src/inc/til/bit.h @@ -5,20 +5,12 @@ namespace til { - // bit_cast is a backport of the STL's std::bit_cast to C++17. - template, std::is_trivially_copyable, std::is_trivially_copyable>, int> = 0> - [[nodiscard]] constexpr To bit_cast(const From& _Val) noexcept - { - // TODO: Replace til::bit_cast and __builtin_bit_cast with std::bit_cast - return __builtin_bit_cast(To, _Val); - } - // When you cast a signed integer to an unsigned one, the compiler will use "sign extension" // so that -1 translates to all bits being set, no matter the size of the target type. // Sometimes you don't need or want that, which is when you can use this function. template [[nodiscard]] constexpr auto as_unsigned(const T& v) noexcept { - return bit_cast>(v); + return std::bit_cast>(v); } } From 36c81f24fbbc3473a3b1bbb214b8234a52740a1a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Mar 2024 13:11:36 -0700 Subject: [PATCH 192/603] Add a WT_SETTINGS_DIR env variable that portable profiles can use (#16949) Basically, title. It'd be a neat idea for portable installs of the Terminal to reference files that are right there in the portable install. This PR adds a `WT_SETTINGS_DIR` var to Terminal's own env block. This allows us to resolve profiles relative to our own settings folder. Closes #16295 --- src/cascadia/TerminalApp/AppLogic.cpp | 15 +++++++++++++++ src/cascadia/TerminalApp/AppLogic.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 29c13fdb13f..0b3ada01efa 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -149,6 +149,11 @@ namespace winrt::TerminalApp::implementation _languageProfileNotifier = winrt::make_self([this]() { _reloadSettings->Run(); }); + + // Do this here, rather than at the top of main. This will prevent us from + // including this variable in the vars we serialize in the + // Remoting::CommandlineArgs up in HandleCommandlineArgs. + _setupFolderPathEnvVar(); } // Method Description: @@ -720,4 +725,14 @@ namespace winrt::TerminalApp::implementation return TerminalApp::ParseCommandlineResult{ winrt::to_hstring(_appArgs.GetExitMessage()), r }; } + // Function Description + // * Adds a `WT_SETTINGS_DIR` env var to our own environment block, that + // points at our settings directory. This allows portable installs to + // refer to files in the portable install using %WT_SETTINGS_DIR% + void AppLogic::_setupFolderPathEnvVar() + { + std::wstring path{ CascadiaSettings::SettingsPath() }; + auto folderPath = path.substr(0, path.find_last_of(L"\\")); + SetEnvironmentVariableW(L"WT_SETTINGS_DIR", folderPath.c_str()); + } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 4f735dc9b29..3c14a4bca41 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -112,6 +112,8 @@ namespace winrt::TerminalApp::implementation void _RegisterSettingsChange(); fire_and_forget _DispatchReloadSettings(); + void _setupFolderPathEnvVar(); + #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; #endif From 1ede0233318088d4a1797c9c5d38904cc3f02e95 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 28 Mar 2024 21:12:10 +0100 Subject: [PATCH 193/603] Properly fix ConPTY buffer corking (#16793) I've found that #16079 was never properly addressed (it still randomly occurred after even after PR #16349), which later led to the issues described in #16769 (nushell flickering due to too many flushes). The crux of the fix is that this brings back the `_noFlushOnEnd` flag that was removed in PR #15991. This is then combined with a change to the cork API: An `uncork` on `VtEngine` now only flushes if `_Flush` got called while it was corked in the first place. `_noFlushOnEnd` prevents us from flushing in between two "unknown" VT sequences (like soft fonts or FTCS) which prevents them from being corrupted. The corking prevents the remaining cases of flushing too often. Long-term, a proper fix would be to pass through VT unmodified. Closes #16769 --- src/renderer/vt/invalidate.cpp | 4 ++++ src/renderer/vt/paint.cpp | 14 +++++++++++++- src/renderer/vt/state.cpp | 19 +++++++++++++++++-- src/renderer/vt/vtrenderer.hpp | 2 ++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/renderer/vt/invalidate.cpp b/src/renderer/vt/invalidate.cpp index 8f80d3eb8ea..489a6b50bc1 100644 --- a/src/renderer/vt/invalidate.cpp +++ b/src/renderer/vt/invalidate.cpp @@ -112,6 +112,10 @@ CATCH_RETURN(); // end paint to specifically handle this. _circled = circled; + // If we flushed for any reason other than circling (i.e, a sequence that we + // didn't understand), we don't need to push the buffer out on EndPaint. + _noFlushOnEnd = !circled; + _trace.TraceTriggerCircling(*pForcePaint); return S_OK; } diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index 4e5d3ec5389..6dc611252c4 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -94,7 +94,19 @@ using namespace Microsoft::Console::Types; RETURN_IF_FAILED(_MoveCursor(_deferredCursorPos)); } - _Flush(); + // If this frame was triggered because we encountered a VT sequence which + // required the buffered state to get printed, we don't want to flush this + // frame to the pipe. That might result in us rendering half the output of a + // particular frame (as emitted by the client). + // + // Instead, we'll leave this frame in _buffer, and just keep appending to + // it as needed. + if (!_noFlushOnEnd) + { + _Flush(); + } + + _noFlushOnEnd = false; return S_OK; } diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index 9749bf63d32..ea176e42f37 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -136,10 +136,19 @@ CATCH_RETURN(); void VtEngine::_Flush() noexcept { - if (!_corked && !_buffer.empty()) + if (_buffer.empty()) + { + return; + } + + if (!_corked) { _flushImpl(); + return; } + + // Defer the flush until someone calls Cork(false). + _flushRequested = true; } // _corked is often true and separating _flushImpl() out allows _flush() to be inlined. @@ -167,7 +176,13 @@ void VtEngine::_flushImpl() noexcept void VtEngine::Cork(bool corked) noexcept { _corked = corked; - _Flush(); + + // Now do the deferred flush from a previous call to _Flush(). + if (!corked && _flushRequested) + { + _flushRequested = false; + _flushImpl(); + } } // Method Description: diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 1a7c25316fd..3bea4528ac5 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -137,7 +137,9 @@ namespace Microsoft::Console::Render bool _resizeQuirk{ false }; bool _passthrough{ false }; + bool _noFlushOnEnd{ false }; bool _corked{ false }; + bool _flushRequested{ false }; std::optional _newBottomLineBG{ std::nullopt }; [[nodiscard]] HRESULT _WriteFill(const size_t n, const char c) noexcept; From c4c52061d54694d183b264b6f71e350114b5d715 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 29 Mar 2024 12:48:58 +0100 Subject: [PATCH 194/603] Implement buffer restore (#16598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset allows Windows Terminal to dump its buffer contents as UTF-16LE VT text onto disk and restore it later. This functionality is enabled whenever `persistedWindowLayout` is being used. Closes #961 Closes #16741 ## Validation Steps Performed * Open multiple windows with multiple tabs and restart the app Everything's restored ✅ * Reopen a tab with output from `RenderingTests.exe` Everything's restored ✅ * Closing tabs and windows with Ctrl+W deletes their buffer dumps ✅ * Closing tabs doesn't create buffer dumps ✅ --- .github/actions/spelling/expect/expect.txt | 2 + src/buffer/out/TextColor.cpp | 17 +- src/buffer/out/TextColor.h | 12 +- src/buffer/out/textBuffer.cpp | 232 +++++++++++++++++- src/buffer/out/textBuffer.hpp | 2 + src/cascadia/Remoting/GetWindowLayoutArgs.cpp | 5 - src/cascadia/Remoting/GetWindowLayoutArgs.h | 31 --- .../Microsoft.Terminal.RemotingLib.vcxproj | 12 - src/cascadia/Remoting/Monarch.cpp | 41 +--- src/cascadia/Remoting/Monarch.h | 6 +- src/cascadia/Remoting/Monarch.idl | 7 - src/cascadia/Remoting/Peasant.cpp | 21 -- src/cascadia/Remoting/Peasant.h | 2 - src/cascadia/Remoting/Peasant.idl | 8 - .../Remoting/QuitAllRequestedArgs.cpp | 5 - src/cascadia/Remoting/QuitAllRequestedArgs.h | 29 --- src/cascadia/Remoting/WindowManager.cpp | 36 --- src/cascadia/Remoting/WindowManager.h | 6 - src/cascadia/Remoting/WindowManager.idl | 4 - .../TerminalApp/AppCommandlineArgs.cpp | 11 + src/cascadia/TerminalApp/AppCommandlineArgs.h | 2 + src/cascadia/TerminalApp/AppLogic.cpp | 16 -- src/cascadia/TerminalApp/AppLogic.h | 2 - src/cascadia/TerminalApp/AppLogic.idl | 1 - src/cascadia/TerminalApp/IPaneContent.idl | 9 +- src/cascadia/TerminalApp/Pane.cpp | 40 +-- src/cascadia/TerminalApp/Pane.h | 4 +- .../Resources/en-US/Resources.resw | 6 +- .../TerminalApp/ScratchpadContent.cpp | 2 +- src/cascadia/TerminalApp/ScratchpadContent.h | 2 +- src/cascadia/TerminalApp/SettingsTab.cpp | 2 +- src/cascadia/TerminalApp/SettingsTab.h | 2 +- src/cascadia/TerminalApp/TabBase.h | 2 +- src/cascadia/TerminalApp/TabManagement.cpp | 16 +- src/cascadia/TerminalApp/TerminalPage.cpp | 65 +++-- src/cascadia/TerminalApp/TerminalPage.h | 20 +- src/cascadia/TerminalApp/TerminalPage.idl | 7 +- .../TerminalApp/TerminalPaneContent.cpp | 37 ++- .../TerminalApp/TerminalPaneContent.h | 2 +- src/cascadia/TerminalApp/TerminalTab.cpp | 6 +- src/cascadia/TerminalApp/TerminalTab.h | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 40 +-- src/cascadia/TerminalApp/TerminalWindow.h | 9 +- src/cascadia/TerminalApp/TerminalWindow.idl | 7 +- .../TerminalConnection.vcxproj | 3 +- src/cascadia/TerminalControl/ControlCore.cpp | 52 +++- src/cascadia/TerminalControl/ControlCore.h | 2 + .../TerminalControl/IControlSettings.idl | 3 +- src/cascadia/TerminalControl/TermControl.cpp | 21 ++ src/cascadia/TerminalControl/TermControl.h | 3 + src/cascadia/TerminalControl/TermControl.idl | 2 + src/cascadia/TerminalCore/Terminal.cpp | 5 + src/cascadia/TerminalCore/Terminal.hpp | 1 + .../TerminalSettingsModel/ActionArgs.cpp | 7 + .../TerminalSettingsModel/ActionArgs.h | 5 + .../TerminalSettingsModel/ActionArgs.idl | 1 + .../ApplicationState.cpp | 17 +- .../TerminalSettingsModel/ApplicationState.h | 2 + .../ApplicationState.idl | 2 + .../TerminalSettingsModel/CascadiaSettings.h | 1 + .../CascadiaSettings.idl | 1 + .../CascadiaSettingsSerialization.cpp | 7 + .../TerminalSettings.cpp | 5 + .../TerminalSettingsModel/TerminalSettings.h | 1 + .../TerminalSettings.idl | 1 + .../UnitTests_Remoting/RemotingTests.cpp | 4 - src/cascadia/WindowsTerminal/AppHost.cpp | 211 ++++++---------- src/cascadia/WindowsTerminal/AppHost.h | 15 +- .../WindowsTerminal/WindowEmperor.cpp | 173 ++++++------- src/cascadia/WindowsTerminal/WindowEmperor.h | 8 - src/cascadia/inc/ControlProperties.h | 1 + src/inc/til/small_vector.h | 14 +- 72 files changed, 670 insertions(+), 688 deletions(-) delete mode 100644 src/cascadia/Remoting/GetWindowLayoutArgs.cpp delete mode 100644 src/cascadia/Remoting/GetWindowLayoutArgs.h delete mode 100644 src/cascadia/Remoting/QuitAllRequestedArgs.cpp delete mode 100644 src/cascadia/Remoting/QuitAllRequestedArgs.h diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 28b9b790d7b..1ce77a78ad5 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -597,6 +597,7 @@ FECF FEEF fesb FFAF +ffd FFDE FFFDb fgbg @@ -757,6 +758,7 @@ hdr HDROP hdrstop HEIGHTSCROLL +hfind hfont hfontresource hglobal diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp index fa84adfac8d..0840ec19f5f 100644 --- a/src/buffer/out/TextColor.cpp +++ b/src/buffer/out/TextColor.cpp @@ -62,6 +62,11 @@ bool TextColor::CanBeBrightened() const noexcept return IsIndex16() || IsDefault(); } +ColorType TextColor::GetType() const noexcept +{ + return _meta; +} + bool TextColor::IsLegacy() const noexcept { return (IsIndex16() || IsIndex256()) && _index < 16; @@ -280,15 +285,3 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept return til::at(CompressedRgbToIndex16, compressedRgb); } } - -// Method Description: -// - Return a COLORREF containing our stored value. Will return garbage if this -//attribute is not a RGB attribute. -// Arguments: -// - -// Return Value: -// - a COLORREF containing our stored value -COLORREF TextColor::GetRGB() const noexcept -{ - return RGB(_red, _green, _blue); -} diff --git a/src/buffer/out/TextColor.h b/src/buffer/out/TextColor.h index 0b71078fbf1..3070f816974 100644 --- a/src/buffer/out/TextColor.h +++ b/src/buffer/out/TextColor.h @@ -119,6 +119,7 @@ struct TextColor } bool CanBeBrightened() const noexcept; + ColorType GetType() const noexcept; bool IsLegacy() const noexcept; bool IsIndex16() const noexcept; bool IsIndex256() const noexcept; @@ -133,12 +134,11 @@ struct TextColor COLORREF GetColor(const std::array& colorTable, const size_t defaultIndex, bool brighten = false) const noexcept; BYTE GetLegacyIndex(const BYTE defaultIndex) const noexcept; - constexpr BYTE GetIndex() const noexcept - { - return _index; - } - - COLORREF GetRGB() const noexcept; + constexpr BYTE GetIndex() const noexcept { return _index; } + constexpr BYTE GetR() const noexcept { return _red; } + constexpr BYTE GetG() const noexcept { return _green; } + constexpr BYTE GetB() const noexcept { return _blue; } + constexpr COLORREF GetRGB() const noexcept { return RGB(_red, _green, _blue); } static constexpr BYTE TransposeLegacyIndex(const size_t index) { diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 5530dedb739..62371474adc 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -11,7 +11,6 @@ #include "UTextAdapter.h" #include "../../types/inc/GlyphWidth.hpp" #include "../renderer/base/renderer.hpp" -#include "../types/inc/convert.hpp" #include "../types/inc/utils.hpp" using namespace Microsoft::Console; @@ -2508,6 +2507,237 @@ void TextBuffer::_AppendRTFText(std::string& contentBuilder, const std::wstring_ } } +void TextBuffer::Serialize(const wchar_t* destination) const +{ + const wil::unique_handle file{ CreateFileW(destination, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) }; + THROW_LAST_ERROR_IF(!file); + + static constexpr size_t writeThreshold = 32 * 1024; + std::wstring buffer; + buffer.reserve(writeThreshold + writeThreshold / 2); + buffer.push_back(L'\uFEFF'); + + const til::CoordType lastRowWithText = GetLastNonSpaceCharacter(nullptr).y; + CharacterAttributes previousAttr = CharacterAttributes::Unused1; + TextColor previousFg; + TextColor previousBg; + TextColor previousUl; + uint16_t previousHyperlinkId = 0; + + // This iterates through each row. The exit condition is at the end + // of the for() loop so that we can properly handle file flushing. + for (til::CoordType currentRow = 0;; currentRow++) + { + const auto& row = GetRowByOffset(currentRow); + + if (const auto lr = row.GetLineRendition(); lr != LineRendition::SingleWidth) + { + static constexpr std::wstring_view mappings[] = { + L"\x1b#6", // LineRendition::DoubleWidth + L"\x1b#3", // LineRendition::DoubleHeightTop + L"\x1b#4", // LineRendition::DoubleHeightBottom + }; + const auto idx = std::clamp(static_cast(lr) - 1, 0, 2); + buffer.append(til::at(mappings, idx)); + } + + const auto& runs = row.Attributes().runs(); + auto it = runs.begin(); + const auto end = runs.end(); + const auto last = end - 1; + til::CoordType oldX = 0; + + for (; it != end; ++it) + { + const auto attr = it->value.GetCharacterAttributes(); + const auto hyperlinkId = it->value.GetHyperlinkId(); + const auto fg = it->value.GetForeground(); + const auto bg = it->value.GetBackground(); + const auto ul = it->value.GetUnderlineColor(); + + if (previousAttr != attr) + { + const auto attrDelta = attr ^ previousAttr; + + { + struct Mapping + { + CharacterAttributes attr; + uint8_t change[2]; // [0] = off, [1] = on + }; + static constexpr Mapping mappings[] = { + { CharacterAttributes::Intense, { 22, 1 } }, + { CharacterAttributes::Italics, { 23, 3 } }, + { CharacterAttributes::Blinking, { 25, 5 } }, + { CharacterAttributes::Invisible, { 28, 8 } }, + { CharacterAttributes::CrossedOut, { 29, 9 } }, + { CharacterAttributes::Faint, { 22, 2 } }, + { CharacterAttributes::TopGridline, { 55, 53 } }, + { CharacterAttributes::ReverseVideo, { 27, 7 } }, + { CharacterAttributes::BottomGridline, { 24, 4 } }, + }; + for (const auto& mapping : mappings) + { + if (WI_IsAnyFlagSet(attrDelta, mapping.attr)) + { + const auto n = til::at(mapping.change, WI_IsAnyFlagSet(attr, mapping.attr)); + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[{}m"), n); + } + } + } + + if (WI_IsAnyFlagSet(attrDelta, CharacterAttributes::UnderlineStyle)) + { + static constexpr std::wstring_view mappings[] = { + L"\x1b[24m", // UnderlineStyle::NoUnderline + L"\x1b[4m", // UnderlineStyle::SinglyUnderlined + L"\x1b[21m", // UnderlineStyle::DoublyUnderlined + L"\x1b[4:3m", // UnderlineStyle::CurlyUnderlined + L"\x1b[4:4m", // UnderlineStyle::DottedUnderlined + L"\x1b[4:5m", // UnderlineStyle::DashedUnderlined + }; + + auto idx = WI_EnumValue(it->value.GetUnderlineStyle()); + if (idx >= std::size(mappings)) + { + idx = 1; // UnderlineStyle::SinglyUnderlined + } + + buffer.append(til::at(mappings, idx)); + } + + previousAttr = attr; + } + + if (previousFg != fg) + { + switch (fg.GetType()) + { + case ColorType::IsDefault: + buffer.append(L"\x1b[39m"); + break; + case ColorType::IsIndex16: + { + uint8_t index = WI_IsFlagSet(fg.GetIndex(), 8) ? 90 : 30; + index += fg.GetIndex() & 7; + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[{}m"), index); + break; + } + case ColorType::IsIndex256: + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[38;5;{}m"), fg.GetIndex()); + break; + case ColorType::IsRgb: + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[38;2;{};{};{}m"), fg.GetR(), fg.GetG(), fg.GetB()); + break; + default: + break; + } + previousFg = fg; + } + + if (previousBg != bg) + { + switch (bg.GetType()) + { + case ColorType::IsDefault: + buffer.append(L"\x1b[49m"); + break; + case ColorType::IsIndex16: + { + uint8_t index = WI_IsFlagSet(bg.GetIndex(), 8) ? 100 : 40; + index += bg.GetIndex() & 7; + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[{}m"), index); + break; + } + case ColorType::IsIndex256: + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[48;5;{}m"), bg.GetIndex()); + break; + case ColorType::IsRgb: + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[48;2;{};{};{}m"), bg.GetR(), bg.GetG(), bg.GetB()); + break; + default: + break; + } + previousBg = bg; + } + + if (previousUl != ul) + { + switch (fg.GetType()) + { + case ColorType::IsDefault: + buffer.append(L"\x1b[59m"); + break; + case ColorType::IsIndex256: + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[58:5:{}m"), ul.GetIndex()); + break; + case ColorType::IsRgb: + fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[58:2::{}:{}:{}m"), ul.GetR(), ul.GetG(), ul.GetB()); + break; + default: + break; + } + previousUl = ul; + } + + if (previousHyperlinkId != hyperlinkId) + { + if (hyperlinkId) + { + const auto uri = GetHyperlinkUriFromId(hyperlinkId); + if (!uri.empty()) + { + buffer.append(L"\x1b]8;;"); + buffer.append(uri); + buffer.append(L"\x1b\\"); + previousHyperlinkId = hyperlinkId; + } + } + else + { + buffer.append(L"\x1b]8;;\x1b\\"); + previousHyperlinkId = 0; + } + } + + auto newX = oldX + it->length; + // Trim whitespace with default attributes from the end of each line. + if (it == last && it->value == TextAttribute{}) + { + // This can result in oldX > newX, but that's okay because GetText() + // is robust against that and returns an empty string. + newX = row.MeasureRight(); + } + + buffer.append(row.GetText(oldX, newX)); + oldX = newX; + } + + const auto moreRowsRemaining = currentRow < lastRowWithText; + + if (!row.WasWrapForced() || !moreRowsRemaining) + { + buffer.append(L"\r\n"); + } + + if (buffer.size() >= writeThreshold || !moreRowsRemaining) + { + const auto fileSize = gsl::narrow(buffer.size() * sizeof(wchar_t)); + DWORD bytesWritten = 0; + THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.get(), buffer.data(), fileSize, &bytesWritten, nullptr)); + if (bytesWritten != fileSize) + { + THROW_WIN32_MSG(ERROR_WRITE_FAULT, "failed to write"); + } + } + + if (!moreRowsRemaining) + { + break; + } + } +} + // Function Description: // - Reflow the contents from the old buffer into the new buffer. The new buffer // can have different dimensions than the old buffer. If it does, then this diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 419e01471e5..8093aea9af8 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -319,6 +319,8 @@ class TextBuffer final const bool isIntenseBold, std::function(const TextAttribute&)> GetAttributeColors) const noexcept; + void Serialize(const wchar_t* destination) const; + struct PositionInformation { til::CoordType mutableViewportTop{ 0 }; diff --git a/src/cascadia/Remoting/GetWindowLayoutArgs.cpp b/src/cascadia/Remoting/GetWindowLayoutArgs.cpp deleted file mode 100644 index f2cc01df40c..00000000000 --- a/src/cascadia/Remoting/GetWindowLayoutArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#include "pch.h" -#include "GetWindowLayoutArgs.h" -#include "GetWindowLayoutArgs.g.cpp" diff --git a/src/cascadia/Remoting/GetWindowLayoutArgs.h b/src/cascadia/Remoting/GetWindowLayoutArgs.h deleted file mode 100644 index 097e375bc70..00000000000 --- a/src/cascadia/Remoting/GetWindowLayoutArgs.h +++ /dev/null @@ -1,31 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- GetWindowLayoutArgs.h - -Abstract: -- This is a helper class for getting the window layout from a peasant. - Depending on if we are running on the monarch or on a peasant we might need - to switch what thread we are executing on. This gives us the option of - either returning the json result synchronously, or as a promise. ---*/ - -#pragma once - -#include "GetWindowLayoutArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct GetWindowLayoutArgs : public GetWindowLayoutArgsT - { - WINRT_PROPERTY(winrt::hstring, WindowLayoutJson, L""); - WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncOperation, WindowLayoutJsonAsync, nullptr) - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(GetWindowLayoutArgs); -} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index ce78fc9c4b2..b5c7ada4790 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -37,12 +37,6 @@ Peasant.idl - - Peasant.idl - - - Monarch.idl - @@ -78,12 +72,6 @@ Peasant.idl - - Peasant.idl - - - Monarch.idl - Create diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 6b48650c5f2..63e74b0fae1 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -6,7 +6,6 @@ #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" -#include "QuitAllRequestedArgs.h" #include "ProposeCommandlineResult.h" #include "Monarch.g.cpp" @@ -135,21 +134,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - used // Return Value: // - - winrt::fire_and_forget Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) + void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - // Let the process hosting the monarch run any needed logic before - // closing all windows. - auto args = winrt::make_self(); - QuitAllRequested.raise(*this, *args); - - if (const auto action = args->BeforeQuitAllAction()) + if (_quitting.exchange(true, std::memory_order_relaxed)) { - co_await action; + return; } - _quitting.store(true); - // Tell all peasants to exit. const auto callback = [&](const auto& id, const auto& p) { // We want to tell our peasant to quit last, so that we don't try // to perform a bunch of elections on quit. @@ -197,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // If we are quitting we don't care about maintaining our list of // peasants anymore, and don't need to notify the host that something // changed. - if (_quitting.load(std::memory_order_acquire)) + if (_quitting.load(std::memory_order_relaxed)) { return; } @@ -1036,30 +1027,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _forEachPeasant(func, onError); } - // Method Description: - // - Ask all peasants to return their window layout as json - // Arguments: - // - - // Return Value: - // - The collection of window layouts from each peasant. - Windows::Foundation::Collections::IVector Monarch::GetAllWindowLayouts() - { - std::vector vec; - auto callback = [&](const auto& /*id*/, const auto& p) { - vec.emplace_back(p.GetWindowLayout()); - }; - auto onError = [](auto&& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_GetAllWindowLayouts_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not get a window layout from"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - _forEachPeasant(callback, onError); - - return winrt::single_threaded_vector(std::move(vec)); - } - void Monarch::RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 48619ac47b8..299932d9610 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -96,7 +96,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void SummonAllWindows(); bool DoesQuakeWindowExist(); Windows::Foundation::Collections::IVectorView GetPeasantInfos(); - Windows::Foundation::Collections::IVector GetAllWindowLayouts(); void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, const Windows::Foundation::IReference& windowBounds); void RequestSendContent(const Remoting::RequestReceiveContentArgs& args); @@ -106,7 +105,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation til::typed_event<> HideNotificationIconRequested; til::typed_event<> WindowCreated; til::typed_event<> WindowClosed; - til::typed_event QuitAllRequested; til::typed_event RequestNewWindow; @@ -146,8 +144,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); - winrt::fire_and_forget _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); // Method Description: // - Helper for doing something on each and every peasant. diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index f57bd6b59b2..c81837e96b5 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -46,11 +46,6 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.IReference WindowID; } - [default_interface] runtimeclass QuitAllRequestedArgs { - QuitAllRequestedArgs(); - Windows.Foundation.IAsyncAction BeforeQuitAllAction; - } - struct PeasantInfo { UInt64 Id; @@ -72,7 +67,6 @@ namespace Microsoft.Terminal.Remoting void SummonAllWindows(); Boolean DoesQuakeWindowExist(); Windows.Foundation.Collections.IVectorView GetPeasantInfos { get; }; - Windows.Foundation.Collections.IVector GetAllWindowLayouts(); void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference bounds); void RequestSendContent(RequestReceiveContentArgs args); @@ -82,7 +76,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; event Windows.Foundation.TypedEventHandler WindowCreated; event Windows.Foundation.TypedEventHandler WindowClosed; - event Windows.Foundation.TypedEventHandler QuitAllRequested; event Windows.Foundation.TypedEventHandler RequestNewWindow; }; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 5d2b54f85af..6378854d199 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -5,7 +5,6 @@ #include "Peasant.h" #include "CommandlineArgs.h" #include "SummonWindowBehavior.h" -#include "GetWindowLayoutArgs.h" #include "Peasant.g.cpp" #include "../../types/inc/utils.hpp" #include "AttachRequest.g.cpp" @@ -309,26 +308,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } - // Method Description: - // - Request and return the window layout from the current TerminalPage - // Arguments: - // - - // Return Value: - // - the window layout as a json string - hstring Peasant::GetWindowLayout() - { - auto args = winrt::make_self(); - GetWindowLayoutRequested.raise(nullptr, *args); - if (const auto op = args->WindowLayoutJsonAsync()) - { - // This will fail if called on the UI thread, so the monarch should - // never set WindowLayoutJsonAsync. - auto str = op.get(); - return str; - } - return args->WindowLayoutJson(); - } - void Peasant::SendContent(const Remoting::RequestReceiveContentArgs& args) { SendContentRequested.raise(*this, args); diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 67d407e599a..dc860ba907d 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -65,7 +65,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); - winrt::hstring GetWindowLayout(); void SendContent(const winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs& args); til::typed_event WindowActivated; @@ -79,7 +78,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation til::typed_event<> HideNotificationIconRequested; til::typed_event<> QuitAllRequested; til::typed_event<> QuitRequested; - til::typed_event GetWindowLayoutRequested; til::typed_event AttachRequested; til::typed_event SendContentRequested; diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index f02d22da9c4..3463d561f07 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -32,12 +32,6 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.DateTime ActivatedTime { get; }; }; - [default_interface] runtimeclass GetWindowLayoutArgs { - GetWindowLayoutArgs(); - String WindowLayoutJson; - Windows.Foundation.IAsyncOperation WindowLayoutJsonAsync; - } - enum MonitorBehavior { InPlace, @@ -88,7 +82,6 @@ namespace Microsoft.Terminal.Remoting void RequestHideNotificationIcon(); void RequestQuitAll(); void Quit(); - String GetWindowLayout(); void AttachContentToWindow(AttachRequest request); void SendContent(RequestReceiveContentArgs args); @@ -102,7 +95,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; - event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; event Windows.Foundation.TypedEventHandler QuitAllRequested; event Windows.Foundation.TypedEventHandler QuitRequested; diff --git a/src/cascadia/Remoting/QuitAllRequestedArgs.cpp b/src/cascadia/Remoting/QuitAllRequestedArgs.cpp deleted file mode 100644 index ed5c39dcf4f..00000000000 --- a/src/cascadia/Remoting/QuitAllRequestedArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#include "pch.h" -#include "QuitAllRequestedArgs.h" -#include "QuitAllRequestedArgs.g.cpp" diff --git a/src/cascadia/Remoting/QuitAllRequestedArgs.h b/src/cascadia/Remoting/QuitAllRequestedArgs.h deleted file mode 100644 index 4fba59cd141..00000000000 --- a/src/cascadia/Remoting/QuitAllRequestedArgs.h +++ /dev/null @@ -1,29 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- QuitAllRequestedArgs.h - -Abstract: -- This is a helper class for allowing the monarch to run code before telling all - peasants to quit. This way the monarch can raise an event and get back a future - to wait for before continuing. ---*/ - -#pragma once - -#include "QuitAllRequestedArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct QuitAllRequestedArgs : public QuitAllRequestedArgsT - { - WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncAction, BeforeQuitAllAction, nullptr) - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(QuitAllRequestedArgs); -} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 57c110cd37d..a9d3a42e018 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -92,7 +92,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.WindowCreated({ get_weak(), &WindowManager::_bubbleWindowCreated }); _monarch.WindowClosed({ get_weak(), &WindowManager::_bubbleWindowClosed }); _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); - _monarch.QuitAllRequested({ get_weak(), &WindowManager::_bubbleQuitAllRequested }); _monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow }); } @@ -356,8 +355,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.AddPeasant(*p); - p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_bubbleGetWindowLayoutRequested }); - TraceLoggingWrite(g_hRemotingProvider, "WindowManager_CreateOurPeasant", TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"), @@ -367,10 +364,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *p; } - void WindowManager::_bubbleGetWindowLayoutRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& e) - { - GetWindowLayoutRequested.raise(s, e); - } void WindowManager::_bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e) { WindowCreated.raise(s, e); @@ -379,10 +372,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowClosed.raise(s, e); } - void WindowManager::_bubbleQuitAllRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& e) - { - QuitAllRequested.raise(s, e); - } void WindowManager::SignalClose(const Remoting::Peasant& peasant) { @@ -429,18 +418,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return 0; } - // Method Description: - // - Ask the monarch to quit all windows. - // Arguments: - // - - // Return Value: - // - - winrt::fire_and_forget WindowManager::RequestQuitAll(Remoting::Peasant peasant) - { - co_await winrt::resume_background(); - peasant.RequestQuitAll(); - } - bool WindowManager::DoesQuakeWindowExist() { return _monarch.DoesQuakeWindowExist(); @@ -451,19 +428,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::get_self(peasant)->ActiveTabTitle(title); } - Windows::Foundation::Collections::IVector WindowManager::GetAllWindowLayouts() - { - if (_monarch) - { - try - { - return _monarch.GetAllWindowLayouts(); - } - CATCH_LOG() - } - return nullptr; - } - winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 96946f1d3cc..6dd935e8a65 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -38,10 +38,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t GetNumberOfPeasants(); - static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant); - Windows::Foundation::Collections::IVector GetAllWindowLayouts(); bool DoesQuakeWindowExist(); winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference windowBounds); @@ -51,8 +49,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation til::typed_event<> WindowCreated; til::typed_event<> WindowClosed; - til::typed_event QuitAllRequested; - til::typed_event GetWindowLayoutRequested; til::typed_event RequestNewWindow; private: @@ -71,8 +67,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); void _bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e); void _bubbleWindowClosed(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e); - void _bubbleQuitAllRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& e); - void _bubbleGetWindowLayoutRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& e); }; } diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 5dd72b7f19a..deaf2b7489e 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -14,12 +14,10 @@ namespace Microsoft.Terminal.Remoting void SignalClose(Peasant p); void UpdateActiveTabTitle(String title, Peasant p); - static void RequestQuitAll(Peasant p); void SummonWindow(SummonWindowSelectionArgs args); void SummonAllWindows(); - Windows.Foundation.Collections.IVector GetAllWindowLayouts(); Windows.Foundation.Collections.IVectorView GetPeasantInfos(); UInt64 GetNumberOfPeasants(); @@ -33,8 +31,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler WindowCreated; event Windows.Foundation.TypedEventHandler WindowClosed; - event Windows.Foundation.TypedEventHandler QuitAllRequested; - event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; event Windows.Foundation.TypedEventHandler RequestNewWindow; diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 5060b4ff2f7..b7a6b581a05 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -549,6 +549,9 @@ void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubc subcommand.profileNameOption = subcommand.subcommand->add_option("-p,--profile", _profileName, RS_A(L"CmdProfileArgDesc")); + subcommand.sessionIdOption = subcommand.subcommand->add_option("--sessionId", + _sessionId, + RS_A(L"CmdSessionIdArgDesc")); subcommand.startingDirectoryOption = subcommand.subcommand->add_option("-d,--startingDirectory", _startingDirectory, RS_A(L"CmdStartingDirArgDesc")); @@ -628,6 +631,13 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT args.Profile(winrt::to_hstring(_profileName)); } + if (*subcommand.sessionIdOption) + { + const auto str = winrt::to_hstring(_sessionId); + const auto id = ::Microsoft::Console::Utils::GuidFromString(str.c_str()); + args.SessionId(id); + } + if (*subcommand.startingDirectoryOption) { args.StartingDirectory(winrt::to_hstring(_startingDirectory)); @@ -714,6 +724,7 @@ bool AppCommandlineArgs::_noCommandsProvided() void AppCommandlineArgs::_resetStateToDefault() { _profileName.clear(); + _sessionId.clear(); _startingDirectory.clear(); _startingTitle.clear(); _startingTabColor.clear(); diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index 9145697f453..7eb5a34f93d 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -62,6 +62,7 @@ class TerminalApp::AppCommandlineArgs final CLI::App* subcommand; CLI::Option* commandlineOption; CLI::Option* profileNameOption; + CLI::Option* sessionIdOption; CLI::Option* startingDirectoryOption; CLI::Option* titleOption; CLI::Option* tabColorOption; @@ -96,6 +97,7 @@ class TerminalApp::AppCommandlineArgs final // Are you adding a new sub-command? Make sure to update _noCommandsProvided! std::string _profileName; + std::string _sessionId; std::string _startingDirectory; std::string _startingTitle; std::string _startingTabColor; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 0b3ada01efa..dc11b506449 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -702,22 +702,6 @@ namespace winrt::TerminalApp::implementation return _settings.GlobalSettings().ShouldUsePersistedLayout(); } - void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) - { - std::vector converted; - converted.reserve(layouts.Size()); - - for (const auto& json : layouts) - { - if (json != L"") - { - converted.emplace_back(WindowLayout::FromJson(json)); - } - } - - ApplicationState::SharedInstance().PersistedWindowLayouts(winrt::single_threaded_vector(std::move(converted))); - } - TerminalApp::ParseCommandlineResult AppLogic::GetParseCommandlineMessage(array_view args) { ::TerminalApp::AppCommandlineArgs _appArgs; diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 3c14a4bca41..43d64184deb 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -53,9 +53,7 @@ namespace winrt::TerminalApp::implementation void NotifyRootInitialized(); bool HasSettingsStartupActions() const noexcept; - bool ShouldUsePersistedLayout() const; - void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 1021e61b99a..362c7405644 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -37,7 +37,6 @@ namespace TerminalApp Boolean HasSettingsStartupActions(); Boolean ShouldUsePersistedLayout(); - void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); void ReloadSettings(); diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index ebe0914ea9c..a26d1a05477 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -3,6 +3,13 @@ namespace TerminalApp { + enum BuildStartupKind + { + None, + Content, + MovePane, + Persist, + }; runtimeclass BellEventArgs { @@ -20,7 +27,7 @@ namespace TerminalApp UInt64 TaskbarProgress { get; }; Boolean ReadOnly { get; }; - Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent); + Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind); void Focus(Windows.UI.Xaml.FocusState reason); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 9ed53d49204..45950e2c776 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -4,12 +4,6 @@ #include "pch.h" #include "Pane.h" -#include "AppLogic.h" - -#include "Utils.h" - -#include - using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Graphics::Display; using namespace winrt::Windows::UI; @@ -20,7 +14,6 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Microsoft::Terminal::TerminalConnection; using namespace winrt::TerminalApp; -using namespace TerminalApp; static const int PaneBorderSize = 2; static const int CombinedPaneBorderSize = 2 * PaneBorderSize; @@ -105,21 +98,13 @@ Pane::Pane(std::shared_ptr first, }); } -// Method Description: -// - Extract the terminal settings from the current (leaf) pane's control -// to be used to create an equivalent control -// Arguments: -// - asContent: when true, we're trying to serialize this pane for moving across -// windows. In that case, we'll need to fill in the content guid for our new -// terminal args. -// Return Value: -// - Arguments appropriate for a SplitPane or NewTab action -NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const +// Extract the terminal settings from the current (leaf) pane's control +// to be used to create an equivalent control +NewTerminalArgs Pane::GetTerminalArgsForPane(BuildStartupKind kind) const { // Leaves are the only things that have controls assert(_IsLeaf()); - - return _content.GetNewTerminalArgs(asContent); + return _content.GetNewTerminalArgs(kind); } // Method Description: @@ -142,16 +127,13 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // - The state from building the startup actions, includes a vector of commands, // the original root pane, the id of the focused pane, and the number of panes // created. -Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, - uint32_t nextId, - const bool asContent, - const bool asMovePane) +Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, BuildStartupKind kind) { // Normally, if we're a leaf, return an empt set of actions, because the // parent pane will build the SplitPane action for us. If we're building // actions for a movePane action though, we'll still need to include // ourselves. - if (!asMovePane && _IsLeaf()) + if (kind != BuildStartupKind::MovePane && _IsLeaf()) { if (_lastActive) { @@ -165,18 +147,18 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, auto buildSplitPane = [&](auto newPane) { ActionAndArgs actionAndArgs; actionAndArgs.Action(ShortcutAction::SplitPane); - const auto terminalArgs{ newPane->GetTerminalArgsForPane(asContent) }; + const auto terminalArgs{ newPane->GetTerminalArgsForPane(kind) }; // When creating a pane the split size is the size of the new pane // and not position. const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right; - const auto splitSize = (asContent && _IsLeaf() ? .5 : 1. - _desiredSplitPosition); + const auto splitSize = (kind != BuildStartupKind::None && _IsLeaf() ? .5 : 1. - _desiredSplitPosition); SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs }; actionAndArgs.Args(args); return actionAndArgs; }; - if (asContent && _IsLeaf()) + if (kind != BuildStartupKind::None && _IsLeaf()) { return { .args = { buildSplitPane(shared_from_this()) }, @@ -223,10 +205,10 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, // We now need to execute the commands for each side of the tree // We've done one split, so the first-most child will have currentId, and the // one after it will be incremented. - auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1); + auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1, kind); // the next id for the second branch depends on how many splits were in the // first child. - auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1); + auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1, kind); std::vector actions{}; actions.reserve(firstState.args.size() + secondState.args.size() + 3); diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 1f237f83190..0f4a5c907dc 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -104,8 +104,8 @@ class Pane : public std::enable_shared_from_this std::optional focusedPaneId; uint32_t panesCreated; }; - BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false, const bool asMovePane = false); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const; + BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::BuildStartupKind kind); + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 3f91408815f..54cd9e9bc30 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -187,9 +187,6 @@ Quit - - Do you want to close all tabs? - Multiple panes @@ -340,6 +337,9 @@ Open with the given profile. Accepts either the name or GUID of a profile + + Sets the WT_SESSION variable; must be a GUID + Create a new split pane diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index dcb13697b54..0ed99203391 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -43,7 +43,7 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } - NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const bool /* asContent */) const + NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const { return nullptr; } diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index 10aa36b6b85..a2b76e47934 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -17,7 +17,7 @@ namespace winrt::TerminalApp::implementation void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); void Close(); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const; + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const; winrt::hstring Title() { return L"Scratchpad"; } uint64_t TaskbarState() { return 0; } diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp index 12107e8efea..483f4f30475 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -57,7 +57,7 @@ namespace winrt::TerminalApp::implementation // re-evaluate including that arg in this action then. // Return Value: // - The list of actions. - std::vector SettingsTab::BuildStartupActions(const bool /*asContent*/) const + std::vector SettingsTab::BuildStartupActions(BuildStartupKind) const { ASSERT_UI_THREAD(); diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h index 657c8387661..e5d7df92faf 100644 --- a/src/cascadia/TerminalApp/SettingsTab.h +++ b/src/cascadia/TerminalApp/SettingsTab.h @@ -30,7 +30,7 @@ namespace winrt::TerminalApp::implementation void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings); void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override; - std::vector BuildStartupActions(const bool asContent = false) const override; + std::vector BuildStartupActions(BuildStartupKind kind) const override; private: winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h index 8a0190ad3c3..9a538550b8d 100644 --- a/src/cascadia/TerminalApp/TabBase.h +++ b/src/cascadia/TerminalApp/TabBase.h @@ -22,7 +22,7 @@ namespace winrt::TerminalApp::implementation void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs); void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap); - virtual std::vector BuildStartupActions(const bool asContent = false) const = 0; + virtual std::vector BuildStartupActions(BuildStartupKind kind) const = 0; virtual std::optional GetTabColor(); void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused, diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index b407c6107c1..8d99ac600f5 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -429,7 +429,7 @@ namespace winrt::TerminalApp::implementation } auto t = winrt::get_self(tab); - auto actions = t->BuildStartupActions(); + auto actions = t->BuildStartupActions(BuildStartupKind::None); _AddPreviouslyClosedPaneOrTab(std::move(actions)); _RemoveTab(tab); @@ -486,7 +486,7 @@ namespace winrt::TerminalApp::implementation // if the user manually closed all tabs. // Do this only if we are the last window; the monarch will notice // we are missing and remove us that way otherwise. - LastTabClosed.raise(*this, winrt::make(!_maintainStateOnTabClose)); + CloseWindowRequested.raise(*this, nullptr); } else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast(tabIndex)) { @@ -767,11 +767,11 @@ namespace winrt::TerminalApp::implementation // This doesn't handle refocusing anything in particular, the // result will be that the last pane created is focused. In the // case of a single pane that is the desired behavior anyways. - auto state = pane->BuildStartupActions(0, 1); + auto state = pane->BuildStartupActions(0, 1, BuildStartupKind::None); { ActionAndArgs splitPaneAction{}; splitPaneAction.Action(ShortcutAction::SplitPane); - SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane() }; + SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane(BuildStartupKind::None) }; splitPaneAction.Args(splitPaneArgs); state.args.emplace(state.args.begin(), std::move(splitPaneAction)); @@ -1139,12 +1139,4 @@ namespace winrt::TerminalApp::implementation { return _tabs.Size() > 1; } - - void TerminalPage::_RemoveAllTabs() - { - // Since _RemoveTabs is asynchronous, create a snapshot of the tabs we want to remove - std::vector tabsToRemove; - std::copy(begin(_tabs), end(_tabs), std::back_inserter(tabsToRemove)); - _RemoveTabs(tabsToRemove); - } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d3fc7a2dc25..591fc6906a0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -5,7 +5,6 @@ #include "pch.h" #include "TerminalPage.h" #include "TerminalPage.g.cpp" -#include "LastTabClosedEventArgs.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" #include "RequestMoveContentArgs.g.cpp" #include "RequestReceiveContentArgs.g.cpp" @@ -654,9 +653,9 @@ namespace winrt::TerminalApp::implementation // GH#12267: Make sure that we don't instantly close ourselves when // we're readying to accept a defterm connection. In that case, we don't // have a tab yet, but will once we're initialized. - if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener)) + if (_tabs.Size() == 0 && !_shouldStartInboundListener && !_isEmbeddingInboundListener) { - LastTabClosed.raise(*this, winrt::make(false)); + CloseWindowRequested.raise(*this, nullptr); co_return; } else @@ -1277,6 +1276,11 @@ namespace winrt::TerminalApp::implementation } } + if (const auto id = settings.SessionId(); id != winrt::guid{}) + { + valueSet.Insert(L"sessionId", Windows::Foundation::PropertyValue::CreateGuid(id)); + } + connection.Initialize(valueSet); TraceLoggingWrite( @@ -1891,19 +1895,11 @@ namespace winrt::TerminalApp::implementation } } - // Method Description: - // - Saves the window position and tab layout to the application state - // - This does not create the InitialPosition field, that needs to be - // added externally. - // Arguments: - // - - // Return Value: - // - the window layout - WindowLayout TerminalPage::GetWindowLayout() + void TerminalPage::PersistState() { if (_startupState != StartupState::Initialized) { - return nullptr; + return; } std::vector actions; @@ -1911,7 +1907,7 @@ namespace winrt::TerminalApp::implementation for (auto tab : _tabs) { auto t = winrt::get_self(tab); - auto tabActions = t->BuildStartupActions(); + auto tabActions = t->BuildStartupActions(BuildStartupKind::Persist); actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end())); } @@ -1938,7 +1934,7 @@ namespace winrt::TerminalApp::implementation actions.emplace_back(std::move(action)); } - WindowLayout layout{}; + WindowLayout layout; layout.TabLayout(winrt::single_threaded_vector(std::move(actions))); auto mode = LaunchMode::DefaultMode; @@ -1955,20 +1951,15 @@ namespace winrt::TerminalApp::implementation layout.InitialSize(windowSize); - return layout; + ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout); } // Method Description: // - Close the terminal app. If there is more // than one tab opened, show a warning dialog. - // Arguments: - // - bypassDialog: if true a dialog won't be shown even if the user would - // normally get confirmation. This is used in the case where the user - // has already been prompted by the Quit action. - fire_and_forget TerminalPage::CloseWindow(bool bypassDialog) + fire_and_forget TerminalPage::CloseWindow() { - if (!bypassDialog && - _HasMultipleTabs() && + if (_HasMultipleTabs() && _settings.GlobalSettings().ConfirmCloseAllTabs() && !_displayingCloseDialog) { @@ -1987,15 +1978,7 @@ namespace winrt::TerminalApp::implementation } } - if (_settings.GlobalSettings().ShouldUsePersistedLayout()) - { - // Don't delete the ApplicationState when all of the tabs are removed. - // If there is still a monarch living they will get the event that - // a window closed and trigger a new save without this window. - _maintainStateOnTabClose = true; - } - - _RemoveAllTabs(); + CloseWindowRequested.raise(*this, nullptr); } // Method Description: @@ -2057,7 +2040,7 @@ namespace winrt::TerminalApp::implementation { if (const auto pane{ terminalTab->GetActivePane() }) { - auto startupActions = pane->BuildStartupActions(0, 1, true, true); + auto startupActions = pane->BuildStartupActions(0, 1, BuildStartupKind::MovePane); _DetachPaneFromWindow(pane); _MoveContent(std::move(startupActions.args), windowId, tabIdx); focusedTab->DetachPane(); @@ -2199,7 +2182,7 @@ namespace winrt::TerminalApp::implementation if (tab) { - auto startupActions = tab->BuildStartupActions(true); + auto startupActions = tab->BuildStartupActions(BuildStartupKind::Content); _DetachTabFromWindow(tab); _MoveContent(std::move(startupActions), windowId, 0); _RemoveTab(*tab); @@ -3041,7 +3024,17 @@ namespace winrt::TerminalApp::implementation // TermControl will copy the settings out of the settings passed to it. const auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection); - return _SetupControl(TermControl{ content }); + const TermControl control{ content }; + + if (const auto id = settings.DefaultSettings().SessionId(); id != winrt::guid{}) + { + const auto settingsDir = CascadiaSettings::SettingsDirectory(); + const auto idStr = Utils::GuidToPlainString(id); + const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr); + control.RestoreFromPath(path); + } + + return _SetupControl(control); } TermControl TerminalPage::_AttachControlToContent(const uint64_t& contentId) @@ -5124,7 +5117,7 @@ namespace winrt::TerminalApp::implementation const uint32_t tabIndex, std::optional dragPoint) { - auto startupActions = _stashed.draggedTab->BuildStartupActions(true); + auto startupActions = _stashed.draggedTab->BuildStartupActions(BuildStartupKind::Content); _DetachTabFromWindow(_stashed.draggedTab); _MoveContent(std::move(startupActions), windowId, tabIndex, dragPoint); diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 639165d1730..227f28edbb5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -7,7 +7,6 @@ #include "TerminalTab.h" #include "AppKeyBindings.h" #include "AppCommandlineArgs.h" -#include "LastTabClosedEventArgs.g.h" #include "RenameWindowRequestedArgs.g.h" #include "RequestMoveContentArgs.g.h" #include "RequestReceiveContentArgs.g.h" @@ -44,15 +43,6 @@ namespace winrt::TerminalApp::implementation ScrollDown = 1 }; - struct LastTabClosedEventArgs : LastTabClosedEventArgsT - { - WINRT_PROPERTY(bool, ClearPersistedState); - - public: - LastTabClosedEventArgs(const bool& shouldClear) : - _ClearPersistedState{ shouldClear } {}; - }; - struct RenameWindowRequestedArgs : RenameWindowRequestedArgsT { WINRT_PROPERTY(winrt::hstring, ProposedName); @@ -105,7 +95,6 @@ namespace winrt::TerminalApp::implementation bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout(); hstring Title(); @@ -121,7 +110,8 @@ namespace winrt::TerminalApp::implementation SuggestionsControl LoadSuggestionsUI(); winrt::fire_and_forget RequestQuit(); - winrt::fire_and_forget CloseWindow(bool bypassDialog); + winrt::fire_and_forget CloseWindow(); + void PersistState(); void ToggleFocusMode(); void ToggleFullscreen(); @@ -175,7 +165,7 @@ namespace winrt::TerminalApp::implementation // -------------------------------- WinRT Events --------------------------------- til::typed_event TitleChanged; - til::typed_event LastTabClosed; + til::typed_event CloseWindowRequested; til::typed_event SetTitleBarContent; til::typed_event FocusModeChanged; til::typed_event FullscreenChanged; @@ -185,7 +175,7 @@ namespace winrt::TerminalApp::implementation til::typed_event SetTaskbarProgress; til::typed_event Initialized; til::typed_event IdentifyWindowsRequested; - til::typed_event RenameWindowRequested; + til::typed_event RenameWindowRequested; til::typed_event SummonWindowRequested; til::typed_event CloseRequested; @@ -232,7 +222,6 @@ namespace winrt::TerminalApp::implementation std::optional _loadFromPersistedLayoutIdx{}; - bool _maintainStateOnTabClose{ false }; bool _rearranging{ false }; std::optional _rearrangeFrom{}; std::optional _rearrangeTo{}; @@ -348,7 +337,6 @@ namespace winrt::TerminalApp::implementation void _DismissTabContextMenus(); void _FocusCurrentTab(const bool focusAlways); bool _HasMultipleTabs() const; - void _RemoveAllTabs(); void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference& customTabSwitcherMode); bool _SelectTab(uint32_t tabIndex); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index f322c0d527e..8ec4fa1dc9c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -14,11 +14,6 @@ namespace TerminalApp void Detach(Microsoft.Terminal.Control.TermControl control); } - [default_interface] runtimeclass LastTabClosedEventArgs - { - Boolean ClearPersistedState { get; }; - }; - [default_interface] runtimeclass RenameWindowRequestedArgs { String ProposedName { get; }; @@ -86,7 +81,7 @@ namespace TerminalApp void SendContentToOther(RequestReceiveContentArgs args); event Windows.Foundation.TypedEventHandler TitleChanged; - event Windows.Foundation.TypedEventHandler LastTabClosed; + event Windows.Foundation.TypedEventHandler CloseWindowRequested; event Windows.Foundation.TypedEventHandler SetTitleBarContent; event Windows.Foundation.TypedEventHandler FocusModeChanged; event Windows.Foundation.TypedEventHandler FullscreenChanged; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 597631d8fdc..49bbe3ff355 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -3,11 +3,14 @@ #include "pch.h" #include "TerminalPaneContent.h" -#include "TerminalPaneContent.g.cpp" + +#include + +#include "../../types/inc/utils.hpp" #include "BellEventArgs.g.cpp" +#include "TerminalPaneContent.g.cpp" -#include using namespace winrt::Windows::Foundation; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Microsoft::Terminal::Settings::Model; @@ -77,7 +80,7 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } - NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const + NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const BuildStartupKind kind) const { NewTerminalArgs args{}; const auto& controlSettings = _control.Settings(); @@ -119,12 +122,32 @@ namespace winrt::TerminalApp::implementation // object. That would work for schemes set by the Terminal, but not ones set // by VT, but that seems good enough. - // Only fill in the ContentId if absolutely needed. If you fill in a number - // here (even 0), we'll serialize that number, AND treat that action as an - // "attach existing" rather than a "create" - if (asContent) + switch (kind) { + case BuildStartupKind::Content: + case BuildStartupKind::MovePane: + // Only fill in the ContentId if absolutely needed. If you fill in a number + // here (even 0), we'll serialize that number, AND treat that action as an + // "attach existing" rather than a "create" args.ContentId(_control.ContentId()); + break; + case BuildStartupKind::Persist: + { + const auto connection = _control.Connection(); + const auto id = connection ? connection.SessionId() : winrt::guid{}; + + if (id != winrt::guid{}) + { + const auto settingsDir = CascadiaSettings::SettingsDirectory(); + const auto idStr = ::Microsoft::Console::Utils::GuidToPlainString(id); + const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr); + _control.PersistToPath(path); + args.SessionId(id); + } + break; + } + default: + break; } return args; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 2c2deee0124..11ad0425379 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -27,7 +27,7 @@ namespace winrt::TerminalApp::implementation void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); void Close(); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const; + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index c5cb7f5b5f2..247601e21d1 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -432,18 +432,18 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - A vector of commands - std::vector TerminalTab::BuildStartupActions(const bool asContent) const + std::vector TerminalTab::BuildStartupActions(BuildStartupKind kind) const { ASSERT_UI_THREAD(); // Give initial ids (0 for the child created with this tab, // 1 for the child after the first split. - auto state = _rootPane->BuildStartupActions(0, 1, asContent); + auto state = _rootPane->BuildStartupActions(0, 1, kind); { ActionAndArgs newTabAction{}; newTabAction.Action(ShortcutAction::NewTab); - NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) }; + NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(kind) }; newTabAction.Args(newTabArgs); state.args.emplace(state.args.begin(), std::move(newTabAction)); diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index df9e4cae825..3ae1a3cc2fa 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -79,7 +79,7 @@ namespace winrt::TerminalApp::implementation void EnterZoom(); void ExitZoom(); - std::vector BuildStartupActions(const bool asContent = false) const override; + std::vector BuildStartupActions(BuildStartupKind kind) const override; int GetLeafPaneCount() const noexcept; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 0ca513a064a..4a37b0a733e 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -267,7 +267,7 @@ namespace winrt::TerminalApp::implementation { if (_root) { - _root->CloseWindow(true); + _root->PersistState(); } } @@ -916,32 +916,11 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void TerminalWindow::CloseWindow(LaunchPosition pos, const bool isLastWindow) + void TerminalWindow::CloseWindow() { if (_root) { - // If persisted layout is enabled and we are the last window closing - // we should save our state. - if (_settings.GlobalSettings().ShouldUsePersistedLayout() && isLastWindow) - { - if (const auto layout = _root->GetWindowLayout()) - { - layout.InitialPosition(pos); - const auto state = ApplicationState::SharedInstance(); - state.PersistedWindowLayouts(winrt::single_threaded_vector({ layout })); - } - } - - _root->CloseWindow(false); - } - } - - void TerminalWindow::ClearPersistedWindowState() - { - if (_settings.GlobalSettings().ShouldUsePersistedLayout()) - { - auto state = ApplicationState::SharedInstance(); - state.PersistedWindowLayouts(nullptr); + _root->CloseWindow(); } } @@ -1134,19 +1113,6 @@ namespace winrt::TerminalApp::implementation return winrt::to_hstring(_appArgs.GetExitMessage()); } - hstring TerminalWindow::GetWindowLayoutJson(LaunchPosition position) - { - if (_root != nullptr) - { - if (const auto layout = _root->GetWindowLayout()) - { - layout.InitialPosition(position); - return WindowLayout::ToJson(layout); - } - } - return L""; - } - void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) { _loadFromPersistedLayoutIdx = idx; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 8bf04971afb..d48d8d4f242 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -95,8 +95,6 @@ namespace winrt::TerminalApp::implementation bool AlwaysOnTop() const; bool AutoHideWindow(); - hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); - void IdentifyWindow(); void RenameFailed(); @@ -104,9 +102,6 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(); void SetPersistedLayoutIdx(const uint32_t idx); - void SetNumberOfOpenWindows(const uint64_t num); - bool ShouldUsePersistedLayout() const; - void ClearPersistedWindowState(); void RequestExitFullscreen(); @@ -125,7 +120,7 @@ namespace winrt::TerminalApp::implementation void TitlebarClicked(); bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); - void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position, const bool isLastWindow); + void CloseWindow(); void WindowVisibilityChanged(const bool showOrHide); winrt::TerminalApp::TaskbarState TaskbarState(); @@ -218,7 +213,7 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent); FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged); - FORWARDED_TYPED_EVENT(LastTabClosed, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs, _root, LastTabClosed); + FORWARDED_TYPED_EVENT(CloseWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, CloseWindowRequested); FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged); FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged); FORWARDED_TYPED_EVENT(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index a1e84211542..1700686e1db 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -74,7 +74,6 @@ namespace TerminalApp void IdentifyWindow(); void SetPersistedLayoutIdx(UInt32 idx); - void ClearPersistedWindowState(); void RenameFailed(); void RequestExitFullscreen(); @@ -89,7 +88,7 @@ namespace TerminalApp Boolean GetInitialAlwaysOnTop(); Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension); void TitlebarClicked(); - void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position, Boolean isLastWindow); + void CloseWindow(); void WindowVisibilityChanged(Boolean showOrHide); TaskbarState TaskbarState{ get; }; @@ -97,8 +96,6 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush FrameBrush { get; }; void WindowActivated(Boolean activated); - String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); - Boolean GetMinimizeToNotificationArea(); Boolean GetAlwaysShowNotificationIcon(); Boolean GetShowTitleInTitlebar(); @@ -118,7 +115,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SetTitleBarContent; event Windows.Foundation.TypedEventHandler TitleChanged; - event Windows.Foundation.TypedEventHandler LastTabClosed; + event Windows.Foundation.TypedEventHandler CloseWindowRequested; event Windows.Foundation.TypedEventHandler RequestedThemeChanged; event Windows.Foundation.TypedEventHandler FocusModeChanged; event Windows.Foundation.TypedEventHandler FullscreenChanged; diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj index f0a2e4e6b49..8eda9937725 100644 --- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj +++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj @@ -96,7 +96,6 @@ - - + \ No newline at end of file diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 95c80e4e9ad..4c36320d9b2 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -9,8 +9,10 @@ #include #include -#include #include +#include +#include +#include #include "EventArgs.h" #include "../../renderer/atlas/AtlasEngine.h" @@ -399,10 +401,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _initializedTerminal.store(true, std::memory_order_relaxed); } // scope for TerminalLock - // Start the connection outside of lock, because it could - // start writing output immediately. - _connection.Start(); - return true; } @@ -1711,6 +1709,50 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + void ControlCore::PersistToPath(const wchar_t* path) const + { + const auto lock = _terminal->LockForReading(); + _terminal->SerializeMainBuffer(path); + } + + void ControlCore::RestoreFromPath(const wchar_t* path) const + { + const wil::unique_handle file{ CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) }; + if (!file) + { + return; + } + + wchar_t buffer[32 * 1024]; + DWORD read = 0; + + // Ensure the text file starts with a UTF-16 BOM. + if (!ReadFile(file.get(), &buffer[0], 2, &read, nullptr) || read < 2 || buffer[0] != L'\uFEFF') + { + return; + } + + for (;;) + { + if (!ReadFile(file.get(), &buffer[0], sizeof(buffer), &read, nullptr)) + { + break; + } + + const auto lock = _terminal->LockForWriting(); + _terminal->Write({ &buffer[0], read / 2 }); + + if (read < sizeof(buffer)) + { + break; + } + } + + // This pushes the restored contents up into the scrollback. + const auto lock = _terminal->LockForWriting(); + _terminal->Write(L"\x1b[2J"); + } + void ControlCore::_rendererWarning(const HRESULT hr, wil::zwstring_view parameter) { RendererWarning.raise(*this, winrt::make(hr, winrt::hstring{ parameter })); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 7dbf228cd26..ee0d68f6a8a 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -150,6 +150,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode); void Close(); + void PersistToPath(const wchar_t* path) const; + void RestoreFromPath(const wchar_t* path) const; #pragma region ICoreState const size_t TaskbarState() const noexcept; diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index 2db92a86a83..29cd96b6f8e 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -32,7 +32,8 @@ namespace Microsoft.Terminal.Control String ProfileName; String ProfileSource; - Boolean EnableUnfocusedAcrylic; + Boolean EnableUnfocusedAcrylic { get; }; + Guid SessionId { get; }; ScrollbarState ScrollState { get; }; String FontFace { get; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 303d6d61482..0e459e9238b 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -5,6 +5,7 @@ #include "TermControl.h" #include +#include #include "TermControlAutomationPeer.h" #include "../../renderer/atlas/AtlasEngine.h" @@ -1068,7 +1069,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return false; } + _interactivity.Initialize(); + + if (!_restorePath.empty()) + { + winrt::get_self(_core)->RestoreFromPath(_restorePath.c_str()); + _restorePath = {}; + } + + _core.Connection().Start(); } else { @@ -2259,6 +2269,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ExpandSelectionToWord(); } + void TermControl::RestoreFromPath(winrt::hstring path) + { + _restorePath = std::move(path); + } + + void TermControl::PersistToPath(const winrt::hstring& path) const + { + winrt::get_self(_core)->PersistToPath(path.c_str()); + } + void TermControl::Close() { if (!_IsClosing()) @@ -2293,6 +2313,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } } + void TermControl::Detach() { _revokers = {}; diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 2a8b28567ec..a12aa964664 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -45,6 +45,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ToggleMarkMode(); bool SwitchSelectionEndpoint(); bool ExpandSelectionToWord(); + void RestoreFromPath(winrt::hstring path); + void PersistToPath(const winrt::hstring& path) const; void Close(); Windows::Foundation::Size CharacterDimensions() const; Windows::Foundation::Size MinimumSize(); @@ -249,6 +251,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation SafeDispatcherTimer _blinkTimer; winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker; + winrt::hstring _restorePath; bool _showMarksInScrollbar{ false }; bool _isBackgroundLight{ false }; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index b105748ff64..8a91707c3af 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -93,6 +93,8 @@ namespace Microsoft.Terminal.Control Boolean SwitchSelectionEndpoint(); Boolean ExpandSelectionToWord(); void ClearBuffer(ClearBufferType clearType); + void RestoreFromPath(String path); + void PersistToPath(String path); void Close(); Windows.Foundation.Size CharacterDimensions { get; }; Windows.Foundation.Size MinimumSize { get; }; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 6eabcdab072..4a6c1187da5 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1549,6 +1549,11 @@ std::wstring_view Terminal::CurrentCommand() const return _activeBuffer().CurrentCommand(); } +void Terminal::SerializeMainBuffer(const wchar_t* destination) const +{ + _mainBuffer->Serialize(destination); +} + void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode) { const auto colorSelection = [this](const til::point coordStart, const til::point coordEnd, const TextAttribute& attr) { diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 80c5e3cc905..b10c0d893ac 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -126,6 +126,7 @@ class Microsoft::Terminal::Core::Terminal final : til::property AlwaysNotifyOnBufferRotation; std::wstring_view CurrentCommand() const; + void SerializeMainBuffer(const wchar_t* destination) const; #pragma region ITerminalApi // These methods are defined in TerminalApi.cpp diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index a276ebeff2b..598b77a4051 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -129,6 +129,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ss << fmt::format(L"--profile \"{}\" ", Profile()); } + + if (const auto id = SessionId(); id != winrt::guid{}) + { + const auto str = ::Microsoft::Console::Utils::GuidToString(id); + ss << fmt::format(L"--sessionId \"{}\" ", str); + } + // The caller is always expected to provide the evaluated profile in the // NewTerminalArgs, not the index // diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 6d83964cbbc..4062cee426c 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -309,6 +309,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ACTION_ARG(Windows::Foundation::IReference, TabColor, nullptr); ACTION_ARG(Windows::Foundation::IReference, ProfileIndex, nullptr); ACTION_ARG(winrt::hstring, Profile, L""); + ACTION_ARG(winrt::guid, SessionId, winrt::guid{}); ACTION_ARG(bool, AppendCommandLine, false); ACTION_ARG(Windows::Foundation::IReference, SuppressApplicationTitle, nullptr); ACTION_ARG(winrt::hstring, ColorScheme); @@ -322,6 +323,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static constexpr std::string_view TabColorKey{ "tabColor" }; static constexpr std::string_view ProfileIndexKey{ "index" }; static constexpr std::string_view ProfileKey{ "profile" }; + static constexpr std::string_view SessionIdKey{ "sessionId" }; static constexpr std::string_view AppendCommandLineKey{ "appendCommandLine" }; static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" }; static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; @@ -362,6 +364,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, TabTitleKey, args->_TabTitle); JsonUtils::GetValueForKey(json, ProfileIndexKey, args->_ProfileIndex); JsonUtils::GetValueForKey(json, ProfileKey, args->_Profile); + JsonUtils::GetValueForKey(json, SessionIdKey, args->_SessionId); JsonUtils::GetValueForKey(json, TabColorKey, args->_TabColor); JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle); JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme); @@ -383,6 +386,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::SetValueForKey(json, TabTitleKey, args->_TabTitle); JsonUtils::SetValueForKey(json, ProfileIndexKey, args->_ProfileIndex); JsonUtils::SetValueForKey(json, ProfileKey, args->_Profile); + JsonUtils::SetValueForKey(json, SessionIdKey, args->_SessionId); JsonUtils::SetValueForKey(json, TabColorKey, args->_TabColor); JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle); JsonUtils::SetValueForKey(json, ColorSchemeKey, args->_ColorScheme); @@ -400,6 +404,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_TabColor = _TabColor; copy->_ProfileIndex = _ProfileIndex; copy->_Profile = _Profile; + copy->_SessionId = _SessionId; copy->_SuppressApplicationTitle = _SuppressApplicationTitle; copy->_ColorScheme = _ColorScheme; copy->_Elevate = _Elevate; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 32f9005fdee..f8e6f4eb9cd 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -132,6 +132,7 @@ namespace Microsoft.Terminal.Settings.Model String TabTitle; Windows.Foundation.IReference TabColor; String Profile; // Either a GUID or a profile's name if the GUID isn't a match + Guid SessionId; Boolean AppendCommandLine; // We use IReference<> to treat some args as nullable where null means diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp index 347a180b708..30b44a55b21 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp @@ -240,8 +240,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Returns the application-global ApplicationState object. Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance() { - auto root{ GetBaseSettingsPath() }; - static auto state = winrt::make_self(root); + static auto state = winrt::make_self(GetBaseSettingsPath()); return *state; } @@ -294,6 +293,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return root; } + void ApplicationState::AppendPersistedWindowLayout(Model::WindowLayout layout) + { + { + const auto state = _state.lock(); + if (!state->PersistedWindowLayouts || !*state->PersistedWindowLayouts) + { + state->PersistedWindowLayouts = winrt::single_threaded_vector(); + } + state->PersistedWindowLayouts->Append(std::move(layout)); + } + + _throttler(); + } + // Generate all getter/setters #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \ type ApplicationState::name() const noexcept \ diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.h b/src/cascadia/TerminalSettingsModel/ApplicationState.h index bacffdba2f7..df7fd803e70 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.h +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.h @@ -69,6 +69,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void FromJson(const Json::Value& root, FileSource parseSource) const noexcept; Json::Value ToJson(FileSource parseSource) const noexcept; + void AppendPersistedWindowLayout(Model::WindowLayout layout); + // State getters/setters #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \ type name() const noexcept; \ diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.idl b/src/cascadia/TerminalSettingsModel/ApplicationState.idl index e48c1e7f86b..e35447773a6 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.idl +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.idl @@ -32,6 +32,8 @@ namespace Microsoft.Terminal.Settings.Model void Flush(); void Reset(); + void AppendPersistedWindowLayout(WindowLayout layout); + String SettingsHash; Windows.Foundation.Collections.IVector PersistedWindowLayouts; Windows.Foundation.Collections.IVector RecentCommands; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index 03f3ea071ec..03553c8b73d 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -106,6 +106,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static Model::CascadiaSettings LoadDefaults(); static Model::CascadiaSettings LoadAll(); + static winrt::hstring SettingsDirectory(); static winrt::hstring SettingsPath(); static winrt::hstring DefaultSettingsPath(); static winrt::hstring ApplicationDisplayName(); diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl index 1889871bb3e..165a7fed9fa 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl @@ -12,6 +12,7 @@ namespace Microsoft.Terminal.Settings.Model static CascadiaSettings LoadDefaults(); static CascadiaSettings LoadAll(); + static String SettingsDirectory { get; }; static String SettingsPath { get; }; static String DefaultSettingsPath { get; }; static Boolean IsPortableMode { get; }; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 244c200dad8..93af43319bc 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -1244,6 +1244,13 @@ winrt::hstring CascadiaSettings::_calculateHash(std::string_view settings, const return winrt::hstring{ hash }; } +// This returns something akin to %LOCALAPPDATA%\Packages\WindowsTerminalDev_8wekyb3d8bbwe\LocalState +// just like SettingsPath(), but without the trailing \settings.json. +winrt::hstring CascadiaSettings::SettingsDirectory() +{ + return winrt::hstring{ GetBaseSettingsPath().native() }; +} + // function Description: // - Returns the full path to the settings file, either within the application // package, or in its unpackaged location. This path is under the "Local diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 6f1d2932699..29b9bb811fe 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -126,6 +126,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (newTerminalArgs) { + if (const auto id = newTerminalArgs.SessionId(); id != winrt::guid{}) + { + defaultSettings.SessionId(id); + } + // Override commandline, starting directory if they exist in newTerminalArgs if (!newTerminalArgs.Commandline().empty()) { diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index d3410ba2628..57ec8659fd6 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -118,6 +118,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, hstring, ProfileName); INHERITABLE_SETTING(Model::TerminalSettings, hstring, ProfileSource); + INHERITABLE_SETTING(Model::TerminalSettings, guid, SessionId); INHERITABLE_SETTING(Model::TerminalSettings, bool, EnableUnfocusedAcrylic, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAcrylic, false); INHERITABLE_SETTING(Model::TerminalSettings, double, Opacity, UseAcrylic() ? 0.5 : 1.0); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl index 23c305ee4e0..32bf6111909 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl @@ -41,6 +41,7 @@ namespace Microsoft.Terminal.Settings.Model // The getters for these are already defined in IControlSettings. So // we're just adding the setters here, because TerminalApp likes to be // able to change these at runtime (e.g. when duplicating a pane). + Guid SessionId { set; }; String Commandline { set; }; String StartingDirectory { set; }; diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 8be39b44ca6..8fc12f493db 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -82,7 +82,6 @@ namespace RemotingUnitTests void Summon(const Remoting::SummonWindowBehavior& /*args*/) DIE; void RequestShowNotificationIcon() DIE; void RequestHideNotificationIcon() DIE; - winrt::hstring GetWindowLayout() DIE; void RequestQuitAll() DIE; void Quit() DIE; void AttachContentToWindow(Remoting::AttachRequest) DIE; @@ -97,7 +96,6 @@ namespace RemotingUnitTests til::typed_event<> HideNotificationIconRequested; til::typed_event<> QuitAllRequested; til::typed_event<> QuitRequested; - til::typed_event GetWindowLayoutRequested; til::typed_event AttachRequested; til::typed_event SendContentRequested; }; @@ -117,7 +115,6 @@ namespace RemotingUnitTests void SummonAllWindows() DIE; bool DoesQuakeWindowExist() DIE; winrt::Windows::Foundation::Collections::IVectorView GetPeasantInfos() DIE; - winrt::Windows::Foundation::Collections::IVector GetAllWindowLayouts() DIE; void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t, winrt::Windows::Foundation::IReference) DIE; void RequestSendContent(Remoting::RequestReceiveContentArgs) DIE; @@ -126,7 +123,6 @@ namespace RemotingUnitTests til::typed_event<> HideNotificationIconRequested; til::typed_event<> WindowCreated; til::typed_event<> WindowClosed; - til::typed_event QuitAllRequested; til::typed_event RequestNewWindow; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 816e4cda6e4..0fe39c1cc56 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -215,49 +215,51 @@ void AppHost::_HandleSessionRestore(const bool startedForContent) // we'll leave it here. const auto numPeasants = _windowManager.GetNumberOfPeasants(); // Don't attempt to session restore if we're just making a window for tear-out - if (!startedForContent && numPeasants == 1) + if (startedForContent || numPeasants != 1 || !_appLogic.ShouldUsePersistedLayout()) { - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (_appLogic.ShouldUsePersistedLayout() && - layouts && - layouts.Size() > 0) + return; + } + + const auto state = ApplicationState::SharedInstance(); + const auto layouts = state.PersistedWindowLayouts(); + + if (layouts && layouts.Size() > 0) + { + uint32_t startIdx = 0; + // We want to create a window for every saved layout. + // If we are the only window, and no commandline arguments were provided + // then we should just use the current window to load the first layout. + // Otherwise create this window normally with its commandline, and create + // a new window using the first saved layout information. + // The 2nd+ layout will always get a new window. + if (!_windowLogic.HasCommandlineArguments() && + !_appLogic.HasSettingsStartupActions()) { - uint32_t startIdx = 0; - // We want to create a window for every saved layout. - // If we are the only window, and no commandline arguments were provided - // then we should just use the current window to load the first layout. - // Otherwise create this window normally with its commandline, and create - // a new window using the first saved layout information. - // The 2nd+ layout will always get a new window. - if (!_windowLogic.HasCommandlineArguments() && - !_appLogic.HasSettingsStartupActions()) - { - _windowLogic.SetPersistedLayoutIdx(startIdx); - startIdx += 1; - } + _windowLogic.SetPersistedLayoutIdx(startIdx); + startIdx += 1; + } - // Create new windows for each of the other saved layouts. - for (const auto size = layouts.Size(); startIdx < size; startIdx += 1) - { - auto newWindowArgs = fmt::format(L"{0} -w new -s {1}", args.Commandline()[0], startIdx); - - STARTUPINFO si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - wil::unique_process_information pi; - - LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr, - newWindowArgs.data(), - nullptr, // lpProcessAttributes - nullptr, // lpThreadAttributes - false, // bInheritHandles - DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, // doCreationFlags - nullptr, // lpEnvironment - nullptr, // lpStartingDirectory - &si, // lpStartupInfo - &pi // lpProcessInformation - )); - } + // Create new windows for each of the other saved layouts. + for (const auto size = layouts.Size(); startIdx < size; startIdx += 1) + { + auto newWindowArgs = fmt::format(L"{0} -w new -s {1}", args.Commandline()[0], startIdx); + + STARTUPINFO si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + wil::unique_process_information pi; + + LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr, + newWindowArgs.data(), + nullptr, // lpProcessAttributes + nullptr, // lpThreadAttributes + false, // bInheritHandles + DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, // doCreationFlags + nullptr, // lpEnvironment + nullptr, // lpStartingDirectory + &si, // lpStartupInfo + &pi // lpProcessInformation + )); } } } @@ -317,7 +319,7 @@ void AppHost::Initialize() // Register the 'X' button of the window for a warning experience of multiple // tabs opened, this is consistent with Alt+F4 closing _windowCallbacks.WindowCloseButtonClicked = _window->WindowCloseButtonClicked([this]() { - _CloseRequested(nullptr, nullptr); + _windowLogic.CloseWindow(); }); // If the user requests a close in another way handle the same as if the 'X' // was clicked. @@ -350,7 +352,7 @@ void AppHost::Initialize() _windowCallbacks.AutomaticShutdownRequested = _window->AutomaticShutdownRequested([this]() { // Raised when the OS is beginning an update of the app. We will quit, // to save our state, before the OS manually kills us. - Remoting::WindowManager::RequestQuitAll(_peasant); + _quit(); }); // Load bearing: make sure the PropertyChanged handler is added before we @@ -362,7 +364,7 @@ void AppHost::Initialize() _windowLogic.Create(); _revokers.TitleChanged = _windowLogic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged }); - _revokers.LastTabClosed = _windowLogic.LastTabClosed(winrt::auto_revoke, { this, &AppHost::LastTabClosed }); + _revokers.CloseWindowRequested = _windowLogic.CloseWindowRequested(winrt::auto_revoke, { this, &AppHost::_CloseRequested }); _revokers.SetTaskbarProgress = _windowLogic.SetTaskbarProgress(winrt::auto_revoke, { this, &AppHost::SetTaskbarProgress }); _revokers.IdentifyWindowsRequested = _windowLogic.IdentifyWindowsRequested(winrt::auto_revoke, { this, &AppHost::_IdentifyWindowsRequested }); _revokers.RenameWindowRequested = _windowLogic.RenameWindowRequested(winrt::auto_revoke, { this, &AppHost::_RenameWindowRequested }); @@ -382,22 +384,6 @@ void AppHost::Initialize() _revokers.RequestReceiveContent = _windowLogic.RequestReceiveContent(winrt::auto_revoke, { this, &AppHost::_handleReceiveContent }); _revokers.SendContentRequested = _peasant.SendContentRequested(winrt::auto_revoke, { this, &AppHost::_handleSendContent }); - // Add our GetWindowLayoutRequested handler AFTER the xaml island is - // started. Our _GetWindowLayoutAsync handler requires us to be able to work - // on our UI thread, which requires that we have a Dispatcher ready for us - // to move to. If we set up this callback in the ctor, then it is possible - // for there to be a time slice where - // * the monarch creates the peasant for us, - // * we get constructed (registering the callback) - // * then the monarch attempts to query all _peasants_ for their layout, - // coming back to ask us even before XAML has been created. - _GetWindowLayoutRequestedToken = _peasant.GetWindowLayoutRequested([this](auto&&, - const Remoting::GetWindowLayoutArgs& args) { - // The peasants are running on separate threads, so they'll need to - // swap what context they are in to the ui thread to get the actual layout. - args.WindowLayoutJsonAsync(_GetWindowLayoutAsync()); - }); - // BODGY // On certain builds of Windows, when Terminal is set as the default // it will accumulate an unbounded amount of queued animations while @@ -452,6 +438,16 @@ void AppHost::Close() } } +winrt::fire_and_forget AppHost::_quit() +{ + const auto peasant = _peasant; + + co_await winrt::resume_background(); + + ApplicationState::SharedInstance().PersistedWindowLayouts(nullptr); + peasant.RequestQuitAll(); +} + void AppHost::_revokeWindowCallbacks() { // You'll recall, IslandWindow isn't a WinRT type so it can't have auto-revokers. @@ -514,42 +510,6 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* _windowManager.UpdateActiveTabTitle(newTitle, _peasant); } -// Method Description: -// - Called when no tab is remaining to close the window. -// Arguments: -// - sender: unused -// - LastTabClosedEventArgs: unused -// Return Value: -// - -void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& args) -{ - // We don't want to try to save layouts if we are about to close. - _peasant.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); - - // If the user closes the last tab, in the last window, _by closing the tab_ - // (not by closing the whole window), we need to manually persist an empty - // window state here. That will cause the terminal to re-open with the usual - // settings (not the persisted state) - if (args.ClearPersistedState() && - _windowManager.GetNumberOfPeasants() == 1) - { - _windowLogic.ClearPersistedWindowState(); - } - // Remove ourself from the list of peasants so that we aren't included in - // any future requests. This will also mean we block until any existing - // event handler finishes. - _windowManager.SignalClose(_peasant); - - if (Utils::IsWindows11()) - { - PostQuitMessage(0); - } - else - { - PostMessageW(_window->GetInteropHandle(), WM_REFRIGERATE, 0, 0); - } -} - LaunchPosition AppHost::_GetWindowLaunchPosition() { LaunchPosition pos{}; @@ -952,42 +912,6 @@ winrt::fire_and_forget AppHost::_peasantNotifyActivateWindow() }); } -// Method Description: -// - Asynchronously get the window layout from the current page. This is -// done async because we need to switch between the ui thread and the calling -// thread. -// - NB: The peasant calling this must not be running on the UI thread, otherwise -// they will crash since they just call .get on the async operation. -// Arguments: -// - -// Return Value: -// - The window layout as a json string. -winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowLayoutAsync() -{ - winrt::hstring layoutJson = L""; - - auto weakThis{ weak_from_this() }; - - // Use the main thread since we are accessing controls. - co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); - - const auto strongThis = weakThis.lock(); - // GH #16235: If we don't have a window logic, we're already refrigerating, and won't have our _window either. - if (!strongThis || _windowLogic == nullptr) - { - co_return layoutJson; - } - - try - { - const auto pos = _GetWindowLaunchPosition(); - layoutJson = _windowLogic.GetWindowLayoutJson(pos); - } - CATCH_LOG() - - co_return layoutJson; -} - void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::SummonWindowBehavior& args) { @@ -1264,13 +1188,14 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation: } _windowLogic.Quit(); + PostQuitMessage(0); } // Raised from TerminalWindow. We handle by bubbling the request to the window manager. void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - Remoting::WindowManager::RequestQuitAll(_peasant); + _quit(); } void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&, @@ -1370,9 +1295,25 @@ void AppHost::_WindowMoved() void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - const auto pos = _GetWindowLaunchPosition(); - const bool isLastWindow = _windowManager.GetNumberOfPeasants() == 1; - _windowLogic.CloseWindow(pos, isLastWindow); + if (_windowManager.GetNumberOfPeasants() <= 1) + { + _quit(); + return; + } + + // Remove ourself from the list of peasants so that we aren't included in + // any future requests. This will also mean we block until any existing + // event handler finishes. + _windowManager.SignalClose(_peasant); + + if (Utils::IsWindows11()) + { + PostQuitMessage(0); + } + else + { + PostMessageW(_window->GetInteropHandle(), WM_REFRIGERATE, 0, 0); + } } void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 25cf599af74..a22fc85a948 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -20,7 +20,6 @@ class AppHost : public std::enable_shared_from_this std::unique_ptr window = nullptr) noexcept; void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); - void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args); void Initialize(); void Close(); @@ -64,8 +63,7 @@ class AppHost : public std::enable_shared_from_this uint32_t _launchShowWindowCommand{ SW_NORMAL }; - void _preInit(); - + winrt::fire_and_forget _quit(); void _revokeWindowCallbacks(); void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); @@ -100,8 +98,6 @@ class AppHost : public std::enable_shared_from_this void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); - winrt::Windows::Foundation::IAsyncOperation _GetWindowLayoutAsync(); - void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args); @@ -112,10 +108,6 @@ class AppHost : public std::enable_shared_from_this winrt::fire_and_forget _RenameWindowRequested(const winrt::Windows::Foundation::IInspectable sender, const winrt::TerminalApp::RenameWindowRequestedArgs args); - GUID _CurrentDesktopGuid(); - - bool _LazyLoadDesktopManager(); - void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& args); @@ -168,8 +160,6 @@ class AppHost : public std::enable_shared_from_this void _stopFrameTimer(); void _updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); - winrt::event_token _GetWindowLayoutRequestedToken; - // 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 // cause us to dtor the old one, which will immediately call revoke on all @@ -194,7 +184,7 @@ class AppHost : public std::enable_shared_from_this winrt::TerminalApp::TerminalWindow::SystemMenuChangeRequested_revoker SystemMenuChangeRequested; winrt::TerminalApp::TerminalWindow::ChangeMaximizeRequested_revoker ChangeMaximizeRequested; winrt::TerminalApp::TerminalWindow::TitleChanged_revoker TitleChanged; - winrt::TerminalApp::TerminalWindow::LastTabClosed_revoker LastTabClosed; + winrt::TerminalApp::TerminalWindow::CloseWindowRequested_revoker CloseWindowRequested; winrt::TerminalApp::TerminalWindow::SetTaskbarProgress_revoker SetTaskbarProgress; winrt::TerminalApp::TerminalWindow::IdentifyWindowsRequested_revoker IdentifyWindowsRequested; winrt::TerminalApp::TerminalWindow::RenameWindowRequested_revoker RenameWindowRequested; @@ -208,7 +198,6 @@ class AppHost : public std::enable_shared_from_this winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged; winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged; - winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; winrt::Microsoft::Terminal::Remoting::Peasant::SendContentRequested_revoker SendContentRequested; } _revokers{}; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 36d49e2c09f..0fc28a20433 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -348,116 +348,17 @@ void WindowEmperor::_becomeMonarch() _revokers.WindowCreated = _manager.WindowCreated(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged }); _revokers.WindowClosed = _manager.WindowClosed(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged }); - - // If the monarch receives a QuitAll event it will signal this event to be - // ran before each peasant is closed. - _revokers.QuitAllRequested = _manager.QuitAllRequested(winrt::auto_revoke, { this, &WindowEmperor::_quitAllRequested }); - - // The monarch should be monitoring if it should save the window layout. - // We want at least some delay to prevent the first save from overwriting - _getWindowLayoutThrottler.emplace(std::chrono::seconds(10), [this]() { _saveWindowLayoutsRepeat(); }); - _getWindowLayoutThrottler.value()(); } // sender and args are always nullptr void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - if (_getWindowLayoutThrottler) - { - _getWindowLayoutThrottler.value()(); - } - // If we closed out the quake window, and don't otherwise need the tray // icon, let's get rid of it. _checkWindowsForNotificationIcon(); } -// Raised from our windowManager (on behalf of the monarch). We respond by -// giving the monarch an async function that the manager should wait on before -// completing the quit. -void WindowEmperor::_quitAllRequested(const winrt::Windows::Foundation::IInspectable&, - const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& args) -{ - _quitting = true; - - // Make sure that the current timer is destroyed so that it doesn't attempt - // to run while we are in the middle of quitting. - if (_getWindowLayoutThrottler.has_value()) - { - _getWindowLayoutThrottler.reset(); - } - - // Tell the monarch to wait for the window layouts to save before - // everyone quits. - args.BeforeQuitAllAction(_saveWindowLayouts()); -} - -#pragma region LayoutPersistence - -winrt::Windows::Foundation::IAsyncAction WindowEmperor::_saveWindowLayouts() -{ - // Make sure we run on a background thread to not block anything. - co_await winrt::resume_background(); - - if (_app.Logic().ShouldUsePersistedLayout()) - { - try - { - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_SaveWindowLayouts_Collect", - TraceLoggingDescription("Logged when collecting window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - const auto layoutJsons = _manager.GetAllWindowLayouts(); - - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_SaveWindowLayouts_Save", - TraceLoggingDescription("Logged when writing window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - _app.Logic().SaveWindowLayoutJsons(layoutJsons); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_SaveWindowLayouts_Failed", - TraceLoggingDescription("An error occurred when collecting or writing window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - - co_return; -} - -winrt::fire_and_forget WindowEmperor::_saveWindowLayoutsRepeat() -{ - // Make sure we run on a background thread to not block anything. - co_await winrt::resume_background(); - - co_await _saveWindowLayouts(); - - // Don't need to save too frequently. - co_await winrt::resume_after(30s); - - // As long as we are supposed to keep saving, request another save. - // This will be delayed by the throttler so that at most one save happens - // per 10 seconds, if a save is requested by another source simultaneously. - if (_getWindowLayoutThrottler.has_value()) - { - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_requestGetLayout", - TraceLoggingDescription("Logged when triggering a throttled write of the window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - _getWindowLayoutThrottler.value()(); - } -} #pragma endregion #pragma region WindowProc @@ -584,6 +485,80 @@ void WindowEmperor::_finalizeSessionPersistence() const // Ensure to write the state.json before we TerminateProcess() state.Flush(); + + if (!_app.Logic().ShouldUsePersistedLayout()) + { + return; + } + + // Get the "buffer_{guid}.txt" files that we expect to be there + std::unordered_set sessionIds; + if (const auto layouts = state.PersistedWindowLayouts()) + { + for (const auto& windowLayout : layouts) + { + for (const auto& actionAndArgs : windowLayout.TabLayout()) + { + const auto args = actionAndArgs.Args(); + NewTerminalArgs terminalArgs{ nullptr }; + + if (const auto tabArgs = args.try_as()) + { + terminalArgs = tabArgs.TerminalArgs(); + } + else if (const auto paneArgs = args.try_as()) + { + terminalArgs = paneArgs.TerminalArgs(); + } + + if (terminalArgs) + { + sessionIds.emplace(terminalArgs.SessionId()); + } + } + } + } + + // Remove the "buffer_{guid}.txt" files that shouldn't be there + // e.g. "buffer_FD40D746-163E-444C-B9B2-6A3EA2B26722.txt" + { + const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } }; + const auto filter = settingsDirectory / L"buffer_*"; + WIN32_FIND_DATAW ffd; + + // This could also use std::filesystem::directory_iterator. + // I was just slightly bothered by how it doesn't have a O(1) .filename() + // function, even though the underlying Win32 APIs provide it for free. + // Both work fine. + const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) }; + if (!handle) + { + return; + } + + do + { + const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName)); + const std::wstring_view name{ &ffd.cFileName[0], nameLen }; + + if (nameLen != 47) + { + continue; + } + + wchar_t guidStr[39]; + guidStr[0] = L'{'; + memcpy(&guidStr[1], name.data() + 7, 36 * sizeof(wchar_t)); + guidStr[37] = L'}'; + guidStr[38] = L'\0'; + + const auto id = Utils::GuidFromString(&guidStr[0]); + if (!sessionIds.contains(id)) + { + std::filesystem::remove(settingsDirectory / name); + } + } while (FindNextFileW(handle.get(), &ffd)); + } } #pragma endregion diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 9f44a276c3c..a35032e6d5e 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -44,8 +44,6 @@ class WindowEmperor : public std::enable_shared_from_this til::shared_mutex>> _oldThreads; - std::optional> _getWindowLayoutThrottler; - winrt::event_token _WindowCreatedToken; winrt::event_token _WindowClosedToken; @@ -61,15 +59,10 @@ class WindowEmperor : public std::enable_shared_from_this void _becomeMonarch(); void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); - void _quitAllRequested(const winrt::Windows::Foundation::IInspectable&, - const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs&); winrt::fire_and_forget _windowIsQuakeWindowChanged(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable args); winrt::fire_and_forget _windowRequestUpdateSettings(); - winrt::Windows::Foundation::IAsyncAction _saveWindowLayouts(); - winrt::fire_and_forget _saveWindowLayoutsRepeat(); - void _createMessageWindow(); void _hotkeyPressed(const long hotkeyIndex); @@ -90,6 +83,5 @@ class WindowEmperor : public std::enable_shared_from_this { winrt::Microsoft::Terminal::Remoting::WindowManager::WindowCreated_revoker WindowCreated; winrt::Microsoft::Terminal::Remoting::WindowManager::WindowClosed_revoker WindowClosed; - winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; } _revokers{}; }; diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index a882cd8b0cf..0c4a50308d3 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -56,6 +56,7 @@ #define CONTROL_SETTINGS(X) \ X(winrt::hstring, ProfileName) \ X(winrt::hstring, ProfileSource) \ + X(winrt::guid, SessionId) \ X(bool, EnableUnfocusedAcrylic, false) \ X(winrt::hstring, Padding, DEFAULT_PADDING) \ X(winrt::hstring, FontFace, L"Consolas") \ diff --git a/src/inc/til/small_vector.h b/src/inc/til/small_vector.h index a88927e7159..f078d37fbe4 100644 --- a/src/inc/til/small_vector.h +++ b/src/inc/til/small_vector.h @@ -577,18 +577,26 @@ namespace til void resize(size_type new_size) { - _generic_resize(new_size, [](auto&& beg, auto&& end) { + _generic_resize(new_size, [](iterator&& beg, iterator&& end) { std::uninitialized_value_construct(beg, end); }); } void resize(size_type new_size, const_reference value) { - _generic_resize(new_size, [&](auto&& beg, auto&& end) { + _generic_resize(new_size, [&](iterator&& beg, iterator&& end) { std::uninitialized_fill(beg, end, value); }); } + void resize_and_overwrite(size_type new_size, auto op) + requires std::is_trivial_v + { + _size = 0; + reserve(new_size); + _size = std::move(op)(_data, new_size); + } + void shrink_to_fit() { if (_capacity == N || _size == _capacity) @@ -863,7 +871,7 @@ namespace til // An optimization for the most common vector type which is trivially constructible, destructible and copyable. // This allows us to drop exception handlers (= no need to push onto the stack) and replace two moves with just one. - if constexpr (noexcept(func(begin())) && std::is_trivially_destructible_v && std::is_trivially_copyable_v) + if constexpr (noexcept(func(begin())) && std::is_trivial_v) { _size = new_size; From 9f08ee7af9056dfcf244d8d1c4619a17558fd11d Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 29 Mar 2024 15:46:51 +0100 Subject: [PATCH 195/603] Buffer Restore: Fix turning off intense/faint (#16970) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This takes care of an edge case in regards to SGR 22: It turns off both intense and faint attributes which means that we may need to turn on one of the two if only one of them turned off. Additionally, this removes the mapping for `BottomGridline` which has no real VT equivalent anyway. ## Validation Steps Performed * Turn session restore on * In pwsh write: ```pwsh "`e[1;2mboth`e[0;1mintense`e[m`n`e[1;2mboth`e[0;2mfaint`e[m" ``` * Close the app and open the `buffer_*.txt` file next to settings.json * It contains... ✅ ``` ␛[1m␛[2mboth␛[22;1mintense␛[22m ␛[1m␛[2mboth␛[22;2mfaint␛[22m ``` --- src/buffer/out/textBuffer.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 62371474adc..40cfc6b17ad 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2557,7 +2557,32 @@ void TextBuffer::Serialize(const wchar_t* destination) const if (previousAttr != attr) { - const auto attrDelta = attr ^ previousAttr; + auto attrDelta = attr ^ previousAttr; + + // There's no escape sequence that only turns off either bold/intense or dim/faint. SGR 22 turns off both. + // This results in two issues in our generic "Mapping" code below. Assuming, both Intense and Faint were on... + // * ...and either turned off, it would emit SGR 22 which turns both attributes off = Wrong. + // * ...and both are now off, it would emit SGR 22 twice. + // + // This extra branch takes care of both issues. If both attributes turned off it'll emit a single \x1b[22m, + // if faint turned off \x1b[22;1m (intense is still on), and \x1b[22;2m if intense turned off (vice versa). + if (WI_AreAllFlagsSet(previousAttr, CharacterAttributes::Intense | CharacterAttributes::Faint) && + WI_IsAnyFlagSet(attrDelta, CharacterAttributes::Intense | CharacterAttributes::Faint)) + { + wchar_t buf[8] = L"\x1b[22m"; + size_t len = 5; + + if (WI_IsAnyFlagSet(attr, CharacterAttributes::Intense | CharacterAttributes::Faint)) + { + buf[4] = L';'; + buf[5] = WI_IsAnyFlagSet(attr, CharacterAttributes::Intense) ? L'1' : L'2'; + buf[6] = L'm'; + len = 7; + } + + buffer.append(&buf[0], len); + WI_ClearAllFlags(attrDelta, CharacterAttributes::Intense | CharacterAttributes::Faint); + } { struct Mapping @@ -2574,7 +2599,6 @@ void TextBuffer::Serialize(const wchar_t* destination) const { CharacterAttributes::Faint, { 22, 2 } }, { CharacterAttributes::TopGridline, { 55, 53 } }, { CharacterAttributes::ReverseVideo, { 27, 7 } }, - { CharacterAttributes::BottomGridline, { 24, 4 } }, }; for (const auto& mapping : mappings) { From 75dea24d6b656460f83369850fa69d1c9c7a0f8d Mon Sep 17 00:00:00 2001 From: Windows Console Service Bot <14666831+consvc@users.noreply.github.com> Date: Fri, 29 Mar 2024 09:47:11 -0500 Subject: [PATCH 196/603] Localization Updates - main - 03/29/2024 03:04:36 (#16967) --- .../Resources/es-ES/Resources.resw | 4 ++ .../Resources/de-DE/Resources.resw | 8 ---- .../Resources/es-ES/Resources.resw | 34 ++++++++++---- .../Resources/fr-FR/Resources.resw | 45 +++++++++++++++---- .../Resources/it-IT/Resources.resw | 34 ++++++++++---- .../Resources/ja-JP/Resources.resw | 8 ---- .../Resources/ko-KR/Resources.resw | 8 ---- .../Resources/pt-BR/Resources.resw | 8 ---- .../Resources/qps-ploc/Resources.resw | 6 +-- .../Resources/qps-ploca/Resources.resw | 6 +-- .../Resources/qps-plocm/Resources.resw | 6 +-- .../Resources/ru-RU/Resources.resw | 8 ---- .../Resources/zh-CN/Resources.resw | 34 ++++++++++---- .../Resources/zh-TW/Resources.resw | 8 ---- .../Resources/es-ES/Resources.resw | 3 ++ .../Resources/fr-FR/Resources.resw | 3 ++ .../Resources/it-IT/Resources.resw | 3 ++ .../Resources/zh-CN/Resources.resw | 3 ++ 18 files changed, 134 insertions(+), 95 deletions(-) diff --git a/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw b/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw index 30231e7f63e..7ecef8b9222 100644 --- a/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/es-ES/Resources.resw @@ -213,6 +213,10 @@ Instale la fuente que falta o elija otra. El representador encontró un error inesperado: {0} {0} is an error code. + + No se pueden encontrar las siguientes fuentes: {0}. Instálelas o elija fuentes diferentes. + {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + El representador encontró un error inesperado: {0:#010x} {1} {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw index 9214556f77b..96d50e58db8 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw @@ -989,10 +989,6 @@ Dieses Profil als Administrator ausführen Header for a control to toggle whether the profile should always open elevated (in an admin window) - - Wenn diese Option aktiviert ist, wird das Profil automatisch als Administrator in einem Terminalfenster geöffnet. Wenn das aktuelle Fenster bereits als Administrator ausgeführt wird, wird es in diesem Fenster geöffnet. - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Größe des Verlaufs Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1225,10 +1221,6 @@ Wenn diese Option aktiviert ist, generiert das Terminal beim Erstellen neuer Registerkarten oder Bereiche mit diesem Profil einen neuen Umgebungsblock. Wenn diese Option deaktiviert ist, erbt die Registerkarte/der Bereich stattdessen die Variablen, mit denen das Terminal gestartet wurde. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Experimentellen Passthrough für virtuelles Terminal aktivieren - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Menü bei Rechtsklick anzeigen This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index e4574ab1f35..db4abb81b4f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -309,6 +309,10 @@ Aplicación de terminal que se inicia cuando se ejecuta una aplicación de línea de comandos sin una sesión existente, como en el menú Inicio o en el cuadro de diálogo Ejecutar. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + API de gráficos + This text is shown next to a list of choices. + Direct3D 11 proporciona una experiencia con más rendimiento y características, mientras que Direct2D es más estable. La opción predeterminada "Automático" seleccionará la API que mejor se adapte a su hardware gráfico. Si experimenta problemas importantes, considere la posibilidad de usar Direct2D. @@ -316,10 +320,24 @@ Automático The default choice between multiple graphics APIs. + + Direct2D + + + Direct3D 11 + + + Deshabilitar la invalidación parcial de la cadena de intercambio + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + De forma predeterminada, el representador de texto usa una cadena de intercambio de FLIP_SEQUENTIAL y declara rectángulos modificados a través de la API de Present1. Cuando esta configuración está habilitada, se usará una cadena de intercambio de FLIP_DISCARD y no se declarará ningún rectángulo con modificaciones. Si uno u otro es mejor depende de su hardware y de otros factores. {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + Uso de la representación de software (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + Cuando está habilitado, el terminal usará un rasterizador de software (WARP). Esta configuración debe dejarse deshabilitada en casi todas las circunstancias. {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". @@ -971,10 +989,6 @@ Ejecutar este perfil como Administrador Header for a control to toggle whether the profile should always open elevated (in an admin window) - - Si se habilita, el perfil se abrirá automáticamente en una ventana de terminal de administración. Si la ventana actual ya se está ejecutando como administrador, se abrirá en esta ventana. - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Tamaño del historial Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1207,10 +1221,6 @@ Cuando está habilitado, el terminal generará un nuevo bloque de entorno al crear nuevas pestañas o paneles con este perfil. Cuando se deshabilita, la pestaña o el panel heredarán las variables con las que se inició el terminal. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Habilitar paso a través de terminal virtual experimental - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Mostrar un menú al hacer clic con el botón derecho This controls how a right-click behaves in the terminal @@ -1786,4 +1796,12 @@ Más información. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + Fuentes que faltan: + This is a label that is followed by a list of missing fonts. + + + Fuentes no monoespaciadas: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index 2f56f3ebd4c..f14f47e5848 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -309,10 +309,39 @@ L’application Terminal qui se lance lorsqu’une application de ligne de commande est exécutée sans session existante, par exemple à partir du menu Démarrer ou de la boîte de dialogue Exécuter. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + Graphics API + This text is shown next to a list of choices. + + + Direct3D 11 offre une expérience plus performante et riche en fonctionnalités, tandis que Direct2D est plus stable. L’option par défaut « Automatique » sélectionne l’API qui correspond le mieux à votre matériel graphique. Si vous rencontrez des problèmes importants, envisagez d’utiliser Direct2D. + Automatique The default choice between multiple graphics APIs. + + Direct2D + + + Direct3D 11 + + + Désactiver l’invalidation partielle de la chaîne d’échange + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + + + Par défaut, le moteur de rendu de texte utilise une chaîne de permutation FLIP_SEQUENTIAL et déclare les rectangles sales via l’API Present1. Lorsque ce paramètre est activé, une chaîne d’échange FLIP_DISCARD est utilisée à la place, et aucun rectangle sale n’est déclaré. La supériorité de l’un ou de l’autre dépend de votre matériel et de divers autres facteurs. + {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + + Utiliser le rendu logiciel (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + + + Lorsque cette option est activée, le terminal utilise un logiciel de tramage (WARP). Ce paramètre doit être désactivé dans presque toutes les circonstances. + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + Colonnes Header for a control to choose the number of columns in the terminal's text grid. @@ -960,10 +989,6 @@ Exécuter ce profil en tant qu’administrateur Header for a control to toggle whether the profile should always open elevated (in an admin window) - - Si l’option est activée, le profil s’ouvre automatiquement dans une fenêtre de terminal d’administration. Si la fenêtre active est déjà en cours d’exécution en tant qu’administrateur, elle s’ouvre dans cette fenêtre. - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Taille de l’historique Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1196,10 +1221,6 @@ Lorsqu’il est activé, le terminal génère un nouveau bloc d’environnement lors de la création de nouveaux onglets ou volets avec ce profil. Lorsqu’il est désactivé, l’onglet/volet hérite à la place des variables avec laquelle le terminal a été démarré. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Activer le relais de terminal virtuel expérimental - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Afficher un menu en cliquant avec le bouton droit This controls how a right-click behaves in the terminal @@ -1775,4 +1796,12 @@ En savoir plus. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + Polices manquantes : + This is a label that is followed by a list of missing fonts. + + + Polices non monospaciales : + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 3d0fb72f844..5bb075b01f9 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -309,6 +309,10 @@ L'applicazione terminale che viene avviata quando viene eseguita un'applicazione della riga di comando senza una sessione esistente, ad esempio dalla finestra di dialogo menu Start o Esegui. A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + API grafica + This text is shown next to a list of choices. + Direct3D 11 offre un'esperienza più performante e con funzionalità più elevate, mentre Direct2D è più stabile. L'opzione predefinita "Automatico" sceglierà l'API più adatta all'hardware grafico. Se si verificano problemi significativi, provare a usare Direct2D. @@ -316,10 +320,24 @@ Automatico The default choice between multiple graphics APIs. + + Direct2D + + + Direct3D 11 + + + Disabilitare invalidazione catena di scambio parziale + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + Per impostazione predefinita, il renderer di testo usa una catena di scambio FLIP_SEQUENTIAL e dichiara rettangoli modificati ma non modificati tramite l'API Present1. Quando questa impostazione è abilitata, verrà utilizzata una catena di scambio FLIP_DISCARD e non verranno dichiarati rettangoli modificati ma non modificati. Il miglioramento dell'uno o dell'altro dipende dall'hardware e da vari altri fattori. {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + Usare il rendering software (WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + Se questa opzione è abilitata, il terminale userà un rasterizzatore software (WARP). Questa impostazione deve essere lasciata disabilitata in quasi tutte le circostanze. {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". @@ -971,10 +989,6 @@ Esegui questo profilo come amministratore Header for a control to toggle whether the profile should always open elevated (in an admin window) - - Se questa opzione è abilitata, il profilo verrà aperto automaticamente in una finestra del terminale di amministrazione. Se la finestra corrente è già in esecuzione come amministratore, verrà aperta in questa finestra. - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Dimensioni cronologia Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1207,10 +1221,6 @@ Se abilitato, il Terminale genera un nuovo blocco ambiente quando crea nuove schede o riquadri con questo profilo. Se disattivato, la scheda/riquadro erediterà le variabili con cui è stato avviato il Terminale. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Abilitare il pass-through sperimentale del terminale virtuale - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Visualizzare un menu al clic con il pulsante destro del mouse This controls how a right-click behaves in the terminal @@ -1786,4 +1796,12 @@ Scopri di più. A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + Tipi di carattere mancanti: + This is a label that is followed by a list of missing fonts. + + + Caratteri senza spaziatura fissa: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index 6cd0a869dd7..e9f4b3eb2f2 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -989,10 +989,6 @@ このプロファイルを管理者として実行する Header for a control to toggle whether the profile should always open elevated (in an admin window) - - 有効にすると、プロファイルは管理者ターミナル ウィンドウで自動的に開きます。現在のウィンドウが既に管理者として実行されている場合は、このウィンドウで開きます。 - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - 履歴のサイズ Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1225,10 +1221,6 @@ 有効にすると、ターミナルは、このプロファイルで新しいタブまたはウィンドウを作成するときに新しい環境ブロックを生成します。無効にすると、代わりにタブ/ペインはターミナルが開始された変数を継承します。 A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - 試験的な仮想ターミナル パススルーを有効にする - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - 右クリックでメニューを表示する This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index 40e83f7dfc4..7e4a769c59b 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -989,10 +989,6 @@ 이 프로필을 관리자 권한으로 실행 Header for a control to toggle whether the profile should always open elevated (in an admin window) - - 사용하도록 설정하면 프로필이 관리 터미널 창에서 자동으로 열립니다. 현재 창이 관리자 권한으로 이미 실행되고 있는 경우 이 창에서 열립니다. - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - 기록 크기 Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1225,10 +1221,6 @@ 사용하도록 설정하면 이 프로필을 사용하여 새 탭이나 창을 만들 때 터미널이 새 환경 블록을 생성합니다. 사용하지 않도록 설정하면 탭이나 창이 터미널이 시작된 변수를 상속합니다. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - 실험적 가상 터미널 통과 사용 - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - 마우스 오른쪽 단추를 클릭할 때 메뉴 표시 This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index 1121be023e6..8352f42f0da 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -989,10 +989,6 @@ Executar esse perfil como Administrador Header for a control to toggle whether the profile should always open elevated (in an admin window) - - Se habilitado, o perfil será aberto automaticamente em uma janela do terminal admin. Se a janela atual já estiver em execução como administrador, ela será aberta nesta janela. - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Tamanho de histórico Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1225,10 +1221,6 @@ Quando habilitado, o Terminal gerará um novo bloco de ambiente ao criar novas guias ou painéis com esse perfil. Quando desabilitado, a guia/painel herdará as variáveis com as quais o Terminal foi iniciado. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Habilitar passagem de terminal virtual experimental - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Exibir um menu ao clicar com o botão direito do mouse This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw index 98bd519ebb1..4d9d01dacaf 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw @@ -990,7 +990,7 @@ Header for a control to toggle whether the profile should always open elevated (in an admin window) - ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ'ŀℓ σрèń ĩй тħїŝ ẅīйδõẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ ẁїłℓ òρĕй іп тħΐş ωϊⁿđоẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". @@ -1225,10 +1225,6 @@ Ẁћέл εņаьŀèď, τħё Ŧëгmĩňªľ ẅīŀℓ ğεлěяαте å ʼnэщ еńνîřόńмęñт ьŀøĉĸ ŵħ℮л čґēãťîņğ ʼnεώ τâвѕ όя рªлёѕ щĭτћ тĥĭѕ ρŗòƒΐℓė. Ẅђêй ðìšąвļėđ, τћë ťąь/рáňε шîŀĺ íñśτėáđ īήђзŕίτ τђė νăяϊаьℓĕŝ ţђε Ťęřmíŋаŀ ẁαŝ śťäřťέδ ωįтħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Зńăьℓē эхφзґímėпτªŀ νіѓтűǻļ τ℮ґмϊñāļ φаśşтнґöúģн !!! !!! !!! !!! !! - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Ðįşφłąÿ ǻ мèηΰ όή řïĝĥť-ćľїçķ !!! !!! !!! This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw index 98bd519ebb1..4d9d01dacaf 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw @@ -990,7 +990,7 @@ Header for a control to toggle whether the profile should always open elevated (in an admin window) - ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ'ŀℓ σрèń ĩй тħїŝ ẅīйδõẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ ẁїłℓ òρĕй іп тħΐş ωϊⁿđоẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". @@ -1225,10 +1225,6 @@ Ẁћέл εņаьŀèď, τħё Ŧëгmĩňªľ ẅīŀℓ ğεлěяαте å ʼnэщ еńνîřόńмęñт ьŀøĉĸ ŵħ℮л čґēãťîņğ ʼnεώ τâвѕ όя рªлёѕ щĭτћ тĥĭѕ ρŗòƒΐℓė. Ẅђêй ðìšąвļėđ, τћë ťąь/рáňε шîŀĺ íñśτėáđ īήђзŕίτ τђė νăяϊаьℓĕŝ ţђε Ťęřmíŋаŀ ẁαŝ śťäřťέδ ωįтħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Зńăьℓē эхφзґímėпτªŀ νіѓтűǻļ τ℮ґмϊñāļ φаśşтнґöúģн !!! !!! !!! !!! !! - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Ðįşφłąÿ ǻ мèηΰ όή řïĝĥť-ćľїçķ !!! !!! !!! This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw index 98bd519ebb1..4d9d01dacaf 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw @@ -990,7 +990,7 @@ Header for a control to toggle whether the profile should always open elevated (in an admin window) - ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ'ŀℓ σрèń ĩй тħїŝ ẅīйδõẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + ̃ éņãвļзδ, ŧĥĕ рŗоƒīľё ẅιľł óрėŋ ΐή åň Ąďmįŋ теѓmίидŀ шïпδσώ аůţòмăтìсаļļγ. Ĭƒ ŧĥе čцѓґ℮ⁿť ẁįлđόŵ їŝ áŀřėãďу ŕύŋņïπġ āš αðмīʼn, ϊţ ẁїłℓ òρĕй іп тħΐş ωϊⁿđоẁ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! A description for what the "elevate" setting does. Presented near "Profile_Elevate". @@ -1225,10 +1225,6 @@ Ẁћέл εņаьŀèď, τħё Ŧëгmĩňªľ ẅīŀℓ ğεлěяαте å ʼnэщ еńνîřόńмęñт ьŀøĉĸ ŵħ℮л čґēãťîņğ ʼnεώ τâвѕ όя рªлёѕ щĭτћ тĥĭѕ ρŗòƒΐℓė. Ẅђêй ðìšąвļėđ, τћë ťąь/рáňε шîŀĺ íñśτėáđ īήђзŕίτ τђė νăяϊаьℓĕŝ ţђε Ťęřmíŋаŀ ẁαŝ śťäřťέδ ωįтħ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Зńăьℓē эхφзґímėпτªŀ νіѓтűǻļ τ℮ґмϊñāļ φаśşтнґöúģн !!! !!! !!! !!! !! - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Ðįşφłąÿ ǻ мèηΰ όή řïĝĥť-ćľїçķ !!! !!! !!! This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index 913ae9ee97f..5d8bb846c6e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -989,10 +989,6 @@ Запустить этот профиль от имени администратора Header for a control to toggle whether the profile should always open elevated (in an admin window) - - Если этот параметр включен, профиль будет автоматически открываться в окне терминала администратора. Если текущее окно уже запущено от имени администратора, он откроется в этом окне. - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - Размер журнала Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1225,10 +1221,6 @@ Если этот параметр включен, Терминал будет создавать новый блок среды при создании новых вкладок или панелей с этим профилем. Если этот параметр отключен, вкладка или панель будет наследовать переменные, с которыми запущен Терминал. A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - Включить экспериментальную сквозную проверку на виртуальном терминале - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - Отображать меню при щелчке правой кнопкой мыши This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 91c54379777..7dcaa741b32 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -309,6 +309,10 @@ 当命令行应用程序在没有现有会话(例如从“开始菜单”或“运行”对话框)运行时启动的终端应用程序。 A description to clarify that the dropdown choice for default terminal will tell the operating system which Terminal application (Windows Terminal, the preview build, the legacy inbox window, or a 3rd party one) to use when starting a command line tool like CMD or Powershell that does not already have a window. + + 图形 API + This text is shown next to a list of choices. + Direct3D 11 提供性能更佳且功能更丰富的体验,而 Direct2D 则更稳定。默认选项“自动”将选择最适合你的图形硬件的 API。如果遇到严重问题,请考虑使用 Direct2D。 @@ -316,10 +320,24 @@ 自动 The default choice between multiple graphics APIs. + + Direct2D + + + Direct3D 11 + + + 禁用部分交换链失效 + "Swap Chain" is an official technical term by Microsoft. This text is shown next to a toggle. + 默认情况下,文本呈现器使用 FLIP_SEQUENTIAL 交换链并通过 Present1 API 声明脏矩形。启用此设置时,将改用 FLIP_DISCARD 交换链,并且不会声明脏矩形。一个或另一个是否更依赖于你的硬件和各种其他因素。 {Locked="Present1","FLIP_DISCARD","FLIP_SEQUENTIAL"} + + 使用软件渲染(WARP) + {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". This text is shown next to a toggle. + 启用后,终端将使用软件光栅 (WARP)。几乎在所有情况下都应禁用此设置。 {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". @@ -971,10 +989,6 @@ 以管理员身份运行此配置文件 Header for a control to toggle whether the profile should always open elevated (in an admin window) - - 如果启用,配置文件将在管理终端窗口中自动打开。如果当前窗口已以管理员身份运行,它将在此窗口中打开。 - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - 历史记录大小 Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1207,10 +1221,6 @@ 启用后,使用此配置文件创建新选项卡或窗格时,终端将生成新的环境块。禁用后,选项卡/窗格将改为继承终端启动时所用的变量。 A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - 启用实验性虚拟终端传递 - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - 右键单击时显示菜单 This controls how a right-click behaves in the terminal @@ -1786,4 +1796,12 @@ 了解详细信息。 A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information. + + 缺少字体: + This is a label that is followed by a list of missing fonts. + + + 非等宽字体: + This is a label that is followed by a list of proportional fonts. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index 0e1d7c5e3ea..2f1cf24cb98 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -989,10 +989,6 @@ 以系統管理員身分執行此設定檔 Header for a control to toggle whether the profile should always open elevated (in an admin window) - - 如果啟用,設定檔將會自動在管理終端機視窗中開啟。如果目前的視窗已以系統管理員身分執行,則會在此視窗中開啟。 - A description for what the "elevate" setting does. Presented near "Profile_Elevate". - 歷史大小 Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -1225,10 +1221,6 @@ 啟用時,終端機將在使用此設定檔建立新索引標籤或窗格時產生新的環境區塊。停用時,索引標籤/窗格將改為繼承終端機啟動時的變數。 A description for what the "Reload environment variables" setting does. Presented near "Profile_ReloadEnvVars". - - 啟用實驗性虛擬終端機通道 - An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY - 在以滑鼠右鍵按一下時顯示選單 This controls how a right-click behaves in the terminal diff --git a/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw index 880bdde2e28..9d9ef4b93f1 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw @@ -700,6 +700,9 @@ Reiniciar conexión + + Abrir el bloc de notas + Seleccionar salida del comando siguiente diff --git a/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw index 154e29f719d..ce62381140b 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw @@ -700,6 +700,9 @@ Redémarrer la connexion + + Ouvrir le bloc-notes + Sélectionner la sortie de commande suivante diff --git a/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw index f0e738bb827..48ba557745d 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw @@ -700,6 +700,9 @@ Riavvia connessione + + Aprire scratchpad + Seleziona l'output del comando successivo diff --git a/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw index b80c73e66ab..24746d831a9 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw @@ -700,6 +700,9 @@ 重新启动连接 + + 打开便笺本 + 选择下一个命令输出 From 3cc82a51d8bc8ab72a55e0ec18e26e02f20a8fef Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 30 Mar 2024 00:15:46 +0100 Subject: [PATCH 197/603] Replace til::some with til::small_vector (#16952) `til::small_vector` had a bug: Its internal backing buffer didn't prevent default initialization! Wrapping it in an `union` fixed that. `til::some` had the same issue, but thinking about it I realized we don't need both classes to exist, so I removed `til::some` since `til::small_vector` is more flexible. Checking the assembly, I noticed that `til::small_vector` with the `union` fix produced a more compact result. I also noticed that in combination with function calls and inlining the bit-wise ANDs in the point/size/rect boolean operators produced poor-ish results. Since their impact on performance is negligible to begin with I simplified that code slightly. Finally, I noticed that the boolean operator for `til::point` was incorrect since it checked for `>0` instead of `>=0`. Luckily nothing seemed to have used that operator yet. (= No inbox regression.) --- src/inc/til/point.h | 2 +- src/inc/til/rect.h | 18 +- src/inc/til/rle.h | 2 - src/inc/til/size.h | 2 +- src/inc/til/small_vector.h | 7 +- src/inc/til/some.h | 267 -------------- src/renderer/atlas/BackendD3D.h | 1 - src/renderer/gdi/paint.cpp | 2 - src/server/ApiMessage.h | 2 - src/til/ut_til/BitmapTests.cpp | 8 +- src/til/ut_til/PointTests.cpp | 18 + src/til/ut_til/RectangleTests.cpp | 64 ++-- src/til/ut_til/SizeTests.cpp | 30 +- src/til/ut_til/SmallVectorTests.cpp | 2 - src/til/ut_til/SomeTests.cpp | 340 ------------------ src/til/ut_til/UnicodeTests.cpp | 4 +- src/til/ut_til/sources | 1 - src/til/ut_til/til.unit.tests.vcxproj | 4 +- src/til/ut_til/til.unit.tests.vcxproj.filters | 6 +- src/types/inc/IInputEvent.hpp | 2 - src/types/inc/viewport.hpp | 2 +- 21 files changed, 77 insertions(+), 707 deletions(-) delete mode 100644 src/inc/til/some.h delete mode 100644 src/til/ut_til/SomeTests.cpp diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 4da8d5c6ddb..678afe706f6 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -58,7 +58,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" constexpr explicit operator bool() const noexcept { - return (x > 0) & (y > 0); + return x >= 0 && y >= 0; } constexpr bool operator<(const point other) const noexcept diff --git a/src/inc/til/rect.h b/src/inc/til/rect.h index 7ad7fe7ab7d..bc7b07b6c83 100644 --- a/src/inc/til/rect.h +++ b/src/inc/til/rect.h @@ -4,11 +4,11 @@ #pragma once #include "bit.h" -#include "some.h" #include "math.h" #include "size.h" #include "point.h" #include "operators.h" +#include "small_vector.h" namespace til // Terminal Implementation Library. Also: "Today I Learned" { @@ -31,8 +31,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" explicit constexpr operator bool() const noexcept { - return (left >= 0) & (top >= 0) & - (right >= left) & (bottom >= top); + return left >= 0 && top >= 0 && right >= left && bottom >= top; } }; @@ -204,8 +203,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" explicit constexpr operator bool() const noexcept { - return (left >= 0) & (top >= 0) & - (right > left) & (bottom > top); + return left >= 0 && top >= 0 && right > left && bottom > top; } constexpr const_iterator begin() const @@ -294,9 +292,9 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" } // - = subtract - constexpr some operator-(const rect& other) const + small_vector operator-(const rect& other) const { - some result; + small_vector result; // We could have up to four rectangles describing the area resulting when you take removeMe out of main. // Find the intersection of the two so we know which bits of removeMe are actually applicable @@ -566,14 +564,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" constexpr bool contains(point pt) const noexcept { - return (pt.x >= left) & (pt.x < right) & - (pt.y >= top) & (pt.y < bottom); + return pt.x >= left && pt.x < right && pt.y >= top && pt.y < bottom; } constexpr bool contains(const rect& rc) const noexcept { - return (rc.left >= left) & (rc.top >= top) & - (rc.right <= right) & (rc.bottom <= bottom); + return rc.left >= left && rc.top >= top && rc.right <= right && rc.bottom <= bottom; } template diff --git a/src/inc/til/rle.h b/src/inc/til/rle.h index 34b6c929a0b..99bdf734c45 100644 --- a/src/inc/til/rle.h +++ b/src/inc/til/rle.h @@ -3,8 +3,6 @@ #pragma once -#include "small_vector.h" - #ifdef UNIT_TESTING class RunLengthEncodingTests; #endif diff --git a/src/inc/til/size.h b/src/inc/til/size.h index 72dbb602852..98800ad7539 100644 --- a/src/inc/til/size.h +++ b/src/inc/til/size.h @@ -39,7 +39,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" constexpr explicit operator bool() const noexcept { - return (width > 0) & (height > 0); + return width > 0 && height > 0; } constexpr size operator+(const size other) const diff --git a/src/inc/til/small_vector.h b/src/inc/til/small_vector.h index f078d37fbe4..b47cd1ead14 100644 --- a/src/inc/til/small_vector.h +++ b/src/inc/til/small_vector.h @@ -652,7 +652,7 @@ namespace til reference emplace_back(Args&&... args) { const auto new_size = _ensure_fits(1); - const auto it = new (_data + _size) T(std::forward(args)...); + const auto it = std::construct_at(_data + _size, std::forward(args)...); _size = new_size; return *it; } @@ -930,7 +930,10 @@ namespace til T* _data; size_t _capacity; size_t _size; - T _buffer[N]; + union + { + T _buffer[N]; + }; }; } diff --git a/src/inc/til/some.h b/src/inc/til/some.h deleted file mode 100644 index ec34a470ca4..00000000000 --- a/src/inc/til/some.h +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -#include - -namespace til // Terminal Implementation Library. Also: "Today I Learned" -{ - template - class some - { - private: - std::array _array; - size_t _used; - -#ifdef UNIT_TESTING - friend class SomeTests; -#endif - - public: - using value_type = T; - using size_type = size_t; - using difference_type = ptrdiff_t; - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = const T&; - - using iterator = typename decltype(_array)::iterator; - using const_iterator = typename decltype(_array)::const_iterator; - - using reverse_iterator = typename decltype(_array)::reverse_iterator; - using const_reverse_iterator = typename decltype(_array)::const_reverse_iterator; - - constexpr some() noexcept : - _array{}, - _used{ 0 } - { - } - - constexpr some(std::initializer_list init) - { - if (init.size() > N) - { - _invalidArg(); - } - - std::copy(init.begin(), init.end(), _array.begin()); - _used = init.size(); - } - - constexpr bool operator==(const til::some& other) const noexcept - { - return std::equal(cbegin(), cend(), other.cbegin(), other.cend()); - } - - constexpr bool operator!=(const til::some& other) const noexcept - { - return !(*this == other); - } - - constexpr void fill(const T& _Value) - { - _array.fill(_Value); - _used = N; - } - - constexpr void swap(some& _Other) noexcept(std::is_nothrow_swappable_v) - { - _array.swap(_Other._array); - std::swap(_used, _Other._used); - } - - constexpr const_iterator begin() const noexcept - { - return _array.begin(); - } - - constexpr const_iterator end() const noexcept - { - return _array.begin() + _used; - } - - constexpr const_reverse_iterator rbegin() const noexcept - { - return const_reverse_iterator(end()); - } - - constexpr const_reverse_iterator rend() const noexcept - { - return const_reverse_iterator(begin()); - } - - constexpr const_iterator cbegin() const noexcept - { - return begin(); - } - - constexpr const_iterator cend() const noexcept - { - return end(); - } - - constexpr const_reverse_iterator crbegin() const noexcept - { - return rbegin(); - } - - constexpr const_reverse_iterator crend() const noexcept - { - return rend(); - } - - constexpr size_type size() const noexcept - { - return _used; - } - - constexpr size_type max_size() const noexcept - { - return N; - } - - constexpr bool empty() const noexcept - { - return !_used; - } - - constexpr void clear() noexcept - { - _used = 0; - _array = {}; // should free members, if necessary. - } - - constexpr const_reference at(size_type pos) const - { - if (_used <= pos) - { - _outOfRange(); - } - - return _array[pos]; - } - - constexpr const_reference operator[](size_type pos) const noexcept - { - return _array[pos]; - } - - constexpr const_reference front() const noexcept - { - return _array[0]; - } - - constexpr const_reference back() const noexcept - { - return _array[_used - 1]; - } - - constexpr const T* data() const noexcept - { - return _array.data(); - } - - constexpr void push_back(const T& val) - { - if (_used >= N) - { - _outOfRange(); - } - - til::at(_array, _used) = val; - - ++_used; - } - - constexpr void push_back(T&& val) - { - if (_used >= N) - { - _outOfRange(); - } - - til::at(_array, _used) = std::move(val); - - ++_used; - } - - constexpr void pop_back() - { - if (_used <= 0) - { - _outOfRange(); - } - - --_used; - - til::at(_array, _used) = 0; - } - - [[noreturn]] constexpr void _invalidArg() const - { - throw std::invalid_argument("invalid argument"); - } - - [[noreturn]] constexpr void _outOfRange() const - { - throw std::out_of_range("invalid some subscript"); - } - - std::wstring to_string() const - { - std::wstringstream wss; - wss << std::endl - << L"Some contains " << size() << " of max size " << max_size() << ":" << std::endl; - wss << L"Elements:" << std::endl; - - for (auto& item : *this) - { - wss << L"\t- " << item.to_string() << std::endl; - } - - return wss.str(); - } - }; -} - -#ifdef __WEX_COMMON_H__ -namespace WEX::TestExecution -{ - template - class VerifyOutputTraits<::til::some> - { - public: - static WEX::Common::NoThrowString ToString(const ::til::some& some) - { - return WEX::Common::NoThrowString(some.to_string().c_str()); - } - }; - - template - class VerifyCompareTraits<::til::some, ::til::some> - { - public: - static bool AreEqual(const ::til::some& expected, const ::til::some& actual) noexcept - { - return expected == actual; - } - - static bool AreSame(const ::til::some& expected, const ::til::some& actual) noexcept - { - return &expected == &actual; - } - - static bool IsLessThan(const ::til::some& expectedLess, const ::til::some& expectedGreater) = delete; - - static bool IsGreaterThan(const ::til::some& expectedGreater, const ::til::some& expectedLess) = delete; - - static bool IsNull(const ::til::some& object) noexcept - { - return object == til::some{}; - } - }; - -}; -#endif diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 25833b831dc..6a12e704a4d 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -5,7 +5,6 @@ #include #include -#include #include "Backend.h" diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 29f8dfc43ad..c449b4d0835 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -4,8 +4,6 @@ #include "precomp.h" #include "gdirenderer.hpp" -#include - #include "../inc/unicode.hpp" #pragma hdrstop diff --git a/src/server/ApiMessage.h b/src/server/ApiMessage.h index d730cff9e00..6c1809aa9d7 100644 --- a/src/server/ApiMessage.h +++ b/src/server/ApiMessage.h @@ -20,8 +20,6 @@ Revision History: #include "ApiMessageState.h" #include "IApiRoutines.h" -#include - class ConsoleProcessHandle; class ConsoleHandleData; diff --git a/src/til/ut_til/BitmapTests.cpp b/src/til/ut_til/BitmapTests.cpp index ee093f52a79..b17bf0fff4e 100644 --- a/src/til/ut_til/BitmapTests.cpp +++ b/src/til/ut_til/BitmapTests.cpp @@ -868,7 +868,7 @@ class BitmapTests // C _ D D // _ _ E _ // _ F F _ - til::some expected; + std::vector expected; expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 2, 1 } }); expected.push_back(til::rect{ til::point{ 3, 0 }, til::size{ 1, 1 } }); expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 1, 1 } }); @@ -877,7 +877,7 @@ class BitmapTests expected.push_back(til::rect{ til::point{ 1, 3 }, til::size{ 2, 1 } }); Log::Comment(L"Run the iterator and collect the runs."); - til::some actual; + std::vector actual; for (auto run : map.runs()) { actual.push_back(run); @@ -1006,7 +1006,7 @@ class BitmapTests // C _ D D // _ _ E _ // _ F F _ - til::some expected; + std::vector expected; expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 2, 1 } }); expected.push_back(til::rect{ til::point{ 3, 0 }, til::size{ 1, 1 } }); expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 1, 1 } }); @@ -1015,7 +1015,7 @@ class BitmapTests expected.push_back(til::rect{ til::point{ 1, 3 }, til::size{ 2, 1 } }); Log::Comment(L"Run the iterator and collect the runs."); - til::some actual; + std::vector actual; for (auto run : map.runs()) { actual.push_back(run); diff --git a/src/til/ut_til/PointTests.cpp b/src/til/ut_til/PointTests.cpp index cd069153c77..738847cee75 100644 --- a/src/til/ut_til/PointTests.cpp +++ b/src/til/ut_til/PointTests.cpp @@ -221,6 +221,24 @@ class PointTests } } + TEST_METHOD(Boolean) + { + SetVerifyOutput verifyOutputScope{ VerifyOutputSettings::LogOnlyFailures }; + + static constexpr til::CoordType values[] = { til::CoordTypeMin, -1, 0, 1, til::CoordTypeMax }; + + for (const auto x : values) + { + for (const auto y : values) + { + const til::point p{ x, y }; + const auto expected = x >= 0 && y >= 0; + const auto actual = static_cast(p); + VERIFY_ARE_EQUAL(expected, actual); + } + } + } + TEST_METHOD(Addition) { Log::Comment(L"Addition of two things that should be in bounds."); diff --git a/src/til/ut_til/RectangleTests.cpp b/src/til/ut_til/RectangleTests.cpp index 8709d1f8d66..f7a882fb987 100644 --- a/src/til/ut_til/RectangleTests.cpp +++ b/src/til/ut_til/RectangleTests.cpp @@ -300,22 +300,26 @@ class RectangleTests TEST_METHOD(Boolean) { - BEGIN_TEST_METHOD_PROPERTIES() - TEST_METHOD_PROPERTY(L"Data:left", L"{0,10}") - TEST_METHOD_PROPERTY(L"Data:top", L"{0,10}") - TEST_METHOD_PROPERTY(L"Data:right", L"{0,10}") - TEST_METHOD_PROPERTY(L"Data:bottom", L"{0,10}") - END_TEST_METHOD_PROPERTIES() + SetVerifyOutput verifyOutputScope{ VerifyOutputSettings::LogOnlyFailures }; - til::CoordType left, top, right, bottom; - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"left", left)); - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"top", top)); - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"right", right)); - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"bottom", bottom)); + static constexpr til::CoordType values[] = { til::CoordTypeMin, -1, 0, 1, til::CoordTypeMax }; - const auto expected = left < right && top < bottom; - const til::rect actual{ left, top, right, bottom }; - VERIFY_ARE_EQUAL(expected, (bool)actual); + for (const auto left : values) + { + for (const auto top : values) + { + for (const auto right : values) + { + for (const auto bottom : values) + { + const til::rect r{ left, top, right, bottom }; + const auto expected = left >= 0 && top >= 0 && right > left && bottom > top; + const auto actual = static_cast(r); + VERIFY_ARE_EQUAL(expected, actual); + } + } + } + } } TEST_METHOD(OrUnion) @@ -364,7 +368,7 @@ class RectangleTests const auto removal = original; // Since it's the same rectangle, nothing's left. We should get no results. - const til::some expected; + const til::small_vector expected; const auto actual = original - removal; VERIFY_ARE_EQUAL(expected, actual); } @@ -375,7 +379,7 @@ class RectangleTests const til::rect removal{ 12, 12, 15, 15 }; // Since they don't overlap, we expect the original to be given back. - const til::some expected{ original }; + const til::small_vector expected{ original }; const auto actual = original - removal; VERIFY_ARE_EQUAL(expected, actual); } @@ -401,7 +405,7 @@ class RectangleTests const til::rect original{ 0, 0, 10, 10 }; const til::rect removal{ -12, 3, 15, 15 }; - const til::some expected{ + const til::small_vector expected{ til::rect{ original.left, original.top, original.right, removal.top } }; const auto actual = original - removal; @@ -428,7 +432,7 @@ class RectangleTests const til::rect original{ 0, 0, 10, 10 }; const til::rect removal{ 3, 3, 15, 15 }; - const til::some expected{ + const til::small_vector expected{ til::rect{ original.left, original.top, original.right, removal.top }, til::rect{ original.left, removal.top, removal.left, original.bottom } }; @@ -452,7 +456,7 @@ class RectangleTests const til::rect original{ 0, 0, 10, 10 }; const til::rect removal{ 3, 3, 15, 6 }; - const til::some expected{ + const til::small_vector expected{ til::rect{ original.left, original.top, original.right, removal.top }, til::rect{ original.left, removal.bottom, original.right, original.bottom }, til::rect{ original.left, removal.top, removal.left, removal.bottom } @@ -483,7 +487,7 @@ class RectangleTests const til::rect original{ 0, 0, 10, 10 }; const til::rect removal{ 3, 3, 6, 6 }; - const til::some expected{ + const til::small_vector expected{ til::rect{ original.left, original.top, original.right, removal.top }, til::rect{ original.left, removal.bottom, original.right, original.bottom }, til::rect{ original.left, removal.top, removal.left, removal.bottom }, @@ -706,26 +710,6 @@ class RectangleTests VERIFY_ARE_EQUAL(expected, rc.size()); } - TEST_METHOD(Empty) - { - BEGIN_TEST_METHOD_PROPERTIES() - TEST_METHOD_PROPERTY(L"Data:left", L"{0,10}") - TEST_METHOD_PROPERTY(L"Data:top", L"{0,10}") - TEST_METHOD_PROPERTY(L"Data:right", L"{0,10}") - TEST_METHOD_PROPERTY(L"Data:bottom", L"{0,10}") - END_TEST_METHOD_PROPERTIES() - - til::CoordType left, top, right, bottom; - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"left", left)); - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"top", top)); - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"right", right)); - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"bottom", bottom)); - - const auto expected = !(left < right && top < bottom); - const til::rect actual{ left, top, right, bottom }; - VERIFY_ARE_EQUAL(expected, actual.empty()); - } - TEST_METHOD(ContainsPoint) { BEGIN_TEST_METHOD_PROPERTIES() diff --git a/src/til/ut_til/SizeTests.cpp b/src/til/ut_til/SizeTests.cpp index a75b48922a2..dce512aa1a7 100644 --- a/src/til/ut_til/SizeTests.cpp +++ b/src/til/ut_til/SizeTests.cpp @@ -147,26 +147,20 @@ class SizeTests TEST_METHOD(Boolean) { - const til::size empty; - VERIFY_IS_FALSE(!!empty); + SetVerifyOutput verifyOutputScope{ VerifyOutputSettings::LogOnlyFailures }; - const til::size yOnly{ 0, 10 }; - VERIFY_IS_FALSE(!!yOnly); + static constexpr til::CoordType values[] = { til::CoordTypeMin, -1, 0, 1, til::CoordTypeMax }; - const til::size xOnly{ 10, 0 }; - VERIFY_IS_FALSE(!!xOnly); - - const til::size both{ 10, 10 }; - VERIFY_IS_TRUE(!!both); - - const til::size yNegative{ 10, -10 }; - VERIFY_IS_FALSE(!!yNegative); - - const til::size xNegative{ -10, 10 }; - VERIFY_IS_FALSE(!!xNegative); - - const til::size bothNegative{ -10, -10 }; - VERIFY_IS_FALSE(!!bothNegative); + for (const auto width : values) + { + for (const auto height : values) + { + const til::size s{ width, height }; + const auto expected = width > 0 && height > 0; + const auto actual = static_cast(s); + VERIFY_ARE_EQUAL(expected, actual); + } + } } TEST_METHOD(Addition) diff --git a/src/til/ut_til/SmallVectorTests.cpp b/src/til/ut_til/SmallVectorTests.cpp index f1a2ed88e54..55a338ae86d 100644 --- a/src/til/ut_til/SmallVectorTests.cpp +++ b/src/til/ut_til/SmallVectorTests.cpp @@ -3,8 +3,6 @@ #include "precomp.h" -#include - using namespace std::literals; using namespace WEX::Common; using namespace WEX::Logging; diff --git a/src/til/ut_til/SomeTests.cpp b/src/til/ut_til/SomeTests.cpp deleted file mode 100644 index 98ea25fd64d..00000000000 --- a/src/til/ut_til/SomeTests.cpp +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -using namespace WEX::Common; -using namespace WEX::Logging; -using namespace WEX::TestExecution; - -class SomeTests -{ - TEST_CLASS(SomeTests); - - TEST_METHOD(Construct) - { - Log::Comment(L"Default Constructor"); - til::some s; - - Log::Comment(L"Valid Initializer List Constructor"); - til::some t{ 1 }; - til::some u{ 1, 2 }; - - Log::Comment(L"Invalid Initializer List Constructor"); - auto f = []() { - til::some v{ 1, 2, 3 }; - }; - - VERIFY_THROWS(f(), std::invalid_argument); - } - - TEST_METHOD(Equality) - { - til::some a{ 1, 2 }; - til::some b{ 1, 2 }; - VERIFY_IS_TRUE(a == b); - - til::some c{ 3, 2 }; - VERIFY_IS_FALSE(a == c); - - til::some d{ 2, 3 }; - VERIFY_IS_FALSE(a == d); - - til::some e{ 1 }; - VERIFY_IS_FALSE(a == e); - } - - TEST_METHOD(Inequality) - { - til::some a{ 1, 2 }; - til::some b{ 1, 2 }; - VERIFY_IS_FALSE(a != b); - - til::some c{ 3, 2 }; - VERIFY_IS_TRUE(a != c); - - til::some d{ 2, 3 }; - VERIFY_IS_TRUE(a != d); - - til::some e{ 1 }; - VERIFY_IS_TRUE(a != e); - } - - TEST_METHOD(Fill) - { - til::some s; - - const auto val = 12; - s.fill(val); - - VERIFY_ARE_EQUAL(s.max_size(), s.size()); - - for (const auto& i : s) - { - VERIFY_ARE_EQUAL(val, i); - } - } - - TEST_METHOD(Swap) - { - til::some a; - til::some b; - - const auto aVal = 900; - a.fill(900); - - const auto bVal = 45; - b.push_back(45); - - const auto aSize = a.size(); - const auto bSize = b.size(); - - a.swap(b); - - VERIFY_ARE_EQUAL(aSize, b.size()); - VERIFY_ARE_EQUAL(bSize, a.size()); - - VERIFY_ARE_EQUAL(bVal, a[0]); - - for (const auto& i : b) - { - VERIFY_ARE_EQUAL(aVal, i); - } - } - - TEST_METHOD(Size) - { - til::some c; - - VERIFY_ARE_EQUAL(0u, c.size()); - - c.push_back(3); - VERIFY_ARE_EQUAL(1u, c.size()); - - c.push_back(12); - VERIFY_ARE_EQUAL(2u, c.size()); - - c.pop_back(); - VERIFY_ARE_EQUAL(1u, c.size()); - - c.pop_back(); - VERIFY_ARE_EQUAL(0u, c.size()); - } - - TEST_METHOD(MaxSize) - { - til::some c; - - VERIFY_ARE_EQUAL(2u, c.max_size()); - - c.push_back(3); - VERIFY_ARE_EQUAL(2u, c.max_size()); - - c.push_back(12); - VERIFY_ARE_EQUAL(2u, c.size()); - - c.pop_back(); - VERIFY_ARE_EQUAL(2u, c.max_size()); - - c.pop_back(); - VERIFY_ARE_EQUAL(2u, c.max_size()); - } - - TEST_METHOD(PushBack) - { - til::some s; - s.push_back(12); - VERIFY_THROWS(s.push_back(12), std::out_of_range); - } - - TEST_METHOD(PopBack) - { - til::some s; - VERIFY_THROWS(s.pop_back(), std::out_of_range); - - s.push_back(12); - VERIFY_THROWS(s.push_back(12), std::out_of_range); - } - - TEST_METHOD(Empty) - { - til::some s; - VERIFY_IS_TRUE(s.empty()); - s.push_back(12); - VERIFY_IS_FALSE(s.empty()); - s.pop_back(); - VERIFY_IS_TRUE(s.empty()); - } - - TEST_METHOD(Clear) - { - til::some s; - VERIFY_IS_TRUE(s.empty()); - s.push_back(12); - VERIFY_IS_FALSE(s.empty()); - VERIFY_ARE_EQUAL(1u, s.size()); - s.clear(); - VERIFY_IS_TRUE(s.empty()); - VERIFY_ARE_EQUAL(0u, s.size()); - } - - TEST_METHOD(ClearFreesMembers) - { - til::some, 2> s; - - auto a = std::make_shared(4); - auto weakA = std::weak_ptr(a); - - auto b = std::make_shared(6); - auto weakB = std::weak_ptr(b); - - s.push_back(std::move(a)); - s.push_back(std::move(b)); - - VERIFY_IS_FALSE(weakA.expired()); - VERIFY_IS_FALSE(weakB.expired()); - - s.clear(); - - VERIFY_IS_TRUE(weakA.expired()); - VERIFY_IS_TRUE(weakB.expired()); - } - - TEST_METHOD(Data) - { - til::some s; - const auto one = 1; - const auto two = 2; - s.push_back(one); - s.push_back(two); - - auto data = s.data(); - - VERIFY_ARE_EQUAL(one, *data); - VERIFY_ARE_EQUAL(two, *(data + 1)); - } - - TEST_METHOD(FrontBack) - { - til::some s; - const auto one = 1; - const auto two = 2; - s.push_back(one); - s.push_back(two); - - VERIFY_ARE_EQUAL(one, s.front()); - VERIFY_ARE_EQUAL(two, s.back()); - } - - TEST_METHOD(Indexing) - { - const auto one = 14; - const auto two = 28; - - til::some s; - VERIFY_THROWS(s.at(0), std::out_of_range); - VERIFY_THROWS(s.at(1), std::out_of_range); - auto a = s[0]; - a = s[1]; - - s.push_back(one); - VERIFY_ARE_EQUAL(one, s.at(0)); - VERIFY_ARE_EQUAL(one, s[0]); - VERIFY_THROWS(s.at(1), std::out_of_range); - a = s[1]; - - s.push_back(two); - VERIFY_ARE_EQUAL(one, s.at(0)); - VERIFY_ARE_EQUAL(one, s[0]); - VERIFY_ARE_EQUAL(two, s.at(1)); - VERIFY_ARE_EQUAL(two, s[1]); - - s.pop_back(); - VERIFY_ARE_EQUAL(one, s.at(0)); - VERIFY_ARE_EQUAL(one, s[0]); - VERIFY_THROWS(s.at(1), std::out_of_range); - a = s[1]; - - s.pop_back(); - VERIFY_THROWS(s.at(0), std::out_of_range); - VERIFY_THROWS(s.at(1), std::out_of_range); - a = s[0]; - a = s[1]; - } - - TEST_METHOD(ForwardIter) - { - const int vals[] = { 17, 99 }; - const int valLength = ARRAYSIZE(vals); - - til::some s; - VERIFY_ARE_EQUAL(s.begin(), s.end()); - VERIFY_ARE_EQUAL(s.cbegin(), s.cend()); - VERIFY_ARE_EQUAL(s.begin(), s.cbegin()); - VERIFY_ARE_EQUAL(s.end(), s.cend()); - - s.push_back(vals[0]); - s.push_back(vals[1]); - - VERIFY_ARE_EQUAL(s.begin() + valLength, s.end()); - VERIFY_ARE_EQUAL(s.cbegin() + valLength, s.cend()); - - auto count = 0; - for (const auto& i : s) - { - VERIFY_ARE_EQUAL(vals[count], i); - ++count; - } - VERIFY_ARE_EQUAL(valLength, count); - - count = 0; - for (auto i = s.cbegin(); i < s.cend(); ++i) - { - VERIFY_ARE_EQUAL(vals[count], *i); - ++count; - } - VERIFY_ARE_EQUAL(valLength, count); - - count = 0; - for (auto i = s.begin(); i < s.end(); ++i) - { - VERIFY_ARE_EQUAL(vals[count], *i); - ++count; - } - VERIFY_ARE_EQUAL(valLength, count); - } - - TEST_METHOD(ReverseIter) - { - const int vals[] = { 17, 99 }; - const int valLength = ARRAYSIZE(vals); - - til::some s; - VERIFY_ARE_EQUAL(s.rbegin(), s.rend()); - VERIFY_ARE_EQUAL(s.crbegin(), s.crend()); - VERIFY_ARE_EQUAL(s.rbegin(), s.crbegin()); - VERIFY_ARE_EQUAL(s.rend(), s.crend()); - - s.push_back(vals[0]); - s.push_back(vals[1]); - - VERIFY_ARE_EQUAL(s.rbegin() + valLength, s.rend()); - VERIFY_ARE_EQUAL(s.crbegin() + valLength, s.crend()); - - auto count = 0; - for (auto i = s.crbegin(); i < s.crend(); ++i) - { - VERIFY_ARE_EQUAL(vals[valLength - count - 1], *i); - ++count; - } - VERIFY_ARE_EQUAL(valLength, count); - - count = 0; - for (auto i = s.rbegin(); i < s.rend(); ++i) - { - VERIFY_ARE_EQUAL(vals[valLength - count - 1], *i); - ++count; - } - VERIFY_ARE_EQUAL(valLength, count); - } -}; diff --git a/src/til/ut_til/UnicodeTests.cpp b/src/til/ut_til/UnicodeTests.cpp index 24d62beeaf6..e81d5b9a7a9 100644 --- a/src/til/ut_til/UnicodeTests.cpp +++ b/src/til/ut_til/UnicodeTests.cpp @@ -51,10 +51,10 @@ class UnicodeTests struct Test { std::wstring_view input; - til::some expected; + std::vector expected; }; - static constexpr std::array tests{ + const std::array tests{ Test{ L"", {} }, Test{ L"a", { L"a" } }, Test{ L"abc", { L"a", L"b", L"c" } }, diff --git a/src/til/ut_til/sources b/src/til/ut_til/sources index ffe976d7cab..2e39e378a84 100644 --- a/src/til/ut_til/sources +++ b/src/til/ut_til/sources @@ -30,7 +30,6 @@ SOURCES = \ RunLengthEncodingTests.cpp \ SizeTests.cpp \ SmallVectorTests.cpp \ - SomeTests.cpp \ StaticMapTests.cpp \ string.cpp \ u8u16convertTests.cpp \ diff --git a/src/til/ut_til/til.unit.tests.vcxproj b/src/til/ut_til/til.unit.tests.vcxproj index 04694e68a16..d6b183d1282 100644 --- a/src/til/ut_til/til.unit.tests.vcxproj +++ b/src/til/ut_til/til.unit.tests.vcxproj @@ -32,7 +32,6 @@ - @@ -64,7 +63,6 @@ - @@ -90,4 +88,4 @@ - \ No newline at end of file + diff --git a/src/til/ut_til/til.unit.tests.vcxproj.filters b/src/til/ut_til/til.unit.tests.vcxproj.filters index c25b0113980..15928ddb533 100644 --- a/src/til/ut_til/til.unit.tests.vcxproj.filters +++ b/src/til/ut_til/til.unit.tests.vcxproj.filters @@ -20,7 +20,6 @@ - @@ -96,9 +95,6 @@ inc - - inc - inc @@ -135,4 +131,4 @@ {7cf29ba4-d33d-4c3b-82e3-ab73e5a79685} - \ No newline at end of file + diff --git a/src/types/inc/IInputEvent.hpp b/src/types/inc/IInputEvent.hpp index 4641c17332d..655acd8f9b7 100644 --- a/src/types/inc/IInputEvent.hpp +++ b/src/types/inc/IInputEvent.hpp @@ -3,8 +3,6 @@ #pragma once -#include - #define ALT_PRESSED (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) #define CTRL_PRESSED (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED) #define MOD_PRESSED (SHIFT_PRESSED | ALT_PRESSED | CTRL_PRESSED) diff --git a/src/types/inc/viewport.hpp b/src/types/inc/viewport.hpp index d762f64348f..b8b2029504e 100644 --- a/src/types/inc/viewport.hpp +++ b/src/types/inc/viewport.hpp @@ -17,7 +17,7 @@ namespace Microsoft::Console::Types { class Viewport; - using SomeViewports = til::some; + using SomeViewports = til::small_vector; class Viewport final { From 9f3dbab7bf442a1d7702bef0058fa2f0ff5736cd Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 2 Apr 2024 01:32:31 +0200 Subject: [PATCH 198/603] Fix bugs introduced in #16821 (custom font fallback) (#16993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Since `FindFontWithLocalizedName` is broken (intentionally and temporarily until #16943 is fixed) we have to be extra be careful not to return a nullptr `Font`. * Portable builds may not have a broken font cache, but also not have the given font (Cascadia Mono for instance) installed. This requires us to load the nearby fonts even if there aren't any exceptions. ## Validation Steps Performed * Open `src/cascadia/CascadiaResources.build.items` and remove the `Condition` for .ttf files * Deploy on a clean Windows 10 VM * Cascadia Mono loads without issues ✅ * Open the `Settings > Defaults > Appearance`, enter a non-existing font and hit Save * Doesn't crash ✅ --- .../TerminalSettingsEditor/Appearances.cpp | 10 +-- .../ProfileViewModel.cpp | 1 + src/renderer/atlas/AtlasEngine.api.cpp | 69 +++++++++++++------ src/renderer/atlas/AtlasEngine.h | 5 +- 4 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index fe9cabc824f..15417166ad2 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -419,6 +419,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation BOOL hasPowerlineCharacters = FALSE; til::iterate_font_families(fontFace, [&](wil::zwstring_view name) { + if (primaryFontName.empty()) + { + primaryFontName = name; + } + std::wstring* accumulator = nullptr; UINT32 index = 0; @@ -434,11 +439,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation break; } - if (primaryFontName.empty()) - { - primaryFontName = name; - } - wil::com_ptr fontFamily; THROW_IF_FAILED(fontCollection->GetFontFamily(index, fontFamily.addressof())); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index a00a077ef5b..d373926b380 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -187,6 +187,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation UpdateFontList(); } const auto& currentFontList{ CompleteFontList() }; + fallbackFont = currentFontList.First().Current(); for (const auto& font : currentFontList) { if (font.LocalizedName() == name) diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index c031f67a154..9c98a12cf52 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -482,32 +482,17 @@ void AtlasEngine::SetWarningCallback(std::functionfont->fontCollection` for a pre-existing font collection, - // before falling back to using the system font collection. This way we can inject our custom one. - // Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection - // instance across font changes, like when zooming the font size rapidly using the scroll wheel. - try + if (FAILED(hr) && _updateWithNearbyFontCollection()) { - _api.s.write()->font.write()->fontCollection = FontCache::GetCached(); + hr = _updateFont(fontInfoDesired, fontInfo, features, axes); } - CATCH_LOG(); } - try - { - _updateFont(fontInfoDesired, fontInfo, features, axes); - return S_OK; - } - CATCH_RETURN(); + return hr; } void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept @@ -536,7 +521,8 @@ void AtlasEngine::_resolveTransparencySettings() noexcept } } -void AtlasEngine::_updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) +[[nodiscard]] HRESULT AtlasEngine::_updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept +try { std::vector fontFeatures; if (!features.empty()) @@ -616,9 +602,12 @@ void AtlasEngine::_updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& _resolveFontMetrics(fontInfoDesired, fontInfo, font); font->fontFeatures = std::move(fontFeatures); font->fontAxisValues = std::move(fontAxisValues); + + return S_OK; } +CATCH_RETURN() -void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics) const +void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics) { const auto& faceName = fontInfoDesired.GetFaceName(); const auto requestedFamily = fontInfoDesired.GetFamily(); @@ -659,6 +648,16 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo BOOL exists = false; THROW_IF_FAILED(fontCollection->FindFamilyName(fontName.c_str(), &index, &exists)); + // In case of a portable build, the given font may not be installed and instead be bundled next to our executable. + if constexpr (Feature_NearbyFontLoading::IsEnabled()) + { + if (!exists && _updateWithNearbyFontCollection()) + { + fontCollection = _api.s->font->fontCollection; + THROW_IF_FAILED(fontCollection->FindFamilyName(fontName.c_str(), &index, &exists)); + } + } + if (!exists) { if (!missingFontNames.empty()) @@ -869,3 +868,29 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo fontMetrics->colorGlyphs = fontInfoDesired.GetEnableColorGlyphs(); } } + +// Nearby fonts are described a couple of times throughout the file. +// This abstraction in particular helps us avoid retrying when it's pointless: +// After all, if the font collection didn't change (no nearby fonts, loading failed, it's already loaded), +// we don't need to try it again. It returns true if retrying is necessary. +[[nodiscard]] bool AtlasEngine::_updateWithNearbyFontCollection() noexcept +{ + // _resolveFontMetrics() checks `_api.s->font->fontCollection` for a pre-existing font collection, + // before falling back to using the system font collection. This way we can inject our custom one. + // Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection + // instance across font changes, like when zooming the font size rapidly using the scroll wheel. + wil::com_ptr collection; + try + { + collection = FontCache::GetCached(); + } + CATCH_LOG(); + + if (!collection || _api.s->font->fontCollection == collection) + { + return false; + } + + _api.s.write()->font.write()->fontCollection = std::move(collection); + return true; +} diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 5c9f954ae2f..6d346d7bfe4 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -94,8 +94,9 @@ namespace Microsoft::Console::Render::Atlas // AtlasEngine.api.cpp void _resolveTransparencySettings() noexcept; - void _updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes); - void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics = nullptr) const; + [[nodiscard]] HRESULT _updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept; + void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics = nullptr); + [[nodiscard]] bool _updateWithNearbyFontCollection() noexcept; // AtlasEngine.r.cpp ATLAS_ATTR_COLD void _recreateAdapter(); From f2d6cceb2a36827ec3fcb0016e7a604ffdba28c2 Mon Sep 17 00:00:00 2001 From: Windows Console Service Bot <14666831+consvc@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:06:27 -0500 Subject: [PATCH 199/603] Localization Updates - main - 03/30/2024 03:04:37 (#16973) --- src/cascadia/TerminalApp/Resources/de-DE/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/es-ES/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/it-IT/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw | 6 +++--- src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw | 6 +++--- .../TerminalSettingsEditor/Resources/de-DE/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/es-ES/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/fr-FR/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/it-IT/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/ja-JP/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/ko-KR/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/pt-BR/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/ru-RU/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/zh-CN/Resources.resw | 4 ++++ .../TerminalSettingsEditor/Resources/zh-TW/Resources.resw | 4 ++++ 23 files changed, 79 insertions(+), 39 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw index f99341de170..948fec8ff61 100644 --- a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw @@ -186,9 +186,6 @@ Beenden - - Möchten Sie alle Registerkarten schließen? - Mehrere Bereiche @@ -335,6 +332,9 @@ Mit dem angegebenen Profil öffnen. Akzeptiert entweder den Namen oder die GUID eines Profils + + Legt die WT_SESSION-Variable fest; muss eine GUID sein. + Erstellen eines neuen geteilten Bereichs diff --git a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw index 7eaf5023fd7..0679cd0ddd6 100644 --- a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw @@ -183,9 +183,6 @@ Salir - - ¿Quieres cerrar todas las pestañas? - Varios paneles @@ -332,6 +329,9 @@ Abre con el perfil determinado. Acepta el nombre o el GUID de un perfil. + + Establece la variable WT_SESSION; debe ser un GUID + Crear un nuevo panel de división diff --git a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw index 3f5a403a79a..887f79be427 100644 --- a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw @@ -183,9 +183,6 @@ Quitter - - Voulez-vous fermer tous les onglets ? - Volets multiples @@ -332,6 +329,9 @@ Ouvrez avec le profil donné. Accepte le nom ou le GUID d’un profil + + Définit la variable WT_SESSION ; doit être un GUID + Créer un volet de fractionnement diff --git a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw index fb7b04cd57d..69122195dbb 100644 --- a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw @@ -183,9 +183,6 @@ Esci - - Vuoi chiudere tutte le schede? - Più riquadri @@ -332,6 +329,9 @@ Apri con il profilo specificato. Accetta il nome oppure il GUID di un profilo + + Imposta la variabile di WT_SESSION; deve essere un GUID + Crea un nuovo riquadro suddiviso diff --git a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw index 48db8a0fd68..3451318e931 100644 --- a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw @@ -184,9 +184,6 @@ 終了 - - すべてのタブを閉じますか? - 複数ウィンドウ @@ -333,6 +330,9 @@ 指定されたプロファイルで開きます。プロファイルの名前または GUID を指定できます + + WT_SESSION 変数を設定します; GUID である必要があります + 新しい分割ウィンドウの作成 diff --git a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw index 820db0c8fed..2b7ebd75146 100644 --- a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw @@ -183,9 +183,6 @@ 끝내기 - - 모든 탭을 닫으시겠습니까? - 여러 창 @@ -332,6 +329,9 @@ 지정된 프로필로 엽니다. 프로필 이름 또는 GUID 허용 + + WT_SESSION 변수를 설정합니다. GUID여야 합니다. + 새 분할 창 만들기 diff --git a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw index 2ba06ec576d..d94a2946410 100644 --- a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw @@ -183,9 +183,6 @@ Encerrar - - Deseja fechar todas as guias? - Vários painéis @@ -332,6 +329,9 @@ Abrir com o perfil determinado. Aceite o nome ou o GUID de um perfil + + Define a variável WT_SESSION; deve ser um GUID + Criar um novo painel dividido diff --git a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw index 206c42a94b4..0e553d2fc36 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw @@ -187,9 +187,6 @@ Qùíт ! - - Đǿ ÿõű ŵãŋт тŏ ¢ľòŝê ăŀľ ŧãвŝ? !!! !!! !!! - Мµļтíрłĕ φдпėŝ !!! ! @@ -340,6 +337,9 @@ Θφêп шĩτћ ťнě ģìνēή ρѓøƒìĺę. Âćĉеφťś ёĩτĥėŗ τђė йάмє øя ĠŮΪÐ õƒ а φŕóƒίℓè !!! !!! !!! !!! !!! !!! !!! + + Šέŧś ŧћэ ẀŦ_ŜÉŜŜІΟΝ νăяíåьłé; mµśτ вэ â ĢŨÎĐ !!! !!! !!! !!! ! + Ĉґéáŧе д ήэẅ ŝφĺĭτ рãňё !!! !!! diff --git a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw index 206c42a94b4..0e553d2fc36 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw @@ -187,9 +187,6 @@ Qùíт ! - - Đǿ ÿõű ŵãŋт тŏ ¢ľòŝê ăŀľ ŧãвŝ? !!! !!! !!! - Мµļтíрłĕ φдпėŝ !!! ! @@ -340,6 +337,9 @@ Θφêп шĩτћ ťнě ģìνēή ρѓøƒìĺę. Âćĉеφťś ёĩτĥėŗ τђė йάмє øя ĠŮΪÐ õƒ а φŕóƒίℓè !!! !!! !!! !!! !!! !!! !!! + + Šέŧś ŧћэ ẀŦ_ŜÉŜŜІΟΝ νăяíåьłé; mµśτ вэ â ĢŨÎĐ !!! !!! !!! !!! ! + Ĉґéáŧе д ήэẅ ŝφĺĭτ рãňё !!! !!! diff --git a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw index 206c42a94b4..0e553d2fc36 100644 --- a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw @@ -187,9 +187,6 @@ Qùíт ! - - Đǿ ÿõű ŵãŋт тŏ ¢ľòŝê ăŀľ ŧãвŝ? !!! !!! !!! - Мµļтíрłĕ φдпėŝ !!! ! @@ -340,6 +337,9 @@ Θφêп шĩτћ ťнě ģìνēή ρѓøƒìĺę. Âćĉеφťś ёĩτĥėŗ τђė йάмє øя ĠŮΪÐ õƒ а φŕóƒίℓè !!! !!! !!! !!! !!! !!! !!! + + Šέŧś ŧћэ ẀŦ_ŜÉŜŜІΟΝ νăяíåьłé; mµśτ вэ â ĢŨÎĐ !!! !!! !!! !!! ! + Ĉґéáŧе д ήэẅ ŝφĺĭτ рãňё !!! !!! diff --git a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw index a66c47aabd6..0a74add4c32 100644 --- a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw @@ -183,9 +183,6 @@ Выход - - Закрыть все вкладки? - Несколько областей @@ -332,6 +329,9 @@ Открыть с помощью данного профиля. Принимается имя или GUID профиля + + Задает переменную WT_SESSION; должно быть GUID + Создать новую область разделения diff --git a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw index 7cee543bce9..78135ba31a8 100644 --- a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw @@ -183,9 +183,6 @@ 退出 - - 是否要关闭所有标签页? - 多个窗格 @@ -332,6 +329,9 @@ 使用给定的配置文件打开。接受配置文件的名称或 GUID + + 设置 WT_SESSION 变量;必须是 GUID + 创建新的拆分窗格 diff --git a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw index 33e548f77ad..5a7e89a6711 100644 --- a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw @@ -183,9 +183,6 @@ 結束 - - 您要關閉所有索引標籤嗎? - 多個窗格 @@ -332,6 +329,9 @@ 使用指定的設定檔開啟。接受設定檔的名稱或 GUID + + 設定 WT_SESSION 變數; 必須為 GUID + 建立新的分割窗格 diff --git a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw index 96d50e58db8..eb3aee0ac6e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw @@ -989,6 +989,10 @@ Dieses Profil als Administrator ausführen Header for a control to toggle whether the profile should always open elevated (in an admin window) + + Wenn diese Option aktiviert ist, wird das Profil automatisch in einem Admin Terminalfenster geöffnet. Wenn das aktuelle Fenster bereits als Administrator ausgeführt wird, wird es in diesem Fenster geöffnet. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + Größe des Verlaufs Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index db4abb81b4f..06ef4c0b3c2 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -989,6 +989,10 @@ Ejecutar este perfil como Administrador Header for a control to toggle whether the profile should always open elevated (in an admin window) + + Si se habilita, el perfil se abrirá automáticamente en una ventana de terminal Administración. Si la ventana actual ya se está ejecutando como administrador, se abrirá en esta ventana. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + Tamaño del historial Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index f14f47e5848..0d2433ba1de 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -989,6 +989,10 @@ Exécuter ce profil en tant qu’administrateur Header for a control to toggle whether the profile should always open elevated (in an admin window) + + Si cette option est activée, le profil s’ouvre automatiquement dans une fenêtre de terminal Administration. Si la fenêtre active est déjà en cours d’exécution en tant qu’administrateur, elle s’ouvre dans cette fenêtre. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + Taille de l’historique Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 5bb075b01f9..6cbfe190b22 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -989,6 +989,10 @@ Esegui questo profilo come amministratore Header for a control to toggle whether the profile should always open elevated (in an admin window) + + Se questa opzione è abilitata, il profilo verrà aperto automaticamente in una finestra del terminale Amministrazione. Se la finestra corrente è già in esecuzione come amministratore, verrà aperta in questa finestra. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + Dimensioni cronologia Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index e9f4b3eb2f2..70e4da642b0 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -989,6 +989,10 @@ このプロファイルを管理者として実行する Header for a control to toggle whether the profile should always open elevated (in an admin window) + + 有効にすると、プロファイルは自動的にターミナル ウィンドウ管理開きます。現在のウィンドウが既に管理者として実行されている場合は、このウィンドウで開きます。 + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + 履歴のサイズ Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index 7e4a769c59b..04bf3054043 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -989,6 +989,10 @@ 이 프로필을 관리자 권한으로 실행 Header for a control to toggle whether the profile should always open elevated (in an admin window) + + 사용하도록 설정하면 프로필이 관리 터미널 창에서 자동으로 열립니다. 현재 창이 관리자 권한으로 이미 실행되고 있는 경우 이 창에서 열립니다. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + 기록 크기 Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index 8352f42f0da..09611a80f06 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -989,6 +989,10 @@ Executar esse perfil como Administrador Header for a control to toggle whether the profile should always open elevated (in an admin window) + + Se habilitado, o perfil será aberto em uma janela Administração terminal automaticamente. Se a janela atual já estiver em execução como administrador, ela será aberta nesta janela. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + Tamanho de histórico Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index 5d8bb846c6e..4c18e988ea3 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -989,6 +989,10 @@ Запустить этот профиль от имени администратора Header for a control to toggle whether the profile should always open elevated (in an admin window) + + Если этот параметр включен, профиль будет автоматически открываться в Администратор терминала. Если текущее окно уже запущено от имени администратора, оно откроется в этом окне. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + Размер журнала Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 7dcaa741b32..1dfc634ae45 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -989,6 +989,10 @@ 以管理员身份运行此配置文件 Header for a control to toggle whether the profile should always open elevated (in an admin window) + + 如果启用,配置文件将在管理员终端窗口中自动打开。如果当前窗口已以管理员身份运行,它将在此窗口中打开。 + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + 历史记录大小 Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index 2f1cf24cb98..ccc0bb47203 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -989,6 +989,10 @@ 以系統管理員身分執行此設定檔 Header for a control to toggle whether the profile should always open elevated (in an admin window) + + 如果啟用,配置檔將會自動在 管理員 終端機視窗中開啟。如果目前的視窗已以系統管理員身分執行,則會在此視窗中開啟。 + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + 歷史大小 Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. From ad0b67239da6031551146ee93e7d526cb0a01c4d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Apr 2024 10:07:33 -0700 Subject: [PATCH 200/603] Add some docs on how to resolve `DEP0700` errors in VS (#16997) This happens to me once a month, and I finally solved it. --- doc/building.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/doc/building.md b/doc/building.md index 968a6abd8db..60f53343937 100644 --- a/doc/building.md +++ b/doc/building.md @@ -154,3 +154,47 @@ popd The `bx` will build just the Terminal package, critically, populating the `CascadiaPackage.build.appxrecipe` file. Once that's been built, then the `DeployAppRecipe.exe` command can be used to deploy a loose layout in the same way that Visual Studio does. Notably, this method of building the Terminal package can't leverage the FastUpToDate check in Visual Studio, so the builds end up being considerably slower for the whole package, as cppwinrt does a lot of work before confirming that it's up to date and doing nothing. + + +### Are you seeing `DEP0700: Registration of the app failed`? + +Once in a blue moon, I get a `DEP0700: Registration of the app failed. +[0x80073CF6] error 0x80070020: Windows cannot register the package because of an +internal error or low memory.` when trying to deploy in VS. For us, that can +happen if the `OpenConsoleProxy.dll` gets locked up, in use by some other +terminal package. + +Doing the equivalent command in powershell can give us more info: + +```pwsh +Add-AppxPackage -register "Z:\dev\public\OpenConsole\src\cascadia\CascadiaPackage\bin\x64\Debug\AppX\AppxManifest.xml" +``` + +That'll suggest `NOTE: For additional information, look for [ActivityId] +dbf551f1-83d0-0007-43e7-9cded083da01 in the Event Log or use the command line +Get-AppPackageLog -ActivityID dbf551f1-83d0-0007-43e7-9cded083da01`. So do that: + +```pwsh +Get-AppPackageLog -ActivityID dbf551f1-83d0-0007-43e7-9cded083da01 +``` + +which will give you a lot of info. In my case, that revealed that the platform +couldn't delete the packaged com entries. The key line was: `AppX Deployment +operation failed with error 0x0 from API Logging data because access was denied +for file: +C:\ProgramData\Microsoft\Windows\AppRepository\Packages\WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe, +user SID: S-1-5-18` + +Take that path, and +```pwsh +sudo start C:\ProgramData\Microsoft\Windows\AppRepository\Packages\WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe +``` + +(use `sudo`, since the path is otherwise locked down). From there, go into the +`PackagedCom` folder, and open [File +Locksmith](https://learn.microsoft.com/en-us/windows/powertoys/file-locksmith) +(or Process Explorer, if you're more familiar with that) on +`OpenConsoleProxy.dll`. Just go ahead and immediately re-launch it as admin, +too. That should list off a couple terminal processes that are just hanging +around. Go ahead and end them all. You should be good to deploy again after +that. From 5f1015953fc65c5542a338de778cbc9acaf01faf Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Apr 2024 10:08:03 -0700 Subject: [PATCH 201/603] Don't explode on session restore (#16998) As noted in #16995. Don't persist us if we weren't ever initialized. In that case, we never got an initial size, never instantiated a buffer, and didn't start the connection yet, so there's nothing for us to add here. If we were supposed to be restored from a path, then we don't need to do anything special here. We'll leave the original file untouched, and the next time we actually are initialized, we'll just use that file then. Closes #16995 --- src/cascadia/TerminalControl/TermControl.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 0e459e9238b..ec9807dcd08 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2276,7 +2276,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::PersistToPath(const winrt::hstring& path) const { - winrt::get_self(_core)->PersistToPath(path.c_str()); + // Don't persist us if we weren't ever initialized. In that case, we + // never got an initial size, never instantiated a buffer, and didn't + // start the connection yet, so there's nothing for us to add here. + // + // If we were supposed to be restored from a path, then we don't need to + // do anything special here. We'll leave the original file untouched, + // and the next time we actually are initialized, we'll just use that + // file then. + if (_initializedTerminal) + { + winrt::get_self(_core)->PersistToPath(path.c_str()); + } } void TermControl::Close() From ad0c28b30ddf36e1719bae5e1ef92f085594e19c Mon Sep 17 00:00:00 2001 From: Marcel W Date: Tue, 2 Apr 2024 19:56:26 +0200 Subject: [PATCH 202/603] Add clamping of initial rows and columns settings (#16989) This clamps the initial rows and columns settings in two areas: - When reading the JSON file - In the settings dialogue For consistency, I've also added a minimum value to the NumberBoxes even though the default Minimum is 1. The Maximum and Minimum are taken from the JSON Schema file (Min 1, Max 999). Closes #11957 --------- Co-authored-by: Dustin L. Howett --- src/cascadia/TerminalSettingsEditor/Launch.xaml | 4 ++++ .../TerminalSettingsModel/GlobalAppSettings.cpp | 11 +++++++++++ .../DeserializationTests.cpp | 15 +++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/cascadia/TerminalSettingsEditor/Launch.xaml b/src/cascadia/TerminalSettingsEditor/Launch.xaml index e68076a5032..2498662a4ce 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.xaml +++ b/src/cascadia/TerminalSettingsEditor/Launch.xaml @@ -184,6 +184,8 @@ Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" + Maximum="999" + Minimum="1" Style="{StaticResource LaunchSizeNumberBoxStyle}" Value="{x:Bind ViewModel.InitialCols, Mode=TwoWay}" /> diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index c307050a998..ff6dd5eec63 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -137,6 +137,17 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_LAYER_JSON) #undef GLOBAL_SETTINGS_LAYER_JSON + // GH#11975 We only want to allow sensible values and prevent crashes, so we are clamping those values + // We only want to assign if the value did change through clamping, + // otherwise we could end up setting defaults that get persisted + if (this->HasInitialCols()) + { + this->InitialCols(std::clamp(this->InitialCols(), 1, 999)); + } + if (this->HasInitialRows()) + { + this->InitialRows(std::clamp(this->InitialRows(), 1, 999)); + } LayerActionsFrom(json, origin, true); JsonUtils::GetValueForKey(json, LegacyReloadEnvironmentVariablesKey, _legacyReloadEnvironmentVariables); diff --git a/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp index 2425e3421d8..bd17076bc94 100644 --- a/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp @@ -47,6 +47,7 @@ namespace SettingsModelUnitTests TEST_METHOD(ValidateKeybindingsWarnings); TEST_METHOD(ValidateColorSchemeInCommands); TEST_METHOD(ValidateExecuteCommandlineWarning); + TEST_METHOD(TestClampingOfStartupColumnAndViewProperties); TEST_METHOD(TestTrailingCommas); TEST_METHOD(TestCommandsAndKeybindings); TEST_METHOD(TestNestedCommandWithoutName); @@ -1301,6 +1302,20 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->Warnings().GetAt(3)); } + void DeserializationTests::TestClampingOfStartupColumnAndViewProperties() + { + static constexpr std::string_view inputSettings{ R"({ + "initialCols" : 1000000, + "initialRows" : -1000000, + "profiles": [{ "name": "profile0" }] + })" }; + + const auto settings = createSettings(inputSettings); + + VERIFY_ARE_EQUAL(999, settings->GlobalSettings().InitialCols()); + VERIFY_ARE_EQUAL(1, settings->GlobalSettings().InitialRows()); + } + void DeserializationTests::TestTrailingCommas() { static constexpr std::string_view badSettings{ R"({ From 4d58137bd4ce0dc3563d4991f1cb6ff1115f25cb Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Wed, 3 Apr 2024 01:27:19 +0530 Subject: [PATCH 203/603] Fix selection expansion for Double-Width and Double-Height rows (#16812) Closes #16782 ### Validation Steps Performed - Double-clicking on a Double-Width row selects the word (identified by delimiters) under the cursor. - Tripple-clicking on a Double-Width row selects the whole line under the cursor. - The same works for Double-Height rows also. - The same works for Single-Width rows also. --- src/buffer/out/textBuffer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 40cfc6b17ad..75cd6fd4f01 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1297,7 +1297,8 @@ void TextBuffer::TriggerNewTextNotification(const std::wstring_view newText) // - the delimiter class for the given char DelimiterClass TextBuffer::_GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const { - return GetRowByOffset(pos.y).DelimiterClassAt(pos.x, wordDelimiters); + const auto realPos = ScreenToBufferPosition(pos); + return GetRowByOffset(realPos.y).DelimiterClassAt(realPos.x, wordDelimiters); } // Method Description: From 04fa18de71a23a1ec66c5e968a8bd96c2e654f4c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Apr 2024 13:27:43 -0700 Subject: [PATCH 204/603] Scratch tool: generate a fragment extension with every color scheme (#16962) #16953 got me thinking: what if we just published an extension ourselves that just packages up every color scheme in the ever-amazing https://github.com/mbadolato/iTerm2-Color-Schemes? Well, this isn't a package for that file. But it is a script to generate a fragment with all of them, and blat it into your `%LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments`. It's a notebook because I've been really fascinated with the Polyglot Notebooks recently. --- .../schemes-fragment/import-all-schemes.ipynb | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/tools/schemes-fragment/import-all-schemes.ipynb diff --git a/src/tools/schemes-fragment/import-all-schemes.ipynb b/src/tools/schemes-fragment/import-all-schemes.ipynb new file mode 100644 index 00000000000..c387d28b6b8 --- /dev/null +++ b/src/tools/schemes-fragment/import-all-schemes.ipynb @@ -0,0 +1,147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ITerm2 schemes for the Terminal\n", + "\n", + "This is a little helper script to gather up all the color schemes in [mbadolato/iTerm2-Color-Schemes](https://github.com/mbadolato/iTerm2-Color-Schemes), and put them in a single fragment extension for the Terminal. \n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + } + }, + "outputs": [], + "source": [ + "# Clone the repo into the temp directory\n", + "\n", + "cd \"$env:TEMP\"\n", + "if (Test-Path -Path \"$env:TEMP\\iTerm2-Color-Schemes\") {\n", + " Remove-Item -Recurse -Force \"$env:TEMP\\iTerm2-Color-Schemes\"\n", + "}\n", + "git clone --depth 1 https://github.com/mbadolato/iTerm2-Color-Schemes.git\n", + "\n", + "cd \"$env:TEMP\\iTerm2-Color-Schemes\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Importing schemes from 'C:\\Users\\migrie\\AppData\\Local\\Temp\\iTerm2-Color-Schemes'\n", + "Found 319 schemes\n", + "\u001b[K Importing C:\\Users\\migrie\\AppData\\Local\\Temp\\iTerm2-Color-Schemes\\windowsterminal\\zenwritten_light.json Imported json for 319 schemes\n", + "\n", + " Directory: C:\\Users\\migrie\\AppData\\Local\\Microsoft\\Windows Terminal\\Fragments\n", + "\n", + "\u001b[32;1mMode \u001b[0m\u001b[32;1m LastWriteTime\u001b[0m \u001b[32;1;3m Length\u001b[0m\u001b[32;1m Name\u001b[0m\n", + "\u001b[32;1m---- \u001b[0m \u001b[32;1m -------------\u001b[0m \u001b[32;1m ------\u001b[0m \u001b[32;1m----\u001b[0m\n", + "d---- 3/28/2024 6:30 AM \u001b[44;1mAllColorSchemes\u001b[0m\n", + "Fragment json written to C:\\Users\\migrie\\AppData\\Local\\Microsoft\\Windows Terminal\\Fragments\\AllColorSchemes\\schemes.json\n", + "\n" + ] + } + ], + "source": [ + "cd \"$env:TEMP\\iTerm2-Color-Schemes\"\n", + "\n", + "Write-Host \"Importing schemes from '$env:TEMP\\iTerm2-Color-Schemes'\"\n", + "\n", + "# Iterate over all the files in the `windowsterminal` directory\n", + "\n", + "$allSchemesFiles = Get-ChildItem -Path \"$env:TEMP\\iTerm2-Color-Schemes\\windowsterminal\" -Filter *.json\n", + "Write-host \"Found $($allSchemesFiles.Count) schemes\"\n", + "\n", + "$allSchemeJsons = @()\n", + "\n", + "$allSchemesFiles | ForEach-Object {\n", + "\n", + " Write-Host \"`r`e[K Importing $_ \" -NoNewline\n", + " $json = Get-Content $_.FullName -Raw | ConvertFrom-Json\n", + " $allSchemeJsons += $json\n", + "\n", + "}\n", + "Write-Host \"\"\n", + "Write-Host \"Imported json for $($allSchemeJsons.Count) schemes\"\n", + "\n", + "# Create a new fragment json in the temp directory with all the schemes added to a \"schemes\" array\n", + "\n", + "$fragmentJson = @{\n", + " \"schemes\" = $allSchemeJsons\n", + "} | ConvertTo-Json\n", + "\n", + "# Remove the existing fragment json if it exists\n", + "$fragmentDir = $env:LOCALAPPDATA + \"\\Microsoft\\Windows Terminal\\Fragments\\AllColorSchemes\"\n", + "$fragmentPath = $fragmentDir + \"\\schemes.json\"\n", + "if (Test-Path -Path $fragmentPath) {\n", + " Remove-Item -Path $fragmentPath\n", + "}\n", + "# make sure the directory exists\n", + "New-Item -Path $fragmentDir -ItemType Directory -Force\n", + "\n", + "# Write the fragment json to the fragments directory\n", + "Write-Output $fragmentJson | Out-File $fragmentPath -Encoding Utf8\n", + "\n", + "write-host \"Fragment json written to $fragmentPath\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After running this notebook, all the color schemes will be available in the Terminal. You'll probably need to go touch the settings to get them reloaded. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "polyglot-notebook" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + }, + { + "aliases": [], + "languageName": "pwsh", + "name": "pwsh" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 2bcbe6b49208cb2040091b1cd2c9b3df8e69f5bd Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 2 Apr 2024 17:52:29 -0500 Subject: [PATCH 205/603] ci/rel: publish symbols using the internal symbol request API instead (#16991) Work is ongoing to remove individually-authenticated service accounts from some pipelines. This moves us closer to that goal. Tested in Nightly 2403.28002. --- .github/actions/spelling/allow/microsoft.txt | 2 + build/pipelines/ob-nightly.yml | 2 + build/pipelines/ob-release.yml | 2 + ...sh-symbols-using-symbolrequestprod-api.yml | 117 ++++++++++++++++++ .../pipeline-onebranch-full-release-build.yml | 9 +- 5 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index 5e7aa5c06ee..9cda69aa947 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -32,6 +32,7 @@ DWINRT enablewttlogging HOMESHARE Intelli +issecret IVisual libucrt libucrtd @@ -74,6 +75,7 @@ sid Skype SRW sxs +symbolrequestprod Sysinternals sysnative systemroot diff --git a/build/pipelines/ob-nightly.yml b/build/pipelines/ob-nightly.yml index 1c90866f9e2..99cff5b206c 100644 --- a/build/pipelines/ob-nightly.yml +++ b/build/pipelines/ob-nightly.yml @@ -33,6 +33,8 @@ extends: publishSymbolsToPublic: true publishVpackToWindows: false symbolExpiryTime: 15 + symbolPublishingSubscription: $(SymbolPublishingServiceConnection) + symbolPublishingProject: $(SymbolPublishingProject) ${{ if eq(true, parameters.publishToAzure) }}: extraPublishJobs: - template: build/pipelines/templates-v2/job-deploy-to-azure-storage.yml@self diff --git a/build/pipelines/ob-release.yml b/build/pipelines/ob-release.yml index ca168e6d337..147fef4e5a1 100644 --- a/build/pipelines/ob-release.yml +++ b/build/pipelines/ob-release.yml @@ -81,3 +81,5 @@ extends: terminalInternalPackageVersion: ${{ parameters.terminalInternalPackageVersion }} publishSymbolsToPublic: ${{ parameters.publishSymbolsToPublic }} publishVpackToWindows: ${{ parameters.publishVpackToWindows }} + symbolPublishingSubscription: $(SymbolPublishingServiceConnection) + symbolPublishingProject: $(SymbolPublishingProject) diff --git a/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml b/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml new file mode 100644 index 00000000000..0f9d338699b --- /dev/null +++ b/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml @@ -0,0 +1,117 @@ +parameters: + - name: includePublicSymbolServer + type: boolean + default: false + - name: pool + type: object + default: [] + - name: dependsOn + type: object + default: null + - name: artifactStem + type: string + default: '' + - name: jobName + type: string + default: PublishSymbols + - name: symbolExpiryTime + type: string + default: 36530 # This is the default from PublishSymbols@2 + - name: variables + type: object + default: {} + - name: subscription + type: string + - name: symbolProject + type: string + +jobs: +- job: ${{ parameters.jobName }} + ${{ if ne(length(parameters.pool), 0) }}: + pool: ${{ parameters.pool }} + ${{ if eq(parameters.includePublicSymbolServer, true) }}: + displayName: Publish Symbols to Internal and MSDL + ${{ else }}: + displayName: Publish Symbols Internally + dependsOn: ${{ parameters.dependsOn }} + variables: + ${{ insert }}: ${{ parameters.variables }} + steps: + - checkout: self + clean: true + fetchDepth: 1 + fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here + submodules: true + persistCredentials: True + + - task: PkgESSetupBuild@12 + displayName: Package ES - Setup Build + inputs: + disableOutputRedirect: true + + - task: DownloadPipelineArtifact@2 + displayName: Download all PDBs from all prior build phases + inputs: + itemPattern: '**/*.pdb' + targetPath: '$(Build.SourcesDirectory)/bin' + + - powershell: |- + Get-PackageProvider -Name NuGet -ForceBootstrap + Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute + displayName: Install Azure Module Dependencies + + # Transit the Azure token from the Service Connection into a secret variable for the rest of the pipeline to use. + - task: AzurePowerShell@5 + displayName: Generate an Azure Token + inputs: + azureSubscription: ${{ parameters.subscription }} + azurePowerShellVersion: LatestVersion + pwsh: true + ScriptType: InlineScript + Inline: |- + $AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token + Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken" + + + - task: PublishSymbols@2 + displayName: Publish Symbols (to current Azure DevOps tenant) + continueOnError: True + inputs: + SymbolsFolder: '$(Build.SourcesDirectory)/bin' + SearchPattern: '**/*.pdb' + IndexSources: false + DetailedLog: true + SymbolsMaximumWaitTime: 30 + SymbolServerType: 'TeamServices' + SymbolsProduct: 'Windows Terminal Converged Symbols' + SymbolsVersion: '$(XES_APPXMANIFESTVERSION)' + SymbolsArtifactName: 'WindowsTerminal_$(XES_APPXMANIFESTVERSION)' + SymbolExpirationInDays: ${{ parameters.symbolExpiryTime }} + env: + LIB: $(Build.SourcesDirectory) + + - pwsh: |- + # Prepare the defaults for IRM + $PSDefaultParameterValues['Invoke-RestMethod:Headers'] = @{ Authorization = "Bearer $(SymbolAccessToken)" } + $PSDefaultParameterValues['Invoke-RestMethod:ContentType'] = "application/json" + $PSDefaultParameterValues['Invoke-RestMethod:Method'] = "POST" + + $BaseUri = "https://symbolrequestprod.trafficmanager.net/projects/${{ parameters.symbolProject }}/requests" + + # Prepare the request + $expiration = (Get-Date).Add([TimeSpan]::FromDays(${{ parameters.symbolExpiryTime }})) + $createRequestBody = @{ + requestName = "WindowsTerminal_$(XES_APPXMANIFESTVERSION)"; + expirationTime = $expiration.ToString(); + } + Write-Host "##[debug]Starting request $($createRequestBody.requestName) with expiration date of $($createRequestBody.expirationTime)" + Invoke-RestMethod -Uri "$BaseUri" -Body ($createRequestBody | ConvertTo-Json -Compress) -Verbose + + # Request symbol publication + $publishRequestBody = @{ + publishToInternalServer = $true; + publishToPublicServer = $${{ parameters.includePublicSymbolServer }}; + } + Write-Host "##[debug]Submitting request $($createRequestBody.requestName) ($($publishRequestBody | ConvertTo-Json -Compress))" + Invoke-RestMethod -Uri "$BaseUri/$($createRequestBody.requestName)" -Body ($publishRequestBody | ConvertTo-Json -Compress) -Verbose + displayName: Publish Symbols using internal REST API diff --git a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml index f66d452b36a..37d21c89a69 100644 --- a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml +++ b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml @@ -52,6 +52,10 @@ parameters: - name: publishVpackToWindows type: boolean default: false + - name: symbolPublishingSubscription + type: string + - name: symbolPublishingProject + type: string - name: extraPublishJobs type: object @@ -248,12 +252,13 @@ extends: displayName: Publish dependsOn: [Build] jobs: - - template: ./build/pipelines/templates-v2/job-publish-symbols.yml@self + - template: ./build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml@self parameters: pool: { type: windows } includePublicSymbolServer: ${{ parameters.publishSymbolsToPublic }} - symbolPatGoesInTaskInputs: true # onebranch tries to muck with the PAT variable, so we need to change how it get the PAT symbolExpiryTime: ${{ parameters.symbolExpiryTime }} + subscription: ${{ parameters.symbolPublishingSubscription }} + symbolProject: ${{ parameters.symbolPublishingProject }} variables: ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true From 67ae9f6c3e42e1ad3431ca02123fcfce57eef188 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 3 Apr 2024 08:31:13 -0700 Subject: [PATCH 206/603] Refactor the SettingsTab to be a pane (#16172) ... technically. We still won't let it actually _be_ a pane, but now it acts like one. It's hosted in a `SettingsPaneContent`. There's no more `SettingsTab`. It totally _can_ be in a pane (but don't?) ## Validation Steps Performed * Still opens the settings * Only opens a single settings tab, or re-activates the existing one * Session restores! * Updates the title of the tab appropriately * I previously _did_ use the scratchpad action to open the settings in a pane, and that worked. ## Related PRs * #16170 * #16171 * #16172 <-- you are here * #16895 Refs #997 Closes #8452 --- .../TerminalApp/AppActionHandlers.cpp | 4 +- src/cascadia/TerminalApp/IPaneContent.idl | 4 + src/cascadia/TerminalApp/Pane.cpp | 55 ++++++-- src/cascadia/TerminalApp/Pane.h | 4 +- .../TerminalApp/ScratchpadContent.cpp | 16 +++ src/cascadia/TerminalApp/ScratchpadContent.h | 5 + .../TerminalApp/SettingsPaneContent.cpp | 84 +++++++++++ .../TerminalApp/SettingsPaneContent.h | 46 ++++++ src/cascadia/TerminalApp/SettingsTab.cpp | 131 ------------------ src/cascadia/TerminalApp/SettingsTab.h | 43 ------ src/cascadia/TerminalApp/SettingsTab.idl | 12 -- src/cascadia/TerminalApp/TabManagement.cpp | 32 ++--- .../TerminalApp/TerminalAppLib.vcxproj | 22 +-- .../TerminalAppLib.vcxproj.filters | 7 +- src/cascadia/TerminalApp/TerminalPage.cpp | 128 ++++------------- src/cascadia/TerminalApp/TerminalPage.h | 6 +- .../TerminalApp/TerminalPaneContent.cpp | 32 ++++- .../TerminalApp/TerminalPaneContent.h | 7 +- .../TerminalApp/TerminalPaneContent.idl | 4 +- .../TerminalApp/TerminalSettingsCache.cpp | 57 ++++++++ .../TerminalApp/TerminalSettingsCache.h | 37 +++++ .../TerminalApp/TerminalSettingsCache.idl | 12 ++ src/cascadia/TerminalApp/TerminalTab.cpp | 50 +++++-- src/cascadia/TerminalApp/TerminalTab.h | 4 +- .../TerminalApp/dll/TerminalApp.vcxproj | 1 - 25 files changed, 440 insertions(+), 363 deletions(-) create mode 100644 src/cascadia/TerminalApp/SettingsPaneContent.cpp create mode 100644 src/cascadia/TerminalApp/SettingsPaneContent.h delete mode 100644 src/cascadia/TerminalApp/SettingsTab.cpp delete mode 100644 src/cascadia/TerminalApp/SettingsTab.h delete mode 100644 src/cascadia/TerminalApp/SettingsTab.idl create mode 100644 src/cascadia/TerminalApp/TerminalSettingsCache.cpp create mode 100644 src/cascadia/TerminalApp/TerminalSettingsCache.h create mode 100644 src/cascadia/TerminalApp/TerminalSettingsCache.idl diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 662ffc25746..22a0582dfda 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1418,7 +1418,7 @@ namespace winrt::TerminalApp::implementation args.Handled(true); } - void TerminalPage::_HandleOpenScratchpad(const IInspectable& /*sender*/, + void TerminalPage::_HandleOpenScratchpad(const IInspectable& sender, const ActionEventArgs& args) { if (Feature_ScratchpadPane::IsEnabled()) @@ -1431,7 +1431,7 @@ namespace winrt::TerminalApp::implementation scratchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler }); const auto resultPane = std::make_shared(*scratchPane); - _SplitPane(_GetFocusedTabImpl(), SplitDirection::Automatic, 0.5f, resultPane); + _SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane); args.Handled(true); } } diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index a26d1a05477..fa258fdbd26 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -19,6 +19,7 @@ namespace TerminalApp interface IPaneContent { Windows.UI.Xaml.FrameworkElement GetRoot(); + void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); Windows.Foundation.Size MinimumSize { get; }; @@ -26,6 +27,9 @@ namespace TerminalApp UInt64 TaskbarState { get; }; UInt64 TaskbarProgress { get; }; Boolean ReadOnly { get; }; + String Icon { get; }; + Windows.Foundation.IReference TabColor { get; }; + Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 45950e2c776..8d159506c15 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1075,6 +1075,29 @@ TermControl Pane::GetLastFocusedTerminalControl() return GetTerminalControl(); } +IPaneContent Pane::GetLastFocusedContent() +{ + if (!_IsLeaf()) + { + if (_lastActive) + { + auto pane = shared_from_this(); + while (const auto p = pane->_parentChildPath.lock()) + { + if (p->_IsLeaf()) + { + return p->_content; + } + pane = p; + } + // We didn't find our child somehow, they might have closed under us. + } + return _firstChild->GetLastFocusedContent(); + } + + return _content; +} + // Method Description: // - Gets the TermControl of this pane. If this Pane is not a leaf this will // return the nullptr; @@ -1215,7 +1238,10 @@ void Pane::UpdateVisuals() void Pane::_Focus() { GotFocus.raise(shared_from_this(), FocusState::Programmatic); - _content.Focus(FocusState::Programmatic); + if (const auto& lastContent{ GetLastFocusedContent() }) + { + lastContent.Focus(FocusState::Programmatic); + } } // Method Description: @@ -1254,20 +1280,21 @@ void Pane::_FocusFirstChild() } } -// Method Description: -// - Updates the settings of this pane, presuming that it is a leaf. -// Arguments: -// - settings: The new TerminalSettings to apply to any matching controls -// - profile: The profile from which these settings originated. -// Return Value: -// - -void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile) +void Pane::UpdateSettings(const CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache) { - assert(_IsLeaf()); - - if (const auto& terminalPane{ _getTerminalContent() }) + if (_content) { - return terminalPane.UpdateSettings(settings, profile); + // We need to do a bit more work here for terminal + // panes. They need to know about the profile that was used for + // them, and about the focused/unfocused settings. + if (const auto& terminalPaneContent{ _content.try_as() }) + { + terminalPaneContent.UpdateTerminalSettings(cache); + } + else + { + _content.UpdateSettings(settings); + } } } @@ -1893,7 +1920,7 @@ void Pane::_SetupEntranceAnimation() auto child = isFirstChild ? _firstChild : _secondChild; auto childGrid = child->_root; // If we are splitting a parent pane this may be null - auto control = child->_content.GetRoot(); + auto control = child->_content ? child->_content.GetRoot() : nullptr; // Build up our animation: // * it'll take as long as our duration (200ms) // * it'll change the value of our property from 0 to secondSize diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 0f4a5c907dc..f93798b91c2 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -73,6 +73,7 @@ class Pane : public std::enable_shared_from_this std::shared_ptr GetActivePane(); winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl(); + winrt::TerminalApp::IPaneContent GetLastFocusedContent(); winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl() const; winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile(); bool IsConnectionClosed() const; @@ -107,8 +108,7 @@ class Pane : public std::enable_shared_from_this BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::BuildStartupKind kind); winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const; - void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, - const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache); bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction); std::shared_ptr NavigateDirection(const std::shared_ptr sourcePane, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction, diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index 0ed99203391..0f39daa1bee 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -26,6 +26,11 @@ namespace winrt::TerminalApp::implementation _root.Children().Append(_box); } + void ScratchpadContent::UpdateSettings(const CascadiaSettings& /*settings*/) + { + // Nothing to do. + } + winrt::Windows::UI::Xaml::FrameworkElement ScratchpadContent::GetRoot() { return _root; @@ -47,4 +52,15 @@ namespace winrt::TerminalApp::implementation { return nullptr; } + + winrt::hstring ScratchpadContent::Icon() const + { + static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote + return winrt::hstring{ glyph }; + } + + winrt::Windows::UI::Xaml::Media::Brush ScratchpadContent::BackgroundBrush() + { + return _root.Background(); + } } diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index a2b76e47934..cd01414e677 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -13,6 +13,8 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + winrt::Windows::Foundation::Size MinimumSize(); void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); @@ -23,6 +25,9 @@ namespace winrt::TerminalApp::implementation uint64_t TaskbarState() { return 0; } uint64_t TaskbarProgress() { return 0; } bool ReadOnly() { return false; } + winrt::hstring Icon() const; + Windows::Foundation::IReference TabColor() const noexcept { return nullptr; } + winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush(); til::typed_event<> ConnectionStateChanged; til::typed_event CloseRequested; diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp new file mode 100644 index 00000000000..3e89c38296b --- /dev/null +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SettingsPaneContent.h" +#include "Utils.h" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +#define ASSERT_UI_THREAD() assert(_sui.Dispatcher().HasThreadAccess()) + +namespace winrt::TerminalApp::implementation +{ + SettingsPaneContent::SettingsPaneContent(CascadiaSettings settings) + { + _sui = winrt::Microsoft::Terminal::Settings::Editor::MainPage{ settings }; + + // Stash away the current requested theme of the app. We'll need that in + // _BackgroundBrush() to do a theme-aware resource lookup + _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme(); + } + + void SettingsPaneContent::UpdateSettings(const CascadiaSettings& settings) + { + ASSERT_UI_THREAD(); + _sui.UpdateSettings(settings); + + _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme(); + } + + winrt::Windows::UI::Xaml::FrameworkElement SettingsPaneContent::GetRoot() + { + return _sui; + } + winrt::Windows::Foundation::Size SettingsPaneContent::MinimumSize() + { + return { 1, 1 }; + } + void SettingsPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason) + { + if (reason != FocusState::Unfocused) + { + _sui.as().Focus(reason); + } + } + void SettingsPaneContent::Close() + { + CloseRequested.raise(*this, nullptr); + } + + NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const BuildStartupKind /*kind*/) const + { + // For now, we're doing a terrible thing in TerminalTab itself to + // generate an OpenSettings action manually, without asking for the pane + // structure. + return nullptr; + } + + winrt::hstring SettingsPaneContent::Icon() const + { + // This is the Setting icon (looks like a gear) + static constexpr std::wstring_view glyph{ L"\xE713" }; + return winrt::hstring{ glyph }; + } + + Windows::Foundation::IReference SettingsPaneContent::TabColor() const noexcept + { + return nullptr; + } + + winrt::Windows::UI::Xaml::Media::Brush SettingsPaneContent::BackgroundBrush() + { + // Look up the color we should use for the settings tab item from our + // resources. This should only be used for when "terminalBackground" is + // requested. + static const auto key = winrt::box_value(L"SettingsUiTabBrush"); + // You can't just do a Application::Current().Resources().TryLookup + // lookup, cause the app theme never changes! Do the hacky version + // instead. + return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as(); + } +} diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h new file mode 100644 index 00000000000..ee3322bf06b --- /dev/null +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "winrt/TerminalApp.h" +#include + +namespace winrt::TerminalApp::implementation +{ + class SettingsPaneContent : public winrt::implements + { + public: + SettingsPaneContent(winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings settings); + + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + + winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); + winrt::Microsoft::Terminal::Settings::Editor::MainPage SettingsUI() { return _sui; } + + winrt::Windows::Foundation::Size MinimumSize(); + void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); + void Close(); + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const BuildStartupKind kind) const; + + winrt::hstring Title() { return RS_(L"SettingsTab"); } + uint64_t TaskbarState() { return 0; } + uint64_t TaskbarProgress() { return 0; } + bool ReadOnly() { return false; } + winrt::hstring Icon() const; + Windows::Foundation::IReference TabColor() const noexcept; + winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush(); + + til::typed_event<> ConnectionStateChanged; + til::typed_event CloseRequested; + til::typed_event BellRequested; + til::typed_event TitleChanged; + til::typed_event TabColorChanged; + til::typed_event TaskbarProgressChanged; + til::typed_event ReadOnlyChanged; + til::typed_event FocusRequested; + + private: + winrt::Microsoft::Terminal::Settings::Editor::MainPage _sui{ nullptr }; + winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; + }; +} diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp deleted file mode 100644 index 483f4f30475..00000000000 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include -#include "SettingsTab.h" -#include "SettingsTab.g.cpp" -#include "Utils.h" - -using namespace winrt; -using namespace winrt::Windows::UI::Xaml; -using namespace winrt::Windows::UI::Core; -using namespace winrt::Microsoft::Terminal::Control; -using namespace winrt::Microsoft::Terminal::Settings::Model; -using namespace winrt::Microsoft::Terminal::Settings::Editor; -using namespace winrt::Windows::System; - -namespace winrt -{ - namespace MUX = Microsoft::UI::Xaml; - namespace WUX = Windows::UI::Xaml; -} - -#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess()) - -namespace winrt::TerminalApp::implementation -{ - SettingsTab::SettingsTab(MainPage settingsUI, - winrt::Windows::UI::Xaml::ElementTheme requestedTheme) - { - Content(settingsUI); - _requestedTheme = requestedTheme; - - _MakeTabViewItem(); - _CreateContextMenu(); - _CreateIcon(); - } - - void SettingsTab::UpdateSettings(CascadiaSettings settings) - { - ASSERT_UI_THREAD(); - - auto settingsUI{ Content().as() }; - settingsUI.UpdateSettings(settings); - - // Stash away the current requested theme of the app. We'll need that in - // _BackgroundBrush() to do a theme-aware resource lookup - _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme(); - } - - // Method Description: - // - Creates a list of actions that can be run to recreate the state of this tab - // Arguments: - // - asContent: unused. There's nothing different we need to do when - // serializing the settings tab for moving to another window. If we ever - // really want to support opening the SUI to a specific page, we can - // re-evaluate including that arg in this action then. - // Return Value: - // - The list of actions. - std::vector SettingsTab::BuildStartupActions(BuildStartupKind) const - { - ASSERT_UI_THREAD(); - - ActionAndArgs action; - action.Action(ShortcutAction::OpenSettings); - OpenSettingsArgs args{ SettingsTarget::SettingsUI }; - action.Args(args); - - return std::vector{ std::move(action) }; - } - - // Method Description: - // - Focus the settings UI - // Arguments: - // - focusState: The FocusState mode by which focus is to be obtained. - // Return Value: - // - - void SettingsTab::Focus(WUX::FocusState focusState) - { - ASSERT_UI_THREAD(); - - _focusState = focusState; - - if (_focusState != FocusState::Unfocused) - { - Content().as().Focus(focusState); - } - } - - // Method Description: - // - Initializes a TabViewItem for this Tab instance. - // Arguments: - // - - // Return Value: - // - - void SettingsTab::_MakeTabViewItem() - { - TabBase::_MakeTabViewItem(); - - Title(RS_(L"SettingsTab")); - TabViewItem().Header(winrt::box_value(Title())); - } - - // Method Description: - // - Set the icon on the TabViewItem for this tab. - // Arguments: - // - - // Return Value: - // - - void SettingsTab::_CreateIcon() - { - // This is the Setting icon (looks like a gear) - static constexpr std::wstring_view glyph{ L"\xE713" }; - - // The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX... - Icon(winrt::hstring{ glyph }); - TabViewItem().IconSource(Microsoft::Terminal::UI::IconPathConverter::IconSourceMUX(glyph, false)); - } - - winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush() - { - // Look up the color we should use for the settings tab item from our - // resources. This should only be used for when "terminalBackground" is - // requested. - static const auto key = winrt::box_value(L"SettingsUiTabBrush"); - // You can't just do a Application::Current().Resources().TryLookup - // lookup, cause the app theme never changes! Do the hacky version - // instead. - return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as(); - } -} diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h deleted file mode 100644 index e5d7df92faf..00000000000 --- a/src/cascadia/TerminalApp/SettingsTab.h +++ /dev/null @@ -1,43 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- SettingsTab.h - -Abstract: -- The SettingsTab is a tab whose content is a Settings UI control. They can - coexist in a TabView with all other types of tabs, like the TerminalTab. - There should only be at most one SettingsTab open at any given time. - -Author(s): -- Leon Liang - October 2020 - ---*/ - -#pragma once -#include "TabBase.h" -#include "SettingsTab.g.h" - -namespace winrt::TerminalApp::implementation -{ - struct SettingsTab : SettingsTabT - { - public: - SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI, - winrt::Windows::UI::Xaml::ElementTheme requestedTheme); - - void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings); - void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override; - - std::vector BuildStartupActions(BuildStartupKind kind) const override; - - private: - winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; - - void _MakeTabViewItem() override; - void _CreateIcon(); - - virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override; - }; -} diff --git a/src/cascadia/TerminalApp/SettingsTab.idl b/src/cascadia/TerminalApp/SettingsTab.idl deleted file mode 100644 index deb45d4f155..00000000000 --- a/src/cascadia/TerminalApp/SettingsTab.idl +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "TabBase.idl"; - -namespace TerminalApp -{ - [default_interface] runtimeclass SettingsTab : TabBase - { - void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); - } -} diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 8d99ac600f5..3f833bb8ba9 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -19,7 +19,6 @@ #include "TabRowControl.h" #include "ColorHelper.h" #include "DebugTapConnection.h" -#include "SettingsTab.h" #include "..\TerminalSettingsModel\FileUtils.h" #include @@ -171,17 +170,8 @@ namespace winrt::TerminalApp::implementation auto tabViewItem = newTabImpl->TabViewItem(); _tabView.TabItems().InsertAt(insertPosition, tabViewItem); - // Set this tab's icon to the icon from the user's profile - if (const auto profile{ newTabImpl->GetFocusedProfile() }) - { - const auto& icon = profile.EvaluatedIcon(); - if (!icon.empty()) - { - const auto theme = _settings.GlobalSettings().CurrentTheme(); - const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default; - newTabImpl->UpdateIcon(icon, iconStyle); - } - } + // Set this tab's icon to the icon from the content + _UpdateTabIcon(*newTabImpl); tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick }); @@ -226,13 +216,15 @@ namespace winrt::TerminalApp::implementation // Arguments: // - pane: The pane to use as the root. // - insertPosition: Optional parameter to indicate the position of tab. - void TerminalPage::_CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition) + TerminalApp::TerminalTab TerminalPage::_CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition) { if (pane) { auto newTabImpl = winrt::make_self(pane); _InitializeTab(newTabImpl, insertPosition); + return *newTabImpl; } + return nullptr; } // Method Description: @@ -242,11 +234,13 @@ namespace winrt::TerminalApp::implementation // - tab: the Tab to update the title for. void TerminalPage::_UpdateTabIcon(TerminalTab& tab) { - if (const auto profile = tab.GetFocusedProfile()) + if (const auto content{ tab.GetActiveContent() }) { + const auto& icon{ content.Icon() }; const auto theme = _settings.GlobalSettings().CurrentTheme(); const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default; - tab.UpdateIcon(profile.EvaluatedIcon(), iconStyle); + + tab.UpdateIcon(icon, iconStyle); } } @@ -800,14 +794,6 @@ namespace winrt::TerminalApp::implementation } } } - else if (auto index{ _GetFocusedTabIndex() }) - { - const auto tab{ _tabs.GetAt(*index) }; - if (tab.try_as()) - { - _HandleCloseTabRequested(tab); - } - } } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 827e9dcf168..d0629da8036 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -88,9 +88,6 @@ PaletteItemTemplateSelector.idl Code - - SettingsTab.idl - TabBase.idl @@ -164,7 +161,13 @@ TerminalPaneContent.idl + + TerminalPaneContent.idl + + + TerminalSettingsCache.idl + SuggestionsControl.xaml @@ -185,9 +188,6 @@ PaletteItemTemplateSelector.idl Code - - SettingsTab.idl - TabBase.idl @@ -272,10 +272,16 @@ TerminalPaneContent.idl - ScratchpadContent.idl + TerminalPaneContent.idl + + + TerminalPaneContent.idl + + TerminalSettingsCache.idl + SuggestionsControl.xaml @@ -295,7 +301,6 @@ Designer - @@ -348,6 +353,7 @@ + diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters index e18881ba012..0f6468fd6b9 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters @@ -24,9 +24,6 @@ tab - - tab - commandPalette @@ -64,9 +61,6 @@ tab - - tab - commandPalette @@ -98,6 +92,7 @@ settings + tab diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 591fc6906a0..e722a744112 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -20,7 +20,7 @@ #include "App.h" #include "ColorHelper.h" #include "DebugTapConnection.h" -#include "SettingsTab.h" +#include "SettingsPaneContent.h" #include "TabRowControl.h" #include "Utils.h" @@ -2336,6 +2336,12 @@ namespace winrt::TerminalApp::implementation } } + // For now, prevent splitting the _settingsTab. We can always revisit this later. + if (*activeTab == _settingsTab) + { + return; + } + // If the caller is calling us with the return value of _MakePane // directly, it's possible that nullptr was returned, if the connections // was supposed to be launched in an elevated window. In that case, do @@ -3271,50 +3277,18 @@ namespace winrt::TerminalApp::implementation // Refresh UI elements - // Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however, - // when we stabilize its guid this will become fully safe. - std::unordered_map> profileGuidSettingsMap; - const auto profileDefaults{ _settings.ProfileDefaults() }; - const auto allProfiles{ _settings.AllProfiles() }; - - profileGuidSettingsMap.reserve(allProfiles.Size() + 1); - - // Include the Defaults profile for consideration - profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr }); - for (const auto& newProfile : allProfiles) - { - // Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many - // panes may not be using all of their profiles at the same time. Lazy evaluation is king! - profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr }); - } + // Recreate the TerminalSettings cache here. We'll use that as we're + // updating terminal panes, so that we don't have to build a _new_ + // TerminalSettings for every profile we update - we can just look them + // up the previous ones we built. + _terminalSettingsCache = TerminalApp::TerminalSettingsCache{ _settings, *_bindings }; for (const auto& tab : _tabs) { if (auto terminalTab{ _GetTerminalTabImpl(tab) }) { - terminalTab->UpdateSettings(); - - // Manually enumerate the panes in each tab; this will let us recycle TerminalSettings - // objects but only have to iterate one time. - terminalTab->GetRootPane()->WalkTree([&](auto&& pane) { - if (const auto profile{ pane->GetProfile() }) - { - const auto found{ profileGuidSettingsMap.find(profile.Guid()) }; - // GH#2455: If there are any panes with controls that had been - // initialized with a Profile that no longer exists in our list of - // profiles, we'll leave it unmodified. The profile doesn't exist - // anymore, so we can't possibly update its settings. - if (found != profileGuidSettingsMap.cend()) - { - auto& pair{ found->second }; - if (!pair.second) - { - pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings); - } - pane->UpdateSettings(pair.second, pair.first); - } - } - }); + // Let the tab know that there are new settings. It's up to each content to decide what to do with them. + terminalTab->UpdateSettings(_settings, _terminalSettingsCache); // Update the icon of the tab for the currently focused profile in that tab. // Only do this for TerminalTabs. Other types of tabs won't have multiple panes @@ -3324,10 +3298,6 @@ namespace winrt::TerminalApp::implementation // Force the TerminalTab to re-grab its currently active control's title. terminalTab->UpdateTitle(); } - else if (auto settingsTab = tab.try_as()) - { - settingsTab.UpdateSettings(_settings); - } auto tabImpl{ winrt::get_self(tab) }; tabImpl->SetActionMap(_settings.ActionMap()); @@ -3889,7 +3859,10 @@ namespace winrt::TerminalApp::implementation } } - winrt::Microsoft::Terminal::Settings::Editor::MainPage sui{ _settings }; + // Create the SUI pane content + auto settingsContent{ winrt::make_self(_settings) }; + auto sui = settingsContent->SettingsUI(); + if (_hostingHwnd) { sui.SetHostingWindow(reinterpret_cast(*_hostingHwnd)); @@ -3905,54 +3878,9 @@ namespace winrt::TerminalApp::implementation } }); - auto newTabImpl = winrt::make_self(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme()); - - // Add the new tab to the list of our tabs. - _tabs.Append(*newTabImpl); - _mruTabs.Append(*newTabImpl); - - newTabImpl->SetDispatch(*_actionDispatch); - newTabImpl->SetActionMap(_settings.ActionMap()); - - // Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command. - _UpdateTabIndices(); - - // Don't capture a strong ref to the tab. If the tab is removed as this - // is called, we don't really care anymore about handling the event. - auto weakTab = make_weak(newTabImpl); - - auto tabViewItem = newTabImpl->TabViewItem(); - _tabView.TabItems().Append(tabViewItem); - - tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick }); - - // When the tab requests close, try to close it (prompt for approval, if required) - newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { - auto page{ weakThis.get() }; - auto tab{ weakTab.get() }; - - if (page && tab) - { - page->_HandleCloseTabRequested(*tab); - } - }); - - // When the tab is closed, remove it from our list of tabs. - newTabImpl->Closed([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { - const auto page = weakThis.get(); - const auto tab = weakTab.get(); - - if (page && tab) - { - page->_RemoveTab(*tab); - } - }); - - _settingsTab = *newTabImpl; - - // This kicks off TabView::SelectionChanged, in response to which - // we'll attach the terminal's Xaml control to the Xaml root. - _tabView.SelectedItem(tabViewItem); + // Create the tab + auto resultPane = std::make_shared(*settingsContent); + _settingsTab = _CreateNewTabFromPane(resultPane); } else { @@ -4571,13 +4499,15 @@ namespace winrt::TerminalApp::implementation til::color bgColor = backgroundSolidBrush.Color(); Media::Brush terminalBrush{ nullptr }; - if (const auto& control{ _GetActiveControl() }) + if (const auto tab{ _GetFocusedTabImpl() }) { - terminalBrush = control.BackgroundBrush(); - } - else if (const auto& settingsTab{ _GetFocusedTab().try_as() }) - { - terminalBrush = settingsTab.Content().try_as().BackgroundBrush(); + if (const auto& pane{ tab->GetActivePane() }) + { + if (const auto& lastContent{ pane->GetLastFocusedContent() }) + { + terminalBrush = lastContent.BackgroundBrush(); + } + } } if (_settings.GlobalSettings().UseAcrylicInTabRow()) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 227f28edbb5..eece631419b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -213,7 +213,7 @@ namespace winrt::TerminalApp::implementation void _UpdateTabIndices(); - TerminalApp::SettingsTab _settingsTab{ nullptr }; + TerminalApp::TerminalTab _settingsTab{ nullptr }; bool _isInFocusMode{ false }; bool _isFullscreen{ false }; @@ -260,6 +260,8 @@ namespace winrt::TerminalApp::implementation TerminalApp::ContentManager _manager{ nullptr }; + TerminalApp::TerminalSettingsCache _terminalSettingsCache{ nullptr }; + struct StashedDragData { winrt::com_ptr draggedTab{ nullptr }; @@ -291,7 +293,7 @@ namespace winrt::TerminalApp::implementation void _OpenNewTabDropdown(); HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs); - void _CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition = -1); + TerminalApp::TerminalTab _CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition = -1); std::wstring _evaluatePathForCwd(std::wstring_view path); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 49bbe3ff355..96a84c4560f 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -80,6 +80,16 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } + winrt::hstring TerminalPaneContent::Icon() const + { + return _profile.EvaluatedIcon(); + } + + Windows::Foundation::IReference TerminalPaneContent::TabColor() const noexcept + { + return _control.TabColor(); + } + NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const BuildStartupKind kind) const { NewTerminalArgs args{}; @@ -328,11 +338,20 @@ namespace winrt::TerminalApp::implementation RestartTerminalRequested.raise(*this, nullptr); } - void TerminalPaneContent::UpdateSettings(const TerminalSettingsCreateResult& settings, - const Profile& profile) + void TerminalPaneContent::UpdateSettings(const CascadiaSettings& /*settings*/) + { + // Do nothing. We'll later be updated manually by + // UpdateTerminalSettings, which we need for profile and + // focused/unfocused settings. + assert(false); // If you hit this, you done goofed. + } + + void TerminalPaneContent::UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache) { - _profile = profile; - _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); + if (const auto& settings{ cache.TryLookup(_profile) }) + { + _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); + } } // Method Description: @@ -344,6 +363,11 @@ namespace winrt::TerminalApp::implementation _isDefTermSession = true; } + winrt::Windows::UI::Xaml::Media::Brush TerminalPaneContent::BackgroundBrush() + { + return _control.BackgroundBrush(); + } + float TerminalPaneContent::SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap) { return _control.SnapDimensionToGrid(direction == PaneSnapDirection::Width, sizeToSnap); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 11ad0425379..57a9a630c0f 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -29,8 +29,8 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const; - void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, - const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + void UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache); void MarkAsDefterm(); @@ -43,6 +43,9 @@ namespace winrt::TerminalApp::implementation uint64_t TaskbarState() { return _control.TaskbarState(); } uint64_t TaskbarProgress() { return _control.TaskbarProgress(); } bool ReadOnly() { return _control.ReadOnly(); } + winrt::hstring Icon() const; + Windows::Foundation::IReference TabColor() const noexcept; + winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush(); float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap); Windows::Foundation::Size GridUnitSize(); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 7e04c8b836c..60c540273c6 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -2,6 +2,7 @@ // Licensed under the MIT license. import "IPaneContent.idl"; +import "TerminalSettingsCache.idl"; namespace TerminalApp { @@ -9,8 +10,7 @@ namespace TerminalApp { Microsoft.Terminal.Control.TermControl GetTermControl(); - void UpdateSettings(const Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings, - const Microsoft.Terminal.Settings.Model.Profile profile); + void UpdateTerminalSettings(TerminalSettingsCache cache); void MarkAsDefterm(); diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.cpp b/src/cascadia/TerminalApp/TerminalSettingsCache.cpp new file mode 100644 index 00000000000..6662afa9384 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.cpp @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TerminalSettingsCache.h" +#include "TerminalSettingsCache.g.cpp" + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + namespace WUX = Windows::UI::Xaml; + namespace MTSM = Microsoft::Terminal::Settings::Model; +} + +namespace winrt::TerminalApp::implementation +{ + TerminalSettingsCache::TerminalSettingsCache(const MTSM::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings) : + _settings{ settings }, + _bindings{ bindings } + { + // Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however, + // when we stabilize its guid this will become fully safe. + const auto profileDefaults{ _settings.ProfileDefaults() }; + const auto allProfiles{ _settings.AllProfiles() }; + + profileGuidSettingsMap.reserve(allProfiles.Size() + 1); + + // Include the Defaults profile for consideration + profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr }); + for (const auto& newProfile : allProfiles) + { + // Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many + // panes may not be using all of their profiles at the same time. Lazy evaluation is king! + profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr }); + } + } + + MTSM::TerminalSettingsCreateResult TerminalSettingsCache::TryLookup(const MTSM::Profile& profile) + { + const auto found{ profileGuidSettingsMap.find(profile.Guid()) }; + // GH#2455: If there are any panes with controls that had been + // initialized with a Profile that no longer exists in our list of + // profiles, we'll leave it unmodified. The profile doesn't exist + // anymore, so we can't possibly update its settings. + if (found != profileGuidSettingsMap.cend()) + { + auto& pair{ found->second }; + if (!pair.second) + { + pair.second = MTSM::TerminalSettings::CreateWithProfile(_settings, pair.first, _bindings); + } + return pair.second; + } + + return nullptr; + } +} diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.h b/src/cascadia/TerminalApp/TerminalSettingsCache.h new file mode 100644 index 00000000000..054ccb4b51a --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.h @@ -0,0 +1,37 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Class Name: +- TerminalSettingsCache.h + +Abstract: +- This is a helper class used as we update the settings for panes. This class + contains a single map of guid -> TerminalSettings, so that as we update all + the panes during a settings reload, we only need to create a TerminalSettings + once per profile. +--*/ +#pragma once + +#include "TerminalSettingsCache.g.h" +#include + +namespace winrt::TerminalApp::implementation +{ + class TerminalSettingsCache : public TerminalSettingsCacheT + { + public: + TerminalSettingsCache(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings); + Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult TryLookup(const Microsoft::Terminal::Settings::Model::Profile& profile); + + private: + Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; + TerminalApp::AppKeyBindings _bindings{ nullptr }; + std::unordered_map> profileGuidSettingsMap; + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(TerminalSettingsCache); +} diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.idl b/src/cascadia/TerminalApp/TerminalSettingsCache.idl new file mode 100644 index 00000000000..933e035a984 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.idl @@ -0,0 +1,12 @@ +import "AppKeyBindings.idl"; + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +namespace TerminalApp +{ + [default_interface] runtimeclass TerminalSettingsCache + { + TerminalSettingsCache(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, AppKeyBindings bindings); + Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult TryLookup(Microsoft.Terminal.Settings.Model.Profile profile); + } +} diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 247601e21d1..9c99b44afaf 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -5,6 +5,7 @@ #include #include "ColorPickupFlyout.h" #include "TerminalTab.h" +#include "SettingsPaneContent.h" #include "TerminalTab.g.cpp" #include "Utils.h" #include "ColorHelper.h" @@ -266,12 +267,18 @@ namespace winrt::TerminalApp::implementation // of the settings that apply to all tabs. // Return Value: // - - void TerminalTab::UpdateSettings() + void TerminalTab::UpdateSettings(const CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache) { ASSERT_UI_THREAD(); // The tabWidthMode may have changed, update the header control accordingly _UpdateHeaderControlMaxWidth(); + + // Update the settings on all our panes. + _rootPane->WalkTree([&](auto pane) { + pane->UpdateSettings(settings, cache); + return false; + }); } // Method Description: @@ -280,7 +287,7 @@ namespace winrt::TerminalApp::implementation // - iconPath: The new path string to use as the IconPath for our TabViewItem // Return Value: // - - void TerminalTab::UpdateIcon(const winrt::hstring iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle) + void TerminalTab::UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle) { ASSERT_UI_THREAD(); @@ -383,7 +390,7 @@ namespace winrt::TerminalApp::implementation return RS_(L"MultiplePanes"); } const auto activeContent = GetActiveContent(); - return activeContent ? activeContent.Title() : L""; + return activeContent ? activeContent.Title() : winrt::hstring{ L"" }; } // Method Description: @@ -440,7 +447,30 @@ namespace winrt::TerminalApp::implementation // 1 for the child after the first split. auto state = _rootPane->BuildStartupActions(0, 1, kind); + // HORRIBLE + // + // Workaround till we know how we actually want to handle state + // restoring other kinda of panes. If this is a settings tab, just + // restore it as a settings tab. Don't bother recreating terminal args + // for every pane. + // + // In the future, we'll want to definitely get rid of + // Pane::GetTerminalArgsForPane, and somehow instead find a better way + // of re-creating the pane state. Probably through a combo of ResizePane + // actions and SetPaneOrientation actions. + if (const auto& settings{ _rootPane->GetContent().try_as() }) + { + ActionAndArgs action; + action.Action(ShortcutAction::OpenSettings); + OpenSettingsArgs args{ SettingsTarget::SettingsUI }; + action.Args(args); + + state.args = std::vector{ std::move(action) }; + } + else { + state = _rootPane->BuildStartupActions(0, 1, kind); + ActionAndArgs newTabAction{}; newTabAction.Action(ShortcutAction::NewTab); NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(kind) }; @@ -1566,12 +1596,12 @@ namespace winrt::TerminalApp::implementation { ASSERT_UI_THREAD(); - std::optional controlTabColor; - if (const auto& control = GetActiveTerminalControl()) + std::optional contentTabColor; + if (const auto& content{ GetActiveContent() }) { - if (const auto color = control.TabColor()) + if (const auto color = content.TabColor()) { - controlTabColor = color.Value(); + contentTabColor = color.Value(); } } @@ -1581,7 +1611,7 @@ namespace winrt::TerminalApp::implementation // Color | | Set by // -------------------- | -- | -- // Runtime Color | _optional_ | Color Picker / `setTabColor` action - // Control Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT + // Content Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT (whatever the tab's content wants) // Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme (handled in _RecalculateAndApplyTabColor) // Tab Default Color | **default** | TabView in XAML // @@ -1590,7 +1620,7 @@ namespace winrt::TerminalApp::implementation // tabview color" (and clear out any colors we've set). return til::coalesce(_runtimeTabColor, - controlTabColor, + contentTabColor, std::optional(std::nullopt)); } @@ -1629,7 +1659,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Media::Brush TerminalTab::_BackgroundBrush() { Media::Brush terminalBrush{ nullptr }; - if (const auto& c{ GetActiveTerminalControl() }) + if (const auto& c{ GetActiveContent() }) { terminalBrush = c.BackgroundBrush(); } diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index 3ae1a3cc2fa..785d785ff31 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -42,7 +42,7 @@ namespace winrt::TerminalApp::implementation std::shared_ptr newPane); void ToggleSplitOrientation(); - void UpdateIcon(const winrt::hstring iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle); + void UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle); void HideIcon(const bool hide); void ShowBellIndicator(const bool show); @@ -58,7 +58,7 @@ namespace winrt::TerminalApp::implementation bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool FocusPane(const uint32_t id); - void UpdateSettings(); + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache); void UpdateTitle(); void Shutdown() override; diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj index df855d79e11..483a27c9e7f 100644 --- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj @@ -36,7 +36,6 @@ - From 1a6ba43dd220d4a318627006bf2a6c8b9a70c7bc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 5 Apr 2024 07:45:21 -0700 Subject: [PATCH 207/603] Get rid of UpdateTerminalSettings (#17009) As @lhecker noted in the #16172 review, `UpdateTerminalSettings` is wacky. We can just pass the cache in at the start, then reset it and reuse it in `UpdateSettings`. One fewer `try_as`! --- src/cascadia/TerminalApp/Pane.cpp | 14 +------ src/cascadia/TerminalApp/Pane.h | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 17 ++++---- .../TerminalApp/TerminalPaneContent.cpp | 12 ++---- .../TerminalApp/TerminalPaneContent.h | 3 +- .../TerminalApp/TerminalPaneContent.idl | 2 - .../TerminalApp/TerminalSettingsCache.cpp | 42 +++++++++++-------- .../TerminalApp/TerminalSettingsCache.h | 1 + .../TerminalApp/TerminalSettingsCache.idl | 1 + src/cascadia/TerminalApp/TerminalTab.cpp | 8 ++-- src/cascadia/TerminalApp/TerminalTab.h | 2 +- 11 files changed, 49 insertions(+), 55 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 8d159506c15..de3c7f76c46 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1280,21 +1280,11 @@ void Pane::_FocusFirstChild() } } -void Pane::UpdateSettings(const CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache) +void Pane::UpdateSettings(const CascadiaSettings& settings) { if (_content) { - // We need to do a bit more work here for terminal - // panes. They need to know about the profile that was used for - // them, and about the focused/unfocused settings. - if (const auto& terminalPaneContent{ _content.try_as() }) - { - terminalPaneContent.UpdateTerminalSettings(cache); - } - else - { - _content.UpdateSettings(settings); - } + _content.UpdateSettings(settings); } } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index f93798b91c2..37ed79b4739 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -108,7 +108,7 @@ class Pane : public std::enable_shared_from_this BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::BuildStartupKind kind); winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const; - void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache); + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction); std::shared_ptr NavigateDirection(const std::shared_ptr sourcePane, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction, diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e722a744112..ec7c066d5ff 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -65,7 +65,6 @@ namespace winrt::TerminalApp::implementation _WindowProperties{ std::move(properties) } { InitializeComponent(); - _WindowProperties.PropertyChanged({ get_weak(), &TerminalPage::_windowPropertyChanged }); } @@ -110,7 +109,11 @@ namespace winrt::TerminalApp::implementation void TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) { assert(Dispatcher().HasThreadAccess()); - + if (_settings == nullptr) + { + // Create this only on the first time we load the settings. + _terminalSettingsCache = TerminalApp::TerminalSettingsCache{ settings, *_bindings }; + } _settings = settings; // Make sure to call SetCommands before _RefreshUIForSettingsReload. @@ -3104,7 +3107,7 @@ namespace winrt::TerminalApp::implementation // serialize the actual profile's GUID along with the content guid. const auto& profile = _settings.GetProfileForArgs(newTerminalArgs); const auto control = _AttachControlToContent(newTerminalArgs.ContentId()); - auto paneContent{ winrt::make(profile, control) }; + auto paneContent{ winrt::make(profile, _terminalSettingsCache, control) }; return std::make_shared(paneContent); } @@ -3161,14 +3164,14 @@ namespace winrt::TerminalApp::implementation const auto control = _CreateNewControlAndContent(controlSettings, connection); - auto paneContent{ winrt::make(profile, control) }; + auto paneContent{ winrt::make(profile, _terminalSettingsCache, control) }; auto resultPane = std::make_shared(paneContent); if (debugConnection) // this will only be set if global debugging is on and tap is active { auto newControl = _CreateNewControlAndContent(controlSettings, debugConnection); // Split (auto) with the debug tap. - auto debugContent{ winrt::make(profile, newControl) }; + auto debugContent{ winrt::make(profile, _terminalSettingsCache, newControl) }; auto debugPane = std::make_shared(debugContent); // Since we're doing this split directly on the pane (instead of going through TerminalTab, @@ -3281,14 +3284,14 @@ namespace winrt::TerminalApp::implementation // updating terminal panes, so that we don't have to build a _new_ // TerminalSettings for every profile we update - we can just look them // up the previous ones we built. - _terminalSettingsCache = TerminalApp::TerminalSettingsCache{ _settings, *_bindings }; + _terminalSettingsCache.Reset(_settings, *_bindings); for (const auto& tab : _tabs) { if (auto terminalTab{ _GetTerminalTabImpl(tab) }) { // Let the tab know that there are new settings. It's up to each content to decide what to do with them. - terminalTab->UpdateSettings(_settings, _terminalSettingsCache); + terminalTab->UpdateSettings(_settings); // Update the icon of the tab for the currently focused profile in that tab. // Only do this for TerminalTabs. Other types of tabs won't have multiple panes diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 96a84c4560f..406ab283510 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -20,8 +20,10 @@ using namespace winrt::Microsoft::Terminal::TerminalConnection; namespace winrt::TerminalApp::implementation { TerminalPaneContent::TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, + const TerminalApp::TerminalSettingsCache& cache, const winrt::Microsoft::Terminal::Control::TermControl& control) : _control{ control }, + _cache{ cache }, _profile{ profile } { _setupControlEvents(); @@ -340,15 +342,7 @@ namespace winrt::TerminalApp::implementation void TerminalPaneContent::UpdateSettings(const CascadiaSettings& /*settings*/) { - // Do nothing. We'll later be updated manually by - // UpdateTerminalSettings, which we need for profile and - // focused/unfocused settings. - assert(false); // If you hit this, you done goofed. - } - - void TerminalPaneContent::UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache) - { - if (const auto& settings{ cache.TryLookup(_profile) }) + if (const auto& settings{ _cache.TryLookup(_profile) }) { _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 57a9a630c0f..e308a5cfb00 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -19,6 +19,7 @@ namespace winrt::TerminalApp::implementation struct TerminalPaneContent : TerminalPaneContentT { TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, + const TerminalApp::TerminalSettingsCache& cache, const winrt::Microsoft::Terminal::Control::TermControl& control); winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); @@ -30,7 +31,6 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - void UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache); void MarkAsDefterm(); @@ -64,6 +64,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr }; winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected }; winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr }; + TerminalApp::TerminalSettingsCache _cache{ nullptr }; bool _isDefTermSession{ false }; winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 60c540273c6..0e4738fff42 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -10,8 +10,6 @@ namespace TerminalApp { Microsoft.Terminal.Control.TermControl GetTermControl(); - void UpdateTerminalSettings(TerminalSettingsCache cache); - void MarkAsDefterm(); Microsoft.Terminal.Settings.Model.Profile GetProfile(); diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.cpp b/src/cascadia/TerminalApp/TerminalSettingsCache.cpp index 6662afa9384..c6b66169f39 100644 --- a/src/cascadia/TerminalApp/TerminalSettingsCache.cpp +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.cpp @@ -14,25 +14,9 @@ namespace winrt namespace winrt::TerminalApp::implementation { - TerminalSettingsCache::TerminalSettingsCache(const MTSM::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings) : - _settings{ settings }, - _bindings{ bindings } + TerminalSettingsCache::TerminalSettingsCache(const MTSM::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings) { - // Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however, - // when we stabilize its guid this will become fully safe. - const auto profileDefaults{ _settings.ProfileDefaults() }; - const auto allProfiles{ _settings.AllProfiles() }; - - profileGuidSettingsMap.reserve(allProfiles.Size() + 1); - - // Include the Defaults profile for consideration - profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr }); - for (const auto& newProfile : allProfiles) - { - // Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many - // panes may not be using all of their profiles at the same time. Lazy evaluation is king! - profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr }); - } + Reset(settings, bindings); } MTSM::TerminalSettingsCreateResult TerminalSettingsCache::TryLookup(const MTSM::Profile& profile) @@ -54,4 +38,26 @@ namespace winrt::TerminalApp::implementation return nullptr; } + + void TerminalSettingsCache::Reset(const MTSM::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings) + { + _settings = settings; + _bindings = bindings; + + // Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however, + // when we stabilize its guid this will become fully safe. + const auto profileDefaults{ _settings.ProfileDefaults() }; + const auto allProfiles{ _settings.AllProfiles() }; + profileGuidSettingsMap.clear(); + profileGuidSettingsMap.reserve(allProfiles.Size() + 1); + + // Include the Defaults profile for consideration + profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr }); + for (const auto& newProfile : allProfiles) + { + // Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many + // panes may not be using all of their profiles at the same time. Lazy evaluation is king! + profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr }); + } + } } diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.h b/src/cascadia/TerminalApp/TerminalSettingsCache.h index 054ccb4b51a..ec1cd42c655 100644 --- a/src/cascadia/TerminalApp/TerminalSettingsCache.h +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.h @@ -23,6 +23,7 @@ namespace winrt::TerminalApp::implementation public: TerminalSettingsCache(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings); Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult TryLookup(const Microsoft::Terminal::Settings::Model::Profile& profile); + void Reset(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings); private: Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.idl b/src/cascadia/TerminalApp/TerminalSettingsCache.idl index 933e035a984..24d766c1872 100644 --- a/src/cascadia/TerminalApp/TerminalSettingsCache.idl +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.idl @@ -8,5 +8,6 @@ namespace TerminalApp { TerminalSettingsCache(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, AppKeyBindings bindings); Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult TryLookup(Microsoft.Terminal.Settings.Model.Profile profile); + void Reset(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, AppKeyBindings bindings); } } diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 9c99b44afaf..639f855e88e 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -267,7 +267,7 @@ namespace winrt::TerminalApp::implementation // of the settings that apply to all tabs. // Return Value: // - - void TerminalTab::UpdateSettings(const CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache) + void TerminalTab::UpdateSettings(const CascadiaSettings& settings) { ASSERT_UI_THREAD(); @@ -276,7 +276,7 @@ namespace winrt::TerminalApp::implementation // Update the settings on all our panes. _rootPane->WalkTree([&](auto pane) { - pane->UpdateSettings(settings, cache); + pane->UpdateSettings(settings); return false; }); } @@ -390,7 +390,7 @@ namespace winrt::TerminalApp::implementation return RS_(L"MultiplePanes"); } const auto activeContent = GetActiveContent(); - return activeContent ? activeContent.Title() : winrt::hstring{ L"" }; + return activeContent ? activeContent.Title() : winrt::hstring{}; } // Method Description: @@ -450,7 +450,7 @@ namespace winrt::TerminalApp::implementation // HORRIBLE // // Workaround till we know how we actually want to handle state - // restoring other kinda of panes. If this is a settings tab, just + // restoring other kinds of panes. If this is a settings tab, just // restore it as a settings tab. Don't bother recreating terminal args // for every pane. // diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index 785d785ff31..16d74567c78 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -58,7 +58,7 @@ namespace winrt::TerminalApp::implementation bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool FocusPane(const uint32_t id); - void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache); + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); void UpdateTitle(); void Shutdown() override; From 8d6e7a8a78345c2a7d27ceba2584567a010ea423 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 5 Apr 2024 20:54:31 +0200 Subject: [PATCH 208/603] Buffer Restore: Fix serializing >32KiB (#17022) I forgot to `buffer.clear()` after a write. Whoops. This includes 2 additional, smaller improvements that I just happened to notice: The `GenRTF` code calls `to_string` despite using `fmt`. --- src/buffer/out/textBuffer.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 75cd6fd4f01..52df2009eaa 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2375,7 +2375,7 @@ std::string TextBuffer::GenRTF(const CopyRequest& req, // \fsN: specifies font size in half-points. E.g. \fs20 results in a font // size of 10 pts. That's why, font size is multiplied by 2 here. - fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\fs{}"), std::to_string(2 * fontHeightPoints)); + fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\fs{}"), 2 * fontHeightPoints); // Set the background color for the page. But the standard way (\cbN) to do // this isn't supported in Word. However, the following control words sequence @@ -2502,8 +2502,7 @@ void TextBuffer::_AppendRTFText(std::string& contentBuilder, const std::wstring_ { // Windows uses unsigned wchar_t - RTF uses signed ones. // '?' is the fallback ascii character. - const auto codeUnitRTFStr = std::to_string(std::bit_cast(codeUnit)); - fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\u{}?"), codeUnitRTFStr); + fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\u{}?"), std::bit_cast(codeUnit)); } } } @@ -2750,10 +2749,8 @@ void TextBuffer::Serialize(const wchar_t* destination) const const auto fileSize = gsl::narrow(buffer.size() * sizeof(wchar_t)); DWORD bytesWritten = 0; THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.get(), buffer.data(), fileSize, &bytesWritten, nullptr)); - if (bytesWritten != fileSize) - { - THROW_WIN32_MSG(ERROR_WRITE_FAULT, "failed to write"); - } + THROW_WIN32_IF_MSG(ERROR_WRITE_FAULT, bytesWritten != fileSize, "failed to write"); + buffer.clear(); } if (!moreRowsRemaining) From 6a69b94fc675bbbb65ae482eace07126d164bf13 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 5 Apr 2024 20:55:04 +0200 Subject: [PATCH 209/603] Increase usage of fmt and FMT_COMPILE (#17012) This is a rather simple PR overall. It mostly touches either test or tracing code. Only a few changes affect the actual runtime. The goal of this changeset is to get rid of the `double` format tables in the OpenConsole build by using `FMT_COMPILE` everywhere. --- src/inc/LibraryIncludes.h | 2 -- src/inc/til/bitmap.h | 11 ++---- src/inc/til/color.h | 19 ++++------- src/inc/til/env.h | 3 +- src/interactivity/win32/uiaTextRange.cpp | 8 ----- src/interactivity/win32/windowproc.cpp | 4 +-- src/renderer/atlas/pch.h | 4 +++ src/types/UiaTracing.cpp | 43 +++++++----------------- src/types/utils.cpp | 11 ++---- 9 files changed, 30 insertions(+), 75 deletions(-) diff --git a/src/inc/LibraryIncludes.h b/src/inc/LibraryIncludes.h index 511e4d4d95e..4dc9f6b21e8 100644 --- a/src/inc/LibraryIncludes.h +++ b/src/inc/LibraryIncludes.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -44,7 +43,6 @@ #include #include #include -#include #include #include #include diff --git a/src/inc/til/bitmap.h b/src/inc/til/bitmap.h index 3934af878cc..686369ade95 100644 --- a/src/inc/til/bitmap.h +++ b/src/inc/til/bitmap.h @@ -466,17 +466,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" std::wstring to_string() const { - std::wstringstream wss; - wss << std::endl - << L"Bitmap of size " << _sz.to_string() << " contains the following dirty regions:" << std::endl; - wss << L"Runs:" << std::endl; - + auto str = fmt::format(FMT_COMPILE(L"Bitmap of size {} contains the following dirty regions:\nRuns:"), _sz.to_string()); for (auto& item : *this) { - wss << L"\t- " << item.to_string() << std::endl; + fmt::format_to(std::back_inserter(str), FMT_COMPILE(L"\n\t- {}"), item.to_string()); } - - return wss.str(); + return str; } private: diff --git a/src/inc/til/color.h b/src/inc/til/color.h index cfeee352288..81dd2d9b6b1 100644 --- a/src/inc/til/color.h +++ b/src/inc/til/color.h @@ -198,24 +198,17 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" std::wstring to_string() const { - std::wstringstream wss; - wss << L"Color " << ToHexString(false); - return wss.str(); + return ToHexString(false); } + std::wstring ToHexString(const bool omitAlpha = false) const { - std::wstringstream wss; - wss << L"#" << std::uppercase << std::setfill(L'0') << std::hex; - // Force the compiler to promote from byte to int. Without it, the - // stringstream will try to write the components as chars - wss << std::setw(2) << static_cast(r); - wss << std::setw(2) << static_cast(g); - wss << std::setw(2) << static_cast(b); - if (!omitAlpha) + auto str = fmt::format(FMT_COMPILE(L"#{:02X}{:02X}{:02X}{:02X}"), r, g, b, a); + if (omitAlpha) { - wss << std::setw(2) << static_cast(a); + str.resize(7); } - return wss.str(); + return str; } }; #pragma warning(pop) diff --git a/src/inc/til/env.h b/src/inc/til/env.h index 33d7fa70e40..e41ae645dd5 100644 --- a/src/inc/til/env.h +++ b/src/inc/til/env.h @@ -86,7 +86,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" inline constexpr wil::zwstring_view system_env_var_root{ LR"(SYSTEM\CurrentControlSet\Control\Session Manager\Environment)" }; inline constexpr wil::zwstring_view user_env_var_root{ LR"(Environment)" }; inline constexpr wil::zwstring_view user_volatile_env_var_root{ LR"(Volatile Environment)" }; - inline constexpr wil::zwstring_view user_volatile_session_env_var_root_pattern{ LR"(Volatile Environment\{0:d})" }; }; }; @@ -472,7 +471,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" // not processing autoexec.bat get_vars_from_registry(HKEY_CURRENT_USER, til::details::vars::reg::user_env_var_root); get_vars_from_registry(HKEY_CURRENT_USER, til::details::vars::reg::user_volatile_env_var_root); - get_vars_from_registry(HKEY_CURRENT_USER, fmt::format(til::details::vars::reg::user_volatile_session_env_var_root_pattern, NtCurrentTeb()->ProcessEnvironmentBlock->SessionId)); + get_vars_from_registry(HKEY_CURRENT_USER, fmt::format(FMT_COMPILE(LR"(Volatile Environment\{})"), NtCurrentTeb()->ProcessEnvironmentBlock->SessionId)); } std::wstring to_string() const diff --git a/src/interactivity/win32/uiaTextRange.cpp b/src/interactivity/win32/uiaTextRange.cpp index d060b3e3985..eb78d090cfc 100644 --- a/src/interactivity/win32/uiaTextRange.cpp +++ b/src/interactivity/win32/uiaTextRange.cpp @@ -60,14 +60,6 @@ IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider* *ppRetVal = nullptr; RETURN_IF_FAILED(MakeAndInitialize(ppRetVal, *this)); -#if defined(_DEBUG) && defined(UiaTextRangeBase_DEBUG_MSGS) - OutputDebugString(L"Clone\n"); - std::wstringstream ss; - ss << _id << L" cloned to " << (static_cast(*ppRetVal))->_id; - std::wstring str = ss.str(); - OutputDebugString(str.c_str()); - OutputDebugString(L"\n"); -#endif // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing /*ApiMsgClone apiMsg; diff --git a/src/interactivity/win32/windowproc.cpp b/src/interactivity/win32/windowproc.cpp index 3e2225e182f..28202eebc9f 100644 --- a/src/interactivity/win32/windowproc.cpp +++ b/src/interactivity/win32/windowproc.cpp @@ -721,9 +721,7 @@ using namespace Microsoft::Console::Types; { try { - std::wstringstream wss; - wss << std::setfill(L'0') << std::setw(8) << wParam; - auto wstr = wss.str(); + const auto wstr = fmt::format(FMT_COMPILE(L"{:08d}"), wParam); LoadKeyboardLayout(wstr.c_str(), KLF_ACTIVATE); } catch (...) diff --git a/src/renderer/atlas/pch.h b/src/renderer/atlas/pch.h index 0192f1995e9..36a2f2143c5 100644 --- a/src/renderer/atlas/pch.h +++ b/src/renderer/atlas/pch.h @@ -50,5 +50,9 @@ #include #pragma warning(pop) +// {fmt}, a C++20-compatible formatting library +#include +#include + #include #include diff --git a/src/types/UiaTracing.cpp b/src/types/UiaTracing.cpp index dae05a073b9..80bc99d1668 100644 --- a/src/types/UiaTracing.cpp +++ b/src/types/UiaTracing.cpp @@ -65,29 +65,14 @@ UiaTracing::~UiaTracing() noexcept std::wstring UiaTracing::_getValue(const ScreenInfoUiaProviderBase& siup) noexcept { - std::wstringstream stream; - stream << "_id: " << siup.GetId(); - return stream.str(); + return fmt::format(FMT_COMPILE(L"_id:{}"), siup.GetId()); } std::wstring UiaTracing::_getValue(const UiaTextRangeBase& utr) noexcept -try { const auto start = utr.GetEndpoint(TextPatternRangeEndpoint_Start); const auto end = utr.GetEndpoint(TextPatternRangeEndpoint_End); - - std::wstringstream stream; - stream << " _id: " << utr.GetId(); - stream << " _start: { " << start.x << ", " << start.y << " }"; - stream << " _end: { " << end.x << ", " << end.y << " }"; - stream << " _degenerate: " << utr.IsDegenerate(); - stream << " _wordDelimiters: " << utr._wordDelimiters; - stream << " content: " << utr._getTextValue(); - return stream.str(); -} -catch (...) -{ - return {}; + return fmt::format(FMT_COMPILE(L"_id:{} _start:{},{} _end:{},{} _degenerate:{} _wordDelimiters:{} content:{}"), utr.GetId(), start.x, start.y, end.x, end.y, utr.IsDegenerate(), utr._wordDelimiters, utr._getTextValue()); } std::wstring UiaTracing::_getValue(const TextPatternRangeEndpoint endpoint) noexcept @@ -485,7 +470,7 @@ void UiaTracing::TextProvider::get_ProviderOptions(const ScreenInfoUiaProviderBa EnsureRegistration(); if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE)) { - auto getOptions = [options]() { + static constexpr auto getOptions = [](ProviderOptions options) { switch (options) { case ProviderOptions_ServerSideProvider: @@ -499,7 +484,7 @@ void UiaTracing::TextProvider::get_ProviderOptions(const ScreenInfoUiaProviderBa g_UiaProviderTraceProvider, "ScreenInfoUiaProvider::get_ProviderOptions", TraceLoggingValue(_getValue(siup).c_str(), "base"), - TraceLoggingValue(getOptions(), "providerOptions"), + TraceLoggingValue(getOptions(options), "providerOptions"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } @@ -510,7 +495,7 @@ void UiaTracing::TextProvider::GetPatternProvider(const ScreenInfoUiaProviderBas EnsureRegistration(); if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE)) { - auto getPattern = [patternId]() { + static constexpr auto getPattern = [](PATTERNID patternId) { switch (patternId) { case UIA_TextPatternId: @@ -524,7 +509,7 @@ void UiaTracing::TextProvider::GetPatternProvider(const ScreenInfoUiaProviderBas g_UiaProviderTraceProvider, "ScreenInfoUiaProvider::get_ProviderOptions", TraceLoggingValue(_getValue(siup).c_str(), "base"), - TraceLoggingValue(getPattern(), "patternId"), + TraceLoggingValue(getPattern(patternId), "patternId"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } @@ -535,7 +520,7 @@ void UiaTracing::TextProvider::GetPropertyValue(const ScreenInfoUiaProviderBase& EnsureRegistration(); if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE)) { - auto getProperty = [propertyId]() { + static constexpr auto getProperty = [](PROPERTYID propertyId) { switch (propertyId) { case UIA_ControlTypePropertyId: @@ -565,7 +550,7 @@ void UiaTracing::TextProvider::GetPropertyValue(const ScreenInfoUiaProviderBase& g_UiaProviderTraceProvider, "ScreenInfoUiaProvider::GetPropertyValue", TraceLoggingValue(_getValue(siup).c_str(), "base"), - TraceLoggingValue(getProperty(), "propertyId"), + TraceLoggingValue(getProperty(propertyId), "propertyId"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } @@ -677,17 +662,15 @@ void UiaTracing::TextProvider::RangeFromPoint(const ScreenInfoUiaProviderBase& s EnsureRegistration(); if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE)) { - auto getPoint = [point]() { - std::wstringstream stream; - stream << "{ " << point.x << ", " << point.y << " }"; - return stream.str(); + static constexpr auto getPoint = [](const UiaPoint& point) { + return fmt::format(FMT_COMPILE(L"{},{}"), (float)point.x, (float)point.y); }; TraceLoggingWrite( g_UiaProviderTraceProvider, "ScreenInfoUiaProvider::RangeFromPoint", TraceLoggingValue(_getValue(siup).c_str(), "base"), - TraceLoggingValue(getPoint().c_str(), "uiaPoint"), + TraceLoggingValue(getPoint(point).c_str(), "uiaPoint"), TraceLoggingValue(_getValue(result).c_str(), "result (utr)"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); @@ -714,7 +697,7 @@ void UiaTracing::TextProvider::get_SupportedTextSelection(const ScreenInfoUiaPro EnsureRegistration(); if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE)) { - auto getResult = [result]() { + static constexpr auto getResult = [](SupportedTextSelection result) { switch (result) { case SupportedTextSelection_Single: @@ -728,7 +711,7 @@ void UiaTracing::TextProvider::get_SupportedTextSelection(const ScreenInfoUiaPro g_UiaProviderTraceProvider, "ScreenInfoUiaProvider::get_SupportedTextSelection", TraceLoggingValue(_getValue(siup).c_str(), "base"), - TraceLoggingValue(getResult(), "result"), + TraceLoggingValue(getResult(result), "result"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 92ce0a0ed09..53cc3d9505b 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -93,14 +93,7 @@ GUID Utils::CreateGuid() // - a string representation of the color std::string Utils::ColorToHexString(const til::color color) { - std::stringstream ss; - ss << "#" << std::uppercase << std::setfill('0') << std::hex; - // Force the compiler to promote from byte to int. Without it, the - // stringstream will try to write the components as chars - ss << std::setw(2) << static_cast(color.r); - ss << std::setw(2) << static_cast(color.g); - ss << std::setw(2) << static_cast(color.b); - return ss.str(); + return fmt::format(FMT_COMPILE("#{:02X}{:02X}{:02X}"), color.r, color.g, color.b); } // Function Description: @@ -809,7 +802,7 @@ std::tuple Utils::MangleStartingDirectoryForWSL(std: } return { - fmt::format(LR"("{}" --cd "{}" {})", executablePath.native(), mangledDirectory, arguments), + fmt::format(FMT_COMPILE(LR"("{}" --cd "{}" {})"), executablePath.native(), mangledDirectory, arguments), std::wstring{} }; } From c063d2bad68d46525a1fc693f4ff1d1c136b6f60 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 5 Apr 2024 12:24:07 -0700 Subject: [PATCH 210/603] Change default pane keybindings to duplicate, rather than use default profile (#16951) As we discussed in length over at #7657. This changes the default alt+shift+- and alt+shift+plus keybindings to split panes by duplicating the pane by default. Closes #7657 --- src/cascadia/TerminalSettingsModel/defaults.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index ae8ed369ff6..7c33e169994 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -484,9 +484,11 @@ { "command": "closeOtherPanes" }, { "command": "closePane", "keys": "ctrl+shift+w" }, { "command": { "action": "splitPane", "split": "up" } }, - { "command": { "action": "splitPane", "split": "down" }, "keys": "alt+shift+-" }, + { "command": { "action": "splitPane", "split": "down" } }, { "command": { "action": "splitPane", "split": "left" } }, - { "command": { "action": "splitPane", "split": "right" }, "keys": "alt+shift+plus" }, + { "command": { "action": "splitPane", "split": "right" } }, + { "command": { "action": "splitPane", "splitMode": "duplicate", "split": "down" }, "keys": "alt+shift+-" }, + { "command": { "action": "splitPane", "splitMode": "duplicate", "split": "right" }, "keys": "alt+shift+plus" }, { "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" }, { "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" }, { "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" }, From dc4026d184df2489276d46de02a222131cd4d333 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 5 Apr 2024 12:29:27 -0700 Subject: [PATCH 211/603] Add support for restoring non-terminal panes, and opening them with `splitPane`, `newTab` (#16914) This changes `NewTabArgs`, `SplitPaneArgs`, and `NewWindowArgs` to accept a `INewContentArgs`, rather than just a `NewTerminalArgs`. This allows a couple things: * Users can open arbitrary types of panes with the existing `splitPane`, `newWindow` actions, just by passing `"type": "scartchpad"` (for example). This is a lot more flexible than re-defining different `"openScratchpad"`, `"openTasksPane"`, etc, etc actions for every kind of pane. * This allows us to use the existing machinery of session restore to also restore non-terminal panes. The `type` property was added to `newTab`, `splitPane`, `newWindow`. When omitted, we still just treat the json as a blob of NewTerminalArgs. There's not actually any other kinds of `INewContentArgs` in this PR (other than the placeholder `GenericContentArgs`). In [`dev/migrie/fhl/md-pane`](https://github.com/microsoft/terminal/compare/dev/migrie/f/tasks-pane...dev/migrie/fhl/md-pane), I have a type of pane that would LOVE to add some args here. So that's forward-thinking. There's really just two stealth types of pane for now: `settings`, and `scratchpad`. Those I DON'T have as constants or anything in this PR. They probably should be? Though, I suspect around the time of the tasks & MD panes, I'll come up with whatever structure I actually want them to take. ### future considerations here * In the future, this should allow extensions to say "I know how to host `foo` content", for 3p content. * The `wt` CLI args were not yet updated to also accept `--type` yet. There's no reason we couldn't easily do that. * I considered adding `ICanHasCommandline` to allow arbitrary content to generate a `wt` commandline-serializable string. Punted on that for now. ## other PRs * #16170 * #16171 * #16172 * #16895 * #16914 <-- you are here Closes #17014 --- .../CommandlineTest.cpp | 593 ++++++++++-------- .../LocalTests_TerminalApp/SettingsTests.cpp | 508 ++++++++------- .../TerminalApp/AppActionHandlers.cpp | 91 ++- src/cascadia/TerminalApp/IPaneContent.idl | 2 +- src/cascadia/TerminalApp/Pane.cpp | 2 +- src/cascadia/TerminalApp/Pane.h | 2 +- .../TerminalApp/ScratchpadContent.cpp | 4 +- src/cascadia/TerminalApp/ScratchpadContent.h | 2 +- .../TerminalApp/SettingsPaneContent.cpp | 7 +- .../TerminalApp/SettingsPaneContent.h | 2 +- src/cascadia/TerminalApp/TabManagement.cpp | 37 +- src/cascadia/TerminalApp/TerminalPage.cpp | 111 +++- src/cascadia/TerminalApp/TerminalPage.h | 10 +- .../TerminalApp/TerminalPaneContent.cpp | 2 +- .../TerminalApp/TerminalPaneContent.h | 2 +- src/cascadia/TerminalApp/TerminalTab.cpp | 23 - src/cascadia/TerminalApp/TerminalWindow.cpp | 2 +- .../TerminalSettingsModel/ActionArgs.cpp | 13 +- .../TerminalSettingsModel/ActionArgs.h | 202 ++++-- .../TerminalSettingsModel/ActionArgs.idl | 35 +- .../UnitTests_SettingsModel/CommandTests.cpp | 27 +- .../DeserializationTests.cpp | 72 ++- .../KeyBindingsTests.cpp | 49 +- .../TerminalSettingsTests.cpp | 248 ++++---- .../WindowsTerminal/WindowEmperor.cpp | 4 +- 25 files changed, 1182 insertions(+), 868 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp index 3d0837d0a64..f3c0efa463d 100644 --- a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp +++ b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp @@ -381,14 +381,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - auto myCommand = myArgs.TerminalArgs().Commandline(); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + auto myCommand = terminalArgs.Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg \"", myCommand); } { @@ -397,14 +398,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - auto myCommand = myArgs.TerminalArgs().Commandline(); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + auto myCommand = terminalArgs.Commandline(); VERIFY_ARE_EQUAL(L"\" with spaces\"", myCommand); } } @@ -421,14 +423,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - auto myCommand = myArgs.TerminalArgs().Commandline(); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + auto myCommand = terminalArgs.Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg ; with spaces\"", myCommand); } } @@ -468,14 +471,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -489,15 +493,16 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"cmd", myArgs.TerminalArgs().Profile()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"cmd", terminalArgs.Profile()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -511,15 +516,16 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"c:\\Foo", myArgs.TerminalArgs().StartingDirectory()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_FALSE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"c:\\Foo", terminalArgs.StartingDirectory()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -533,15 +539,16 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"powershell.exe", myArgs.TerminalArgs().Commandline()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"powershell.exe", terminalArgs.Commandline()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -555,16 +562,17 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - auto myCommand = myArgs.TerminalArgs().Commandline(); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + auto myCommand = terminalArgs.Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\"", myCommand); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -578,16 +586,17 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - auto myCommand = myArgs.TerminalArgs().Commandline(); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + auto myCommand = terminalArgs.Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\" another-arg \"more spaces in this one\"", myCommand); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -601,15 +610,16 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"Windows PowerShell", myArgs.TerminalArgs().Profile()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"Windows PowerShell", terminalArgs.Profile()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -623,14 +633,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"wsl -d Alpine", terminalArgs.Commandline()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -644,16 +655,17 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"wsl -d Alpine", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"1", terminalArgs.Profile()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -669,15 +681,16 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_ARE_EQUAL(til::color(myArgs.TerminalArgs().TabColor().Value()), expectedColor); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NOT_NULL(terminalArgs.TabColor()); + VERIFY_ARE_EQUAL(til::color(terminalArgs.TabColor().Value()), expectedColor); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -693,15 +706,16 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().ColorScheme().empty()); - VERIFY_ARE_EQUAL(expectedScheme, myArgs.TerminalArgs().ColorScheme()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_IS_FALSE(terminalArgs.ColorScheme().empty()); + VERIFY_ARE_EQUAL(expectedScheme, terminalArgs.ColorScheme()); } } @@ -732,7 +746,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { AppCommandlineArgs appArgs{}; @@ -752,7 +767,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Down, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { AppCommandlineArgs appArgs{}; @@ -774,7 +790,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Right, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { AppCommandlineArgs appArgs{}; @@ -795,7 +812,8 @@ namespace TerminalAppLocalTests auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitType::Duplicate, myArgs.SplitMode()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { AppCommandlineArgs appArgs{}; @@ -815,16 +833,17 @@ namespace TerminalAppLocalTests auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"wsl -d Alpine", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"1", terminalArgs.Profile()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -844,16 +863,17 @@ namespace TerminalAppLocalTests auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Down, myArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"wsl -d Alpine", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"1", terminalArgs.Profile()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } { AppCommandlineArgs appArgs{}; @@ -873,16 +893,17 @@ namespace TerminalAppLocalTests auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine -H", myArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"wsl -d Alpine -H", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"1", terminalArgs.Profile()); + VERIFY_IS_TRUE(terminalArgs.ColorScheme().empty()); } } @@ -923,13 +944,14 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } { AppCommandlineArgs appArgs{}; @@ -943,14 +965,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"cmd", myArgs.TerminalArgs().Profile()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"cmd", terminalArgs.Profile()); } { AppCommandlineArgs appArgs{}; @@ -964,14 +987,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"c:\\Foo", myArgs.TerminalArgs().StartingDirectory()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_FALSE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"c:\\Foo", terminalArgs.StartingDirectory()); } { AppCommandlineArgs appArgs{}; @@ -985,14 +1009,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"powershell.exe", myArgs.TerminalArgs().Commandline()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"powershell.exe", terminalArgs.Commandline()); } { AppCommandlineArgs appArgs{}; @@ -1006,14 +1031,15 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\"", myArgs.TerminalArgs().Commandline()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\"", terminalArgs.Commandline()); } } @@ -1469,32 +1495,38 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(2u, appArgs._startupActions.size()); - auto actionAndArgs = appArgs._startupActions.at(0); - VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); - VERIFY_IS_NOT_NULL(actionAndArgs.Args()); - auto myArgs = actionAndArgs.Args().try_as(); - VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - - actionAndArgs = appArgs._startupActions.at(1); - VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); - VERIFY_IS_NOT_NULL(actionAndArgs.Args()); - myArgs = actionAndArgs.Args().try_as(); - VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"slpit-pane", myArgs.TerminalArgs().Commandline()); + { + auto actionAndArgs = appArgs._startupActions.at(0); + VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); + VERIFY_IS_NOT_NULL(actionAndArgs.Args()); + auto myArgs = actionAndArgs.Args().try_as(); + VERIFY_IS_NOT_NULL(myArgs); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + } + + { + auto actionAndArgs = appArgs._startupActions.at(1); + VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); + VERIFY_IS_NOT_NULL(actionAndArgs.Args()); + auto myArgs = actionAndArgs.Args().try_as(); + VERIFY_IS_NOT_NULL(myArgs); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"slpit-pane", terminalArgs.Commandline()); + } } { @@ -1511,8 +1543,9 @@ namespace TerminalAppLocalTests auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_ARE_EQUAL(L"slpit-pane -H", myArgs.TerminalArgs().Commandline()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_ARE_EQUAL(L"slpit-pane -H", terminalArgs.Commandline()); } } @@ -1530,9 +1563,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"C:\\", myArgs.TerminalArgs().StartingDirectory()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_ARE_EQUAL(L"wsl -d Alpine", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"C:\\", terminalArgs.StartingDirectory()); } { // two parsing terminators, new-tab command AppCommandlineArgs appArgs{}; @@ -1546,9 +1580,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine -- sleep 10", myArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"C:\\", myArgs.TerminalArgs().StartingDirectory()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_ARE_EQUAL(L"wsl -d Alpine -- sleep 10", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"C:\\", terminalArgs.StartingDirectory()); } { // two parsing terminators, *no* command AppCommandlineArgs appArgs{}; @@ -1562,9 +1597,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_ARE_EQUAL(L"wsl -d Alpine -- sleep 10", myArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"C:\\", myArgs.TerminalArgs().StartingDirectory()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_ARE_EQUAL(L"wsl -d Alpine -- sleep 10", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"C:\\", terminalArgs.StartingDirectory()); } } @@ -1578,13 +1614,14 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } void CommandlineTest::TestMultipleCommandExecuteCommandlineAction() @@ -1598,13 +1635,14 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } { auto actionAndArgs = actions.at(1); @@ -1612,13 +1650,14 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); - VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_NULL(terminalArgs.TabColor()); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } } @@ -1739,13 +1778,14 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"cmd", myArgs.TerminalArgs().Profile()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.ProfileIndex() == nullptr); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"cmd", terminalArgs.Profile()); } { Log::Comment(NoThrowString().Format(L"Pass a launch mode and command line")); @@ -1763,13 +1803,14 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(actionAndArgs.Args()); auto myArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(myArgs); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); - VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); - VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"powershell.exe", myArgs.TerminalArgs().Commandline()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.ProfileIndex() == nullptr); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"powershell.exe", terminalArgs.Commandline()); } } @@ -1800,7 +1841,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(0.5f, myArgs.SplitSize()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { AppCommandlineArgs appArgs{}; @@ -1820,7 +1862,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { AppCommandlineArgs appArgs{}; @@ -1841,7 +1884,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { auto actionAndArgs = appArgs._startupActions.at(2); @@ -1851,7 +1895,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(0.5f, myArgs.SplitSize()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } } { @@ -1873,7 +1918,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } { auto actionAndArgs = appArgs._startupActions.at(2); @@ -1883,7 +1929,8 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(myArgs); VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection()); VERIFY_ARE_EQUAL(0.7f, myArgs.SplitSize()); - VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + auto terminalArgs{ myArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); } } } diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp index 8116e9e4574..ead353fc900 100644 --- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -176,12 +176,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"${profile.name}", terminalArgs.Profile()); } const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; @@ -201,12 +202,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile()); } { @@ -220,12 +222,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); } { @@ -239,12 +242,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); } } @@ -302,12 +306,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"${profile.name}", terminalArgs.Profile()); } const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; @@ -328,12 +333,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile()); } { @@ -348,12 +354,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); } { @@ -368,12 +375,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); } } @@ -433,12 +441,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"${profile.name}", terminalArgs.Profile()); } const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; @@ -459,12 +468,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile()); } { @@ -479,12 +489,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1\"", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1\"", terminalArgs.Profile()); } { @@ -499,12 +510,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); } } @@ -692,12 +704,13 @@ namespace TerminalAppLocalTests const auto& realArgs = childActionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"ssh me@first.com", realArgs.TerminalArgs().Commandline()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"ssh me@first.com", terminalArgs.Commandline()); VERIFY_IS_FALSE(child.HasNestedCommands()); } @@ -712,12 +725,13 @@ namespace TerminalAppLocalTests const auto& realArgs = childActionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"ssh me@second.com", realArgs.TerminalArgs().Commandline()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"ssh me@second.com", terminalArgs.Commandline()); VERIFY_IS_FALSE(child.HasNestedCommands()); } @@ -818,12 +832,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(name, realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(name, terminalArgs.Profile()); VERIFY_IS_FALSE(childCommand.HasNestedCommands()); } @@ -839,12 +854,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(name, realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(name, terminalArgs.Profile()); VERIFY_IS_FALSE(childCommand.HasNestedCommands()); } @@ -860,12 +876,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(name, realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(name, terminalArgs.Profile()); VERIFY_IS_FALSE(childCommand.HasNestedCommands()); } @@ -951,12 +968,13 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(name, realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(name, terminalArgs.Profile()); VERIFY_IS_FALSE(command.HasNestedCommands()); } @@ -1069,12 +1087,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(name, realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(name, terminalArgs.Profile()); VERIFY_IS_FALSE(childCommand.HasNestedCommands()); } @@ -1090,12 +1109,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(name, realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(name, terminalArgs.Profile()); VERIFY_IS_FALSE(childCommand.HasNestedCommands()); } @@ -1111,12 +1131,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(name, realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(name, terminalArgs.Profile()); VERIFY_IS_FALSE(childCommand.HasNestedCommands()); } @@ -1245,12 +1266,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"${scheme.name}", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"${scheme.name}", terminalArgs.Profile()); } const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; @@ -1274,12 +1296,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"Campbell", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"Campbell", terminalArgs.Profile()); } { @@ -1294,12 +1317,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"Campbell PowerShell", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"Campbell PowerShell", terminalArgs.Profile()); } { @@ -1314,12 +1338,13 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"Vintage", realArgs.TerminalArgs().Profile()); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"Vintage", terminalArgs.Profile()); } } @@ -1385,15 +1410,16 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NULL(realArgs.TerminalArgs().Elevate()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile()); + VERIFY_IS_NULL(terminalArgs.Elevate()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(false, termSettings.Elevate()); @@ -1407,15 +1433,16 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NULL(realArgs.TerminalArgs().Elevate()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); + VERIFY_IS_NULL(terminalArgs.Elevate()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(true, termSettings.Elevate()); @@ -1429,15 +1456,16 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NULL(realArgs.TerminalArgs().Elevate()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); + VERIFY_IS_NULL(terminalArgs.Elevate()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(false, termSettings.Elevate()); @@ -1452,16 +1480,17 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Elevate().Value()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile()); + VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); + VERIFY_IS_FALSE(terminalArgs.Elevate().Value()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(false, termSettings.Elevate()); @@ -1475,16 +1504,17 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Elevate().Value()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); + VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); + VERIFY_IS_FALSE(terminalArgs.Elevate().Value()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(false, termSettings.Elevate()); @@ -1498,16 +1528,17 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Elevate().Value()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); + VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); + VERIFY_IS_FALSE(terminalArgs.Elevate().Value()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(false, termSettings.Elevate()); @@ -1522,16 +1553,17 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Elevate().Value()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile()); + VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); + VERIFY_IS_TRUE(terminalArgs.Elevate().Value()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(true, termSettings.Elevate()); @@ -1544,16 +1576,17 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Elevate().Value()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); + VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); + VERIFY_IS_TRUE(terminalArgs.Elevate().Value()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(true, termSettings.Elevate()); @@ -1567,16 +1600,17 @@ namespace TerminalAppLocalTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Elevate().Value()); - - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr); + auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); + VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); + VERIFY_IS_TRUE(terminalArgs.Elevate().Value()); + + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); const auto termSettings = termSettingsResult.DefaultSettings(); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(true, termSettings.Elevate()); diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 22a0582dfda..2fd6680574f 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -238,6 +238,32 @@ namespace winrt::TerminalApp::implementation } } + // * Helper to try and get a ProfileIndex out of a NewTerminalArgs out of a + // NewContentArgs. For the new tab and split pane action, we want to _not_ + // handle the event if an invalid profile index was passed. + // + // Return value: + // * True if the args are NewTerminalArgs, and the profile index was out of bounds. + // * False otherwise. + static bool _shouldBailForInvalidProfileIndex(const CascadiaSettings& settings, const INewContentArgs& args) + { + if (!args) + { + return false; + } + if (const auto& terminalArgs{ args.try_as() }) + { + if (const auto index = terminalArgs.ProfileIndex()) + { + if (gsl::narrow(index.Value()) >= settings.ActiveProfiles().Size()) + { + return true; + } + } + } + return false; + } + void TerminalPage::_HandleSplitPane(const IInspectable& sender, const ActionEventArgs& args) { @@ -247,16 +273,10 @@ namespace winrt::TerminalApp::implementation } else if (const auto& realArgs = args.ActionArgs().try_as()) { - if (const auto& newTerminalArgs{ realArgs.TerminalArgs() }) + if (_shouldBailForInvalidProfileIndex(_settings, realArgs.ContentArgs())) { - if (const auto index = realArgs.TerminalArgs().ProfileIndex()) - { - if (gsl::narrow(index.Value()) >= _settings.ActiveProfiles().Size()) - { - args.Handled(false); - return; - } - } + args.Handled(false); + return; } const auto& duplicateFromTab{ realArgs.SplitMode() == SplitType::Duplicate ? _GetFocusedTab() : nullptr }; @@ -267,7 +287,7 @@ namespace winrt::TerminalApp::implementation realArgs.SplitDirection(), // This is safe, we're already filtering so the value is (0, 1) ::base::saturated_cast(realArgs.SplitSize()), - _MakePane(realArgs.TerminalArgs(), duplicateFromTab)); + _MakePane(realArgs.ContentArgs(), duplicateFromTab)); args.Handled(true); } } @@ -445,19 +465,13 @@ namespace winrt::TerminalApp::implementation } else if (const auto& realArgs = args.ActionArgs().try_as()) { - if (const auto& newTerminalArgs{ realArgs.TerminalArgs() }) + if (_shouldBailForInvalidProfileIndex(_settings, realArgs.ContentArgs())) { - if (const auto index = newTerminalArgs.ProfileIndex()) - { - if (gsl::narrow(index.Value()) >= _settings.ActiveProfiles().Size()) - { - args.Handled(false); - return; - } - } + args.Handled(false); + return; } - LOG_IF_FAILED(_OpenNewTab(realArgs.TerminalArgs())); + LOG_IF_FAILED(_OpenNewTab(realArgs.ContentArgs())); args.Handled(true); } } @@ -869,8 +883,23 @@ namespace winrt::TerminalApp::implementation // - // Important: Don't take the param by reference, since we'll be doing work // on another thread. - fire_and_forget TerminalPage::_OpenNewWindow(const NewTerminalArgs newTerminalArgs) + fire_and_forget TerminalPage::_OpenNewWindow(const INewContentArgs newContentArgs) { + auto terminalArgs{ newContentArgs.try_as() }; + + // Do nothing for non-terminal panes. + // + // Theoretically, we could define a `IHasCommandline` interface, and + // stick `ToCommandline` on that interface, for any kind of pane that + // wants to be convertable to a wt commandline. + // + // Another idea we're thinking about is just `wt do {literal json for an + // action}`, which might be less leaky + if (terminalArgs == nullptr) + { + co_return; + } + // Hop to the BG thread co_await winrt::resume_background(); @@ -883,8 +912,7 @@ namespace winrt::TerminalApp::implementation // `-w -1` will ensure a new window is created. winrt::hstring cmdline{ fmt::format(L"-w -1 new-tab {}", - newTerminalArgs ? newTerminalArgs.ToCommandline().c_str() : - L"") + terminalArgs.ToCommandline().c_str()) }; // Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we @@ -909,29 +937,32 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleNewWindow(const IInspectable& /*sender*/, const ActionEventArgs& actionArgs) { - NewTerminalArgs newTerminalArgs{ nullptr }; + INewContentArgs newContentArgs{ nullptr }; // If the caller provided NewTerminalArgs, then try to use those if (actionArgs) { if (const auto& realArgs = actionArgs.ActionArgs().try_as()) { - newTerminalArgs = realArgs.TerminalArgs(); + newContentArgs = realArgs.ContentArgs(); } } // Otherwise, if no NewTerminalArgs were provided, then just use a // default-constructed one. The default-constructed one implies that // nothing about the launch should be modified (just use the default // profile). - if (!newTerminalArgs) + if (!newContentArgs) { - newTerminalArgs = NewTerminalArgs(); + newContentArgs = NewTerminalArgs{}; } - const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) }; + if (const auto& terminalArgs{ newContentArgs.try_as() }) + { + const auto profile{ _settings.GetProfileForArgs(terminalArgs) }; + terminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid())); + } // Manually fill in the evaluated profile. - newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid())); - _OpenNewWindow(newTerminalArgs); + _OpenNewWindow(newContentArgs); actionArgs.Handled(true); } diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index fa258fdbd26..f4c6ce13957 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -31,7 +31,7 @@ namespace TerminalApp Windows.Foundation.IReference TabColor { get; }; Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; - Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind); + Microsoft.Terminal.Settings.Model.INewContentArgs GetNewTerminalArgs(BuildStartupKind kind); void Focus(Windows.UI.Xaml.FocusState reason); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index de3c7f76c46..7d11f4edc63 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -100,7 +100,7 @@ Pane::Pane(std::shared_ptr first, // Extract the terminal settings from the current (leaf) pane's control // to be used to create an equivalent control -NewTerminalArgs Pane::GetTerminalArgsForPane(BuildStartupKind kind) const +INewContentArgs Pane::GetTerminalArgsForPane(BuildStartupKind kind) const { // Leaves are the only things that have controls assert(_IsLeaf()); diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 37ed79b4739..67daee1694f 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -106,7 +106,7 @@ class Pane : public std::enable_shared_from_this uint32_t panesCreated; }; BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::BuildStartupKind kind); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const; + winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction); diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index 0f39daa1bee..16346c9b038 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -48,9 +48,9 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } - NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const + INewContentArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const { - return nullptr; + return BaseContentArgs(L"scratchpad"); } winrt::hstring ScratchpadContent::Icon() const diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index cd01414e677..407773fedd1 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); void Close(); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const; + winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const; winrt::hstring Title() { return L"Scratchpad"; } uint64_t TaskbarState() { return 0; } diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index 3e89c38296b..7e48ae9f6be 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -50,12 +50,9 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } - NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const BuildStartupKind /*kind*/) const + INewContentArgs SettingsPaneContent::GetNewTerminalArgs(const BuildStartupKind /*kind*/) const { - // For now, we're doing a terrible thing in TerminalTab itself to - // generate an OpenSettings action manually, without asking for the pane - // structure. - return nullptr; + return BaseContentArgs(L"settings"); } winrt::hstring SettingsPaneContent::Icon() const diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index ee3322bf06b..6169df84beb 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -20,7 +20,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Size MinimumSize(); void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); void Close(); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const BuildStartupKind kind) const; + winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(const BuildStartupKind kind) const; winrt::hstring Title() { return RS_(L"SettingsTab"); } uint64_t TaskbarState() { return 0; } diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 3f833bb8ba9..f2039d0043e 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -62,30 +62,33 @@ namespace winrt::TerminalApp::implementation // - existingConnection: An optional connection that is already established to a PTY // for this tab to host instead of creating one. // If not defined, the tab will create the connection. - HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs) + HRESULT TerminalPage::_OpenNewTab(const INewContentArgs& newContentArgs) try { - const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) }; - // GH#11114: GetProfileForArgs can return null if the index is higher - // than the number of available profiles. - if (!profile) + if (const auto& newTerminalArgs{ newContentArgs.try_as() }) { - return S_FALSE; - } - const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) }; + const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) }; + // GH#11114: GetProfileForArgs can return null if the index is higher + // than the number of available profiles. + if (!profile) + { + return S_FALSE; + } + const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) }; - // Try to handle auto-elevation - if (_maybeElevate(newTerminalArgs, settings, profile)) - { - return S_OK; + // Try to handle auto-elevation + if (_maybeElevate(newTerminalArgs, settings, profile)) + { + return S_OK; + } + // We can't go in the other direction (elevated->unelevated) + // unfortunately. This seems to be due to Centennial quirks. It works + // unpackaged, but not packaged. } - // We can't go in the other direction (elevated->unelevated) - // unfortunately. This seems to be due to Centennial quirks. It works - // unpackaged, but not packaged. - // + // This call to _MakePane won't return nullptr, we already checked that // case above with the _maybeElevate call. - _CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr)); + _CreateNewTabFromPane(_MakePane(newContentArgs, nullptr)); return S_OK; } CATCH_RETURN(); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index ec7c066d5ff..2f1b66b561a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -21,6 +21,7 @@ #include "ColorHelper.h" #include "DebugTapConnection.h" #include "SettingsPaneContent.h" +#include "ScratchpadContent.h" #include "TabRowControl.h" #include "Utils.h" @@ -315,6 +316,7 @@ namespace winrt::TerminalApp::implementation // Check that there's at least one action that's not just an elevated newTab action. for (const auto& action : _startupActions) { + // Only new terminal panes will be requesting elevation. NewTerminalArgs newTerminalArgs{ nullptr }; if (action.Action() == ShortcutAction::NewTab) @@ -322,7 +324,7 @@ namespace winrt::TerminalApp::implementation const auto& args{ action.Args().try_as() }; if (args) { - newTerminalArgs = args.TerminalArgs(); + newTerminalArgs = args.ContentArgs().try_as(); } else { @@ -335,7 +337,7 @@ namespace winrt::TerminalApp::implementation const auto& args{ action.Args().try_as() }; if (args) { - newTerminalArgs = args.TerminalArgs(); + newTerminalArgs = args.ContentArgs().try_as(); } else { @@ -3095,9 +3097,9 @@ namespace winrt::TerminalApp::implementation // - If the newTerminalArgs required us to open the pane as a new elevated // connection, then we'll return nullptr. Otherwise, we'll return a new // Pane for this connection. - std::shared_ptr TerminalPage::_MakePane(const NewTerminalArgs& newTerminalArgs, - const winrt::TerminalApp::TabBase& sourceTab, - TerminalConnection::ITerminalConnection existingConnection) + std::shared_ptr TerminalPage::_MakeTerminalPane(const NewTerminalArgs& newTerminalArgs, + const winrt::TerminalApp::TabBase& sourceTab, + TerminalConnection::ITerminalConnection existingConnection) { // First things first - Check for making a pane from content ID. if (newTerminalArgs && @@ -3165,6 +3167,7 @@ namespace winrt::TerminalApp::implementation const auto control = _CreateNewControlAndContent(controlSettings, connection); auto paneContent{ winrt::make(profile, _terminalSettingsCache, control) }; + auto resultPane = std::make_shared(paneContent); if (debugConnection) // this will only be set if global debugging is on and tap is active @@ -3189,6 +3192,41 @@ namespace winrt::TerminalApp::implementation return resultPane; } + std::shared_ptr TerminalPage::_MakePane(const INewContentArgs& contentArgs, + const winrt::TerminalApp::TabBase& sourceTab, + TerminalConnection::ITerminalConnection existingConnection) + + { + if (const auto& newTerminalArgs{ contentArgs.try_as() }) + { + // Terminals are of course special, and have to deal with debug taps, duplicating the tab, etc. + return _MakeTerminalPane(newTerminalArgs, sourceTab, existingConnection); + } + + IPaneContent content{ nullptr }; + + const auto& paneType{ contentArgs.Type() }; + if (paneType == L"scratchpad") + { + const auto& scratchPane{ winrt::make_self() }; + + // This is maybe a little wacky - add our key event handler to the pane + // we made. So that we can get actions for keys that the content didn't + // handle. + scratchPane->GetRoot().KeyDown({ get_weak(), &TerminalPage::_KeyDownHandler }); + + content = *scratchPane; + } + else if (paneType == L"settings") + { + content = _makeSettingsContent(); + } + + assert(content); + + return std::make_shared(content); + } + void TerminalPage::_restartPaneConnection( const TerminalApp::TerminalPaneContent& paneContent, const winrt::Windows::Foundation::IInspectable&) @@ -3841,6 +3879,39 @@ namespace winrt::TerminalApp::implementation CATCH_RETURN() } + TerminalApp::IPaneContent TerminalPage::_makeSettingsContent() + { + if (auto app{ winrt::Windows::UI::Xaml::Application::Current().try_as() }) + { + if (auto appPrivate{ winrt::get_self(app) }) + { + // Lazily load the Settings UI components so that we don't do it on startup. + appPrivate->PrepareForSettingsUI(); + } + } + + // Create the SUI pane content + auto settingsContent{ winrt::make_self(_settings) }; + auto sui = settingsContent->SettingsUI(); + + if (_hostingHwnd) + { + sui.SetHostingWindow(reinterpret_cast(*_hostingHwnd)); + } + + // GH#8767 - let unhandled keys in the SUI try to run commands too. + sui.KeyDown({ get_weak(), &TerminalPage::_KeyDownHandler }); + + sui.OpenJson([weakThis{ get_weak() }](auto&& /*s*/, winrt::Microsoft::Terminal::Settings::Model::SettingsTarget e) { + if (auto page{ weakThis.get() }) + { + page->_LaunchSettings(e); + } + }); + + return *settingsContent; + } + // Method Description: // - Creates a settings UI tab and focuses it. If there's already a settings UI tab open, // just focus the existing one. @@ -3853,36 +3924,8 @@ namespace winrt::TerminalApp::implementation // If we're holding the settings tab's switch command, don't create a new one, switch to the existing one. if (!_settingsTab) { - if (auto app{ winrt::Windows::UI::Xaml::Application::Current().try_as() }) - { - if (auto appPrivate{ winrt::get_self(app) }) - { - // Lazily load the Settings UI components so that we don't do it on startup. - appPrivate->PrepareForSettingsUI(); - } - } - - // Create the SUI pane content - auto settingsContent{ winrt::make_self(_settings) }; - auto sui = settingsContent->SettingsUI(); - - if (_hostingHwnd) - { - sui.SetHostingWindow(reinterpret_cast(*_hostingHwnd)); - } - - // GH#8767 - let unhandled keys in the SUI try to run commands too. - sui.KeyDown({ this, &TerminalPage::_KeyDownHandler }); - - sui.OpenJson([weakThis{ get_weak() }](auto&& /*s*/, winrt::Microsoft::Terminal::Settings::Model::SettingsTarget e) { - if (auto page{ weakThis.get() }) - { - page->_LaunchSettings(e); - } - }); - // Create the tab - auto resultPane = std::make_shared(*settingsContent); + auto resultPane = std::make_shared(_makeSettingsContent()); _settingsTab = _CreateNewTabFromPane(resultPane); } else diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index eece631419b..1fd2acfaee6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -292,7 +292,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutProfile(const Microsoft::Terminal::Settings::Model::Profile profile, int profileIndex); void _OpenNewTabDropdown(); - HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs); + HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::INewContentArgs& newContentArgs); TerminalApp::TerminalTab _CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition = -1); std::wstring _evaluatePathForCwd(std::wstring_view path); @@ -301,7 +301,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(const TerminalApp::TerminalPaneContent& paneContent); void _restartPaneConnection(const TerminalApp::TerminalPaneContent&, const winrt::Windows::Foundation::IInspectable&); - winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); + winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::INewContentArgs newContentArgs); void _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); @@ -435,7 +435,11 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl _SetupControl(const winrt::Microsoft::Terminal::Control::TermControl& term); winrt::Microsoft::Terminal::Control::TermControl _AttachControlToContent(const uint64_t& contentGuid); - std::shared_ptr _MakePane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr, + TerminalApp::IPaneContent _makeSettingsContent(); + std::shared_ptr _MakeTerminalPane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr, + const winrt::TerminalApp::TabBase& sourceTab = nullptr, + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); + std::shared_ptr _MakePane(const Microsoft::Terminal::Settings::Model::INewContentArgs& newContentArgs = nullptr, const winrt::TerminalApp::TabBase& sourceTab = nullptr, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 406ab283510..652486e441a 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -92,7 +92,7 @@ namespace winrt::TerminalApp::implementation return _control.TabColor(); } - NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const BuildStartupKind kind) const + INewContentArgs TerminalPaneContent::GetNewTerminalArgs(const BuildStartupKind kind) const { NewTerminalArgs args{}; const auto& controlSettings = _control.Settings(); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index e308a5cfb00..2995e5f1872 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -28,7 +28,7 @@ namespace winrt::TerminalApp::implementation void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); void Close(); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const; + winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 639f855e88e..ceb3860f1ff 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -447,30 +447,7 @@ namespace winrt::TerminalApp::implementation // 1 for the child after the first split. auto state = _rootPane->BuildStartupActions(0, 1, kind); - // HORRIBLE - // - // Workaround till we know how we actually want to handle state - // restoring other kinds of panes. If this is a settings tab, just - // restore it as a settings tab. Don't bother recreating terminal args - // for every pane. - // - // In the future, we'll want to definitely get rid of - // Pane::GetTerminalArgsForPane, and somehow instead find a better way - // of re-creating the pane state. Probably through a combo of ResizePane - // actions and SetPaneOrientation actions. - if (const auto& settings{ _rootPane->GetContent().try_as() }) { - ActionAndArgs action; - action.Action(ShortcutAction::OpenSettings); - OpenSettingsArgs args{ SettingsTarget::SettingsUI }; - action.Args(args); - - state.args = std::vector{ std::move(action) }; - } - else - { - state = _rootPane->BuildStartupActions(0, 1, kind); - ActionAndArgs newTabAction{}; newTabAction.Action(ShortcutAction::NewTab); NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(kind) }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 4a37b0a733e..bd246bb53de 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1238,7 +1238,7 @@ namespace winrt::TerminalApp::implementation // Create the equivalent NewTab action. const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, Settings::Model::NewTabArgs(firstAction.Args() ? - firstAction.Args().try_as().TerminalArgs() : + firstAction.Args().try_as().ContentArgs() : nullptr) }; args.SetAt(0, newAction); } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 598b77a4051..24f4e445953 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -6,6 +6,7 @@ #include "ActionArgs.h" #include "ActionEventArgs.g.cpp" +#include "BaseContentArgs.g.cpp" #include "NewTerminalArgs.g.cpp" #include "CopyTextArgs.g.cpp" #include "NewTabArgs.g.cpp" @@ -245,9 +246,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring NewTabArgs::GenerateName() const { winrt::hstring newTerminalArgsStr; - if (TerminalArgs()) + if (ContentArgs()) { - newTerminalArgsStr = TerminalArgs().GenerateName(); + newTerminalArgsStr = ContentArgs().GenerateName(); } if (newTerminalArgsStr.empty()) @@ -465,9 +466,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } winrt::hstring newTerminalArgsStr; - if (TerminalArgs()) + if (ContentArgs()) { - newTerminalArgsStr = TerminalArgs().GenerateName(); + newTerminalArgsStr = ContentArgs().GenerateName(); } if (SplitMode() != SplitType::Duplicate && !newTerminalArgsStr.empty()) @@ -769,9 +770,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring NewWindowArgs::GenerateName() const { winrt::hstring newTerminalArgsStr; - if (TerminalArgs()) + if (ContentArgs()) { - newTerminalArgsStr = TerminalArgs().GenerateName(); + newTerminalArgsStr = ContentArgs().GenerateName(); } if (newTerminalArgsStr.empty()) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 4062cee426c..bf34a6af140 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -6,6 +6,7 @@ // HEY YOU: When adding ActionArgs types, make sure to add the corresponding // *.g.cpp to ActionArgs.cpp! #include "ActionEventArgs.g.h" +#include "BaseContentArgs.g.h" #include "NewTerminalArgs.g.h" #include "CopyTextArgs.g.h" #include "NewTabArgs.g.h" @@ -71,7 +72,7 @@ public: \ _##name = value; \ } \ \ -private: \ +protected: \ std::optional _##name{ std::nullopt }; // Notes on defining ActionArgs and ActionEventArgs: @@ -295,6 +296,56 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(bool, Handled, false); }; + struct BaseContentArgs : public BaseContentArgsT + { + BaseContentArgs(winrt::hstring type) : + _Type{ type } {} + + BaseContentArgs() : + BaseContentArgs(L"") {} + + ACTION_ARG(winrt::hstring, Type, L""); + + static constexpr std::string_view TypeKey{ "type" }; + + public: + bool Equals(INewContentArgs other) const + { + return other.Type() == _Type; + } + size_t Hash() const + { + til::hasher h; + Hash(h); + return h.finalize(); + } + void Hash(til::hasher& h) const + { + h.write(Type()); + } + INewContentArgs Copy() const + { + auto copy{ winrt::make_self() }; + copy->_Type = _Type; + return *copy; + } + winrt::hstring GenerateName() const + { + return winrt::hstring{ L"type: " } + Type(); + } + static Json::Value ToJson(const Model::BaseContentArgs& val) + { + if (!val) + { + return {}; + } + auto args{ get_self(val) }; + Json::Value json{ Json::ValueType::objectValue }; + JsonUtils::SetValueForKey(json, TypeKey, args->_Type); + return json; + } + }; + // Although it may _seem_ like NewTerminalArgs can use ACTION_ARG_BODY, it // actually can't, because it isn't an `IActionArgs`, which breaks some // assumptions made in the macro. @@ -303,6 +354,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation NewTerminalArgs() = default; NewTerminalArgs(int32_t& profileIndex) : _ProfileIndex{ profileIndex } {}; + + ACTION_ARG(winrt::hstring, Type, L""); + ACTION_ARG(winrt::hstring, Commandline, L""); ACTION_ARG(winrt::hstring, StartingDirectory, L""); ACTION_ARG(winrt::hstring, TabTitle, L""); @@ -335,7 +389,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation hstring GenerateName() const; hstring ToCommandline() const; - bool Equals(const Model::NewTerminalArgs& other) + bool Equals(const Model::INewContentArgs& other) { auto otherAsUs = other.try_as(); if (otherAsUs) @@ -433,6 +487,45 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation h.write(ContentId()); } }; + + static std::tuple> ContentArgsFromJson(const Json::Value& json) + { + winrt::hstring type; + JsonUtils::GetValueForKey(json, "type", type); + if (type.empty()) + { + auto terminalArgs = winrt::Microsoft::Terminal::Settings::Model::implementation::NewTerminalArgs::FromJson(json); + // Don't let the user specify the __content property in their + // settings. That's an internal-use-only property. + if (terminalArgs.ContentId()) + { + return { terminalArgs, { SettingsLoadWarnings::InvalidUseOfContent } }; + } + return { terminalArgs, {} }; + } + + // For now, we don't support any other concrete types of content + // with args. Just return a placeholder type that only includes the type + return { *winrt::make_self(type), {} }; + } + static Json::Value ContentArgsToJson(const Model::INewContentArgs& contentArgs) + { + if (contentArgs == nullptr) + { + return {}; + } + // TerminalArgs don't have a type. + if (contentArgs.Type().empty()) + { + return winrt::Microsoft::Terminal::Settings::Model::implementation::NewTerminalArgs::ToJson(contentArgs.try_as()); + } + + // For now, we don't support any other concrete types of content + // with args. Just return a placeholder. + auto base{ winrt::make_self(contentArgs.Type()) }; + return BaseContentArgs::ToJson(*base); + } + } template<> @@ -463,6 +556,20 @@ struct til::hash_trait } } }; +template<> +struct til::hash_trait +{ + using M = winrt::Microsoft::Terminal::Settings::Model::INewContentArgs; + + void operator()(hasher& h, const M& value) const noexcept + { + if (value) + { + h.write(value.Type()); + h.write(value.Hash()); + } + } +}; namespace winrt::Microsoft::Terminal::Settings::Model::implementation { @@ -473,9 +580,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct NewTabArgs : public NewTabArgsT { NewTabArgs() = default; - NewTabArgs(const Model::NewTerminalArgs& terminalArgs) : - _TerminalArgs{ terminalArgs } {}; - WINRT_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr); + NewTabArgs(const Model::INewContentArgs& terminalArgs) : + _ContentArgs{ terminalArgs } {}; + WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, nullptr); public: hstring GenerateName() const; @@ -485,7 +592,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto otherAsUs = other.try_as(); if (otherAsUs) { - return otherAsUs->_TerminalArgs.Equals(_TerminalArgs); + return otherAsUs->_ContentArgs.Equals(_ContentArgs); } return false; }; @@ -493,15 +600,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); - args->_TerminalArgs = NewTerminalArgs::FromJson(json); - - // Don't let the user specify the __content property in their - // settings. That's an internal-use-only property. - if (args->_TerminalArgs.ContentId()) - { - return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; - } - return { *args, {} }; + auto [content, warnings] = ContentArgsFromJson(json); + args->_ContentArgs = content; + return { *args, warnings }; } static Json::Value ToJson(const IActionArgs& val) { @@ -510,18 +611,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return {}; } const auto args{ get_self(val) }; - return NewTerminalArgs::ToJson(args->_TerminalArgs); + return ContentArgsToJson(args->_ContentArgs); } IActionArgs Copy() const { auto copy{ winrt::make_self() }; - copy->_TerminalArgs = _TerminalArgs.Copy(); + copy->_ContentArgs = _ContentArgs.Copy(); return *copy; } size_t Hash() const { til::hasher h; - h.write(TerminalArgs()); + h.write(ContentArgs()); return h.finalize(); } }; @@ -529,22 +630,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct SplitPaneArgs : public SplitPaneArgsT { SplitPaneArgs() = default; - SplitPaneArgs(SplitType splitMode, SplitDirection direction, double size, const Model::NewTerminalArgs& terminalArgs) : + SplitPaneArgs(SplitType splitMode, SplitDirection direction, double size, const Model::INewContentArgs& terminalArgs) : _SplitMode{ splitMode }, _SplitDirection{ direction }, _SplitSize{ size }, - _TerminalArgs{ terminalArgs } {}; - SplitPaneArgs(SplitDirection direction, double size, const Model::NewTerminalArgs& terminalArgs) : + _ContentArgs{ terminalArgs } {}; + SplitPaneArgs(SplitDirection direction, double size, const Model::INewContentArgs& terminalArgs) : _SplitDirection{ direction }, _SplitSize{ size }, - _TerminalArgs{ terminalArgs } {}; - SplitPaneArgs(SplitDirection direction, const Model::NewTerminalArgs& terminalArgs) : + _ContentArgs{ terminalArgs } {}; + SplitPaneArgs(SplitDirection direction, const Model::INewContentArgs& terminalArgs) : _SplitDirection{ direction }, - _TerminalArgs{ terminalArgs } {}; + _ContentArgs{ terminalArgs } {}; SplitPaneArgs(SplitType splitMode) : _SplitMode{ splitMode } {}; + ACTION_ARG(Model::SplitDirection, SplitDirection, SplitDirection::Automatic); - WINRT_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr); + WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, nullptr); ACTION_ARG(SplitType, SplitMode, SplitType::Manual); ACTION_ARG(double, SplitSize, .5); @@ -561,8 +663,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (otherAsUs) { return otherAsUs->_SplitDirection == _SplitDirection && - (otherAsUs->_TerminalArgs ? otherAsUs->_TerminalArgs.Equals(_TerminalArgs) : - otherAsUs->_TerminalArgs == _TerminalArgs) && + (otherAsUs->_ContentArgs ? otherAsUs->_ContentArgs.Equals(_ContentArgs) : + otherAsUs->_ContentArgs == _ContentArgs) && otherAsUs->_SplitSize == _SplitSize && otherAsUs->_SplitMode == _SplitMode; } @@ -572,7 +674,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); - args->_TerminalArgs = NewTerminalArgs::FromJson(json); JsonUtils::GetValueForKey(json, SplitKey, args->_SplitDirection); JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode); JsonUtils::GetValueForKey(json, SplitSizeKey, args->_SplitSize); @@ -581,14 +682,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return { nullptr, { SettingsLoadWarnings::InvalidSplitSize } }; } - // Don't let the user specify the __content property in their - // settings. That's an internal-use-only property. - if (args->_TerminalArgs.ContentId()) - { - return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; - } - - return { *args, {} }; + auto [content, warnings] = ContentArgsFromJson(json); + args->_ContentArgs = content; + return { *args, warnings }; } static Json::Value ToJson(const IActionArgs& val) { @@ -597,7 +693,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return {}; } const auto args{ get_self(val) }; - auto json{ NewTerminalArgs::ToJson(args->_TerminalArgs) }; + auto json{ ContentArgsToJson(args->_ContentArgs) }; JsonUtils::SetValueForKey(json, SplitKey, args->_SplitDirection); JsonUtils::SetValueForKey(json, SplitModeKey, args->_SplitMode); JsonUtils::SetValueForKey(json, SplitSizeKey, args->_SplitSize); @@ -607,7 +703,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { auto copy{ winrt::make_self() }; copy->_SplitDirection = _SplitDirection; - copy->_TerminalArgs = _TerminalArgs.Copy(); + copy->_ContentArgs = _ContentArgs.Copy(); copy->_SplitMode = _SplitMode; copy->_SplitSize = _SplitSize; return *copy; @@ -616,7 +712,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { til::hasher h; h.write(SplitDirection()); - h.write(TerminalArgs()); + h.write(ContentArgs()); h.write(SplitMode()); h.write(SplitSize()); return h.finalize(); @@ -626,9 +722,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct NewWindowArgs : public NewWindowArgsT { NewWindowArgs() = default; - NewWindowArgs(const Model::NewTerminalArgs& terminalArgs) : - _TerminalArgs{ terminalArgs } {}; - WINRT_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr); + NewWindowArgs(const Model::INewContentArgs& terminalArgs) : + _ContentArgs{ terminalArgs } {}; + WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, nullptr); public: hstring GenerateName() const; @@ -638,7 +734,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto otherAsUs = other.try_as(); if (otherAsUs) { - return otherAsUs->_TerminalArgs.Equals(_TerminalArgs); + return otherAsUs->_ContentArgs.Equals(_ContentArgs); } return false; }; @@ -646,16 +742,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); - args->_TerminalArgs = NewTerminalArgs::FromJson(json); - - // Don't let the user specify the __content property in their - // settings. That's an internal-use-only property. - if (args->_TerminalArgs.ContentId()) - { - return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; - } - - return { *args, {} }; + auto [content, warnings] = ContentArgsFromJson(json); + args->_ContentArgs = content; + return { *args, warnings }; } static Json::Value ToJson(const IActionArgs& val) { @@ -664,18 +753,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return {}; } const auto args{ get_self(val) }; - return NewTerminalArgs::ToJson(args->_TerminalArgs); + return ContentArgsToJson(args->_ContentArgs); } IActionArgs Copy() const { auto copy{ winrt::make_self() }; - copy->_TerminalArgs = _TerminalArgs.Copy(); + copy->_ContentArgs = _ContentArgs.Copy(); return *copy; } size_t Hash() const { til::hasher h; - h.write(TerminalArgs()); + h.write(ContentArgs()); return h.finalize(); } }; @@ -829,6 +918,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation { BASIC_FACTORY(ActionEventArgs); + BASIC_FACTORY(BaseContentArgs); BASIC_FACTORY(CopyTextArgs); BASIC_FACTORY(SwitchToTabArgs); BASIC_FACTORY(NewTerminalArgs); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index f8e6f4eb9cd..05ed70069a9 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -122,10 +122,22 @@ namespace Microsoft.Terminal.Settings.Model All = 0xffffffff, }; - [default_interface] runtimeclass NewTerminalArgs { + interface INewContentArgs { + String Type { get; }; + Boolean Equals(INewContentArgs other); + UInt64 Hash(); + INewContentArgs Copy(); + String GenerateName(); + }; + + runtimeclass BaseContentArgs : [default] INewContentArgs { + BaseContentArgs(); + BaseContentArgs(String type); + }; + + runtimeclass NewTerminalArgs : INewContentArgs { NewTerminalArgs(); NewTerminalArgs(Int32 profileIndex); - NewTerminalArgs Copy(); String Commandline; String StartingDirectory; @@ -153,10 +165,7 @@ namespace Microsoft.Terminal.Settings.Model UInt64 ContentId{ get; set; }; - Boolean Equals(NewTerminalArgs other); - String GenerateName(); String ToCommandline(); - UInt64 Hash(); }; [default_interface] runtimeclass ActionEventArgs : IActionEventArgs @@ -175,8 +184,8 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass NewTabArgs : IActionArgs { - NewTabArgs(NewTerminalArgs terminalArgs); - NewTerminalArgs TerminalArgs { get; }; + NewTabArgs(INewContentArgs contentArgs); + INewContentArgs ContentArgs { get; }; }; [default_interface] runtimeclass MovePaneArgs : IActionArgs @@ -221,13 +230,13 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass SplitPaneArgs : IActionArgs { - SplitPaneArgs(SplitType splitMode, SplitDirection split, Double size, NewTerminalArgs terminalArgs); - SplitPaneArgs(SplitDirection split, Double size, NewTerminalArgs terminalArgs); - SplitPaneArgs(SplitDirection split, NewTerminalArgs terminalArgs); + SplitPaneArgs(SplitType splitMode, SplitDirection split, Double size, INewContentArgs contentArgs); + SplitPaneArgs(SplitDirection split, Double size, INewContentArgs contentArgs); + SplitPaneArgs(SplitDirection split, INewContentArgs contentArgs); SplitPaneArgs(SplitType splitMode); SplitDirection SplitDirection { get; }; - NewTerminalArgs TerminalArgs { get; }; + INewContentArgs ContentArgs { get; }; SplitType SplitMode { get; }; Double SplitSize { get; }; }; @@ -347,8 +356,8 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass NewWindowArgs : IActionArgs { - NewWindowArgs(NewTerminalArgs terminalArgs); - NewTerminalArgs TerminalArgs { get; }; + NewWindowArgs(INewContentArgs contentArgs); + INewContentArgs ContentArgs { get; }; }; [default_interface] runtimeclass PrevTabArgs : IActionArgs diff --git a/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp index 8db1141736f..b4940852c91 100644 --- a/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp @@ -473,7 +473,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); VERIFY_ARE_EQUAL(L"", cmdline); @@ -486,7 +487,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline); @@ -499,7 +501,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline); @@ -512,7 +515,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); VERIFY_ARE_EQUAL(L"-- \"bar.exe\"", cmdline); @@ -525,7 +529,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); Log::Comment(NoThrowString().Format( @@ -540,7 +545,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); Log::Comment(NoThrowString().Format( @@ -555,7 +561,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); Log::Comment(NoThrowString().Format( @@ -570,7 +577,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); Log::Comment(NoThrowString().Format( @@ -585,7 +593,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action()); const auto& realArgs = command.ActionAndArgs().Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); - const auto& terminalArgs = realArgs.TerminalArgs(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto& terminalArgs = realArgs.ContentArgs().try_as(); VERIFY_IS_NOT_NULL(terminalArgs); auto cmdline = terminalArgs.ToCommandline(); Log::Comment(NoThrowString().Format( diff --git a/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp index bd17076bc94..50462a72346 100644 --- a/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp @@ -1390,11 +1390,13 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } Log::Comment(L"Note that we're skipping ctrl+B, since that doesn't have `keys` set."); @@ -1407,11 +1409,13 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } { const KeyChord kc{ true, false, false, false, static_cast('D'), 0 }; @@ -1421,11 +1425,13 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } { const KeyChord kc{ true, false, false, false, static_cast('E'), 0 }; @@ -1435,11 +1441,13 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } { const KeyChord kc{ true, false, false, false, static_cast('F'), 0 }; @@ -1449,11 +1457,13 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } Log::Comment(L"Now verify the commands"); @@ -1478,11 +1488,13 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); } { // This was renamed to null (aka removed from the name map) in F. So this does not exist. diff --git a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp index c5f4be49045..afe22c8930c 100644 --- a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp @@ -27,6 +27,7 @@ namespace SettingsModelUnitTests TEST_METHOD(ManyKeysSameAction); TEST_METHOD(LayerKeybindings); TEST_METHOD(HashDeduplication); + TEST_METHOD(HashContentArgs); TEST_METHOD(UnbindKeybindings); TEST_METHOD(LayerScancodeKeybindings); TEST_METHOD(TestExplicitUnbind); @@ -161,6 +162,32 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(1u, actionMap->_ActionMap.size()); } + void KeyBindingsTests::HashContentArgs() + { + Log::Comment(L"These are two actions with different content args. They should have different hashes for their terminal args."); + const auto actionMap = winrt::make_self(); + actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", } , "keys": ["ctrl+c"] } ])"), OriginTag::None); + actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", "index": 0 } , "keys": ["ctrl+shift+c"] } ])"), OriginTag::None); + VERIFY_ARE_EQUAL(2u, actionMap->_ActionMap.size()); + + KeyChord ctrlC{ VirtualKeyModifiers::Control, static_cast('C'), 0 }; + KeyChord ctrlShiftC{ VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift, static_cast('C'), 0 }; + + auto hashFromKey = [&](auto& kc) { + auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc); + VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); + const auto& realArgs = actionAndArgs.Args().as(); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + return terminalArgs.Hash(); + }; + + const auto hashOne = hashFromKey(ctrlC); + const auto hashTwo = hashFromKey(ctrlShiftC); + + VERIFY_ARE_NOT_EQUAL(hashOne, hashTwo); + } + void KeyBindingsTests::UnbindKeybindings() { const std::string bindings0String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" }; @@ -318,8 +345,10 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); const auto& realArgs = actionAndArgs.Args().as(); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_NULL(realArgs.TerminalArgs().ProfileIndex()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_NULL(terminalArgs.ProfileIndex()); } { Log::Comment(NoThrowString().Format( @@ -329,9 +358,11 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); const auto& realArgs = actionAndArgs.Args().as(); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex()); - VERIFY_ARE_EQUAL(0, realArgs.TerminalArgs().ProfileIndex().Value()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_NOT_NULL(terminalArgs.ProfileIndex()); + VERIFY_ARE_EQUAL(0, terminalArgs.ProfileIndex().Value()); } { Log::Comment(NoThrowString().Format( @@ -342,9 +373,11 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); const auto& realArgs = actionAndArgs.Args().as(); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex()); - VERIFY_ARE_EQUAL(11, realArgs.TerminalArgs().ProfileIndex().Value()); + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_NOT_NULL(terminalArgs.ProfileIndex()); + VERIFY_ARE_EQUAL(11, terminalArgs.ProfileIndex().Value()); } { diff --git a/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp index f45fd1624f7..051d722f941 100644 --- a/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp @@ -320,14 +320,16 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); @@ -341,15 +343,17 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", terminalArgs.Profile()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); @@ -363,15 +367,17 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); @@ -385,15 +391,17 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, profile.Guid()); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); @@ -407,15 +415,17 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"foo.exe", terminalArgs.Commandline()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); // This action specified a command but no profile; it gets reassigned to the base profile VERIFY_ARE_EQUAL(settings->ProfileDefaults(), profile); @@ -430,16 +440,18 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); - VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); + VERIFY_ARE_EQUAL(L"foo.exe", terminalArgs.Commandline()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); @@ -452,14 +464,16 @@ namespace SettingsModelUnitTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); @@ -472,15 +486,17 @@ namespace SettingsModelUnitTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_FALSE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"c:\\foo", terminalArgs.StartingDirectory()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); @@ -494,16 +510,18 @@ namespace SettingsModelUnitTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_FALSE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_TRUE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"c:\\foo", terminalArgs.StartingDirectory()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, profile.Guid()); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); @@ -517,15 +535,17 @@ namespace SettingsModelUnitTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_FALSE(terminalArgs.TabTitle().empty()); + VERIFY_IS_TRUE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"bar", terminalArgs.TabTitle()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); @@ -539,16 +559,18 @@ namespace SettingsModelUnitTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle()); - VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_TRUE(terminalArgs.Commandline().empty()); + VERIFY_IS_TRUE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_FALSE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"bar", terminalArgs.TabTitle()); + VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, profile.Guid()); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); @@ -562,18 +584,20 @@ namespace SettingsModelUnitTests const auto& realArgs = actionAndArgs.Args().try_as(); VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value - VERIFY_IS_NOT_NULL(realArgs.TerminalArgs()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().StartingDirectory().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); - VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline()); - VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory()); - VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle()); - VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); - - const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) }; + VERIFY_IS_NOT_NULL(realArgs.ContentArgs()); + const auto terminalArgs{ realArgs.ContentArgs().try_as() }; + VERIFY_IS_NOT_NULL(terminalArgs); + VERIFY_IS_FALSE(terminalArgs.Commandline().empty()); + VERIFY_IS_FALSE(terminalArgs.StartingDirectory().empty()); + VERIFY_IS_FALSE(terminalArgs.TabTitle().empty()); + VERIFY_IS_FALSE(terminalArgs.Profile().empty()); + VERIFY_ARE_EQUAL(L"foo.exe", terminalArgs.Commandline()); + VERIFY_ARE_EQUAL(L"c:\\foo", terminalArgs.StartingDirectory()); + VERIFY_ARE_EQUAL(L"bar", terminalArgs.TabTitle()); + VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); + + const auto profile{ settings->GetProfileForArgs(terminalArgs) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 0fc28a20433..a8f0961e420 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -504,11 +504,11 @@ void WindowEmperor::_finalizeSessionPersistence() const if (const auto tabArgs = args.try_as()) { - terminalArgs = tabArgs.TerminalArgs(); + terminalArgs = tabArgs.ContentArgs().try_as(); } else if (const auto paneArgs = args.try_as()) { - terminalArgs = paneArgs.TerminalArgs(); + terminalArgs = paneArgs.ContentArgs().try_as(); } if (terminalArgs) From c3f44f77303747976aea0114f7cca983326ddb48 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 5 Apr 2024 13:16:10 -0700 Subject: [PATCH 212/603] Rewrite how marks are stored & add reflow (#16937) This is pretty much a huge refactoring of how marks are stored in the buffer. Gone is the list of `ScrollMark`s in the buffer that store regions of text as points marking the ends. Those would be nigh impossible to reflow nicely. Instead, we're going to use `TextAttribute`s to store the kind of output we've got - `Prompt`, `Command`, `Output`, or, the default, `None`. Those already reflow nicely! But we also need to store things like, the exit code for the command. That's why we've now added `ScrollbarData` to `ROW`s. There's really only going to be one prompt->output on a single row. So, we only need to store one ScrollbarData per-row. When a command ends, we can just go update the mark on the row that started that command. But iterating over the whole buffer to find the next/previous prompt/command/output region sounds complicated. So, to avoid everyone needing to do some variant of that, we've added `MarkExtents` (which is literally just the same mark structure as before). TextBuffer can figure out where all the mark regions are, and hand that back to callers. This allows ControlCore to be basically unchanged. _But collecting up all the regions for all the marks sounds expensive! We need to update the scrollbar frequently, we can't just collect those up every time!_ No we can't! But we also don't need to. The scrollbar doesn't need to know where all the marks start and end and if they have commands and this and that - no. We only need to know the rows that have marks on them. So, we've now also got `ScrollMark` to represent just a mark on a scrollbar at a specific row on the buffer. We can get those quickly. * [x] I added a bunch of tests for this. * [x] I played with it and it feels good, even after a reflow (finally) * See: * #11000 * #15057 (I'm not marking this as closed. The stacked PR will close this, when I move marks to Stable) --- src/buffer/out/Marks.hpp | 91 +++ src/buffer/out/Row.cpp | 43 ++ src/buffer/out/Row.hpp | 8 + src/buffer/out/TextAttribute.cpp | 3 +- src/buffer/out/TextAttribute.hpp | 31 +- src/buffer/out/textBuffer.cpp | 442 +++++++++--- src/buffer/out/textBuffer.hpp | 63 +- src/cascadia/TerminalControl/ControlCore.cpp | 96 ++- src/cascadia/TerminalControl/ControlCore.h | 6 +- src/cascadia/TerminalControl/ICoreState.idl | 7 +- src/cascadia/TerminalControl/TermControl.cpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 94 ++- src/cascadia/TerminalCore/Terminal.hpp | 27 +- src/cascadia/TerminalCore/TerminalApi.cpp | 143 +--- .../UnitTests_Control/ControlCoreTests.cpp | 2 +- .../ConptyRoundtripTests.cpp | 638 +++++++++++++++++- src/host/VtIo.cpp | 3 +- src/host/VtIo.hpp | 2 +- src/host/getset.cpp | 3 +- src/host/globals.cpp | 4 +- src/host/globals.h | 2 +- src/host/outputStream.cpp | 19 - src/host/outputStream.hpp | 5 - src/host/ut_host/ScreenBufferTests.cpp | 176 +++++ src/host/ut_host/TextBufferTests.cpp | 134 ++++ src/terminal/adapter/ITerminalApi.hpp | 5 - src/terminal/adapter/adaptDispatch.cpp | 59 +- .../adapter/ut_adapter/adapterTest.cpp | 16 - 28 files changed, 1628 insertions(+), 496 deletions(-) create mode 100644 src/buffer/out/Marks.hpp diff --git a/src/buffer/out/Marks.hpp b/src/buffer/out/Marks.hpp new file mode 100644 index 00000000000..655416f8354 --- /dev/null +++ b/src/buffer/out/Marks.hpp @@ -0,0 +1,91 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- marks.hpp + +Abstract: +- Definitions for types that are used for "scroll marks" and shell integration + in the buffer. +- Scroll marks are identified by the existence of "ScrollbarData" on a ROW in the buffer. +- Shell integration will then also markup the buffer with special + TextAttributes, to identify regions of text as the Prompt, the Command, the + Output, etc. +- MarkExtents are used to abstract away those regions of text, so a caller + doesn't need to iterate over the buffer themselves. +--*/ + +#pragma once + +enum class MarkCategory : uint8_t +{ + Default = 0, + Error = 1, + Warning = 2, + Success = 3, + Prompt = 4 +}; + +// This is the data that's stored on each ROW, to suggest that there's something +// interesting on this row to show in the scrollbar. Also used in conjunction +// with shell integration - when a prompt is added through shell integration, +// we'll also add a scrollbar mark as a quick "bookmark" to the start of that +// command. +struct ScrollbarData +{ + MarkCategory category{ MarkCategory::Default }; + + // Scrollbar marks may have been given a color, or not. + std::optional color; + + // Prompts without an exit code haven't had a matching FTCS CommandEnd + // called yet. Any value other than 0 is an error. + std::optional exitCode; + // Future consideration: stick the literal command as a string on here, if + // we were given it with the 633;E sequence. +}; + +// Helper struct for describing the bounds of a command and it's output, +// * The Prompt is between the start & end +// * The Command is between the end & commandEnd +// * The Output is between the commandEnd & outputEnd +// +// These are not actually stored in the buffer. The buffer can produce them for +// callers, to make reasoning about regions of the buffer easier. +struct MarkExtents +{ + // Data from the row + ScrollbarData data; + + til::point start; + til::point end; // exclusive + std::optional commandEnd; + std::optional outputEnd; + + // MarkCategory category{ MarkCategory::Info }; + // Other things we may want to think about in the future are listed in + // GH#11000 + + bool HasCommand() const noexcept + { + return commandEnd.has_value() && *commandEnd != end; + } + bool HasOutput() const noexcept + { + return outputEnd.has_value() && *outputEnd != *commandEnd; + } + std::pair GetExtent() const + { + til::point realEnd{ til::coalesce_value(outputEnd, commandEnd, end) }; + return std::make_pair(start, realEnd); + } +}; + +// Another helper, for when callers would like to know just about the data of +// the scrollbar, but don't actually need all the extents of prompts. +struct ScrollMark +{ + til::CoordType row{ 0 }; + ScrollbarData data; +}; diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 4722dc0dfc4..8e318da41ef 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -230,6 +230,7 @@ void ROW::Reset(const TextAttribute& attr) noexcept _lineRendition = LineRendition::SingleWidth; _wrapForced = false; _doubleBytePadded = false; + _promptData = std::nullopt; _init(); } @@ -1181,3 +1182,45 @@ CharToColumnMapper ROW::_createCharToColumnMapper(ptrdiff_t offset) const noexce const auto guessedColumn = gsl::narrow_cast(clamp(offset, 0, _columnCount)); return CharToColumnMapper{ _chars.data(), _charOffsets.data(), lastChar, guessedColumn }; } + +const std::optional& ROW::GetScrollbarData() const noexcept +{ + return _promptData; +} +void ROW::SetScrollbarData(std::optional data) noexcept +{ + _promptData = data; +} + +void ROW::StartPrompt() noexcept +{ + if (!_promptData.has_value()) + { + // You'd be tempted to write: + // + // _promptData = ScrollbarData{ + // .category = MarkCategory::Prompt, + // .color = std::nullopt, + // .exitCode = std::nullopt, + // }; + // + // But that's not very optimal! Read this thread for a breakdown of how + // weird std::optional can be some times: + // + // https://github.com/microsoft/terminal/pull/16937#discussion_r1553660833 + + _promptData.emplace(MarkCategory::Prompt); + } +} + +void ROW::EndOutput(std::optional error) noexcept +{ + if (_promptData.has_value()) + { + _promptData->exitCode = error; + if (error.has_value()) + { + _promptData->category = *error == 0 ? MarkCategory::Success : MarkCategory::Error; + } + } +} diff --git a/src/buffer/out/Row.hpp b/src/buffer/out/Row.hpp index 197343df6d8..d26fc87e7ed 100644 --- a/src/buffer/out/Row.hpp +++ b/src/buffer/out/Row.hpp @@ -8,6 +8,7 @@ #include "LineRendition.hpp" #include "OutputCell.hpp" #include "OutputCellIterator.hpp" +#include "Marks.hpp" class ROW; class TextBuffer; @@ -167,6 +168,11 @@ class ROW final auto AttrBegin() const noexcept { return _attr.begin(); } auto AttrEnd() const noexcept { return _attr.end(); } + const std::optional& GetScrollbarData() const noexcept; + void SetScrollbarData(std::optional data) noexcept; + void StartPrompt() noexcept; + void EndOutput(std::optional error) noexcept; + #ifdef UNIT_TESTING friend constexpr bool operator==(const ROW& a, const ROW& b) noexcept; friend class RowTests; @@ -299,6 +305,8 @@ class ROW final bool _wrapForced = false; // Occurs when the user runs out of text to support a double byte character and we're forced to the next line bool _doubleBytePadded = false; + + std::optional _promptData = std::nullopt; }; #ifdef UNIT_TESTING diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index d5b0e5e4a5a..65799d1d6f5 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -7,7 +7,7 @@ // Keeping TextColor compact helps us keeping TextAttribute compact, // which in turn ensures that our buffer memory usage is low. -static_assert(sizeof(TextAttribute) == 16); +static_assert(sizeof(TextAttribute) == 18); static_assert(alignof(TextAttribute) == 2); // Ensure that we can memcpy() and memmove() the struct for performance. static_assert(std::is_trivially_copyable_v); @@ -434,4 +434,5 @@ void TextAttribute::SetStandardErase() noexcept { _attrs = CharacterAttributes::Normal; _hyperlinkId = 0; + _markKind = MarkKind::None; } diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index 4fe8c9e637e..44d8563176a 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -38,6 +38,15 @@ enum class UnderlineStyle Max = DashedUnderlined }; +// We only need a few bits, but uint8_t apparently doesn't satisfy std::has_unique_object_representations_v +enum class MarkKind : uint16_t +{ + None = 0, + Prompt = 1, + Command = 2, + Output = 3, +}; + class TextAttribute final { public: @@ -46,7 +55,8 @@ class TextAttribute final _foreground{}, _background{}, _hyperlinkId{ 0 }, - _underlineColor{} + _underlineColor{}, + _markKind{ MarkKind::None } { } @@ -55,7 +65,8 @@ class TextAttribute final _foreground{ gsl::at(s_legacyForegroundColorMap, wLegacyAttr & FG_ATTRS) }, _background{ gsl::at(s_legacyBackgroundColorMap, (wLegacyAttr & BG_ATTRS) >> 4) }, _hyperlinkId{ 0 }, - _underlineColor{} + _underlineColor{}, + _markKind{ MarkKind::None } { } @@ -66,7 +77,8 @@ class TextAttribute final _foreground{ rgbForeground }, _background{ rgbBackground }, _hyperlinkId{ 0 }, - _underlineColor{ rgbUnderline } + _underlineColor{ rgbUnderline }, + _markKind{ MarkKind::None } { } @@ -75,7 +87,8 @@ class TextAttribute final _foreground{ foreground }, _background{ background }, _hyperlinkId{ hyperlinkId }, - _underlineColor{ underlineColor } + _underlineColor{ underlineColor }, + _markKind{ MarkKind::None } { } @@ -135,6 +148,15 @@ class TextAttribute final return _attrs; } + constexpr void SetMarkAttributes(const MarkKind attrs) noexcept + { + _markKind = attrs; + } + constexpr MarkKind GetMarkAttributes() const noexcept + { + return _markKind; + } + bool IsHyperlink() const noexcept; TextColor GetForeground() const noexcept; @@ -202,6 +224,7 @@ class TextAttribute final TextColor _foreground; // sizeof: 4, alignof: 1 TextColor _background; // sizeof: 4, alignof: 1 TextColor _underlineColor; // sizeof: 4, alignof: 1 + MarkKind _markKind; // sizeof: 2, alignof: 1 #ifdef UNIT_TESTING friend class TextBufferTests; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 52df2009eaa..713cf2a09a9 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1178,7 +1178,6 @@ void TextBuffer::ClearScrollback(const til::CoordType start, const til::CoordTyp GetMutableRowByOffset(y).Reset(_initialAttributes); } - ScrollMarks(-start); ClearMarksInRange(til::point{ 0, height }, til::point{ _width, _height }); } @@ -2869,6 +2868,18 @@ void TextBuffer::Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const View oldRowLimit = std::max(oldRowLimit, oldCursorPos.x + 1); } + // Immediately copy this mark over to our new row. The positions of the + // marks themselves will be preserved, since they're just text + // attributes. But the "bookmark" needs to get moved to the new row too. + // * If a row wraps as it reflows, that's fine - we want to leave the + // mark on the row it started on. + // * If the second row of a wrapped row had a mark, and it de-flows onto a + // single row, that's fine! The mark was on that logical row. + if (oldRow.GetScrollbarData().has_value()) + { + newBuffer.GetMutableRowByOffset(newY).SetScrollbarData(oldRow.GetScrollbarData()); + } + til::CoordType oldX = 0; // Copy oldRow into newBuffer until oldRow has been fully consumed. @@ -2988,9 +2999,6 @@ void TextBuffer::Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const View assert(newCursorPos.y >= 0 && newCursorPos.y < newHeight); newCursor.SetSize(oldCursor.GetSize()); newCursor.SetPosition(newCursorPos); - - newBuffer._marks = oldBuffer._marks; - newBuffer._trimMarksOutsideBuffer(); } // Method Description: @@ -3140,9 +3148,82 @@ std::vector TextBuffer::SearchText(const std::wstring_view& nee return results; } -const std::vector& TextBuffer::GetMarks() const noexcept +// Collect up all the rows that were marked, and the data marked on that row. +// This is what should be used for hot paths, like updating the scrollbar. +std::vector TextBuffer::GetMarkRows() const { - return _marks; + std::vector marks; + const auto bottom = _estimateOffsetOfLastCommittedRow(); + for (auto y = 0; y <= bottom; y++) + { + const auto& row = GetRowByOffset(y); + const auto& data{ row.GetScrollbarData() }; + if (data.has_value()) + { + marks.emplace_back(y, *data); + } + } + return marks; +} + +// Get all the regions for all the shell integration marks in the buffer. +// Marks will be returned in top-down order. +// +// This possibly iterates over every run in the buffer, so don't do this on a +// hot path. Just do this once per user input, if at all possible. +// +// Use `limit` to control how many you get, _starting from the bottom_. (e.g. +// limit=1 will just give you the "most recent mark"). +std::vector TextBuffer::GetMarkExtents(size_t limit) const +{ + if (limit == 0u) + { + return {}; + } + + std::vector marks{}; + const auto bottom = _estimateOffsetOfLastCommittedRow(); + auto lastPromptY = bottom; + for (auto promptY = bottom; promptY >= 0; promptY--) + { + const auto& currRow = GetRowByOffset(promptY); + auto& rowPromptData = currRow.GetScrollbarData(); + if (!rowPromptData.has_value()) + { + // This row didn't start a prompt, don't even look here. + continue; + } + + // Future thought! In #11000 & #14792, we considered the possibility of + // scrolling to only an error mark, or something like that. Perhaps in + // the future, add a customizable filter that's a set of types of mark + // to include? + // + // For now, skip any "Default" marks, since those came from the UI. We + // just want the ones that correspond to shell integration. + + if (rowPromptData->category == MarkCategory::Default) + { + continue; + } + + // This row did start a prompt! Find the prompt that starts here. + // Presumably, no rows below us will have prompts, so pass in the last + // row with text as the bottom + marks.push_back(_scrollMarkExtentForRow(promptY, lastPromptY)); + + // operator>=(T, optional) will return true if the optional is + // nullopt, unfortunately. + if (marks.size() >= limit) + { + break; + } + + lastPromptY = promptY; + } + + std::reverse(marks.begin(), marks.end()); + return marks; } // Remove all marks between `start` & `end`, inclusive. @@ -3150,115 +3231,318 @@ void TextBuffer::ClearMarksInRange( const til::point start, const til::point end) { - auto inRange = [&start, &end](const ScrollMark& m) { - return (m.start >= start && m.start <= end) || - (m.end >= start && m.end <= end); - }; + auto top = std::clamp(std::min(start.y, end.y), 0, _height - 1); + auto bottom = std::clamp(std::max(start.y, end.y), 0, _estimateOffsetOfLastCommittedRow()); - _marks.erase(std::remove_if(_marks.begin(), - _marks.end(), - inRange), - _marks.end()); + for (auto y = top; y <= bottom; y++) + { + auto& row = GetMutableRowByOffset(y); + auto& runs = row.Attributes().runs(); + row.SetScrollbarData(std::nullopt); + for (auto& [attr, length] : runs) + { + attr.SetMarkAttributes(MarkKind::None); + } + } } -void TextBuffer::ClearAllMarks() noexcept +void TextBuffer::ClearAllMarks() { - _marks.clear(); + ClearMarksInRange({ 0, 0 }, { _width - 1, _height - 1 }); } -// Adjust all the marks in the y-direction by `delta`. Positive values move the -// marks down (the positive y direction). Negative values move up. This will -// trim marks that are no longer have a start in the bounds of the buffer -void TextBuffer::ScrollMarks(const int delta) +// Collect up the extent of the prompt and possibly command and output for the +// mark that starts on this row. +MarkExtents TextBuffer::_scrollMarkExtentForRow(const til::CoordType rowOffset, + const til::CoordType bottomInclusive) const { - for (auto& mark : _marks) - { - mark.start.y += delta; + const auto& startRow = GetRowByOffset(rowOffset); + const auto& rowPromptData = startRow.GetScrollbarData(); + assert(rowPromptData.has_value()); - // If the mark had sub-regions, then move those pointers too - if (mark.commandEnd.has_value()) + MarkExtents mark{ + .data = *rowPromptData, + }; + + bool startedPrompt = false; + bool startedCommand = false; + bool startedOutput = false; + MarkKind lastMarkKind = MarkKind::Output; + + const auto endThisMark = [&](auto x, auto y) { + if (startedOutput) + { + mark.outputEnd = til::point{ x, y }; + } + if (!startedOutput && startedCommand) { - (*mark.commandEnd).y += delta; + mark.commandEnd = til::point{ x, y }; } - if (mark.outputEnd.has_value()) + if (!startedCommand) { - (*mark.outputEnd).y += delta; + mark.end = til::point{ x, y }; } + }; + auto x = 0; + auto y = rowOffset; + til::point lastMarkedText{ x, y }; + for (; y <= bottomInclusive; y++) + { + // Now we need to iterate over text attributes. We need to find a + // segment of Prompt attributes, we'll skip those. Then there should be + // Command attributes. Collect up all of those, till we get to the next + // Output attribute. + + const auto& row = GetRowByOffset(y); + const auto runs = row.Attributes().runs(); + x = 0; + for (const auto& [attr, length] : runs) + { + const auto nextX = gsl::narrow_cast(x + length); + const auto markKind{ attr.GetMarkAttributes() }; + + if (markKind != MarkKind::None) + { + lastMarkedText = { nextX, y }; + + if (markKind == MarkKind::Prompt) + { + if (startedCommand || startedOutput) + { + // we got a _new_ prompt. bail out. + break; + } + if (!startedPrompt) + { + // We entered the first prompt here + startedPrompt = true; + mark.start = til::point{ x, y }; + } + endThisMark(lastMarkedText.x, lastMarkedText.y); + } + else if (markKind == MarkKind::Command && startedPrompt) + { + startedCommand = true; + endThisMark(lastMarkedText.x, lastMarkedText.y); + } + else if ((markKind == MarkKind::Output) && startedPrompt) + { + startedOutput = true; + if (!mark.commandEnd.has_value()) + { + // immediately just end the command at the start here, so we can treat this whole run as output + mark.commandEnd = mark.end; + startedCommand = true; + } + + endThisMark(lastMarkedText.x, lastMarkedText.y); + } + // Otherwise, we've changed from any state -> any state, and it doesn't really matter. + lastMarkKind = markKind; + } + // advance to next run of text + x = nextX; + } + // we went over all the runs in this row, but we're not done yet. Keep iterating on the next row. } - _trimMarksOutsideBuffer(); -} -// Method Description: -// - Add a mark to our list of marks, and treat it as the active "prompt". For -// the sake of shell integration, we need to know which mark represents the -// current prompt/command/output. Internally, we'll always treat the _last_ -// mark in the list as the current prompt. -// Arguments: -// - m: the mark to add. -void TextBuffer::StartPromptMark(const ScrollMark& m) -{ - _marks.push_back(m); + // Okay, we're at the bottom of the buffer? Yea, just return what we found. + if (!startedCommand) + { + // If we never got to a Command or Output run, then we never set .end. + // Set it here to the last run we saw. + endThisMark(lastMarkedText.x, lastMarkedText.y); + } + return mark; } -// Method Description: -// - Add a mark to our list of marks. Don't treat this as the active prompt. -// This should be used for marks created by the UI or from other user input. -// By inserting at the start of the list, we can separate out marks that were -// generated by client programs vs ones created by the user. -// Arguments: -// - m: the mark to add. -void TextBuffer::AddMark(const ScrollMark& m) + +std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const { - _marks.insert(_marks.begin(), m); + std::wstring commandBuilder; + MarkKind lastMarkKind = MarkKind::Prompt; + for (auto y = rowOffset; y <= bottomInclusive; y++) + { + // Now we need to iterate over text attributes. We need to find a + // segment of Prompt attributes, we'll skip those. Then there should be + // Command attributes. Collect up all of those, till we get to the next + // Output attribute. + + const auto& row = GetRowByOffset(y); + const auto runs = row.Attributes().runs(); + auto x = 0; + for (const auto& [attr, length] : runs) + { + const auto nextX = gsl::narrow_cast(x + length); + const auto markKind{ attr.GetMarkAttributes() }; + if (markKind != lastMarkKind) + { + if (lastMarkKind == MarkKind::Command) + { + // We've changed away from being in a command. We're done. + // Return what we've gotten so far. + return commandBuilder; + } + // Otherwise, we've changed from any state -> any state, and it doesn't really matter. + lastMarkKind = markKind; + } + + if (markKind == MarkKind::Command) + { + commandBuilder += row.GetText(x, nextX); + } + // advance to next run of text + x = nextX; + } + // we went over all the runs in this row, but we're not done yet. Keep iterating on the next row. + } + // Okay, we're at the bottom of the buffer? Yea, just return what we found. + return commandBuilder; } -void TextBuffer::_trimMarksOutsideBuffer() +std::wstring TextBuffer::CurrentCommand() const { - const til::CoordType height = _height; - std::erase_if(_marks, [height](const auto& m) { - return (m.start.y < 0) || (m.start.y >= height); - }); + auto promptY = GetCursor().GetPosition().y; + for (; promptY >= 0; promptY--) + { + const auto& currRow = GetRowByOffset(promptY); + auto& rowPromptData = currRow.GetScrollbarData(); + if (!rowPromptData.has_value()) + { + // This row didn't start a prompt, don't even look here. + continue; + } + + // This row did start a prompt! Find the prompt that starts here. + // Presumably, no rows below us will have prompts, so pass in the last + // row with text as the bottom + return _commandForRow(promptY, _estimateOffsetOfLastCommittedRow()); + } + return L""; } -std::wstring_view TextBuffer::CurrentCommand() const +std::vector TextBuffer::Commands() const { - if (_marks.size() == 0) + std::vector commands{}; + const auto bottom = _estimateOffsetOfLastCommittedRow(); + auto lastPromptY = bottom; + for (auto promptY = bottom; promptY >= 0; promptY--) { - return L""; + const auto& currRow = GetRowByOffset(promptY); + auto& rowPromptData = currRow.GetScrollbarData(); + if (!rowPromptData.has_value()) + { + // This row didn't start a prompt, don't even look here. + continue; + } + + // This row did start a prompt! Find the prompt that starts here. + // Presumably, no rows below us will have prompts, so pass in the last + // row with text as the bottom + auto foundCommand = _commandForRow(promptY, lastPromptY); + if (!foundCommand.empty()) + { + commands.emplace_back(std::move(foundCommand)); + } + lastPromptY = promptY; } + std::reverse(commands.begin(), commands.end()); + return commands; +} - const auto& curr{ _marks.back() }; - const auto& start{ curr.end }; - const auto& end{ GetCursor().GetPosition() }; +void TextBuffer::StartPrompt() +{ + const auto currentRowOffset = GetCursor().GetPosition().y; + auto& currentRow = GetMutableRowByOffset(currentRowOffset); + currentRow.StartPrompt(); - const auto line = start.y; - const auto& row = GetRowByOffset(line); - return row.GetText(start.x, end.x); + _currentAttributes.SetMarkAttributes(MarkKind::Prompt); } -void TextBuffer::SetCurrentPromptEnd(const til::point pos) noexcept +bool TextBuffer::_createPromptMarkIfNeeded() { - if (_marks.empty()) + // We might get here out-of-order, without seeing a StartPrompt (FTCS A) + // first. Since StartPrompt actually sets up the prompt mark on the ROW, we + // need to do a bit of extra work here to start a new mark (if the last one + // wasn't in an appropriate state). + + const auto mostRecentMarks = GetMarkExtents(1u); + if (!mostRecentMarks.empty()) { - return; + const auto& mostRecentMark = til::at(mostRecentMarks, 0); + if (!mostRecentMark.HasOutput()) + { + // The most recent command mark _didn't_ have output yet. Great! + // we'll leave it alone, and just start treating text as Command or Output. + return false; + } + + // The most recent command mark had output. That suggests that either: + // * shell integration wasn't enabled (but the user would still + // like lines with enters to be marked as prompts) + // * or we're in the middle of a command that's ongoing. + + // If it does have a command, then we're still in the output of + // that command. + // --> the current attrs should already be set to Output. + if (mostRecentMark.HasCommand()) + { + return false; + } + // If the mark doesn't have any command - then we know we're + // playing silly games with just marking whole lines as prompts, + // then immediately going to output. + // --> Below, we'll add a new mark to this row. } - auto& curr{ _marks.back() }; - curr.end = pos; + + // There were no marks at all! + // --> add a new mark to this row, set all the attrs in this row + // to be Prompt, and set the current attrs to Output. + + auto& row = GetMutableRowByOffset(GetCursor().GetPosition().y); + row.StartPrompt(); + return true; +} + +bool TextBuffer::StartCommand() +{ + const auto createdMark = _createPromptMarkIfNeeded(); + _currentAttributes.SetMarkAttributes(MarkKind::Command); + return createdMark; +} +bool TextBuffer::StartOutput() +{ + const auto createdMark = _createPromptMarkIfNeeded(); + _currentAttributes.SetMarkAttributes(MarkKind::Output); + return createdMark; } -void TextBuffer::SetCurrentCommandEnd(const til::point pos) noexcept + +// Find the row above the cursor where this most recent prompt started, and set +// the exit code on that row's scroll mark. +void TextBuffer::EndCurrentCommand(std::optional error) { - if (_marks.empty()) + _currentAttributes.SetMarkAttributes(MarkKind::None); + + for (auto y = GetCursor().GetPosition().y; y >= 0; y--) { - return; + auto& currRow = GetMutableRowByOffset(y); + auto& rowPromptData = currRow.GetScrollbarData(); + if (rowPromptData.has_value()) + { + currRow.EndOutput(error); + return; + } } - auto& curr{ _marks.back() }; - curr.commandEnd = pos; } -void TextBuffer::SetCurrentOutputEnd(const til::point pos, ::MarkCategory category) noexcept + +void TextBuffer::SetScrollbarData(ScrollbarData mark, til::CoordType y) +{ + auto& row = GetMutableRowByOffset(y); + row.SetScrollbarData(mark); +} +void TextBuffer::ManuallyMarkRowAsPrompt(til::CoordType y) { - if (_marks.empty()) + auto& row = GetMutableRowByOffset(y); + for (auto& [attr, len] : row.Attributes().runs()) { - return; + attr.SetMarkAttributes(MarkKind::Prompt); } - auto& curr{ _marks.back() }; - curr.outputEnd = pos; - curr.category = category; } diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 8093aea9af8..79872cea0c4 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -66,41 +66,6 @@ namespace Microsoft::Console::Render class Renderer; } -enum class MarkCategory -{ - Prompt = 0, - Error = 1, - Warning = 2, - Success = 3, - Info = 4 -}; -struct ScrollMark -{ - std::optional color; - til::point start; - til::point end; // exclusive - std::optional commandEnd; - std::optional outputEnd; - - MarkCategory category{ MarkCategory::Info }; - // Other things we may want to think about in the future are listed in - // GH#11000 - - bool HasCommand() const noexcept - { - return commandEnd.has_value() && *commandEnd != end; - } - bool HasOutput() const noexcept - { - return outputEnd.has_value() && *outputEnd != *commandEnd; - } - std::pair GetExtent() const - { - til::point realEnd{ til::coalesce_value(outputEnd, commandEnd, end) }; - return std::make_pair(til::point{ start }, realEnd); - } -}; - class TextBuffer final { public: @@ -332,16 +297,19 @@ class TextBuffer final std::vector SearchText(const std::wstring_view& needle, bool caseInsensitive) const; std::vector SearchText(const std::wstring_view& needle, bool caseInsensitive, til::CoordType rowBeg, til::CoordType rowEnd) const; - const std::vector& GetMarks() const noexcept; + // Mark handling + std::vector GetMarkRows() const; + std::vector GetMarkExtents(size_t limit = SIZE_T_MAX) const; void ClearMarksInRange(const til::point start, const til::point end); - void ClearAllMarks() noexcept; - void ScrollMarks(const int delta); - void StartPromptMark(const ScrollMark& m); - void AddMark(const ScrollMark& m); - void SetCurrentPromptEnd(const til::point pos) noexcept; - void SetCurrentCommandEnd(const til::point pos) noexcept; - void SetCurrentOutputEnd(const til::point pos, ::MarkCategory category) noexcept; - std::wstring_view CurrentCommand() const; + void ClearAllMarks(); + std::wstring CurrentCommand() const; + std::vector Commands() const; + void StartPrompt(); + bool StartCommand(); + bool StartOutput(); + void EndCurrentCommand(std::optional error); + void SetScrollbarData(ScrollbarData mark, til::CoordType y); + void ManuallyMarkRowAsPrompt(til::CoordType y); private: void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes); @@ -366,7 +334,11 @@ class TextBuffer final til::point _GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const; til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const; void _PruneHyperlinks(); - void _trimMarksOutsideBuffer(); + + std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const; + MarkExtents _scrollMarkExtentForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const; + bool _createPromptMarkIfNeeded(); + std::tuple _RowCopyHelper(const CopyRequest& req, const til::CoordType iRow, const ROW& row) const; static void _AppendRTFText(std::string& contentBuilder, const std::wstring_view& text); @@ -439,7 +411,6 @@ class TextBuffer final uint64_t _lastMutationId = 0; Cursor _cursor; - std::vector _marks; bool _isActiveBuffer = false; #ifdef UNIT_TESTING diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 4c36320d9b2..fbe2eaafb77 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1928,7 +1928,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto cursorPos{ _terminal->GetCursorPosition() }; // Does the current buffer line have a mark on it? - const auto& marks{ _terminal->GetScrollMarks() }; + const auto& marks{ _terminal->GetMarkExtents() }; if (!marks.empty()) { const auto& last{ marks.back() }; @@ -2144,32 +2144,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto& textBuffer = _terminal->GetTextBuffer(); std::vector commands; - - for (const auto& mark : _terminal->GetScrollMarks()) + const auto bufferCommands{ textBuffer.Commands() }; + for (const auto& commandInBuffer : bufferCommands) { - // The command text is between the `end` (which denotes the end of - // the prompt) and the `commandEnd`. - bool markHasCommand = mark.commandEnd.has_value() && - mark.commandEnd != mark.end; - if (!markHasCommand) - { - continue; - } - - // Get the text of the command - const auto line = mark.end.y; - const auto& row = textBuffer.GetRowByOffset(line); - const auto commandText = row.GetText(mark.end.x, mark.commandEnd->x); - - // Trim off trailing spaces. - const auto strEnd = commandText.find_last_not_of(UNICODE_SPACE); + const auto strEnd = commandInBuffer.find_last_not_of(UNICODE_SPACE); if (strEnd != std::string::npos) { - const auto trimmed = commandText.substr(0, strEnd + 1); + const auto trimmed = commandInBuffer.substr(0, strEnd + 1); commands.push_back(winrt::hstring{ trimmed }); } } + auto context = winrt::make_self(std::move(commands)); context->CurrentCommandline(winrt::hstring{ _terminal->CurrentCommand() }); @@ -2381,20 +2367,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation _owningHwnd = owner; } + // This one is fairly hot! it gets called every time we redraw the scrollbar + // marks, which is frequently. Fortunately, we don't need to bother with + // collecting up the actual extents of the marks in here - we just need the + // rows they start on. Windows::Foundation::Collections::IVector ControlCore::ScrollMarks() const { const auto lock = _terminal->LockForReading(); - const auto& internalMarks = _terminal->GetScrollMarks(); + const auto& markRows = _terminal->GetMarkRows(); std::vector v; - v.reserve(internalMarks.size()); + v.reserve(markRows.size()); - for (const auto& mark : internalMarks) + for (const auto& mark : markRows) { v.emplace_back( - mark.start.to_core_point(), - mark.end.to_core_point(), - OptionalFromColor(_terminal->GetColorForMark(mark))); + mark.row, + OptionalFromColor(_terminal->GetColorForMark(mark.data))); } return winrt::single_threaded_vector(std::move(v)); @@ -2403,26 +2392,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::AddMark(const Control::ScrollMark& mark) { const auto lock = _terminal->LockForReading(); - ::ScrollMark m{}; + ::ScrollbarData m{}; if (mark.Color.HasValue) { m.color = til::color{ mark.Color.Color }; } + const auto row = (_terminal->IsSelectionActive()) ? + _terminal->GetSelectionAnchor().y : + _terminal->GetTextBuffer().GetCursor().GetPosition().y; - if (_terminal->IsSelectionActive()) - { - m.start = til::point{ _terminal->GetSelectionAnchor() }; - m.end = til::point{ _terminal->GetSelectionEnd() }; - } - else - { - m.start = m.end = til::point{ _terminal->GetTextBuffer().GetCursor().GetPosition() }; - } - - // The version of this that only accepts a ScrollMark will automatically - // set the start & end to the cursor position. - _terminal->AddMark(m, m.start, m.end, true); + _terminal->AddMarkFromUI(m, row); } void ControlCore::ClearMark() @@ -2441,9 +2421,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation { const auto lock = _terminal->LockForWriting(); const auto currentOffset = ScrollOffset(); - const auto& marks{ _terminal->GetScrollMarks() }; + const auto& marks{ _terminal->GetMarkExtents() }; - std::optional<::ScrollMark> tgt; + std::optional<::MarkExtents> tgt; switch (direction) { @@ -2557,8 +2537,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation const til::point start = _terminal->IsSelectionActive() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) : _terminal->GetTextBuffer().GetCursor().GetPosition(); - std::optional<::ScrollMark> nearest{ std::nullopt }; - const auto& marks{ _terminal->GetScrollMarks() }; + std::optional<::MarkExtents> nearest{ std::nullopt }; + const auto& marks{ _terminal->GetMarkExtents() }; // Early return so we don't have to check for the validity of `nearest` below after the loop exits. if (marks.empty()) @@ -2599,8 +2579,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation const til::point start = _terminal->IsSelectionActive() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) : _terminal->GetTextBuffer().GetCursor().GetPosition(); - std::optional<::ScrollMark> nearest{ std::nullopt }; - const auto& marks{ _terminal->GetScrollMarks() }; + std::optional<::MarkExtents> nearest{ std::nullopt }; + const auto& marks{ _terminal->GetMarkExtents() }; static constexpr til::point worst{ til::CoordTypeMax, til::CoordTypeMax }; til::point bestDistance{ worst }; @@ -2676,8 +2656,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_contextMenuSelectMark( const til::point& pos, - bool (*filter)(const ::ScrollMark&), - til::point_span (*getSpan)(const ::ScrollMark&)) + bool (*filter)(const ::MarkExtents&), + til::point_span (*getSpan)(const ::MarkExtents&)) { const auto lock = _terminal->LockForWriting(); @@ -2686,7 +2666,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return; } - const auto& marks{ _terminal->GetScrollMarks() }; + const auto& marks{ _terminal->GetMarkExtents() }; + for (auto&& m : marks) { // If the caller gave us a way to filter marks, check that now. @@ -2712,20 +2693,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _contextMenuSelectMark( _contextMenuBufferPosition, - [](const ::ScrollMark& m) -> bool { return !m.HasCommand(); }, - [](const ::ScrollMark& m) { return til::point_span{ m.end, *m.commandEnd }; }); + [](const ::MarkExtents& m) -> bool { return !m.HasCommand(); }, + [](const ::MarkExtents& m) { return til::point_span{ m.end, *m.commandEnd }; }); } void ControlCore::ContextMenuSelectOutput() { _contextMenuSelectMark( _contextMenuBufferPosition, - [](const ::ScrollMark& m) -> bool { return !m.HasOutput(); }, - [](const ::ScrollMark& m) { return til::point_span{ *m.commandEnd, *m.outputEnd }; }); + [](const ::MarkExtents& m) -> bool { return !m.HasOutput(); }, + [](const ::MarkExtents& m) { return til::point_span{ *m.commandEnd, *m.outputEnd }; }); } bool ControlCore::_clickedOnMark( const til::point& pos, - bool (*filter)(const ::ScrollMark&)) + bool (*filter)(const ::MarkExtents&)) { const auto lock = _terminal->LockForWriting(); @@ -2738,13 +2719,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // DO show this if the click was on a mark with a command - const auto& marks{ _terminal->GetScrollMarks() }; + const auto& marks{ _terminal->GetMarkExtents() }; for (auto&& m : marks) { if (filter && filter(m)) { continue; } + const auto [start, end] = m.GetExtent(); if (start <= pos && end >= pos) @@ -2765,7 +2747,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Relies on the anchor set in AnchorContextMenu return _clickedOnMark(_contextMenuBufferPosition, - [](const ::ScrollMark& m) -> bool { return !m.HasCommand(); }); + [](const ::MarkExtents& m) -> bool { return !m.HasCommand(); }); } // Method Description: @@ -2774,6 +2756,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Relies on the anchor set in AnchorContextMenu return _clickedOnMark(_contextMenuBufferPosition, - [](const ::ScrollMark& m) -> bool { return !m.HasOutput(); }); + [](const ::MarkExtents& m) -> bool { return !m.HasOutput(); }); } } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index ee0d68f6a8a..9b6acfcc8e1 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -411,10 +411,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _contextMenuSelectMark( const til::point& pos, - bool (*filter)(const ::ScrollMark&), - til::point_span (*getSpan)(const ::ScrollMark&)); + bool (*filter)(const ::MarkExtents&), + til::point_span (*getSpan)(const ::MarkExtents&)); - bool _clickedOnMark(const til::point& pos, bool (*filter)(const ::ScrollMark&)); + bool _clickedOnMark(const til::point& pos, bool (*filter)(const ::MarkExtents&)); inline bool _IsClosing() const noexcept { diff --git a/src/cascadia/TerminalControl/ICoreState.idl b/src/cascadia/TerminalControl/ICoreState.idl index 0073b70788a..a3333473c28 100644 --- a/src/cascadia/TerminalControl/ICoreState.idl +++ b/src/cascadia/TerminalControl/ICoreState.idl @@ -13,11 +13,8 @@ namespace Microsoft.Terminal.Control struct ScrollMark { - // There are other members of DispatchTypes::ScrollMark, but these are - // all we need to expose up and set downwards currently. Additional - // members can be bubbled as necessary. - Microsoft.Terminal.Core.Point Start; - Microsoft.Terminal.Core.Point End; // exclusive + // Additional members can be bubbled as necessary. + Int32 Row; Microsoft.Terminal.Core.OptionalColor Color; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index ec9807dcd08..06f2ef89709 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -369,7 +369,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { for (const auto& m : marks) { - const auto row = m.Start.Y; + const auto row = m.Row; const til::color color{ m.Color.Color }; const auto base = dataAt(row); drawPip(base, color); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 4a6c1187da5..9d3dd1d1097 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -397,6 +397,9 @@ try proposedTop = ::base::ClampSub(proposedTop, ::base::ClampSub(proposedBottom, bufferSize.height)); } + // Keep the cursor in the mutable viewport + proposedTop = std::min(proposedTop, newCursorPos.y); + _mutableViewport = Viewport::FromDimensions({ 0, proposedTop }, viewportSize); _mainBuffer.swap(newTextBuffer); @@ -720,20 +723,26 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s // Then treat this line like it's a prompt mark. if (_autoMarkPrompts && vkey == VK_RETURN && !_inAltBuffer()) { - // * If we have a current prompt: - // - Then we did know that the prompt started, (we may have also - // already gotten a MarkCommandStart sequence). The user has pressed - // enter, and we're treating that like the prompt has now ended. - // - Perform a FTCS_COMMAND_EXECUTED, so that we start marking this - // as output. - // - This enables CMD to have full FTCS support, even though there's - // no point in CMD to insert a "pre exec" hook - // * Else: We don't have a prompt. We don't know anything else, but we - // can set the whole line as the prompt, no command, and start the - // command_executed now. + // We need to be a little tricky here, to try and support folks that are + // auto-marking prompts, but don't necessarily have the rest of shell + // integration enabled. + // + // We'll set the current attributes to Output, so that the output after + // here is marked as the command output. But we also need to make sure + // that a mark was started. + // We can't just check if the current row has a mark - there could be a + // multiline prompt. // - // Fortunately, MarkOutputStart will do all this logic for us! - MarkOutputStart(); + // (TextBuffer::_createPromptMarkIfNeeded does that work for us) + + const bool createdMark = _activeBuffer().StartOutput(); + if (createdMark) + { + _activeBuffer().ManuallyMarkRowAsPrompt(_activeBuffer().GetCursor().GetPosition().y); + + // This changed the scrollbar marks - raise a notification to update them + _NotifyScrollEvent(); + } } const auto keyDown = SynthesizeKeyEvent(true, 1, vkey, scanCode, ch, states.Value()); @@ -1435,36 +1444,19 @@ PointTree Terminal::_getPatterns(til::CoordType beg, til::CoordType end) const } // NOTE: This is the version of AddMark that comes from the UI. The VT api call into this too. -void Terminal::AddMark(const ScrollMark& mark, - const til::point& start, - const til::point& end, - const bool fromUi) +void Terminal::AddMarkFromUI(ScrollbarData mark, + til::CoordType y) { if (_inAltBuffer()) { return; } - ScrollMark m = mark; - m.start = start; - m.end = end; - - // If the mark came from the user adding a mark via the UI, don't make it the active prompt mark. - if (fromUi) - { - _activeBuffer().AddMark(m); - } - else - { - _activeBuffer().StartPromptMark(m); - } + _activeBuffer().SetScrollbarData(mark, y); // Tell the control that the scrollbar has somehow changed. Used as a // workaround to force the control to redraw any scrollbar marks _NotifyScrollEvent(); - - // DON'T set _currentPrompt. The VT impl will do that for you. We don't want - // UI-driven marks to set that. } void Terminal::ClearMark() @@ -1495,30 +1487,30 @@ void Terminal::ClearAllMarks() _NotifyScrollEvent(); } -const std::vector& Terminal::GetScrollMarks() const noexcept +std::vector Terminal::GetMarkRows() const +{ + // We want to return _no_ marks when we're in the alt buffer, to effectively + // hide them. + return _inAltBuffer() ? std::vector{} : _activeBuffer().GetMarkRows(); +} +std::vector Terminal::GetMarkExtents() const { - // TODO: GH#11000 - when the marks are stored per-buffer, get rid of this. // We want to return _no_ marks when we're in the alt buffer, to effectively - // hide them. We need to return a reference, so we can't just ctor an empty - // list here just for when we're in the alt buffer. - return _activeBuffer().GetMarks(); + // hide them. + return _inAltBuffer() ? std::vector{} : _activeBuffer().GetMarkExtents(); } -til::color Terminal::GetColorForMark(const ScrollMark& mark) const +til::color Terminal::GetColorForMark(const ScrollbarData& markData) const { - if (mark.color.has_value()) + if (markData.color.has_value()) { - return *mark.color; + return *markData.color; } const auto& renderSettings = GetRenderSettings(); - switch (mark.category) - { - case MarkCategory::Prompt: + switch (markData.category) { - return renderSettings.GetColorAlias(ColorAlias::DefaultForeground); - } case MarkCategory::Error: { return renderSettings.GetColorTableEntry(TextColor::BRIGHT_RED); @@ -1531,21 +1523,17 @@ til::color Terminal::GetColorForMark(const ScrollMark& mark) const { return renderSettings.GetColorTableEntry(TextColor::BRIGHT_GREEN); } + case MarkCategory::Prompt: + case MarkCategory::Default: default: - case MarkCategory::Info: { return renderSettings.GetColorAlias(ColorAlias::DefaultForeground); } } } -std::wstring_view Terminal::CurrentCommand() const +std::wstring Terminal::CurrentCommand() const { - if (_currentPromptState != PromptState::Command) - { - return L""; - } - return _activeBuffer().CurrentCommand(); } diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index b10c0d893ac..e792ee8d21c 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -117,15 +117,14 @@ class Microsoft::Terminal::Core::Terminal final : RenderSettings& GetRenderSettings() noexcept; const RenderSettings& GetRenderSettings() const noexcept; - const std::vector& GetScrollMarks() const noexcept; - void AddMark(const ScrollMark& mark, - const til::point& start, - const til::point& end, - const bool fromUi); + std::vector GetMarkRows() const; + std::vector GetMarkExtents() const; + void AddMarkFromUI(ScrollbarData mark, til::CoordType y); til::property AlwaysNotifyOnBufferRotation; - std::wstring_view CurrentCommand() const; + std::wstring CurrentCommand() const; + void SerializeMainBuffer(const wchar_t* destination) const; #pragma region ITerminalApi @@ -152,11 +151,6 @@ class Microsoft::Terminal::Core::Terminal final : void UseAlternateScreenBuffer(const TextAttribute& attrs) override; void UseMainScreenBuffer() override; - void MarkPrompt(const ScrollMark& mark) override; - void MarkCommandStart() override; - void MarkOutputStart() override; - void MarkCommandFinish(std::optional error) override; - bool IsConsolePty() const noexcept override; bool IsVtInputEnabled() const noexcept override; void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override; @@ -168,7 +162,7 @@ class Microsoft::Terminal::Core::Terminal final : void ClearMark(); void ClearAllMarks(); - til::color GetColorForMark(const ScrollMark& mark) const; + til::color GetColorForMark(const ScrollbarData& markData) const; #pragma region ITerminalInput // These methods are defined in Terminal.cpp @@ -435,15 +429,6 @@ class Microsoft::Terminal::Core::Terminal final : }; std::optional _lastKeyEventCodes; - enum class PromptState : uint32_t - { - None = 0, - Prompt, - Command, - Output, - }; - PromptState _currentPromptState{ PromptState::None }; - static WORD _ScanCodeFromVirtualKey(const WORD vkey) noexcept; static WORD _VirtualKeyFromScanCode(const WORD scanCode) noexcept; static WORD _VirtualKeyFromCharacter(const wchar_t ch) noexcept; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 9a5ea2221ac..121910cd697 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -300,140 +300,6 @@ void Terminal::UseMainScreenBuffer() _activeBuffer().TriggerRedrawAll(); } -// NOTE: This is the version of AddMark that comes from VT -void Terminal::MarkPrompt(const ScrollMark& mark) -{ - _assertLocked(); - - static bool logged = false; - if (!logged) - { - TraceLoggingWrite( - g_hCTerminalCoreProvider, - "ShellIntegrationMarkAdded", - TraceLoggingDescription("A mark was added via VT at least once"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - - logged = true; - } - - const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() }; - AddMark(mark, cursorPos, cursorPos, false); - - if (mark.category == MarkCategory::Prompt) - { - _currentPromptState = PromptState::Prompt; - } -} - -void Terminal::MarkCommandStart() -{ - _assertLocked(); - - const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() }; - - if ((_currentPromptState == PromptState::Prompt) && - (_activeBuffer().GetMarks().size() > 0)) - { - // We were in the right state, and there's a previous mark to work - // with. - // - //We can just do the work below safely. - } - else if (_currentPromptState == PromptState::Command) - { - // We're already in the command state. We don't want to end the current - // mark. We don't want to make a new one. We want to just leave the - // current command going. - return; - } - else - { - // If there was no last mark, or we're in a weird state, - // then abandon the current state, and just insert a new Prompt mark that - // start's & ends here, and got to State::Command. - - ScrollMark mark; - mark.category = MarkCategory::Prompt; - AddMark(mark, cursorPos, cursorPos, false); - } - _activeBuffer().SetCurrentPromptEnd(cursorPos); - _currentPromptState = PromptState::Command; -} - -void Terminal::MarkOutputStart() -{ - _assertLocked(); - - const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() }; - - if ((_currentPromptState == PromptState::Command) && - (_activeBuffer().GetMarks().size() > 0)) - { - // We were in the right state, and there's a previous mark to work - // with. - // - //We can just do the work below safely. - } - else if (_currentPromptState == PromptState::Output) - { - // We're already in the output state. We don't want to end the current - // mark. We don't want to make a new one. We want to just leave the - // current output going. - return; - } - else - { - // If there was no last mark, or we're in a weird state, - // then abandon the current state, and just insert a new Prompt mark that - // start's & ends here, and the command ends here, and go to State::Output. - - ScrollMark mark; - mark.category = MarkCategory::Prompt; - AddMark(mark, cursorPos, cursorPos, false); - } - _activeBuffer().SetCurrentCommandEnd(cursorPos); - _currentPromptState = PromptState::Output; -} - -void Terminal::MarkCommandFinish(std::optional error) -{ - _assertLocked(); - - const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() }; - auto category = MarkCategory::Prompt; - if (error.has_value()) - { - category = *error == 0u ? - MarkCategory::Success : - MarkCategory::Error; - } - - if ((_currentPromptState == PromptState::Output) && - (_activeBuffer().GetMarks().size() > 0)) - { - // We were in the right state, and there's a previous mark to work - // with. - // - //We can just do the work below safely. - } - else - { - // If there was no last mark, or we're in a weird state, then abandon - // the current state, and just insert a new Prompt mark that start's & - // ends here, and the command ends here, AND the output ends here. and - // go to State::Output. - - ScrollMark mark; - mark.category = MarkCategory::Prompt; - AddMark(mark, cursorPos, cursorPos, false); - _activeBuffer().SetCurrentCommandEnd(cursorPos); - } - _activeBuffer().SetCurrentOutputEnd(cursorPos, category); - _currentPromptState = PromptState::None; -} - // Method Description: // - Reacts to a client asking us to show or hide the window. // Arguments: @@ -497,16 +363,9 @@ void Terminal::NotifyBufferRotation(const int delta) // manually erase our pattern intervals since the locations have changed now _patternIntervalTree = {}; - auto& marks{ _activeBuffer().GetMarks() }; - const auto hasScrollMarks = marks.size() > 0; - if (hasScrollMarks) - { - _activeBuffer().ScrollMarks(-delta); - } - const auto oldScrollOffset = _scrollOffset; _PreserveUserScrollOffset(delta); - if (_scrollOffset != oldScrollOffset || hasScrollMarks || AlwaysNotifyOnBufferRotation()) + if (_scrollOffset != oldScrollOffset || AlwaysNotifyOnBufferRotation()) { _NotifyScrollEvent(); } diff --git a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp index c38c0221c5a..f4657e82527 100644 --- a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp @@ -502,7 +502,7 @@ namespace ControlUnitTests const auto& start = core->_terminal->GetSelectionAnchor(); const auto& end = core->_terminal->GetSelectionEnd(); const til::point expectedStart{ 24, 0 }; // The character after the prompt - const til::point expectedEnd{ 29, 3 }; // x = buffer.right + const til::point expectedEnd{ 21, 3 }; // x = the end of the text VERIFY_ARE_EQUAL(expectedStart, start); VERIFY_ARE_EQUAL(expectedEnd, end); } diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index f7948e5c59d..4c582d0f44a 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -131,7 +131,9 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final // Manually set the console into conpty mode. We're not actually going // to set up the pipes for conpty, but we want the console to behave // like it would in conpty mode. - g.EnableConptyModeForTests(std::move(vtRenderEngine)); + // + // Also, make sure to backdoor enable the resize quirk here too. + g.EnableConptyModeForTests(std::move(vtRenderEngine), true); expectedOutput.clear(); _checkConptyOutput = true; @@ -233,6 +235,11 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final TEST_METHOD(TestNoExtendedAttrsOptimization); TEST_METHOD(TestNoBackgroundAttrsOptimization); + TEST_METHOD(SimplePromptRegions); + TEST_METHOD(MultilinePromptRegions); + TEST_METHOD(ManyMultilinePromptsWithTrailingSpaces); + TEST_METHOD(ReflowPromptRegions); + private: bool _writeCallback(const char* const pch, const size_t cch); void _flushFirstFrame(); @@ -333,8 +340,9 @@ void ConptyRoundtripTests::_clearConpty() _resizeConpty(newSize.width, newSize.height); // After we resize, make sure to get the new textBuffers - return { &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetTextBuffer(), - term->_mainBuffer.get() }; + TextBuffer* hostTb = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetTextBuffer(); + TextBuffer* termTb = term->_mainBuffer.get(); + return { hostTb, termTb }; } void ConptyRoundtripTests::ConptyOutputTestCanary() @@ -745,6 +753,9 @@ void ConptyRoundtripTests::MoveCursorAtEOL() VERIFY_SUCCEEDED(renderer.PaintFrame()); verifyData1(termTb); + + // This test is sometimes flaky in cleanup. + expectedOutput.clear(); } void ConptyRoundtripTests::TestResizeHeight() @@ -2638,6 +2649,7 @@ void ConptyRoundtripTests::ResizeRepaintVimExeBuffer() auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport) { const auto firstRow = viewport.top; const auto width = viewport.width(); + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; // First row VERIFY_IS_FALSE(tb.GetRowByOffset(firstRow).WasWrapForced()); @@ -2694,7 +2706,6 @@ void ConptyRoundtripTests::ResizeRepaintVimExeBuffer() Log::Comment(L"========== Checking the host buffer state (after) =========="); verifyBuffer(*hostTb, si.GetViewport().ToExclusive()); - Log::Comment(L"Painting the frame"); VERIFY_SUCCEEDED(renderer.PaintFrame()); @@ -4315,3 +4326,622 @@ void ConptyRoundtripTests::TestNoBackgroundAttrsOptimization() Log::Comment(L"========== Check terminal buffer =========="); verifyBuffer(*termTb); } + +#define FTCS_A L"\x1b]133;A\x1b\\" +#define FTCS_B L"\x1b]133;B\x1b\\" +#define FTCS_C L"\x1b]133;C\x1b\\" +#define FTCS_D L"\x1b]133;D\x1b\\" + +void ConptyRoundtripTests::SimplePromptRegions() +{ + Log::Comment(L"Same as the ScreenBufferTests::ComplicatedPromptRegions, but in conpty"); + + auto& g = ServiceLocator::LocateGlobals(); + auto& renderer = *g.pRender; + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& sm = si.GetStateMachine(); + + auto* hostTb = &si.GetTextBuffer(); + auto* termTb = term->_mainBuffer.get(); + + gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state. + auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + + _flushFirstFrame(); + + _checkConptyOutput = false; + + auto verifyBuffer = [&](const TextBuffer& tb) { + const auto& cursor = tb.GetCursor(); + { + const til::point expectedCursor{ 17, 4 }; + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition()); + } + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + const auto& row0 = tb.GetRowByOffset(0); + const auto& row4 = tb.GetRowByOffset(4); + VERIFY_IS_TRUE(row0.GetScrollbarData().has_value()); + VERIFY_IS_TRUE(row4.GetScrollbarData().has_value()); + + const auto marks = tb.GetMarkExtents(); + VERIFY_ARE_EQUAL(2u, marks.size()); + + { + auto& mark = marks[0]; + const til::point expectedStart{ 0, 0 }; + const til::point expectedEnd{ 17, 0 }; + const til::point expectedOutputStart{ 24, 0 }; // `Foo-Bar` is 7 characters + const til::point expectedOutputEnd{ 13, 3 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + auto& mark = marks[1]; + const til::point expectedStart{ 0, 4 }; + const til::point expectedEnd{ 17, 4 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + VERIFY_IS_FALSE(mark.commandEnd.has_value()); + VERIFY_IS_FALSE(mark.outputEnd.has_value()); + } + }; + + Log::Comment(L"========== Fill test content =========="); + + auto _writePrompt = [](StateMachine& stateMachine, const auto& path) { + // A prompt looks like: + // `PWSH C:\Windows> ` + // + // which is 17 characters for C:\Windows + stateMachine.ProcessString(FTCS_D); + stateMachine.ProcessString(FTCS_A); + stateMachine.ProcessString(L"\x1b]9;9;"); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"\x7"); + stateMachine.ProcessString(L"PWSH "); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"> "); + stateMachine.ProcessString(FTCS_B); + }; + + _writePrompt(sm, L"C:\\Windows"); + sm.ProcessString(L"Foo-bar"); + sm.ProcessString(FTCS_C); + sm.ProcessString(L"\r\n"); + sm.ProcessString(L"This is some text \r\n"); // y=1 + sm.ProcessString(L"with varying amounts \r\n"); // y=2 + sm.ProcessString(L"of whitespace\r\n"); // y=3 + + _writePrompt(sm, L"C:\\Windows"); // y=4 + + Log::Comment(L"========== Check host buffer =========="); + verifyBuffer(*hostTb); + + Log::Comment(L"Painting the frame"); + VERIFY_SUCCEEDED(renderer.PaintFrame()); + + Log::Comment(L"========== Check terminal buffer =========="); + verifyBuffer(*termTb); +} + +void ConptyRoundtripTests::MultilinePromptRegions() +{ + auto& g = ServiceLocator::LocateGlobals(); + auto& renderer = *g.pRender; + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& sm = si.GetStateMachine(); + + auto* hostTb = &si.GetTextBuffer(); + auto* termTb = term->_mainBuffer.get(); + + gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state. + auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + + _flushFirstFrame(); + + _checkConptyOutput = false; + + auto bufferWidth = term->GetViewport().Width(); + + auto verifyBuffer = [&](const TextBuffer& tb) { + const auto& cursor = tb.GetCursor(); + { + const til::point expectedCursor{ 2, 6 }; + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition()); + } + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + const auto& row0 = tb.GetRowByOffset(0); + const auto& row5 = tb.GetRowByOffset(5); + VERIFY_IS_TRUE(row0.GetScrollbarData().has_value()); + VERIFY_IS_TRUE(row5.GetScrollbarData().has_value()); + + const auto marks = tb.GetMarkExtents(); + VERIFY_ARE_EQUAL(2u, marks.size()); + + { + Log::Comment(L"Row 0"); + const auto& row = tb.GetRowByOffset(0); + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(2u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + VERIFY_ARE_EQUAL(17, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Prompt, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - 17, run1.length); + VERIFY_ARE_EQUAL(MarkKind::None, run1.value.GetMarkAttributes()); + } + { + Log::Comment(L"Row 1"); + const auto& row = tb.GetRowByOffset(1); + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(3u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + auto run2 = runs[2]; + VERIFY_ARE_EQUAL(2, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Prompt, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(7, run1.length); + VERIFY_ARE_EQUAL(MarkKind::Command, run1.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - 9, run2.length); + VERIFY_ARE_EQUAL(MarkKind::None, run2.value.GetMarkAttributes()); + } + { + Log::Comment(L"Row 2"); + const auto& row = tb.GetRowByOffset(2); + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(2u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + VERIFY_ARE_EQUAL(22, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Output, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - 22, run1.length); + VERIFY_ARE_EQUAL(MarkKind::None, run1.value.GetMarkAttributes()); + } + { + Log::Comment(L"Row 3"); + const auto& row = tb.GetRowByOffset(3); + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(2u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + VERIFY_ARE_EQUAL(22, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Output, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - 22, run1.length); + VERIFY_ARE_EQUAL(MarkKind::None, run1.value.GetMarkAttributes()); + } + + { + auto& mark = marks[0]; + const til::point expectedStart{ 0, 0 }; + const til::point expectedEnd{ 2, 1 }; + const til::point expectedOutputStart{ 9, 1 }; // `Foo-Bar` is 7 characters + const til::point expectedOutputEnd{ 13, 4 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + auto& mark = marks[1]; + const til::point expectedStart{ 0, 5 }; + const til::point expectedEnd{ 2, 6 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + VERIFY_IS_FALSE(mark.commandEnd.has_value()); + VERIFY_IS_FALSE(mark.outputEnd.has_value()); + } + }; + + Log::Comment(L"========== Fill test content =========="); + + auto _writePrompt = [](StateMachine& stateMachine, const auto& path) { + // A prompt looks like: + // `PWSH C:\Windows >` + // `> ` + // + // which two rows. The first is 17 characters for C:\Windows + stateMachine.ProcessString(FTCS_D); + stateMachine.ProcessString(FTCS_A); + stateMachine.ProcessString(L"\x1b]9;9;"); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"\x7"); + stateMachine.ProcessString(L"PWSH "); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L" >\r\n"); + stateMachine.ProcessString(L"> "); + stateMachine.ProcessString(FTCS_B); + }; + + _writePrompt(sm, L"C:\\Windows"); // y=0,1 + sm.ProcessString(L"Foo-bar"); + sm.ProcessString(FTCS_C); + sm.ProcessString(L"\r\n"); + sm.ProcessString(L"This is some text \r\n"); // y=2 + sm.ProcessString(L"with varying amounts \r\n"); // y=3 + sm.ProcessString(L"of whitespace\r\n"); // y=4 + + _writePrompt(sm, L"C:\\Windows"); // y=5, 6 + + Log::Comment(L"========== Check host buffer =========="); + verifyBuffer(*hostTb); + + Log::Comment(L"Painting the frame"); + VERIFY_SUCCEEDED(renderer.PaintFrame()); + + Log::Comment(L"========== Check terminal buffer =========="); + verifyBuffer(*termTb); +} + +void ConptyRoundtripTests::ManyMultilinePromptsWithTrailingSpaces() +{ + auto& g = ServiceLocator::LocateGlobals(); + auto& renderer = *g.pRender; + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& sm = si.GetStateMachine(); + + auto* hostTb = &si.GetTextBuffer(); + auto* termTb = term->_mainBuffer.get(); + + gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state. + auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + + _flushFirstFrame(); + + _checkConptyOutput = false; + + auto bufferWidth = term->GetViewport().Width(); + + auto verifyFirstRowOfPrompt = [&](const ROW& row) { + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(2u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + VERIFY_ARE_EQUAL(17, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Prompt, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - 17, run1.length); + VERIFY_ARE_EQUAL(MarkKind::None, run1.value.GetMarkAttributes()); + }; + auto verifySecondRowOfPrompt = [&](const ROW& row, const auto expectedCommandLength) { + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(3u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + auto run2 = runs[2]; + VERIFY_ARE_EQUAL(2, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Prompt, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(expectedCommandLength, run1.length); + VERIFY_ARE_EQUAL(MarkKind::Command, run1.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - (2 + expectedCommandLength), run2.length); + VERIFY_ARE_EQUAL(MarkKind::None, run2.value.GetMarkAttributes()); + }; + + auto verifyBuffer = [&](const TextBuffer& tb) { + const auto& cursor = tb.GetCursor(); + { + const til::point expectedCursor{ 0, 11 }; + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition()); + } + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + const auto marks = tb.GetMarkExtents(); + VERIFY_ARE_EQUAL(3u, marks.size()); + + Log::Comment(L"Row 0"); + verifyFirstRowOfPrompt(tb.GetRowByOffset(0)); + + Log::Comment(L"Row 1"); + verifySecondRowOfPrompt(tb.GetRowByOffset(1), 7); + + { + Log::Comment(L"Row 2"); + const auto& row = tb.GetRowByOffset(2); + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(2u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + VERIFY_ARE_EQUAL(22, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Output, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - 22, run1.length); + VERIFY_ARE_EQUAL(MarkKind::None, run1.value.GetMarkAttributes()); + } + { + Log::Comment(L"Row 3"); + const auto& row = tb.GetRowByOffset(3); + const auto& attrs = row.Attributes(); + const auto& runs = attrs.runs(); + VERIFY_ARE_EQUAL(2u, runs.size()); + auto run0 = runs[0]; + auto run1 = runs[1]; + VERIFY_ARE_EQUAL(22, run0.length); + VERIFY_ARE_EQUAL(MarkKind::Output, run0.value.GetMarkAttributes()); + + VERIFY_ARE_EQUAL(bufferWidth - 22, run1.length); + VERIFY_ARE_EQUAL(MarkKind::None, run1.value.GetMarkAttributes()); + } + + Log::Comment(L"Row 5"); + verifyFirstRowOfPrompt(tb.GetRowByOffset(5)); + + Log::Comment(L"Row 6"); + verifySecondRowOfPrompt(tb.GetRowByOffset(6), 7); + + Log::Comment(L"Row 8"); + verifyFirstRowOfPrompt(tb.GetRowByOffset(8)); + + Log::Comment(L"Row 9"); + verifySecondRowOfPrompt(tb.GetRowByOffset(9), 6); + + { + Log::Comment(L"Foo-bar mark on rows 0 & 1"); + + auto& mark = marks[0]; + const til::point expectedStart{ 0, 0 }; + const til::point expectedEnd{ 2, 1 }; + + // The command ends at {9,1} (the end of the Foo-Bar string). + // However, the first character in the output is at {0,2}. + const til::point expectedOutputStart{ 9, 1 }; // `Foo-Bar` is 7 characters + const til::point expectedOutputEnd{ 13, 4 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + Log::Comment(L"Boo-far mark on rows 5 & 6"); + auto& mark = marks[1]; + const til::point expectedStart{ 0, 5 }; + const til::point expectedEnd{ 2, 6 }; + const til::point expectedOutputStart{ 9, 6 }; // `Boo-far` is 7 characters + const til::point expectedOutputEnd{ 22, 7 }; + + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + Log::Comment(L"yikes? mark on rows 8 & 9"); + auto& mark = marks[2]; + const til::point expectedStart{ 0, 8 }; + const til::point expectedEnd{ 2, 9 }; + const til::point expectedOutputStart{ 8, 9 }; // `yikes?` is 6 characters + const til::point expectedOutputEnd{ 22, 10 }; + + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + }; + + Log::Comment(L"========== Fill test content =========="); + + auto writePrompt = [](StateMachine& stateMachine, const auto& path) { + // A prompt looks like: + // `PWSH C:\Windows >` + // `> ` + // + // which two rows. The first is 17 characters for C:\Windows + stateMachine.ProcessString(FTCS_D); + stateMachine.ProcessString(FTCS_A); + stateMachine.ProcessString(L"\x1b]9;9;"); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"\x7"); + stateMachine.ProcessString(L"PWSH "); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L" >\r\n"); + stateMachine.ProcessString(L"> "); + stateMachine.ProcessString(FTCS_B); + }; + auto writeCommand = [](StateMachine& stateMachine, const auto& cmd) { + stateMachine.ProcessString(cmd); + stateMachine.ProcessString(FTCS_C); + stateMachine.ProcessString(L"\r\n"); + }; + + writePrompt(sm, L"C:\\Windows"); // y=0,1 + writeCommand(sm, L"Foo-bar"); + sm.ProcessString(L"This is some text \r\n"); // y=2 + sm.ProcessString(L"with varying amounts \r\n"); // y=3 + sm.ProcessString(L"of whitespace\r\n"); // y=4 + + writePrompt(sm, L"C:\\Windows"); // y=5, 6 + writeCommand(sm, L"Boo-far"); // y=6 + sm.ProcessString(L"This is more text \r\n"); // y=7 + + writePrompt(sm, L"C:\\Windows"); // y=8,9 + writeCommand(sm, L"yikes?"); // y=9 + sm.ProcessString(L"This is even more \r\n"); // y=10 + + Log::Comment(L"========== Check host buffer =========="); + verifyBuffer(*hostTb); + + Log::Comment(L"Painting the frame"); + VERIFY_SUCCEEDED(renderer.PaintFrame()); + + Log::Comment(L"========== Check terminal buffer =========="); + verifyBuffer(*termTb); +} + +void ConptyRoundtripTests::ReflowPromptRegions() +{ + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method") // always isolate things that resize the buffer + TEST_METHOD_PROPERTY(L"Data:dx", L"{-15, -1, 0, 1, 15}") + END_TEST_METHOD_PROPERTIES() + + INIT_TEST_PROPERTY(int, dx, L"The change in width of the buffer"); + + auto& g = ServiceLocator::LocateGlobals(); + auto& renderer = *g.pRender; + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& sm = si.GetStateMachine(); + + auto* hostTb = &si.GetTextBuffer(); + auto* termTb = term->_mainBuffer.get(); + + gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state. + auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + + _flushFirstFrame(); + + _checkConptyOutput = false; + + auto originalWidth = term->GetViewport().Width(); + + auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& /*viewport*/, const bool /*isTerminal*/, const bool afterResize) { + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + // Just the dx=+1 case doesn't unwrap the line onto one line, but the dx=+15 case does. + const bool unwrapped = afterResize && dx > 1; + const int unwrapAdjust = unwrapped ? -1 : 0; + const auto marks = tb.GetMarkExtents(); + VERIFY_ARE_EQUAL(3u, marks.size()); + { + Log::Comment(L"Mark 0"); + + auto& mark = marks[0]; + const til::point expectedStart{ 0, 0 }; + const til::point expectedEnd{ 10, 0 }; + const til::point expectedOutputStart{ 17, 0 }; // `Foo-Bar` is 7 characters + const til::point expectedOutputEnd{ 13, 3 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + Log::Comment(L"Mark 1"); + + auto& mark = marks[1]; + const til::point expectedStart{ 0, 4 }; + const til::point expectedEnd{ 10, 4 }; + // {originalWidth} characters of 'F', maybe wrapped. + const til::point originalPos = til::point{ 10, 5 }; + til::point afterPos = originalPos; + // walk that original pos dx times into the actual real place in the buffer. + auto bufferViewport = tb.GetSize(); + const auto walkDir = Viewport::WalkDir{ dx < 0 ? Viewport::XWalk::LeftToRight : Viewport::XWalk::RightToLeft, + dx < 0 ? Viewport::YWalk::TopToBottom : Viewport::YWalk::BottomToTop }; + for (auto i = 0; i < std::abs(dx); i++) + { + bufferViewport.WalkInBounds(afterPos, + walkDir); + } + const auto expectedOutputStart = !afterResize ? + originalPos : // printed exactly a row, so we're exactly below the prompt + afterPos; + const til::point expectedOutputEnd{ 22, 6 + unwrapAdjust }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + Log::Comment(L"Mark 2"); + + auto& mark = marks[2]; + const til::point expectedStart{ 0, 7 + unwrapAdjust }; + const til::point expectedEnd{ 10, 7 + unwrapAdjust }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + VERIFY_IS_TRUE(mark.commandEnd.has_value()); + VERIFY_IS_FALSE(mark.outputEnd.has_value()); + } + }; + + Log::Comment(L"========== Fill test content =========="); + + auto writePrompt = [](StateMachine& stateMachine, const auto& path) { + // A prompt looks like: + // `PWSH C:\> ` + // + // which is 10 characters for "C:\" + stateMachine.ProcessString(FTCS_D); + stateMachine.ProcessString(FTCS_A); + stateMachine.ProcessString(L"\x1b]9;9;"); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"\x7"); + stateMachine.ProcessString(L"PWSH "); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"> "); + stateMachine.ProcessString(FTCS_B); + }; + auto writeCommand = [](StateMachine& stateMachine, const auto& cmd) { + stateMachine.ProcessString(cmd); + stateMachine.ProcessString(FTCS_C); + stateMachine.ProcessString(L"\r\n"); + }; + + // This first prompt didn't reflow at all + writePrompt(sm, L"C:\\"); // y=0 + writeCommand(sm, L"Foo-bar"); // y=0 + sm.ProcessString(L"This is some text \r\n"); // y=1 + sm.ProcessString(L"with varying amounts \r\n"); // y=2 + sm.ProcessString(L"of whitespace\r\n"); // y=3 + + // This second one, the command does. It stretches across lines + writePrompt(sm, L"C:\\"); // y=4 + writeCommand(sm, std::wstring(originalWidth, L'F')); // y=4,5 + sm.ProcessString(L"This is more text \r\n"); // y=6 + + writePrompt(sm, L"C:\\"); // y=7 + writeCommand(sm, L"yikes?"); // y=7 + + Log::Comment(L"========== Checking the host buffer state (before) =========="); + verifyBuffer(*hostTb, si.GetViewport().ToExclusive(), false, false); + + Log::Comment(L"Painting the frame"); + VERIFY_SUCCEEDED(renderer.PaintFrame()); + + Log::Comment(L"========== Checking the terminal buffer state (before) =========="); + verifyBuffer(*termTb, term->_mutableViewport.ToExclusive(), true, false); + + // After we resize, make sure to get the new textBuffers + std::tie(hostTb, termTb) = _performResize({ TerminalViewWidth + dx, + TerminalViewHeight }); + + Log::Comment(L"Painting the frame"); + VERIFY_SUCCEEDED(renderer.PaintFrame()); + + Log::Comment(L"========== Checking the host buffer state (after) =========="); + verifyBuffer(*hostTb, si.GetViewport().ToExclusive(), false, true); + + Log::Comment(L"Painting the frame"); + VERIFY_SUCCEEDED(renderer.PaintFrame()); + + Log::Comment(L"========== Checking the terminal buffer state (after) =========="); + verifyBuffer(*termTb, term->_mutableViewport.ToExclusive(), true, true); +} diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index 3d6412add00..23500c2f6a8 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -446,9 +446,10 @@ void VtIo::CorkRenderer(bool corked) const noexcept // - vtRenderEngine: a VT renderer that our VtIo should use as the vt engine during these tests // Return Value: // - -void VtIo::EnableConptyModeForTests(std::unique_ptr vtRenderEngine) +void VtIo::EnableConptyModeForTests(std::unique_ptr vtRenderEngine, const bool resizeQuirk) { _initialized = true; + _resizeQuirk = resizeQuirk; _pVtRenderEngine = std::move(vtRenderEngine); } #endif diff --git a/src/host/VtIo.hpp b/src/host/VtIo.hpp index 7cd12036f5d..eccdb06aaac 100644 --- a/src/host/VtIo.hpp +++ b/src/host/VtIo.hpp @@ -43,7 +43,7 @@ namespace Microsoft::Console::VirtualTerminal void CorkRenderer(bool corked) const noexcept; #ifdef UNIT_TESTING - void EnableConptyModeForTests(std::unique_ptr vtRenderEngine); + void EnableConptyModeForTests(std::unique_ptr vtRenderEngine, const bool resizeQuirk = false); #endif bool IsResizeQuirkEnabled() const; diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 3d371d4b24f..495771fdad3 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -859,7 +859,8 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont // GH#3490 - If we're in conpty mode, don't invalidate the entire // viewport. In conpty mode, the VtEngine will later decide what // part of the buffer actually needs to be re-sent to the terminal. - if (!(g.getConsoleInformation().IsInVtIoMode() && g.getConsoleInformation().GetVtIo()->IsResizeQuirkEnabled())) + if (!(g.getConsoleInformation().IsInVtIoMode() && + g.getConsoleInformation().GetVtIo()->IsResizeQuirkEnabled())) { WriteToScreen(context, context.GetViewport()); } diff --git a/src/host/globals.cpp b/src/host/globals.cpp index f36fc3686ab..d1bbb4f297b 100644 --- a/src/host/globals.cpp +++ b/src/host/globals.cpp @@ -35,9 +35,9 @@ bool Globals::IsHeadless() const // - vtRenderEngine: a VT renderer that our VtIo should use as the vt engine during these tests // Return Value: // - -void Globals::EnableConptyModeForTests(std::unique_ptr vtRenderEngine) +void Globals::EnableConptyModeForTests(std::unique_ptr vtRenderEngine, const bool resizeQuirk) { launchArgs.EnableConptyModeForTests(); - getConsoleInformation().GetVtIo()->EnableConptyModeForTests(std::move(vtRenderEngine)); + getConsoleInformation().GetVtIo()->EnableConptyModeForTests(std::move(vtRenderEngine), resizeQuirk); } #endif diff --git a/src/host/globals.h b/src/host/globals.h index 589cf166480..9cebbc0808a 100644 --- a/src/host/globals.h +++ b/src/host/globals.h @@ -77,7 +77,7 @@ class Globals bool defaultTerminalMarkerCheckRequired = false; #ifdef UNIT_TESTING - void EnableConptyModeForTests(std::unique_ptr vtRenderEngine); + void EnableConptyModeForTests(std::unique_ptr vtRenderEngine, const bool resizeQuirk = false); #endif private: diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 70b270e3cd8..044889544c7 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -424,25 +424,6 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta) } } -void ConhostInternalGetSet::MarkPrompt(const ::ScrollMark& /*mark*/) -{ - // Not implemented for conhost. -} - -void ConhostInternalGetSet::MarkCommandStart() -{ - // Not implemented for conhost. -} - -void ConhostInternalGetSet::MarkOutputStart() -{ - // Not implemented for conhost. -} - -void ConhostInternalGetSet::MarkCommandFinish(std::optional /*error*/) -{ - // Not implemented for conhost. -} void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/) { // Not implemented for conhost. diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 45ea6052e21..850efe9892a 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -69,11 +69,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void NotifyAccessibilityChange(const til::rect& changedRect) override; void NotifyBufferRotation(const int delta) override; - void MarkPrompt(const ScrollMark& mark) override; - void MarkCommandStart() override; - void MarkOutputStart() override; - void MarkCommandFinish(std::optional error) override; - void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; private: diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 98a6f09b2e5..7e34d2f31e6 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -264,6 +264,10 @@ class ScreenBufferTests TEST_METHOD(DelayedWrapReset); TEST_METHOD(EraseColorMode); + + TEST_METHOD(SimpleMarkCommand); + TEST_METHOD(SimpleWrappedCommand); + TEST_METHOD(SimplePromptRegions); }; void ScreenBufferTests::SingleAlternateBufferCreationTest() @@ -8404,3 +8408,175 @@ void ScreenBufferTests::EraseColorMode() VERIFY_ARE_EQUAL(expectedEraseAttr, cellData->TextAttr()); VERIFY_ARE_EQUAL(L" ", cellData->Chars()); } + +#define FTCS_A L"\x1b]133;A\x1b\\" +#define FTCS_B L"\x1b]133;B\x1b\\" +#define FTCS_C L"\x1b]133;C\x1b\\" +#define FTCS_D L"\x1b]133;D\x1b\\" + +void ScreenBufferTests::SimpleMarkCommand() +{ + auto& g = ServiceLocator::LocateGlobals(); + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& tbi = si.GetTextBuffer(); + auto& stateMachine = si.GetStateMachine(); + + stateMachine.ProcessString(L"Zero\n"); + + { + const auto currentRowOffset = tbi.GetCursor().GetPosition().y; + auto& currentRow = tbi.GetRowByOffset(currentRowOffset); + + stateMachine.ProcessString(FTCS_A L"A Prompt" FTCS_B L"my_command" FTCS_C L"\n"); + + VERIFY_IS_TRUE(currentRow.GetScrollbarData().has_value()); + } + + stateMachine.ProcessString(L"Two\n"); + VERIFY_ARE_EQUAL(L"my_command", tbi.CurrentCommand()); + + stateMachine.ProcessString(FTCS_D FTCS_A L"B Prompt" FTCS_B); + + VERIFY_ARE_EQUAL(tbi.CurrentCommand(), L""); + + stateMachine.ProcessString(L"some of a command"); + VERIFY_ARE_EQUAL(tbi.CurrentCommand(), L"some of a command"); + // Now add some color in the middle of the command: + stateMachine.ProcessString(L"\x1b[31m"); + stateMachine.ProcessString(L" & more of a command"); + + VERIFY_ARE_EQUAL(L"some of a command & more of a command", tbi.CurrentCommand()); + + std::vector expectedCommands{ L"my_command", + L"some of a command & more of a command" }; + VERIFY_ARE_EQUAL(expectedCommands, tbi.Commands()); +} + +void ScreenBufferTests::SimpleWrappedCommand() +{ + auto& g = ServiceLocator::LocateGlobals(); + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& tbi = si.GetTextBuffer(); + auto& stateMachine = si.GetStateMachine(); + + stateMachine.ProcessString(L"Zero\n"); + + const auto oneHundredZeros = std::wstring(100, L'0'); + { + const auto originalRowOffset = tbi.GetCursor().GetPosition().y; + auto& originalRow = tbi.GetRowByOffset(originalRowOffset); + stateMachine.ProcessString(FTCS_A L"A Prompt" FTCS_B); + + // This command is literally 100 '0' characters, so that we _know_ we wrapped. + stateMachine.ProcessString(oneHundredZeros); + + const auto secondRowOffset = tbi.GetCursor().GetPosition().y; + VERIFY_ARE_NOT_EQUAL(originalRowOffset, secondRowOffset); + auto& secondRow = tbi.GetRowByOffset(secondRowOffset); + + VERIFY_IS_TRUE(originalRow.GetScrollbarData().has_value()); + VERIFY_IS_FALSE(secondRow.GetScrollbarData().has_value()); + + stateMachine.ProcessString(FTCS_C L"\n"); + + VERIFY_IS_TRUE(originalRow.GetScrollbarData().has_value()); + VERIFY_IS_FALSE(secondRow.GetScrollbarData().has_value()); + } + + stateMachine.ProcessString(L"Two\n"); + VERIFY_ARE_EQUAL(oneHundredZeros, tbi.CurrentCommand()); + + stateMachine.ProcessString(FTCS_D FTCS_A L"B Prompt" FTCS_B); + + VERIFY_ARE_EQUAL(tbi.CurrentCommand(), L""); + + stateMachine.ProcessString(L"some of a command"); + VERIFY_ARE_EQUAL(tbi.CurrentCommand(), L"some of a command"); + // Now add some color in the middle of the command: + stateMachine.ProcessString(L"\x1b[31m"); + stateMachine.ProcessString(L" & more of a command"); + + VERIFY_ARE_EQUAL(L"some of a command & more of a command", tbi.CurrentCommand()); + + std::vector expectedCommands{ oneHundredZeros, + L"some of a command & more of a command" }; + VERIFY_ARE_EQUAL(expectedCommands, tbi.Commands()); +} + +static void _writePrompt(StateMachine& stateMachine, const auto& path) +{ + stateMachine.ProcessString(FTCS_D); + stateMachine.ProcessString(FTCS_A); + stateMachine.ProcessString(L"\x1b]9;9;"); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"\x7"); + stateMachine.ProcessString(L"PWSH "); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"> "); + stateMachine.ProcessString(FTCS_B); +} + +void ScreenBufferTests::SimplePromptRegions() +{ + auto& g = ServiceLocator::LocateGlobals(); + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& tbi = si.GetTextBuffer(); + auto& stateMachine = si.GetStateMachine(); + + // A prompt looks like: + // `PWSH C:\Windows> ` + // + // which is 17 characters for C:\Windows + + _writePrompt(stateMachine, L"C:\\Windows"); + stateMachine.ProcessString(L"Foo-bar"); + stateMachine.ProcessString(FTCS_C); + stateMachine.ProcessString(L"\r\n"); + stateMachine.ProcessString(L"This is some text \r\n"); // y=1 + stateMachine.ProcessString(L"with varying amounts \r\n"); // y=2 + stateMachine.ProcessString(L"of whitespace \r\n"); // y=3 + + _writePrompt(stateMachine, L"C:\\Windows"); // y=4 + + Log::Comment(L"Check the buffer contents"); + const auto& cursor = tbi.GetCursor(); + + { + const til::point expectedCursor{ 17, 4 }; + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition()); + } + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + const auto& row0 = tbi.GetRowByOffset(0); + const auto& row4 = tbi.GetRowByOffset(4); + VERIFY_IS_TRUE(row0.GetScrollbarData().has_value()); + VERIFY_IS_TRUE(row4.GetScrollbarData().has_value()); + + const auto marks = tbi.GetMarkExtents(); + VERIFY_ARE_EQUAL(2u, marks.size()); + + { + auto& mark = marks[0]; + const til::point expectedStart{ 0, 0 }; + const til::point expectedEnd{ 17, 0 }; + const til::point expectedOutputStart{ 24, 0 }; // `Foo-Bar` is 7 characters + const til::point expectedOutputEnd{ 22, 3 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + auto& mark = marks[1]; + const til::point expectedStart{ 0, 4 }; + const til::point expectedEnd{ 17, 4 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + VERIFY_IS_FALSE(mark.commandEnd.has_value()); + VERIFY_IS_FALSE(mark.outputEnd.has_value()); + } +} diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index 648b054778b..33660d876a8 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -161,6 +161,8 @@ class TextBufferTests TEST_METHOD(HyperlinkTrim); TEST_METHOD(NoHyperlinkTrim); + + TEST_METHOD(ReflowPromptRegions); }; void TextBufferTests::TestBufferCreate() @@ -2852,3 +2854,135 @@ void TextBufferTests::NoHyperlinkTrim() VERIFY_ARE_EQUAL(_buffer->GetHyperlinkUriFromId(id), url); VERIFY_ARE_EQUAL(_buffer->_hyperlinkCustomIdMap[finalCustomId], id); } + +#define FTCS_A L"\x1b]133;A\x1b\\" +#define FTCS_B L"\x1b]133;B\x1b\\" +#define FTCS_C L"\x1b]133;C\x1b\\" +#define FTCS_D L"\x1b]133;D\x1b\\" +void TextBufferTests::ReflowPromptRegions() +{ + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method") // always isolate things that resize the buffer + TEST_METHOD_PROPERTY(L"Data:dx", L"{-15, -1, 0, 1, 15}") + END_TEST_METHOD_PROPERTIES() + + INIT_TEST_PROPERTY(int, dx, L"The change in width of the buffer"); + + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); + auto* tbi = &si.GetTextBuffer(); + auto& sm = si.GetStateMachine(); + const auto oldSize{ tbi->GetSize() }; + + auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& /*viewport*/, const bool /*isTerminal*/, const bool afterResize) { + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + // Just the dx=+1 case doesn't unwrap the line onto one line, but the dx=+15 case does. + const bool unwrapped = afterResize && dx > 1; + const int unwrapAdjust = unwrapped ? -1 : 0; + const auto marks = tb.GetMarkExtents(); + VERIFY_ARE_EQUAL(3u, marks.size()); + { + Log::Comment(L"Mark 0"); + + auto& mark = marks[0]; + const til::point expectedStart{ 0, 0 }; + const til::point expectedEnd{ 10, 0 }; + const til::point expectedOutputStart{ 17, 0 }; // `Foo-Bar` is 7 characters + const til::point expectedOutputEnd{ 13, 3 }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + Log::Comment(L"Mark 1"); + + auto& mark = marks[1]; + const til::point expectedStart{ 0, 4 }; + const til::point expectedEnd{ 10, 4 }; + // {originalWidth} characters of 'F', maybe wrapped. + const til::point originalPos = til::point{ 10, 5 }; + til::point afterPos = originalPos; + // walk that original pos dx times into the actual real place in the buffer. + auto bufferViewport = tb.GetSize(); + const auto walkDir = Viewport::WalkDir{ dx < 0 ? Viewport::XWalk::LeftToRight : Viewport::XWalk::RightToLeft, + dx < 0 ? Viewport::YWalk::TopToBottom : Viewport::YWalk::BottomToTop }; + for (auto i = 0; i < std::abs(dx); i++) + { + bufferViewport.WalkInBounds(afterPos, + walkDir); + } + const auto expectedOutputStart = !afterResize ? + originalPos : // printed exactly a row, so we're exactly below the prompt + afterPos; + const til::point expectedOutputEnd{ 22, 6 + unwrapAdjust }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + + VERIFY_ARE_EQUAL(expectedOutputStart, *mark.commandEnd); + VERIFY_ARE_EQUAL(expectedOutputEnd, *mark.outputEnd); + } + { + Log::Comment(L"Mark 2"); + + auto& mark = marks[2]; + const til::point expectedStart{ 0, 7 + unwrapAdjust }; + const til::point expectedEnd{ 10, 7 + unwrapAdjust }; + VERIFY_ARE_EQUAL(expectedStart, mark.start); + VERIFY_ARE_EQUAL(expectedEnd, mark.end); + VERIFY_IS_TRUE(mark.commandEnd.has_value()); + VERIFY_IS_FALSE(mark.outputEnd.has_value()); + } + }; + + Log::Comment(L"========== Fill test content =========="); + + auto writePrompt = [](StateMachine& stateMachine, const auto& path) { + // A prompt looks like: + // `PWSH C:\> ` + // + // which is 10 characters for "C:\" + stateMachine.ProcessString(FTCS_D); + stateMachine.ProcessString(FTCS_A); + stateMachine.ProcessString(L"\x1b]9;9;"); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"\x7"); + stateMachine.ProcessString(L"PWSH "); + stateMachine.ProcessString(path); + stateMachine.ProcessString(L"> "); + stateMachine.ProcessString(FTCS_B); + }; + auto writeCommand = [](StateMachine& stateMachine, const auto& cmd) { + stateMachine.ProcessString(cmd); + stateMachine.ProcessString(FTCS_C); + stateMachine.ProcessString(L"\r\n"); + }; + + // This first prompt didn't reflow at all + writePrompt(sm, L"C:\\"); // y=0 + writeCommand(sm, L"Foo-bar"); // y=0 + sm.ProcessString(L"This is some text \r\n"); // y=1 + sm.ProcessString(L"with varying amounts \r\n"); // y=2 + sm.ProcessString(L"of whitespace\r\n"); // y=3 + + // This second one, the command does. It stretches across lines + writePrompt(sm, L"C:\\"); // y=4 + writeCommand(sm, std::wstring(oldSize.Width(), L'F')); // y=4,5 + sm.ProcessString(L"This is more text \r\n"); // y=6 + + writePrompt(sm, L"C:\\"); // y=7 + writeCommand(sm, L"yikes?"); // y=7 + + Log::Comment(L"========== Checking the buffer state (before) =========="); + verifyBuffer(*tbi, si.GetViewport().ToExclusive(), false, false); + + // After we resize, make sure to get the new textBuffers + til::size newSize{ oldSize.Width() + dx, oldSize.Height() }; + auto newBuffer = std::make_unique(newSize, TextAttribute{ 0x7 }, 0, false, _renderer); + TextBuffer::Reflow(*tbi, *newBuffer); + + Log::Comment(L"========== Checking the host buffer state (after) =========="); + verifyBuffer(*newBuffer, si.GetViewport().ToExclusive(), false, true); +} diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 3375a968b04..24125780f4b 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -81,11 +81,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0; virtual void NotifyBufferRotation(const int delta) = 0; - virtual void MarkPrompt(const ScrollMark& mark) = 0; - virtual void MarkCommandStart() = 0; - virtual void MarkOutputStart() = 0; - virtual void MarkCommandFinish(std::optional error) = 0; - virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; }; } diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index dfe540936d0..cdf50148ef3 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3671,7 +3671,7 @@ bool AdaptDispatch::DoConEmuAction(const std::wstring_view string) // This seems like basically the same as 133;B - the end of the prompt, the start of the commandline. else if (subParam == 12) { - _api.MarkCommandStart(); + _api.GetTextBuffer().StartCommand(); return true; } @@ -3690,12 +3690,12 @@ bool AdaptDispatch::DoConEmuAction(const std::wstring_view string) // - false in conhost, true for the SetMark action, otherwise false. bool AdaptDispatch::DoITerm2Action(const std::wstring_view string) { - // This is not implemented in conhost. - if (_api.IsConsolePty()) + const auto isConPty = _api.IsConsolePty(); + if (isConPty) { - // Flush the frame manually, to make sure marks end up on the right line, like the alt buffer sequence. + // Flush the frame manually, to make sure marks end up on the right + // line, like the alt buffer sequence. _renderer.TriggerFlush(false); - return false; } if constexpr (!Feature_ScrollbarMarks::IsEnabled()) @@ -3712,14 +3712,14 @@ bool AdaptDispatch::DoITerm2Action(const std::wstring_view string) const auto action = til::at(parts, 0); + bool handled = false; if (action == L"SetMark") { - ScrollMark mark; - mark.category = MarkCategory::Prompt; - _api.MarkPrompt(mark); - return true; + _api.GetTextBuffer().StartPrompt(); + handled = true; } - return false; + + return handled && !isConPty; } // Method Description: @@ -3734,12 +3734,12 @@ bool AdaptDispatch::DoITerm2Action(const std::wstring_view string) // - false in conhost, true for the SetMark action, otherwise false. bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string) { - // This is not implemented in conhost. - if (_api.IsConsolePty()) + const auto isConPty = _api.IsConsolePty(); + if (isConPty) { - // Flush the frame manually, to make sure marks end up on the right line, like the alt buffer sequence. + // Flush the frame manually, to make sure marks end up on the right + // line, like the alt buffer sequence. _renderer.TriggerFlush(false); - return false; } if constexpr (!Feature_ScrollbarMarks::IsEnabled()) @@ -3753,7 +3753,7 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string) { return false; } - + bool handled = false; const auto action = til::at(parts, 0); if (action.size() == 1) { @@ -3761,21 +3761,21 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string) { case L'A': // FTCS_PROMPT { - // Simply just mark this line as a prompt line. - ScrollMark mark; - mark.category = MarkCategory::Prompt; - _api.MarkPrompt(mark); - return true; + _api.GetTextBuffer().StartPrompt(); + handled = true; + break; } case L'B': // FTCS_COMMAND_START { - _api.MarkCommandStart(); - return true; + _api.GetTextBuffer().StartCommand(); + handled = true; + break; } case L'C': // FTCS_COMMAND_EXECUTED { - _api.MarkOutputStart(); - return true; + _api.GetTextBuffer().StartOutput(); + handled = true; + break; } case L'D': // FTCS_COMMAND_FINISHED { @@ -3793,12 +3793,15 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string) error = Utils::StringToUint(errorString, parsedError) ? parsedError : UINT_MAX; } - _api.MarkCommandFinish(error); - return true; + + _api.GetTextBuffer().EndCurrentCommand(error); + + handled = true; + break; } default: { - return false; + handled = false; } } } @@ -3807,7 +3810,7 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string) // simple state machine here to track the most recently emitted mark from // this set of sequences, and which sequence was emitted last, so we can // modify the state of that mark as we go. - return false; + return handled && !isConPty; } // Method Description: // - Performs a VsCode action diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index da8b7a35d3a..c4012933dc1 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -215,22 +215,6 @@ class TestGetSet final : public ITerminalApi Log::Comment(L"NotifyBufferRotation MOCK called..."); } - void MarkPrompt(const ScrollMark& /*mark*/) override - { - Log::Comment(L"MarkPrompt MOCK called..."); - } - void MarkCommandStart() override - { - Log::Comment(L"MarkCommandStart MOCK called..."); - } - void MarkOutputStart() override - { - Log::Comment(L"MarkOutputStart MOCK called..."); - } - void MarkCommandFinish(std::optional /*error*/) override - { - Log::Comment(L"MarkCommandFinish MOCK called..."); - } void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override { Log::Comment(L"InvokeCompletions MOCK called..."); From 156b9aeea69775751923f253fd9e5c0225d2e30a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 5 Apr 2024 13:43:44 -0700 Subject: [PATCH 213/603] Make marks a stable feature (#16938) * Switches the marks feature to being stable. * Renames the settings, to remove "experimental." Stacked on top of #16937, so that we actually finish reflow before merging this. Closes #15057 --- src/cascadia/TerminalSettingsModel/MTSMSettings.h | 4 ++-- src/cascadia/TerminalSettingsModel/Profile.cpp | 8 ++++++++ src/features.xml | 11 ++++------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index cde90f910b1..0375d2802ff 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -93,8 +93,8 @@ Author(s): X(bool, RightClickContextMenu, "experimental.rightClickContextMenu", false) \ X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ X(bool, Elevate, "elevate", false) \ - X(bool, AutoMarkPrompts, "experimental.autoMarkPrompts", false) \ - X(bool, ShowMarks, "experimental.showMarksOnScrollbar", false) \ + X(bool, AutoMarkPrompts, "autoMarkPrompts", false) \ + X(bool, ShowMarks, "showMarksOnScrollbar", false) \ X(bool, RepositionCursorWithMouse, "experimental.repositionCursorWithMouse", false) \ X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true) diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp index 0db012e7bc7..0f568b37e53 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.cpp +++ b/src/cascadia/TerminalSettingsModel/Profile.cpp @@ -34,6 +34,9 @@ static constexpr std::string_view PaddingKey{ "padding" }; static constexpr std::string_view TabColorKey{ "tabColor" }; static constexpr std::string_view UnfocusedAppearanceKey{ "unfocusedAppearance" }; +static constexpr std::string_view LegacyAutoMarkPromptsKey{ "experimental.autoMarkPrompts" }; +static constexpr std::string_view LegacyShowMarksKey{ "experimental.showMarksOnScrollbar" }; + Profile::Profile(guid guid) noexcept : _Guid(guid) { @@ -182,6 +185,11 @@ void Profile::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, TabColorKey, _TabColor); + // Try to load some legacy keys, to migrate them. + // Done _before_ the MTSM_PROFILE_SETTINGS, which have the updated keys. + JsonUtils::GetValueForKey(json, LegacyShowMarksKey, _ShowMarks); + JsonUtils::GetValueForKey(json, LegacyAutoMarkPromptsKey, _AutoMarkPrompts); + #define PROFILE_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \ JsonUtils::GetValueForKey(json, jsonKey, _##name); diff --git a/src/features.xml b/src/features.xml index 8fbf160a8c7..c2af5ed009a 100644 --- a/src/features.xml +++ b/src/features.xml @@ -89,13 +89,10 @@ Feature_ScrollbarMarks Enables the experimental scrollbar marks feature. - AlwaysDisabled - - - Dev - Canary - Preview - + AlwaysEnabled + + WindowsInbox + From b90eb93d26bdfe2ee5b3a6970f9984a1f66e6794 Mon Sep 17 00:00:00 2001 From: Windows Console Service Bot <14666831+consvc@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:25:58 -0500 Subject: [PATCH 214/603] Localization Updates - main - 04/06/2024 03:04:16 (#17025) --- .../TerminalSettingsEditor/Resources/zh-CN/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 1dfc634ae45..298c9279e9b 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -1737,7 +1737,7 @@ The label for a "key chord listener" control that sets the keys a key binding is bound to. - 文本格式化 + 文本格式 Header for a control to how text is formatted From 7adc3743d89de24fd97aac9145b900319ebf7b64 Mon Sep 17 00:00:00 2001 From: Marcel W Date: Tue, 9 Apr 2024 21:43:00 +0200 Subject: [PATCH 215/603] Add more profile GUID tests (#17030) ## Summary of the Pull Request Adding more profile GUID tests ## References and Relevant Issues Closes #2119 ## Detailed Description of the Pull Request / Additional comments Currently, there are formats that simply break GUID parsing (the commented out test cases). Should we catch that and treat it like a "null" GUID or should we generate a new GUID for those profiles? ## Validation Steps Performed ## PR Checklist - [x] Closes #2119 - [x] Tests added/passed - [ ] Documentation updated - If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx - [ ] Schema updated (if necessary) --- .../UnitTests_SettingsModel/ProfileTests.cpp | 64 +++++++++++++------ 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp b/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp index 214d39af586..97ce4751b4c 100644 --- a/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp @@ -45,6 +45,15 @@ namespace SettingsModelUnitTests // this test that includes synthesizing GUIDS for profiles without GUIDs // set + const auto parseAndVerifyProfile = [](const std::string profile, const bool hasGuid) { + const auto profileAsJson = VerifyParseSucceeded(profile); + const auto profileParsed = implementation::Profile::FromJson(profileAsJson); + + VERIFY_ARE_EQUAL(profileParsed->HasGuid(), hasGuid); + return profileParsed; + }; + + // Invalid GUID Values const std::string profileWithoutGuid{ R"({ "name" : "profile0" })" }; @@ -55,37 +64,50 @@ namespace SettingsModelUnitTests "name" : "profile2", "guid" : null })" }; + const std::string profileWithHyphenlessGuid{ R"({ + "name" : "profile4", + "guid" : "{6239A42C1DE449A380BDE8FDD045185C}" + })" }; + const std::string profileWithRawGuid{ R"({ + "name" : "profile4", + "guid" : "6239a42c-1de4-49a3-80bd-e8fdd045185c" + })" }; + const std::string profileWithGuidFormatP{ R"({ + "name" : "profile4", + "guid" : "(6239a42c-1de4-49a3-80bd-e8fdd045185c)\\" + })" }; + // Valid GUIDs const std::string profileWithNullGuid{ R"({ "name" : "profile3", "guid" : "{00000000-0000-0000-0000-000000000000}" })" }; - const std::string profileWithGuid{ R"({ + const std::string profileWithGuidFormatB{ R"({ "name" : "profile4", "guid" : "{6239a42c-1de4-49a3-80bd-e8fdd045185c}" })" }; + const std::string profileWithGuidUpperCaseFormatB{ R"({ + "name" : "profile4", + "guid" : "{6239A42C-1DE4-49A3-80BD-E8FDD045185C}" + })" }; - const auto profile0Json = VerifyParseSucceeded(profileWithoutGuid); - const auto profile1Json = VerifyParseSucceeded(secondProfileWithoutGuid); - const auto profile2Json = VerifyParseSucceeded(profileWithNullForGuid); - const auto profile3Json = VerifyParseSucceeded(profileWithNullGuid); - const auto profile4Json = VerifyParseSucceeded(profileWithGuid); - - const auto profile0 = implementation::Profile::FromJson(profile0Json); - const auto profile1 = implementation::Profile::FromJson(profile1Json); - const auto profile2 = implementation::Profile::FromJson(profile2Json); - const auto profile3 = implementation::Profile::FromJson(profile3Json); - const auto profile4 = implementation::Profile::FromJson(profile4Json); - const winrt::guid cmdGuid = Utils::GuidFromString(L"{6239a42c-1de4-49a3-80bd-e8fdd045185c}"); - const winrt::guid nullGuid{}; + parseAndVerifyProfile(profileWithoutGuid, false); + parseAndVerifyProfile(secondProfileWithoutGuid, false); - VERIFY_IS_FALSE(profile0->HasGuid()); - VERIFY_IS_FALSE(profile1->HasGuid()); - VERIFY_IS_FALSE(profile2->HasGuid()); - VERIFY_IS_TRUE(profile3->HasGuid()); - VERIFY_IS_TRUE(profile4->HasGuid()); + // The following crash JSON parsing + VERIFY_THROWS(parseAndVerifyProfile(profileWithHyphenlessGuid, false), std::exception); + VERIFY_THROWS(parseAndVerifyProfile(profileWithRawGuid, false), std::exception); + VERIFY_THROWS(parseAndVerifyProfile(profileWithGuidFormatP, false), std::exception); + + const auto parsedNullGuidProfile = parseAndVerifyProfile(profileWithNullGuid, true); + const auto parsedGuidProfileFormatB = parseAndVerifyProfile(profileWithGuidFormatB, true); + const auto parsedGuidProfileUpperCaseFormatB = parseAndVerifyProfile(profileWithGuidUpperCaseFormatB, true); + + const winrt::guid nullGuid{}; + const winrt::guid cmdGuid = Utils::GuidFromString(L"{6239a42c-1de4-49a3-80bd-e8fdd045185c}"); - VERIFY_ARE_EQUAL(profile3->Guid(), nullGuid); - VERIFY_ARE_EQUAL(profile4->Guid(), cmdGuid); + VERIFY_ARE_EQUAL(parsedNullGuidProfile->Guid(), nullGuid); + VERIFY_ARE_EQUAL(parsedGuidProfileFormatB->Guid(), cmdGuid); + VERIFY_ARE_EQUAL(parsedGuidProfileUpperCaseFormatB->Guid(), cmdGuid); } void ProfileTests::LayerProfileProperties() From 8bd9578b3ca9d18ba362ce1fff9b396d426b39e9 Mon Sep 17 00:00:00 2001 From: Harsh Narayan Jha <50262541+HarshNarayanJha@users.noreply.github.com> Date: Wed, 10 Apr 2024 05:41:43 +0530 Subject: [PATCH 216/603] =?UTF-8?q?Fix:=20UI=20style=20errors:=20Menu=20it?= =?UTF-8?q?ems=20capitalization=20and=20=E2=80=A6=20(ellipses)=20mark=20mi?= =?UTF-8?q?suse=20(#16886)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I changed the improper capitalization and misuse of ellipses mark (...) in the context menu and various other places. Fixes issues #16819 and #16846 ## PR Checklist - [ ] Closes #16846 - [x] Tests added/passed - NA - [x] Documentation updated - NA - If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx - [x] Schema updated (if necessary) - NA --- .../Resources/en-US/Resources.resw | 60 +++++++++---------- src/cascadia/TerminalApp/TerminalTab.cpp | 16 ++--- .../Resources/en-US/Resources.resw | 22 +++---- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 54cd9e9bc30..15b79c40ffc 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -191,43 +191,43 @@ Multiple panes - Close... + Close - Close Tabs to the Right + Close tabs to the right - Close Other Tabs + Close other tabs - Close Tab + Close tab - Close Pane + Close pane - Split Tab + Split tab - Split Pane + Split pane - Web Search + Web search - Color... + Change tab color - Custom... + Custom Reset - Rename Tab + Rename tab - Duplicate Tab + Duplicate tab Found a profile with an invalid "backgroundImage". Defaulting that profile to have no background image. Make sure that when setting a "backgroundImage", the value is a valid file path to an image. @@ -462,7 +462,7 @@ About - Send Feedback + Send feedback OK @@ -472,11 +472,11 @@ This is the heading for a version number label - Getting Started + Getting started A hyperlink name for a guide on how to get started using Terminal - Source Code + Source code A hyperlink name for the Terminal's documentation @@ -484,15 +484,15 @@ A hyperlink name for user documentation - Release Notes + Release notes A hyperlink name for the Terminal's release notes - Privacy Policy + Privacy policy A hyperlink name for the Terminal's privacy policy - Third-Party Notices + Third-Party notices A hyperlink name for the Terminal's third-party notices @@ -579,10 +579,10 @@ Failed parsing command line: - Command Palette + Command palette - Tab Switcher + Tab switcher Type a tab name... @@ -731,10 +731,10 @@ Maximize - Restore Down + Restore down - Command Palette + Command palette Focus Terminal @@ -754,7 +754,7 @@ Split the window and start in given directory - Export Text + Export text Failed to export terminal content @@ -766,7 +766,7 @@ Find - Plain Text + Plain text Termination behavior can be configured in advanced profile settings. @@ -775,7 +775,7 @@ Don't show again - This Terminal window is running as Admin + This Terminal window is running as administrator Suggestions found: {0} @@ -835,10 +835,10 @@ Close this tab - Empty... + Empty - Close Pane + Close pane Close the active pane if multiple panes are present @@ -848,13 +848,13 @@ Text used to identify the reset button - Move Tab to New Window + Move tab to new window Moves tab to a new window - Run as Administrator + Run as administrator This text is displayed on context menu for profile entries in add new tab button. @@ -893,7 +893,7 @@ If set, the command will be appended to the profile's default command instead of replacing it. - Restart Connection + Restart connection Restart the active pane connection diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index ceb3860f1ff..e0a93ee9275 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1372,7 +1372,7 @@ namespace winrt::TerminalApp::implementation { auto weakThis{ get_weak() }; - // "Color..." + // "Change tab color..." Controls::MenuFlyoutItem chooseColorMenuItem; { Controls::FontIcon colorPickSymbol; @@ -1391,7 +1391,7 @@ namespace winrt::TerminalApp::implementation Controls::MenuFlyoutItem renameTabMenuItem; { - // "Rename Tab" + // "Rename tab" Controls::FontIcon renameTabSymbol; renameTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); renameTabSymbol.Glyph(L"\xE8AC"); // Rename @@ -1408,7 +1408,7 @@ namespace winrt::TerminalApp::implementation Controls::MenuFlyoutItem duplicateTabMenuItem; { - // "Duplicate Tab" + // "Duplicate tab" Controls::FontIcon duplicateTabSymbol; duplicateTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); duplicateTabSymbol.Glyph(L"\xF5ED"); @@ -1425,7 +1425,7 @@ namespace winrt::TerminalApp::implementation Controls::MenuFlyoutItem splitTabMenuItem; { - // "Split Tab" + // "Split tab" Controls::FontIcon splitTabSymbol; splitTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); splitTabSymbol.Glyph(L"\xF246"); // ViewDashboard @@ -1442,7 +1442,7 @@ namespace winrt::TerminalApp::implementation Controls::MenuFlyoutItem moveTabToNewWindowMenuItem; { - // "Move Tab to New Window" + // "Move tab to new window" Controls::FontIcon moveTabToNewWindowTabSymbol; moveTabToNewWindowTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); moveTabToNewWindowTabSymbol.Glyph(L"\xE8A7"); @@ -1459,7 +1459,7 @@ namespace winrt::TerminalApp::implementation Controls::MenuFlyoutItem closePaneMenuItem = _closePaneMenuItem; { - // "Close Pane" + // "Close pane" closePaneMenuItem.Click({ get_weak(), &TerminalTab::_closePaneClicked }); closePaneMenuItem.Text(RS_(L"ClosePaneText")); @@ -1471,7 +1471,7 @@ namespace winrt::TerminalApp::implementation Controls::MenuFlyoutItem exportTabMenuItem; { - // "Export Tab" + // "Export tab" Controls::FontIcon exportTabSymbol; exportTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); exportTabSymbol.Glyph(L"\xE74E"); // Save @@ -1505,7 +1505,7 @@ namespace winrt::TerminalApp::implementation Controls::MenuFlyoutItem restartConnectionMenuItem = _restartConnectionMenuItem; { - // "Restart Connection" + // "Restart connection" Controls::FontIcon restartConnectionSymbol; restartConnectionSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); restartConnectionSymbol.Glyph(L"\xE72C"); diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 03e330d9ecd..6179099bdf5 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Select color scheme... + Select color scheme - New Tab... + New tab - Split Pane... + Split pane Terminal (Portable) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Set the tab color... + Set the tab color Paste @@ -347,7 +347,7 @@ Reset tab title - Rename tab title... + Rename tab title Resize pane @@ -441,7 +441,7 @@ Switch to the last tab - Search for tab... + Search for tab Toggle always on top mode @@ -450,10 +450,10 @@ Toggle command palette - Recent commands... + Recent commands - Open suggestions... + Open suggestions Toggle command palette in command line mode @@ -505,7 +505,7 @@ Break into the debugger - Open settings... + Open settings Rename window to "{0}" @@ -515,7 +515,7 @@ Reset window name - Rename window... + Rename window Display Terminal's current working directory @@ -566,7 +566,7 @@ Quit the Terminal - Set background opacity... + Set background opacity Increase background opacity by {0}% From 4fd15c993719104641cf4ae16be0e4d28887b4f2 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 10 Apr 2024 17:12:40 +0200 Subject: [PATCH 217/603] Remove dependency on IsGlyphFullWidth for IRM/DECSWL (#16903) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gets rid off the implicit dependency on `IsGlyphFullWidth` for the IRM and DECSWL/DECDWL/DECDHL implementations. ## Validation Steps Performed In pwsh: * ``"`e[31mab`e[m`b`e[4h`e[32m$('*'*10)`e[m`e[4l"`` prints a red "a", 10 green "*" and a red "b" ✅ * ``"`e[31mab`e[m`b`e[4h`e[32m$('*'*1000)`e[m`e[4l"`` prints a red "a" and a couple lines of green "*" ✅ * ``"`e[31mf$('o'*70)`e[m`e#6`e#5"`` the right half of the row is erased ✅ --- .github/actions/spelling/expect/expect.txt | 3 + src/buffer/out/Row.cpp | 9 +- src/buffer/out/Row.hpp | 1 - src/buffer/out/textBuffer.cpp | 37 +++++++- src/buffer/out/textBuffer.hpp | 3 +- .../out/ut_textbuffer/UTextAdapterTests.cpp | 2 +- src/host/_stream.cpp | 2 +- src/host/readDataCooked.cpp | 12 +-- src/host/ut_host/TextBufferTests.cpp | 91 ++++++++++++++++++- src/inc/til/rle.h | 12 --- src/terminal/adapter/adaptDispatch.cpp | 29 ++---- 11 files changed, 142 insertions(+), 59 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 1ce77a78ad5..e458086ce89 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1,3 +1,4 @@ +aaaaabbb aabbcc ABANDONFONT abbcc @@ -92,6 +93,8 @@ backported backstory barbaz Bazz +bbb +bbccb BBDM bbwe bcount diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 8e318da41ef..4793b86dac9 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -349,12 +349,6 @@ void ROW::_init() noexcept #pragma warning(push) } -void ROW::TransferAttributes(const til::small_rle& attr, til::CoordType newWidth) -{ - _attr = attr; - _attr.resize_trailing_extent(gsl::narrow(newWidth)); -} - void ROW::CopyFrom(const ROW& source) { _lineRendition = source._lineRendition; @@ -366,7 +360,8 @@ void ROW::CopyFrom(const ROW& source) }; CopyTextFrom(state); - TransferAttributes(source.Attributes(), _columnCount); + _attr = source.Attributes(); + _attr.resize_trailing_extent(_columnCount); } // Returns the previous possible cursor position, preceding the given column. diff --git a/src/buffer/out/Row.hpp b/src/buffer/out/Row.hpp index d26fc87e7ed..317c935edce 100644 --- a/src/buffer/out/Row.hpp +++ b/src/buffer/out/Row.hpp @@ -132,7 +132,6 @@ class ROW final til::CoordType GetReadableColumnCount() const noexcept; void Reset(const TextAttribute& attr) noexcept; - void TransferAttributes(const til::small_rle& attr, til::CoordType newWidth); void CopyFrom(const ROW& source); til::CoordType NavigateToPrevious(til::CoordType column) const noexcept; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 713cf2a09a9..bb2be49fd1f 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -562,7 +562,7 @@ til::point TextBuffer::NavigateCursor(til::point position, til::CoordType distan // This function is intended for writing regular "lines" of text as it'll set the wrap flag on the given row. // You can continue calling the function on the same row as long as state.columnEnd < state.columnLimit. -void TextBuffer::Write(til::CoordType row, const TextAttribute& attributes, RowWriteState& state) +void TextBuffer::Replace(til::CoordType row, const TextAttribute& attributes, RowWriteState& state) { auto& r = GetMutableRowByOffset(row); r.ReplaceText(state); @@ -570,6 +570,36 @@ void TextBuffer::Write(til::CoordType row, const TextAttribute& attributes, RowW TriggerRedraw(Viewport::FromExclusive({ state.columnBeginDirty, row, state.columnEndDirty, row + 1 })); } +void TextBuffer::Insert(til::CoordType row, const TextAttribute& attributes, RowWriteState& state) +{ + auto& r = GetMutableRowByOffset(row); + auto& scratch = GetScratchpadRow(); + + scratch.CopyFrom(r); + + r.ReplaceText(state); + r.ReplaceAttributes(state.columnBegin, state.columnEnd, attributes); + + // Restore trailing text from our backup in scratch. + RowWriteState restoreState{ + .text = scratch.GetText(state.columnBegin, state.columnLimit), + .columnBegin = state.columnEnd, + .columnLimit = state.columnLimit, + }; + r.ReplaceText(restoreState); + + // Restore trailing attributes as well. + if (const auto copyAmount = restoreState.columnEnd - restoreState.columnBegin; copyAmount > 0) + { + auto& rowAttr = r.Attributes(); + const auto& scratchAttr = scratch.Attributes(); + const auto restoreAttr = scratchAttr.slice(gsl::narrow(state.columnBegin), gsl::narrow(state.columnBegin + copyAmount)); + rowAttr.replace(gsl::narrow(restoreState.columnBegin), gsl::narrow(restoreState.columnEnd), restoreAttr); + } + + TriggerRedraw(Viewport::FromExclusive({ state.columnBeginDirty, row, restoreState.columnEndDirty, row + 1 })); +} + // Fills an area of the buffer with a given fill character(s) and attributes. void TextBuffer::FillRect(const til::rect& rect, const std::wstring_view& fill, const TextAttribute& attributes) { @@ -1083,11 +1113,8 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition, cons // And if it's no longer single width, the right half of the row should be erased. if (lineRendition != LineRendition::SingleWidth) { - const auto fillChar = L' '; const auto fillOffset = GetLineWidth(rowIndex); - const auto fillLength = gsl::narrow(GetSize().Width() - fillOffset); - const OutputCellIterator fillData{ fillChar, fillAttributes, fillLength }; - row.WriteCells(fillData, fillOffset, false); + FillRect({ fillOffset, rowIndex, til::CoordTypeMax, rowIndex + 1 }, L" ", fillAttributes); // We also need to make sure the cursor is clamped within the new width. GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition)); } diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 79872cea0c4..2e1d0532215 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -107,7 +107,8 @@ class TextBuffer final til::point NavigateCursor(til::point position, til::CoordType distance) const; // Text insertion functions - void Write(til::CoordType row, const TextAttribute& attributes, RowWriteState& state); + void Replace(til::CoordType row, const TextAttribute& attributes, RowWriteState& state); + void Insert(til::CoordType row, const TextAttribute& attributes, RowWriteState& state); void FillRect(const til::rect& rect, const std::wstring_view& fill, const TextAttribute& attributes); OutputCellIterator Write(const OutputCellIterator givenIt); diff --git a/src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp b/src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp index ee879f4f677..427e90e6dac 100644 --- a/src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp +++ b/src/buffer/out/ut_textbuffer/UTextAdapterTests.cpp @@ -41,7 +41,7 @@ class UTextAdapterTests RowWriteState state{ .text = L"abc 𝒶𝒷𝒸 abc ネコちゃん", }; - buffer.Write(0, TextAttribute{}, state); + buffer.Replace(0, TextAttribute{}, state); VERIFY_IS_TRUE(state.text.empty()); static constexpr auto s = [](til::CoordType beg, til::CoordType end) -> til::point_span { diff --git a/src/host/_stream.cpp b/src/host/_stream.cpp index 8d884e7136e..e030cec03c7 100644 --- a/src/host/_stream.cpp +++ b/src/host/_stream.cpp @@ -125,7 +125,7 @@ void _writeCharsLegacyUnprocessed(SCREEN_INFORMATION& screenInfo, const std::wst auto cursorPosition = textBuffer.GetCursor().GetPosition(); state.columnBegin = cursorPosition.x; - textBuffer.Write(cursorPosition.y, textBuffer.GetCurrentAttributes(), state); + textBuffer.Replace(cursorPosition.y, textBuffer.GetCurrentAttributes(), state); cursorPosition.x = state.columnEnd; if (wrapAtEOL && state.columnEnd >= state.columnLimit) diff --git a/src/host/readDataCooked.cpp b/src/host/readDataCooked.cpp index 4aa458b1cdf..6028acf074c 100644 --- a/src/host/readDataCooked.cpp +++ b/src/host/readDataCooked.cpp @@ -1192,13 +1192,13 @@ try buffer.front() = L'┌'; buffer.back() = L'┐'; state.text = buffer; - textBuffer.Write(contentRect.top - 1, attributes, state); + textBuffer.Replace(contentRect.top - 1, attributes, state); // bottom line └───┘ buffer.front() = L'└'; buffer.back() = L'┘'; state.text = buffer; - textBuffer.Write(contentRect.bottom, attributes, state); + textBuffer.Replace(contentRect.bottom, attributes, state); // middle lines │ │ buffer.assign(widthSizeT, L' '); @@ -1207,7 +1207,7 @@ try for (til::CoordType y = contentRect.top; y < contentRect.bottom; ++y) { state.text = buffer; - textBuffer.Write(y, attributes, state); + textBuffer.Replace(y, attributes, state); } } @@ -1391,7 +1391,7 @@ void COOKED_READ_DATA::_popupHandleCommandNumberInput(Popup& popup, const wchar_ .columnBegin = popup.contentRect.right - CommandNumberMaxInputLength, .columnLimit = popup.contentRect.right, }; - _screenInfo.GetTextBuffer().Write(popup.contentRect.top, _screenInfo.GetPopupAttributes(), state); + _screenInfo.GetTextBuffer().Replace(popup.contentRect.top, _screenInfo.GetPopupAttributes(), state); } } @@ -1474,7 +1474,7 @@ void COOKED_READ_DATA::_popupDrawPrompt(const Popup& popup, const UINT id) const .columnBegin = popup.contentRect.left, .columnLimit = popup.contentRect.right, }; - _screenInfo.GetTextBuffer().Write(popup.contentRect.top, _screenInfo.GetPopupAttributes(), state); + _screenInfo.GetTextBuffer().Replace(popup.contentRect.top, _screenInfo.GetPopupAttributes(), state); } void COOKED_READ_DATA::_popupDrawCommandList(Popup& popup) const @@ -1533,7 +1533,7 @@ void COOKED_READ_DATA::_popupDrawCommandList(Popup& popup) const buffer.append(width, L' '); state.text = buffer; - _screenInfo.GetTextBuffer().Write(y, attr, state); + _screenInfo.GetTextBuffer().Replace(y, attr, state); } cl.dirtyHeight = height; diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index 33660d876a8..8f837985b70 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -147,7 +147,8 @@ class TextBufferTests TEST_METHOD(TestBurrito); TEST_METHOD(TestOverwriteChars); - TEST_METHOD(TestRowReplaceText); + TEST_METHOD(TestReplace); + TEST_METHOD(TestInsert); TEST_METHOD(TestAppendRTFText); @@ -2005,13 +2006,12 @@ void TextBufferTests::TestOverwriteChars() #undef complex1 } -void TextBufferTests::TestRowReplaceText() +void TextBufferTests::TestReplace() { static constexpr til::size bufferSize{ 10, 3 }; static constexpr UINT cursorSize = 12; const TextAttribute attr{ 0x7f }; TextBuffer buffer{ bufferSize, attr, cursorSize, false, _renderer }; - auto& row = buffer.GetMutableRowByOffset(0); #define complex L"\U0001F41B" @@ -2075,17 +2075,98 @@ void TextBufferTests::TestRowReplaceText() .columnBegin = t.input.columnBegin, .columnLimit = t.input.columnLimit, }; - row.ReplaceText(actual); + buffer.Replace(0, attr, actual); VERIFY_ARE_EQUAL(t.expected.text, actual.text); VERIFY_ARE_EQUAL(t.expected.columnEnd, actual.columnEnd); VERIFY_ARE_EQUAL(t.expected.columnBeginDirty, actual.columnBeginDirty); VERIFY_ARE_EQUAL(t.expected.columnEndDirty, actual.columnEndDirty); - VERIFY_ARE_EQUAL(t.expectedRow, row.GetText()); + VERIFY_ARE_EQUAL(t.expectedRow, buffer.GetRowByOffset(0).GetText()); } #undef complex } +void TextBufferTests::TestInsert() +{ + static constexpr til::size bufferSize{ 10, 3 }; + static constexpr UINT cursorSize = 12; + static constexpr TextAttribute attr1{ 0x11111111, 0x00000000 }; + static constexpr TextAttribute attr2{ 0x22222222, 0x00000000 }; + static constexpr TextAttribute attr3{ 0x33333333, 0x00000000 }; + TextBuffer buffer{ bufferSize, attr1, cursorSize, false, _renderer }; + + struct Test + { + const wchar_t* description; + struct + { + std::wstring_view text; + til::CoordType columnBegin = 0; + til::CoordType columnLimit = 0; + TextAttribute attr; + } input; + struct + { + std::wstring_view text; + til::CoordType columnEnd = 0; + til::CoordType columnBeginDirty = 0; + til::CoordType columnEndDirty = 0; + } expected; + std::wstring_view expectedRow; + }; + + static constexpr std::array tests{ + Test{ + L"Not enough space -> early exit", + { L"aaa", 5, 5, attr1 }, + { L"aaa", 5, 5, 5 }, + L" ", + }, + Test{ + L"Too much to fit", + { L"aaaaabbb", 0, 5, attr1 }, + { L"bbb", 5, 0, 5 }, + L"aaaaa ", + }, + Test{ + L"Wide char intersects limit", + { L"bbbb😄", 0, 5, attr2 }, + { L"😄", 5, 0, 5 }, + L"bbbb ", + }, + Test{ + L"Insert middle", + { L"cc", 2, 5, attr3 }, + { L"", 4, 2, 4 }, + L"bbccb ", + }, + }; + + for (const auto& t : tests) + { + Log::Comment(t.description); + RowWriteState actual{ + .text = t.input.text, + .columnBegin = t.input.columnBegin, + .columnLimit = t.input.columnLimit, + }; + buffer.Insert(0, t.input.attr, actual); + VERIFY_ARE_EQUAL(t.expected.text, actual.text); + VERIFY_ARE_EQUAL(t.expected.columnEnd, actual.columnEnd); + VERIFY_ARE_EQUAL(t.expected.columnBeginDirty, actual.columnBeginDirty); + VERIFY_ARE_EQUAL(t.expected.columnEndDirty, actual.columnEndDirty); + VERIFY_ARE_EQUAL(t.expectedRow, buffer.GetRowByOffset(0).GetText()); + } + + auto& scratch = buffer.GetScratchpadRow(); + scratch.ReplaceAttributes(0, 5, attr2); + scratch.ReplaceAttributes(2, 4, attr3); + + const auto& expectedAttr = scratch.Attributes(); + const auto& actualAttr = buffer.GetRowByOffset(0).Attributes(); + VERIFY_ARE_EQUAL(expectedAttr, actualAttr); +} + void TextBufferTests::TestAppendRTFText() { { diff --git a/src/inc/til/rle.h b/src/inc/til/rle.h index 99bdf734c45..b91b11b8a34 100644 --- a/src/inc/til/rle.h +++ b/src/inc/til/rle.h @@ -1042,18 +1042,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" #ifdef __WEX_COMMON_H__ namespace WEX::TestExecution { - template - class VerifyOutputTraits<::til::basic_rle> - { - using rle_vector = ::til::basic_rle; - - public: - static WEX::Common::NoThrowString ToString(const rle_vector& object) - { - return WEX::Common::NoThrowString(object.to_string().c_str()); - } - }; - template class VerifyCompareTraits<::til::basic_rle, ::til::basic_rle> { diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index cdf50148ef3..818261cf112 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -5,7 +5,6 @@ #include "adaptDispatch.hpp" #include "../../renderer/base/renderer.hpp" -#include "../../types/inc/GlyphWidth.hpp" #include "../../types/inc/Viewport.hpp" #include "../../types/inc/utils.hpp" #include "../../inc/unicode.hpp" @@ -119,26 +118,17 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) } } - if (_modes.test(Mode::InsertReplace)) - { - // If insert-replace mode is enabled, we first measure how many cells - // the string will occupy, and scroll the target area right by that - // amount to make space for the incoming text. - const OutputCellIterator it(state.text, attributes); - auto measureIt = it; - while (measureIt && measureIt.GetCellDistance(it) < state.columnLimit) - { - ++measureIt; - } - const auto row = cursorPosition.y; - const auto cellCount = measureIt.GetCellDistance(it); - _ScrollRectHorizontally(textBuffer, { cursorPosition.x, row, state.columnLimit, row + 1 }, cellCount); - } - state.columnBegin = cursorPosition.x; const auto textPositionBefore = state.text.data(); - textBuffer.Write(cursorPosition.y, attributes, state); + if (_modes.test(Mode::InsertReplace)) + { + textBuffer.Insert(cursorPosition.y, attributes, state); + } + else + { + textBuffer.Replace(cursorPosition.y, attributes, state); + } const auto textPositionAfter = state.text.data(); // TODO: A row should not be marked as wrapped just because we wrote the last column. @@ -190,8 +180,7 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) // Notify UIA of new text. // It's important to do this here instead of in TextBuffer, because here you - // have access to the entire line of text, whereas TextBuffer writes it one - // character at a time via the OutputCellIterator. + // have access to the entire line of text. textBuffer.TriggerNewTextNotification(string); } From 20b0bed46df71c596b4fb68edfb6bb4b4ebf1c89 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 10 Apr 2024 20:51:02 +0200 Subject: [PATCH 218/603] Reduce cost of cursor invalidation (#15500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Performance of printing enwik8.txt at the following block sizes: 4KiB (printf): 53MB/s -> 58MB/s 128KiB (cat): 170MB/s -> 235MB/s This commit is imperfect. Support for more than one rendering engine was "hacked" into `Renderer` and is not quite correct. As such, this commit cannot fix cursor invalidation correctly either, and while some bugs are fixed (engines may see highly inconsistent TextBuffer and Cursor states), it introduces others (an error in the first engine may result in the second engine not executing). Neither of those are good and the underlying issue remains to be fixed. ## Validation Steps Performed * Seems ok? ✅ --- src/buffer/out/LineRendition.hpp | 11 +- src/buffer/out/cursor.cpp | 6 +- src/buffer/out/textBuffer.cpp | 20 +- src/buffer/out/textBuffer.hpp | 2 +- src/cascadia/TerminalControl/ControlCore.cpp | 6 +- src/cascadia/TerminalControl/HwndTerminal.cpp | 11 +- src/host/getset.cpp | 2 +- src/renderer/base/renderer.cpp | 190 +++++++++--------- src/renderer/base/renderer.hpp | 3 +- 9 files changed, 125 insertions(+), 126 deletions(-) diff --git a/src/buffer/out/LineRendition.hpp b/src/buffer/out/LineRendition.hpp index 954bd38509f..564f39f03f0 100644 --- a/src/buffer/out/LineRendition.hpp +++ b/src/buffer/out/LineRendition.hpp @@ -23,21 +23,24 @@ enum class LineRendition : uint8_t constexpr til::inclusive_rect ScreenToBufferLine(const til::inclusive_rect& line, const LineRendition lineRendition) { - // Use shift right to quickly divide the Left and Right by 2 for double width lines. const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1; return { line.left >> scale, line.top, line.right >> scale, line.bottom }; } -constexpr til::point ScreenToBufferLine(const til::point& line, const LineRendition lineRendition) +constexpr til::point ScreenToBufferLineInclusive(const til::point& line, const LineRendition lineRendition) { - // Use shift right to quickly divide the Left and Right by 2 for double width lines. const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1; return { line.x >> scale, line.y }; } +constexpr til::rect BufferToScreenLine(const til::rect& line, const LineRendition lineRendition) +{ + const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1; + return { line.left << scale, line.top, line.right << scale, line.bottom }; +} + constexpr til::inclusive_rect BufferToScreenLine(const til::inclusive_rect& line, const LineRendition lineRendition) { - // Use shift left to quickly multiply the Left and Right by 2 for double width lines. const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1; return { line.left << scale, line.top, (line.right << scale) + scale, line.bottom }; } diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 9084a60003d..273c05f8ea1 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -188,11 +188,7 @@ void Cursor::_RedrawCursor() noexcept // - void Cursor::_RedrawCursorAlways() noexcept { - try - { - _parentBuffer.TriggerRedrawCursor(_cPosition); - } - CATCH_LOG(); + _parentBuffer.NotifyPaintFrame(); } void Cursor::SetPosition(const til::point cPosition) noexcept diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index bb2be49fd1f..1b5b28c621b 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1265,19 +1265,19 @@ Microsoft::Console::Render::Renderer& TextBuffer::GetRenderer() noexcept return _renderer; } -void TextBuffer::TriggerRedraw(const Viewport& viewport) +void TextBuffer::NotifyPaintFrame() noexcept { if (_isActiveBuffer) { - _renderer.TriggerRedraw(viewport); + _renderer.NotifyPaintFrame(); } } -void TextBuffer::TriggerRedrawCursor(const til::point position) +void TextBuffer::TriggerRedraw(const Viewport& viewport) { if (_isActiveBuffer) { - _renderer.TriggerRedrawCursor(&position); + _renderer.TriggerRedraw(viewport); } } @@ -1920,8 +1920,8 @@ std::vector TextBuffer::GetTextSpans(til::point start, til::poi // equivalent buffer offsets, taking line rendition into account. if (!bufferCoordinates) { - higherCoord = ScreenToBufferLine(higherCoord, GetLineRendition(higherCoord.y)); - lowerCoord = ScreenToBufferLine(lowerCoord, GetLineRendition(lowerCoord.y)); + higherCoord = ScreenToBufferLineInclusive(higherCoord, GetLineRendition(higherCoord.y)); + lowerCoord = ScreenToBufferLineInclusive(lowerCoord, GetLineRendition(lowerCoord.y)); } til::inclusive_rect asRect = { higherCoord.x, higherCoord.y, lowerCoord.x, lowerCoord.y }; @@ -2032,8 +2032,8 @@ std::tuple TextBuffer::_RowCopyHelper(cons if (req.blockSelection) { const auto lineRendition = row.GetLineRendition(); - const auto minX = req.bufferCoordinates ? req.minX : ScreenToBufferLine(til::point{ req.minX, iRow }, lineRendition).x; - const auto maxX = req.bufferCoordinates ? req.maxX : ScreenToBufferLine(til::point{ req.maxX, iRow }, lineRendition).x; + const auto minX = req.bufferCoordinates ? req.minX : ScreenToBufferLineInclusive(til::point{ req.minX, iRow }, lineRendition).x; + const auto maxX = req.bufferCoordinates ? req.maxX : ScreenToBufferLineInclusive(til::point{ req.maxX, iRow }, lineRendition).x; rowBeg = minX; rowEnd = maxX + 1; // +1 to get an exclusive end @@ -2041,8 +2041,8 @@ std::tuple TextBuffer::_RowCopyHelper(cons else { const auto lineRendition = row.GetLineRendition(); - const auto beg = req.bufferCoordinates ? req.beg : ScreenToBufferLine(req.beg, lineRendition); - const auto end = req.bufferCoordinates ? req.end : ScreenToBufferLine(req.end, lineRendition); + const auto beg = req.bufferCoordinates ? req.beg : ScreenToBufferLineInclusive(req.beg, lineRendition); + const auto end = req.bufferCoordinates ? req.end : ScreenToBufferLineInclusive(req.end, lineRendition); rowBeg = iRow != beg.y ? 0 : beg.x; rowEnd = iRow != end.y ? row.GetReadableColumnCount() : end.x + 1; // +1 to get an exclusive end diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 2e1d0532215..c7b68926ef8 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -169,8 +169,8 @@ class TextBuffer final Microsoft::Console::Render::Renderer& GetRenderer() noexcept; + void NotifyPaintFrame() noexcept; void TriggerRedraw(const Microsoft::Console::Types::Viewport& viewport); - void TriggerRedrawCursor(const til::point position); void TriggerRedrawAll(); void TriggerScroll(); void TriggerScroll(const til::point delta); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index fbe2eaafb77..8a71034217c 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -218,10 +218,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { Close(); - if (_renderer) - { - _renderer->TriggerTeardown(); - } + _renderer.reset(); + _renderEngine.reset(); } void ControlCore::Detach() diff --git a/src/cascadia/TerminalControl/HwndTerminal.cpp b/src/cascadia/TerminalControl/HwndTerminal.cpp index bd05b7ca8a2..598028a88eb 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.cpp +++ b/src/cascadia/TerminalControl/HwndTerminal.cpp @@ -248,15 +248,8 @@ try // This ensures that teardown is reentrant. // Shut down the renderer (and therefore the thread) before we implode - if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) }) - { - if (auto localRenderer{ std::exchange(_renderer, nullptr) }) - { - localRenderer->TriggerTeardown(); - // renderer is destroyed - } - // renderEngine is destroyed - } + _renderer.reset(); + _renderEngine.reset(); if (auto localHwnd{ _hwnd.release() }) { diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 495771fdad3..304b4fd65c7 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -730,7 +730,7 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont // When evaluating the X offset, we must convert the buffer position to // equivalent screen coordinates, taking line rendition into account. const auto lineRendition = buffer.GetTextBuffer().GetLineRendition(position.y); - const auto screenPosition = BufferToScreenLine({ position.x, position.y, position.x, position.y }, lineRendition); + const auto screenPosition = BufferToScreenLine(til::inclusive_rect{ position.x, position.y, position.x, position.y }, lineRendition); if (currentViewport.left > screenPosition.left) { diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index c7c5c491815..4137d140d36 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -64,44 +64,90 @@ Renderer::~Renderer() // - HRESULT S_OK, GDI error, Safe Math error, or state/argument errors. [[nodiscard]] HRESULT Renderer::PaintFrame() { - FOREACH_ENGINE(pEngine) + auto tries = maxRetriesForRenderEngine; + while (tries > 0) { - auto tries = maxRetriesForRenderEngine; - while (tries > 0) + if (_destructing) { - if (_destructing) - { - return S_FALSE; - } + return S_FALSE; + } + + // BODGY: Optimally we would want to retry per engine, but that causes different + // problems (intermittent inconsistent states between text renderer and UIA output, + // not being able to lock the cursor location, etc.). + const auto hr = _PaintFrame(); + if (SUCCEEDED(hr)) + { + break; + } + + LOG_HR_IF(hr, hr != E_PENDING); - const auto hr = _PaintFrameForEngine(pEngine); - if (SUCCEEDED(hr)) + if (--tries == 0) + { + // Stop trying. + _pThread->DisablePainting(); + if (_pfnRendererEnteredErrorState) { - break; + _pfnRendererEnteredErrorState(); } + // If there's no callback, we still don't want to FAIL_FAST: the renderer going black + // isn't near as bad as the entire application aborting. We're a component. We shouldn't + // abort applications that host us. + return S_FALSE; + } + + // Add a bit of backoff. + // Sleep 150ms, 300ms, 450ms before failing out and disabling the renderer. + Sleep(renderBackoffBaseTimeMilliseconds * (maxRetriesForRenderEngine - tries)); + } - LOG_HR_IF(hr, hr != E_PENDING); + return S_OK; +} - if (--tries == 0) +[[nodiscard]] HRESULT Renderer::_PaintFrame() noexcept +{ + { + _pData->LockConsole(); + auto unlock = wil::scope_exit([&]() { + _pData->UnlockConsole(); + }); + + // Last chance check if anything scrolled without an explicit invalidate notification since the last frame. + _CheckViewportAndScroll(); + + if (_currentCursorOptions) + { + const auto coord = _currentCursorOptions->coordCursor; + const auto& buffer = _pData->GetTextBuffer(); + const auto lineRendition = buffer.GetLineRendition(coord.y); + const auto cursorWidth = _pData->IsCursorDoubleWidth() ? 2 : 1; + + til::rect cursorRect{ coord.x, coord.y, coord.x + cursorWidth, coord.y + 1 }; + cursorRect = BufferToScreenLine(cursorRect, lineRendition); + + if (buffer.GetSize().TrimToViewport(&cursorRect)) { - // Stop trying. - _pThread->DisablePainting(); - if (_pfnRendererEnteredErrorState) + FOREACH_ENGINE(pEngine) { - _pfnRendererEnteredErrorState(); + LOG_IF_FAILED(pEngine->InvalidateCursor(&cursorRect)); } - // If there's no callback, we still don't want to FAIL_FAST: the renderer going black - // isn't near as bad as the entire application aborting. We're a component. We shouldn't - // abort applications that host us. - return S_FALSE; } + } - // Add a bit of backoff. - // Sleep 150ms, 300ms, 450ms before failing out and disabling the renderer. - Sleep(renderBackoffBaseTimeMilliseconds * (maxRetriesForRenderEngine - tries)); + _currentCursorOptions = _GetCursorInfo(); + + FOREACH_ENGINE(pEngine) + { + RETURN_IF_FAILED(_PaintFrameForEngine(pEngine)); } } + FOREACH_ENGINE(pEngine) + { + RETURN_IF_FAILED(pEngine->Present()); + } + return S_OK; } @@ -110,14 +156,6 @@ try { FAIL_FAST_IF_NULL(pEngine); // This is a programming error. Fail fast. - _pData->LockConsole(); - auto unlock = wil::scope_exit([&]() { - _pData->UnlockConsole(); - }); - - // Last chance check if anything scrolled without an explicit invalidate notification since the last frame. - _CheckViewportAndScroll(); - // Try to start painting a frame const auto hr = pEngine->StartPaint(); RETURN_IF_FAILED(hr); @@ -172,12 +210,6 @@ try // Force scope exit end paint to finish up collecting information and possibly painting endPaint.reset(); - // Force scope exit unlock to let go of global lock so other threads can run - unlock.reset(); - - // Trigger out-of-lock presentation for renderers that can support it - RETURN_IF_FAILED(pEngine->Present()); - // As we leave the scope, EndPaint will be called (declared above) return S_OK; } @@ -255,47 +287,6 @@ void Renderer::TriggerRedraw(const til::point* const pcoord) TriggerRedraw(Viewport::FromCoord(*pcoord)); // this will notify to paint if we need it. } -// Routine Description: -// - Called when the cursor has moved in the buffer. Allows for RenderEngines to -// differentiate between cursor movements and other invalidates. -// Visual Renderers (ex GDI) should invalidate the position, while the VT -// engine ignores this. See MSFT:14711161. -// Arguments: -// - pcoord: The buffer-space position of the cursor. -// Return Value: -// - -void Renderer::TriggerRedrawCursor(const til::point* const pcoord) -{ - // We first need to make sure the cursor position is within the buffer, - // otherwise testing for a double width character can throw an exception. - const auto& buffer = _pData->GetTextBuffer(); - if (buffer.GetSize().IsInBounds(*pcoord)) - { - // We then calculate the region covered by the cursor. This requires - // converting the buffer coordinates to an equivalent range of screen - // cells for the cursor, taking line rendition into account. - const auto lineRendition = buffer.GetLineRendition(pcoord->y); - const auto cursorWidth = _pData->IsCursorDoubleWidth() ? 2 : 1; - til::inclusive_rect cursorRect = { pcoord->x, pcoord->y, pcoord->x + cursorWidth - 1, pcoord->y }; - cursorRect = BufferToScreenLine(cursorRect, lineRendition); - - // That region is then clipped within the viewport boundaries and we - // only trigger a redraw if the resulting region is not empty. - auto view = _pData->GetViewport(); - auto updateRect = til::rect{ cursorRect }; - if (view.TrimToViewport(&updateRect)) - { - view.ConvertToOrigin(&updateRect); - FOREACH_ENGINE(pEngine) - { - LOG_IF_FAILED(pEngine->InvalidateCursor(&updateRect)); - } - - NotifyPaintFrame(); - } - } -} - // Routine Description: // - Called when something that changes the output state has occurred and the entire frame is now potentially invalid. // - NOTE: Use sparingly. Try to reduce the refresh region where possible. Only use when a global state change has occurred. @@ -336,6 +327,8 @@ void Renderer::TriggerTeardown() noexcept // We need to shut down the paint thread on teardown. _pThread->WaitForPaintCompletionAndDisable(INFINITE); + auto repaint = false; + // Then walk through and do one final paint on the caller's thread. FOREACH_ENGINE(pEngine) { @@ -343,10 +336,15 @@ void Renderer::TriggerTeardown() noexcept auto hr = pEngine->PrepareForTeardown(&fEngineRequestsRepaint); LOG_IF_FAILED(hr); - if (SUCCEEDED(hr) && fEngineRequestsRepaint) - { - LOG_IF_FAILED(_PaintFrameForEngine(pEngine)); - } + repaint |= SUCCEEDED(hr) && fEngineRequestsRepaint; + } + + // BODGY: The only time repaint is true is when VtEngine is used. + // Coincidentally VtEngine always runs alone, so if repaint is true, there's only a single engine + // to repaint anyways and there's no danger is accidentally repainting an engine that didn't want to. + if (repaint) + { + LOG_IF_FAILED(_PaintFrame()); } } @@ -468,6 +466,7 @@ void Renderer::TriggerScroll(const til::point* const pcoordDelta) void Renderer::TriggerFlush(const bool circling) { const auto rects = _GetSelectionRects(); + auto repaint = false; FOREACH_ENGINE(pEngine) { @@ -477,10 +476,15 @@ void Renderer::TriggerFlush(const bool circling) LOG_IF_FAILED(pEngine->InvalidateSelection(rects)); - if (SUCCEEDED(hr) && fEngineRequestsRepaint) - { - LOG_IF_FAILED(_PaintFrameForEngine(pEngine)); - } + repaint |= SUCCEEDED(hr) && fEngineRequestsRepaint; + } + + // BODGY: The only time repaint is true is when VtEngine is used. + // Coincidentally VtEngine always runs alone, so if repaint is true, there's only a single engine + // to repaint anyways and there's no danger is accidentally repainting an engine that didn't want to. + if (repaint) + { + LOG_IF_FAILED(_PaintFrame()); } } @@ -642,7 +646,6 @@ void Renderer::EnablePainting() // When the renderer is constructed, the initial viewport won't be available yet, // but once EnablePainting is called it should be safe to retrieve. _viewport = _pData->GetViewport(); - _forceUpdateViewport = true; // When running the unit tests, we may be using a render without a render thread. if (_pThread) @@ -1106,10 +1109,9 @@ bool Renderer::_isInHoveredInterval(const til::point coordTarget) const noexcept // - void Renderer::_PaintCursor(_In_ IRenderEngine* const pEngine) { - const auto cursorInfo = _GetCursorInfo(); - if (cursorInfo.has_value()) + if (_currentCursorOptions) { - LOG_IF_FAILED(pEngine->PaintCursor(cursorInfo.value())); + LOG_IF_FAILED(pEngine->PaintCursor(*_currentCursorOptions)); } } @@ -1127,7 +1129,7 @@ void Renderer::_PaintCursor(_In_ IRenderEngine* const pEngine) [[nodiscard]] HRESULT Renderer::_PrepareRenderInfo(_In_ IRenderEngine* const pEngine) { RenderFrameInfo info; - info.cursorInfo = _GetCursorInfo(); + info.cursorInfo = _currentCursorOptions; return pEngine->PrepareRenderInfo(info); } @@ -1340,6 +1342,11 @@ void Renderer::_ScrollPreviousSelection(const til::point delta) { rc += delta; } + + if (_currentCursorOptions) + { + _currentCursorOptions->coordCursor += delta; + } } } @@ -1361,6 +1368,7 @@ void Renderer::AddRenderEngine(_In_ IRenderEngine* const pEngine) if (!p) { p = pEngine; + _forceUpdateViewport = true; return; } } diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 1cd61799a8f..dc027acea20 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -50,7 +50,6 @@ namespace Microsoft::Console::Render void TriggerSystemRedraw(const til::rect* const prcDirtyClient); void TriggerRedraw(const Microsoft::Console::Types::Viewport& region); void TriggerRedraw(const til::point* const pcoord); - void TriggerRedrawCursor(const til::point* const pcoord); void TriggerRedrawAll(const bool backgroundChanged = false, const bool frameChanged = false); void TriggerTeardown() noexcept; @@ -96,6 +95,7 @@ namespace Microsoft::Console::Render static GridLineSet s_GetGridlines(const TextAttribute& textAttribute) noexcept; static bool s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar); + [[nodiscard]] HRESULT _PaintFrame() noexcept; [[nodiscard]] HRESULT _PaintFrameForEngine(_In_ IRenderEngine* const pEngine) noexcept; bool _CheckViewportAndScroll(); [[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine); @@ -126,6 +126,7 @@ namespace Microsoft::Console::Render uint16_t _hyperlinkHoveredId = 0; std::optional::interval> _hoveredInterval; Microsoft::Console::Types::Viewport _viewport; + std::optional _currentCursorOptions; std::vector _clusterBuffer; std::vector _previousSelection; std::vector _previousSearchSelection; From 5f3a857192b2fa6f4e5ae87eb3ee59a1571515a2 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 10 Apr 2024 21:35:11 +0200 Subject: [PATCH 219/603] Replace WinRT clipboard API with Win32 for copying (#17006) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the spirit of #15360 this implements the copy part. The problem is that we have an issue accessing the clipboard while other applications continue to work just fine. The major difference between us and the others is that we use the WinRT clipboard APIs. So, the idea is that we just use the Win32 APIs instead. The feel-good side-effect is that this is (no joke) 200-1000x faster, but I suspect no one will notice the -3ms difference down to <0.01ms. The objective effect however is that it just works. This may resolve #16982. ## Validation Steps Performed * Cycle through Text/HTML/RTF-only in the Interaction settings * Paste the contents into Word each time * Text is plain and HTML/RTF are colored ✅ --- src/cascadia/TerminalApp/TerminalPage.cpp | 52 -------- src/cascadia/TerminalApp/TerminalPage.h | 1 - src/cascadia/TerminalControl/ControlCore.cpp | 120 ++++++++++++++---- src/cascadia/TerminalControl/ControlCore.h | 3 +- src/cascadia/TerminalControl/ControlCore.idl | 1 - src/cascadia/TerminalControl/EventArgs.cpp | 1 - src/cascadia/TerminalControl/EventArgs.h | 28 ---- src/cascadia/TerminalControl/EventArgs.idl | 8 -- src/cascadia/TerminalControl/TermControl.cpp | 1 - src/cascadia/TerminalControl/TermControl.h | 2 - src/cascadia/TerminalControl/TermControl.idl | 1 - src/cascadia/TerminalCore/Terminal.cpp | 2 +- src/cascadia/TerminalCore/Terminal.hpp | 6 +- src/cascadia/TerminalCore/TerminalApi.cpp | 2 +- src/host/outputStream.cpp | 2 +- src/host/outputStream.hpp | 2 +- src/terminal/adapter/ITermDispatch.hpp | 2 +- src/terminal/adapter/ITerminalApi.hpp | 2 +- src/terminal/adapter/adaptDispatch.cpp | 2 +- src/terminal/adapter/adaptDispatch.hpp | 2 +- src/terminal/adapter/termDispatch.hpp | 2 +- .../adapter/ut_adapter/adapterTest.cpp | 2 +- .../parser/ut_parser/OutputEngineTest.cpp | 4 +- 23 files changed, 113 insertions(+), 135 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2f1b66b561a..ff4b6743e69 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1645,10 +1645,6 @@ namespace winrt::TerminalApp::implementation { term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler }); - // Add an event handler when the terminal's selection wants to be copied. - // When the text buffer data is retrieved, we'll copy the data into the Clipboard - term.CopyToClipboard({ this, &TerminalPage::_CopyToClipboardHandler }); - // Add an event handler when the terminal wants to paste data from the Clipboard. term.PasteFromClipboard({ this, &TerminalPage::_PasteFromClipboardHandler }); @@ -2556,54 +2552,6 @@ namespace winrt::TerminalApp::implementation return dimension; } - // Method Description: - // - Place `copiedData` into the clipboard as text. Triggered when a - // terminal control raises its CopyToClipboard event. - // Arguments: - // - copiedData: the new string content to place on the clipboard. - winrt::fire_and_forget TerminalPage::_CopyToClipboardHandler(const IInspectable /*sender*/, - const CopyToClipboardEventArgs copiedData) - { - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); - - auto dataPack = DataPackage(); - dataPack.RequestedOperation(DataPackageOperation::Copy); - - const auto copyFormats = copiedData.Formats() != nullptr ? - copiedData.Formats().Value() : - static_cast(0); - - // copy text to dataPack - dataPack.SetText(copiedData.Text()); - - if (WI_IsFlagSet(copyFormats, CopyFormat::HTML)) - { - // copy html to dataPack - const auto htmlData = copiedData.Html(); - if (!htmlData.empty()) - { - dataPack.SetHtmlFormat(htmlData); - } - } - - if (WI_IsFlagSet(copyFormats, CopyFormat::RTF)) - { - // copy rtf data to dataPack - const auto rtfData = copiedData.Rtf(); - if (!rtfData.empty()) - { - dataPack.SetRtf(rtfData); - } - } - - try - { - Clipboard::SetContent(dataPack); - Clipboard::Flush(); - } - CATCH_LOG(); - } - static wil::unique_close_clipboard_call _openClipboard(HWND hwnd) { bool success = false; diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 1fd2acfaee6..711d9f7b7ec 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -394,7 +394,6 @@ namespace winrt::TerminalApp::implementation void _ScrollToBufferEdge(ScrollDirection scrollDirection); void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Control::KeyChord& keyChord); - winrt::fire_and_forget _CopyToClipboardHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::CopyToClipboardEventArgs copiedData); winrt::fire_and_forget _PasteFromClipboardHandler(const IInspectable sender, const Microsoft::Terminal::Control::PasteFromClipboardEventArgs eventArgs); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 8a71034217c..c704b412310 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1219,11 +1219,87 @@ namespace winrt::Microsoft::Terminal::Control::implementation _updateSelectionUI(); } + static wil::unique_close_clipboard_call _openClipboard(HWND hwnd) + { + bool success = false; + + // OpenClipboard may fail to acquire the internal lock --> retry. + for (DWORD sleep = 10;; sleep *= 2) + { + if (OpenClipboard(hwnd)) + { + success = true; + break; + } + // 10 iterations + if (sleep > 10000) + { + break; + } + Sleep(sleep); + } + + return wil::unique_close_clipboard_call{ success }; + } + + static void _copyToClipboard(const UINT format, const void* src, const size_t bytes) + { + wil::unique_hglobal handle{ THROW_LAST_ERROR_IF_NULL(GlobalAlloc(GMEM_MOVEABLE, bytes)) }; + + const auto locked = GlobalLock(handle.get()); + memcpy(locked, src, bytes); + GlobalUnlock(handle.get()); + + THROW_LAST_ERROR_IF_NULL(SetClipboardData(format, handle.get())); + handle.release(); + } + + static void _copyToClipboardRegisteredFormat(const wchar_t* format, const void* src, size_t bytes) + { + const auto id = RegisterClipboardFormatW(format); + if (!id) + { + LOG_LAST_ERROR(); + return; + } + _copyToClipboard(id, src, bytes); + } + + static void copyToClipboard(wil::zwstring_view text, std::string_view html, std::string_view rtf) + { + const auto clipboard = _openClipboard(nullptr); + if (!clipboard) + { + LOG_LAST_ERROR(); + return; + } + + EmptyClipboard(); + + if (!text.empty()) + { + // As per: https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats + // CF_UNICODETEXT: [...] A null character signals the end of the data. + // --> We add +1 to the length. This works because .c_str() is null-terminated. + _copyToClipboard(CF_UNICODETEXT, text.c_str(), (text.size() + 1) * sizeof(wchar_t)); + } + + if (!html.empty()) + { + _copyToClipboardRegisteredFormat(L"HTML Format", html.data(), html.size()); + } + + if (!rtf.empty()) + { + _copyToClipboardRegisteredFormat(L"Rich Text Format", rtf.data(), rtf.size()); + } + } + // Called when the Terminal wants to set something to the clipboard, i.e. // when an OSC 52 is emitted. - void ControlCore::_terminalCopyToClipboard(std::wstring_view wstr) + void ControlCore::_terminalCopyToClipboard(wil::zwstring_view wstr) { - CopyToClipboard.raise(*this, winrt::make(winrt::hstring{ wstr })); + copyToClipboard(wstr, {}, {}); } // Method Description: @@ -1236,31 +1312,29 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ControlCore::CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference& formats) { - const auto lock = _terminal->LockForWriting(); - - // no selection --> nothing to copy - if (!_terminal->IsSelectionActive()) + ::Microsoft::Terminal::Core::Terminal::TextCopyData payload; { - return false; - } + const auto lock = _terminal->LockForWriting(); - // use action's copyFormatting if it's present, else fallback to globally - // set copyFormatting. - const auto copyFormats = formats != nullptr ? formats.Value() : _settings->CopyFormatting(); + // no selection --> nothing to copy + if (!_terminal->IsSelectionActive()) + { + return false; + } - const auto copyHtml = WI_IsFlagSet(copyFormats, CopyFormat::HTML); - const auto copyRtf = WI_IsFlagSet(copyFormats, CopyFormat::RTF); + // use action's copyFormatting if it's present, else fallback to globally + // set copyFormatting. + const auto copyFormats = formats != nullptr ? formats.Value() : _settings->CopyFormatting(); - // extract text from buffer - // RetrieveSelectedTextFromBuffer will lock while it's reading - const auto& [textData, htmlData, rtfData] = _terminal->RetrieveSelectedTextFromBuffer(singleLine, copyHtml, copyRtf); - - // send data up for clipboard - CopyToClipboard.raise(*this, - winrt::make(winrt::hstring{ textData }, - winrt::to_hstring(htmlData), - winrt::to_hstring(rtfData), - copyFormats)); + const auto copyHtml = WI_IsFlagSet(copyFormats, CopyFormat::HTML); + const auto copyRtf = WI_IsFlagSet(copyFormats, CopyFormat::RTF); + + // extract text from buffer + // RetrieveSelectedTextFromBuffer will lock while it's reading + payload = _terminal->RetrieveSelectedTextFromBuffer(singleLine, copyHtml, copyRtf); + } + + copyToClipboard(payload.plainText, payload.html, payload.rtf); return true; } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 9b6acfcc8e1..0faffb6953e 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -266,7 +266,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // clang-format off til::typed_event FontSizeChanged; - til::typed_event CopyToClipboard; til::typed_event TitleChanged; til::typed_event<> WarningBell; til::typed_event<> TabColorChanged; @@ -371,7 +370,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _sendInputToConnection(std::wstring_view wstr); #pragma region TerminalCoreCallbacks - void _terminalCopyToClipboard(std::wstring_view wstr); + void _terminalCopyToClipboard(wil::zwstring_view wstr); void _terminalWarningBell(); void _terminalTitleChanged(std::wstring_view wstr); void _terminalScrollPositionChanged(const int viewTop, diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 01223fb1a14..632d2d73751 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -160,7 +160,6 @@ namespace Microsoft.Terminal.Control Boolean ShouldShowSelectOutput(); // These events are called from some background thread - event Windows.Foundation.TypedEventHandler CopyToClipboard; event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler WarningBell; event Windows.Foundation.TypedEventHandler TabColorChanged; diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index 93e147feaa8..730545d42a2 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -5,7 +5,6 @@ #include "EventArgs.h" #include "FontSizeChangedArgs.g.cpp" #include "TitleChangedEventArgs.g.cpp" -#include "CopyToClipboardEventArgs.g.cpp" #include "ContextMenuRequestedEventArgs.g.cpp" #include "PasteFromClipboardEventArgs.g.cpp" #include "OpenHyperlinkEventArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 2ed2c2b828b..33c215cb3bc 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -5,7 +5,6 @@ #include "FontSizeChangedArgs.g.h" #include "TitleChangedEventArgs.g.h" -#include "CopyToClipboardEventArgs.g.h" #include "ContextMenuRequestedEventArgs.g.h" #include "PasteFromClipboardEventArgs.g.h" #include "OpenHyperlinkEventArgs.g.h" @@ -47,33 +46,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(hstring, Title); }; - struct CopyToClipboardEventArgs : public CopyToClipboardEventArgsT - { - public: - CopyToClipboardEventArgs(hstring text) : - _text(text), - _html(), - _rtf(), - _formats(static_cast(0)) {} - - CopyToClipboardEventArgs(hstring text, hstring html, hstring rtf, Windows::Foundation::IReference formats) : - _text(text), - _html(html), - _rtf(rtf), - _formats(formats) {} - - hstring Text() { return _text; }; - hstring Html() { return _html; }; - hstring Rtf() { return _rtf; }; - Windows::Foundation::IReference Formats() { return _formats; }; - - private: - hstring _text; - hstring _html; - hstring _rtf; - Windows::Foundation::IReference _formats; - }; - struct ContextMenuRequestedEventArgs : public ContextMenuRequestedEventArgsT { public: diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 92784fc6414..958b3e15a64 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -24,14 +24,6 @@ namespace Microsoft.Terminal.Control Int32 Height { get; }; } - runtimeclass CopyToClipboardEventArgs - { - String Text { get; }; - String Html { get; }; - String Rtf { get; }; - Windows.Foundation.IReference Formats { get; }; - } - runtimeclass ContextMenuRequestedEventArgs { Windows.Foundation.Point Position { get; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 06f2ef89709..7c4a5b47630 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -93,7 +93,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.ContextMenuRequested = _interactivity.ContextMenuRequested(winrt::auto_revoke, { get_weak(), &TermControl::_contextMenuHandler }); // "Bubbled" events - ones we want to handle, by raising our own event. - _revokers.CopyToClipboard = _core.CopyToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCopyToClipboard }); _revokers.TitleChanged = _core.TitleChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTitleChanged }); _revokers.TabColorChanged = _core.TabColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTabColorChanged }); _revokers.TaskbarProgressChanged = _core.TaskbarProgressChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSetTaskbarProgress }); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index a12aa964664..24142dc8a7b 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -184,7 +184,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE // Those attach the handler to the core directly, and will explode if // the core ever gets detached & reattached to another window. - BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable); @@ -398,7 +397,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::FoundMatch_revoker FoundMatch; Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers; Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink; - Control::ControlCore::CopyToClipboard_revoker CopyToClipboard; Control::ControlCore::TitleChanged_revoker TitleChanged; Control::ControlCore::TabColorChanged_revoker TabColorChanged; Control::ControlCore::TaskbarProgressChanged_revoker TaskbarProgressChanged; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 8a91707c3af..d42afeffc28 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -53,7 +53,6 @@ namespace Microsoft.Terminal.Control Microsoft.Terminal.Control.IControlSettings Settings { get; }; event Windows.Foundation.TypedEventHandler TitleChanged; - event Windows.Foundation.TypedEventHandler CopyToClipboard; event Windows.Foundation.TypedEventHandler PasteFromClipboard; event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler SetTaskbarProgress; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 9d3dd1d1097..0d2c6dbf43b 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1126,7 +1126,7 @@ void Terminal::SetTitleChangedCallback(std::function pf _pfnTitleChanged.swap(pfn); } -void Terminal::SetCopyToClipboardCallback(std::function pfn) noexcept +void Terminal::SetCopyToClipboardCallback(std::function pfn) noexcept { _pfnCopyToClipboard.swap(pfn); } diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index e792ee8d21c..fc57907ad19 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -143,7 +143,7 @@ class Microsoft::Terminal::Core::Terminal final : bool ResizeWindow(const til::CoordType width, const til::CoordType height) noexcept override; void SetConsoleOutputCP(const unsigned int codepage) noexcept override; unsigned int GetConsoleOutputCP() const noexcept override; - void CopyToClipboard(std::wstring_view content) override; + void CopyToClipboard(wil::zwstring_view content) override; void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override; void SetWorkingDirectory(std::wstring_view uri) override; void PlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) override; @@ -225,7 +225,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetWriteInputCallback(std::function pfn) noexcept; void SetWarningBellCallback(std::function pfn) noexcept; void SetTitleChangedCallback(std::function pfn) noexcept; - void SetCopyToClipboardCallback(std::function pfn) noexcept; + void SetCopyToClipboardCallback(std::function pfn) noexcept; void SetScrollPositionChangedCallback(std::function pfn) noexcept; void SetCursorPositionChangedCallback(std::function pfn) noexcept; void TaskbarProgressChangedCallback(std::function pfn) noexcept; @@ -324,7 +324,7 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnWriteInput; std::function _pfnWarningBell; std::function _pfnTitleChanged; - std::function _pfnCopyToClipboard; + std::function _pfnCopyToClipboard; // I've specifically put this instance here as it requires // alignas(std::hardware_destructive_interference_size) diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 121910cd697..42f1c903c33 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -113,7 +113,7 @@ unsigned int Terminal::GetConsoleOutputCP() const noexcept return CP_UTF8; } -void Terminal::CopyToClipboard(std::wstring_view content) +void Terminal::CopyToClipboard(wil::zwstring_view content) { _pfnCopyToClipboard(content); } diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 044889544c7..fb20d74b75b 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -251,7 +251,7 @@ unsigned int ConhostInternalGetSet::GetConsoleOutputCP() const // - content - the text to be copied. // Return Value: // - -void ConhostInternalGetSet::CopyToClipboard(const std::wstring_view /*content*/) +void ConhostInternalGetSet::CopyToClipboard(const wil::zwstring_view /*content*/) { // TODO } diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 850efe9892a..01d8abaf17f 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -58,7 +58,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void SetConsoleOutputCP(const unsigned int codepage) override; unsigned int GetConsoleOutputCP() const override; - void CopyToClipboard(const std::wstring_view content) override; + void CopyToClipboard(const wil::zwstring_view content) override; void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override; void SetWorkingDirectory(const std::wstring_view uri) override; void PlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) override; diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index d0c96b6949e..eed892f5a94 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -123,7 +123,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR virtual bool SetCursorColor(const COLORREF color) = 0; // OSCSetCursorColor, OSCResetCursorColor - virtual bool SetClipboard(std::wstring_view content) = 0; // OSCSetClipboard + virtual bool SetClipboard(wil::zwstring_view content) = 0; // OSCSetClipboard // DTTERM_WindowManipulation virtual bool WindowManipulation(const DispatchTypes::WindowManipulationType function, diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 24125780f4b..4381c4ecbdd 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -70,7 +70,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void SetConsoleOutputCP(const unsigned int codepage) = 0; virtual unsigned int GetConsoleOutputCP() const = 0; - virtual void CopyToClipboard(const std::wstring_view content) = 0; + virtual void CopyToClipboard(const wil::zwstring_view content) = 0; virtual void SetTaskbarProgress(const DispatchTypes::TaskbarState state, const size_t progress) = 0; virtual void SetWorkingDirectory(const std::wstring_view uri) = 0; virtual void PlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) = 0; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 818261cf112..6f0681ad58f 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3383,7 +3383,7 @@ bool AdaptDispatch::SetCursorColor(const COLORREF cursorColor) // - content - The content to copy to clipboard. Must be null terminated. // Return Value: // - True if handled successfully. False otherwise. -bool AdaptDispatch::SetClipboard(const std::wstring_view content) +bool AdaptDispatch::SetClipboard(const wil::zwstring_view content) { // Return false to forward the operation to the hosting terminal, // since ConPTY can't handle this itself. diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 9285d1126b7..36bb0312145 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -120,7 +120,7 @@ namespace Microsoft::Console::VirtualTerminal bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR bool SetCursorColor(const COLORREF cursorColor) override; - bool SetClipboard(const std::wstring_view content) override; // OSCSetClipboard + bool SetClipboard(const wil::zwstring_view content) override; // OSCSetClipboard bool SetColorTableEntry(const size_t tableIndex, const DWORD color) override; // OSCColorTable diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index e2b8f298329..82bb4f21320 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -116,7 +116,7 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool SetCursorStyle(const DispatchTypes::CursorStyle /*cursorStyle*/) override { return false; } // DECSCUSR bool SetCursorColor(const COLORREF /*color*/) override { return false; } // OSCSetCursorColor, OSCResetCursorColor - bool SetClipboard(std::wstring_view /*content*/) override { return false; } // OscSetClipboard + bool SetClipboard(wil::zwstring_view /*content*/) override { return false; } // OscSetClipboard // DTTERM_WindowManipulation bool WindowManipulation(const DispatchTypes::WindowManipulationType /*function*/, diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index c4012933dc1..5cd5fbdc29e 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -179,7 +179,7 @@ class TestGetSet final : public ITerminalApi return _expectedOutputCP; } - void CopyToClipboard(const std::wstring_view /*content*/) + void CopyToClipboard(const wil::zwstring_view /*content*/) { Log::Comment(L"CopyToClipboard MOCK called..."); } diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp index 9409f064080..f19ad423fbc 100644 --- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp @@ -1453,9 +1453,9 @@ class StatefulDispatch final : public TermDispatch return true; } - bool SetClipboard(std::wstring_view content) noexcept override + bool SetClipboard(wil::zwstring_view content) noexcept override { - _copyContent = { content.begin(), content.end() }; + _copyContent = content; return true; } From f4d8a74082be50783c5d5c92c5bd631f0dfe938c Mon Sep 17 00:00:00 2001 From: Windows Console Service Bot <14666831+consvc@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:36:13 -0500 Subject: [PATCH 220/603] Localization Updates - main - associated with #16886 (#17035) --- .../Resources/de-DE/Resources.resw | 20 +++---- .../Resources/es-ES/Resources.resw | 20 +++---- .../Resources/fr-FR/Resources.resw | 26 ++++---- .../Resources/it-IT/Resources.resw | 16 ++--- .../Resources/ja-JP/Resources.resw | 24 ++++---- .../Resources/ko-KR/Resources.resw | 28 ++++----- .../Resources/pt-BR/Resources.resw | 48 +++++++-------- .../Resources/qps-ploc/Resources.resw | 60 +++++++++---------- .../Resources/qps-ploca/Resources.resw | 60 +++++++++---------- .../Resources/qps-plocm/Resources.resw | 60 +++++++++---------- .../Resources/ru-RU/Resources.resw | 14 ++--- .../Resources/zh-CN/Resources.resw | 14 ++--- .../Resources/zh-TW/Resources.resw | 18 +++--- .../Resources/de-DE/Resources.resw | 22 +++---- .../Resources/es-ES/Resources.resw | 20 +++---- .../Resources/fr-FR/Resources.resw | 22 +++---- .../Resources/it-IT/Resources.resw | 22 +++---- .../Resources/ja-JP/Resources.resw | 20 +++---- .../Resources/ko-KR/Resources.resw | 18 +++--- .../Resources/pt-BR/Resources.resw | 18 +++--- .../Resources/qps-ploc/Resources.resw | 22 +++---- .../Resources/qps-ploca/Resources.resw | 22 +++---- .../Resources/qps-plocm/Resources.resw | 22 +++---- .../Resources/ru-RU/Resources.resw | 22 +++---- .../Resources/zh-CN/Resources.resw | 22 +++---- .../Resources/zh-TW/Resources.resw | 22 +++---- 26 files changed, 341 insertions(+), 341 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw index 948fec8ff61..eb9e1a40c09 100644 --- a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw @@ -190,16 +190,16 @@ Mehrere Bereiche - Schließen... + Schließen - Registerkarten auf der rechten Seite schließen + Tabs nach rechts schließen Andere Registerkarten schließen - Registerkarte schließen + Tab schließen Bereich schließen @@ -208,16 +208,16 @@ Registerkarte teilen - Geteilter Bereich + Bereich teilen Websuche - Farbe... + Registerkartenfarbe ändern - Benutzerdefiniert... + Benutzerdefiniert Zurücksetzen @@ -226,7 +226,7 @@ Registerkarte umbenennen - Registerkarte duplizieren + Registerkarte kopieren Profil mit einem ungültigen "backgroundImage" gefunden. Dieses Profil hat standardmäßig kein Hintergrundbild. Stellen Sie sicher, dass beim Festlegen eines "backgroundImage" der Wert ein gültiger Dateipfad zu einem Bild ist. @@ -487,7 +487,7 @@ A hyperlink name for the Terminal's privacy policy - Drittanbieter-Hinweise + Hinweise von Drittanbietern A hyperlink name for the Terminal's third-party notices @@ -761,7 +761,7 @@ Suchen - Nur Text + Unformatierter Text Das Beendigungsverhalten kann in den erweiterten Profileinstellungen konfiguriert werden. @@ -830,7 +830,7 @@ Diese Registerkarte schließen - Leer... + Leer Bereich schließen diff --git a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw index 0679cd0ddd6..f0ce1d07a48 100644 --- a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw @@ -187,7 +187,7 @@ Varios paneles - Cerrar... + Cerrar Cerrar las pestañas de la derecha @@ -202,19 +202,19 @@ Cerrar panel - Dividir tabla + Dividir pestaña Panel dividido - Búsqueda en la Web + Búsqueda en la web - Color... + Cambiar color de pestaña - Configuración personalizada... + Personalizar Restablecer @@ -464,7 +464,7 @@ This is the heading for a version number label - Tareas iniciales + Introducción A hyperlink name for a guide on how to get started using Terminal @@ -480,7 +480,7 @@ A hyperlink name for the Terminal's release notes - Directiva de privacidad + Política de privacidad A hyperlink name for the Terminal's privacy policy @@ -723,7 +723,7 @@ Maximizar - Restaurar a tamaño normal + Restaurar Paleta de comandos @@ -827,7 +827,7 @@ Cerrar esta pestaña - Vacío... + Vacío Cerrar panel @@ -840,7 +840,7 @@ Text used to identify the reset button - Mover la Pestaña a una Nueva ventana + Mover pestaña a nueva ventana Mueve la pestaña a una nueva ventana diff --git a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw index 887f79be427..68f9c84a82c 100644 --- a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw @@ -187,13 +187,13 @@ Volets multiples - Fermez... + Fermer - Fermer les Onglets à Droite + Fermer les onglets à droite - Fermez les Autres onglets + Fermer les autres onglets Fermer l’onglet @@ -205,16 +205,16 @@ Fractionner l’onglet - Fractionner le volet... + Fractionner le volet Recherche web - Couleur... + Modifier la couleur de l’onglet - Personnalisée... + Personnalisé Réinitialiser @@ -464,7 +464,7 @@ This is the heading for a version number label - Prise en main + Bien démarrer A hyperlink name for a guide on how to get started using Terminal @@ -480,11 +480,11 @@ A hyperlink name for the Terminal's release notes - Politique de confidentialité + Charte de confidentialité A hyperlink name for the Terminal's privacy policy - Avis de tiers + Mentions tierces A hyperlink name for the Terminal's third-party notices @@ -723,7 +723,7 @@ Agrandir - Niveau inférieur + Restaurer Palette de commandes @@ -767,7 +767,7 @@ Ne plus afficher - Cette fenêtre de terminal s’exécute en tant qu’Administrateur + Cette fenêtre de terminal s’exécute en tant qu’administrateur {0} suggestions trouvées @@ -827,7 +827,7 @@ Fermer cet onglet - Vide... + Vide Fermer le volet @@ -846,7 +846,7 @@ Déplacer l'onglet vers une nouvelle fenêtre - Exécuter en tant qu'administrateur + Exécuter en temps qu'administrateur (restreint) This text is displayed on context menu for profile entries in add new tab button. diff --git a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw index 69122195dbb..0f1b1a0b147 100644 --- a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw @@ -187,7 +187,7 @@ Più riquadri - Chiudi... + Chiudi Chiudi schede a destra @@ -199,7 +199,7 @@ Chiudi scheda - Chiudi riquadro + Chiudi il riquadro Dividi scheda @@ -211,10 +211,10 @@ Ricerca nel Web - Colore... + Cambia colore scheda - Personalizzato... + Personalizzato Reimposta @@ -464,7 +464,7 @@ This is the heading for a version number label - Attività iniziali + Introduzione A hyperlink name for a guide on how to get started using Terminal @@ -484,7 +484,7 @@ A hyperlink name for the Terminal's privacy policy - Informative di terze parti + Comunicazioni di terze parti A hyperlink name for the Terminal's third-party notices @@ -723,7 +723,7 @@ Ingrandisci - Ripristina visualizzazione normale + Ripristina in basso Riquadro comandi @@ -830,7 +830,7 @@ Vuoto - Chiudi riquadro + Chiudi il riquadro Chiude il riquadro attivo se sono presenti più riquadri diff --git a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw index 3451318e931..e9141817fc8 100644 --- a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw @@ -188,7 +188,7 @@ 複数ウィンドウ - 閉じる... + 閉じる タブを右側に閉じる @@ -203,28 +203,28 @@ ウィンドウを閉じる - 分割タブ + [分割] タブ - 分割ウィンドウ + ウィンドウを分割する Web 検索 - 色... + タブの色の変更 - カスタム... + カスタム リセット - タブ名を変更 + [名前の変更] タブ - タブの複製 + タブを複製する 無効な "backgroundImage" を持つプロファイルが見つかりました。既定では、そのプロファイルに背景画像はありません。"backgroundImage" を設定するときに、値が画像への有効なファイル パスとなっていることをご確認ください。 @@ -485,7 +485,7 @@ A hyperlink name for the Terminal's privacy policy - サードパーティに関する通知 + サード パーティ通知 A hyperlink name for the Terminal's third-party notices @@ -724,7 +724,7 @@ 最大化 - 元に戻す + 元に戻す (縮小) コマンド パレット @@ -759,7 +759,7 @@ 検索する - プレーンテキスト + テキスト 終了動作は、プロファイルの詳細設定で構成できます。 @@ -828,7 +828,7 @@ このタブを閉じます - 空っぽ... + なし ウィンドウを閉じる @@ -847,7 +847,7 @@ タブを新しいウィンドウに移動 - 管理者として実行する + 管理者として実行 This text is displayed on context menu for profile entries in add new tab button. diff --git a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw index 2b7ebd75146..2f9e77e9206 100644 --- a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw @@ -187,10 +187,10 @@ 여러 창 - 닫기... + 닫기 - 오른쪽으로 탭 닫기 + 오른쪽에 있는 탭 닫기 다른 탭 닫기 @@ -211,10 +211,10 @@ 웹 검색 - 색... + 탭 색 변경 - 사용자 정의... + 사용자 지정 다시 설정 @@ -454,7 +454,7 @@ 정보 - 피드백 보내기 + 의견 보내기 확인 @@ -468,7 +468,7 @@ A hyperlink name for a guide on how to get started using Terminal - 소스 코드 + 원본 코드 A hyperlink name for the Terminal's documentation @@ -480,11 +480,11 @@ A hyperlink name for the Terminal's release notes - 개인정보 취급방침 + 개인정보 보호정책 A hyperlink name for the Terminal's privacy policy - 타사 통지 사항 + 타사 통지 A hyperlink name for the Terminal's third-party notices @@ -571,7 +571,7 @@ 명령줄 구문 분석 오류: - 명령 도구 모음 + 명령 팔레트 탭 전환기 @@ -723,10 +723,10 @@ 최대화 - 아래로 복원 + 이전 크기로 복원 - 명령 도구 모음 + 명령 팔레트 포커스 터미널 @@ -827,7 +827,7 @@ 이 탭 닫기 - 비어 있음... + 비어 있음 창 닫기 @@ -840,13 +840,13 @@ Text used to identify the reset button - 새 창으로 탭 이동 + 탭을 새 창으로 이동 탭을 새 창으로 이동 - 관리자로 실행 + 관리자 권한으로 실행 This text is displayed on context menu for profile entries in add new tab button. diff --git a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw index d94a2946410..b6ca19082f1 100644 --- a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw @@ -187,34 +187,34 @@ Vários painéis - Fechar... + Fechar - Fechar Guias à Direita + Fechar guias à direita - Fechar Outras Guias + Fechar outras guias Fechar guia - Fechar Painel + Fechar o painel - Dividir Guia + Guia Dividir - Painel dividido + Dividir painel - Pesquisa na web + Pesquisa na Web - Cor... + Alterar cor da guia - Personalizados... + Personalizado Restaurar @@ -223,7 +223,7 @@ Renomear guia - Duplicar Guia + Duplicar guia Foi encontrado um perfil com um "backgroundImage" inválido. O perfil deve ser o padrão para que não haja nenhuma imagem de tela de fundo. Certifique-se de que, ao definir um "backgroundImage", o valor é um caminho de arquivo válido para uma imagem. @@ -454,7 +454,7 @@ Sobre - Enviar Comentários + Enviar comentários OK @@ -464,7 +464,7 @@ This is the heading for a version number label - Ponto de Partida + Introdução A hyperlink name for a guide on how to get started using Terminal @@ -484,7 +484,7 @@ A hyperlink name for the Terminal's privacy policy - Avisos de terceiros + Avisos de Terceiros A hyperlink name for the Terminal's third-party notices @@ -571,10 +571,10 @@ Falha ao analisar a linha de comando: - Paleta de Comandos + Paleta de comandos - Seletor de guias + Alternador de guias Digite o nome da guia... @@ -726,7 +726,7 @@ Restaurar Tamanho Original - Paleta de Comandos + Paleta de comandos Terminal de foco @@ -746,7 +746,7 @@ Dividir a janela e iniciar em determinado diretório - Exportar Texto + Exportar texto Falha ao exportar o conteúdo do terminal @@ -758,7 +758,7 @@ Localizar - Texto Simples + Texto sem formatação O comportamento de término pode ser configurado nas configurações avançadas do perfil. @@ -767,7 +767,7 @@ Não mostra de novo - Esta janela do Terminal está funcionando como Administrador + Esta janela do Terminal está sendo executada como administrador Sugestões encontradas: {0} @@ -827,10 +827,10 @@ Fechar esta guia - Vazio... + Vazio - Fechar Painel + Fechar o painel Feche o painel ativo se vários painéis estiverem presentes @@ -840,13 +840,13 @@ Text used to identify the reset button - Mover Guia para Nova Janela + Mover guia para nova janela Move a guia para uma nova janela - Executar como Administrador + Executar como administrador This text is displayed on context menu for profile entries in add new tab button. @@ -885,7 +885,7 @@ Se definido, o comando será acrescentado ao comando padrão do perfil em vez de substituí-lo. - Reiniciar Conexão + Reiniciar conexão Reiniciar a conexão do painel ativo diff --git a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw index 0e553d2fc36..67031f290fa 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw @@ -191,43 +191,43 @@ Мµļтíрłĕ φдпėŝ !!! ! - Ćļôŝέ... !! + Ćļôŝέ ! - Ċĺοşέ Ţаъş ťό ŧђé Яΐğђт !!! !!! + Ċĺοşέ ţаъş ťό ŧђé яΐğђт !!! !!! - Ćĺόѕ℮ Όтђèř Ŧâьś !!! ! + Ćĺόѕ℮ őтђèř ŧâьś !!! ! - Сĺôšę Ťăв !!! + Сĺôšę ťăв !!! - Ćŀöśё Раņé !!! + Ćŀöśё ρаņé !!! - Šрľīτ Τàв !!! + Šрľīτ τàв !!! - Šрŀіт Ρªňë !!! + Šрŀіт φªňë !!! - Ẅёв Şĕаŕčĥ !!! + Ẅёв şĕаŕčĥ !!! - Ċõŀόř... !! + Ċħāņğě τāв ςōĺöя !!! ! - Ċµѕťøм... !!! + Ċµѕťøм ! Яěšěŧ ! - Γεñамē Ťãв !!! + Γεñамē ťãв !!! - Ďϋφľіčάтέ Τàв !!! + Ďϋφľіčάтέ τàв !!! ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! @@ -462,7 +462,7 @@ Åвōύţ ! - Ѕеηð ₣ę℮đвäçк !!! + Ѕеηð ƒę℮đвäçк !!! ΦΚ @@ -472,11 +472,11 @@ This is the heading for a version number label - Ġеťтΐñĝ Ѕτдŗτęď !!! ! + Ġеťтΐñĝ ѕτдŗτęď !!! ! A hyperlink name for a guide on how to get started using Terminal - Ѕοџŗсė Çŏđе !!! + Ѕοџŗсė ¢ŏđе !!! A hyperlink name for the Terminal's documentation @@ -484,15 +484,15 @@ A hyperlink name for user documentation - Ŗеľ℮àşε Ŋòτéš !!! + Ŗеľ℮àşε πòτéš !!! A hyperlink name for the Terminal's release notes - Ρґїνãсÿ Рöĺĩςỳ !!! ! + Ρґїνãсÿ ρöĺĩςỳ !!! ! A hyperlink name for the Terminal's privacy policy - Ţћĩřð-Ρářŧγ ∏οŧīĉęŝ !!! !!! + Ţћĩřð-Ρářŧγ ñοŧīĉęŝ !!! !!! A hyperlink name for the Terminal's third-party notices @@ -579,10 +579,10 @@ ₣āіľ℮ď рàгśīпģ ¢бммäⁿδ ĺīñè: !!! !!! !! - Ćσmmăηδ Рάŀĕтţ℮ !!! ! + Ćσmmăηδ ράŀĕтţ℮ !!! ! - Τăь Ѕωîťςћêг !!! + Τăь ѕωîťςћêг !!! Ţýρё ă тăъ пâmě... !!! !! @@ -731,10 +731,10 @@ Μą×ìmϊżé !! - Ŕèšŧòяё Ðǿẃи !!! + Ŕèšŧòяё ďǿẃи !!! - Ċòмmāńδ Рªľėτťë !!! ! + Ċòмmāńδ рªľėτťë !!! ! ₣ôćűŝ Ţеґмĭйâŀ !!! ! @@ -754,7 +754,7 @@ Ŝρℓΐŧ ŧнė ẁίňďõŵ άпδ ŝţâґţ ίń ģįνëʼn δϊгέ¢ŧøяў !!! !!! !!! !!! ! - Ė×φōŗŧ Ţєхŧ !!! + Ė×φōŗŧ ţєхŧ !!! ₣ăìľεď ťθ эхроґт ţеґmίñдļ ¢ōйт℮лť !!! !!! !!! @@ -766,7 +766,7 @@ ₣ìпđ ! - Ρĺáīň Тěхт !!! + Ρĺáīň тěхт !!! Ťéямїлâŧîόň ь℮ћäνįőř čªή вĕ ċοñƒĩġџřèδ įŋ ăδνåл¢êð ряòƒιļє şėŧтіиĝś. !!! !!! !!! !!! !!! !!! !! @@ -775,7 +775,7 @@ Ďόń'ţ šħόω ãĝάϊл !!! ! - Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ Ãðmĭⁿ !!! !!! !!! !!! + Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ áðmĭⁿîşŧŕãţθŕ !!! !!! !!! !!! !! Ѕũğġεšтįóпş ƒōцʼnđ: {0} !!! !!! @@ -835,10 +835,10 @@ Ĉłоśэ ťĥìŝ ţªъ !!! ! - Ёмφţγ... !! + Ёмφţγ ! - Ĉĺοŝе Ρаиę !!! + Ĉĺοŝе φаиę !!! Çĺόś℮ τнĕ ă¢τίν℮ рáлĕ ιƒ mϋŀţїрĺë φàńęś άŗє рřęšеńт !!! !!! !!! !!! !!! @@ -848,13 +848,13 @@ Text used to identify the reset button - Мόνз Ţǻь ŧö П℮ω Щĭŋδōώ !!! !!! + Мόνз ţǻь ŧö ʼn℮ω ώĭŋδōώ !!! !!! Мøνëŝ ŧªъ ŧǿ ã пεẃ шίŋđоẁ !!! !!! ! - Ŕμŋ ąś Āďmįиíšťґąţőя !!! !!! + Ŕμŋ ąś åďmįиíšťґąţőя !!! !!! This text is displayed on context menu for profile entries in add new tab button. @@ -893,7 +893,7 @@ Ĩƒ šęţ, ŧнĕ ¢ömmдлδ ŵîĺł ьέ åφрєйδĕđ τσ ŧђė рřŏƒїłє'ş đзƒªūľţ ¢οmмăńδ іñѕţέáđ øƒ ѓēρļąċĭлĝ їţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Γēѕŧâяŧ Ĉǿńńēčťїöл !!! !! + Γēѕŧâяŧ ćǿńńēčťїöл !!! !! Γėşťáгţ ŧħ℮ ãčтĩνέ ρăйё сǿηńëςтιóņ !!! !!! !!! ! diff --git a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw index 0e553d2fc36..67031f290fa 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw @@ -191,43 +191,43 @@ Мµļтíрłĕ φдпėŝ !!! ! - Ćļôŝέ... !! + Ćļôŝέ ! - Ċĺοşέ Ţаъş ťό ŧђé Яΐğђт !!! !!! + Ċĺοşέ ţаъş ťό ŧђé яΐğђт !!! !!! - Ćĺόѕ℮ Όтђèř Ŧâьś !!! ! + Ćĺόѕ℮ őтђèř ŧâьś !!! ! - Сĺôšę Ťăв !!! + Сĺôšę ťăв !!! - Ćŀöśё Раņé !!! + Ćŀöśё ρаņé !!! - Šрľīτ Τàв !!! + Šрľīτ τàв !!! - Šрŀіт Ρªňë !!! + Šрŀіт φªňë !!! - Ẅёв Şĕаŕčĥ !!! + Ẅёв şĕаŕčĥ !!! - Ċõŀόř... !! + Ċħāņğě τāв ςōĺöя !!! ! - Ċµѕťøм... !!! + Ċµѕťøм ! Яěšěŧ ! - Γεñамē Ťãв !!! + Γεñамē ťãв !!! - Ďϋφľіčάтέ Τàв !!! + Ďϋφľіčάтέ τàв !!! ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! @@ -462,7 +462,7 @@ Åвōύţ ! - Ѕеηð ₣ę℮đвäçк !!! + Ѕеηð ƒę℮đвäçк !!! ΦΚ @@ -472,11 +472,11 @@ This is the heading for a version number label - Ġеťтΐñĝ Ѕτдŗτęď !!! ! + Ġеťтΐñĝ ѕτдŗτęď !!! ! A hyperlink name for a guide on how to get started using Terminal - Ѕοџŗсė Çŏđе !!! + Ѕοџŗсė ¢ŏđе !!! A hyperlink name for the Terminal's documentation @@ -484,15 +484,15 @@ A hyperlink name for user documentation - Ŗеľ℮àşε Ŋòτéš !!! + Ŗеľ℮àşε πòτéš !!! A hyperlink name for the Terminal's release notes - Ρґїνãсÿ Рöĺĩςỳ !!! ! + Ρґїνãсÿ ρöĺĩςỳ !!! ! A hyperlink name for the Terminal's privacy policy - Ţћĩřð-Ρářŧγ ∏οŧīĉęŝ !!! !!! + Ţћĩřð-Ρářŧγ ñοŧīĉęŝ !!! !!! A hyperlink name for the Terminal's third-party notices @@ -579,10 +579,10 @@ ₣āіľ℮ď рàгśīпģ ¢бммäⁿδ ĺīñè: !!! !!! !! - Ćσmmăηδ Рάŀĕтţ℮ !!! ! + Ćσmmăηδ ράŀĕтţ℮ !!! ! - Τăь Ѕωîťςћêг !!! + Τăь ѕωîťςћêг !!! Ţýρё ă тăъ пâmě... !!! !! @@ -731,10 +731,10 @@ Μą×ìmϊżé !! - Ŕèšŧòяё Ðǿẃи !!! + Ŕèšŧòяё ďǿẃи !!! - Ċòмmāńδ Рªľėτťë !!! ! + Ċòмmāńδ рªľėτťë !!! ! ₣ôćűŝ Ţеґмĭйâŀ !!! ! @@ -754,7 +754,7 @@ Ŝρℓΐŧ ŧнė ẁίňďõŵ άпδ ŝţâґţ ίń ģįνëʼn δϊгέ¢ŧøяў !!! !!! !!! !!! ! - Ė×φōŗŧ Ţєхŧ !!! + Ė×φōŗŧ ţєхŧ !!! ₣ăìľεď ťθ эхроґт ţеґmίñдļ ¢ōйт℮лť !!! !!! !!! @@ -766,7 +766,7 @@ ₣ìпđ ! - Ρĺáīň Тěхт !!! + Ρĺáīň тěхт !!! Ťéямїлâŧîόň ь℮ћäνįőř čªή вĕ ċοñƒĩġџřèδ įŋ ăδνåл¢êð ряòƒιļє şėŧтіиĝś. !!! !!! !!! !!! !!! !!! !! @@ -775,7 +775,7 @@ Ďόń'ţ šħόω ãĝάϊл !!! ! - Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ Ãðmĭⁿ !!! !!! !!! !!! + Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ áðmĭⁿîşŧŕãţθŕ !!! !!! !!! !!! !! Ѕũğġεšтįóпş ƒōцʼnđ: {0} !!! !!! @@ -835,10 +835,10 @@ Ĉłоśэ ťĥìŝ ţªъ !!! ! - Ёмφţγ... !! + Ёмφţγ ! - Ĉĺοŝе Ρаиę !!! + Ĉĺοŝе φаиę !!! Çĺόś℮ τнĕ ă¢τίν℮ рáлĕ ιƒ mϋŀţїрĺë φàńęś άŗє рřęšеńт !!! !!! !!! !!! !!! @@ -848,13 +848,13 @@ Text used to identify the reset button - Мόνз Ţǻь ŧö П℮ω Щĭŋδōώ !!! !!! + Мόνз ţǻь ŧö ʼn℮ω ώĭŋδōώ !!! !!! Мøνëŝ ŧªъ ŧǿ ã пεẃ шίŋđоẁ !!! !!! ! - Ŕμŋ ąś Āďmįиíšťґąţőя !!! !!! + Ŕμŋ ąś åďmįиíšťґąţőя !!! !!! This text is displayed on context menu for profile entries in add new tab button. @@ -893,7 +893,7 @@ Ĩƒ šęţ, ŧнĕ ¢ömmдлδ ŵîĺł ьέ åφрєйδĕđ τσ ŧђė рřŏƒїłє'ş đзƒªūľţ ¢οmмăńδ іñѕţέáđ øƒ ѓēρļąċĭлĝ їţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Γēѕŧâяŧ Ĉǿńńēčťїöл !!! !! + Γēѕŧâяŧ ćǿńńēčťїöл !!! !! Γėşťáгţ ŧħ℮ ãčтĩνέ ρăйё сǿηńëςтιóņ !!! !!! !!! ! diff --git a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw index 0e553d2fc36..67031f290fa 100644 --- a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw @@ -191,43 +191,43 @@ Мµļтíрłĕ φдпėŝ !!! ! - Ćļôŝέ... !! + Ćļôŝέ ! - Ċĺοşέ Ţаъş ťό ŧђé Яΐğђт !!! !!! + Ċĺοşέ ţаъş ťό ŧђé яΐğђт !!! !!! - Ćĺόѕ℮ Όтђèř Ŧâьś !!! ! + Ćĺόѕ℮ őтђèř ŧâьś !!! ! - Сĺôšę Ťăв !!! + Сĺôšę ťăв !!! - Ćŀöśё Раņé !!! + Ćŀöśё ρаņé !!! - Šрľīτ Τàв !!! + Šрľīτ τàв !!! - Šрŀіт Ρªňë !!! + Šрŀіт φªňë !!! - Ẅёв Şĕаŕčĥ !!! + Ẅёв şĕаŕčĥ !!! - Ċõŀόř... !! + Ċħāņğě τāв ςōĺöя !!! ! - Ċµѕťøм... !!! + Ċµѕťøм ! Яěšěŧ ! - Γεñамē Ťãв !!! + Γεñамē ťãв !!! - Ďϋφľіčάтέ Τàв !!! + Ďϋφľіčάтέ τàв !!! ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! @@ -462,7 +462,7 @@ Åвōύţ ! - Ѕеηð ₣ę℮đвäçк !!! + Ѕеηð ƒę℮đвäçк !!! ΦΚ @@ -472,11 +472,11 @@ This is the heading for a version number label - Ġеťтΐñĝ Ѕτдŗτęď !!! ! + Ġеťтΐñĝ ѕτдŗτęď !!! ! A hyperlink name for a guide on how to get started using Terminal - Ѕοџŗсė Çŏđе !!! + Ѕοџŗсė ¢ŏđе !!! A hyperlink name for the Terminal's documentation @@ -484,15 +484,15 @@ A hyperlink name for user documentation - Ŗеľ℮àşε Ŋòτéš !!! + Ŗеľ℮àşε πòτéš !!! A hyperlink name for the Terminal's release notes - Ρґїνãсÿ Рöĺĩςỳ !!! ! + Ρґїνãсÿ ρöĺĩςỳ !!! ! A hyperlink name for the Terminal's privacy policy - Ţћĩřð-Ρářŧγ ∏οŧīĉęŝ !!! !!! + Ţћĩřð-Ρářŧγ ñοŧīĉęŝ !!! !!! A hyperlink name for the Terminal's third-party notices @@ -579,10 +579,10 @@ ₣āіľ℮ď рàгśīпģ ¢бммäⁿδ ĺīñè: !!! !!! !! - Ćσmmăηδ Рάŀĕтţ℮ !!! ! + Ćσmmăηδ ράŀĕтţ℮ !!! ! - Τăь Ѕωîťςћêг !!! + Τăь ѕωîťςћêг !!! Ţýρё ă тăъ пâmě... !!! !! @@ -731,10 +731,10 @@ Μą×ìmϊżé !! - Ŕèšŧòяё Ðǿẃи !!! + Ŕèšŧòяё ďǿẃи !!! - Ċòмmāńδ Рªľėτťë !!! ! + Ċòмmāńδ рªľėτťë !!! ! ₣ôćűŝ Ţеґмĭйâŀ !!! ! @@ -754,7 +754,7 @@ Ŝρℓΐŧ ŧнė ẁίňďõŵ άпδ ŝţâґţ ίń ģįνëʼn δϊгέ¢ŧøяў !!! !!! !!! !!! ! - Ė×φōŗŧ Ţєхŧ !!! + Ė×φōŗŧ ţєхŧ !!! ₣ăìľεď ťθ эхроґт ţеґmίñдļ ¢ōйт℮лť !!! !!! !!! @@ -766,7 +766,7 @@ ₣ìпđ ! - Ρĺáīň Тěхт !!! + Ρĺáīň тěхт !!! Ťéямїлâŧîόň ь℮ћäνįőř čªή вĕ ċοñƒĩġџřèδ įŋ ăδνåл¢êð ряòƒιļє şėŧтіиĝś. !!! !!! !!! !!! !!! !!! !! @@ -775,7 +775,7 @@ Ďόń'ţ šħόω ãĝάϊл !!! ! - Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ Ãðmĭⁿ !!! !!! !!! !!! + Ţђіś Тĕřмїηǻℓ шĩⁿðöŵ ïѕ ѓüñňĩñģ ãŝ áðmĭⁿîşŧŕãţθŕ !!! !!! !!! !!! !! Ѕũğġεšтįóпş ƒōцʼnđ: {0} !!! !!! @@ -835,10 +835,10 @@ Ĉłоśэ ťĥìŝ ţªъ !!! ! - Ёмφţγ... !! + Ёмφţγ ! - Ĉĺοŝе Ρаиę !!! + Ĉĺοŝе φаиę !!! Çĺόś℮ τнĕ ă¢τίν℮ рáлĕ ιƒ mϋŀţїрĺë φàńęś άŗє рřęšеńт !!! !!! !!! !!! !!! @@ -848,13 +848,13 @@ Text used to identify the reset button - Мόνз Ţǻь ŧö П℮ω Щĭŋδōώ !!! !!! + Мόνз ţǻь ŧö ʼn℮ω ώĭŋδōώ !!! !!! Мøνëŝ ŧªъ ŧǿ ã пεẃ шίŋđоẁ !!! !!! ! - Ŕμŋ ąś Āďmįиíšťґąţőя !!! !!! + Ŕμŋ ąś åďmįиíšťґąţőя !!! !!! This text is displayed on context menu for profile entries in add new tab button. @@ -893,7 +893,7 @@ Ĩƒ šęţ, ŧнĕ ¢ömmдлδ ŵîĺł ьέ åφрєйδĕđ τσ ŧђė рřŏƒїłє'ş đзƒªūľţ ¢οmмăńδ іñѕţέáđ øƒ ѓēρļąċĭлĝ їţ. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - Γēѕŧâяŧ Ĉǿńńēčťїöл !!! !! + Γēѕŧâяŧ ćǿńńēčťїöл !!! !! Γėşťáгţ ŧħ℮ ãčтĩνέ ρăйё сǿηńëςтιóņ !!! !!! !!! ! diff --git a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw index 0a74add4c32..b4f32ce009f 100644 --- a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw @@ -193,7 +193,7 @@ Закрыть вкладки справа - Закрыть другие вкладки + Закрыть остальные вкладки Закрыть вкладку @@ -211,10 +211,10 @@ Поиск в Интернете - Цвет... + Изменить цвет вкладки - Настраиваемый... + Настраиваемый Сбросить @@ -464,7 +464,7 @@ This is the heading for a version number label - Начало работы + Приступая к работе A hyperlink name for a guide on how to get started using Terminal @@ -484,7 +484,7 @@ A hyperlink name for the Terminal's privacy policy - Уведомления сторонних производителей + Уведомления третьих лиц A hyperlink name for the Terminal's third-party notices @@ -723,7 +723,7 @@ Развернуть - Восстановить размер + Кнопка "Свернуть в окно" Палитра команд @@ -827,7 +827,7 @@ Закрыть эту вкладку - Пусто... + Очистить Закрыть панель diff --git a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw index 78135ba31a8..e535d5a293a 100644 --- a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw @@ -187,7 +187,7 @@ 多个窗格 - 关闭… + 关闭 关闭右侧标签页 @@ -208,13 +208,13 @@ 拆分窗格 - Web 搜索 + 网络搜索 - 颜色... + 更改选项卡颜色 - 自定义... + 自定义 重置 @@ -223,7 +223,7 @@ 重命名选项卡 - 复制选项卡 + 复制标签页 找到一个具有无效 "backgroundImage" 的配置文件。将该配置文件设置为默认设置为不包含背景图像。请确保在设置 "backgroundImage" 时,该值是指向图像的有效文件路径。 @@ -480,7 +480,7 @@ A hyperlink name for the Terminal's release notes - 隐私策略 + 隐私政策 A hyperlink name for the Terminal's privacy policy @@ -827,7 +827,7 @@ 关闭此选项卡 - 空白... + 关闭窗格 diff --git a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw index 5a7e89a6711..49ed1b10b97 100644 --- a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw @@ -187,7 +187,7 @@ 多個窗格 - 關閉... + 關閉 關閉右側的索引標籤 @@ -202,19 +202,19 @@ 關閉窗格 - 分割 Tab + 分割索引標籤 分割窗格 - Web 搜尋 + 網頁搜尋 - 色彩... + 變更索引標籤色彩 - 自訂...​​ + 自訂 重設 @@ -468,7 +468,7 @@ A hyperlink name for a guide on how to get started using Terminal - 原始程式碼 + 原始碼 A hyperlink name for the Terminal's documentation @@ -480,11 +480,11 @@ A hyperlink name for the Terminal's release notes - 隱私權原則 + 隱私權政策 A hyperlink name for the Terminal's privacy policy - 第三方聲明 + 第三方注意事項 A hyperlink name for the Terminal's third-party notices @@ -827,7 +827,7 @@ 關閉此索引標籤 - 空白... + 清空 關閉窗格 diff --git a/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw index 1b088d57051..8ab253b98c7 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Farbschema auswählen... + Farbschema auswählen - Neue Registerkarte... + Neue Registerkarte - Bereich teilen... + Bereich teilen Terminal (portierbar) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Die Farbe der Registerkarte festlegen... + Registerkartenfarbe festlegen Einfügen @@ -347,7 +347,7 @@ Den Titel der Registerkarte zurücksetzen - Registerkartentitel umbenennen... + Registerkartentitel umbenennen Bereich skalieren @@ -441,7 +441,7 @@ Zur letzten Registerkarte wechseln - Registerkarte suchen... + Registerkarte suchen Modus "Immer im Vordergrund" umschalten @@ -450,10 +450,10 @@ Befehlspalette ein/aus - Zuletzt verwendete Befehle... + Zuletzt verwendete Befehle - Vorschläge öffnen... + Vorschläge öffnen Befehlspalette im Befehlszeilenmodus ein/aus @@ -505,7 +505,7 @@ Debugger unterbrechen - Einstellungen öffnen... + Einstellungen öffnen Fenster in „{0}“ umbenennen @@ -515,7 +515,7 @@ Fensternamen zurücksetzen - Fenster umbenennen... + Fenster umbenennen Aktuelles Arbeitsverzeichnis des Terminals anzeigen @@ -566,7 +566,7 @@ Terminal beenden - Hintergrunddeckkraft festlegen... + Hintergrunddeckkraft festlegen Hintergrunddeckkraft um {0} % erhöhen diff --git a/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw index 9d9ef4b93f1..c3c94c309ab 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Seleccionar combinación de colores... + Seleccionar combinación de colores - Nueva pestaña... + Nueva pestaña - Panel dividido... + Panel dividido Terminal (portátil) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Establecer el color de la pestaña... + Establecer el color de la pestaña Pegar @@ -347,7 +347,7 @@ Restablecer título de la pestaña - Cambiar nombre de título de la pestaña... + Cambiar título de la pestaña Cambiar el tamaño del panel @@ -441,7 +441,7 @@ Cambiar a la última pestaña - Buscar pestañas... + Buscar pestaña Alternar siempre en el modo superior @@ -450,10 +450,10 @@ Alternar paleta de comandos - Comandos recientes... + Comandos recientes - Abrir sugerencias... + Abrir sugerencias Alternar paleta de comandos en modo de línea de comandos @@ -505,7 +505,7 @@ Interrumpir en el depurador - Abrir configuración... + Abrir configuración Cambiar el nombre de la ventana por "{0}" @@ -566,7 +566,7 @@ Salir del terminal - Establecer la opacidad del fondo... + Establecer la opacidad del fondo Aumentar la opacidad del fondo en un {0} % diff --git a/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw index ce62381140b..21fe9bcf229 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Sélectionner le modèle de couleurs... + Sélectionner un modèle de couleurs - Nouvel onglet... + Nouvel onglet - Fractionner le volet... + Fractionner le volet Terminal (portable) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Définir la couleur de l’onglet... + Définir la couleur de l’onglet Coller @@ -347,7 +347,7 @@ Rétablir le titre de l’onglet - Renommer le titre de l'onglet... + Renommer le titre de l'onglet Redimensionner le volet @@ -441,7 +441,7 @@ Basculer vers le dernier onglet - Recherche de l’onglet... + Recherche de l’onglet Activer le mode Toujours visible @@ -450,10 +450,10 @@ Activer/désactiver la palette de commandes - Commandes récentes... + Commandes récentes - Suggestions ouvertes... + Ouvrir les suggestions Basculer la palette de commandes en mode ligne de commande @@ -505,7 +505,7 @@ Accès dans le débogueur - Ouvrir les paramètres... + Ouvrir les paramètres Renommer la fenêtre en « {0} » @@ -515,7 +515,7 @@ Réinitialiser le nom de la fenêtre - Renommer la fenêtre... + Fenêtre pour renommer Afficher le répertoire de travail actuel du terminal @@ -566,7 +566,7 @@ Quitter le terminal - Définir l’opacité de l’arrière-plan... + Définir l’opacité de l’arrière-plan Augmenter l’opacité de l’arrière-plan de {0} % diff --git a/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw index 48ba557745d..68d19388cee 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Seleziona combinazione colori... + Seleziona combinazione colori - Nuova scheda... + Nuova scheda - Suddividi riquadro... + Suddividi riquadro Terminale (portatile) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Imposta il colore della scheda... + Imposta il colore della scheda Incolla @@ -347,7 +347,7 @@ Reimposta il titolo della scheda - Rinomina il titolo della scheda ... + Rinomina il titolo della scheda Ridimensiona riquadro @@ -441,7 +441,7 @@ Passa all'ultima scheda - Cerca scheda... + Cerca scheda Modalità attiva/disattiva sempre in alto @@ -450,10 +450,10 @@ Attiva/disattiva riquadro dei comandi - Comandi recenti... + Comandi recenti - Apri suggerimenti... + Apri i suggerimenti Attiva/disattiva il pannello dei comandi in modalità riga di comando @@ -505,7 +505,7 @@ Interrompi il debugger - Apri impostazioni... + Apri impostazioni Rinomina finestra in "{0}" @@ -515,7 +515,7 @@ Reimposta nome finestra - Rinomina finestra... + Rinomina finestra Visualizza la directory di lavoro corrente del terminale @@ -566,7 +566,7 @@ Esci da Terminale - Imposta l'opacità di sfondo... + Imposta l'opacità di sfondo Aumenta l'opacità di sfondo del {0}% diff --git a/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw index 173663fc990..3a86cbc0288 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 配色パターンの選択... + 配色パターンの選択 - 新しいタブ... + 新しいタブ - ウィンドウを分割します... + ウィンドウを分割する ターミナル (ポータブル) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - タブの色を設定する... + タブの色の設定 貼り付け @@ -347,7 +347,7 @@ タブのタイトルをリセット - タブ名を変更する + タブ タイトル名の変更 ウィンドウのサイズの変更する @@ -441,7 +441,7 @@ 最後のタブに切り替える - タブを検索します... + タブの検索 常に手前に表示するモードに切り替える @@ -450,10 +450,10 @@ コマンド パレットに切り替える - 最近使ったコマンド... + 最近使ったコマンド - おすすめを開く... + 候補を開く コマンド ライン モードでコマンド パレットを切り替えます @@ -505,7 +505,7 @@ デバッガーに挿入します - 設定を開く... + 設定を開く ウィンドウの名前を "{0}" に変更する @@ -566,7 +566,7 @@ ターミナルの終了 - 背景の不透明度を設定... + 背景の不透明度の設定 背景の不透明度を {0}% 上げる diff --git a/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw index 90be00c1d20..b776a0cffd6 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 색 구성표 선택... + 색 구성표 선택 - 새 탭... + 새 탭 - 분할 창... + 분할 창 터미널(이식 가능) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - 탭 색 설정... + 탭 색 설정 붙여넣기 @@ -347,7 +347,7 @@ 탭 제목 재설정 - 탭 이름 바꾸기... + 탭 제목 이름 바꾸기 창 크기 조정 @@ -450,10 +450,10 @@ 토글 명령 팔레트 - 최근 명령... + 최근 명령 - 제안 열기... + 제안 사항 열기 명령줄 모드에서 명령 팔레트 설정/해제 @@ -505,7 +505,7 @@ 디버거로 나누기 - 설정 열기... + 설정 열기 "{0}"(으)로 창 이름 바꾸기 @@ -566,7 +566,7 @@ 터미널 종료 - 배경 불투명도 설정... + 배경 불투명도 설정 배경 불투명도를 {0}% 증가 diff --git a/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw index aa4a632c367..cb4bd2648a6 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Selecionar esquema de cores... + Selecionar esquema de cores - Nova Guia... + Nova guia - Dividir painel... + Dividir painel Terminal (Portátil) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Definir a cor da guia... + Definir a cor da guia Colar @@ -347,7 +347,7 @@ Redefinir título da guia - Renomear o título da guia... + Renomear título da guia Redimensionar painel @@ -441,7 +441,7 @@ Alternar para a última guia - Buscar guia + Pesquisar guia Alternar sempre no modo superior @@ -450,7 +450,7 @@ Ativar/desativar paleta de comandos - Comandos recentes... + Comandos recentes Abrir sugestões @@ -505,7 +505,7 @@ Invadir o depurador - Abrir as configurações... + Abrir configurações Renomear a janela para "{0}" @@ -566,7 +566,7 @@ Saia do Terminal - Definir a opacidade da tela de fundo + Definir opacidade da tela de fundo Aumentar a opacidade da tela de fundo em {0}% diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw index d2eea062990..e93d8c85bee 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē... !!! !!! + Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē !!! !!! - Ŋéŵ Тάъ... !!! + Ŋéŵ тάъ !! - Śрĺíŧ Рāлë... !!! + Śрĺíŧ ρāлë !!! Ţèѓmĩŋąĺ (Ρθŗτаьℓ℮) !!! !!! @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Şėŧ ŧнę ťáь ċσŀőґ... !!! !!! + Şėŧ ŧнę ťáь ċσŀőґ !!! !! Ρášτé ! @@ -347,7 +347,7 @@ Řėś℮ŧ тαв τįтℓę !!! ! - Гęйªm℮ тåъ ŧīτļĕ... !!! !!! + Гęйªm℮ тåъ ŧīτļĕ !!! ! Ґëśîžє рãле !!! @@ -441,7 +441,7 @@ Ѕẃітčћ ţõ ťħέ ľάšť ţаь !!! !!! - Ѕėàřĉħ ƒôґ ŧâь... !!! !! + Ѕėàřĉħ ƒôґ ŧâь !!! ! Ŧōĝġļě αŀώªÿŝ òⁿ тοр мöðέ !!! !!! ! @@ -450,10 +450,10 @@ Ţōĝğļė čσmmάήđ рåŀęŧŧз !!! !!! - Γë¢єйť ćøмmåηđŝ... !!! !! + Γë¢єйť ćøмmåηđŝ !!! ! - Ǿрέʼn şµġġеšŧìŏπѕ... !!! !!! + Ǿрέʼn şµġġеšŧìŏπѕ !!! ! Ťŏġĝļĕ çбmмáήď φàłėŧτĕ ίη ċσmмäпδ ℓìņє mόð℮ !!! !!! !!! !!! @@ -505,7 +505,7 @@ Вгεаĸ ĩňŧθ ŧђė đěвцģģέŕ !!! !!! - Öφéη ѕēττϊⁿğš... !!! ! + Öφéη ѕēττϊⁿğš !!! Γзňãмē шіňďоώ ŧō "{0}" !!! !!! @@ -515,7 +515,7 @@ Ŗεšзť ωĩйδōẁ ñâмé !!! !! - Ґёʼnάmë шϊйďθŵ... !!! ! + Ґёʼnάmë шϊйďθŵ !!! Ďіŝρłάỳ Τ℮ѓmìйаĺ'š čûŗяēʼnτ ώоřκìņĝ ďιяęĉтσґỳ !!! !!! !!! !!! ! @@ -566,7 +566,7 @@ Qυϊτ ŧħз Τέяmίŋăŀ !!! !! - Ѕěт ьªċķĝґøūņð óφǻĉїτў... !!! !!! ! + Ѕěт ьªċķĝґøūņð óφǻĉїτў !!! !!! Ĭñçŕ℮àŝę ваċкġřŏųлď ǿφāςíτу ьỳ {0}% !!! !!! !!! ! diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw index d2eea062990..e93d8c85bee 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē... !!! !!! + Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē !!! !!! - Ŋéŵ Тάъ... !!! + Ŋéŵ тάъ !! - Śрĺíŧ Рāлë... !!! + Śрĺíŧ ρāлë !!! Ţèѓmĩŋąĺ (Ρθŗτаьℓ℮) !!! !!! @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Şėŧ ŧнę ťáь ċσŀőґ... !!! !!! + Şėŧ ŧнę ťáь ċσŀőґ !!! !! Ρášτé ! @@ -347,7 +347,7 @@ Řėś℮ŧ тαв τįтℓę !!! ! - Гęйªm℮ тåъ ŧīτļĕ... !!! !!! + Гęйªm℮ тåъ ŧīτļĕ !!! ! Ґëśîžє рãле !!! @@ -441,7 +441,7 @@ Ѕẃітčћ ţõ ťħέ ľάšť ţаь !!! !!! - Ѕėàřĉħ ƒôґ ŧâь... !!! !! + Ѕėàřĉħ ƒôґ ŧâь !!! ! Ŧōĝġļě αŀώªÿŝ òⁿ тοр мöðέ !!! !!! ! @@ -450,10 +450,10 @@ Ţōĝğļė čσmmάήđ рåŀęŧŧз !!! !!! - Γë¢єйť ćøмmåηđŝ... !!! !! + Γë¢єйť ćøмmåηđŝ !!! ! - Ǿрέʼn şµġġеšŧìŏπѕ... !!! !!! + Ǿрέʼn şµġġеšŧìŏπѕ !!! ! Ťŏġĝļĕ çбmмáήď φàłėŧτĕ ίη ċσmмäпδ ℓìņє mόð℮ !!! !!! !!! !!! @@ -505,7 +505,7 @@ Вгεаĸ ĩňŧθ ŧђė đěвцģģέŕ !!! !!! - Öφéη ѕēττϊⁿğš... !!! ! + Öφéη ѕēττϊⁿğš !!! Γзňãмē шіňďоώ ŧō "{0}" !!! !!! @@ -515,7 +515,7 @@ Ŗεšзť ωĩйδōẁ ñâмé !!! !! - Ґёʼnάmë шϊйďθŵ... !!! ! + Ґёʼnάmë шϊйďθŵ !!! Ďіŝρłάỳ Τ℮ѓmìйаĺ'š čûŗяēʼnτ ώоřκìņĝ ďιяęĉтσґỳ !!! !!! !!! !!! ! @@ -566,7 +566,7 @@ Qυϊτ ŧħз Τέяmίŋăŀ !!! !! - Ѕěт ьªċķĝґøūņð óφǻĉїτў... !!! !!! ! + Ѕěт ьªċķĝґøūņð óφǻĉїτў !!! !!! Ĭñçŕ℮àŝę ваċкġřŏųлď ǿφāςíτу ьỳ {0}% !!! !!! !!! ! diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw index d2eea062990..e93d8c85bee 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē... !!! !!! + Ŝêĺĕćţ ćŏĺбŕ ѕćнêmē !!! !!! - Ŋéŵ Тάъ... !!! + Ŋéŵ тάъ !! - Śрĺíŧ Рāлë... !!! + Śрĺíŧ ρāлë !!! Ţèѓmĩŋąĺ (Ρθŗτаьℓ℮) !!! !!! @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Şėŧ ŧнę ťáь ċσŀőґ... !!! !!! + Şėŧ ŧнę ťáь ċσŀőґ !!! !! Ρášτé ! @@ -347,7 +347,7 @@ Řėś℮ŧ тαв τįтℓę !!! ! - Гęйªm℮ тåъ ŧīτļĕ... !!! !!! + Гęйªm℮ тåъ ŧīτļĕ !!! ! Ґëśîžє рãле !!! @@ -441,7 +441,7 @@ Ѕẃітčћ ţõ ťħέ ľάšť ţаь !!! !!! - Ѕėàřĉħ ƒôґ ŧâь... !!! !! + Ѕėàřĉħ ƒôґ ŧâь !!! ! Ŧōĝġļě αŀώªÿŝ òⁿ тοр мöðέ !!! !!! ! @@ -450,10 +450,10 @@ Ţōĝğļė čσmmάήđ рåŀęŧŧз !!! !!! - Γë¢єйť ćøмmåηđŝ... !!! !! + Γë¢єйť ćøмmåηđŝ !!! ! - Ǿрέʼn şµġġеšŧìŏπѕ... !!! !!! + Ǿрέʼn şµġġеšŧìŏπѕ !!! ! Ťŏġĝļĕ çбmмáήď φàłėŧτĕ ίη ċσmмäпδ ℓìņє mόð℮ !!! !!! !!! !!! @@ -505,7 +505,7 @@ Вгεаĸ ĩňŧθ ŧђė đěвцģģέŕ !!! !!! - Öφéη ѕēττϊⁿğš... !!! ! + Öφéη ѕēττϊⁿğš !!! Γзňãмē шіňďоώ ŧō "{0}" !!! !!! @@ -515,7 +515,7 @@ Ŗεšзť ωĩйδōẁ ñâмé !!! !! - Ґёʼnάmë шϊйďθŵ... !!! ! + Ґёʼnάmë шϊйďθŵ !!! Ďіŝρłάỳ Τ℮ѓmìйаĺ'š čûŗяēʼnτ ώоřκìņĝ ďιяęĉтσґỳ !!! !!! !!! !!! ! @@ -566,7 +566,7 @@ Qυϊτ ŧħз Τέяmίŋăŀ !!! !! - Ѕěт ьªċķĝґøūņð óφǻĉїτў... !!! !!! ! + Ѕěт ьªċķĝґøūņð óφǻĉїτў !!! !!! Ĭñçŕ℮àŝę ваċкġřŏųлď ǿφāςíτу ьỳ {0}% !!! !!! !!! ! diff --git a/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw index d5e70d81948..1134777776e 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Выбрать цветовую схему... + Выбрать цветовую схему - Новая вкладка... + Новая вкладка - Разделить область... + Разделить область Терминал (переносной) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - Задать цвет вкладки… + Задать цвет вкладки Вставить @@ -347,7 +347,7 @@ Сбросить заголовок вкладки - Переименовать заголовок вкладки... + Переименовать заголовок вкладки Изменить размер области @@ -441,7 +441,7 @@ Переключиться на последнюю вкладку - Поиск вкладки... + Поиск вкладки Режим "поверх других окон" @@ -450,10 +450,10 @@ Показать или скрыть палитру команд - Недавние команды... + Последние команды - Открыть предложения... + Открыть предложения Включить или отключить палитру команд в режиме командной строки @@ -505,7 +505,7 @@ Перейти в отладчик - Открыть настройки... + Открыть параметры Переименовать окно в "{0}" @@ -515,7 +515,7 @@ Сбросить имя окна - Переименовать окно... + Переименовать окно Отобразить текущий рабочий каталог Терминала @@ -566,7 +566,7 @@ Выйти из терминала - Установить непрозрачность фона... + Установить непрозрачность фона Увеличить непрозрачность фона на {0}% diff --git a/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw index 24746d831a9..39c3259a3e3 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 选择配色方案... + 选择配色方案 - 新选项卡... + 新建标签页 - 拆分窗格... + 拆分窗格 终端 (便携) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - 设置选项卡颜色... + 设置选项卡颜色 粘贴 @@ -347,7 +347,7 @@ 重置标签页标题 - 正在重命名选项卡标题... + 重命名选项卡标题 调整窗格大小 @@ -441,7 +441,7 @@ 切换到最后一个选项卡 - 搜索选项卡... + 搜索选项卡 切换“总在最前面”模式 @@ -450,10 +450,10 @@ 切换命令面板 - 最近使用的命令... + 最近使用的命令 - 打开建议... + 打开建议 在命令行模式里切换命令面板 @@ -505,7 +505,7 @@ 强行进入调试程序 - 打开设置... + 打开设置 重命名窗口到 “{0}” @@ -515,7 +515,7 @@ 重置窗口名称 - 重命名窗口... + 重命名窗口 显示终端的当前工作目录 @@ -566,7 +566,7 @@ 退出终端 - 设置背景不透明度... + 设置背景不透明度 将背景不透明度增加 {0}% diff --git a/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw index 536d4b900b5..946440cb0c0 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 選取彩色配置... + 選取彩色配置 - 新增索引標籤... + 新索引標籤 - 分割窗格... + 分割窗格 終端機 (可攜式) @@ -325,7 +325,7 @@ {Locked="JSON"}. "JSON" is the extension of the file that will be opened. - 設定索引標籤色彩... + 設定索引標籤色彩 貼上 @@ -347,7 +347,7 @@ 重設索引標籤標題 - 將索引標籤重新命名... + 重新命名索引標籤標題 調整頁面大小 @@ -441,7 +441,7 @@ 切換到最後一個索引標籤 - 搜尋索引標籤... + 搜尋索引標籤 啟用 [最上層顯示] 模式 @@ -450,10 +450,10 @@ 切換命令選擇區 - 最近使用的命令... + 最近使用的命令 - 開啟建議... + 開啟建議 在命令列模式中切換命令選擇區 @@ -505,7 +505,7 @@ 在偵錯工具中中斷 - 開啟設定... + 開啟設定 將視窗重新命名為「{0}」 @@ -515,7 +515,7 @@ 重設視窗名稱 - 重新命名視窗... + 重新命名視窗 顯示終端機目前的工作目錄 @@ -566,7 +566,7 @@ 結束 [終端機] - 設定背景不透明度... + 設定背景不透明度 增加背景不透明度為 {0}% From 11c4aa459d846f3ea9cd68cf5555cbe83cfef4ab Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Tue, 16 Apr 2024 21:06:59 +0530 Subject: [PATCH 221/603] Fix window style under minimized state (#17058) Closes: #13961 This PR changes the window styling we use under the minimized state. We now retain the active window styling so the content's height doesn't change when switching between minimized and maximized states. ## Validation Steps Performed - Open Terminal and go into Maximized mode. - Click on the Minimize button. - No `SizeChanged` event in `ControlCore`. - Click on the WT icon in the taskbar to restore it. - No `SizeChanged` event in `ControlCore`. --- src/cascadia/TerminalApp/MinMaxCloseControl.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp index fac7d2b2b3f..f4db97176e6 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp @@ -116,6 +116,9 @@ namespace winrt::TerminalApp::implementation switch (visualState) { + case WindowVisualState::WindowVisualStateIconified: + // Iconified (aka minimized) state should preserve the active window styling + break; case WindowVisualState::WindowVisualStateMaximized: VisualStateManager::GoToState(MaximizeButton(), L"WindowStateMaximized", false); @@ -124,9 +127,7 @@ namespace winrt::TerminalApp::implementation CloseButton().Height(maximizedHeight); MaximizeToolTip().Text(RS_(L"WindowRestoreDownButtonToolTip")); break; - case WindowVisualState::WindowVisualStateNormal: - case WindowVisualState::WindowVisualStateIconified: default: VisualStateManager::GoToState(MaximizeButton(), L"WindowStateNormal", false); From d632c39cc3cef3075f09d881e03801518310d641 Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Wed, 17 Apr 2024 00:30:31 +0530 Subject: [PATCH 222/603] Fix wrong CommandPallete access (#17069) Closes: #17032 We were wrongly calling the Ctor of CommandPalette which led to the creation of an uninitialized winrt command palette object, and then OnCreateAutomationPeer() was called on that. This seems to be the cause of #17032. ## Validation Steps Performed - Open WT. - Try to tear off a tab out of the tab headers view. - WT doesn't crash. --- src/cascadia/TerminalApp/TabManagement.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index f2039d0043e..3053d746df2 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -881,7 +881,10 @@ namespace winrt::TerminalApp::implementation } } - CommandPalette().Visibility(Visibility::Collapsed); + if (const auto p = CommandPaletteElement()) + { + p.Visibility(Visibility::Collapsed); + } _UpdateTabView(); } From 90b8bb7c2d8932fc601aed9c26f13c77d5a1321d Mon Sep 17 00:00:00 2001 From: Tushar Singh Date: Wed, 17 Apr 2024 21:41:31 +0530 Subject: [PATCH 223/603] Improve Search Highlighting (#16611) ### The changeset involves: - Decoupling Selection and Search Highlighting code paths. - We no longer invalidate search highlights when: - Left-clicking on terminal - A new selection is made - Left-clicking on Search-box - Dispatching Find Next/Prev Match Action. (The search highlight was removed after pressing the first key of the Action's key combination) - And, anything that doesn't change buffer content, shouldn't invalidate the highlighted region (E.g. Cursor movement) - Highlighting foreground color is *actually* applied to the highlighted text. - Double-clicking on SearchBox no longer starts a text selection in the terminal. - Selected text is properly populated in the Search Box (#16355) Closes: #16355 ![image](https://github.com/microsoft/terminal/assets/55626797/8fd0345b-a8b2-4bc2-a25e-15d710127b63) ## Some Implementation Details ### Detecting text layout changes in the Control layer As Search Highlight regions need to be removed when new text is added, or the existing text is re-arranged due to window resize or similar events, a new event `TextLayoutUpdated` is added that notifies `CoreControl` of any text layout changes. The event is used to invalidate and remove all search highlight regions from the buffer (because the regions might not be _fresh_ anymore. The new event is raised when: 1. `AdaptDispatch` writes new text into the buffer. 2. MainBuffer is switched to AltBuffer or vice-versa. 3. The user resized the window. 4. Font size changed. 5. Zoom level changed. (Intensionally,) It's not raised when: 1. Buffer is scrolled. 2. The text cursor is moved. When `ControlCore` receives a `TextLayoutUpdated` event, it clears the Search Highlights in the *render data*, and raises an `UpdateSearchResults` event to notify `TermControl` to update the Search UI (`SearchBoxControl`). In the future, we can use `TextLayoutUpdated` event to start a new search which would refresh the results automatically after a slight delay (throttled). *VSCode already does this today*. ### How does AtlasEngine draw the highlighted regions? We follow a similar idea as for drawing the Selection region. When new regions are available, the old+new regions are marked invalidated. Later, a call to `_drawHighlighted()` is made at the end of `PaintBufferLine()` to override the highlighted regions' colors with highlight colors. The highlighting colors replace the buffer colors while search highlights are active. Note that to paint search highlights, we currently invalidate the row completely. This forces text shaping for the rows in the viewport that have at least one highlighted region. This is done to keep the (already lengthy) PR... simple. We could take advantage of the fact that only colors have changed and not the characters (or glyphs). I'm expecting that this could be improved like: 1. When search regions are added, we add the highlighting colors to the color bitmaps without causing text shaping. 2. When search regions are removed, we re-fill the color bitmaps with the original colors from the Buffer. ## Validation Steps: - New text, window resize, font size changes, zooming, and pasting content into the terminal removes search highlights. - highlighting colors override the foreground and background color of the text (in the rendered output). - Blinking, faded, reverse video, Intense text is highlighted as expected. --- src/buffer/out/search.cpp | 31 +-- src/buffer/out/search.h | 7 +- src/cascadia/TerminalControl/ControlCore.cpp | 90 ++++++-- src/cascadia/TerminalControl/ControlCore.h | 6 +- src/cascadia/TerminalControl/ControlCore.idl | 3 +- src/cascadia/TerminalControl/EventArgs.cpp | 2 +- src/cascadia/TerminalControl/EventArgs.h | 10 +- src/cascadia/TerminalControl/EventArgs.idl | 9 +- .../TerminalControl/SearchBoxControl.cpp | 35 ++- .../TerminalControl/SearchBoxControl.h | 3 + .../TerminalControl/SearchBoxControl.idl | 1 + .../TerminalControl/SearchBoxControl.xaml | 2 + src/cascadia/TerminalControl/TermControl.cpp | 60 +++-- src/cascadia/TerminalControl/TermControl.h | 5 +- src/cascadia/TerminalCore/Terminal.cpp | 29 +++ src/cascadia/TerminalCore/Terminal.hpp | 15 +- src/cascadia/TerminalCore/TerminalApi.cpp | 16 ++ .../TerminalCore/TerminalSelection.cpp | 35 --- .../TerminalCore/terminalrenderdata.cpp | 69 +++--- .../UnitTests_TerminalCore/ScrollTest.cpp | 1 - src/host/outputStream.cpp | 7 +- src/host/outputStream.hpp | 1 + src/host/renderData.cpp | 9 +- src/host/renderData.hpp | 4 +- src/host/ut_host/VtIoTests.cpp | 13 +- src/inc/til/point.h | 34 +++ src/inc/til/rect.h | 14 ++ src/interactivity/onecore/BgfxEngine.cpp | 5 - src/interactivity/onecore/BgfxEngine.hpp | 1 - src/renderer/atlas/AtlasEngine.api.cpp | 22 ++ src/renderer/atlas/AtlasEngine.cpp | 216 +++++++++++++----- src/renderer/atlas/AtlasEngine.h | 15 +- src/renderer/base/RenderEngineBase.cpp | 7 +- src/renderer/base/renderer.cpp | 75 +++--- src/renderer/base/renderer.hpp | 3 +- src/renderer/gdi/gdirenderer.hpp | 1 - src/renderer/gdi/paint.cpp | 7 - src/renderer/inc/IRenderData.hpp | 4 +- src/renderer/inc/IRenderEngine.hpp | 7 +- src/renderer/inc/RenderEngineBase.hpp | 3 +- src/renderer/uia/UiaRenderer.cpp | 5 - src/renderer/uia/UiaRenderer.hpp | 1 - src/renderer/vt/paint.cpp | 5 - src/renderer/vt/vtrenderer.hpp | 1 - src/renderer/wddmcon/WddmConRenderer.cpp | 5 - src/renderer/wddmcon/WddmConRenderer.hpp | 1 - src/terminal/adapter/ITerminalApi.hpp | 1 + src/terminal/adapter/adaptDispatch.cpp | 6 +- .../adapter/ut_adapter/adapterTest.cpp | 5 + 49 files changed, 571 insertions(+), 336 deletions(-) diff --git a/src/buffer/out/search.cpp b/src/buffer/out/search.cpp index fd8942e0bac..336fa2957fb 100644 --- a/src/buffer/out/search.cpp +++ b/src/buffer/out/search.cpp @@ -8,7 +8,7 @@ using namespace Microsoft::Console::Types; -bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive) +bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive, std::vector* prevResults) { const auto& textBuffer = renderData.GetTextBuffer(); const auto lastMutationId = textBuffer.GetLastMutationId(); @@ -26,10 +26,13 @@ bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, c _caseInsensitive = caseInsensitive; _lastMutationId = lastMutationId; + if (prevResults) + { + *prevResults = std::move(_results); + } _results = textBuffer.SearchText(needle, caseInsensitive); _index = reverse ? gsl::narrow_cast(_results.size()) - 1 : 0; _step = reverse ? -1 : 1; - return true; } @@ -111,28 +114,6 @@ const til::point_span* Search::GetCurrent() const noexcept return nullptr; } -void Search::HighlightResults() const -{ - std::vector toSelect; - const auto& textBuffer = _renderData->GetTextBuffer(); - - for (const auto& r : _results) - { - const auto rbStart = textBuffer.BufferToScreenPosition(r.start); - const auto rbEnd = textBuffer.BufferToScreenPosition(r.end); - - til::inclusive_rect re; - re.top = rbStart.y; - re.bottom = rbEnd.y; - re.left = rbStart.x; - re.right = rbEnd.x; - - toSelect.emplace_back(re); - } - - _renderData->SelectSearchRegions(std::move(toSelect)); -} - // Routine Description: // - Takes the found word and selects it in the screen buffer @@ -161,4 +142,4 @@ const std::vector& Search::Results() const noexcept ptrdiff_t Search::CurrentMatch() const noexcept { return _index; -} +} \ No newline at end of file diff --git a/src/buffer/out/search.h b/src/buffer/out/search.h index a338f1272c4..c115b8bdd52 100644 --- a/src/buffer/out/search.h +++ b/src/buffer/out/search.h @@ -25,7 +25,11 @@ class Search final public: Search() = default; - bool ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive); + bool ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, + const std::wstring_view& needle, + bool reverse, + bool caseInsensitive, + std::vector* prevResults = nullptr); void MoveToCurrentSelection(); void MoveToPoint(til::point anchor) noexcept; @@ -33,7 +37,6 @@ class Search final void FindNext() noexcept; const til::point_span* GetCurrent() const noexcept; - void HighlightResults() const; bool SelectCurrent() const; const std::vector& Results() const noexcept; diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index c704b412310..16461400629 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -120,6 +120,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnShowWindowChanged = std::bind(&ControlCore::_terminalShowWindowChanged, this, std::placeholders::_1); _terminal->SetShowWindowCallback(pfnShowWindowChanged); + auto pfnTextLayoutUpdated = std::bind(&ControlCore::_terminalTextLayoutUpdated, this); + _terminal->SetTextLayoutUpdatedCallback(pfnTextLayoutUpdated); + auto pfnPlayMidiNote = std::bind(&ControlCore::_terminalPlayMidiNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); _terminal->SetPlayMidiNoteCallback(pfnPlayMidiNote); @@ -1123,6 +1126,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (SUCCEEDED(hr) && hr != S_FALSE) { _connection.Resize(vp.Height(), vp.Width()); + + // let the UI know that the text layout has been updated + _terminal->NotifyTextLayoutUpdated(); } } @@ -1638,6 +1644,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation ShowWindowChanged.raise(*this, *showWindow); } + void ControlCore::_terminalTextLayoutUpdated() + { + ClearSearch(); + + // send an UpdateSearchResults event to the UI to put the Search UI into inactive state. + auto evArgs = winrt::make_self(); + evArgs->State(SearchState::Inactive); + UpdateSearchResults.raise(*this, *evArgs); + } + // Method Description: // - Plays a single MIDI note, blocking for the duration. // Arguments: @@ -1701,43 +1717,45 @@ namespace winrt::Microsoft::Terminal::Control::implementation { const auto lock = _terminal->LockForWriting(); - if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive)) + std::vector oldResults; + if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive, &oldResults)) { - _searcher.HighlightResults(); - _searcher.MoveToCurrentSelection(); _cachedSearchResultRows = {}; + if (SnapSearchResultToSelection()) + { + _searcher.MoveToCurrentSelection(); + SnapSearchResultToSelection(false); + } + + _terminal->SetSearchHighlights(_searcher.Results()); + _terminal->SetSearchHighlightFocused(_searcher.CurrentMatch()); } else { _searcher.FindNext(); + _terminal->SetSearchHighlightFocused(_searcher.CurrentMatch()); } + _renderer->TriggerSearchHighlight(oldResults); - const auto foundMatch = _searcher.SelectCurrent(); - auto foundResults = winrt::make_self(foundMatch); - if (foundMatch) + auto evArgs = winrt::make_self(); + if (!text.empty()) { - // this is used for search, - // DO NOT call _updateSelectionUI() here. - // We don't want to show the markers so manually tell it to clear it. - _terminal->SetBlockSelection(false); - UpdateSelectionMarkers.raise(*this, winrt::make(true)); - - foundResults->TotalMatches(gsl::narrow(_searcher.Results().size())); - foundResults->CurrentMatch(gsl::narrow(_searcher.CurrentMatch())); - - _terminal->AlwaysNotifyOnBufferRotation(true); + evArgs->State(SearchState::Active); + if (_searcher.GetCurrent()) + { + evArgs->FoundMatch(true); + evArgs->TotalMatches(gsl::narrow(_searcher.Results().size())); + evArgs->CurrentMatch(gsl::narrow(_searcher.CurrentMatch())); + } } - _renderer->TriggerSelection(); - // Raise a FoundMatch event, which the control will use to notify - // narrator if there was any results in the buffer - FoundMatch.raise(*this, *foundResults); + // Raise an UpdateSearchResults event, which the control will use to update the + // UI and notify the narrator about the updated search results in the buffer + UpdateSearchResults.raise(*this, *evArgs); } Windows::Foundation::Collections::IVector ControlCore::SearchResultRows() { - const auto lock = _terminal->LockForReading(); - if (!_cachedSearchResultRows) { auto results = std::vector(); @@ -1761,8 +1779,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::ClearSearch() { - _terminal->AlwaysNotifyOnBufferRotation(false); - _searcher = {}; + // nothing to clear if there's no results + if (_searcher.GetCurrent()) + { + const auto lock = _terminal->LockForWriting(); + _terminal->SetSearchHighlights({}); + _terminal->SetSearchHighlightFocused({}); + _renderer->TriggerSearchHighlight(_searcher.Results()); + _searcher = {}; + _cachedSearchResultRows = {}; + } + } + + // Method Description: + // - Tells ControlCore to snap the current search result index to currently + // selected text if the search was started using it. + void ControlCore::SnapSearchResultToSelection(bool shouldSnap) noexcept + { + _snapSearchResultToSelection = shouldSnap; + } + + // Method Description: + // - Returns true, if we should snap the current search result index to + // the currently selected text after a new search is started, else false. + bool ControlCore::SnapSearchResultToSelection() const noexcept + { + return _snapSearchResultToSelection; } void ControlCore::Close() diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 0faffb6953e..31d8fe00c1b 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -221,6 +221,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive); void ClearSearch(); + void SnapSearchResultToSelection(bool snap) noexcept; + bool SnapSearchResultToSelection() const noexcept; Windows::Foundation::Collections::IVector SearchResultRows(); @@ -281,7 +283,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event RaiseNotice; til::typed_event TransparencyChanged; til::typed_event<> ReceivedOutput; - til::typed_event FoundMatch; + til::typed_event UpdateSearchResults; til::typed_event ShowWindowChanged; til::typed_event UpdateSelectionMarkers; til::typed_event OpenHyperlink; @@ -321,6 +323,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr }; ::Search _searcher; + bool _snapSearchResultToSelection; winrt::handle _lastSwapChainHandle{ nullptr }; @@ -379,6 +382,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _terminalCursorPositionChanged(); void _terminalTaskbarProgressChanged(); void _terminalShowWindowChanged(bool showOrHide); + void _terminalTextLayoutUpdated(); void _terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 632d2d73751..91b58a41c71 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -131,6 +131,7 @@ namespace Microsoft.Terminal.Control void Search(String text, Boolean goForward, Boolean caseSensitive); void ClearSearch(); IVector SearchResultRows { get; }; + Boolean SnapSearchResultToSelection; Microsoft.Terminal.Core.Color ForegroundColor { get; }; Microsoft.Terminal.Core.Color BackgroundColor { get; }; @@ -179,7 +180,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler RaiseNotice; event Windows.Foundation.TypedEventHandler TransparencyChanged; event Windows.Foundation.TypedEventHandler ReceivedOutput; - event Windows.Foundation.TypedEventHandler FoundMatch; + event Windows.Foundation.TypedEventHandler UpdateSearchResults; event Windows.Foundation.TypedEventHandler UpdateSelectionMarkers; event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler CloseTerminalRequested; diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index 730545d42a2..4c4d1690953 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -12,7 +12,7 @@ #include "ScrollPositionChangedArgs.g.cpp" #include "RendererWarningArgs.g.cpp" #include "TransparencyChangedEventArgs.g.cpp" -#include "FoundResultsArgs.g.cpp" +#include "UpdateSearchResultsEventArgs.g.cpp" #include "ShowWindowArgs.g.cpp" #include "UpdateSelectionMarkersEventArgs.g.cpp" #include "CompletionsChangedEventArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 33c215cb3bc..247a8c988aa 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -12,7 +12,7 @@ #include "ScrollPositionChangedArgs.g.h" #include "RendererWarningArgs.g.h" #include "TransparencyChangedEventArgs.g.h" -#include "FoundResultsArgs.g.h" +#include "UpdateSearchResultsEventArgs.g.h" #include "ShowWindowArgs.g.h" #include "UpdateSelectionMarkersEventArgs.g.h" #include "CompletionsChangedEventArgs.g.h" @@ -141,14 +141,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(double, Opacity); }; - struct FoundResultsArgs : public FoundResultsArgsT + struct UpdateSearchResultsEventArgs : public UpdateSearchResultsEventArgsT { public: - FoundResultsArgs(const bool foundMatch) : - _FoundMatch(foundMatch) - { - } + UpdateSearchResultsEventArgs() = default; + WINRT_PROPERTY(SearchState, State, SearchState::Inactive); WINRT_PROPERTY(bool, FoundMatch); WINRT_PROPERTY(int32_t, TotalMatches); WINRT_PROPERTY(int32_t, CurrentMatch); diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 958b3e15a64..19b31cd97d8 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -78,8 +78,15 @@ namespace Microsoft.Terminal.Control Double Opacity { get; }; } - runtimeclass FoundResultsArgs + enum SearchState { + Inactive = 0, + Active = 1, + }; + + runtimeclass UpdateSearchResultsEventArgs + { + SearchState State { get; }; Boolean FoundMatch { get; }; Int32 TotalMatches { get; }; Int32 CurrentMatch { get; }; diff --git a/src/cascadia/TerminalControl/SearchBoxControl.cpp b/src/cascadia/TerminalControl/SearchBoxControl.cpp index dbb7bb77045..9ce4bba86a8 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.cpp +++ b/src/cascadia/TerminalControl/SearchBoxControl.cpp @@ -26,18 +26,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation }); this->CharacterReceived({ this, &SearchBoxControl::_CharacterHandler }); this->KeyDown({ this, &SearchBoxControl::_KeyDownHandler }); - this->RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) { - // Once the control is visible again we trigger SearchChanged event. - // We do this since we probably have a value from the previous search, - // and in such case logically the search changes from "nothing" to this value. - // A good example for SearchChanged event consumer is Terminal Control. - // Once the Search Box is open we want the Terminal Control - // to immediately perform the search with the value appearing in the box. - if (Visibility() == Visibility::Visible) - { - SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); - } - }); _focusableElements.insert(TextBox()); _focusableElements.insert(CloseButton()); @@ -421,6 +409,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); } + // Method Description: + // - Handler for searchbox pointer-pressed. + // - Marks pointer events as handled so they don't bubble up to the terminal. + void SearchBoxControl::SearchBoxPointerPressedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e) + { + e.Handled(true); + } + + // Method Description: + // - Handler for searchbox pointer-released. + // - Marks pointer events as handled so they don't bubble up to the terminal. + void SearchBoxControl::SearchBoxPointerReleasedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e) + { + e.Handled(true); + } + // Method Description: // - Formats a status message representing the search state: // * "Searching" - if totalMatches is negative @@ -518,6 +522,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation StatusBox().Text(status); } + // Method Description: + // - Removes the status message in the status box. + void SearchBoxControl::ClearStatus() + { + StatusBox().Text(L""); + } + // Method Description: // - Enables / disables results navigation buttons // Arguments: diff --git a/src/cascadia/TerminalControl/SearchBoxControl.h b/src/cascadia/TerminalControl/SearchBoxControl.h index 293f5d0270b..53a738c399e 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.h +++ b/src/cascadia/TerminalControl/SearchBoxControl.h @@ -39,6 +39,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void PopulateTextbox(const winrt::hstring& text); bool ContainsFocus(); void SetStatus(int32_t totalMatches, int32_t currentMatch); + void ClearStatus(); bool NavigationEnabled(); void NavigationEnabled(bool enabled); @@ -48,6 +49,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); void CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); + void SearchBoxPointerPressedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void SearchBoxPointerReleasedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); til::event Search; til::event SearchChanged; diff --git a/src/cascadia/TerminalControl/SearchBoxControl.idl b/src/cascadia/TerminalControl/SearchBoxControl.idl index 80cfcd87b33..42abd1fa1b3 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.idl +++ b/src/cascadia/TerminalControl/SearchBoxControl.idl @@ -12,6 +12,7 @@ namespace Microsoft.Terminal.Control void PopulateTextbox(String text); Boolean ContainsFocus(); void SetStatus(Int32 totalMatches, Int32 currentMatch); + void ClearStatus(); Windows.Foundation.Rect ContentClipRect{ get; }; Double OpenAnimationStartPoint{ get; }; diff --git a/src/cascadia/TerminalControl/SearchBoxControl.xaml b/src/cascadia/TerminalControl/SearchBoxControl.xaml index d69bef8a1c2..c17fcc3c010 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.xaml +++ b/src/cascadia/TerminalControl/SearchBoxControl.xaml @@ -181,6 +181,8 @@ BorderThickness="{ThemeResource FlyoutBorderThemeThickness}" CornerRadius="{ThemeResource OverlayCornerRadius}" Orientation="Horizontal" + PointerPressed="SearchBoxPointerPressedHandler" + PointerReleased="SearchBoxPointerReleasedHandler" Shadow="{StaticResource SharedShadow}" Translation="0,0,16"> diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 7c4a5b47630..96c78f0afc5 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -85,7 +85,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.TransparencyChanged = _core.TransparencyChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreTransparencyChanged }); _revokers.RaiseNotice = _core.RaiseNotice(winrt::auto_revoke, { get_weak(), &TermControl::_coreRaisedNotice }); _revokers.HoveredHyperlinkChanged = _core.HoveredHyperlinkChanged(winrt::auto_revoke, { get_weak(), &TermControl::_hoveredHyperlinkChanged }); - _revokers.FoundMatch = _core.FoundMatch(winrt::auto_revoke, { get_weak(), &TermControl::_coreFoundMatch }); + _revokers.UpdateSearchResults = _core.UpdateSearchResults(winrt::auto_revoke, { get_weak(), &TermControl::_coreUpdateSearchResults }); _revokers.UpdateSelectionMarkers = _core.UpdateSelectionMarkers(winrt::auto_revoke, { get_weak(), &TermControl::_updateSelectionMarkers }); _revokers.coreOpenHyperlink = _core.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler }); _revokers.interactivityOpenHyperlink = _interactivity.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler }); @@ -416,6 +416,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // but since code paths differ, extra work is required to ensure correctness. if (!_core.HasMultiLineSelection()) { + _core.SnapSearchResultToSelection(true); const auto selectedLine{ _core.SelectedText(true) }; _searchBox->PopulateTextbox(selectedLine); } @@ -501,7 +502,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, const RoutedEventArgs& /*args*/) { - _core.ClearSearch(); _searchBox->Close(); // Set focus back to terminal control @@ -3523,17 +3523,41 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Method Description: - // - Called when the core raises a FoundMatch event. That's done in response - // to us starting a search query with ControlCore::Search. + // - Triggers an update on scrollbar to redraw search scroll marks. + // - Called when the search results have changed, and the scroll marks' + // positions need to be updated. + void TermControl::_UpdateSearchScrollMarks() + { + // Manually send a scrollbar update, on the UI thread. We're already + // UI-driven, so that's okay. We're not really changing the scrollbar, + // but we do want to update the position of any search marks. The Core + // might send a scrollbar update event too, but if the first search hit + // is in the visible viewport, then the pips won't display until the + // user first scrolls. + auto scrollBar = ScrollBar(); + ScrollBarUpdate update{ + .newValue = scrollBar.Value(), + .newMaximum = scrollBar.Maximum(), + .newMinimum = scrollBar.Minimum(), + .newViewportSize = scrollBar.ViewportSize(), + }; + _throttledUpdateScrollbar(update); + } + + // Method Description: + // - Called when the core raises a UpdateSearchResults event. That's done in response to: + // - starting a search query with ControlCore::Search. + // - clearing search results due to change in buffer content. // - The args will tell us if there were or were not any results for that // particular search. We'll use that to control what to announce to // Narrator. When we have more elaborate search information to report, we // may want to report that here. (see GH #3920) // Arguments: - // - args: contains information about the results that were or were not found. + // - args: contains information about the search state and results that were + // or were not found. // Return Value: // - - winrt::fire_and_forget TermControl::_coreFoundMatch(const IInspectable& /*sender*/, Control::FoundResultsArgs args) + winrt::fire_and_forget TermControl::_coreUpdateSearchResults(const IInspectable& /*sender*/, Control::UpdateSearchResultsEventArgs args) { co_await wil::resume_foreground(Dispatcher()); if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(*this) }) @@ -3545,25 +3569,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation L"SearchBoxResultAnnouncement" /* unique name for this group of notifications */); } - // Manually send a scrollbar update, now, on the UI thread. We're - // already UI-driven, so that's okay. We're not really changing the - // scrollbar, but we do want to update the position of any marks. The - // Core might send a scrollbar updated event too, but if the first - // search hit is in the visible viewport, then the pips won't display - // until the user first scrolls. - auto scrollBar = ScrollBar(); - ScrollBarUpdate update{ - .newValue = scrollBar.Value(), - .newMaximum = scrollBar.Maximum(), - .newMinimum = scrollBar.Minimum(), - .newViewportSize = scrollBar.ViewportSize(), - }; - _throttledUpdateScrollbar(update); + _UpdateSearchScrollMarks(); if (_searchBox) { - _searchBox->SetStatus(args.TotalMatches(), args.CurrentMatch()); _searchBox->NavigationEnabled(true); + if (args.State() == Control::SearchState::Inactive) + { + _searchBox->ClearStatus(); + } + else + { + _searchBox->SetStatus(args.TotalMatches(), args.CurrentMatch()); + } } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 24142dc8a7b..4240bb23efe 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -352,6 +352,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive); void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); + void _UpdateSearchScrollMarks(); // TSFInputControl Handlers void _CompositionCompleted(winrt::hstring text); @@ -365,7 +366,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args); void _coreWarningBell(const IInspectable& sender, const IInspectable& args); - winrt::fire_and_forget _coreFoundMatch(const IInspectable& sender, Control::FoundResultsArgs args); + winrt::fire_and_forget _coreUpdateSearchResults(const IInspectable& sender, Control::UpdateSearchResultsEventArgs args); til::point _toPosInDips(const Core::Point terminalCellPos); void _throttledUpdateScrollbar(const ScrollBarUpdate& update); @@ -394,7 +395,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::TransparencyChanged_revoker TransparencyChanged; Control::ControlCore::RaiseNotice_revoker RaiseNotice; Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged; - Control::ControlCore::FoundMatch_revoker FoundMatch; + Control::ControlCore::UpdateSearchResults_revoker UpdateSearchResults; Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers; Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink; Control::ControlCore::TitleChanged_revoker TitleChanged; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 0d2c6dbf43b..4eccd3be999 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1259,6 +1259,35 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi _pfnCompletionsChanged.swap(pfn); } +void Terminal::SetTextLayoutUpdatedCallback(std::function pfn) noexcept +{ + _pfnTextLayoutUpdated.swap(pfn); +} + +// Method Description: +// - Stores the search highlighted regions in the terminal +void Terminal::SetSearchHighlights(const std::vector& highlights) noexcept +{ + _assertLocked(); + _searchHighlights = highlights; +} + +// Method Description: +// - Stores the focused search highlighted region in the terminal +// - If the region isn't empty, it will be brought into view +void Terminal::SetSearchHighlightFocused(const size_t focusedIdx) +{ + _assertLocked(); + _searchHighlightFocused = focusedIdx; + + // bring the focused region into the view if the index is in valid range + if (focusedIdx < _searchHighlights.size()) + { + const auto focused = til::at(_searchHighlights, focusedIdx); + _ScrollToPoints(focused.start, focused.end); + } +} + Scheme Terminal::GetColorScheme() const { const auto& renderSettings = GetRenderSettings(); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index fc57907ad19..6a6017db889 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -155,6 +155,7 @@ class Microsoft::Terminal::Core::Terminal final : bool IsVtInputEnabled() const noexcept override; void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override; void NotifyBufferRotation(const int delta) override; + void NotifyTextLayoutUpdated() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; @@ -210,12 +211,12 @@ class Microsoft::Terminal::Core::Terminal final : std::pair GetAttributeColors(const TextAttribute& attr) const noexcept override; std::vector GetSelectionRects() noexcept override; - std::vector GetSearchSelectionRects() noexcept override; + std::span GetSearchHighlights() const noexcept override; + const til::point_span* GetSearchHighlightFocused() const noexcept override; const bool IsSelectionActive() const noexcept override; const bool IsBlockSelection() const noexcept override; void ClearSelection() override; void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override; - void SelectSearchRegions(std::vector source) override; const til::point GetSelectionAnchor() const noexcept override; const til::point GetSelectionEnd() const noexcept override; const std::wstring_view GetConsoleTitle() const noexcept override; @@ -232,6 +233,9 @@ class Microsoft::Terminal::Core::Terminal final : void SetShowWindowCallback(std::function pfn) noexcept; void SetPlayMidiNoteCallback(std::function pfn) noexcept; void CompletionsChangedCallback(std::function pfn) noexcept; + void SetTextLayoutUpdatedCallback(std::function pfn) noexcept; + void SetSearchHighlights(const std::vector& highlights) noexcept; + void SetSearchHighlightFocused(const size_t focusedIdx); void BlinkCursor() noexcept; void SetCursorOn(const bool isOn) noexcept; @@ -340,6 +344,7 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnShowWindowChanged; std::function _pfnPlayMidiNote; std::function _pfnCompletionsChanged; + std::function _pfnTextLayoutUpdated; RenderSettings _renderSettings; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; @@ -349,6 +354,9 @@ class Microsoft::Terminal::Core::Terminal final : std::wstring _startingTitle; std::optional _startingTabColor; + std::vector _searchHighlights; + size_t _searchHighlightFocused = 0; + CursorType _defaultCursorShape = CursorType::Legacy; til::enumset _systemMode{ Mode::AutoWrap }; @@ -381,7 +389,6 @@ class Microsoft::Terminal::Core::Terminal final : til::point pivot; }; std::optional _selection; - std::vector _searchSelections; bool _blockSelection = false; std::wstring _wordDelimiters; SelectionExpansion _multiClickSelectionMode = SelectionExpansion::Char; @@ -447,6 +454,7 @@ class Microsoft::Terminal::Core::Terminal final : Microsoft::Console::Types::Viewport _GetVisibleViewport() const noexcept; void _PreserveUserScrollOffset(const int viewportDelta) noexcept; + til::CoordType _ScrollToPoints(const til::point coordStart, const til::point coordEnd); void _NotifyScrollEvent(); @@ -460,7 +468,6 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region TextSelection // These methods are defined in TerminalSelection.cpp std::vector _GetSelectionRects() const noexcept; - std::vector _GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept; std::vector _GetSelectionSpans() const noexcept; std::pair _PivotSelection(const til::point targetPos, bool& targetStart) const noexcept; std::pair _ExpandSelectionAnchors(std::pair anchors) const; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 42f1c903c33..07c81649b7c 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -239,6 +239,8 @@ void Terminal::UseAlternateScreenBuffer(const TextAttribute& attrs) // Update scrollbars _NotifyScrollEvent(); + NotifyTextLayoutUpdated(); + // redraw the screen try { @@ -296,6 +298,8 @@ void Terminal::UseMainScreenBuffer() // Update scrollbars _NotifyScrollEvent(); + NotifyTextLayoutUpdated(); + // redraw the screen _activeBuffer().TriggerRedrawAll(); } @@ -370,3 +374,15 @@ void Terminal::NotifyBufferRotation(const int delta) _NotifyScrollEvent(); } } + +// Method Description: +// - Notifies the terminal UI layer that the text layout has changed. +// - This will be called when new text is added, or when the text is +// rearranged in the buffer due to window resize. +void Terminal::NotifyTextLayoutUpdated() +{ + if (_pfnTextLayoutUpdated) + { + _pfnTextLayoutUpdated(); + } +} diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index ae1db3507b4..44c9051ed01 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -63,40 +63,6 @@ std::vector Terminal::_GetSelectionRects() const noexcept return result; } -// Method Description: -// - Helper to determine the selected region of the buffer. Used for rendering. -// Return Value: -// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin. -std::vector Terminal::_GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept -{ - std::vector result; - try - { - auto lowerIt = std::lower_bound(_searchSelections.begin(), _searchSelections.end(), viewport.Top(), [](const til::inclusive_rect& rect, til::CoordType value) { - return rect.top < value; - }); - - auto upperIt = std::upper_bound(_searchSelections.begin(), _searchSelections.end(), viewport.BottomExclusive(), [](til::CoordType value, const til::inclusive_rect& rect) { - return value < rect.top; - }); - - for (auto selection = lowerIt; selection != upperIt; ++selection) - { - const auto start = til::point{ selection->left, selection->top }; - const auto end = til::point{ selection->right, selection->bottom }; - const auto adj = _activeBuffer().GetTextRects(start, end, _blockSelection, false); - for (auto a : adj) - { - result.emplace_back(a); - } - } - - return result; - } - CATCH_LOG(); - return result; -} - // Method Description: // - Identical to GetTextRects if it's a block selection, else returns a single span for the whole selection. // Return Value: @@ -858,7 +824,6 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos) noex void Terminal::ClearSelection() { _assertLocked(); - _searchSelections.clear(); _selection = std::nullopt; _selectionMode = SelectionInteractionMode::None; _selectionIsTargetingUrl = false; diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 8dd9b4dae9a..a9eaf11fa80 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -150,32 +150,37 @@ catch (...) return {}; } -std::vector Terminal::GetSearchSelectionRects() noexcept -try +// Method Description: +// - Helper to determine the search highlights in the buffer. Used for rendering. +// Return Value: +// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin. +std::span Terminal::GetSearchHighlights() const noexcept { - std::vector result; + _assertLocked(); + return _searchHighlights; +} - for (const auto& lineRect : _GetSearchSelectionRects(_GetVisibleViewport())) +const til::point_span* Terminal::GetSearchHighlightFocused() const noexcept +{ + _assertLocked(); + if (_searchHighlightFocused < _searchHighlights.size()) { - result.emplace_back(Viewport::FromInclusive(lineRect)); + return &til::at(_searchHighlights, _searchHighlightFocused); } - - return result; -} -catch (...) -{ - LOG_CAUGHT_EXCEPTION(); - return {}; + return nullptr; } -void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd) +// Method Description: +// - If necessary, scrolls the viewport such that the start point is in the +// viewport, and if that's already the case, also brings the end point inside +// the viewport +// Arguments: +// - coordStart - The start point +// - coordEnd - The end point +// Return Value: +// - The updated scroll offset +til::CoordType Terminal::_ScrollToPoints(const til::point coordStart, const til::point coordEnd) { -#pragma warning(push) -#pragma warning(disable : 26496) // cpp core checks wants these const, but they're decremented below. - auto realCoordStart = coordStart; - auto realCoordEnd = coordEnd; -#pragma warning(pop) - auto notifyScrollChange = false; if (coordStart.y < _VisibleStartIndex()) { @@ -199,28 +204,18 @@ void Terminal::SelectNewRegion(const til::point coordStart, const til::point coo _NotifyScrollEvent(); } - realCoordStart.y -= _VisibleStartIndex(); - realCoordEnd.y -= _VisibleStartIndex(); - - SetSelectionAnchor(realCoordStart); - SetSelectionEnd(realCoordEnd, SelectionExpansion::Char); + return _scrollOffset; } -void Terminal::SelectSearchRegions(std::vector rects) +void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd) { - _searchSelections.clear(); - for (auto& rect : rects) - { - rect.top -= _VisibleStartIndex(); - rect.bottom -= _VisibleStartIndex(); + const auto newScrollOffset = _ScrollToPoints(coordStart, coordEnd); - const auto realStart = _ConvertToBufferCell(til::point{ rect.left, rect.top }); - const auto realEnd = _ConvertToBufferCell(til::point{ rect.right, rect.bottom }); - - auto rr = til::inclusive_rect{ realStart.x, realStart.y, realEnd.x, realEnd.y }; - - _searchSelections.emplace_back(rr); - } + // update the selection coordinates so they're relative to the new scroll-offset + const auto newCoordStart = til::point{ coordStart.x, coordStart.y - newScrollOffset }; + const auto newCoordEnd = til::point{ coordEnd.x, coordEnd.y - newScrollOffset }; + SetSelectionAnchor(newCoordStart); + SetSelectionEnd(newCoordEnd, SelectionExpansion::Char); } const std::wstring_view Terminal::GetConsoleTitle() const noexcept diff --git a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp index 92770d79727..33cf3cee8cf 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp @@ -51,7 +51,6 @@ namespace HRESULT PaintBufferLine(std::span /*clusters*/, til::point /*coord*/, bool /*fTrimLeft*/, bool /*lineWrapped*/) noexcept { return S_OK; } HRESULT PaintBufferGridLines(GridLineSet /*lines*/, COLORREF /*gridlineColor*/, COLORREF /*underlineColor*/, size_t /*cchLine*/, til::point /*coordTarget*/) noexcept { return S_OK; } HRESULT PaintSelection(const til::rect& /*rect*/) noexcept { return S_OK; } - HRESULT PaintSelections(const std::vector& /*rects*/) noexcept { return S_OK; } HRESULT PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; } HRESULT UpdateDrawingBrushes(const TextAttribute& /*textAttributes*/, const RenderSettings& /*renderSettings*/, gsl::not_null /*pData*/, bool /*usingSoftFont*/, bool /*isSettingDefaultBrushes*/) noexcept { return S_OK; } HRESULT UpdateFont(const FontInfoDesired& /*FontInfoDesired*/, _Out_ FontInfo& /*FontInfo*/) noexcept { return S_OK; } diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index fb20d74b75b..25a2f1fdb2d 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -424,7 +424,12 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta) } } -void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/) +void ConhostInternalGetSet::NotifyTextLayoutUpdated() { // Not implemented for conhost. } + +void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/) +{ + // Not implemented for conhost. +} \ No newline at end of file diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 01d8abaf17f..ca5711f6387 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -68,6 +68,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void NotifyAccessibilityChange(const til::rect& changedRect) override; void NotifyBufferRotation(const int delta) override; + void NotifyTextLayoutUpdated() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index d9b094626ba..c2ad7e99d5f 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -81,10 +81,10 @@ std::vector RenderData::GetSelectionRects() noexcept // Method Description: // - Retrieves one rectangle per line describing the area of the viewport -// that should be highlighted in some way to represent a user-interactive selection +// that should be highlighted // Return Value: -// - Vector of Viewports describing the area selected -std::vector RenderData::GetSearchSelectionRects() noexcept +// - Vector of rects describing the highlighted area +std::span RenderData::GetSearchHighlights() const noexcept { return {}; } @@ -381,8 +381,9 @@ void RenderData::SelectNewRegion(const til::point coordStart, const til::point c Selection::Instance().SelectNewRegion(coordStart, coordEnd); } -void RenderData::SelectSearchRegions(std::vector source) +const til::point_span* RenderData::GetSearchHighlightFocused() const noexcept { + return nullptr; } // Routine Description: diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index 52056d7a6f5..db879fb54e5 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -26,7 +26,6 @@ class RenderData final : const FontInfo& GetFontInfo() const noexcept override; std::vector GetSelectionRects() noexcept override; - std::vector GetSearchSelectionRects() noexcept override; void LockConsole() noexcept override; void UnlockConsole() noexcept override; @@ -55,7 +54,8 @@ class RenderData final : const bool IsBlockSelection() const noexcept override; void ClearSelection() override; void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override; - void SelectSearchRegions(std::vector source) override; + std::span GetSearchHighlights() const noexcept override; + const til::point_span* GetSearchHighlightFocused() const noexcept override; const til::point GetSelectionAnchor() const noexcept override; const til::point GetSelectionEnd() const noexcept override; const bool IsUiaDataInitialized() const noexcept override { return true; } diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index 23d2d3a9c7e..dbabcc5a280 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -274,11 +274,6 @@ class MockRenderData : public IRenderData return std::vector{}; } - std::vector GetSearchSelectionRects() noexcept override - { - return std::vector{}; - } - void LockConsole() noexcept override { } @@ -360,8 +355,14 @@ class MockRenderData : public IRenderData { } - void SelectSearchRegions(std::vector /*source*/) override + std::span GetSearchHighlights() const noexcept override + { + return {}; + } + + const til::point_span* GetSearchHighlightFocused() const noexcept override { + return nullptr; } const til::point GetSelectionAnchor() const noexcept diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 678afe706f6..17bc9d1c861 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -288,6 +288,40 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" { return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0; } + + // Calls func(row, begX, endX) for each row and begX and begY are inclusive coordinates, + // because point_span itself also uses inclusive coordinates. + // In other words, it turns a + // ``` + // +----------------+ + // | #########| + // |################| + // |#### | + // +----------------+ + // ``` + // into: + // ``` + // func(0, 8, 15) + // func(1, 0, 15) + // func(2, 0, 4) + // ``` + constexpr void iterate_rows(til::CoordType width, auto&& func) const + { + // Copy the members so that the compiler knows it doesn't + // need to re-read them on every loop iteration. + const auto w = width - 1; + const auto ax = std::clamp(start.x, 0, w); + const auto ay = start.y; + const auto bx = std::clamp(end.x, 0, w); + const auto by = end.y; + + for (auto y = ay; y <= by; ++y) + { + const auto x1 = y != ay ? 0 : ax; + const auto x2 = y != by ? w : bx; + func(y, x1, x2); + } + } }; } diff --git a/src/inc/til/rect.h b/src/inc/til/rect.h index bc7b07b6c83..a62500950eb 100644 --- a/src/inc/til/rect.h +++ b/src/inc/til/rect.h @@ -201,6 +201,20 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0; } + constexpr rect to_origin(const rect& other) const + { + return to_origin(other.origin()); + } + + constexpr rect to_origin(const point& origin) const + { + const auto l = details::extract(::base::CheckSub(left, origin.x)); + const auto t = details::extract(::base::CheckSub(top, origin.y)); + const auto r = details::extract(::base::CheckSub(right, origin.x)); + const auto b = details::extract(::base::CheckSub(bottom, origin.y)); + return { l, t, r, b }; + } + explicit constexpr operator bool() const noexcept { return left >= 0 && top >= 0 && right > left && bottom > top; diff --git a/src/interactivity/onecore/BgfxEngine.cpp b/src/interactivity/onecore/BgfxEngine.cpp index a730ae8219e..d508603d5f9 100644 --- a/src/interactivity/onecore/BgfxEngine.cpp +++ b/src/interactivity/onecore/BgfxEngine.cpp @@ -161,11 +161,6 @@ CATCH_RETURN() return S_OK; } -[[nodiscard]] HRESULT BgfxEngine::PaintSelections(const std::vector& /*rects*/) noexcept -{ - return S_OK; -} - [[nodiscard]] HRESULT BgfxEngine::PaintCursor(const CursorOptions& options) noexcept try { diff --git a/src/interactivity/onecore/BgfxEngine.hpp b/src/interactivity/onecore/BgfxEngine.hpp index c787baba392..e9759ce0306 100644 --- a/src/interactivity/onecore/BgfxEngine.hpp +++ b/src/interactivity/onecore/BgfxEngine.hpp @@ -53,7 +53,6 @@ namespace Microsoft::Console::Render const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; - [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 9c98a12cf52..9ef05279bf5 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -95,6 +95,28 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2& out) noexcept return S_OK; } +[[nodiscard]] HRESULT AtlasEngine::InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept +{ + const auto viewportOrigin = til::point{ _api.s->viewportOffset.x, _api.s->viewportOffset.y }; + const auto viewport = til::rect{ 0, 0, _api.s->viewportCellCount.x, _api.s->viewportCellCount.y }; + const auto cellCountX = static_cast(_api.s->viewportCellCount.x); + for (const auto& hi : highlights) + { + hi.iterate_rows(cellCountX, [&](til::CoordType row, til::CoordType beg, til::CoordType end) { + const auto shift = til::at(renditions, row) != LineRendition::SingleWidth ? 1 : 0; + beg <<= shift; + end <<= shift; + til::rect rect{ beg, row, end + 1, row + 1 }; + rect = rect.to_origin(viewportOrigin); + rect &= viewport; + _api.invalidatedRows.start = gsl::narrow_cast(std::min(_api.invalidatedRows.start, std::max(0, rect.top))); + _api.invalidatedRows.end = gsl::narrow_cast(std::max(_api.invalidatedRows.end, std::max(0, rect.bottom))); + }); + } + + return S_OK; +} + [[nodiscard]] HRESULT AtlasEngine::InvalidateScroll(const til::point* const pcoordDelta) noexcept { // InvalidateScroll() is a "synchronous" API. Any Invalidate()s after diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index 8cfbe995a14..f1e43bc98a3 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -285,8 +285,36 @@ CATCH_RETURN() return S_OK; } -[[nodiscard]] HRESULT AtlasEngine::PrepareRenderInfo(const RenderFrameInfo& info) noexcept +[[nodiscard]] HRESULT AtlasEngine::PrepareRenderInfo(RenderFrameInfo info) noexcept { + // remove the highlighted regions that falls outside of the dirty region + { + const auto& highlights = info.searchHighlights; + + // get the buffer origin relative to the viewport, and use it to calculate + // the dirty region to be relative to the buffer origin + const til::CoordType offsetX = _p.s->viewportOffset.x; + const til::CoordType offsetY = _p.s->viewportOffset.y; + const til::point bufferOrigin{ -offsetX, -offsetY }; + const auto dr = _api.dirtyRect.to_origin(bufferOrigin); + + const auto hiBeg = std::lower_bound(highlights.begin(), highlights.end(), dr.top, [](const auto& ps, const auto& drTop) { return ps.end.y < drTop; }); + const auto hiEnd = std::upper_bound(hiBeg, highlights.end(), dr.bottom, [](const auto& drBottom, const auto& ps) { return drBottom < ps.start.y; }); + _api.searchHighlights = { hiBeg, hiEnd }; + + // do the same for the focused search highlight + if (info.searchHighlightFocused) + { + const auto focusedStartY = info.searchHighlightFocused->start.y; + const auto focusedEndY = info.searchHighlightFocused->end.y; + const auto isFocusedInside = (focusedStartY >= dr.top && focusedStartY < dr.bottom) || (focusedEndY >= dr.top && focusedEndY < dr.bottom) || (focusedStartY < dr.top && focusedEndY >= dr.bottom); + if (isFocusedInside) + { + _api.searchHighlightFocused = { info.searchHighlightFocused, 1 }; + } + } + } + return S_OK; } @@ -308,6 +336,126 @@ CATCH_RETURN() return S_OK; } +void AtlasEngine::_fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept +{ + const auto bitmap = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y; + const auto shift = gsl::narrow_cast(_p.rows[y]->lineRendition != LineRendition::SingleWidth); + auto beg = bitmap + (x1 << shift); + auto end = bitmap + (x2 << shift); + + const u32 colors[] = { + u32ColorPremultiply(bgColor), + fgColor, + }; + + // This fills the color in the background bitmap, and then in the foreground bitmap. + for (size_t i = 0; i < 2; ++i) + { + const auto color = colors[i]; + + for (auto it = beg; it != end; ++it) + { + if (*it != color) + { + _p.colorBitmapGenerations[i].bump(); + std::fill(it, end, color); + break; + } + } + + // go to the same range in the same row, but in the foreground bitmap + beg += _p.colorBitmapDepthStride; + end += _p.colorBitmapDepthStride; + } +} + +// Method Description: +// - Applies the given highlighting colors to the columns in the highlighted regions within a given range. +// - Resumes from the last partially painted region if any. +// Arguments: +// - highlights: the list of highlighted regions (yet to be painted) +// - row: the row for which highlighted regions are to be painted +// - begX: the starting (inclusive) column to paint from (in Buffer coord) +// - endX: the ending (exclusive) column to paint up to (in Buffer coord) +// - fgColor: the foreground highlight color +// - bgColor: the background highlight color +// Returns: +// - S_OK if we painted successfully, else an appropriate HRESULT error code +[[nodiscard]] HRESULT AtlasEngine::_drawHighlighted(std::span& highlights, const u16 row, const u16 begX, const u16 endX, const u32 fgColor, const u32 bgColor) noexcept +try +{ + if (highlights.empty()) + { + return S_OK; + } + + const til::CoordType y = row; + const til::CoordType x1 = begX; + const til::CoordType x2 = endX; + const auto offset = til::point{ _p.s->viewportOffset.x, _p.s->viewportOffset.y }; + auto it = highlights.begin(); + const auto itEnd = highlights.end(); + auto hiStart = it->start - offset; + auto hiEnd = it->end - offset; + + // Nothing to paint if we haven't reached the row where the highlight region begins + if (y < hiStart.y) + { + return S_OK; + } + + // We might have painted a multi-line highlight region in the last call, + // so we need to make sure we paint the whole region before moving on to + // the next region. + if (y > hiStart.y) + { + const auto isFinalRow = y == hiEnd.y; + const auto end = isFinalRow ? std::min(hiEnd.x + 1, x2) : x2; + _fillColorBitmap(row, x1, end, fgColor, bgColor); + + // Return early if we couldn't paint the whole region. We will resume + // from here in the next call. + if (!isFinalRow || end == x2) + { + return S_OK; + } + + ++it; + } + + // Pick a highlighted region and (try) to paint it + while (it != itEnd) + { + hiStart = it->start - offset; + hiEnd = it->end - offset; + + const auto isStartInside = y == hiStart.y && hiStart.x < x2; + const auto isEndInside = y == hiEnd.y && hiEnd.x < x2; + if (isStartInside && isEndInside) + { + _fillColorBitmap(row, hiStart.x, static_cast(hiEnd.x) + 1, fgColor, bgColor); + ++it; + } + else + { + // Paint the region partially if start is within the range. + if (isStartInside) + { + const auto start = std::max(x1, hiStart.x); + _fillColorBitmap(y, start, x2, fgColor, bgColor); + } + + break; + } + } + + // Shorten the list by removing processed regions + highlights = { it, itEnd }; + + return S_OK; +} +CATCH_RETURN() + [[nodiscard]] HRESULT AtlasEngine::PaintBufferLine(std::span clusters, til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept try { @@ -345,34 +493,12 @@ try _api.bufferLineColumn.emplace_back(columnEnd); } - { - const auto row = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y; - auto beg = row + (static_cast(x) << shift); - auto end = row + (static_cast(columnEnd) << shift); - - const u32 colors[] = { - u32ColorPremultiply(_api.currentBackground), - _api.currentForeground, - }; - - for (size_t i = 0; i < 2; ++i) - { - const auto color = colors[i]; - - for (auto it = beg; it != end; ++it) - { - if (*it != color) - { - _p.colorBitmapGenerations[i].bump(); - std::fill(it, end, color); - break; - } - } + // Apply the current foreground and background colors to the cells + _fillColorBitmap(y, x, columnEnd, _api.currentForeground, _api.currentBackground); - beg += _p.colorBitmapDepthStride; - end += _p.colorBitmapDepthStride; - } - } + // Apply the highlighting colors to the highlighted cells + RETURN_IF_FAILED(_drawHighlighted(_api.searchHighlights, y, x, columnEnd, highlightFg, highlightBg)); + RETURN_IF_FAILED(_drawHighlighted(_api.searchHighlightFocused, y, x, columnEnd, highlightFocusFg, highlightFocusBg)); _api.lastPaintBufferLineCoord = { x, y }; return S_OK; @@ -418,40 +544,6 @@ try } CATCH_RETURN() -[[nodiscard]] HRESULT AtlasEngine::PaintSelections(const std::vector& rects) noexcept -try -{ - if (rects.empty()) - { - return S_OK; - } - - for (const auto& rect : rects) - { - const auto y = gsl::narrow_cast(clamp(rect.top, 0, _p.s->viewportCellCount.y)); - const auto from = gsl::narrow_cast(clamp(rect.left, 0, _p.s->viewportCellCount.x - 1)); - const auto to = gsl::narrow_cast(clamp(rect.right, from, _p.s->viewportCellCount.x)); - - if (rect.bottom <= 0 || rect.top >= _p.s->viewportCellCount.y) - { - continue; - } - - const auto bg = &_p.backgroundBitmap[_p.colorBitmapRowStride * y]; - const auto fg = &_p.foregroundBitmap[_p.colorBitmapRowStride * y]; - std::fill(bg + from, bg + to, 0xff3296ff); - std::fill(fg + from, fg + to, 0xff000000); - } - - for (int i = 0; i < 2; ++i) - { - _p.colorBitmapGenerations[i].bump(); - } - - return S_OK; -} -CATCH_RETURN() - [[nodiscard]] HRESULT AtlasEngine::PaintCursor(const CursorOptions& options) noexcept try { diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 6d346d7bfe4..9f38b7094ff 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -33,19 +33,19 @@ namespace Microsoft::Console::Render::Atlas [[nodiscard]] HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept override; [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; + [[nodiscard]] HRESULT InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept override; [[nodiscard]] HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept override; [[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override; - [[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override; + [[nodiscard]] HRESULT PrepareRenderInfo(RenderFrameInfo info) noexcept override; [[nodiscard]] HRESULT ResetLineTransform() noexcept override; [[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept override; [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; - [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; @@ -91,6 +91,8 @@ namespace Microsoft::Console::Render::Atlas void _mapCharacters(const wchar_t* text, u32 textLength, u32* mappedLength, IDWriteFontFace2** mappedFontFace) const; void _mapComplex(IDWriteFontFace2* mappedFontFace, u32 idx, u32 length, ShapedRow& row); ATLAS_ATTR_COLD void _mapReplacementCharacter(u32 from, u32 to, ShapedRow& row); + void _fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept; + [[nodiscard]] HRESULT _drawHighlighted(std::span& highlights, const u16 row, const u16 begX, const u16 endX, const u32 fgColor, const u32 bgColor) noexcept; // AtlasEngine.api.cpp void _resolveTransparencySettings() noexcept; @@ -117,6 +119,11 @@ namespace Microsoft::Console::Render::Atlas static constexpr range invalidatedRowsNone{ u16max, u16min }; static constexpr range invalidatedRowsAll{ u16min, u16max }; + static constexpr u32 highlightBg = 0xff00ffff; + static constexpr u32 highlightFg = 0xff000000; + static constexpr u32 highlightFocusBg = 0xff3296ff; + static constexpr u32 highlightFocusFg = 0xff000000; + std::unique_ptr _b; RenderingPayload _p; @@ -173,6 +180,10 @@ namespace Microsoft::Console::Render::Atlas // UpdateHyperlinkHoveredId() u16 hyperlinkHoveredId = 0; + // These tracks the highlighted regions on the screen that are yet to be painted. + std::span searchHighlights; + std::span searchHighlightFocused; + // dirtyRect is a computed value based on invalidatedRows. til::rect dirtyRect; // These "invalidation" fields are reset in EndPaint() diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 224ff155f35..6ab523a1a4d 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -7,6 +7,11 @@ using namespace Microsoft::Console; using namespace Microsoft::Console::Render; +[[nodiscard]] HRESULT RenderEngineBase::InvalidateHighlight(std::span /*highlights*/, const std::vector& /*renditions*/) noexcept +{ + return S_OK; +} + HRESULT RenderEngineBase::InvalidateTitle(const std::wstring_view proposedTitle) noexcept { if (proposedTitle != _lastFrameTitle) @@ -42,7 +47,7 @@ HRESULT RenderEngineBase::UpdateSoftFont(const std::span /*bitPa return S_FALSE; } -HRESULT RenderEngineBase::PrepareRenderInfo(const RenderFrameInfo& /*info*/) noexcept +HRESULT RenderEngineBase::PrepareRenderInfo(RenderFrameInfo /*info*/) noexcept { return S_FALSE; } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 4137d140d36..2d6f4cc1a28 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -360,7 +360,6 @@ void Renderer::TriggerSelection() { // Get selection rectangles auto rects = _GetSelectionRects(); - auto searchSelections = _GetSearchSelectionRects(); // Make a viewport representing the coordinates that are currently presentable. const til::rect viewport{ _pData->GetViewport().Dimensions() }; @@ -373,20 +372,45 @@ void Renderer::TriggerSelection() FOREACH_ENGINE(pEngine) { - LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSearchSelection)); LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection)); - LOG_IF_FAILED(pEngine->InvalidateSelection(searchSelections)); LOG_IF_FAILED(pEngine->InvalidateSelection(rects)); } _previousSelection = std::move(rects); - _previousSearchSelection = std::move(searchSelections); - NotifyPaintFrame(); } CATCH_LOG(); } +// Routine Description: +// - Called when the search highlight areas in the console have changed. +void Renderer::TriggerSearchHighlight(const std::vector& oldHighlights) +try +{ + const auto& buffer = _pData->GetTextBuffer(); + const auto rows = buffer.TotalRowCount(); + + std::vector renditions; + renditions.reserve(rows); + for (til::CoordType row = 0; row < rows; ++row) + { + renditions.emplace_back(buffer.GetLineRendition(row)); + } + + // no need to invalidate focused search highlight separately as they are + // included in (all) search highlights. + const auto newHighlights = _pData->GetSearchHighlights(); + + FOREACH_ENGINE(pEngine) + { + LOG_IF_FAILED(pEngine->InvalidateHighlight(oldHighlights, renditions)); + LOG_IF_FAILED(pEngine->InvalidateHighlight(newHighlights, renditions)); + } + + NotifyPaintFrame(); +} +CATCH_LOG() + // Routine Description: // - Called when we want to check if the viewport has moved and scroll accordingly if so. // Arguments: @@ -1129,8 +1153,9 @@ void Renderer::_PaintCursor(_In_ IRenderEngine* const pEngine) [[nodiscard]] HRESULT Renderer::_PrepareRenderInfo(_In_ IRenderEngine* const pEngine) { RenderFrameInfo info; - info.cursorInfo = _currentCursorOptions; - return pEngine->PrepareRenderInfo(info); + info.searchHighlights = _pData->GetSearchHighlights(); + info.searchHighlightFocused = _pData->GetSearchHighlightFocused(); + return pEngine->PrepareRenderInfo(std::move(info)); } // Routine Description: @@ -1212,19 +1237,10 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine) // Get selection rectangles const auto rectangles = _GetSelectionRects(); - const auto searchRectangles = _GetSearchSelectionRects(); std::vector dirtySearchRectangles; for (auto& dirtyRect : dirtyAreas) { - for (const auto& sr : searchRectangles) - { - if (const auto rectCopy = sr & dirtyRect) - { - dirtySearchRectangles.emplace_back(rectCopy); - } - } - for (const auto& rect : rectangles) { if (const auto rectCopy = rect & dirtyRect) @@ -1233,11 +1249,6 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine) } } } - - if (!dirtySearchRectangles.empty()) - { - LOG_IF_FAILED(pEngine->PaintSelections(std::move(dirtySearchRectangles))); - } } CATCH_LOG(); } @@ -1303,28 +1314,6 @@ std::vector Renderer::_GetSelectionRects() const return result; } -std::vector Renderer::_GetSearchSelectionRects() const -{ - const auto& buffer = _pData->GetTextBuffer(); - auto rects = _pData->GetSearchSelectionRects(); - // Adjust rectangles to viewport - auto view = _pData->GetViewport(); - - std::vector result; - result.reserve(rects.size()); - - for (auto rect : rects) - { - // Convert buffer offsets to the equivalent range of screen cells - // expected by callers, taking line rendition into account. - const auto lineRendition = buffer.GetLineRendition(rect.Top()); - rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition)); - result.emplace_back(view.ConvertToOrigin(rect).ToExclusive()); - } - - return result; -} - // Method Description: // - Offsets all of the selection rectangles we might be holding onto // as the previously selected area. If the whole viewport scrolls, diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index dc027acea20..a8184ce63df 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -54,6 +54,7 @@ namespace Microsoft::Console::Render void TriggerTeardown() noexcept; void TriggerSelection(); + void TriggerSearchHighlight(const std::vector& oldHighlights); void TriggerScroll(); void TriggerScroll(const til::point* const pcoordDelta); @@ -110,7 +111,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes); [[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine); std::vector _GetSelectionRects() const; - std::vector _GetSearchSelectionRects() const; void _ScrollPreviousSelection(const til::point delta); [[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine); bool _isInHoveredInterval(til::point coordTarget) const noexcept; @@ -129,7 +129,6 @@ namespace Microsoft::Console::Render std::optional _currentCursorOptions; std::vector _clusterBuffer; std::vector _previousSelection; - std::vector _previousSearchSelection; std::function _pfnBackgroundColorChanged; std::function _pfnFrameColorChanged; std::function _pfnRendererEnteredErrorState; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 601085c35ff..78a3f02953f 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -57,7 +57,6 @@ namespace Microsoft::Console::Render const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; - [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index c449b4d0835..6fd8d7521ee 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -834,13 +834,6 @@ CATCH_RETURN(); return S_OK; } -[[nodiscard]] HRESULT GdiEngine::PaintSelections(const std::vector& rects) noexcept -{ - UNREFERENCED_PARAMETER(rects); - - return S_OK; -} - #ifdef DBG void GdiEngine::_CreateDebugWindow() diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index cfc035a7f9b..f9c08c83b9f 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -47,7 +47,8 @@ namespace Microsoft::Console::Render virtual const TextBuffer& GetTextBuffer() const noexcept = 0; virtual const FontInfo& GetFontInfo() const noexcept = 0; virtual std::vector GetSelectionRects() noexcept = 0; - virtual std::vector GetSearchSelectionRects() noexcept = 0; + virtual std::span GetSearchHighlights() const noexcept = 0; + virtual const til::point_span* GetSearchHighlightFocused() const noexcept = 0; virtual void LockConsole() noexcept = 0; virtual void UnlockConsole() noexcept = 0; @@ -72,7 +73,6 @@ namespace Microsoft::Console::Render virtual const bool IsBlockSelection() const = 0; virtual void ClearSelection() = 0; virtual void SelectNewRegion(const til::point coordStart, const til::point coordEnd) = 0; - virtual void SelectSearchRegions(std::vector source) = 0; virtual const til::point GetSelectionAnchor() const noexcept = 0; virtual const til::point GetSelectionEnd() const noexcept = 0; virtual const bool IsUiaDataInitialized() const noexcept = 0; diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 5dcaf17685a..7533de8a4d0 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -29,7 +29,8 @@ namespace Microsoft::Console::Render { struct RenderFrameInfo { - std::optional cursorInfo; + std::span searchHighlights; + const til::point_span* searchHighlightFocused; }; enum class GridLines @@ -66,19 +67,19 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector& rectangles) noexcept = 0; + [[nodiscard]] virtual HRESULT InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept = 0; [[nodiscard]] virtual HRESULT NotifyNewText(const std::wstring_view newText) noexcept = 0; - [[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0; + [[nodiscard]] virtual HRESULT PrepareRenderInfo(RenderFrameInfo info) noexcept = 0; [[nodiscard]] virtual HRESULT ResetLineTransform() noexcept = 0; [[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept = 0; [[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0; [[nodiscard]] virtual HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept = 0; [[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF gridlineColor, COLORREF underlineColor, size_t cchLine, til::point coordTarget) noexcept = 0; [[nodiscard]] virtual HRESULT PaintSelection(const til::rect& rect) noexcept = 0; - [[nodiscard]] virtual HRESULT PaintSelections(const std::vector& rects) noexcept = 0; [[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept = 0; diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index 6390f5fe7f6..5fd8a94c8a3 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -24,6 +24,7 @@ namespace Microsoft::Console::Render class RenderEngineBase : public IRenderEngine { public: + [[nodiscard]] HRESULT InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept override; [[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override; [[nodiscard]] HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept override; @@ -34,7 +35,7 @@ namespace Microsoft::Console::Render const til::size cellSize, const size_t centeringHint) noexcept override; - [[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override; + [[nodiscard]] HRESULT PrepareRenderInfo(RenderFrameInfo info) noexcept override; [[nodiscard]] HRESULT ResetLineTransform() noexcept override; [[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition, diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index a245491dead..d1e72fbd0eb 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -386,11 +386,6 @@ void UiaEngine::WaitUntilCanRender() noexcept return S_FALSE; } -[[nodiscard]] HRESULT UiaEngine::PaintSelections(const std::vector& /*rect*/) noexcept -{ - return S_FALSE; -} - // Routine Description: // - Draws the cursor on the screen // For UIA, this doesn't mean anything. So do nothing. diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 5d244dcc130..3fd069f425a 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -51,7 +51,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PaintBufferLine(const std::span clusters, const til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; - [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, const gsl::not_null pData, const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index 6dc611252c4..af08cdd5b58 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -245,11 +245,6 @@ using namespace Microsoft::Console::Types; return S_OK; } -[[nodiscard]] HRESULT VtEngine::PaintSelections(const std::vector& /*rect*/) noexcept -{ - return S_OK; -} - // Routine Description: // - Write a VT sequence to change the current colors of text. Writes true RGB // color sequences. diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 3bea4528ac5..0135e791169 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -64,7 +64,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; - [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override; diff --git a/src/renderer/wddmcon/WddmConRenderer.cpp b/src/renderer/wddmcon/WddmConRenderer.cpp index db729467cae..fda8ae6c21b 100644 --- a/src/renderer/wddmcon/WddmConRenderer.cpp +++ b/src/renderer/wddmcon/WddmConRenderer.cpp @@ -298,11 +298,6 @@ CATCH_RETURN() return S_OK; } -[[nodiscard]] HRESULT WddmConEngine::PaintSelections(const std::vector& /*rects*/) noexcept -{ - return S_OK; -} - [[nodiscard]] HRESULT WddmConEngine::PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; diff --git a/src/renderer/wddmcon/WddmConRenderer.hpp b/src/renderer/wddmcon/WddmConRenderer.hpp index f37146daf5b..1110c870782 100644 --- a/src/renderer/wddmcon/WddmConRenderer.hpp +++ b/src/renderer/wddmcon/WddmConRenderer.hpp @@ -46,7 +46,6 @@ namespace Microsoft::Console::Render const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; - [[nodiscard]] HRESULT PaintSelections(const std::vector& rects) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 4381c4ecbdd..b42ffe283f9 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -80,6 +80,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0; virtual void NotifyBufferRotation(const int delta) = 0; + virtual void NotifyTextLayoutUpdated() = 0; virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; }; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 6f0681ad58f..1a887f99f83 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -178,9 +178,11 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) _ApplyCursorMovementFlags(cursor); - // Notify UIA of new text. + // Notify terminal and UIA of new text. // It's important to do this here instead of in TextBuffer, because here you - // have access to the entire line of text. + // have access to the entire line of text, whereas TextBuffer writes it one + // character at a time via the OutputCellIterator. + _api.NotifyTextLayoutUpdated(); textBuffer.TriggerNewTextNotification(string); } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 5cd5fbdc29e..b17bc4429f9 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -215,6 +215,11 @@ class TestGetSet final : public ITerminalApi Log::Comment(L"NotifyBufferRotation MOCK called..."); } + void NotifyTextLayoutUpdated() override + { + Log::Comment(L"NotifyTextLayoutUpdated MOCK called..."); + } + void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override { Log::Comment(L"InvokeCompletions MOCK called..."); From 643f7167a66428c777f1e59b03a38279a5c2b688 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 17 Apr 2024 10:52:29 -0700 Subject: [PATCH 224/603] Also remember to persist window positions (#17066) This got lost in #16598. `TerminalPage` needs to ask the window where the it actually is, so it can persist it. More details in https://github.com/microsoft/terminal/pull/16598#discussion_r1511519304 Closes #17010 --- src/cascadia/TerminalApp/TerminalPage.cpp | 7 +++++++ src/cascadia/TerminalApp/TerminalPage.h | 10 ++++++++++ src/cascadia/TerminalApp/TerminalPage.idl | 7 +++++++ src/cascadia/TerminalApp/TerminalWindow.h | 2 ++ src/cascadia/TerminalApp/TerminalWindow.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 9 +++++++++ src/cascadia/WindowsTerminal/AppHost.h | 4 ++++ 7 files changed, 40 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index ff4b6743e69..1749acce26f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -8,6 +8,7 @@ #include "RenameWindowRequestedArgs.g.cpp" #include "RequestMoveContentArgs.g.cpp" #include "RequestReceiveContentArgs.g.cpp" +#include "LaunchPositionRequest.g.cpp" #include @@ -1952,6 +1953,12 @@ namespace winrt::TerminalApp::implementation layout.InitialSize(windowSize); + // We don't actually know our own position. So we have to ask the window + // layer for that. + const auto launchPosRequest{ winrt::make() }; + RequestLaunchPosition.raise(*this, launchPosRequest); + layout.InitialPosition(launchPosRequest.Position()); + ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 711d9f7b7ec..0dba78abd4b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -10,6 +10,7 @@ #include "RenameWindowRequestedArgs.g.h" #include "RequestMoveContentArgs.g.h" #include "RequestReceiveContentArgs.g.h" +#include "LaunchPositionRequest.g.h" #include "Toast.h" #define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); @@ -79,6 +80,13 @@ namespace winrt::TerminalApp::implementation _TabIndex{ tabIndex } {}; }; + struct LaunchPositionRequest : LaunchPositionRequestT + { + LaunchPositionRequest() = default; + + til::property Position; + }; + struct TerminalPage : TerminalPageT { public: @@ -186,6 +194,8 @@ namespace winrt::TerminalApp::implementation til::typed_event RequestMoveContent; til::typed_event RequestReceiveContent; + til::typed_event RequestLaunchPosition; + 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); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 8ec4fa1dc9c..667350056b8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -51,6 +51,11 @@ namespace TerminalApp Boolean IsQuakeWindow(); }; + runtimeclass LaunchPositionRequest + { + Microsoft.Terminal.Settings.Model.LaunchPosition Position; + } + [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, Microsoft.Terminal.UI.IDirectKeyListener { TerminalPage(WindowProperties properties, ContentManager manager); @@ -98,5 +103,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RequestMoveContent; event Windows.Foundation.TypedEventHandler RequestReceiveContent; + + event Windows.Foundation.TypedEventHandler RequestLaunchPosition; } } diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index d48d8d4f242..b10c29a8d72 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -231,6 +231,8 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent); FORWARDED_TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs, _root, RequestReceiveContent); + FORWARDED_TYPED_EVENT(RequestLaunchPosition, Windows::Foundation::IInspectable, winrt::TerminalApp::LaunchPositionRequest, _root, RequestLaunchPosition); + #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; #endif diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 1700686e1db..75c47fce8bf 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -137,6 +137,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RequestMoveContent; event Windows.Foundation.TypedEventHandler RequestReceiveContent; + event Windows.Foundation.TypedEventHandler RequestLaunchPosition; void AttachContent(String content, UInt32 tabIndex); void SendContentToOther(RequestReceiveContentArgs args); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0fe39c1cc56..5470db596f5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -341,6 +341,7 @@ void AppHost::Initialize() _revokers.RaiseVisualBell = _windowLogic.RaiseVisualBell(winrt::auto_revoke, { this, &AppHost::_RaiseVisualBell }); _revokers.SystemMenuChangeRequested = _windowLogic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested }); _revokers.ChangeMaximizeRequested = _windowLogic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested }); + _revokers.RequestLaunchPosition = _windowLogic.RequestLaunchPosition(winrt::auto_revoke, { this, &AppHost::_HandleRequestLaunchPosition }); _windowCallbacks.MaximizeChanged = _window->MaximizeChanged([this](bool newMaximize) { if (_windowLogic) @@ -510,6 +511,14 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* _windowManager.UpdateActiveTabTitle(newTitle, _peasant); } +// The terminal page is responsible for persisting it's own state, but it does +// need to ask us where exactly on the screen the window is. +void AppHost::_HandleRequestLaunchPosition(const winrt::Windows::Foundation::IInspectable& /*sender*/, + winrt::TerminalApp::LaunchPositionRequest args) +{ + args.Position(_GetWindowLaunchPosition()); +} + LaunchPosition AppHost::_GetWindowLaunchPosition() { LaunchPosition pos{}; diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index a22fc85a948..cc90c21c1c1 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -160,6 +160,9 @@ class AppHost : public std::enable_shared_from_this void _stopFrameTimer(); void _updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); + void _HandleRequestLaunchPosition(const winrt::Windows::Foundation::IInspectable& sender, + winrt::TerminalApp::LaunchPositionRequest 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 // cause us to dtor the old one, which will immediately call revoke on all @@ -195,6 +198,7 @@ class AppHost : public std::enable_shared_from_this 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::PropertyChanged_revoker PropertyChanged; winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged; From 06ab6f3e1f2143a1b2681dd954df2a67041373eb Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Wed, 17 Apr 2024 17:30:25 -0700 Subject: [PATCH 225/603] Add IDs to Commands (#16904) As laid out in #16816, adds an `ID` field to `Command`. **This first PR only adds IDs for built-in commands in defaults, and generates IDs for user-created commands that don't define an ID.** Also note that for now we **will not** be allowing IDs for iterable/nested commands. The follow-up PR is where we will actually use the IDs by referring to commands with them. Refs #16816 ## Validation Steps Performed User-created commands in the settings file get rewritten with generated IDs --- .../TerminalSettingsModel/ActionAndArgs.cpp | 30 +++ .../TerminalSettingsModel/ActionAndArgs.h | 1 + .../TerminalSettingsModel/ActionMap.cpp | 19 ++ .../TerminalSettingsModel/ActionMap.h | 1 + .../CascadiaSettingsSerialization.cpp | 4 + .../TerminalSettingsModel/Command.cpp | 32 ++- src/cascadia/TerminalSettingsModel/Command.h | 4 + .../TerminalSettingsModel/Command.idl | 1 + .../TerminalSettingsModel/defaults.json | 250 +++++++++--------- .../SerializationTests.cpp | 137 +++++++++- 10 files changed, 352 insertions(+), 127 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 66126cda15f..7bba13bc4cc 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -450,6 +450,36 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return found != GeneratedActionNames.end() ? found->second : L""; } + // Function Description: + // - This will generate an ID for this ActionAndArgs, based on the ShortcutAction and the Args + // - It will always create the same ID if the ShortcutAction and the Args are the same + // - Note: this should only be called for User-created actions + // - Example: The "SendInput 'abc'" action will have the generated ID "User.sendInput." + // Return Value: + // - The ID, based on the ShortcutAction and the Args + winrt::hstring ActionAndArgs::GenerateID() const + { + if (_Action != ShortcutAction::Invalid) + { + auto actionKeyString = ActionToStringMap.find(_Action)->second; + auto result = fmt::format(FMT_COMPILE(L"User.{}"), actionKeyString); + if (_Args) + { + // If there are args, we need to append the hash of the args + // However, to make it a little more presentable we + // 1. truncate the hash to 32 bits + // 2. convert it to a hex string + // there is a _tiny_ chance of collision because of the truncate but unlikely for + // the number of commands a user is expected to have + const auto argsHash32 = static_cast(_Args.Hash() & 0xFFFFFFFF); + // {0:X} formats the truncated hash to an uppercase hex string + fmt::format_to(std::back_inserter(result), FMT_COMPILE(L".{:X}"), argsHash32); + } + return winrt::hstring{ result }; + } + return L""; + } + winrt::hstring ActionAndArgs::Serialize(const winrt::Windows::Foundation::Collections::IVector& args) { Json::Value json{ Json::objectValue }; diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h index a2afefaa493..9c841ca727c 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h @@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation com_ptr Copy() const; hstring GenerateName() const; + hstring GenerateID() const; WINRT_PROPERTY(ShortcutAction, Action, ShortcutAction::Invalid); WINRT_PROPERTY(IActionArgs, Args, nullptr); diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index 76565dff444..220cd825565 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -795,6 +795,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return nullptr; } + bool ActionMap::GenerateIDsForActions() + { + bool fixedUp{ false }; + for (auto actionPair : _ActionMap) + { + auto cmdImpl{ winrt::get_self(actionPair.second) }; + + // Note: this function should ONLY be called for the action map in the user's settings file + // this debug assert should verify that for debug builds + assert(cmdImpl->Origin() == OriginTag::User); + + if (cmdImpl->ID().empty()) + { + fixedUp = cmdImpl->GenerateID() || fixedUp; + } + } + return fixedUp; + } + // Method Description: // - Rebinds a key binding to a new key chord // Arguments: diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index de9b9ca2361..880b9989ff6 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -71,6 +71,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Json::Value ToJson() const; // modification + bool GenerateIDsForActions(); bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys); void DeleteKeyBinding(const Control::KeyChord& keys); void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action); diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 93af43319bc..37112fe76b6 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -504,6 +504,10 @@ bool SettingsLoader::FixupUserSettings() fixedUp = true; } + // we need to generate an ID for a command in the user settings if it doesn't already have one + auto actionMap{ winrt::get_self(userSettings.globals->ActionMap()) }; + fixedUp = actionMap->GenerateIDsForActions() || fixedUp; + return fixedUp; } diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 91082a4f6d7..c20be960a67 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -21,6 +21,7 @@ namespace winrt } static constexpr std::string_view NameKey{ "name" }; +static constexpr std::string_view IDKey{ "id" }; static constexpr std::string_view IconKey{ "icon" }; static constexpr std::string_view ActionKey{ "command" }; static constexpr std::string_view IterateOnKey{ "iterateOn" }; @@ -39,7 +40,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { auto command{ winrt::make_self() }; command->_name = _name; - command->_Origin = OriginTag::User; + command->_Origin = _Origin; + command->_ID = _ID; command->_ActionAndArgs = *get_self(_ActionAndArgs)->Copy(); command->_keyMappings = _keyMappings; command->_iconPath = _iconPath; @@ -114,6 +116,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } } + hstring Command::ID() const noexcept + { + return hstring{ _ID }; + } + + bool Command::GenerateID() + { + if (_ActionAndArgs) + { + auto actionAndArgsImpl{ winrt::get_self(_ActionAndArgs) }; + if (const auto generatedID = actionAndArgsImpl->GenerateID(); !generatedID.empty()) + { + _ID = generatedID; + return true; + } + } + return false; + } + void Command::Name(const hstring& value) { if (!_name.has_value() || _name.value() != value) @@ -264,6 +285,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { auto result = winrt::make_self(); result->_Origin = origin; + JsonUtils::GetValueForKey(json, IDKey, result->_ID); auto nested = false; JsonUtils::GetValueForKey(json, IterateOnKey, result->_IterateOn); @@ -423,6 +445,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Json::Value cmdJson{ Json::ValueType::objectValue }; JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath); JsonUtils::SetValueForKey(cmdJson, NameKey, _name); + if (!_ID.empty()) + { + JsonUtils::SetValueForKey(cmdJson, IDKey, _ID); + } if (_ActionAndArgs) { @@ -443,6 +469,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // First iteration also writes icon and name JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath); JsonUtils::SetValueForKey(cmdJson, NameKey, _name); + if (!_ID.empty()) + { + JsonUtils::SetValueForKey(cmdJson, IDKey, _ID); + } } if (_ActionAndArgs) diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 5e94ae721c8..f22e35348c7 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -61,6 +61,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation hstring Name() const noexcept; void Name(const hstring& name); + hstring ID() const noexcept; + bool GenerateID(); + Control::KeyChord Keys() const noexcept; hstring KeyChordText() const noexcept; std::vector KeyMappings() const noexcept; @@ -84,6 +87,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Windows::Foundation::Collections::IMap _subcommands{ nullptr }; std::vector _keyMappings; std::optional _name; + std::wstring _ID; std::optional _iconPath; bool _nestedCommand{ false }; diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index c9cec5012a4..aa23458f55d 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -36,6 +36,7 @@ namespace Microsoft.Terminal.Settings.Model Command(); String Name { get; }; + String ID { get; }; ActionAndArgs ActionAndArgs { get; }; Microsoft.Terminal.Control.KeyChord Keys { get; }; void RegisterKey(Microsoft.Terminal.Control.KeyChord keys); diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 7c33e169994..2c79c63b7c8 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -423,145 +423,145 @@ "actions": [ // Application-level Keys - { "command": "closeWindow", "keys": "alt+f4" }, - { "command": "toggleFullscreen", "keys": "alt+enter" }, - { "command": "toggleFullscreen", "keys": "f11" }, - { "command": "toggleFocusMode" }, - { "command": "toggleAlwaysOnTop" }, - { "command": "openNewTabDropdown", "keys": "ctrl+shift+space" }, - { "command": { "action": "openSettings", "target": "settingsUI" }, "keys": "ctrl+," }, - { "command": { "action": "openSettings", "target": "settingsFile" }, "keys": "ctrl+shift+," }, - { "command": { "action": "openSettings", "target": "defaultsFile" }, "keys": "ctrl+alt+," }, - { "command": "find", "keys": "ctrl+shift+f" }, - { "command": { "action": "findMatch", "direction": "next" } }, - { "command": { "action": "findMatch", "direction": "prev" } }, - { "command": "toggleShaderEffects" }, - { "command": "openTabColorPicker" }, - { "command": "renameTab" }, - { "command": "openTabRenamer" }, - { "command": "commandPalette", "keys":"ctrl+shift+p" }, - { "command": "identifyWindow" }, - { "command": "openWindowRenamer" }, - { "command": "quakeMode", "keys":"win+sc(41)" }, - { "command": "openSystemMenu", "keys": "alt+space" }, - { "command": "quit" }, - { "command": "restoreLastClosed" }, - { "command": "openAbout" }, + { "command": "closeWindow", "keys": "alt+f4", "id": "Terminal.CloseWindow" }, + { "command": "toggleFullscreen", "keys": "alt+enter", "id": "Terminal.ToggleFullscreen" }, + { "command": "toggleFullscreen", "keys": "f11", "id": "Terminal.ToggleFullscreen" }, + { "command": "toggleFocusMode", "id": "Terminal.ToggleFocusMode" }, + { "command": "toggleAlwaysOnTop", "id": "Terminal.ToggleAlwaysOnTop" }, + { "command": "openNewTabDropdown", "keys": "ctrl+shift+space", "id": "Terminal.OpenNewTabDropdown" }, + { "command": { "action": "openSettings", "target": "settingsUI" }, "keys": "ctrl+,", "id": "Terminal.OpenSettingsUI" }, + { "command": { "action": "openSettings", "target": "settingsFile" }, "keys": "ctrl+shift+,", "id": "Terminal.OpenSettingsFile" }, + { "command": { "action": "openSettings", "target": "defaultsFile" }, "keys": "ctrl+alt+,", "id": "Terminal.OpenDefaultSettingsFile" }, + { "command": "find", "keys": "ctrl+shift+f", "id": "Terminal.FindText" }, + { "command": { "action": "findMatch", "direction": "next" }, "id": "Terminal.FindNextMatch" }, + { "command": { "action": "findMatch", "direction": "prev" }, "id": "Terminal.FindPrevMatch" }, + { "command": "toggleShaderEffects", "id": "Terminal.ToggleShaderEffects" }, + { "command": "openTabColorPicker", "id": "Terminal.OpenTabColorPicker" }, + { "command": "renameTab", "id": "Terminal.RenameTab" }, + { "command": "openTabRenamer", "id": "Terminal.OpenTabRenamer" }, + { "command": "commandPalette", "keys":"ctrl+shift+p", "id": "Terminal.ToggleCommandPalette" }, + { "command": "identifyWindow", "id": "Terminal.IdentifyWindow" }, + { "command": "openWindowRenamer", "id": "Terminal.OpenWindowRenamer" }, + { "command": "quakeMode", "keys":"win+sc(41)", "id": "Terminal.QuakeMode" }, + { "command": "openSystemMenu", "keys": "alt+space", "id": "Terminal.OpenSystemMenu" }, + { "command": "quit", "id": "Terminal.Quit" }, + { "command": "restoreLastClosed", "id": "Terminal.RestoreLastClosed" }, + { "command": "openAbout", "id": "Terminal.OpenAboutDialog" }, // Tab Management // "command": "closeTab" is unbound by default. // The closeTab command closes a tab without confirmation, even if it has multiple panes. - { "command": "closeOtherTabs" }, - { "command": "closeTabsAfter" }, - { "command": { "action" : "moveTab", "direction": "forward" }}, - { "command": { "action" : "moveTab", "direction": "backward" }}, - { "command": "newTab", "keys": "ctrl+shift+t" }, - { "command": "newWindow", "keys": "ctrl+shift+n" }, - { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+shift+1" }, - { "command": { "action": "newTab", "index": 1 }, "keys": "ctrl+shift+2" }, - { "command": { "action": "newTab", "index": 2 }, "keys": "ctrl+shift+3" }, - { "command": { "action": "newTab", "index": 3 }, "keys": "ctrl+shift+4" }, - { "command": { "action": "newTab", "index": 4 }, "keys": "ctrl+shift+5" }, - { "command": { "action": "newTab", "index": 5 }, "keys": "ctrl+shift+6" }, - { "command": { "action": "newTab", "index": 6 }, "keys": "ctrl+shift+7" }, - { "command": { "action": "newTab", "index": 7 }, "keys": "ctrl+shift+8" }, - { "command": { "action": "newTab", "index": 8 }, "keys": "ctrl+shift+9" }, - { "command": "duplicateTab", "keys": "ctrl+shift+d" }, - { "command": "nextTab", "keys": "ctrl+tab" }, - { "command": "prevTab", "keys": "ctrl+shift+tab" }, - { "command": { "action": "switchToTab", "index": 0 }, "keys": "ctrl+alt+1" }, - { "command": { "action": "switchToTab", "index": 1 }, "keys": "ctrl+alt+2" }, - { "command": { "action": "switchToTab", "index": 2 }, "keys": "ctrl+alt+3" }, - { "command": { "action": "switchToTab", "index": 3 }, "keys": "ctrl+alt+4" }, - { "command": { "action": "switchToTab", "index": 4 }, "keys": "ctrl+alt+5" }, - { "command": { "action": "switchToTab", "index": 5 }, "keys": "ctrl+alt+6" }, - { "command": { "action": "switchToTab", "index": 6 }, "keys": "ctrl+alt+7" }, - { "command": { "action": "switchToTab", "index": 7 }, "keys": "ctrl+alt+8" }, - { "command": { "action": "switchToTab", "index": 4294967295 }, "keys": "ctrl+alt+9" }, - { "command": { "action": "moveTab", "window": "new" }, }, + { "command": "closeOtherTabs", "id": "Terminal.CloseOtherTabs" }, + { "command": "closeTabsAfter", "id": "Terminal.CloseTabsAfter" }, + { "command": { "action" : "moveTab", "direction": "forward" }, "id": "Terminal.MoveTabForward" }, + { "command": { "action" : "moveTab", "direction": "backward" }, "id": "Terminal.MoveTabBackward" }, + { "command": "newTab", "keys": "ctrl+shift+t", "id": "Terminal.OpenNewTab" }, + { "command": "newWindow", "keys": "ctrl+shift+n", "id": "Terminal.OpenNewWindow" }, + { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+shift+1", "id": "Terminal.OpenNewTabProfile0" }, + { "command": { "action": "newTab", "index": 1 }, "keys": "ctrl+shift+2", "id": "Terminal.OpenNewTabProfile1" }, + { "command": { "action": "newTab", "index": 2 }, "keys": "ctrl+shift+3", "id": "Terminal.OpenNewTabProfile2" }, + { "command": { "action": "newTab", "index": 3 }, "keys": "ctrl+shift+4", "id": "Terminal.OpenNewTabProfile3" }, + { "command": { "action": "newTab", "index": 4 }, "keys": "ctrl+shift+5", "id": "Terminal.OpenNewTabProfile4" }, + { "command": { "action": "newTab", "index": 5 }, "keys": "ctrl+shift+6", "id": "Terminal.OpenNewTabProfile5" }, + { "command": { "action": "newTab", "index": 6 }, "keys": "ctrl+shift+7", "id": "Terminal.OpenNewTabProfile6" }, + { "command": { "action": "newTab", "index": 7 }, "keys": "ctrl+shift+8", "id": "Terminal.OpenNewTabProfile7" }, + { "command": { "action": "newTab", "index": 8 }, "keys": "ctrl+shift+9", "id": "Terminal.OpenNewTabProfile8" }, + { "command": "duplicateTab", "keys": "ctrl+shift+d", "id": "Terminal.DuplicateTab" }, + { "command": "nextTab", "keys": "ctrl+tab", "id": "Terminal.NextTab" }, + { "command": "prevTab", "keys": "ctrl+shift+tab", "id": "Terminal.PrevTab" }, + { "command": { "action": "switchToTab", "index": 0 }, "keys": "ctrl+alt+1", "id": "Terminal.SwitchToTab0" }, + { "command": { "action": "switchToTab", "index": 1 }, "keys": "ctrl+alt+2", "id": "Terminal.SwitchToTab1" }, + { "command": { "action": "switchToTab", "index": 2 }, "keys": "ctrl+alt+3", "id": "Terminal.SwitchToTab2" }, + { "command": { "action": "switchToTab", "index": 3 }, "keys": "ctrl+alt+4", "id": "Terminal.SwitchToTab3" }, + { "command": { "action": "switchToTab", "index": 4 }, "keys": "ctrl+alt+5", "id": "Terminal.SwitchToTab4" }, + { "command": { "action": "switchToTab", "index": 5 }, "keys": "ctrl+alt+6", "id": "Terminal.SwitchToTab5" }, + { "command": { "action": "switchToTab", "index": 6 }, "keys": "ctrl+alt+7", "id": "Terminal.SwitchToTab6" }, + { "command": { "action": "switchToTab", "index": 7 }, "keys": "ctrl+alt+8", "id": "Terminal.SwitchToTab7" }, + { "command": { "action": "switchToTab", "index": 4294967295 }, "keys": "ctrl+alt+9", "id": "Terminal.SwitchToLastTab" }, + { "command": { "action": "moveTab", "window": "new" }, "id": "Terminal.MoveTabToNewWindow" }, // Pane Management - { "command": "closeOtherPanes" }, - { "command": "closePane", "keys": "ctrl+shift+w" }, - { "command": { "action": "splitPane", "split": "up" } }, - { "command": { "action": "splitPane", "split": "down" } }, - { "command": { "action": "splitPane", "split": "left" } }, - { "command": { "action": "splitPane", "split": "right" } }, - { "command": { "action": "splitPane", "splitMode": "duplicate", "split": "down" }, "keys": "alt+shift+-" }, - { "command": { "action": "splitPane", "splitMode": "duplicate", "split": "right" }, "keys": "alt+shift+plus" }, - { "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" }, - { "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" }, - { "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" }, - { "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up" }, - { "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down" }, - { "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left" }, - { "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right" }, - { "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up" }, - { "command": { "action": "moveFocus", "direction": "previous" }, "keys": "ctrl+alt+left"}, - { "command": { "action": "moveFocus", "direction": "previousInOrder" } }, - { "command": { "action": "moveFocus", "direction": "nextInOrder" } }, - { "command": { "action": "moveFocus", "direction": "first" } }, - { "command": { "action": "moveFocus", "direction": "parent" } }, - { "command": { "action": "moveFocus", "direction": "child" } }, - { "command": { "action": "swapPane", "direction": "down" } }, - { "command": { "action": "swapPane", "direction": "left" } }, - { "command": { "action": "swapPane", "direction": "right" } }, - { "command": { "action": "swapPane", "direction": "up" } }, - { "command": { "action": "swapPane", "direction": "previous"} }, - { "command": { "action": "swapPane", "direction": "previousInOrder"} }, - { "command": { "action": "swapPane", "direction": "nextInOrder"} }, - { "command": { "action": "swapPane", "direction": "first" } }, - { "command": "toggleBroadcastInput" }, - { "command": "togglePaneZoom" }, - { "command": "toggleSplitOrientation" }, - { "command": "toggleReadOnlyMode" }, - { "command": "enableReadOnlyMode" }, - { "command": "disableReadOnlyMode" }, - { "command": { "action": "movePane", "index": 0 } }, - { "command": { "action": "movePane", "index": 1 } }, - { "command": { "action": "movePane", "index": 2 } }, - { "command": { "action": "movePane", "index": 3 } }, - { "command": { "action": "movePane", "index": 4 } }, - { "command": { "action": "movePane", "index": 5 } }, - { "command": { "action": "movePane", "index": 6 } }, - { "command": { "action": "movePane", "index": 7 } }, - { "command": { "action": "movePane", "index": 8 } }, - { "command": { "action": "movePane", "window": "new" }, }, - { "command": "restartConnection" }, + { "command": "closeOtherPanes", "id": "Terminal.CloseOtherPanes" }, + { "command": "closePane", "keys": "ctrl+shift+w", "id": "Terminal.ClosePane" }, + { "command": { "action": "splitPane", "split": "up" }, "id": "Terminal.SplitPaneUp" }, + { "command": { "action": "splitPane", "split": "down" }, "id": "Terminal.SplitPaneDown" }, + { "command": { "action": "splitPane", "split": "left" }, "id": "Terminal.SplitPaneLeft" }, + { "command": { "action": "splitPane", "split": "right" }, "id": "Terminal.SplitPaneRight" }, + { "command": { "action": "splitPane", "splitMode": "duplicate", "split": "down" }, "keys": "alt+shift+-", "id": "Terminal.SplitPaneDuplicateDown" }, + { "command": { "action": "splitPane", "splitMode": "duplicate", "split": "right" }, "keys": "alt+shift+plus", "id": "Terminal.SplitPaneDuplicateRight" }, + { "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down", "id": "Terminal.ResizePaneDown" }, + { "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left", "id": "Terminal.ResizePaneLeft" }, + { "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right", "id": "Terminal.ResizePaneRight" }, + { "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up", "id": "Terminal.ResizePaneUp" }, + { "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down", "id": "Terminal.MoveFocusDown" }, + { "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left", "id": "Terminal.MoveFocusLeft" }, + { "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right", "id": "Terminal.MoveFocusRight" }, + { "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up", "id": "Terminal.MoveFocusUp" }, + { "command": { "action": "moveFocus", "direction": "previous" }, "keys": "ctrl+alt+left", "id": "Terminal.MoveFocusPrevious" }, + { "command": { "action": "moveFocus", "direction": "previousInOrder" }, "id": "Terminal.MoveFocusPreviousInOrder" }, + { "command": { "action": "moveFocus", "direction": "nextInOrder" }, "id": "Terminal.MoveFocusNextInOrder" }, + { "command": { "action": "moveFocus", "direction": "first" }, "id": "Terminal.MoveFocusFirst" }, + { "command": { "action": "moveFocus", "direction": "parent" }, "id": "Terminal.MoveFocusParent" }, + { "command": { "action": "moveFocus", "direction": "child" }, "id": "Terminal.MoveFocusChild" }, + { "command": { "action": "swapPane", "direction": "down" }, "id": "Terminal.SwapPaneDown" }, + { "command": { "action": "swapPane", "direction": "left" }, "id": "Terminal.SwapPaneLeft" }, + { "command": { "action": "swapPane", "direction": "right" }, "id": "Terminal.SwapPaneRight" }, + { "command": { "action": "swapPane", "direction": "up" }, "id": "Terminal.SwapPaneUp" }, + { "command": { "action": "swapPane", "direction": "previous"}, "id": "Terminal.SwapPanePrevious" }, + { "command": { "action": "swapPane", "direction": "previousInOrder"}, "id": "Terminal.SwapPanePreviousInOrder" }, + { "command": { "action": "swapPane", "direction": "nextInOrder"}, "id": "Terminal.SwapPaneNextInOrder" }, + { "command": { "action": "swapPane", "direction": "first" }, "id": "Terminal.SwapPaneFirst" }, + { "command": "toggleBroadcastInput", "id": "Terminal.ToggleBroadcastInput" }, + { "command": "togglePaneZoom", "id": "Terminal.TogglePaneZoom" }, + { "command": "toggleSplitOrientation", "id": "Terminal.ToggleSplitOrientation" }, + { "command": "toggleReadOnlyMode", "id": "Terminal.ToggleReadOnlyMode" }, + { "command": "enableReadOnlyMode", "id": "Terminal.EnableReadOnlyMode" }, + { "command": "disableReadOnlyMode", "id": "Terminal.DisableReadOnlyMode" }, + { "command": { "action": "movePane", "index": 0 }, "id": "Terminal.MovePaneToTab0" }, + { "command": { "action": "movePane", "index": 1 }, "id": "Terminal.MovePaneToTab1" }, + { "command": { "action": "movePane", "index": 2 }, "id": "Terminal.MovePaneToTab2" }, + { "command": { "action": "movePane", "index": 3 }, "id": "Terminal.MovePaneToTab3" }, + { "command": { "action": "movePane", "index": 4 }, "id": "Terminal.MovePaneToTab4" }, + { "command": { "action": "movePane", "index": 5 }, "id": "Terminal.MovePaneToTab5" }, + { "command": { "action": "movePane", "index": 6 }, "id": "Terminal.MovePaneToTab6" }, + { "command": { "action": "movePane", "index": 7 }, "id": "Terminal.MovePaneToTab7" }, + { "command": { "action": "movePane", "index": 8 }, "id": "Terminal.MovePaneToTab8" }, + { "command": { "action": "movePane", "window": "new" }, "id": "Terminal.MovePaneToNewWindow" }, + { "command": "restartConnection", "id": "Terminal.RestartConnection" }, // Clipboard Integration - { "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+shift+c" }, - { "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+insert" }, - { "command": { "action": "copy", "singleLine": false }, "keys": "enter" }, - { "command": "paste", "keys": "ctrl+shift+v" }, - { "command": "paste", "keys": "shift+insert" }, - { "command": "selectAll", "keys": "ctrl+shift+a" }, - { "command": "markMode", "keys": "ctrl+shift+m" }, - { "command": "toggleBlockSelection" }, - { "command": "switchSelectionEndpoint" }, - { "command": "expandSelectionToWord" }, - { "command": "showContextMenu", "keys": "menu" }, + { "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+shift+c", "id": "Terminal.CopySelectedText" }, + { "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+insert", "id": "Terminal.CopySelectedText" }, + { "command": { "action": "copy", "singleLine": false }, "keys": "enter", "id": "Terminal.CopySelectedText" }, + { "command": "paste", "keys": "ctrl+shift+v", "id": "Terminal.PasteFromClipboard" }, + { "command": "paste", "keys": "shift+insert", "id": "Terminal.PasteFromClipboard" }, + { "command": "selectAll", "keys": "ctrl+shift+a", "id": "Terminal.SelectAll" }, + { "command": "markMode", "keys": "ctrl+shift+m", "id": "Terminal.ToggleMarkMode" }, + { "command": "toggleBlockSelection", "id": "Terminal.ToggleBlockSelection" }, + { "command": "switchSelectionEndpoint", "id": "Terminal.SwitchSelectionEndpoint" }, + { "command": "expandSelectionToWord", "id": "Terminal.ExpandSelectionToWord" }, + { "command": "showContextMenu", "keys": "menu", "id": "Terminal.ShowContextMenu" }, // Web Search - { "command": { "action": "searchWeb" }, "name": { "key": "SearchWebCommandKey" } }, + { "command": { "action": "searchWeb" }, "name": { "key": "SearchWebCommandKey" }, "id": "Terminal.SearchWeb" }, // Scrollback - { "command": "scrollDown", "keys": "ctrl+shift+down" }, - { "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" }, - { "command": "scrollUp", "keys": "ctrl+shift+up" }, - { "command": "scrollUpPage", "keys": "ctrl+shift+pgup" }, - { "command": "scrollToTop", "keys": "ctrl+shift+home" }, - { "command": "scrollToBottom", "keys": "ctrl+shift+end" }, - { "command": { "action": "clearBuffer", "clear": "all" } }, - { "command": "exportBuffer" }, + { "command": "scrollDown", "keys": "ctrl+shift+down", "id": "Terminal.ScrollDown" }, + { "command": "scrollDownPage", "keys": "ctrl+shift+pgdn", "id": "Terminal.ScrollDownPage" }, + { "command": "scrollUp", "keys": "ctrl+shift+up", "id": "Terminal.ScrollUp" }, + { "command": "scrollUpPage", "keys": "ctrl+shift+pgup", "id": "Terminal.ScrollUpPage" }, + { "command": "scrollToTop", "keys": "ctrl+shift+home", "id": "Terminal.ScrollToTop" }, + { "command": "scrollToBottom", "keys": "ctrl+shift+end", "id": "Terminal.ScrollToBottom" }, + { "command": { "action": "clearBuffer", "clear": "all" }, "id": "Terminal.ClearBuffer" }, + { "command": "exportBuffer", "id": "Terminal.ExportBuffer" }, // Visual Adjustments - { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+plus" }, - { "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+minus" }, - { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+numpad_plus" }, - { "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+numpad_minus" }, - { "command": "resetFontSize", "keys": "ctrl+0" }, - { "command": "resetFontSize", "keys": "ctrl+numpad_0" }, + { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+plus", "id": "Terminal.IncreaseFontSize" }, + { "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+minus", "id": "Terminal.DecreaseFontSize" }, + { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+numpad_plus", "id": "Terminal.IncreaseFontSize" }, + { "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+numpad_minus", "id": "Terminal.DecreaseFontSize" }, + { "command": "resetFontSize", "keys": "ctrl+0", "id": "Terminal.ResetFontSize" }, + { "command": "resetFontSize", "keys": "ctrl+numpad_0", "id": "Terminal.ResetFontSize" }, // Other commands { diff --git a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp index 8cfd4a14ccf..8d9c55b6f89 100644 --- a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp @@ -16,6 +16,14 @@ using namespace WEX::Common; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; +// Different architectures will hash the same SendInput command to a different ID +// Check for the correct ID based on the architecture +#if defined(_M_IX86) +#define SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH "56911147" +#else +#define SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH "A020D2" +#endif + namespace SettingsModelUnitTests { class SerializationTests : public JsonTestClass @@ -36,6 +44,10 @@ namespace SettingsModelUnitTests TEST_METHOD(RoundtripUserModifiedColorSchemeCollisionUnusedByProfiles); TEST_METHOD(RoundtripUserDeletedColorSchemeCollision); + TEST_METHOD(RoundtripGenerateActionID); + TEST_METHOD(NoGeneratedIDsForIterableAndNestedCommands); + TEST_METHOD(GeneratedActionIDsEqualForIdenticalCommands); + private: // Method Description: // - deserializes and reserializes a json string representing a settings object model of type T @@ -491,7 +503,7 @@ namespace SettingsModelUnitTests } ], "actions": [ - { "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" } + { "command": { "action": "sendInput", "input": "VT Griese Mode" }, "id": "User.sendInput.E02B3DF9", "keys": "ctrl+k" } ], "theme": "system", "themes": [] @@ -946,4 +958,127 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult)); } + + void SerializationTests::RoundtripGenerateActionID() + { + static constexpr std::string_view oldSettingsJson{ R"( + { + "actions": [ + { + "name": "foo", + "command": { "action": "sendInput", "input": "just some input" }, + "keys": "ctrl+shift+w" + } + ] + })" }; + + // Key differences: - the sendInput action now has a generated ID + // - this generated ID was created at the time of writing this test, + // and should remain robust (i.e. every time we hash the args we should get the same result) + static constexpr std::string_view newSettingsJson{ R"( + { + "actions": [ + { + "name": "foo", + "command": { "action": "sendInput", "input": "just some input" }, + "keys": "ctrl+shift+w", + "id" : "User.sendInput.)" SEND_INPUT_ARCH_SPECIFIC_ACTION_HASH R"(" + } + ] + })" }; + + implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) }; + oldLoader.MergeInboxIntoUserSettings(); + oldLoader.FinalizeLayering(); + VERIFY_IS_TRUE(oldLoader.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk"); + const auto oldSettings = winrt::make_self(std::move(oldLoader)); + const auto oldResult{ oldSettings->ToJson() }; + + implementation::SettingsLoader newLoader{ newSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) }; + newLoader.MergeInboxIntoUserSettings(); + newLoader.FinalizeLayering(); + newLoader.FixupUserSettings(); + const auto newSettings = winrt::make_self(std::move(newLoader)); + const auto newResult{ newSettings->ToJson() }; + + VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult)); + } + + void SerializationTests::NoGeneratedIDsForIterableAndNestedCommands() + { + // for iterable commands, nested commands, and user-defined actions that already have + // an ID, we do not need to generate an ID + static constexpr std::string_view oldSettingsJson{ R"( + { + "actions": [ + { + "name": "foo", + "command": "closePane", + "keys": "ctrl+shift+w", + "id": "thisIsMyClosePane" + }, + { + "iterateOn": "profiles", + "icon": "${profile.icon}", + "name": "${profile.name}", + "command": { "action": "newTab", "profile": "${profile.name}" } + }, + { + "name": "Change font size...", + "commands": [ + { "command": { "action": "adjustFontSize", "delta": 1.0 } }, + { "command": { "action": "adjustFontSize", "delta": -1.0 } }, + { "command": "resetFontSize" }, + ] + } + ] + })" }; + + implementation::SettingsLoader oldLoader{ oldSettingsJson, implementation::LoadStringResource(IDR_DEFAULTS) }; + oldLoader.MergeInboxIntoUserSettings(); + oldLoader.FinalizeLayering(); + VERIFY_IS_FALSE(oldLoader.FixupUserSettings(), L"Validate that there is no need to write back to disk"); + } + + void SerializationTests::GeneratedActionIDsEqualForIdenticalCommands() + { + static constexpr std::string_view settingsJson1{ R"( + { + "actions": [ + { + "name": "foo", + "command": { "action": "sendInput", "input": "this is some other input string" }, + "keys": "ctrl+shift+w" + } + ] + })" }; + + // Both settings files define the same action, so the generated ID should be the same for both + static constexpr std::string_view settingsJson2{ R"( + { + "actions": [ + { + "name": "foo", + "command": { "action": "sendInput", "input": "this is some other input string" }, + "keys": "ctrl+shift+w" + } + ] + })" }; + + implementation::SettingsLoader loader1{ settingsJson1, implementation::LoadStringResource(IDR_DEFAULTS) }; + loader1.MergeInboxIntoUserSettings(); + loader1.FinalizeLayering(); + VERIFY_IS_TRUE(loader1.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk"); + const auto settings1 = winrt::make_self(std::move(loader1)); + const auto result1{ settings1->ToJson() }; + + implementation::SettingsLoader loader2{ settingsJson2, implementation::LoadStringResource(IDR_DEFAULTS) }; + loader2.MergeInboxIntoUserSettings(); + loader2.FinalizeLayering(); + VERIFY_IS_TRUE(loader2.FixupUserSettings(), L"Validate that this will indicate we need to write them back to disk"); + const auto settings2 = winrt::make_self(std::move(loader2)); + const auto result2{ settings2->ToJson() }; + + VERIFY_ARE_EQUAL(toString(result1), toString(result2)); + } } From 4e7b63c664bc09da3ba2f08287cf03d3729ce3a2 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 18 Apr 2024 19:47:28 +0200 Subject: [PATCH 226/603] A minor TSF refactoring (#17067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next in the popular series of minor refactorings: Out with the old, in with the new! This PR removes all of the existing TSF code, both for conhost and Windows Terminal. conhost's TSF implementation was awful: It allocated an entire text buffer _per line_ of input. Additionally, its implementation spanned a whopping 40 files and almost 5000 lines of code. Windows Terminal's implementation was absolutely fine in comparison, but it was user unfriendly due to two reasons: Its usage of the `CoreTextServices` WinRT API indirectly meant that it used a non-transitory TSF document, which is not the right choice for a terminal. A `TF_SS_TRANSITORY` document (-context) indicates to TSF that it cannot undo a previously completed composition which is exactly what we need: Once composition has completed we send the result to the shell and we cannot undo this later on. The WinRT API does not allow us to use `TF_SS_TRANSITORY` and so it's unsuitable for our application. Additionally, the implementation used XAML to render the composition instead of being part of our text renderer, which resulted in the text looking weird and hard to read. The new implementation spans just 8 files and is ~1000 lines which should make it significantly easier to maintain. The architecture is not particularly great, but it's certainly better than what we had. The implementation is almost entirely identical between both conhost and Windows Terminal and thus they both also behave identical. It fixes an uncountable number of subtle bugs in the conhost TSF implementation, as it failed to check for status codes after calls. It also adds several new features, like support for wavy underlines (as used by the Japanese IME), dashed underlines (the default for various languages now, like Vietnamese), colored underlines, colored foreground/background controlled by the IME, and more! I have tried to replicate the following issues and have a high confidence that they're resolved now: Closes #1304 Closes #3730 Closes #4052 Closes #5007 (as it is not applicable anymore) Closes #5110 Closes #6186 Closes #6192 Closes #13805 Closes #14349 Closes #14407 Closes #16180 For the following issues I'm not entirely sure if it'll fix it, but I suspect it's somewhat likely: #13681 #16305 #16817 Lastly, there's one remaining bug that I don't know how to resolve. However, that issue also plagues conhost and Windows Terminal right now, so it's at least not a regression: * Press Win+. (emoji picker) and close it * Move the window around * Press Win+. This will open the emoji picker at the old window location. It also occurs when the cursor moves within the window. While this is super annoying, I could not find a way to fix it. ## Validation Steps Performed * See the above closed issues * Use Vietnamese Telex and type "xin choaf" Results in "xin chào" ✅ * Use the MS Japanese IME and press Alt+` Toggles between the last 2 modes ✅ * Use the MS Japanese IME, type "kyouhaishaheiku", and press Space * The text is converted, underlined and the first part is doubly underlined ✅ * Left/Right moves between the 3 segments ✅ * Home/End moves between start/end ✅ * Esc puts a wavy line under the current segment ✅ * Use the Korean IME, type "gksgks" This results in "한한" ✅ * Use the Korean IME, type "gks", and press Right Ctrl Opens a popup which allows you to navigate with Arrow/Tab keys ✅ --- .github/actions/spelling/expect/alphabet.txt | 1 - .github/actions/spelling/expect/expect.txt | 64 +- OpenConsole.sln | 6 +- doc/ORGANIZATION.md | 3 - src/buffer/out/Row.cpp | 8 +- src/buffer/out/Row.hpp | 2 - src/buffer/out/textBuffer.hpp | 2 - src/cascadia/TerminalControl/ControlCore.cpp | 44 +- src/cascadia/TerminalControl/ControlCore.h | 10 +- src/cascadia/TerminalControl/ControlCore.idl | 2 - .../TerminalControl/TSFInputControl.cpp | 440 ------- .../TerminalControl/TSFInputControl.h | 92 -- .../TerminalControl/TSFInputControl.idl | 36 - .../TerminalControl/TSFInputControl.xaml | 17 - src/cascadia/TerminalControl/TermControl.cpp | 251 ++-- src/cascadia/TerminalControl/TermControl.h | 44 +- src/cascadia/TerminalControl/TermControl.xaml | 5 - .../TerminalControlLib.vcxproj | 27 +- src/cascadia/TerminalCore/Terminal.cpp | 29 - src/cascadia/TerminalCore/Terminal.hpp | 8 +- .../TerminalCore/terminalrenderdata.cpp | 7 +- .../TerminalBufferTests.cpp | 42 - src/host/_output.cpp | 2 - src/host/conareainfo.cpp | 221 ---- src/host/conareainfo.h | 74 -- src/host/conimeinfo.cpp | 507 ------- src/host/conimeinfo.h | 91 -- src/host/consoleInformation.cpp | 2 - src/host/conv.h | 29 - src/host/convarea.cpp | 163 --- src/host/getset.cpp | 5 - src/host/globals.h | 4 +- src/host/host-common.vcxitems | 6 - src/host/inputBuffer.cpp | 2 - src/host/inputBuffer.hpp | 1 - src/host/lib/hostlib.vcxproj.filters | 23 +- src/host/output.cpp | 2 - src/host/precomp.h | 3 - src/host/renderData.cpp | 45 +- src/host/renderData.hpp | 4 +- src/host/screenInfo.cpp | 12 - src/host/screenInfo.hpp | 3 - src/host/server.h | 3 - src/host/sources.inc | 3 - src/host/ut_host/VtIoTests.cpp | 7 +- src/inc/conime.h | 58 - src/inc/contsf.h | 40 - .../win32/CustomWindowMessages.h | 2 +- src/interactivity/win32/WindowIme.cpp | 68 - src/interactivity/win32/lib/win32.LIB.vcxproj | 2 - .../win32/lib/win32.LIB.vcxproj.filters | 6 - src/interactivity/win32/menu.cpp | 2 - src/interactivity/win32/sources.inc | 1 - src/interactivity/win32/window.cpp | 6 - src/interactivity/win32/windowime.hpp | 9 - src/interactivity/win32/windowio.cpp | 22 +- src/interactivity/win32/windowproc.cpp | 129 +- src/renderer/atlas/AtlasEngine.cpp | 1 + src/renderer/base/renderer.cpp | 210 +-- src/renderer/base/renderer.hpp | 13 +- src/renderer/inc/IRenderData.hpp | 32 +- src/tsf/ConsoleTSF.cpp | 549 -------- src/tsf/ConsoleTSF.h | 220 ---- src/tsf/Handle.cpp | 82 ++ src/tsf/Handle.h | 54 + src/tsf/Implementation.cpp | 663 ++++++++++ src/tsf/Implementation.h | 108 ++ src/tsf/TfCatUtil.cpp | 59 - src/tsf/TfCatUtil.h | 38 - src/tsf/TfConvArea.cpp | 103 -- src/tsf/TfConvArea.h | 44 - src/tsf/TfCtxtComp.h | 44 - src/tsf/TfDispAttr.cpp | 199 --- src/tsf/TfDispAttr.h | 51 - src/tsf/TfEditSession.cpp | 1167 ----------------- src/tsf/TfEditSession.h | 219 ---- src/tsf/TfTxtevCb.cpp | 169 --- src/tsf/contsf.cpp | 33 - src/tsf/globals.h | 57 - src/tsf/precomp.h | 9 +- src/tsf/sources | 11 +- src/tsf/tsf.vcxproj | 21 +- src/tsf/tsf.vcxproj.filters | 49 +- 83 files changed, 1408 insertions(+), 5494 deletions(-) delete mode 100644 src/cascadia/TerminalControl/TSFInputControl.cpp delete mode 100644 src/cascadia/TerminalControl/TSFInputControl.h delete mode 100644 src/cascadia/TerminalControl/TSFInputControl.idl delete mode 100644 src/cascadia/TerminalControl/TSFInputControl.xaml delete mode 100644 src/host/conareainfo.cpp delete mode 100644 src/host/conareainfo.h delete mode 100644 src/host/conimeinfo.cpp delete mode 100644 src/host/conimeinfo.h delete mode 100644 src/host/conv.h delete mode 100644 src/host/convarea.cpp delete mode 100644 src/inc/conime.h delete mode 100644 src/inc/contsf.h delete mode 100644 src/interactivity/win32/WindowIme.cpp delete mode 100644 src/interactivity/win32/windowime.hpp delete mode 100644 src/tsf/ConsoleTSF.cpp delete mode 100644 src/tsf/ConsoleTSF.h create mode 100644 src/tsf/Handle.cpp create mode 100644 src/tsf/Handle.h create mode 100644 src/tsf/Implementation.cpp create mode 100644 src/tsf/Implementation.h delete mode 100644 src/tsf/TfCatUtil.cpp delete mode 100644 src/tsf/TfCatUtil.h delete mode 100644 src/tsf/TfConvArea.cpp delete mode 100644 src/tsf/TfConvArea.h delete mode 100644 src/tsf/TfCtxtComp.h delete mode 100644 src/tsf/TfDispAttr.cpp delete mode 100644 src/tsf/TfDispAttr.h delete mode 100644 src/tsf/TfEditSession.cpp delete mode 100644 src/tsf/TfEditSession.h delete mode 100644 src/tsf/TfTxtevCb.cpp delete mode 100644 src/tsf/contsf.cpp delete mode 100644 src/tsf/globals.h diff --git a/.github/actions/spelling/expect/alphabet.txt b/.github/actions/spelling/expect/alphabet.txt index 97180b72115..6a2ee3bb519 100644 --- a/.github/actions/spelling/expect/alphabet.txt +++ b/.github/actions/spelling/expect/alphabet.txt @@ -1,4 +1,3 @@ -AAAa AAAAA AAAAAAAAAAAAA AAAAAABBBBBBCCC diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index e458086ce89..36e345cfbbd 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -10,6 +10,7 @@ ACCESSTOKEN acidev ACIOSS ACover +acp actctx ACTCTXW ADDALIAS @@ -67,10 +68,10 @@ ASBSET asdfghjkl ASetting ASingle +ASYNCDONTCARE ASYNCWINDOWPOS atch ATest -ATTRCOLOR aumid Authenticode AUTOBUDDY @@ -124,8 +125,6 @@ Blt BLUESCROLL BODGY BOLDFONT -BOOLIFY -bools Borland boutput boxheader @@ -156,7 +155,6 @@ cazamor CBash cbiex CBN -CBoolean cbt CCCBB cch @@ -164,12 +162,9 @@ CCHAR CCmd ccolor CCom -CComp CConsole -CConversion CCRT cdd -CEdit CELLSIZE cfae cfie @@ -179,18 +174,17 @@ CFuzz cgscrn chafa changelists -chaof charinfo CHARSETINFO chh chshdng CHT -Cic CLASSSTRING CLE cleartype CLICKACTIVE clickdown +CLIENTID clipbrd CLIPCHILDREN CLIPSIBLINGS @@ -229,7 +223,6 @@ commdlg COMMITID componentization conapi -conareainfo conattrs conbufferout concfg @@ -240,8 +233,7 @@ condrv conechokey conemu conhost -conime -conimeinfo +CONIME conintegrity conintegrityuwp coninteractivitybase @@ -279,7 +271,6 @@ contentfiles conterm contsf contypes -convarea conwinuserrefs coordnew COPYCOLOR @@ -314,7 +305,6 @@ csrutil CSTYLE CSwitch CTerminal -CText ctl ctlseqs CTRLEVENT @@ -322,7 +312,7 @@ CTRLFREQUENCY CTRLKEYSHORTCUTS Ctrls CTRLVOLUME -Ctxt +CUAS CUF cupxy CURRENTFONT @@ -486,7 +476,6 @@ DISABLEDELAYEDEXPANSION DISABLENOSCROLL DISPATCHNOTIFY DISPLAYATTRIBUTE -DISPLAYATTRIBUTEPROPERTY DISPLAYCHANGE distros dlg @@ -562,7 +551,6 @@ entrypoints ENU ENUMLOGFONT ENUMLOGFONTEX -enumranges EOK EPres EQU @@ -621,7 +609,6 @@ FINDDOWN FINDSTRINGEXACT FINDUP FIter -FIXEDCONVERTED FIXEDFILEINFO Flg flyouts @@ -736,9 +723,8 @@ Greyscale gridline gset gsl -GTP guc -guidatom +GUIDATOM GValue GWL GWLP @@ -837,7 +823,6 @@ IEnd IEnum IFACEMETHODIMP ification -IGNOREEND IGNORELANGUAGE IHosted iid @@ -861,7 +846,6 @@ inkscape INLINEPREFIX inproc Inputkeyinfo -INPUTPROCESSORPROFILE Inputreadhandledata INSERTMODE INTERACTIVITYBASE @@ -873,9 +857,7 @@ INVALIDARG INVALIDATERECT Ioctl ipch -ipp IProperty -IPSINK ipsp IShell ISwap @@ -895,7 +877,6 @@ JOBOBJECT JOBOBJECTINFOCLASS JPN jsoncpp -Jsons jsprovider jumplist KAttrs @@ -925,6 +906,7 @@ KLMNOPQRSTY KOK KPRIORITY KVM +kyouhaishaheiku langid LANGUAGELIST lasterror @@ -987,7 +969,6 @@ lpdw lpelfe lpfn LPFNADDPROPSHEETPAGE -lpl LPMEASUREITEMSTRUCT LPMINMAXINFO lpmsg @@ -1063,6 +1044,7 @@ MENUITEMINFO MENUSELECT messageext metaproj +Mgrs microsoftpublicsymbols midl mii @@ -1117,7 +1099,6 @@ muxes myapplet mybranch mydir -MYMAX Mypair Myval NAMELENGTH @@ -1161,6 +1142,7 @@ NOCOPYBITS NODUP noexcepts NOFONT +NOHIDDENTEXT NOINTEGRALHEIGHT NOINTERFACE NOLINKINFO @@ -1184,6 +1166,7 @@ NORMALDISPLAY NOSCRATCH NOSEARCH noselect +NOSELECTION NOSENDCHANGING NOSIZE NOSNAPSHOT @@ -1274,6 +1257,7 @@ packageuwp PACKAGEVERSIONNUMBER PACKCOORD PACKVERSION +pacp pagedown pageup PAINTPARAMS @@ -1285,7 +1269,6 @@ parms PATCOPY pathcch PATTERNID -pcat pcb pcch PCCHAR @@ -1309,9 +1292,9 @@ PCSTR PCWCH PCWCHAR PCWSTR -pda pdbs pdbstr +pdcs PDPs pdtobj pdw @@ -1341,6 +1324,7 @@ PIDLIST pids pii piml +pimpl pinvoke pipename pipestr @@ -1372,7 +1356,6 @@ POSXSCROLL POSYSCROLL PPEB ppf -ppguid ppidl PPROC ppropvar @@ -1402,7 +1385,7 @@ PROCESSINFOCLASS PRODEXT PROPERTYID PROPERTYKEY -PROPERTYVAL +propertyval propsheet PROPSHEETHEADER PROPSHEETPAGE @@ -1434,6 +1417,7 @@ ptch ptsz PTYIn PUCHAR +pvar pwch PWDDMCONSOLECONTEXT pws @@ -1519,6 +1503,7 @@ rgn rgp rgpwsz rgrc +rguid rgw RIGHTALIGN RIGHTBUTTON @@ -1526,6 +1511,7 @@ riid RIS roadmap robomac +rodata rosetta RRF rrr @@ -1648,7 +1634,6 @@ sidebyside SIF SIGDN Signtool -SINGLEFLAG SINGLETHREADED siup sixel @@ -1721,6 +1706,7 @@ SYMED SYNCPAINT syscalls SYSCHAR +SYSCOLOR SYSCOMMAND SYSDEADCHAR SYSKEYDOWN @@ -1786,7 +1772,6 @@ TEXTMETRIC TEXTMETRICW textmode texttests -TFCAT TFunction THUMBPOSITION THUMBTRACK @@ -1817,7 +1802,6 @@ Tpqrst tracelogging traceviewpp trackbar -TRACKCOMPOSITION trackpad transitioning Trd @@ -1841,8 +1825,6 @@ TTM TTo tvpp tvtseq -Txtev -typechecked TYUI UAC uap @@ -1860,10 +1842,8 @@ UIACCESS uiacore uiautomationcore uielem -UIELEMENTENABLEDONLY UINTs ul -ulcch uld uldash uldb @@ -1957,7 +1937,6 @@ vstest VSTS VSTT vswhere -vtapi vtapp VTE VTID @@ -2020,7 +1999,6 @@ WINDOWALPHA windowdpiapi WINDOWEDGE windowext -windowime WINDOWINFO windowio windowmetrics @@ -2071,7 +2049,6 @@ WNDCLASSW Wndproc WNegative WNull -wnwb workarea WOutside WOWARM @@ -2110,7 +2087,6 @@ WTs WTSOFTFONT wtw wtypes -Wubi WUX WVerify WWith @@ -2137,9 +2113,7 @@ xes XFG XFile XFORM -xin -xinchaof -xinxinchaof +XIn XManifest XMath xorg diff --git a/OpenConsole.sln b/OpenConsole.sln index 809dd25a346..411f5ca0ed6 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -171,6 +171,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control.Lib", "src\cascadia\TerminalControl\TerminalControlLib.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}" ProjectSection(ProjectDependencies) = postProject {1CF55140-EF6A-4736-A403-957E4F7430BB} = {1CF55140-EF6A-4736-A403-957E4F7430BB} + {2FD12FBB-1DDB-46D8-B818-1023C624CACA} = {2FD12FBB-1DDB-46D8-B818-1023C624CACA} {48D21369-3D7B-4431-9967-24E81292CF63} = {48D21369-3D7B-4431-9967-24E81292CF63} {8222900C-8B6C-452A-91AC-BE95DB04B95F} = {8222900C-8B6C-452A-91AC-BE95DB04B95F} {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} = {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} @@ -412,7 +413,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TerminalStress", "src\tools EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderingTests", "src\tools\RenderingTests\RenderingTests.vcxproj", "{37C995E0-2349-4154-8E77-4A52C0C7F46D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UIHelpers", "src\cascadia\UIHelpers\UIHelpers.vcxproj", "{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.UI", "src\cascadia\UIHelpers\UIHelpers.vcxproj", "{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "benchcat", "src\tools\benchcat\benchcat.vcxproj", "{2C836962-9543-4CE5-B834-D28E1F124B66}" EndProject @@ -477,6 +478,7 @@ Global {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.Build.0 = Debug|ARM64 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.ActiveCfg = Debug|x64 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.Build.0 = Debug|x64 + {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.Deploy.0 = Debug|x64 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x86.ActiveCfg = Debug|Win32 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x86.Build.0 = Debug|Win32 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 @@ -2443,7 +2445,7 @@ Global {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} {4C8E6BB0-4713-4ADB-BD04-81628ECEAF20} = {81C352DB-1818-45B7-A284-18E259F1CC87} {D57841D1-8294-4F2B-BB8B-D2A35738DECD} = {81C352DB-1818-45B7-A284-18E259F1CC87} - {2FD12FBB-1DDB-46D8-B818-1023C624CACA} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} + {2FD12FBB-1DDB-46D8-B818-1023C624CACA} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} {3AE13314-1939-4DFA-9C14-38CA0834050C} = {F1995847-4AE5-479A-BBAF-382E51A63532} {DCF55140-EF6A-4736-A403-957E4F7430BB} = {F1995847-4AE5-479A-BBAF-382E51A63532} {1CF55140-EF6A-4736-A403-957E4F7430BB} = {F1995847-4AE5-479A-BBAF-382E51A63532} diff --git a/doc/ORGANIZATION.md b/doc/ORGANIZATION.md index 5f93639d099..18edbdabe58 100644 --- a/doc/ORGANIZATION.md +++ b/doc/ORGANIZATION.md @@ -65,9 +65,6 @@ * `clipboard.cpp` * Handles the command prompt line as you see in CMD.exe (known as the processed input line… most other shells handle this themselves with raw input and don’t use ours. This is a legacy of bad architectural design, putting stuff in conhost not in CMD) * `cmdline.cpp` -* Handles shunting IME data back and forth to the TSF library and to and from the various buffers - * `Conimeinfo.cpp` - * `Convarea.cpp` * Contains the global state for the entire console application * `consoleInformation.cpp` * Stuff related to the low-level server communication over our protocol with the driver diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 4793b86dac9..aaf2dcab20b 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -914,7 +914,7 @@ const til::small_rle& ROW::Attributes() const noexce TextAttribute ROW::GetAttrByColumn(const til::CoordType column) const { - return _attr.at(_clampedUint16(column)); + return _attr.at(_clampedColumn(column)); } std::vector ROW::GetHyperlinks() const @@ -1097,12 +1097,6 @@ DelimiterClass ROW::DelimiterClassAt(til::CoordType column, const std::wstring_v } } -template -constexpr uint16_t ROW::_clampedUint16(T v) noexcept -{ - return static_cast(clamp(v, 0, 65535)); -} - template constexpr uint16_t ROW::_clampedColumn(T v) const noexcept { diff --git a/src/buffer/out/Row.hpp b/src/buffer/out/Row.hpp index 317c935edce..a13e6e7996a 100644 --- a/src/buffer/out/Row.hpp +++ b/src/buffer/out/Row.hpp @@ -230,8 +230,6 @@ class ROW final static constexpr uint16_t CharOffsetsTrailer = 0x8000; static constexpr uint16_t CharOffsetsMask = 0x7fff; - template - static constexpr uint16_t _clampedUint16(T v) noexcept; template constexpr uint16_t _clampedColumn(T v) const noexcept; template diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index c7b68926ef8..f250749c577 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -49,8 +49,6 @@ filling in the last row, and updating the screen. #pragma once -#include - #include "cursor.h" #include "Row.hpp" #include "TextAttribute.hpp" diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 16461400629..5531ca25cba 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -111,9 +111,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnScrollPositionChanged = std::bind(&ControlCore::_terminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); _terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged); - auto pfnTerminalCursorPositionChanged = std::bind(&ControlCore::_terminalCursorPositionChanged, this); - _terminal->SetCursorPositionChangedCallback(pfnTerminalCursorPositionChanged); - auto pfnTerminalTaskbarProgressChanged = std::bind(&ControlCore::_terminalTaskbarProgressChanged, this); _terminal->TaskbarProgressChangedCallback(pfnTerminalTaskbarProgressChanged); @@ -171,10 +168,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // A few different events should be throttled, so they don't fire absolutely all the time: - // * _tsfTryRedrawCanvas: When the cursor position moves, we need to - // inform TSF, so it can move the canvas for the composition. We - // throttle this so that we're not hopping across the process boundary - // every time that the cursor moves. // * _updatePatternLocations: When there's new output, or we scroll the // viewport, we should re-check if there are any visible hyperlinks. // But we don't really need to do this every single time text is @@ -184,16 +177,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // We can throttle this to once every 8ms, which will get us out of // the way of the main output & rendering threads. const auto shared = _shared.lock(); - shared->tsfTryRedrawCanvas = std::make_shared>( - _dispatcher, - TsfRedrawInterval, - [weakThis = get_weak()]() { - if (auto core{ weakThis.get() }; !core->_IsClosing()) - { - core->CursorPositionChanged.raise(*core, nullptr); - } - }); - // NOTE: Calling UpdatePatternLocations from a background // thread is a workaround for us to hit GH#12607 less often. shared->updatePatternLocations = std::make_unique>( @@ -235,7 +218,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // thread. These will be recreated in _setupDispatcherAndCallbacks, when // we're re-attached to a new control (on a possibly new UI thread). const auto shared = _shared.lock(); - shared->tsfTryRedrawCanvas.reset(); shared->updatePatternLocations.reset(); shared->updateScrollBar.reset(); } @@ -456,7 +438,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - wstr: the string of characters to write to the terminal connection. // Return Value: // - - void ControlCore::SendInput(const winrt::hstring& wstr) + void ControlCore::SendInput(const std::wstring_view wstr) { _sendInputToConnection(wstr); } @@ -1040,7 +1022,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto fontWeight = _settings->FontWeight(); _desiredFont = { fontFace, 0, fontWeight.Weight, newSize, CP_UTF8 }; _actualFont = { fontFace, 0, fontWeight.Weight, _desiredFont.GetEngineSize(), CP_UTF8, false }; - _actualFontFaceName = { fontFace }; _desiredFont.SetEnableBuiltinGlyphs(_builtinGlyphs); _desiredFont.SetEnableColorGlyphs(_colorGlyphs); @@ -1445,13 +1426,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation ::base::saturated_cast(fontSize.height) }; } - winrt::hstring ControlCore::FontFaceName() const noexcept - { - // This getter used to return _actualFont.GetFaceName(), however GetFaceName() returns a STL - // string and we need to return a WinRT string. This would require an additional allocation. - // This method is called 10/s by TSFInputControl at the time of writing. - return _actualFontFaceName; - } uint16_t ControlCore::FontWeight() const noexcept { @@ -1622,17 +1596,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::_terminalCursorPositionChanged() - { - // When the buffer's cursor moves, start the throttled func to - // eventually dispatch a CursorPositionChanged event. - const auto shared = _shared.lock_shared(); - if (shared->tsfTryRedrawCanvas) - { - shared->tsfTryRedrawCanvas->Run(); - } - } - void ControlCore::_terminalTaskbarProgressChanged() { TaskbarProgressChanged.raise(*this, nullptr); @@ -2183,6 +2146,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + ::Microsoft::Console::Render::Renderer* ControlCore::GetRenderer() const noexcept + { + return _renderer.get(); + } + uint64_t ControlCore::SwapChainHandle() const { // This is only ever called by TermControl::AttachContent, which occurs diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 31d8fe00c1b..8748a52ee58 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -100,6 +100,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme); + ::Microsoft::Console::Render::Renderer* GetRenderer() const noexcept; uint64_t SwapChainHandle() const; void AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); @@ -113,13 +114,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Windows::Foundation::Size FontSizeInDips() const; winrt::Windows::Foundation::Size FontSize() const noexcept; - winrt::hstring FontFaceName() const noexcept; uint16_t FontWeight() const noexcept; til::color ForegroundColor() const; til::color BackgroundColor() const; - void SendInput(const winrt::hstring& wstr); + void SendInput(std::wstring_view wstr); void PasteText(const winrt::hstring& hstr); bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference& formats); void SelectAll(); @@ -243,8 +243,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring ReadEntireBuffer() const; Control::CommandHistoryContext CommandHistory() const; - static bool IsVintageOpacityAvailable() noexcept; - void AdjustOpacity(const double opacity, const bool relative); void WindowVisibilityChanged(const bool showOrHide); @@ -273,7 +271,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event<> TabColorChanged; til::typed_event<> BackgroundColorChanged; til::typed_event ScrollPositionChanged; - til::typed_event<> CursorPositionChanged; til::typed_event<> TaskbarProgressChanged; til::typed_event<> ConnectionStateChanged; til::typed_event<> HoveredHyperlinkChanged; @@ -298,7 +295,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation private: struct SharedState { - std::shared_ptr> tsfTryRedrawCanvas; std::unique_ptr> updatePatternLocations; std::shared_ptr> updateScrollBar; }; @@ -329,7 +325,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation FontInfoDesired _desiredFont; FontInfo _actualFont; - winrt::hstring _actualFontFaceName; bool _builtinGlyphs = true; bool _colorGlyphs = true; CSSLengthPercentage _cellWidth; @@ -379,7 +374,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _terminalScrollPositionChanged(const int viewTop, const int viewHeight, const int bufferSize); - void _terminalCursorPositionChanged(); void _terminalTaskbarProgressChanged(); void _terminalShowWindowChanged(bool showOrHide); void _terminalTextLayoutUpdated(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 91b58a41c71..d3f7e919620 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -87,7 +87,6 @@ namespace Microsoft.Terminal.Control UInt64 SwapChainHandle { get; }; Windows.Foundation.Size FontSize { get; }; - String FontFaceName { get; }; UInt16 FontWeight { get; }; Double Opacity { get; }; Boolean UseAcrylic { get; }; @@ -172,7 +171,6 @@ namespace Microsoft.Terminal.Control // These events are always called from the UI thread (bugs aside) event Windows.Foundation.TypedEventHandler FontSizeChanged; event Windows.Foundation.TypedEventHandler ScrollPositionChanged; - event Windows.Foundation.TypedEventHandler CursorPositionChanged; event Windows.Foundation.TypedEventHandler ConnectionStateChanged; event Windows.Foundation.TypedEventHandler HoveredHyperlinkChanged; event Windows.Foundation.TypedEventHandler SwapChainChanged; diff --git a/src/cascadia/TerminalControl/TSFInputControl.cpp b/src/cascadia/TerminalControl/TSFInputControl.cpp deleted file mode 100644 index 29ac69e4963..00000000000 --- a/src/cascadia/TerminalControl/TSFInputControl.cpp +++ /dev/null @@ -1,440 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "TSFInputControl.h" -#include "TSFInputControl.g.cpp" - -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::Graphics::Display; -using namespace winrt::Windows::UI::Core; -using namespace winrt::Windows::UI::Text; -using namespace winrt::Windows::UI::Text::Core; -using namespace winrt::Windows::UI::Xaml; - -namespace winrt::Microsoft::Terminal::Control::implementation -{ - TSFInputControl::TSFInputControl() - { - InitializeComponent(); - - // Create a CoreTextEditingContext for since we are acting like a custom edit control - auto manager = CoreTextServicesManager::GetForCurrentView(); - _editContext = manager.CreateEditContext(); - - // InputPane is manually shown inside of TermControl. - _editContext.InputPaneDisplayPolicy(CoreTextInputPaneDisplayPolicy::Manual); - - // Set the input scope to AlphanumericHalfWidth in order to facilitate those CJK input methods to open in English mode by default. - // AlphanumericHalfWidth scope doesn't prevent input method from switching to composition mode, it accepts any character too. - // Besides, Text scope turns on typing intelligence, but that doesn't work in this project. - _editContext.InputScope(CoreTextInputScope::AlphanumericHalfWidth); - - _textRequestedRevoker = _editContext.TextRequested(winrt::auto_revoke, { this, &TSFInputControl::_textRequestedHandler }); - - _selectionRequestedRevoker = _editContext.SelectionRequested(winrt::auto_revoke, { this, &TSFInputControl::_selectionRequestedHandler }); - - _focusRemovedRevoker = _editContext.FocusRemoved(winrt::auto_revoke, { this, &TSFInputControl::_focusRemovedHandler }); - - _textUpdatingRevoker = _editContext.TextUpdating(winrt::auto_revoke, { this, &TSFInputControl::_textUpdatingHandler }); - - _selectionUpdatingRevoker = _editContext.SelectionUpdating(winrt::auto_revoke, { this, &TSFInputControl::_selectionUpdatingHandler }); - - _formatUpdatingRevoker = _editContext.FormatUpdating(winrt::auto_revoke, { this, &TSFInputControl::_formatUpdatingHandler }); - - _layoutRequestedRevoker = _editContext.LayoutRequested(winrt::auto_revoke, { this, &TSFInputControl::_layoutRequestedHandler }); - - _compositionStartedRevoker = _editContext.CompositionStarted(winrt::auto_revoke, { this, &TSFInputControl::_compositionStartedHandler }); - - _compositionCompletedRevoker = _editContext.CompositionCompleted(winrt::auto_revoke, { this, &TSFInputControl::_compositionCompletedHandler }); - } - - // Method Description: - // - Prepares this TSFInputControl to be removed from the UI hierarchy. - void TSFInputControl::Close() - { - // Explicitly disconnect the LayoutRequested handler -- it can cause problems during application teardown. - // See GH#4159 for more info. - // Also disconnect compositionCompleted and textUpdating explicitly. It seems to occasionally cause problems if - // a composition is active during application teardown. - _layoutRequestedRevoker.revoke(); - _compositionCompletedRevoker.revoke(); - _textUpdatingRevoker.revoke(); - } - - // Method Description: - // - NotifyFocusEnter handler for notifying CoreEditTextContext of focus enter - // when TerminalControl receives focus. - // Arguments: - // - - // Return Value: - // - - void TSFInputControl::NotifyFocusEnter() - { - _editContext.NotifyFocusEnter(); - _focused = true; - } - - // Method Description: - // - NotifyFocusEnter handler for notifying CoreEditTextContext of focus leaving. - // when TerminalControl no longer has focus. - // Arguments: - // - - // Return Value: - // - - void TSFInputControl::NotifyFocusLeave() - { - _editContext.NotifyFocusLeave(); - _focused = false; - } - - // Method Description: - // - Clears the input buffer and tells the text server to clear their buffer as well. - // Also clears the TextBlock and sets the active text starting point to 0. - // Arguments: - // - - // Return Value: - // - - void TSFInputControl::ClearBuffer() - { - if (!_inputBuffer.empty()) - { - _inputBuffer.clear(); - _selection = {}; - _activeTextStart = 0; - _editContext.NotifyTextChanged({ 0, INT32_MAX }, 0, _selection); - TextBlock().Text({}); - } - } - - // Method Description: - // - Redraw the canvas if certain dimensions have changed since the last - // redraw. This includes the Terminal cursor position, the Canvas width, and the TextBlock height. - // Arguments: - // - - // Return Value: - // - - void TSFInputControl::TryRedrawCanvas() - try - { - if (!_focused || !Canvas()) - { - return; - } - - // Get the cursor position in text buffer position - auto cursorArgs = winrt::make_self(); - CurrentCursorPosition.raise(*this, *cursorArgs); - const til::point cursorPos{ til::math::flooring, cursorArgs->CurrentPosition() }; - - const auto actualCanvasWidth{ Canvas().ActualWidth() }; - const auto actualTextBlockHeight{ TextBlock().ActualHeight() }; - const auto actualWindowBounds{ CoreWindow::GetForCurrentThread().Bounds() }; - - if (_currentTerminalCursorPos == cursorPos && - _currentCanvasWidth == actualCanvasWidth && - _currentTextBlockHeight == actualTextBlockHeight && - _currentWindowBounds == actualWindowBounds) - { - return; - } - - _currentTerminalCursorPos = cursorPos; - _currentCanvasWidth = actualCanvasWidth; - _currentTextBlockHeight = actualTextBlockHeight; - _currentWindowBounds = actualWindowBounds; - - _RedrawCanvas(); - } - CATCH_LOG() - - // Method Description: - // - Redraw the Canvas and update the current Text Bounds and Control Bounds for - // the CoreTextEditContext. - // Arguments: - // - - // Return Value: - // - - void TSFInputControl::_RedrawCanvas() - { - // Get Font Info as we use this is the pixel size for characters in the display - auto fontArgs = winrt::make_self(); - CurrentFontInfo.raise(*this, *fontArgs); - - const til::size fontSize{ til::math::flooring, fontArgs->FontSize() }; - - // Convert text buffer cursor position to client coordinate position - // within the window. This point is in _pixels_ - const auto clientCursorPos{ _currentTerminalCursorPos * fontSize }; - - // Get scale factor for view - const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); - - const til::point clientCursorInDips{ til::math::flooring, clientCursorPos.x / scaleFactor, clientCursorPos.y / scaleFactor }; - - // Position our TextBlock at the cursor position - Canvas().SetLeft(TextBlock(), clientCursorInDips.x); - Canvas().SetTop(TextBlock(), clientCursorInDips.y); - - // calculate FontSize in pixels from Points - const double fontSizePx = (fontSize.height * 72) / USER_DEFAULT_SCREEN_DPI; - const auto unscaledFontSizePx = fontSizePx / scaleFactor; - - // Make sure to unscale the font size to correct for DPI! XAML needs - // things in DIPs, and the fontSize is in pixels. - TextBlock().FontSize(unscaledFontSizePx); - TextBlock().FontFamily(Media::FontFamily(fontArgs->FontFace())); - TextBlock().FontWeight(fontArgs->FontWeight()); - - // TextBlock's actual dimensions right after initialization is 0w x 0h. So, - // if an IME is displayed before TextBlock has text (like showing the emoji picker - // using Win+.), it'll be placed higher than intended. - TextBlock().MinWidth(unscaledFontSizePx); - TextBlock().MinHeight(unscaledFontSizePx); - _currentTextBlockHeight = std::max(unscaledFontSizePx, _currentTextBlockHeight); - - const auto widthToTerminalEnd = _currentCanvasWidth - clientCursorInDips.x; - // Make sure that we're setting the MaxWidth to a positive number - a - // negative number here will crash us in mysterious ways with a useless - // stack trace - const auto newMaxWidth = std::max(0.0, widthToTerminalEnd); - TextBlock().MaxWidth(newMaxWidth); - - // Get window in screen coordinates, this is the entire window including - // tabs. THIS IS IN DIPs - const til::point windowOrigin{ til::math::flooring, _currentWindowBounds.X, _currentWindowBounds.Y }; - - // Get the offset (margin + tabs, etc..) of the control within the window - const til::point controlOrigin{ til::math::flooring, - this->TransformToVisual(nullptr).TransformPoint(Point(0, 0)) }; - - // The controlAbsoluteOrigin is the origin of the control relative to - // the origin of the displays. THIS IS IN DIPs - const auto controlAbsoluteOrigin{ windowOrigin + controlOrigin }; - - // Convert the control origin to pixels - const auto scaledFrameOrigin = til::point{ til::math::flooring, controlAbsoluteOrigin.x * scaleFactor, controlAbsoluteOrigin.y * scaleFactor }; - - // Get the location of the cursor in the display, in pixels. - auto screenCursorPos{ scaledFrameOrigin + clientCursorPos }; - - // GH #5007 - make sure to account for wrapping the IME composition at - // the right side of the viewport. - const auto textBlockHeight = ::base::ClampMul(_currentTextBlockHeight, scaleFactor); - - // Get the bounds of the composition text, in pixels. - const til::rect textBounds{ til::point{ screenCursorPos.x, screenCursorPos.y }, - til::size{ 0, textBlockHeight } }; - - _currentTextBounds = textBounds.to_winrt_rect(); - - _currentControlBounds = Rect(static_cast(screenCursorPos.x), - static_cast(screenCursorPos.y), - 0.0f, - static_cast(fontSize.height)); - } - - // Method Description: - // - Handler for LayoutRequested event by CoreEditContext responsible - // for returning the current position the IME should be placed - // in screen coordinates on the screen. TSFInputControls internal - // XAML controls (TextBlock/Canvas) are also positioned and updated. - // NOTE: documentation says application should handle this event - // Arguments: - // - sender: CoreTextEditContext sending the request. - // - args: CoreTextLayoutRequestedEventArgs to be updated with position information. - // Return Value: - // - - void TSFInputControl::_layoutRequestedHandler(CoreTextEditContext sender, const CoreTextLayoutRequestedEventArgs& args) - { - auto request = args.Request(); - - TryRedrawCanvas(); - - // Set the text block bounds - request.LayoutBounds().TextBounds(_currentTextBounds); - - // Set the control bounds of the whole control - request.LayoutBounds().ControlBounds(_currentControlBounds); - } - - // Method Description: - // - Handler for CompositionStarted event by CoreEditContext responsible - // for making internal TSFInputControl controls visible. - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - args: CoreTextCompositionStartedEventArgs. Not used in method. - // Return Value: - // - - void TSFInputControl::_compositionStartedHandler(CoreTextEditContext sender, const CoreTextCompositionStartedEventArgs& /*args*/) - { - _inComposition = true; - } - - // Method Description: - // - Handler for CompositionCompleted event by CoreEditContext responsible - // for making internal TSFInputControl controls visible. - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - args: CoreTextCompositionCompletedEventArgs. Not used in method. - // Return Value: - // - - void TSFInputControl::_compositionCompletedHandler(CoreTextEditContext sender, const CoreTextCompositionCompletedEventArgs& /*args*/) - { - _inComposition = false; - _SendAndClearText(); - } - - // Method Description: - // - Handler for FocusRemoved event by CoreEditContext responsible - // for removing focus for the TSFInputControl control accordingly - // when focus was forcibly removed from text input control. - // NOTE: Documentation says application should handle this event - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - object: CoreTextCompositionStartedEventArgs. Not used in method. - // Return Value: - // - - void TSFInputControl::_focusRemovedHandler(CoreTextEditContext sender, const winrt::Windows::Foundation::IInspectable& /*object*/) - { - } - - // Method Description: - // - Handler for TextRequested event by CoreEditContext responsible - // for returning the range of text requested. - // NOTE: Documentation says application should handle this event - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - args: CoreTextTextRequestedEventArgs to be updated with requested range text. - // Return Value: - // - - void TSFInputControl::_textRequestedHandler(CoreTextEditContext sender, const CoreTextTextRequestedEventArgs& args) - { - try - { - const auto range = args.Request().Range(); - const auto text = _inputBuffer.substr( - range.StartCaretPosition, - range.EndCaretPosition - range.StartCaretPosition); - args.Request().Text(text); - } - CATCH_LOG(); - } - - // Method Description: - // - Handler for SelectionRequested event by CoreEditContext responsible - // for returning the currently selected text. - // TSFInputControl currently doesn't allow selection, so nothing happens. - // NOTE: Documentation says application should handle this event - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - args: CoreTextSelectionRequestedEventArgs for providing data for the SelectionRequested event. Not used in method. - // Return Value: - // - - void TSFInputControl::_selectionRequestedHandler(CoreTextEditContext sender, const CoreTextSelectionRequestedEventArgs& args) - { - args.Request().Selection(_selection); - } - - // Method Description: - // - Handler for SelectionUpdating event by CoreEditContext responsible - // for handling modifications to the range of text currently selected. - // TSFInputControl doesn't currently allow selection, so nothing happens. - // NOTE: Documentation says application should set its selection range accordingly - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - args: CoreTextSelectionUpdatingEventArgs for providing data for the SelectionUpdating event. Not used in method. - // Return Value: - // - - void TSFInputControl::_selectionUpdatingHandler(CoreTextEditContext sender, const CoreTextSelectionUpdatingEventArgs& args) - { - _selection = args.Selection(); - args.Result(CoreTextSelectionUpdatingResult::Succeeded); - } - - // Method Description: - // - Handler for TextUpdating event by CoreEditContext responsible - // for handling text updates. - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - args: CoreTextTextUpdatingEventArgs contains new text to update buffer with. - // Return Value: - // - - void TSFInputControl::_textUpdatingHandler(CoreTextEditContext sender, const CoreTextTextUpdatingEventArgs& args) - { - try - { - const auto incomingText = args.Text(); - const auto range = args.Range(); - - _inputBuffer = _inputBuffer.replace( - range.StartCaretPosition, - range.EndCaretPosition - range.StartCaretPosition, - incomingText); - _selection = args.NewSelection(); - // GH#5054: Pressing backspace might move the caret before the _activeTextStart. - _activeTextStart = std::min(_activeTextStart, _inputBuffer.size()); - - // Emojis/Kaomojis/Symbols chosen through the IME without starting composition - // will be sent straight through to the terminal. - if (!_inComposition) - { - _SendAndClearText(); - } - else - { - Canvas().Visibility(Visibility::Visible); - const auto text = _inputBuffer.substr(_activeTextStart); - TextBlock().Text(text); - } - - // Notify the TSF that the update succeeded - args.Result(CoreTextTextUpdatingResult::Succeeded); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - - // indicate updating failed. - args.Result(CoreTextTextUpdatingResult::Failed); - } - } - - void TSFInputControl::_SendAndClearText() - { - const auto text = _inputBuffer.substr(_activeTextStart); - if (text.empty()) - { - return; - } - - CompositionCompleted.raise(text); - - _activeTextStart = _inputBuffer.size(); - - TextBlock().Text({}); - - // After we reset the TextBlock to empty string, we want to make sure - // ActualHeight reflects the respective height. It seems that ActualHeight - // isn't updated until there's new text in the TextBlock, so the next time a user - // invokes "Win+." for the emoji picker IME, it would end up - // using the pre-reset TextBlock().ActualHeight(). - TextBlock().UpdateLayout(); - - // hide the controls until text input starts again - Canvas().Visibility(Visibility::Collapsed); - } - - // Method Description: - // - Handler for FormatUpdating event by CoreEditContext responsible - // for handling different format updates for a particular range of text. - // TSFInputControl doesn't do anything with this event. - // Arguments: - // - sender: CoreTextEditContext sending the request. Not used in method. - // - args: CoreTextFormatUpdatingEventArgs Provides data for the FormatUpdating event. Not used in method. - // Return Value: - // - - void TSFInputControl::_formatUpdatingHandler(CoreTextEditContext sender, const CoreTextFormatUpdatingEventArgs& /*args*/) - { - } -} diff --git a/src/cascadia/TerminalControl/TSFInputControl.h b/src/cascadia/TerminalControl/TSFInputControl.h deleted file mode 100644 index 95a79b96aa8..00000000000 --- a/src/cascadia/TerminalControl/TSFInputControl.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once -#include "TSFInputControl.g.h" -#include "CursorPositionEventArgs.g.h" -#include "FontInfoEventArgs.g.h" - -namespace winrt::Microsoft::Terminal::Control::implementation -{ - struct CursorPositionEventArgs : - public CursorPositionEventArgsT - { - public: - CursorPositionEventArgs() = default; - - WINRT_PROPERTY(Windows::Foundation::Point, CurrentPosition); - }; - - struct FontInfoEventArgs : - public FontInfoEventArgsT - { - public: - FontInfoEventArgs() = default; - - WINRT_PROPERTY(Windows::Foundation::Size, FontSize); - - WINRT_PROPERTY(winrt::hstring, FontFace); - - WINRT_PROPERTY(Windows::UI::Text::FontWeight, FontWeight); - }; - - struct TSFInputControl : TSFInputControlT - { - public: - TSFInputControl(); - - void NotifyFocusEnter(); - void NotifyFocusLeave(); - void ClearBuffer(); - void TryRedrawCanvas(); - - void Close(); - - // -------------------------------- WinRT Events --------------------------------- - til::typed_event CurrentCursorPosition; - til::typed_event CurrentFontInfo; - til::event CompositionCompleted; - - private: - void _layoutRequestedHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextLayoutRequestedEventArgs& args); - void _compositionStartedHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextCompositionStartedEventArgs& args); - void _compositionCompletedHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextCompositionCompletedEventArgs& args); - void _focusRemovedHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::Foundation::IInspectable& object); - void _selectionRequestedHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextSelectionRequestedEventArgs& args); - void _textRequestedHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextTextRequestedEventArgs& args); - void _selectionUpdatingHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextSelectionUpdatingEventArgs& args); - void _textUpdatingHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextTextUpdatingEventArgs& args); - void _formatUpdatingHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextFormatUpdatingEventArgs& args); - - void _SendAndClearText(); - void _RedrawCanvas(); - - winrt::Windows::UI::Text::Core::CoreTextEditContext::TextRequested_revoker _textRequestedRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::SelectionRequested_revoker _selectionRequestedRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::FocusRemoved_revoker _focusRemovedRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::TextUpdating_revoker _textUpdatingRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::SelectionUpdating_revoker _selectionUpdatingRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::FormatUpdating_revoker _formatUpdatingRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::LayoutRequested_revoker _layoutRequestedRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionStarted_revoker _compositionStartedRevoker; - winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionCompleted_revoker _compositionCompletedRevoker; - - Windows::UI::Text::Core::CoreTextEditContext _editContext{ nullptr }; - std::wstring _inputBuffer; - winrt::Windows::UI::Text::Core::CoreTextRange _selection{}; - size_t _activeTextStart = 0; - bool _inComposition = false; - bool _focused = false; - - til::point _currentTerminalCursorPos{}; - double _currentCanvasWidth = 0.0; - double _currentTextBlockHeight = 0.0; - winrt::Windows::Foundation::Rect _currentControlBounds{}; - winrt::Windows::Foundation::Rect _currentTextBounds{}; - winrt::Windows::Foundation::Rect _currentWindowBounds{}; - }; -} -namespace winrt::Microsoft::Terminal::Control::factory_implementation -{ - BASIC_FACTORY(TSFInputControl); -} diff --git a/src/cascadia/TerminalControl/TSFInputControl.idl b/src/cascadia/TerminalControl/TSFInputControl.idl deleted file mode 100644 index 6a6f6814f2a..00000000000 --- a/src/cascadia/TerminalControl/TSFInputControl.idl +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -namespace Microsoft.Terminal.Control -{ - delegate void CompositionCompletedEventArgs(String text); - - runtimeclass CursorPositionEventArgs - { - Windows.Foundation.Point CurrentPosition { get; set; }; - } - - runtimeclass FontInfoEventArgs - { - String FontFace { get; set; }; - Windows.Foundation.Size FontSize { get; set; }; - Windows.UI.Text.FontWeight FontWeight { get; set; }; - } - - [default_interface] - runtimeclass TSFInputControl : Windows.UI.Xaml.Controls.UserControl - { - TSFInputControl(); - - event CompositionCompletedEventArgs CompositionCompleted; - event Windows.Foundation.TypedEventHandler CurrentCursorPosition; - event Windows.Foundation.TypedEventHandler CurrentFontInfo; - - void NotifyFocusEnter(); - void NotifyFocusLeave(); - void ClearBuffer(); - void TryRedrawCanvas(); - - void Close(); - } -} diff --git a/src/cascadia/TerminalControl/TSFInputControl.xaml b/src/cascadia/TerminalControl/TSFInputControl.xaml deleted file mode 100644 index bc7fca22bfe..00000000000 --- a/src/cascadia/TerminalControl/TSFInputControl.xaml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 96c78f0afc5..acab829312c 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -5,10 +5,10 @@ #include "TermControl.h" #include -#include #include "TermControlAutomationPeer.h" #include "../../renderer/atlas/AtlasEngine.h" +#include "../../tsf/Handle.h" #include "TermControl.g.cpp" @@ -41,11 +41,130 @@ constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds( constexpr const auto TerminalWarningBellInterval = std::chrono::milliseconds(1000); DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::CopyFormat); - DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState); +static Microsoft::Console::TSF::Handle& GetTSFHandle() +{ + // https://en.cppreference.com/w/cpp/language/storage_duration + // > Variables declared at block scope with the specifier static or thread_local + // > [...] are initialized the first time control passes through their declaration + // --> Lazy, per-(window-)thread initialization of the TSF handle + thread_local auto s_tsf = ::Microsoft::Console::TSF::Handle::Create(); + return s_tsf; +} + namespace winrt::Microsoft::Terminal::Control::implementation { + TsfDataProvider::TsfDataProvider(TermControl* termControl) noexcept : + _termControl{ termControl } + { + } + + STDMETHODIMP TsfDataProvider::QueryInterface(REFIID, void**) noexcept + { + return E_NOTIMPL; + } + + ULONG STDMETHODCALLTYPE TsfDataProvider::AddRef() noexcept + { + return 1; + } + + ULONG STDMETHODCALLTYPE TsfDataProvider::Release() noexcept + { + return 1; + } + + HWND TsfDataProvider::GetHwnd() + { + if (!_hwnd) + { + // WinUI's WinRT based TSF runs in its own window "Windows.UI.Input.InputSite.WindowClass" (..."great") + // and in order for us to snatch the focus away from that one we need to find its HWND. + // The way we do it here is by finding the existing, active TSF context and getting the HWND from it. + _hwnd = GetTSFHandle().FindWindowOfActiveTSF(); + if (!_hwnd) + { + _hwnd = reinterpret_cast(_termControl->OwningHwnd()); + } + } + return _hwnd; + } + + RECT TsfDataProvider::GetViewport() + { + const auto scaleFactor = static_cast(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel()); + const auto globalOrigin = CoreWindow::GetForCurrentThread().Bounds(); + const auto localOrigin = _termControl->TransformToVisual(nullptr).TransformPoint({}); + const auto size = _termControl->ActualSize(); + + const auto left = globalOrigin.X + localOrigin.X; + const auto top = globalOrigin.Y + localOrigin.Y; + const auto right = left + size.x; + const auto bottom = top + size.y; + + return { + lroundf(left * scaleFactor), + lroundf(top * scaleFactor), + lroundf(right * scaleFactor), + lroundf(bottom * scaleFactor), + }; + } + + RECT TsfDataProvider::GetCursorPosition() + { + const auto core = _getCore(); + if (!core) + { + return {}; + } + + const auto scaleFactor = static_cast(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel()); + const auto globalOrigin = CoreWindow::GetForCurrentThread().Bounds(); + const auto localOrigin = _termControl->TransformToVisual(nullptr).TransformPoint({}); + const auto padding = _termControl->GetPadding(); + const auto cursorPosition = core->CursorPosition(); + const auto fontSize = core->FontSize(); + + // fontSize is not in DIPs, so we need to first multiply by scaleFactor and then do the rest. + const auto left = (globalOrigin.X + localOrigin.X + static_cast(padding.Left)) * scaleFactor + cursorPosition.X * fontSize.Width; + const auto top = (globalOrigin.Y + localOrigin.Y + static_cast(padding.Top)) * scaleFactor + cursorPosition.Y * fontSize.Height; + const auto right = left + fontSize.Width; + const auto bottom = top + fontSize.Height; + + return { + lroundf(left), + lroundf(top), + lroundf(right), + lroundf(bottom), + }; + } + + void TsfDataProvider::HandleOutput(std::wstring_view text) + { + const auto core = _getCore(); + if (!core) + { + return; + } + core->SendInput(text); + } + + ::Microsoft::Console::Render::Renderer* TsfDataProvider::GetRenderer() + { + const auto core = _getCore(); + if (!core) + { + return nullptr; + } + return core->GetRenderer(); + } + + ControlCore* TsfDataProvider::_getCore() const noexcept + { + return get_self(_termControl->_core); + } + TermControl::TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) : @@ -154,7 +273,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // attached content before we set up the throttled func, and that'll A/V _revokers.coreScrollPositionChanged = _core.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged }); _revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell }); - _revokers.CursorPositionChanged = _core.CursorPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_CursorPositionChanged }); static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000)); _autoScrollTimer.Interval(AutoScrollUpdateInterval); @@ -590,18 +708,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation SelectionStartMarker().Fill(cursorColorBrush); SelectionEndMarker().Fill(cursorColorBrush); - // Set TSF Foreground - Media::SolidColorBrush foregroundBrush{}; - if (_core.Settings().UseBackgroundImageForWindow()) - { - foregroundBrush.Color(Windows::UI::Colors::Transparent()); - } - else - { - foregroundBrush.Color(static_cast(newAppearance.DefaultForeground())); - } - TSFInputControl().Foreground(foregroundBrush); - _core.ApplyAppearance(_focused); } @@ -654,8 +760,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto newMargin = ParseThicknessFromPadding(settings.Padding()); SwapChainPanel().Margin(newMargin); - TSFInputControl().Margin(newMargin); - // Apply settings for scrollbar if (settings.ScrollState() == ScrollbarState::Hidden) { @@ -1170,19 +1274,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - // GH#11479: TSF wants to be notified of any character input via ICoreTextEditContext::NotifyTextChanged(). - // TSF is built and tested around the idea that you inform it of any text changes that happen - // when it doesn't currently compose characters. For instance writing "xin chaof" with the - // Vietnamese IME should produce "xin chào". After writing "xin" it'll emit a composition - // completion event and we'll write "xin" to the shell. It now has no input focus and won't know - // about the whitespace. If you then write "chaof", it'll emit another composition completion - // event for "xinchaof" and the resulting output in the shell will finally read "xinxinchaof". - // A composition completion event technically doesn't mean that the completed text is now - // immutable after all. We could (and probably should) inform TSF of any input changes, - // but we technically aren't a text input field. The immediate solution was - // to simply force TSF to clear its text whenever we have input focus. - TSFInputControl().ClearBuffer(); - HidePointerCursor.raise(*this, nullptr); const auto ch = e.Character(); @@ -1970,12 +2061,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return; } - - if (TSFInputControl() != nullptr) - { - TSFInputControl().NotifyFocusEnter(); - } - if (_cursorTimer) { // When the terminal focuses, show the cursor immediately @@ -1996,6 +2081,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { UpdateAppearance(_core.FocusedAppearance()); } + + GetTSFHandle().Focus(&_tsfDataProvider); } // Method Description: @@ -2021,11 +2108,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _interactivity.LostFocus(); } - if (TSFInputControl() != nullptr) - { - TSFInputControl().NotifyFocusLeave(); - } - if (_cursorTimer && !_displayCursorWhileBlurred()) { _cursorTimer.Stop(); @@ -2043,6 +2125,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { UpdateAppearance(_core.UnfocusedAppearance()); } + + GetTSFHandle().Unfocus(&_tsfDataProvider); } // Method Description: @@ -2169,27 +2253,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - // Method Description: - // - Tells TSFInputControl to redraw the Canvas/TextBlock so it'll update - // to be where the current cursor position is. - // Arguments: - // - N/A - winrt::fire_and_forget TermControl::_CursorPositionChanged(const IInspectable& /*sender*/, - const IInspectable& /*args*/) - { - // Prior to GH#10187, this fired a trailing throttled func to update the - // TSF canvas only every 100ms. Now, the throttling occurs on the - // ControlCore side. If we're told to update the cursor position, we can - // just go ahead and do it. - // This can come in off the COM thread - hop back to the UI thread. - auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher()); - if (auto control{ weakThis.get() }; !control->_IsClosing()) - { - control->TSFInputControl().TryRedrawCanvas(); - } - } - hstring TermControl::Title() { return _core.Title(); @@ -2304,9 +2367,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers = {}; - // Disconnect the TSF input control so it doesn't receive EditContext events. - TSFInputControl().Close(); - // At the time of writing, closing the last tab of a window inexplicably // does not lead to the destruction of the remaining TermControl instance(s). // On Win10 we don't destroy window threads due to bugs in DesktopWindowXamlSource. @@ -2317,6 +2377,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation _cursorTimer.Stop(); _blinkTimer.Stop(); + // This is absolutely crucial, as the TSF code tries to hold a strong reference to _tsfDataProvider, + // but right now _tsfDataProvider implements IUnknown as a no-op. This ensures that TSF stops referencing us. + // ~TermControl() calls Close() so this should be safe. + GetTSFHandle().Unfocus(&_tsfDataProvider); + if (!_detached) { _interactivity.Close(); @@ -2742,62 +2807,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation return relativeToMarginInPixels; } - // Method Description: - // - Composition Completion handler for the TSFInputControl that - // handles writing text out to TerminalConnection - // Arguments: - // - text: the text to write to TerminalConnection - // Return Value: - // - - void TermControl::_CompositionCompleted(winrt::hstring text) - { - if (_IsClosing()) - { - return; - } - - // SendInput will take care of broadcasting for us. - SendInput(text); - } - - // Method Description: - // - CurrentCursorPosition handler for the TSFInputControl that - // handles returning current cursor position. - // Arguments: - // - eventArgs: event for storing the current cursor position - // Return Value: - // - - void TermControl::_CurrentCursorPositionHandler(const IInspectable& /*sender*/, - const CursorPositionEventArgs& eventArgs) - { - if (!_initializedTerminal) - { - // fake it - eventArgs.CurrentPosition({ 0, 0 }); - return; - } - - const auto cursorPos = _core.CursorPosition(); - eventArgs.CurrentPosition({ static_cast(cursorPos.X), static_cast(cursorPos.Y) }); - } - - // Method Description: - // - FontInfo handler for the TSFInputControl that - // handles returning current font information - // Arguments: - // - eventArgs: event for storing the current font information - // Return Value: - // - - void TermControl::_FontInfoHandler(const IInspectable& /*sender*/, - const FontInfoEventArgs& eventArgs) - { - eventArgs.FontSize(CharacterDimensions()); - eventArgs.FontFace(_core.FontFaceName()); - ::winrt::Windows::UI::Text::FontWeight weight; - weight.Weight = _core.FontWeight(); - eventArgs.FontWeight(weight); - } - // Method Description: // - Calculates speed of single axis of auto scrolling. It has to allow for both // fast and precise selection. diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 4240bb23efe..4da7ec63d0c 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -3,14 +3,12 @@ #pragma once +#include "SearchBoxControl.h" #include "TermControl.g.h" -#include "XamlLights.h" -#include "EventArgs.h" -#include "../../renderer/base/Renderer.hpp" -#include "../../renderer/uia/UiaRenderer.hpp" +#include "../../buffer/out/search.h" #include "../../cascadia/TerminalCore/Terminal.hpp" -#include "../buffer/out/search.h" -#include "SearchBoxControl.h" +#include "../../renderer/uia/UiaRenderer.hpp" +#include "../../tsf/Handle.h" #include "ControlInteractivity.h" #include "ControlSettings.h" @@ -22,6 +20,30 @@ namespace Microsoft::Console::VirtualTerminal namespace winrt::Microsoft::Terminal::Control::implementation { + struct TermControl; + + struct TsfDataProvider : ::Microsoft::Console::TSF::IDataProvider + { + explicit TsfDataProvider(TermControl* termControl) noexcept; + virtual ~TsfDataProvider() = default; + + STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj) noexcept override; + ULONG STDMETHODCALLTYPE AddRef() noexcept override; + ULONG STDMETHODCALLTYPE Release() noexcept override; + + HWND GetHwnd() override; + RECT GetViewport() override; + RECT GetCursorPosition() override; + void HandleOutput(std::wstring_view text) override; + ::Microsoft::Console::Render::Renderer* GetRenderer() override; + + private: + ControlCore* _getCore() const noexcept; + + TermControl* _termControl = nullptr; + HWND _hwnd = nullptr; + }; + struct TermControl : TermControlT { TermControl(Control::ControlInteractivity content); @@ -201,6 +223,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation private: friend struct TermControlT; // friend our parent so it can bind private event handlers + friend struct TsfDataProvider; // NOTE: _uiaEngine must be ordered before _core. // @@ -213,7 +236,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::TermControlAutomationPeer _automationPeer{ nullptr }; Control::ControlInteractivity _interactivity{ nullptr }; Control::ControlCore _core{ nullptr }; - + TsfDataProvider _tsfDataProvider{ this }; winrt::com_ptr _searchBox; bool _closing{ false }; @@ -328,7 +351,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _TerminalTabColorChanged(const std::optional color); void _ScrollPositionChanged(const IInspectable& sender, const Control::ScrollPositionChangedArgs& args); - winrt::fire_and_forget _CursorPositionChanged(const IInspectable& sender, const IInspectable& args); bool _CapturePointer(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); bool _ReleasePointerCapture(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); @@ -354,11 +376,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); void _UpdateSearchScrollMarks(); - // TSFInputControl Handlers - void _CompositionCompleted(winrt::hstring text); - void _CurrentCursorPositionHandler(const IInspectable& sender, const CursorPositionEventArgs& eventArgs); - void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs); - void _hoveredHyperlinkChanged(const IInspectable& sender, const IInspectable& args); winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args); @@ -388,7 +405,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation { Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged; Control::ControlCore::WarningBell_revoker WarningBell; - Control::ControlCore::CursorPositionChanged_revoker CursorPositionChanged; Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState; Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged; Control::ControlCore::FontSizeChanged_revoker FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 043a95d3b2e..feb8508440d 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1332,11 +1332,6 @@ - - InteractivityAutomationPeer.idl - - TSFInputControl.xaml - @@ -102,9 +99,6 @@ TermControl.xaml - - TSFInputControl.xaml - TermControlAutomationPeer.idl @@ -137,9 +131,6 @@ - - TSFInputControl.xaml - @@ -149,9 +140,6 @@ Designer - - Designer - @@ -160,15 +148,16 @@ - - - + + + + - - - + - + + + false false diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 4eccd3be999..25ea33fd47b 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -422,19 +422,7 @@ CATCH_RETURN() void Terminal::Write(std::wstring_view stringView) { - const auto& cursor = _activeBuffer().GetCursor(); - const til::point cursorPosBefore{ cursor.GetPosition() }; - _stateMachine->ProcessString(stringView); - - const til::point cursorPosAfter{ cursor.GetPosition() }; - - // Firing the CursorPositionChanged event is very expensive so we try not to - // do that when the cursor does not need to be redrawn. - if (cursorPosBefore != cursorPosAfter) - { - _NotifyTerminalCursorPositionChanged(); - } } // Method Description: @@ -1099,18 +1087,6 @@ void Terminal::_NotifyScrollEvent() } } -void Terminal::_NotifyTerminalCursorPositionChanged() noexcept -{ - if (_pfnCursorPositionChanged) - { - try - { - _pfnCursorPositionChanged(); - } - CATCH_LOG(); - } -} - void Terminal::SetWriteInputCallback(std::function pfn) noexcept { _pfnWriteInput.swap(pfn); @@ -1136,11 +1112,6 @@ void Terminal::SetScrollPositionChangedCallback(std::function pfn) noexcept -{ - _pfnCursorPositionChanged.swap(pfn); -} - // Method Description: // - Allows settings a callback for settings the taskbar progress indicator // Arguments: diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 6a6017db889..7f1c20789e9 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -189,7 +189,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region IRenderData Microsoft::Console::Types::Viewport GetViewport() noexcept override; til::point GetTextBufferEndPosition() const noexcept override; - const TextBuffer& GetTextBuffer() const noexcept override; + TextBuffer& GetTextBuffer() const noexcept override; const FontInfo& GetFontInfo() const noexcept override; void LockConsole() noexcept override; @@ -203,7 +203,6 @@ class Microsoft::Terminal::Core::Terminal final : ULONG GetCursorPixelWidth() const noexcept override; CursorType GetCursorStyle() const noexcept override; bool IsCursorDoubleWidth() const override; - const std::vector GetOverlays() const noexcept override; const bool IsGridLineDrawingAllowed() noexcept override; const std::wstring GetHyperlinkUri(uint16_t id) const override; const std::wstring GetHyperlinkCustomId(uint16_t id) const override; @@ -228,7 +227,6 @@ class Microsoft::Terminal::Core::Terminal final : void SetTitleChangedCallback(std::function pfn) noexcept; void SetCopyToClipboardCallback(std::function pfn) noexcept; void SetScrollPositionChangedCallback(std::function pfn) noexcept; - void SetCursorPositionChangedCallback(std::function pfn) noexcept; void TaskbarProgressChangedCallback(std::function pfn) noexcept; void SetShowWindowCallback(std::function pfn) noexcept; void SetPlayMidiNoteCallback(std::function pfn) noexcept; @@ -339,7 +337,6 @@ class Microsoft::Terminal::Core::Terminal final : til::recursive_ticket_lock _readWriteLock; std::function _pfnScrollPositionChanged; - std::function _pfnCursorPositionChanged; std::function _pfnTaskbarProgressChanged; std::function _pfnShowWindowChanged; std::function _pfnPlayMidiNote; @@ -457,9 +454,6 @@ class Microsoft::Terminal::Core::Terminal final : til::CoordType _ScrollToPoints(const til::point coordStart, const til::point coordEnd); void _NotifyScrollEvent(); - - void _NotifyTerminalCursorPositionChanged() noexcept; - bool _inAltBuffer() const noexcept; TextBuffer& _activeBuffer() const noexcept; void _updateUrlDetection(); diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index a9eaf11fa80..ec28b59d56d 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -22,7 +22,7 @@ til::point Terminal::GetTextBufferEndPosition() const noexcept return { _GetMutableViewport().Width() - 1, ViewEndIndex() }; } -const TextBuffer& Terminal::GetTextBuffer() const noexcept +TextBuffer& Terminal::GetTextBuffer() const noexcept { return _activeBuffer(); } @@ -79,11 +79,6 @@ bool Terminal::IsCursorDoubleWidth() const return buffer.GetRowByOffset(position.y).DbcsAttrAt(position.x) != DbcsAttribute::Single; } -const std::vector Terminal::GetOverlays() const noexcept -{ - return {}; -} - const bool Terminal::IsGridLineDrawingAllowed() noexcept { return true; diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp index d789bfc5583..051732333cc 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp @@ -51,8 +51,6 @@ class TerminalCoreUnitTests::TerminalBufferTests final TEST_METHOD(TestGetReverseTab); - TEST_METHOD(TestCursorNotifications); - TEST_METHOD_SETUP(MethodSetup) { // STEP 1: Set up the Terminal @@ -596,43 +594,3 @@ void TerminalBufferTests::TestGetReverseTab() L"Cursor adjusted to last item in the sample list from position beyond end."); } } - -void TerminalBufferTests::TestCursorNotifications() -{ - // Test for GH#11170 - - // Suppress test exceptions. If they occur in the lambda, they'll just crash - // TAEF, which is annoying. - const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; - - auto callbackWasCalled = false; - auto expectedCallbacks = 0; - auto cb = [&expectedCallbacks, &callbackWasCalled]() mutable { - Log::Comment(L"Callback triggered"); - callbackWasCalled = true; - expectedCallbacks--; - VERIFY_IS_GREATER_THAN_OR_EQUAL(expectedCallbacks, 0); - }; - term->_pfnCursorPositionChanged = cb; - - // The exact number of callbacks here is fungible, if need be. - - expectedCallbacks = 1; - callbackWasCalled = false; - term->Write(L"Foo"); - VERIFY_ARE_EQUAL(0, expectedCallbacks); - VERIFY_IS_TRUE(callbackWasCalled); - - expectedCallbacks = 1; - callbackWasCalled = false; - term->Write(L"Foo\r\nBar"); - VERIFY_ARE_EQUAL(0, expectedCallbacks); - VERIFY_IS_TRUE(callbackWasCalled); - - expectedCallbacks = 2; // One for each Write - callbackWasCalled = false; - term->Write(L"Foo\r\nBar"); - term->Write(L"Foo\r\nBar"); - VERIFY_ARE_EQUAL(0, expectedCallbacks); - VERIFY_IS_TRUE(callbackWasCalled); -} diff --git a/src/host/_output.cpp b/src/host/_output.cpp index 8ad7117ea0b..0804a7ffab9 100644 --- a/src/host/_output.cpp +++ b/src/host/_output.cpp @@ -52,8 +52,6 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region) ServiceLocator::LocateGlobals().pRender->TriggerRedraw(region); } } - - WriteConvRegionToScreen(screenInfo, region); } // Routine Description: diff --git a/src/host/conareainfo.cpp b/src/host/conareainfo.cpp deleted file mode 100644 index 0699c7bf15c..00000000000 --- a/src/host/conareainfo.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "conareainfo.h" - -#include "_output.h" - -#include "../interactivity/inc/ServiceLocator.hpp" -#include "../types/inc/viewport.hpp" - -#pragma hdrstop - -using namespace Microsoft::Console::Types; -using Microsoft::Console::Interactivity::ServiceLocator; - -ConversionAreaBufferInfo::ConversionAreaBufferInfo(const til::size coordBufferSize) : - coordCaBuffer(coordBufferSize) -{ -} - -ConversionAreaInfo::ConversionAreaInfo(const til::size bufferSize, - const til::size windowSize, - const TextAttribute& fill, - const TextAttribute& popupFill, - const FontInfo fontInfo) : - _caInfo{ bufferSize }, - _isHidden{ true }, - _screenBuffer{ nullptr } -{ - SCREEN_INFORMATION* pNewScreen = nullptr; - - // cursor has no height because it won't be rendered for conversion area - THROW_IF_NTSTATUS_FAILED(SCREEN_INFORMATION::CreateInstance(windowSize, - fontInfo, - bufferSize, - fill, - popupFill, - 0, - &pNewScreen)); - - // Suppress painting notifications for modifying a conversion area cursor as they're not actually rendered. - pNewScreen->GetTextBuffer().GetCursor().SetIsConversionArea(true); - pNewScreen->ConvScreenInfo = this; - - _screenBuffer.reset(pNewScreen); -} - -ConversionAreaInfo::ConversionAreaInfo(ConversionAreaInfo&& other) : - _caInfo(other._caInfo), - _isHidden(other._isHidden), - _screenBuffer(nullptr) -{ - std::swap(_screenBuffer, other._screenBuffer); -} - -// Routine Description: -// - Describes whether the conversion area should be drawn or should be hidden. -// Arguments: -// - -// Return Value: -// - True if it should not be drawn. False if it should be drawn. -bool ConversionAreaInfo::IsHidden() const noexcept -{ - return _isHidden; -} - -// Routine Description: -// - Sets a value describing whether the conversion area should be drawn or should be hidden. -// Arguments: -// - fIsHidden - True if it should not be drawn. False if it should be drawn. -// Return Value: -// - -void ConversionAreaInfo::SetHidden(const bool fIsHidden) noexcept -{ - _isHidden = fIsHidden; -} - -// Routine Description: -// - Retrieves the underlying text buffer for use in rendering data -const TextBuffer& ConversionAreaInfo::GetTextBuffer() const noexcept -{ - return _screenBuffer->GetTextBuffer(); -} - -// Routine Description: -// - Retrieves the layout/overlay information about where to place this conversion area relative to the -// existing screen buffers and viewports. -const ConversionAreaBufferInfo& ConversionAreaInfo::GetAreaBufferInfo() const noexcept -{ - return _caInfo; -} - -// Routine Description: -// - Forwards a color attribute setting request to the internal screen information -// Arguments: -// - attr - Color to apply to internal screen buffer -void ConversionAreaInfo::SetAttributes(const TextAttribute& attr) -{ - _screenBuffer->SetAttributes(attr); -} - -// Routine Description: -// - Writes text into the conversion area. Since conversion areas are only -// one line, you can only specify the column to write. -// Arguments: -// - text - Text to insert into the conversion area buffer -// - column - Column to start at (X position) -void ConversionAreaInfo::WriteText(const std::vector& text, - const til::CoordType column) -{ - std::span view(text.data(), text.size()); - _screenBuffer->Write(view, { column, 0 }); -} - -// Routine Description: -// - Clears out a conversion area -void ConversionAreaInfo::ClearArea() noexcept -{ - SetHidden(true); - - try - { - _screenBuffer->ClearTextData(); - } - CATCH_LOG(); - - Paint(); -} - -[[nodiscard]] HRESULT ConversionAreaInfo::Resize(const til::size newSize) noexcept -{ - // attempt to resize underlying buffers - RETURN_IF_NTSTATUS_FAILED(_screenBuffer->ResizeScreenBuffer(newSize, FALSE)); - - // store new size - _caInfo.coordCaBuffer = newSize; - - // restrict viewport to buffer size. - const til::point restriction = { newSize.width - 1, newSize.height - 1 }; - _caInfo.rcViewCaWindow.left = std::min(_caInfo.rcViewCaWindow.left, restriction.x); - _caInfo.rcViewCaWindow.right = std::min(_caInfo.rcViewCaWindow.right, restriction.x); - _caInfo.rcViewCaWindow.top = std::min(_caInfo.rcViewCaWindow.top, restriction.y); - _caInfo.rcViewCaWindow.bottom = std::min(_caInfo.rcViewCaWindow.bottom, restriction.y); - - return S_OK; -} - -void ConversionAreaInfo::SetWindowInfo(const til::inclusive_rect& view) noexcept -{ - if (view.left != _caInfo.rcViewCaWindow.left || - view.top != _caInfo.rcViewCaWindow.top || - view.right != _caInfo.rcViewCaWindow.right || - view.bottom != _caInfo.rcViewCaWindow.bottom) - { - if (!IsHidden()) - { - SetHidden(true); - Paint(); - - _caInfo.rcViewCaWindow = view; - SetHidden(false); - Paint(); - } - else - { - _caInfo.rcViewCaWindow = view; - } - } -} - -void ConversionAreaInfo::SetViewPos(const til::point pos) noexcept -{ - if (IsHidden()) - { - _caInfo.coordConView = pos; - } - else - { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - - auto OldRegion = _caInfo.rcViewCaWindow; - OldRegion.left += _caInfo.coordConView.x; - OldRegion.right += _caInfo.coordConView.x; - OldRegion.top += _caInfo.coordConView.y; - OldRegion.bottom += _caInfo.coordConView.y; - WriteToScreen(gci.GetActiveOutputBuffer(), Viewport::FromInclusive(OldRegion)); - - _caInfo.coordConView = pos; - - auto NewRegion = _caInfo.rcViewCaWindow; - NewRegion.left += _caInfo.coordConView.x; - NewRegion.right += _caInfo.coordConView.x; - NewRegion.top += _caInfo.coordConView.y; - NewRegion.bottom += _caInfo.coordConView.y; - WriteToScreen(gci.GetActiveOutputBuffer(), Viewport::FromInclusive(NewRegion)); - } -} - -void ConversionAreaInfo::Paint() const noexcept -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - auto& ScreenInfo = gci.GetActiveOutputBuffer(); - const auto viewport = ScreenInfo.GetViewport(); - - til::inclusive_rect WriteRegion; - WriteRegion.left = viewport.Left() + _caInfo.coordConView.x + _caInfo.rcViewCaWindow.left; - WriteRegion.right = WriteRegion.left + (_caInfo.rcViewCaWindow.right - _caInfo.rcViewCaWindow.left); - WriteRegion.top = viewport.Top() + _caInfo.coordConView.y + _caInfo.rcViewCaWindow.top; - WriteRegion.bottom = WriteRegion.top + (_caInfo.rcViewCaWindow.bottom - _caInfo.rcViewCaWindow.top); - - if (!IsHidden()) - { - WriteConvRegionToScreen(ScreenInfo, Viewport::FromInclusive(WriteRegion)); - } - else - { - WriteToScreen(ScreenInfo, Viewport::FromInclusive(WriteRegion)); - } -} diff --git a/src/host/conareainfo.h b/src/host/conareainfo.h deleted file mode 100644 index f334cbac813..00000000000 --- a/src/host/conareainfo.h +++ /dev/null @@ -1,74 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- conareainfo.h - -Abstract: -- This module contains the structures for the console IME conversion area -- The conversion area is the overlay on the screen where a user attempts to form - a string that they would like to insert into the buffer. - -Author: -- Michael Niksa (MiNiksa) 10-May-2018 - -Revision History: -- From pieces of convarea.cpp originally authored by KazuM ---*/ - -#pragma once - -#include "../buffer/out/OutputCell.hpp" -#include "../buffer/out/TextAttribute.hpp" -#include "../renderer/inc/FontInfo.hpp" - -class SCREEN_INFORMATION; -class TextBuffer; - -// Internal structures and definitions used by the conversion area. -class ConversionAreaBufferInfo final -{ -public: - til::size coordCaBuffer; - til::inclusive_rect rcViewCaWindow; - til::point coordConView; - - explicit ConversionAreaBufferInfo(const til::size coordBufferSize); -}; - -class ConversionAreaInfo final -{ -public: - ConversionAreaInfo(const til::size bufferSize, - const til::size windowSize, - const TextAttribute& fill, - const TextAttribute& popupFill, - const FontInfo fontInfo); - ~ConversionAreaInfo() = default; - ConversionAreaInfo(const ConversionAreaInfo&) = delete; - ConversionAreaInfo(ConversionAreaInfo&& other); - ConversionAreaInfo& operator=(const ConversionAreaInfo&) & = delete; - ConversionAreaInfo& operator=(ConversionAreaInfo&&) & = delete; - - bool IsHidden() const noexcept; - void SetHidden(const bool fIsHidden) noexcept; - void ClearArea() noexcept; - - [[nodiscard]] HRESULT Resize(const til::size newSize) noexcept; - - void SetViewPos(const til::point pos) noexcept; - void SetWindowInfo(const til::inclusive_rect& view) noexcept; - void Paint() const noexcept; - - void WriteText(const std::vector& text, const til::CoordType column); - void SetAttributes(const TextAttribute& attr); - - const TextBuffer& GetTextBuffer() const noexcept; - const ConversionAreaBufferInfo& GetAreaBufferInfo() const noexcept; - -private: - ConversionAreaBufferInfo _caInfo; - std::unique_ptr _screenBuffer; - bool _isHidden; -}; diff --git a/src/host/conimeinfo.cpp b/src/host/conimeinfo.cpp deleted file mode 100644 index fc2ce5ae74c..00000000000 --- a/src/host/conimeinfo.cpp +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "conimeinfo.h" - -#include - -#include "conareainfo.h" -#include "_output.h" -#include "dbcs.h" -#include "../interactivity/inc/ServiceLocator.hpp" -#include "../types/inc/GlyphWidth.hpp" - -// Attributes flags: -#define COMMON_LVB_GRID_SINGLEFLAG 0x2000 // DBCS: Grid attribute: use for ime cursor. - -using Microsoft::Console::Interactivity::ServiceLocator; - -ConsoleImeInfo::ConsoleImeInfo() : - _isSavedCursorVisible(false) -{ -} - -// Routine Description: -// - Copies default attribute (color) data from the active screen buffer into the conversion area buffers -void ConsoleImeInfo::RefreshAreaAttributes() -{ - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto attributes = gci.GetActiveOutputBuffer().GetAttributes(); - - for (auto& area : ConvAreaCompStr) - { - area.SetAttributes(attributes); - } -} - -// Routine Description: -// - Takes the internally held composition message data from the last WriteCompMessage call -// and attempts to redraw it on the screen which will account for changes in viewport dimensions -void ConsoleImeInfo::RedrawCompMessage() -{ - if (!_text.empty()) - { - ClearAllAreas(); - _WriteUndeterminedChars(_text, _attributes, _colorArray); - } -} - -// Routine Description: -// - Writes an undetermined composition message to the screen including the text -// and color and cursor positioning attribute data so the user can walk through -// what they're proposing to insert into the buffer. -// Arguments: -// - text - The actual text of what the user would like to insert (UTF-16) -// - attributes - Encoded attributes including the cursor position and the color index (to the array) -// - colorArray - An array of colors to use for the text -void ConsoleImeInfo::WriteCompMessage(const std::wstring_view text, - const std::span attributes, - const std::span colorArray) -{ - ClearAllAreas(); - - // MSFT:29219348 only hide the cursor after the IME produces a string. - // See notes in convarea.cpp ImeStartComposition(). - SaveCursorVisibility(); - - // Save copies of the composition message in case we need to redraw it as things scroll/resize - _text = text; - _attributes.assign(attributes.begin(), attributes.end()); - _colorArray.assign(colorArray.begin(), colorArray.end()); - - _WriteUndeterminedChars(text, attributes, colorArray); -} - -// Routine Description: -// - Writes the final result into the screen buffer through the input queue -// as if the user had inputted it (if their keyboard was able to) -// Arguments: -// - text - The actual text of what the user would like to insert (UTF-16) -void ConsoleImeInfo::WriteResultMessage(const std::wstring_view text) -{ - ClearAllAreas(); - - _InsertConvertedString(text); - - _ClearComposition(); -} - -// Routine Description: -// - Clears internally cached composition data from the last WriteCompMessage call. -void ConsoleImeInfo::_ClearComposition() -{ - _text.clear(); - _attributes.clear(); - _colorArray.clear(); -} - -// Routine Description: -// - Clears out all conversion areas -void ConsoleImeInfo::ClearAllAreas() -{ - for (auto& area : ConvAreaCompStr) - { - if (!area.IsHidden()) - { - area.ClearArea(); - } - } - - // Also clear internal buffer of string data. - _ClearComposition(); -} - -// Routine Description: -// - Resizes all conversion areas to the new dimensions -// Arguments: -// - newSize - New size for conversion areas -// Return Value: -// - S_OK or appropriate failure HRESULT. -[[nodiscard]] HRESULT ConsoleImeInfo::ResizeAllAreas(const til::size newSize) -{ - for (auto& area : ConvAreaCompStr) - { - if (!area.IsHidden()) - { - area.SetHidden(true); - area.Paint(); - } - - RETURN_IF_FAILED(area.Resize(newSize)); - } - - return S_OK; -} - -// Routine Description: -// - Adds another conversion area to the current list of conversion areas (lines) available for IME candidate text -// Arguments: -// - -// Return Value: -// - Status successful or appropriate HRESULT response. -[[nodiscard]] HRESULT ConsoleImeInfo::_AddConversionArea() -{ - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - - auto bufferSize = gci.GetActiveOutputBuffer().GetBufferSize().Dimensions(); - bufferSize.height = 1; - - const auto windowSize = gci.GetActiveOutputBuffer().GetViewport().Dimensions(); - - const auto fill = gci.GetActiveOutputBuffer().GetAttributes(); - - const auto popupFill = gci.GetActiveOutputBuffer().GetPopupAttributes(); - - const auto& fontInfo = gci.GetActiveOutputBuffer().GetCurrentFont(); - - try - { - ConvAreaCompStr.emplace_back(bufferSize, - windowSize, - fill, - popupFill, - fontInfo); - } - CATCH_RETURN(); - - RefreshAreaAttributes(); - - return S_OK; -} - -// Routine Description: -// - Helper method to decode the cursor and color position out of the encoded attributes -// and color array and return it in the TextAttribute structure format -// Arguments: -// - pos - Character position in the string (and matching encoded attributes array) -// - attributes - Encoded attributes holding cursor and color array position -// - colorArray - Colors to choose from -// Return Value: -// - TextAttribute object with color and cursor and line drawing data. -TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos, - const std::span attributes, - const std::span colorArray) -{ - // Encoded attribute is the shorthand information passed from the IME - // that contains a cursor position packed in along with which color in the - // given array should apply to the text. - auto encodedAttribute = attributes[pos]; - - // Legacy attribute is in the color/line format that is understood for drawing - // We use the lower 3 bits (0-7) from the encoded attribute as the array index to start - // creating our legacy attribute. - auto legacyAttribute = colorArray[encodedAttribute & (CONIME_ATTRCOLOR_SIZE - 1)]; - - if (WI_IsFlagSet(encodedAttribute, CONIME_CURSOR_RIGHT)) - { - WI_SetFlag(legacyAttribute, COMMON_LVB_GRID_SINGLEFLAG); - WI_SetFlag(legacyAttribute, COMMON_LVB_GRID_RVERTICAL); - } - else if (WI_IsFlagSet(encodedAttribute, CONIME_CURSOR_LEFT)) - { - WI_SetFlag(legacyAttribute, COMMON_LVB_GRID_SINGLEFLAG); - WI_SetFlag(legacyAttribute, COMMON_LVB_GRID_LVERTICAL); - } - - return TextAttribute(legacyAttribute); -} - -// Routine Description: -// - Converts IME-formatted information into OutputCells to determine what can fit into each -// displayable cell inside the console output buffer. -// Arguments: -// - text - Text data provided by the IME -// - attributes - Encoded color and cursor position data provided by the IME -// - colorArray - Array of color values provided by the IME. -// Return Value: -// - Vector of OutputCells where each one represents one cell of the output buffer. -std::vector ConsoleImeInfo::s_ConvertToCells(const std::wstring_view text, - const std::span attributes, - const std::span colorArray) -{ - std::vector cells; - - // - Walk through all of the grouped up text, match up the correct attribute to it, and make a new cell. - size_t attributesUsed = 0; - for (const auto& parsedGlyph : til::utf16_iterator{ text }) - { - const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() }; - // Collect up attributes that apply to this glyph range. - auto drawingAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray); - attributesUsed++; - - // The IME gave us an attribute for every glyph position in a surrogate pair. - // But the only important information will be the cursor position. - // Check all additional attributes to see if the cursor resides on top of them. - for (size_t i = 1; i < glyph.size(); i++) - { - auto additionalAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray); - attributesUsed++; - if (additionalAttr.IsLeftVerticalDisplayed()) - { - drawingAttr.SetLeftVerticalDisplayed(true); - } - if (additionalAttr.IsRightVerticalDisplayed()) - { - drawingAttr.SetRightVerticalDisplayed(true); - } - } - - // We have to determine if the glyph range is 1 column or two. - // If it's full width, it's two, and we need to make sure we don't draw the cursor - // right down the middle of the character. - // Otherwise it's one column and we'll push it in with the default empty DbcsAttribute. - DbcsAttribute dbcsAttr = DbcsAttribute::Single; - if (IsGlyphFullWidth(glyph)) - { - auto leftHalfAttr = drawingAttr; - auto rightHalfAttr = drawingAttr; - - // Don't draw lines in the middle of full width glyphs. - // If we need a right vertical, don't apply it to the left side of the character - if (leftHalfAttr.IsRightVerticalDisplayed()) - { - leftHalfAttr.SetRightVerticalDisplayed(false); - } - - dbcsAttr = DbcsAttribute::Leading; - cells.emplace_back(glyph, dbcsAttr, leftHalfAttr); - dbcsAttr = DbcsAttribute::Trailing; - - // If we need a left vertical, don't apply it to the right side of the character - if (rightHalfAttr.IsLeftVerticalDisplayed()) - { - rightHalfAttr.SetLeftVerticalDisplayed(false); - } - cells.emplace_back(glyph, dbcsAttr, rightHalfAttr); - } - else - { - cells.emplace_back(glyph, dbcsAttr, drawingAttr); - } - } - - return cells; -} - -// Routine Description: -// - Walks through the cells given and attempts to fill a conversion area line with as much data as can fit. -// - Each conversion area represents one line of the display starting at the cursor position filling to the right edge -// of the display. -// - The first conversion area should be placed from the screen buffer's current cursor position to the right -// edge of the viewport. -// - All subsequent areas should use one entire line of the viewport. -// Arguments: -// - begin - Beginning position in OutputCells for iteration -// - end - Ending position in OutputCells for iteration -// - pos - Reference to the coordinate position in the viewport that this conversion area will occupy. -// - Updated to set up the next conversion area down a line (and to the left viewport edge) -// - view - The rectangle representing the viewable area of the screen right now to let us know how many cells can fit. -// - screenInfo - A reference to the screen information we will use for accessibility notifications -// Return Value: -// - Updated begin position for the next call. It will normally be >begin and <= end. -// However, if text couldn't fit in our line (full-width character starting at the very last cell) -// then we will give back the same begin and update the position for the next call to try again. -// If the viewport is deemed too small, we'll skip past it and advance begin past the entire full-width character. -std::vector::const_iterator ConsoleImeInfo::_WriteConversionArea(const std::vector::const_iterator begin, - const std::vector::const_iterator end, - til::point& pos, - const Microsoft::Console::Types::Viewport view, - SCREEN_INFORMATION& screenInfo) -{ - // The position in the viewport where we will start inserting cells for this conversion area - // NOTE: We might exit early if there's not enough space to fit here, so we take a copy of - // the original and increment it up front. - const auto insertionPos = pos; - - // Advance the cursor position to set up the next call for success (insert the next conversion area - // at the beginning of the following line) - pos.x = view.Left(); - pos.y++; - - // The index of the last column in the viewport. (view is inclusive) - const auto finalViewColumn = view.RightInclusive(); - - // The maximum number of cells we can insert into a line. - const auto lineWidth = finalViewColumn - insertionPos.x + 1; // +1 because view was inclusive - - // The iterator to the beginning position to form our line - const auto lineBegin = begin; - - // The total number of cells we could insert. - const auto size = end - begin; - FAIL_FAST_IF(size <= 0); // It's a programming error to have <= 0 cells to insert. - - // The end is the smaller of the remaining number of cells or the amount of line cells we can write before - // hitting the right edge of the viewport - auto lineEnd = lineBegin + std::min(size, (ptrdiff_t)lineWidth); - - // We must attempt to compensate for ending on a leading byte. We can't split a full-width character across lines. - // As such, if the last item is a leading byte, back the end up by one. - // Get the last cell in the run and if it's a leading byte, move the end position back one so we don't - // try to insert it. - const auto lastCell = lineEnd - 1; - if (lastCell->DbcsAttr() == DbcsAttribute::Leading) - { - lineEnd--; - } - - // GH#12730 - if the lineVec would now be empty, just return early. Failing - // to do so will later cause a crash trying to construct an empty view. - if (lineEnd <= lineBegin) - { - return lineEnd; - } - - // Copy out the substring into a vector. - const std::vector lineVec(lineBegin, lineEnd); - - // Add a conversion area to the internal state to hold this line. - THROW_IF_FAILED(_AddConversionArea()); - - // Get the added conversion area. - auto& area = ConvAreaCompStr.back(); - - // Write our text into the conversion area. - area.WriteText(lineVec, insertionPos.x); - - // Set the viewport and positioning parameters for the conversion area to describe to the renderer - // the appropriate location to overlay this conversion area on top of the main screen buffer inside the viewport. - const til::inclusive_rect region{ insertionPos.x, 0, gsl::narrow(insertionPos.x + lineVec.size() - 1), 0 }; - area.SetWindowInfo(region); - area.SetViewPos({ 0 - view.Left(), insertionPos.y - view.Top() }); - - // Make it visible and paint it. - area.SetHidden(false); - area.Paint(); - - // Notify accessibility that we have updated the text in this display region within the viewport. - if (screenInfo.HasAccessibilityEventing()) - { - screenInfo.NotifyAccessibilityEventing(region.left, insertionPos.y, region.right, insertionPos.y); - } - - // Hand back the iterator representing the end of what we used to be fed into the beginning of the next call. - return lineEnd; -} - -// Routine Description: -// - Takes information from the IME message to write the "undetermined" text to the -// conversion area overlays on the screen. -// - The "undetermined" text represents the word or phrase that the user is currently building -// using the IME. They haven't "determined" what they want yet, so it's "undetermined" right now. -// Arguments: -// - text - View into the text characters provided by the IME. -// - attributes - Attributes specifying which color and cursor positioning information should apply to -// each text character. This view must be the same size as the text view. -// - colorArray - 8 colors to be used to format the text for display -void ConsoleImeInfo::_WriteUndeterminedChars(const std::wstring_view text, - const std::span attributes, - const std::span colorArray) -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - auto& screenInfo = gci.GetActiveOutputBuffer(); - - // Ensure cursor is visible for prompt line - screenInfo.MakeCurrentCursorVisible(); - - // Clear out existing conversion areas. - ConvAreaCompStr.clear(); - - // If the text length and attribute length don't match, - // it's a programming error on our part. We control the sizes here. - FAIL_FAST_IF(text.size() != attributes.size()); - - // If we have no text, return. We've already cleared above. - if (text.empty()) - { - return; - } - - // Convert data-to-be-stored into OutputCells. - const auto cells = s_ConvertToCells(text, attributes, colorArray); - - // Get some starting position information of where to place the conversion areas on top of the existing - // screen buffer and viewport positioning. - // Each conversion area write will adjust these to set up any subsequent calls to go onto the next line. - auto pos = screenInfo.GetTextBuffer().GetCursor().GetPosition(); - // Convert the cursor buffer position to the equivalent screen - // coordinates, taking line rendition into account. - pos = screenInfo.GetTextBuffer().BufferToScreenPosition(pos); - - const auto view = screenInfo.GetViewport(); - // Set cursor position relative to viewport - - // Set up our iterators. We will walk through the entire set of cells from beginning to end. - // The first time, we will give the iterators as the whole span and the begin - // will be moved forward by the conversion area write to set up the next call. - auto begin = cells.cbegin(); - const auto end = cells.cend(); - - // Write over and over updating the beginning iterator until we reach the end. - do - { - begin = _WriteConversionArea(begin, end, pos, view, screenInfo); - } while (begin < end); -} - -// Routine Description: -// - Takes the final text string and injects it into the input buffer -// Arguments: -// - text - The text to inject into the input buffer -void ConsoleImeInfo::_InsertConvertedString(const std::wstring_view text) -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - - auto& screenInfo = gci.GetActiveOutputBuffer(); - if (screenInfo.GetTextBuffer().GetCursor().IsOn()) - { - gci.GetCursorBlinker().TimerRoutine(screenInfo); - } - - const auto dwControlKeyState = GetControlKeyState(0); - InputEventQueue inEvents; - auto keyEvent = SynthesizeKeyEvent(true, 1, 0, 0, 0, dwControlKeyState); - - for (const auto& ch : text) - { - keyEvent.Event.KeyEvent.uChar.UnicodeChar = ch; - inEvents.push_back(keyEvent); - } - - gci.pInputBuffer->Write(inEvents); -} - -// Routine Description: -// - Backs up the global cursor visibility state if it is shown and disables -// it while we work on the conversion areas. -void ConsoleImeInfo::SaveCursorVisibility() -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - auto& cursor = gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor(); - - // Cursor turn OFF. - if (cursor.IsVisible()) - { - _isSavedCursorVisible = true; - - cursor.SetIsVisible(false); - } -} - -// Routine Description: -// - Restores the global cursor visibility state if it was on when it was backed up. -void ConsoleImeInfo::RestoreCursorVisibility() -{ - if (_isSavedCursorVisible) - { - _isSavedCursorVisible = false; - - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - auto& cursor = gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor(); - - cursor.SetIsVisible(true); - } -} diff --git a/src/host/conimeinfo.h b/src/host/conimeinfo.h deleted file mode 100644 index 81ea359d8a5..00000000000 --- a/src/host/conimeinfo.h +++ /dev/null @@ -1,91 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- conimeinfo.h - -Abstract: -- This module contains the structures for the console IME entrypoints - for overall control - -Author: -- Michael Niksa (MiNiksa) 10-May-2018 - -Revision History: -- From pieces of convarea.cpp originally authored by KazuM ---*/ - -#pragma once - -#include "../inc/conime.h" -#include "../buffer/out/OutputCell.hpp" -#include "../buffer/out/TextAttribute.hpp" -#include "../renderer/inc/FontInfo.hpp" -#include "../types/inc/viewport.hpp" - -#include "conareainfo.h" - -class SCREEN_INFORMATION; - -class ConsoleImeInfo final -{ -public: - // IME composition string information - // There is one "composition string" per line that must be rendered on the screen - std::vector ConvAreaCompStr; - - ConsoleImeInfo(); - ~ConsoleImeInfo() = default; - ConsoleImeInfo(const ConsoleImeInfo&) = delete; - ConsoleImeInfo(ConsoleImeInfo&&) = delete; - ConsoleImeInfo& operator=(const ConsoleImeInfo&) & = delete; - ConsoleImeInfo& operator=(ConsoleImeInfo&&) & = delete; - - void RefreshAreaAttributes(); - void ClearAllAreas(); - - [[nodiscard]] HRESULT ResizeAllAreas(const til::size newSize); - - void WriteCompMessage(const std::wstring_view text, - const std::span attributes, - const std::span colorArray); - - void WriteResultMessage(const std::wstring_view text); - - void RedrawCompMessage(); - - void SaveCursorVisibility(); - void RestoreCursorVisibility(); - -private: - [[nodiscard]] HRESULT _AddConversionArea(); - - void _ClearComposition(); - - void _WriteUndeterminedChars(const std::wstring_view text, - const std::span attributes, - const std::span colorArray); - - void _InsertConvertedString(const std::wstring_view text); - - static TextAttribute s_RetrieveAttributeAt(const size_t pos, - const std::span attributes, - const std::span colorArray); - - static std::vector s_ConvertToCells(const std::wstring_view text, - const std::span attributes, - const std::span colorArray); - - std::vector::const_iterator _WriteConversionArea(const std::vector::const_iterator begin, - const std::vector::const_iterator end, - til::point& pos, - const Microsoft::Console::Types::Viewport view, - SCREEN_INFORMATION& screenInfo); - - bool _isSavedCursorVisible; - - std::wstring _text; - std::vector _attributes; - std::vector _colorArray; -}; diff --git a/src/host/consoleInformation.cpp b/src/host/consoleInformation.cpp index 074bd56911f..12e8c5e5b55 100644 --- a/src/host/consoleInformation.cpp +++ b/src/host/consoleInformation.cpp @@ -102,8 +102,6 @@ ULONG CONSOLE_INFORMATION::GetCSRecursionCount() const noexcept gci.GetActiveOutputBuffer().ScrollScale = gci.GetScrollScale(); - gci.ConsoleIme.RefreshAreaAttributes(); - if (SUCCEEDED_NTSTATUS(Status)) { return STATUS_SUCCESS; diff --git a/src/host/conv.h b/src/host/conv.h deleted file mode 100644 index fb1a02395e4..00000000000 --- a/src/host/conv.h +++ /dev/null @@ -1,29 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- conv.h - -Abstract: -- This module contains the internal structures and definitions used by the conversion area. -- "Conversion area" refers to either the in-line area where the text color changes and suggests options in IME-based languages - or to the reserved line at the bottom of the screen offering suggestions and the current IME mode. - -Author: -- KazuM March 8, 1993 - -Revision History: ---*/ - -#pragma once - -#include "server.h" - -#include "../types/inc/Viewport.hpp" - -void WriteConvRegionToScreen(const SCREEN_INFORMATION& ScreenInfo, - const Microsoft::Console::Types::Viewport& convRegion); - -[[nodiscard]] HRESULT ConsoleImeResizeCompStrView(); -[[nodiscard]] HRESULT ConsoleImeResizeCompStrScreenBuffer(const til::size coordNewScreenSize); diff --git a/src/host/convarea.cpp b/src/host/convarea.cpp deleted file mode 100644 index 62c9796a56f..00000000000 --- a/src/host/convarea.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "_output.h" - -#include "../interactivity/inc/ServiceLocator.hpp" - -#pragma hdrstop - -using namespace Microsoft::Console::Types; -using Microsoft::Console::Interactivity::ServiceLocator; - -void WriteConvRegionToScreen(const SCREEN_INFORMATION& ScreenInfo, - const Viewport& convRegion) -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - if (!ScreenInfo.IsActiveScreenBuffer()) - { - return; - } - - const auto pIme = &gci.ConsoleIme; - - for (unsigned int i = 0; i < pIme->ConvAreaCompStr.size(); ++i) - { - const auto& ConvAreaInfo = pIme->ConvAreaCompStr[i]; - - if (!ConvAreaInfo.IsHidden()) - { - const auto currentViewport = ScreenInfo.GetViewport().ToInclusive(); - const auto areaInfo = ConvAreaInfo.GetAreaBufferInfo(); - - // Do clipping region - til::inclusive_rect Region; - Region.left = currentViewport.left + areaInfo.rcViewCaWindow.left + areaInfo.coordConView.x; - Region.right = Region.left + (areaInfo.rcViewCaWindow.right - areaInfo.rcViewCaWindow.left); - Region.top = currentViewport.top + areaInfo.rcViewCaWindow.top + areaInfo.coordConView.y; - Region.bottom = Region.top + (areaInfo.rcViewCaWindow.bottom - areaInfo.rcViewCaWindow.top); - - Region.left = std::max(Region.left, currentViewport.left); - Region.top = std::max(Region.top, currentViewport.top); - Region.right = std::min(Region.right, currentViewport.right); - Region.bottom = std::min(Region.bottom, currentViewport.bottom); - - if (Region) - { - Region.left = std::max(Region.left, convRegion.Left()); - Region.top = std::max(Region.top, convRegion.Top()); - Region.right = std::min(Region.right, convRegion.RightInclusive()); - Region.bottom = std::min(Region.bottom, convRegion.BottomInclusive()); - if (Region) - { - // if we have a renderer, we need to update. - // we've already confirmed (above with an early return) that we're on conversion areas that are a part of the active (visible/rendered) screen - // so send invalidates to those regions such that we're queried for data on the next frame and repainted. - if (ServiceLocator::LocateGlobals().pRender != nullptr) - { - ServiceLocator::LocateGlobals().pRender->TriggerRedraw(Viewport::FromInclusive(Region)); - } - } - } - } - } -} - -[[nodiscard]] HRESULT ConsoleImeResizeCompStrView() -{ - try - { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto pIme = &gci.ConsoleIme; - pIme->RedrawCompMessage(); - } - CATCH_RETURN(); - - return S_OK; -} - -[[nodiscard]] HRESULT ConsoleImeResizeCompStrScreenBuffer(const til::size coordNewScreenSize) -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto pIme = &gci.ConsoleIme; - - return pIme->ResizeAllAreas(coordNewScreenSize); -} - -[[nodiscard]] HRESULT ImeStartComposition() -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); - auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); - - // MSFT:29219348 Some IME implementations do not produce composition strings, and - // their users have come to rely on the cursor that conhost traditionally left on - // until a composition string showed up. - // One such IME is WNWB's "Universal Wubi input method" from wnwb.com (v. 10+). - // We shouldn't hide the cursor here so as to not break those IMEs. - - gci.pInputBuffer->fInComposition = true; - return S_OK; -} - -[[nodiscard]] HRESULT ImeEndComposition() -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); - auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); - - const auto pIme = &gci.ConsoleIme; - pIme->RestoreCursorVisibility(); - - gci.pInputBuffer->fInComposition = false; - return S_OK; -} - -[[nodiscard]] HRESULT ImeComposeData(std::wstring_view text, - std::span attributes, - std::span colorArray) -{ - try - { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); - auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); - - const auto pIme = &gci.ConsoleIme; - pIme->WriteCompMessage(text, attributes, colorArray); - } - CATCH_RETURN(); - return S_OK; -} - -[[nodiscard]] HRESULT ImeClearComposeData() -{ - try - { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); - auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); - - const auto pIme = &gci.ConsoleIme; - pIme->ClearAllAreas(); - } - CATCH_RETURN(); - return S_OK; -} - -[[nodiscard]] HRESULT ImeComposeResult(std::wstring_view text) -{ - try - { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); - auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); - - const auto pIme = &gci.ConsoleIme; - pIme->WriteResultMessage(text); - } - CATCH_RETURN(); - return S_OK; -} diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 304b4fd65c7..3595da335cf 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -717,8 +717,6 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont RETURN_IF_NTSTATUS_FAILED(buffer.SetCursorPosition(position, true)); - LOG_IF_FAILED(ConsoleImeResizeCompStrView()); - // Attempt to "snap" the viewport to the cursor position. If the cursor // is not in the current viewport, we'll try and move the viewport so // that the cursor is visible. @@ -976,7 +974,6 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont [[nodiscard]] HRESULT ApiRoutines::SetConsoleTextAttributeImpl(SCREEN_INFORMATION& context, const WORD attribute) noexcept { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); try { LockConsole(); @@ -987,8 +984,6 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont const TextAttribute attr{ attribute }; context.SetAttributes(attr); - gci.ConsoleIme.RefreshAreaAttributes(); - return S_OK; } CATCH_RETURN(); diff --git a/src/host/globals.h b/src/host/globals.h index 9cebbc0808a..812bf059c41 100644 --- a/src/host/globals.h +++ b/src/host/globals.h @@ -25,6 +25,7 @@ Revision History: #include "../propslib/DelegationConfig.hpp" #include "../renderer/base/Renderer.hpp" #include "../server/DeviceComm.h" +#include "../tsf/Handle.h" #include "../server/ConDrvDeviceComm.h" #include @@ -60,9 +61,8 @@ class Globals DWORD dwInputThreadId; std::vector WordDelimiters; - Microsoft::Console::Render::Renderer* pRender; - + Microsoft::Console::TSF::Handle tsf; Microsoft::Console::Render::IFontDefaultList* pFontDefaultList; bool IsHeadless() const; diff --git a/src/host/host-common.vcxitems b/src/host/host-common.vcxitems index 3456180ff1f..9e51ab38c88 100644 --- a/src/host/host-common.vcxitems +++ b/src/host/host-common.vcxitems @@ -6,10 +6,7 @@ - - - @@ -58,11 +55,8 @@ - - - diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index 2e28a17ef67..a843a01082a 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -27,8 +27,6 @@ using namespace Microsoft::Console; InputBuffer::InputBuffer() : InputMode{ INPUT_BUFFER_DEFAULT_INPUT_MODE } { - // initialize buffer header - fInComposition = false; } // Transfer as many `wchar_t`s from source over to the `char`/`wchar_t` buffer `target`. After it returns, diff --git a/src/host/inputBuffer.hpp b/src/host/inputBuffer.hpp index 019c6e628bd..653f04e36f1 100644 --- a/src/host/inputBuffer.hpp +++ b/src/host/inputBuffer.hpp @@ -23,7 +23,6 @@ class InputBuffer final : public ConsoleObjectHeader public: DWORD InputMode; ConsoleWaitQueue WaitQueue; // formerly ReadWaitQueue - bool fInComposition; // specifies if there's an ongoing text composition InputBuffer(); diff --git a/src/host/lib/hostlib.vcxproj.filters b/src/host/lib/hostlib.vcxproj.filters index c70ea1a4d9f..a2e17a06cd9 100644 --- a/src/host/lib/hostlib.vcxproj.filters +++ b/src/host/lib/hostlib.vcxproj.filters @@ -27,9 +27,6 @@ Source Files - - Source Files - Source Files @@ -105,9 +102,6 @@ Source Files - - Source Files - Source Files @@ -150,9 +144,6 @@ Source Files - - Source Files - Source Files @@ -191,9 +182,6 @@ Header Files - - Header Files - Header Files @@ -215,15 +203,9 @@ Header Files - - Header Files - Header Files - - Header Files - Header Files @@ -299,9 +281,6 @@ Header Files - - Header Files - Header Files @@ -313,4 +292,4 @@ - + \ No newline at end of file diff --git a/src/host/output.cpp b/src/host/output.cpp index bf1e0a91040..1958e0671a4 100644 --- a/src/host/output.cpp +++ b/src/host/output.cpp @@ -464,8 +464,6 @@ void SetActiveScreenBuffer(SCREEN_INFORMATION& screenInfo) // Set window size. screenInfo.PostUpdateWindowSize(); - gci.ConsoleIme.RefreshAreaAttributes(); - // Write data to screen. WriteToScreen(screenInfo, screenInfo.GetViewport()); } diff --git a/src/host/precomp.h b/src/host/precomp.h index e28931ebaf4..11787fc7b10 100644 --- a/src/host/precomp.h +++ b/src/host/precomp.h @@ -48,8 +48,6 @@ Module Name: #include #include "conserv.h" -#include "conv.h" - #pragma prefast(push) #pragma prefast(disable : 26071, "Range violation in Intsafe. Not ours.") #define ENABLE_INTSAFE_SIGNED_FUNCTIONS // Only unsigned intsafe math/casts available without this def @@ -84,7 +82,6 @@ TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider); #define CON_DPIAPI_INDIRECT #endif -#include "../inc/contsf.h" #include "../inc/conattrs.hpp" // TODO: MSFT 9355094 Find a better way of doing this. http://osgvsowi/9355094 diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index c2ad7e99d5f..f242f2753f0 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -42,9 +42,9 @@ til::point RenderData::GetTextBufferEndPosition() const noexcept // the appropriate windowing. // Return Value: // - Text buffer with cell information for display -const TextBuffer& RenderData::GetTextBuffer() const noexcept +TextBuffer& RenderData::GetTextBuffer() const noexcept { - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); return gci.GetActiveOutputBuffer().GetTextBuffer(); } @@ -210,47 +210,6 @@ ULONG RenderData::GetCursorPixelWidth() const noexcept return ServiceLocator::LocateGlobals().cursorPixelWidth; } -// Routine Description: -// - Retrieves overlays to be drawn on top of the main screen buffer area. -// - Overlays are drawn from first to last -// (the highest overlay should be given last) -// Return Value: -// - Iterable set of overlays -const std::vector RenderData::GetOverlays() const noexcept -{ - std::vector overlays; - - try - { - // First retrieve the IME information and build overlays. - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto& ime = gci.ConsoleIme; - - for (const auto& composition : ime.ConvAreaCompStr) - { - // Only send the overlay to the renderer on request if it's not supposed to be hidden at this moment. - if (!composition.IsHidden()) - { - // This is holding the data. - const auto& textBuffer = composition.GetTextBuffer(); - - // The origin of the text buffer above (top left corner) is supposed to sit at this - // point within the visible viewport of the current window. - const auto origin = composition.GetAreaBufferInfo().coordConView; - - // This is the area of the viewport that is actually in use relative to the text buffer itself. - // (e.g. 0,0 is the origin of the text buffer above, not the placement within the visible viewport) - const auto used = Viewport::FromInclusive(composition.GetAreaBufferInfo().rcViewCaWindow); - - overlays.emplace_back(Microsoft::Console::Render::RenderOverlay{ textBuffer, origin, used }); - } - } - } - CATCH_LOG(); - - return overlays; -} - // Method Description: // - Returns true if the cursor should be drawn twice as wide as usual because // the cursor is currently over a cell with a double-wide character in it. diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index db879fb54e5..839366b4949 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -22,7 +22,7 @@ class RenderData final : public: Microsoft::Console::Types::Viewport GetViewport() noexcept override; til::point GetTextBufferEndPosition() const noexcept override; - const TextBuffer& GetTextBuffer() const noexcept override; + TextBuffer& GetTextBuffer() const noexcept override; const FontInfo& GetFontInfo() const noexcept override; std::vector GetSelectionRects() noexcept override; @@ -38,8 +38,6 @@ class RenderData final : ULONG GetCursorPixelWidth() const noexcept override; bool IsCursorDoubleWidth() const override; - const std::vector GetOverlays() const noexcept override; - const bool IsGridLineDrawingAllowed() noexcept override; const std::wstring_view GetConsoleTitle() const noexcept override; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 9b0ff2ca4e4..daaedf87d7a 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -40,7 +40,6 @@ SCREEN_INFORMATION::SCREEN_INFORMATION( Next{ nullptr }, WriteConsoleDbcsLeadByte{ 0, 0 }, FillOutDbcsLeadChar{ 0 }, - ConvScreenInfo{ nullptr }, ScrollScale{ 1ul }, _pConsoleWindowMetrics{ pMetrics }, _pAccessibilityNotifier{ pNotifier }, @@ -1496,15 +1495,6 @@ NT_CATCH_RETURN() NotifyAccessibilityEventing(0, 0, coordNewScreenSize.width - 1, coordNewScreenSize.height - 1); } - if ((!ConvScreenInfo)) - { - if (FAILED(ConsoleImeResizeCompStrScreenBuffer(coordNewScreenSize))) - { - // If something went wrong, just bail out. - return STATUS_INVALID_HANDLE; - } - } - // Fire off an event to let accessibility apps know the layout has changed. if (_pAccessibilityNotifier && IsActiveScreenBuffer()) { @@ -2114,8 +2104,6 @@ void SCREEN_INFORMATION::SetDefaultAttributes(const TextAttribute& attributes, _textBuffer->TriggerRedrawAll(); } - gci.ConsoleIme.RefreshAreaAttributes(); - // If we're an alt buffer, also update our main buffer. if (_psiMainBuffer) { diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index c0351050566..035529e0d6e 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -176,9 +176,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console BYTE WriteConsoleDbcsLeadByte[2]; BYTE FillOutDbcsLeadChar; - // non ownership pointer - ConversionAreaInfo* ConvScreenInfo; - UINT ScrollScale; bool IsActiveScreenBuffer() const; diff --git a/src/host/server.h b/src/host/server.h index cfa1ba14dc6..0cf24ef42ff 100644 --- a/src/host/server.h +++ b/src/host/server.h @@ -16,7 +16,6 @@ Revision History: #pragma once -#include "conimeinfo.h" #include "CursorBlinker.hpp" #include "IIoProvider.hpp" #include "readDataCooked.hpp" @@ -98,8 +97,6 @@ class CONSOLE_INFORMATION : CPINFO CPInfo = {}; CPINFO OutputCPInfo = {}; - ConsoleImeInfo ConsoleIme; - void LockConsole() noexcept; void UnlockConsole() noexcept; til::recursive_ticket_lock_suspension SuspendLock() noexcept; diff --git a/src/host/sources.inc b/src/host/sources.inc index 0c6b8fd14d2..adf9ee9fb4f 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -67,7 +67,6 @@ SOURCES = \ ..\outputStream.cpp \ ..\stream.cpp \ ..\dbcs.cpp \ - ..\convarea.cpp \ ..\screenInfo.cpp \ ..\_output.cpp \ ..\_stream.cpp \ @@ -83,8 +82,6 @@ SOURCES = \ ..\writeData.cpp \ ..\renderData.cpp \ ..\renderFontDefaults.cpp \ - ..\conareainfo.cpp \ - ..\conimeinfo.cpp \ ..\ConsoleArguments.cpp \ diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index dbabcc5a280..4644a4deb5a 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -259,7 +259,7 @@ class MockRenderData : public IRenderData return {}; } - const TextBuffer& GetTextBuffer() const noexcept override + TextBuffer& GetTextBuffer() const noexcept override { FAIL_FAST_HR(E_NOTIMPL); } @@ -322,11 +322,6 @@ class MockRenderData : public IRenderData return false; } - const std::vector GetOverlays() const noexcept override - { - return std::vector{}; - } - const bool IsGridLineDrawingAllowed() noexcept override { return false; diff --git a/src/inc/conime.h b/src/inc/conime.h deleted file mode 100644 index 96ca7c3e0b6..00000000000 --- a/src/inc/conime.h +++ /dev/null @@ -1,58 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - conime.h - -Abstract: - - This module contains the internal structures and definitions used - by the console IME. - -Author: - - v-HirShi Jul.4.1995 - -Revision History: - ---*/ - -#pragma once - -constexpr unsigned short CONIME_ATTRCOLOR_SIZE = 8; - -constexpr BYTE CONIME_CURSOR_RIGHT = 0x10; -constexpr BYTE CONIME_CURSOR_LEFT = 0x20; - -[[nodiscard]] HRESULT ImeStartComposition(); - -[[nodiscard]] HRESULT ImeEndComposition(); - -[[nodiscard]] HRESULT ImeComposeData(std::wstring_view text, - std::span attributes, - std::span colorArray); - -[[nodiscard]] HRESULT ImeClearComposeData(); - -[[nodiscard]] HRESULT ImeComposeResult(std::wstring_view text); - -// Default composition color attributes -#define DEFAULT_COMP_ENTERED \ - (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | \ - COMMON_LVB_UNDERSCORE) -#define DEFAULT_COMP_ALREADY_CONVERTED \ - (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | \ - BACKGROUND_BLUE) -#define DEFAULT_COMP_CONVERSION \ - (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | \ - COMMON_LVB_UNDERSCORE) -#define DEFAULT_COMP_YET_CONVERTED \ - (FOREGROUND_BLUE | \ - BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | \ - COMMON_LVB_UNDERSCORE) -#define DEFAULT_COMP_INPUT_ERROR \ - (FOREGROUND_RED | \ - COMMON_LVB_UNDERSCORE) diff --git a/src/inc/contsf.h b/src/inc/contsf.h deleted file mode 100644 index 07594e104eb..00000000000 --- a/src/inc/contsf.h +++ /dev/null @@ -1,40 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - contsf.h - -Abstract: - - This module contains the internal structures and definitions used - by the console IME. - -Author: - - v-HirShi Jul.4.1995 - -Revision History: - ---*/ - -#pragma once - -#include "conime.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef RECT (*GetSuggestionWindowPos)(); -typedef RECT (*GetTextBoxAreaPos)(); - -BOOL ActivateTextServices(HWND hwndConsole, GetSuggestionWindowPos pfnPosition, GetTextBoxAreaPos pfnTextArea); -void DeactivateTextServices(); -BOOL NotifyTextServices(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* lplResult); - -#ifdef __cplusplus -} -#endif diff --git a/src/interactivity/win32/CustomWindowMessages.h b/src/interactivity/win32/CustomWindowMessages.h index c203df2cac4..bf366602499 100644 --- a/src/interactivity/win32/CustomWindowMessages.h +++ b/src/interactivity/win32/CustomWindowMessages.h @@ -15,7 +15,7 @@ // unused (CM_CONSOLE_SHUTDOWN) (WM_USER + 7) // unused (CM_HIDE_WINDOW) (WM_USER + 8) #define CM_CONIME_CREATE (WM_USER+9) -#define CM_SET_CONSOLEIME_WINDOW (WM_USER+10) +// unused #define CM_SET_CONSOLEIME_WINDOW (WM_USER+10) #define CM_WAIT_CONIME_PROCESS (WM_USER+11) // unused CM_SET_IME_CODEPAGE (WM_USER+12) // unused CM_SET_NLSMODE (WM_USER+13) diff --git a/src/interactivity/win32/WindowIme.cpp b/src/interactivity/win32/WindowIme.cpp deleted file mode 100644 index f66338d7de3..00000000000 --- a/src/interactivity/win32/WindowIme.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "../inc/ServiceLocator.hpp" - -#include "window.hpp" - -#pragma hdrstop - -// Routine Description: -// - This method gives a rectangle to where the command edit line text is currently rendered -// such that the IME suggestion window can pop up in a suitable location adjacent to the given rectangle. -// Arguments: -// - -// Return Value: -// - Rectangle specifying current command line edit area. -RECT GetImeSuggestionWindowPos() -{ - using Microsoft::Console::Interactivity::ServiceLocator; - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto& screenBuffer = gci.GetActiveOutputBuffer(); - - const auto coordFont = screenBuffer.GetCurrentFont().GetSize(); - auto coordCursor = screenBuffer.GetTextBuffer().GetCursor().GetPosition(); - - // Adjust the cursor position to be relative to the viewport. - // This means that if the cursor is at row 30 in the buffer but the viewport is showing rows 20-40 right now on screen - // that the "relative" position is that it is on the 11th line from the top (or 10th by index). - // Correct by subtracting the top/left corner from the cursor's position. - const auto srViewport = screenBuffer.GetViewport().ToInclusive(); - coordCursor.x -= srViewport.left; - coordCursor.y -= srViewport.top; - - // Map the point to be just under the current cursor position. Convert from coordinate to pixels using font. - POINT ptSuggestion; - ptSuggestion.x = (coordCursor.x + 1) * coordFont.width; - ptSuggestion.y = (coordCursor.y) * coordFont.height; - - // Adjust client point to screen point via HWND. - ClientToScreen(ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), &ptSuggestion); - - // Move into suggestion rectangle. - RECT rcSuggestion{}; - rcSuggestion.top = rcSuggestion.bottom = ptSuggestion.y; - rcSuggestion.left = rcSuggestion.right = ptSuggestion.x; - - // Add 1 line height and a few characters of width to represent the area where we're writing text. - // This could be more exact by looking up the CONVAREA but it works well enough this way. - // If there is a future issue with the pop-up window, tweak these metrics. - rcSuggestion.bottom += coordFont.height; - rcSuggestion.right += (coordFont.width * 10); - - return rcSuggestion; -} - -// Routine Description: -// - This method gives a rectangle to where text box is currently rendered -// such that the touch keyboard can pop up when the rectangle is tapped. -// Arguments: -// - -// Return Value: -// - Rectangle specifying current text box area. -RECT GetTextBoxArea() -{ - return Microsoft::Console::Interactivity::ServiceLocator::LocateConsoleWindow()->GetWindowRect().to_win32_rect(); -} diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj b/src/interactivity/win32/lib/win32.LIB.vcxproj index 1988841434b..6d6df33f5f8 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj @@ -32,7 +32,6 @@ - @@ -54,7 +53,6 @@ - diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters index 52ecd14f5b7..afe3fd0fba3 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters @@ -48,9 +48,6 @@ Source Files - - Source Files - Source Files @@ -110,9 +107,6 @@ Header Files - - Header Files - Header Files diff --git a/src/interactivity/win32/menu.cpp b/src/interactivity/win32/menu.cpp index 44e803931ff..e9736728a74 100644 --- a/src/interactivity/win32/menu.cpp +++ b/src/interactivity/win32/menu.cpp @@ -595,8 +595,6 @@ void Menu::s_PropertiesUpdate(PCONSOLE_STATE_INFO pStateInfo) // those properties specifically from the registry in case they were changed. ServiceLocator::LocateConsoleWindow()->PostUpdateExtendedEditKeys(); - gci.ConsoleIme.RefreshAreaAttributes(); - gci.SetInterceptCopyPaste(!!pStateInfo->InterceptCopyPaste); } diff --git a/src/interactivity/win32/sources.inc b/src/interactivity/win32/sources.inc index 3580a5d6186..f816c285db0 100644 --- a/src/interactivity/win32/sources.inc +++ b/src/interactivity/win32/sources.inc @@ -50,7 +50,6 @@ SOURCES = \ ..\UiaTextRange.cpp \ ..\window.cpp \ ..\windowdpiapi.cpp \ - ..\windowime.cpp \ ..\windowio.cpp \ ..\WindowMetrics.cpp \ ..\windowproc.cpp \ diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp index 3ef07add747..ed42acb60d5 100644 --- a/src/interactivity/win32/window.cpp +++ b/src/interactivity/win32/window.cpp @@ -334,8 +334,6 @@ void Window::_UpdateSystemMetrics() const if (SUCCEEDED_NTSTATUS(status)) { - gci.ConsoleIme.RefreshAreaAttributes(); - // Do WM_GETICON workaround. Must call WM_SETICON once or apps calling WM_GETICON will get null. LOG_IF_FAILED(Icon::Instance().ApplyWindowMessageWorkaround(hWnd)); @@ -455,8 +453,6 @@ void Window::ChangeViewport(const til::inclusive_rect& NewWindow) Tracing::s_TraceWindowViewport(ScreenInfo.GetViewport()); } - LOG_IF_FAILED(ConsoleImeResizeCompStrView()); - ScreenInfo.UpdateScrollBars(); } @@ -686,8 +682,6 @@ void Window::_UpdateWindowSize(const til::size sizeNew) _resizingWindow--; } - LOG_IF_FAILED(ConsoleImeResizeCompStrView()); - return STATUS_SUCCESS; } diff --git a/src/interactivity/win32/windowime.hpp b/src/interactivity/win32/windowime.hpp deleted file mode 100644 index dc7e6d241dc..00000000000 --- a/src/interactivity/win32/windowime.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#pragma hdrstop - -RECT GetImeSuggestionWindowPos(); -RECT GetTextBoxArea(); diff --git a/src/interactivity/win32/windowio.cpp b/src/interactivity/win32/windowio.cpp index 94d7f3b2a86..261ed4a80c8 100644 --- a/src/interactivity/win32/windowio.cpp +++ b/src/interactivity/win32/windowio.cpp @@ -2,22 +2,16 @@ // Licensed under the MIT license. #include "precomp.h" - #include "windowio.hpp" -#include "ConsoleControl.hpp" -#include "find.h" #include "clipboard.hpp" +#include "ConsoleControl.hpp" #include "consoleKeyInfo.hpp" +#include "find.h" #include "window.hpp" - -#include "../../host/ApiRoutines.h" -#include "../../host/init.hpp" -#include "../../host/input.h" #include "../../host/handle.h" +#include "../../host/init.hpp" #include "../../host/scrolling.hpp" -#include "../../host/output.h" - #include "../inc/ServiceLocator.hpp" #pragma hdrstop @@ -125,7 +119,8 @@ void HandleKeyEvent(const HWND hWnd, const LPARAM lParam, _Inout_opt_ PBOOL pfUnlockConsole) { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& g = ServiceLocator::LocateGlobals(); + auto& gci = g.getConsoleInformation(); // BOGUS for WM_CHAR/WM_DEADCHAR, in which LOWORD(wParam) is a character auto VirtualKeyCode = LOWORD(wParam); @@ -153,7 +148,7 @@ void HandleKeyEvent(const HWND hWnd, RetrieveKeyInfo(hWnd, &VirtualKeyCode, &VirtualScanCode, - !gci.pInputBuffer->fInComposition); + !g.tsf.HasActiveComposition()); // --- END LOAD BEARING CODE --- } @@ -363,7 +358,7 @@ void HandleKeyEvent(const HWND hWnd, return; } - if (gci.pInputBuffer->fInComposition) + if (g.tsf.HasActiveComposition()) { return; } @@ -1003,9 +998,6 @@ DWORD WINAPI ConsoleInputThreadProcWin32(LPVOID /*lpParameter*/) // -- END LOAD BEARING CODE } - // Free all resources used by this thread - DeactivateTextServices(); - if (nullptr != hhook) { UnhookWindowsHookEx(hhook); diff --git a/src/interactivity/win32/windowproc.cpp b/src/interactivity/win32/windowproc.cpp index 28202eebc9f..0dc8cf3853b 100644 --- a/src/interactivity/win32/windowproc.cpp +++ b/src/interactivity/win32/windowproc.cpp @@ -2,41 +2,116 @@ // Licensed under the MIT license. #include "precomp.h" +#include "window.hpp" -#include "Clipboard.hpp" -#include "ConsoleControl.hpp" +#include "clipboard.hpp" #include "find.h" #include "menu.hpp" -#include "window.hpp" #include "windowdpiapi.hpp" -#include "windowime.hpp" #include "windowio.hpp" #include "windowmetrics.hpp" - -#include "../../host/_output.h" -#include "../../host/output.h" -#include "../../host/dbcs.h" #include "../../host/handle.h" -#include "../../host/input.h" -#include "../../host/misc.h" #include "../../host/registry.hpp" #include "../../host/scrolling.hpp" -#include "../../host/srvinit.h" - -#include "../inc/ServiceLocator.hpp" - #include "../../inc/conint.h" - +#include "../inc/ServiceLocator.hpp" #include "../interactivity/win32/CustomWindowMessages.h" - #include "../interactivity/win32/windowUiaProvider.hpp" -#include -#include - using namespace Microsoft::Console::Interactivity::Win32; using namespace Microsoft::Console::Types; +// NOTE: We put this struct into a `static constexpr` (= ".rodata", read-only data segment), which means it +// cannot have any mutable members right now. If you need any, you have to make it a non-const `static`. +struct TsfDataProvider : Microsoft::Console::TSF::IDataProvider +{ + virtual ~TsfDataProvider() = default; + + STDMETHODIMP TsfDataProvider::QueryInterface(REFIID, void**) noexcept override + { + return E_NOTIMPL; + } + + ULONG STDMETHODCALLTYPE TsfDataProvider::AddRef() noexcept override + { + return 1; + } + + ULONG STDMETHODCALLTYPE TsfDataProvider::Release() noexcept override + { + return 1; + } + + HWND GetHwnd() override + { + return Microsoft::Console::Interactivity::ServiceLocator::LocateConsoleWindow()->GetWindowHandle(); + } + + RECT GetViewport() override + { + const auto hwnd = GetHwnd(); + + RECT rc; + GetClientRect(hwnd, &rc); + + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect + // > The left and top members are zero. The right and bottom members contain the width and height of the window. + // --> We can turn the client rect into a screen-relative rect by adding the left/top position. + ClientToScreen(hwnd, reinterpret_cast(&rc)); + rc.right += rc.left; + rc.bottom += rc.top; + + return rc; + } + + RECT GetCursorPosition() override + { + const auto& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); + const auto& screenBuffer = gci.GetActiveOutputBuffer(); + + // Map the absolute cursor position to a viewport-relative one. + const auto viewport = screenBuffer.GetViewport().ToExclusive(); + auto coordCursor = screenBuffer.GetTextBuffer().GetCursor().GetPosition(); + coordCursor.x -= viewport.left; + coordCursor.y -= viewport.top; + + coordCursor.x = std::clamp(coordCursor.x, 0, viewport.width() - 1); + coordCursor.y = std::clamp(coordCursor.y, 0, viewport.height() - 1); + + // Convert from columns/rows to pixels. + const auto coordFont = screenBuffer.GetCurrentFont().GetSize(); + POINT ptSuggestion{ + .x = coordCursor.x * coordFont.width, + .y = coordCursor.y * coordFont.height, + }; + + ClientToScreen(GetHwnd(), &ptSuggestion); + + return { + .left = ptSuggestion.x, + .top = ptSuggestion.y, + .right = ptSuggestion.x + coordFont.width, + .bottom = ptSuggestion.y + coordFont.height, + }; + } + + void HandleOutput(std::wstring_view text) override + { + auto& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); + gci.LockConsole(); + const auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + gci.GetActiveInputBuffer()->WriteString(text); + } + + Microsoft::Console::Render::Renderer* GetRenderer() override + { + auto& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); + return g.pRender; + } +}; + +static constexpr TsfDataProvider s_tsfDataProvider; + // The static and specific window procedures for this class are contained here #pragma region Window Procedure @@ -256,8 +331,11 @@ using namespace Microsoft::Console::Types; HandleFocusEvent(TRUE); - // ActivateTextServices does nothing if already active so this is OK to be called every focus. - ActivateTextServices(ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), GetImeSuggestionWindowPos, GetTextBoxArea); + if (!g.tsf) + { + g.tsf = TSF::Handle::Create(); + g.tsf.AssociateFocus(const_cast(&s_tsfDataProvider)); + } // set the text area to have focus for accessibility consumers if (_pUiaProvider) @@ -855,7 +933,9 @@ void Window::_HandleWindowPosChanged(const LPARAM lParam) // - void Window::_HandleDrop(const WPARAM wParam) const { - Clipboard::Instance().PasteDrop((HDROP)wParam); + const auto drop = reinterpret_cast(wParam); + Clipboard::Instance().PasteDrop(drop); + DragFinish(drop); } [[nodiscard]] LRESULT Window::_HandleGetObject(const HWND hwnd, const WPARAM wParam, const LPARAM lParam) @@ -887,11 +967,6 @@ BOOL Window::PostUpdateWindowSize() const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); const auto& ScreenInfo = GetScreenInfo(); - if (ScreenInfo.ConvScreenInfo != nullptr) - { - return FALSE; - } - if (gci.Flags & CONSOLE_SETTING_WINDOW_SIZE) { return FALSE; diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index f1e43bc98a3..28295aeadd2 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -18,6 +18,7 @@ // and thus may access both _r and _api. #pragma warning(disable : 4100) // '...': unreferenced formal parameter +#pragma warning(disable : 4127) // conditional expression is constant // Disable a bunch of warnings which get in the way of writing performant code. #pragma warning(disable : 26429) // Symbol 'data' is never tested for nullness, it can be marked as not_null (f.23). #pragma warning(disable : 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4). diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 2d6f4cc1a28..deb8925c943 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -56,6 +56,11 @@ Renderer::~Renderer() _pThread.reset(); } +IRenderData* Renderer::GetRenderData() const noexcept +{ + return _pData; +} + // Routine Description: // - Walks through the console data structures to compose a new frame based on the data that has changed since last call and outputs it to the connected rendering engine. // Arguments: @@ -118,24 +123,94 @@ Renderer::~Renderer() if (_currentCursorOptions) { - const auto coord = _currentCursorOptions->coordCursor; const auto& buffer = _pData->GetTextBuffer(); - const auto lineRendition = buffer.GetLineRendition(coord.y); - const auto cursorWidth = _pData->IsCursorDoubleWidth() ? 2 : 1; - - til::rect cursorRect{ coord.x, coord.y, coord.x + cursorWidth, coord.y + 1 }; - cursorRect = BufferToScreenLine(cursorRect, lineRendition); + const auto view = buffer.GetSize(); + const auto coord = _currentCursorOptions->coordCursor; - if (buffer.GetSize().TrimToViewport(&cursorRect)) + // If we had previously drawn a composition at the previous cursor position + // we need to invalidate the entire line because who knows what changed. + // (It's possible to figure that out, but not worth the effort right now.) + if (_compositionCache) { - FOREACH_ENGINE(pEngine) + til::rect rect{ 0, coord.y, til::CoordTypeMax, coord.y + 1 }; + if (view.TrimToViewport(&rect)) + { + FOREACH_ENGINE(pEngine) + { + LOG_IF_FAILED(pEngine->Invalidate(&rect)); + } + } + } + else + { + const auto lineRendition = buffer.GetLineRendition(coord.y); + const auto cursorWidth = _pData->IsCursorDoubleWidth() ? 2 : 1; + + til::rect rect{ coord.x, coord.y, coord.x + cursorWidth, coord.y + 1 }; + rect = BufferToScreenLine(rect, lineRendition); + + if (view.TrimToViewport(&rect)) { - LOG_IF_FAILED(pEngine->InvalidateCursor(&cursorRect)); + FOREACH_ENGINE(pEngine) + { + LOG_IF_FAILED(pEngine->InvalidateCursor(&rect)); + } } } } _currentCursorOptions = _GetCursorInfo(); + _compositionCache.reset(); + + // Invalidate the line that the active TSF composition is on, + // so that _PaintBufferOutput() actually gets a chance to draw it. + if (!_pData->activeComposition.text.empty()) + { + const auto viewport = _pData->GetViewport(); + const auto coordCursor = _pData->GetCursorPosition(); + + til::rect line{ 0, coordCursor.y, til::CoordTypeMax, coordCursor.y + 1 }; + if (viewport.TrimToViewport(&line)) + { + viewport.ConvertToOrigin(&line); + + FOREACH_ENGINE(pEngine) + { + LOG_IF_FAILED(pEngine->Invalidate(&line)); + } + + auto& buffer = _pData->GetTextBuffer(); + auto& scratch = buffer.GetScratchpadRow(); + + std::wstring_view text{ _pData->activeComposition.text }; + RowWriteState state{ + .columnLimit = buffer.GetRowByOffset(line.top).GetReadableColumnCount(), + }; + + state.text = text.substr(0, _pData->activeComposition.cursorPos); + scratch.ReplaceText(state); + const auto cursorOffset = state.columnEnd; + + state.text = text.substr(_pData->activeComposition.cursorPos); + state.columnBegin = state.columnEnd; + scratch.ReplaceText(state); + + // Ideally the text is inserted at the position of the cursor (`coordCursor`), + // but if we got more text than fits into the remaining space until the end of the line, + // then we'll insert the text aligned to the end of the line. + const auto remaining = state.columnLimit - state.columnEnd; + const auto beg = std::clamp(coordCursor.x, 0, remaining); + + const auto baseAttribute = buffer.GetRowByOffset(coordCursor.y).GetAttrByColumn(coordCursor.x); + _compositionCache.emplace(til::point{ beg, coordCursor.y }, baseAttribute); + + // Fake-move the cursor to where it needs to be in the active composition. + if (_currentCursorOptions) + { + _currentCursorOptions->coordCursor.x = std::min(beg + cursorOffset, line.right - 1); + } + } + } FOREACH_ENGINE(pEngine) { @@ -195,9 +270,6 @@ try // 2. Paint Rows of Text _PaintBufferOutput(pEngine); - // 3. Paint overlays that reside above the text buffer - _PaintOverlays(pEngine); - // 4. Paint Selection _PaintSelection(pEngine); @@ -716,6 +788,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) // It can move left/right or top/bottom depending on how the viewport is scrolled // relative to the entire buffer. const auto view = _pData->GetViewport(); + const auto compositionRow = _compositionCache ? _compositionCache->absoluteOrigin.y : -1; // This is effectively the number of cells on the visible screen that need to be redrawn. // The origin is always 0, 0 because it represents the screen itself, not the underlying buffer. @@ -746,7 +819,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) const auto redraw = Viewport::Intersect(dirty, view); // Retrieve the text buffer so we can read information out of it. - const auto& buffer = _pData->GetTextBuffer(); + auto& buffer = _pData->GetTextBuffer(); // Now walk through each row of text that we need to redraw. for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++) @@ -754,6 +827,53 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) // Calculate the boundaries of a single line. This is from the left to right edge of the dirty // area in width and exactly 1 tall. const auto screenLine = til::inclusive_rect{ redraw.Left(), row, redraw.RightInclusive(), row }; + const auto& r = buffer.GetRowByOffset(row); + + // Draw the active composition. + // We have to use some tricks here with const_cast, because the code after it relies on TextBufferCellIterator, + // which isn't compatible with the scratchpad row. This forces us to back up and modify the actual row `r`. + ROW* rowBackup = nullptr; + if (row == compositionRow) + { + auto& scratch = buffer.GetScratchpadRow(); + scratch.CopyFrom(r); + rowBackup = &scratch; + + std::wstring_view text{ _pData->activeComposition.text }; + RowWriteState state{ + .columnLimit = r.GetReadableColumnCount(), + .columnEnd = _compositionCache->absoluteOrigin.x, + }; + + size_t off = 0; + for (const auto& range : _pData->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(r).ReplaceText(state); + const_cast(r).ReplaceAttributes(state.columnBegin, state.columnEnd, attr); + off += len; + } + } + const auto restore = wil::scope_exit([&] { + if (rowBackup) + { + const_cast(r).CopyFrom(*rowBackup); + } + }); // Convert the screen coordinates of the line to an equivalent // range of buffer cells, taking line rendition into account. @@ -1158,70 +1278,6 @@ void Renderer::_PaintCursor(_In_ IRenderEngine* const pEngine) return pEngine->PrepareRenderInfo(std::move(info)); } -// Routine Description: -// - Paint helper to draw text that overlays the main buffer to provide user interactivity regions -// - This supports IME composition. -// Arguments: -// - engine - The render engine that we're targeting. -// - overlay - The overlay to draw. -// Return Value: -// - -void Renderer::_PaintOverlay(IRenderEngine& engine, - const RenderOverlay& overlay) -{ - try - { - // Now get the overlay's viewport and adjust it to where it is supposed to be relative to the window. - auto srCaView = overlay.region.ToExclusive(); - srCaView.top += overlay.origin.y; - srCaView.bottom += overlay.origin.y; - srCaView.left += overlay.origin.x; - srCaView.right += overlay.origin.x; - - std::span dirtyAreas; - LOG_IF_FAILED(engine.GetDirtyArea(dirtyAreas)); - - for (const auto& rect : dirtyAreas) - { - if (const auto viewDirty = rect & srCaView) - { - for (auto iRow = viewDirty.top; iRow < viewDirty.bottom; iRow++) - { - const til::point target{ viewDirty.left, iRow }; - const auto source = target - overlay.origin; - - auto it = overlay.buffer.GetCellLineDataAt(source); - - _PaintBufferOutputHelper(&engine, it, target, false); - } - } - } - } - CATCH_LOG(); -} - -// Routine Description: -// - Paint helper to draw the composition string portion of the IME. -// - This specifically is the string that appears at the cursor on the input line showing what the user is currently typing. -// - See also: Generic Paint IME helper method. -// Arguments: -// - -// Return Value: -// - -void Renderer::_PaintOverlays(_In_ IRenderEngine* const pEngine) -{ - try - { - const auto overlays = _pData->GetOverlays(); - - for (const auto& overlay : overlays) - { - _PaintOverlay(*pEngine, overlay); - } - } - CATCH_LOG(); -} - // Routine Description: // - Paint helper to draw the selected area of the window. // Arguments: diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index a8184ce63df..1944beb4b92 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -44,6 +44,8 @@ namespace Microsoft::Console::Render ~Renderer(); + IRenderData* GetRenderData() const noexcept; + [[nodiscard]] HRESULT PaintFrame(); void NotifyPaintFrame() noexcept; @@ -93,6 +95,14 @@ namespace Microsoft::Console::Render void UpdateLastHoveredInterval(const std::optional::interval>& newInterval); private: + // Caches some essential information about the active composition. + // This allows us to properly invalidate it between frames, etc. + struct CompositionCache + { + til::point absoluteOrigin; + TextAttribute baseAttribute; + }; + static GridLineSet s_GetGridlines(const TextAttribute& textAttribute) noexcept; static bool s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar); @@ -106,8 +116,6 @@ namespace Microsoft::Console::Render bool _isHoveredHyperlink(const TextAttribute& textAttribute) const noexcept; void _PaintSelection(_In_ IRenderEngine* const pEngine); void _PaintCursor(_In_ IRenderEngine* const pEngine); - void _PaintOverlays(_In_ IRenderEngine* const pEngine); - void _PaintOverlay(IRenderEngine& engine, const RenderOverlay& overlay); [[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes); [[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine); std::vector _GetSelectionRects() const; @@ -127,6 +135,7 @@ namespace Microsoft::Console::Render std::optional::interval> _hoveredInterval; Microsoft::Console::Types::Viewport _viewport; std::optional _currentCursorOptions; + std::optional _compositionCache; std::vector _clusterBuffer; std::vector _previousSelection; std::function _pfnBackgroundColorChanged; diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index f9c08c83b9f..e55bdcbe87c 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -14,26 +14,26 @@ Author(s): #pragma once -#include "../../host/conimeinfo.h" #include "../../buffer/out/TextAttribute.hpp" +#include "../../renderer/inc/FontInfo.hpp" +#include "../../types/inc/viewport.hpp" class Cursor; +class TextBuffer; namespace Microsoft::Console::Render { - struct RenderOverlay final + struct CompositionRange { - // This is where the data is stored - const TextBuffer& buffer; - - // This is where the top left of the stored buffer should be overlaid on the screen - // (relative to the current visible viewport) - const til::point origin; + size_t len; // The number of chars in Composition::text that this .attr applies to + TextAttribute attr; + }; - // This is the area of the buffer that is actually used for overlay. - // Anything outside of this is considered empty by the overlay and shouldn't be used - // for painting purposes. - const Microsoft::Console::Types::Viewport region; + struct Composition + { + std::wstring text; + til::small_vector attributes; + size_t cursorPos = 0; }; class IRenderData @@ -44,7 +44,7 @@ namespace Microsoft::Console::Render // This block used to be IBaseData. virtual Microsoft::Console::Types::Viewport GetViewport() noexcept = 0; virtual til::point GetTextBufferEndPosition() const noexcept = 0; - virtual const TextBuffer& GetTextBuffer() const noexcept = 0; + virtual TextBuffer& GetTextBuffer() const noexcept = 0; virtual const FontInfo& GetFontInfo() const noexcept = 0; virtual std::vector GetSelectionRects() noexcept = 0; virtual std::span GetSearchHighlights() const noexcept = 0; @@ -60,7 +60,6 @@ namespace Microsoft::Console::Render virtual CursorType GetCursorStyle() const noexcept = 0; virtual ULONG GetCursorPixelWidth() const noexcept = 0; virtual bool IsCursorDoubleWidth() const = 0; - virtual const std::vector GetOverlays() const noexcept = 0; virtual const bool IsGridLineDrawingAllowed() noexcept = 0; virtual const std::wstring_view GetConsoleTitle() const noexcept = 0; virtual const std::wstring GetHyperlinkUri(uint16_t id) const = 0; @@ -76,5 +75,10 @@ namespace Microsoft::Console::Render virtual const til::point GetSelectionAnchor() const noexcept = 0; virtual const til::point GetSelectionEnd() const noexcept = 0; virtual const bool IsUiaDataInitialized() const noexcept = 0; + + // Ideally this would not be stored on an interface, however ideally IRenderData should not be an interface in the first place. + // This is because we should have only 1 way how to represent render data across the codebase anyway, and it should + // be by-value in a struct so that we can snapshot it and release the terminal lock as quickly as possible. + Composition activeComposition; }; } diff --git a/src/tsf/ConsoleTSF.cpp b/src/tsf/ConsoleTSF.cpp deleted file mode 100644 index c3e1b04b33a..00000000000 --- a/src/tsf/ConsoleTSF.cpp +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "TfConvArea.h" -#include "TfEditSession.h" - -/* 626761ad-78d2-44d2-be8b-752cf122acec */ -const GUID GUID_APPLICATION = { 0x626761ad, 0x78d2, 0x44d2, { 0xbe, 0x8b, 0x75, 0x2c, 0xf1, 0x22, 0xac, 0xec } }; - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::Initialize -// -//---------------------------------------------------------------------------- - -#define Init_CheckResult() \ - if (FAILED(hr)) \ - { \ - Uninitialize(); \ - return hr; \ - } - -[[nodiscard]] HRESULT CConsoleTSF::Initialize() -{ - HRESULT hr; - - if (_spITfThreadMgr) - { - return S_FALSE; - } - - // Activate per-thread Cicero in custom UI mode (TF_TMAE_UIELEMENTENABLEDONLY). - - hr = ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - Init_CheckResult(); - _fCoInitialized = TRUE; - - hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&_spITfThreadMgr)); - Init_CheckResult(); - - hr = _spITfThreadMgr->ActivateEx(&_tid, TF_TMAE_CONSOLE); - Init_CheckResult(); - - // Create Cicero document manager and input context. - - hr = _spITfThreadMgr->CreateDocumentMgr(&_spITfDocumentMgr); - Init_CheckResult(); - - TfEditCookie ecTmp; - hr = _spITfDocumentMgr->CreateContext(_tid, - 0, - static_cast(this), - &_spITfInputContext, - &ecTmp); - Init_CheckResult(); - - // Set the context owner before attaching the context to the doc. - wil::com_ptr_nothrow spSrcIC; - hr = _spITfInputContext.query_to(&spSrcIC); - Init_CheckResult(); - - hr = spSrcIC->AdviseSink(IID_ITfContextOwner, static_cast(this), &_dwContextOwnerCookie); - Init_CheckResult(); - - hr = _spITfDocumentMgr->Push(_spITfInputContext.get()); - Init_CheckResult(); - - // Collect the active keyboard layout info. - - wil::com_ptr_nothrow spITfProfilesMgr; - hr = ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spITfProfilesMgr)); - if (SUCCEEDED(hr)) - { - TF_INPUTPROCESSORPROFILE ipp; - hr = spITfProfilesMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &ipp); - if (SUCCEEDED(hr)) - { - OnActivated(ipp.dwProfileType, ipp.langid, ipp.clsid, ipp.catid, ipp.guidProfile, ipp.hkl, ipp.dwFlags); - } - } - Init_CheckResult(); - - // Setup some useful Cicero event sinks and callbacks. - // _spITfThreadMgr && _spITfInputContext must be non-null for checks above to have succeeded, so - // we're not going to check them again here. try_query will A/V if they're null. - auto spSrcTIM = _spITfThreadMgr.try_query(); - auto spSrcICS = _spITfInputContext.try_query(); - - hr = (spSrcTIM && spSrcIC && spSrcICS) ? S_OK : E_FAIL; - Init_CheckResult(); - - hr = spSrcTIM->AdviseSink(IID_ITfInputProcessorProfileActivationSink, - static_cast(this), - &_dwActivationSinkCookie); - Init_CheckResult(); - - hr = spSrcTIM->AdviseSink(IID_ITfUIElementSink, static_cast(this), &_dwUIElementSinkCookie); - Init_CheckResult(); - - hr = spSrcIC->AdviseSink(IID_ITfTextEditSink, static_cast(this), &_dwTextEditSinkCookie); - Init_CheckResult(); - - hr = spSrcICS->AdviseSingleSink(_tid, IID_ITfCleanupContextSink, static_cast(this)); - Init_CheckResult(); - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::Uninitialize() -// -//---------------------------------------------------------------------------- - -void CConsoleTSF::Uninitialize() -{ - // Destroy the current conversion area object - - if (_pConversionArea) - { - delete _pConversionArea; - _pConversionArea = nullptr; - } - - // Detach Cicero event sinks. - if (_spITfInputContext) - { - auto spSrcICS = _spITfInputContext.try_query(); - if (spSrcICS) - { - spSrcICS->UnadviseSingleSink(_tid, IID_ITfCleanupContextSink); - } - } - - // Associate the document\context with the console window. - - if (_spITfThreadMgr) - { - auto spSrcTIM = _spITfThreadMgr.try_query(); - if (spSrcTIM) - { - if (_dwUIElementSinkCookie) - { - spSrcTIM->UnadviseSink(_dwUIElementSinkCookie); - } - if (_dwActivationSinkCookie) - { - spSrcTIM->UnadviseSink(_dwActivationSinkCookie); - } - } - } - - _dwUIElementSinkCookie = 0; - _dwActivationSinkCookie = 0; - - if (_spITfInputContext) - { - auto spSrcIC = _spITfInputContext.try_query(); - if (spSrcIC) - { - if (_dwContextOwnerCookie) - { - spSrcIC->UnadviseSink(_dwContextOwnerCookie); - } - if (_dwTextEditSinkCookie) - { - spSrcIC->UnadviseSink(_dwTextEditSinkCookie); - } - } - } - _dwContextOwnerCookie = 0; - _dwTextEditSinkCookie = 0; - - // Clear the Cicero reference to our document manager. - - if (_spITfThreadMgr && _spITfDocumentMgr) - { - wil::com_ptr_nothrow spDocMgr; - _spITfThreadMgr->AssociateFocus(_hwndConsole, nullptr, &spDocMgr); - } - - // Dismiss the input context and document manager. - - if (_spITfDocumentMgr) - { - _spITfDocumentMgr->Pop(TF_POPF_ALL); - } - - _spITfInputContext.reset(); - _spITfDocumentMgr.reset(); - - // Deactivate per-thread Cicero and uninitialize COM. - - if (_spITfThreadMgr) - { - _spITfThreadMgr->Deactivate(); - _spITfThreadMgr.reset(); - } - if (_fCoInitialized) - { - ::CoUninitialize(); - _fCoInitialized = FALSE; - } -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::IUnknown::QueryInterface -// CConsoleTSF::IUnknown::AddRef -// CConsoleTSF::IUnknown::Release -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::QueryInterface(REFIID riid, void** ppvObj) -{ - if (!ppvObj) - { - return E_FAIL; - } - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_ITfCleanupContextSink)) - { - *ppvObj = static_cast(this); - } - else if (IsEqualGUID(riid, IID_ITfContextOwnerCompositionSink)) - { - *ppvObj = static_cast(this); - } - else if (IsEqualIID(riid, IID_ITfUIElementSink)) - { - *ppvObj = static_cast(this); - } - else if (IsEqualIID(riid, IID_ITfContextOwner)) - { - *ppvObj = static_cast(this); - } - else if (IsEqualIID(riid, IID_ITfInputProcessorProfileActivationSink)) - { - *ppvObj = static_cast(this); - } - else if (IsEqualIID(riid, IID_ITfTextEditSink)) - { - *ppvObj = static_cast(this); - } - else if (IsEqualGUID(riid, IID_IUnknown)) - { - *ppvObj = this; - } - if (*ppvObj) - { - AddRef(); - } - return (*ppvObj) ? S_OK : E_NOINTERFACE; -} - -STDAPI_(ULONG) -CConsoleTSF::AddRef() -{ - return InterlockedIncrement(&_cRef); -} - -STDAPI_(ULONG) -CConsoleTSF::Release() -{ - auto cr = InterlockedDecrement(&_cRef); - if (cr == 0) - { - if (g_pConsoleTSF == this) - { - g_pConsoleTSF = nullptr; - } - delete this; - } - return cr; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::ITfCleanupContextSink::OnCleanupContext -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::OnCleanupContext(TfEditCookie ecWrite, ITfContext* pic) -{ - // - // Remove GUID_PROP_COMPOSING - // - wil::com_ptr_nothrow prop; - if (SUCCEEDED(pic->GetProperty(GUID_PROP_COMPOSING, &prop))) - { - wil::com_ptr_nothrow enumranges; - if (SUCCEEDED(prop->EnumRanges(ecWrite, &enumranges, nullptr))) - { - wil::com_ptr_nothrow rangeTmp; - while (enumranges->Next(1, &rangeTmp, nullptr) == S_OK) - { - VARIANT var; - VariantInit(&var); - prop->GetValue(ecWrite, rangeTmp.get(), &var); - if ((var.vt == VT_I4) && (var.lVal != 0)) - { - prop->Clear(ecWrite, rangeTmp.get()); - } - } - } - } - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::ITfContextOwnerCompositionSink::OnStartComposition -// CConsoleTSF::ITfContextOwnerCompositionSink::OnUpdateComposition -// CConsoleTSF::ITfContextOwnerCompositionSink::OnEndComposition -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::OnStartComposition(ITfCompositionView* pCompView, BOOL* pfOk) -{ - if (!_pConversionArea || (_cCompositions > 0 && (!_fModifyingDoc))) - { - *pfOk = FALSE; - } - else - { - *pfOk = TRUE; - // Ignore compositions triggered by our own edit sessions - // (i.e. when the application is the composition owner) - auto clsidCompositionOwner = GUID_APPLICATION; - pCompView->GetOwnerClsid(&clsidCompositionOwner); - if (!IsEqualGUID(clsidCompositionOwner, GUID_APPLICATION)) - { - _cCompositions++; - if (_cCompositions == 1) - { - LOG_IF_FAILED(ImeStartComposition()); - } - } - } - return S_OK; -} - -STDMETHODIMP CConsoleTSF::OnUpdateComposition(ITfCompositionView* /*pComp*/, ITfRange*) -{ - return S_OK; -} - -STDMETHODIMP CConsoleTSF::OnEndComposition(ITfCompositionView* pCompView) -{ - if (!_cCompositions || !_pConversionArea) - { - return E_FAIL; - } - // Ignore compositions triggered by our own edit sessions - // (i.e. when the application is the composition owner) - auto clsidCompositionOwner = GUID_APPLICATION; - pCompView->GetOwnerClsid(&clsidCompositionOwner); - if (!IsEqualGUID(clsidCompositionOwner, GUID_APPLICATION)) - { - _cCompositions--; - if (!_cCompositions) - { - LOG_IF_FAILED(_OnCompleteComposition()); - LOG_IF_FAILED(ImeEndComposition()); - } - } - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::ITfTextEditSink::OnEndEdit -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::OnEndEdit(ITfContext* pInputContext, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord) -{ - if (_cCompositions && _pConversionArea && _HasCompositionChanged(pInputContext, ecReadOnly, pEditRecord)) - { - LOG_IF_FAILED(_OnUpdateComposition()); - } - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::ITfInputProcessorProfileActivationSink::OnActivated -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::OnActivated(DWORD /*dwProfileType*/, - LANGID /*langid*/, - REFCLSID /*clsid*/, - REFGUID catid, - REFGUID /*guidProfile*/, - HKL /*hkl*/, - DWORD dwFlags) -{ - if (!(dwFlags & TF_IPSINK_FLAG_ACTIVE)) - { - return S_OK; - } - if (!IsEqualGUID(catid, GUID_TFCAT_TIP_KEYBOARD)) - { - // Don't care for non-keyboard profiles. - return S_OK; - } - - try - { - CreateConversionArea(); - } - CATCH_RETURN(); - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::ITfUIElementSink::BeginUIElement -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::BeginUIElement(DWORD /*dwUIElementId*/, BOOL* pbShow) -{ - *pbShow = TRUE; - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::ITfUIElementSink::UpdateUIElement -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::UpdateUIElement(DWORD /*dwUIElementId*/) -{ - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::ITfUIElementSink::EndUIElement -// -//---------------------------------------------------------------------------- - -STDMETHODIMP CConsoleTSF::EndUIElement(DWORD /*dwUIElementId*/) -{ - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::CreateConversionAreaService -// -//---------------------------------------------------------------------------- - -CConversionArea* CConsoleTSF::CreateConversionArea() -{ - BOOL fHadConvArea = (_pConversionArea != nullptr); - - if (!_pConversionArea) - { - _pConversionArea = new CConversionArea(); - } - - // Associate the document\context with the console window. - if (!fHadConvArea) - { - wil::com_ptr_nothrow spPrevDocMgr; - _spITfThreadMgr->AssociateFocus(_hwndConsole, _pConversionArea ? _spITfDocumentMgr.get() : nullptr, &spPrevDocMgr); - } - - return _pConversionArea; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::OnUpdateComposition() -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CConsoleTSF::_OnUpdateComposition() -{ - if (_fEditSessionRequested) - { - return S_FALSE; - } - - auto hr = E_OUTOFMEMORY; - auto pEditSession = new (std::nothrow) CEditSessionUpdateCompositionString(); - if (pEditSession) - { - // Can't use TF_ES_SYNC because called from OnEndEdit. - _fEditSessionRequested = TRUE; - _spITfInputContext->RequestEditSession(_tid, pEditSession, TF_ES_READWRITE, &hr); - if (FAILED(hr)) - { - pEditSession->Release(); - _fEditSessionRequested = FALSE; - } - } - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::OnCompleteComposition() -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CConsoleTSF::_OnCompleteComposition() -{ - // Update the composition area. - - auto hr = E_OUTOFMEMORY; - auto pEditSession = new (std::nothrow) CEditSessionCompositionComplete(); - if (pEditSession) - { - // The composition could have been finalized because of a caret move, therefore it must be - // inserted synchronously while at the original caret position.(TF_ES_SYNC is ok for a nested RO session). - _spITfInputContext->RequestEditSession(_tid, pEditSession, TF_ES_READ | TF_ES_SYNC, &hr); - if (FAILED(hr)) - { - pEditSession->Release(); - } - } - - // Cleanup (empty the context range) after the last composition, unless a new one has started. - if (!_fCleanupSessionRequested) - { - _fCleanupSessionRequested = TRUE; - auto pEditSessionCleanup = new (std::nothrow) CEditSessionCompositionCleanup(); - if (pEditSessionCleanup) - { - // Can't use TF_ES_SYNC because requesting RW while called within another session. - // For the same reason, must use explicit TF_ES_ASYNC, or the request will be rejected otherwise. - _spITfInputContext->RequestEditSession(_tid, pEditSessionCleanup, TF_ES_READWRITE | TF_ES_ASYNC, &hr); - if (FAILED(hr)) - { - pEditSessionCleanup->Release(); - _fCleanupSessionRequested = FALSE; - } - } - } - return hr; -} diff --git a/src/tsf/ConsoleTSF.h b/src/tsf/ConsoleTSF.h deleted file mode 100644 index 9a1b582bf22..00000000000 --- a/src/tsf/ConsoleTSF.h +++ /dev/null @@ -1,220 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfContext.h - -Abstract: - - This file defines the CConsoleTSF Interface Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#pragma once - -class CConversionArea; - -class CConsoleTSF final : - public ITfContextOwner, - public ITfContextOwnerCompositionSink, - public ITfInputProcessorProfileActivationSink, - public ITfUIElementSink, - public ITfCleanupContextSink, - public ITfTextEditSink -{ -public: - CConsoleTSF(HWND hwndConsole, - GetSuggestionWindowPos pfnPosition, - GetTextBoxAreaPos pfnTextArea) : - _hwndConsole(hwndConsole), - _pfnPosition(pfnPosition), - _pfnTextArea(pfnTextArea), - _cRef(1), - _tid() - { - } - - virtual ~CConsoleTSF() = default; - [[nodiscard]] HRESULT Initialize(); - void Uninitialize(); - -public: - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj); - STDMETHODIMP_(ULONG) - AddRef(void); - STDMETHODIMP_(ULONG) - Release(void); - - // ITfContextOwner - STDMETHODIMP GetACPFromPoint(const POINT*, DWORD, LONG* pCP) - { - if (pCP) - { - *pCP = 0; - } - - return S_OK; - } - - // This returns Rectangle of the text box of whole console. - // When a user taps inside the rectangle while hardware keyboard is not available, - // touch keyboard is invoked. - STDMETHODIMP GetScreenExt(RECT* pRect) - { - if (pRect) - { - *pRect = _pfnTextArea(); - } - - return S_OK; - } - - // This returns rectangle of current command line edit area. - // When a user types in East Asian language, candidate window is shown at this position. - // Emoji and more panel (Win+.) is shown at the position, too. - STDMETHODIMP GetTextExt(LONG, LONG, RECT* pRect, BOOL* pbClipped) - { - if (pRect) - { - *pRect = _pfnPosition(); - } - - if (pbClipped) - { - *pbClipped = FALSE; - } - - return S_OK; - } - - STDMETHODIMP GetStatus(TF_STATUS* pTfStatus) - { - if (pTfStatus) - { - pTfStatus->dwDynamicFlags = 0; - pTfStatus->dwStaticFlags = TF_SS_TRANSITORY; - } - return pTfStatus ? S_OK : E_INVALIDARG; - } - STDMETHODIMP GetWnd(HWND* phwnd) - { - *phwnd = _hwndConsole; - return S_OK; - } - STDMETHODIMP GetAttribute(REFGUID, VARIANT*) - { - return E_NOTIMPL; - } - - // ITfContextOwnerCompositionSink methods - STDMETHODIMP OnStartComposition(ITfCompositionView* pComposition, BOOL* pfOk); - STDMETHODIMP OnUpdateComposition(ITfCompositionView* pComposition, ITfRange* pRangeNew); - STDMETHODIMP OnEndComposition(ITfCompositionView* pComposition); - - // ITfInputProcessorProfileActivationSink - STDMETHODIMP OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags); - - // ITfUIElementSink methods - STDMETHODIMP BeginUIElement(DWORD dwUIElementId, BOOL* pbShow); - STDMETHODIMP UpdateUIElement(DWORD dwUIElementId); - STDMETHODIMP EndUIElement(DWORD dwUIElementId); - - // ITfCleanupContextSink methods - STDMETHODIMP OnCleanupContext(TfEditCookie ecWrite, ITfContext* pic); - - // ITfTextEditSink methods - STDMETHODIMP OnEndEdit(ITfContext* pInputContext, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord); - -public: - CConversionArea* CreateConversionArea(); - CConversionArea* GetConversionArea() { return _pConversionArea; } - ITfContext* GetInputContext() { return _spITfInputContext.get(); } - HWND GetConsoleHwnd() { return _hwndConsole; } - TfClientId GetTfClientId() { return _tid; } - BOOL IsInComposition() { return (_cCompositions > 0); } - void OnEditSession() { _fEditSessionRequested = FALSE; } - BOOL IsPendingCompositionCleanup() { return _fCleanupSessionRequested || _fCompositionCleanupSkipped; } - void OnCompositionCleanup(BOOL bSucceeded) - { - _fCleanupSessionRequested = FALSE; - _fCompositionCleanupSkipped = !bSucceeded; - } - void SetModifyingDocFlag(BOOL fSet) { _fModifyingDoc = fSet; } - void SetFocus(BOOL fSet) - { - if (!fSet && _cCompositions) - { - // Close (terminate) any open compositions when losing the input focus. - if (_spITfInputContext) - { - auto spCompositionServices = _spITfInputContext.try_query(); - if (spCompositionServices) - { - spCompositionServices->TerminateComposition(nullptr); - } - } - } - } - - // A workaround for a MS Korean IME scenario where the IME appends a whitespace - // composition programmatically right after completing a keyboard input composition. - // Since post-composition clean-up is an async operation, the programmatic whitespace - // composition gets completed before the previous composition cleanup happened, - // and this results in a double insertion of the first composition. To avoid that, we'll - // store the length of the last completed composition here until it's cleaned up. - // (for simplicity, this patch doesn't provide a generic solution for all possible - // scenarios with subsequent synchronous compositions, only for the known 'append'). - long GetCompletedRangeLength() const { return _cchCompleted; } - void SetCompletedRangeLength(long cch) { _cchCompleted = cch; } - -private: - [[nodiscard]] HRESULT _OnUpdateComposition(); - [[nodiscard]] HRESULT _OnCompleteComposition(); - BOOL _HasCompositionChanged(ITfContext* pInputContext, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord); - -private: - // ref count. - DWORD _cRef; - - // Cicero stuff. - TfClientId _tid; - wil::com_ptr_nothrow _spITfThreadMgr; - wil::com_ptr_nothrow _spITfDocumentMgr; - wil::com_ptr_nothrow _spITfInputContext; - - // Event sink cookies. - DWORD _dwContextOwnerCookie = 0; - DWORD _dwUIElementSinkCookie = 0; - DWORD _dwTextEditSinkCookie = 0; - DWORD _dwActivationSinkCookie = 0; - - // Conversion area object for the languages. - CConversionArea* _pConversionArea = nullptr; - - // Console info. - HWND _hwndConsole; - GetSuggestionWindowPos _pfnPosition; - GetTextBoxAreaPos _pfnTextArea; - - // Miscellaneous flags - BOOL _fModifyingDoc = FALSE; // Set TRUE, when calls ITfRange::SetText - BOOL _fCoInitialized = FALSE; - BOOL _fEditSessionRequested = FALSE; - BOOL _fCleanupSessionRequested = FALSE; - BOOL _fCompositionCleanupSkipped = FALSE; - - int _cCompositions = 0; - long _cchCompleted = 0; // length of completed composition waiting for cleanup -}; - -extern CConsoleTSF* g_pConsoleTSF; diff --git a/src/tsf/Handle.cpp b/src/tsf/Handle.cpp new file mode 100644 index 00000000000..ab3f7c5f786 --- /dev/null +++ b/src/tsf/Handle.cpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "Handle.h" + +#include "Implementation.h" + +using namespace Microsoft::Console::TSF; + +Handle Handle::Create() +{ + Handle handle; + handle._impl = new Implementation(); + handle._impl->Initialize(); + return handle; +} + +Handle::~Handle() +{ + if (_impl) + { + _impl->Uninitialize(); + _impl->Release(); + } +} + +Handle::Handle(Handle&& other) noexcept : + _impl{ other._impl } +{ + other._impl = nullptr; +} + +Handle& Handle::operator=(Handle&& other) noexcept +{ + if (this != &other) + { + this->~Handle(); + _impl = other._impl; + other._impl = nullptr; + } + return *this; +} + +Handle::operator bool() const noexcept +{ + return _impl != nullptr; +} + +HWND Handle::FindWindowOfActiveTSF() const noexcept +{ + return _impl ? _impl->FindWindowOfActiveTSF() : nullptr; +} + +void Handle::AssociateFocus(IDataProvider* provider) const +{ + if (_impl) + { + _impl->AssociateFocus(provider); + } +} + +void Handle::Focus(IDataProvider* provider) const +{ + if (_impl) + { + _impl->Focus(provider); + } +} + +void Handle::Unfocus(IDataProvider* provider) const +{ + if (_impl) + { + _impl->Unfocus(provider); + } +} + +bool Handle::HasActiveComposition() const noexcept +{ + return _impl ? _impl->HasActiveComposition() : false; +} diff --git a/src/tsf/Handle.h b/src/tsf/Handle.h new file mode 100644 index 00000000000..fc56920ae40 --- /dev/null +++ b/src/tsf/Handle.h @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace Microsoft::Console::Render +{ + class Renderer; +} + +namespace Microsoft::Console::TSF +{ + struct Implementation; + + // It is fine for any of the IDataProvider functions to throw. + // However, this doesn't apply to the IUnknown ones. + // + // NOTE: This is a pure virtual interface, just like those for COM. + // It cannot have a `~IDataProvider() = default;` destructor, because then it would need a vtable. + MIDL_INTERFACE("A86B8AAF-1531-40F5-95BB-611AA9DBDC18") + IDataProvider : IUnknown + { + virtual HWND GetHwnd() = 0; + virtual RECT GetViewport() = 0; + virtual RECT GetCursorPosition() = 0; + virtual void HandleOutput(std::wstring_view text) = 0; + virtual Render::Renderer* GetRenderer() = 0; + }; + + // A pimpl idiom wrapper for `Implementation` so that we don't pull in all the TSF headers everywhere. + // Simultaneously it allows us to handle AdviseSink/UnadviseSink properly, because those hold strong + // references on `Implementation` which results in an (unfortunate but intentional) reference cycle. + struct Handle + { + static Handle Create(); + + Handle() = default; + ~Handle(); + Handle(const Handle&) = delete; + Handle& operator=(const Handle&) = delete; + Handle(Handle&& other) noexcept; + Handle& operator=(Handle&& other) noexcept; + + explicit operator bool() const noexcept; + HWND FindWindowOfActiveTSF() const noexcept; + void AssociateFocus(IDataProvider* provider) const; + void Focus(IDataProvider* provider) const; + void Unfocus(IDataProvider* provider) const; + bool HasActiveComposition() const noexcept; + + private: + Implementation* _impl = nullptr; + }; +} diff --git a/src/tsf/Implementation.cpp b/src/tsf/Implementation.cpp new file mode 100644 index 00000000000..d3ad65ae9c5 --- /dev/null +++ b/src/tsf/Implementation.cpp @@ -0,0 +1,663 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "Implementation.h" + +#include "Handle.h" +#include "../buffer/out/TextAttribute.hpp" +#include "../renderer/base/renderer.hpp" + +#pragma warning(disable : 4100) // '...': unreferenced formal parameter + +using namespace Microsoft::Console::TSF; + +static void TfSelectionClose(const TF_SELECTION* sel) +{ + if (const auto r = sel->range) + { + r->Release(); + } +} +using unique_tf_selection = wil::unique_struct; + +static void TfPropertyvalClose(TF_PROPERTYVAL* val) +{ + VariantClear(&val->varValue); +} +using unique_tf_propertyval = wil::unique_struct; + +void Implementation::Initialize() +{ + _categoryMgr = wil::CoCreateInstance(CLSID_TF_CategoryMgr, CLSCTX_INPROC_SERVER); + _displayAttributeMgr = wil::CoCreateInstance(CLSID_TF_DisplayAttributeMgr); + + // There's no point in calling TF_GetThreadMgr. ITfThreadMgr is a per-thread singleton. + _threadMgrEx = wil::CoCreateInstance(CLSID_TF_ThreadMgr, CLSCTX_INPROC_SERVER); + + THROW_IF_FAILED(_threadMgrEx->ActivateEx(&_clientId, TF_TMAE_CONSOLE)); + THROW_IF_FAILED(_threadMgrEx->CreateDocumentMgr(_documentMgr.addressof())); + + TfEditCookie ecTextStore; + THROW_IF_FAILED(_documentMgr->CreateContext(_clientId, 0, static_cast(this), _context.addressof(), &ecTextStore)); + + _contextSource = _context.query(); + THROW_IF_FAILED(_contextSource->AdviseSink(IID_ITfContextOwner, static_cast(this), &_cookieContextOwner)); + THROW_IF_FAILED(_contextSource->AdviseSink(IID_ITfTextEditSink, static_cast(this), &_cookieTextEditSink)); + + THROW_IF_FAILED(_documentMgr->Push(_context.get())); +} + +void Implementation::Uninitialize() noexcept +{ + _provider.reset(); + + if (_associatedHwnd) + { + wil::com_ptr prev; + std::ignore = _threadMgrEx->AssociateFocus(_associatedHwnd, nullptr, prev.addressof()); + } + + if (_cookieTextEditSink != TF_INVALID_COOKIE) + { + std::ignore = _contextSource->UnadviseSink(_cookieTextEditSink); + } + if (_cookieContextOwner != TF_INVALID_COOKIE) + { + std::ignore = _contextSource->UnadviseSink(_cookieContextOwner); + } + + if (_documentMgr) + { + std::ignore = _documentMgr->Pop(TF_POPF_ALL); + } + if (_threadMgrEx) + { + std::ignore = _threadMgrEx->Deactivate(); + } +} + +HWND Implementation::FindWindowOfActiveTSF() const noexcept +{ + wil::com_ptr enumDocumentMgrs; + if (FAILED_LOG(_threadMgrEx->EnumDocumentMgrs(enumDocumentMgrs.addressof()))) + { + return nullptr; + } + + wil::com_ptr document; + if (FAILED_LOG(enumDocumentMgrs->Next(1, document.addressof(), nullptr))) + { + return nullptr; + } + + wil::com_ptr context; + if (FAILED_LOG(document->GetTop(context.addressof()))) + { + return nullptr; + } + + wil::com_ptr view; + if (FAILED_LOG(context->GetActiveView(view.addressof()))) + { + return nullptr; + } + + HWND hwnd; + if (FAILED_LOG(view->GetWnd(&hwnd))) + { + return nullptr; + } + + return hwnd; +} + +void Implementation::AssociateFocus(IDataProvider* provider) +{ + _provider = provider; + _associatedHwnd = _provider->GetHwnd(); + + wil::com_ptr prev; + THROW_IF_FAILED(_threadMgrEx->AssociateFocus(_associatedHwnd, _documentMgr.get(), prev.addressof())); +} + +void Implementation::Focus(IDataProvider* provider) +{ + _provider = provider; + + THROW_IF_FAILED(_threadMgrEx->SetFocus(_documentMgr.get())); +} + +void Implementation::Unfocus(IDataProvider* provider) +{ + if (!_provider || _provider != provider) + { + return; + } + + { + const auto renderer = _provider->GetRenderer(); + const auto renderData = renderer->GetRenderData(); + + renderData->LockConsole(); + const auto unlock = wil::scope_exit([&]() { + renderData->UnlockConsole(); + }); + + if (!renderData->activeComposition.text.empty()) + { + auto& comp = renderData->activeComposition; + comp.text.clear(); + comp.attributes.clear(); + renderer->NotifyPaintFrame(); + } + } + + _provider.reset(); + + if (_compositions > 0) + { + if (const auto svc = _context.try_query()) + { + svc->TerminateComposition(nullptr); + } + } +} + +bool Implementation::HasActiveComposition() const noexcept +{ + return _compositions > 0; +} + +#pragma region IUnknown + +STDMETHODIMP Implementation::QueryInterface(REFIID riid, void** ppvObj) noexcept +{ + if (!ppvObj) + { + return E_POINTER; + } + + if (IsEqualGUID(riid, IID_ITfContextOwner)) + { + *ppvObj = static_cast(this); + } + else if (IsEqualGUID(riid, IID_ITfContextOwnerCompositionSink)) + { + *ppvObj = static_cast(this); + } + else if (IsEqualGUID(riid, IID_ITfTextEditSink)) + { + *ppvObj = static_cast(this); + } + else if (IsEqualGUID(riid, IID_IUnknown)) + { + *ppvObj = static_cast(static_cast(this)); + } + else + { + *ppvObj = nullptr; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG STDMETHODCALLTYPE Implementation::AddRef() noexcept +{ + return InterlockedIncrement(&_referenceCount); +} + +ULONG STDMETHODCALLTYPE Implementation::Release() noexcept +{ + const auto r = InterlockedDecrement(&_referenceCount); + if (r == 0) + { + delete this; + } + return r; +} + +#pragma endregion IUnknown + +#pragma region ITfContextOwner + +STDMETHODIMP Implementation::GetACPFromPoint(const POINT* ptScreen, DWORD dwFlags, LONG* pacp) noexcept +{ + assert(false); + return E_NOTIMPL; +} + +// This returns rectangle of current command line edit area. +// When a user types in East Asian language, candidate window is shown at this position. +// Emoji and more panel (Win+.) is shown at the position, too. +STDMETHODIMP Implementation::GetTextExt(LONG acpStart, LONG acpEnd, RECT* prc, BOOL* pfClipped) noexcept +try +{ + if (prc) + { + *prc = _provider ? _provider->GetCursorPosition() : RECT{}; + } + + if (pfClipped) + { + *pfClipped = FALSE; + } + + return S_OK; +} +CATCH_RETURN() + +// This returns Rectangle of the text box of whole console. +// When a user taps inside the rectangle while hardware keyboard is not available, touch keyboard is invoked. +STDMETHODIMP Implementation::GetScreenExt(RECT* prc) noexcept +try +{ + if (prc) + { + *prc = _provider ? _provider->GetViewport() : RECT{}; + } + + return S_OK; +} +CATCH_RETURN() + +STDMETHODIMP Implementation::GetStatus(TF_STATUS* pdcs) noexcept +{ + if (pdcs) + { + pdcs->dwDynamicFlags = 0; + // The use of TF_SS_TRANSITORY / TS_SS_TRANSITORY is incredibly important... + // ...and it has the least complete description: + // > TS_SS_TRANSITORY: The document is expected to have a short usage cycle. + // + // Proper documentation about the flag has been lost and can only be found via archive.org: + // http://web.archive.org/web/20140520210042/http://blogs.msdn.com/b/tsfaware/archive/2007/04/25/transitory-contexts.aspx + // It states: + // > The most significant difference is that Transitory contexts don't retain state - once you end the composition [...], + // > any knowledge of the document (or any previous insertions/modifications/etc.) is gone. + // In other words, non-transitory contexts expect access to previously completed contents, which is something we cannot provide. + // Because once some text has finished composition we'll immediately send it to the shell via HandleOutput(), which we cannot undo. + // It's also the primary reason why we cannot use the WinRT CoreTextServices APIs, as they don't set TS_SS_TRANSITORY. + // + // Additionally, "short usage cycle" also significantly undersells another importance of the flag: + // If set, it enables CUAS, the Cicero Unaware Application Support, which is an emulation layer that fakes IMM32. + // Cicero is the internal code name for TSF. In other words, "TS_SS_TRANSITORY" = "Disable modern TSF". + // This results in a couple modern composition features not working (Korean reconversion primarily), + // but it's a trade-off we're forced to make, because otherwise it doesn't work at all. + // + // TS_SS_NOHIDDENTEXT tells TSF that we don't support TS_RT_HIDDEN, which is used if a document contains hidden markup + // inside the text. For instance an HTML document contains tags which aren't visible, but nonetheless exist. + // It's not publicly documented, but allegedly specifying this flag results in a minor performance uplift. + // Ironically, the only two places that mention this flag internally state: + // > perf: we could check TS_SS_NOHIDDENTEXT for better perf + pdcs->dwStaticFlags = TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT; + } + + return S_OK; +} + +STDMETHODIMP Implementation::GetWnd(HWND* phwnd) noexcept +{ + *phwnd = _provider ? _provider->GetHwnd() : nullptr; + return S_OK; +} + +STDMETHODIMP Implementation::GetAttribute(REFGUID rguidAttribute, VARIANT* pvarValue) noexcept +{ + return E_NOTIMPL; +} + +#pragma endregion ITfContextOwner + +#pragma region ITfContextOwnerCompositionSink + +STDMETHODIMP Implementation::OnStartComposition(ITfCompositionView* pComposition, BOOL* pfOk) noexcept +try +{ + _compositions++; + *pfOk = TRUE; + return S_OK; +} +CATCH_RETURN() + +STDMETHODIMP Implementation::OnUpdateComposition(ITfCompositionView* pComposition, ITfRange* pRangeNew) noexcept +{ + return S_OK; +} + +STDMETHODIMP Implementation::OnEndComposition(ITfCompositionView* pComposition) noexcept +try +{ + if (_compositions <= 0) + { + return E_FAIL; + } + + _compositions--; + if (_compositions == 0) + { + // https://learn.microsoft.com/en-us/windows/win32/api/msctf/nf-msctf-itfcontext-requesteditsession + // > A text service can request an edit session within the context of an existing edit session, + // > provided a write access session is not requested within a read-only session. + // --> Requires TF_ES_ASYNC to work properly. TF_ES_ASYNCDONTCARE randomly fails because... TSF. + std::ignore = _request(_editSessionCompositionUpdate, TF_ES_READWRITE | TF_ES_ASYNC); + } + + return S_OK; +} +CATCH_RETURN() + +#pragma endregion ITfContextOwnerCompositionSink + +#pragma region ITfTextEditSink + +STDMETHODIMP Implementation::OnEndEdit(ITfContext* pic, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord) noexcept +try +{ + if (_compositions == 1) + { + // https://learn.microsoft.com/en-us/windows/win32/api/msctf/nf-msctf-itfcontext-requesteditsession + // > A text service can request an edit session within the context of an existing edit session, + // > provided a write access session is not requested within a read-only session. + // --> Requires TF_ES_ASYNC to work properly. TF_ES_ASYNCDONTCARE randomly fails because... TSF. + std::ignore = _request(_editSessionCompositionUpdate, TF_ES_READWRITE | TF_ES_ASYNC); + } + + return S_OK; +} +CATCH_RETURN() + +#pragma endregion ITfTextEditSink + +Implementation::EditSessionProxyBase::EditSessionProxyBase(Implementation* self) noexcept : + self{ self } +{ +} + +STDMETHODIMP Implementation::EditSessionProxyBase::QueryInterface(REFIID riid, void** ppvObj) noexcept +{ + if (!ppvObj) + { + return E_POINTER; + } + + if (IsEqualGUID(riid, IID_ITfEditSession)) + { + *ppvObj = static_cast(this); + } + else if (IsEqualGUID(riid, IID_IUnknown)) + { + *ppvObj = static_cast(this); + } + else + { + *ppvObj = nullptr; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG STDMETHODCALLTYPE Implementation::EditSessionProxyBase::AddRef() noexcept +{ + return InterlockedIncrement(&referenceCount); +} + +ULONG STDMETHODCALLTYPE Implementation::EditSessionProxyBase::Release() noexcept +{ + FAIL_FAST_IF(referenceCount == 0); + return InterlockedDecrement(&referenceCount); +} + +[[nodiscard]] HRESULT Implementation::_request(EditSessionProxyBase& session, DWORD flags) const +{ + // Some of the sessions are async, and we don't want to send another request if one is still in flight. + if (session.referenceCount) + { + return S_FALSE; + } + + HRESULT hr = S_OK; + THROW_IF_FAILED(_context->RequestEditSession(_clientId, &session, flags, &hr)); + RETURN_IF_FAILED(hr); + return S_OK; +} + +void Implementation::_doCompositionUpdate(TfEditCookie ec) +{ + wil::com_ptr fullRange; + LONG fullRangeLength; + THROW_IF_FAILED(_context->GetStart(ec, fullRange.addressof())); + THROW_IF_FAILED(fullRange->ShiftEnd(ec, LONG_MAX, &fullRangeLength, nullptr)); + + std::wstring finalizedString; + std::wstring activeComposition; + til::small_vector activeCompositionRanges; + bool firstRange = true; + + const GUID* guids[] = { &GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE }; + wil::com_ptr props; + THROW_IF_FAILED(_context->TrackProperties(&guids[0], ARRAYSIZE(guids), nullptr, 0, props.addressof())); + + wil::com_ptr enumRanges; + THROW_IF_FAILED(props->EnumRanges(ec, enumRanges.addressof(), fullRange.get())); + + // IEnumTfRanges::Next returns S_FALSE when it has reached the end of the list. + // This includes any call where the number of returned items is less than what was requested. + for (HRESULT nextResult = S_OK; nextResult == S_OK;) + { + ITfRange* ranges[8]; + ULONG rangesCount; + nextResult = enumRanges->Next(ARRAYSIZE(ranges), &ranges[0], &rangesCount); + + const auto cleanup = wil::scope_exit([&] { + for (ULONG i = 0; i < rangesCount; ++i) + { + ranges[i]->Release(); + } + }); + + for (ULONG i = 0; i < rangesCount; ++i) + { + const auto range = ranges[i]; + + bool composing = false; + TfGuidAtom atom = TF_INVALID_GUIDATOM; + { + wil::unique_variant var; + THROW_IF_FAILED(props->GetValue(ec, range, var.addressof())); + + wil::com_ptr propVal; + wil::com_query_to(var.punkVal, propVal.addressof()); + + unique_tf_propertyval propVals[2]; + THROW_IF_FAILED(propVal->Next(2, propVals[0].addressof(), nullptr)); + + for (const auto& val : propVals) + { + if (IsEqualGUID(val.guidId, GUID_PROP_COMPOSING)) + { + composing = V_VT(&val.varValue) == VT_I4 && V_I4(&val.varValue) != 0; + } + else if (IsEqualGUID(val.guidId, GUID_PROP_ATTRIBUTE)) + { + atom = V_VT(&val.varValue) == VT_I4 ? static_cast(V_I4(&val.varValue)) : TF_INVALID_GUIDATOM; + } + } + } + + size_t totalLen = 0; + for (;;) + { + // GetText() won't throw if the range is empty. It'll simply return len == 0. + // However, you'll likely never see this happen with a bufCap this large (try 16 instead or something). + // It seems TSF doesn't support such large compositions in any language. + static constexpr ULONG bufCap = 128; + WCHAR buf[bufCap]; + ULONG len = bufCap; + THROW_IF_FAILED(range->GetText(ec, TF_TF_MOVESTART, buf, len, &len)); + + if (!composing && firstRange) + { + finalizedString.append(buf, len); + } + else + { + activeComposition.append(buf, len); + } + + totalLen += len; + + if (len < bufCap) + { + break; + } + } + + const auto attr = _textAttributeFromAtom(atom); + activeCompositionRanges.emplace_back(totalLen, attr); + + firstRange = false; + } + } + + LONG cursorPos = LONG_MAX; + { + // According to the docs this may result in TF_E_NOSELECTION. While I haven't actually seen that happen myself yet, + // I don't want this to result in log-spam, which is why this doesn't use SUCCEEDED_LOG(). + unique_tf_selection sel; + ULONG selCount; + if (SUCCEEDED(_context->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &sel, &selCount)) && selCount == 1) + { + wil::com_ptr start; + THROW_IF_FAILED(_context->GetStart(ec, start.addressof())); + + TF_HALTCOND hc{ + .pHaltRange = sel.range, + .aHaltPos = sel.style.ase == TF_AE_START ? TF_ANCHOR_START : TF_ANCHOR_END, + }; + THROW_IF_FAILED(start->ShiftEnd(ec, LONG_MAX, &cursorPos, &hc)); + } + + // Compensate for the fact that we'll be erasing the start of the string below. + cursorPos -= static_cast(finalizedString.size()); + cursorPos = std::clamp(cursorPos, 0l, static_cast(activeComposition.size())); + } + + if (!finalizedString.empty()) + { + // Erase the text that's done with composition from the context. + wil::com_ptr range; + LONG cch; + THROW_IF_FAILED(_context->GetStart(ec, range.addressof())); + THROW_IF_FAILED(range->ShiftEnd(ec, static_cast(finalizedString.size()), &cch, nullptr)); + THROW_IF_FAILED(range->SetText(ec, 0, nullptr, 0)); + } + + if (_provider) + { + { + const auto renderer = _provider->GetRenderer(); + const auto renderData = renderer->GetRenderData(); + + renderData->LockConsole(); + const auto unlock = wil::scope_exit([&]() { + renderData->UnlockConsole(); + }); + + auto& comp = renderData->activeComposition; + comp.text = std::move(activeComposition); + comp.attributes = std::move(activeCompositionRanges); + // The code block above that calculates the `cursorPos` will clamp it to a positive number. + comp.cursorPos = static_cast(cursorPos); + renderer->NotifyPaintFrame(); + } + + if (!finalizedString.empty()) + { + _provider->HandleOutput(finalizedString); + } + } +} + +TextAttribute Implementation::_textAttributeFromAtom(TfGuidAtom atom) const +{ + TextAttribute attr; + + // You get TF_INVALID_GUIDATOM by (for instance) using the Vietnamese Telex IME. + // A dashed underline is used because that's what Firefox used at the time and it + // looked kind of neat. In the past, conhost used a blue background and white text. + if (atom == TF_INVALID_GUIDATOM) + { + attr.SetUnderlineStyle(UnderlineStyle::DashedUnderlined); + return attr; + } + + GUID guid; + if (FAILED_LOG(_categoryMgr->GetGUID(atom, &guid))) + { + return attr; + } + + wil::com_ptr dai; + if (FAILED_LOG(_displayAttributeMgr->GetDisplayAttributeInfo(guid, dai.addressof(), nullptr))) + { + return attr; + } + + TF_DISPLAYATTRIBUTE da; + THROW_IF_FAILED(dai->GetAttributeInfo(&da)); + + if (da.crText.type != TF_CT_NONE) + { + attr.SetForeground(_colorFromDisplayAttribute(da.crText)); + } + if (da.crBk.type != TF_CT_NONE) + { + attr.SetBackground(_colorFromDisplayAttribute(da.crBk)); + } + if (da.lsStyle >= TF_LS_NONE && da.lsStyle <= TF_LS_SQUIGGLE) + { + static constexpr UnderlineStyle lut[] = { + /* TF_LS_NONE */ UnderlineStyle::NoUnderline, + /* TF_LS_SOLID */ UnderlineStyle::SinglyUnderlined, + /* TF_LS_DOT */ UnderlineStyle::DottedUnderlined, + /* TF_LS_DASH */ UnderlineStyle::DashedUnderlined, + /* TF_LS_SQUIGGLE */ UnderlineStyle::CurlyUnderlined, + }; + attr.SetUnderlineStyle(lut[da.lsStyle]); + } + // You can reproduce bold lines with the Japanese IME by typing "kyouhaishaheiku" and pressing space. + // The IME will allow you to navigate between the 3 parts of the composition and the current one is + // marked as fBoldLine. We don't support bold lines so we just use a double underline instead. + if (da.fBoldLine) + { + attr.SetUnderlineStyle(UnderlineStyle::DoublyUnderlined); + } + if (da.crLine.type != TF_CT_NONE) + { + attr.SetUnderlineColor(_colorFromDisplayAttribute(da.crLine)); + } + + return attr; +} + +COLORREF Implementation::_colorFromDisplayAttribute(TF_DA_COLOR color) +{ + switch (color.type) + { + case TF_CT_SYSCOLOR: + return GetSysColor(color.nIndex); + case TF_CT_COLORREF: + return color.cr; + default: + // If you get here you either called this when .type is TF_CT_NONE + // (don't call in that case; there's no color to be had), or + // there's a new .type which you need to add. + assert(false); + return 0; + } +} diff --git a/src/tsf/Implementation.h b/src/tsf/Implementation.h new file mode 100644 index 00000000000..7071141cae5 --- /dev/null +++ b/src/tsf/Implementation.h @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +class TextAttribute; + +namespace Microsoft::Console::Render +{ + class Renderer; +} + +namespace Microsoft::Console::TSF +{ + struct IDataProvider; + + struct Implementation : ITfContextOwner, ITfContextOwnerCompositionSink, ITfTextEditSink + { + virtual ~Implementation() = default; + + void Initialize(); + void Uninitialize() noexcept; + HWND FindWindowOfActiveTSF() const noexcept; + void AssociateFocus(IDataProvider* provider); + void Focus(IDataProvider* provider); + void Unfocus(IDataProvider* provider); + bool HasActiveComposition() const noexcept; + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj) noexcept override; + ULONG STDMETHODCALLTYPE AddRef() noexcept override; + ULONG STDMETHODCALLTYPE Release() noexcept override; + + // ITfContextOwner + STDMETHODIMP GetACPFromPoint(const POINT* ptScreen, DWORD dwFlags, LONG* pacp) noexcept override; + STDMETHODIMP GetTextExt(LONG acpStart, LONG acpEnd, RECT* prc, BOOL* pfClipped) noexcept override; + STDMETHODIMP GetScreenExt(RECT* prc) noexcept override; + STDMETHODIMP GetStatus(TF_STATUS* pdcs) noexcept override; + STDMETHODIMP GetWnd(HWND* phwnd) noexcept override; + STDMETHODIMP GetAttribute(REFGUID rguidAttribute, VARIANT* pvarValue) noexcept override; + + // ITfContextOwnerCompositionSink methods + STDMETHODIMP OnStartComposition(ITfCompositionView* pComposition, BOOL* pfOk) noexcept override; + STDMETHODIMP OnUpdateComposition(ITfCompositionView* pComposition, ITfRange* pRangeNew) noexcept override; + STDMETHODIMP OnEndComposition(ITfCompositionView* pComposition) noexcept override; + + // ITfTextEditSink methods + STDMETHODIMP OnEndEdit(ITfContext* pic, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord) noexcept override; + + private: + struct EditSessionProxyBase : ITfEditSession + { + explicit EditSessionProxyBase(Implementation* self) noexcept; + virtual ~EditSessionProxyBase() = default; + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj) noexcept override; + ULONG STDMETHODCALLTYPE AddRef() noexcept override; + ULONG STDMETHODCALLTYPE Release() noexcept override; + + ULONG referenceCount = 0; + Implementation* self = nullptr; + }; + + // In the past we had 3 different `ITfEditSession`s (update, finish, cleanup). + // Due to refactoring only 1 is left now, but this code remains in case we need more in the future. + // It allows you to statically bind a callback function to a `ITfEditSession` proxy type. + template + struct EditSessionProxy : EditSessionProxyBase + { + using EditSessionProxyBase::EditSessionProxyBase; + + // ITfEditSession method + STDMETHODIMP DoEditSession(TfEditCookie ec) noexcept override + { + try + { + (self->*Callback)(ec); + return S_OK; + } + CATCH_RETURN(); + } + }; + + [[nodiscard]] HRESULT _request(EditSessionProxyBase& session, DWORD flags) const; + void _doCompositionUpdate(TfEditCookie ec); + TextAttribute _textAttributeFromAtom(TfGuidAtom atom) const; + static COLORREF _colorFromDisplayAttribute(TF_DA_COLOR color); + + ULONG _referenceCount = 1; + + wil::com_ptr _provider; + HWND _associatedHwnd = nullptr; + + wil::com_ptr_t _categoryMgr; + wil::com_ptr _displayAttributeMgr; + wil::com_ptr _threadMgrEx; + wil::com_ptr _documentMgr; + wil::com_ptr _context; + wil::com_ptr _contextSource; + DWORD _cookieContextOwner = TF_INVALID_COOKIE; + DWORD _cookieTextEditSink = TF_INVALID_COOKIE; + TfClientId _clientId = TF_CLIENTID_NULL; + + EditSessionProxy<&Implementation::_doCompositionUpdate> _editSessionCompositionUpdate{ this }; + int _compositions = 0; + }; +} diff --git a/src/tsf/TfCatUtil.cpp b/src/tsf/TfCatUtil.cpp deleted file mode 100644 index 2ded71dd88e..00000000000 --- a/src/tsf/TfCatUtil.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfCatUtil.cpp - -Abstract: - - This file implements the CicCategoryMgr Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#include "precomp.h" -#include "TfCatUtil.h" - -//+--------------------------------------------------------------------------- -// -// CicCategoryMgr::ctor -// CicCategoryMgr::dtor -// -//---------------------------------------------------------------------------- - -CicCategoryMgr::CicCategoryMgr() = default; - -CicCategoryMgr::~CicCategoryMgr() = default; - -//+--------------------------------------------------------------------------- -// -// CicCategoryMgr::GetGUIDFromGUIDATOM -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CicCategoryMgr::GetGUIDFromGUIDATOM(TfGuidAtom guidatom, GUID* pguid) -{ - return m_pcat->GetGUID(guidatom, pguid); -} - -//+--------------------------------------------------------------------------- -// -// CicCategoryMgr::InitCategoryInstance -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CicCategoryMgr::InitCategoryInstance() -{ - // - // Create ITfCategoryMgr instance. - // - return ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_pcat)); -} diff --git a/src/tsf/TfCatUtil.h b/src/tsf/TfCatUtil.h deleted file mode 100644 index c749098dd50..00000000000 --- a/src/tsf/TfCatUtil.h +++ /dev/null @@ -1,38 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfCatUtil.h - -Abstract: - - This file defines the CicCategoryMgr Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#pragma once - -class CicCategoryMgr -{ -public: - CicCategoryMgr(); - virtual ~CicCategoryMgr(); - -public: - [[nodiscard]] HRESULT GetGUIDFromGUIDATOM(TfGuidAtom guidatom, GUID* pguid); - [[nodiscard]] HRESULT InitCategoryInstance(); - - inline ITfCategoryMgr* GetCategoryMgr() { return m_pcat.get(); } - -private: - wil::com_ptr_nothrow m_pcat; -}; diff --git a/src/tsf/TfConvArea.cpp b/src/tsf/TfConvArea.cpp deleted file mode 100644 index 940bb7af098..00000000000 --- a/src/tsf/TfConvArea.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfConvArea.cpp - -Abstract: - - This file implements the CConversionArea Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#include "precomp.h" -#include "ConsoleTSF.h" -#include "TfCtxtComp.h" -#include "TfConvArea.h" - -//+--------------------------------------------------------------------------- -// CConversionArea -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CConversionArea::DrawComposition(const std::wstring_view CompStr, - const std::vector& DisplayAttributes, - const DWORD CompCursorPos) -{ - // Set up colors. - static const std::array colors{ DEFAULT_COMP_ENTERED, - DEFAULT_COMP_ALREADY_CONVERTED, - DEFAULT_COMP_CONVERSION, - DEFAULT_COMP_YET_CONVERTED, - DEFAULT_COMP_INPUT_ERROR, - DEFAULT_COMP_INPUT_ERROR, - DEFAULT_COMP_INPUT_ERROR, - DEFAULT_COMP_INPUT_ERROR }; - - const auto encodedAttributes = _DisplayAttributesToEncodedAttributes(DisplayAttributes, - CompCursorPos); - - std::span attributes(encodedAttributes.data(), encodedAttributes.size()); - std::span colorArray(colors.data(), colors.size()); - - return ImeComposeData(CompStr, attributes, colorArray); -} - -[[nodiscard]] HRESULT CConversionArea::ClearComposition() -{ - return ImeClearComposeData(); -} - -[[nodiscard]] HRESULT CConversionArea::DrawResult(const std::wstring_view ResultStr) -{ - return ImeComposeResult(ResultStr); -} - -[[nodiscard]] std::vector CConversionArea::_DisplayAttributesToEncodedAttributes(const std::vector& DisplayAttributes, - const DWORD CompCursorPos) -{ - std::vector encodedAttrs; - for (const auto& da : DisplayAttributes) - { - BYTE bAttr; - - if (da.bAttr == TF_ATTR_OTHER || da.bAttr > TF_ATTR_FIXEDCONVERTED) - { - bAttr = ATTR_TARGET_CONVERTED; - } - else - { - if (da.bAttr == TF_ATTR_INPUT_ERROR) - { - bAttr = ATTR_CONVERTED; - } - else - { - bAttr = (BYTE)da.bAttr; - } - } - encodedAttrs.emplace_back(bAttr); - } - - if (CompCursorPos != -1) - { - if (CompCursorPos == 0) - { - encodedAttrs[CompCursorPos] |= CONIME_CURSOR_LEFT; // special handling for ConSrv... 0x20 = COMMON_LVB_GRID_SINGLEFLAG + COMMON_LVB_GRID_LVERTICAL - } - else if (CompCursorPos - 1 < DisplayAttributes.size()) - { - encodedAttrs[CompCursorPos - 1] |= CONIME_CURSOR_RIGHT; // special handling for ConSrv... 0x10 = COMMON_LVB_GRID_SINGLEFLAG + COMMON_LVB_GRID_RVERTICAL - } - } - - return encodedAttrs; -} diff --git a/src/tsf/TfConvArea.h b/src/tsf/TfConvArea.h deleted file mode 100644 index e6ae9466ed5..00000000000 --- a/src/tsf/TfConvArea.h +++ /dev/null @@ -1,44 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfConvArea.h - -Abstract: - - This file defines the CConversionAreaJapanese Interface Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#pragma once - -//+--------------------------------------------------------------------------- -// -// CConversionArea::Pure virtual class -// -//---------------------------------------------------------------------------- - -class CConversionArea -{ -public: - [[nodiscard]] HRESULT DrawComposition(const std::wstring_view CompStr, - const std::vector& DisplayAttributes, - const DWORD CompCursorPos = -1); - - [[nodiscard]] HRESULT ClearComposition(); - - [[nodiscard]] HRESULT DrawResult(const std::wstring_view ResultStr); - -private: - [[nodiscard]] std::vector _DisplayAttributesToEncodedAttributes(const std::vector& DisplayAttributes, - const DWORD CompCursorPos); -}; diff --git a/src/tsf/TfCtxtComp.h b/src/tsf/TfCtxtComp.h deleted file mode 100644 index 5b5ef460319..00000000000 --- a/src/tsf/TfCtxtComp.h +++ /dev/null @@ -1,44 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfCtxtComp.h - -Abstract: - - This file defines the Context of Composition Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// CCompCursorPos - -class CCompCursorPos -{ -public: - CCompCursorPos() - { - m_CursorPosition = 0; - } - - void SetCursorPosition(DWORD CursorPosition) - { - m_CursorPosition = CursorPosition; - } - - DWORD GetCursorPosition() { return m_CursorPosition; } - -private: - DWORD m_CursorPosition; -}; diff --git a/src/tsf/TfDispAttr.cpp b/src/tsf/TfDispAttr.cpp deleted file mode 100644 index 48e1d044e85..00000000000 --- a/src/tsf/TfDispAttr.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfDispAttr.cpp - -Abstract: - - This file implements the CicDisplayAttributeMgr Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#include "precomp.h" -#include "TfDispAttr.h" - -//+--------------------------------------------------------------------------- -// -// CicDisplayAttributeMgr::ctor -// CicDisplayAttributeMgr::dtor -// -//---------------------------------------------------------------------------- - -CicDisplayAttributeMgr::CicDisplayAttributeMgr() = default; - -CicDisplayAttributeMgr::~CicDisplayAttributeMgr() = default; - -//+--------------------------------------------------------------------------- -// -// CicDisplayAttributeMgr::GetDisplayAttributeTrackPropertyRange -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CicDisplayAttributeMgr::GetDisplayAttributeTrackPropertyRange(TfEditCookie ec, - ITfContext* pic, - ITfRange* pRange, - ITfReadOnlyProperty** ppProp, - IEnumTfRanges** ppEnum, - ULONG* pulNumProp) -{ - auto hr = E_FAIL; - try - { - auto ulNumProp = static_cast(m_DispAttrProp.size()); - if (ulNumProp) - { - // TrackProperties wants an array of GUID *'s - auto ppguidProp = std::make_unique(ulNumProp); - for (ULONG i = 0; i < ulNumProp; i++) - { - ppguidProp[i] = &m_DispAttrProp.at(i); - } - - wil::com_ptr pProp; - if (SUCCEEDED(hr = pic->TrackProperties(ppguidProp.get(), ulNumProp, nullptr, NULL, &pProp))) - { - hr = pProp->EnumRanges(ec, ppEnum, pRange); - if (SUCCEEDED(hr)) - { - *ppProp = pProp.detach(); - } - } - - if (SUCCEEDED(hr)) - { - *pulNumProp = ulNumProp; - } - } - } - CATCH_RETURN(); - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CicDisplayAttributeMgr::GetDisplayAttributeData -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CicDisplayAttributeMgr::GetDisplayAttributeData(ITfCategoryMgr* pcat, - TfEditCookie ec, - ITfReadOnlyProperty* pProp, - ITfRange* pRange, - TF_DISPLAYATTRIBUTE* pda, - TfGuidAtom* pguid, - ULONG /*ulNumProp*/) -{ - VARIANT var; - - auto hr = E_FAIL; - - if (SUCCEEDED(pProp->GetValue(ec, pRange, &var))) - { - FAIL_FAST_IF(!(var.vt == VT_UNKNOWN)); - - wil::com_ptr_nothrow pEnumPropertyVal; - if (wil::try_com_query_to(var.punkVal, &pEnumPropertyVal)) - { - TF_PROPERTYVAL tfPropVal; - while (pEnumPropertyVal->Next(1, &tfPropVal, nullptr) == S_OK) - { - if (tfPropVal.varValue.vt == VT_EMPTY) - { - continue; // prop has no value over this span - } - - FAIL_FAST_IF(!(tfPropVal.varValue.vt == VT_I4)); // expecting GUIDATOMs - - auto gaVal = (TfGuidAtom)tfPropVal.varValue.lVal; - - GUID guid; - pcat->GetGUID(gaVal, &guid); - - wil::com_ptr_nothrow pDAI; - if (SUCCEEDED(m_pDAM->GetDisplayAttributeInfo(guid, &pDAI, NULL))) - { - // - // Issue: for simple apps. - // - // Small apps can not show multi underline. So - // this helper function returns only one - // DISPLAYATTRIBUTE structure. - // - if (pda) - { - pDAI->GetAttributeInfo(pda); - } - - if (pguid) - { - *pguid = gaVal; - } - - hr = S_OK; - break; - } - } - } - VariantClear(&var); - } - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CicDisplayAttributeMgr::InitCategoryInstance -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CicDisplayAttributeMgr::InitDisplayAttributeInstance(ITfCategoryMgr* pcat) -{ - HRESULT hr; - - // - // Create ITfDisplayAttributeMgr instance. - // - if (FAILED(hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_pDAM)))) - { - return hr; - } - - wil::com_ptr_nothrow pEnumProp; - pcat->EnumItemsInCategory(GUID_TFCAT_DISPLAYATTRIBUTEPROPERTY, &pEnumProp); - - // - // make a database for Display Attribute Properties. - // - if (pEnumProp) - { - GUID guidProp; - - try - { - // - // add System Display Attribute first. - // so no other Display Attribute property overwrite it. - // - m_DispAttrProp.emplace_back(GUID_PROP_ATTRIBUTE); - - while (pEnumProp->Next(1, &guidProp, nullptr) == S_OK) - { - if (!IsEqualGUID(guidProp, GUID_PROP_ATTRIBUTE)) - { - m_DispAttrProp.emplace_back(guidProp); - } - } - } - CATCH_RETURN(); - } - return hr; -} diff --git a/src/tsf/TfDispAttr.h b/src/tsf/TfDispAttr.h deleted file mode 100644 index 74fc4dd7f25..00000000000 --- a/src/tsf/TfDispAttr.h +++ /dev/null @@ -1,51 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfDispAttr.h - -Abstract: - - This file defines the CicDisplayAttributeMgr Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#pragma once - -class CicDisplayAttributeMgr -{ -public: - CicDisplayAttributeMgr(); - virtual ~CicDisplayAttributeMgr(); - -public: - [[nodiscard]] HRESULT GetDisplayAttributeTrackPropertyRange(TfEditCookie ec, - ITfContext* pic, - ITfRange* pRange, - ITfReadOnlyProperty** ppProp, - IEnumTfRanges** ppEnum, - ULONG* pulNumProp); - [[nodiscard]] HRESULT GetDisplayAttributeData(ITfCategoryMgr* pcat, - TfEditCookie ec, - ITfReadOnlyProperty* pProp, - ITfRange* pRange, - TF_DISPLAYATTRIBUTE* pda, - TfGuidAtom* pguid, - ULONG ulNumProp); - [[nodiscard]] HRESULT InitDisplayAttributeInstance(ITfCategoryMgr* pcat); - - inline ITfDisplayAttributeMgr* GetDisplayAttributeMgr() { return m_pDAM.get(); } - -private: - wil::com_ptr_nothrow m_pDAM; - std::vector m_DispAttrProp; -}; diff --git a/src/tsf/TfEditSession.cpp b/src/tsf/TfEditSession.cpp deleted file mode 100644 index 2e728d0c9ed..00000000000 --- a/src/tsf/TfEditSession.cpp +++ /dev/null @@ -1,1167 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfEditSession.cpp - -Abstract: - - This file implements the CEditSessionObject Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#include "precomp.h" -#include "TfConvArea.h" -#include "TfCatUtil.h" -#include "TfDispAttr.h" -#include "TfEditSession.h" - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::IUnknown::QueryInterface -// CEditSessionObject::IUnknown::AddRef -// CEditSessionObject::IUnknown::Release -// -//---------------------------------------------------------------------------- - -STDAPI CEditSessionObject::QueryInterface(REFIID riid, void** ppvObj) -{ - *ppvObj = nullptr; - - if (IsEqualIID(riid, IID_ITfEditSession)) - { - *ppvObj = static_cast(this); - } - else if (IsEqualIID(riid, IID_IUnknown)) - { - *ppvObj = static_cast(this); - } - - if (*ppvObj) - { - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -STDAPI_(ULONG) -CEditSessionObject::AddRef() -{ - return ++m_cRef; -} - -STDAPI_(ULONG) -CEditSessionObject::Release() -{ - long cr; - - cr = --m_cRef; - FAIL_FAST_IF(!(cr >= 0)); - - if (cr == 0) - { - delete this; - } - - return cr; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::GetAllTextRange -// -//---------------------------------------------------------------------------- - -// static -[[nodiscard]] HRESULT CEditSessionObject::GetAllTextRange(TfEditCookie ec, - ITfContext* ic, - ITfRange** range, - LONG* lpTextLength, - TF_HALTCOND* lpHaltCond) -{ - HRESULT hr; - - // - // init lpTextLength first. - // - *lpTextLength = 0; - - // - // Create the range that covers all the text. - // - wil::com_ptr_nothrow rangeFull; - if (FAILED(hr = ic->GetStart(ec, &rangeFull))) - { - return hr; - } - - LONG cch = 0; - if (FAILED(hr = rangeFull->ShiftEnd(ec, LONG_MAX, &cch, lpHaltCond))) - { - return hr; - } - - if (FAILED(hr = rangeFull->Clone(range))) - { - return hr; - } - - *lpTextLength = cch; - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::SetTextInRange -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionObject::SetTextInRange(TfEditCookie ec, - ITfRange* range, - __in_ecount_opt(len) LPWSTR psz, - DWORD len) -{ - auto hr = E_FAIL; - if (g_pConsoleTSF) - { - g_pConsoleTSF->SetModifyingDocFlag(TRUE); - hr = range->SetText(ec, 0, psz, len); - g_pConsoleTSF->SetModifyingDocFlag(FALSE); - } - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::ClearTextInRange -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionObject::ClearTextInRange(TfEditCookie ec, ITfRange* range) -{ - // - // Clear the text in Cicero TOM - // - return SetTextInRange(ec, range, nullptr, 0); -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::_GetCursorPosition -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionObject::_GetCursorPosition(TfEditCookie ec, CCompCursorPos& CompCursorPos) -{ - auto pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; - if (pic == nullptr) - { - return E_FAIL; - } - - HRESULT hr; - ULONG cFetched; - - TF_SELECTION sel; - sel.range = nullptr; - - if (SUCCEEDED(hr = pic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &sel, &cFetched))) - { - wil::com_ptr_nothrow start; - LONG ich; - TF_HALTCOND hc; - - hc.pHaltRange = sel.range; - hc.aHaltPos = (sel.style.ase == TF_AE_START) ? TF_ANCHOR_START : TF_ANCHOR_END; - hc.dwFlags = 0; - - if (SUCCEEDED(hr = GetAllTextRange(ec, pic, &start, &ich, &hc))) - { - CompCursorPos.SetCursorPosition(ich); - } - - SafeReleaseClear(sel.range); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::_GetTextAndAttribute -// -//---------------------------------------------------------------------------- - -// -// Get text and attribute in given range -// -// ITfRange::range -// TF_ANCHOR_START -// |======================================================================| -// +--------------------+ #+----------+ -// |ITfRange::pPropRange| #|pPropRange| -// +--------------------+ #+----------+ -// | GUID_ATOM | # -// +--------------------+ # -// ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^# -// ITfRange::gap_range gap_range # -// # -// V -// ITfRange::no_display_attribute_range -// result_comp -// +1 <- 0 -> -1 -// - -[[nodiscard]] HRESULT CEditSessionObject::_GetTextAndAttribute(TfEditCookie ec, - ITfRange* rangeIn, - std::wstring& CompStr, - std::vector& CompGuid, - std::wstring& ResultStr, - BOOL bInWriteSession, - CicCategoryMgr* pCicCatMgr, - CicDisplayAttributeMgr* pCicDispAttr) -{ - HRESULT hr; - - auto pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; - if (pic == nullptr) - { - return E_FAIL; - } - - // - // Get no display attribute range if there exist. - // Otherwise, result range is the same to input range. - // - LONG result_comp; - wil::com_ptr_nothrow no_display_attribute_range; - if (FAILED(hr = rangeIn->Clone(&no_display_attribute_range))) - { - return hr; - } - - const GUID* guids[] = { &GUID_PROP_COMPOSING }; - const int guid_size = sizeof(guids) / sizeof(GUID*); - - if (FAILED(hr = _GetNoDisplayAttributeRange(ec, rangeIn, guids, guid_size, no_display_attribute_range.get()))) - { - return hr; - } - - wil::com_ptr_nothrow propComp; - if (FAILED(hr = pic->TrackProperties(guids, guid_size, // system property - NULL, - 0, // application property - &propComp))) - { - return hr; - } - - wil::com_ptr_nothrow enumComp; - if (FAILED(hr = propComp->EnumRanges(ec, &enumComp, rangeIn))) - { - return hr; - } - - wil::com_ptr_nothrow range; - while (enumComp->Next(1, &range, nullptr) == S_OK) - { - VARIANT var; - auto fCompExist = FALSE; - - hr = propComp->GetValue(ec, range.get(), &var); - if (S_OK == hr) - { - wil::com_ptr_nothrow EnumPropVal; - if (wil::try_com_query_to(var.punkVal, &EnumPropVal)) - { - TF_PROPERTYVAL tfPropertyVal; - - while (EnumPropVal->Next(1, &tfPropertyVal, nullptr) == S_OK) - { - for (auto i = 0; i < guid_size; i++) - { - if (IsEqualGUID(tfPropertyVal.guidId, *guids[i])) - { - if ((V_VT(&tfPropertyVal.varValue) == VT_I4 && V_I4(&tfPropertyVal.varValue) != 0)) - { - fCompExist = TRUE; - break; - } - } - } - - VariantClear(&tfPropertyVal.varValue); - - if (fCompExist) - { - break; - } - } - } - } - - VariantClear(&var); - - ULONG ulNumProp; - - wil::com_ptr_nothrow enumProp; - wil::com_ptr_nothrow prop; - if (FAILED(hr = pCicDispAttr->GetDisplayAttributeTrackPropertyRange(ec, pic, range.get(), &prop, &enumProp, &ulNumProp))) - { - return hr; - } - - // use text range for get text - wil::com_ptr_nothrow textRange; - if (FAILED(hr = range->Clone(&textRange))) - { - return hr; - } - - // use text range for gap text (no property range). - wil::com_ptr_nothrow gap_range; - if (FAILED(hr = range->Clone(&gap_range))) - { - return hr; - } - - wil::com_ptr_nothrow pPropRange; - while (enumProp->Next(1, &pPropRange, nullptr) == S_OK) - { - // pick up the gap up to the next property - gap_range->ShiftEndToRange(ec, pPropRange.get(), TF_ANCHOR_START); - - // - // GAP range - // - no_display_attribute_range->CompareStart(ec, gap_range.get(), TF_ANCHOR_START, &result_comp); - LOG_IF_FAILED(_GetTextAndAttributeGapRange(ec, - gap_range.get(), - result_comp, - CompStr, - CompGuid, - ResultStr)); - - // - // Get display attribute data if some GUID_ATOM exist. - // - TF_DISPLAYATTRIBUTE da; - auto guidatom = TF_INVALID_GUIDATOM; - - LOG_IF_FAILED(pCicDispAttr->GetDisplayAttributeData(pCicCatMgr->GetCategoryMgr(), - ec, - prop.get(), - pPropRange.get(), - &da, - &guidatom, - ulNumProp)); - - // - // Property range - // - no_display_attribute_range->CompareStart(ec, pPropRange.get(), TF_ANCHOR_START, &result_comp); - - // Adjust GAP range's start anchor to the end of property range. - gap_range->ShiftStartToRange(ec, pPropRange.get(), TF_ANCHOR_END); - - // - // Get property text - // - LOG_IF_FAILED(_GetTextAndAttributePropertyRange(ec, - pPropRange.get(), - fCompExist, - result_comp, - bInWriteSession, - da, - guidatom, - CompStr, - CompGuid, - ResultStr)); - - } // while - - // the last non-attr - textRange->ShiftStartToRange(ec, gap_range.get(), TF_ANCHOR_START); - textRange->ShiftEndToRange(ec, range.get(), TF_ANCHOR_END); - - BOOL fEmpty; - while (textRange->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty) - { - WCHAR wstr0[256 + 1]; - ULONG ulcch0 = ARRAYSIZE(wstr0) - 1; - textRange->GetText(ec, TF_TF_MOVESTART, wstr0, ulcch0, &ulcch0); - - TfGuidAtom guidatom; - guidatom = TF_INVALID_GUIDATOM; - - TF_DISPLAYATTRIBUTE da; - da.bAttr = TF_ATTR_INPUT; - - try - { - CompGuid.insert(CompGuid.end(), ulcch0, guidatom); - CompStr.append(wstr0, ulcch0); - } - CATCH_RETURN(); - } - - textRange->Collapse(ec, TF_ANCHOR_END); - - } // out-most while for GUID_PROP_COMPOSING - - // - // set GUID_PROP_CONIME_TRACKCOMPOSITION - // - wil::com_ptr_nothrow PropertyTrackComposition; - if (SUCCEEDED(hr = pic->GetProperty(GUID_PROP_CONIME_TRACKCOMPOSITION, &PropertyTrackComposition))) - { - VARIANT var; - var.vt = VT_I4; - var.lVal = 1; - PropertyTrackComposition->SetValue(ec, rangeIn, &var); - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::_GetTextAndAttributeGapRange -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionObject::_GetTextAndAttributeGapRange(TfEditCookie ec, - ITfRange* gap_range, - LONG result_comp, - std::wstring& CompStr, - std::vector& CompGuid, - std::wstring& ResultStr) -{ - TfGuidAtom guidatom; - guidatom = TF_INVALID_GUIDATOM; - - TF_DISPLAYATTRIBUTE da; - da.bAttr = TF_ATTR_INPUT; - - BOOL fEmpty; - WCHAR wstr0[256 + 1]; - ULONG ulcch0; - - while (gap_range->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty) - { - wil::com_ptr_nothrow backup_range; - if (FAILED(gap_range->Clone(&backup_range))) - { - return E_FAIL; - } - - // - // Retrieve gap text if there exist. - // - ulcch0 = ARRAYSIZE(wstr0) - 1; - if (FAILED(gap_range->GetText(ec, - TF_TF_MOVESTART, // Move range to next after get text. - wstr0, - ulcch0, - &ulcch0))) - { - return E_FAIL; - } - - try - { - if (result_comp <= 0) - { - CompGuid.insert(CompGuid.end(), ulcch0, guidatom); - CompStr.append(wstr0, ulcch0); - } - else - { - ResultStr.append(wstr0, ulcch0); - LOG_IF_FAILED(ClearTextInRange(ec, backup_range.get())); - } - } - CATCH_RETURN(); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::_GetTextAndAttributePropertyRange -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionObject::_GetTextAndAttributePropertyRange(TfEditCookie ec, - ITfRange* pPropRange, - BOOL fCompExist, - LONG result_comp, - BOOL bInWriteSession, - TF_DISPLAYATTRIBUTE da, - TfGuidAtom guidatom, - std::wstring& CompStr, - std::vector& CompGuid, - std::wstring& ResultStr) -{ - BOOL fEmpty; - WCHAR wstr0[256 + 1]; - ULONG ulcch0; - - while (pPropRange->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty) - { - wil::com_ptr_nothrow backup_range; - if (FAILED(pPropRange->Clone(&backup_range))) - { - return E_FAIL; - } - - // - // Retrieve property text if there exist. - // - ulcch0 = ARRAYSIZE(wstr0) - 1; - if (FAILED(pPropRange->GetText(ec, - TF_TF_MOVESTART, // Move range to next after get text. - wstr0, - ulcch0, - &ulcch0))) - { - return E_FAIL; - } - - try - { - // see if there is a valid disp attribute - if (fCompExist == TRUE && result_comp <= 0) - { - if (guidatom == TF_INVALID_GUIDATOM) - { - da.bAttr = TF_ATTR_INPUT; - } - CompGuid.insert(CompGuid.end(), ulcch0, guidatom); - CompStr.append(wstr0, ulcch0); - } - else if (bInWriteSession) - { - // if there's no disp attribute attached, it probably means - // the part of string is finalized. - // - ResultStr.append(wstr0, ulcch0); - - // it was a 'determined' string - // so the doc has to shrink - // - LOG_IF_FAILED(ClearTextInRange(ec, backup_range.get())); - } - else - { - // - // Prevent infinite loop - // - break; - } - } - CATCH_RETURN(); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject::_GetNoDisplayAttributeRange -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionObject::_GetNoDisplayAttributeRange(TfEditCookie ec, - ITfRange* rangeIn, - const GUID** guids, - const int guid_size, - ITfRange* no_display_attribute_range) -{ - auto pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; - if (pic == nullptr) - { - return E_FAIL; - } - - wil::com_ptr_nothrow propComp; - auto hr = pic->TrackProperties(guids, guid_size, // system property - nullptr, - 0, // application property - &propComp); - if (FAILED(hr)) - { - return hr; - } - - wil::com_ptr_nothrow enumComp; - hr = propComp->EnumRanges(ec, &enumComp, rangeIn); - if (FAILED(hr)) - { - return hr; - } - - wil::com_ptr_nothrow pRange; - - while (enumComp->Next(1, &pRange, nullptr) == S_OK) - { - VARIANT var; - auto fCompExist = FALSE; - - hr = propComp->GetValue(ec, pRange.get(), &var); - if (S_OK == hr) - { - wil::com_ptr_nothrow EnumPropVal; - if (wil::try_com_query_to(var.punkVal, &EnumPropVal)) - { - TF_PROPERTYVAL tfPropertyVal; - - while (EnumPropVal->Next(1, &tfPropertyVal, nullptr) == S_OK) - { - for (auto i = 0; i < guid_size; i++) - { - if (IsEqualGUID(tfPropertyVal.guidId, *guids[i])) - { - if ((V_VT(&tfPropertyVal.varValue) == VT_I4 && V_I4(&tfPropertyVal.varValue) != 0)) - { - fCompExist = TRUE; - break; - } - } - } - - VariantClear(&tfPropertyVal.varValue); - - if (fCompExist) - { - break; - } - } - } - } - - if (!fCompExist) - { - // Adjust GAP range's start anchor to the end of property range. - no_display_attribute_range->ShiftStartToRange(ec, pRange.get(), TF_ANCHOR_START); - } - - VariantClear(&var); - } - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionCompositionComplete::CompComplete -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionCompositionComplete::CompComplete(TfEditCookie ec) -{ - auto pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; - RETURN_HR_IF_NULL(E_FAIL, pic); - - // Get the whole text, finalize it, and set empty string in TOM - wil::com_ptr_nothrow spRange; - LONG cch; - - RETURN_IF_FAILED(GetAllTextRange(ec, pic, &spRange, &cch)); - - // Check if a part of the range has already been finalized but not removed yet. - // Adjust the range appropriately to avoid inserting the same text twice. - auto cchCompleted = g_pConsoleTSF->GetCompletedRangeLength(); - if ((cchCompleted > 0) && - (cchCompleted < cch) && - SUCCEEDED(spRange->ShiftStart(ec, cchCompleted, &cchCompleted, NULL))) - { - FAIL_FAST_IF(!((cchCompleted > 0) && (cchCompleted < cch))); - cch -= cchCompleted; - } - else - { - cchCompleted = 0; - } - - // Get conversion area service. - auto conv_area = g_pConsoleTSF->GetConversionArea(); - RETURN_HR_IF_NULL(E_FAIL, conv_area); - - // If there is no string in TextStore we don't have to do anything. - if (!cch) - { - // Clear composition - LOG_IF_FAILED(conv_area->ClearComposition()); - return S_OK; - } - - auto hr = S_OK; - try - { - auto wstr = std::make_unique(cch + 1); - - // Get the whole text, finalize it, and erase the whole text. - if (SUCCEEDED(spRange->GetText(ec, TF_TF_IGNOREEND, wstr.get(), (ULONG)cch, (ULONG*)&cch))) - { - // Make Result String. - hr = conv_area->DrawResult({ wstr.get(), static_cast(cch) }); - } - } - CATCH_RETURN(); - - // Update the stored length of the completed fragment. - g_pConsoleTSF->SetCompletedRangeLength(cchCompleted + cch); - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionCompositionCleanup::EmptyCompositionRange() -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionCompositionCleanup::EmptyCompositionRange(TfEditCookie ec) -{ - if (!g_pConsoleTSF) - { - return E_FAIL; - } - if (!g_pConsoleTSF->IsPendingCompositionCleanup()) - { - return S_OK; - } - - auto hr = E_FAIL; - auto pic = g_pConsoleTSF->GetInputContext(); - if (pic != nullptr) - { - // Cleanup (empty the context range) after the last composition. - - hr = S_OK; - auto cchCompleted = g_pConsoleTSF->GetCompletedRangeLength(); - if (cchCompleted != 0) - { - wil::com_ptr_nothrow spRange; - LONG cch; - hr = GetAllTextRange(ec, pic, &spRange, &cch); - if (SUCCEEDED(hr)) - { - // Clean up only the completed part (which start is expected to coincide with the start of the full range). - if (cchCompleted < cch) - { - spRange->ShiftEnd(ec, (cchCompleted - cch), &cch, nullptr); - } - hr = ClearTextInRange(ec, spRange.get()); - g_pConsoleTSF->SetCompletedRangeLength(0); // cleaned up all completed text - } - } - } - g_pConsoleTSF->OnCompositionCleanup(SUCCEEDED(hr)); - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionUpdateCompositionString::UpdateCompositionString -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionUpdateCompositionString::UpdateCompositionString(TfEditCookie ec) -{ - HRESULT hr; - - auto pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; - if (pic == nullptr) - { - return E_FAIL; - } - - // Reset the 'edit session requested' flag. - g_pConsoleTSF->OnEditSession(); - - // If the composition has been cancelled\finalized, no update necessary. - if (!g_pConsoleTSF->IsInComposition()) - { - return S_OK; - } - - BOOL bInWriteSession; - if (FAILED(hr = pic->InWriteSession(g_pConsoleTSF->GetTfClientId(), &bInWriteSession))) - { - return hr; - } - - wil::com_ptr_nothrow FullTextRange; - LONG lTextLength; - if (FAILED(hr = GetAllTextRange(ec, pic, &FullTextRange, &lTextLength))) - { - return hr; - } - - wil::com_ptr_nothrow InterimRange; - auto fInterim = FALSE; - if (FAILED(hr = _IsInterimSelection(ec, &InterimRange, &fInterim))) - { - return hr; - } - - CicCategoryMgr* pCicCat = nullptr; - CicDisplayAttributeMgr* pDispAttr = nullptr; - - // - // Create Cicero Category Manager and Display Attribute Manager - // - hr = _CreateCategoryAndDisplayAttributeManager(&pCicCat, &pDispAttr); - if (SUCCEEDED(hr)) - { - if (fInterim) - { - hr = _MakeInterimString(ec, - FullTextRange.get(), - InterimRange.get(), - lTextLength, - bInWriteSession, - pCicCat, - pDispAttr); - } - else - { - hr = _MakeCompositionString(ec, FullTextRange.get(), bInWriteSession, pCicCat, pDispAttr); - } - } - - if (pCicCat) - { - delete pCicCat; - } - if (pDispAttr) - { - delete pDispAttr; - } - - return hr; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionUpdateCompositionString::_IsInterimSelection -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_IsInterimSelection(TfEditCookie ec, - ITfRange** pInterimRange, - BOOL* pfInterim) -{ - auto pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; - if (pic == nullptr) - { - return E_FAIL; - } - - ULONG cFetched; - - TF_SELECTION sel; - sel.range = nullptr; - - *pfInterim = FALSE; - if (pic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &sel, &cFetched) != S_OK) - { - // no selection. we can return S_OK. - return S_OK; - } - - if (sel.style.fInterimChar && sel.range) - { - HRESULT hr; - if (FAILED(hr = sel.range->Clone(pInterimRange))) - { - SafeReleaseClear(sel.range); - return hr; - } - - *pfInterim = TRUE; - } - - SafeReleaseClear(sel.range); - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionUpdateCompositionString::_MakeCompositionString -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_MakeCompositionString(TfEditCookie ec, - ITfRange* FullTextRange, - BOOL bInWriteSession, - CicCategoryMgr* pCicCatMgr, - CicDisplayAttributeMgr* pCicDispAttr) -{ - std::wstring CompStr; - std::vector CompGuid; - CCompCursorPos CompCursorPos; - std::wstring ResultStr; - auto fIgnorePreviousCompositionResult = FALSE; - - RETURN_IF_FAILED(_GetTextAndAttribute(ec, - FullTextRange, - CompStr, - CompGuid, - ResultStr, - bInWriteSession, - pCicCatMgr, - pCicDispAttr)); - - if (g_pConsoleTSF && g_pConsoleTSF->IsPendingCompositionCleanup()) - { - // Don't draw the previous composition result if there was a cleanup session requested for it. - fIgnorePreviousCompositionResult = TRUE; - // Cancel pending cleanup, since the ResultStr was cleared from the composition in _GetTextAndAttribute. - g_pConsoleTSF->OnCompositionCleanup(TRUE); - } - - RETURN_IF_FAILED(_GetCursorPosition(ec, CompCursorPos)); - - // Get display attribute manager - auto dam = pCicDispAttr->GetDisplayAttributeMgr(); - RETURN_HR_IF_NULL(E_FAIL, dam); - - // Get category manager - auto cat = pCicCatMgr->GetCategoryMgr(); - RETURN_HR_IF_NULL(E_FAIL, cat); - - // Allocate and fill TF_DISPLAYATTRIBUTE - try - { - // Get conversion area service. - auto conv_area = g_pConsoleTSF ? g_pConsoleTSF->GetConversionArea() : nullptr; - RETURN_HR_IF_NULL(E_FAIL, conv_area); - - if (!ResultStr.empty() && !fIgnorePreviousCompositionResult) - { - return conv_area->DrawResult(ResultStr); - } - if (!CompStr.empty()) - { - const auto cchDisplayAttribute = CompGuid.size(); - std::vector DisplayAttributes; - DisplayAttributes.reserve(cchDisplayAttribute); - - for (size_t i = 0; i < cchDisplayAttribute; i++) - { - TF_DISPLAYATTRIBUTE da; - ZeroMemory(&da, sizeof(da)); - da.bAttr = TF_ATTR_OTHER; - - GUID guid; - if (SUCCEEDED(cat->GetGUID(CompGuid.at(i), &guid))) - { - CLSID clsid; - wil::com_ptr_nothrow dai; - if (SUCCEEDED(dam->GetDisplayAttributeInfo(guid, &dai, &clsid))) - { - dai->GetAttributeInfo(&da); - } - } - - DisplayAttributes.emplace_back(da); - } - - return conv_area->DrawComposition(CompStr, // composition string - DisplayAttributes, // display attributes - CompCursorPos.GetCursorPosition()); // cursor position - } - } - CATCH_RETURN(); - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionUpdateCompositionString::_MakeInterimString -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_MakeInterimString(TfEditCookie ec, - ITfRange* FullTextRange, - ITfRange* InterimRange, - LONG lTextLength, - BOOL bInWriteSession, - CicCategoryMgr* pCicCatMgr, - CicDisplayAttributeMgr* pCicDispAttr) -{ - LONG lStartResult; - LONG lEndResult; - - FullTextRange->CompareStart(ec, InterimRange, TF_ANCHOR_START, &lStartResult); - RETURN_HR_IF(E_FAIL, lStartResult > 0); - - FullTextRange->CompareEnd(ec, InterimRange, TF_ANCHOR_END, &lEndResult); - RETURN_HR_IF(E_FAIL, lEndResult < 0); - - if (lStartResult < 0) - { - // Make result string. - RETURN_IF_FAILED(FullTextRange->ShiftEndToRange(ec, InterimRange, TF_ANCHOR_START)); - - // Interim char assume 1 char length. - // Full text length - 1 means result string length. - lTextLength--; - - FAIL_FAST_IF(!(lTextLength > 0)); - - if (lTextLength > 0) - { - try - { - auto wstr = std::make_unique(lTextLength + 1); - - // Get the result text, finalize it, and erase the result text. - if (SUCCEEDED(FullTextRange->GetText(ec, TF_TF_IGNOREEND, wstr.get(), (ULONG)lTextLength, (ULONG*)&lTextLength))) - { - // Clear the TOM - LOG_IF_FAILED(ClearTextInRange(ec, FullTextRange)); - } - } - CATCH_RETURN(); - } - } - - // Make interim character - std::wstring CompStr; - std::vector CompGuid; - std::wstring _tempResultStr; - - RETURN_IF_FAILED(_GetTextAndAttribute(ec, - InterimRange, - CompStr, - CompGuid, - _tempResultStr, - bInWriteSession, - pCicCatMgr, - pCicDispAttr)); - - // Get display attribute manager - auto dam = pCicDispAttr->GetDisplayAttributeMgr(); - RETURN_HR_IF_NULL(E_FAIL, dam); - - // Get category manager - auto cat = pCicCatMgr->GetCategoryMgr(); - RETURN_HR_IF_NULL(E_FAIL, cat); - - // Allocate and fill TF_DISPLAYATTRIBUTE - try - { - // Get conversion area service. - auto conv_area = g_pConsoleTSF ? g_pConsoleTSF->GetConversionArea() : nullptr; - RETURN_HR_IF_NULL(E_FAIL, conv_area); - - if (!CompStr.empty()) - { - const auto cchDisplayAttribute = CompGuid.size(); - std::vector DisplayAttributes; - DisplayAttributes.reserve(cchDisplayAttribute); - - for (size_t i = 0; i < cchDisplayAttribute; i++) - { - TF_DISPLAYATTRIBUTE da; - ZeroMemory(&da, sizeof(da)); - da.bAttr = TF_ATTR_OTHER; - GUID guid; - if (SUCCEEDED(cat->GetGUID(CompGuid.at(i), &guid))) - { - CLSID clsid; - wil::com_ptr_nothrow dai; - if (SUCCEEDED(dam->GetDisplayAttributeInfo(guid, &dai, &clsid))) - { - dai->GetAttributeInfo(&da); - } - } - - DisplayAttributes.emplace_back(da); - } - - return conv_area->DrawComposition(CompStr, // composition string (Interim string) - DisplayAttributes); // display attributes - } - } - CATCH_RETURN(); - - return S_OK; -} - -//+--------------------------------------------------------------------------- -// -// CEditSessionUpdateCompositionString::_CreateCategoryAndDisplayAttributeManager -// -//---------------------------------------------------------------------------- - -[[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_CreateCategoryAndDisplayAttributeManager( - CicCategoryMgr** pCicCatMgr, - CicDisplayAttributeMgr** pCicDispAttr) -{ - auto hr = E_OUTOFMEMORY; - - CicCategoryMgr* pTmpCat = nullptr; - CicDisplayAttributeMgr* pTmpDispAttr = nullptr; - - // - // Create Cicero Category Manager - // - pTmpCat = new (std::nothrow) CicCategoryMgr; - if (pTmpCat) - { - if (SUCCEEDED(hr = pTmpCat->InitCategoryInstance())) - { - auto pcat = pTmpCat->GetCategoryMgr(); - if (pcat) - { - // - // Create Cicero Display Attribute Manager - // - pTmpDispAttr = new (std::nothrow) CicDisplayAttributeMgr; - if (pTmpDispAttr) - { - if (SUCCEEDED(hr = pTmpDispAttr->InitDisplayAttributeInstance(pcat))) - { - *pCicCatMgr = pTmpCat; - *pCicDispAttr = pTmpDispAttr; - } - } - } - } - } - - if (FAILED(hr)) - { - if (pTmpCat) - { - delete pTmpCat; - } - if (pTmpDispAttr) - { - delete pTmpDispAttr; - } - } - - return hr; -} diff --git a/src/tsf/TfEditSession.h b/src/tsf/TfEditSession.h deleted file mode 100644 index 61ac5c1cac5..00000000000 --- a/src/tsf/TfEditSession.h +++ /dev/null @@ -1,219 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfEditSession.h - -Abstract: - - This file defines the CEditSessionObject Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#pragma once - -class CicCategoryMgr; -class CicDisplayAttributeMgr; - -/* 183C627A-B46C-44ad-B797-82F6BEC82131 */ -const GUID GUID_PROP_CONIME_TRACKCOMPOSITION = { - 0x183c627a, - 0xb46c, - 0x44ad, - { 0xb7, 0x97, 0x82, 0xf6, 0xbe, 0xc8, 0x21, 0x31 } -}; - -//+--------------------------------------------------------------------------- -// -// CEditSessionObject -// -//---------------------------------------------------------------------------- - -class CEditSessionObject : public ITfEditSession -{ -public: - CEditSessionObject() : - m_cRef(1) {} - virtual ~CEditSessionObject() = default; - -public: - // - // IUnknown methods - // - STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj); - STDMETHODIMP_(ULONG) - AddRef(void); - STDMETHODIMP_(ULONG) - Release(void); - - // - // ITfEditSession method - // - STDMETHODIMP DoEditSession(TfEditCookie ec) - { - auto hr = _DoEditSession(ec); - Release(); // Release reference count for asynchronous edit session. - return hr; - } - - // - // ImmIfSessionObject methods - // -protected: - [[nodiscard]] virtual HRESULT _DoEditSession(TfEditCookie ec) = 0; - - // - // EditSession methods. - // -public: - [[nodiscard]] static HRESULT GetAllTextRange(TfEditCookie ec, - ITfContext* ic, - ITfRange** range, - LONG* lpTextLength, - TF_HALTCOND* lpHaltCond = nullptr); - -protected: - [[nodiscard]] HRESULT SetTextInRange(TfEditCookie ec, - ITfRange* range, - __in_ecount_opt(len) LPWSTR psz, - DWORD len); - [[nodiscard]] HRESULT ClearTextInRange(TfEditCookie ec, - ITfRange* range); - - [[nodiscard]] HRESULT _GetTextAndAttribute(TfEditCookie ec, - ITfRange* range, - std::wstring& CompStr, - std::vector CompGuid, - BOOL bInWriteSession, - CicCategoryMgr* pCicCatMgr, - CicDisplayAttributeMgr* pCicDispAttr) - { - std::wstring ResultStr; - return _GetTextAndAttribute(ec, range, CompStr, CompGuid, ResultStr, bInWriteSession, pCicCatMgr, pCicDispAttr); - } - - [[nodiscard]] HRESULT _GetTextAndAttribute(TfEditCookie ec, - ITfRange* range, - std::wstring& CompStr, - std::vector& CompGuid, - std::wstring& ResultStr, - BOOL bInWriteSession, - CicCategoryMgr* pCicCatMgr, - CicDisplayAttributeMgr* pCicDispAttr); - - [[nodiscard]] HRESULT _GetTextAndAttributeGapRange(TfEditCookie ec, - ITfRange* gap_range, - LONG result_comp, - std::wstring& CompStr, - std::vector& CompGuid, - std::wstring& ResultStr); - - [[nodiscard]] HRESULT _GetTextAndAttributePropertyRange(TfEditCookie ec, - ITfRange* pPropRange, - BOOL fDispAttribute, - LONG result_comp, - BOOL bInWriteSession, - TF_DISPLAYATTRIBUTE da, - TfGuidAtom guidatom, - std::wstring& CompStr, - std::vector& CompGuid, - std::wstring& ResultStr); - - [[nodiscard]] HRESULT _GetNoDisplayAttributeRange(TfEditCookie ec, - ITfRange* range, - const GUID** guids, - const int guid_size, - ITfRange* no_display_attribute_range); - - [[nodiscard]] HRESULT _GetCursorPosition(TfEditCookie ec, - CCompCursorPos& CompCursorPos); - -private: - int m_cRef; -}; - -//+--------------------------------------------------------------------------- -// -// CEditSessionCompositionComplete -// -//---------------------------------------------------------------------------- - -class CEditSessionCompositionComplete : public CEditSessionObject -{ -public: - CEditSessionCompositionComplete() = default; - - [[nodiscard]] HRESULT _DoEditSession(TfEditCookie ec) - { - return CompComplete(ec); - } - - [[nodiscard]] HRESULT CompComplete(TfEditCookie ec); -}; - -//+--------------------------------------------------------------------------- -// -// CEditSessionCompositionCleanup -// -//---------------------------------------------------------------------------- - -class CEditSessionCompositionCleanup : public CEditSessionObject -{ -public: - CEditSessionCompositionCleanup() = default; - - [[nodiscard]] HRESULT _DoEditSession(TfEditCookie ec) - { - return EmptyCompositionRange(ec); - } - - [[nodiscard]] HRESULT EmptyCompositionRange(TfEditCookie ec); -}; - -//+--------------------------------------------------------------------------- -// -// CEditSessionUpdateCompositionString -// -//---------------------------------------------------------------------------- - -class CEditSessionUpdateCompositionString : public CEditSessionObject -{ -public: - CEditSessionUpdateCompositionString() = default; - - [[nodiscard]] HRESULT _DoEditSession(TfEditCookie ec) - { - return UpdateCompositionString(ec); - } - - [[nodiscard]] HRESULT UpdateCompositionString(TfEditCookie ec); - -private: - [[nodiscard]] HRESULT _IsInterimSelection(TfEditCookie ec, ITfRange** pInterimRange, BOOL* pfInterim); - - [[nodiscard]] HRESULT _MakeCompositionString(TfEditCookie ec, - ITfRange* FullTextRange, - BOOL bInWriteSession, - CicCategoryMgr* pCicCatMgr, - CicDisplayAttributeMgr* pCicDispAttr); - - [[nodiscard]] HRESULT _MakeInterimString(TfEditCookie ec, - ITfRange* FullTextRange, - ITfRange* InterimRange, - LONG lTextLength, - BOOL bInWriteSession, - CicCategoryMgr* pCicCatMgr, - CicDisplayAttributeMgr* pCicDispAttr); - - [[nodiscard]] HRESULT _CreateCategoryAndDisplayAttributeManager(CicCategoryMgr** pCicCatMgr, - CicDisplayAttributeMgr** pCicDispAttr); -}; diff --git a/src/tsf/TfTxtevCb.cpp b/src/tsf/TfTxtevCb.cpp deleted file mode 100644 index 90bac9db2da..00000000000 --- a/src/tsf/TfTxtevCb.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - TfTxtevCb.cpp - -Abstract: - - This file implements the CTextEventSinkCallBack Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#include "precomp.h" -#include "ConsoleTSF.h" -#include "TfEditSession.h" - -//+--------------------------------------------------------------------------- -// -// CConsoleTSF::HasCompositionChanged -// -//---------------------------------------------------------------------------- - -BOOL CConsoleTSF::_HasCompositionChanged(ITfContext* pInputContext, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord) -{ - BOOL fChanged; - if (SUCCEEDED(pEditRecord->GetSelectionStatus(&fChanged))) - { - if (fChanged) - { - return TRUE; - } - } - - // - // Find GUID_PROP_CONIME_TRACKCOMPOSITION property. - // - - wil::com_ptr_nothrow Property; - wil::com_ptr_nothrow FoundRange; - wil::com_ptr_nothrow PropertyTrackComposition; - - auto bFound = FALSE; - - if (SUCCEEDED(pInputContext->GetProperty(GUID_PROP_CONIME_TRACKCOMPOSITION, &Property))) - { - wil::com_ptr_nothrow EnumFindFirstTrackCompRange; - - if (SUCCEEDED(Property->EnumRanges(ecReadOnly, &EnumFindFirstTrackCompRange, NULL))) - { - HRESULT hr; - wil::com_ptr_nothrow range; - - while ((hr = EnumFindFirstTrackCompRange->Next(1, &range, nullptr)) == S_OK) - { - VARIANT var; - VariantInit(&var); - - hr = Property->GetValue(ecReadOnly, range.get(), &var); - if (SUCCEEDED(hr)) - { - if ((V_VT(&var) == VT_I4 && V_I4(&var) != 0)) - { - range->Clone(&FoundRange); - bFound = TRUE; // FOUND!! - break; - } - } - - VariantClear(&var); - - if (bFound) - { - break; // FOUND!! - } - } - } - } - - // - // if there is no track composition property, - // the composition has been changed since we put it. - // - if (!bFound) - { - return TRUE; - } - - if (FoundRange == nullptr) - { - return FALSE; - } - - bFound = FALSE; // RESET bFound flag... - - wil::com_ptr_nothrow rangeTrackComposition; - if (SUCCEEDED(FoundRange->Clone(&rangeTrackComposition))) - { - // - // get the text range that does not include read only area for - // reconversion. - // - wil::com_ptr_nothrow rangeAllText; - LONG cch; - if (SUCCEEDED(CEditSessionObject::GetAllTextRange(ecReadOnly, pInputContext, &rangeAllText, &cch))) - { - LONG lResult; - if (SUCCEEDED(rangeTrackComposition->CompareStart(ecReadOnly, rangeAllText.get(), TF_ANCHOR_START, &lResult))) - { - // - // if the start position of the track composition range is not - // the beginning of IC, - // the composition has been changed since we put it. - // - if (lResult != 0) - { - bFound = TRUE; // FOUND!! - } - else if (SUCCEEDED(rangeTrackComposition->CompareEnd(ecReadOnly, rangeAllText.get(), TF_ANCHOR_END, &lResult))) - { - // - // if the start position of the track composition range is not - // the beginning of IC, - // the composition has been changed since we put it. - // - // - // If we find the changes in these property, we need to update hIMC. - // - const GUID* guids[] = { &GUID_PROP_COMPOSING, - &GUID_PROP_ATTRIBUTE }; - const int guid_size = sizeof(guids) / sizeof(GUID*); - - wil::com_ptr_nothrow EnumPropertyChanged; - - if (lResult != 0) - { - bFound = TRUE; // FOUND!! - } - else if (SUCCEEDED(pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, guids, guid_size, &EnumPropertyChanged))) - { - HRESULT hr; - wil::com_ptr_nothrow range; - - while ((hr = EnumPropertyChanged->Next(1, &range, nullptr)) == S_OK) - { - BOOL empty; - if (range->IsEmpty(ecReadOnly, &empty) == S_OK && empty) - { - continue; - } - - bFound = TRUE; // FOUND!! - break; - } - } - } - } - } - } - return bFound; -} diff --git a/src/tsf/contsf.cpp b/src/tsf/contsf.cpp deleted file mode 100644 index 81a98c5b84e..00000000000 --- a/src/tsf/contsf.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -CConsoleTSF* g_pConsoleTSF = nullptr; - -extern "C" BOOL ActivateTextServices(HWND hwndConsole, GetSuggestionWindowPos pfnPosition, GetTextBoxAreaPos pfnTextArea) -{ - if (!g_pConsoleTSF && hwndConsole) - { - g_pConsoleTSF = new (std::nothrow) CConsoleTSF(hwndConsole, pfnPosition, pfnTextArea); - if (g_pConsoleTSF && SUCCEEDED(g_pConsoleTSF->Initialize())) - { - // Conhost calls this function only when the console window has focus. - g_pConsoleTSF->SetFocus(TRUE); - } - else - { - SafeReleaseClear(g_pConsoleTSF); - } - } - return g_pConsoleTSF ? TRUE : FALSE; -} - -extern "C" void DeactivateTextServices() -{ - if (g_pConsoleTSF) - { - g_pConsoleTSF->Uninitialize(); - SafeReleaseClear(g_pConsoleTSF); - } -} diff --git a/src/tsf/globals.h b/src/tsf/globals.h deleted file mode 100644 index 0fc503f0702..00000000000 --- a/src/tsf/globals.h +++ /dev/null @@ -1,57 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. - -Module Name: - - globals.h - -Abstract: - - Contains declarations for all globally scoped names in the program. - This file defines the CBoolean Interface Class. - -Author: - -Revision History: - -Notes: - ---*/ - -#pragma once - -// -// SAFECAST(obj, type) -// -// This macro is extremely useful for enforcing strong typechecking on other -// macros. It generates no code. -// -// Simply insert this macro at the beginning of an expression list for -// each parameter that must be typechecked. For example, for the -// definition of MYMAX(x, y), where x and y absolutely must be integers, -// use: -// -// #define MYMAX(x, y) (SAFECAST(x, int), SAFECAST(y, int), ((x) > (y) ? (x) : (y))) -// -// -#define SAFECAST(_obj, _type) (((_type)(_obj) == (_obj) ? 0 : 0), (_type)(_obj)) - -// -// Bitfields don't get along too well with bools, -// so here's an easy way to convert them: -// -#define BOOLIFY(expr) (!!(expr)) - -// -// generic COM stuff -// -#define SafeReleaseClear(punk) \ - { \ - if ((punk) != NULL) \ - { \ - (punk)->Release(); \ - (punk) = NULL; \ - } \ - } diff --git a/src/tsf/precomp.h b/src/tsf/precomp.h index d81ef6efb8c..bf7aac22627 100644 --- a/src/tsf/precomp.h +++ b/src/tsf/precomp.h @@ -37,16 +37,11 @@ extern "C" { #include #include #include +#include +#include #include // Cicero header #include // ITextStore standard attributes // This includes support libraries from the CRT, STL, WIL, and GSL #include "LibraryIncludes.h" - -#include "../inc/contsf.h" - -#include "globals.h" - -#include "ConsoleTSF.h" -#include "TfCtxtComp.h" diff --git a/src/tsf/sources b/src/tsf/sources index 54c79162097..648f380a056 100644 --- a/src/tsf/sources +++ b/src/tsf/sources @@ -42,17 +42,10 @@ PRECOMPILED_PCH = precomp.pch PRECOMPILED_OBJ = precomp.obj SOURCES = \ - contsf.cpp \ - ConsoleTSF.cpp \ - TfConvArea.cpp \ - TfCatUtil.cpp \ - TfDispAttr.cpp \ - TfEditSession.cpp \ - TfTxtevCb.cpp \ + Handle.cpp \ + Implementation.cpp \ INCLUDES = \ $(INCLUDES); \ ..\inc; \ $(MINWIN_INTERNAL_PRIV_SDK_INC_PATH_L); \ - $(SDK_INC_PATH)\atl30; \ - $(ONECORE_EXTERNAL_SDK_INC_PATH)\atl30; \ diff --git a/src/tsf/tsf.vcxproj b/src/tsf/tsf.vcxproj index 26da838865e..97217966bd0 100644 --- a/src/tsf/tsf.vcxproj +++ b/src/tsf/tsf.vcxproj @@ -11,29 +11,18 @@ - - - - - - - + + Create - - - + + - - - - - - + \ No newline at end of file diff --git a/src/tsf/tsf.vcxproj.filters b/src/tsf/tsf.vcxproj.filters index 3b4f7739ed4..21b300cc00f 100644 --- a/src/tsf/tsf.vcxproj.filters +++ b/src/tsf/tsf.vcxproj.filters @@ -15,58 +15,29 @@ - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - + Source Files - + Source Files - + Source Files - - Header Files - - - Header Files - - - Header Files - Header Files - - Header Files - - + Header Files - - Header Files - - - Header Files - - + Header Files - + + + + + \ No newline at end of file From f49cf44b799b4a4017ab7dce5c29694cda0fe725 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 22 Apr 2024 22:45:10 +0200 Subject: [PATCH 227/603] Add more TraceLogging to ApiDispatchers (#17085) More TraceLogging = More better? I made this change as I noticed that most calls are not being logged. Even after this change some crucial information won't be logged (for instance arrays of `INPUT_RECORD`), because I couldn't come up with a clever way to do so, but I think this is better than nothing. --- src/host/_output.cpp | 1 - src/host/misc.cpp | 2 - src/host/tracing.hpp | 41 ++-- src/propsheet/console.h | 4 - src/server/ApiDispatchers.cpp | 385 +++++++++++++++++++++++++++++++--- 5 files changed, 375 insertions(+), 58 deletions(-) diff --git a/src/host/_output.cpp b/src/host/_output.cpp index 0804a7ffab9..44701989a51 100644 --- a/src/host/_output.cpp +++ b/src/host/_output.cpp @@ -30,7 +30,6 @@ using Microsoft::Console::Interactivity::ServiceLocator; // - void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region) { - DBGOUTPUT(("WriteToScreen\n")); const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); // update to screen, if we're not iconic. if (!screenInfo.IsActiveScreenBuffer() || WI_IsFlagSet(gci.Flags, CONSOLE_IS_ICONIC)) diff --git a/src/host/misc.cpp b/src/host/misc.cpp index 329328f5672..3cadbf9f4c7 100644 --- a/src/host/misc.cpp +++ b/src/host/misc.cpp @@ -64,7 +64,6 @@ int ConvertToOem(const UINT uiCodePage, const UINT cchTarget) noexcept { FAIL_FAST_IF(!(pwchSource != (LPWSTR)pchTarget)); - DBGCHARS(("ConvertToOem U->%d %.*ls\n", uiCodePage, cchSource > 10 ? 10 : cchSource, pwchSource)); // clang-format off #pragma prefast(suppress: __WARNING_W2A_BEST_FIT, "WC_NO_BEST_FIT_CHARS doesn't work in many codepages. Retain old behavior.") // clang-format on @@ -80,6 +79,5 @@ int ConvertOutputToUnicode(_In_ UINT uiCodePage, { FAIL_FAST_IF(!(cchTarget > 0)); pwchTarget[0] = L'\0'; - DBGCHARS(("ConvertOutputToUnicode %d->U %.*s\n", uiCodePage, cchSource > 10 ? 10 : cchSource, pchSource)); return MultiByteToWideChar(uiCodePage, MB_USEGLYPHCHARS, pchSource, cchSource, pwchTarget, cchTarget); } diff --git a/src/host/tracing.hpp b/src/host/tracing.hpp index 03cf423f4d7..7549d54850e 100644 --- a/src/host/tracing.hpp +++ b/src/host/tracing.hpp @@ -17,28 +17,29 @@ Author(s): #pragma once -#include - #include "../types/inc/Viewport.hpp" -#if DBG -#define DBGCHARS(_params_) \ - { \ - Tracing::s_TraceChars _params_; \ - } -#define DBGOUTPUT(_params_) \ - { \ - Tracing::s_TraceOutput _params_; \ - } -#else -#define DBGCHARS(_params_) -#define DBGOUTPUT(_params_) -#endif - -#define TraceLoggingConsoleCoord(value, name) \ - TraceLoggingStruct(2, name), \ - TraceLoggingInt32(value.X, "X"), \ - TraceLoggingInt32(value.Y, "Y") +#define TraceLoggingConsoleCoord(value, name) \ + TraceLoggingPackedData(&value, sizeof(COORD)), \ + TraceLoggingPackedStruct(2, name), \ + TraceLoggingPackedMetadata(TlgInINT16, "X"), \ + TraceLoggingPackedMetadata(TlgInINT16, "Y") + +#define TraceLoggingConsoleSmallRect(value, name) \ + TraceLoggingPackedData(&value, sizeof(SMALL_RECT)), \ + TraceLoggingPackedStruct(4, name), \ + TraceLoggingInt16(TlgInINT16, "Left"), \ + TraceLoggingInt16(TlgInINT16, "Top"), \ + TraceLoggingInt16(TlgInINT16, "Right"), \ + TraceLoggingInt16(TlgInINT16, "Bottom") + +// We intentionally don't differentiate between A and W versions of CHAR_INFO, because some particularly nasty +// applications smuggle data in the upper bytes of the UnicodeChar field while using the A APIs and then they +// expect to read the same values back at a later time, which is something we stopped supporting. +#define TraceLoggingConsoleCharInfo(value, name) \ + TraceLoggingStruct(2, name), \ + TraceLoggingInt16(value.Char.UnicodeChar, "Char"), \ + TraceLoggingInt16(value.Attributes, "Attributes") class Tracing { diff --git a/src/propsheet/console.h b/src/propsheet/console.h index dfd0efd87ed..ef15b4f3747 100644 --- a/src/propsheet/console.h +++ b/src/propsheet/console.h @@ -196,13 +196,9 @@ void Undo(HWND hControlWindow); #define DBGFONTS(_params_) #define DBGFONTS2(_params_) - #define DBGCHARS(_params_) - #define DBGOUTPUT(_params_) #else #define DBGFONTS(_params_) #define DBGFONTS2(_params_) - #define DBGCHARS(_params_) - #define DBGOUTPUT(_params_) #endif // clang-format on diff --git a/src/server/ApiDispatchers.cpp b/src/server/ApiDispatchers.cpp index 8039977b5e5..9009f71c19f 100644 --- a/src/server/ApiDispatchers.cpp +++ b/src/server/ApiDispatchers.cpp @@ -34,6 +34,15 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) return p ? p->dwThreadId : 0; } +template +constexpr T saturate(auto val) +{ + constexpr auto min = std::numeric_limits::min(); + constexpr auto max = std::numeric_limits::max(); +#pragma warning(suppress : 4267) // '...': conversion from '...' to 'T', possible loss of data + return val < min ? min : (val > max ? max : val); +} + [[nodiscard]] HRESULT ApiDispatchers::ServerGetConsoleCP(_Inout_ CONSOLE_API_MSG* const m, _Inout_ BOOL* const /*pbReplyPending*/) { @@ -88,7 +97,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) TraceConsoleAPICallWithOrigin( "SetConsoleMode", TraceLoggingBool(pObjectHandle->IsInputHandle(), "InputHandle"), - TraceLoggingHexULong(a->Mode, "Mode")); + TraceLoggingHexUInt32(a->Mode, "Mode")); if (pObjectHandle->IsInputHandle()) { @@ -115,7 +124,13 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) InputBuffer* pObj; RETURN_IF_FAILED(pObjectHandle->GetInputBuffer(GENERIC_READ, &pObj)); - return m->_pApiRoutines->GetNumberOfConsoleInputEventsImpl(*pObj, a->ReadyEvents); + RETURN_IF_FAILED_EXPECTED(m->_pApiRoutines->GetNumberOfConsoleInputEventsImpl(*pObj, a->ReadyEvents)); + + TraceConsoleAPICallWithOrigin( + "GetNumberOfConsoleInputEvents", + TraceLoggingHexUInt32(a->ReadyEvents, "ReadyEvents")); + + return S_OK; } [[nodiscard]] HRESULT ApiDispatchers::ServerGetConsoleInput(_Inout_ CONSOLE_API_MSG* const m, @@ -135,6 +150,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) // Make sure we have a valid input buffer. const auto pHandleData = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pHandleData); + InputBuffer* pInputBuffer; RETURN_IF_FAILED(pHandleData->GetInputBuffer(GENERIC_READ, &pInputBuffer)); @@ -146,6 +162,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) const auto rgRecords = reinterpret_cast(pvBuffer); const auto cRecords = cbBufferSize / sizeof(INPUT_RECORD); + TraceConsoleAPICallWithOrigin( + "GetConsoleInput", + TraceLoggingHexUInt16(a->Flags, "Flags"), + TraceLoggingBoolean(a->Unicode, "Unicode"), + TraceLoggingUIntPtr(cRecords, "Records")); + const auto fIsPeek = WI_IsFlagSet(a->Flags, CONSOLE_READ_NOREMOVE); const auto fIsWaitAllowed = WI_IsFlagClear(a->Flags, CONSOLE_READ_NOWAIT); @@ -269,6 +291,14 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) } CATCH_RETURN(); + TraceConsoleAPICallWithOrigin( + "ReadConsole", + TraceLoggingBoolean(a->Unicode, "Unicode"), + TraceLoggingBoolean(a->ProcessControlZ, "ProcessControlZ"), + TraceLoggingCountedWideString(exeView.data(), saturate(exeView.size()), "ExeName"), + TraceLoggingCountedWideString(initialData.data(), saturate(initialData.size()), "InitialChars"), + TraceLoggingHexUInt32(a->CtrlWakeupMask, "CtrlWakeupMask")); + // ReadConsole needs this to get details associated with an attached process (such as the command history list, telemetry metadata). const auto hConsoleClient = (HANDLE)m->GetProcessHandle(); @@ -408,7 +438,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.FillConsoleOutput; // Capture length of initial fill. - size_t fill = a->Length; + const auto fill = a->Length; // Set written length to 0 in case we early return. a->Length = 0; @@ -425,6 +455,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { case CONSOLE_ATTRIBUTE: { + TraceConsoleAPICallWithOrigin( + "FillConsoleOutputAttribute", + TraceLoggingConsoleCoord(a->WriteCoord, "WriteCoord"), + TraceLoggingUInt32(fill, "Length"), + TraceLoggingHexUInt16(a->Element, "Attribute")); + hr = m->_pApiRoutines->FillConsoleOutputAttributeImpl(*pScreenInfo, a->Element, fill, @@ -435,6 +471,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) case CONSOLE_REAL_UNICODE: case CONSOLE_FALSE_UNICODE: { + TraceConsoleAPICallWithOrigin( + "FillConsoleOutputCharacterW", + TraceLoggingConsoleCoord(a->WriteCoord, "WriteCoord"), + TraceLoggingUInt32(fill, "Length"), + TraceLoggingWChar(a->Element, "Character")); + // GH#3126 if the client application is powershell.exe, then we might // need to enable a compatibility shim. hr = m->_pApiRoutines->FillConsoleOutputCharacterWImpl(*pScreenInfo, @@ -447,6 +489,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) } case CONSOLE_ASCII: { + TraceConsoleAPICallWithOrigin( + "FillConsoleOutputCharacterA", + TraceLoggingConsoleCoord(a->WriteCoord, "WriteCoord"), + TraceLoggingUInt32(fill, "Length"), + TraceLoggingChar(static_cast(a->Element), "Character")); + hr = m->_pApiRoutines->FillConsoleOutputCharacterAImpl(*pScreenInfo, static_cast(a->Element), fill, @@ -466,6 +514,8 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) [[nodiscard]] HRESULT ApiDispatchers::ServerSetConsoleActiveScreenBuffer(_Inout_ CONSOLE_API_MSG* const m, _Inout_ BOOL* const /*pbReplyPending*/) { + TraceConsoleAPICallWithOrigin("SetConsoleActiveScreenBuffer"); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); @@ -479,6 +529,8 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) [[nodiscard]] HRESULT ApiDispatchers::ServerFlushConsoleInputBuffer(_Inout_ CONSOLE_API_MSG* const m, _Inout_ BOOL* const /*pbReplyPending*/) { + TraceConsoleAPICallWithOrigin("ServerFlushConsoleInputBuffer"); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); @@ -494,6 +546,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.SetConsoleCP; + TraceConsoleAPICallWithOrigin( + "SetConsoleCP", + TraceLoggingBool(!a->Output, "InputHandle"), + TraceLoggingHexUInt32(a->CodePage, "CodePage")); + if (a->Output) { return m->_pApiRoutines->SetConsoleOutputCodePageImpl(a->CodePage); @@ -518,6 +575,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) auto visible = false; m->_pApiRoutines->GetConsoleCursorInfoImpl(*pObj, a->CursorSize, visible); a->Visible = !!visible; + + TraceConsoleAPICallWithOrigin( + "GetConsoleCursorInfo", + TraceLoggingUInt32(a->CursorSize, "CursorSize"), + TraceLoggingBoolean(a->Visible, "Visible")); + return S_OK; } @@ -526,6 +589,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.SetConsoleCursorInfo; + TraceConsoleAPICallWithOrigin( + "SetConsoleCursorInfo", + TraceLoggingUInt32(a->CursorSize, "CursorSize"), + TraceLoggingBoolean(a->Visible, "Visible")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); @@ -564,6 +632,18 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) a->Attributes = ex.wAttributes; a->PopupAttributes = ex.wPopupAttributes; + TraceConsoleAPICallWithOrigin( + "GetConsoleScreenBufferInfo", + TraceLoggingConsoleCoord(a->Size, "Size"), + TraceLoggingConsoleCoord(a->CursorPosition, "CursorPosition"), + TraceLoggingConsoleCoord(a->ScrollPosition, "ScrollPosition"), + TraceLoggingHexUInt16(a->Attributes, "Attributes"), + TraceLoggingConsoleCoord(a->CurrentWindowSize, "CurrentWindowSize"), + TraceLoggingConsoleCoord(a->MaximumWindowSize, "MaximumWindowSize"), + TraceLoggingHexUInt16(a->PopupAttributes, "PopupAttributes"), + TraceLoggingBoolean(a->FullscreenSupported, "FullscreenSupported"), + TraceLoggingHexULongFixedArray(&a->ColorTable[0], 16, "ColorTable")); + return S_OK; } @@ -572,6 +652,18 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.SetConsoleScreenBufferInfo; + TraceConsoleAPICallWithOrigin( + "SetConsoleScreenBufferInfo", + TraceLoggingConsoleCoord(a->Size, "Size"), + TraceLoggingConsoleCoord(a->CursorPosition, "CursorPosition"), + TraceLoggingConsoleCoord(a->ScrollPosition, "ScrollPosition"), + TraceLoggingHexUInt16(a->Attributes, "Attributes"), + TraceLoggingConsoleCoord(a->CurrentWindowSize, "CurrentWindowSize"), + TraceLoggingConsoleCoord(a->MaximumWindowSize, "MaximumWindowSize"), + TraceLoggingHexUInt16(a->PopupAttributes, "PopupAttributes"), + TraceLoggingBoolean(a->FullscreenSupported, "FullscreenSupported"), + TraceLoggingHexULongFixedArray(&a->ColorTable[0], 16, "ColorTable")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); @@ -594,12 +686,6 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) ex.wAttributes = a->Attributes; ex.wPopupAttributes = a->PopupAttributes; - TraceConsoleAPICallWithOrigin( - "SetConsoleScreenBufferInfoEx", - TraceLoggingConsoleCoord(a->Size, "BufferSize"), - TraceLoggingConsoleCoord(a->CurrentWindowSize, "WindowSize"), - TraceLoggingConsoleCoord(a->MaximumWindowSize, "MaxWindowSize")); - return m->_pApiRoutines->SetConsoleScreenBufferInfoExImpl(*pObj, ex); } @@ -608,16 +694,16 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.SetConsoleScreenBufferSize; + TraceConsoleAPICallWithOrigin( + "SetConsoleScreenBufferSize", + TraceLoggingConsoleCoord(a->Size, "BufferSize")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); SCREEN_INFORMATION* pObj; RETURN_IF_FAILED(pObjectHandle->GetScreenBuffer(GENERIC_WRITE, &pObj)); - TraceConsoleAPICallWithOrigin( - "SetConsoleScreenBufferSize", - TraceLoggingConsoleCoord(a->Size, "BufferSize")); - return m->_pApiRoutines->SetConsoleScreenBufferSizeImpl(*pObj, til::wrap_coord_size(a->Size)); } @@ -626,6 +712,10 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.SetConsoleCursorPosition; + TraceConsoleAPICallWithOrigin( + "SetConsoleCursorPosition", + TraceLoggingConsoleCoord(a->CursorPosition, "CursorPosition")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); @@ -648,7 +738,13 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) auto size = til::wrap_coord_size(a->Size); m->_pApiRoutines->GetLargestConsoleWindowSizeImpl(*pObj, size); - return til::unwrap_coord_size_hr(size, a->Size); + RETURN_IF_FAILED_EXPECTED(til::unwrap_coord_size_hr(size, a->Size)); + + TraceConsoleAPICallWithOrigin( + "GetLargestConsoleWindowSize", + TraceLoggingConsoleCoord(a->Size, "Size")); + + return S_OK; } [[nodiscard]] HRESULT ApiDispatchers::ServerScrollConsoleScreenBuffer(_Inout_ CONSOLE_API_MSG* const m, @@ -656,6 +752,15 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.ScrollConsoleScreenBuffer; + TraceConsoleAPICallWithOrigin( + "ScrollConsoleScreenBuffer", + TraceLoggingConsoleSmallRect(a->ScrollRectangle, "ScrollRectangle"), + TraceLoggingConsoleSmallRect(a->ClipRectangle, "ClipRectangle"), + TraceLoggingBoolean(a->Clip, "Clip"), + TraceLoggingBoolean(a->Unicode, "Unicode"), + TraceLoggingConsoleCoord(a->DestinationOrigin, "DestinationOrigin"), + TraceLoggingConsoleCharInfo(a->Fill, "Fill")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); @@ -690,16 +795,16 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.SetConsoleTextAttribute; + TraceConsoleAPICallWithOrigin( + "SetConsoleTextAttribute", + TraceLoggingHexUInt16(a->Attributes, "Attributes")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); SCREEN_INFORMATION* pObj; RETURN_IF_FAILED(pObjectHandle->GetScreenBuffer(GENERIC_WRITE, &pObj)); - TraceConsoleAPICallWithOrigin( - "SetConsoleTextAttribute", - TraceLoggingHexUInt16(a->Attributes, "Attributes")); - RETURN_HR(m->_pApiRoutines->SetConsoleTextAttributeImpl(*pObj, a->Attributes)); } @@ -708,20 +813,17 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL2.SetConsoleWindowInfo; + TraceConsoleAPICallWithOrigin( + "SetConsoleWindowInfo", + TraceLoggingBool(a->Absolute, "IsWindowRectAbsolute"), + TraceLoggingConsoleSmallRect(a->Window, "Window")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); SCREEN_INFORMATION* pObj; RETURN_IF_FAILED(pObjectHandle->GetScreenBuffer(GENERIC_WRITE, &pObj)); - TraceConsoleAPICallWithOrigin( - "SetConsoleWindowInfo", - TraceLoggingBool(a->Absolute, "IsWindowRectAbsolute"), - TraceLoggingInt32(a->Window.Left, "WindowRectLeft"), - TraceLoggingInt32(a->Window.Right, "WindowRectRight"), - TraceLoggingInt32(a->Window.Top, "WindowRectTop"), - TraceLoggingInt32(a->Window.Bottom, "WindowRectBottom")); - return m->_pApiRoutines->SetConsoleWindowInfoImpl(*pObj, a->Absolute, til::wrap_small_rect(a->Window)); } @@ -749,6 +851,10 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) case CONSOLE_ATTRIBUTE: { const std::span buffer(reinterpret_cast(pvBuffer), cbBuffer / sizeof(WORD)); + TraceConsoleAPICallWithOrigin( + "ReadConsoleOutputAttribute", + TraceLoggingConsoleCoord(a->ReadCoord, "ReadCoord"), + TraceLoggingUIntPtr(buffer.size(), "Records")); RETURN_IF_FAILED(m->_pApiRoutines->ReadConsoleOutputAttributeImpl(*pScreenInfo, til::wrap_coord(a->ReadCoord), buffer, written)); break; } @@ -756,12 +862,20 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) case CONSOLE_FALSE_UNICODE: { const std::span buffer(reinterpret_cast(pvBuffer), cbBuffer / sizeof(wchar_t)); + TraceConsoleAPICallWithOrigin( + "ReadConsoleOutputCharacterW", + TraceLoggingConsoleCoord(a->ReadCoord, "ReadCoord"), + TraceLoggingUIntPtr(buffer.size(), "Records")); RETURN_IF_FAILED(m->_pApiRoutines->ReadConsoleOutputCharacterWImpl(*pScreenInfo, til::wrap_coord(a->ReadCoord), buffer, written)); break; } case CONSOLE_ASCII: { const std::span buffer(reinterpret_cast(pvBuffer), cbBuffer); + TraceConsoleAPICallWithOrigin( + "ReadConsoleOutputCharacterA", + TraceLoggingConsoleCoord(a->ReadCoord, "ReadCoord"), + TraceLoggingUIntPtr(buffer.size(), "Records")); RETURN_IF_FAILED(m->_pApiRoutines->ReadConsoleOutputCharacterAImpl(*pScreenInfo, til::wrap_coord(a->ReadCoord), buffer, written)); break; } @@ -798,6 +912,13 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) size_t written; std::span buffer(reinterpret_cast(pvBuffer), cbSize / sizeof(INPUT_RECORD)); + + TraceConsoleAPICallWithOrigin( + "WriteConsoleInput", + TraceLoggingBoolean(a->Unicode, "Unicode"), + TraceLoggingBoolean(a->Append, "Append"), + TraceLoggingUIntPtr(buffer.size(), "Records")); + if (!a->Unicode) { RETURN_IF_FAILED(m->_pApiRoutines->WriteConsoleInputAImpl(*pInputBuffer, buffer, written, !!a->Append)); @@ -841,6 +962,13 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) RETURN_HR_IF(E_INVALIDARG, cbSize < regionBytes); // If given fewer bytes on input than we need to do this write, it's invalid. const std::span buffer(reinterpret_cast(pvBuffer), cbSize / sizeof(CHAR_INFO)); + + TraceConsoleAPICallWithOrigin( + "WriteConsoleOutput", + TraceLoggingBoolean(a->Unicode, "Unicode"), + TraceLoggingConsoleSmallRect(a->CharRegion, "CharRegion"), + TraceLoggingUIntPtr(buffer.size(), "Records")); + if (!a->Unicode) { RETURN_IF_FAILED(m->_pApiRoutines->WriteConsoleOutputAImpl(*pScreenInfo, buffer, originalRegion, writtenRegion)); @@ -883,7 +1011,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) TraceConsoleAPICallWithOrigin( "WriteConsoleOutputCharacterA", TraceLoggingConsoleCoord(a->WriteCoord, "WriteCoord"), - TraceLoggingUInt32(a->NumRecords, "NumRecords")); + TraceLoggingCountedString(text.data(), saturate(text.size()), "Buffer")); hr = m->_pApiRoutines->WriteConsoleOutputCharacterAImpl(*pScreenInfo, text, @@ -900,7 +1028,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) TraceConsoleAPICallWithOrigin( "WriteConsoleOutputCharacterW", TraceLoggingConsoleCoord(a->WriteCoord, "WriteCoord"), - TraceLoggingUInt32(a->NumRecords, "NumRecords")); + TraceLoggingCountedWideString(text.data(), saturate(text.size()), "Buffer")); hr = m->_pApiRoutines->WriteConsoleOutputCharacterWImpl(*pScreenInfo, text, @@ -916,7 +1044,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) TraceConsoleAPICallWithOrigin( "WriteConsoleOutputAttribute", TraceLoggingConsoleCoord(a->WriteCoord, "WriteCoord"), - TraceLoggingUInt32(a->NumRecords, "NumRecords")); + TraceLoggingHexUInt16Array(text.data(), saturate(text.size()), "Buffer")); hr = m->_pApiRoutines->WriteConsoleOutputAttributeImpl(*pScreenInfo, text, @@ -965,6 +1093,13 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) RETURN_HR_IF(E_INVALIDARG, regionArea > 0 && ((regionArea > ULONG_MAX / sizeof(CHAR_INFO)) || (cbBuffer < regionBytes))); std::span buffer(reinterpret_cast(pvBuffer), cbBuffer / sizeof(CHAR_INFO)); + + TraceConsoleAPICallWithOrigin( + "ReadConsoleOutput", + TraceLoggingBoolean(a->Unicode, "Unicode"), + TraceLoggingConsoleSmallRect(a->CharRegion, "CharRegion"), + TraceLoggingUIntPtr(buffer.size(), "Records")); + auto finalRegion = Microsoft::Console::Types::Viewport::Empty(); // the actual region read out of the buffer if (!a->Unicode) { @@ -1016,6 +1151,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) LOG_IF_FAILED(m->_pApiRoutines->GetConsoleTitleWImpl(buffer, written, needed)); } + TraceConsoleAPICallWithOrigin( + "GetConsoleTitleW", + TraceLoggingBoolean(a->Original, "Original"), + TraceLoggingCountedWideString(buffer.data(), saturate(written), "Buffer")); + // We must return the needed length of the title string in the TitleLength. LOG_IF_FAILED(SizeTToULong(needed, &a->TitleLength)); @@ -1036,6 +1176,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) hr = m->_pApiRoutines->GetConsoleTitleAImpl(buffer, written, needed); } + TraceConsoleAPICallWithOrigin( + "GetConsoleTitleA", + TraceLoggingBoolean(a->Original, "Original"), + TraceLoggingCountedString(buffer.data(), saturate(written), "Buffer")); + // We must return the needed length of the title string in the TitleLength. LOG_IF_FAILED(SizeTToULong(needed, &a->TitleLength)); @@ -1059,11 +1204,21 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) if (a->Unicode) { const std::wstring_view title(reinterpret_cast(pvBuffer), cbOriginalLength / sizeof(wchar_t)); + + TraceConsoleAPICallWithOrigin( + "SetConsoleTitleW", + TraceLoggingCountedWideString(title.data(), saturate(title.size()), "Buffer")); + return m->_pApiRoutines->SetConsoleTitleWImpl(title); } else { const std::string_view title(reinterpret_cast(pvBuffer), cbOriginalLength); + + TraceConsoleAPICallWithOrigin( + "SetConsoleTitleA", + TraceLoggingCountedString(title.data(), saturate(title.size()), "Buffer")); + return m->_pApiRoutines->SetConsoleTitleAImpl(title); } } @@ -1074,6 +1229,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) const auto a = &m->u.consoleMsgL3.GetConsoleMouseInfo; m->_pApiRoutines->GetNumberOfConsoleMouseButtonsImpl(a->NumButtons); + + TraceConsoleAPICallWithOrigin( + "GetConsoleMouseInfo", + TraceLoggingUInt32(a->NumButtons, "NumButtons")); + return S_OK; } @@ -1090,7 +1250,14 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) auto size = til::wrap_coord_size(a->FontSize); RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleFontSizeImpl(*pObj, a->FontIndex, size)); - return til::unwrap_coord_size_hr(size, a->FontSize); + RETURN_IF_FAILED_EXPECTED(til::unwrap_coord_size_hr(size, a->FontSize)); + + TraceConsoleAPICallWithOrigin( + "GetConsoleFontSize", + TraceLoggingUInt32(a->FontIndex, "FontIndex"), + TraceLoggingConsoleCoord(a->FontSize, "FontSize")); + + return S_OK; } [[nodiscard]] HRESULT ApiDispatchers::ServerGetConsoleCurrentFont(_Inout_ CONSOLE_API_MSG* const m, @@ -1115,6 +1282,15 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) a->FontSize = FontInfo.dwFontSize; a->FontWeight = FontInfo.FontWeight; + TraceConsoleAPICallWithOrigin( + "GetConsoleFontSize", + TraceLoggingBoolean(a->MaximumWindow, "MaximumWindow"), + TraceLoggingUInt32(a->FontIndex, "FontIndex"), + TraceLoggingConsoleCoord(a->FontSize, "FontSize"), + TraceLoggingUInt32(a->FontFamily, "FontFamily"), + TraceLoggingUInt32(a->FontWeight, "FontWeight"), + TraceLoggingWideString(&a->FaceName[0], "FaceName")); + return S_OK; } @@ -1123,6 +1299,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL3.SetConsoleDisplayMode; + TraceConsoleAPICallWithOrigin( + "SetConsoleDisplayMode", + TraceLoggingHexUInt32(a->dwFlags, "dwFlags"), + TraceLoggingConsoleCoord(a->ScreenBufferDimensions, "ScreenBufferDimensions")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); @@ -1131,7 +1312,9 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) auto size = til::wrap_coord_size(a->ScreenBufferDimensions); RETURN_IF_FAILED(m->_pApiRoutines->SetConsoleDisplayModeImpl(*pObj, a->dwFlags, size)); - return til::unwrap_coord_size_hr(size, a->ScreenBufferDimensions); + RETURN_IF_FAILED_EXPECTED(til::unwrap_coord_size_hr(size, a->ScreenBufferDimensions)); + + return S_OK; } [[nodiscard]] HRESULT ApiDispatchers::ServerGetConsoleDisplayMode(_Inout_ CONSOLE_API_MSG* const m, @@ -1142,6 +1325,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) // Historically this has never checked the handles. It just returns global state. m->_pApiRoutines->GetConsoleDisplayModeImpl(a->ModeFlags); + + TraceConsoleAPICallWithOrigin( + "GetConsoleDisplayMode", + TraceLoggingHexUInt32(a->ModeFlags, "ModeFlags")); + return S_OK; } @@ -1180,6 +1368,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) const std::wstring_view inputSource(reinterpret_cast(pvInputSource), cbInputSource / sizeof(wchar_t)); const std::wstring_view inputTarget(reinterpret_cast(pvInputTarget), cbInputTarget / sizeof(wchar_t)); + TraceConsoleAPICallWithOrigin( + "AddConsoleAliasW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedWideString(inputSource.data(), saturate(inputSource.size()), "Source"), + TraceLoggingCountedWideString(inputTarget.data(), saturate(inputTarget.size()), "Target")); + return m->_pApiRoutines->AddConsoleAliasWImpl(inputSource, inputTarget, inputExeName); } else @@ -1188,6 +1382,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) const std::string_view inputSource(pvInputSource, cbInputSource); const std::string_view inputTarget(pvInputTarget, cbInputTarget); + TraceConsoleAPICallWithOrigin( + "AddConsoleAliasA", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedString(inputSource.data(), saturate(inputSource.size()), "Source"), + TraceLoggingCountedString(inputTarget.data(), saturate(inputTarget.size()), "Target")); + return m->_pApiRoutines->AddConsoleAliasAImpl(inputSource, inputTarget, inputExeName); } } @@ -1233,6 +1433,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) hr = m->_pApiRoutines->GetConsoleAliasWImpl(inputSource, outputBuffer, cchWritten, inputExeName); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedWideString(inputSource.data(), saturate(inputSource.size()), "Source"), + TraceLoggingCountedWideString(outputBuffer.data(), saturate(cchWritten), "Output")); + // We must set the reply length in bytes. Convert back from characters. RETURN_IF_FAILED(SizeTMult(cchWritten, sizeof(wchar_t), &cbWritten)); } @@ -1245,6 +1451,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) hr = m->_pApiRoutines->GetConsoleAliasAImpl(inputSource, outputBuffer, cchWritten, inputExeName); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasW", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedString(inputSource.data(), saturate(inputSource.size()), "Source"), + TraceLoggingCountedString(outputBuffer.data(), saturate(cchWritten), "Output")); + cbWritten = cchWritten; } @@ -1281,6 +1493,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) size_t cchAliasesLength; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasesLengthWImpl(inputExeName, cchAliasesLength)); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasesLengthW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingUIntPtr(cchAliasesLength, "Length")); + RETURN_IF_FAILED(SizeTMult(cchAliasesLength, sizeof(wchar_t), &cbAliasesLength)); } else @@ -1289,6 +1506,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) size_t cchAliasesLength; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasesLengthAImpl(inputExeName, cchAliasesLength)); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasesLengthA", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingUIntPtr(cchAliasesLength, "Length")); + cbAliasesLength = cchAliasesLength; } @@ -1307,12 +1529,22 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { size_t cchAliasExesLength; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasExesLengthWImpl(cchAliasExesLength)); + + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasExesLengthW", + TraceLoggingUIntPtr(cchAliasExesLength, "Length")); + cbAliasExesLength = cchAliasExesLength * sizeof(wchar_t); } else { size_t cchAliasExesLength; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasExesLengthAImpl(cchAliasExesLength)); + + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasExesLengthA", + TraceLoggingUIntPtr(cchAliasExesLength, "Length")); + cbAliasExesLength = cchAliasExesLength; } @@ -1343,6 +1575,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasesWImpl(inputExeName, outputBuffer, cchWritten)); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasesW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedWideString(outputBuffer.data(), saturate(cchWritten), "Output")); + // We must set the reply length in bytes. Convert back from characters. RETURN_IF_FAILED(SizeTMult(cchWritten, sizeof(wchar_t), &cbWritten)); } @@ -1354,6 +1591,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasesAImpl(inputExeName, outputBuffer, cchWritten)); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasesA", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedString(outputBuffer.data(), saturate(cchWritten), "Output")); + cbWritten = cchWritten; } @@ -1380,6 +1622,10 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) size_t cchWritten; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasExesWImpl(outputBuffer, cchWritten)); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasExesW", + TraceLoggingCountedWideString(outputBuffer.data(), saturate(cchWritten), "Output")); + RETURN_IF_FAILED(SizeTMult(cchWritten, sizeof(wchar_t), &cbWritten)); } else @@ -1388,6 +1634,10 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) size_t cchWritten; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleAliasExesAImpl(outputBuffer, cchWritten)); + TraceConsoleAPICallWithOrigin( + "GetConsoleAliasExesA", + TraceLoggingCountedString(outputBuffer.data(), saturate(cchWritten), "Output")); + cbWritten = cchWritten; } @@ -1412,12 +1662,20 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const std::wstring_view inputExeName(reinterpret_cast(pvExeName), cbExeNameLength / sizeof(wchar_t)); + TraceConsoleAPICallWithOrigin( + "ExpungeConsoleCommandHistoryW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName")); + return m->_pApiRoutines->ExpungeConsoleCommandHistoryWImpl(inputExeName); } else { const std::string_view inputExeName(reinterpret_cast(pvExeName), cbExeNameLength); + TraceConsoleAPICallWithOrigin( + "ExpungeConsoleCommandHistoryA", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName")); + return m->_pApiRoutines->ExpungeConsoleCommandHistoryAImpl(inputExeName); } } @@ -1426,6 +1684,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) _Inout_ BOOL* const /*pbReplyPending*/) { const auto a = &m->u.consoleMsgL3.SetConsoleNumberOfCommandsW; + PVOID pvExeName; ULONG cbExeNameLength; RETURN_IF_FAILED(m->GetInputBuffer(&pvExeName, &cbExeNameLength)); @@ -1435,12 +1694,22 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const std::wstring_view inputExeName(reinterpret_cast(pvExeName), cbExeNameLength / sizeof(wchar_t)); + TraceConsoleAPICallWithOrigin( + "SetConsoleNumberOfCommandsW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingUInt32(a->NumCommands, "NumCommands")); + return m->_pApiRoutines->SetConsoleNumberOfCommandsWImpl(inputExeName, NumberOfCommands); } else { const std::string_view inputExeName(reinterpret_cast(pvExeName), cbExeNameLength); + TraceConsoleAPICallWithOrigin( + "SetConsoleNumberOfCommandsA", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingUInt32(a->NumCommands, "NumCommands")); + return m->_pApiRoutines->SetConsoleNumberOfCommandsAImpl(inputExeName, NumberOfCommands); } } @@ -1462,6 +1731,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleCommandHistoryLengthWImpl(inputExeName, cchCommandHistoryLength)); + TraceConsoleAPICallWithOrigin( + "GetConsoleCommandHistoryLengthW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingUIntPtr(cchCommandHistoryLength, "CommandHistoryLength")); + // We must set the reply length in bytes. Convert back from characters. RETURN_IF_FAILED(SizeTMult(cchCommandHistoryLength, sizeof(wchar_t), &cbCommandHistoryLength)); } @@ -1472,6 +1746,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleCommandHistoryLengthAImpl(inputExeName, cchCommandHistoryLength)); + TraceConsoleAPICallWithOrigin( + "GetConsoleCommandHistoryLengthA", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingUIntPtr(cchCommandHistoryLength, "CommandHistoryLength")); + cbCommandHistoryLength = cchCommandHistoryLength; } @@ -1502,6 +1781,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) size_t cchWritten; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleCommandHistoryWImpl(inputExeName, outputBuffer, cchWritten)); + TraceConsoleAPICallWithOrigin( + "GetConsoleCommandHistoryW", + TraceLoggingCountedWideString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedWideString(outputBuffer.data(), saturate(cchWritten), "Output")); + // We must set the reply length in bytes. Convert back from characters. RETURN_IF_FAILED(SizeTMult(cchWritten, sizeof(wchar_t), &cbWritten)); } @@ -1512,6 +1796,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) size_t cchWritten; RETURN_IF_FAILED(m->_pApiRoutines->GetConsoleCommandHistoryAImpl(inputExeName, outputBuffer, cchWritten)); + TraceConsoleAPICallWithOrigin( + "GetConsoleCommandHistory", + TraceLoggingCountedString(inputExeName.data(), saturate(inputExeName.size()), "ExeName"), + TraceLoggingCountedString(outputBuffer.data(), saturate(cchWritten), "Output")); + cbWritten = cchWritten; } @@ -1529,6 +1818,11 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) const auto a = &m->u.consoleMsgL3.GetConsoleWindow; m->_pApiRoutines->GetConsoleWindowImpl(a->hwnd); + + TraceConsoleAPICallWithOrigin( + "GetConsoleWindow", + TraceLoggingPointer(a->hwnd, "hwnd")); + return S_OK; } @@ -1538,6 +1832,14 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) const auto a = &m->u.consoleMsgL3.GetConsoleSelectionInfo; m->_pApiRoutines->GetConsoleSelectionInfoImpl(a->SelectionInfo); + + TraceConsoleAPICallWithOrigin( + "GetConsoleSelectionInfo", + TraceLoggingStruct(3, "SelectionInfo"), + TraceLoggingUInt32(a->SelectionInfo.dwFlags, "dwFlags"), + TraceLoggingConsoleCoord(a->SelectionInfo.dwSelectionAnchor, "dwSelectionAnchor"), + TraceLoggingConsoleSmallRect(a->SelectionInfo.srSelection, "srSelection")); + return S_OK; } @@ -1555,6 +1857,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) a->HistoryBufferSize = info.HistoryBufferSize; a->NumberOfHistoryBuffers = info.NumberOfHistoryBuffers; + TraceConsoleAPICallWithOrigin( + "GetConsoleHistory", + TraceLoggingUInt32(a->HistoryBufferSize, "HistoryBufferSize"), + TraceLoggingUInt32(a->NumberOfHistoryBuffers, "NumberOfHistoryBuffers"), + TraceLoggingUInt32(a->dwFlags, "dwFlags")); + return S_OK; } @@ -1563,6 +1871,12 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL3.SetConsoleHistory; + TraceConsoleAPICallWithOrigin( + "SetConsoleHistory", + TraceLoggingUInt32(a->HistoryBufferSize, "HistoryBufferSize"), + TraceLoggingUInt32(a->NumberOfHistoryBuffers, "NumberOfHistoryBuffers"), + TraceLoggingUInt32(a->dwFlags, "dwFlags")); + CONSOLE_HISTORY_INFO info; info.cbSize = sizeof(info); info.dwFlags = a->dwFlags; @@ -1577,6 +1891,15 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) { const auto a = &m->u.consoleMsgL3.SetCurrentConsoleFont; + TraceConsoleAPICallWithOrigin( + "SetCurrentConsoleFont", + TraceLoggingBoolean(a->MaximumWindow, "MaximumWindow"), + TraceLoggingUInt32(a->FontIndex, "FontIndex"), + TraceLoggingConsoleCoord(a->FontSize, "FontSize"), + TraceLoggingUInt32(a->FontFamily, "FontFamily"), + TraceLoggingUInt32(a->FontWeight, "FontWeight"), + TraceLoggingWideString(&a->FaceName[0], "FaceName")); + const auto pObjectHandle = m->GetObjectHandle(); RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); From 99061ee272b72c6d565802068f7b09ab12e4173e Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 23 Apr 2024 02:07:00 +0200 Subject: [PATCH 228/603] Use float instead of double by default (#17100) While `double` is probably generally preferable for UI code, our application is essentially a complex wrapper wrapper around DWrite, D2D and D3D, all of which use `float` exclusively. Of course it also uses XAML, but that one uses `float` for roughly 1/3rd of its API functions, so I'm not sure what it prefers. Additionally, it's mostly a coincidence that we use WinUI/XAML for Windows Terminal whereas DWrite/D2D/D3D are effectively essential. This is demonstrated by the fact that we have a `HwndTerminal`, while there's no alternative to e.g. D3D on Windows. The goal of this PR is that DIP based calculations never end up mixing `float` and `double`. This PR also changes opacity-related values to `float` because I felt like that fits the theme. --- .../TerminalApp/ActionPreviewHandlers.cpp | 2 +- .../TerminalApp/AppActionHandlers.cpp | 4 +- src/cascadia/TerminalApp/Pane.cpp | 12 ++-- src/cascadia/TerminalApp/TerminalPage.cpp | 8 +-- src/cascadia/TerminalApp/TitlebarControl.cpp | 6 +- src/cascadia/TerminalApp/TitlebarControl.h | 2 +- src/cascadia/TerminalApp/TitlebarControl.idl | 2 +- src/cascadia/TerminalControl/ControlCore.cpp | 24 +++---- src/cascadia/TerminalControl/ControlCore.h | 12 ++-- src/cascadia/TerminalControl/ControlCore.idl | 4 +- .../TerminalControl/ControlInteractivity.cpp | 16 ++--- .../TerminalControl/ControlInteractivity.h | 6 +- .../TerminalControl/ControlInteractivity.idl | 2 +- src/cascadia/TerminalControl/EventArgs.h | 4 +- src/cascadia/TerminalControl/EventArgs.idl | 2 +- src/cascadia/TerminalControl/HwndTerminal.cpp | 4 +- src/cascadia/TerminalControl/HwndTerminal.hpp | 2 +- .../TerminalControl/IControlAppearance.idl | 4 +- .../InteractivityAutomationPeer.cpp | 6 +- .../InteractivityAutomationPeer.h | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 64 ++++++++++--------- src/cascadia/TerminalControl/TermControl.h | 4 +- src/cascadia/TerminalControl/TermControl.idl | 4 +- src/cascadia/TerminalCore/ICoreAppearance.idl | 8 +-- .../TerminalSettingsEditor/Appearances.cpp | 2 +- .../TerminalSettingsEditor/Appearances.idl | 2 +- .../TerminalSettingsEditor/ProfileViewModel.h | 2 +- .../ProfileViewModel.idl | 2 +- .../TerminalSettingsModel/ActionArgs.h | 6 +- .../TerminalSettingsModel/ActionArgs.idl | 6 +- .../AppearanceConfig.cpp | 4 +- .../TerminalSettingsModel/AppearanceConfig.h | 2 +- .../IAppearanceConfig.idl | 4 +- .../TerminalSettingsModel/JsonUtils.h | 4 +- .../TerminalSettingsModel/MTSMSettings.h | 2 +- .../TerminalSettingsModel/TerminalSettings.h | 4 +- .../TerminalSettingsSerializationHelpers.h | 10 +-- .../UnitTests_Control/ControlCoreTests.cpp | 56 ++++++++-------- .../ControlInteractivityTests.cpp | 18 +++--- .../UnitTests_SettingsModel/CommandTests.cpp | 18 +++--- src/cascadia/WindowsTerminal/IslandWindow.cpp | 2 +- src/cascadia/inc/ControlProperties.h | 4 +- src/types/IControlAccessibilityInfo.h | 2 +- src/types/TermControlUiaProvider.cpp | 2 +- src/types/TermControlUiaProvider.hpp | 2 +- 45 files changed, 180 insertions(+), 178 deletions(-) diff --git a/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp b/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp index 67212bbbe43..03e45ea3f91 100644 --- a/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp +++ b/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp @@ -127,7 +127,7 @@ namespace winrt::TerminalApp::implementation auto originalOpacity{ control.BackgroundOpacity() }; // Apply the new opacity - control.AdjustOpacity(args.Opacity() / 100.0, args.Relative()); + control.AdjustOpacity(args.Opacity() / 100.0f, args.Relative()); if (backup) { diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 2fd6680574f..d044cbe13f2 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -286,7 +286,7 @@ namespace winrt::TerminalApp::implementation _SplitPane(terminalTab, realArgs.SplitDirection(), // This is safe, we're already filtering so the value is (0, 1) - ::base::saturated_cast(realArgs.SplitSize()), + realArgs.SplitSize(), _MakePane(realArgs.ContentArgs(), duplicateFromTab)); args.Handled(true); } @@ -1247,7 +1247,7 @@ namespace winrt::TerminalApp::implementation if (const auto& realArgs = args.ActionArgs().try_as()) { const auto res = _ApplyToActiveControls([&](auto& control) { - control.AdjustOpacity(realArgs.Opacity() / 100.0, realArgs.Relative()); + control.AdjustOpacity(realArgs.Opacity() / 100.0f, realArgs.Relative()); }); args.Handled(res); } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 7d11f4edc63..ac599612359 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -151,7 +151,7 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n // When creating a pane the split size is the size of the new pane // and not position. const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right; - const auto splitSize = (kind != BuildStartupKind::None && _IsLeaf() ? .5 : 1. - _desiredSplitPosition); + const auto splitSize = (kind != BuildStartupKind::None && _IsLeaf() ? 0.5f : 1.0f - _desiredSplitPosition); SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs }; actionAndArgs.Args(args); @@ -1595,12 +1595,12 @@ void Pane::_CloseChildRoutine(const bool closeFirst) const auto splitWidth = _splitState == SplitState::Vertical; Size removedOriginalSize{ - ::base::saturated_cast(removedChild->_root.ActualWidth()), - ::base::saturated_cast(removedChild->_root.ActualHeight()) + static_cast(removedChild->_root.ActualWidth()), + static_cast(removedChild->_root.ActualHeight()) }; Size remainingOriginalSize{ - ::base::saturated_cast(remainingChild->_root.ActualWidth()), - ::base::saturated_cast(remainingChild->_root.ActualHeight()) + static_cast(remainingChild->_root.ActualWidth()), + static_cast(remainingChild->_root.ActualHeight()) }; // Remove both children from the grid @@ -1902,7 +1902,7 @@ void Pane::_SetupEntranceAnimation() // looks bad. _secondChild->_root.Background(_themeResources.unfocusedBorderBrush); - const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast(totalSize)); + const auto [firstSize, secondSize] = _CalcChildrenSizes(static_cast(totalSize)); // This is safe to capture this, because it's only being called in the // context of this method (not on another thread) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 1749acce26f..b10e2157c1b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1947,8 +1947,8 @@ namespace winrt::TerminalApp::implementation layout.LaunchMode({ mode }); // Only save the content size because the tab size will be added on load. - const auto contentWidth = ::base::saturated_cast(_tabContent.ActualWidth()); - const auto contentHeight = ::base::saturated_cast(_tabContent.ActualHeight()); + const auto contentWidth = static_cast(_tabContent.ActualWidth()); + const auto contentHeight = static_cast(_tabContent.ActualHeight()); const winrt::Windows::Foundation::Size windowSize{ contentWidth, contentHeight }; layout.InitialSize(windowSize); @@ -2358,8 +2358,8 @@ namespace winrt::TerminalApp::implementation { return; } - const auto contentWidth = ::base::saturated_cast(_tabContent.ActualWidth()); - const auto contentHeight = ::base::saturated_cast(_tabContent.ActualHeight()); + const auto contentWidth = static_cast(_tabContent.ActualWidth()); + const auto contentHeight = static_cast(_tabContent.ActualHeight()); const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight }; const auto realSplitType = activeTab->PreCalculateCanSplit(splitDirection, splitSize, availableSpace); diff --git a/src/cascadia/TerminalApp/TitlebarControl.cpp b/src/cascadia/TerminalApp/TitlebarControl.cpp index bd5748eb279..76ff3cc7d6a 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.cpp +++ b/src/cascadia/TerminalApp/TitlebarControl.cpp @@ -44,12 +44,12 @@ namespace winrt::TerminalApp::implementation }); } - double TitlebarControl::CaptionButtonWidth() + float TitlebarControl::CaptionButtonWidth() { // Divide by three, since we know there are only three buttons. When // Windows 12 comes along and adds another, we can update this /s - static auto width{ MinMaxCloseControl().ActualWidth() / 3.0 }; - return width; + const auto minMaxCloseWidth = MinMaxCloseControl().ActualWidth(); + return static_cast(minMaxCloseWidth) / 3.0f; } IInspectable TitlebarControl::Content() diff --git a/src/cascadia/TerminalApp/TitlebarControl.h b/src/cascadia/TerminalApp/TitlebarControl.h index 259361302ad..4dee4fb7301 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.h +++ b/src/cascadia/TerminalApp/TitlebarControl.h @@ -15,7 +15,7 @@ namespace winrt::TerminalApp::implementation void PressButton(CaptionButton button); winrt::fire_and_forget ClickButton(CaptionButton button); void ReleaseButtons(); - double CaptionButtonWidth(); + float CaptionButtonWidth(); IInspectable Content(); void Content(IInspectable content); diff --git a/src/cascadia/TerminalApp/TitlebarControl.idl b/src/cascadia/TerminalApp/TitlebarControl.idl index 1227effd977..b7a06ba987e 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.idl +++ b/src/cascadia/TerminalApp/TitlebarControl.idl @@ -27,7 +27,7 @@ namespace TerminalApp void PressButton(CaptionButton button); void ClickButton(CaptionButton button); void ReleaseButtons(); - Double CaptionButtonWidth { get; }; + Single CaptionButtonWidth { get; }; IInspectable Content; Windows.UI.Xaml.Controls.Border DragBar { get; }; diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 5531ca25cba..cfba0a1d46a 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -677,7 +677,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::AdjustOpacity(const double adjustment) + void ControlCore::AdjustOpacity(const float adjustment) { if (adjustment == 0) { @@ -694,11 +694,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - focused (default == true): Whether the window is focused or unfocused. // Return Value: // - - void ControlCore::_setOpacity(const double opacity, bool focused) + void ControlCore::_setOpacity(const float opacity, bool focused) { - const auto newOpacity = std::clamp(opacity, - 0.0, - 1.0); + const auto newOpacity = std::clamp(opacity, 0.0f, 1.0f); if (newOpacity == Opacity()) { @@ -713,7 +711,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _runtimeFocusedOpacity = focused ? newOpacity : _runtimeFocusedOpacity; // Manually turn off acrylic if they turn off transparency. - _runtimeUseAcrylic = newOpacity < 1.0 && _settings->UseAcrylic(); + _runtimeUseAcrylic = newOpacity < 1.0f && _settings->UseAcrylic(); // Update the renderer as well. It might need to fall back from // cleartype -> grayscale if the BG is transparent / acrylic. @@ -881,7 +879,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Method Description: // - Updates the appearance of the current terminal. // - INVARIANT: This method can only be called if the caller DOES NOT HAVE writing lock on the terminal. - void ControlCore::ApplyAppearance(const bool& focused) + void ControlCore::ApplyAppearance(const bool focused) { const auto lock = _terminal->LockForWriting(); const auto& newAppearance{ focused ? _settings->FocusedAppearance() : _settings->UnfocusedAppearance() }; @@ -900,10 +898,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Incase EnableUnfocusedAcrylic is disabled and Focused Acrylic is set to true, // the terminal should ignore the unfocused opacity from settings. // The Focused Opacity from settings should be ignored if overridden at runtime. - bool useFocusedRuntimeOpacity = focused || (!_settings->EnableUnfocusedAcrylic() && UseAcrylic()); - double newOpacity = useFocusedRuntimeOpacity ? - FocusedOpacity() : - newAppearance->Opacity(); + const auto useFocusedRuntimeOpacity = focused || (!_settings->EnableUnfocusedAcrylic() && UseAcrylic()); + const auto newOpacity = useFocusedRuntimeOpacity ? FocusedOpacity() : newAppearance->Opacity(); _setOpacity(newOpacity, focused); // No need to update Acrylic if UnfocusedAcrylic is disabled @@ -1422,8 +1418,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { const auto fontSize = _actualFont.GetSize(); return { - ::base::saturated_cast(fontSize.width), - ::base::saturated_cast(fontSize.height) + static_cast(fontSize.width), + static_cast(fontSize.height) }; } @@ -2350,7 +2346,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _settings->HasUnfocusedAppearance(); } - void ControlCore::AdjustOpacity(const double opacityAdjust, const bool relative) + void ControlCore::AdjustOpacity(const float opacityAdjust, const bool relative) { if (relative) { diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 8748a52ee58..ee2e1e5a841 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -91,7 +91,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void Detach(); void UpdateSettings(const Control::IControlSettings& settings, const IControlAppearance& newAppearance); - void ApplyAppearance(const bool& focused); + void ApplyAppearance(const bool focused); Control::IControlSettings Settings(); Control::IControlAppearance FocusedAppearance() const; Control::IControlAppearance UnfocusedAppearance() const; @@ -136,7 +136,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void LostFocus(); void ToggleShaderEffects(); - void AdjustOpacity(const double adjustment); + void AdjustOpacity(const float adjustment); void ResumeRendering(); void SetHoveredCell(Core::Point terminalPosition); @@ -243,7 +243,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring ReadEntireBuffer() const; Control::CommandHistoryContext CommandHistory() const; - void AdjustOpacity(const double opacity, const bool relative); + void AdjustOpacity(const float opacity, const bool relative); void WindowVisibilityChanged(const bool showOrHide); @@ -258,8 +258,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ShouldShowSelectCommand(); bool ShouldShowSelectOutput(); - RUNTIME_SETTING(double, Opacity, _settings->Opacity()); - RUNTIME_SETTING(double, FocusedOpacity, FocusedAppearance().Opacity()); + RUNTIME_SETTING(float, Opacity, _settings->Opacity()); + RUNTIME_SETTING(float, FocusedOpacity, FocusedAppearance().Opacity()); RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic()); // -------------------------------- WinRT Events --------------------------------- @@ -399,7 +399,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _updateAntiAliasingMode(); void _connectionOutputHandler(const hstring& hstr); void _updateHoveredCell(const std::optional terminalPosition); - void _setOpacity(const double opacity, const bool focused = true); + void _setOpacity(const float opacity, const bool focused = true); bool _isBackgroundTransparent(); void _focusChanged(bool focused); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index d3f7e919620..038f21c0f41 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -88,7 +88,7 @@ namespace Microsoft.Terminal.Control Windows.Foundation.Size FontSize { get; }; UInt16 FontWeight { get; }; - Double Opacity { get; }; + Single Opacity { get; }; Boolean UseAcrylic { get; }; Boolean TryMarkModeKeybinding(Int16 vkey, @@ -149,7 +149,7 @@ namespace Microsoft.Terminal.Control String ReadEntireBuffer(); CommandHistoryContext CommandHistory(); - void AdjustOpacity(Double Opacity, Boolean relative); + void AdjustOpacity(Single Opacity, Boolean relative); void WindowVisibilityChanged(Boolean showOrHide); void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index cb55cca2315..7b59babd2e5 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -518,7 +518,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlInteractivity::_mouseTransparencyHandler(const int32_t mouseDelta) const { // Transparency is on a scale of [0.0,1.0], so only increment by .01. - const auto effectiveDelta = mouseDelta < 0 ? -.01 : .01; + const auto effectiveDelta = mouseDelta < 0 ? -.01f : .01f; _core->AdjustOpacity(effectiveDelta); } @@ -554,7 +554,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // underneath us. We wouldn't know - we don't want the overhead of // another ScrollPositionChanged handler. If the scrollbar should be // somewhere other than where it is currently, then start from that row. - const auto currentInternalRow = ::base::saturated_cast(::std::round(_internalScrollbarPosition)); + const auto currentInternalRow = std::lround(_internalScrollbarPosition); const auto currentCoreRow = _core->ScrollOffset(); const auto currentOffset = currentInternalRow == currentCoreRow ? _internalScrollbarPosition : @@ -564,13 +564,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation // However, for us, the signs are flipped. // With one of the precision mice, one click is always a multiple of 120 (WHEEL_DELTA), // but the "smooth scrolling" mode results in non-int values - const auto rowDelta = mouseDelta / (-1.0 * WHEEL_DELTA); + const auto rowDelta = mouseDelta / (-1.0f * WHEEL_DELTA); // WHEEL_PAGESCROLL is a Win32 constant that represents the "scroll one page // at a time" setting. If we ignore it, we will scroll a truly absurd number // of rows. - const auto rowsToScroll{ _rowsToScroll == WHEEL_PAGESCROLL ? ::base::saturated_cast(_core->ViewHeight()) : _rowsToScroll }; - auto newValue = (rowsToScroll * rowDelta) + (currentOffset); + const auto rowsToScroll{ _rowsToScroll == WHEEL_PAGESCROLL ? _core->ViewHeight() : _rowsToScroll }; + const auto newValue = rowsToScroll * rowDelta + currentOffset; // Update the Core's viewport position, and raise a // ScrollPositionChanged event to update the scrollbar @@ -600,17 +600,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - newValue: The new top of the viewport // Return Value: // - - void ControlInteractivity::UpdateScrollbar(const double newValue) + void ControlInteractivity::UpdateScrollbar(const float newValue) { // Set this as the new value of our internal scrollbar representation. // We're doing this so we can accumulate fractional amounts of a row to // scroll each time the mouse scrolls. - _internalScrollbarPosition = std::clamp(newValue, 0.0, _core->BufferHeight()); + _internalScrollbarPosition = std::clamp(newValue, 0.0f, static_cast(_core->BufferHeight())); // If the new scrollbar position, rounded to an int, is at a different // row, then actually update the scroll position in the core, and raise // a ScrollPositionChanged to inform the control. - auto viewTop = ::base::saturated_cast(::std::round(_internalScrollbarPosition)); + const auto viewTop = std::lround(_internalScrollbarPosition); if (viewTop != _core->ScrollOffset()) { _core->UserScrollViewport(viewTop); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 81eeaca9067..38789827ec3 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -78,7 +78,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const Core::Point pixelPosition, const Control::MouseButtonState state); - void UpdateScrollbar(const double newValue); + void UpdateScrollbar(const float newValue); #pragma endregion @@ -110,8 +110,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine; winrt::com_ptr _core{ nullptr }; - unsigned int _rowsToScroll; - double _internalScrollbarPosition{ 0.0 }; + UINT _rowsToScroll = 3; + float _internalScrollbarPosition = 0; // If this is set, then we assume we are in the middle of panning the // viewport via touch input. diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 086b009ba09..d1ca9720dc6 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -64,7 +64,7 @@ namespace Microsoft.Terminal.Control Microsoft.Terminal.Core.Point pixelPosition, MouseButtonState state); - void UpdateScrollbar(Double newValue); + void UpdateScrollbar(Single newValue); Boolean ManglePathsForWsl { get; }; diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 247a8c988aa..d8990c6f212 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -133,12 +133,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation struct TransparencyChangedEventArgs : public TransparencyChangedEventArgsT { public: - TransparencyChangedEventArgs(const double opacity) : + TransparencyChangedEventArgs(const float opacity) : _Opacity(opacity) { } - WINRT_PROPERTY(double, Opacity); + WINRT_PROPERTY(float, Opacity); }; struct UpdateSearchResultsEventArgs : public UpdateSearchResultsEventArgsT diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 19b31cd97d8..d04044073e8 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -75,7 +75,7 @@ namespace Microsoft.Terminal.Control runtimeclass TransparencyChangedEventArgs { - Double Opacity { get; }; + Single Opacity { get; }; } enum SearchState diff --git a/src/cascadia/TerminalControl/HwndTerminal.cpp b/src/cascadia/TerminalControl/HwndTerminal.cpp index 598028a88eb..91cb3aaa4df 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.cpp +++ b/src/cascadia/TerminalControl/HwndTerminal.cpp @@ -1090,9 +1090,9 @@ til::rect HwndTerminal::GetPadding() const noexcept return {}; } -double HwndTerminal::GetScaleFactor() const noexcept +float HwndTerminal::GetScaleFactor() const noexcept { - return static_cast(_currentDpi) / static_cast(USER_DEFAULT_SCREEN_DPI); + return static_cast(_currentDpi) / static_cast(USER_DEFAULT_SCREEN_DPI); } void HwndTerminal::ChangeViewport(const til::inclusive_rect& NewWindow) diff --git a/src/cascadia/TerminalControl/HwndTerminal.hpp b/src/cascadia/TerminalControl/HwndTerminal.hpp index 45780a1d50e..e561f1793fc 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.hpp +++ b/src/cascadia/TerminalControl/HwndTerminal.hpp @@ -146,7 +146,7 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo // Inherited via IControlAccessibilityInfo til::size GetFontSize() const noexcept override; til::rect GetBounds() const noexcept override; - double GetScaleFactor() const noexcept override; + float GetScaleFactor() const noexcept override; void ChangeViewport(const til::inclusive_rect& NewWindow) override; HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept override; til::rect GetPadding() const noexcept override; diff --git a/src/cascadia/TerminalControl/IControlAppearance.idl b/src/cascadia/TerminalControl/IControlAppearance.idl index c94e6373008..fca4be44d1f 100644 --- a/src/cascadia/TerminalControl/IControlAppearance.idl +++ b/src/cascadia/TerminalControl/IControlAppearance.idl @@ -7,12 +7,12 @@ namespace Microsoft.Terminal.Control { Microsoft.Terminal.Core.Color SelectionBackground { get; }; String BackgroundImage { get; }; - Double BackgroundImageOpacity { get; }; + Single BackgroundImageOpacity { get; }; Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode { get; }; Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment { get; }; Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment { get; }; // IntenseIsBold and IntenseIsBright are in Core Appearance - Double Opacity { get; }; + Single Opacity { get; }; Boolean UseAcrylic { get; }; // Experimental settings diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp index 03f50060468..4640acac461 100644 --- a/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp @@ -169,14 +169,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _controlPadding; } - double InteractivityAutomationPeer::GetScaleFactor() const noexcept + float InteractivityAutomationPeer::GetScaleFactor() const noexcept { - return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); + return static_cast(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel()); } void InteractivityAutomationPeer::ChangeViewport(const til::inclusive_rect& NewWindow) { - _interactivity->UpdateScrollbar(NewWindow.top); + _interactivity->UpdateScrollbar(static_cast(NewWindow.top)); } #pragma endregion diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.h b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h index fb3bdfe626d..53240468f58 100644 --- a/src/cascadia/TerminalControl/InteractivityAutomationPeer.h +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h @@ -66,7 +66,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation virtual til::size GetFontSize() const noexcept override; virtual til::rect GetBounds() const noexcept override; virtual til::rect GetPadding() const noexcept override; - virtual double GetScaleFactor() const noexcept override; + virtual float GetScaleFactor() const noexcept override; virtual void ChangeViewport(const til::inclusive_rect& NewWindow) override; virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) override; #pragma endregion diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index acab829312c..81934f10f65 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -779,10 +779,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation _interactivity.UpdateSettings(); if (_automationPeer) { - _automationPeer.SetControlPadding(Core::Padding{ newMargin.Left, - newMargin.Top, - newMargin.Right, - newMargin.Bottom }); + _automationPeer.SetControlPadding(Core::Padding{ + static_cast(newMargin.Left), + static_cast(newMargin.Top), + static_cast(newMargin.Right), + static_cast(newMargin.Bottom), + }); } _showMarksInScrollbar = settings.ShowMarks(); @@ -1050,10 +1052,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (const auto& interactivityAutoPeer{ _interactivity.OnCreateAutomationPeer() }) { const auto margins{ SwapChainPanel().Margin() }; - const Core::Padding padding{ margins.Left, - margins.Top, - margins.Right, - margins.Bottom }; + const Core::Padding padding{ + static_cast(margins.Left), + static_cast(margins.Top), + static_cast(margins.Right), + static_cast(margins.Bottom), + }; _automationPeer = winrt::make(get_strong(), padding, interactivityAutoPeer); return _automationPeer; } @@ -1254,10 +1258,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _automationPeer.UpdateControlBounds(); const auto margins{ GetPadding() }; - _automationPeer.SetControlPadding(Core::Padding{ margins.Left, - margins.Top, - margins.Right, - margins.Bottom }); + _automationPeer.SetControlPadding(Core::Padding{ + static_cast(margins.Left), + static_cast(margins.Top), + static_cast(margins.Right), + static_cast(margins.Bottom), + }); } // Likewise, run the event handlers outside of lock (they could @@ -1903,7 +1909,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } const auto newValue = args.NewValue(); - _interactivity.UpdateScrollbar(newValue); + _interactivity.UpdateScrollbar(static_cast(newValue)); // User input takes priority over terminal events so cancel // any pending scroll bar update if the user scrolls. @@ -2451,14 +2457,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation // If the settings have negative or zero row or column counts, ignore those counts. // (The lower TerminalCore layer also has upper bounds as well, but at this layer // we may eventually impose different ones depending on how many pixels we can address.) - const auto cols = ::base::saturated_cast(std::max(commandlineCols > 0 ? - commandlineCols : - settings.InitialCols(), - 1)); - const auto rows = ::base::saturated_cast(std::max(commandlineRows > 0 ? - commandlineRows : - settings.InitialRows(), - 1)); + const auto cols = static_cast(std::max(commandlineCols > 0 ? + commandlineCols : + settings.InitialCols(), + 1)); + const auto rows = static_cast(std::max(commandlineRows > 0 ? + commandlineRows : + settings.InitialRows(), + 1)); const winrt::Windows::Foundation::Size initialSize{ cols, rows }; @@ -2763,14 +2769,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Convert text buffer cursor position to client coordinate position // within the window. This point is in _pixels_ - const double clientCursorPosX = terminalPos.x * fontSize.Width; - const double clientCursorPosY = terminalPos.y * fontSize.Height; + const auto clientCursorPosX = terminalPos.x * fontSize.Width; + const auto clientCursorPosY = terminalPos.y * fontSize.Height; // Get scale factor for view - const double scaleFactor = SwapChainPanel().CompositionScaleX(); + const auto scaleFactor = SwapChainPanel().CompositionScaleX(); - const double clientCursorInDipsX = clientCursorPosX / scaleFactor; - const double clientCursorInDipsY = clientCursorPosY / scaleFactor; + const auto clientCursorInDipsX = clientCursorPosX / scaleFactor; + const auto clientCursorInDipsY = clientCursorPosY / scaleFactor; auto padding{ GetPadding() }; til::point relativeToOrigin{ til::math::rounding, @@ -3504,7 +3510,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core.ColorScheme(scheme); } - void TermControl::AdjustOpacity(const double opacity, const bool relative) + void TermControl::AdjustOpacity(const float opacity, const bool relative) { _core.AdjustOpacity(opacity, relative); } @@ -3513,7 +3519,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // defines an "Opacity", which we're actually not setting at all. We're // not overriding or changing _that_ value. Callers that want the opacity // set by the settings should call this instead. - double TermControl::BackgroundOpacity() const + float TermControl::BackgroundOpacity() const { return _core.Opacity(); } @@ -3646,7 +3652,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation cursorPos.y * fontSize.Height }; // Get scale factor for view - const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); + const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); // Adjust to DIPs const til::point clientCursorInDips{ til::math::rounding, clientCursorPos.X / scaleFactor, clientCursorPos.Y / scaleFactor }; diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 4da7ec63d0c..8568cb49b0a 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -100,7 +100,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool BracketedPasteEnabled() const noexcept; - double BackgroundOpacity() const; + float BackgroundOpacity() const; uint64_t OwningHwnd(); void OwningHwnd(uint64_t owner); @@ -171,7 +171,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept; - void AdjustOpacity(const double opacity, const bool relative); + void AdjustOpacity(const float opacity, const bool relative); bool RawWriteKeyEvent(const WORD vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown); bool RawWriteChar(const wchar_t character, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index d42afeffc28..ab17ab64d9f 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -126,13 +126,13 @@ namespace Microsoft.Terminal.Control String ReadEntireBuffer(); CommandHistoryContext CommandHistory(); - void AdjustOpacity(Double Opacity, Boolean relative); + void AdjustOpacity(Single Opacity, Boolean relative); // You'd think this should just be "Opacity", but UIElement already // defines an "Opacity", which we're actually not setting at all. We're // not overriding or changing _that_ value. Callers that want the // opacity set by the settings should call this instead. - Double BackgroundOpacity { get; }; + Single BackgroundOpacity { get; }; CursorDisplayState CursorVisibility; diff --git a/src/cascadia/TerminalCore/ICoreAppearance.idl b/src/cascadia/TerminalCore/ICoreAppearance.idl index 6d314969e9c..4ff3c60e5a9 100644 --- a/src/cascadia/TerminalCore/ICoreAppearance.idl +++ b/src/cascadia/TerminalCore/ICoreAppearance.idl @@ -59,10 +59,10 @@ namespace Microsoft.Terminal.Core // Same thing here, but with padding. Can't use Windows.UI.Thickness, so // we'll declare our own. struct Padding { - Double Left; - Double Top; - Double Right; - Double Bottom; + Single Left; + Single Top; + Single Right; + Single Bottom; }; // This is a projection of Microsoft::Terminal::Core::ControlKeyStates, diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index 15417166ad2..036b045ca5c 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -537,7 +537,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void AppearanceViewModel::SetBackgroundImageOpacityFromPercentageValue(double percentageValue) { - BackgroundImageOpacity(winrt::Microsoft::Terminal::UI::Converters::PercentageValueToPercentage(percentageValue)); + BackgroundImageOpacity(static_cast(percentageValue) / 100.0f); } void AppearanceViewModel::SetBackgroundImagePath(winrt::hstring path) diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.idl b/src/cascadia/TerminalSettingsEditor/Appearances.idl index 88fb2c6f898..2855fa21b72 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.idl +++ b/src/cascadia/TerminalSettingsEditor/Appearances.idl @@ -91,7 +91,7 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Core.CursorStyle, CursorShape); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(UInt32, CursorHeight); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, BackgroundImagePath); - OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Double, BackgroundImageOpacity); + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, BackgroundImageOpacity); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.ConvergedAlignment, BackgroundImageAlignment); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.IntenseStyle, IntenseTextStyle); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index 3dd30223fe2..04225a46efa 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void SetAcrylicOpacityPercentageValue(double value) { - Opacity(winrt::Microsoft::Terminal::UI::Converters::PercentageValueToPercentage(value)); + Opacity(static_cast(value) / 100.0f); }; void SetPadding(double value) diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index 135182a4fbf..315afdfb4fe 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -95,7 +95,7 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference, TabColor); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SuppressApplicationTitle); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, UseAcrylic); - OBSERVABLE_PROJECTED_PROFILE_SETTING(Double, Opacity); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Single, Opacity); OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.ScrollbarState, ScrollState); OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Padding); OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Commandline); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index bf34a6af140..3f0a922b6b7 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -630,12 +630,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct SplitPaneArgs : public SplitPaneArgsT { SplitPaneArgs() = default; - SplitPaneArgs(SplitType splitMode, SplitDirection direction, double size, const Model::INewContentArgs& terminalArgs) : + SplitPaneArgs(SplitType splitMode, SplitDirection direction, float size, const Model::INewContentArgs& terminalArgs) : _SplitMode{ splitMode }, _SplitDirection{ direction }, _SplitSize{ size }, _ContentArgs{ terminalArgs } {}; - SplitPaneArgs(SplitDirection direction, double size, const Model::INewContentArgs& terminalArgs) : + SplitPaneArgs(SplitDirection direction, float size, const Model::INewContentArgs& terminalArgs) : _SplitDirection{ direction }, _SplitSize{ size }, _ContentArgs{ terminalArgs } {}; @@ -648,7 +648,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ACTION_ARG(Model::SplitDirection, SplitDirection, SplitDirection::Automatic); WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, nullptr); ACTION_ARG(SplitType, SplitMode, SplitType::Manual); - ACTION_ARG(double, SplitSize, .5); + ACTION_ARG(float, SplitSize, 0.5f); static constexpr std::string_view SplitKey{ "split" }; static constexpr std::string_view SplitModeKey{ "splitMode" }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 05ed70069a9..af03e807176 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -230,15 +230,15 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass SplitPaneArgs : IActionArgs { - SplitPaneArgs(SplitType splitMode, SplitDirection split, Double size, INewContentArgs contentArgs); - SplitPaneArgs(SplitDirection split, Double size, INewContentArgs contentArgs); + SplitPaneArgs(SplitType splitMode, SplitDirection split, Single size, INewContentArgs contentArgs); + SplitPaneArgs(SplitDirection split, Single size, INewContentArgs contentArgs); SplitPaneArgs(SplitDirection split, INewContentArgs contentArgs); SplitPaneArgs(SplitType splitMode); SplitDirection SplitDirection { get; }; INewContentArgs ContentArgs { get; }; SplitType SplitMode { get; }; - Double SplitSize { get; }; + Single SplitSize { get; }; }; [default_interface] runtimeclass OpenSettingsArgs : IActionArgs diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp index 90513b146ca..92af888c525 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp @@ -53,7 +53,7 @@ Json::Value AppearanceConfig::ToJson() const JsonUtils::SetValueForKey(json, BackgroundKey, _Background); JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor); - JsonUtils::SetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); + JsonUtils::SetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); if (HasDarkColorSchemeName() || HasLightColorSchemeName()) { // check if the setting is coming from the UI, if so grab the ColorSchemeName until the settings UI is fixed. @@ -95,7 +95,7 @@ void AppearanceConfig::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor); JsonUtils::GetValueForKey(json, LegacyAcrylicTransparencyKey, _Opacity); - JsonUtils::GetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); + JsonUtils::GetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); if (json["colorScheme"].isString()) { // to make the UI happy, set ColorSchemeName. diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h index 7e8f35e6633..cffbdede6db 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h @@ -40,7 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, Background, nullptr); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, SelectionBackground, nullptr); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, CursorColor, nullptr); - INHERITABLE_SETTING(Model::IAppearanceConfig, double, Opacity, 1.0); + INHERITABLE_SETTING(Model::IAppearanceConfig, float, Opacity, 1.0f); INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, DarkColorSchemeName, L"Campbell"); INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, LightColorSchemeName, L"Campbell"); diff --git a/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl index bf8d8c5ed95..f05cb853997 100644 --- a/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl +++ b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl @@ -45,7 +45,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_APPEARANCE_SETTING(String, BackgroundImagePath); String ExpandedBackgroundImagePath { get; }; - INHERITABLE_APPEARANCE_SETTING(Double, BackgroundImageOpacity); + INHERITABLE_APPEARANCE_SETTING(Single, BackgroundImageOpacity); INHERITABLE_APPEARANCE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode); INHERITABLE_APPEARANCE_SETTING(ConvergedAlignment, BackgroundImageAlignment); @@ -54,7 +54,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_APPEARANCE_SETTING(String, PixelShaderImagePath); INHERITABLE_APPEARANCE_SETTING(IntenseStyle, IntenseTextStyle); INHERITABLE_APPEARANCE_SETTING(Microsoft.Terminal.Core.AdjustTextMode, AdjustIndistinguishableColors); - INHERITABLE_APPEARANCE_SETTING(Double, Opacity); + INHERITABLE_APPEARANCE_SETTING(Single, Opacity); INHERITABLE_APPEARANCE_SETTING(Boolean, UseAcrylic); }; } diff --git a/src/cascadia/TerminalSettingsModel/JsonUtils.h b/src/cascadia/TerminalSettingsModel/JsonUtils.h index 6b1804d5196..ec94d23c92e 100644 --- a/src/cascadia/TerminalSettingsModel/JsonUtils.h +++ b/src/cascadia/TerminalSettingsModel/JsonUtils.h @@ -729,7 +729,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils return json.isNumeric(); } - Json::Value ToJson(const float& val) + Json::Value ToJson(const float val) { return val; } @@ -753,7 +753,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils return json.isNumeric(); } - Json::Value ToJson(const double& val) + Json::Value ToJson(const double val) { return val; } diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 0375d2802ff..f0e30684c53 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -121,7 +121,7 @@ Author(s): #define MTSM_APPEARANCE_SETTINGS(X) \ X(Core::CursorStyle, CursorShape, "cursorShape", Core::CursorStyle::Bar) \ X(uint32_t, CursorHeight, "cursorHeight", DEFAULT_CURSOR_HEIGHT) \ - X(double, BackgroundImageOpacity, "backgroundImageOpacity", 1.0) \ + X(float, BackgroundImageOpacity, "backgroundImageOpacity", 1.0f) \ X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, "backgroundImageStretchMode", winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \ X(bool, RetroTerminalEffect, "experimental.retroTerminalEffect", false) \ X(hstring, PixelShaderPath, "experimental.pixelShaderPath") \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 57ec8659fd6..45b4db2aa4d 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -121,7 +121,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, guid, SessionId); INHERITABLE_SETTING(Model::TerminalSettings, bool, EnableUnfocusedAcrylic, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAcrylic, false); - INHERITABLE_SETTING(Model::TerminalSettings, double, Opacity, UseAcrylic() ? 0.5 : 1.0); + INHERITABLE_SETTING(Model::TerminalSettings, float, Opacity, UseAcrylic() ? 0.5f : 1.0f); INHERITABLE_SETTING(Model::TerminalSettings, hstring, Padding, DEFAULT_PADDING); INHERITABLE_SETTING(Model::TerminalSettings, hstring, FontFace, DEFAULT_FONT_FACE); INHERITABLE_SETTING(Model::TerminalSettings, float, FontSize, DEFAULT_FONT_SIZE); @@ -136,7 +136,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, Model::ColorScheme, AppliedColorScheme); INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage); - INHERITABLE_SETTING(Model::TerminalSettings, double, BackgroundImageOpacity, 1.0); + INHERITABLE_SETTING(Model::TerminalSettings, float, BackgroundImageOpacity, 1.0f); INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill); INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 1168e0660fb..d15c65c0fab 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -353,11 +353,11 @@ struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winr } }; -struct IntAsFloatPercentConversionTrait : ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait +struct IntAsFloatPercentConversionTrait : ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait { - double FromJson(const Json::Value& json) + float FromJson(const Json::Value& json) { - return ::base::saturated_cast(json.asUInt()) / 100.0; + return static_cast(json.asUInt()) / 100.0f; } bool CanConvert(const Json::Value& json) @@ -370,9 +370,9 @@ struct IntAsFloatPercentConversionTrait : ::Microsoft::Terminal::Settings::Model return value >= 0 && value <= 100; } - Json::Value ToJson(const double& val) + Json::Value ToJson(const float val) { - return std::clamp(::base::saturated_cast(std::round(val * 100.0)), 0u, 100u); + return std::clamp(::base::saturated_cast(std::round(val * 100.0f)), 0u, 100u); } std::string TypeDescription() const diff --git a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp index f4657e82527..aeefdca9950 100644 --- a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp @@ -137,14 +137,14 @@ namespace ControlUnitTests VERIFY_IS_NOT_NULL(core); // A callback to make sure that we're raising TransparencyChanged events - auto expectedOpacity = 0.5; + auto expectedOpacity = 0.5f; auto opacityCallback = [&](auto&&, Control::TransparencyChangedEventArgs args) mutable { VERIFY_ARE_EQUAL(expectedOpacity, args.Opacity()); VERIFY_ARE_EQUAL(expectedOpacity, core->Opacity()); // The Settings object's opacity shouldn't be changed - VERIFY_ARE_EQUAL(0.5, settings->Opacity()); + VERIFY_ARE_EQUAL(0.5f, settings->Opacity()); - if (expectedOpacity < 1.0) + if (expectedOpacity < 1.0f) { VERIFY_IS_TRUE(settings->UseAcrylic()); VERIFY_IS_TRUE(core->_settings->UseAcrylic()); @@ -153,7 +153,7 @@ namespace ControlUnitTests // GH#603: Adjusting opacity shouldn't change whether or not we // requested acrylic. - auto expectedUseAcrylic = expectedOpacity < 1.0; + auto expectedUseAcrylic = expectedOpacity < 1.0f; VERIFY_IS_TRUE(core->_settings->UseAcrylic()); VERIFY_ARE_EQUAL(expectedUseAcrylic, core->UseAcrylic()); }; @@ -166,39 +166,39 @@ namespace ControlUnitTests VERIFY_IS_TRUE(core->_initializedTerminal); Log::Comment(L"Increasing opacity till fully opaque"); - expectedOpacity += 0.1; // = 0.6; - core->AdjustOpacity(0.1); - expectedOpacity += 0.1; // = 0.7; - core->AdjustOpacity(0.1); - expectedOpacity += 0.1; // = 0.8; - core->AdjustOpacity(0.1); - expectedOpacity += 0.1; // = 0.9; - core->AdjustOpacity(0.1); - expectedOpacity += 0.1; // = 1.0; + expectedOpacity += 0.1f; // = 0.6; + core->AdjustOpacity(0.1f); + expectedOpacity += 0.1f; // = 0.7; + core->AdjustOpacity(0.1f); + expectedOpacity += 0.1f; // = 0.8; + core->AdjustOpacity(0.1f); + expectedOpacity += 0.1f; // = 0.9; + core->AdjustOpacity(0.1f); + expectedOpacity += 0.1f; // = 1.0; // cast to float because floating point numbers are mean - VERIFY_ARE_EQUAL(1.0f, base::saturated_cast(expectedOpacity)); - core->AdjustOpacity(0.1); + VERIFY_ARE_EQUAL(1.0f, expectedOpacity); + core->AdjustOpacity(0.1f); Log::Comment(L"Increasing opacity more doesn't actually change it to be >1.0"); - expectedOpacity = 1.0; - core->AdjustOpacity(0.1); + expectedOpacity = 1.0f; + core->AdjustOpacity(0.1f); Log::Comment(L"Decrease opacity"); - expectedOpacity -= 0.25; // = 0.75; - core->AdjustOpacity(-0.25); - expectedOpacity -= 0.25; // = 0.5; - core->AdjustOpacity(-0.25); - expectedOpacity -= 0.25; // = 0.25; - core->AdjustOpacity(-0.25); - expectedOpacity -= 0.25; // = 0.05; + expectedOpacity -= 0.25f; // = 0.75; + core->AdjustOpacity(-0.25f); + expectedOpacity -= 0.25f; // = 0.5; + core->AdjustOpacity(-0.25f); + expectedOpacity -= 0.25f; // = 0.25; + core->AdjustOpacity(-0.25f); + expectedOpacity -= 0.25f; // = 0.05; // cast to float because floating point numbers are mean - VERIFY_ARE_EQUAL(0.0f, base::saturated_cast(expectedOpacity)); - core->AdjustOpacity(-0.25); + VERIFY_ARE_EQUAL(0.0f, expectedOpacity); + core->AdjustOpacity(-0.25f); Log::Comment(L"Decreasing opacity more doesn't actually change it to be < 0"); - expectedOpacity = 0.0; - core->AdjustOpacity(-0.25); + expectedOpacity = 0.0f; + core->AdjustOpacity(-0.25f); } void ControlCoreTests::TestFreeAfterClose() diff --git a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp index 192cc2ad9d8..f95d9f0f16d 100644 --- a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp @@ -140,14 +140,14 @@ namespace ControlUnitTests auto [core, interactivity] = _createCoreAndInteractivity(*settings, *conn); // A callback to make sure that we're raising TransparencyChanged events - auto expectedOpacity = 0.5; + auto expectedOpacity = 0.5f; auto opacityCallback = [&](auto&&, Control::TransparencyChangedEventArgs args) mutable { VERIFY_ARE_EQUAL(expectedOpacity, args.Opacity()); VERIFY_ARE_EQUAL(expectedOpacity, core->Opacity()); // The Settings object's opacity shouldn't be changed - VERIFY_ARE_EQUAL(0.5, settings->Opacity()); + VERIFY_ARE_EQUAL(0.5f, settings->Opacity()); - auto expectedUseAcrylic = expectedOpacity < 1.0 && + auto expectedUseAcrylic = expectedOpacity < 1.0f && (useAcrylic); VERIFY_ARE_EQUAL(useAcrylic, settings->UseAcrylic()); VERIFY_ARE_EQUAL(expectedUseAcrylic, core->UseAcrylic()); @@ -162,10 +162,10 @@ namespace ControlUnitTests for (auto i = 0; i < 55; i++) { // each mouse wheel only adjusts opacity by .01 - expectedOpacity += 0.01; - if (expectedOpacity >= 1.0) + expectedOpacity += 0.01f; + if (expectedOpacity >= 1.0f) { - expectedOpacity = 1.0; + expectedOpacity = 1.0f; } // The mouse location and buttons don't matter here. @@ -180,10 +180,10 @@ namespace ControlUnitTests for (auto i = 0; i < 105; i++) { // each mouse wheel only adjusts opacity by .01 - expectedOpacity -= 0.01; - if (expectedOpacity <= 0.0) + expectedOpacity -= 0.01f; + if (expectedOpacity <= 0.0f) { - expectedOpacity = 0.0; + expectedOpacity = 0.0f; } // The mouse location and buttons don't matter here. diff --git a/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp index b4940852c91..4ef9c8a153b 100644 --- a/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/CommandTests.cpp @@ -156,7 +156,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command2"); @@ -167,7 +167,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command4"); @@ -178,7 +178,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command5"); @@ -189,7 +189,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command6"); @@ -211,7 +211,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command8"); @@ -222,7 +222,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Left, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command9"); @@ -233,7 +233,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Up, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command10"); @@ -244,7 +244,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.5f, realArgs.SplitSize()); } } @@ -274,7 +274,7 @@ namespace SettingsModelUnitTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection()); - VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize()); + VERIFY_ARE_EQUAL(0.25f, realArgs.SplitSize()); } } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index ab800163ff9..44e8fb3745b 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -1423,7 +1423,7 @@ void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool { const auto end = std::chrono::system_clock::now(); const auto millis = std::chrono::duration_cast(end - start); - const auto dt = ::base::saturated_cast(millis.count()); + const auto dt = static_cast(millis.count()); if (dt > animationDuration) { diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index 0c4a50308d3..f848f401b6b 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -19,10 +19,10 @@ // All of these settings are defined in IControlAppearance. #define CONTROL_APPEARANCE_SETTINGS(X) \ X(til::color, SelectionBackground, DEFAULT_FOREGROUND) \ - X(double, Opacity, 1.0) \ + X(float, Opacity, 1.0f) \ X(bool, UseAcrylic, false) \ X(winrt::hstring, BackgroundImage) \ - X(double, BackgroundImageOpacity, 1.0) \ + X(float, BackgroundImageOpacity, 1.0f) \ X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \ X(winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center) \ X(winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center) \ diff --git a/src/types/IControlAccessibilityInfo.h b/src/types/IControlAccessibilityInfo.h index 128feff9981..0bbe9fcf804 100644 --- a/src/types/IControlAccessibilityInfo.h +++ b/src/types/IControlAccessibilityInfo.h @@ -27,7 +27,7 @@ namespace Microsoft::Console::Types virtual til::size GetFontSize() const noexcept = 0; virtual til::rect GetBounds() const noexcept = 0; virtual til::rect GetPadding() const noexcept = 0; - virtual double GetScaleFactor() const noexcept = 0; + virtual float GetScaleFactor() const noexcept = 0; virtual void ChangeViewport(const til::inclusive_rect& NewWindow) = 0; virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) = 0; diff --git a/src/types/TermControlUiaProvider.cpp b/src/types/TermControlUiaProvider.cpp index b41b8191398..3cfc041f5da 100644 --- a/src/types/TermControlUiaProvider.cpp +++ b/src/types/TermControlUiaProvider.cpp @@ -145,7 +145,7 @@ til::rect TermControlUiaProvider::GetPadding() const noexcept return _controlInfo->GetPadding(); } -double TermControlUiaProvider::GetScaleFactor() const noexcept +float TermControlUiaProvider::GetScaleFactor() const noexcept { return _controlInfo->GetScaleFactor(); } diff --git a/src/types/TermControlUiaProvider.hpp b/src/types/TermControlUiaProvider.hpp index 6cc8a7774cb..ff2e2441414 100644 --- a/src/types/TermControlUiaProvider.hpp +++ b/src/types/TermControlUiaProvider.hpp @@ -45,7 +45,7 @@ namespace Microsoft::Terminal til::size GetFontSize() const noexcept; til::rect GetPadding() const noexcept; - double GetScaleFactor() const noexcept; + float GetScaleFactor() const noexcept; void ChangeViewport(const til::inclusive_rect& NewWindow) override; protected: From 19c24aced98057ca40cfc8356c4bd3060e1eca7e Mon Sep 17 00:00:00 2001 From: Jvr <109031036+Jvr2022@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:16:46 +0200 Subject: [PATCH 229/603] Update actions/add-to-project to 1.0.1 (#17097) --- .github/workflows/addToProject.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/addToProject.yml b/.github/workflows/addToProject.yml index 4baa537dd19..e756dec59da 100644 --- a/.github/workflows/addToProject.yml +++ b/.github/workflows/addToProject.yml @@ -13,7 +13,7 @@ jobs: name: Add issue to project runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@v0.5.0 + - uses: actions/add-to-project@v1.0.1 with: project-url: https://github.com/orgs/microsoft/projects/159 github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} From a590a1bff076317a1f0d1fd7565af4b6f0f0acf3 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 23 Apr 2024 18:18:12 +0200 Subject: [PATCH 230/603] Fix TerminalPage not being released on window close (#17107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because this holds onto the root element, `TerminalPage` gets "leaked" on Windows 10 when a window is closed until another is opened. ## Validation Steps Performed * Set a breakpoint in `Renderer::~Renderer` * Open and close a window * Breakpoint used to not get hit and now it does ✅ --- src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp | 2 -- src/cascadia/WindowsTerminal/NonClientIslandWindow.h | 1 - 2 files changed, 3 deletions(-) diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index bab65132266..73a2ea89443 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -405,8 +405,6 @@ bool NonClientIslandWindow::Initialize() // - void NonClientIslandWindow::SetContent(winrt::Windows::UI::Xaml::UIElement content) { - _clientContent = content; - _rootGrid.Children().Append(content); // SetRow only works on FrameworkElement's, so cast it to a FWE before diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index b2cbb5a8a29..bf9c815237c 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -58,7 +58,6 @@ class NonClientIslandWindow : public IslandWindow std::optional _oldIslandPos; winrt::TerminalApp::TitlebarControl _titlebar{ nullptr }; - winrt::Windows::UI::Xaml::UIElement _clientContent{ nullptr }; wil::unique_hbrush _backgroundBrush; til::color _backgroundBrushColor; From daffb2dbbffaeb78a5d9763f0c222927cf3af109 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 23 Apr 2024 18:19:38 +0200 Subject: [PATCH 231/603] AtlasEngine: Fix custom shader time imprecision (#17104) Since floats are imprecise we need to constrain the time value into a range that can be accurately represented. Assuming a monitor refresh rate of 1000 Hz, we can still easily represent 1000 seconds accurately (roughly 16 minutes). So to solve this, we'll simply treat the shader time modulo 1000s. This may lead to some unexpected jank every 16min but it keeps any ongoing animation smooth otherwise. --- src/renderer/atlas/BackendD3D.cpp | 29 +++++++++++++++++++++++++++-- src/renderer/atlas/BackendD3D.h | 3 ++- src/renderer/atlas/common.h | 2 ++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 6d7a86c5e4d..d6fb766f336 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -47,6 +47,20 @@ using namespace Microsoft::Console::Render::Atlas; static constexpr D2D1_MATRIX_3X2_F identityTransform{ .m11 = 1, .m22 = 1 }; static constexpr D2D1_COLOR_F whiteColor{ 1, 1, 1, 1 }; +static u64 queryPerfFreq() noexcept +{ + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + return std::bit_cast(li.QuadPart); +} + +static u64 queryPerfCount() noexcept +{ + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return std::bit_cast(li.QuadPart); +} + BackendD3D::BackendD3D(const RenderingPayload& p) { THROW_IF_FAILED(p.device->CreateVertexShader(&shader_vs[0], sizeof(shader_vs), nullptr, _vertexShader.addressof())); @@ -485,7 +499,14 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p) THROW_IF_FAILED(p.device->CreateSamplerState(&desc, _customShaderSamplerState.put())); } - _customShaderStartTime = std::chrono::steady_clock::now(); + // Since floats are imprecise we need to constrain the time value into a range that can be accurately represented. + // Assuming a monitor refresh rate of 1000 Hz, we can still easily represent 1000 seconds accurately (roughly 16 minutes). + // 10000 seconds would already result in a 50% error. So to avoid this, we use queryPerfCount() modulo _customShaderPerfTickMod. + // The use of a power of 10 is intentional, because shaders are often periodic and this makes any decimal multiplier up to 3 fractional + // digits not break the periodicity. For instance, with a wraparound of 1000 seconds sin(1.234*x) is still perfectly periodic. + const auto freq = queryPerfFreq(); + _customShaderPerfTickMod = freq * 1000; + _customShaderSecsPerPerfTick = 1.0f / freq; } } @@ -2111,8 +2132,12 @@ void BackendD3D::_debugDumpRenderTarget(const RenderingPayload& p) void BackendD3D::_executeCustomShader(RenderingPayload& p) { { + // See the comment in _recreateCustomShader() which initializes the two members below and explains what they do. + const auto now = queryPerfCount(); + const auto time = static_cast(now % _customShaderPerfTickMod) * _customShaderSecsPerPerfTick; + const CustomConstBuffer data{ - .time = std::chrono::duration(std::chrono::steady_clock::now() - _customShaderStartTime).count(), + .time = time, .scale = static_cast(p.s->font->dpi) / static_cast(USER_DEFAULT_SCREEN_DPI), .resolution = { static_cast(_viewportCellCount.x * p.s->font->cellSize.x), diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 6a12e704a4d..ae9803d51f8 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -248,7 +248,8 @@ namespace Microsoft::Console::Render::Atlas wil::com_ptr _customShaderSamplerState; wil::com_ptr _customShaderTexture; wil::com_ptr _customShaderTextureView; - std::chrono::steady_clock::time_point _customShaderStartTime; + u64 _customShaderPerfTickMod = 0; + f32 _customShaderSecsPerPerfTick = 0; wil::com_ptr _backgroundBitmap; wil::com_ptr _backgroundBitmapView; diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index ad9bf2587be..120b3d86662 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -146,6 +146,8 @@ namespace Microsoft::Console::Render::Atlas using i32x4 = vec4; using i32r = rect; + using u64 = uint64_t; + using f32 = float; using f32x2 = vec2; using f32x4 = vec4; From 87a9f72b9a323ef2e49db9e74bd740cc9c2aab31 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 23 Apr 2024 12:41:59 -0700 Subject: [PATCH 232/603] Don't explode when duplicating a pane (#17110) I forgot to check here if the `INewContentArgs` were null or not. Pretty dumb mistake honestly. Closes #17075 Closes #17076 --- src/cascadia/TerminalApp/TerminalPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index b10e2157c1b..3a82a05aebf 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3152,7 +3152,8 @@ namespace winrt::TerminalApp::implementation TerminalConnection::ITerminalConnection existingConnection) { - if (const auto& newTerminalArgs{ contentArgs.try_as() }) + const auto& newTerminalArgs{ contentArgs.try_as() }; + if (contentArgs == nullptr || newTerminalArgs != nullptr || contentArgs.Type().empty()) { // Terminals are of course special, and have to deal with debug taps, duplicating the tab, etc. return _MakeTerminalPane(newTerminalArgs, sourceTab, existingConnection); From 360e86b5368f229c8ece4a332ebf62b3b2bdb215 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 24 Apr 2024 00:04:35 +0200 Subject: [PATCH 233/603] Fix search highlights during reflow (#17092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR extends `til::throttled_func` to also support debouncing: * throttling: "At most 1 call every N seconds" * debouncing: "Exactly 1 call after N seconds of inactivity" Based on the latter the following series of changes were made: * An `OutputIdle` event was added to `ControlCore` which is raised once there hasn't been any incoming data in 100ms. This also triggers an update of our regex patterns (URL detection). * The event is then caught by `TermControl` which calls `Search()`. * `Search()` in turn was modified to return its results by-value as a struct, which avoids the need for a search-update event and simplifies how we update the UI. This architectural change, most importantly the removal of the `TextLayoutUpdated` event, fixes a DoS bug in Windows Terminal: As the event leads to UI thread activity, printing lots of text continuously results in the UI thread becoming unresponsive. On top of these, a number of improvements were made: * `IRenderEngine::InvalidateHighlight` was changed to take the `TextBuffer` by-reference which avoids the need to accumulate the line renditions in a `std::vector` first. This improves Debug build performance during reflow by what I guess must be roughly a magnitude faster. This difference is very noticeable. * When closing the search box, `ClearSearch()` is called to remove the highlights. The search text is restored when it's reopened, however the current search position isn't. Closes #17073 Closes #17089 ## Validation Steps Performed * UIA announcements: * Pressing Ctrl+Shift+F the first time does not lead to one ✅ * Typing the first letter does ✅ * Closing doesn't ✅ * Reopening does (as it restores the letter) ✅ * Closing the search box dismisses the highlights ✅ * Resizing the window recalculates the highlights ✅ * Changing the terminal output while the box is open recalculates the highlights ✅ --- src/buffer/out/search.cpp | 14 +- src/cascadia/TerminalControl/ControlCore.cpp | 131 ++++++++---------- src/cascadia/TerminalControl/ControlCore.h | 8 +- src/cascadia/TerminalControl/ControlCore.idl | 12 +- src/cascadia/TerminalControl/EventArgs.cpp | 1 - src/cascadia/TerminalControl/EventArgs.h | 12 -- src/cascadia/TerminalControl/EventArgs.idl | 8 -- .../TerminalControl/SearchBoxControl.cpp | 37 ++--- .../TerminalControl/SearchBoxControl.h | 7 +- .../TerminalControl/SearchBoxControl.idl | 2 - src/cascadia/TerminalControl/TermControl.cpp | 108 +++++++-------- src/cascadia/TerminalControl/TermControl.h | 8 +- src/cascadia/TerminalCore/Terminal.cpp | 5 - src/cascadia/TerminalCore/Terminal.hpp | 3 - src/cascadia/TerminalCore/TerminalApi.cpp | 16 --- src/host/outputStream.cpp | 7 +- src/host/outputStream.hpp | 1 - src/inc/til/throttled_func.h | 54 +++++--- src/renderer/atlas/AtlasEngine.api.cpp | 5 +- src/renderer/atlas/AtlasEngine.h | 2 +- src/renderer/base/RenderEngineBase.cpp | 2 +- src/renderer/base/renderer.cpp | 19 ++- src/renderer/inc/IRenderEngine.hpp | 2 +- src/renderer/inc/RenderEngineBase.hpp | 2 +- src/terminal/adapter/ITerminalApi.hpp | 1 - src/terminal/adapter/adaptDispatch.cpp | 1 - .../adapter/ut_adapter/adapterTest.cpp | 5 - 27 files changed, 200 insertions(+), 273 deletions(-) diff --git a/src/buffer/out/search.cpp b/src/buffer/out/search.cpp index 336fa2957fb..08ed8ad2ef7 100644 --- a/src/buffer/out/search.cpp +++ b/src/buffer/out/search.cpp @@ -13,7 +13,8 @@ bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, c const auto& textBuffer = renderData.GetTextBuffer(); const auto lastMutationId = textBuffer.GetLastMutationId(); - if (_needle == needle && + if (_renderData == &renderData && + _needle == needle && _caseInsensitive == caseInsensitive && _lastMutationId == lastMutationId) { @@ -21,15 +22,16 @@ bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, c return false; } + if (prevResults) + { + *prevResults = std::move(_results); + } + _renderData = &renderData; _needle = needle; _caseInsensitive = caseInsensitive; _lastMutationId = lastMutationId; - if (prevResults) - { - *prevResults = std::move(_results); - } _results = textBuffer.SearchText(needle, caseInsensitive); _index = reverse ? gsl::narrow_cast(_results.size()) - 1 : 0; _step = reverse ? -1 : 1; @@ -142,4 +144,4 @@ const std::vector& Search::Results() const noexcept ptrdiff_t Search::CurrentMatch() const noexcept { return _index; -} \ No newline at end of file +} diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index cfba0a1d46a..1e82b825eb0 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -29,19 +29,6 @@ using namespace winrt::Windows::Graphics::Display; using namespace winrt::Windows::System; using namespace winrt::Windows::ApplicationModel::DataTransfer; -// The minimum delay between updates to the scroll bar's values. -// The updates are throttled to limit power usage. -constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8); - -// The minimum delay between updating the TSF input control. -constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100); - -// The minimum delay between updating the locations of regex patterns -constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(500); - -// The delay before performing the search after change of search criteria -constexpr const auto SearchAfterChangeDelay = std::chrono::milliseconds(200); - namespace winrt::Microsoft::Terminal::Control::implementation { static winrt::Microsoft::Terminal::Core::OptionalColor OptionalFromColor(const til::color& c) noexcept @@ -117,9 +104,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnShowWindowChanged = std::bind(&ControlCore::_terminalShowWindowChanged, this, std::placeholders::_1); _terminal->SetShowWindowCallback(pfnShowWindowChanged); - auto pfnTextLayoutUpdated = std::bind(&ControlCore::_terminalTextLayoutUpdated, this); - _terminal->SetTextLayoutUpdatedCallback(pfnTextLayoutUpdated); - auto pfnPlayMidiNote = std::bind(&ControlCore::_terminalPlayMidiNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); _terminal->SetPlayMidiNoteCallback(pfnPlayMidiNote); @@ -167,21 +151,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation _dispatcher = controller.DispatcherQueue(); } - // A few different events should be throttled, so they don't fire absolutely all the time: - // * _updatePatternLocations: When there's new output, or we scroll the - // viewport, we should re-check if there are any visible hyperlinks. - // But we don't really need to do this every single time text is - // output, we can limit this update to once every 500ms. - // * _updateScrollBar: Same idea as the TSF update - we don't _really_ - // need to hop across the process boundary every time text is output. - // We can throttle this to once every 8ms, which will get us out of - // the way of the main output & rendering threads. const auto shared = _shared.lock(); + // Raises an OutputIdle event once there hasn't been any output for at least 100ms. + // It also updates all regex patterns in the viewport. + // // NOTE: Calling UpdatePatternLocations from a background // thread is a workaround for us to hit GH#12607 less often. - shared->updatePatternLocations = std::make_unique>( - UpdatePatternLocationsInterval, - [weakTerminal = std::weak_ptr{ _terminal }]() { + shared->outputIdle = std::make_unique>( + std::chrono::milliseconds{ 100 }, + [weakTerminal = std::weak_ptr{ _terminal }, weakThis = get_weak(), dispatcher = _dispatcher]() { + dispatcher.TryEnqueue(DispatcherQueuePriority::Normal, [weakThis]() { + if (const auto self = weakThis.get(); !self->_IsClosing()) + { + self->OutputIdle.raise(*self, nullptr); + } + }); + if (const auto t = weakTerminal.lock()) { const auto lock = t->LockForWriting(); @@ -189,9 +174,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation } }); + // Scrollbar updates are also expensive (XAML), so we'll throttle them as well. shared->updateScrollBar = std::make_shared>( _dispatcher, - ScrollBarUpdateInterval, + std::chrono::milliseconds{ 8 }, [weakThis = get_weak()](const auto& update) { if (auto core{ weakThis.get() }; !core->_IsClosing()) { @@ -218,7 +204,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // thread. These will be recreated in _setupDispatcherAndCallbacks, when // we're re-attached to a new control (on a possibly new UI thread). const auto shared = _shared.lock(); - shared->updatePatternLocations.reset(); + shared->outputIdle.reset(); shared->updateScrollBar.reset(); } @@ -671,9 +657,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation } const auto shared = _shared.lock_shared(); - if (shared->updatePatternLocations) + if (shared->outputIdle) { - (*shared->updatePatternLocations)(); + (*shared->outputIdle)(); } } @@ -1100,12 +1086,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation // If this function succeeds with S_FALSE, then the terminal didn't // actually change size. No need to notify the connection of this no-op. const auto hr = _terminal->UserResize({ vp.Width(), vp.Height() }); - if (SUCCEEDED(hr) && hr != S_FALSE) + if (FAILED(hr) || hr == S_FALSE) { - _connection.Resize(vp.Height(), vp.Width()); + return; + } + + _connection.Resize(vp.Height(), vp.Width()); - // let the UI know that the text layout has been updated - _terminal->NotifyTextLayoutUpdated(); + // TermControl will call Search() once the OutputIdle even fires after 100ms. + // Until then we need to hide the now-stale search results from the renderer. + ClearSearch(); + const auto shared = _shared.lock_shared(); + if (shared->outputIdle) + { + (*shared->outputIdle)(); } } @@ -1603,16 +1597,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation ShowWindowChanged.raise(*this, *showWindow); } - void ControlCore::_terminalTextLayoutUpdated() - { - ClearSearch(); - - // send an UpdateSearchResults event to the UI to put the Search UI into inactive state. - auto evArgs = winrt::make_self(); - evArgs->State(SearchState::Inactive); - UpdateSearchResults.raise(*this, *evArgs); - } - // Method Description: // - Plays a single MIDI note, blocking for the duration. // Arguments: @@ -1672,13 +1656,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - caseSensitive: boolean that represents if the current search is case sensitive // Return Value: // - - void ControlCore::Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive) + SearchResults ControlCore::Search(const std::wstring_view& text, const bool goForward, const bool caseSensitive, const bool reset) { const auto lock = _terminal->LockForWriting(); + bool searchInvalidated = false; std::vector oldResults; if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive, &oldResults)) { + searchInvalidated = true; + _cachedSearchResultRows = {}; if (SnapSearchResultToSelection()) { @@ -1687,30 +1674,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation } _terminal->SetSearchHighlights(_searcher.Results()); - _terminal->SetSearchHighlightFocused(_searcher.CurrentMatch()); } - else + else if (!reset) { _searcher.FindNext(); - _terminal->SetSearchHighlightFocused(_searcher.CurrentMatch()); } - _renderer->TriggerSearchHighlight(oldResults); - auto evArgs = winrt::make_self(); - if (!text.empty()) + int32_t totalMatches = 0; + int32_t currentMatch = 0; + if (const auto idx = _searcher.CurrentMatch(); idx >= 0) { - evArgs->State(SearchState::Active); - if (_searcher.GetCurrent()) - { - evArgs->FoundMatch(true); - evArgs->TotalMatches(gsl::narrow(_searcher.Results().size())); - evArgs->CurrentMatch(gsl::narrow(_searcher.CurrentMatch())); - } + totalMatches = gsl::narrow(_searcher.Results().size()); + currentMatch = gsl::narrow(idx); + _terminal->SetSearchHighlightFocused(gsl::narrow(idx)); } - // Raise an UpdateSearchResults event, which the control will use to update the - // UI and notify the narrator about the updated search results in the buffer - UpdateSearchResults.raise(*this, *evArgs); + _renderer->TriggerSearchHighlight(oldResults); + + return { + .TotalMatches = totalMatches, + .CurrentMatch = currentMatch, + .SearchInvalidated = searchInvalidated, + }; } Windows::Foundation::Collections::IVector ControlCore::SearchResultRows() @@ -1738,16 +1723,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::ClearSearch() { - // nothing to clear if there's no results - if (_searcher.GetCurrent()) - { - const auto lock = _terminal->LockForWriting(); - _terminal->SetSearchHighlights({}); - _terminal->SetSearchHighlightFocused({}); - _renderer->TriggerSearchHighlight(_searcher.Results()); - _searcher = {}; - _cachedSearchResultRows = {}; - } + const auto lock = _terminal->LockForWriting(); + _terminal->SetSearchHighlights({}); + _terminal->SetSearchHighlightFocused({}); + _renderer->TriggerSearchHighlight(_searcher.Results()); + _searcher = {}; + _cachedSearchResultRows = {}; } // Method Description: @@ -2130,9 +2111,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Start the throttled update of where our hyperlinks are. const auto shared = _shared.lock_shared(); - if (shared->updatePatternLocations) + if (shared->outputIdle) { - (*shared->updatePatternLocations)(); + (*shared->outputIdle)(); } } catch (...) diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index ee2e1e5a841..adcaf821024 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -219,7 +219,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SetSelectionAnchor(const til::point position); void SetEndSelectionPoint(const til::point position); - void Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive); + SearchResults Search(const std::wstring_view& text, bool goForward, bool caseSensitive, bool reset); void ClearSearch(); void SnapSearchResultToSelection(bool snap) noexcept; bool SnapSearchResultToSelection() const noexcept; @@ -279,8 +279,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event RendererWarning; til::typed_event RaiseNotice; til::typed_event TransparencyChanged; - til::typed_event<> ReceivedOutput; - til::typed_event UpdateSearchResults; + til::typed_event<> OutputIdle; til::typed_event ShowWindowChanged; til::typed_event UpdateSelectionMarkers; til::typed_event OpenHyperlink; @@ -295,7 +294,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation private: struct SharedState { - std::unique_ptr> updatePatternLocations; + std::unique_ptr> outputIdle; std::shared_ptr> updateScrollBar; }; @@ -376,7 +375,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation const int bufferSize); void _terminalTaskbarProgressChanged(); void _terminalShowWindowChanged(bool showOrHide); - void _terminalTextLayoutUpdated(); void _terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 038f21c0f41..ac5eed73452 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -49,6 +49,13 @@ namespace Microsoft.Terminal.Control Boolean EndAtRightBoundary; }; + struct SearchResults + { + Int32 TotalMatches; + Int32 CurrentMatch; + Boolean SearchInvalidated; + }; + [default_interface] runtimeclass SelectionColor { SelectionColor(); @@ -127,7 +134,7 @@ namespace Microsoft.Terminal.Control void ResumeRendering(); void BlinkAttributeTick(); - void Search(String text, Boolean goForward, Boolean caseSensitive); + SearchResults Search(String text, Boolean goForward, Boolean caseSensitive, Boolean reset); void ClearSearch(); IVector SearchResultRows { get; }; Boolean SnapSearchResultToSelection; @@ -177,8 +184,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler RendererWarning; event Windows.Foundation.TypedEventHandler RaiseNotice; event Windows.Foundation.TypedEventHandler TransparencyChanged; - event Windows.Foundation.TypedEventHandler ReceivedOutput; - event Windows.Foundation.TypedEventHandler UpdateSearchResults; + event Windows.Foundation.TypedEventHandler OutputIdle; event Windows.Foundation.TypedEventHandler UpdateSelectionMarkers; event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler CloseTerminalRequested; diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index 4c4d1690953..fb69e9e0541 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -12,7 +12,6 @@ #include "ScrollPositionChangedArgs.g.cpp" #include "RendererWarningArgs.g.cpp" #include "TransparencyChangedEventArgs.g.cpp" -#include "UpdateSearchResultsEventArgs.g.cpp" #include "ShowWindowArgs.g.cpp" #include "UpdateSelectionMarkersEventArgs.g.cpp" #include "CompletionsChangedEventArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index d8990c6f212..423e5695d73 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -12,7 +12,6 @@ #include "ScrollPositionChangedArgs.g.h" #include "RendererWarningArgs.g.h" #include "TransparencyChangedEventArgs.g.h" -#include "UpdateSearchResultsEventArgs.g.h" #include "ShowWindowArgs.g.h" #include "UpdateSelectionMarkersEventArgs.g.h" #include "CompletionsChangedEventArgs.g.h" @@ -141,17 +140,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(float, Opacity); }; - struct UpdateSearchResultsEventArgs : public UpdateSearchResultsEventArgsT - { - public: - UpdateSearchResultsEventArgs() = default; - - WINRT_PROPERTY(SearchState, State, SearchState::Inactive); - WINRT_PROPERTY(bool, FoundMatch); - WINRT_PROPERTY(int32_t, TotalMatches); - WINRT_PROPERTY(int32_t, CurrentMatch); - }; - struct ShowWindowArgs : public ShowWindowArgsT { public: diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index d04044073e8..6b0bf11e359 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -84,14 +84,6 @@ namespace Microsoft.Terminal.Control Active = 1, }; - runtimeclass UpdateSearchResultsEventArgs - { - SearchState State { get; }; - Boolean FoundMatch { get; }; - Int32 TotalMatches { get; }; - Int32 CurrentMatch { get; }; - } - runtimeclass ShowWindowArgs { Boolean ShowOrHide { get; }; diff --git a/src/cascadia/TerminalControl/SearchBoxControl.cpp b/src/cascadia/TerminalControl/SearchBoxControl.cpp index 9ce4bba86a8..eaaae04add9 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.cpp +++ b/src/cascadia/TerminalControl/SearchBoxControl.cpp @@ -195,6 +195,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + winrt::hstring SearchBoxControl::Text() + { + return TextBox().Text(); + } + // Method Description: // - Check if the current search direction is forward // Arguments: @@ -202,7 +207,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Return Value: // - bool: the current search direction, determined by the // states of the two direction buttons - bool SearchBoxControl::_GoForward() + bool SearchBoxControl::GoForward() { return GoForwardButton().IsChecked().GetBoolean(); } @@ -214,7 +219,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Return Value: // - bool: whether the current search is case sensitive (case button is checked ) // or not - bool SearchBoxControl::_CaseSensitive() + bool SearchBoxControl::CaseSensitive() { return CaseSensitivityButton().IsChecked().GetBoolean(); } @@ -240,11 +245,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto state = CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift); if (WI_IsFlagSet(state, CoreVirtualKeyStates::Down)) { - Search.raise(TextBox().Text(), !_GoForward(), _CaseSensitive()); + Search.raise(Text(), !GoForward(), CaseSensitive()); } else { - Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); + Search.raise(Text(), GoForward(), CaseSensitive()); } e.Handled(true); } @@ -335,7 +340,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // kick off search - Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); + Search.raise(Text(), GoForward(), CaseSensitive()); } // Method Description: @@ -356,7 +361,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // kick off search - Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); + Search.raise(Text(), GoForward(), CaseSensitive()); } // Method Description: @@ -394,7 +399,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void SearchBoxControl::TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/) { - SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); + SearchChanged.raise(Text(), GoForward(), CaseSensitive()); } // Method Description: @@ -406,7 +411,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void SearchBoxControl::CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/) { - SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive()); + SearchChanged.raise(Text(), GoForward(), CaseSensitive()); } // Method Description: @@ -528,20 +533,4 @@ namespace winrt::Microsoft::Terminal::Control::implementation { StatusBox().Text(L""); } - - // Method Description: - // - Enables / disables results navigation buttons - // Arguments: - // - enable: if true, the buttons should be enabled - // Return Value: - // - - void SearchBoxControl::NavigationEnabled(bool enabled) - { - GoBackwardButton().IsEnabled(enabled); - GoForwardButton().IsEnabled(enabled); - } - bool SearchBoxControl::NavigationEnabled() - { - return GoBackwardButton().IsEnabled() || GoForwardButton().IsEnabled(); - } } diff --git a/src/cascadia/TerminalControl/SearchBoxControl.h b/src/cascadia/TerminalControl/SearchBoxControl.h index 53a738c399e..60ea00da481 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.h +++ b/src/cascadia/TerminalControl/SearchBoxControl.h @@ -35,13 +35,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation void Open(std::function callback); void Close(); + winrt::hstring Text(); + bool GoForward(); + bool CaseSensitive(); void SetFocusOnTextbox(); void PopulateTextbox(const winrt::hstring& text); bool ContainsFocus(); void SetStatus(int32_t totalMatches, int32_t currentMatch); void ClearStatus(); - bool NavigationEnabled(); - void NavigationEnabled(bool enabled); void GoBackwardClicked(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& /*e*/); void GoForwardClicked(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& /*e*/); @@ -77,8 +78,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation static double _TextWidth(winrt::hstring text, double fontSize); double _GetStatusMaxWidth(); - bool _GoForward(); - bool _CaseSensitive(); void _KeyDownHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); void _CharacterHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs& e); }; diff --git a/src/cascadia/TerminalControl/SearchBoxControl.idl b/src/cascadia/TerminalControl/SearchBoxControl.idl index 42abd1fa1b3..3abf4f50229 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.idl +++ b/src/cascadia/TerminalControl/SearchBoxControl.idl @@ -16,8 +16,6 @@ namespace Microsoft.Terminal.Control Windows.Foundation.Rect ContentClipRect{ get; }; Double OpenAnimationStartPoint{ get; }; - Boolean NavigationEnabled; - event SearchHandler Search; event SearchHandler SearchChanged; event Windows.Foundation.TypedEventHandler Closed; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 81934f10f65..22efc0b18e2 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -204,7 +204,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.TransparencyChanged = _core.TransparencyChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreTransparencyChanged }); _revokers.RaiseNotice = _core.RaiseNotice(winrt::auto_revoke, { get_weak(), &TermControl::_coreRaisedNotice }); _revokers.HoveredHyperlinkChanged = _core.HoveredHyperlinkChanged(winrt::auto_revoke, { get_weak(), &TermControl::_hoveredHyperlinkChanged }); - _revokers.UpdateSearchResults = _core.UpdateSearchResults(winrt::auto_revoke, { get_weak(), &TermControl::_coreUpdateSearchResults }); + _revokers.OutputIdle = _core.OutputIdle(winrt::auto_revoke, { get_weak(), &TermControl::_coreOutputIdle }); _revokers.UpdateSelectionMarkers = _core.UpdateSelectionMarkers(winrt::auto_revoke, { get_weak(), &TermControl::_updateSelectionMarkers }); _revokers.coreOpenHyperlink = _core.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler }); _revokers.interactivityOpenHyperlink = _interactivity.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler }); @@ -534,13 +534,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation // but since code paths differ, extra work is required to ensure correctness. if (!_core.HasMultiLineSelection()) { - _core.SnapSearchResultToSelection(true); const auto selectedLine{ _core.SelectedText(true) }; _searchBox->PopulateTextbox(selectedLine); } } - _searchBox->Open([searchBox]() { searchBox.SetFocusOnTextbox(); }); + _searchBox->Open([weakThis = get_weak()]() { + if (const auto self = weakThis.get(); !self->_IsClosing()) + { + self->_searchBox->SetFocusOnTextbox(); + self->_refreshSearch(); + } + }); } } } @@ -551,13 +556,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return; } - if (!_searchBox) + if (!_searchBox || _searchBox->Visibility() != Visibility::Visible) { CreateSearchBoxControl(); } else { - _core.Search(_searchBox->TextBox().Text(), goForward, false); + _handleSearchResults(_core.Search(_searchBox->Text(), goForward, _searchBox->CaseSensitive(), false)); } } @@ -588,7 +593,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const bool goForward, const bool caseSensitive) { - _core.Search(text, goForward, caseSensitive); + _handleSearchResults(_core.Search(text, goForward, caseSensitive, false)); } // Method Description: @@ -605,7 +610,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (_searchBox && _searchBox->Visibility() == Visibility::Visible) { - _core.Search(text, goForward, caseSensitive); + _handleSearchResults(_core.Search(text, goForward, caseSensitive, false)); } } @@ -621,6 +626,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const RoutedEventArgs& /*args*/) { _searchBox->Close(); + _core.ClearSearch(); // Set focus back to terminal control this->Focus(FocusState::Programmatic); @@ -3537,69 +3543,63 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.SelectedText(trimTrailingWhitespace); } - // Method Description: - // - Triggers an update on scrollbar to redraw search scroll marks. - // - Called when the search results have changed, and the scroll marks' - // positions need to be updated. - void TermControl::_UpdateSearchScrollMarks() + void TermControl::_refreshSearch() { - // Manually send a scrollbar update, on the UI thread. We're already - // UI-driven, so that's okay. We're not really changing the scrollbar, - // but we do want to update the position of any search marks. The Core - // might send a scrollbar update event too, but if the first search hit - // is in the visible viewport, then the pips won't display until the - // user first scrolls. - auto scrollBar = ScrollBar(); - ScrollBarUpdate update{ - .newValue = scrollBar.Value(), - .newMaximum = scrollBar.Maximum(), - .newMinimum = scrollBar.Minimum(), - .newViewportSize = scrollBar.ViewportSize(), - }; - _throttledUpdateScrollbar(update); + if (!_searchBox || _searchBox->Visibility() != Visibility::Visible) + { + return; + } + + const auto text = _searchBox->Text(); + if (text.empty()) + { + return; + } + + const auto goForward = _searchBox->GoForward(); + const auto caseSensitive = _searchBox->CaseSensitive(); + _handleSearchResults(_core.Search(text, goForward, caseSensitive, true)); } - // Method Description: - // - Called when the core raises a UpdateSearchResults event. That's done in response to: - // - starting a search query with ControlCore::Search. - // - clearing search results due to change in buffer content. - // - The args will tell us if there were or were not any results for that - // particular search. We'll use that to control what to announce to - // Narrator. When we have more elaborate search information to report, we - // may want to report that here. (see GH #3920) - // Arguments: - // - args: contains information about the search state and results that were - // or were not found. - // Return Value: - // - - winrt::fire_and_forget TermControl::_coreUpdateSearchResults(const IInspectable& /*sender*/, Control::UpdateSearchResultsEventArgs args) + void TermControl::_handleSearchResults(SearchResults results) { - co_await wil::resume_foreground(Dispatcher()); - if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(*this) }) + if (!_searchBox) { - automationPeer.RaiseNotificationEvent( - Automation::Peers::AutomationNotificationKind::ActionCompleted, - Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent, - args.FoundMatch() ? RS_(L"SearchBox_MatchesAvailable") : RS_(L"SearchBox_NoMatches"), // what to announce if results were found - L"SearchBoxResultAnnouncement" /* unique name for this group of notifications */); + return; } - _UpdateSearchScrollMarks(); + _searchBox->SetStatus(results.TotalMatches, results.CurrentMatch); - if (_searchBox) + if (results.SearchInvalidated) { - _searchBox->NavigationEnabled(true); - if (args.State() == Control::SearchState::Inactive) + if (_showMarksInScrollbar) { - _searchBox->ClearStatus(); + const auto scrollBar = ScrollBar(); + ScrollBarUpdate update{ + .newValue = scrollBar.Value(), + .newMaximum = scrollBar.Maximum(), + .newMinimum = scrollBar.Minimum(), + .newViewportSize = scrollBar.ViewportSize(), + }; + _updateScrollBar->Run(update); } - else + + if (auto automationPeer{ FrameworkElementAutomationPeer::FromElement(*this) }) { - _searchBox->SetStatus(args.TotalMatches(), args.CurrentMatch()); + automationPeer.RaiseNotificationEvent( + AutomationNotificationKind::ActionCompleted, + AutomationNotificationProcessing::ImportantMostRecent, + results.TotalMatches > 0 ? RS_(L"SearchBox_MatchesAvailable") : RS_(L"SearchBox_NoMatches"), // what to announce if results were found + L"SearchBoxResultAnnouncement" /* unique name for this group of notifications */); } } } + void TermControl::_coreOutputIdle(const IInspectable& /*sender*/, const IInspectable& /*args*/) + { + _refreshSearch(); + } + void TermControl::OwningHwnd(uint64_t owner) { _core.OwningHwnd(owner); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 8568cb49b0a..4b7c4a50784 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -371,10 +371,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const; void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive); - void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive); void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); - void _UpdateSearchScrollMarks(); + void _refreshSearch(); + void _handleSearchResults(SearchResults results); void _hoveredHyperlinkChanged(const IInspectable& sender, const IInspectable& args); winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args); @@ -383,7 +383,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args); void _coreWarningBell(const IInspectable& sender, const IInspectable& args); - winrt::fire_and_forget _coreUpdateSearchResults(const IInspectable& sender, Control::UpdateSearchResultsEventArgs args); + void _coreOutputIdle(const IInspectable& sender, const IInspectable& args); til::point _toPosInDips(const Core::Point terminalCellPos); void _throttledUpdateScrollbar(const ScrollBarUpdate& update); @@ -411,7 +411,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::TransparencyChanged_revoker TransparencyChanged; Control::ControlCore::RaiseNotice_revoker RaiseNotice; Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged; - Control::ControlCore::UpdateSearchResults_revoker UpdateSearchResults; + Control::ControlCore::OutputIdle_revoker OutputIdle; Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers; Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink; Control::ControlCore::TitleChanged_revoker TitleChanged; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 25ea33fd47b..854d54394d0 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1230,11 +1230,6 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi _pfnCompletionsChanged.swap(pfn); } -void Terminal::SetTextLayoutUpdatedCallback(std::function pfn) noexcept -{ - _pfnTextLayoutUpdated.swap(pfn); -} - // Method Description: // - Stores the search highlighted regions in the terminal void Terminal::SetSearchHighlights(const std::vector& highlights) noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 7f1c20789e9..75b822b1386 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -155,7 +155,6 @@ class Microsoft::Terminal::Core::Terminal final : bool IsVtInputEnabled() const noexcept override; void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override; void NotifyBufferRotation(const int delta) override; - void NotifyTextLayoutUpdated() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; @@ -231,7 +230,6 @@ class Microsoft::Terminal::Core::Terminal final : void SetShowWindowCallback(std::function pfn) noexcept; void SetPlayMidiNoteCallback(std::function pfn) noexcept; void CompletionsChangedCallback(std::function pfn) noexcept; - void SetTextLayoutUpdatedCallback(std::function pfn) noexcept; void SetSearchHighlights(const std::vector& highlights) noexcept; void SetSearchHighlightFocused(const size_t focusedIdx); @@ -341,7 +339,6 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnShowWindowChanged; std::function _pfnPlayMidiNote; std::function _pfnCompletionsChanged; - std::function _pfnTextLayoutUpdated; RenderSettings _renderSettings; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 07c81649b7c..42f1c903c33 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -239,8 +239,6 @@ void Terminal::UseAlternateScreenBuffer(const TextAttribute& attrs) // Update scrollbars _NotifyScrollEvent(); - NotifyTextLayoutUpdated(); - // redraw the screen try { @@ -298,8 +296,6 @@ void Terminal::UseMainScreenBuffer() // Update scrollbars _NotifyScrollEvent(); - NotifyTextLayoutUpdated(); - // redraw the screen _activeBuffer().TriggerRedrawAll(); } @@ -374,15 +370,3 @@ void Terminal::NotifyBufferRotation(const int delta) _NotifyScrollEvent(); } } - -// Method Description: -// - Notifies the terminal UI layer that the text layout has changed. -// - This will be called when new text is added, or when the text is -// rearranged in the buffer due to window resize. -void Terminal::NotifyTextLayoutUpdated() -{ - if (_pfnTextLayoutUpdated) - { - _pfnTextLayoutUpdated(); - } -} diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 25a2f1fdb2d..fb20d74b75b 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -424,12 +424,7 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta) } } -void ConhostInternalGetSet::NotifyTextLayoutUpdated() -{ - // Not implemented for conhost. -} - void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/) { // Not implemented for conhost. -} \ No newline at end of file +} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index ca5711f6387..01d8abaf17f 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -68,7 +68,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void NotifyAccessibilityChange(const til::rect& changedRect) override; void NotifyBufferRotation(const int delta) override; - void NotifyTextLayoutUpdated() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; diff --git a/src/inc/til/throttled_func.h b/src/inc/til/throttled_func.h index 01c4e356aa3..d250f475af5 100644 --- a/src/inc/til/throttled_func.h +++ b/src/inc/til/throttled_func.h @@ -81,7 +81,7 @@ namespace til }; } // namespace details - template + template class throttled_func { public: @@ -118,15 +118,35 @@ namespace til throttled_func& operator=(throttled_func&&) = delete; // Throttles the invocation of the function passed to the constructor. - // If this is a trailing_throttled_func: - // If you call this function again before the underlying - // timer has expired, the new arguments will be used. + // + // If Debounce is true and you call this function again before the + // underlying timer has expired, its timeout will be reset. + // + // If Leading is true and you call this function again before the + // underlying timer has expired, the new arguments will be used. template void operator()(MakeArgs&&... args) { - if (!_storage.emplace(std::forward(args)...)) + const auto hadValue = _storage.emplace(std::forward(args)...); + + if constexpr (Debounce) { - _leading_edge(); + SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); + } + else + { + if (!hadValue) + { + SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); + } + } + + if constexpr (Leading) + { + if (!hadValue) + { + _func(); + } } } @@ -165,19 +185,9 @@ namespace til } CATCH_LOG() - void _leading_edge() - { - if constexpr (leading) - { - _func(); - } - - SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); - } - void _trailing_edge() { - if constexpr (leading) + if constexpr (Leading) { _storage.reset(); } @@ -187,7 +197,7 @@ namespace til } } - inline wil::unique_threadpool_timer _createTimer() + wil::unique_threadpool_timer _createTimer() { wil::unique_threadpool_timer timer{ CreateThreadpoolTimer(&_timer_callback, this, nullptr) }; THROW_LAST_ERROR_IF(!timer); @@ -201,6 +211,10 @@ namespace til }; template - using throttled_func_trailing = throttled_func; - using throttled_func_leading = throttled_func; + using throttled_func_trailing = throttled_func; + using throttled_func_leading = throttled_func; + + template + using debounced_func_trailing = throttled_func; + using debounced_func_leading = throttled_func; } // namespace til diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 9ef05279bf5..674dc3baf7e 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -5,6 +5,7 @@ #include "AtlasEngine.h" #include "Backend.h" +#include "../../buffer/out/textBuffer.hpp" #include "../base/FontCache.h" // #### NOTE #### @@ -95,7 +96,7 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2& out) noexcept return S_OK; } -[[nodiscard]] HRESULT AtlasEngine::InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept +[[nodiscard]] HRESULT AtlasEngine::InvalidateHighlight(std::span highlights, const TextBuffer& buffer) noexcept { const auto viewportOrigin = til::point{ _api.s->viewportOffset.x, _api.s->viewportOffset.y }; const auto viewport = til::rect{ 0, 0, _api.s->viewportCellCount.x, _api.s->viewportCellCount.y }; @@ -103,7 +104,7 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2& out) noexcept for (const auto& hi : highlights) { hi.iterate_rows(cellCountX, [&](til::CoordType row, til::CoordType beg, til::CoordType end) { - const auto shift = til::at(renditions, row) != LineRendition::SingleWidth ? 1 : 0; + const auto shift = buffer.GetLineRendition(row) != LineRendition::SingleWidth ? 1 : 0; beg <<= shift; end <<= shift; til::rect rect{ beg, row, end + 1, row + 1 }; diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 9f38b7094ff..9ad4425beae 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -33,7 +33,7 @@ namespace Microsoft::Console::Render::Atlas [[nodiscard]] HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept override; [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; - [[nodiscard]] HRESULT InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept override; + [[nodiscard]] HRESULT InvalidateHighlight(std::span highlights, const TextBuffer& buffer) noexcept override; [[nodiscard]] HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept override; diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 6ab523a1a4d..1847c1d287f 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -7,7 +7,7 @@ using namespace Microsoft::Console; using namespace Microsoft::Console::Render; -[[nodiscard]] HRESULT RenderEngineBase::InvalidateHighlight(std::span /*highlights*/, const std::vector& /*renditions*/) noexcept +[[nodiscard]] HRESULT RenderEngineBase::InvalidateHighlight(std::span /*highlights*/, const TextBuffer& /*renditions*/) noexcept { return S_OK; } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index deb8925c943..82b1eaea1b4 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -459,24 +459,21 @@ void Renderer::TriggerSelection() void Renderer::TriggerSearchHighlight(const std::vector& oldHighlights) try { - const auto& buffer = _pData->GetTextBuffer(); - const auto rows = buffer.TotalRowCount(); + // no need to invalidate focused search highlight separately as they are + // included in (all) search highlights. + const auto newHighlights = _pData->GetSearchHighlights(); - std::vector renditions; - renditions.reserve(rows); - for (til::CoordType row = 0; row < rows; ++row) + if (oldHighlights.empty() && newHighlights.empty()) { - renditions.emplace_back(buffer.GetLineRendition(row)); + return; } - // no need to invalidate focused search highlight separately as they are - // included in (all) search highlights. - const auto newHighlights = _pData->GetSearchHighlights(); + const auto& buffer = _pData->GetTextBuffer(); FOREACH_ENGINE(pEngine) { - LOG_IF_FAILED(pEngine->InvalidateHighlight(oldHighlights, renditions)); - LOG_IF_FAILED(pEngine->InvalidateHighlight(newHighlights, renditions)); + LOG_IF_FAILED(pEngine->InvalidateHighlight(oldHighlights, buffer)); + LOG_IF_FAILED(pEngine->InvalidateHighlight(newHighlights, buffer)); } NotifyPaintFrame(); diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 7533de8a4d0..a7afb4244ba 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -67,7 +67,7 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector& rectangles) noexcept = 0; - [[nodiscard]] virtual HRESULT InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept = 0; + [[nodiscard]] virtual HRESULT InvalidateHighlight(std::span highlights, const TextBuffer& buffer) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept = 0; diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index 5fd8a94c8a3..1108921edb5 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -24,7 +24,7 @@ namespace Microsoft::Console::Render class RenderEngineBase : public IRenderEngine { public: - [[nodiscard]] HRESULT InvalidateHighlight(std::span highlights, const std::vector& renditions) noexcept override; + [[nodiscard]] HRESULT InvalidateHighlight(std::span highlights, const TextBuffer& buffer) noexcept override; [[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override; [[nodiscard]] HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept override; diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index b42ffe283f9..4381c4ecbdd 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -80,7 +80,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0; virtual void NotifyBufferRotation(const int delta) = 0; - virtual void NotifyTextLayoutUpdated() = 0; virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; }; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 1a887f99f83..34bc5cd80e6 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -182,7 +182,6 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) // It's important to do this here instead of in TextBuffer, because here you // have access to the entire line of text, whereas TextBuffer writes it one // character at a time via the OutputCellIterator. - _api.NotifyTextLayoutUpdated(); textBuffer.TriggerNewTextNotification(string); } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index b17bc4429f9..5cd5fbdc29e 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -215,11 +215,6 @@ class TestGetSet final : public ITerminalApi Log::Comment(L"NotifyBufferRotation MOCK called..."); } - void NotifyTextLayoutUpdated() override - { - Log::Comment(L"NotifyTextLayoutUpdated MOCK called..."); - } - void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override { Log::Comment(L"InvokeCompletions MOCK called..."); From ce4e0df7b0da5549a69219cd3e866b1bda1c2fbd Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Tue, 23 Apr 2024 17:17:08 -0700 Subject: [PATCH 234/603] Update Azure Cloud Shell API to the newer version (#17115) Updates the `api-version` to `2023-02-01-preview` when requesting for CloudShell settings and shell ## Validation Steps Performed Can still use Azure Cloud Shell through Windows Terminal --- src/cascadia/TerminalConnection/AzureConnection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index 973ade12d63..e633e9b80c9 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -962,7 +962,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // - the user's cloud shell settings WDJ::JsonObject AzureConnection::_GetCloudShellUserSettings() { - auto uri{ fmt::format(L"{}providers/Microsoft.Portal/userSettings/cloudconsole?api-version=2020-04-01-preview", _resourceUri) }; + auto uri{ fmt::format(L"{}providers/Microsoft.Portal/userSettings/cloudconsole?api-version=2023-02-01-preview", _resourceUri) }; return _SendRequestReturningJson(uri, nullptr); } @@ -972,7 +972,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // - the uri for the cloud shell winrt::hstring AzureConnection::_GetCloudShell() { - auto uri{ fmt::format(L"{}providers/Microsoft.Portal/consoles/default?api-version=2020-04-01-preview", _resourceUri) }; + auto uri{ fmt::format(L"{}providers/Microsoft.Portal/consoles/default?api-version=2023-02-01-preview", _resourceUri) }; WWH::HttpStringContent content{ LR"-({"properties": {"osType": "linux"}})-", From 3a63832c31c3e4ef7b22169216a5a14607efe3eb Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 24 Apr 2024 12:28:02 -0500 Subject: [PATCH 235/603] [REPLAY] Move to AzureFileCopy@6 for Managed Identity support (#17121) This is required for us to move off Entra ID Application identity. (cherry picked from commit 2e7c3fa3132fa3bff23255480c593cbd3432eee5) This was approved in #16957, so I will merge with one signoff. --- build/pipelines/templates-v2/job-deploy-to-azure-storage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml index 69b82e57bb3..e2ed380fd45 100644 --- a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml +++ b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml @@ -80,7 +80,7 @@ jobs: Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute displayName: Install Azure Module Dependencies - - task: AzureFileCopy@5 + - task: AzureFileCopy@6 displayName: Publish to Storage Account inputs: sourcePath: _out/* From 19f43f70bdbf6ea3d3c575b6a532cae2ee5ca6a2 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 24 Apr 2024 13:00:57 -0500 Subject: [PATCH 236/603] build: disable CheckCFlags for now, as it is blowing up the build (#17116) OneBranch no likey. A test build is running now. --- .github/actions/spelling/allow/microsoft.txt | 2 ++ .../pipeline-onebranch-full-release-build.yml | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index 9cda69aa947..876d9fd960e 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -14,6 +14,7 @@ autoexec backplating bitmaps BOMs +checkcflags COMPUTERNAME CPLs cpptools @@ -101,3 +102,4 @@ wtl wtt wttlog Xamarin +xfgcheck diff --git a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml index 37d21c89a69..9fc9df3068f 100644 --- a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml +++ b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml @@ -82,6 +82,7 @@ extends: cloudvault: # https://aka.ms/obpipelines/cloudvault enabled: false globalSdl: # https://aka.ms/obpipelines/sdl + enableCheckCFlags: false # CheckCFlags is broken and exploding our builds; to remove, :g/BAD-FLAGS/d asyncSdl: enabled: true tsaOptionsFile: 'build/config/tsa.json' @@ -107,6 +108,8 @@ extends: parameters: pool: { type: windows } variables: + ob_sdl_checkcflags_enabled: false # BAD-FLAGS + ob_sdl_xfgcheck_enabled: false # BAD-FLAGS ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -141,6 +144,8 @@ extends: parameters: pool: { type: windows } variables: + ob_sdl_checkcflags_enabled: false # BAD-FLAGS + ob_sdl_xfgcheck_enabled: false # BAD-FLAGS ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -172,6 +177,8 @@ extends: parameters: pool: { type: windows } variables: + ob_sdl_checkcflags_enabled: false # BAD-FLAGS + ob_sdl_xfgcheck_enabled: false # BAD-FLAGS ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -223,6 +230,8 @@ extends: parameters: pool: { type: windows } variables: + ob_sdl_checkcflags_enabled: false # BAD-FLAGS + ob_sdl_xfgcheck_enabled: false # BAD-FLAGS ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -238,6 +247,8 @@ extends: parameters: pool: { type: windows } variables: + ob_sdl_checkcflags_enabled: false # BAD-FLAGS + ob_sdl_xfgcheck_enabled: false # BAD-FLAGS ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -260,6 +271,8 @@ extends: subscription: ${{ parameters.symbolPublishingSubscription }} symbolProject: ${{ parameters.symbolPublishingProject }} variables: + ob_sdl_checkcflags_enabled: false # BAD-FLAGS + ob_sdl_xfgcheck_enabled: false # BAD-FLAGS ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(Build.ArtifactStagingDirectory) From 0c3c7470b084ce5d5a3d76dfeb4c56d070ac761d Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 24 Apr 2024 23:23:50 +0200 Subject: [PATCH 237/603] Add a system message to session restore (#17113) This adds a system message which displays the time at which the buffer snapshot was written to disk. Additionally, this PR moves the snapshot loading into a background thread, so that the UI thread is unblocked and that multiple tabs/panes can load simultaneously. Closes #17031 Closes #17074 ## Validation Steps Performed Repeatedly closing and opening WT adds more and more messages. Currently, the messages get somewhat corrupted due to a bug in our line-wrap handling, or some similar part. --- .github/actions/spelling/expect/expect.txt | 1 + src/cascadia/TerminalControl/ControlCore.cpp | 46 ++++++++++++++++-- .../Resources/en-US/Resources.resw | 4 ++ src/cascadia/TerminalControl/TermControl.cpp | 47 +++++++++++++++++-- src/cascadia/TerminalControl/TermControl.h | 1 + 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 36e345cfbbd..775694924d3 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1718,6 +1718,7 @@ sysparams sysparamsext SYSTEMHAND SYSTEMMENU +SYSTEMTIME tabview TAdd taef diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 1e82b825eb0..da46f21b655 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1777,6 +1777,38 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } + FILETIME lastWriteTime; + SYSTEMTIME lastWriteSystemTime; + if (!GetFileTime(file.get(), nullptr, nullptr, &lastWriteTime) || + !FileTimeToSystemTime(&lastWriteTime, &lastWriteSystemTime)) + { + return; + } + + wchar_t dateBuf[256]; + const auto dateLen = GetDateFormatEx(nullptr, 0, &lastWriteSystemTime, nullptr, &dateBuf[0], ARRAYSIZE(dateBuf), nullptr); + wchar_t timeBuf[256]; + const auto timeLen = GetTimeFormatEx(nullptr, 0, &lastWriteSystemTime, nullptr, &timeBuf[0], ARRAYSIZE(timeBuf)); + + std::wstring message; + if (dateLen > 0 && timeLen > 0) + { + const auto msg = RS_(L"SessionRestoreMessage"); + const std::wstring_view date{ &dateBuf[0], gsl::narrow_cast(dateLen) }; + const std::wstring_view time{ &timeBuf[0], gsl::narrow_cast(timeLen) }; + // This escape sequence string + // * sets the color to white on a bright black background ("\x1b[100;37m") + // * prints " [Restored