diff --git a/src/cascadia/TerminalApp/FilteredCommand.cpp b/src/cascadia/TerminalApp/FilteredCommand.cpp index 98a7b53de4b..d6c6c38a284 100644 --- a/src/cascadia/TerminalApp/FilteredCommand.cpp +++ b/src/cascadia/TerminalApp/FilteredCommand.cpp @@ -21,11 +21,22 @@ namespace winrt::TerminalApp::implementation { // This class is a wrapper of PaletteItem, that is used as an item of a filterable list in CommandPalette. // It manages a highlighted text that is computed by matching search filter characters to item name - FilteredCommand::FilteredCommand(const winrt::TerminalApp::PaletteItem& item) : - _Item(item), - _Filter(L""), - _Weight(0) + FilteredCommand::FilteredCommand(const winrt::TerminalApp::PaletteItem& item) { + // Actually implement the ctor in _constructFilteredCommand + _constructFilteredCommand(item); + } + + // We need to actually implement the ctor in a separate helper. This is + // because we have a FilteredTask class which derives from FilteredCommand. + // HOWEVER, for cppwinrt ~ r e a s o n s ~, it doesn't actually derive from + // FilteredCommand directly, so we can't just use the FilteredCommand ctor + // directly in the base class. + void FilteredCommand::_constructFilteredCommand(const winrt::TerminalApp::PaletteItem& item) + { + _Item = item; + _Filter = L""; + _Weight = 0; _HighlightedName = _computeHighlightedName(); // Recompute the highlighted name if the item name changes diff --git a/src/cascadia/TerminalApp/FilteredCommand.h b/src/cascadia/TerminalApp/FilteredCommand.h index e5df7bb018a..f304ad032a3 100644 --- a/src/cascadia/TerminalApp/FilteredCommand.h +++ b/src/cascadia/TerminalApp/FilteredCommand.h @@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation FilteredCommand() = default; FilteredCommand(const winrt::TerminalApp::PaletteItem& item); - void UpdateFilter(const winrt::hstring& filter); + virtual void UpdateFilter(const winrt::hstring& filter); static int Compare(const winrt::TerminalApp::FilteredCommand& first, const winrt::TerminalApp::FilteredCommand& second); @@ -29,6 +29,9 @@ namespace winrt::TerminalApp::implementation WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, PropertyChanged.raise); WINRT_OBSERVABLE_PROPERTY(int, Weight, PropertyChanged.raise); + protected: + void _constructFilteredCommand(const winrt::TerminalApp::PaletteItem& item); + private: winrt::TerminalApp::HighlightedText _computeHighlightedName(); int _computeWeight(); diff --git a/src/cascadia/TerminalApp/FilteredCommand.idl b/src/cascadia/TerminalApp/FilteredCommand.idl index ce2b04b92c1..a63e6e81100 100644 --- a/src/cascadia/TerminalApp/FilteredCommand.idl +++ b/src/cascadia/TerminalApp/FilteredCommand.idl @@ -6,7 +6,7 @@ import "HighlightedTextControl.idl"; namespace TerminalApp { - [default_interface] runtimeclass FilteredCommand : Windows.UI.Xaml.Data.INotifyPropertyChanged + [default_interface] unsealed runtimeclass FilteredCommand : Windows.UI.Xaml.Data.INotifyPropertyChanged { FilteredCommand(); FilteredCommand(PaletteItem item); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index eb153ce1ed8..96667a60fa1 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -2944,7 +2944,8 @@ void Pane::FinalizeConfigurationGivenDefault() // - Returns true if the pane or one of its descendants is read-only bool Pane::ContainsReadOnly() const { - return _IsLeaf() ? _content.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly()); + return _IsLeaf() ? (_content == nullptr ? false : _content.ReadOnly()) : + (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly()); } // Method Description: diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index efe3e1278e2..95babb6e4a6 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -907,6 +907,28 @@ Restart the active pane connection + + Snippets + Header for a page that includes small "snippets" of text for the user to enter + + + Filter snippets... + Placeholder text for a text box to filter snippets (on the same page as the 'SnippetPaneTitle') + + + Input this command + + + Input this command + + + No snippets found in your settings. + Text shown to user when no snippets are found in their settings + + + Add some "Send input" actions in your settings to have them show up here. + Additional information presented to the user to let them know they can add a "Send input" action in their setting to populate the snippets pane + Action saved diff --git a/src/cascadia/TerminalApp/SnippetsPaneContent.cpp b/src/cascadia/TerminalApp/SnippetsPaneContent.cpp new file mode 100644 index 00000000000..7e8c1695543 --- /dev/null +++ b/src/cascadia/TerminalApp/SnippetsPaneContent.cpp @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SnippetsPaneContent.h" +#include "SnippetsPaneContent.g.cpp" +#include "FilteredTask.g.cpp" +#include "Utils.h" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::System; +using namespace winrt::Microsoft::Terminal::Settings; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +namespace winrt +{ + namespace WUX = Windows::UI::Xaml; + namespace MUX = Microsoft::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} + +namespace winrt::TerminalApp::implementation +{ + SnippetsPaneContent::SnippetsPaneContent() + { + InitializeComponent(); + + WUX::Automation::AutomationProperties::SetName(*this, RS_(L"SnippetPaneTitle/Text")); + } + + void SnippetsPaneContent::_updateFilteredCommands() + { + const auto& queryString = _filterBox().Text(); + + // DON'T replace the itemSource here. If you do, it'll un-expand all the + // nested items the user has expanded. Instead, just update the filter. + // That'll also trigger a PropertyChanged for the Visibility property. + for (const auto& t : _allTasks) + { + auto impl = winrt::get_self(t); + impl->UpdateFilter(queryString); + } + } + + void SnippetsPaneContent::UpdateSettings(const CascadiaSettings& settings) + { + _settings = settings; + + // You'd think that `FilterToSendInput(queryString)` would work. It + // doesn't! That uses the queryString as the current command the user + // has typed, then relies on the suggestions UI to _also_ filter with that + // string. + + const auto tasks = _settings.GlobalSettings().ActionMap().FilterToSendInput(winrt::hstring{}); // IVector + _allTasks = winrt::single_threaded_observable_vector(); + for (const auto& t : tasks) + { + const auto& filtered{ winrt::make(t) }; + _allTasks.Append(filtered); + } + _treeView().ItemsSource(_allTasks); + + _updateFilteredCommands(); + + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"HasSnippets" }); + } + + bool SnippetsPaneContent::HasSnippets() const + { + return _allTasks.Size() != 0; + } + + void SnippetsPaneContent::_filterTextChanged(const IInspectable& /*sender*/, + const Windows::UI::Xaml::RoutedEventArgs& /*args*/) + { + _updateFilteredCommands(); + } + + winrt::Windows::UI::Xaml::FrameworkElement SnippetsPaneContent::GetRoot() + { + return *this; + } + winrt::Windows::Foundation::Size SnippetsPaneContent::MinimumSize() + { + return { 200, 200 }; + } + void SnippetsPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason) + { + _filterBox().Focus(reason); + } + void SnippetsPaneContent::Close() + { + CloseRequested.raise(*this, nullptr); + } + + INewContentArgs SnippetsPaneContent::GetNewTerminalArgs(BuildStartupKind /*kind*/) const + { + return BaseContentArgs(L"snippets"); + } + + winrt::hstring SnippetsPaneContent::Icon() const + { + static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote + return winrt::hstring{ glyph }; + } + + winrt::WUX::Media::Brush SnippetsPaneContent::BackgroundBrush() + { + static const auto key = winrt::box_value(L"UnfocusedBorderBrush"); + return ThemeLookup(WUX::Application::Current().Resources(), + _settings.GlobalSettings().CurrentTheme().RequestedTheme(), + key) + .try_as(); + } + + void SnippetsPaneContent::SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control) + { + _control = control; + } + + void SnippetsPaneContent::_runCommand(const Microsoft::Terminal::Settings::Model::Command& command) + { + if (const auto& strongControl{ _control.get() }) + { + // By using the last active control as the sender here, the + // action dispatch will send this to the active control, + // thinking that it is the control that requested this event. + strongControl.Focus(winrt::WUX::FocusState::Programmatic); + DispatchCommandRequested.raise(strongControl, command); + } + } + + void SnippetsPaneContent::_runCommandButtonClicked(const Windows::Foundation::IInspectable& sender, + const Windows::UI::Xaml::RoutedEventArgs&) + { + if (const auto& taskVM{ sender.try_as().DataContext().try_as() }) + { + _runCommand(taskVM->Command()); + } + } + + // Called when one of the items in the list is tapped, or enter/space is + // pressed on it while focused. Notably, this isn't the Tapped event - it + // isn't called when the user clicks the dropdown arrow (that does usually + // also trigger a Tapped). + // + // We'll use this to toggle the expanded state of nested items, since the + // tree view arrow is so little + void SnippetsPaneContent::_treeItemInvokedHandler(const IInspectable& /*sender*/, + const MUX::Controls::TreeViewItemInvokedEventArgs& e) + { + // The InvokedItem here is the item in the data collection that was + // bound itself. + if (const auto& taskVM{ e.InvokedItem().try_as() }) + { + if (taskVM->HasChildren()) + { + // We then need to find the actual TreeViewItem for that + // FilteredTask. + if (const auto& item{ _treeView().ContainerFromItem(*taskVM).try_as() }) + { + item.IsExpanded(!item.IsExpanded()); + } + } + } + } + + // Raised on individual TreeViewItems. We'll use this event to send the + // input on an Enter/Space keypress, when a leaf item is selected. + void SnippetsPaneContent::_treeItemKeyUpHandler(const IInspectable& sender, + const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e) + { + const auto& item{ sender.try_as() }; + if (!item) + { + return; + } + const auto& taskVM{ item.DataContext().try_as() }; + if (!taskVM || taskVM->HasChildren()) + { + return; + } + + const auto& key = e.OriginalKey(); + if (key == VirtualKey::Enter || key == VirtualKey::Space) + { + if (const auto& button = e.OriginalSource().try_as()) + { + // Let the button handle the Enter key so an eventually attached click handler will be called + e.Handled(false); + return; + } + + _runCommand(taskVM->Command()); + e.Handled(true); + } + } + +} diff --git a/src/cascadia/TerminalApp/SnippetsPaneContent.h b/src/cascadia/TerminalApp/SnippetsPaneContent.h new file mode 100644 index 00000000000..493121c9150 --- /dev/null +++ b/src/cascadia/TerminalApp/SnippetsPaneContent.h @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "SnippetsPaneContent.g.h" +#include "FilteredTask.g.h" +#include "FilteredCommand.h" +#include "ActionPaletteItem.h" +#include + +namespace winrt::TerminalApp::implementation +{ + struct SnippetsPaneContent : SnippetsPaneContentT + { + SnippetsPaneContent(); + + winrt::Windows::UI::Xaml::FrameworkElement GetRoot(); + + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + + winrt::Windows::Foundation::Size MinimumSize(); + void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic); + void Close(); + winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const; + + winrt::hstring Title() { return RS_(L"SnippetPaneTitle/Text"); } + uint64_t TaskbarState() { return 0; } + uint64_t TaskbarProgress() { return 0; } + bool ReadOnly() { return false; } + winrt::hstring Icon() const; + Windows::Foundation::IReference TabColor() const noexcept { return nullptr; } + winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush(); + + void SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control); + bool HasSnippets() const; + + til::typed_event<> ConnectionStateChanged; + til::typed_event CloseRequested; + til::typed_event BellRequested; + til::typed_event TitleChanged; + til::typed_event TabColorChanged; + til::typed_event TaskbarProgressChanged; + til::typed_event ReadOnlyChanged; + til::typed_event FocusRequested; + + til::typed_event DispatchCommandRequested; + + til::property_changed_event PropertyChanged; + + private: + friend struct SnippetsPaneContentT; // for Xaml to bind events + + winrt::weak_ref _control{ nullptr }; + winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; + winrt::Windows::Foundation::Collections::IObservableVector _allTasks{ nullptr }; + + void _runCommandButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&); + void _filterTextChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); + + void _updateFilteredCommands(); + void _treeItemInvokedHandler(const Windows::Foundation::IInspectable& sender, const Microsoft::UI::Xaml::Controls::TreeViewItemInvokedEventArgs& e); + void _treeItemKeyUpHandler(const IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); + void _runCommand(const Microsoft::Terminal::Settings::Model::Command& command); + }; + + struct FilteredTask : FilteredTaskT + { + FilteredTask() = default; + + FilteredTask(const winrt::Microsoft::Terminal::Settings::Model::Command& command) + { + _filteredCommand = winrt::make_self(winrt::make(command, winrt::hstring{})); + _command = command; + + // The Children() method must always return a non-null vector + _children = winrt::single_threaded_observable_vector(); + if (_command.HasNestedCommands()) + { + for (const auto& [_, child] : _command.NestedCommands()) + { + auto vm{ winrt::make(child) }; + _children.Append(vm); + } + } + } + + void UpdateFilter(const winrt::hstring& filter) + { + _filteredCommand->UpdateFilter(filter); + for (const auto& c : _children) + { + auto impl = winrt::get_self(c); + impl->UpdateFilter(filter); + } + + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Visibility" }); + } + + winrt::hstring Input() + { + if (const auto& actionItem{ _filteredCommand->Item().try_as() }) + { + if (const auto& command{ actionItem.Command() }) + { + if (const auto& sendInput{ command.ActionAndArgs().Args().try_as() }) + { + return winrt::hstring{ til::visualize_nonspace_control_codes(sendInput.Input().c_str()) }; + } + } + } + return winrt::hstring{}; + }; + + winrt::Windows::Foundation::Collections::IObservableVector Children() { return _children; } + bool HasChildren() { return _children.Size() > 0; } + winrt::Microsoft::Terminal::Settings::Model::Command Command() { return _command; } + winrt::TerminalApp::FilteredCommand FilteredCommand() { return *_filteredCommand; } + + int32_t Row() { return HasChildren() ? 2 : 1; } // See the BODGY comment in the .XAML for explanation + + // Used to control if this item is visible in the TreeView. Turns out, + // TreeView is in fact sane enough to remove items entirely if they're + // Collapsed. + winrt::Windows::UI::Xaml::Visibility Visibility() + { + // Is there no filter, or do we match it? + if (_filteredCommand->Filter().empty() || _filteredCommand->Weight() > 0) + { + return winrt::Windows::UI::Xaml::Visibility::Visible; + } + // If we don't match, maybe one of our children does + auto totalWeight = _filteredCommand->Weight(); + for (const auto& c : _children) + { + auto impl = winrt::get_self(c); + totalWeight += impl->_filteredCommand->Weight(); + } + + return totalWeight > 0 ? winrt::Windows::UI::Xaml::Visibility::Visible : winrt::Windows::UI::Xaml::Visibility::Collapsed; + }; + + til::property_changed_event PropertyChanged; + + private: + winrt::Microsoft::Terminal::Settings::Model::Command _command{ nullptr }; + winrt::com_ptr _filteredCommand{ nullptr }; + winrt::Windows::Foundation::Collections::IObservableVector _children{ nullptr }; + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(SnippetsPaneContent); +} diff --git a/src/cascadia/TerminalApp/SnippetsPaneContent.xaml b/src/cascadia/TerminalApp/SnippetsPaneContent.xaml new file mode 100644 index 00000000000..a887b72b757 --- /dev/null +++ b/src/cascadia/TerminalApp/SnippetsPaneContent.xaml @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #282828 + #90ef90 + #8888 + + + #F9F9F9 + #257f01 + #88222222 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 238fecf9afc..8549c32cd40 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -71,6 +71,9 @@ Designer + + Designer + @@ -161,6 +164,9 @@ TerminalPaneContent.idl + + SnippetsPaneContent.xaml + TerminalPaneContent.idl @@ -274,6 +280,9 @@ TerminalPaneContent.idl + + SnippetsPaneContent.xaml + TerminalPaneContent.idl @@ -352,7 +361,10 @@ - + + TaskPaneContent.xaml + Code + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index aa0bf06cf23..0e3dd5256bc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -16,6 +16,7 @@ #include "DebugTapConnection.h" #include "SettingsPaneContent.h" #include "ScratchpadContent.h" +#include "SnippetsPaneContent.h" #include "TabRowControl.h" #include "TerminalPage.g.cpp" @@ -451,10 +452,10 @@ namespace winrt::TerminalApp::implementation // - command - command to dispatch // Return Value: // - - void TerminalPage::_OnDispatchCommandRequested(const IInspectable& /*sender*/, const Microsoft::Terminal::Settings::Model::Command& command) + void TerminalPage::_OnDispatchCommandRequested(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::Command& command) { const auto& actionAndArgs = command.ActionAndArgs(); - _actionDispatch->DoAction(actionAndArgs); + _actionDispatch->DoAction(sender, actionAndArgs); } // Method Description: @@ -620,9 +621,12 @@ namespace winrt::TerminalApp::implementation // GH#6586: now that we're done processing all startup commands, // focus the active control. This will work as expected for both // commandline invocations and for `wt` action invocations. - if (const auto control = _GetActiveControl()) + if (const auto& terminalTab{ _GetFocusedTabImpl() }) { - control.Focus(FocusState::Programmatic); + if (const auto& content{ terminalTab->GetActiveContent() }) + { + content.Focus(FocusState::Programmatic); + } } } if (initial) @@ -2466,16 +2470,16 @@ namespace winrt::TerminalApp::implementation } _UnZoomIfNeeded(); - auto [original, _] = activeTab->SplitPane(*realSplitType, splitSize, newPane); + auto [original, newGuy] = activeTab->SplitPane(*realSplitType, splitSize, newPane); // After GH#6586, the control will no longer focus itself // automatically when it's finished being laid out. Manually focus // the control here instead. if (_startupState == StartupState::Initialized) { - if (const auto control = _GetActiveControl()) + if (const auto& content{ newGuy->GetContent() }) { - control.Focus(FocusState::Programmatic); + content.Focus(FocusState::Programmatic); } } } @@ -3270,6 +3274,8 @@ namespace winrt::TerminalApp::implementation return resultPane; } + // NOTE: callers of _MakePane should be able to accept nullptr as a return + // value gracefully. std::shared_ptr TerminalPage::_MakePane(const INewContentArgs& contentArgs, const winrt::TerminalApp::TabBase& sourceTab, TerminalConnection::ITerminalConnection existingConnection) @@ -3300,6 +3306,36 @@ namespace winrt::TerminalApp::implementation { content = _makeSettingsContent(); } + else if (paneType == L"snippets") + { + // Prevent the user from opening a bunch of snippets panes. + // + // Look at the focused tab, and if it already has one, then just focus it. + const bool found = _GetFocusedTab().try_as()->GetRootPane()->WalkTree([](const auto& p) -> bool { + if (const auto& snippets{ p->GetContent().try_as() }) + { + snippets->Focus(FocusState::Programmatic); + return true; + } + return false; + }); + // Bail out if we already found one. + if (found) + { + return nullptr; + } + + const auto& tasksContent{ winrt::make_self() }; + tasksContent->UpdateSettings(_settings); + tasksContent->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler }); + tasksContent->DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested }); + if (const auto& termControl{ _GetActiveControl() }) + { + tasksContent->SetLastActiveControl(termControl); + } + + content = *tasksContent; + } assert(content); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 0e4738fff42..255ddef547b 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -3,6 +3,7 @@ import "IPaneContent.idl"; import "TerminalSettingsCache.idl"; +import "FilteredCommand.idl"; namespace TerminalApp { @@ -16,4 +17,22 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RestartTerminalRequested; } + + [default_interface] runtimeclass FilteredTask : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + String Input{ get; }; + Windows.Foundation.Collections.IObservableVector Children { get; }; + Boolean HasChildren { get; }; + Int32 Row { get; }; + Windows.UI.Xaml.Visibility Visibility { get; }; + TerminalApp.FilteredCommand FilteredCommand{ get; }; + } + + [default_interface] runtimeclass SnippetsPaneContent : Windows.UI.Xaml.Controls.UserControl, IPaneContent, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + SnippetsPaneContent(); + void SetLastActiveControl(Microsoft.Terminal.Control.TermControl control); + Boolean HasSnippets { get; }; + event Windows.Foundation.TypedEventHandler DispatchCommandRequested; + } } diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 72995e29bac..961df655dd8 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -525,7 +525,7 @@ namespace winrt::TerminalApp::implementation // - pane: The new pane to add to the tree of panes; note that this pane // could itself be a parent pane/the root node of a tree of panes // Return Value: - // - + // - a pair of (the Pane that now holds the original content, the new Pane in the tree) std::pair, std::shared_ptr> TerminalTab::SplitPane(SplitDirection splitType, const float splitSize, std::shared_ptr pane) @@ -1223,6 +1223,20 @@ namespace winrt::TerminalApp::implementation // Raise our own ActivePaneChanged event. ActivePaneChanged.raise(*this, nullptr); + + // If the new active pane is a terminal, tell other interested panes + // what the new active pane is. + const auto content{ pane->GetContent() }; + if (const auto termContent{ content.try_as() }) + { + const auto& termControl{ termContent.GetTermControl() }; + _rootPane->WalkTree([termControl](const auto& p) { + if (const auto& taskPane{ p->GetContent().try_as() }) + { + taskPane.SetLastActiveControl(termControl); + } + }); + } } // Method Description: diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index b65b7eabcd4..f4b39abc297 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -126,6 +126,10 @@ Split pane + + Open snippets pane + This will open a pane with a list of the users's saved commands ("snippets") in it + Terminal (Portable) This display name is used when the Terminal application is running in a "portable" mode, where settings are not stored in a shared location. diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 1a3e7c6c111..f6713e265b0 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -446,6 +446,7 @@ { "command": "quit", "id": "Terminal.Quit" }, { "command": "restoreLastClosed", "id": "Terminal.RestoreLastClosed" }, { "command": "openAbout", "id": "Terminal.OpenAboutDialog" }, + { "command": "experimental.openTasks", "id": "Terminal.OpenTasks" }, // Tab Management // "command": "closeTab" is unbound by default. @@ -528,6 +529,7 @@ { "command": { "action": "movePane", "index": 8 }, "id": "Terminal.MovePaneToTab8" }, { "command": { "action": "movePane", "window": "new" }, "id": "Terminal.MovePaneToNewWindow" }, { "command": "restartConnection", "id": "Terminal.RestartConnection" }, + { "command": { "action": "splitPane", "type": "snippets" }, "id": "Terminal.OpenSnippetsPane", "name": { "key": "SnippetsPaneCommandName" } }, // Clipboard Integration { "command": { "action": "copy", "singleLine": false }, "id": "Terminal.CopyToClipboard" }, diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index 17c720614b1..fa1575d32ae 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -133,7 +133,7 @@ public: \ _##name = value; \ } \ \ -private: \ +protected: \ type _##name{ __VA_ARGS__ }; // Use this macro to quickly implement both the getter and setter for an @@ -158,7 +158,7 @@ public: } \ }; \ \ -private: \ +protected: \ type _##name{ __VA_ARGS__ }; \ void _set##name(const type& value) \ { \