terminal/src/cascadia/TerminalApp/AppActionHandlers.cpp
Mike Griese 6268a4779c
Implement and action for manually clearing the Terminal (and conpty) buffer (#10906)
## Summary of the Pull Request

![clear-buffer-000](https://user-images.githubusercontent.com/18356694/127570078-90c6089e-0430-4dfc-bcd4-a0cde20c9167.gif)

This adds a new action, `clearBuffer`. It accepts 3 values for the `clear` type:
* `"clear": "screen"`: Clear the terminal viewport content. Leaves the scrollback untouched. Moves the cursor row to the top of the viewport (unmodified).
* `"clear": "scrollback"`: Clear the scrollback. Leaves the viewport untouched.
* `"clear": "all"`: (**default**) Clear the scrollback and the visible viewport. Moves the cursor row to the top of the viewport (unmodified).

"Clear Buffer" has also been added to `defaults.json`.

## References
* From microsoft/vscode#75141 originally

## PR Checklist
* [x] Closes #1193
* [x] Closes #1882
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

This is a bit tricky, because we need to plumb it all the way through conpty to clear the buffer. If we don't, then conpty will immediately just redraw the screen. So this sends a signal to the attached conpty, and then waits for conpty to draw the updated, cleared, screen back to us.

## Validation Steps Performed
* works for each of the three clear types as expected
* tests pass.
* works even with `ping -t 8.8.8.8` as you'd hope.
2021-09-02 14:59:42 +00:00

911 lines
33 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "App.h"
#include "TerminalPage.h"
#include "../WinRTUtils/inc/WtExeUtils.h"
#include "../../types/inc/utils.hpp"
#include "Utils.h"
using namespace winrt::Windows::ApplicationModel::DataTransfer;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::System;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
using namespace ::TerminalApp;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
using IInspectable = Windows::Foundation::IInspectable;
}
namespace winrt::TerminalApp::implementation
{
void TerminalPage::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_OpenNewTabDropdown();
args.Handled(true);
}
void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_DuplicateFocusedTab();
args.Handled(true);
}
void TerminalPage::_HandleCloseTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto realArgs = args.ActionArgs().try_as<CloseTabArgs>())
{
uint32_t index;
if (realArgs.Index())
{
index = realArgs.Index().Value();
}
else if (auto focusedTabIndex = _GetFocusedTabIndex())
{
index = *focusedTabIndex;
}
else
{
args.Handled(false);
return;
}
_CloseTabAtIndex(index);
args.Handled(true);
}
}
void TerminalPage::_HandleClosePane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_CloseFocusedPane();
args.Handled(true);
}
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
CloseWindow();
args.Handled(true);
}
void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
const auto& realArgs = args.ActionArgs().try_as<ScrollUpArgs>();
if (realArgs)
{
_Scroll(ScrollUp, realArgs.RowsToScroll());
args.Handled(true);
}
}
void TerminalPage::_HandleScrollDown(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
const auto& realArgs = args.ActionArgs().try_as<ScrollDownArgs>();
if (realArgs)
{
_Scroll(ScrollDown, realArgs.RowsToScroll());
args.Handled(true);
}
}
void TerminalPage::_HandleNextTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
const auto& realArgs = args.ActionArgs().try_as<NextTabArgs>();
if (realArgs)
{
_SelectNextTab(true, realArgs.SwitcherMode());
args.Handled(true);
}
}
void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
const auto& realArgs = args.ActionArgs().try_as<PrevTabArgs>();
if (realArgs)
{
_SelectNextTab(false, realArgs.SwitcherMode());
args.Handled(true);
}
}
void TerminalPage::_HandleSendInput(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args == nullptr)
{
args.Handled(false);
}
else if (const auto& realArgs = args.ActionArgs().try_as<SendInputArgs>())
{
if (const auto termControl{ _GetActiveControl() })
{
termControl.SendInput(realArgs.Input());
args.Handled(true);
}
}
}
void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args == nullptr)
{
args.Handled(false);
}
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
{
auto moved = _MovePane(realArgs.TabIndex());
args.Handled(moved);
}
}
void TerminalPage::_HandleSplitPane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args == nullptr)
{
args.Handled(false);
}
else if (const auto& realArgs = args.ActionArgs().try_as<SplitPaneArgs>())
{
_SplitPane(realArgs.SplitStyle(),
realArgs.SplitMode(),
// This is safe, we're already filtering so the value is (0, 1)
::base::saturated_cast<float>(realArgs.SplitSize()),
realArgs.TerminalArgs());
args.Handled(true);
}
}
void TerminalPage::_HandleToggleSplitOrientation(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ToggleSplitOrientation();
args.Handled(true);
}
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
// Don't do anything if there's only one pane. It's already zoomed.
if (activeTab->GetLeafPaneCount() > 1)
{
// First thing's first, remove the current content from the UI
// tree. This is important, because we might be leaving zoom, and if
// a pane is zoomed, then it's currently in the UI tree, and should
// be removed before it's re-added in Pane::Restore
_tabContent.Children().Clear();
// Togging the zoom on the tab will cause the tab to inform us of
// the new root Content for this tab.
activeTab->ToggleZoom();
}
}
args.Handled(true);
}
void TerminalPage::_HandleTogglePaneReadOnly(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
activeTab->TogglePaneReadOnly();
}
args.Handled(true);
}
void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ScrollPage(ScrollUp);
args.Handled(true);
}
void TerminalPage::_HandleScrollDownPage(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ScrollPage(ScrollDown);
args.Handled(true);
}
void TerminalPage::_HandleScrollToTop(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ScrollToBufferEdge(ScrollUp);
args.Handled(true);
}
void TerminalPage::_HandleScrollToBottom(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ScrollToBufferEdge(ScrollDown);
args.Handled(true);
}
void TerminalPage::_HandleFindMatch(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<FindMatchArgs>())
{
if (const auto& control{ _GetActiveControl() })
{
control.SearchMatch(realArgs.Direction() == FindMatchDirection::Next);
args.Handled(true);
}
}
}
void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<OpenSettingsArgs>())
{
_LaunchSettings(realArgs.Target());
args.Handled(true);
}
}
void TerminalPage::_HandlePasteText(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_PasteText();
args.Handled(true);
}
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args == nullptr)
{
LOG_IF_FAILED(_OpenNewTab(nullptr));
args.Handled(true);
}
else if (const auto& realArgs = args.ActionArgs().try_as<NewTabArgs>())
{
LOG_IF_FAILED(_OpenNewTab(realArgs.TerminalArgs()));
args.Handled(true);
}
}
void TerminalPage::_HandleSwitchToTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SwitchToTabArgs>())
{
_SelectTab({ realArgs.TabIndex() });
args.Handled(true);
}
}
void TerminalPage::_HandleResizePane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ResizePaneArgs>())
{
if (realArgs.ResizeDirection() == ResizeDirection::None)
{
// Do nothing
args.Handled(false);
}
else
{
_ResizePane(realArgs.ResizeDirection());
args.Handled(true);
}
}
}
void TerminalPage::_HandleMoveFocus(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<MoveFocusArgs>())
{
if (realArgs.FocusDirection() == FocusDirection::None)
{
// Do nothing
args.Handled(false);
}
else
{
// Mark as handled only when the move succeeded (e.g. when there
// is a pane to move to), otherwise mark as unhandled so the
// keychord can propagate to the terminal (GH#6129)
const auto moveSucceeded = _MoveFocus(realArgs.FocusDirection());
args.Handled(moveSucceeded);
}
}
}
void TerminalPage::_HandleSwapPane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SwapPaneArgs>())
{
if (realArgs.Direction() == FocusDirection::None)
{
// Do nothing
args.Handled(false);
}
else
{
auto swapped = _SwapPane(realArgs.Direction());
args.Handled(swapped);
}
}
}
void TerminalPage::_HandleCopyText(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<CopyTextArgs>())
{
const auto handled = _CopyText(realArgs.SingleLine(), realArgs.CopyFormatting());
args.Handled(handled);
}
}
void TerminalPage::_HandleAdjustFontSize(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<AdjustFontSizeArgs>())
{
if (const auto& termControl{ _GetActiveControl() })
{
termControl.AdjustFontSize(realArgs.Delta());
args.Handled(true);
}
}
}
void TerminalPage::_HandleFind(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_Find();
args.Handled(true);
}
void TerminalPage::_HandleResetFontSize(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& termControl{ _GetActiveControl() })
{
termControl.ResetFontSize();
args.Handled(true);
}
}
void TerminalPage::_HandleToggleShaderEffects(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& termControl{ _GetActiveControl() })
{
termControl.ToggleShaderEffects();
args.Handled(true);
}
}
void TerminalPage::_HandleToggleFocusMode(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
ToggleFocusMode();
args.Handled(true);
}
void TerminalPage::_HandleToggleFullscreen(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
ToggleFullscreen();
args.Handled(true);
}
void TerminalPage::_HandleToggleAlwaysOnTop(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
ToggleAlwaysOnTop();
args.Handled(true);
}
void TerminalPage::_HandleToggleCommandPalette(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ToggleCommandPaletteArgs>())
{
CommandPalette().EnableCommandPaletteMode(realArgs.LaunchMode());
CommandPalette().Visibility(CommandPalette().Visibility() == Visibility::Visible ?
Visibility::Collapsed :
Visibility::Visible);
args.Handled(true);
}
}
void TerminalPage::_HandleSetColorScheme(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
args.Handled(false);
if (const auto& realArgs = args.ActionArgs().try_as<SetColorSchemeArgs>())
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
if (auto activeControl = activeTab->GetActiveTerminalControl())
{
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
{
// Start by getting the current settings of the control
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
auto parentSettings = controlSettings;
// Those are the _runtime_ settings however. What we
// need to do is:
//
// 1. Blow away any colors set in the runtime settings.
// 2. Apply the color scheme to the parent settings.
//
// 1 is important to make sure that the effects of
// something like `colortool` are cleared when setting
// the scheme.
if (controlSettings.GetParent() != nullptr)
{
parentSettings = controlSettings.GetParent();
}
// ApplyColorScheme(nullptr) will clear the old color scheme.
controlSettings.ApplyColorScheme(nullptr);
parentSettings.ApplyColorScheme(scheme);
activeControl.UpdateSettings();
args.Handled(true);
}
}
}
}
}
void TerminalPage::_HandleSetTabColor(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
Windows::Foundation::IReference<Windows::UI::Color> tabColor;
if (const auto& realArgs = args.ActionArgs().try_as<SetTabColorArgs>())
{
tabColor = realArgs.TabColor();
}
if (const auto activeTab{ _GetFocusedTabImpl() })
{
if (tabColor)
{
activeTab->SetRuntimeTabColor(tabColor.Value());
}
else
{
activeTab->ResetRuntimeTabColor();
}
}
args.Handled(true);
}
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
activeTab->ActivateColorPicker();
}
args.Handled(true);
}
void TerminalPage::_HandleRenameTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
std::optional<winrt::hstring> title;
if (const auto& realArgs = args.ActionArgs().try_as<RenameTabArgs>())
{
title = realArgs.Title();
}
if (const auto activeTab{ _GetFocusedTabImpl() })
{
if (title.has_value())
{
activeTab->SetTabText(title.value());
}
else
{
activeTab->ResetTabText();
}
}
args.Handled(true);
}
void TerminalPage::_HandleOpenTabRenamer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
activeTab->ActivateTabRenamer();
}
args.Handled(true);
}
void TerminalPage::_HandleExecuteCommandline(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<ExecuteCommandlineArgs>())
{
auto actions = winrt::single_threaded_vector<ActionAndArgs>(std::move(
TerminalPage::ConvertExecuteCommandlineToActions(realArgs)));
if (_startupActions.Size() != 0)
{
actionArgs.Handled(true);
ProcessStartupActions(actions, false);
}
}
}
void TerminalPage::_HandleCloseOtherTabs(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<CloseOtherTabsArgs>())
{
uint32_t index;
if (realArgs.Index())
{
index = realArgs.Index().Value();
}
else if (auto focusedTabIndex = _GetFocusedTabIndex())
{
index = *focusedTabIndex;
}
else
{
// Do nothing
actionArgs.Handled(false);
return;
}
// Since _RemoveTabs is asynchronous, create a snapshot of the tabs we want to remove
std::vector<winrt::TerminalApp::TabBase> tabsToRemove;
if (index > 0)
{
std::copy(begin(_tabs), begin(_tabs) + index, std::back_inserter(tabsToRemove));
}
if (index + 1 < _tabs.Size())
{
std::copy(begin(_tabs) + index + 1, end(_tabs), std::back_inserter(tabsToRemove));
}
_RemoveTabs(tabsToRemove);
actionArgs.Handled(true);
}
}
void TerminalPage::_HandleCloseTabsAfter(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<CloseTabsAfterArgs>())
{
uint32_t index;
if (realArgs.Index())
{
index = realArgs.Index().Value();
}
else if (auto focusedTabIndex = _GetFocusedTabIndex())
{
index = *focusedTabIndex;
}
else
{
// Do nothing
actionArgs.Handled(false);
return;
}
// Since _RemoveTabs is asynchronous, create a snapshot of the tabs we want to remove
std::vector<winrt::TerminalApp::TabBase> tabsToRemove;
std::copy(begin(_tabs) + index + 1, end(_tabs), std::back_inserter(tabsToRemove));
_RemoveTabs(tabsToRemove);
// TODO:GH#7182 For whatever reason, if you run this action
// when the tab that's currently focused is _before_ the `index`
// param, then the tabs will expand to fill the entire width of the
// tab row, until you mouse over them. Probably has something to do
// with tabs not resizing down until there's a mouse exit event.
actionArgs.Handled(true);
}
}
void TerminalPage::_HandleTabSearch(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
CommandPalette().SetTabs(_tabs, _mruTabs);
CommandPalette().EnableTabSearchMode();
CommandPalette().Visibility(Visibility::Visible);
args.Handled(true);
}
void TerminalPage::_HandleMoveTab(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<MoveTabArgs>())
{
auto direction = realArgs.Direction();
if (direction != MoveTabDirection::None)
{
if (auto focusedTabIndex = _GetFocusedTabIndex())
{
auto currentTabIndex = focusedTabIndex.value();
auto delta = direction == MoveTabDirection::Forward ? 1 : -1;
_TryMoveTab(currentTabIndex, currentTabIndex + delta);
}
}
actionArgs.Handled(true);
}
}
void TerminalPage::_HandleBreakIntoDebugger(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
if (_settings.GlobalSettings().DebugFeaturesEnabled())
{
actionArgs.Handled(true);
DebugBreak();
}
}
// Function Description:
// - Helper to launch a new WT instance. It can either launch the instance
// elevated or unelevated.
// - To launch elevated, it will as the shell to elevate the process for us.
// This might cause a UAC prompt. The elevation is performed on a
// background thread, as to not block the UI thread.
// Arguments:
// - elevate: If true, launch the new Terminal elevated using `runas`
// - newTerminalArgs: A NewTerminalArgs describing the terminal instance
// that should be spawned. The Profile should be filled in with the GUID
// of the profile we want to launch.
// Return Value:
// - <none>
// Important: Don't take the param by reference, since we'll be doing work
// on another thread.
fire_and_forget TerminalPage::_OpenNewWindow(const bool elevate,
const NewTerminalArgs newTerminalArgs)
{
// Hop to the BG thread
co_await winrt::resume_background();
// This will get us the correct exe for dev/preview/release. If you
// don't stick this in a local, it'll get mangled by ShellExecute. I
// have no idea why.
const auto exePath{ GetWtExePath() };
// Build the commandline to pass to wt for this set of NewTerminalArgs
// `-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"")
};
// Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we
// can pass the SEE_MASK_NOASYNC flag. That flag allows us to safely
// call this on the background thread, and have ShellExecute _not_ call
// back to us on the main thread. Without this, if you close the
// Terminal quickly after the UAC prompt, the elevated WT will never
// actually spawn.
SHELLEXECUTEINFOW seInfo{ 0 };
seInfo.cbSize = sizeof(seInfo);
seInfo.fMask = SEE_MASK_NOASYNC;
// `runas` will cause the shell to launch this child process elevated.
// `open` will just run the executable normally.
seInfo.lpVerb = elevate ? L"runas" : L"open";
seInfo.lpFile = exePath.c_str();
seInfo.lpParameters = cmdline.c_str();
seInfo.nShow = SW_SHOWNORMAL;
LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo));
co_return;
}
void TerminalPage::_HandleNewWindow(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
NewTerminalArgs newTerminalArgs{ nullptr };
// If the caller provided NewTerminalArgs, then try to use those
if (actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<NewWindowArgs>())
{
newTerminalArgs = realArgs.TerminalArgs();
}
}
// 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)
{
newTerminalArgs = NewTerminalArgs();
}
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
// Manually fill in the evaluated profile.
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
_OpenNewWindow(false, newTerminalArgs);
actionArgs.Handled(true);
}
// Method Description:
// - Raise a IdentifyWindowsRequested event. This will bubble up to the
// AppLogic, to the AppHost, to the Peasant, to the Monarch, then get
// distributed down to _all_ the Peasants, as to display info about the
// window in _every_ Peasant window.
// - This action is also buggy right now, because TeachingTips behave
// weird in XAML Islands. See microsoft-ui-xaml#4382
// Arguments:
// - <unused>
// Return Value:
// - <none>
void TerminalPage::_HandleIdentifyWindows(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_IdentifyWindowsRequestedHandlers(*this, nullptr);
args.Handled(true);
}
// Method Description:
// - Display the "Toast" with the name and ID of this window.
// - Unlike _HandleIdentifyWindow**s**, this event just displays the window
// ID and name in the current window. It does not involve any bubbling
// up/down the page/logic/host/manager/peasant/monarch.
// Arguments:
// - <unused>
// Return Value:
// - <none>
void TerminalPage::_HandleIdentifyWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
IdentifyWindow();
args.Handled(true);
}
void TerminalPage::_HandleRenameWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<RenameWindowArgs>())
{
const auto newName = realArgs.Name();
const auto request = winrt::make_self<implementation::RenameWindowRequestedArgs>(newName);
_RenameWindowRequestedHandlers(*this, *request);
args.Handled(true);
}
}
}
void TerminalPage::_HandleOpenWindowRenamer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (WindowRenamer() == nullptr)
{
// We need to use FindName to lazy-load this object
if (MUX::Controls::TeachingTip tip{ FindName(L"WindowRenamer").try_as<MUX::Controls::TeachingTip>() })
{
tip.Closed({ get_weak(), &TerminalPage::_FocusActiveControl });
}
}
_UpdateTeachingTipTheme(WindowRenamer().try_as<winrt::Windows::UI::Xaml::FrameworkElement>());
WindowRenamer().IsOpen(true);
// PAIN: We can't immediately focus the textbox in the TeachingTip. It's
// not technically focusable until it is opened. However, it doesn't
// provide an event to tell us when it is opened. That's tracked in
// microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to
// click on the text box manually.
//
// We're also not using a ContentDialog for this, because in Xaml
// Islands a text box in a ContentDialog won't receive _any_ keypresses.
// Fun!
// WindowRenamerTextBox().Focus(FocusState::Programmatic);
args.Handled(true);
}
void TerminalPage::_HandleGlobalSummon(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
// Manually return false. These shouldn't ever get here, except for when
// we fail to register for the global hotkey. In that case, returning
// false here will let the underlying terminal still process the key, as
// if it wasn't bound at all.
args.Handled(false);
}
void TerminalPage::_HandleQuakeMode(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
// Manually return false. These shouldn't ever get here, except for when
// we fail to register for the global hotkey. In that case, returning
// false here will let the underlying terminal still process the key, as
// if it wasn't bound at all.
args.Handled(false);
}
void TerminalPage::_HandleFocusPane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<FocusPaneArgs>())
{
const auto paneId = realArgs.Id();
if (const auto activeTab{ _GetFocusedTabImpl() })
{
_UnZoomIfNeeded();
args.Handled(activeTab->FocusPane(paneId));
}
}
}
}
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>())
{
if (const auto termControl{ _GetActiveControl() })
{
termControl.ClearBuffer(realArgs.Clear());
args.Handled(true);
}
}
}
}
void TerminalPage::_HandleMultipleActions(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<MultipleActionsArgs>())
{
for (const auto& action : realArgs.Actions())
{
_actionDispatch->DoAction(action);
}
args.Handled(true);
}
}
}
}