From 9047bbbafb8a4cb421868131569360ece89a304c Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Mon, 8 Feb 2021 14:01:40 -0800 Subject: [PATCH] Separate runtime TerminalSettings from profile-TerminalSettings (#8602) ## Summary of the Pull Request The TerminalSettings object we create from profiles no longer gets passed into the control, instead, a child of that object gets passed into the control. Any overrides the control makes to the settings then live in the child. So, when we do a settings reload, we simply update the child's parent and the overrides will remain. ## PR Checklist * [ ] Closes #xxx * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [ ] 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. * [x] I work here ## Validation Steps Performed Manual testing --- .../CommandlineTest.cpp | 6 +- .../LocalTests_TerminalApp/TabTests.cpp | 4 +- .../TerminalApp/AppActionHandlers.cpp | 2 +- src/cascadia/TerminalApp/Pane.cpp | 8 +- src/cascadia/TerminalApp/TerminalPage.cpp | 4 +- src/cascadia/TerminalApp/TerminalSettings.cpp | 50 +++++++- src/cascadia/TerminalApp/TerminalSettings.h | 117 +++++++++--------- src/cascadia/TerminalControl/TermControl.cpp | 9 +- src/cascadia/TerminalControl/TermControl.h | 2 +- src/cascadia/TerminalControl/TermControl.idl | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 5 +- .../TerminalSettingsModel/IInheritable.h | 5 + src/types/colorTable.cpp | 5 + src/types/inc/colorTable.hpp | 1 + 14 files changed, 140 insertions(+), 80 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp index 40af4ab5f7e..c28c2e0b04b 100644 --- a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp +++ b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp @@ -1262,7 +1262,7 @@ namespace TerminalAppLocalTests void CommandlineTest::TestSimpleExecuteCommandlineAction() { ExecuteCommandlineArgs args{ L"new-tab" }; - auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(args); + auto actions = winrt::TerminalApp::implementation::TerminalPage::ConvertExecuteCommandlineToActions(args); VERIFY_ARE_EQUAL(1u, actions.size()); auto actionAndArgs = actions.at(0); VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); @@ -1281,7 +1281,7 @@ namespace TerminalAppLocalTests void CommandlineTest::TestMultipleCommandExecuteCommandlineAction() { ExecuteCommandlineArgs args{ L"new-tab ; split-pane" }; - auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(args); + auto actions = winrt::TerminalApp::implementation::TerminalPage::ConvertExecuteCommandlineToActions(args); VERIFY_ARE_EQUAL(2u, actions.size()); { auto actionAndArgs = actions.at(0); @@ -1317,7 +1317,7 @@ namespace TerminalAppLocalTests { // -H and -V cannot be combined. ExecuteCommandlineArgs args{ L"split-pane -H -V" }; - auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(args); + auto actions = winrt::TerminalApp::implementation::TerminalPage::ConvertExecuteCommandlineToActions(args); VERIFY_ARE_EQUAL(0u, actions.size()); } diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index 244207924dd..2479f162838 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -899,10 +899,10 @@ namespace TerminalAppLocalTests page->_SelectNextTab(true); }); - const auto palette = winrt::get_self(page->CommandPalette()); + const auto palette = winrt::get_self(page->CommandPalette()); VERIFY_ARE_EQUAL(1u, palette->_switcherStartIdx, L"Verify the index is 1 as we went right"); - VERIFY_ARE_EQUAL(implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode"); + VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode"); Log::Comment(L"Verify command palette preserves MRU order of tabs"); VERIFY_ARE_EQUAL(4u, palette->_filteredActions.Size()); diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 2d6a562f77c..ea5ea18a71f 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -363,7 +363,7 @@ namespace winrt::TerminalApp::implementation { auto controlSettings = activeControl.Settings().as(); controlSettings->ApplyColorScheme(scheme); - activeControl.UpdateSettings(*controlSettings); + activeControl.UpdateSettings(); args.Handled(true); } } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 0c064dcd677..f7d681564a5 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -642,7 +642,13 @@ void Pane::UpdateSettings(const TerminalSettings& settings, const GUID& profile) { if (profile == _profile) { - _control.UpdateSettings(settings); + // Update the parent of the control's settings object (and not the object itself) so + // that any overrides made by the control don't get affected by the reload + auto child = winrt::get_self(_control.Settings()); + auto parent = winrt::get_self(settings); + child->ClearParents(); + child->InsertParent(0, parent->get_strong()); + _control.UpdateSettings(); } } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 7c49ecdf63d..f1ba8371108 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -760,7 +760,9 @@ namespace winrt::TerminalApp::implementation } } - TermControl term{ settings, connection }; + // Give term control a child of the settings so that any overrides go in the child + // This way, when we do a settings reload we just update the parent and the overrides remain + TermControl term{ *(winrt::get_self(settings)->CreateChild()), connection }; auto newTabImpl = winrt::make_self(profileGuid, term); diff --git a/src/cascadia/TerminalApp/TerminalSettings.cpp b/src/cascadia/TerminalApp/TerminalSettings.cpp index 38ccfe45bbc..9b564490f77 100644 --- a/src/cascadia/TerminalApp/TerminalSettings.cpp +++ b/src/cascadia/TerminalApp/TerminalSettings.cpp @@ -226,13 +226,57 @@ namespace winrt::TerminalApp::implementation _CursorColor = til::color{ scheme.CursorColor() }; const auto table = scheme.Table(); - std::transform(table.cbegin(), table.cend(), _colorTable.begin(), [](auto&& color) { + std::array colorTable{}; + std::transform(table.cbegin(), table.cend(), colorTable.begin(), [](auto&& color) { return static_cast(til::color{ color }); }); + ColorTable(colorTable); } - uint32_t TerminalSettings::GetColorTableEntry(int32_t index) const noexcept + uint32_t TerminalSettings::GetColorTableEntry(int32_t index) noexcept { - return _colorTable.at(index); + return ColorTable().at(index); + } + + void TerminalSettings::ColorTable(std::array colors) + { + _ColorTable = colors; + } + + std::array TerminalSettings::ColorTable() + { + auto span = _getColorTableImpl(); + std::array colorTable{}; + if (span.size() > 0) + { + std::transform(span.begin(), span.end(), colorTable.begin(), [](auto&& color) { + return static_cast(til::color{ color }); + }); + } + else + { + const auto campbellSpan = CampbellColorTable(); + std::transform(campbellSpan.begin(), campbellSpan.end(), colorTable.begin(), [](auto&& color) { + return static_cast(til::color{ color }); + }); + } + return colorTable; + } + + gsl::span TerminalSettings::_getColorTableImpl() + { + if (_ColorTable.has_value()) + { + return gsl::make_span(*_ColorTable); + } + for (auto&& parent : _parents) + { + auto parentSpan = parent->_getColorTableImpl(); + if (parentSpan.size() > 0) + { + return parentSpan; + } + } + return {}; } } diff --git a/src/cascadia/TerminalApp/TerminalSettings.h b/src/cascadia/TerminalApp/TerminalSettings.h index f8ec65e2e27..ee54c98a667 100644 --- a/src/cascadia/TerminalApp/TerminalSettings.h +++ b/src/cascadia/TerminalApp/TerminalSettings.h @@ -15,10 +15,14 @@ Author(s): #pragma once #include "TerminalSettings.g.h" +#include "../TerminalSettingsModel/IInheritable.h" #include "../inc/cppwinrt_utils.h" +#include "../../types/inc/colorTable.hpp" #include #include +using namespace Microsoft::Console::Utils; + // fwdecl unittest classes namespace TerminalAppLocalTests { @@ -27,7 +31,7 @@ namespace TerminalAppLocalTests namespace winrt::TerminalApp::implementation { - struct TerminalSettings : TerminalSettingsT + struct TerminalSettings : TerminalSettingsT, winrt::Microsoft::Terminal::Settings::Model::implementation::IInheritable { TerminalSettings() = default; TerminalSettings(const Microsoft::Terminal::Settings::Model::CascadiaSettings& appSettings, @@ -40,35 +44,31 @@ namespace winrt::TerminalApp::implementation void ApplyColorScheme(const Microsoft::Terminal::Settings::Model::ColorScheme& scheme); -// TECHNICALLY, the hstring copy assignment can throw, but the GETSET_PROPERTY -// macro defines the operator as `noexcept`. We're not really worried about it, -// because the only time it will throw is when we're out of memory, and then -// we've got much worse problems. So just suppress that warning for now. -#pragma warning(push) -#pragma warning(disable : 26447) // --------------------------- Core Settings --------------------------- // All of these settings are defined in ICoreSettings. // GetColorTableEntry needs to be implemented manually, to get a // particular value from the array. - uint32_t GetColorTableEntry(int32_t index) const noexcept; - - GETSET_PROPERTY(uint32_t, DefaultForeground, DEFAULT_FOREGROUND_WITH_ALPHA); - GETSET_PROPERTY(uint32_t, DefaultBackground, DEFAULT_BACKGROUND_WITH_ALPHA); - GETSET_PROPERTY(uint32_t, SelectionBackground, DEFAULT_FOREGROUND); - GETSET_PROPERTY(int32_t, HistorySize, DEFAULT_HISTORY_SIZE); - GETSET_PROPERTY(int32_t, InitialRows, 30); - GETSET_PROPERTY(int32_t, InitialCols, 80); - - GETSET_PROPERTY(bool, SnapOnInput, true); - GETSET_PROPERTY(bool, AltGrAliasing, true); - GETSET_PROPERTY(uint32_t, CursorColor, DEFAULT_CURSOR_COLOR); - GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Vintage); - GETSET_PROPERTY(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); - GETSET_PROPERTY(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); - GETSET_PROPERTY(bool, CopyOnSelect, false); - - GETSET_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); + uint32_t GetColorTableEntry(int32_t index) noexcept; + void ColorTable(std::array colors); + std::array ColorTable(); + + GETSET_SETTING(uint32_t, DefaultForeground, DEFAULT_FOREGROUND_WITH_ALPHA); + GETSET_SETTING(uint32_t, DefaultBackground, DEFAULT_BACKGROUND_WITH_ALPHA); + GETSET_SETTING(uint32_t, SelectionBackground, DEFAULT_FOREGROUND); + GETSET_SETTING(int32_t, HistorySize, DEFAULT_HISTORY_SIZE); + GETSET_SETTING(int32_t, InitialRows, 30); + GETSET_SETTING(int32_t, InitialCols, 80); + + GETSET_SETTING(bool, SnapOnInput, true); + GETSET_SETTING(bool, AltGrAliasing, true); + GETSET_SETTING(uint32_t, CursorColor, DEFAULT_CURSOR_COLOR); + GETSET_SETTING(Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Vintage); + GETSET_SETTING(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); + GETSET_SETTING(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); + GETSET_SETTING(bool, CopyOnSelect, false); + + GETSET_SETTING(Windows::Foundation::IReference, TabColor, nullptr); // When set, StartingTabColor allows to create a terminal with a "sticky" tab color. // This color is prioritized above the TabColor (that is usually initialized based on profile settings). @@ -78,55 +78,54 @@ namespace winrt::TerminalApp::implementation // TODO: to ensure that this property is not populated during settings reload, // we should consider moving this property to a separate interface, // passed to the terminal only upon creation. - GETSET_PROPERTY(Windows::Foundation::IReference, StartingTabColor, nullptr); + GETSET_SETTING(Windows::Foundation::IReference, StartingTabColor, nullptr); // ------------------------ End of Core Settings ----------------------- - GETSET_PROPERTY(hstring, ProfileName); - GETSET_PROPERTY(bool, UseAcrylic, false); - GETSET_PROPERTY(double, TintOpacity, 0.5); - GETSET_PROPERTY(hstring, Padding, DEFAULT_PADDING); - GETSET_PROPERTY(hstring, FontFace, DEFAULT_FONT_FACE); - GETSET_PROPERTY(int32_t, FontSize, DEFAULT_FONT_SIZE); + GETSET_SETTING(hstring, ProfileName); + GETSET_SETTING(bool, UseAcrylic, false); + GETSET_SETTING(double, TintOpacity, 0.5); + GETSET_SETTING(hstring, Padding, DEFAULT_PADDING); + GETSET_SETTING(hstring, FontFace, DEFAULT_FONT_FACE); + GETSET_SETTING(int32_t, FontSize, DEFAULT_FONT_SIZE); - GETSET_PROPERTY(winrt::Windows::UI::Text::FontWeight, FontWeight); + GETSET_SETTING(winrt::Windows::UI::Text::FontWeight, FontWeight); - GETSET_PROPERTY(hstring, BackgroundImage); - GETSET_PROPERTY(double, BackgroundImageOpacity, 1.0); + GETSET_SETTING(hstring, BackgroundImage); + GETSET_SETTING(double, BackgroundImageOpacity, 1.0); - GETSET_PROPERTY(winrt::Windows::UI::Xaml::Media::Stretch, - BackgroundImageStretchMode, - winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill); - GETSET_PROPERTY(winrt::Windows::UI::Xaml::HorizontalAlignment, - BackgroundImageHorizontalAlignment, - winrt::Windows::UI::Xaml::HorizontalAlignment::Center); - GETSET_PROPERTY(winrt::Windows::UI::Xaml::VerticalAlignment, - BackgroundImageVerticalAlignment, - winrt::Windows::UI::Xaml::VerticalAlignment::Center); + GETSET_SETTING(winrt::Windows::UI::Xaml::Media::Stretch, + BackgroundImageStretchMode, + winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill); + GETSET_SETTING(winrt::Windows::UI::Xaml::HorizontalAlignment, + BackgroundImageHorizontalAlignment, + winrt::Windows::UI::Xaml::HorizontalAlignment::Center); + GETSET_SETTING(winrt::Windows::UI::Xaml::VerticalAlignment, + BackgroundImageVerticalAlignment, + winrt::Windows::UI::Xaml::VerticalAlignment::Center); - GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::IKeyBindings, KeyBindings, nullptr); + GETSET_SETTING(Microsoft::Terminal::TerminalControl::IKeyBindings, KeyBindings, nullptr); - GETSET_PROPERTY(hstring, Commandline); - GETSET_PROPERTY(hstring, StartingDirectory); - GETSET_PROPERTY(hstring, StartingTitle); - GETSET_PROPERTY(bool, SuppressApplicationTitle); - GETSET_PROPERTY(hstring, EnvironmentVariables); + GETSET_SETTING(hstring, Commandline); + GETSET_SETTING(hstring, StartingDirectory); + GETSET_SETTING(hstring, StartingTitle); + GETSET_SETTING(bool, SuppressApplicationTitle); + GETSET_SETTING(hstring, EnvironmentVariables); - GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible); + GETSET_SETTING(Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible); - GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale); + GETSET_SETTING(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale); - GETSET_PROPERTY(bool, RetroTerminalEffect, false); - GETSET_PROPERTY(bool, ForceFullRepaintRendering, false); - GETSET_PROPERTY(bool, SoftwareRendering, false); - GETSET_PROPERTY(bool, ForceVTInput, false); + GETSET_SETTING(bool, RetroTerminalEffect, false); + GETSET_SETTING(bool, ForceFullRepaintRendering, false); + GETSET_SETTING(bool, SoftwareRendering, false); + GETSET_SETTING(bool, ForceVTInput, false); GETSET_PROPERTY(hstring, PixelShaderPath); -#pragma warning(pop) private: - std::array _colorTable{}; - + std::optional> _ColorTable; + gsl::span _getColorTableImpl(); void _ApplyProfileSettings(const Microsoft::Terminal::Settings::Model::Profile& profile, const Windows::Foundation::Collections::IMapView& schemes); void _ApplyGlobalSettings(const Microsoft::Terminal::Settings::Model::GlobalAppSettings& globalSettings) noexcept; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 96ece398676..0761eb064b1 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -278,12 +278,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Method Description: // - Given new settings for this profile, applies the settings to the current terminal. // Arguments: - // - newSettings: New settings values for the profile in this terminal. + // - // Return Value: // - - winrt::fire_and_forget TermControl::UpdateSettings(IControlSettings newSettings) + winrt::fire_and_forget TermControl::UpdateSettings() { - _settings = newSettings; auto weakThis{ get_weak() }; // Dispatch a call to the UI thread to apply the new settings to the @@ -560,10 +559,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { solidColor.Color(newBgColor); } - - // Set the default background as transparent to prevent the - // DX layer from overwriting the background image or acrylic effect - _settings.DefaultBackground(static_cast(newBgColor.with_alpha(0))); } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 5c3585ae301..b95daef022c 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -102,7 +102,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection); - winrt::fire_and_forget UpdateSettings(IControlSettings newSettings); + winrt::fire_and_forget UpdateSettings(); hstring Title(); hstring GetProfileName() const; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index e0b6cb90e3f..5bfbdaa1f4f 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -65,7 +65,7 @@ namespace Microsoft.Terminal.TerminalControl static Windows.Foundation.Size GetProposedDimensions(Microsoft.Terminal.TerminalControl.IControlSettings settings, UInt32 dpi); - void UpdateSettings(Microsoft.Terminal.TerminalControl.IControlSettings newSettings); + void UpdateSettings(); Microsoft.Terminal.TerminalControl.IControlSettings Settings { get; }; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 151e09a3b1e..942def662ca 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -112,8 +112,11 @@ void Terminal::CreateFromSettings(ICoreSettings settings, // - settings: an ICoreSettings with new settings values for us to use. void Terminal::UpdateSettings(ICoreSettings settings) { + // Set the default background as transparent to prevent the + // DX layer from overwriting the background image or acrylic effect + til::color newBackgroundColor{ static_cast(settings.DefaultBackground()) }; + _defaultBg = newBackgroundColor.with_alpha(0); _defaultFg = settings.DefaultForeground(); - _defaultBg = settings.DefaultBackground(); CursorType cursorShape = CursorType::VerticalBar; switch (settings.CursorShape()) diff --git a/src/cascadia/TerminalSettingsModel/IInheritable.h b/src/cascadia/TerminalSettingsModel/IInheritable.h index 539b21a54f6..c8bac327034 100644 --- a/src/cascadia/TerminalSettingsModel/IInheritable.h +++ b/src/cascadia/TerminalSettingsModel/IInheritable.h @@ -41,6 +41,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return child; } + void ClearParents() + { + _parents.clear(); + } + void InsertParent(com_ptr parent) { _parents.push_back(parent); diff --git a/src/types/colorTable.cpp b/src/types/colorTable.cpp index 033f53598e6..63067454938 100644 --- a/src/types/colorTable.cpp +++ b/src/types/colorTable.cpp @@ -475,6 +475,11 @@ void Utils::InitializeCampbellColorTable(const gsl::span table) std::copy(campbellColorTable.begin(), campbellColorTable.end(), table.begin()); } +gsl::span Utils::CampbellColorTable() +{ + return gsl::make_span(campbellColorTable); +} + // Function Description: // - Fill the first 16 entries of a given color table with the Campbell color // scheme, in the Windows BGR order. diff --git a/src/types/inc/colorTable.hpp b/src/types/inc/colorTable.hpp index b9afde82131..9754fc362a5 100644 --- a/src/types/inc/colorTable.hpp +++ b/src/types/inc/colorTable.hpp @@ -17,6 +17,7 @@ namespace Microsoft::Console::Utils void InitializeCampbellColorTableForConhost(const gsl::span table); void SwapANSIColorOrderForConhost(const gsl::span table); void Initialize256ColorTable(const gsl::span table); + gsl::span CampbellColorTable(); std::optional ColorFromXOrgAppColorName(const std::wstring_view wstr) noexcept;