From 96f4a9daef8f679feb820444c4bb182bf9894499 Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Thu, 8 Jul 2021 08:25:43 -0700 Subject: [PATCH] Add tray icon when quake window is minimized (#10179) This PR is a small start in a broader "Minimize to Tray" feature (#5727). This particular change is scoped only to the scenario when a quake window is minimized. Currently the only way to bring back the quake window when it's minimized is to press the global hotkey again. This gives another option - to press the terminal icon in the tray. Eventually though, minimize to tray will be available for any window, and I'd like more time to flesh out the general porpoise scenarios and context menus. Having just a bit in this PR also helps reviewers by keeping it small! --- .github/actions/spelling/allow/apis.txt | 7 ++ .github/actions/spelling/expect/expect.txt | 2 + src/cascadia/WinRTUtils/WinRTUtils.vcxproj | 2 +- .../{ => inc}/ScopedResourceLoader.h | 0 src/cascadia/WindowsTerminal/AppHost.cpp | 83 ++++++++++++++++++- src/cascadia/WindowsTerminal/AppHost.h | 5 ++ src/cascadia/WindowsTerminal/BaseWindow.h | 4 +- .../WindowsTerminal/CustomWindowMessages.h | 8 ++ src/cascadia/WindowsTerminal/IslandWindow.cpp | 14 ++++ src/cascadia/WindowsTerminal/IslandWindow.h | 2 + .../WindowsTerminal/WindowsTerminal.vcxproj | 5 ++ src/cascadia/WindowsTerminal/icon.cpp | 19 +++-- src/cascadia/WindowsTerminal/icon.h | 1 + src/cascadia/WindowsTerminal/pch.h | 1 + 14 files changed, 141 insertions(+), 12 deletions(-) rename src/cascadia/WinRTUtils/{ => inc}/ScopedResourceLoader.h (100%) create mode 100644 src/cascadia/WindowsTerminal/CustomWindowMessages.h diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 032570e55..7bd696c07 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -68,6 +68,7 @@ ITab ITaskbar IUri IVirtual +KEYSELECT LCID llabs llu @@ -80,12 +81,16 @@ MULTIPLEUSE NCHITTEST NCLBUTTONDBLCLK NCRBUTTONDBLCLK +NIF +NIN NOAGGREGATION NOASYNC NOCHANGEDIR NOPROGRESS NOREDIRECTIONBITMAP NOREPEAT +NOTIFYICON +NOTIFYICONDATA ntprivapi oaidl ocidl @@ -108,9 +113,11 @@ RSHIFT schandle semver serializer +SETVERSION SHELLEXECUTEINFOW shobjidl SHOWMINIMIZED +SHOWTIP SINGLEUSE SIZENS smoothstep diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 00fe5ab1e..ef0a37c6d 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2773,6 +2773,7 @@ Xes xff XFile XFORM +xIcon XManifest XMath XMFLOAT @@ -2806,6 +2807,7 @@ YCast YCENTER YCount YDPI +yIcon yml YOffset YPosition diff --git a/src/cascadia/WinRTUtils/WinRTUtils.vcxproj b/src/cascadia/WinRTUtils/WinRTUtils.vcxproj index be5cee9e0..2bd60e143 100644 --- a/src/cascadia/WinRTUtils/WinRTUtils.vcxproj +++ b/src/cascadia/WinRTUtils/WinRTUtils.vcxproj @@ -15,7 +15,7 @@ - + diff --git a/src/cascadia/WinRTUtils/ScopedResourceLoader.h b/src/cascadia/WinRTUtils/inc/ScopedResourceLoader.h similarity index 100% rename from src/cascadia/WinRTUtils/ScopedResourceLoader.h rename to src/cascadia/WinRTUtils/inc/ScopedResourceLoader.h diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 753166bf1..8566f2219 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -9,6 +9,9 @@ #include "../WinRTUtils/inc/WtExeUtils.h" #include "resource.h" #include "VirtualDesktopUtils.h" +#include "icon.h" + +#include using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Composition; @@ -77,9 +80,15 @@ AppHost::AppHost() noexcept : _window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled }); _window->WindowActivated({ this, &AppHost::_WindowActivated }); _window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed }); + _window->NotifyTrayIconPressed({ this, &AppHost::_HandleTrayIconPressed }); _window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); _window->MakeWindow(); + if (_window->IsQuakeWindow()) + { + _UpdateTrayIcon(); + } + _windowManager.BecameMonarch({ this, &AppHost::_BecomeMonarch }); if (_windowManager.IsMonarch()) { @@ -90,6 +99,11 @@ AppHost::AppHost() noexcept : AppHost::~AppHost() { // destruction order is important for proper teardown here + if (_trayIconData) + { + Shell_NotifyIcon(NIM_DELETE, &_trayIconData.value()); + _trayIconData.reset(); + } _window = nullptr; _app.Close(); @@ -925,12 +939,26 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { + if (_window->IsQuakeWindow() && !_logic.IsQuakeWindow()) + { + // If we're exiting quake mode, we should make our + // tray icon disappear. + if (_trayIconData) + { + Shell_NotifyIcon(NIM_DELETE, &_trayIconData.value()); + _trayIconData.reset(); + } + } + else if (!_window->IsQuakeWindow() && _logic.IsQuakeWindow()) + { + _UpdateTrayIcon(); + } + _window->IsQuakeWindow(_logic.IsQuakeWindow()); } void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable&) - { const Remoting::SummonWindowBehavior summonArgs{}; summonArgs.MoveToCurrentDesktop(false); @@ -939,3 +967,56 @@ void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspecta summonArgs.ToggleVisibility(false); // Do not toggle, just make visible. _HandleSummon(sender, summonArgs); } + +void AppHost::_HandleTrayIconPressed() +{ + // Currently scoping "minimize to tray" to only + // the quake window. + if (_logic.IsQuakeWindow()) + { + const Remoting::SummonWindowBehavior summonArgs{}; + summonArgs.DropdownDuration(200); + _window->SummonWindow(summonArgs); + } +} + +// Method Description: +// - Creates and adds an icon to the notification tray. +// Arguments: +// - +// Return Value: +// - +void AppHost::_UpdateTrayIcon() +{ + if (!_trayIconData && _window->GetHandle()) + { + NOTIFYICONDATA nid{}; + + // This HWND will receive the callbacks sent by the tray icon. + nid.hWnd = _window->GetHandle(); + + // App-defined identifier of the icon. The HWND and ID are used + // to identify which icon to operate on when calling Shell_NotifyIcon. + // Multiple icons can be associated with one HWND, but here we're only + // going to be showing one so the ID doesn't really matter. + nid.uID = 1; + + nid.uCallbackMessage = CM_NOTIFY_FROM_TRAY; + + ScopedResourceLoader cascadiaLoader{ L"Resources" }; + + nid.hIcon = static_cast(GetActiveAppIconHandle(ICON_SMALL)); + StringCchCopy(nid.szTip, ARRAYSIZE(nid.szTip), cascadiaLoader.GetLocalizedString(L"AppName").c_str()); + nid.uFlags = NIF_MESSAGE | NIF_SHOWTIP | NIF_TIP | NIF_ICON; + Shell_NotifyIcon(NIM_ADD, &nid); + + // For whatever reason, the NIM_ADD call doesn't seem to set the version + // properly, resulting in us being unable to receive the expected notification + // events. We actually have to make a separate NIM_SETVERSION call for it to + // work properly. + nid.uVersion = NOTIFYICON_VERSION_4; + Shell_NotifyIcon(NIM_SETVERSION, &nid); + + _trayIconData = nid; + } +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 1efaf2bf9..a8511dbc2 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -85,4 +85,9 @@ private: void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + + void _UpdateTrayIcon(); + void _HandleTrayIconPressed(); + + std::optional _trayIconData; }; diff --git a/src/cascadia/WindowsTerminal/BaseWindow.h b/src/cascadia/WindowsTerminal/BaseWindow.h index d1a8b86d7..581b38617 100644 --- a/src/cascadia/WindowsTerminal/BaseWindow.h +++ b/src/cascadia/WindowsTerminal/BaseWindow.h @@ -3,9 +3,7 @@ #pragma once -// Custom window messages -#define CM_UPDATE_TITLE (WM_USER) - +#include "CustomWindowMessages.h" #include template diff --git a/src/cascadia/WindowsTerminal/CustomWindowMessages.h b/src/cascadia/WindowsTerminal/CustomWindowMessages.h new file mode 100644 index 000000000..beb0766af --- /dev/null +++ b/src/cascadia/WindowsTerminal/CustomWindowMessages.h @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +// Custom window messages +#define CM_UPDATE_TITLE (WM_USER) +#define CM_NOTIFY_FROM_TRAY (WM_USER + 1) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 95503aaa2..8ecd355c1 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -452,6 +452,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { if (wparam == SIZE_MINIMIZED && _isQuakeWindow) { + _NotifyWindowHiddenHandlers(); ShowWindow(GetHandle(), SW_HIDE); return 0; } @@ -506,6 +507,19 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize case WM_THEMECHANGED: UpdateWindowIconForActiveMetrics(_window.get()); return 0; + case CM_NOTIFY_FROM_TRAY: + { + switch (LOWORD(lparam)) + { + case NIN_SELECT: + case NIN_KEYSELECT: + { + _NotifyTrayIconPressedHandlers(); + return 0; + } + } + break; + } } // TODO: handle messages here... diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index a7fe739da..0e5859bc4 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -51,6 +51,8 @@ public: WINRT_CALLBACK(MouseScrolled, winrt::delegate); WINRT_CALLBACK(WindowActivated, winrt::delegate); WINRT_CALLBACK(HotkeyPressed, winrt::delegate); + WINRT_CALLBACK(NotifyTrayIconPressed, winrt::delegate); + WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate); protected: void ForceResize() diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index b92f8589f..ee7de8ced 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -44,6 +44,7 @@ + @@ -81,6 +82,10 @@ + + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} + false +