Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/cascadia/WindowsTerminal/AppHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ void AppHost::Initialize()
// set that content as well.
_window->SetContent(_windowLogic.GetRoot());
_window->OnAppInitialized();

_PopulateNewTabSubmenu();
}

void AppHost::Close()
Expand Down Expand Up @@ -1022,6 +1024,9 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
_window->SetAutoHideWindow(_windowLogic.AutoHideWindow());
_window->SetShowTabsFullscreen(_windowLogic.ShowTabsFullscreen());
_updateTheme();

// Rebuild the "New Tab" submenu in the system menu to reflect any profile changes
_PopulateNewTabSubmenu();
}

void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&,
Expand Down Expand Up @@ -1091,6 +1096,73 @@ void AppHost::_SystemMenuChangeRequested(const winrt::Windows::Foundation::IInsp
}
}

// Method Description:
// - Populates a "New Tab" submenu in the system menu (Alt+Space) with
// the user's active profiles.
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppHost::_PopulateNewTabSubmenu()
{
if (!_window || !_windowLogic)
{
return;
}

_window->RemoveSystemSubMenu(L"New Tab");

const auto settings = _appLogic.Settings();
if (!settings)
{
return;
}

const auto activeProfiles = settings.ActiveProfiles();
if (!activeProfiles || activeProfiles.Size() == 0)
{
return;
}

const auto defaultProfileGuid = settings.GlobalSettings().DefaultProfile();

std::vector<std::pair<winrt::hstring, winrt::delegate<void()>>> profileItems;
profileItems.reserve(activeProfiles.Size());

for (uint32_t i = 0; i < activeProfiles.Size(); i++)
{
const auto profile = activeProfiles.GetAt(i);
auto profileName = profile.Name();

if (profile.Guid() == defaultProfileGuid)
{
profileName = profileName + L" (default)";
}

auto callback = winrt::delegate<void()>([weakThis = weak_from_this(), name = winrt::hstring{ profileName }]() {
if (auto strongThis = weakThis.lock())
{
if (strongThis->_windowLogic)
{
const std::array<winrt::hstring, 4> args{
winrt::hstring{ L"wt.exe" },
winrt::hstring{ L"new-tab" },
winrt::hstring{ L"--profile" },
name,
};
winrt::TerminalApp::CommandlineArgs cmdArgs;
cmdArgs.Commandline(args);
strongThis->_windowLogic.ExecuteCommandline(std::move(cmdArgs));
}
}
});

Comment thread
etbala marked this conversation as resolved.
Outdated
profileItems.emplace_back(std::move(profileName), std::move(callback));
}

_window->AddSystemSubMenu(L"New Tab", profileItems);
}

// Method Description:
// - BODGY workaround for GH#9320. When the window moves, dismiss all the popups
// in the UI tree. Xaml Islands unfortunately doesn't do this for us, see
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/WindowsTerminal/AppHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ class AppHost : public std::enable_shared_from_this<AppHost>
void _HandleRequestLaunchPosition(const winrt::Windows::Foundation::IInspectable& sender,
winrt::TerminalApp::LaunchPositionRequest args);

void _PopulateNewTabSubmenu();

// Helper struct. By putting these all into one struct, we can revoke them
// all at once, by assigning _revokers to a fresh Revokers instance. That'll
// cause us to dtor the old one, which will immediately call revoke on all
Expand Down
93 changes: 93 additions & 0 deletions src/cascadia/WindowsTerminal/IslandWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,52 @@
_systemMenuNextItemId++;
}

void IslandWindow::AddSystemSubMenu(const winrt::hstring& menuLabel, const std::vector<std::pair<winrt::hstring, winrt::delegate<void()>>>& items)
{

const auto systemMenu = GetSystemMenu(_window.get(), FALSE);

HMENU subMenu = CreatePopupMenu();
if (!subMenu)
{
LOG_LAST_ERROR();
return;
}

for (const auto& [label, callback] : items)
{
auto wID = _systemMenuNextItemId;

MENUITEMINFOW item;
item.cbSize = sizeof(MENUITEMINFOW);
item.fMask = MIIM_STATE | MIIM_ID | MIIM_STRING;
item.fState = MF_ENABLED;
item.wID = wID;
item.dwTypeData = const_cast<LPWSTR>(label.c_str());
item.cch = static_cast<UINT>(label.size());

if (LOG_LAST_ERROR_IF(!InsertMenuItemW(subMenu, wID, FALSE, &item)))
{
continue;
}
_systemMenuItems.insert({ wID, { label, callback } });
_systemMenuNextItemId++;
}

MENUITEMINFOW subMenuItem;
subMenuItem.cbSize = sizeof(MENUITEMINFOW);
subMenuItem.fMask = MIIM_STRING | MIIM_SUBMENU;
subMenuItem.hSubMenu = subMenu;
subMenuItem.dwTypeData = const_cast<LPWSTR>(menuLabel.c_str());
subMenuItem.cch = static_cast<UINT>(menuLabel.size());

if (LOG_LAST_ERROR_IF(!InsertMenuItemW(systemMenu, _systemMenuNextItemId, FALSE, &subMenuItem)))
{
DestroyMenu(subMenu);
return;
}
}

void IslandWindow::RemoveFromSystemMenu(const winrt::hstring& itemLabel)
{
const auto systemMenu = GetSystemMenu(_window.get(), FALSE);
Expand All @@ -1832,6 +1878,53 @@
_systemMenuItems.erase(it->first);
}

// Method Description:
// - Removes a submenu from the system menu by its label. Also removes all
// child items from the _systemMenuItems tracking map. The submenu HMENU
// is destroyed automatically by DeleteMenu.
// Arguments:
// - menuLabel: The label of the submenu to remove.
void IslandWindow::RemoveSystemSubMenu(const winrt::hstring& menuLabel)
{
const auto systemMenu = GetSystemMenu(_window.get(), FALSE);
const auto itemCount = GetMenuItemCount(systemMenu);
if (LOG_LAST_ERROR_IF(itemCount == -1))
{
return;
}

for (int i = 0; i < itemCount; i++)
{
wchar_t buffer[256]{};
MENUITEMINFOW mii{};
mii.cbSize = sizeof(MENUITEMINFOW);
mii.fMask = MIIM_STRING | MIIM_SUBMENU;
mii.dwTypeData = buffer;
mii.cch = ARRAYSIZE(buffer);

if (!GetMenuItemInfoW(systemMenu, static_cast<UINT>(i), TRUE, &mii))
{
continue;
}

if (mii.hSubMenu && menuLabel == std::wstring_view{ buffer })
{
const auto subItemCount = GetMenuItemCount(mii.hSubMenu);
for (int j = 0; j < subItemCount; j++)
{
const auto subItemId = GetMenuItemID(mii.hSubMenu, j);
if (subItemId != static_cast<UINT>(-1))
{
_systemMenuItems.erase(subItemId);
}
}

DeleteMenu(systemMenu, static_cast<UINT>(i), MF_BYPOSITION);
Comment thread Fixed
return;
}
}
}

void IslandWindow::_resetSystemMenu()
{
// GetSystemMenu(..., true) will revert the menu to the default state.
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/WindowsTerminal/IslandWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ class IslandWindow :

void OpenSystemMenu(const std::optional<int> mouseX, const std::optional<int> mouseY) const noexcept;
void AddToSystemMenu(const winrt::hstring& itemLabel, winrt::delegate<void()> callback);
void AddSystemSubMenu(const winrt::hstring& menuLabel, const std::vector<std::pair<winrt::hstring, winrt::delegate<void()>>>& items);
void RemoveFromSystemMenu(const winrt::hstring& itemLabel);
void RemoveSystemSubMenu(const winrt::hstring& menuLabel);

void UseDarkTheme(const bool v);
virtual void UseMica(const bool newValue, const double titlebarOpacity);
Expand Down
Loading