From 67dc14e374df324620fb081c7b3023d20413a2b0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Feb 2021 06:45:22 -0600 Subject: [PATCH 1/9] start working on draft, push so I don't lose it --- .../#653 - Quake Mode/#653 - Quake Mode.md | 401 ++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 doc/specs/#653 - Quake Mode/#653 - Quake Mode.md diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md new file mode 100644 index 00000000000..35b057242f6 --- /dev/null +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -0,0 +1,401 @@ +--- +author: Mike Griese @zadjii-msft +created on: 2021-02-23 +last updated: 2021-02-23 +issue id: #653 +--- + +# Quake Mode + +## Abstract + +Many existing terminals support a feature whereby a user can press a keybinding +anywhere in the OS, and summon their terminal application. Oftentimes the act of +summoning this window is accompanied by a "dropdown" animation, where the window +slides in to view from the top of the screen. This global summon action is often +referred to as "quake mode", a reference to the videogame Quake who's console +slid in from the top. + +This spec addresses both of the following two issues: +* "Quake Mode" ([#653]) +* "Minimize to tray" ([#5727]) + +Readers should make sure to have read the [Process Model 2.0 Spec], for +background on Monarch and Peasant processes. + +## Background + +### Inspiration + +[comment]: # Are there existing precedents for this type of configuration? These may be in the Terminal, or in other applications + +For an example of the original Quake console in action, take a look at the following video (noisy video warning): [Quake 3 sample]. + +* guake implements this as... TODO +* yakuake implements this by ... TODO + +Both these terminals are listed in #653 with descriptions of their specifics. I should include them here. + +### User Stories + +[comment]: # List off the use cases where two users might want a feature to have different behavior based on user preference. Include links to issues that might be relevant. + +The original quake mode thread (#653) is absolutely _filled_ with variations on +how users want to be able to summon their terminal windows. These include, but +are not limited to: + +* **Story A** Press a hotkey anywhere to activate the single Terminal window + wherever it was +* **Story B** Press a hotkey anywhere to activate the single Terminal window _on + the current monitor_. If it wasn't previously on that monitor, move it there. +* **Story C** When the Terminal is summoned using the hotkey, have it "slide in" + from the top. Similarly, slide out on deactivate. +* **Story D** Ctrl+1 to activate the terminal on monitor 1, + Ctrl+2 to activate the terminal on monitor 2. +* **Story E** Variable dropdown speed +* **Story F** Minimize to tray, press a hotkey to activate the terminal window + (#5727) +* **Story G** Terminal doesn't appear in alt+tab view, press a hotkey to + activate the single terminal window / the nearest terminal window (I'm not + sure this is distinct from the above) +* **Story H** Press a hotkey to activate the "nearest" terminal window. + - TODO: I'm not totally sure that anyone wants this one tbh. + + +### Future Considerations + +I don't believe there are any other related features that are planned that +aren't already included in this spec. + +[TODO]: # Which is the "current" monitor? The one with the mouse or the one with the active window? Users disagree! Users want it configurable! Gah! Do we make it `onMonitor: onCurrent|onCurrentWindow|forCurent|` + + +#### Hide from alt-tab + +[TODO]: # TODO + +This is another request that's all over [#653]. It's unclear if this is the same as minimize to tray. I'm not sure that there's anyone who wants the terminal to `minimizeToTray`, but then _not_ hide from the alt+tab list. + +## Solution Design + +To implement this feature, we'll add the following settings: +* a new action, named `globalSummon`. +* a new global, app setting named `minimizeToTray` +* a new global, app setting named `alwaysShowTrayIcon` + +### `globalSummon` Action + +[comment]: # Also, outline various different proposed designs for this setting. These won't all be winners, but may help during the decision making process. For each proposed design: + +The `globalSummon` action will be a keybinding the user can use to summon a +Terminal window from anywhere in the OS. Various arguments to the action will +specify which window is summoned, to where, and how the window should behave on +summon. + +From a technical perspective, the action will work by using the +[`RegisterHotKey`] function. This API allows us to bind a particular hotkey with +the OS. Whenever that hotkey is pressed, our window message loop will recieve a +`WM_HOTKEY`. + +Since users may want to bind multiple keys to summon different windows, we'll +need to allow the user to specify multiple keybindings simultaneously, each with +their own set of args. The following are three proposals for different ways for +allowing users to specify multiple global hotkeys: + +
+ +#### Proposal 1A + +We stick all the `globalSummon`s in the actions array, like they're any other +keybinding. + +However, these are not keys that are handled by the TerminalApp layer itself. +These are keys that need to be registered with the OS. So they don't make sense +to put in the normal `KeyMap`. We'll need to parse them not into the `KeyMap`, +but into a global summon list that the window layer can get at easier. + +**Pros**: +* We re-use the existing _action_ arg parsing entirely. +* These actions would show up as commands for the command palette. + - I don't know if that's desirable. Would we want them to? They'd need to + raise events into the window layer, for the window to dispatch them. + - If it is desirable, then these actions could also be added to the context + menu, the new tab dropdown, anywhere we plan on placing actions. + +**Cons**: +* We'll need to modify the keybinding parsing to place globalSummon actions into + a totally different object +* Not sure that having these available in the command palette really makes sense + +#### Proposal 1B + +If we're going to need another data structure (separate from the `KeyMap`), then +why have the `globalSummon` actions in `actions` at all? Instead, we could add a +top-level `globalHotkeys` array, with only global summon actions in it. + +```json +{ + "globalHotkeys": [ + { "keys": "win+1", "command": { "action": "globalSummon", "monitor": 1 } }, + { "keys": "win+2", "command": { "action": "globalSummon", "monitor": 2, "desktop": "toCurrent" } } + ] +} +``` + +**Pros**: +* We re-use the existing _action_ arg parsing entirely. +* Don't need to modify (complicate) the current keybinding parsing. +* Clearly separates these actions from the bulk of other actions + +**Cons**: +* Is fairly verbose to add to the settings. `"command": { "action": + "globalSummon"` is present in _all_ of the actions, but doesn't add value. +* Difficult, but not impossible, to get these actions into other places in the + UI that we want actions (unsure if this is even wanted) + +#### Proposal 1C + +The previous proposal is qute verbose. We've already got these actions in their +own setting. We could instead add a top-level `globalHotkeys` array, but with a +less verbose syntax. Everything in here is a global summon action, no need to +repeat ourselves: + +```json +{ + "globalHotkeys": [ + { "keys": "win+1", "monitor": 1 }, + { "keys": "win+2", "monitor": 2, "desktop": "toCurrent" } + ] +} +``` + +**Pros**: +* Is the least verbose way of adding these actions. +* Don't need to modify (complicate) the current keybinding parsing. +* Clearly separates these actions from the bulk of other actions. + +**Cons**: +* We're going to need a new _action_ parser just for global hotkeys like this. + One that always tries the action as a `globalSummon` action. +* **H**ard to get these actions into other places in the UI that we want actions + (unsure if this is even wanted) + +#### Conclusion + +From an engineering standpoint, I like 1C the best. It doesn't complicate the +existing keybinding parsing. The cost of modifying the existing action arg +parsing just for globalSummon actions shouldn't be too high. It's also the most +ergonomic for users to add this fairly complex setting. + +
+ +When looking at the list of requested scenarios, there are lots of different +ways people would like to use the global summon action. Some want the most +recent window activated, always. Others want to have one window _per monitor_. +Some would like to move the window to where the user is currently interacting +with the PC, and others want to activate the window where it already exists. +Trying to properly express all these possible configurations is complex. The +settings should be unambiguous as to what will happen when you press the +keybinding. + +I believe that in order to accurately support all the variations that people +might want, we'll need two properties in the `globalSummon` action. These +properties will specify _which_ window we're summoning, and _where_ to summon +the window. + +[TODO]: # todo +```json +"monitor": "any"|"toCurrent"|"forCurrent"|int, +"desktop": null|"toCurrent"|"onCurrentOnly" +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Leave where it isMove to current desktopOn current desktop only
Summon the MRU windowGo to the desktop the window is on (leave position alone)Move the window to this desktop (leave position alone) + +If there isn't one on this desktop: +* create a new one (default position) + +Else: +* activate the one on this desktop (don't move it) +
Summon the MRU window TO the current monitorGo to the desktop the window is on, move to this monitorMove the window to this desktop, move to this monitor + +If there isn't one on this desktop: +* create a new one (on this monitor) + +Else: +* activate the one on this desktop, move to this window +
Summon the MRU window for the current monitor + +If there is a window on this monitor on any desktop, +* Go to the desktop the window is on (leave position alone) + +else +* Create a new window on this monitor & desktop + + +If there is a window on this monitor on any desktop, +* Move the window to this desktop (leave position alone) + +else +* Create a new window on this monitor & desktop + + +If there isn't one on this desktop, (even if there is one on this monitor on another desktop), +* create a new one on this monitor + +Else if ( there is one on this desktop, not this monitor) +* create a new one on this monitor + +Else (one on this desktop & monitor) +* Activate the one on this desktop (don't move) +
Summon the MRU window for monitor N + +If there is a window on monitor N on any desktop, +* Go to the desktop the window is on (leave position alone) + +else +* Create a new window on this monitor & desktop + + +If there is a window on monitor N on any desktop, +* Move the window to this desktop (leave position alone) + +else +* Create a new window on this monitor & desktop + + +If there isn't one on this desktop, (even if there is one on monitor N on another desktop), +* create a new one on monitor N + +Else if ( there is one on this desktop, not monitor N) +* create a new one on monitor N + +Else (one on this desktop & monitor N) +* Activate the one on this desktop (don't move) +
+ + + +### Proposal 1: + +[comment]: # Describe the values for the properties, how it'll be exposed in both JSON and the Settings UI, and list pros and cons for this design. If there are technical details for this proposal, include them here. + +* **Pros**: +* **Cons**: + +### Minimize to Tray + +[TODO]: # TODO: add samples of how it's implemented +[TODO]: # TODO: Find a sample for hiding the window from the taskbar (when minimize to tray is active) + +The tray icon could include a list of all the active windows. Maybe we'll have +two items on the tray icon: + +``` +Focus Terminal +--- +Windows > Window 1 - + Window 2 - "This-window-does-have-a-name" +``` + +Just clicking on the icon would summon the recent terminal window. Right +clicking would show the menu with "Focus Terminal" and "Windows" in it, and +"Windows" would have nested entries for each Terminal window. Clicking those +would summon them. + +The tray notification would be visible always when the user has +`"minimizeToTray": true` set in their settings. If the user has that set to +false, but would still like the tray, they can specify `"alwaysShowTrayIcon": +true`. That will cause the tray icon to always be added to the system tray. + +There's not a combination of settings where the Terminal is "minimized to the +tray", and there's _no tray icon visible_. We don't want to let users get into a +state where the Terminal is running, but is totally hidden from their control. + +## Conclusion + +[comment]: # Of the above proposals, which should we decide on, and why? + + +## UI/UX Design + +[comment]: # How will different values of this setting affect the end user? + +## Potential Issues + + + + + + + +
Compatibility + +[comment]: # Will the proposed change break existing code/behaviors? If so, how, and is the breaking change "worth it"? + +
+ +[comment]: # If there are any other potential issues, make sure to include them here. + + +## Resources + +Docs on adding a system tray item: +* https://docs.microsoft.com/en-us/windows/win32/shell/notification-area +* https://www.codeproject.com/Articles/18783/Example-of-a-SysTray-App-in-Win32 + +[comment]: # Be sure to add links to references, resources, footnotes, etc. + + + + +### Footnotes + +[1]: + + +[#653]: https://github.com/microsoft/terminal/issues/653 +[#5727]: https://github.com/microsoft/terminal/issues/5727 +[Process Model 2.0 Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0/%235000%20-%20Process%20Model%202.0.md +[Quake 3 sample]: https://youtu.be/ZmR6HQbuHPA?t=27 +[`RegisterHotKey`]: # TODO From ffd34af5024fe0e2f67e79da9233f4db000748a9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Feb 2021 10:28:12 -0600 Subject: [PATCH 2/9] fesh out these properties --- .../#653 - Quake Mode/#653 - Quake Mode.md | 89 ++++++++++++++++--- 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md index 35b057242f6..fd03474c0fd 100644 --- a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -104,7 +104,7 @@ allowing users to specify multiple global hotkeys:
-#### Proposal 1A +##### Proposal 1A We stick all the `globalSummon`s in the actions array, like they're any other keybinding. @@ -127,7 +127,7 @@ but into a global summon list that the window layer can get at easier. a totally different object * Not sure that having these available in the command palette really makes sense -#### Proposal 1B +##### Proposal 1B If we're going to need another data structure (separate from the `KeyMap`), then why have the `globalSummon` actions in `actions` at all? Instead, we could add a @@ -153,7 +153,7 @@ top-level `globalHotkeys` array, with only global summon actions in it. * Difficult, but not impossible, to get these actions into other places in the UI that we want actions (unsure if this is even wanted) -#### Proposal 1C +##### Proposal 1C The previous proposal is qute verbose. We've already got these actions in their own setting. We could instead add a top-level `globalHotkeys` array, but with a @@ -180,15 +180,17 @@ repeat ourselves: * **H**ard to get these actions into other places in the UI that we want actions (unsure if this is even wanted) -#### Conclusion +##### Conclusion From an engineering standpoint, I like 1C the best. It doesn't complicate the existing keybinding parsing. The cost of modifying the existing action arg -parsing just for globalSummon actions shouldn't be too high. It's also the most -ergonomic for users to add this fairly complex setting. +parsing just for `globalSummon` actions shouldn't be too high. It's also the +most ergonomic for users to add this fairly complex setting.
+#### Which window, and where? + When looking at the list of requested scenarios, there are lots of different ways people would like to use the global summon action. Some want the most recent window activated, always. Others want to have one window _per monitor_. @@ -201,14 +203,35 @@ keybinding. I believe that in order to accurately support all the variations that people might want, we'll need two properties in the `globalSummon` action. These properties will specify _which_ window we're summoning, and _where_ to summon -the window. +the window. To try and satisfy all these scenarios, I'm proposing the following +two arguments to the `globalSummon` action: -[TODO]: # todo ```json -"monitor": "any"|"toCurrent"|"forCurrent"|int, -"desktop": null|"toCurrent"|"onCurrentOnly" +"monitor": "any"|"toCurrent"|"onCurrent"|int, +"desktop": null|"toCurrent"|"onCurrent" ``` +The way these settings can be combined is in a table below. As an overview: + +* `monitor`: This controls the monitor that the window will be summoned from/to + - `"any"`/omitted: (_default_) Summon the MRU window, regardless of which + monitor it's currently on. + - `"toCurrent"`: Summon the MRU window TO the current monitor. + - `"onCurrent"`: Summon the MRU window ALREADY ON the current monitor. + - `int`: Summon the MRU window for the given monitor (as identified by the + "Identify" displays feature in the OS settings) + +* `desktop`: This controls how the terminal should interact with virtual desktops. + - `null`/omitted: (_default_) Leave the window on whatever desktop it's + already on - we'll switch to that desktop as we activate the window. + - **TODO: FOR DISCUSSION**: Should this just be `any` to match the above? + Read the rest of the doc and come back. + - `"toCurrent"`: Move the window to the current virtual desktop + - `"onCurrent"`: Only summon the window if it's already on the current virtual + desktop + +Together, these settings interact in the following ways: + @@ -315,6 +338,52 @@ Else (one on this desktop & monitor N)
+##### Stories, revisited + +With the above settings, let's re-examine the original user stories, and see how +they fit into the above settings. (_Stories that are omitted aren't relevant to +the discussion of these settings_) + +> When the `desktop` param is omitted below, that can be interpreted as "any +> `desktop` value will make sense here" + +* **Story A** Press a hotkey anywhere to activate the single Terminal window + wherever it was + - This is `{ "monitor": "any", "desktop": null }` +* **Story B** Press a hotkey anywhere to activate the single Terminal window _on + the current monitor_. If it wasn't previously on that monitor, move it there. + - This is `{ "monitor": "toCurrent" }` +* **Story D** Ctrl+1 to activate the terminal on monitor 1, + Ctrl+2 to activate the terminal on monitor 2. + - This is `[ { "keys": "ctrl+1", monitor": 1 }, { "keys": "ctrl+2", monitor": 2 } ]` + +As some additional examples: + +```json +// Go to the MRU window, wherever it is +{ "keys": "win+1", "monitor":"any", "desktop": null }, +// Since "any" & null are the default values, just placing a single entry here +// will bind the same behavior: +{ "keys": "win+1" }, + +// activate the MRU window, and move it to this desktop & this monitor +{ "keys": "win+2", "monitor":"toCurrent", "desktop": "toCurrent" }, + +// activate the MRU window on this desktop +{ "keys": "win+3", "monitor":"any", "desktop": "onCurrent" }, + +// Activate the MRU window on monitor 2 (from any desktop), and place it on the +// current desktop. If there isn't one on monitor 2, make a new one. +{ "keys": "win+4", "monitor": 2, "desktop": "toCurrent" }, + +// Activate the MRU window on monitor 3 (ONLY THIS desktop), or make a new one. +{ "keys": "win+5", "monitor": 3, "desktop": "onCurrent" }, + +// Activate the MRU window on this monitor (from any desktop), and place it on +// the current desktop. If there isn't one on this monitor, make a new one. +{ "keys": "win+6", "monitor": "onCurrent", "desktop": "toCurrent" }, +``` + ### Proposal 1: From 9c36d1e3455942d11f8a83cf5d31916c71fdff34 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Feb 2021 12:18:49 -0600 Subject: [PATCH 3/9] This is all the 'quake mode' design bits. Still need minimize to taskbar and more history/context --- .../#653 - Quake Mode/#653 - Quake Mode.md | 156 +++++++++++++----- 1 file changed, 117 insertions(+), 39 deletions(-) diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md index fd03474c0fd..5573b631276 100644 --- a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -38,7 +38,6 @@ Both these terminals are listed in #653 with descriptions of their specifics. I ### User Stories -[comment]: # List off the use cases where two users might want a feature to have different behavior based on user preference. Include links to issues that might be relevant. The original quake mode thread (#653) is absolutely _filled_ with variations on how users want to be able to summon their terminal windows. These include, but @@ -61,6 +60,8 @@ are not limited to: * **Story H** Press a hotkey to activate the "nearest" terminal window. - TODO: I'm not totally sure that anyone wants this one tbh. +[TODO]: # todo ^ + ### Future Considerations @@ -85,8 +86,6 @@ To implement this feature, we'll add the following settings: ### `globalSummon` Action -[comment]: # Also, outline various different proposed designs for this setting. These won't all be winners, but may help during the decision making process. For each proposed design: - The `globalSummon` action will be a keybinding the user can use to summon a Terminal window from anywhere in the OS. Various arguments to the action will specify which window is summoned, to where, and how the window should behave on @@ -95,15 +94,19 @@ summon. From a technical perspective, the action will work by using the [`RegisterHotKey`] function. This API allows us to bind a particular hotkey with the OS. Whenever that hotkey is pressed, our window message loop will recieve a -`WM_HOTKEY`. +`WM_HOTKEY`. We'll use the payload of that window message to lookup the action +arguments for that hotkey. Then we'll use those arguments to control which +window is invoked, where, and how the window behaves. + +Since `RegisterHotKey` can only be used to + +#### Where in the settings? Since users may want to bind multiple keys to summon different windows, we'll need to allow the user to specify multiple keybindings simultaneously, each with their own set of args. The following are three proposals for different ways for allowing users to specify multiple global hotkeys: -
- ##### Proposal 1A We stick all the `globalSummon`s in the actions array, like they're any other @@ -187,8 +190,6 @@ existing keybinding parsing. The cost of modifying the existing action arg parsing just for `globalSummon` actions shouldn't be too high. It's also the most ergonomic for users to add this fairly complex setting. -
- #### Which window, and where? When looking at the list of requested scenarios, there are lots of different @@ -232,17 +233,23 @@ The way these settings can be combined is in a table below. As an overview: Together, these settings interact in the following ways: + - - - - + - + - + + + + + + + + @@ -257,8 +264,7 @@ Else: - - + - - + - - + -
Leave where it isMove to current desktopOn current desktop only"desktop"
Summon the MRU window"monitor"null
Leave where it is
"toCurrent"
Move to current desktop
"onCurrent"
On current desktop only
"any"
Summon the MRU window
Go to the desktop the window is on (leave position alone) Move the window to this desktop (leave position alone)
Summon the MRU window TO the current monitor"toCurrent"
Summon the MRU window TO the current monitor
Go to the desktop the window is on, move to this monitor Move the window to this desktop, move to this monitor @@ -272,8 +278,7 @@ Else:
Summon the MRU window for the current monitor"onCurrent"
Summon the MRU window for the current monitor
If there is a window on this monitor on any desktop, @@ -292,7 +297,8 @@ else -If there isn't one on this desktop, (even if there is one on this monitor on another desktop), +If there isn't one on this desktop, (even if there is one on this monitor on +another desktop), * create a new one on this monitor Else if ( there is one on this desktop, not this monitor) @@ -304,8 +310,7 @@ Else (one on this desktop & monitor)
Summon the MRU window for monitor Nint
Summon the MRU window for monitor N
If there is a window on monitor N on any desktop, @@ -324,7 +329,8 @@ else -If there isn't one on this desktop, (even if there is one on monitor N on another desktop), +If there isn't one on this desktop, (even if there is one on monitor N on +another desktop), * create a new one on monitor N Else if ( there is one on this desktop, not monitor N) @@ -334,7 +340,6 @@ Else (one on this desktop & monitor N) * Activate the one on this desktop (don't move)
@@ -384,13 +389,39 @@ As some additional examples: { "keys": "win+6", "monitor": "onCurrent", "desktop": "toCurrent" }, ``` - -### Proposal 1: - -[comment]: # Describe the values for the properties, how it'll be exposed in both JSON and the Settings UI, and list pros and cons for this design. If there are technical details for this proposal, include them here. - -* **Pros**: -* **Cons**: +#### Other properties + +Some users would like the terminal to just appear when the global hotkey is +pressed. Others would like the true quake-like experience, where the terminal +window "slides-in" from the top of the monitor. Furthermore, some users would +like to configure the speed at which that dropdown happens. To support this +functionality, the `globalSummon` action will support the following property: + +* `"dropdownDuration": float` + - When omitted, `0`, or a negative number: (_default_) No animation is used + when summoning the window. The summoned window is focused immediately where + it is. + - When a positive number is provided, the terminal will use that value as a + duration (in seconds) to slide the terminal into position when activated. + +We could have alternatively provided a `"dropdownSpeed"` setting, that provided +a number of pixels per second. In my opinion, that would be harder for users to +use correctly. I believe that it's easier for users to mentally picture "I'd +like the dropdown to last 100ms" vs "My monitor is 1504px tall, so I need to set +this to 15040 to make the window traverse the entire display in .1s" + +Some users might want to be able to use the global hotkey to hide the window +when the window is already visible. This would let the hotkey act as a sort of +global toggle for the Terminal window. Others might not like that behavior, and +just want the action to always bring the Terminal into focus, and do nothing if +the terminal is already focused. To facilitate both these use cases, we'll add +the following property: + +* `"hideWhenVisible": bool` + - When `true`: (_default_) When this hotkey is pressed, and the terminal + window is currently active, minimize the window. + - When `false`: When this hotkey is pressed, and the terminal window is + currently active, do nothing. ### Minimize to Tray @@ -421,14 +452,25 @@ There's not a combination of settings where the Terminal is "minimized to the tray", and there's _no tray icon visible_. We don't want to let users get into a state where the Terminal is running, but is totally hidden from their control. -## Conclusion - -[comment]: # Of the above proposals, which should we decide on, and why? - - ## UI/UX Design -[comment]: # How will different values of this setting affect the end user? +To summarize, we're proposing the following set of settings: + +```jsonc +{ + "minimizeToTray": bool, + "alwaysShowTrayIcon": bool, + "globalHotkeys": [ + { + "keys": KeyChord, + "dropdownDuration": float, + "hideWhenVisible": bool, + "monitor": "any"|"toCurrent"|"onCurrent"|int, + "desktop": null|"toCurrent"|"onCurrent" + } + ] +} +``` ## Potential Issues @@ -438,13 +480,48 @@ state where the Terminal is running, but is totally hidden from their control. Compatibility -[comment]: # Will the proposed change break existing code/behaviors? If so, how, and is the breaking change "worth it"? +As part of this set of changes, we'll also be allowing the Win key in +keybindings. Generally, the OS reserves the Windows key for its own shortcuts. +For example, Win+R for the run dialog, Win+A for the +Action Center, Win+V for the cloud clipboard, etc. Users will now be +able to use the win key themselves, but they should be aware that the OS has +"first dibs" on any hotkeys involving the Windows key. -[comment]: # If there are any other potential issues, make sure to include them here. +If there are any other applications running that have already registered hotkeys +with `RegisterHotKey`, then it's possible that the Terminal's attempt to +register that hotkey will fail. If that should happen, then we should display a +warning dialog to the user indicating which hotkey will not work (because it's +already used for something else). + + +## Implementation plan + +Currently, in [`dev/migrie/f/653-QUAKE-MODE`], I have some sample rudimentary +code to implement quake mode support. It allows for only a single global hotkey +that summons the MRU window, without dropdown. That would be a good place for +anyone starting to work on this feature. From there, I imagine the following +work would be needed: + +* [ ] Change the `globalHotKey` to a list of `globalHotkeys`. `AppHost` would + need to be able to get _all_ of these hotkeys, and register all of them. Each + one would need to be assigned a unique ID, so `WM_HOTKEY` can identify which + hotkey was pressed. + - This could be committed without any other args to the `globalHotkeys`. + We'd assume the "default" behavior or summoning the MRU window, where it + is, no dropdown, to start with. From there, we'd add the remaining + properties: + * [ ] Add support for the `hideWhenVisible` property + * [ ] Add support for the `desktop` property to control how window summoning + interacts with virtual desktops + * [ ] Add support for the `monitor` which monitor the window appears on. + * [ ] Add support for the `dropdownDuration` property +* [ ] Add the `minimizeToTray` setting, and implement it without any sort of flyout + * [ ] Add a list of windows to the right-click flyout on the tray icon + * [ ] Add support for the `alwaysShowTrayIcon` setting ## Resources @@ -467,4 +544,5 @@ Docs on adding a system tray item: [#5727]: https://github.com/microsoft/terminal/issues/5727 [Process Model 2.0 Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0/%235000%20-%20Process%20Model%202.0.md [Quake 3 sample]: https://youtu.be/ZmR6HQbuHPA?t=27 -[`RegisterHotKey`]: # TODO +[`RegisterHotKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey +[`dev/migrie/f/653-QUAKE-MODE`]: https://github.com/microsoft/terminal/tree/dev/migrie/f/653-QUAKE-MODE From c39eb7f4926ddeda983706b1d06d39d289352d56 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Feb 2021 16:37:21 -0600 Subject: [PATCH 4/9] why the heck couldn't I write a 12 page paper in a single day back in college? I would have killed for that power --- .../#653 - Quake Mode/#653 - Quake Mode.md | 165 +++++++++++++----- doc/specs/#653 - Quake Mode/tray-icon-000.png | Bin 0 -> 19929 bytes 2 files changed, 123 insertions(+), 42 deletions(-) create mode 100644 doc/specs/#653 - Quake Mode/tray-icon-000.png diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md index 5573b631276..9240b7ed054 100644 --- a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -1,7 +1,7 @@ --- author: Mike Griese @zadjii-msft created on: 2021-02-23 -last updated: 2021-02-23 +last updated: 2021-02-24 issue id: #653 --- @@ -27,18 +27,19 @@ background on Monarch and Peasant processes. ### Inspiration -[comment]: # Are there existing precedents for this type of configuration? These may be in the Terminal, or in other applications +For an example of the original Quake console in action, take a look at the +following video (noisy video warning): [Quake 3 sample]. Additionally, plenty of +existing terminal emulators support similar functionality: -For an example of the original Quake console in action, take a look at the following video (noisy video warning): [Quake 3 sample]. - -* guake implements this as... TODO -* yakuake implements this by ... TODO - -Both these terminals are listed in #653 with descriptions of their specifics. I should include them here. +* **Tilda** allows the user to specify different keys to summon the window on + different monitors. +* **Guake** alternatively allows the user to either sumon the terminal window to + a specific monitor, or whichever monitor the mouse is on. Guake only allows + one single instance, so pressing the global hotkey always summons the same + instance. ### User Stories - The original quake mode thread (#653) is absolutely _filled_ with variations on how users want to be able to summon their terminal windows. These include, but are not limited to: @@ -57,25 +58,6 @@ are not limited to: * **Story G** Terminal doesn't appear in alt+tab view, press a hotkey to activate the single terminal window / the nearest terminal window (I'm not sure this is distinct from the above) -* **Story H** Press a hotkey to activate the "nearest" terminal window. - - TODO: I'm not totally sure that anyone wants this one tbh. - -[TODO]: # todo ^ - - -### Future Considerations - -I don't believe there are any other related features that are planned that -aren't already included in this spec. - -[TODO]: # Which is the "current" monitor? The one with the mouse or the one with the active window? Users disagree! Users want it configurable! Gah! Do we make it `onMonitor: onCurrent|onCurrentWindow|forCurent|` - - -#### Hide from alt-tab - -[TODO]: # TODO - -This is another request that's all over [#653]. It's unclear if this is the same as minimize to tray. I'm not sure that there's anyone who wants the terminal to `minimizeToTray`, but then _not_ hide from the alt+tab list. ## Solution Design @@ -98,7 +80,15 @@ the OS. Whenever that hotkey is pressed, our window message loop will recieve a arguments for that hotkey. Then we'll use those arguments to control which window is invoked, where, and how the window behaves. -Since `RegisterHotKey` can only be used to +Since `RegisterHotKey` can only be used to register a hotkey _once_ with the OS, +we'll need to make sure it's only ever set up by the Monarch process. We know +that there will only ever be one Monarch for the Terminal at a time, so it's the +perfect process to have the responsibility of managing the global hotkey. + +The Monarch will be responsible for calling `RegisterHotKey`, and processing the +`WM_HOTKEY` messages. It will then dispatch method calls to the appropriate +window to summon it. When a Monarch dies and a new process becomes the Monarch, +the new Monarch will re-register for the hotkeys. #### Where in the settings? @@ -425,23 +415,45 @@ the following property: ### Minimize to Tray -[TODO]: # TODO: add samples of how it's implemented -[TODO]: # TODO: Find a sample for hiding the window from the taskbar (when minimize to tray is active) +Many users have requested that the terminal additionally supports minimizing the +window "to the tray icon". This is a bit like when you close the Teams window, +but Teams is actually still running in the system tray, or the "notification +area". + +![The Teams tray icon](tray-icon-000.png) + +_fig 1: an example of the Teams tray icon in the notification area_. + +When users want to be able to "minimize to the tray", they want: +* The window to no longer appear on the taskbar +* The window to no longer appear in the alt-tab order + +When minimized to the tray, it's almost as if there's no window for the Terminal +at all. This can be combined with the global hotkey to quickly restore the window. -The tray icon could include a list of all the active windows. Maybe we'll have -two items on the tray icon: +The tray icon could be used for a variety of purposes. As a simple start, we +could include the following three options: ``` Focus Terminal --- Windows > Window 1 - Window 2 - "This-window-does-have-a-name" +--- +Quit ``` Just clicking on the icon would summon the recent terminal window. Right -clicking would show the menu with "Focus Terminal" and "Windows" in it, and -"Windows" would have nested entries for each Terminal window. Clicking those -would summon them. +clicking would show the menu with "Focus Terminal", "Windows" and "Quit" in it, and +"Windows" would have nested entries for each Terminal window. + +* "Focus Terminal" would do just that - summon the most recent terminal window, + wherever it is. +* "Windows" would have nested popups for each open Terminal window. Each of + these nested entries would display the name and ID of the window. Clicking + them would summon that window (wherever it may be) +* "Quit" would be akin to quit in browsers - close all open windows + [[1]](#footnote-1). The tray notification would be visible always when the user has `"minimizeToTray": true` set in their settings. If the user has that set to @@ -452,6 +464,11 @@ There's not a combination of settings where the Terminal is "minimized to the tray", and there's _no tray icon visible_. We don't want to let users get into a state where the Terminal is running, but is totally hidden from their control. +From a technical standpoint, the tray icon is managed similar to the global +hotkey. The Monarch process is responsible for setting it up, and processing the +messages. When a Monarch dies and a new process becomes the Monarch, then it +will re-create the tray icon. + ## UI/UX Design To summarize, we're proposing the following set of settings: @@ -487,6 +504,44 @@ Action Center, Win+V for the cloud clipboard, etc. Users will now be able to use the win key themselves, but they should be aware that the OS has "first dibs" on any hotkeys involving the Windows key. + + + +Mixed elevation + + +Only one app at a time gets to register for global hotkeys. However, from the +Terminal's perspective, unelevated and elevated windows will act like different +apps. Each privilege level has its own Monarch. The two are unable to +communicate across the elevation boundary. + +This means that if the user often runs terminals in both contexts, then only one +will have the global hotkeys bound. The naïve implementation would have the +first elevation level "win" the keybindings. + +A different option would be to have elevated windows not register global hotkeys +_at all_. I don't believe that there's any sort of security implication for +having a global hotkey for an elevated window. + +A third option would be to have some sort of `"whenElevated": bool?` property +for global hotkeys. This would explicitly enable a given hotkey for unelevated +vs elevated windows. +* `"whenElevated": null`: behave as normal - the first context level to run wins +* `"whenElevated": true`: only register the hotkey when running elevated +* `"whenElevated": false`: only register the hotkey when running unelevated + + + + +OneCore / Windows 10X + + +I'm fairly certain that none of these APIs would work on Windows 10X at all. +These features would have to initially be disabled in a pure UWP version of the +Terminal, until we could find workarounds. Since the window layer is the one +responsible for the management of the hotkeys and the tray icon, we're not too +worried about this. + @@ -497,6 +552,13 @@ register that hotkey will fail. If that should happen, then we should display a warning dialog to the user indicating which hotkey will not work (because it's already used for something else). +Which is the "current" monitor? The one with the mouse or the one with the +active window? This isn't something that has an obvious answer. Guake implements +this feature where the "current monitor" is the one with the mouse on it. At +least for the first iterations of this action, that's what we'll use. + +`monitor: onCurrent|onCurrentWindow|toCurrent|` + ## Implementation plan @@ -524,24 +586,43 @@ work would be needed: * [ ] Add support for the `alwaysShowTrayIcon` setting +### Future Considerations + +I don't believe there are any other tracked requests that are planned that +aren't already included in this spec. + +* While writing this spec, I wondered if anyone would want something like + `"window": "name" int` as an arg to the `globalSummon` action. This would let + the user say "I always want to summon the window named `name` / the window + with the given ID". If that window doesn't exist, make one. It's an + interesting idea, and would override the MRU selection based on current + desktop/monitor. This hasn't been explicitly requested, so I'm not diving into + it too deeply. +* Should the tray icon's list of windows include window titles? Both the name + and title? Maybe something like `({name}|{id}): {title}`? I'd bet that most + people don't end up naming their windows. + ## Resources Docs on adding a system tray item: * https://docs.microsoft.com/en-us/windows/win32/shell/notification-area * https://www.codeproject.com/Articles/18783/Example-of-a-SysTray-App-in-Win32 -[comment]: # Be sure to add links to references, resources, footnotes, etc. - - - +Docs regarding hiding a window from the taskbar: +* https://docs.microsoft.com/en-us/previous-versions//bb776822(v=vs.85)?redirectedfrom=MSDN#managing-taskbar-buttons ### Footnotes -
[1]: - +[1]: Quitting the terminal is different than closing the +windows one-by-one. Quiiting implies an atomic action, for closing all the +windows. Once [#766] lands, this will give us a chance to persist the state of +_all_ open windows. This will allow us to re-open with all the user's windows, +not just the one that happened to be closed last. [#653]: https://github.com/microsoft/terminal/issues/653 +[#766]: https://github.com/microsoft/terminal/issues/766 [#5727]: https://github.com/microsoft/terminal/issues/5727 + [Process Model 2.0 Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0/%235000%20-%20Process%20Model%202.0.md [Quake 3 sample]: https://youtu.be/ZmR6HQbuHPA?t=27 [`RegisterHotKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey diff --git a/doc/specs/#653 - Quake Mode/tray-icon-000.png b/doc/specs/#653 - Quake Mode/tray-icon-000.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2b8df90c7b7ab1795827f4c92a98dd14be14d5 GIT binary patch literal 19929 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 literal 0 HcmV?d00001 From 0d8874787a7fc113355522af46b569e841362be6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 15 Mar 2021 17:00:59 -0500 Subject: [PATCH 5/9] Addressing everything from the spec review --- .github/actions/spelling/dictionary/apis.txt | 1 + .github/actions/spelling/expect/expect.txt | 2 + .github/actions/spelling/expect/web.txt | 1 + .../#653 - Quake Mode/#653 - Quake Mode.md | 270 ++++++++++-------- 4 files changed, 155 insertions(+), 119 deletions(-) diff --git a/.github/actions/spelling/dictionary/apis.txt b/.github/actions/spelling/dictionary/apis.txt index cd95ff2d5be..bd441e8bfaa 100644 --- a/.github/actions/spelling/dictionary/apis.txt +++ b/.github/actions/spelling/dictionary/apis.txt @@ -5,6 +5,7 @@ bitfield bitfields CLASSNOTAVAILABLE cmdletbinding +colspan COLORPROPERTY CXICON CYICON diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index c3aba85a2c8..50fce35b8fd 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1004,6 +1004,7 @@ horiz HORZ hostable hostlib +hotkeys HPA HPAINTBUFFER HPCON @@ -1193,6 +1194,7 @@ jpeg jpg JPN json +jsonc jsoncpp jsprovider jumplist diff --git a/.github/actions/spelling/expect/web.txt b/.github/actions/spelling/expect/web.txt index d2156560b0b..b68cd0a783f 100644 --- a/.github/actions/spelling/expect/web.txt +++ b/.github/actions/spelling/expect/web.txt @@ -16,3 +16,4 @@ appshellintegration cppreference gfycat what3words +Guake diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md index 9240b7ed054..5d62ad92462 100644 --- a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -1,7 +1,7 @@ --- author: Mike Griese @zadjii-msft created on: 2021-02-23 -last updated: 2021-02-24 +last updated: 2021-03-15 issue id: #653 --- @@ -13,7 +13,7 @@ Many existing terminals support a feature whereby a user can press a keybinding anywhere in the OS, and summon their terminal application. Oftentimes the act of summoning this window is accompanied by a "dropdown" animation, where the window slides in to view from the top of the screen. This global summon action is often -referred to as "quake mode", a reference to the videogame Quake who's console +referred to as "quake mode", a reference to the video game Quake, who's console slid in from the top. This spec addresses both of the following two issues: @@ -33,7 +33,7 @@ existing terminal emulators support similar functionality: * **Tilda** allows the user to specify different keys to summon the window on different monitors. -* **Guake** alternatively allows the user to either sumon the terminal window to +* **Guake** alternatively allows the user to either summon the terminal window to a specific monitor, or whichever monitor the mouse is on. Guake only allows one single instance, so pressing the global hotkey always summons the same instance. @@ -65,6 +65,7 @@ To implement this feature, we'll add the following settings: * a new action, named `globalSummon`. * a new global, app setting named `minimizeToTray` * a new global, app setting named `alwaysShowTrayIcon` +* a new action, named `quakeMode`, and a specially named `_quake` window. ### `globalSummon` Action @@ -94,91 +95,24 @@ the new Monarch will re-register for the hotkeys. Since users may want to bind multiple keys to summon different windows, we'll need to allow the user to specify multiple keybindings simultaneously, each with -their own set of args. The following are three proposals for different ways for -allowing users to specify multiple global hotkeys: - -##### Proposal 1A +their own set of args. We stick all the `globalSummon`s in the actions array, like they're any other keybinding. However, these are not keys that are handled by the TerminalApp layer itself. -These are keys that need to be registered with the OS. So they don't make sense -to put in the normal `KeyMap`. We'll need to parse them not into the `KeyMap`, -but into a global summon list that the window layer can get at easier. - -**Pros**: -* We re-use the existing _action_ arg parsing entirely. -* These actions would show up as commands for the command palette. - - I don't know if that's desirable. Would we want them to? They'd need to - raise events into the window layer, for the window to dispatch them. - - If it is desirable, then these actions could also be added to the context - menu, the new tab dropdown, anywhere we plan on placing actions. - -**Cons**: -* We'll need to modify the keybinding parsing to place globalSummon actions into - a totally different object -* Not sure that having these available in the command palette really makes sense - -##### Proposal 1B - -If we're going to need another data structure (separate from the `KeyMap`), then -why have the `globalSummon` actions in `actions` at all? Instead, we could add a -top-level `globalHotkeys` array, with only global summon actions in it. - -```json -{ - "globalHotkeys": [ - { "keys": "win+1", "command": { "action": "globalSummon", "monitor": 1 } }, - { "keys": "win+2", "command": { "action": "globalSummon", "monitor": 2, "desktop": "toCurrent" } } - ] -} -``` - -**Pros**: -* We re-use the existing _action_ arg parsing entirely. -* Don't need to modify (complicate) the current keybinding parsing. -* Clearly separates these actions from the bulk of other actions - -**Cons**: -* Is fairly verbose to add to the settings. `"command": { "action": - "globalSummon"` is present in _all_ of the actions, but doesn't add value. -* Difficult, but not impossible, to get these actions into other places in the - UI that we want actions (unsure if this is even wanted) - -##### Proposal 1C - -The previous proposal is qute verbose. We've already got these actions in their -own setting. We could instead add a top-level `globalHotkeys` array, but with a -less verbose syntax. Everything in here is a global summon action, no need to -repeat ourselves: - -```json -{ - "globalHotkeys": [ - { "keys": "win+1", "monitor": 1 }, - { "keys": "win+2", "monitor": 2, "desktop": "toCurrent" } - ] -} -``` - -**Pros**: -* Is the least verbose way of adding these actions. -* Don't need to modify (complicate) the current keybinding parsing. -* Clearly separates these actions from the bulk of other actions. - -**Cons**: -* We're going to need a new _action_ parser just for global hotkeys like this. - One that always tries the action as a `globalSummon` action. -* **H**ard to get these actions into other places in the UI that we want actions - (unsure if this is even wanted) - -##### Conclusion - -From an engineering standpoint, I like 1C the best. It doesn't complicate the -existing keybinding parsing. The cost of modifying the existing action arg -parsing just for `globalSummon` actions shouldn't be too high. It's also the -most ergonomic for users to add this fairly complex setting. +These are keys that need to be registered with the OS. So while they will be in +the normal `KeyMap`, they will need to be retrieved from that object and +manually passed to the window layer. + +> A previous iteration of this spec considered placing the `globalSummon` +> actions in their own top-level array of the settings file, separate from the +> keybindings. This is no longer being considered, because it would not work for +> the case where the user has something like: +> ```json +> { "keys": "ctrl+c", "command": { "action": "globalSummon", "monitor": 1 } }, +> { "keys": "ctrl+v", "command": { "action": "copy" } }, +> ``` #### Which window, and where? @@ -199,27 +133,29 @@ two arguments to the `globalSummon` action: ```json "monitor": "any"|"toCurrent"|"onCurrent"|int, -"desktop": null|"toCurrent"|"onCurrent" +"desktop": "any"|"toCurrent"|"onCurrent" ``` The way these settings can be combined is in a table below. As an overview: * `monitor`: This controls the monitor that the window will be summoned from/to - - `"any"`/omitted: (_default_) Summon the MRU window, regardless of which - monitor it's currently on. - - `"toCurrent"`: Summon the MRU window TO the current monitor. - - `"onCurrent"`: Summon the MRU window ALREADY ON the current monitor. + - `"any"`: Summon the MRU window, regardless of which monitor it's currently + on. + - `"toCurrent"`/omitted: (_default_): Summon the MRU window **TO** the current + monitor. + - `"onCurrent"`: Summon the MRU window **ALREADY ON** the current monitor. - `int`: Summon the MRU window for the given monitor (as identified by the "Identify" displays feature in the OS settings) * `desktop`: This controls how the terminal should interact with virtual desktops. - - `null`/omitted: (_default_) Leave the window on whatever desktop it's - already on - we'll switch to that desktop as we activate the window. - - **TODO: FOR DISCUSSION**: Should this just be `any` to match the above? - Read the rest of the doc and come back. - - `"toCurrent"`: Move the window to the current virtual desktop - - `"onCurrent"`: Only summon the window if it's already on the current virtual - desktop + - `"any"`: Leave the window on whatever desktop it's already on - we'll switch + to that desktop as we activate the window. + - > NOTE: A previous version of this spec had this enum value as `null`. + This was changed to `"any"` for parity with the `monitor` property. + - `"toCurrent"`/omitted: (_default_): Move the window **to** the current + virtual desktop + - `"onCurrent"`: Only summon the window if it's **already on** the current + virtual desktop Together, these settings interact in the following ways: @@ -233,7 +169,7 @@ for pure markdown, sorry. --> "monitor" -null
Leave where it is +any
Leave where it is "toCurrent"
Move to current desktop "onCurrent"
On current desktop only @@ -379,6 +315,43 @@ As some additional examples: { "keys": "win+6", "monitor": "onCurrent", "desktop": "toCurrent" }, ``` +#### Summoning a specific window + +What if you want to press a keybinding to always summon a specific, named +window? This window might not be the most recent terminal window, nor one that +would be selected by the `monitor` and `desktop` selectors. You could name a +window "Epona", and press `win+e` to always summon the "Epona" window. + +We'll add the following property to address this scenario + +* `"window": string|int` + - When omitted (_default_): Use monitor and desktop to find the appropriate + MRU window to summon. + - When provided: Always summon the window who's name or ID matches the given + `window` value. If no such window exists, then create a new window with that + name/id. + +When provided _with_ `monitor` and `desktop`, `window` behaves in the following +ways: +* `desktop` + - `"any"`: Go to the desktop the given window is already on. + - `"toCurrent"`: If the window is on another virtual desktop, then move it to + the currently active one. + - `"onCurrent"`: If the window is on another virtual desktop, then move it to + the currently active one. +* `monitor` + - `"any"`: Leave the window on the monitor it is already on. + - `"toCurrent"`: If the window is on another monitor, then move it to the + currently active one. + - `"onCurrent"`: If the window is on another monitor, then move it to the + currently active one. + - ``: If the window is on another monitor, then move it to the specified + monitor. + +> NOTE: You read that right, `onCurrent` and `toCurrent` both do the same thing +> when `window` is provided. They both already know which window to select, the +> context of moving to the "current" monitor is all that those parameters add. + #### Other properties Some users would like the terminal to just appear when the global hotkey is @@ -388,11 +361,13 @@ like to configure the speed at which that dropdown happens. To support this functionality, the `globalSummon` action will support the following property: * `"dropdownDuration": float` - - When omitted, `0`, or a negative number: (_default_) No animation is used + - When omitted, `0`, or a negative number: No animation is used when summoning the window. The summoned window is focused immediately where it is. - When a positive number is provided, the terminal will use that value as a duration (in seconds) to slide the terminal into position when activated. + - The default would be some sensible value. The pane animation is .2s, so + `0.2` might be a resonable default here. We could have alternatively provided a `"dropdownSpeed"` setting, that provided a number of pixels per second. In my opinion, that would be harder for users to @@ -410,9 +385,54 @@ the following property: * `"hideWhenVisible": bool` - When `true`: (_default_) When this hotkey is pressed, and the terminal window is currently active, minimize the window. + - When `dropdownDuration` is not `0`, then the window will slide back off + the top at the same speed as it would come down. - When `false`: When this hotkey is pressed, and the terminal window is currently active, do nothing. +### Quake Mode + +In addition to just summoning the window from anywhere, some terminals also +support a special "quake mode" buffer or window. This window is one that closely +emulates the console from quake: +* It's docked to the top of the screen +* It takes the full width of the monitor, and only the bottom can be resized +* It often doesn't have any other UI elements, like tabs + +For fun, we'll also be adding a special `"_quake"` window with the same +behavior. If the user names a window `_quake`, then it will behave in the +following special ways: + +* On launch, it will ignore the `initialPosition` and + `initialRows`/`initialCols` setting, and instead resize to the top half of the + monitor. +* On launch, it will ignore the `launchMode` setting, and always launch in focus + mode. + - Users can disable focus mode on the `_quake` window if they do want tabs. +* It will not be resizable from any side except the bottom of the window, nor + will it be drag-able. +* It will not be a valid target for the "most recent window" for window + glomming. If it's the only open window, with `"windowingBehavior": + "useExisting*"`, then a new window will be created instead. + - It _is_ still a valid target for something like `wt -w _quake new-tab` + +A window at runtime can be renamed to become the `_quake` window (if no other +`_quake` window exists). When it does, it will resize to the position of the +quake window, and enter focus mode. + +We'll also be adding a special action `quakeMode`. This action is a special case +of the `globalSummon` action, to specifically invoke the quake window in the +current place. It is basically the same thing as the more elaborate: + +```json +{ + "monitor": "toCurrent", + "desktop": "toCurrent", + "window": "_quake", + "dropdownDuration": 0.5 +}, +``` + ### Minimize to Tray Many users have requested that the terminal additionally supports minimizing the @@ -477,13 +497,22 @@ To summarize, we're proposing the following set of settings: { "minimizeToTray": bool, "alwaysShowTrayIcon": bool, - "globalHotkeys": [ + "actions": [ + { + "keys": KeyChord, + "command": { + "action": "globalSummon", + "dropdownDuration": float, + "hideWhenVisible": bool, + "monitor": "any"|"toCurrent"|"onCurrent"|int, + "desktop": "any"|"toCurrent"|"onCurrent" + } + }, { "keys": KeyChord, - "dropdownDuration": float, - "hideWhenVisible": bool, - "monitor": "any"|"toCurrent"|"onCurrent"|int, - "desktop": null|"toCurrent"|"onCurrent" + "command": { + "action": "quakeMode" + } } ] } @@ -568,14 +597,13 @@ that summons the MRU window, without dropdown. That would be a good place for anyone starting to work on this feature. From there, I imagine the following work would be needed: -* [ ] Change the `globalHotKey` to a list of `globalHotkeys`. `AppHost` would - need to be able to get _all_ of these hotkeys, and register all of them. Each - one would need to be assigned a unique ID, so `WM_HOTKEY` can identify which - hotkey was pressed. - - This could be committed without any other args to the `globalHotkeys`. - We'd assume the "default" behavior or summoning the MRU window, where it - is, no dropdown, to start with. From there, we'd add the remaining - properties: +* [ ] Add a `globalSummon` action. `AppHost` would need to be able to get _all_ + of these actions, and register all of them. Each one would need to be assigned + a unique ID, so `WM_HOTKEY` can identify which hotkey was pressed. + - This could be committed without any other args to the `globalHotkeys`. in + this initial version, the behavior would be summoning the MRU window, + where it is, no dropdown, to start with. From there, we'd add the + remaining properties: * [ ] Add support for the `hideWhenVisible` property * [ ] Add support for the `desktop` property to control how window summoning interacts with virtual desktops @@ -584,6 +612,12 @@ work would be needed: * [ ] Add the `minimizeToTray` setting, and implement it without any sort of flyout * [ ] Add a list of windows to the right-click flyout on the tray icon * [ ] Add support for the `alwaysShowTrayIcon` setting +* [ ] When the user creates a window named `_quake`, ignore the initial size, + position, and launch mode, and create the window in quake mode instead. + * [ ] Exempt the `_quake` window from window glomming + * [ ] Add the `quakeMode` action, which `globalSummon`'s the `_quake` window. + * [ ] Prevent the `_quake` window from being dragged or resized on the + top/left/right. ### Future Considerations @@ -591,16 +625,14 @@ work would be needed: I don't believe there are any other tracked requests that are planned that aren't already included in this spec. -* While writing this spec, I wondered if anyone would want something like - `"window": "name" int` as an arg to the `globalSummon` action. This would let - the user say "I always want to summon the window named `name` / the window - with the given ID". If that window doesn't exist, make one. It's an - interesting idea, and would override the MRU selection based on current - desktop/monitor. This hasn't been explicitly requested, so I'm not diving into - it too deeply. * Should the tray icon's list of windows include window titles? Both the name and title? Maybe something like `({name}|{id}): {title}`? I'd bet that most people don't end up naming their windows. +* Dropdown duration could be a `float|bool`, with `true`->(whatever the default + is), `false`->0. + - We could have the setting appear as a pair of radio buttons, with the first + disabling dropdown, and the second enabling a text box for inputting an + animation duration. ## Resources @@ -609,12 +641,12 @@ Docs on adding a system tray item: * https://www.codeproject.com/Articles/18783/Example-of-a-SysTray-App-in-Win32 Docs regarding hiding a window from the taskbar: -* https://docs.microsoft.com/en-us/previous-versions//bb776822(v=vs.85)?redirectedfrom=MSDN#managing-taskbar-buttons +* https://docs.microsoft.com/en-us/previous-versions//bb776822(v=vs.85)#managing-taskbar-buttons ### Footnotes
[1]: Quitting the terminal is different than closing the -windows one-by-one. Quiiting implies an atomic action, for closing all the +windows one-by-one. Quiting implies an atomic action, for closing all the windows. Once [#766] lands, this will give us a chance to persist the state of _all_ open windows. This will allow us to re-open with all the user's windows, not just the one that happened to be closed last. From 45c4f3d552231e80c810301d390177a5d482b800 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 16 Mar 2021 16:01:37 -0500 Subject: [PATCH 6/9] update from PR notes --- .../#653 - Quake Mode/#653 - Quake Mode.md | 81 ++++++++++++------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md index 5d62ad92462..0a4918f8597 100644 --- a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -1,7 +1,7 @@ --- author: Mike Griese @zadjii-msft created on: 2021-02-23 -last updated: 2021-03-15 +last updated: 2021-03-16 issue id: #653 --- @@ -76,7 +76,7 @@ summon. From a technical perspective, the action will work by using the [`RegisterHotKey`] function. This API allows us to bind a particular hotkey with -the OS. Whenever that hotkey is pressed, our window message loop will recieve a +the OS. Whenever that hotkey is pressed, our window message loop will receive a `WM_HOTKEY`. We'll use the payload of that window message to lookup the action arguments for that hotkey. Then we'll use those arguments to control which window is invoked, where, and how the window behaves. @@ -280,7 +280,7 @@ the discussion of these settings_) * **Story A** Press a hotkey anywhere to activate the single Terminal window wherever it was - - This is `{ "monitor": "any", "desktop": null }` + - This is `{ "monitor": "any", "desktop": "any" }` * **Story B** Press a hotkey anywhere to activate the single Terminal window _on the current monitor_. If it wasn't previously on that monitor, move it there. - This is `{ "monitor": "toCurrent" }` @@ -292,27 +292,27 @@ As some additional examples: ```json // Go to the MRU window, wherever it is -{ "keys": "win+1", "monitor":"any", "desktop": null }, -// Since "any" & null are the default values, just placing a single entry here -// will bind the same behavior: -{ "keys": "win+1" }, +{ "keys": "win+1", "command":{ "action":"globalSummon", "monitor":"any", "desktop": "any" } }, // activate the MRU window, and move it to this desktop & this monitor -{ "keys": "win+2", "monitor":"toCurrent", "desktop": "toCurrent" }, +{ "keys": "win+2", "command":{ "action":"globalSummon", "monitor":"toCurrent", "desktop": "toCurrent" } }, +// Since "toCurrent" & "toCurrent" are the default values, just placing a single +// entry here will bind the same behavior: +{ "keys": "win+2", "command": "globalSummon" }, // activate the MRU window on this desktop -{ "keys": "win+3", "monitor":"any", "desktop": "onCurrent" }, +{ "keys": "win+3", "command":{ "action":"globalSummon", "monitor":"any", "desktop": "onCurrent" } }, // Activate the MRU window on monitor 2 (from any desktop), and place it on the // current desktop. If there isn't one on monitor 2, make a new one. -{ "keys": "win+4", "monitor": 2, "desktop": "toCurrent" }, +{ "keys": "win+4", "command":{ "action":"globalSummon", "monitor": 2, "desktop": "toCurrent" } }, // Activate the MRU window on monitor 3 (ONLY THIS desktop), or make a new one. -{ "keys": "win+5", "monitor": 3, "desktop": "onCurrent" }, +{ "keys": "win+5", "command":{ "action":"globalSummon", "monitor": 3, "desktop": "onCurrent" } }, // Activate the MRU window on this monitor (from any desktop), and place it on // the current desktop. If there isn't one on this monitor, make a new one. -{ "keys": "win+6", "monitor": "onCurrent", "desktop": "toCurrent" }, +{ "keys": "win+6", "command":{ "action":"globalSummon", "monitor": "onCurrent", "desktop": "toCurrent" } }, ``` #### Summoning a specific window @@ -367,7 +367,7 @@ functionality, the `globalSummon` action will support the following property: - When a positive number is provided, the terminal will use that value as a duration (in seconds) to slide the terminal into position when activated. - The default would be some sensible value. The pane animation is .2s, so - `0.2` might be a resonable default here. + `0.2` might be a reasonable default here. We could have alternatively provided a `"dropdownSpeed"` setting, that provided a number of pixels per second. In my opinion, that would be harder for users to @@ -375,6 +375,9 @@ use correctly. I believe that it's easier for users to mentally picture "I'd like the dropdown to last 100ms" vs "My monitor is 1504px tall, so I need to set this to 15040 to make the window traverse the entire display in .1s" +> NOTE: `dropdownDuration` will be ignored when the user has animations disabled +> in the OS. In that case, the terminal will just appear, as if it was set to 0. + Some users might want to be able to use the global hotkey to hide the window when the window is already visible. This would let the hotkey act as a sort of global toggle for the Terminal window. Others might not like that behavior, and @@ -382,7 +385,7 @@ just want the action to always bring the Terminal into focus, and do nothing if the terminal is already focused. To facilitate both these use cases, we'll add the following property: -* `"hideWhenVisible": bool` +* `"toggleVisibility": bool` - When `true`: (_default_) When this hotkey is pressed, and the terminal window is currently active, minimize the window. - When `dropdownDuration` is not `0`, then the window will slide back off @@ -429,6 +432,7 @@ current place. It is basically the same thing as the more elaborate: "monitor": "toCurrent", "desktop": "toCurrent", "window": "_quake", + "toggleVisibility": true, "dropdownDuration": 0.5 }, ``` @@ -449,7 +453,8 @@ When users want to be able to "minimize to the tray", they want: * The window to no longer appear in the alt-tab order When minimized to the tray, it's almost as if there's no window for the Terminal -at all. This can be combined with the global hotkey to quickly restore the window. +at all. This can be combined with the global hotkey (or the tray icon's context +menu) to quickly restore the window. The tray icon could be used for a variety of purposes. As a simple start, we could include the following three options: @@ -503,7 +508,7 @@ To summarize, we're proposing the following set of settings: "command": { "action": "globalSummon", "dropdownDuration": float, - "hideWhenVisible": bool, + "toggleVisibility": bool, "monitor": "any"|"toCurrent"|"onCurrent"|int, "desktop": "any"|"toCurrent"|"onCurrent" } @@ -545,7 +550,7 @@ apps. Each privilege level has its own Monarch. The two are unable to communicate across the elevation boundary. This means that if the user often runs terminals in both contexts, then only one -will have the global hotkeys bound. The naïve implementation would have the +will have the global hotkeys bound. The naive implementation would have the first elevation level "win" the keybindings. A different option would be to have elevated windows not register global hotkeys @@ -575,19 +580,24 @@ worried about this. -If there are any other applications running that have already registered hotkeys -with `RegisterHotKey`, then it's possible that the Terminal's attempt to -register that hotkey will fail. If that should happen, then we should display a -warning dialog to the user indicating which hotkey will not work (because it's -already used for something else). - -Which is the "current" monitor? The one with the mouse or the one with the -active window? This isn't something that has an obvious answer. Guake implements -this feature where the "current monitor" is the one with the mouse on it. At -least for the first iterations of this action, that's what we'll use. - -`monitor: onCurrent|onCurrentWindow|toCurrent|` - +* If there are any other applications running that have already registered + hotkeys with `RegisterHotKey`, then it's possible that the Terminal's attempt + to register that hotkey will fail. If that should happen, then we should + display a warning dialog to the user indicating which hotkey will not work + (because it's already used for something else). + +* Which is the "current" monitor? The one with the mouse or the one with the + active window? This isn't something that has an obvious answer. Guake + implements this feature where the "current monitor" is the one with the mouse + on it. At least for the first iterations of this action, that's what we'll + use. + `monitor: onCurrent|onCurrentWindow|toCurrent|` + +* Currently, running both the Release and Preview versions of the Terminal at + the same time side-by-side is not generally supported. (For example, `wt.exe` + can only ever point at one of two.) If a user binds the same key to a + `globalSummon` or `quakeMode` action, then only one of the apps will actually + be able to successfully claim the global hotkey. ## Implementation plan @@ -604,7 +614,7 @@ work would be needed: this initial version, the behavior would be summoning the MRU window, where it is, no dropdown, to start with. From there, we'd add the remaining properties: - * [ ] Add support for the `hideWhenVisible` property + * [ ] Add support for the `toggleVisibility` property * [ ] Add support for the `desktop` property to control how window summoning interacts with virtual desktops * [ ] Add support for the `monitor` which monitor the window appears on. @@ -633,6 +643,15 @@ aren't already included in this spec. - We could have the setting appear as a pair of radio buttons, with the first disabling dropdown, and the second enabling a text box for inputting an animation duration. +* It might be an interesting idea to have the ability to dock the quake window + to a specific side of the monitor, not just the top. We could probably do that + with a global setting `"quakeModeDockSide": "top"|"left"|"bottom"|"right"` or + something like that. +* We might want to pre-load the quake window into the tray icon as an entry for + "Quake Mode", and otherwise exclude it from the list of windows in that menu. +* We might think of other things for the Quake Mode window in the future - this + spec is by no means comprehensive. For example, it might make sense for the + quake mode window to automatically open in "always on top" mode. ## Resources From a9128db0e240bea3060e7e45387cc5a49fa8b258 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 13 Apr 2021 06:06:49 -0500 Subject: [PATCH 7/9] bump spec from feedback --- .../#653 - Quake Mode/#653 - Quake Mode.md | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md index 0a4918f8597..b31c2a198d2 100644 --- a/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md +++ b/doc/specs/#653 - Quake Mode/#653 - Quake Mode.md @@ -1,7 +1,7 @@ --- author: Mike Griese @zadjii-msft created on: 2021-02-23 -last updated: 2021-03-16 +last updated: 2021-04-13 issue id: #653 --- @@ -157,6 +157,9 @@ The way these settings can be combined is in a table below. As an overview: - `"onCurrent"`: Only summon the window if it's **already on** the current virtual desktop +Neither `desktop` nor `monitor` is a required parameter - if either is ommitted, +the omitted property will default to `toCurrent`. + Together, these settings interact in the following ways: