mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Allow actions in the new tab dropdown (#17281)
Allows the user to define entries in the new tab menu that execute actions, based on their action Id Closes #3759 Closes #9362
This commit is contained in:
parent
d6b6aacb4f
commit
aeed0782bc
@ -629,7 +629,8 @@
|
||||
"folder",
|
||||
"separator",
|
||||
"remainingProfiles",
|
||||
"matchProfiles"
|
||||
"matchProfiles",
|
||||
"action"
|
||||
]
|
||||
},
|
||||
"NewTabMenuEntry": {
|
||||
@ -781,6 +782,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ActionEntry": {
|
||||
"description": "An action in the new tab dropdown",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "action"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The ID of the action to show in this entry"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SwitchToAdjacentTabArgs": {
|
||||
"oneOf": [
|
||||
{
|
||||
@ -2054,6 +2077,9 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/RemainingProfilesEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/ActionEntry"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1004,6 +1004,18 @@ namespace winrt::TerminalApp::implementation
|
||||
items.push_back(profileItem);
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Action:
|
||||
{
|
||||
const auto actionEntry = entry.as<ActionEntry>();
|
||||
const auto actionId = actionEntry.ActionId();
|
||||
if (_settings.ActionMap().GetActionByID(actionId))
|
||||
{
|
||||
auto actionItem = _CreateNewTabFlyoutAction(actionId);
|
||||
items.push_back(actionItem);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1094,6 +1106,41 @@ namespace winrt::TerminalApp::implementation
|
||||
return profileMenuItem;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method creates a flyout menu item for a given action
|
||||
// It makes sure to set the correct icon, keybinding, and click-action.
|
||||
WUX::Controls::MenuFlyoutItem TerminalPage::_CreateNewTabFlyoutAction(const winrt::hstring& actionId)
|
||||
{
|
||||
auto actionMenuItem = WUX::Controls::MenuFlyoutItem{};
|
||||
const auto action{ _settings.ActionMap().GetActionByID(actionId) };
|
||||
const auto actionKeyChord{ _settings.ActionMap().GetKeyBindingForAction(actionId) };
|
||||
|
||||
if (actionKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(actionMenuItem, actionKeyChord);
|
||||
}
|
||||
|
||||
actionMenuItem.Text(action.Name());
|
||||
|
||||
// If there's an icon set for this action, set it as the icon for
|
||||
// this flyout item
|
||||
const auto& iconPath = action.IconPath();
|
||||
if (!iconPath.empty())
|
||||
{
|
||||
const auto icon = _CreateNewTabFlyoutIcon(iconPath);
|
||||
actionMenuItem.Icon(icon);
|
||||
}
|
||||
|
||||
actionMenuItem.Click([action, weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->_actionDispatch->DoAction(action.ActionAndArgs());
|
||||
}
|
||||
});
|
||||
|
||||
return actionMenuItem;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method to create an IconElement that can be passed to MenuFlyoutItems and
|
||||
// MenuFlyoutSubItems
|
||||
|
||||
@ -300,6 +300,7 @@ namespace winrt::TerminalApp::implementation
|
||||
std::vector<winrt::Windows::UI::Xaml::Controls::MenuFlyoutItemBase> _CreateNewTabFlyoutItems(winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::NewTabMenuEntry> entries);
|
||||
winrt::Windows::UI::Xaml::Controls::IconElement _CreateNewTabFlyoutIcon(const winrt::hstring& icon);
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutProfile(const Microsoft::Terminal::Settings::Model::Profile profile, int profileIndex);
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutAction(const winrt::hstring& actionId);
|
||||
|
||||
void _OpenNewTabDropdown();
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::INewContentArgs& newContentArgs);
|
||||
|
||||
36
src/cascadia/TerminalSettingsModel/ActionEntry.cpp
Normal file
36
src/cascadia/TerminalSettingsModel/ActionEntry.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ActionEntry.h"
|
||||
#include "JsonUtils.h"
|
||||
|
||||
#include "ActionEntry.g.cpp"
|
||||
|
||||
using namespace Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
|
||||
static constexpr std::string_view ActionIdKey{ "id" };
|
||||
|
||||
ActionEntry::ActionEntry() noexcept :
|
||||
ActionEntryT<ActionEntry, NewTabMenuEntry>(NewTabMenuEntryType::Action)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Value ActionEntry::ToJson() const
|
||||
{
|
||||
auto json = NewTabMenuEntry::ToJson();
|
||||
|
||||
JsonUtils::SetValueForKey(json, ActionIdKey, _ActionId);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
winrt::com_ptr<NewTabMenuEntry> ActionEntry::FromJson(const Json::Value& json)
|
||||
{
|
||||
auto entry = winrt::make_self<ActionEntry>();
|
||||
|
||||
JsonUtils::GetValueForKey(json, ActionIdKey, entry->_ActionId);
|
||||
|
||||
return entry;
|
||||
}
|
||||
37
src/cascadia/TerminalSettingsModel/ActionEntry.h
Normal file
37
src/cascadia/TerminalSettingsModel/ActionEntry.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- ActionEntry.h
|
||||
|
||||
Abstract:
|
||||
- An action entry in the "new tab" dropdown menu
|
||||
|
||||
Author(s):
|
||||
- Pankaj Bhojwani - May 2024
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "NewTabMenuEntry.h"
|
||||
#include "ActionEntry.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
struct ActionEntry : ActionEntryT<ActionEntry, NewTabMenuEntry>
|
||||
{
|
||||
public:
|
||||
ActionEntry() noexcept;
|
||||
|
||||
Json::Value ToJson() const override;
|
||||
static com_ptr<NewTabMenuEntry> FromJson(const Json::Value& json);
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, ActionId);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ActionEntry);
|
||||
}
|
||||
@ -596,6 +596,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return _GetActionByKeyChordInternal(keys).value_or(nullptr);
|
||||
}
|
||||
|
||||
Model::Command ActionMap::GetActionByID(const winrt::hstring& cmdID) const
|
||||
{
|
||||
return _GetActionByID(cmdID);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves the assigned command ID with the given key chord.
|
||||
// - Can return nullopt to differentiate explicit unbinding vs lack of binding.
|
||||
|
||||
@ -60,6 +60,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
// queries
|
||||
Model::Command GetActionByKeyChord(const Control::KeyChord& keys) const;
|
||||
Model::Command GetActionByID(const winrt::hstring& cmdID) const;
|
||||
bool IsKeyChordExplicitlyUnbound(const Control::KeyChord& keys) const;
|
||||
Control::KeyChord GetKeyBindingForAction(const winrt::hstring& cmdID);
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Boolean IsKeyChordExplicitlyUnbound(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
Command GetActionByKeyChord(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
Command GetActionByID(String cmdID);
|
||||
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(String cmdID);
|
||||
|
||||
Windows.Foundation.Collections.IMapView<String, ActionAndArgs> AvailableActions { get; };
|
||||
|
||||
@ -28,6 +28,9 @@
|
||||
<ClInclude Include="SeparatorEntry.h">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ActionEntry.h">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FolderEntry.h">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@ -185,6 +188,9 @@
|
||||
<ClCompile Include="SeparatorEntry.cpp">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ActionEntry.cpp">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FolderEntry.cpp">
|
||||
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "SeparatorEntry.h"
|
||||
#include "FolderEntry.h"
|
||||
#include "ProfileEntry.h"
|
||||
#include "ActionEntry.h"
|
||||
#include "RemainingProfilesEntry.h"
|
||||
#include "MatchProfilesEntry.h"
|
||||
|
||||
@ -52,6 +53,8 @@ winrt::com_ptr<NewTabMenuEntry> NewTabMenuEntry::FromJson(const Json::Value& jso
|
||||
return RemainingProfilesEntry::FromJson(json);
|
||||
case NewTabMenuEntryType::MatchProfiles:
|
||||
return MatchProfilesEntry::FromJson(json);
|
||||
case NewTabMenuEntryType::Action:
|
||||
return ActionEntry::FromJson(json);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -12,7 +12,8 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Separator,
|
||||
Folder,
|
||||
RemainingProfiles,
|
||||
MatchProfiles
|
||||
MatchProfiles,
|
||||
Action
|
||||
};
|
||||
|
||||
[default_interface] unsealed runtimeclass NewTabMenuEntry
|
||||
@ -34,6 +35,13 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Int32 ProfileIndex;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ActionEntry : NewTabMenuEntry
|
||||
{
|
||||
ActionEntry();
|
||||
|
||||
String ActionId;
|
||||
}
|
||||
|
||||
enum FolderEntryInlining
|
||||
{
|
||||
Never = 0,
|
||||
|
||||
@ -678,8 +678,9 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollToMarkDirection)
|
||||
// Possible NewTabMenuEntryType values
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::NewTabMenuEntryType)
|
||||
{
|
||||
JSON_MAPPINGS(5) = {
|
||||
JSON_MAPPINGS(6) = {
|
||||
pair_type{ "profile", ValueType::Profile },
|
||||
pair_type{ "action", ValueType::Action },
|
||||
pair_type{ "separator", ValueType::Separator },
|
||||
pair_type{ "folder", ValueType::Folder },
|
||||
pair_type{ "remainingProfiles", ValueType::RemainingProfiles },
|
||||
|
||||
@ -38,6 +38,9 @@
|
||||
<ClInclude Include="../Profile.h" />
|
||||
<ClInclude Include="../TerminalWarnings.h" />
|
||||
<ClInclude Include="../NewTabMenuEntry.h" />
|
||||
<ClInclude Include="../ActionEntry.h">
|
||||
<DependentUpon>../NewTabMenuEntry.h</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../SeparatorEntry.h">
|
||||
<DependentUpon>../NewTabMenuEntry.h</DependentUpon>
|
||||
</ClInclude>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user