Add settings entry into titlebar context menu (#11404)

<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Adds ability for app to change system context menu

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #9666 
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
Sergey 2021-11-04 19:47:58 +03:00 committed by GitHub
parent 8826cc028b
commit ab6ba9bdbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 136 additions and 4 deletions

View File

@ -9,6 +9,7 @@ BUILDBRANCH
BUILDMSG
BUILDNUMBER
BYPOSITION
BYCOMMAND
charconv
CLASSNOTAVAILABLE
cmdletbinding
@ -85,6 +86,7 @@ LSHIFT
MENUCOMMAND
MENUDATA
MENUINFO
MENUITEMINFOW
memicmp
mptt
mov

View File

@ -334,6 +334,9 @@ namespace winrt::TerminalApp::implementation
_RefreshThemeRoutine();
_ApplyStartupTaskStateChange();
auto args = winrt::make_self<SystemMenuChangeArgs>(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &AppLogic::_OpenSettingsUI));
_SystemMenuChangeRequestedHandlers(*this, *args);
TraceLoggingWrite(
g_hTerminalAppProvider,
"AppCreated",
@ -1051,6 +1054,11 @@ namespace winrt::TerminalApp::implementation
_SettingsChangedHandlers(*this, nullptr);
}
void AppLogic::_OpenSettingsUI()
{
_root->OpenSettingsUI();
}
// Method Description:
// - Returns a pointer to the global shared settings.
[[nodiscard]] CascadiaSettings AppLogic::GetSettings() const noexcept

View File

@ -5,6 +5,7 @@
#include "AppLogic.g.h"
#include "FindTargetWindowResult.g.h"
#include "SystemMenuChangeArgs.g.h"
#include "Jumplist.h"
#include "LanguageProfileNotifier.h"
#include "TerminalPage.h"
@ -35,6 +36,17 @@ namespace winrt::TerminalApp::implementation
FindTargetWindowResult(id, L""){};
};
struct SystemMenuChangeArgs : SystemMenuChangeArgsT<SystemMenuChangeArgs>
{
WINRT_PROPERTY(winrt::hstring, Name, L"");
WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add);
WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr);
public:
SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) :
_Name{ name }, _Action{ action }, _Handler{ handler } {};
};
struct AppLogic : AppLogicT<AppLogic, IInitializeWithWindow>
{
public:
@ -113,6 +125,7 @@ namespace winrt::TerminalApp::implementation
// -------------------------------- WinRT Events ---------------------------------
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme);
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
private:
bool _isUwp{ false };
@ -163,6 +176,7 @@ namespace winrt::TerminalApp::implementation
void _RegisterSettingsChange();
fire_and_forget _DispatchReloadSettings();
void _ReloadSettings();
void _OpenSettingsUI();
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);

View File

@ -19,6 +19,20 @@ namespace TerminalApp
String WindowName { get; };
};
delegate void SystemMenuItemHandler();
enum SystemMenuChangeAction
{
Add = 0,
Remove = 1
};
[default_interface] runtimeclass SystemMenuChangeArgs {
String Name { get; };
SystemMenuChangeAction Action { get; };
SystemMenuItemHandler Handler { get; };
};
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter
{
AppLogic();
@ -110,5 +124,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
}
}

View File

@ -2111,7 +2111,7 @@ namespace winrt::TerminalApp::implementation
{
if (target == SettingsTarget::SettingsUI)
{
_OpenSettingsUI();
OpenSettingsUI();
}
else
{
@ -2759,7 +2759,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalPage::_OpenSettingsUI()
void TerminalPage::OpenSettingsUI()
{
// If we're holding the settings tab's switch command, don't create a new one, switch to the existing one.
if (!_settingsTab)

View File

@ -121,6 +121,8 @@ namespace winrt::TerminalApp::implementation
bool IsQuakeWindow() const noexcept;
bool IsElevated() const noexcept;
void OpenSettingsUI();
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// -------------------------------- WinRT Events ---------------------------------
@ -366,8 +368,6 @@ namespace winrt::TerminalApp::implementation
void _UnZoomIfNeeded();
void _OpenSettingsUI();
static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll);
static uint32_t _ReadSystemRowsToScroll();

View File

@ -325,6 +325,7 @@ void AppHost::Initialize()
_logic.FocusModeChanged({ this, &AppHost::_FocusModeChanged });
_logic.AlwaysOnTopChanged({ this, &AppHost::_AlwaysOnTopChanged });
_logic.RaiseVisualBell({ this, &AppHost::_RaiseVisualBell });
_logic.SystemMenuChangeRequested({ this, &AppHost::_SystemMenuChangeRequested });
_logic.Create();
@ -1248,6 +1249,27 @@ void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&,
_window->OpenSystemMenu(std::nullopt, std::nullopt);
}
void AppHost::_SystemMenuChangeRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::TerminalApp::SystemMenuChangeArgs& args)
{
switch (args.Action())
{
case winrt::TerminalApp::SystemMenuChangeAction::Add:
{
auto handler = args.Handler();
_window->AddToSystemMenu(args.Name(), [handler]() { handler(); });
break;
}
case winrt::TerminalApp::SystemMenuChangeAction::Remove:
{
_window->RemoveFromSystemMenu(args.Name());
break;
}
default:
{
}
}
}
// Method Description:
// - Creates a Notification Icon and hooks up its handlers
// Arguments:

View File

@ -96,6 +96,9 @@ private:
void _OpenSystemMenu(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _SystemMenuChangeRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::TerminalApp::SystemMenuChangeArgs& args);
winrt::fire_and_forget _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);

View File

@ -24,6 +24,7 @@ using namespace ::Microsoft::Console::Types;
using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers;
#define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS"
#define IDM_SYSTEM_MENU_BEGIN 0x1000
const UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
@ -321,6 +322,8 @@ void IslandWindow::Initialize()
}
}
_systemMenuNextItemId = IDM_SYSTEM_MENU_BEGIN;
// Enable vintage opacity by removing the XAML emergency backstop, GH#603.
// We don't really care if this failed or not.
TerminalTrySetTransparentBackground(true);
@ -608,6 +611,15 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
_NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam);
return 0;
}
case WM_SYSCOMMAND:
{
auto search = _systemMenuItems.find(LOWORD(wparam));
if (search != _systemMenuItems.end())
{
search->second.callback();
}
break;
}
default:
// We'll want to receive this message when explorer.exe restarts
// so that we can re-add our icon to the notification area.
@ -1717,5 +1729,50 @@ void IslandWindow::OpenSystemMenu(const std::optional<int> mouseX, const std::op
}
}
void IslandWindow::AddToSystemMenu(const winrt::hstring& itemLabel, winrt::delegate<void()> callback)
{
const HMENU systemMenu = GetSystemMenu(_window.get(), FALSE);
UINT 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>(itemLabel.c_str());
item.cch = static_cast<UINT>(itemLabel.size());
if (LOG_LAST_ERROR_IF(!InsertMenuItemW(systemMenu, wID, FALSE, &item)))
{
return;
}
_systemMenuItems.insert({ wID, { itemLabel, callback } });
_systemMenuNextItemId++;
}
void IslandWindow::RemoveFromSystemMenu(const winrt::hstring& itemLabel)
{
const HMENU systemMenu = GetSystemMenu(_window.get(), FALSE);
int itemCount = GetMenuItemCount(systemMenu);
if (LOG_LAST_ERROR_IF(itemCount == -1))
{
return;
}
auto it = std::find_if(_systemMenuItems.begin(), _systemMenuItems.end(), [&itemLabel](const std::pair<UINT, SystemMenuItemInfo>& elem) {
return elem.second.label == itemLabel;
});
if (it == _systemMenuItems.end())
{
return;
}
if (LOG_LAST_ERROR_IF(!DeleteMenu(systemMenu, it->first, MF_BYCOMMAND)))
{
return;
}
_systemMenuItems.erase(it->first);
}
DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);

View File

@ -8,6 +8,12 @@
void SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept;
struct SystemMenuItemInfo
{
winrt::hstring label;
winrt::delegate<void()> callback;
};
class IslandWindow :
public BaseWindow<IslandWindow>
{
@ -54,6 +60,8 @@ public:
void SetMinimizeToNotificationAreaBehavior(bool MinimizeToNotificationArea) noexcept;
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 RemoveFromSystemMenu(const winrt::hstring& itemLabel);
DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
@ -131,6 +139,9 @@ protected:
bool _minimizeToNotificationArea{ false };
std::unordered_map<UINT, SystemMenuItemInfo> _systemMenuItems;
UINT _systemMenuNextItemId;
private:
// This minimum width allows for width the tabs fit
static constexpr long minimumWidth = 460L;