Skip to content

Commit e321c00

Browse files
committed
PRE-MERGE microsoft#17330 Add a snippets pane
2 parents b968f91 + 5fd1100 commit e321c00

18 files changed

+602
-11
lines changed

src/cascadia/TerminalApp/AppActionHandlers.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,4 +1521,5 @@ namespace winrt::TerminalApp::implementation
15211521
_ShowAboutDialog();
15221522
args.Handled(true);
15231523
}
1524+
15241525
}

src/cascadia/TerminalApp/FilteredCommand.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,22 @@ namespace winrt::TerminalApp::implementation
2121
{
2222
// This class is a wrapper of PaletteItem, that is used as an item of a filterable list in CommandPalette.
2323
// It manages a highlighted text that is computed by matching search filter characters to item name
24-
FilteredCommand::FilteredCommand(const winrt::TerminalApp::PaletteItem& item) :
25-
_Item(item),
26-
_Filter(L""),
27-
_Weight(0)
24+
FilteredCommand::FilteredCommand(const winrt::TerminalApp::PaletteItem& item)
2825
{
26+
// Actually implement the ctor in _constructFilteredCommand
27+
_constructFilteredCommand(item);
28+
}
29+
30+
// We need to actually implement the ctor in a separate helper. This is
31+
// because we have a FilteredTask class which derives from FilteredCommand.
32+
// HOWEVER, for cppwinrt ~ r e a s o n s ~, it doesn't actually derive from
33+
// FilteredCommand directly, so we can't just use the FilteredCommand ctor
34+
// directly in the base class.
35+
void FilteredCommand::_constructFilteredCommand(const winrt::TerminalApp::PaletteItem& item)
36+
{
37+
_Item = item;
38+
_Filter = L"";
39+
_Weight = 0;
2940
_HighlightedName = _computeHighlightedName();
3041

3142
// Recompute the highlighted name if the item name changes

src/cascadia/TerminalApp/FilteredCommand.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation
1919
FilteredCommand() = default;
2020
FilteredCommand(const winrt::TerminalApp::PaletteItem& item);
2121

22-
void UpdateFilter(const winrt::hstring& filter);
22+
virtual void UpdateFilter(const winrt::hstring& filter);
2323

2424
static int Compare(const winrt::TerminalApp::FilteredCommand& first, const winrt::TerminalApp::FilteredCommand& second);
2525

@@ -29,6 +29,9 @@ namespace winrt::TerminalApp::implementation
2929
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, PropertyChanged.raise);
3030
WINRT_OBSERVABLE_PROPERTY(int, Weight, PropertyChanged.raise);
3131

32+
protected:
33+
void _constructFilteredCommand(const winrt::TerminalApp::PaletteItem& item);
34+
3235
private:
3336
winrt::TerminalApp::HighlightedText _computeHighlightedName();
3437
int _computeWeight();

src/cascadia/TerminalApp/FilteredCommand.idl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import "HighlightedTextControl.idl";
66

77
namespace TerminalApp
88
{
9-
[default_interface] runtimeclass FilteredCommand : Windows.UI.Xaml.Data.INotifyPropertyChanged
9+
[default_interface] unsealed runtimeclass FilteredCommand : Windows.UI.Xaml.Data.INotifyPropertyChanged
1010
{
1111
FilteredCommand();
1212
FilteredCommand(PaletteItem item);

src/cascadia/TerminalApp/Resources/en-US/Resources.resw

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,4 +913,15 @@
913913
<data name="ActionSaveFailedToast.Title" xml:space="preserve">
914914
<value>Action save failed</value>
915915
</data>
916+
<data name="SnippetPaneTitle.Text" xml:space="preserve">
917+
<value>Snippets</value>
918+
<comment>Header for a page that includes small snippets of text for the user to enter</comment>
919+
</data>
920+
<data name="SnippetsFilterBox.PlaceholderText" xml:space="preserve">
921+
<value>Filter snippets...</value>
922+
<comment>Placeholder text for a text box to filter snippets (on the same page as the 'SnippetPaneTitle')</comment>
923+
</data>
924+
<data name="SnippetPlayButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
925+
<value>Input this command</value>
926+
</data>
916927
</root>
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
#include "pch.h"
5+
#include "SnippetsPaneContent.h"
6+
#include "SnippetsPaneContent.g.cpp"
7+
#include "FilteredTask.g.cpp"
8+
9+
using namespace winrt::Windows::Foundation;
10+
using namespace winrt::Microsoft::Terminal::Settings;
11+
using namespace winrt::Microsoft::Terminal::Settings::Model;
12+
13+
namespace winrt
14+
{
15+
namespace WUX = Windows::UI::Xaml;
16+
namespace MUX = Microsoft::UI::Xaml;
17+
using IInspectable = Windows::Foundation::IInspectable;
18+
}
19+
20+
namespace winrt::TerminalApp::implementation
21+
{
22+
SnippetsPaneContent::SnippetsPaneContent()
23+
{
24+
InitializeComponent();
25+
26+
// auto res = Windows::UI::Xaml::Application::Current().Resources();
27+
auto bg = Resources().Lookup(winrt::box_value(L"PageBackground"));
28+
Background(bg.try_as<WUX::Media::Brush>());
29+
}
30+
31+
void SnippetsPaneContent::_updateFilteredCommands()
32+
{
33+
const auto& queryString = _filterBox().Text();
34+
35+
// DON'T replace the itemSource here. If you do, it'll un-expand all the
36+
// nested items the user has expanded. Instead, just update the filter.
37+
// That'll also trigger a PropertyChanged for the Visibility property.
38+
for (const auto& t : _allTasks)
39+
{
40+
t.UpdateFilter(queryString);
41+
}
42+
}
43+
44+
void SnippetsPaneContent::UpdateSettings(const CascadiaSettings& settings)
45+
{
46+
_settings = settings;
47+
48+
// You'd think that `FilterToSendInput(queryString)` would work. It
49+
// doesn't! That uses the queryString as the current command the user
50+
// has typed, then relies on the suggestions UI to _also_ filter with that
51+
// string.
52+
53+
const auto tasks = _settings.GlobalSettings().ActionMap().FilterToSendInput(L""); // IVector<Model::Command>
54+
_allTasks = winrt::single_threaded_observable_vector<TerminalApp::FilteredTask>();
55+
for (const auto& t : tasks)
56+
{
57+
const auto& filtered{ winrt::make<FilteredTask>(t) };
58+
_allTasks.Append(filtered);
59+
}
60+
_treeView().ItemsSource(_allTasks);
61+
62+
_updateFilteredCommands();
63+
}
64+
65+
void SnippetsPaneContent::_filterTextChanged(const IInspectable& /*sender*/,
66+
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
67+
{
68+
_updateFilteredCommands();
69+
}
70+
71+
winrt::Windows::UI::Xaml::FrameworkElement SnippetsPaneContent::GetRoot()
72+
{
73+
return *this;
74+
}
75+
winrt::Windows::Foundation::Size SnippetsPaneContent::MinimumSize()
76+
{
77+
return { 1, 1 };
78+
}
79+
void SnippetsPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
80+
{
81+
reason;
82+
// _box.Focus(reason);
83+
}
84+
void SnippetsPaneContent::Close()
85+
{
86+
CloseRequested.raise(*this, nullptr);
87+
}
88+
89+
INewContentArgs SnippetsPaneContent::GetNewTerminalArgs(BuildStartupKind /*kind*/) const
90+
{
91+
return BaseContentArgs(L"snippets");
92+
}
93+
94+
winrt::hstring SnippetsPaneContent::Icon() const
95+
{
96+
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
97+
return winrt::hstring{ glyph };
98+
}
99+
100+
winrt::Windows::UI::Xaml::Media::Brush SnippetsPaneContent::BackgroundBrush()
101+
{
102+
return Background();
103+
}
104+
105+
void SnippetsPaneContent::SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control)
106+
{
107+
_control = control;
108+
}
109+
110+
void SnippetsPaneContent::_runCommandButtonClicked(const Windows::Foundation::IInspectable& sender,
111+
const Windows::UI::Xaml::RoutedEventArgs&)
112+
{
113+
if (const auto& taskVM{ sender.try_as<WUX::Controls::Button>().DataContext().try_as<FilteredTask>() })
114+
{
115+
if (const auto& strongControl{ _control.get() })
116+
{
117+
// By using the last active control as the sender here, the
118+
// action dispatch will send this to the active control,
119+
// thinking that it is the control that requested this event.
120+
DispatchCommandRequested.raise(strongControl, taskVM->Command());
121+
}
122+
}
123+
}
124+
125+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
#pragma once
5+
#include "SnippetsPaneContent.g.h"
6+
#include "FilteredTask.g.h"
7+
#include "FilteredCommand.h"
8+
#include "ActionPaletteItem.h"
9+
#include <LibraryResources.h>
10+
11+
namespace winrt::TerminalApp::implementation
12+
{
13+
struct SnippetsPaneContent : SnippetsPaneContentT<SnippetsPaneContent>
14+
{
15+
SnippetsPaneContent();
16+
17+
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
18+
19+
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
20+
21+
winrt::Windows::Foundation::Size MinimumSize();
22+
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
23+
void Close();
24+
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const;
25+
26+
winrt::hstring Title() { return RS_(L"SnippetPaneTitle/Text"); }
27+
uint64_t TaskbarState() { return 0; }
28+
uint64_t TaskbarProgress() { return 0; }
29+
bool ReadOnly() { return false; }
30+
winrt::hstring Icon() const;
31+
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
32+
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
33+
34+
void SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control);
35+
36+
til::typed_event<> ConnectionStateChanged;
37+
til::typed_event<IPaneContent> CloseRequested;
38+
til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
39+
til::typed_event<IPaneContent> TitleChanged;
40+
til::typed_event<IPaneContent> TabColorChanged;
41+
til::typed_event<IPaneContent> TaskbarProgressChanged;
42+
til::typed_event<IPaneContent> ReadOnlyChanged;
43+
til::typed_event<IPaneContent> FocusRequested;
44+
45+
til::typed_event<winrt::Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::Command> DispatchCommandRequested;
46+
47+
private:
48+
friend struct SnippetsPaneContentT<SnippetsPaneContent>; // for Xaml to bind events
49+
50+
winrt::weak_ref<Microsoft::Terminal::Control::TermControl> _control{ nullptr };
51+
winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
52+
winrt::Windows::Foundation::Collections::IObservableVector<TerminalApp::FilteredTask> _allTasks{ nullptr };
53+
54+
void _runCommandButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
55+
void _filterTextChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
56+
57+
void _updateFilteredCommands();
58+
};
59+
60+
struct FilteredTask : FilteredTaskT<FilteredTask, TerminalApp::implementation::FilteredCommand>
61+
{
62+
FilteredTask() = default;
63+
64+
FilteredTask(const winrt::Microsoft::Terminal::Settings::Model::Command& command)
65+
{
66+
_constructFilteredCommand(winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(command, L""));
67+
_command = command;
68+
69+
// The Children() method must always return a non-null vector
70+
_children = winrt::single_threaded_observable_vector<TerminalApp::FilteredTask>();
71+
if (_command.HasNestedCommands())
72+
{
73+
for (const auto& [_, child] : _command.NestedCommands())
74+
{
75+
auto vm{ winrt::make<FilteredTask>(child) };
76+
_children.Append(vm);
77+
}
78+
}
79+
}
80+
81+
void UpdateFilter(const winrt::hstring& filter) override
82+
{
83+
TerminalApp::implementation::FilteredCommand::UpdateFilter(filter);
84+
for (const auto& c : _children)
85+
{
86+
c.UpdateFilter(filter);
87+
}
88+
89+
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Visibility" });
90+
}
91+
92+
winrt::hstring Input()
93+
{
94+
if (const auto& actionItem{ _Item.try_as<winrt::TerminalApp::ActionPaletteItem>() })
95+
{
96+
if (const auto& command{ actionItem.Command() })
97+
{
98+
if (const auto& sendInput{ command.ActionAndArgs().Args().try_as<winrt::Microsoft::Terminal::Settings::Model::SendInputArgs>() })
99+
{
100+
return sendInput.Input();
101+
}
102+
}
103+
}
104+
return L"";
105+
};
106+
107+
winrt::Windows::Foundation::Collections::IObservableVector<TerminalApp::FilteredTask> Children() { return _children; }
108+
bool HasChildren() { return _children.Size() > 0; }
109+
winrt::Microsoft::Terminal::Settings::Model::Command Command() { return _command; }
110+
111+
// Used to control if this item is visible in the TreeView. Turns out,
112+
// TreeView is in fact sane enough to remove items entirely if they're
113+
// Collapsed.
114+
winrt::Windows::UI::Xaml::Visibility Visibility()
115+
{
116+
// Is there no filter, or do we match it?
117+
if (_Filter.empty() || _Weight > 0)
118+
{
119+
return winrt::Windows::UI::Xaml::Visibility::Visible;
120+
}
121+
// If we don't match, maybe one of our children does
122+
auto totalWeight = _Weight;
123+
for (const auto& c : _children)
124+
{
125+
totalWeight += c.Weight();
126+
}
127+
128+
return totalWeight > 0 ? winrt::Windows::UI::Xaml::Visibility::Visible : winrt::Windows::UI::Xaml::Visibility::Collapsed;
129+
};
130+
131+
private:
132+
winrt::Microsoft::Terminal::Settings::Model::Command _command{ nullptr };
133+
winrt::Windows::Foundation::Collections::IObservableVector<TerminalApp::FilteredTask> _children{ nullptr };
134+
};
135+
}
136+
137+
namespace winrt::TerminalApp::factory_implementation
138+
{
139+
BASIC_FACTORY(SnippetsPaneContent);
140+
}

0 commit comments

Comments
 (0)