terminal/src/cascadia/TerminalApp/AppActionHandlers.cpp
Dustin L. Howett 4eeaddc583
Make Tab an unsealed runtimeclass (and rename it to TabBase) (#8153)
In preparation for the Settings UI, we needed to make some changes to
Tab to abstract out shared, common functionality between different types
of tab. This is the result of that work. All code references to the
settings have been removed or reverted.

Contains changes from #8053, #7802.

The messages below only make sense in the context of the Settings UI,
which this pull request does not bring in. They do, however, provide
valuable information.

From #7802 (@leonMSFT):

> This PR's goal was to add an option to the `OpenSettings` keybinding to
> open the Settings UI in a tab. In order to implement that, a couple of
> changes had to be made to `Tab`, specifically:
>
> - Introduce a tab interface named `ITab`
> - Create/Rename two new Tab classes that implement `ITab` called
>   `SettingsTab` and `TerminalTab`
>

From #8053:

> `TerminalTab` and `SettingsTab` share some implementation details. The
> close submenu introduced in #7728 is a good example of functionality
> that is consistent across all tabs. This PR transforms `ITab` from an
> interface, into an [unsealed runtime class] to de-duplicate some
> functionality. Most of the logic from `SettingsTab` was moved there
> because I expect the default behavior of a tab to resemble the
> `SettingsTab` over a `TerminalTab`.
>
> ## References
> Verified that Close submenu work was transferred over (#7728, #7961, #8010).
>
> ## Validation Steps Performed
> Check close submenu on first/last tab when multiple tabs are open.
>
> Closes #7969
>
> [unsealed runtime class]: https://docs.microsoft.com/en-us/uwp/midl-3/intro#base-classes

Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>

Co-authored-by: Leon Liang <lelian@microsoft.com>
Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
2020-11-04 10:15:05 -08:00

543 lines
18 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "App.h"
#include "TerminalPage.h"
#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::TerminalControl;
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)
{
_DuplicateTabViewItem();
args.Handled(true);
}
void TerminalPage::_HandleCloseTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_CloseFocusedTab();
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)
{
_SelectNextTab(true);
args.Handled(true);
}
void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_SelectNextTab(false);
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>())
{
const auto termControl = _GetActiveControl();
termControl.SendInput(realArgs.Input());
args.Handled(true);
}
}
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(), realArgs.TerminalArgs());
args.Handled(true);
}
}
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (auto focusedTab = _GetFocusedTab())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
// Don't do anything if there's only one pane. It's already zoomed.
if (activeTab && 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::_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::_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)
{
_OpenNewTab(nullptr);
args.Handled(true);
}
else if (const auto& realArgs = args.ActionArgs().try_as<NewTabArgs>())
{
_OpenNewTab(realArgs.TerminalArgs());
args.Handled(true);
}
}
void TerminalPage::_HandleSwitchToTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SwitchToTabArgs>())
{
const auto handled = _SelectTab({ realArgs.TabIndex() });
args.Handled(handled);
}
}
void TerminalPage::_HandleResizePane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ResizePaneArgs>())
{
if (realArgs.Direction() == Direction::None)
{
// Do nothing
args.Handled(false);
}
else
{
_ResizePane(realArgs.Direction());
args.Handled(true);
}
}
}
void TerminalPage::_HandleMoveFocus(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<MoveFocusArgs>())
{
if (realArgs.Direction() == Direction::None)
{
// Do nothing
args.Handled(false);
}
else
{
_MoveFocus(realArgs.Direction());
args.Handled(true);
}
}
}
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>())
{
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)
{
const auto termControl = _GetActiveControl();
termControl.ResetFontSize();
args.Handled(true);
}
void TerminalPage::_HandleToggleRetroEffect(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
const auto termControl = _GetActiveControl();
termControl.ToggleRetroEffect();
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)
{
// TODO GH#6677: When we add support for commandline mode, first set the
// mode that the command palette should be in, before making it visible.
CommandPalette().EnableCommandPaletteMode();
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 (auto focusedTab = _GetFocusedTab())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
if (auto activeControl = activeTab->GetActiveTerminalControl())
{
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
{
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
controlSettings->ApplyColorScheme(scheme);
activeControl.UpdateSettings(*controlSettings);
args.Handled(true);
}
}
}
}
}
}
void TerminalPage::_HandleSetTabColor(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
std::optional<til::color> tabColor;
if (const auto& realArgs = args.ActionArgs().try_as<SetTabColorArgs>())
{
if (realArgs.TabColor() != nullptr)
{
tabColor = realArgs.TabColor().Value();
}
}
if (auto focusedTab = _GetFocusedTab())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
if (tabColor.has_value())
{
activeTab->SetRuntimeTabColor(tabColor.value());
}
else
{
activeTab->ResetRuntimeTabColor();
}
}
}
args.Handled(true);
}
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (auto focusedTab = _GetFocusedTab())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
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 (auto focusedTab = _GetFocusedTab())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
if (title.has_value())
{
activeTab->SetTabText(title.value());
}
else
{
activeTab->ResetTabText();
}
}
}
args.Handled(true);
}
void TerminalPage::_HandleOpenTabRenamer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (auto focusedTab = _GetFocusedTab())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
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;
}
// Remove tabs after the current one
while (_tabs.Size() > index + 1)
{
_RemoveTabViewItemByIndex(_tabs.Size() - 1);
}
// Remove all of them leading up to the selected tab
while (_tabs.Size() > 1)
{
_RemoveTabViewItemByIndex(0);
}
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;
}
// Remove tabs after the current one
while (_tabs.Size() > index + 1)
{
_RemoveTabViewItemByIndex(_tabs.Size() - 1);
}
// 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::_HandleOpenTabSearch(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
// Tab search is always in-order.
auto tabCommands = winrt::single_threaded_vector<Command>();
for (const auto& tab : _tabs)
{
tabCommands.Append(tab.SwitchToTabCommand());
}
CommandPalette().SetTabActions(tabCommands);
auto opt = _GetFocusedTabIndex();
uint32_t startIdx = opt.value_or(0);
CommandPalette().EnableTabSwitcherMode(true, startIdx);
CommandPalette().Visibility(Visibility::Visible);
args.Handled(true);
}
}