From f353323a233d0547c06a1800fb74c5e8f9ba58a5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 12 May 2023 13:32:12 -0500 Subject: [PATCH 01/63] I wanted to do this in one shot but _zelda_ --- src/cascadia/TerminalApp/IPaneContent.idl | 23 + src/cascadia/TerminalApp/Pane.cpp | 1106 +++++++++-------- src/cascadia/TerminalApp/Pane.h | 45 +- .../TerminalApp/TerminalAppLib.vcxproj | 8 + src/cascadia/TerminalApp/TerminalPage.cpp | 6 +- .../TerminalApp/TerminalPaneContent.cpp | 235 ++++ .../TerminalApp/TerminalPaneContent.h | 65 + .../TerminalApp/TerminalPaneContent.idl | 17 + src/cascadia/TerminalApp/TerminalTab.cpp | 13 +- 9 files changed, 944 insertions(+), 574 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/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl new file mode 100644 index 00000000000..c99392f336e --- /dev/null +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace TerminalApp +{ + interface IPaneContent + { + Windows.UI.Xaml.FrameworkElement GetRoot(); + + Windows.Foundation.Size MinSize { get; }; + + String Title { get; }; + UInt64 TaskbarState { get; }; + UInt64 TaskbarProgress { get; }; + Boolean ReadOnly { get; }; + + void Focus(); + + void Close(); + // event CloseRequested(...); + + }; +} diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index be3c28f7bab..16f05a6506a 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -33,19 +33,23 @@ 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); + + // _setupControlEvents(); // 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::_ControlGotFocusHandler }); + _lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler }); + } // 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,17 +106,17 @@ 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 }); -} -void Pane::_removeControlEvents() -{ - _controlEvents = {}; -} +// 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 }); +// } +// void Pane::_removeControlEvents() +// { +// _controlEvents = {}; +// } // Method Description: // - Extract the terminal settings from the current (leaf) pane's control @@ -128,12 +132,19 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // Leaves are the only things that have controls assert(_IsLeaf()); + // TODO! this should be in the IPaneContent interface + if (const auto& terminalPane{ _getTerminalContent() }; !terminalPane) + { + return nullptr; + } + auto termControl{ _content.GetRoot().try_as() }; + NewTerminalArgs args{}; - auto controlSettings = _control.Settings(); + auto controlSettings = termControl.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()) + if (const auto dir = termControl.WorkingDirectory(); !dir.empty()) { args.StartingDirectory(dir); } @@ -173,7 +184,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // "attach existing" rather than a "create" if (asContent) { - args.ContentId(_control.ContentId()); + args.ContentId(termControl.ContentId()); } return args; @@ -1022,199 +1033,199 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr return { nullptr, nullptr, offset }; } -// 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: -// - -void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) -{ - std::unique_lock lock{ _createCloseLock }; - // 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; - } - - const auto newConnectionState = _control.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. - 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. - 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*/) -{ - std::unique_lock lock{ _createCloseLock }; - - // 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; - } - _RestartTerminalRequestedHandlers(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() }) - { - // BODGY - // GH#12258: We learned that if you leave the MediaPlayer open, and - // press the media keys (like play/pause), then the OS will _replay the - // bell_. So we have to re-create the MediaPlayer each time we want to - // play the bell, to make sure a subsequent play doesn't come through - // and reactivate the old one. - - if (!_bellPlayer) - { - // The MediaPlayer might not exist on Windows N SKU. - try - { - _bellPlayer = winrt::Windows::Media::Playback::MediaPlayer(); - } - 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(); - - // This lambda will clean up the bell player when we're done with it. - auto weakThis2{ weak_from_this() }; - _mediaEndedRevoker = _bellPlayer.MediaEnded(winrt::auto_revoke, [weakThis2](auto&&, auto&&) { - if (auto self{ weakThis2.lock() }) - { - if (self->_bellPlayer) - { - // We need to make sure clear out the current track - // that's being played, again, so that the system can't - // come through and replay it. In testing, we needed to - // do this, closing the MediaPlayer alone wasn't good - // enough. - self->_bellPlayer.Pause(); - self->_bellPlayer.Source(nullptr); - self->_bellPlayer.Close(); - } - self->_mediaEndedRevoker.revoke(); - self->_bellPlayer = nullptr; - } - }); - } - } -} - -// 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*/) -{ - if (!_IsLeaf()) - { - return; - } - 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 - _PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar)); - } - } -} +// // 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: +// // - +// void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, +// const winrt::Windows::Foundation::IInspectable& /*args*/) +// { +// std::unique_lock lock{ _createCloseLock }; +// // 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; +// } + +// const auto newConnectionState = _control.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. +// 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. +// 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*/) +// { +// std::unique_lock lock{ _createCloseLock }; + +// // 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; +// } +// _RestartTerminalRequestedHandlers(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() }) +// { +// // BODGY +// // GH#12258: We learned that if you leave the MediaPlayer open, and +// // press the media keys (like play/pause), then the OS will _replay the +// // bell_. So we have to re-create the MediaPlayer each time we want to +// // play the bell, to make sure a subsequent play doesn't come through +// // and reactivate the old one. + +// if (!_bellPlayer) +// { +// // The MediaPlayer might not exist on Windows N SKU. +// try +// { +// _bellPlayer = winrt::Windows::Media::Playback::MediaPlayer(); +// } +// 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(); + +// // This lambda will clean up the bell player when we're done with it. +// auto weakThis2{ weak_from_this() }; +// _mediaEndedRevoker = _bellPlayer.MediaEnded(winrt::auto_revoke, [weakThis2](auto&&, auto&&) { +// if (auto self{ weakThis2.lock() }) +// { +// if (self->_bellPlayer) +// { +// // We need to make sure clear out the current track +// // that's being played, again, so that the system can't +// // come through and replay it. In testing, we needed to +// // do this, closing the MediaPlayer alone wasn't good +// // enough. +// self->_bellPlayer.Pause(); +// self->_bellPlayer.Source(nullptr); +// self->_bellPlayer.Close(); +// } +// self->_mediaEndedRevoker.revoke(); +// self->_bellPlayer = nullptr; +// } +// }); +// } +// } +// } + +// // 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*/) +// { +// if (!_IsLeaf()) +// { +// return; +// } +// 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 +// _PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar)); +// } +// } +// } // Event Description: // - Called when our control gains focus. We'll use this to trigger our GotFocus @@ -1266,21 +1277,9 @@ void Pane::Shutdown() // modify our tree std::unique_lock lock{ _createCloseLock }; - // 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. - _mediaEndedRevoker.revoke(); - if (_bellPlayer) - { - _bellPlayer.Pause(); - _bellPlayer.Source(nullptr); - _bellPlayer.Close(); - } - _bellPlayer = nullptr; - if (_IsLeaf()) { - _control.Close(); + _content.Close(); } else { @@ -1335,7 +1334,14 @@ TermControl Pane::GetLastFocusedTerminalControl() { if (p->_IsLeaf()) { - return p->_control; + if (const auto& terminalPane{ p->_content.try_as() }) + { + return terminalPane.GetTerminal(); + } + else + { + return nullptr; + } } pane = p; } @@ -1343,7 +1349,15 @@ TermControl Pane::GetLastFocusedTerminalControl() } return _firstChild->GetLastFocusedTerminalControl(); } - return _control; + + if (const auto& terminalPane{ _content.try_as() }) + { + return terminalPane.GetTerminal(); + } + else + { + return nullptr; + } } // Method Description: @@ -1355,7 +1369,14 @@ TermControl Pane::GetLastFocusedTerminalControl() // - nullptr if this Pane is a parent, otherwise the TermControl of this Pane. TermControl Pane::GetTerminalControl() { - return _IsLeaf() ? _control : nullptr; + if (const auto& terminalPane{ _getTerminalContent() }) + { + return terminalPane.GetTerminal(); + } + else + { + return nullptr; + } } // Method Description: @@ -1402,7 +1423,11 @@ void Pane::SetActive() Profile Pane::GetFocusedProfile() { auto lastFocused = GetActivePane(); - return lastFocused ? lastFocused->_profile : nullptr; + if (const auto& terminalPane{ lastFocused->_getTerminalContent() }) + { + return terminalPane.GetProfile(); + } + return nullptr; } // Method Description: @@ -1527,9 +1552,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: @@ -1615,7 +1641,7 @@ std::shared_ptr Pane::DetachPane(std::shared_ptr pane) // 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, const bool /*isDetaching*/) { // Lock the create/close lock so that another operation won't concurrently // modify our tree @@ -1655,35 +1681,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. @@ -1703,7 +1710,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 @@ -1712,14 +1720,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::_ControlGotFocusHandler }); + _lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler }); + } // 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(); // See GH#7252 // Manually fire off the GotFocus event. Typically, this is done @@ -1758,15 +1769,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(); @@ -1851,126 +1853,126 @@ winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst) if (auto pane{ weakThis.get() }) { - // This will query if animations are enabled via the "Show animations in - // Windows" setting in the OS - winrt::Windows::UI::ViewManagement::UISettings uiSettings; - const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); - const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); + //// This will query if animations are enabled via the "Show animations in + //// Windows" setting in the OS + //winrt::Windows::UI::ViewManagement::UISettings uiSettings; + //const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); + //const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); - // GH#7252: If either child is zoomed, just skip the animation. It won't work. - const auto eitherChildZoomed = pane->_firstChild->_zoomed || pane->_secondChild->_zoomed; + //// GH#7252: If either child is zoomed, just skip the animation. It won't work. + //const auto eitherChildZoomed = pane->_firstChild->_zoomed || pane->_secondChild->_zoomed; // If animations are disabled, just skip this and go straight to // _CloseChild. Curiously, the pane opening animation doesn't need this, // and will skip straight to Completed when animations are disabled, but // this one doesn't seem to. - if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed) - { - pane->_CloseChild(closeFirst, false); - co_return; - } - - // Setup the animation - - auto removedChild = closeFirst ? _firstChild : _secondChild; - auto remainingChild = closeFirst ? _secondChild : _firstChild; - const auto splitWidth = _splitState == SplitState::Vertical; - - Size removedOriginalSize{ - ::base::saturated_cast(removedChild->_root.ActualWidth()), - ::base::saturated_cast(removedChild->_root.ActualHeight()) - }; - Size remainingOriginalSize{ - ::base::saturated_cast(remainingChild->_root.ActualWidth()), - ::base::saturated_cast(remainingChild->_root.ActualHeight()) - }; - - // Remove both children from the grid - _borderFirst.Child(nullptr); - _borderSecond.Child(nullptr); - - if (_splitState == SplitState::Vertical) - { - Controls::Grid::SetColumn(_borderFirst, 0); - Controls::Grid::SetColumn(_borderSecond, 1); - } - else if (_splitState == SplitState::Horizontal) - { - Controls::Grid::SetRow(_borderFirst, 0); - Controls::Grid::SetRow(_borderSecond, 1); - } - - // Create the dummy grid. This grid will be the one we actually animate, - // in the place of the closed pane. - Controls::Grid dummyGrid; - // GH#603 - we can safely add a BG here, as the control is gone right - // away, to fill the space as the rest of the pane expands. - dummyGrid.Background(_themeResources.unfocusedBorderBrush); - // It should be the size of the closed pane. - dummyGrid.Width(removedOriginalSize.Width); - dummyGrid.Height(removedOriginalSize.Height); - - _borderFirst.Child(closeFirst ? dummyGrid : remainingChild->GetRootElement()); - _borderSecond.Child(closeFirst ? remainingChild->GetRootElement() : dummyGrid); - - // Set up the rows/cols as auto/auto, so they'll only use the size of - // the elements in the grid. - // - // * For the closed pane, we want to make that row/col "auto" sized, so - // it takes up as much space as is available. - // * For the remaining pane, we'll make that row/col "*" sized, so it - // takes all the remaining space. As the dummy grid is resized down, - // the remaining pane will expand to take the rest of the space. - _root.ColumnDefinitions().Clear(); - _root.RowDefinitions().Clear(); - if (_splitState == SplitState::Vertical) - { - auto firstColDef = Controls::ColumnDefinition(); - auto secondColDef = Controls::ColumnDefinition(); - firstColDef.Width(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - secondColDef.Width(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - _root.ColumnDefinitions().Append(firstColDef); - _root.ColumnDefinitions().Append(secondColDef); - } - else if (_splitState == SplitState::Horizontal) - { - auto firstRowDef = Controls::RowDefinition(); - auto secondRowDef = Controls::RowDefinition(); - firstRowDef.Height(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - secondRowDef.Height(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - _root.RowDefinitions().Append(firstRowDef); - _root.RowDefinitions().Append(secondRowDef); - } - - // Animate the dummy grid from its current size down to 0 - Media::Animation::DoubleAnimation animation{}; - animation.Duration(AnimationDuration); - animation.From(splitWidth ? removedOriginalSize.Width : removedOriginalSize.Height); - animation.To(0.0); - // This easing is the same as the entrance animation. - animation.EasingFunction(Media::Animation::QuadraticEase{}); - animation.EnableDependentAnimation(true); - - Media::Animation::Storyboard s; - s.Duration(AnimationDuration); - s.Children().Append(animation); - s.SetTarget(animation, dummyGrid); - s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); - - // Start the animation. - s.Begin(); - - std::weak_ptr weakThis{ shared_from_this() }; - - // When the animation is completed, reparent the child's content up to - // us, and remove the child nodes from the tree. - animation.Completed([weakThis, closeFirst](auto&&, auto&&) { - if (auto pane{ weakThis.lock() }) - { - // 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); - } - }); + // if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed) + // { + pane->_CloseChild(closeFirst, false); + co_return; + // } + + // // Setup the animation + + // auto removedChild = closeFirst ? _firstChild : _secondChild; + // auto remainingChild = closeFirst ? _secondChild : _firstChild; + // const auto splitWidth = _splitState == SplitState::Vertical; + + // Size removedOriginalSize{ + // ::base::saturated_cast(removedChild->_root.ActualWidth()), + // ::base::saturated_cast(removedChild->_root.ActualHeight()) + // }; + // Size remainingOriginalSize{ + // ::base::saturated_cast(remainingChild->_root.ActualWidth()), + // ::base::saturated_cast(remainingChild->_root.ActualHeight()) + // }; + + // // Remove both children from the grid + // _borderFirst.Child(nullptr); + // _borderSecond.Child(nullptr); + + // if (_splitState == SplitState::Vertical) + // { + // Controls::Grid::SetColumn(_borderFirst, 0); + // Controls::Grid::SetColumn(_borderSecond, 1); + // } + // else if (_splitState == SplitState::Horizontal) + // { + // Controls::Grid::SetRow(_borderFirst, 0); + // Controls::Grid::SetRow(_borderSecond, 1); + // } + + // // Create the dummy grid. This grid will be the one we actually animate, + // // in the place of the closed pane. + // Controls::Grid dummyGrid; + // // GH#603 - we can safely add a BG here, as the control is gone right + // // away, to fill the space as the rest of the pane expands. + // dummyGrid.Background(_themeResources.unfocusedBorderBrush); + // // It should be the size of the closed pane. + // dummyGrid.Width(removedOriginalSize.Width); + // dummyGrid.Height(removedOriginalSize.Height); + + // _borderFirst.Child(closeFirst ? dummyGrid : remainingChild->GetRootElement()); + // _borderSecond.Child(closeFirst ? remainingChild->GetRootElement() : dummyGrid); + + // // Set up the rows/cols as auto/auto, so they'll only use the size of + // // the elements in the grid. + // // + // // * For the closed pane, we want to make that row/col "auto" sized, so + // // it takes up as much space as is available. + // // * For the remaining pane, we'll make that row/col "*" sized, so it + // // takes all the remaining space. As the dummy grid is resized down, + // // the remaining pane will expand to take the rest of the space. + // _root.ColumnDefinitions().Clear(); + // _root.RowDefinitions().Clear(); + // if (_splitState == SplitState::Vertical) + // { + // auto firstColDef = Controls::ColumnDefinition(); + // auto secondColDef = Controls::ColumnDefinition(); + // firstColDef.Width(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + // secondColDef.Width(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + // _root.ColumnDefinitions().Append(firstColDef); + // _root.ColumnDefinitions().Append(secondColDef); + // } + // else if (_splitState == SplitState::Horizontal) + // { + // auto firstRowDef = Controls::RowDefinition(); + // auto secondRowDef = Controls::RowDefinition(); + // firstRowDef.Height(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + // secondRowDef.Height(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + // _root.RowDefinitions().Append(firstRowDef); + // _root.RowDefinitions().Append(secondRowDef); + // } + + // // Animate the dummy grid from its current size down to 0 + // Media::Animation::DoubleAnimation animation{}; + // animation.Duration(AnimationDuration); + // animation.From(splitWidth ? removedOriginalSize.Width : removedOriginalSize.Height); + // animation.To(0.0); + // // This easing is the same as the entrance animation. + // animation.EasingFunction(Media::Animation::QuadraticEase{}); + // animation.EnableDependentAnimation(true); + + // Media::Animation::Storyboard s; + // s.Duration(AnimationDuration); + // s.Children().Append(animation); + // s.SetTarget(animation, dummyGrid); + // s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); + + // // Start the animation. + // s.Begin(); + + // std::weak_ptr weakThis{ shared_from_this() }; + + // // When the animation is completed, reparent the child's content up to + // // us, and remove the child nodes from the tree. + // animation.Completed([weakThis, closeFirst](auto&&, auto&&) { + // if (auto pane{ weakThis.lock() }) + // { + // // 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); + // } + // }); } } @@ -2155,151 +2157,151 @@ void Pane::_ApplySplitDefinitions() // have been set up. void Pane::_SetupEntranceAnimation() { - // This will query if animations are enabled via the "Show animations in - // Windows" setting in the OS - winrt::Windows::UI::ViewManagement::UISettings uiSettings; - const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); - const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); - - const auto splitWidth = _splitState == SplitState::Vertical; - const auto totalSize = splitWidth ? _root.ActualWidth() : _root.ActualHeight(); - // If we don't have a size yet, it's likely that we're in startup, or we're - // being executed as a sequence of actions. In that case, just skip the - // animation. - if (totalSize <= 0 || !animationsEnabledInOS || !animationsEnabledInApp) - { - return; - } - - // Use the unfocused border color as the pane background, so an actual color - // appears behind panes as we animate them sliding in. - // - // GH#603 - We set only the background of the new pane, while it animates - // in. Once the animation is done, we'll remove that background, so if the - // user wants vintage opacity, they'll be able to see what's under the - // window. - // * If we don't give it a background, then the BG will be entirely transparent. - // * If we give the parent (us) root BG a color, then a transparent pane - // will flash opaque during the animation, then back to transparent, which - // looks bad. - _secondChild->_root.Background(_themeResources.unfocusedBorderBrush); - - const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast(totalSize)); + // // This will query if animations are enabled via the "Show animations in + // // Windows" setting in the OS + // winrt::Windows::UI::ViewManagement::UISettings uiSettings; + // const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); + // const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); + + // const auto splitWidth = _splitState == SplitState::Vertical; + // const auto totalSize = splitWidth ? _root.ActualWidth() : _root.ActualHeight(); + // // If we don't have a size yet, it's likely that we're in startup, or we're + // // being executed as a sequence of actions. In that case, just skip the + // // animation. + // if (totalSize <= 0 || !animationsEnabledInOS || !animationsEnabledInApp) + // { + // return; + // } + + // // Use the unfocused border color as the pane background, so an actual color + // // appears behind panes as we animate them sliding in. + // // + // // GH#603 - We set only the background of the new pane, while it animates + // // in. Once the animation is done, we'll remove that background, so if the + // // user wants vintage opacity, they'll be able to see what's under the + // // window. + // // * If we don't give it a background, then the BG will be entirely transparent. + // // * If we give the parent (us) root BG a color, then a transparent pane + // // will flash opaque during the animation, then back to transparent, which + // // looks bad. + // _secondChild->_root.Background(_themeResources.unfocusedBorderBrush); + + // const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast(totalSize)); // This is safe to capture this, because it's only being called in the // context of this method (not on another thread) - auto setupAnimation = [&](const auto& size, const bool isFirstChild) { - auto child = isFirstChild ? _firstChild : _secondChild; - auto childGrid = child->_root; - // If we are splitting a parent pane this may be null - auto control = child->_control; - // 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 - // * it'll animate that value using a quadratic function (like f(t) = t^2) - // * IMPORTANT! We'll manually tell the animation that "yes we know what - // we're doing, we want an animation here." - Media::Animation::DoubleAnimation animation{}; - animation.Duration(AnimationDuration); - if (isFirstChild) - { - // If we're animating the first pane, the size should decrease, from - // the full size down to the given size. - animation.From(totalSize); - animation.To(size); - } - else - { - // Otherwise, we want to show the pane getting larger, so animate - // from 0 to the requested size. - animation.From(0.0); - animation.To(size); - } - animation.EasingFunction(Media::Animation::QuadraticEase{}); - animation.EnableDependentAnimation(true); - - // Now we're going to set up the Storyboard. This is a unit that uses the - // Animation from above, and actually applies it to a property. - // * we'll set it up for the same duration as the animation we have - // * Apply the animation to the grid of the new pane we're adding to the tree. - // * apply the animation to the Width or Height property. - Media::Animation::Storyboard s; - s.Duration(AnimationDuration); - s.Children().Append(animation); - s.SetTarget(animation, childGrid); - s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); - - // BE TRICKY: - // We're animating the width or height of our child pane's grid. - // - // We DON'T want to change the size of the control itself, because the - // terminal has to reflow the buffer every time the control changes size. So - // what we're going to do there is manually set the control's size to how - // big we _actually know_ the control will be. - // - // We're also going to be changing alignment of our child pane and the - // control. This way, we'll be able to have the control stick to the inside - // of the child pane's grid (the side that's moving), while we also have the - // pane's grid stick to "outside" of the grid (the side that's not moving) - if (splitWidth) - { - // If we're animating the first child, then stick to the top/left of - // the parent pane, otherwise use the bottom/right. This is always - // the "outside" of the parent pane. - childGrid.HorizontalAlignment(isFirstChild ? HorizontalAlignment::Left : HorizontalAlignment::Right); - if (control) - { - control.HorizontalAlignment(HorizontalAlignment::Left); - control.Width(isFirstChild ? totalSize : size); - } - - // When the animation is completed, undo the trickiness from before, to - // restore the controls to the behavior they'd usually have. - animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { - childGrid.Width(NAN); - childGrid.HorizontalAlignment(HorizontalAlignment::Stretch); - if (control) - { - control.Width(NAN); - control.HorizontalAlignment(HorizontalAlignment::Stretch); - } - root.Background(nullptr); - }); - } - else - { - // If we're animating the first child, then stick to the top/left of - // the parent pane, otherwise use the bottom/right. This is always - // the "outside" of the parent pane. - childGrid.VerticalAlignment(isFirstChild ? VerticalAlignment::Top : VerticalAlignment::Bottom); - if (control) - { - control.VerticalAlignment(VerticalAlignment::Top); - control.Height(isFirstChild ? totalSize : size); - } - - // When the animation is completed, undo the trickiness from before, to - // restore the controls to the behavior they'd usually have. - animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { - childGrid.Height(NAN); - childGrid.VerticalAlignment(VerticalAlignment::Stretch); - if (control) - { - control.Height(NAN); - control.VerticalAlignment(VerticalAlignment::Stretch); - } - root.Background(nullptr); - }); - } - - // Start the animation. - s.Begin(); - }; - - // TODO: GH#7365 - animating the first child right now doesn't _really_ do - // anything. We could do better though. - setupAnimation(firstSize, true); - setupAnimation(secondSize, false); + // auto setupAnimation = [&](const auto& size, const bool isFirstChild) { + // auto child = isFirstChild ? _firstChild : _secondChild; + // auto childGrid = child->_root; + // // If we are splitting a parent pane this may be null + // auto control = child->_control; + // // 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 + // // * it'll animate that value using a quadratic function (like f(t) = t^2) + // // * IMPORTANT! We'll manually tell the animation that "yes we know what + // // we're doing, we want an animation here." + // Media::Animation::DoubleAnimation animation{}; + // animation.Duration(AnimationDuration); + // if (isFirstChild) + // { + // // If we're animating the first pane, the size should decrease, from + // // the full size down to the given size. + // animation.From(totalSize); + // animation.To(size); + // } + // else + // { + // // Otherwise, we want to show the pane getting larger, so animate + // // from 0 to the requested size. + // animation.From(0.0); + // animation.To(size); + // } + // animation.EasingFunction(Media::Animation::QuadraticEase{}); + // animation.EnableDependentAnimation(true); + + // // Now we're going to set up the Storyboard. This is a unit that uses the + // // Animation from above, and actually applies it to a property. + // // * we'll set it up for the same duration as the animation we have + // // * Apply the animation to the grid of the new pane we're adding to the tree. + // // * apply the animation to the Width or Height property. + // Media::Animation::Storyboard s; + // s.Duration(AnimationDuration); + // s.Children().Append(animation); + // s.SetTarget(animation, childGrid); + // s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); + + // // BE TRICKY: + // // We're animating the width or height of our child pane's grid. + // // + // // We DON'T want to change the size of the control itself, because the + // // terminal has to reflow the buffer every time the control changes size. So + // // what we're going to do there is manually set the control's size to how + // // big we _actually know_ the control will be. + // // + // // We're also going to be changing alignment of our child pane and the + // // control. This way, we'll be able to have the control stick to the inside + // // of the child pane's grid (the side that's moving), while we also have the + // // pane's grid stick to "outside" of the grid (the side that's not moving) + // if (splitWidth) + // { + // // If we're animating the first child, then stick to the top/left of + // // the parent pane, otherwise use the bottom/right. This is always + // // the "outside" of the parent pane. + // childGrid.HorizontalAlignment(isFirstChild ? HorizontalAlignment::Left : HorizontalAlignment::Right); + // if (control) + // { + // control.HorizontalAlignment(HorizontalAlignment::Left); + // control.Width(isFirstChild ? totalSize : size); + // } + + // // When the animation is completed, undo the trickiness from before, to + // // restore the controls to the behavior they'd usually have. + // animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { + // childGrid.Width(NAN); + // childGrid.HorizontalAlignment(HorizontalAlignment::Stretch); + // if (control) + // { + // control.Width(NAN); + // control.HorizontalAlignment(HorizontalAlignment::Stretch); + // } + // root.Background(nullptr); + // }); + // } + // else + // { + // // If we're animating the first child, then stick to the top/left of + // // the parent pane, otherwise use the bottom/right. This is always + // // the "outside" of the parent pane. + // childGrid.VerticalAlignment(isFirstChild ? VerticalAlignment::Top : VerticalAlignment::Bottom); + // if (control) + // { + // control.VerticalAlignment(VerticalAlignment::Top); + // control.Height(isFirstChild ? totalSize : size); + // } + + // // When the animation is completed, undo the trickiness from before, to + // // restore the controls to the behavior they'd usually have. + // animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { + // childGrid.Height(NAN); + // childGrid.VerticalAlignment(VerticalAlignment::Stretch); + // if (control) + // { + // control.Height(NAN); + // control.VerticalAlignment(VerticalAlignment::Stretch); + // } + // root.Background(nullptr); + // }); + // } + + // // Start the animation. + // s.Begin(); + // }; + + // // TODO: GH#7365 - animating the first child right now doesn't _really_ do + // // anything. We could do better though. + // setupAnimation(firstSize, true); + // setupAnimation(secondSize, false); } // Method Description: @@ -2517,9 +2519,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. @@ -2548,11 +2547,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; } _splitState = actualSplitType; @@ -2874,8 +2870,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 { + // TODO!: Again, bad. We're special-casing that the content just so happens to have a TermControl + const auto& termControl{ _content.GetRoot().try_as() }; + if (_IsLeaf()) { + if (!termControl) + { + return { dimension, dimension }; + } + // If we're a leaf pane, align to the grid of controlling terminal const auto minSize = _GetMinSize(); @@ -2886,7 +2890,7 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const return { minDimension, minDimension }; } - auto lower = _control.SnapDimensionToGrid(widthOrHeight, dimension); + auto lower = termControl.SnapDimensionToGrid(widthOrHeight, dimension); if (widthOrHeight) { lower += WI_IsFlagSet(_borders, Borders::Left) ? PaneBorderSize : 0; @@ -2906,7 +2910,7 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const } else { - const auto cellSize = _control.CharacterDimensions(); + const auto cellSize = termControl.CharacterDimensions(); const auto higher = lower + (widthOrHeight ? cellSize.Width : cellSize.Height); return { lower, higher }; } @@ -2950,8 +2954,9 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const // Return Value: // - void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& sizeNode) const -{ - if (_IsLeaf()) +{ // TODO!: Again, bad. We're special-casing that the content just so happens to have a TermControl + const auto& termControl{ _content.GetRoot().try_as() }; + if (_IsLeaf() && termControl) { // We're a leaf pane, so just add one more row or column (unless isMinimumSize // is true, see below). @@ -2966,10 +2971,19 @@ void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& si } else { - const auto cellSize = _control.CharacterDimensions(); + const auto cellSize = termControl.CharacterDimensions(); sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height; } } + else if (_IsLeaf()) + { + // 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 { // We're a parent pane, so we have to advance dimension of our children panes. In @@ -3072,7 +3086,7 @@ Size Pane::_GetMinSize() const { if (_IsLeaf()) { - auto controlSize = _control.MinimumSize(); + auto controlSize = _content.MinSize(); auto newWidth = controlSize.Width; auto newHeight = controlSize.Height; @@ -3170,14 +3184,18 @@ int Pane::GetLeafPaneCount() const noexcept // created via default handoff void Pane::FinalizeConfigurationGivenDefault() { - _isDefTermSession = true; + if (const auto& terminalPane{ _content.try_as() }) + { + terminalPane.FinalizeConfigurationGivenDefault(); + } + // _isDefTermSession = true; } // 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: @@ -3192,8 +3210,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 diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 6fce797d77c..80e644ea352 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 @@ -60,8 +61,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, @@ -80,7 +80,11 @@ 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(); @@ -232,10 +236,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; @@ -245,16 +247,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; - } _controlEvents; - void _setupControlEvents(); - void _removeControlEvents(); - winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker; winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker; @@ -264,13 +256,14 @@ class Pane : public std::enable_shared_from_this bool _zoomed{ false }; - winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; - winrt::Windows::Media::Playback::MediaPlayer::MediaEnded_revoker _mediaEndedRevoker; - 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, @@ -300,15 +293,15 @@ class Pane : public std::enable_shared_from_this void _Focus(); void _FocusFirstChild(); - void _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 _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, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& 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*/); + // 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; @@ -320,7 +313,7 @@ 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); + // 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 diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index a22d4f6c538..ffc38f7c2eb 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -153,6 +153,9 @@ TerminalWindow.idl + + TerminalPaneContent.idl + @@ -255,6 +258,9 @@ TerminalWindow.idl + + TerminalPaneContent.idl + @@ -322,6 +328,8 @@ + + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 405640860b1..84f34af0326 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2960,13 +2960,15 @@ namespace winrt::TerminalApp::implementation const auto control = _CreateNewControlAndContent(controlSettings, connection); - auto resultPane = std::make_shared(profile, control); + auto terminalPane{ winrt::make(profile, control) }; + auto resultPane = std::make_shared(terminalPane); 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 debugTerminalPane{ winrt::make(profile, newControl) }; + auto debugPane = std::make_shared(debugTerminalPane); // Since we're doing this split directly on the pane (instead of going through TerminalTab, // we need to handle the panes 'active' states diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp new file mode 100644 index 00000000000..922c8e6ecf3 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TerminalPaneContent.h" +#include "TerminalPaneContent.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, { this, &TerminalPaneContent::_ControlWarningBellHandler }); + _controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { this, &TerminalPaneContent::_CloseTerminalRequestedHandler }); + _controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { this, &TerminalPaneContent::_RestartTerminalRequestedHandler }); + } + void TerminalPaneContent::_removeControlEvents() + { + _controlEvents = {}; + } + + winrt::Windows::UI::Xaml::FrameworkElement TerminalPaneContent::GetRoot() + { + return _control; + } + winrt::Microsoft::Terminal::Control::TermControl TerminalPaneContent::GetTerminal() + { + return _control; + } + winrt::Windows::Foundation::Size TerminalPaneContent::MinSize() + { + return _control.MinimumSize(); + } + void TerminalPaneContent::Focus() + { + _control.Focus(FocusState::Programmatic); + } + 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. + _mediaEndedRevoker.revoke(); + if (_bellPlayer) + { + _bellPlayer.Pause(); + _bellPlayer.Source(nullptr); + _bellPlayer.Close(); + } + _bellPlayer = 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: + // - + void TerminalPaneContent::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + const auto newConnectionState = _control.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. + 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. + 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)) + { + // TODO! ask the Pane to close + // 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(); + } + + // TODO! + // // 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)); + } + } + } + + 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() }) + { + // BODGY + // GH#12258: We learned that if you leave the MediaPlayer open, and + // press the media keys (like play/pause), then the OS will _replay the + // bell_. So we have to re-create the MediaPlayer each time we want to + // play the bell, to make sure a subsequent play doesn't come through + // and reactivate the old one. + + if (!_bellPlayer) + { + // The MediaPlayer might not exist on Windows N SKU. + try + { + _bellPlayer = winrt::Windows::Media::Playback::MediaPlayer(); + } + 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(); + + // This lambda will clean up the bell player when we're done with it. + auto weakThis2{ get_weak() }; + _mediaEndedRevoker = _bellPlayer.MediaEnded(winrt::auto_revoke, [weakThis2](auto&&, auto&&) { + if (auto self{ weakThis2.get() }) + { + if (self->_bellPlayer) + { + // We need to make sure clear out the current track + // that's being played, again, so that the system can't + // come through and replay it. In testing, we needed to + // do this, closing the MediaPlayer alone wasn't good + // enough. + self->_bellPlayer.Pause(); + self->_bellPlayer.Source(nullptr); + self->_bellPlayer.Close(); + } + self->_mediaEndedRevoker.revoke(); + self->_bellPlayer = nullptr; + } + }); + } + } + } + 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*/) + { + _RestartTerminalRequestedHandlers(*this, nullptr); + } + + void TerminalPaneContent::UpdateSettings(const TerminalSettingsCreateResult& settings, + const Profile& profile) + { + _profile = profile; + _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); + } +} diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h new file mode 100644 index 00000000000..b459fedc314 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "TerminalPaneContent.g.h" +#include "../../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::TerminalApp::implementation +{ + 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 GetTerminal(); + winrt::Windows::Foundation::Size MinSize(); + void Focus(); + void Close(); + + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, + const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); + + 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(); } + + 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 }; + winrt::Windows::Media::Playback::MediaPlayer::MediaEnded_revoker _mediaEndedRevoker; + + 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; + } _controlEvents; + void _setupControlEvents(); + void _removeControlEvents(); + + winrt::fire_and_forget _playBellSound(winrt::Windows::Foundation::Uri uri); + + void _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, + // const winrt::Windows::UI::Xaml::RoutedEventArgs& e); + // void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, + // const winrt::Windows::UI::Xaml::RoutedEventArgs& 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*/); + }; +} diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl new file mode 100644 index 00000000000..2ea16b5bdc4 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "IPaneContent.idl"; + +namespace TerminalApp +{ + [default_interface] runtimeclass TerminalPaneContent : IPaneContent + { + Microsoft.Terminal.Control.TermControl GetTerminal(); + + void UpdateSettings(const Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings, + const Microsoft.Terminal.Settings.Model.Profile profile); + + Microsoft.Terminal.Settings.Model.Profile GetProfile(); + } +} diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 28f9152794b..6db28842266 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -507,7 +507,11 @@ namespace winrt::TerminalApp::implementation if (p->_IsLeaf()) { p->Id(_nextPaneId); - _AttachEventHandlersToControl(p->Id().value(), p->_control); + // TODO! this feels hacky + if (const auto& termPane{ p->_content.try_as() }) + { + _AttachEventHandlersToControl(p->Id().value(), termPane.GetTerminal()); + } _nextPaneId++; } return false; @@ -616,7 +620,12 @@ namespace winrt::TerminalApp::implementation if (p->_IsLeaf()) { p->Id(_nextPaneId); - _AttachEventHandlersToControl(p->Id().value(), p->_control); + + // TODO! this feels hacky + if (const auto& termPane{ p->_content.try_as() }) + { + _AttachEventHandlersToControl(p->Id().value(), termPane.GetTerminal()); + } _nextPaneId++; } }); From ef6bb8a73ca25fb6593cd8ff0e6bcabe006f7b5e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 17 Jul 2023 12:35:27 -0500 Subject: [PATCH 02/63] hey look, it builds now --- .../TerminalApp/AppActionHandlers.cpp | 9 +++- src/cascadia/TerminalApp/Pane.cpp | 15 +----- src/cascadia/TerminalApp/Pane.h | 6 +-- src/cascadia/TerminalApp/TabManagement.cpp | 4 +- src/cascadia/TerminalApp/TerminalPage.cpp | 26 ++++++---- src/cascadia/TerminalApp/TerminalPage.h | 6 +-- .../TerminalApp/TerminalPaneContent.cpp | 48 +++++++------------ .../TerminalApp/TerminalPaneContent.h | 7 ++- .../TerminalApp/TerminalPaneContent.idl | 5 ++ 9 files changed, 58 insertions(+), 68 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 74ae4cdbb6a..f600453a632 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1279,7 +1279,14 @@ namespace winrt::TerminalApp::implementation { if (const auto activePane{ activeTab->GetActivePane() }) { - _restartPaneConnection(activePane); + activePane; + // TODO! If we don't expose the IPaneContent, then there's no + // way to get a TerminalPaneContent to pass to + // _restartPaneConnection / _duplicateConnectionForRestart. We + // probably need to change the signature to accept a + // TermControl&Profile + + // _restartPaneConnection(activePane); } } args.Handled(true); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 5aa5d9dd751..e5c257201c3 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1251,19 +1251,6 @@ void Pane::Shutdown() // modify our tree std::unique_lock lock{ _createCloseLock }; - // TODO! This was added in main after I started working on this - // // 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()) { _content.Close(); @@ -3173,7 +3160,7 @@ void Pane::FinalizeConfigurationGivenDefault() { if (const auto& terminalPane{ _content.try_as() }) { - terminalPane.FinalizeConfigurationGivenDefault(); + terminalPane.MarkAsDefterm(); } // _isDefTermSession = true; } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 1b0f18a6509..e3aea4cdd76 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -216,7 +216,7 @@ class Pane : public std::enable_shared_from_this WINRT_CALLBACK(LostFocus, winrt::delegate>); WINRT_CALLBACK(PaneRaiseBell, winrt::Windows::Foundation::EventHandler); WINRT_CALLBACK(Detached, winrt::delegate>); - WINRT_CALLBACK(RestartTerminalRequested, winrt::delegate>); + // WINRT_CALLBACK(RestartTerminalRequested, winrt::delegate>); private: struct PanePoint; @@ -256,10 +256,6 @@ class Pane : public std::enable_shared_from_this bool _zoomed{ false }; - // TODO! these were added in main after I started working on this - // winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; - // bool _bellPlayerCreated{ false }; - bool _IsLeaf() const noexcept; bool _HasFocusedChild() const noexcept; void _SetupChildCloseHandlers(); diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 8d8cca21ef6..eb05f15ddc0 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/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f4fb4788536..7cedbbab0bf 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1282,15 +1282,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.GetTerminal() }; if (control == nullptr) { return nullptr; } const auto& connection = control.Connection(); - auto profile{ pane->GetProfile() }; + auto profile{ paneContent.GetProfile() }; TerminalSettingsCreateResult controlSettings{ nullptr }; @@ -2922,10 +2927,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 terminalPane{ winrt::make(profile, control) }; + return std::make_shared(terminalPane); } TerminalSettingsCreateResult controlSettings{ nullptr }; @@ -3003,16 +3007,18 @@ namespace winrt::TerminalApp::implementation original->SetActive(); } - resultPane->RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection }); + terminalPane.RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection }); 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) }) + if (const auto& connection{ _duplicateConnectionForRestart(paneContent) }) { - pane->GetTerminalControl().Connection(connection); + paneContent.GetTerminal().Connection(connection); connection.Start(); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index bde39cca453..31100ca1eb3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -294,14 +294,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); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 922c8e6ecf3..55eebc2cc4b 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -57,14 +57,14 @@ namespace winrt::TerminalApp::implementation // 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. - _mediaEndedRevoker.revoke(); if (_bellPlayer) { _bellPlayer.Pause(); _bellPlayer.Source(nullptr); _bellPlayer.Close(); + _bellPlayer = nullptr; + _bellPlayerCreated = false; } - _bellPlayer = nullptr; } // Method Description: @@ -168,19 +168,15 @@ namespace winrt::TerminalApp::implementation co_await wil::resume_foreground(_control.Dispatcher()); if (auto pane{ weakThis.get() }) { - // BODGY - // GH#12258: We learned that if you leave the MediaPlayer open, and - // press the media keys (like play/pause), then the OS will _replay the - // bell_. So we have to re-create the MediaPlayer each time we want to - // play the bell, to make sure a subsequent play doesn't come through - // and reactivate the old one. - - if (!_bellPlayer) + 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(); } @@ -190,27 +186,6 @@ namespace winrt::TerminalApp::implementation const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) }; _bellPlayer.Source(item); _bellPlayer.Play(); - - // This lambda will clean up the bell player when we're done with it. - auto weakThis2{ get_weak() }; - _mediaEndedRevoker = _bellPlayer.MediaEnded(winrt::auto_revoke, [weakThis2](auto&&, auto&&) { - if (auto self{ weakThis2.get() }) - { - if (self->_bellPlayer) - { - // We need to make sure clear out the current track - // that's being played, again, so that the system can't - // come through and replay it. In testing, we needed to - // do this, closing the MediaPlayer alone wasn't good - // enough. - self->_bellPlayer.Pause(); - self->_bellPlayer.Source(nullptr); - self->_bellPlayer.Close(); - } - self->_mediaEndedRevoker.revoke(); - self->_bellPlayer = nullptr; - } - }); } } } @@ -223,7 +198,7 @@ namespace winrt::TerminalApp::implementation void TerminalPaneContent::_RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - _RestartTerminalRequestedHandlers(*this, nullptr); + RestartTerminalRequested.raise(*this, nullptr); } void TerminalPaneContent::UpdateSettings(const TerminalSettingsCreateResult& settings, @@ -232,4 +207,13 @@ namespace winrt::TerminalApp::implementation _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; + } } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index b459fedc314..69a9dd34052 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -4,6 +4,7 @@ #pragma once #include "TerminalPaneContent.g.h" #include "../../cascadia/inc/cppwinrt_utils.h" +#include namespace winrt::TerminalApp::implementation { @@ -21,6 +22,8 @@ namespace winrt::TerminalApp::implementation 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; @@ -31,6 +34,8 @@ namespace winrt::TerminalApp::implementation uint64_t TaskbarProgress() { return _control.TaskbarProgress(); } bool ReadOnly() { return _control.ReadOnly(); } + til::typed_event RestartTerminalRequested; + private: winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr }; winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected }; @@ -38,7 +43,7 @@ namespace winrt::TerminalApp::implementation bool _isDefTermSession{ false }; winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; - winrt::Windows::Media::Playback::MediaPlayer::MediaEnded_revoker _mediaEndedRevoker; + bool _bellPlayerCreated{ false }; struct ControlEventTokens { diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 2ea16b5bdc4..d23f31e3d48 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -12,6 +12,11 @@ namespace TerminalApp 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; } } From 5b3aa54b560ba2ac30005f568def90baa0de2d5e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 17 Jul 2023 12:42:43 -0500 Subject: [PATCH 03/63] move GetNewTerminalArgs into IPaneContent --- src/cascadia/TerminalApp/IPaneContent.idl | 2 + src/cascadia/TerminalApp/Pane.cpp | 57 +------------------ .../TerminalApp/TerminalPaneContent.cpp | 53 +++++++++++++++++ .../TerminalApp/TerminalPaneContent.h | 2 + 4 files changed, 58 insertions(+), 56 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index c99392f336e..919d299bea5 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -14,6 +14,8 @@ namespace TerminalApp UInt64 TaskbarProgress { get; }; Boolean ReadOnly { get; }; + Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent); + void Focus(); void Close(); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index e5c257201c3..f3f5f87119b 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -132,62 +132,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // Leaves are the only things that have controls assert(_IsLeaf()); - // TODO! this should be in the IPaneContent interface - if (const auto& terminalPane{ _getTerminalContent() }; !terminalPane) - { - return nullptr; - } - auto termControl{ _content.GetRoot().try_as() }; - - NewTerminalArgs args{}; - auto controlSettings = termControl.Settings(); - - args.Profile(controlSettings.ProfileName()); - // If we know the user's working directory use it instead of the profile. - if (const auto dir = termControl.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(termControl.ContentId()); - } - - return args; + return _content.GetNewTerminalArgs(asContent); } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 55eebc2cc4b..5f434a7b177 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -67,6 +67,59 @@ namespace winrt::TerminalApp::implementation } } + NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const + { + 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; + } + // Method Description: // - Called when our attached control is closed. Triggers listeners to our close // event, if we're a leaf pane. diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 69a9dd34052..51d1d05c65b 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -19,6 +19,8 @@ namespace winrt::TerminalApp::implementation void Focus(); 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); From 84df8197d481d2e193ab89fb20d53d35e3cd561a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 17 Jul 2023 14:22:12 -0500 Subject: [PATCH 04/63] close event --- src/cascadia/TerminalApp/IPaneContent.idl | 2 ++ src/cascadia/TerminalApp/TerminalPaneContent.cpp | 5 +++-- src/cascadia/TerminalApp/TerminalPaneContent.h | 1 + src/cascadia/TerminalApp/TerminalPaneContent.idl | 1 - src/inc/til/winrt.h | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 919d299bea5..389f0e8cf31 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -21,5 +21,7 @@ namespace TerminalApp void Close(); // event CloseRequested(...); + event Windows.Foundation.TypedEventHandler CloseRequested; + }; } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 5f434a7b177..f499010f0ef 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -65,6 +65,8 @@ namespace winrt::TerminalApp::implementation _bellPlayer = nullptr; _bellPlayerCreated = false; } + + CloseRequested.raise(*this, nullptr); } NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const @@ -164,8 +166,7 @@ namespace winrt::TerminalApp::implementation if ((mode == CloseOnExitMode::Always) || ((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed)) { - // TODO! ask the Pane to close - // Close(); + Close(); } } } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 51d1d05c65b..9d68ffd9336 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -37,6 +37,7 @@ namespace winrt::TerminalApp::implementation bool ReadOnly() { return _control.ReadOnly(); } til::typed_event RestartTerminalRequested; + til::typed_event<> CloseRequested; private: winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index d23f31e3d48..43eb96739a5 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -16,7 +16,6 @@ namespace TerminalApp Microsoft.Terminal.Settings.Model.Profile GetProfile(); - event Windows.Foundation.TypedEventHandler RestartTerminalRequested; } } diff --git a/src/inc/til/winrt.h b/src/inc/til/winrt.h index 28bcefdf354..bc3451cfffc 100644 --- a/src/inc/til/winrt.h +++ b/src/inc/til/winrt.h @@ -70,7 +70,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" winrt::event _handlers; }; - template + template struct typed_event { typed_event() = default; From 7c9ffb0e02e5323251769983805d57b754e6d192 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 06:06:07 -0500 Subject: [PATCH 05/63] snapping now uses an interface, so that it's not TermControl-specific --- src/cascadia/TerminalApp/IPaneContent.idl | 12 ++++ src/cascadia/TerminalApp/Pane.cpp | 69 +++++++++++-------- .../TerminalApp/TerminalPaneContent.cpp | 9 +++ .../TerminalApp/TerminalPaneContent.h | 3 + .../TerminalApp/TerminalPaneContent.idl | 2 +- 5 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 389f0e8cf31..734abe6896e 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -22,6 +22,18 @@ namespace TerminalApp // event CloseRequested(...); event Windows.Foundation.TypedEventHandler CloseRequested; + }; + + + enum PaneSnapDirection + { + Width, + Height + }; + interface ISnappable + { + Single SnapDownToGrid(PaneSnapDirection direction, Single sizeToSnap); + Windows.Foundation.Size GridSize { get; }; }; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index f3f5f87119b..beff22af2f4 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -2789,12 +2789,12 @@ 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 { - // TODO!: Again, bad. We're special-casing that the content just so happens to have a TermControl - const auto& termControl{ _content.GetRoot().try_as() }; + const auto direction{ widthOrHeight ? PaneSnapDirection::Width : PaneSnapDirection::Height }; if (_IsLeaf()) { - if (!termControl) + const auto& snappable{ _content.try_as() }; + if (!snappable) { return { dimension, dimension }; } @@ -2809,8 +2809,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const return { minDimension, minDimension }; } - auto lower = termControl.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; @@ -2829,8 +2831,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const } else { - const auto cellSize = termControl.CharacterDimensions(); - const auto higher = lower + (widthOrHeight ? cellSize.Width : cellSize.Height); + const auto cellSize = snappable.GridSize(); + const auto higher = lower + (direction == PaneSnapDirection::Width ? + cellSize.Width : + cellSize.Height); return { lower, higher }; } } @@ -2873,36 +2877,41 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const // Return Value: // - void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& sizeNode) const -{ // TODO!: Again, bad. We're special-casing that the content just so happens to have a TermControl - const auto& termControl{ _content.GetRoot().try_as() }; - if (_IsLeaf() && termControl) +{ + 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.GridSize(); + sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height; + } } else { - const auto cellSize = termControl.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 if (_IsLeaf()) - { - // 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 { // We're a parent pane, so we have to advance dimension of our children panes. In diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index f499010f0ef..d2f89ea35a9 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -270,4 +270,13 @@ namespace winrt::TerminalApp::implementation { _isDefTermSession = true; } + + float TerminalPaneContent::SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap) + { + return _control.SnapDimensionToGrid(direction == PaneSnapDirection::Width, sizeToSnap); + } + Windows::Foundation::Size TerminalPaneContent::GridSize() + { + return _control.CharacterDimensions(); + } } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 9d68ffd9336..44ccba4ae2b 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -36,6 +36,9 @@ namespace winrt::TerminalApp::implementation uint64_t TaskbarProgress() { return _control.TaskbarProgress(); } bool ReadOnly() { return _control.ReadOnly(); } + float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap); + Windows::Foundation::Size GridSize(); + til::typed_event RestartTerminalRequested; til::typed_event<> CloseRequested; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 43eb96739a5..1e39f41c168 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -5,7 +5,7 @@ import "IPaneContent.idl"; namespace TerminalApp { - [default_interface] runtimeclass TerminalPaneContent : IPaneContent + [default_interface] runtimeclass TerminalPaneContent : IPaneContent, ISnappable { Microsoft.Terminal.Control.TermControl GetTerminal(); From a1da6c117eaa651ae8190f8a54adcea9b778dd28 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 10:13:44 -0500 Subject: [PATCH 06/63] huge shuffling so that pane content can raise events instead of relying on termcontrol --- src/cascadia/TerminalApp/IPaneContent.idl | 16 +- src/cascadia/TerminalApp/Pane.cpp | 471 ++++++------------ src/cascadia/TerminalApp/Pane.h | 1 + src/cascadia/TerminalApp/PaneArgs.cpp | 6 + src/cascadia/TerminalApp/PaneArgs.h | 18 + .../TerminalApp/TerminalAppLib.vcxproj | 6 + .../TerminalApp/TerminalPaneContent.cpp | 44 +- .../TerminalApp/TerminalPaneContent.h | 25 +- src/cascadia/TerminalApp/TerminalTab.cpp | 194 ++++---- src/cascadia/TerminalApp/TerminalTab.h | 19 +- src/cascadia/TerminalApp/pch.h | 1 + 11 files changed, 356 insertions(+), 445 deletions(-) create mode 100644 src/cascadia/TerminalApp/PaneArgs.cpp create mode 100644 src/cascadia/TerminalApp/PaneArgs.h diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 734abe6896e..54442316b1c 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -3,6 +3,12 @@ namespace TerminalApp { + + runtimeclass BellEventArgs + { + Boolean FlashTaskbar { get; }; + }; + interface IPaneContent { Windows.UI.Xaml.FrameworkElement GetRoot(); @@ -16,12 +22,18 @@ namespace TerminalApp Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent); - void Focus(); + void Focus(Windows.UI.Xaml.FocusState reason); void Close(); - // event CloseRequested(...); event Windows.Foundation.TypedEventHandler CloseRequested; + + 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; }; diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index beff22af2f4..9d3b0e0fb99 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -106,18 +106,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 }); -// } -// 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 @@ -978,174 +966,6 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr return { nullptr, nullptr, offset }; } -// // 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: -// // - -// void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, -// const winrt::Windows::Foundation::IInspectable& /*args*/) -// { -// std::unique_lock lock{ _createCloseLock }; -// // 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; -// } - -// const auto newConnectionState = _control.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. -// 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. -// 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*/) -// { -// std::unique_lock lock{ _createCloseLock }; - -// // 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; -// } -// _RestartTerminalRequestedHandlers(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*/) -// { -// if (!_IsLeaf()) -// { -// return; -// } -// 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 -// _PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar)); -// } -// } -// } - // Event Description: // - Called when our control gains focus. We'll use this to trigger our GotFocus // callback. The tab that's hosting us should have registered a callback which @@ -1418,10 +1238,7 @@ void Pane::UpdateVisuals() void Pane::_Focus() { _GotFocusHandlers(shared_from_this(), FocusState::Programmatic); - if (const auto& control = GetLastFocusedTerminalControl()) - { - control.Focus(FocusState::Programmatic); - } + _content.Focus(FocusState::Programmatic); } // Method Description: @@ -1649,7 +1466,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool /*isDetaching*/) // focus our control now. This should trigger our own GotFocus event. if (usedToFocusClosedChildsTerminal || _lastActive) { - _content.Focus(); + _content.Focus(FocusState::Programmatic); // See GH#7252 // Manually fire off the GotFocus event. Typically, this is done @@ -2076,151 +1893,151 @@ void Pane::_ApplySplitDefinitions() // have been set up. void Pane::_SetupEntranceAnimation() { - // // This will query if animations are enabled via the "Show animations in - // // Windows" setting in the OS - // winrt::Windows::UI::ViewManagement::UISettings uiSettings; - // const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); - // const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); - - // const auto splitWidth = _splitState == SplitState::Vertical; - // const auto totalSize = splitWidth ? _root.ActualWidth() : _root.ActualHeight(); - // // If we don't have a size yet, it's likely that we're in startup, or we're - // // being executed as a sequence of actions. In that case, just skip the - // // animation. - // if (totalSize <= 0 || !animationsEnabledInOS || !animationsEnabledInApp) - // { - // return; - // } - - // // Use the unfocused border color as the pane background, so an actual color - // // appears behind panes as we animate them sliding in. - // // - // // GH#603 - We set only the background of the new pane, while it animates - // // in. Once the animation is done, we'll remove that background, so if the - // // user wants vintage opacity, they'll be able to see what's under the - // // window. - // // * If we don't give it a background, then the BG will be entirely transparent. - // // * If we give the parent (us) root BG a color, then a transparent pane - // // will flash opaque during the animation, then back to transparent, which - // // looks bad. - // _secondChild->_root.Background(_themeResources.unfocusedBorderBrush); - - // const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast(totalSize)); + // This will query if animations are enabled via the "Show animations in + // Windows" setting in the OS + winrt::Windows::UI::ViewManagement::UISettings uiSettings; + const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); + const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); + + const auto splitWidth = _splitState == SplitState::Vertical; + const auto totalSize = splitWidth ? _root.ActualWidth() : _root.ActualHeight(); + // If we don't have a size yet, it's likely that we're in startup, or we're + // being executed as a sequence of actions. In that case, just skip the + // animation. + if (totalSize <= 0 || !animationsEnabledInOS || !animationsEnabledInApp) + { + return; + } + + // Use the unfocused border color as the pane background, so an actual color + // appears behind panes as we animate them sliding in. + // + // GH#603 - We set only the background of the new pane, while it animates + // in. Once the animation is done, we'll remove that background, so if the + // user wants vintage opacity, they'll be able to see what's under the + // window. + // * If we don't give it a background, then the BG will be entirely transparent. + // * If we give the parent (us) root BG a color, then a transparent pane + // will flash opaque during the animation, then back to transparent, which + // looks bad. + _secondChild->_root.Background(_themeResources.unfocusedBorderBrush); + + const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast(totalSize)); // This is safe to capture this, because it's only being called in the // context of this method (not on another thread) - // auto setupAnimation = [&](const auto& size, const bool isFirstChild) { - // auto child = isFirstChild ? _firstChild : _secondChild; - // auto childGrid = child->_root; - // // If we are splitting a parent pane this may be null - // auto control = child->_control; - // // 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 - // // * it'll animate that value using a quadratic function (like f(t) = t^2) - // // * IMPORTANT! We'll manually tell the animation that "yes we know what - // // we're doing, we want an animation here." - // Media::Animation::DoubleAnimation animation{}; - // animation.Duration(AnimationDuration); - // if (isFirstChild) - // { - // // If we're animating the first pane, the size should decrease, from - // // the full size down to the given size. - // animation.From(totalSize); - // animation.To(size); - // } - // else - // { - // // Otherwise, we want to show the pane getting larger, so animate - // // from 0 to the requested size. - // animation.From(0.0); - // animation.To(size); - // } - // animation.EasingFunction(Media::Animation::QuadraticEase{}); - // animation.EnableDependentAnimation(true); - - // // Now we're going to set up the Storyboard. This is a unit that uses the - // // Animation from above, and actually applies it to a property. - // // * we'll set it up for the same duration as the animation we have - // // * Apply the animation to the grid of the new pane we're adding to the tree. - // // * apply the animation to the Width or Height property. - // Media::Animation::Storyboard s; - // s.Duration(AnimationDuration); - // s.Children().Append(animation); - // s.SetTarget(animation, childGrid); - // s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); - - // // BE TRICKY: - // // We're animating the width or height of our child pane's grid. - // // - // // We DON'T want to change the size of the control itself, because the - // // terminal has to reflow the buffer every time the control changes size. So - // // what we're going to do there is manually set the control's size to how - // // big we _actually know_ the control will be. - // // - // // We're also going to be changing alignment of our child pane and the - // // control. This way, we'll be able to have the control stick to the inside - // // of the child pane's grid (the side that's moving), while we also have the - // // pane's grid stick to "outside" of the grid (the side that's not moving) - // if (splitWidth) - // { - // // If we're animating the first child, then stick to the top/left of - // // the parent pane, otherwise use the bottom/right. This is always - // // the "outside" of the parent pane. - // childGrid.HorizontalAlignment(isFirstChild ? HorizontalAlignment::Left : HorizontalAlignment::Right); - // if (control) - // { - // control.HorizontalAlignment(HorizontalAlignment::Left); - // control.Width(isFirstChild ? totalSize : size); - // } - - // // When the animation is completed, undo the trickiness from before, to - // // restore the controls to the behavior they'd usually have. - // animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { - // childGrid.Width(NAN); - // childGrid.HorizontalAlignment(HorizontalAlignment::Stretch); - // if (control) - // { - // control.Width(NAN); - // control.HorizontalAlignment(HorizontalAlignment::Stretch); - // } - // root.Background(nullptr); - // }); - // } - // else - // { - // // If we're animating the first child, then stick to the top/left of - // // the parent pane, otherwise use the bottom/right. This is always - // // the "outside" of the parent pane. - // childGrid.VerticalAlignment(isFirstChild ? VerticalAlignment::Top : VerticalAlignment::Bottom); - // if (control) - // { - // control.VerticalAlignment(VerticalAlignment::Top); - // control.Height(isFirstChild ? totalSize : size); - // } - - // // When the animation is completed, undo the trickiness from before, to - // // restore the controls to the behavior they'd usually have. - // animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { - // childGrid.Height(NAN); - // childGrid.VerticalAlignment(VerticalAlignment::Stretch); - // if (control) - // { - // control.Height(NAN); - // control.VerticalAlignment(VerticalAlignment::Stretch); - // } - // root.Background(nullptr); - // }); - // } - - // // Start the animation. - // s.Begin(); - // }; - - // // TODO: GH#7365 - animating the first child right now doesn't _really_ do - // // anything. We could do better though. - // setupAnimation(firstSize, true); - // setupAnimation(secondSize, false); + auto setupAnimation = [&](const auto& size, const bool isFirstChild) { + 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(); + // 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 + // * it'll animate that value using a quadratic function (like f(t) = t^2) + // * IMPORTANT! We'll manually tell the animation that "yes we know what + // we're doing, we want an animation here." + Media::Animation::DoubleAnimation animation{}; + animation.Duration(AnimationDuration); + if (isFirstChild) + { + // If we're animating the first pane, the size should decrease, from + // the full size down to the given size. + animation.From(totalSize); + animation.To(size); + } + else + { + // Otherwise, we want to show the pane getting larger, so animate + // from 0 to the requested size. + animation.From(0.0); + animation.To(size); + } + animation.EasingFunction(Media::Animation::QuadraticEase{}); + animation.EnableDependentAnimation(true); + + // Now we're going to set up the Storyboard. This is a unit that uses the + // Animation from above, and actually applies it to a property. + // * we'll set it up for the same duration as the animation we have + // * Apply the animation to the grid of the new pane we're adding to the tree. + // * apply the animation to the Width or Height property. + Media::Animation::Storyboard s; + s.Duration(AnimationDuration); + s.Children().Append(animation); + s.SetTarget(animation, childGrid); + s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); + + // BE TRICKY: + // We're animating the width or height of our child pane's grid. + // + // We DON'T want to change the size of the control itself, because the + // terminal has to reflow the buffer every time the control changes size. So + // what we're going to do there is manually set the control's size to how + // big we _actually know_ the control will be. + // + // We're also going to be changing alignment of our child pane and the + // control. This way, we'll be able to have the control stick to the inside + // of the child pane's grid (the side that's moving), while we also have the + // pane's grid stick to "outside" of the grid (the side that's not moving) + if (splitWidth) + { + // If we're animating the first child, then stick to the top/left of + // the parent pane, otherwise use the bottom/right. This is always + // the "outside" of the parent pane. + childGrid.HorizontalAlignment(isFirstChild ? HorizontalAlignment::Left : HorizontalAlignment::Right); + if (control) + { + control.HorizontalAlignment(HorizontalAlignment::Left); + control.Width(isFirstChild ? totalSize : size); + } + + // When the animation is completed, undo the trickiness from before, to + // restore the controls to the behavior they'd usually have. + animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { + childGrid.Width(NAN); + childGrid.HorizontalAlignment(HorizontalAlignment::Stretch); + if (control) + { + control.Width(NAN); + control.HorizontalAlignment(HorizontalAlignment::Stretch); + } + root.Background(nullptr); + }); + } + else + { + // If we're animating the first child, then stick to the top/left of + // the parent pane, otherwise use the bottom/right. This is always + // the "outside" of the parent pane. + childGrid.VerticalAlignment(isFirstChild ? VerticalAlignment::Top : VerticalAlignment::Bottom); + if (control) + { + control.VerticalAlignment(VerticalAlignment::Top); + control.Height(isFirstChild ? totalSize : size); + } + + // When the animation is completed, undo the trickiness from before, to + // restore the controls to the behavior they'd usually have. + animation.Completed([childGrid, control, root = _secondChild->_root](auto&&, auto&&) { + childGrid.Height(NAN); + childGrid.VerticalAlignment(VerticalAlignment::Stretch); + if (control) + { + control.Height(NAN); + control.VerticalAlignment(VerticalAlignment::Stretch); + } + root.Background(nullptr); + }); + } + + // Start the animation. + s.Begin(); + }; + + // TODO: GH#7365 - animating the first child right now doesn't _really_ do + // anything. We could do better though. + setupAnimation(firstSize, true); + setupAnimation(secondSize, false); } // Method Description: diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index e3aea4cdd76..5bca5e62d81 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -88,6 +88,7 @@ class Pane : public std::enable_shared_from_this } winrt::Windows::UI::Xaml::Controls::Grid GetRootElement(); + winrt::TerminalApp::IPaneContent GetContent() const noexcept { return _IsLeaf() ? _content : nullptr; } bool WasLastFocused() const noexcept; void UpdateVisuals(); diff --git a/src/cascadia/TerminalApp/PaneArgs.cpp b/src/cascadia/TerminalApp/PaneArgs.cpp new file mode 100644 index 00000000000..2b5beddb8b5 --- /dev/null +++ b/src/cascadia/TerminalApp/PaneArgs.cpp @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "PaneArgs.h" +#include "BellEventArgs.g.cpp" diff --git a/src/cascadia/TerminalApp/PaneArgs.h b/src/cascadia/TerminalApp/PaneArgs.h new file mode 100644 index 00000000000..5627623be9e --- /dev/null +++ b/src/cascadia/TerminalApp/PaneArgs.h @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "BellEventArgs.g.h" + +namespace winrt::TerminalApp::implementation +{ + struct BellEventArgs : public BellEventArgsT + { + public: + BellEventArgs(bool flashTaskbar) : + FlashTaskbar(flashTaskbar) {} + + til::property FlashTaskbar; + }; +}; diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 965c8b65dc4..a856e9104ac 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -131,6 +131,9 @@ EmptyStringVisibilityConverter.idl + + IPaneContent.idl + @@ -233,6 +236,9 @@ EmptyStringVisibilityConverter.idl + + IPaneContent.idl + diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index d2f89ea35a9..949c5e32707 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "TerminalPaneContent.h" +#include "PaneArgs.h" #include "TerminalPaneContent.g.cpp" #include @@ -25,9 +26,15 @@ namespace winrt::TerminalApp::implementation void TerminalPaneContent::_setupControlEvents() { _controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &TerminalPaneContent::_ControlConnectionStateChangedHandler }); - _controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { this, &TerminalPaneContent::_ControlWarningBellHandler }); - _controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { this, &TerminalPaneContent::_CloseTerminalRequestedHandler }); - _controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { this, &TerminalPaneContent::_RestartTerminalRequestedHandler }); + _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() { @@ -46,9 +53,9 @@ namespace winrt::TerminalApp::implementation { return _control.MinimumSize(); } - void TerminalPaneContent::Focus() + void TerminalPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason) { - _control.Focus(FocusState::Programmatic); + _control.Focus(reason); } void TerminalPaneContent::Close() { @@ -122,6 +129,27 @@ namespace winrt::TerminalApp::implementation 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. @@ -209,9 +237,9 @@ namespace winrt::TerminalApp::implementation _control.BellLightOn(); } - // TODO! - // // 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)); + // raise the event with the bool value corresponding to the taskbar flag + BellRequested.raise(*this, + *winrt::make_self(WI_IsFlagSet(_profile.BellStyle(), BellStyle::Taskbar))); } } } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 44ccba4ae2b..9c7ea872853 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -16,7 +16,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); winrt::Microsoft::Terminal::Control::TermControl GetTerminal(); winrt::Windows::Foundation::Size MinSize(); - void Focus(); + 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; @@ -41,6 +41,12 @@ namespace winrt::TerminalApp::implementation til::typed_event RestartTerminalRequested; 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 }; @@ -57,6 +63,13 @@ namespace winrt::TerminalApp::implementation 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(); @@ -66,11 +79,19 @@ namespace winrt::TerminalApp::implementation void _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 _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 _ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, // const winrt::Windows::UI::Xaml::RoutedEventArgs& e); // void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, // const winrt::Windows::UI::Xaml::RoutedEventArgs& e); - void _CloseTerminalRequestedHandler(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/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 63f6e6834ed..843ad2227c4 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -202,9 +202,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); } }); } @@ -507,10 +507,9 @@ namespace winrt::TerminalApp::implementation if (p->_IsLeaf()) { p->Id(_nextPaneId); - // TODO! this feels hacky - if (const auto& termPane{ p->_content.try_as() }) + if (const auto& content{ p->GetContent() }) { - _AttachEventHandlersToControl(p->Id().value(), termPane.GetTerminal()); + _AttachEventHandlersToContent(p->Id().value(), content); } _nextPaneId++; } @@ -621,10 +620,9 @@ namespace winrt::TerminalApp::implementation { p->Id(_nextPaneId); - // TODO! this feels hacky - if (const auto& termPane{ p->_content.try_as() }) + if (const auto& content{ p->GetContent() }) { - _AttachEventHandlersToControl(p->Id().value(), termPane.GetTerminal()); + _AttachEventHandlersToContent(p->Id().value(), content); } _nextPaneId++; } @@ -882,24 +880,18 @@ 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; + events = {}; - control.TitleChanged(events.titleToken); - control.TabColorChanged(events.colorToken); - control.SetTaskbarProgress(events.taskbarToken); - control.ReadOnlyChanged(events.readOnlyToken); - control.FocusFollowMouseRequested(events.focusToken); - - _controlEvents.erase(paneId); + _contentEvents.erase(paneId); } } @@ -914,66 +906,102 @@ 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{}; + ContentEventTokens events{}; - events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - co_await wil::resume_foreground(dispatcher); - // Check if Tab's lifetime has expired - if (auto tab{ weakThis.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.TitleChanged = content.TitleChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget { + co_await wil::resume_foreground(dispatcher); + // Check if Tab's lifetime has expired + if (auto tab{ weakThis.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.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.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.TabColorChanged = content.TabColorChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget { + co_await wil::resume_foreground(dispatcher); + if (auto tab{ weakThis.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.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - co_await wil::resume_foreground(dispatcher); - // Check if Tab's lifetime has expired - if (auto tab{ weakThis.get() }) - { - tab->_UpdateProgressState(); - } - }); + events.TaskbarProgressChanged = content.TaskbarProgressChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget { + co_await wil::resume_foreground(dispatcher); + // Check if Tab's lifetime has expired + if (auto tab{ weakThis.get() }) + { + tab->_UpdateProgressState(); + } + }); - events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { - co_await wil::resume_foreground(dispatcher); - if (auto tab{ weakThis.get() }) - { - tab->_RecalculateAndApplyReadOnly(); - } - }); + events.ReadOnlyChanged = content.ReadOnlyChanged( + winrt::auto_revoke, + [dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget { + co_await wil::resume_foreground(dispatcher); + if (auto tab{ weakThis.get() }) + { + tab->_RecalculateAndApplyReadOnly(); + } + }); - events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget { - co_await wil::resume_foreground(dispatcher); - if (const auto tab{ weakThis.get() }) - { - if (tab->_focusState != FocusState::Unfocused) + events.FocusRequested = content.FocusRequested( + winrt::auto_revoke, + [dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget { + co_await wil::resume_foreground(dispatcher); + if (const auto tab{ weakThis.get() }) { - if (const auto termControl{ sender.try_as() }) + if (tab->_focusState != FocusState::Unfocused) { - termControl.Focus(FocusState::Pointer); + if (const auto content{ sender.try_as() }) + { + content.Focus(FocusState::Pointer); + } } } - } - }); + }); + + // Add a PaneRaiseBell event handler to the Pane + events.BellRequested = content.BellRequested( + winrt::auto_revoke, + [weakThis](auto&& /*s*/, auto&& args) { + if (auto tab{ weakThis.get() }) + { + if (args.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->_TabRaiseVisualBellHandlers(); + } + + // 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(); + } + } + }); - _controlEvents[paneId] = events; + _contentEvents[paneId] = std::move(events); } // Method Description: @@ -1188,36 +1216,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->_TabRaiseVisualBellHandlers(); - } - - // 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() }) { @@ -1225,14 +1229,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) { diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index e2a225e2c63..a361f2daa6b 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -126,15 +126,16 @@ 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 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::ReadOnlyChanged_revoker ReadOnlyChanged; + winrt::TerminalApp::IPaneContent::FocusRequested_revoker FocusRequested; }; - std::unordered_map _controlEvents; + std::unordered_map _contentEvents; winrt::event_token _rootClosedToken{}; @@ -163,8 +164,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); diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index 3d2aebf1f7d..0c80d792422 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -81,6 +81,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider); // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" +#include #include #include // must go after the CoreDispatcher type is defined From 049c043279e6de5d32a38af363f84dbd3fdfc978 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 10:26:32 -0500 Subject: [PATCH 07/63] some last cleanups --- src/cascadia/TerminalApp/Pane.cpp | 244 +++++++++--------- src/cascadia/TerminalApp/Pane.h | 12 +- .../TerminalApp/TerminalPaneContent.h | 7 +- 3 files changed, 124 insertions(+), 139 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 9d3b0e0fb99..1311dfeddb9 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -42,13 +42,11 @@ Pane::Pane(const IPaneContent& content, const bool lastFocused) : const auto& control{ _content.GetRoot() }; _borderFirst.Child(control); - // _setupControlEvents(); - // Register an event with the control to have it inform us when it gains focus. if (control) { - _gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler }); - _lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler }); + _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. @@ -974,7 +972,7 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr // - // 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; @@ -989,7 +987,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 */) { _LostFocusHandlers(shared_from_this()); @@ -1458,8 +1456,8 @@ void Pane::_CloseChild(const bool closeFirst, const bool /*isDetaching*/) // re-attach our handler for the control's GotFocus event. if (control) { - _gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler }); - _lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler }); + _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, @@ -1589,126 +1587,126 @@ winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst) if (auto pane{ weakThis.get() }) { - //// This will query if animations are enabled via the "Show animations in - //// Windows" setting in the OS - //winrt::Windows::UI::ViewManagement::UISettings uiSettings; - //const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); - //const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); + // This will query if animations are enabled via the "Show animations in + // Windows" setting in the OS + winrt::Windows::UI::ViewManagement::UISettings uiSettings; + const auto animationsEnabledInOS = uiSettings.AnimationsEnabled(); + const auto animationsEnabledInApp = Media::Animation::Timeline::AllowDependentAnimations(); - //// GH#7252: If either child is zoomed, just skip the animation. It won't work. - //const auto eitherChildZoomed = pane->_firstChild->_zoomed || pane->_secondChild->_zoomed; + // GH#7252: If either child is zoomed, just skip the animation. It won't work. + const auto eitherChildZoomed = pane->_firstChild->_zoomed || pane->_secondChild->_zoomed; // If animations are disabled, just skip this and go straight to // _CloseChild. Curiously, the pane opening animation doesn't need this, // and will skip straight to Completed when animations are disabled, but // this one doesn't seem to. - // if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed) - // { - pane->_CloseChild(closeFirst, false); - co_return; - // } - - // // Setup the animation - - // auto removedChild = closeFirst ? _firstChild : _secondChild; - // auto remainingChild = closeFirst ? _secondChild : _firstChild; - // const auto splitWidth = _splitState == SplitState::Vertical; - - // Size removedOriginalSize{ - // ::base::saturated_cast(removedChild->_root.ActualWidth()), - // ::base::saturated_cast(removedChild->_root.ActualHeight()) - // }; - // Size remainingOriginalSize{ - // ::base::saturated_cast(remainingChild->_root.ActualWidth()), - // ::base::saturated_cast(remainingChild->_root.ActualHeight()) - // }; - - // // Remove both children from the grid - // _borderFirst.Child(nullptr); - // _borderSecond.Child(nullptr); - - // if (_splitState == SplitState::Vertical) - // { - // Controls::Grid::SetColumn(_borderFirst, 0); - // Controls::Grid::SetColumn(_borderSecond, 1); - // } - // else if (_splitState == SplitState::Horizontal) - // { - // Controls::Grid::SetRow(_borderFirst, 0); - // Controls::Grid::SetRow(_borderSecond, 1); - // } - - // // Create the dummy grid. This grid will be the one we actually animate, - // // in the place of the closed pane. - // Controls::Grid dummyGrid; - // // GH#603 - we can safely add a BG here, as the control is gone right - // // away, to fill the space as the rest of the pane expands. - // dummyGrid.Background(_themeResources.unfocusedBorderBrush); - // // It should be the size of the closed pane. - // dummyGrid.Width(removedOriginalSize.Width); - // dummyGrid.Height(removedOriginalSize.Height); - - // _borderFirst.Child(closeFirst ? dummyGrid : remainingChild->GetRootElement()); - // _borderSecond.Child(closeFirst ? remainingChild->GetRootElement() : dummyGrid); - - // // Set up the rows/cols as auto/auto, so they'll only use the size of - // // the elements in the grid. - // // - // // * For the closed pane, we want to make that row/col "auto" sized, so - // // it takes up as much space as is available. - // // * For the remaining pane, we'll make that row/col "*" sized, so it - // // takes all the remaining space. As the dummy grid is resized down, - // // the remaining pane will expand to take the rest of the space. - // _root.ColumnDefinitions().Clear(); - // _root.RowDefinitions().Clear(); - // if (_splitState == SplitState::Vertical) - // { - // auto firstColDef = Controls::ColumnDefinition(); - // auto secondColDef = Controls::ColumnDefinition(); - // firstColDef.Width(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - // secondColDef.Width(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - // _root.ColumnDefinitions().Append(firstColDef); - // _root.ColumnDefinitions().Append(secondColDef); - // } - // else if (_splitState == SplitState::Horizontal) - // { - // auto firstRowDef = Controls::RowDefinition(); - // auto secondRowDef = Controls::RowDefinition(); - // firstRowDef.Height(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - // secondRowDef.Height(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); - // _root.RowDefinitions().Append(firstRowDef); - // _root.RowDefinitions().Append(secondRowDef); - // } - - // // Animate the dummy grid from its current size down to 0 - // Media::Animation::DoubleAnimation animation{}; - // animation.Duration(AnimationDuration); - // animation.From(splitWidth ? removedOriginalSize.Width : removedOriginalSize.Height); - // animation.To(0.0); - // // This easing is the same as the entrance animation. - // animation.EasingFunction(Media::Animation::QuadraticEase{}); - // animation.EnableDependentAnimation(true); - - // Media::Animation::Storyboard s; - // s.Duration(AnimationDuration); - // s.Children().Append(animation); - // s.SetTarget(animation, dummyGrid); - // s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); - - // // Start the animation. - // s.Begin(); - - // std::weak_ptr weakThis{ shared_from_this() }; - - // // When the animation is completed, reparent the child's content up to - // // us, and remove the child nodes from the tree. - // animation.Completed([weakThis, closeFirst](auto&&, auto&&) { - // if (auto pane{ weakThis.lock() }) - // { - // // 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); - // } - // }); + if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed) + { + pane->_CloseChild(closeFirst, false); + co_return; + } + + // Setup the animation + + auto removedChild = closeFirst ? _firstChild : _secondChild; + auto remainingChild = closeFirst ? _secondChild : _firstChild; + const auto splitWidth = _splitState == SplitState::Vertical; + + Size removedOriginalSize{ + ::base::saturated_cast(removedChild->_root.ActualWidth()), + ::base::saturated_cast(removedChild->_root.ActualHeight()) + }; + Size remainingOriginalSize{ + ::base::saturated_cast(remainingChild->_root.ActualWidth()), + ::base::saturated_cast(remainingChild->_root.ActualHeight()) + }; + + // Remove both children from the grid + _borderFirst.Child(nullptr); + _borderSecond.Child(nullptr); + + if (_splitState == SplitState::Vertical) + { + Controls::Grid::SetColumn(_borderFirst, 0); + Controls::Grid::SetColumn(_borderSecond, 1); + } + else if (_splitState == SplitState::Horizontal) + { + Controls::Grid::SetRow(_borderFirst, 0); + Controls::Grid::SetRow(_borderSecond, 1); + } + + // Create the dummy grid. This grid will be the one we actually animate, + // in the place of the closed pane. + Controls::Grid dummyGrid; + // GH#603 - we can safely add a BG here, as the control is gone right + // away, to fill the space as the rest of the pane expands. + dummyGrid.Background(_themeResources.unfocusedBorderBrush); + // It should be the size of the closed pane. + dummyGrid.Width(removedOriginalSize.Width); + dummyGrid.Height(removedOriginalSize.Height); + + _borderFirst.Child(closeFirst ? dummyGrid : remainingChild->GetRootElement()); + _borderSecond.Child(closeFirst ? remainingChild->GetRootElement() : dummyGrid); + + // Set up the rows/cols as auto/auto, so they'll only use the size of + // the elements in the grid. + // + // * For the closed pane, we want to make that row/col "auto" sized, so + // it takes up as much space as is available. + // * For the remaining pane, we'll make that row/col "*" sized, so it + // takes all the remaining space. As the dummy grid is resized down, + // the remaining pane will expand to take the rest of the space. + _root.ColumnDefinitions().Clear(); + _root.RowDefinitions().Clear(); + if (_splitState == SplitState::Vertical) + { + auto firstColDef = Controls::ColumnDefinition(); + auto secondColDef = Controls::ColumnDefinition(); + firstColDef.Width(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + secondColDef.Width(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + _root.ColumnDefinitions().Append(firstColDef); + _root.ColumnDefinitions().Append(secondColDef); + } + else if (_splitState == SplitState::Horizontal) + { + auto firstRowDef = Controls::RowDefinition(); + auto secondRowDef = Controls::RowDefinition(); + firstRowDef.Height(!closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + secondRowDef.Height(closeFirst ? GridLengthHelper::FromValueAndType(1, GridUnitType::Star) : GridLengthHelper::Auto()); + _root.RowDefinitions().Append(firstRowDef); + _root.RowDefinitions().Append(secondRowDef); + } + + // Animate the dummy grid from its current size down to 0 + Media::Animation::DoubleAnimation animation{}; + animation.Duration(AnimationDuration); + animation.From(splitWidth ? removedOriginalSize.Width : removedOriginalSize.Height); + animation.To(0.0); + // This easing is the same as the entrance animation. + animation.EasingFunction(Media::Animation::QuadraticEase{}); + animation.EnableDependentAnimation(true); + + Media::Animation::Storyboard s; + s.Duration(AnimationDuration); + s.Children().Append(animation); + s.SetTarget(animation, dummyGrid); + s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height"); + + // Start the animation. + s.Begin(); + + std::weak_ptr weakThis{ shared_from_this() }; + + // When the animation is completed, reparent the child's content up to + // us, and remove the child nodes from the tree. + animation.Completed([weakThis, closeFirst](auto&&, auto&&) { + if (auto pane{ weakThis.lock() }) + { + // 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); + } + }); } } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 5bca5e62d81..81c9c125b4b 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -217,7 +217,6 @@ class Pane : public std::enable_shared_from_this WINRT_CALLBACK(LostFocus, winrt::delegate>); WINRT_CALLBACK(PaneRaiseBell, winrt::Windows::Foundation::EventHandler); WINRT_CALLBACK(Detached, winrt::delegate>); - // WINRT_CALLBACK(RestartTerminalRequested, winrt::delegate>); private: struct PanePoint; @@ -294,15 +293,10 @@ class Pane : public std::enable_shared_from_this void _Focus(); void _FocusFirstChild(); - // void _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 _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; @@ -314,8 +308,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/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 9c7ea872853..262e177bdef 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -86,12 +86,7 @@ namespace winrt::TerminalApp::implementation 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 _ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, - // const winrt::Windows::UI::Xaml::RoutedEventArgs& e); - // void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender, - // const winrt::Windows::UI::Xaml::RoutedEventArgs& e); - void - _CloseTerminalRequestedHandler(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*/); }; } From 2dd8f409b23d38bac65ed08311f0ed8da57e01a3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 11:48:33 -0500 Subject: [PATCH 08/63] [TO PARENT] dead code --- src/cascadia/TerminalApp/Pane.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 1311dfeddb9..5c61cc4758c 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -2931,7 +2931,6 @@ void Pane::FinalizeConfigurationGivenDefault() { terminalPane.MarkAsDefterm(); } - // _isDefTermSession = true; } // Method Description: From 1b39db7ab0dd40d3889fd00ba47a51a87755c75a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 12:58:55 -0500 Subject: [PATCH 09/63] Single commit that adds the whole scratchpad and action --- .../TerminalApp/AppActionHandlers.cpp | 10 ++++ .../TerminalApp/ScratchpadContent.cpp | 54 ++++++++++++++++++ src/cascadia/TerminalApp/ScratchpadContent.h | 37 ++++++++++++ .../TerminalApp/TerminalAppLib.vcxproj | 6 ++ .../TerminalApp/TerminalPaneContent.idl | 5 ++ .../TerminalSettingsModel/ActionAndArgs.cpp | 2 + .../AllShortcutActions.h | 3 +- .../Resources/en-US/Resources.resw | 57 ++++++++++--------- 8 files changed, 146 insertions(+), 28 deletions(-) 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 f600453a632..494276081ee 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" @@ -1301,4 +1302,13 @@ namespace winrt::TerminalApp::implementation } args.Handled(true); } + + void TerminalPage::_HandleOpenScratchpad(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + auto scratchPane{ winrt::make_self() }; + auto resultPane = std::make_shared(*scratchPane); + _SplitPane(SplitDirection::Automatic, 0.5f, resultPane); + args.Handled(true); + } } diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp new file mode 100644 index 00000000000..a2a0f5dc921 --- /dev/null +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "ScratchpadContent.h" +#include "PaneArgs.h" +#include "ScratchpadContent.g.cpp" + +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{}; + _root.VerticalAlignment(VerticalAlignment::Stretch); + _root.HorizontalAlignment(HorizontalAlignment::Stretch); + + auto res = Windows::UI::Xaml::Application::Current().Resources(); + auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush")); + // _root.Background(Media::SolidColorBrush{ winrt::Windows::UI::Colors::Red() }); + _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::MinSize() + { + 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..0193fcfc15e --- /dev/null +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "ScratchpadContent.g.h" + +namespace winrt::TerminalApp::implementation +{ + struct ScratchpadContent : ScratchpadContentT + { + ScratchpadContent(); + + winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); + + winrt::Windows::Foundation::Size MinSize(); + 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<> 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 a856e9104ac..4a9a6fe7de3 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -164,6 +164,9 @@ TerminalPaneContent.idl + + TerminalPaneContent.idl + @@ -272,6 +275,9 @@ TerminalPaneContent.idl + + ScratchpadContent.idl + diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 1e39f41c168..4f5be007e9e 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -18,4 +18,9 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RestartTerminalRequested; } + + + [default_interface] runtimeclass ScratchpadContent : IPaneContent + { + } } diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 75ba4487b0c..4096bfc300d 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -93,6 +93,7 @@ static constexpr std::string_view ColorSelectionKey{ "experimental.colorSelectio 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 OpenScratchpadKey{ "openScratchpad" }; static constexpr std::string_view ActionKey{ "action" }; @@ -424,6 +425,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::ShowContextMenu, RS_(L"ShowContextMenuCommandKey") }, { ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") }, { ShortcutAction::RestartConnection, RS_(L"RestartConnectionKey") }, + { ShortcutAction::OpenScratchpad, RS_(L"OpenScratchpadKey") }, }; }(); diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h index 127b0bf2df9..56db36f15d7 100644 --- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h +++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h @@ -106,7 +106,8 @@ ON_ALL_ACTIONS(ShowContextMenu) \ ON_ALL_ACTIONS(ExpandSelectionToWord) \ ON_ALL_ACTIONS(CloseOtherPanes) \ - ON_ALL_ACTIONS(RestartConnection) + ON_ALL_ACTIONS(RestartConnection) \ + ON_ALL_ACTIONS(OpenScratchpad) #define ALL_SHORTCUT_ACTIONS_WITH_ARGS \ ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \ diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 414783c6557..a2220a369ad 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -1,17 +1,17 @@ - @@ -687,6 +687,9 @@ Restart connection + + Open scratchpad + Select next command output From 63ba8e19fd4d43176e25668c50bdd6886612324e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 13:21:18 -0500 Subject: [PATCH 10/63] [PARENT] You know what, I just went for it. --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 494276081ee..4971eb0aad3 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1280,14 +1280,7 @@ namespace winrt::TerminalApp::implementation { if (const auto activePane{ activeTab->GetActivePane() }) { - activePane; - // TODO! If we don't expose the IPaneContent, then there's no - // way to get a TerminalPaneContent to pass to - // _restartPaneConnection / _duplicateConnectionForRestart. We - // probably need to change the signature to accept a - // TermControl&Profile - - // _restartPaneConnection(activePane); + _restartPaneConnection(activePane->GetContent().try_as(), nullptr); } } args.Handled(true); From 262d95aae5b1caa2957b113b7fece2d8802a0c56 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 13:47:38 -0500 Subject: [PATCH 11/63] [PARENT] try to use GetActiveTerminalControl less in TerminalTab --- src/cascadia/TerminalApp/TerminalTab.cpp | 16 +++++++++++++--- src/cascadia/TerminalApp/TerminalTab.h | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 843ad2227c4..7bb1fee27c9 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -188,6 +188,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 @@ -371,8 +376,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: @@ -1448,7 +1453,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->_RequestFocusActiveControlHandlers(); } diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index a361f2daa6b..4b83813ed39 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; From 2d4030683a494c214c434513c194c2caed22fe3c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 13:47:58 -0500 Subject: [PATCH 12/63] Let's just make it experimental --- src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 4096bfc300d..ffd3ec1773e 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -93,7 +93,7 @@ static constexpr std::string_view ColorSelectionKey{ "experimental.colorSelectio 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 OpenScratchpadKey{ "openScratchpad" }; +static constexpr std::string_view OpenScratchpadKey{ "experimental.openScratchpad" }; static constexpr std::string_view ActionKey{ "action" }; From a23c1a24dc95556340dd97e7e99c8d857111f53e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 20 Jul 2023 07:39:02 -0500 Subject: [PATCH 13/63] keybindings too --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 56b78aa2fbd..17f12d780e8 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1311,6 +1311,12 @@ namespace winrt::TerminalApp::implementation const ActionEventArgs& args) { 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 }); + auto resultPane = std::make_shared(*scratchPane); _SplitPane(SplitDirection::Automatic, 0.5f, resultPane); args.Handled(true); From 5582e1bcc87411f404701749d221e66702122dac Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 13:21:18 -0500 Subject: [PATCH 14/63] [PARENT] You know what, I just went for it. (cherry picked from commit 63ba8e19fd4d43176e25668c50bdd6886612324e) --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index ea15786f877..aba9604bad3 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1290,14 +1290,7 @@ namespace winrt::TerminalApp::implementation { if (const auto activePane{ activeTab->GetActivePane() }) { - activePane; - // TODO! If we don't expose the IPaneContent, then there's no - // way to get a TerminalPaneContent to pass to - // _restartPaneConnection / _duplicateConnectionForRestart. We - // probably need to change the signature to accept a - // TermControl&Profile - - // _restartPaneConnection(activePane); + _restartPaneConnection(activePane->GetContent().try_as(), nullptr); } } args.Handled(true); From f89368c19bf9d129faff31ea343078d43710208f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 18 Jul 2023 13:47:38 -0500 Subject: [PATCH 15/63] [PARENT] try to use GetActiveTerminalControl less in TerminalTab (cherry picked from commit 262d95aae5b1caa2957b113b7fece2d8802a0c56) --- src/cascadia/TerminalApp/TerminalTab.cpp | 16 +++++++++++++--- src/cascadia/TerminalApp/TerminalTab.h | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 39dbced16ea..9ac4b95acef 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -188,6 +188,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 @@ -371,8 +376,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: @@ -1433,7 +1438,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->_RequestFocusActiveControlHandlers(); } diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index cfe06084a05..9757c97f4f3 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; From 1cc9835454acbeef1b82a11ea7aea97b4d6e34c0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 27 Jul 2023 13:58:44 -0500 Subject: [PATCH 16/63] feature flags too --- .../TerminalApp/AppActionHandlers.cpp | 19 +++++++++++-------- .../TerminalApp/ScratchpadContent.cpp | 1 - src/features.xml | 10 ++++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 8c7832e5df6..d7004cb5758 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1320,15 +1320,18 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleOpenScratchpad(const IInspectable& /*sender*/, const ActionEventArgs& args) { - auto scratchPane{ winrt::make_self() }; + if (Feature_ScratchpadPane::IsEnabled()) + { + 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 }); + // 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 }); - auto resultPane = std::make_shared(*scratchPane); - _SplitPane(SplitDirection::Automatic, 0.5f, resultPane); - args.Handled(true); + auto resultPane = std::make_shared(*scratchPane); + _SplitPane(SplitDirection::Automatic, 0.5f, resultPane); + args.Handled(true); + } } } diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index a2a0f5dc921..6dc690448e8 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -20,7 +20,6 @@ namespace winrt::TerminalApp::implementation auto res = Windows::UI::Xaml::Application::Current().Resources(); auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush")); - // _root.Background(Media::SolidColorBrush{ winrt::Windows::UI::Colors::Red() }); _root.Background(bg.try_as()); _box = winrt::Windows::UI::Xaml::Controls::TextBox{}; diff --git a/src/features.xml b/src/features.xml index adb444b65fb..ed8af5fdcb6 100644 --- a/src/features.xml +++ b/src/features.xml @@ -173,4 +173,14 @@ + + Feature_ScratchpadPane + Allow the user to create scratchpad panes. Mostly just exists to validate non-terminal panes. + 997 + AlwaysDisabled + + Dev + + + From cbd61b0a7d687e9812dce274324f5c26b5ddb2a1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 27 Jul 2023 15:55:05 -0500 Subject: [PATCH 17/63] POC: yea, this works --- .../TerminalApp/AppActionHandlers.cpp | 4 +- .../TerminalApp/SettingsPaneContent.cpp | 58 +++++++++++++++++++ .../TerminalApp/SettingsPaneContent.h | 43 ++++++++++++++ .../TerminalApp/TerminalAppLib.vcxproj | 8 ++- .../TerminalApp/TerminalPaneContent.idl | 4 ++ 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 src/cascadia/TerminalApp/SettingsPaneContent.cpp create mode 100644 src/cascadia/TerminalApp/SettingsPaneContent.h diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index d7004cb5758..38ef6982d5c 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -6,6 +6,7 @@ #include "TerminalPage.h" #include "ScratchpadContent.h" +#include "SettingsPaneContent.h" #include "../WinRTUtils/inc/WtExeUtils.h" #include "../../types/inc/utils.hpp" #include "Utils.h" @@ -1322,7 +1323,8 @@ namespace winrt::TerminalApp::implementation { if (Feature_ScratchpadPane::IsEnabled()) { - auto scratchPane{ winrt::make_self() }; + // auto scratchPane{ winrt::make_self() }; + auto scratchPane{ winrt::make_self(_settings) }; // 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 diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp new file mode 100644 index 00000000000..6dafc949367 --- /dev/null +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SettingsPaneContent.h" +#include "PaneArgs.h" +#include "SettingsPaneContent.g.cpp" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +namespace winrt::TerminalApp::implementation +{ + SettingsPaneContent::SettingsPaneContent(CascadiaSettings settings) + { + _root = winrt::Windows::UI::Xaml::Controls::Grid{}; + _root.VerticalAlignment(VerticalAlignment::Stretch); + _root.HorizontalAlignment(HorizontalAlignment::Stretch); + + _sui = winrt::Microsoft::Terminal::Settings::Editor::MainPage{ settings }; + + 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 SettingsPaneContent::GetRoot() + { + return _sui; + } + winrt::Windows::Foundation::Size SettingsPaneContent::MinSize() + { + 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 bool /* asContent */) const + { + return nullptr; + } +} diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h new file mode 100644 index 00000000000..a97107e2bfb --- /dev/null +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "SettingsPaneContent.g.h" + +namespace winrt::TerminalApp::implementation +{ + struct SettingsPaneContent : SettingsPaneContentT + { + SettingsPaneContent(winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings settings); + + winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); + + winrt::Windows::Foundation::Size MinSize(); + 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<> 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 }; + winrt::Microsoft::Terminal::Settings::Editor::MainPage _sui{ nullptr }; + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(SettingsPaneContent); +} diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 4a9a6fe7de3..ee5ff612081 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -167,6 +167,9 @@ TerminalPaneContent.idl + + TerminalPaneContent.idl + @@ -276,7 +279,10 @@ TerminalPaneContent.idl - ScratchpadContent.idl + TerminalPaneContent.idl + + + TerminalPaneContent.idl diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 4f5be007e9e..b553cc09d7c 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -23,4 +23,8 @@ namespace TerminalApp [default_interface] runtimeclass ScratchpadContent : IPaneContent { } + [default_interface] runtimeclass SettingsPaneContent : IPaneContent + { + SettingsPaneContent(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); + } } From 29d0d576566c68b21b77541e523acbf1ddb2ecbe Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 27 Jul 2023 16:29:26 -0500 Subject: [PATCH 18/63] this works better than it has any right to --- .../TerminalApp/AppActionHandlers.cpp | 5 +- .../TerminalApp/SettingsPaneContent.cpp | 1 + .../TerminalApp/SettingsPaneContent.h | 5 +- src/cascadia/TerminalApp/SettingsTab.cpp | 6 ++ src/cascadia/TerminalApp/TabManagement.cpp | 4 +- src/cascadia/TerminalApp/TerminalPage.cpp | 84 +++++++++++-------- src/cascadia/TerminalApp/TerminalPage.h | 4 +- 7 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 38ef6982d5c..8bbb1056bb8 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -6,7 +6,6 @@ #include "TerminalPage.h" #include "ScratchpadContent.h" -#include "SettingsPaneContent.h" #include "../WinRTUtils/inc/WtExeUtils.h" #include "../../types/inc/utils.hpp" #include "Utils.h" @@ -1323,8 +1322,8 @@ namespace winrt::TerminalApp::implementation { if (Feature_ScratchpadPane::IsEnabled()) { - // auto scratchPane{ winrt::make_self() }; - auto scratchPane{ winrt::make_self(_settings) }; + auto scratchPane{ winrt::make_self() }; + // auto scratchPane{ winrt::make_self(_settings) }; // 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 diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index 6dafc949367..d1d182f7037 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -53,6 +53,7 @@ namespace winrt::TerminalApp::implementation NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const bool /* asContent */) const { + // TODO! hey, can we somehow replicate std::vector SettingsTab::BuildStartupActions? return nullptr; } } diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index a97107e2bfb..97121520033 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -11,13 +11,14 @@ namespace winrt::TerminalApp::implementation SettingsPaneContent(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 MinSize(); 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"; } + winrt::hstring Title() { return RS_(L"SettingsTab"); } uint64_t TaskbarState() { return 0; } uint64_t TaskbarProgress() { return 0; } bool ReadOnly() { return false; } @@ -31,8 +32,6 @@ namespace winrt::TerminalApp::implementation til::typed_event<> FocusRequested; private: - winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr }; - winrt::Windows::UI::Xaml::Controls::TextBox _box{ nullptr }; winrt::Microsoft::Terminal::Settings::Editor::MainPage _sui{ nullptr }; }; } diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp index 107bbb45732..2e45c2fa1cb 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -38,6 +38,7 @@ namespace winrt::TerminalApp::implementation void SettingsTab::UpdateSettings(CascadiaSettings settings) { + // TODO! oh noes, we need to do this too in the content ASSERT_UI_THREAD(); auto settingsUI{ Content().as() }; @@ -109,6 +110,8 @@ namespace winrt::TerminalApp::implementation // - void SettingsTab::_CreateIcon() { + // TODO! make sure this works + // This is the Setting icon (looks like a gear) static constexpr std::wstring_view glyph{ L"\xE713" }; @@ -119,6 +122,9 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush() { + // TODO! make sure this still works. It would be ironic if this _just + // worked_ because the SUI was the same color as a tab with no styling. + // 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. diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index eb05f15ddc0..f484bef420c 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -275,13 +275,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: diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index b2ffdcef957..8fc518cb700 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -22,6 +22,7 @@ #include "ColorHelper.h" #include "DebugTapConnection.h" #include "SettingsTab.h" +#include "SettingsPaneContent.h" #include "TabRowControl.h" #include "Utils.h" @@ -2264,6 +2265,8 @@ namespace winrt::TerminalApp::implementation const float splitSize, std::shared_ptr newPane) { + // TODO! Prevent splitting the _settingsTab! + // 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 @@ -3768,7 +3771,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)); @@ -3784,52 +3790,58 @@ namespace winrt::TerminalApp::implementation } }); - auto newTabImpl = winrt::make_self(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme()); + // Create the tab + auto resultPane = std::make_shared(*settingsContent); + _settingsTab = _CreateNewTabFromPane(resultPane); - // Add the new tab to the list of our tabs. - _tabs.Append(*newTabImpl); - _mruTabs.Append(*newTabImpl); + // auto newTabImpl = winrt::make_self(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme()); - newTabImpl->SetDispatch(*_actionDispatch); - newTabImpl->SetActionMap(_settings.ActionMap()); + // // Add the new tab to the list of our tabs. + // _tabs.Append(*newTabImpl); + // _mruTabs.Append(*newTabImpl); - // Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command. - _UpdateTabIndices(); + // newTabImpl->SetDispatch(*_actionDispatch); + // newTabImpl->SetActionMap(_settings.ActionMap()); - // 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); + // // Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command. + // _UpdateTabIndices(); - auto tabViewItem = newTabImpl->TabViewItem(); - _tabView.TabItems().Append(tabViewItem); + // // 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); - tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick }); + // auto tabViewItem = newTabImpl->TabViewItem(); + // _tabView.TabItems().Append(tabViewItem); - // 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() }; + // tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick }); - if (page && tab) - { - page->_HandleCloseTabRequested(*tab); - } - }); + // // 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() }; - // When the tab is closed, remove it from our list of tabs. - newTabImpl->Closed([tabViewItem, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { - if (auto page{ weakThis.get() }) - { - page->_settingsTab = nullptr; - page->_RemoveOnCloseRoutine(tabViewItem, page); - } - }); + // if (page && tab) + // { + // page->_HandleCloseTabRequested(*tab); + // } + // }); + + // TODO! Make sure we remove the _settingsTab if it is closed! + + // // When the tab is closed, remove it from our list of tabs. + // newTabImpl->Closed([tabViewItem, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { + // if (auto page{ weakThis.get() }) + // { + // page->_settingsTab = nullptr; + // page->_RemoveOnCloseRoutine(tabViewItem, page); + // } + // }); - _settingsTab = *newTabImpl; + // _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); + //// This kicks off TabView::SelectionChanged, in response to which + //// we'll attach the terminal's Xaml control to the Xaml root. + //_tabView.SelectedItem(tabViewItem); } else { diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index f310777b162..0b4f3384770 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -220,7 +220,7 @@ namespace winrt::TerminalApp::implementation void _UpdateTabIndices(); - TerminalApp::SettingsTab _settingsTab{ nullptr }; + TerminalApp::TerminalTab _settingsTab{ nullptr }; bool _isInFocusMode{ false }; bool _isFullscreen{ false }; @@ -297,7 +297,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); From fb7c80938bd5662599efa01679b30ee55ffee835 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 1 Aug 2023 11:37:10 -0500 Subject: [PATCH 19/63] derp --- src/cascadia/TerminalApp/SettingsPaneContent.cpp | 14 -------------- src/cascadia/TerminalApp/SettingsPaneContent.h | 1 + 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index d1d182f7037..40682811f51 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -14,21 +14,7 @@ namespace winrt::TerminalApp::implementation { SettingsPaneContent::SettingsPaneContent(CascadiaSettings settings) { - _root = winrt::Windows::UI::Xaml::Controls::Grid{}; - _root.VerticalAlignment(VerticalAlignment::Stretch); - _root.HorizontalAlignment(HorizontalAlignment::Stretch); - _sui = winrt::Microsoft::Terminal::Settings::Editor::MainPage{ settings }; - - 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 SettingsPaneContent::GetRoot() diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index 97121520033..23cb8e22775 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -3,6 +3,7 @@ #pragma once #include "SettingsPaneContent.g.h" +#include namespace winrt::TerminalApp::implementation { From 842326daa5b5dcde126197e49f39489c64a70e11 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 3 Aug 2023 11:31:57 -0500 Subject: [PATCH 20/63] icons for non-terminal pane content --- src/cascadia/TerminalApp/IPaneContent.idl | 1 + src/cascadia/TerminalApp/ScratchpadContent.cpp | 6 ++++++ src/cascadia/TerminalApp/ScratchpadContent.h | 1 + src/cascadia/TerminalApp/SettingsPaneContent.cpp | 7 +++++++ src/cascadia/TerminalApp/SettingsPaneContent.h | 1 + src/cascadia/TerminalApp/TabManagement.cpp | 14 ++++---------- src/cascadia/TerminalApp/TerminalPage.cpp | 6 +++++- src/cascadia/TerminalApp/TerminalPaneContent.cpp | 5 +++++ src/cascadia/TerminalApp/TerminalPaneContent.h | 1 + 9 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 54442316b1c..55563bf49fe 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -19,6 +19,7 @@ namespace TerminalApp UInt64 TaskbarState { get; }; UInt64 TaskbarProgress { get; }; Boolean ReadOnly { get; }; + String Icon { get; }; Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent); diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index 6dc690448e8..5b9d850a583 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -50,4 +50,10 @@ namespace winrt::TerminalApp::implementation { return nullptr; } + + winrt::hstring ScratchpadContent::Icon() const + { + static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote + return winrt::hstring{ glyph }; + } } diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index 0193fcfc15e..6bda39ab747 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -21,6 +21,7 @@ namespace winrt::TerminalApp::implementation uint64_t TaskbarState() { return 0; } uint64_t TaskbarProgress() { return 0; } bool ReadOnly() { return false; } + winrt::hstring Icon() const; til::typed_event<> CloseRequested; til::typed_event BellRequested; diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index 40682811f51..29d9d312a9d 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -42,4 +42,11 @@ namespace winrt::TerminalApp::implementation // TODO! hey, can we somehow replicate std::vector SettingsTab::BuildStartupActions? 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 }; + } } diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index 23cb8e22775..6509a03f067 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -23,6 +23,7 @@ namespace winrt::TerminalApp::implementation uint64_t TaskbarState() { return 0; } uint64_t TaskbarProgress() { return 0; } bool ReadOnly() { return false; } + winrt::hstring Icon() const; til::typed_event<> CloseRequested; til::typed_event BellRequested; diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index f484bef420c..6afbdf5890f 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -226,14 +226,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() }) - { - if (!profile.Icon().empty()) - { - newTabImpl->UpdateIcon(profile.Icon()); - } - } + // Set this tab's icon to the icon from the content + _UpdateTabIcon(*newTabImpl); tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick }); @@ -293,9 +287,9 @@ 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() }) { - tab.UpdateIcon(profile.Icon()); + tab.UpdateIcon(content.Icon()); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 8fc518cb700..94c5c305c71 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2265,7 +2265,11 @@ namespace winrt::TerminalApp::implementation const float splitSize, std::shared_ptr newPane) { - // TODO! Prevent splitting the _settingsTab! + // For now, prevent splitting the _settingsTab. We can always revisit this later. + if (tab == _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 diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 949c5e32707..dca646cb132 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -76,6 +76,11 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } + winrt::hstring TerminalPaneContent::Icon() const + { + return _profile.Icon(); + } + NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const { NewTerminalArgs args{}; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 3ff9f893f7d..00973d8ac4d 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -35,6 +35,7 @@ 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; float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap); Windows::Foundation::Size GridSize(); From 521e3015415a74c6dc70a079b9a5242b79d19f44 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 3 Aug 2023 13:50:11 -0500 Subject: [PATCH 21/63] update settings should work now --- src/cascadia/TerminalApp/IPaneContent.idl | 1 + src/cascadia/TerminalApp/Pane.cpp | 12 ++++++++++-- src/cascadia/TerminalApp/Pane.h | 5 +++-- src/cascadia/TerminalApp/ScratchpadContent.cpp | 5 +++++ src/cascadia/TerminalApp/ScratchpadContent.h | 2 ++ src/cascadia/TerminalApp/SettingsPaneContent.cpp | 12 ++++++++++++ src/cascadia/TerminalApp/SettingsPaneContent.h | 2 ++ src/cascadia/TerminalApp/TabManagement.cpp | 3 ++- src/cascadia/TerminalApp/TerminalPage.cpp | 10 ++++++++-- src/cascadia/TerminalApp/TerminalPaneContent.cpp | 11 +++++++++-- src/cascadia/TerminalApp/TerminalPaneContent.h | 5 +++-- src/cascadia/TerminalApp/TerminalPaneContent.idl | 4 ++-- src/cascadia/TerminalApp/TerminalTab.cpp | 13 +++++++++---- src/cascadia/TerminalApp/TerminalTab.h | 4 ++-- 14 files changed, 70 insertions(+), 19 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 55563bf49fe..6e7e8cfbc35 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -12,6 +12,7 @@ namespace TerminalApp interface IPaneContent { Windows.UI.Xaml.FrameworkElement GetRoot(); + void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); Windows.Foundation.Size MinSize { get; }; diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 5c251403809..f8f6b1dbd65 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1276,6 +1276,14 @@ void Pane::_FocusFirstChild() } } +void Pane::UpdateSettings(const CascadiaSettings& settings) +{ + if (_content) + { + _content.UpdateSettings(settings); + } +} + // Method Description: // - Updates the settings of this pane, presuming that it is a leaf. // Arguments: @@ -1283,13 +1291,13 @@ void Pane::_FocusFirstChild() // - profile: The profile from which these settings originated. // Return Value: // - -void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile) +void Pane::UpdateTerminalSettings(const TerminalSettingsCreateResult& settings, const Profile& profile) { assert(_IsLeaf()); if (const auto& terminalPane{ _getTerminalContent() }) { - return terminalPane.UpdateSettings(settings, profile); + return terminalPane.UpdateTerminalSettings(settings, profile); } } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index d10b603a55a..fb542a43bca 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -106,8 +106,9 @@ class Pane : public std::enable_shared_from_this 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; - 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 winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, + const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); 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 5b9d850a583..93a897d05dd 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -29,6 +29,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; diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index 6bda39ab747..d7f3b07e32e 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -12,6 +12,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 MinSize(); void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); void Close(); diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index 29d9d312a9d..e02d19e54f6 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -10,6 +10,8 @@ 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) @@ -17,6 +19,16 @@ namespace winrt::TerminalApp::implementation _sui = winrt::Microsoft::Terminal::Settings::Editor::MainPage{ settings }; } + void SettingsPaneContent::UpdateSettings(const CascadiaSettings& settings) + { + ASSERT_UI_THREAD(); + _sui.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(); + } + winrt::Windows::UI::Xaml::FrameworkElement SettingsPaneContent::GetRoot() { return _sui; diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index 6509a03f067..d9a05a56af9 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -11,6 +11,8 @@ namespace winrt::TerminalApp::implementation { 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; } diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 6afbdf5890f..62eb82126cb 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -289,7 +289,8 @@ namespace winrt::TerminalApp::implementation { if (const auto content{ tab.GetActiveContent() }) { - tab.UpdateIcon(content.Icon()); + const auto& icon{ content.Icon() }; + tab.UpdateIcon(icon); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 94c5c305c71..0659b911942 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3154,11 +3154,17 @@ namespace winrt::TerminalApp::implementation { if (auto terminalTab{ _GetTerminalTabImpl(tab) }) { - terminalTab->UpdateSettings(); + // 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); + + // FURTHERMORE 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. // 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 the pane isn't a terminal pane, it won't have a profile. if (const auto profile{ pane->GetProfile() }) { const auto found{ profileGuidSettingsMap.find(profile.Guid()) }; @@ -3173,7 +3179,7 @@ namespace winrt::TerminalApp::implementation { pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings); } - pane->UpdateSettings(pair.second, pair.first); + pane->UpdateTerminalSettings(pair.second, pair.first); } } }); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index dca646cb132..d2539c63c97 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -288,8 +288,15 @@ 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. + } + + void TerminalPaneContent::UpdateTerminalSettings(const TerminalSettingsCreateResult& settings, + const Profile& profile) { _profile = profile; _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 00973d8ac4d..6a5f7b696fd 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -21,8 +21,9 @@ namespace winrt::TerminalApp::implementation 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 UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + void UpdateTerminalSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, + const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); void MarkAsDefterm(); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index b553cc09d7c..f041ddc517d 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -9,8 +9,8 @@ namespace TerminalApp { Microsoft.Terminal.Control.TermControl GetTerminal(); - void UpdateSettings(const Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings, - const Microsoft.Terminal.Settings.Model.Profile profile); + void UpdateTerminalSettings(Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings, + Microsoft.Terminal.Settings.Model.Profile profile); void MarkAsDefterm(); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 9ac4b95acef..ed3b658eb85 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -268,12 +268,18 @@ namespace winrt::TerminalApp::implementation // of the settings that apply to all tabs. // Return Value: // - - void TerminalTab::UpdateSettings() + void TerminalTab::UpdateSettings(const CascadiaSettings& settings) { 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); + return false; + }); } // Method Description: @@ -282,7 +288,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) + void TerminalTab::UpdateIcon(const winrt::hstring& iconPath) { ASSERT_UI_THREAD(); @@ -377,7 +383,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: @@ -987,7 +993,6 @@ namespace winrt::TerminalApp::implementation if (const auto& termContent{ content.try_as() }) { _addBroadcastHandlers(termContent.GetTerminal(), events); - } } diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index 9757c97f4f3..8bc8dca6520 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); + void UpdateIcon(const winrt::hstring& iconPath); 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); void UpdateTitle(); void Shutdown() override; From 95310695381a4b08d8656094481b7b3848249208 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 7 Aug 2023 15:17:09 -0500 Subject: [PATCH 22/63] background brush, done --- src/cascadia/TerminalApp/IPaneContent.idl | 2 ++ .../TerminalApp/ScratchpadContent.cpp | 5 ++++ src/cascadia/TerminalApp/ScratchpadContent.h | 2 ++ .../TerminalApp/SettingsPaneContent.cpp | 26 ++++++++++++++++--- .../TerminalApp/SettingsPaneContent.h | 3 +++ src/cascadia/TerminalApp/SettingsTab.cpp | 6 ----- .../TerminalApp/TerminalPaneContent.cpp | 10 +++++++ .../TerminalApp/TerminalPaneContent.h | 2 ++ src/cascadia/TerminalApp/TerminalTab.cpp | 14 +++++----- 9 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 6e7e8cfbc35..8b035def244 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -21,6 +21,8 @@ namespace TerminalApp 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(Boolean asContent); diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index 93a897d05dd..98cb623e0f7 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -61,4 +61,9 @@ namespace winrt::TerminalApp::implementation 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 d7f3b07e32e..fd968551ca3 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -24,6 +24,8 @@ namespace winrt::TerminalApp::implementation 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<> CloseRequested; til::typed_event BellRequested; diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index e02d19e54f6..cfd82d20e9b 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -5,6 +5,7 @@ #include "SettingsPaneContent.h" #include "PaneArgs.h" #include "SettingsPaneContent.g.cpp" +#include "Utils.h" using namespace winrt::Windows::Foundation; using namespace winrt::Windows::UI::Xaml; @@ -17,6 +18,10 @@ 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) @@ -24,9 +29,7 @@ namespace winrt::TerminalApp::implementation ASSERT_UI_THREAD(); _sui.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(); + _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme(); } winrt::Windows::UI::Xaml::FrameworkElement SettingsPaneContent::GetRoot() @@ -61,4 +64,21 @@ namespace winrt::TerminalApp::implementation 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 index d9a05a56af9..646af648bf2 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -26,6 +26,8 @@ namespace winrt::TerminalApp::implementation 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<> CloseRequested; til::typed_event BellRequested; @@ -37,6 +39,7 @@ namespace winrt::TerminalApp::implementation 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 index 2e45c2fa1cb..107bbb45732 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -38,7 +38,6 @@ namespace winrt::TerminalApp::implementation void SettingsTab::UpdateSettings(CascadiaSettings settings) { - // TODO! oh noes, we need to do this too in the content ASSERT_UI_THREAD(); auto settingsUI{ Content().as() }; @@ -110,8 +109,6 @@ namespace winrt::TerminalApp::implementation // - void SettingsTab::_CreateIcon() { - // TODO! make sure this works - // This is the Setting icon (looks like a gear) static constexpr std::wstring_view glyph{ L"\xE713" }; @@ -122,9 +119,6 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush() { - // TODO! make sure this still works. It would be ironic if this _just - // worked_ because the SUI was the same color as a tab with no styling. - // 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. diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index d2539c63c97..a27319ab084 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -81,6 +81,11 @@ namespace winrt::TerminalApp::implementation return _profile.Icon(); } + Windows::Foundation::IReference TerminalPaneContent::TabColor() const noexcept + { + return _control.TabColor(); + } + NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const { NewTerminalArgs args{}; @@ -311,6 +316,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 6a5f7b696fd..0c21449a636 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -37,6 +37,8 @@ namespace winrt::TerminalApp::implementation 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 GridSize(); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index ed3b658eb85..b9cb6ddb974 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1470,12 +1470,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(); } } @@ -1485,7 +1485,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 // @@ -1494,7 +1494,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)); } @@ -1533,7 +1533,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(); } From d726165330e092dcd55147fce3528185d831e9fa Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 13 Oct 2023 12:06:59 -0500 Subject: [PATCH 23/63] terrible, but it works --- .../TerminalApp/SettingsPaneContent.cpp | 4 ++- src/cascadia/TerminalApp/TerminalTab.cpp | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index cfd82d20e9b..549ecf8629d 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -54,7 +54,9 @@ namespace winrt::TerminalApp::implementation NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const bool /* asContent */) const { - // TODO! hey, can we somehow replicate std::vector SettingsTab::BuildStartupActions? + // 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; } diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 7e770ce1d90..a6fb21c128c 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -450,9 +450,32 @@ namespace winrt::TerminalApp::implementation // 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); + Pane::BuildStartupState state; + // 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 somehown 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, asContent); + ActionAndArgs newTabAction{}; newTabAction.Action(ShortcutAction::NewTab); NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) }; From e82c627ebea96bb0ed6e29fcdefba6e973f9b193 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 13 Oct 2023 12:09:08 -0500 Subject: [PATCH 24/63] dead code removal --- src/cascadia/TerminalApp/TerminalPage.cpp | 59 ----------------------- src/features.xml | 1 + 2 files changed, 1 insertion(+), 59 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 069046e0798..6ecd20f54aa 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3931,65 +3931,6 @@ namespace winrt::TerminalApp::implementation // Create the tab auto resultPane = std::make_shared(*settingsContent); _settingsTab = _CreateNewTabFromPane(resultPane); - - // 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); - // } - // }); - - // TODO! Make sure we remove the _settingsTab if it is closed! - // ---------------------- main - // 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); - // } - // }); - // ======= - // // When the tab is closed, remove it from our list of tabs. - // newTabImpl->Closed([tabViewItem, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { - // if (auto page{ weakThis.get() }) - // { - // page->_settingsTab = nullptr; - // page->_RemoveOnCloseRoutine(tabViewItem, page); - // } - // }); - // -------------------------- dev/migrie/fhl/scratchpad-pane - - // _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); } else { diff --git a/src/features.xml b/src/features.xml index af144d202cd..47b00ee5ede 100644 --- a/src/features.xml +++ b/src/features.xml @@ -194,6 +194,7 @@ AlwaysDisabled Dev + Canary From 81889a685ca115d2c2763a02edec1ab131c230c6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 13 Oct 2023 14:08:49 -0500 Subject: [PATCH 25/63] derp --- src/cascadia/TerminalApp/Pane.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index d55611b33fd..237c71dafb2 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -2927,7 +2927,6 @@ void Pane::FinalizeConfigurationGivenDefault() { terminalPane.MarkAsDefterm(); } - // _isDefTermSession = true; } // Method Description: From 5f4087ff00365e0f53f83a1e36203e78d2009231 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 13 Oct 2023 14:56:50 -0500 Subject: [PATCH 26/63] finish exorcising SettingsTab --- src/cascadia/TerminalApp/SettingsTab.cpp | 131 ------------------ src/cascadia/TerminalApp/SettingsTab.h | 43 ------ src/cascadia/TerminalApp/SettingsTab.idl | 12 -- src/cascadia/TerminalApp/TabManagement.cpp | 9 -- .../TerminalApp/TerminalAppLib.vcxproj | 7 - .../TerminalAppLib.vcxproj.filters | 11 +- src/cascadia/TerminalApp/TerminalPage.cpp | 16 +-- .../TerminalApp/dll/TerminalApp.vcxproj | 1 - 8 files changed, 6 insertions(+), 224 deletions(-) delete mode 100644 src/cascadia/TerminalApp/SettingsTab.cpp delete mode 100644 src/cascadia/TerminalApp/SettingsTab.h delete mode 100644 src/cascadia/TerminalApp/SettingsTab.idl diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp deleted file mode 100644 index 1c9c4049f66..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(const bool /*asContent*/) 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(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 657c8387661..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(const bool asContent = false) 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 23795328212..de14b948d0f 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 @@ -794,14 +793,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 101d7193d8a..b24eda43144 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -88,9 +88,6 @@ PaletteItemTemplateSelector.idl Code - - SettingsTab.idl - TabBase.idl @@ -194,9 +191,6 @@ PaletteItemTemplateSelector.idl Code - - SettingsTab.idl - TabBase.idl @@ -312,7 +306,6 @@ Designer - diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters index 03cb821e51e..69481889e0c 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 @@ -99,9 +93,6 @@ settings - - tab - tab @@ -187,4 +178,4 @@ app - \ No newline at end of file + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 6ecd20f54aa..6aad11ca486 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -21,7 +21,6 @@ #include "App.h" #include "ColorHelper.h" #include "DebugTapConnection.h" -#include "SettingsTab.h" #include "SettingsPaneContent.h" #include "TabRowControl.h" #include "Utils.h" @@ -3341,10 +3340,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()); @@ -4612,13 +4607,12 @@ 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() }) + { + terminalBrush = pane->GetContent().BackgroundBrush(); + } } if (_settings.GlobalSettings().UseAcrylicInTabRow()) diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj index 4197e1cc70f..4308abf78be 100644 --- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj @@ -36,7 +36,6 @@ - From fb74fc8c6a5f1377f4f8b95618a667cbd0742d29 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 13 Oct 2023 14:58:19 -0500 Subject: [PATCH 27/63] dead code --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index bac17ef5fbf..e5b908655fc 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1427,7 +1427,6 @@ namespace winrt::TerminalApp::implementation if (Feature_ScratchpadPane::IsEnabled()) { auto scratchPane{ winrt::make_self() }; - // auto scratchPane{ winrt::make_self(_settings) }; // 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 From fd0640997d5d746942563eb2f74a8259969f9c98 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 13 Oct 2023 15:17:38 -0500 Subject: [PATCH 28/63] annoying build break --- .../LocalTests_TerminalApp/TabTests.cpp | 20 +++++++++---------- src/cascadia/LocalTests_TerminalApp/pch.h | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) 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 f82561888b1..154fe5d0177 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 From 58e8f3c11ccdeb3194b58ad29ad5f738a4c6c684 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 25 Oct 2023 09:37:23 -0500 Subject: [PATCH 29/63] mostly nits --- src/cascadia/TerminalApp/Pane.cpp | 11 ++++------- src/cascadia/TerminalApp/Pane.h | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++++ .../TerminalApp/TerminalPaneContent.cpp | 18 +++++++++--------- src/cascadia/TerminalApp/TerminalPaneContent.h | 10 +++++----- src/cascadia/TerminalApp/TerminalTab.cpp | 6 +++--- 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 237c71dafb2..22927afdd38 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1351,7 +1351,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. @@ -1380,12 +1380,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 @@ -1601,7 +1598,7 @@ void Pane::_CloseChildRoutine(const bool closeFirst) // this one doesn't seem to. if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed) { - _CloseChild(closeFirst, false); + _CloseChild(closeFirst); return; } @@ -1704,7 +1701,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); } }); } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index de9aad13616..ec4754c1373 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -295,7 +295,7 @@ 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(); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d2c129f072d..44fac10722e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3192,6 +3192,10 @@ namespace winrt::TerminalApp::implementation const TerminalApp::TerminalPaneContent& paneContent, const winrt::Windows::Foundation::IInspectable&) { + // 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) }) { paneContent.GetTerminal().Connection(connection); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 3863a377824..9d97788c6e7 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -25,10 +25,10 @@ namespace winrt::TerminalApp::implementation 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._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 }); @@ -79,7 +79,7 @@ namespace winrt::TerminalApp::implementation NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const { NewTerminalArgs args{}; - auto controlSettings = _control.Settings(); + const auto& controlSettings = _control.Settings(); args.Profile(controlSettings.ProfileName()); // If we know the user's working directory use it instead of the profile. @@ -160,7 +160,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - winrt::fire_and_forget TerminalPaneContent::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, + winrt::fire_and_forget TerminalPaneContent::_controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) { ConnectionStateChanged.raise(sender, args); @@ -228,7 +228,7 @@ namespace winrt::TerminalApp::implementation // has the 'visual' flag set // Arguments: // - - void TerminalPaneContent::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + void TerminalPaneContent::_controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) { if (_profile) @@ -292,13 +292,13 @@ namespace winrt::TerminalApp::implementation } } } - void TerminalPaneContent::_CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + 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*/, + void TerminalPaneContent::_restartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { RestartTerminalRequested.raise(*this, nullptr); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 953b3d8879d..2d26e5b1924 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -77,10 +77,10 @@ namespace winrt::TerminalApp::implementation 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, + 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 _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); @@ -88,7 +88,7 @@ namespace winrt::TerminalApp::implementation 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*/); + 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/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 843e8672f75..0adcca4eb6e 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -909,9 +909,9 @@ namespace winrt::TerminalApp::implementation auto it = _contentEvents.find(paneId); if (it != _contentEvents.end()) { - auto& events = it->second; - events = {}; - + // revoke the event handlers by resetting the event struct + it->second = {}; + // and remove it from the map _contentEvents.erase(paneId); } } From 7bc1457d4247c559110fcebd35c116f27bc50975 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 25 Oct 2023 11:03:41 -0500 Subject: [PATCH 30/63] nits and such --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 4 ++-- src/cascadia/TerminalApp/ScratchpadContent.cpp | 3 +-- src/features.xml | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index d5202a79dbd..39cadb146d1 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1426,14 +1426,14 @@ namespace winrt::TerminalApp::implementation { if (Feature_ScratchpadPane::IsEnabled()) { - auto scratchPane{ winrt::make_self() }; + 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 }); - auto resultPane = std::make_shared(*scratchPane); + const auto resultPane = std::make_shared(*scratchPane); _SplitPane(_GetFocusedTabImpl(), SplitDirection::Automatic, 0.5f, resultPane); args.Handled(true); } diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index 6dc690448e8..bb821f98c7d 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -15,8 +15,7 @@ namespace winrt::TerminalApp::implementation ScratchpadContent::ScratchpadContent() { _root = winrt::Windows::UI::Xaml::Controls::Grid{}; - _root.VerticalAlignment(VerticalAlignment::Stretch); - _root.HorizontalAlignment(HorizontalAlignment::Stretch); + // Vertical and HorizontalAlignment are Stretch by default auto res = Windows::UI::Xaml::Application::Current().Resources(); auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush")); diff --git a/src/features.xml b/src/features.xml index af144d202cd..47b00ee5ede 100644 --- a/src/features.xml +++ b/src/features.xml @@ -194,6 +194,7 @@ AlwaysDisabled Dev + Canary From 389ba20a9867239191a7162a6f813aa871baf602 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 25 Oct 2023 14:41:57 -0500 Subject: [PATCH 31/63] spel --- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 96d5df68fb9..80e87a750b0 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -460,7 +460,7 @@ namespace winrt::TerminalApp::implementation // for every pane. // // In the future, we'll want to definitely get rid of - // Pane::GetTerminalArgsForPane, and somehown instead find a better way + // 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() }) From 4cec7e9b4b6063a910e2db573f81402fe736ed8e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Nov 2023 06:01:55 -0600 Subject: [PATCH 32/63] try to remove a few of these but ultimately, eh --- src/cascadia/TerminalApp/TerminalTab.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 0adcca4eb6e..ac48d6d7322 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1686,6 +1686,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.GetTerminal(); + } + } + 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 @@ -1697,14 +1709,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) @@ -1729,14 +1741,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) @@ -1821,7 +1833,7 @@ namespace winrt::TerminalApp::implementation { return; } - if (const auto& control{ p->GetTerminalControl() }) + if (const auto& control{ _termControlFromPane(p) }) { auto it = _contentEvents.find(*paneId); if (it != _contentEvents.end()) From 6bc711de065466221738610fb5b9cba40ae1cf60 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Nov 2023 11:10:58 -0600 Subject: [PATCH 33/63] maybe I'm not that good at coding --- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index ac48d6d7322..ca4cab21777 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1686,7 +1686,7 @@ namespace winrt::TerminalApp::implementation return _zoomedPane != nullptr; } - TermControl& _termControlFromPane(const auto& pane) + TermControl _termControlFromPane(const auto& pane) { if (const auto content{ pane->GetContent() }) { From 0a11643f1dfaafdc6ae73d948dfabb336309a83b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 4 Mar 2024 16:30:16 -0600 Subject: [PATCH 34/63] sanely pass around a cache instead of... whatever that was. --- src/cascadia/TerminalApp/Pane.cpp | 31 +++---- src/cascadia/TerminalApp/Pane.h | 4 +- .../TerminalApp/TerminalAppLib.vcxproj | 7 ++ src/cascadia/TerminalApp/TerminalPage.cpp | 86 ++++++++++--------- src/cascadia/TerminalApp/TerminalPage.h | 2 + .../TerminalApp/TerminalPaneContent.cpp | 10 ++- .../TerminalApp/TerminalPaneContent.h | 3 +- .../TerminalApp/TerminalPaneContent.idl | 4 +- .../TerminalApp/TerminalSettingsCache.cpp | 57 ++++++++++++ .../TerminalApp/TerminalSettingsCache.h | 48 +++++++++++ .../TerminalApp/TerminalSettingsCache.idl | 12 +++ src/cascadia/TerminalApp/TerminalTab.cpp | 4 +- src/cascadia/TerminalApp/TerminalTab.h | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 2 +- 14 files changed, 196 insertions(+), 76 deletions(-) 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/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 22bd57edca4..45dc360d426 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1285,28 +1285,21 @@ void Pane::_FocusFirstChild() } } -void Pane::UpdateSettings(const CascadiaSettings& settings) +void Pane::UpdateSettings(const CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache) { if (_content) { - _content.UpdateSettings(settings); - } -} - -// 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::UpdateTerminalSettings(const TerminalSettingsCreateResult& settings, const Profile& profile) -{ - assert(_IsLeaf()); - - if (const auto& terminalPane{ _getTerminalContent() }) - { - return terminalPane.UpdateTerminalSettings(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); + } } } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index d0865294b17..635c234bf17 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -107,9 +107,7 @@ class Pane : public std::enable_shared_from_this 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; - void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - void UpdateTerminalSettings(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/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index dc386b431ff..f6e19acdc85 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -171,6 +171,9 @@ TerminalPaneContent.idl + + TerminalSettingsCache.idl + SuggestionsControl.xaml @@ -288,6 +291,9 @@ + + TerminalSettingsCache.idl + SuggestionsControl.xaml @@ -361,6 +367,7 @@ + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 7dcb46df6ae..e11ba50b2ff 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3306,56 +3306,58 @@ 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() }; + _terminalSettingsCache = TerminalApp::TerminalSettingsCache{ _settings, *_bindings }; - profileGuidSettingsMap.reserve(allProfiles.Size() + 1); + // // 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() }; - // 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 }); - } + // 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 }); + // } 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); - - // FURTHERMORE 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. - - // 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 the pane isn't a terminal pane, it won't have a profile. - 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->UpdateTerminalSettings(pair.second, pair.first); - } - } - }); + terminalTab->UpdateSettings(_settings, _terminalSettingsCache); + + // // FURTHERMORE 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. + + // // 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 the pane isn't a terminal pane, it won't have a profile. + // 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->UpdateTerminalSettings(pair.second, pair.first); + // } + // } + // }); // 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/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 6f8b865195a..00215eb749b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -272,6 +272,8 @@ namespace winrt::TerminalApp::implementation TerminalApp::ContentManager _manager{ nullptr }; + TerminalApp::TerminalSettingsCache _terminalSettingsCache{ nullptr }; + struct StashedDragData { winrt::com_ptr draggedTab{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index af0fa258e2e..4813c08443a 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -319,13 +319,15 @@ namespace winrt::TerminalApp::implementation // 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 TerminalSettingsCreateResult& settings, - const Profile& profile) + 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: diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index eb72f17ff35..91da5b31c07 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -22,8 +22,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - void UpdateTerminalSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, - const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); + void UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache); void MarkAsDefterm(); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index f041ddc517d..adb24d8eb35 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 GetTerminal(); - void UpdateTerminalSettings(Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings, - 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..8fd199fa4ca --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.h @@ -0,0 +1,48 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Class Name: +- ContentManager.h + +Abstract: +- This is a helper class for tracking all of the terminal "content" instances of + the Terminal. These are all the ControlInteractivity & ControlCore's of each + of our TermControls. These are each assigned a GUID on creation, and stored in + a map for later lookup. +- This is used to enable moving panes between windows. TermControl's are not + thread-agile, so they cannot be reused on other threads. However, the content + is. This helper, which exists as a singleton across all the threads in the + Terminal app, allows each thread to create content, assign it to a + TermControl, detach it from that control, and reattach to new controls on + other threads. +- When you want to create a new TermControl, call CreateCore to instantiate a + new content with a GUID for later reparenting. +- Detach can be used to temporarily remove a content from its hosted + TermControl. After detaching, you can still use LookupCore & + TermControl::AttachContent to re-attach to the content. +--*/ +#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 d298f2ec571..6f2fd3635f3 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -266,7 +266,7 @@ namespace winrt::TerminalApp::implementation // of the settings that apply to all tabs. // Return Value: // - - void TerminalTab::UpdateSettings(const CascadiaSettings& settings) + void TerminalTab::UpdateSettings(const CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache) { ASSERT_UI_THREAD(); @@ -275,7 +275,7 @@ namespace winrt::TerminalApp::implementation // Update the settings on all our panes. _rootPane->WalkTree([&](auto pane) { - pane->UpdateSettings(settings); + pane->UpdateSettings(settings, cache); return false; }); } diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index df1e9102698..e3088f07ada 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); + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache); void UpdateTitle(); void Shutdown() override; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b9c81abe67a..d1526faf534 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -333,7 +333,7 @@ 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); + // assert(false); return; } From 4d47cd58661e51c73b320d56b60f245bd73bd895 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 4 Mar 2024 16:34:36 -0600 Subject: [PATCH 35/63] cleanup --- src/cascadia/TerminalApp/TerminalPage.cpp | 48 ++----------------- .../TerminalApp/TerminalSettingsCache.h | 21 ++------ 2 files changed, 9 insertions(+), 60 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e11ba50b2ff..8e271850844 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3306,25 +3306,12 @@ namespace winrt::TerminalApp::implementation // Refresh UI elements + // 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 }; - // // 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 }); - // } - for (const auto& tab : _tabs) { if (auto terminalTab{ _GetTerminalTabImpl(tab) }) @@ -3332,33 +3319,6 @@ namespace winrt::TerminalApp::implementation // 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); - // // FURTHERMORE 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. - - // // 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 the pane isn't a terminal pane, it won't have a profile. - // 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->UpdateTerminalSettings(pair.second, pair.first); - // } - // } - // }); - // 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 // and profiles so the Title and Icon will be set once and only once on init. diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.h b/src/cascadia/TerminalApp/TerminalSettingsCache.h index 8fd199fa4ca..054ccb4b51a 100644 --- a/src/cascadia/TerminalApp/TerminalSettingsCache.h +++ b/src/cascadia/TerminalApp/TerminalSettingsCache.h @@ -3,24 +3,13 @@ Copyright (c) Microsoft Corporation Licensed under the MIT license. Class Name: -- ContentManager.h +- TerminalSettingsCache.h Abstract: -- This is a helper class for tracking all of the terminal "content" instances of - the Terminal. These are all the ControlInteractivity & ControlCore's of each - of our TermControls. These are each assigned a GUID on creation, and stored in - a map for later lookup. -- This is used to enable moving panes between windows. TermControl's are not - thread-agile, so they cannot be reused on other threads. However, the content - is. This helper, which exists as a singleton across all the threads in the - Terminal app, allows each thread to create content, assign it to a - TermControl, detach it from that control, and reattach to new controls on - other threads. -- When you want to create a new TermControl, call CreateCore to instantiate a - new content with a GUID for later reparenting. -- Detach can be used to temporarily remove a content from its hosted - TermControl. After detaching, you can still use LookupCore & - TermControl::AttachContent to re-attach to the content. +- 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 From b6254f8294140db2d9f6934e0adc64082b495880 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Mar 2024 09:49:48 -0600 Subject: [PATCH 36/63] GREAT-GRANDPARENT: This fixes a crash in parent pane selection (cherry picked from commit 91a0d0e26dc3ca6e595799cec782305e1df839b9) --- src/cascadia/TerminalApp/Pane.cpp | 31 +++++++++++++++++++++-- src/cascadia/TerminalApp/Pane.h | 1 + src/cascadia/TerminalApp/TerminalPage.cpp | 5 +++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 45dc360d426..c62f81fcaa7 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1106,6 +1106,29 @@ TermControl Pane::GetLastFocusedTerminalControl() } } +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; @@ -1246,7 +1269,11 @@ void Pane::UpdateVisuals() void Pane::_Focus() { _GotFocusHandlers(shared_from_this(), FocusState::Programmatic); - _content.Focus(FocusState::Programmatic); + if (const auto& lastContent{ GetLastFocusedContent() }) + { + lastContent.Focus(FocusState::Programmatic); + } + } // Method Description: @@ -1925,7 +1952,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 635c234bf17..7ae85bc95bf 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; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 8e271850844..1ef15cafe68 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4598,7 +4598,10 @@ namespace winrt::TerminalApp::implementation { if (const auto& pane{ tab->GetActivePane() }) { - terminalBrush = pane->GetContent().BackgroundBrush(); + if (const auto& lastContent{ pane->GetLastFocusedContent() }) + { + terminalBrush = lastContent.BackgroundBrush(); + } } } From 524d65869972b651836dde479aecce4a272d09ca Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 15 Mar 2024 12:01:42 -0500 Subject: [PATCH 37/63] GREAT-GREAT-GRANDPARENT: Hey when a pane wants to get closed, we should close it --- src/cascadia/TerminalApp/TerminalTab.cpp | 26 ++++++++++++++++++++++++ src/cascadia/TerminalApp/TerminalTab.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 63effef8867..66800ed7388 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -927,6 +927,32 @@ namespace winrt::TerminalApp::implementation auto dispatcher = TabViewItem().Dispatcher(); 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.TitleChanged = content.TitleChanged( winrt::auto_revoke, [dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index b8c40655f47..3ff128a9e2b 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -132,6 +132,7 @@ namespace winrt::TerminalApp::implementation 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; From ecc036a3e7acff218151ab3754eb8c95a6a64ce8 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 19 Mar 2024 06:39:09 -0500 Subject: [PATCH 38/63] Start just one file at a time --- .../TerminalSettingsModel/ActionArgs.cpp | 12 +- .../TerminalSettingsModel/ActionArgs.h | 140 +++++++++++------- .../TerminalSettingsModel/ActionArgs.idl | 31 ++-- 3 files changed, 108 insertions(+), 75 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index a276ebeff2b..90cb3fcdaba 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -238,9 +238,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()) @@ -458,9 +458,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()) @@ -762,9 +762,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 6d83964cbbc..33d87e48708 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -295,10 +295,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(bool, Handled, false); }; + struct ContentArgsBase + { + ACTION_ARG(winrt::hstring, Type, L""); + }; + // 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. - struct NewTerminalArgs : public NewTerminalArgsT + struct NewTerminalArgs : public NewTerminalArgsT, public ContentArgsBase { NewTerminalArgs() = default; NewTerminalArgs(int32_t& profileIndex) : @@ -333,7 +338,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) @@ -428,6 +433,34 @@ 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, {} }; + } + + return { nullptr, {} }; + } + static Json::Value ContentArgsToJson(const Model::INewContentArgs& contentArgs) + { + if (contentArgs.Type().empty()) + { + return winrt::Microsoft::Terminal::Settings::Model::implementation::NewTerminalArgs::ToJson(contentArgs.try_as()); + } + return Json::Value::null; + } + } template<> @@ -458,6 +491,19 @@ 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()); + } + } +}; namespace winrt::Microsoft::Terminal::Settings::Model::implementation { @@ -468,9 +514,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; @@ -480,7 +526,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; }; @@ -488,15 +534,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) { @@ -505,18 +545,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(); } }; @@ -524,22 +564,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); @@ -556,8 +597,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; } @@ -567,7 +608,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); @@ -576,14 +616,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) { @@ -592,7 +627,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); @@ -602,7 +637,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; @@ -611,7 +646,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(); @@ -621,9 +656,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; @@ -633,7 +668,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; }; @@ -641,16 +676,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) { @@ -659,18 +687,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(); } }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 32f9005fdee..0f421d3ec8f 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -122,10 +122,18 @@ namespace Microsoft.Terminal.Settings.Model All = 0xffffffff, }; - [default_interface] runtimeclass NewTerminalArgs { + interface INewContentArgs { + String Type; + Boolean Equals(INewContentArgs other); + UInt64 Hash(); + INewContentArgs Copy(); + String GenerateName(); + }; + + + [default_interface] runtimeclass NewTerminalArgs : INewContentArgs{ NewTerminalArgs(); NewTerminalArgs(Int32 profileIndex); - NewTerminalArgs Copy(); String Commandline; String StartingDirectory; @@ -152,10 +160,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 @@ -174,8 +179,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 @@ -220,13 +225,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; }; }; @@ -346,8 +351,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 From 6789ec0765f65635c02eacde9a109ca5be74a404 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 19 Mar 2024 13:34:35 -0500 Subject: [PATCH 39/63] some of the easier nits --- src/cascadia/TerminalApp/Pane.cpp | 3 +++ src/cascadia/TerminalApp/Pane.h | 13 +++++++++++++ src/cascadia/TerminalApp/PaneArgs.cpp | 6 ------ src/cascadia/TerminalApp/PaneArgs.h | 18 ------------------ src/cascadia/TerminalApp/TerminalPage.cpp | 14 +++++++------- src/cascadia/TerminalApp/TerminalPaneContent.h | 4 +--- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- 7 files changed, 25 insertions(+), 35 deletions(-) delete mode 100644 src/cascadia/TerminalApp/PaneArgs.cpp delete mode 100644 src/cascadia/TerminalApp/PaneArgs.h diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 22927afdd38..90ec59719f6 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -3,6 +3,9 @@ #include "pch.h" #include "Pane.h" + +#include "BellEventArgs.g.cpp" + #include "AppLogic.h" #include "Utils.h" diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index ec4754c1373..f8f059bb053 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -22,6 +22,7 @@ #include "TaskbarState.h" #include "TerminalPaneContent.h" +#include "BellEventArgs.g.h" // fwdecl unittest classes namespace TerminalAppLocalTests @@ -59,6 +60,18 @@ struct PaneResources winrt::Windows::UI::Xaml::Media::SolidColorBrush broadcastBorderBrush{ nullptr }; }; +namespace winrt::TerminalApp::implementation +{ + struct BellEventArgs : public BellEventArgsT + { + public: + BellEventArgs(bool flashTaskbar) : + FlashTaskbar(flashTaskbar) {} + + til::property FlashTaskbar; + }; +}; + class Pane : public std::enable_shared_from_this { public: diff --git a/src/cascadia/TerminalApp/PaneArgs.cpp b/src/cascadia/TerminalApp/PaneArgs.cpp deleted file mode 100644 index 2b5beddb8b5..00000000000 --- a/src/cascadia/TerminalApp/PaneArgs.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "PaneArgs.h" -#include "BellEventArgs.g.cpp" diff --git a/src/cascadia/TerminalApp/PaneArgs.h b/src/cascadia/TerminalApp/PaneArgs.h deleted file mode 100644 index 5627623be9e..00000000000 --- a/src/cascadia/TerminalApp/PaneArgs.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -#include "BellEventArgs.g.h" - -namespace winrt::TerminalApp::implementation -{ - struct BellEventArgs : public BellEventArgsT - { - public: - BellEventArgs(bool flashTaskbar) : - FlashTaskbar(flashTaskbar) {} - - til::property FlashTaskbar; - }; -}; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 19c486b13a3..5fc8f121f31 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3125,8 +3125,8 @@ 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 terminalPane{ winrt::make(profile, control) }; - return std::make_shared(terminalPane); + auto paneContent{ winrt::make(profile, control) }; + return std::make_shared(paneContent); } TerminalSettingsCreateResult controlSettings{ nullptr }; @@ -3182,15 +3182,15 @@ namespace winrt::TerminalApp::implementation const auto control = _CreateNewControlAndContent(controlSettings, connection); - auto terminalPane{ winrt::make(profile, control) }; - auto resultPane = std::make_shared(terminalPane); + 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 debugTerminalPane{ winrt::make(profile, newControl) }; - auto debugPane = std::make_shared(debugTerminalPane); + 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 @@ -3204,7 +3204,7 @@ namespace winrt::TerminalApp::implementation original->SetActive(); } - _RegisterPaneEvents(terminalPane); + _RegisterPaneEvents(paneContent); return resultPane; } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 2d26e5b1924..0b6c968f4cc 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -3,8 +3,6 @@ #pragma once #include "TerminalPaneContent.g.h" -#include "../../cascadia/inc/cppwinrt_utils.h" -#include namespace winrt::TerminalApp::implementation { @@ -29,7 +27,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const { return _profile; - }; + } winrt::hstring Title() { return _control.Title(); } uint64_t TaskbarState() { return _control.TaskbarState(); } diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 66800ed7388..86be96b6204 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -930,7 +930,7 @@ namespace winrt::TerminalApp::implementation 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. + // 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 From fd8b083a4693451523ef8999e51bd6eed7a55e27 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 19 Mar 2024 13:55:11 -0500 Subject: [PATCH 40/63] get rid of this file --- src/cascadia/TerminalApp/IPaneContent.idl | 4 ++-- src/cascadia/TerminalApp/Pane.cpp | 12 ++++-------- src/cascadia/TerminalApp/Pane.h | 13 ------------- src/cascadia/TerminalApp/TerminalAppLib.vcxproj | 6 ------ src/cascadia/TerminalApp/TerminalPaneContent.cpp | 7 ++++--- src/cascadia/TerminalApp/TerminalPaneContent.h | 14 ++++++++++++-- 6 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 7232de01e1c..11da1e8fff2 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -13,7 +13,7 @@ namespace TerminalApp { Windows.UI.Xaml.FrameworkElement GetRoot(); - Windows.Foundation.Size MinSize { get; }; + Windows.Foundation.Size MinimumSize { get; }; String Title { get; }; UInt64 TaskbarState { get; }; @@ -47,6 +47,6 @@ namespace TerminalApp interface ISnappable { Single SnapDownToGrid(PaneSnapDirection direction, Single sizeToSnap); - Windows.Foundation.Size GridSize { get; }; + Windows.Foundation.Size GridUnitSize { get; }; }; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 90ec59719f6..6cd91801e82 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -4,8 +4,6 @@ #include "pch.h" #include "Pane.h" -#include "BellEventArgs.g.cpp" - #include "AppLogic.h" #include "Utils.h" @@ -2642,7 +2640,7 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const } else { - const auto cellSize = snappable.GridSize(); + const auto cellSize = snappable.GridUnitSize(); const auto higher = lower + (direction == PaneSnapDirection::Width ? cellSize.Width : cellSize.Height); @@ -2703,13 +2701,11 @@ void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& si // 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; + sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher; } else { - const auto cellSize = snappable.GridSize(); + const auto cellSize = snappable.GridUnitSize(); sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height; } } @@ -2825,7 +2821,7 @@ Size Pane::_GetMinSize() const { if (_IsLeaf()) { - auto controlSize = _content.MinSize(); + auto controlSize = _content.MinimumSize(); auto newWidth = controlSize.Width; auto newHeight = controlSize.Height; diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index f8f059bb053..ec4754c1373 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -22,7 +22,6 @@ #include "TaskbarState.h" #include "TerminalPaneContent.h" -#include "BellEventArgs.g.h" // fwdecl unittest classes namespace TerminalAppLocalTests @@ -60,18 +59,6 @@ struct PaneResources winrt::Windows::UI::Xaml::Media::SolidColorBrush broadcastBorderBrush{ nullptr }; }; -namespace winrt::TerminalApp::implementation -{ - struct BellEventArgs : public BellEventArgsT - { - public: - BellEventArgs(bool flashTaskbar) : - FlashTaskbar(flashTaskbar) {} - - til::property FlashTaskbar; - }; -}; - class Pane : public std::enable_shared_from_this { public: diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 30bf93f06a4..d6abba2db90 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -131,9 +131,6 @@ - - IPaneContent.idl - @@ -236,9 +233,6 @@ - - IPaneContent.idl - NotUsing diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 9d97788c6e7..ee7ec9d2cf2 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -3,9 +3,10 @@ #include "pch.h" #include "TerminalPaneContent.h" -#include "PaneArgs.h" #include "TerminalPaneContent.g.cpp" +#include "BellEventArgs.g.cpp" + #include using namespace winrt::Windows::Foundation; using namespace winrt::Windows::UI::Xaml; @@ -49,7 +50,7 @@ namespace winrt::TerminalApp::implementation { return _control; } - winrt::Windows::Foundation::Size TerminalPaneContent::MinSize() + winrt::Windows::Foundation::Size TerminalPaneContent::MinimumSize() { return _control.MinimumSize(); } @@ -324,7 +325,7 @@ namespace winrt::TerminalApp::implementation { return _control.SnapDimensionToGrid(direction == PaneSnapDirection::Width, sizeToSnap); } - Windows::Foundation::Size TerminalPaneContent::GridSize() + Windows::Foundation::Size TerminalPaneContent::GridUnitSize() { return _control.CharacterDimensions(); } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 0b6c968f4cc..fdb26707dae 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -3,9 +3,19 @@ #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, @@ -13,7 +23,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); winrt::Microsoft::Terminal::Control::TermControl GetTerminal(); - winrt::Windows::Foundation::Size MinSize(); + winrt::Windows::Foundation::Size MinimumSize(); void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); void Close(); @@ -35,7 +45,7 @@ namespace winrt::TerminalApp::implementation bool ReadOnly() { return _control.ReadOnly(); } float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap); - Windows::Foundation::Size GridSize(); + Windows::Foundation::Size GridUnitSize(); til::typed_event RestartTerminalRequested; til::typed_event<> CloseRequested; From 052dc78af50ea11f4d66651bf0352dc844b05273 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 19 Mar 2024 14:54:30 -0500 Subject: [PATCH 41/63] more nits --- src/cascadia/TerminalApp/Pane.cpp | 22 ++++--------------- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- .../TerminalApp/TerminalPaneContent.cpp | 2 +- .../TerminalApp/TerminalPaneContent.h | 2 +- .../TerminalApp/TerminalPaneContent.idl | 2 +- src/cascadia/TerminalApp/TerminalTab.cpp | 4 ++-- 6 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 6cd91801e82..d2687d39891 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1081,14 +1081,7 @@ TermControl Pane::GetLastFocusedTerminalControl() { if (p->_IsLeaf()) { - if (const auto& terminalPane{ p->_content.try_as() }) - { - return terminalPane.GetTerminal(); - } - else - { - return nullptr; - } + return p->GetTerminalControl(); } pane = p; } @@ -1096,15 +1089,8 @@ TermControl Pane::GetLastFocusedTerminalControl() } return _firstChild->GetLastFocusedTerminalControl(); } - - if (const auto& terminalPane{ _content.try_as() }) - { - return terminalPane.GetTerminal(); - } - else - { - return nullptr; - } + // we _are_ a leaf. + return GetTerminalControl(); } // Method Description: @@ -1118,7 +1104,7 @@ TermControl Pane::GetTerminalControl() const { if (const auto& terminalPane{ _getTerminalContent() }) { - return terminalPane.GetTerminal(); + return terminalPane.GetTermControl(); } else { diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 5fc8f121f31..adda5136679 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1306,7 +1306,7 @@ namespace winrt::TerminalApp::implementation return nullptr; } - const auto& control{ paneContent.GetTerminal() }; + const auto& control{ paneContent.GetTermControl() }; if (control == nullptr) { return nullptr; @@ -3219,7 +3219,7 @@ namespace winrt::TerminalApp::implementation // for nulls if (const auto& connection{ _duplicateConnectionForRestart(paneContent) }) { - paneContent.GetTerminal().Connection(connection); + paneContent.GetTermControl().Connection(connection); connection.Start(); } } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index ee7ec9d2cf2..597631d8fdc 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -46,7 +46,7 @@ namespace winrt::TerminalApp::implementation { return _control; } - winrt::Microsoft::Terminal::Control::TermControl TerminalPaneContent::GetTerminal() + winrt::Microsoft::Terminal::Control::TermControl TerminalPaneContent::GetTermControl() { return _control; } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index fdb26707dae..1fd917fa325 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -22,7 +22,7 @@ namespace winrt::TerminalApp::implementation const winrt::Microsoft::Terminal::Control::TermControl& control); winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); - winrt::Microsoft::Terminal::Control::TermControl GetTerminal(); + 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(); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 1e39f41c168..7e04c8b836c 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -7,7 +7,7 @@ namespace TerminalApp { [default_interface] runtimeclass TerminalPaneContent : IPaneContent, ISnappable { - Microsoft.Terminal.Control.TermControl GetTerminal(); + Microsoft.Terminal.Control.TermControl GetTermControl(); void UpdateSettings(const Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings, const Microsoft.Terminal.Settings.Model.Profile profile); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 86be96b6204..2325e149e60 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1040,7 +1040,7 @@ namespace winrt::TerminalApp::implementation { if (const auto& termContent{ content.try_as() }) { - _addBroadcastHandlers(termContent.GetTerminal(), events); + _addBroadcastHandlers(termContent.GetTermControl(), events); } } @@ -1722,7 +1722,7 @@ namespace winrt::TerminalApp::implementation { if (const auto termContent{ content.try_as() }) { - return termContent.GetTerminal(); + return termContent.GetTermControl(); } } return nullptr; From a7533faf459211fa6fba1d632ca745498f66958c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 19 Mar 2024 15:15:08 -0500 Subject: [PATCH 42/63] eh these events are from pane content anyways! --- src/cascadia/TerminalApp/IPaneContent.idl | 14 +++++++------- src/cascadia/TerminalApp/TerminalPaneContent.h | 14 +++++++------- src/cascadia/TerminalApp/TerminalTab.cpp | 7 ++----- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 11da1e8fff2..ebe0914ea9c 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -26,15 +26,15 @@ namespace TerminalApp void Close(); - event Windows.Foundation.TypedEventHandler CloseRequested; + event Windows.Foundation.TypedEventHandler CloseRequested; - event Windows.Foundation.TypedEventHandler BellRequested; - event Windows.Foundation.TypedEventHandler TitleChanged; - event Windows.Foundation.TypedEventHandler TabColorChanged; - event Windows.Foundation.TypedEventHandler TaskbarProgressChanged; event Windows.Foundation.TypedEventHandler ConnectionStateChanged; - event Windows.Foundation.TypedEventHandler ReadOnlyChanged; - event Windows.Foundation.TypedEventHandler FocusRequested; + 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; }; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 1fd917fa325..2c2deee0124 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -48,14 +48,14 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Size GridUnitSize(); til::typed_event RestartTerminalRequested; - til::typed_event<> CloseRequested; - til::typed_event BellRequested; - til::typed_event<> TitleChanged; - til::typed_event<> TabColorChanged; - til::typed_event<> TaskbarProgressChanged; til::typed_event<> ConnectionStateChanged; - til::typed_event<> ReadOnlyChanged; - til::typed_event<> FocusRequested; + 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 }; diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 2325e149e60..5f44ae21d6a 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1021,17 +1021,14 @@ namespace winrt::TerminalApp::implementation events.FocusRequested = content.FocusRequested( winrt::auto_revoke, - [dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget { + [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()) { - if (const auto content{ sender.try_as() }) - { - content.Focus(FocusState::Pointer); - } + sender.Focus(FocusState::Pointer); } } }); From 826fc087b03f7aff6cd657651a54335f94e68ee5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 19 Mar 2024 15:50:58 -0500 Subject: [PATCH 43/63] hey there buddy, did you get lost? --- src/cascadia/TerminalApp/Pane.h | 1 - src/cascadia/TerminalApp/TerminalTab.cpp | 27 ++++++++++++++++++++ src/cascadia/TerminalControl/TermControl.cpp | 1 - 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index ec4754c1373..7b27fc6f31d 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -222,7 +222,6 @@ class Pane : public std::enable_shared_from_this WINRT_CALLBACK(GotFocus, gotFocusArgs); WINRT_CALLBACK(LostFocus, winrt::delegate>); - WINRT_CALLBACK(PaneRaiseBell, winrt::Windows::Foundation::EventHandler); WINRT_CALLBACK(Detached, winrt::delegate>); private: diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 5f44ae21d6a..287be2da5d0 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1033,6 +1033,33 @@ namespace winrt::TerminalApp::implementation } }); + 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->_TabRaiseVisualBellHandlers(); + } + + // 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 (_tabStatus.IsInputBroadcastActive()) { if (const auto& termContent{ content.try_as() }) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b9c81abe67a..de7b6fc88a9 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; } From 52970ef8544899c275e0c5abfbfcce518439d647 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 19 Mar 2024 16:30:00 -0500 Subject: [PATCH 44/63] RegisterBigTimeEncapsulationViolatingTerminalPaneContentEvents --- src/cascadia/TerminalApp/TerminalPage.cpp | 17 +---------------- src/cascadia/TerminalApp/TerminalPage.h | 1 - src/cascadia/TerminalApp/TerminalTab.cpp | 11 ++++++++++- src/cascadia/TerminalApp/TerminalTab.h | 6 ++++++ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index adda5136679..fdc223bb9ab 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1731,11 +1731,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(const TerminalApp::TerminalPaneContent& paneContent) - { - paneContent.RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection }); + hostingTab.RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection }); } // Method Description: @@ -2384,16 +2381,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. - if (const auto& paneContent{ original->GetContent().try_as() }) - { - _RegisterPaneEvents(*paneContent); - } - // After GH#6586, the control will no longer focus itself // automatically when it's finished being laid out. Manually focus // the control here instead. @@ -3204,8 +3191,6 @@ namespace winrt::TerminalApp::implementation original->SetActive(); } - _RegisterPaneEvents(paneContent); - return resultPane; } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 62ab115788d..02705fa71ca 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -345,7 +345,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(const TerminalApp::TerminalPaneContent& paneContent); void _DismissTabContextMenus(); void _FocusCurrentTab(const bool focusAlways); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 287be2da5d0..4467659faa6 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -904,7 +904,6 @@ namespace winrt::TerminalApp::implementation if (it != _contentEvents.end()) { // revoke the event handlers by resetting the event struct - it->second = {}; // and remove it from the map _contentEvents.erase(paneId); } @@ -1060,6 +1059,11 @@ namespace winrt::TerminalApp::implementation } }); + if (const auto& terminal{ content.try_as() }) + { + events.RestartTerminalRequested = terminal.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalTab::_bubbleRestartTerminalRequested }); + } + if (_tabStatus.IsInputBroadcastActive()) { if (const auto& termContent{ content.try_as() }) @@ -1998,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 3ff128a9e2b..221112274d9 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -97,6 +97,8 @@ namespace winrt::TerminalApp::implementation return _tabStatus; } + til::typed_event RestartTerminalRequested; + WINRT_CALLBACK(ActivePaneChanged, winrt::delegate<>); WINRT_CALLBACK(TabRaiseVisualBell, winrt::delegate<>); TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable); @@ -138,6 +140,8 @@ namespace winrt::TerminalApp::implementation 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 _contentEvents; @@ -196,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; }; } From e0bb8409b32236b699101e6b2e4229ce2db84126 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 20 Mar 2024 09:13:54 -0500 Subject: [PATCH 45/63] Fix scratch pane for merge (cherry picked from commit 591080db39c0e7bf77647d7100b302bbe5debad5) --- src/cascadia/TerminalApp/ScratchpadContent.cpp | 3 +-- src/cascadia/TerminalApp/ScratchpadContent.h | 17 +++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index bb821f98c7d..fd7a8cd6975 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "ScratchpadContent.h" -#include "PaneArgs.h" #include "ScratchpadContent.g.cpp" using namespace winrt::Windows::Foundation; @@ -32,7 +31,7 @@ namespace winrt::TerminalApp::implementation { return _root; } - winrt::Windows::Foundation::Size ScratchpadContent::MinSize() + winrt::Windows::Foundation::Size ScratchpadContent::MinimumSize() { return { 1, 1 }; } diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index de1c9d6d647..995c92419b4 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -12,7 +12,8 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); - winrt::Windows::Foundation::Size MinSize(); + 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; @@ -22,14 +23,14 @@ namespace winrt::TerminalApp::implementation uint64_t TaskbarProgress() { return 0; } bool ReadOnly() { return false; } - til::typed_event<> CloseRequested; - til::typed_event BellRequested; - til::typed_event<> TitleChanged; - til::typed_event<> TabColorChanged; - til::typed_event<> TaskbarProgressChanged; til::typed_event<> ConnectionStateChanged; - til::typed_event<> ReadOnlyChanged; - til::typed_event<> FocusRequested; + 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 }; From 352e0a211a012119a137e39769babb95bc51b982 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 20 Mar 2024 09:14:28 -0500 Subject: [PATCH 46/63] fix settings pane for merge (cherry picked from commit 0c6a353967a8a7260e1fffeb137e2593c3046587) --- src/cascadia/TerminalApp/SettingsPaneContent.cpp | 3 +-- src/cascadia/TerminalApp/SettingsPaneContent.h | 16 ++++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index 549ecf8629d..5b16b6ae28f 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "SettingsPaneContent.h" -#include "PaneArgs.h" #include "SettingsPaneContent.g.cpp" #include "Utils.h" @@ -36,7 +35,7 @@ namespace winrt::TerminalApp::implementation { return _sui; } - winrt::Windows::Foundation::Size SettingsPaneContent::MinSize() + winrt::Windows::Foundation::Size SettingsPaneContent::MinimumSize() { return { 1, 1 }; } diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index f989ed15045..19fb5b8439d 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -16,7 +16,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); winrt::Microsoft::Terminal::Settings::Editor::MainPage SettingsUI() { return _sui; } - winrt::Windows::Foundation::Size MinSize(); + 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; @@ -29,14 +29,14 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::IReference TabColor() const noexcept; winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush(); - til::typed_event<> CloseRequested; - til::typed_event BellRequested; - til::typed_event<> TitleChanged; - til::typed_event<> TabColorChanged; - til::typed_event<> TaskbarProgressChanged; til::typed_event<> ConnectionStateChanged; - til::typed_event<> ReadOnlyChanged; - til::typed_event<> FocusRequested; + 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 }; From 40a34087096106cb5800d863ecae7f96e0312489 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 20 Mar 2024 11:45:45 -0500 Subject: [PATCH 47/63] hey it compiles? --- .../TerminalApp/AppActionHandlers.cpp | 92 +++++++++++++------ src/cascadia/TerminalApp/TabManagement.cpp | 37 ++++---- src/cascadia/TerminalApp/TerminalPage.cpp | 91 ++++++++++++------ src/cascadia/TerminalApp/TerminalPage.h | 10 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 +- 5 files changed, 155 insertions(+), 77 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index ad53b420c18..a432436ae71 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. + 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,8 @@ 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"") + newContentArgs ? terminalArgs.ToCommandline().c_str() : + L"") }; // Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we @@ -909,29 +938,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/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 9f10174da67..a7532adece9 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 e41159a8062..2b6691d04b1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -22,6 +22,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 { @@ -3106,9 +3108,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 && @@ -3174,7 +3176,6 @@ namespace winrt::TerminalApp::implementation } const auto control = _CreateNewControlAndContent(controlSettings, connection); - auto paneContent{ winrt::make(profile, control) }; auto resultPane = std::make_shared(paneContent); @@ -3200,6 +3201,39 @@ 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({ this, &TerminalPage::_KeyDownHandler }); + + content = *scratchPane; + } + else if (paneType == L"settings") + { + content = _makeSettingsPane(); + } + + return std::make_shared(content); + } + void TerminalPage::_restartPaneConnection( const TerminalApp::TerminalPaneContent& paneContent, const winrt::Windows::Foundation::IInspectable&) @@ -3855,6 +3889,30 @@ namespace winrt::TerminalApp::implementation CATCH_RETURN() } + TerminalApp::IPaneContent TerminalPage::_makeSettingsPane() + { + // 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); + } + }); + + 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. @@ -3876,27 +3934,8 @@ namespace winrt::TerminalApp::implementation } } - // 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(_makeSettingsPane()); _settingsTab = _CreateNewTabFromPane(resultPane); } else diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 82c8482aace..f010d0fb6c3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -304,7 +304,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); @@ -313,7 +313,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); @@ -448,7 +448,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 _makeSettingsPane(); + 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/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 51e7f758da5..0510153b701 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1272,7 +1272,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); } From ea9522a456e5863987cad258f64a6e61ee5852ef Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 20 Mar 2024 12:36:23 -0500 Subject: [PATCH 48/63] hey it runs... but it crashes --- .../TerminalSettingsModel/ActionArgs.h | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 33d87e48708..b95fcb5a8bc 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -295,19 +295,66 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(bool, Handled, false); }; - struct ContentArgsBase + struct ContentArgsBase : public winrt::implements { + ContentArgsBase(winrt::hstring type) : + _Type{ type } {} + + ContentArgsBase() : + ContentArgsBase(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 winrt::com_ptr& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + JsonUtils::SetValueForKey(json, TypeKey, val->_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. - struct NewTerminalArgs : public NewTerminalArgsT, public ContentArgsBase + struct NewTerminalArgs : public NewTerminalArgsT { 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""); @@ -450,7 +497,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return { terminalArgs, {} }; } - return { nullptr, {} }; + // 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) { @@ -458,7 +507,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return winrt::Microsoft::Terminal::Settings::Model::implementation::NewTerminalArgs::ToJson(contentArgs.try_as()); } - return Json::Value::null; + + // 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 ContentArgsBase::ToJson(base); } } From 18fe0d8c228f53bdc11dd0c804da6ce21ad7a2cb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 20 Mar 2024 15:07:24 -0500 Subject: [PATCH 49/63] stashing, but I think this builds --- src/cascadia/TerminalApp/IPaneContent.idl | 2 +- src/cascadia/TerminalApp/Pane.cpp | 3 +-- src/cascadia/TerminalApp/Pane.h | 2 +- .../TerminalApp/ScratchpadContent.cpp | 4 +-- src/cascadia/TerminalApp/ScratchpadContent.h | 2 +- .../TerminalApp/SettingsPaneContent.cpp | 2 +- .../TerminalApp/SettingsPaneContent.h | 2 +- .../TerminalApp/TerminalPaneContent.cpp | 2 +- .../TerminalApp/TerminalPaneContent.h | 2 +- .../TerminalSettingsModel/ActionArgs.cpp | 1 + .../TerminalSettingsModel/ActionArgs.h | 25 +++++++++++-------- .../TerminalSettingsModel/ActionArgs.idl | 4 +++ 12 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index b2031bac6f9..7f0e3ed8c20 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -24,7 +24,7 @@ namespace TerminalApp Windows.Foundation.IReference TabColor { get; }; Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; - Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent); + Microsoft.Terminal.Settings.Model.INewContentArgs GetNewTerminalArgs(Boolean asContent); void Focus(Windows.UI.Xaml.FocusState reason); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 057d2589311..a197c64d2a1 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -114,7 +114,7 @@ Pane::Pane(std::shared_ptr first, // terminal args. // Return Value: // - Arguments appropriate for a SplitPane or NewTab action -NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const +INewContentArgs Pane::GetTerminalArgsForPane(const bool asContent) const { // Leaves are the only things that have controls assert(_IsLeaf()); @@ -1260,7 +1260,6 @@ void Pane::_Focus() { lastContent.Focus(FocusState::Programmatic); } - } // Method Description: diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index f334151e513..d7044c26990 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, const bool asContent = false, const bool asMovePane = false); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const; + winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetTerminalArgsForPane(const bool asContent = false) const; 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); diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index fdab6bf9d02..a261d766777 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -49,9 +49,9 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } - NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const bool /* asContent */) const + INewContentArgs ScratchpadContent::GetNewTerminalArgs(const bool /* asContent */) const { - return nullptr; + return GenericContentArgs(L"scratchpad"); } winrt::hstring ScratchpadContent::Icon() const diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index d6edf3784d8..93527d6b5a0 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -18,7 +18,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::INewContentArgs GetNewTerminalArgs(const bool asContent) 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 5b16b6ae28f..5f9724c890a 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -51,7 +51,7 @@ namespace winrt::TerminalApp::implementation CloseRequested.raise(*this, nullptr); } - NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const bool /* asContent */) const + INewContentArgs SettingsPaneContent::GetNewTerminalArgs(const bool /* asContent */) const { // For now, we're doing a terrible thing in TerminalTab itself to // generate an OpenSettings action manually, without asking for the pane diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index 19fb5b8439d..aab6f1a9144 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -19,7 +19,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 bool asContent) const; + winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(const bool asContent) const; winrt::hstring Title() { return RS_(L"SettingsTab"); } uint64_t TaskbarState() { return 0; } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index e8de73ab7ad..40ea6024749 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -87,7 +87,7 @@ namespace winrt::TerminalApp::implementation return _control.TabColor(); } - NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const + INewContentArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const { NewTerminalArgs args{}; const auto& controlSettings = _control.Settings(); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 7d3a9f5c0da..7cbca955bee 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::INewContentArgs GetNewTerminalArgs(const bool asContent) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); void UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 90cb3fcdaba..654fa136d96 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 "GenericContentArgs.g.cpp" #include "NewTerminalArgs.g.cpp" #include "CopyTextArgs.g.cpp" #include "NewTabArgs.g.cpp" diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index b95fcb5a8bc..e0ceb2c3c68 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 "GenericContentArgs.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,13 +296,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(bool, Handled, false); }; - struct ContentArgsBase : public winrt::implements + struct GenericContentArgs : public GenericContentArgsT { - ContentArgsBase(winrt::hstring type) : + GenericContentArgs(winrt::hstring type) : _Type{ type } {} - ContentArgsBase() : - ContentArgsBase(L"") {} + GenericContentArgs() : + GenericContentArgs(L"") {} ACTION_ARG(winrt::hstring, Type, L""); @@ -324,7 +325,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } INewContentArgs Copy() const { - auto copy{ winrt::make_self() }; + auto copy{ winrt::make_self() }; copy->_Type = _Type; return *copy; } @@ -332,14 +333,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return winrt::hstring{ L"type: " } + Type(); } - static Json::Value ToJson(const winrt::com_ptr& val) + static Json::Value ToJson(const Model::GenericContentArgs& val) { if (!val) { return {}; } + auto args{ get_self(val) }; Json::Value json{ Json::ValueType::objectValue }; - JsonUtils::SetValueForKey(json, TypeKey, val->_Type); + JsonUtils::SetValueForKey(json, TypeKey, args->_Type); return json; } }; @@ -499,7 +501,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // 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), {} }; + return { *winrt::make_self(type), {} }; } static Json::Value ContentArgsToJson(const Model::INewContentArgs& contentArgs) { @@ -510,8 +512,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // 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 ContentArgsBase::ToJson(base); + auto base{ winrt::make_self(contentArgs.Type()) }; + return GenericContentArgs::ToJson(*base); } } @@ -905,6 +907,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation { BASIC_FACTORY(ActionEventArgs); + BASIC_FACTORY(GenericContentArgs); BASIC_FACTORY(CopyTextArgs); BASIC_FACTORY(SwitchToTabArgs); BASIC_FACTORY(NewTerminalArgs); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 0f421d3ec8f..806f14a568d 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -130,6 +130,10 @@ namespace Microsoft.Terminal.Settings.Model String GenerateName(); }; + [default_interface] runtimeclass GenericContentArgs : INewContentArgs{ + GenericContentArgs(); + GenericContentArgs(String type); + }; [default_interface] runtimeclass NewTerminalArgs : INewContentArgs{ NewTerminalArgs(); From 8a37c52e36e69f522810ec9ca1b8878e11f62164 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 21 Mar 2024 06:54:39 -0500 Subject: [PATCH 50/63] this just works, amazing --- src/cascadia/TerminalApp/TerminalPage.cpp | 18 +++++++++--------- .../TerminalSettingsModel/ActionArgs.h | 5 +++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2b6691d04b1..093195b7fa8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3891,6 +3891,15 @@ namespace winrt::TerminalApp::implementation TerminalApp::IPaneContent TerminalPage::_makeSettingsPane() { + 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(); @@ -3925,15 +3934,6 @@ 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 tab auto resultPane = std::make_shared(_makeSettingsPane()); _settingsTab = _CreateNewTabFromPane(resultPane); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index e0ceb2c3c68..2ccca13ba8b 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -505,6 +505,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } 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()); From 3ae152f289cbd0245d7baa37d88bb4ad457f3c8e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 21 Mar 2024 09:29:15 -0500 Subject: [PATCH 51/63] would you look at that? the tests caught something! --- .../TerminalSettingsModel/ActionArgs.h | 3 +- .../UnitTests_SettingsModel/CommandTests.cpp | 27 +- .../DeserializationTests.cpp | 72 ++--- .../KeyBindingsTests.cpp | 49 +++- .../TerminalSettingsTests.cpp | 248 ++++++++++-------- 5 files changed, 239 insertions(+), 160 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 2ccca13ba8b..b5efda58cbd 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -509,7 +509,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return {}; } - // TerminalArgs don't have a type. + // TerminalArgs don't have a type. if (contentArgs.Type().empty()) { return winrt::Microsoft::Terminal::Settings::Model::implementation::NewTerminalArgs::ToJson(contentArgs.try_as()); @@ -561,6 +561,7 @@ struct til::hash_trait(); 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 2425e3421d8..1c24efd55d0 100644 --- a/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp @@ -1375,11 +1375,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."); @@ -1392,11 +1394,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 }; @@ -1406,11 +1410,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 }; @@ -1420,11 +1426,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 }; @@ -1434,11 +1442,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"); @@ -1463,11 +1473,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 8eed7276f1b..b43f8bea045 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"] } ])")); + actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", "index": 0 } , "keys": ["ctrl+shift+c"] } ])")); + 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()); From df73d755414a916fcc73d4d36d52fff4def481fd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 21 Mar 2024 13:51:34 -0500 Subject: [PATCH 52/63] derp --- src/cascadia/TerminalApp/TerminalTab.cpp | 26 +----------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 46f053a1ec7..c5cb7f5b5f2 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1043,7 +1043,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 @@ -1322,30 +1322,6 @@ 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(); From b6e4b62e15e63bec96121a561088e2be295888a5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Mar 2024 11:37:35 -0500 Subject: [PATCH 53/63] Doesn't really need to be projected --- src/cascadia/TerminalApp/ScratchpadContent.cpp | 1 - src/cascadia/TerminalApp/ScratchpadContent.h | 5 +++-- src/cascadia/TerminalApp/TerminalPaneContent.idl | 5 ----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index fd7a8cd6975..dcb13697b54 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "ScratchpadContent.h" -#include "ScratchpadContent.g.cpp" using namespace winrt::Windows::Foundation; using namespace winrt::Windows::UI::Xaml; diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index 995c92419b4..10aa36b6b85 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -2,12 +2,13 @@ // Licensed under the MIT license. #pragma once -#include "ScratchpadContent.g.h" +#include "winrt/TerminalApp.h" namespace winrt::TerminalApp::implementation { - struct ScratchpadContent : ScratchpadContentT + class ScratchpadContent : public winrt::implements { + public: ScratchpadContent(); winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 3820dd74ca1..7e04c8b836c 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -18,9 +18,4 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RestartTerminalRequested; } - - - [default_interface] runtimeclass ScratchpadContent : IPaneContent - { - } } From 1d20599186ddd1ea71acbf18c83c6ce74abd1090 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Mar 2024 11:38:47 -0500 Subject: [PATCH 54/63] un fix this file --- .../Resources/en-US/Resources.resw | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 0e39d8c9f31..03e330d9ecd 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -1,17 +1,17 @@ - @@ -727,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 From 10e1e46945c2c2820da7840187cf14c53fccb86d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Mar 2024 13:55:11 -0500 Subject: [PATCH 55/63] ALSO doesn't really need to be projected --- src/cascadia/TerminalApp/SettingsPaneContent.cpp | 1 - src/cascadia/TerminalApp/SettingsPaneContent.h | 10 +++------- src/cascadia/TerminalApp/TerminalPaneContent.idl | 5 ----- src/cascadia/TerminalApp/TerminalTab.cpp | 1 + 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index 5b16b6ae28f..98dfe917f66 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "SettingsPaneContent.h" -#include "SettingsPaneContent.g.cpp" #include "Utils.h" using namespace winrt::Windows::Foundation; diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h index 19fb5b8439d..5d370ef71d2 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.h +++ b/src/cascadia/TerminalApp/SettingsPaneContent.h @@ -2,13 +2,14 @@ // Licensed under the MIT license. #pragma once -#include "SettingsPaneContent.g.h" +#include "winrt/TerminalApp.h" #include namespace winrt::TerminalApp::implementation { - struct SettingsPaneContent : SettingsPaneContentT + class SettingsPaneContent : public winrt::implements { + public: SettingsPaneContent(winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings settings); void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); @@ -43,8 +44,3 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; }; } - -namespace winrt::TerminalApp::factory_implementation -{ - BASIC_FACTORY(SettingsPaneContent); -} diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index c7feca553bc..60c540273c6 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -18,9 +18,4 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RestartTerminalRequested; } - - [default_interface] runtimeclass SettingsPaneContent : IPaneContent - { - SettingsPaneContent(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); - } } diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 7578309b441..4411cd2a28b 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" From 07562d575c320f87f00add39ad689b32b4a21f35 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 3 Apr 2024 13:52:18 -0500 Subject: [PATCH 56/63] nits --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.idl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 18f03c125d0..d111caa3392 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -245,7 +245,7 @@ namespace winrt::TerminalApp::implementation // Return value: // * True if the args are NewTerminalArgs, and the profile index was out of bounds. // * False otherwise. - bool _shouldBailForInvalidProfileIndex(const CascadiaSettings& settings, const INewContentArgs& args) + static bool _shouldBailForInvalidProfileIndex(const CascadiaSettings& settings, const INewContentArgs& args) { if (!args) { diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 23ee7bcb73e..23343b49716 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -123,7 +123,7 @@ namespace Microsoft.Terminal.Settings.Model }; interface INewContentArgs { - String Type; + String Type { get; }; Boolean Equals(INewContentArgs other); UInt64 Hash(); INewContentArgs Copy(); From 893b25ef3e14c324d814432028316b7fcd08a212 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 4 Apr 2024 15:46:02 -0500 Subject: [PATCH 57/63] this is contrived --- .../TerminalApp/ScratchpadContent.cpp | 2 +- src/cascadia/TerminalApp/ScratchpadContent.h | 50 +++++++++++++++++++ .../TerminalSettingsModel/ActionArgs.cpp | 1 - .../TerminalSettingsModel/ActionArgs.h | 33 ++++++------ .../TerminalSettingsModel/ActionArgs.idl | 5 -- .../KeyBindingsTests.cpp | 4 +- 6 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index 23d90207557..b3a1afa6b92 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -50,7 +50,7 @@ namespace winrt::TerminalApp::implementation INewContentArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const { - return GenericContentArgs(L"scratchpad"); + return *winrt::make_self(L"scratchpad"); } winrt::hstring ScratchpadContent::Icon() const diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index 407773fedd1..9ffb2c48529 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -3,6 +3,7 @@ #pragma once #include "winrt/TerminalApp.h" +#include "../TerminalSettingsModel/HashUtils.h" namespace winrt::TerminalApp::implementation { @@ -42,4 +43,53 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr }; winrt::Windows::UI::Xaml::Controls::TextBox _box{ nullptr }; }; + + struct BaseContentArgs : public winrt::implements + { + BaseContentArgs(winrt::hstring type) : + Type{ type } {} + BaseContentArgs() : + BaseContentArgs(L"") {} + til::property Type; + + static constexpr std::string_view TypeKey{ "type" }; + + public: + bool Equals(winrt::Microsoft::Terminal::Settings::Model::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()); + } + winrt::Microsoft::Terminal::Settings::Model::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 winrt::com_ptr args) + // { + // if (!val) + // { + // return {}; + // } + // // auto args{ get_self(val) }; + // Json::Value json{ Json::ValueType::objectValue }; + // JsonUtils::SetValueForKey(json, TypeKey, args.Type()); + // return json; + // } + }; + } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 4d086d919e6..22cfceb21a2 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -6,7 +6,6 @@ #include "ActionArgs.h" #include "ActionEventArgs.g.cpp" -#include "GenericContentArgs.g.cpp" #include "NewTerminalArgs.g.cpp" #include "CopyTextArgs.g.cpp" #include "NewTabArgs.g.cpp" diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index fb87a151e38..ab07ced93cf 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -6,7 +6,6 @@ // HEY YOU: When adding ActionArgs types, make sure to add the corresponding // *.g.cpp to ActionArgs.cpp! #include "ActionEventArgs.g.h" -#include "GenericContentArgs.g.h" #include "NewTerminalArgs.g.h" #include "CopyTextArgs.g.h" #include "NewTabArgs.g.h" @@ -296,7 +295,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(bool, Handled, false); }; - struct GenericContentArgs : public GenericContentArgsT + struct GenericContentArgs : public winrt::implements { GenericContentArgs(winrt::hstring type) : _Type{ type } {} @@ -333,17 +332,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return winrt::hstring{ L"type: " } + Type(); } - static Json::Value ToJson(const Model::GenericContentArgs& val) - { - if (!val) - { - return {}; - } - auto args{ get_self(val) }; - Json::Value json{ Json::ValueType::objectValue }; - JsonUtils::SetValueForKey(json, TypeKey, args->_Type); - return json; - } + // static Json::Value ToJson(const Model::GenericContentArgs& 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 @@ -522,8 +521,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // 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 GenericContentArgs::ToJson(*base); + // auto base{ winrt::make_self(contentArgs.Type()) }; + // return GenericContentArgs::ToJson(*base); + Json::Value json{ Json::ValueType::objectValue }; + JsonUtils::SetValueForKey(json, "type", contentArgs.Type()); + return json; } } @@ -918,7 +920,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation { BASIC_FACTORY(ActionEventArgs); - BASIC_FACTORY(GenericContentArgs); BASIC_FACTORY(CopyTextArgs); BASIC_FACTORY(SwitchToTabArgs); BASIC_FACTORY(NewTerminalArgs); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 23343b49716..55dac96aee4 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -130,11 +130,6 @@ namespace Microsoft.Terminal.Settings.Model String GenerateName(); }; - [default_interface] runtimeclass GenericContentArgs : INewContentArgs{ - GenericContentArgs(); - GenericContentArgs(String type); - }; - [default_interface] runtimeclass NewTerminalArgs : INewContentArgs{ NewTerminalArgs(); NewTerminalArgs(Int32 profileIndex); diff --git a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp index 1b901276c67..afe22c8930c 100644 --- a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp @@ -166,8 +166,8 @@ namespace SettingsModelUnitTests { 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"] } ])")); - actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", "index": 0 } , "keys": ["ctrl+shift+c"] } ])")); + 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 }; From df04c0775b6fcb930aa6ea97f77b5a59c1687b1e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 4 Apr 2024 15:46:08 -0500 Subject: [PATCH 58/63] Revert "this is contrived" This reverts commit 893b25ef3e14c324d814432028316b7fcd08a212. --- .../TerminalApp/ScratchpadContent.cpp | 2 +- src/cascadia/TerminalApp/ScratchpadContent.h | 50 ------------------- .../TerminalSettingsModel/ActionArgs.cpp | 1 + .../TerminalSettingsModel/ActionArgs.h | 33 ++++++------ .../TerminalSettingsModel/ActionArgs.idl | 5 ++ .../KeyBindingsTests.cpp | 4 +- 6 files changed, 25 insertions(+), 70 deletions(-) diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index b3a1afa6b92..23d90207557 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -50,7 +50,7 @@ namespace winrt::TerminalApp::implementation INewContentArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const { - return *winrt::make_self(L"scratchpad"); + return GenericContentArgs(L"scratchpad"); } winrt::hstring ScratchpadContent::Icon() const diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h index 9ffb2c48529..407773fedd1 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.h +++ b/src/cascadia/TerminalApp/ScratchpadContent.h @@ -3,7 +3,6 @@ #pragma once #include "winrt/TerminalApp.h" -#include "../TerminalSettingsModel/HashUtils.h" namespace winrt::TerminalApp::implementation { @@ -43,53 +42,4 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr }; winrt::Windows::UI::Xaml::Controls::TextBox _box{ nullptr }; }; - - struct BaseContentArgs : public winrt::implements - { - BaseContentArgs(winrt::hstring type) : - Type{ type } {} - BaseContentArgs() : - BaseContentArgs(L"") {} - til::property Type; - - static constexpr std::string_view TypeKey{ "type" }; - - public: - bool Equals(winrt::Microsoft::Terminal::Settings::Model::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()); - } - winrt::Microsoft::Terminal::Settings::Model::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 winrt::com_ptr args) - // { - // if (!val) - // { - // return {}; - // } - // // auto args{ get_self(val) }; - // Json::Value json{ Json::ValueType::objectValue }; - // JsonUtils::SetValueForKey(json, TypeKey, args.Type()); - // return json; - // } - }; - } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 22cfceb21a2..4d086d919e6 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 "GenericContentArgs.g.cpp" #include "NewTerminalArgs.g.cpp" #include "CopyTextArgs.g.cpp" #include "NewTabArgs.g.cpp" diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index ab07ced93cf..fb87a151e38 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 "GenericContentArgs.g.h" #include "NewTerminalArgs.g.h" #include "CopyTextArgs.g.h" #include "NewTabArgs.g.h" @@ -295,7 +296,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(bool, Handled, false); }; - struct GenericContentArgs : public winrt::implements + struct GenericContentArgs : public GenericContentArgsT { GenericContentArgs(winrt::hstring type) : _Type{ type } {} @@ -332,17 +333,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return winrt::hstring{ L"type: " } + Type(); } - // static Json::Value ToJson(const Model::GenericContentArgs& val) - // { - // if (!val) - // { - // return {}; - // } - // auto args{ get_self(val) }; - // Json::Value json{ Json::ValueType::objectValue }; - // JsonUtils::SetValueForKey(json, TypeKey, args->_Type); - // return json; - // } + static Json::Value ToJson(const Model::GenericContentArgs& 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 @@ -521,11 +522,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // 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 GenericContentArgs::ToJson(*base); - Json::Value json{ Json::ValueType::objectValue }; - JsonUtils::SetValueForKey(json, "type", contentArgs.Type()); - return json; + auto base{ winrt::make_self(contentArgs.Type()) }; + return GenericContentArgs::ToJson(*base); } } @@ -920,6 +918,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation { BASIC_FACTORY(ActionEventArgs); + BASIC_FACTORY(GenericContentArgs); BASIC_FACTORY(CopyTextArgs); BASIC_FACTORY(SwitchToTabArgs); BASIC_FACTORY(NewTerminalArgs); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 55dac96aee4..23343b49716 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -130,6 +130,11 @@ namespace Microsoft.Terminal.Settings.Model String GenerateName(); }; + [default_interface] runtimeclass GenericContentArgs : INewContentArgs{ + GenericContentArgs(); + GenericContentArgs(String type); + }; + [default_interface] runtimeclass NewTerminalArgs : INewContentArgs{ NewTerminalArgs(); NewTerminalArgs(Int32 profileIndex); diff --git a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp index afe22c8930c..1b901276c67 100644 --- a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp @@ -166,8 +166,8 @@ namespace SettingsModelUnitTests { 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); + actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", } , "keys": ["ctrl+c"] } ])")); + actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", "index": 0 } , "keys": ["ctrl+shift+c"] } ])")); VERIFY_ARE_EQUAL(2u, actionMap->_ActionMap.size()); KeyChord ctrlC{ VirtualKeyModifiers::Control, static_cast('C'), 0 }; From 3d48ef43af104485b8de582e7953ea1034e8a257 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 4 Apr 2024 15:59:16 -0500 Subject: [PATCH 59/63] fix some broken tests --- .../CommandlineTest.cpp | 593 ++++++++++-------- .../LocalTests_TerminalApp/SettingsTests.cpp | 508 ++++++++------- .../TerminalApp/ScratchpadContent.cpp | 2 +- .../TerminalSettingsModel/ActionArgs.cpp | 2 +- .../TerminalSettingsModel/ActionArgs.h | 24 +- .../TerminalSettingsModel/ActionArgs.idl | 6 +- 6 files changed, 608 insertions(+), 527 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/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp index 23d90207557..16346c9b038 100644 --- a/src/cascadia/TerminalApp/ScratchpadContent.cpp +++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp @@ -50,7 +50,7 @@ namespace winrt::TerminalApp::implementation INewContentArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const { - return GenericContentArgs(L"scratchpad"); + return BaseContentArgs(L"scratchpad"); } winrt::hstring ScratchpadContent::Icon() const diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 4d086d919e6..24f4e445953 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -6,7 +6,7 @@ #include "ActionArgs.h" #include "ActionEventArgs.g.cpp" -#include "GenericContentArgs.g.cpp" +#include "BaseContentArgs.g.cpp" #include "NewTerminalArgs.g.cpp" #include "CopyTextArgs.g.cpp" #include "NewTabArgs.g.cpp" diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index fb87a151e38..bf34a6af140 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -6,7 +6,7 @@ // HEY YOU: When adding ActionArgs types, make sure to add the corresponding // *.g.cpp to ActionArgs.cpp! #include "ActionEventArgs.g.h" -#include "GenericContentArgs.g.h" +#include "BaseContentArgs.g.h" #include "NewTerminalArgs.g.h" #include "CopyTextArgs.g.h" #include "NewTabArgs.g.h" @@ -296,13 +296,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(bool, Handled, false); }; - struct GenericContentArgs : public GenericContentArgsT + struct BaseContentArgs : public BaseContentArgsT { - GenericContentArgs(winrt::hstring type) : + BaseContentArgs(winrt::hstring type) : _Type{ type } {} - GenericContentArgs() : - GenericContentArgs(L"") {} + BaseContentArgs() : + BaseContentArgs(L"") {} ACTION_ARG(winrt::hstring, Type, L""); @@ -325,7 +325,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } INewContentArgs Copy() const { - auto copy{ winrt::make_self() }; + auto copy{ winrt::make_self() }; copy->_Type = _Type; return *copy; } @@ -333,13 +333,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return winrt::hstring{ L"type: " } + Type(); } - static Json::Value ToJson(const Model::GenericContentArgs& val) + static Json::Value ToJson(const Model::BaseContentArgs& val) { if (!val) { return {}; } - auto args{ get_self(val) }; + auto args{ get_self(val) }; Json::Value json{ Json::ValueType::objectValue }; JsonUtils::SetValueForKey(json, TypeKey, args->_Type); return json; @@ -506,7 +506,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // 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), {} }; + return { *winrt::make_self(type), {} }; } static Json::Value ContentArgsToJson(const Model::INewContentArgs& contentArgs) { @@ -522,8 +522,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // 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 GenericContentArgs::ToJson(*base); + auto base{ winrt::make_self(contentArgs.Type()) }; + return BaseContentArgs::ToJson(*base); } } @@ -918,7 +918,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation { BASIC_FACTORY(ActionEventArgs); - BASIC_FACTORY(GenericContentArgs); + 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 23343b49716..a835544abc8 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -130,9 +130,9 @@ namespace Microsoft.Terminal.Settings.Model String GenerateName(); }; - [default_interface] runtimeclass GenericContentArgs : INewContentArgs{ - GenericContentArgs(); - GenericContentArgs(String type); + [default_interface] runtimeclass BaseContentArgs : INewContentArgs{ + BaseContentArgs(); + BaseContentArgs(String type); }; [default_interface] runtimeclass NewTerminalArgs : INewContentArgs{ From e5ebbb016e03bd04acf8873a023c8f59a8cb531f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 4 Apr 2024 16:42:14 -0500 Subject: [PATCH 60/63] god VS just save the file --- src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp index 1b901276c67..afe22c8930c 100644 --- a/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/KeyBindingsTests.cpp @@ -166,8 +166,8 @@ namespace SettingsModelUnitTests { 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"] } ])")); - actionMap->LayerJson(VerifyParseSucceeded(R"([ { "command": { "action": "newTab", "index": 0 } , "keys": ["ctrl+shift+c"] } ])")); + 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 }; From 162eb8360fd73f06acc4be3f9b092abd0a218621 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 4 Apr 2024 16:51:38 -0500 Subject: [PATCH 61/63] fixes #16914 --- .../TerminalApp/SettingsPaneContent.cpp | 5 +--- src/cascadia/TerminalApp/TerminalTab.cpp | 23 +------------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp index 8c1f2c57f5f..7e48ae9f6be 100644 --- a/src/cascadia/TerminalApp/SettingsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp @@ -52,10 +52,7 @@ namespace winrt::TerminalApp::implementation 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/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 9c99b44afaf..7bcbccb374e 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -447,30 +447,9 @@ 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 = _rootPane->BuildStartupActions(0, 1, kind); - 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) }; From 309e6a74666a4a43323350a88734dc1122b37553 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 5 Apr 2024 06:19:16 -0500 Subject: [PATCH 62/63] nits --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 5 ++--- src/cascadia/TerminalApp/TerminalPage.cpp | 8 +++++--- src/cascadia/TerminalApp/TerminalPage.h | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.idl | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index d111caa3392..2fd6680574f 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -912,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 {}", - newContentArgs ? terminalArgs.ToCommandline().c_str() : - L"") + terminalArgs.ToCommandline().c_str()) }; // Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we @@ -953,7 +952,7 @@ namespace winrt::TerminalApp::implementation // profile). if (!newContentArgs) { - newContentArgs = NewTerminalArgs(); + newContentArgs = NewTerminalArgs{}; } if (const auto& terminalArgs{ newContentArgs.try_as() }) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 432a6158d9d..0d5ed81d2b7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3214,9 +3214,11 @@ namespace winrt::TerminalApp::implementation } else if (paneType == L"settings") { - content = _makeSettingsPane(); + content = _makeSettingsContent(); } + assert(content); + return std::make_shared(content); } @@ -3872,7 +3874,7 @@ namespace winrt::TerminalApp::implementation CATCH_RETURN() } - TerminalApp::IPaneContent TerminalPage::_makeSettingsPane() + TerminalApp::IPaneContent TerminalPage::_makeSettingsContent() { if (auto app{ winrt::Windows::UI::Xaml::Application::Current().try_as() }) { @@ -3918,7 +3920,7 @@ namespace winrt::TerminalApp::implementation if (!_settingsTab) { // Create the tab - auto resultPane = std::make_shared(_makeSettingsPane()); + 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 6da4c19ea15..1fd2acfaee6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -435,7 +435,7 @@ 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); - TerminalApp::IPaneContent _makeSettingsPane(); + 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); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index a835544abc8..05ed70069a9 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -130,12 +130,12 @@ namespace Microsoft.Terminal.Settings.Model String GenerateName(); }; - [default_interface] runtimeclass BaseContentArgs : INewContentArgs{ + runtimeclass BaseContentArgs : [default] INewContentArgs { BaseContentArgs(); BaseContentArgs(String type); }; - [default_interface] runtimeclass NewTerminalArgs : INewContentArgs{ + runtimeclass NewTerminalArgs : INewContentArgs { NewTerminalArgs(); NewTerminalArgs(Int32 profileIndex); From 2fa426694172118e277f7e074869178f0181aecc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 5 Apr 2024 08:49:19 -0500 Subject: [PATCH 63/63] weaks --- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 0d5ed81d2b7..d73a3678418 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3208,7 +3208,7 @@ namespace winrt::TerminalApp::implementation // 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 }); + scratchPane->GetRoot().KeyDown({ get_weak(), &TerminalPage::_KeyDownHandler }); content = *scratchPane; } @@ -3895,7 +3895,7 @@ namespace winrt::TerminalApp::implementation } // GH#8767 - let unhandled keys in the SUI try to run commands too. - sui.KeyDown({ this, &TerminalPage::_KeyDownHandler }); + 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() })