Skip to content

Commit

Permalink
Allow actions in the new tab dropdown (#17281)
Browse files Browse the repository at this point in the history
Allows the user to define entries in the new tab menu that execute
actions, based on their action Id

Closes #3759
Closes #9362
  • Loading branch information
PankajBhojwani authored Jun 6, 2024
1 parent d6b6aac commit aeed078
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 4 deletions.
28 changes: 27 additions & 1 deletion doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,8 @@
"folder",
"separator",
"remainingProfiles",
"matchProfiles"
"matchProfiles",
"action"
]
},
"NewTabMenuEntry": {
Expand Down Expand Up @@ -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": [
{
Expand Down Expand Up @@ -2054,6 +2077,9 @@
},
{
"$ref": "#/$defs/RemainingProfilesEntry"
},
{
"$ref": "#/$defs/ActionEntry"
}
]
}
Expand Down
47 changes: 47 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
36 changes: 36 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionEntry.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 37 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionEntry.h
Original file line number Diff line number Diff line change
@@ -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);
}
5 changes: 5 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/ActionMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalSettingsModel/ActionMap.idl
Original file line number Diff line number Diff line change
Expand Up @@ -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; };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalSettingsModel/NewTabMenuEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "SeparatorEntry.h"
#include "FolderEntry.h"
#include "ProfileEntry.h"
#include "ActionEntry.h"
#include "RemainingProfilesEntry.h"
#include "MatchProfilesEntry.h"

Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 9 additions & 1 deletion src/cascadia/TerminalSettingsModel/NewTabMenuEntry.idl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ namespace Microsoft.Terminal.Settings.Model
Separator,
Folder,
RemainingProfiles,
MatchProfiles
MatchProfiles,
Action
};

[default_interface] unsealed runtimeclass NewTabMenuEntry
Expand All @@ -34,6 +35,13 @@ namespace Microsoft.Terminal.Settings.Model
Int32 ProfileIndex;
}

[default_interface] runtimeclass ActionEntry : NewTabMenuEntry
{
ActionEntry();

String ActionId;
}

enum FolderEntryInlining
{
Never = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

0 comments on commit aeed078

Please sign in to comment.