Skip to content

Commit dc4026d

Browse files
authored
Add support for restoring non-terminal panes, and opening them with splitPane, newTab (#16914)
This changes `NewTabArgs`, `SplitPaneArgs`, and `NewWindowArgs` to accept a `INewContentArgs`, rather than just a `NewTerminalArgs`. This allows a couple things: * Users can open arbitrary types of panes with the existing `splitPane`, `newWindow` actions, just by passing `"type": "scartchpad"` (for example). This is a lot more flexible than re-defining different `"openScratchpad"`, `"openTasksPane"`, etc, etc actions for every kind of pane. * This allows us to use the existing machinery of session restore to also restore non-terminal panes. The `type` property was added to `newTab`, `splitPane`, `newWindow`. When omitted, we still just treat the json as a blob of NewTerminalArgs. There's not actually any other kinds of `INewContentArgs` in this PR (other than the placeholder `GenericContentArgs`). In [`dev/migrie/fhl/md-pane`](dev/migrie/f/tasks-pane...dev/migrie/fhl/md-pane), I have a type of pane that would LOVE to add some args here. So that's forward-thinking. There's really just two stealth types of pane for now: `settings`, and `scratchpad`. Those I DON'T have as constants or anything in this PR. They probably should be? Though, I suspect around the time of the tasks & MD panes, I'll come up with whatever structure I actually want them to take. ### future considerations here * In the future, this should allow extensions to say "I know how to host `foo` content", for 3p content. * The `wt` CLI args were not yet updated to also accept `--type` yet. There's no reason we couldn't easily do that. * I considered adding `ICanHasCommandline` to allow arbitrary content to generate a `wt` commandline-serializable string. Punted on that for now. ## other PRs * #16170 * #16171 * #16172 * #16895 * #16914 <-- you are here Closes #17014
1 parent c063d2b commit dc4026d

25 files changed

+1182
-868
lines changed

src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp

+320-273
Large diffs are not rendered by default.

src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp

+271-237
Large diffs are not rendered by default.

src/cascadia/TerminalApp/AppActionHandlers.cpp

+61-30
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,32 @@ namespace winrt::TerminalApp::implementation
238238
}
239239
}
240240

241+
// * Helper to try and get a ProfileIndex out of a NewTerminalArgs out of a
242+
// NewContentArgs. For the new tab and split pane action, we want to _not_
243+
// handle the event if an invalid profile index was passed.
244+
//
245+
// Return value:
246+
// * True if the args are NewTerminalArgs, and the profile index was out of bounds.
247+
// * False otherwise.
248+
static bool _shouldBailForInvalidProfileIndex(const CascadiaSettings& settings, const INewContentArgs& args)
249+
{
250+
if (!args)
251+
{
252+
return false;
253+
}
254+
if (const auto& terminalArgs{ args.try_as<NewTerminalArgs>() })
255+
{
256+
if (const auto index = terminalArgs.ProfileIndex())
257+
{
258+
if (gsl::narrow<uint32_t>(index.Value()) >= settings.ActiveProfiles().Size())
259+
{
260+
return true;
261+
}
262+
}
263+
}
264+
return false;
265+
}
266+
241267
void TerminalPage::_HandleSplitPane(const IInspectable& sender,
242268
const ActionEventArgs& args)
243269
{
@@ -247,16 +273,10 @@ namespace winrt::TerminalApp::implementation
247273
}
248274
else if (const auto& realArgs = args.ActionArgs().try_as<SplitPaneArgs>())
249275
{
250-
if (const auto& newTerminalArgs{ realArgs.TerminalArgs() })
276+
if (_shouldBailForInvalidProfileIndex(_settings, realArgs.ContentArgs()))
251277
{
252-
if (const auto index = realArgs.TerminalArgs().ProfileIndex())
253-
{
254-
if (gsl::narrow<uint32_t>(index.Value()) >= _settings.ActiveProfiles().Size())
255-
{
256-
args.Handled(false);
257-
return;
258-
}
259-
}
278+
args.Handled(false);
279+
return;
260280
}
261281

262282
const auto& duplicateFromTab{ realArgs.SplitMode() == SplitType::Duplicate ? _GetFocusedTab() : nullptr };
@@ -267,7 +287,7 @@ namespace winrt::TerminalApp::implementation
267287
realArgs.SplitDirection(),
268288
// This is safe, we're already filtering so the value is (0, 1)
269289
::base::saturated_cast<float>(realArgs.SplitSize()),
270-
_MakePane(realArgs.TerminalArgs(), duplicateFromTab));
290+
_MakePane(realArgs.ContentArgs(), duplicateFromTab));
271291
args.Handled(true);
272292
}
273293
}
@@ -445,19 +465,13 @@ namespace winrt::TerminalApp::implementation
445465
}
446466
else if (const auto& realArgs = args.ActionArgs().try_as<NewTabArgs>())
447467
{
448-
if (const auto& newTerminalArgs{ realArgs.TerminalArgs() })
468+
if (_shouldBailForInvalidProfileIndex(_settings, realArgs.ContentArgs()))
449469
{
450-
if (const auto index = newTerminalArgs.ProfileIndex())
451-
{
452-
if (gsl::narrow<uint32_t>(index.Value()) >= _settings.ActiveProfiles().Size())
453-
{
454-
args.Handled(false);
455-
return;
456-
}
457-
}
470+
args.Handled(false);
471+
return;
458472
}
459473

460-
LOG_IF_FAILED(_OpenNewTab(realArgs.TerminalArgs()));
474+
LOG_IF_FAILED(_OpenNewTab(realArgs.ContentArgs()));
461475
args.Handled(true);
462476
}
463477
}
@@ -869,8 +883,23 @@ namespace winrt::TerminalApp::implementation
869883
// - <none>
870884
// Important: Don't take the param by reference, since we'll be doing work
871885
// on another thread.
872-
fire_and_forget TerminalPage::_OpenNewWindow(const NewTerminalArgs newTerminalArgs)
886+
fire_and_forget TerminalPage::_OpenNewWindow(const INewContentArgs newContentArgs)
873887
{
888+
auto terminalArgs{ newContentArgs.try_as<NewTerminalArgs>() };
889+
890+
// Do nothing for non-terminal panes.
891+
//
892+
// Theoretically, we could define a `IHasCommandline` interface, and
893+
// stick `ToCommandline` on that interface, for any kind of pane that
894+
// wants to be convertable to a wt commandline.
895+
//
896+
// Another idea we're thinking about is just `wt do {literal json for an
897+
// action}`, which might be less leaky
898+
if (terminalArgs == nullptr)
899+
{
900+
co_return;
901+
}
902+
874903
// Hop to the BG thread
875904
co_await winrt::resume_background();
876905

@@ -883,8 +912,7 @@ namespace winrt::TerminalApp::implementation
883912
// `-w -1` will ensure a new window is created.
884913
winrt::hstring cmdline{
885914
fmt::format(L"-w -1 new-tab {}",
886-
newTerminalArgs ? newTerminalArgs.ToCommandline().c_str() :
887-
L"")
915+
terminalArgs.ToCommandline().c_str())
888916
};
889917

890918
// Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we
@@ -909,29 +937,32 @@ namespace winrt::TerminalApp::implementation
909937
void TerminalPage::_HandleNewWindow(const IInspectable& /*sender*/,
910938
const ActionEventArgs& actionArgs)
911939
{
912-
NewTerminalArgs newTerminalArgs{ nullptr };
940+
INewContentArgs newContentArgs{ nullptr };
913941
// If the caller provided NewTerminalArgs, then try to use those
914942
if (actionArgs)
915943
{
916944
if (const auto& realArgs = actionArgs.ActionArgs().try_as<NewWindowArgs>())
917945
{
918-
newTerminalArgs = realArgs.TerminalArgs();
946+
newContentArgs = realArgs.ContentArgs();
919947
}
920948
}
921949
// Otherwise, if no NewTerminalArgs were provided, then just use a
922950
// default-constructed one. The default-constructed one implies that
923951
// nothing about the launch should be modified (just use the default
924952
// profile).
925-
if (!newTerminalArgs)
953+
if (!newContentArgs)
926954
{
927-
newTerminalArgs = NewTerminalArgs();
955+
newContentArgs = NewTerminalArgs{};
928956
}
929957

930-
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
958+
if (const auto& terminalArgs{ newContentArgs.try_as<NewTerminalArgs>() })
959+
{
960+
const auto profile{ _settings.GetProfileForArgs(terminalArgs) };
961+
terminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
962+
}
931963

932964
// Manually fill in the evaluated profile.
933-
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
934-
_OpenNewWindow(newTerminalArgs);
965+
_OpenNewWindow(newContentArgs);
935966
actionArgs.Handled(true);
936967
}
937968

src/cascadia/TerminalApp/IPaneContent.idl

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace TerminalApp
3131
Windows.Foundation.IReference<Windows.UI.Color> TabColor { get; };
3232
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
3333

34-
Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind);
34+
Microsoft.Terminal.Settings.Model.INewContentArgs GetNewTerminalArgs(BuildStartupKind kind);
3535

3636
void Focus(Windows.UI.Xaml.FocusState reason);
3737

src/cascadia/TerminalApp/Pane.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ Pane::Pane(std::shared_ptr<Pane> first,
100100

101101
// Extract the terminal settings from the current (leaf) pane's control
102102
// to be used to create an equivalent control
103-
NewTerminalArgs Pane::GetTerminalArgsForPane(BuildStartupKind kind) const
103+
INewContentArgs Pane::GetTerminalArgsForPane(BuildStartupKind kind) const
104104
{
105105
// Leaves are the only things that have controls
106106
assert(_IsLeaf());

src/cascadia/TerminalApp/Pane.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class Pane : public std::enable_shared_from_this<Pane>
106106
uint32_t panesCreated;
107107
};
108108
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::BuildStartupKind kind);
109-
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const;
109+
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const;
110110

111111
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
112112
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);

src/cascadia/TerminalApp/ScratchpadContent.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ namespace winrt::TerminalApp::implementation
4848
CloseRequested.raise(*this, nullptr);
4949
}
5050

51-
NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const
51+
INewContentArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const
5252
{
53-
return nullptr;
53+
return BaseContentArgs(L"scratchpad");
5454
}
5555

5656
winrt::hstring ScratchpadContent::Icon() const

src/cascadia/TerminalApp/ScratchpadContent.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation
1919

2020
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
2121
void Close();
22-
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const;
22+
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const;
2323

2424
winrt::hstring Title() { return L"Scratchpad"; }
2525
uint64_t TaskbarState() { return 0; }

src/cascadia/TerminalApp/SettingsPaneContent.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,9 @@ namespace winrt::TerminalApp::implementation
5050
CloseRequested.raise(*this, nullptr);
5151
}
5252

53-
NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const BuildStartupKind /*kind*/) const
53+
INewContentArgs SettingsPaneContent::GetNewTerminalArgs(const BuildStartupKind /*kind*/) const
5454
{
55-
// For now, we're doing a terrible thing in TerminalTab itself to
56-
// generate an OpenSettings action manually, without asking for the pane
57-
// structure.
58-
return nullptr;
55+
return BaseContentArgs(L"settings");
5956
}
6057

6158
winrt::hstring SettingsPaneContent::Icon() const

src/cascadia/TerminalApp/SettingsPaneContent.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace winrt::TerminalApp::implementation
2020
winrt::Windows::Foundation::Size MinimumSize();
2121
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
2222
void Close();
23-
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const BuildStartupKind kind) const;
23+
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(const BuildStartupKind kind) const;
2424

2525
winrt::hstring Title() { return RS_(L"SettingsTab"); }
2626
uint64_t TaskbarState() { return 0; }

src/cascadia/TerminalApp/TabManagement.cpp

+20-17
Original file line numberDiff line numberDiff line change
@@ -62,30 +62,33 @@ namespace winrt::TerminalApp::implementation
6262
// - existingConnection: An optional connection that is already established to a PTY
6363
// for this tab to host instead of creating one.
6464
// If not defined, the tab will create the connection.
65-
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs)
65+
HRESULT TerminalPage::_OpenNewTab(const INewContentArgs& newContentArgs)
6666
try
6767
{
68-
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
69-
// GH#11114: GetProfileForArgs can return null if the index is higher
70-
// than the number of available profiles.
71-
if (!profile)
68+
if (const auto& newTerminalArgs{ newContentArgs.try_as<NewTerminalArgs>() })
7269
{
73-
return S_FALSE;
74-
}
75-
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
70+
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
71+
// GH#11114: GetProfileForArgs can return null if the index is higher
72+
// than the number of available profiles.
73+
if (!profile)
74+
{
75+
return S_FALSE;
76+
}
77+
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
7678

77-
// Try to handle auto-elevation
78-
if (_maybeElevate(newTerminalArgs, settings, profile))
79-
{
80-
return S_OK;
79+
// Try to handle auto-elevation
80+
if (_maybeElevate(newTerminalArgs, settings, profile))
81+
{
82+
return S_OK;
83+
}
84+
// We can't go in the other direction (elevated->unelevated)
85+
// unfortunately. This seems to be due to Centennial quirks. It works
86+
// unpackaged, but not packaged.
8187
}
82-
// We can't go in the other direction (elevated->unelevated)
83-
// unfortunately. This seems to be due to Centennial quirks. It works
84-
// unpackaged, but not packaged.
85-
//
88+
8689
// This call to _MakePane won't return nullptr, we already checked that
8790
// case above with the _maybeElevate call.
88-
_CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr));
91+
_CreateNewTabFromPane(_MakePane(newContentArgs, nullptr));
8992
return S_OK;
9093
}
9194
CATCH_RETURN();

0 commit comments

Comments
 (0)