Skip to content

Commit

Permalink
Add Minimize to Tray and Tray Icon (#10368)
Browse files Browse the repository at this point in the history
A brief summary of the behavior of the tray icon:
- There will only ever be one tray icon representing all windows.
- Left-Click on a Tray Icon brings up the MRU window.
- Right-Click on a Tray Icon brings up a Context Menu:
```
Focus Terminal
----------------
Windows --> Window ID 1 - <unnamed window>
            Named Window
            Named Window Again
 ```
- Focus Terminal will bring up the MRU window.
- Clicking on any of the Window "names" in the submenu will summon the window.

## Settings Changes

Two new global settings are introduced: `alwaysShowTrayIcon` and `minimizeToTray`. Here's a chart explaining the behavior with the two settings.

|                      | `alwaysShowTrayIcon:true`                                          | `alwaysShowTrayIcon:false`                                         |
|----------------------|------------------------------------------------------------------|------------------------------------------------------------------|
| `minimizeToTray:true`  | tray icon is always shown. minimize button will hide the window. | tray icon is always shown. minimize button will hide the window. |
| `minimizeToTray:false` | tray icon is always shown.                                       | tray icon is not shown ever.                                     |

Closes #5727

## References
[Spec for Minimize to Tray](https://github.com/microsoft/terminal/blob/main/doc/specs/%23653%20-%20Quake%20Mode/%23653%20-%20Quake%20Mode.md#minimize-to-tray)
Docs PR - MicrosoftDocs/terminal#352
#10448 - My list of TODOs
  • Loading branch information
leonMSFT committed Aug 12, 2021
1 parent d3f9859 commit a0edb12
Show file tree
Hide file tree
Showing 34 changed files with 773 additions and 85 deletions.
7 changes: 7 additions & 0 deletions .github/actions/spelling/allow/apis.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ ACCEPTFILES
ACCESSDENIED
alignas
alignof
APPLYTOSUBMENUS
bitfield
bitfields
BUILDBRANCH
BUILDMSG
BUILDNUMBER
BYPOSITION
charconv
CLASSNOTAVAILABLE
cmdletbinding
Expand Down Expand Up @@ -78,6 +80,9 @@ llu
localtime
lround
LSHIFT
MENUCOMMAND
MENUDATA
MENUINFO
memicmp
mptt
mov
Expand All @@ -94,6 +99,7 @@ NOCHANGEDIR
NOPROGRESS
NOREDIRECTIONBITMAP
NOREPEAT
NOTIFYBYPOS
NOTIFYICON
NOTIFYICONDATA
ntprivapi
Expand Down Expand Up @@ -141,6 +147,7 @@ Stubless
Subheader
Subpage
syscall
TASKBARCREATED
TBPF
THEMECHANGED
tlg
Expand Down
2 changes: 2 additions & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2801,6 +2801,7 @@ xes
xff
XFile
XFORM
xIcon
XManifest
XMath
XMFLOAT
Expand Down Expand Up @@ -2833,6 +2834,7 @@ YCast
YCENTER
YCount
YDPI
yIcon
yml
YOffset
YPosition
Expand Down
10 changes: 10 additions & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,16 @@
"minimum": 0,
"type": [ "integer", "string" ],
"deprecated": true
},
"minimizeToTray": {
"default": "false",
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the system tray through which the user can access their windows.",
"type": "boolean"
},
"alwaysShowTrayIcon": {
"default": "false",
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
"type": "boolean"
},
"actions": {
"description": "Properties are specific to each custom action.",
Expand Down
87 changes: 77 additions & 10 deletions src/cascadia/Remoting/Monarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
CATCH_LOG();
}

// This is a private constructor to be used in unit tests, where we don't
// want each Monarch to necessarily use the current PID.
// This constructor is intended to be used in unit tests,
// but we need to make it public in order to use make_self
// in the tests. It's not exposed through the idl though
// so it's not _truly_ fully public which should be acceptable.
Monarch::Monarch(const uint64_t testPID) :
_ourPID{ testPID }
{
Expand Down Expand Up @@ -78,6 +80,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
peasant.RenameRequested({ this, &Monarch::_renameRequested });

peasant.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
peasant.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });

_peasants[newPeasantsId] = peasant;

TraceLoggingWrite(g_hRemotingProvider,
Expand Down Expand Up @@ -738,20 +743,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
try
{
args.FoundMatch(false);

// If a WindowID is provided from the args, use that first.
uint64_t windowId = 0;
// If no name was provided, then just summon the MRU window.
if (searchedForName.empty())
if (args.WindowID())
{
// Use the value of the `desktop` arg to determine if we should
// limit to the current desktop (desktop:onCurrent) or not
// (desktop:any or desktop:toCurrent)
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
windowId = args.WindowID().Value();
}
else
{
// Try to find a peasant that currently has this name
windowId = _lookupPeasantIdForName(searchedForName);
// If no name was provided, then just summon the MRU window.
if (searchedForName.empty())
{
// Use the value of the `desktop` arg to determine if we should
// limit to the current desktop (desktop:onCurrent) or not
// (desktop:any or desktop:toCurrent)
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
}
else
{
// Try to find a peasant that currently has this name
windowId = _lookupPeasantIdForName(searchedForName);
}
}

if (auto targetPeasant{ _getPeasant(windowId) })
{
targetPeasant.Summon(args.SummonBehavior());
Expand Down Expand Up @@ -789,4 +804,56 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}

// Method Description:
// - This method creates a map of peasant IDs to peasant names
// while removing dead peasants.
// Arguments:
// - <none>
// Return Value:
// - A map of peasant IDs to their names.
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> Monarch::GetPeasantNames()
{
auto names = winrt::single_threaded_map<uint64_t, winrt::hstring>();

std::vector<uint64_t> peasantsToErase{};
for (const auto& [id, p] : _peasants)
{
try
{
names.Insert(id, p.WindowName());
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
peasantsToErase.push_back(id);
}
}

// Remove the dead peasants we came across while iterating.
for (const auto& id : peasantsToErase)
{
_peasants.erase(id);
_clearOldMruEntries(id);
}

return names.GetView();
}

void Monarch::SummonAllWindows()
{
auto callback = [](auto&& p, auto&& /*id*/) {
SummonWindowBehavior args{};
args.ToggleVisibility(false);
p.Summon(args);
};
auto onError = [](auto&& id) {
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SummonAll_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
};
_forAllPeasantsIgnoringTheDead(callback, onError);
}
}
7 changes: 6 additions & 1 deletion src/cascadia/Remoting/Monarch.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
struct Monarch : public MonarchT<Monarch>
{
Monarch();
Monarch(const uint64_t testPID);
~Monarch();

uint64_t GetPID();
Expand All @@ -51,10 +52,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);

void SummonAllWindows();
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();

TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);

private:
Monarch(const uint64_t testPID);
uint64_t _ourPID;

uint64_t _nextPeasantID{ 1 };
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/Remoting/Monarch.idl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Remoting

Boolean FoundMatch;
SummonWindowBehavior SummonBehavior;
Windows.Foundation.IReference<UInt64> WindowID;
}


Expand All @@ -40,6 +41,11 @@ namespace Microsoft.Terminal.Remoting
void HandleActivatePeasant(WindowActivatedArgs args);
void SummonWindow(SummonWindowSelectionArgs args);

void SummonAllWindows();
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames { get; };

event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
};
}
39 changes: 37 additions & 2 deletions src/cascadia/Remoting/Peasant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
}

// This is a private constructor to be used in unit tests, where we don't
// want each Peasant to necessarily use the current PID.
// This constructor is intended to be used in unit tests,
// but we need to make it public in order to use make_self
// in the tests. It's not exposed through the idl though
// so it's not _truly_ fully public which should be acceptable.
Peasant::Peasant(const uint64_t testPID) :
_ourPID{ testPID }
{
Expand All @@ -31,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
_id = id;
}

uint64_t Peasant::GetID()
{
return _id;
Expand Down Expand Up @@ -222,4 +225,36 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}

void Peasant::RequestShowTrayIcon()
{
try
{
_ShowTrayIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestShowTrayIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}

void Peasant::RequestHideTrayIcon()
{
try
{
_HideTrayIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestHideTrayIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
4 changes: 4 additions & 0 deletions src/cascadia/Remoting/Peasant.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestIdentifyWindows();
void DisplayWindowId();
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
void RequestShowTrayIcon();
void RequestHideTrayIcon();

winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();

Expand All @@ -40,6 +42,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);

private:
Peasant(const uint64_t testPID);
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/Remoting/Peasant.idl
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,17 @@ namespace Microsoft.Terminal.Remoting
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
void Summon(SummonWindowBehavior behavior);
void RequestShowTrayIcon();
void RequestHideTrayIcon();

event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
};

[default_interface] runtimeclass Peasant : IPeasant
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/Remoting/SummonWindowSelectionArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
WINRT_PROPERTY(bool, FoundMatch, false);
WINRT_PROPERTY(bool, OnCurrentDesktop, false);
WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);

WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, WindowID);
};
}

Expand Down
Loading

0 comments on commit a0edb12

Please sign in to comment.