Add "monitor": "any"|"toCurrent"|"toMouse" setting to globalSummon (#10092)

#### ⚠️ this pr targets #9977

## Summary of the Pull Request

This adds support for part of the `monitor` property for `globalSummon`. It also goes a little off-spec:

```json
"monitor": "any"|"toCurrent"|"toMouse"
```

* `monitor`: This controls the monitor that the window will be summoned from/to
  - `"any"`: Summon the MRU window, regardless of which monitor it's currently on.
  - `"toCurrent"`/omitted: (_default_): Summon the MRU window **TO** the monitor with the current **foreground** window.
  - [**NEW**] `"toMouse"`: Summon the MRU window **TO** the monitor where the **mouse** cursor is.

When I was playing with this, It felt like `toMouse` was always what I wanted, not `toCurrent`. We can always just comment that out if we think that's contentious - I'm aware I didn't originally spec that.

## References
* Original thread: #653
* Spec: #9274 
* megathread: #8888

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-60325291
* [x] I work here
* [ ] Tests added/passed
* [ ] Requires documentation to be updated 😢 

## Detailed Description of the Pull Request / Additional comments

I made `toMouse` the default because it felt better. fite-me.jpg 

## Validation Steps Performed
my ever evolving blob:

```jsonc
    { "keys": "ctrl+`", "command": { "action": "quakeMode" } },
    { "keys": "ctrl+1", "command": { "action": "globalSummon" } },
    // { "keys": "ctrl+2", "command": { "action": "globalSummon", "desktop": "toCurrent" } },
    // { "keys": "ctrl+2", "command": { "action": "globalSummon", "toggleVisibility": false } },
    // { "keys": "ctrl+2", "command": { "action": "globalSummon", "dropdownDuration": 2000 } },
    { "keys": "ctrl+2", "command": { "action": "globalSummon", "monitor": "any" } },
    // { "keys": "ctrl+3", "command": { "action": "globalSummon", "desktop": "onCurrent" } },
    { "keys": "ctrl+3", "command": { "action": "globalSummon", "monitor": "toMouse" } },
    // { "keys": "ctrl+4", "command": { "action": "globalSummon", "desktop": "any" } },
    { "keys": "ctrl+4", "command": { "action": "globalSummon", "monitor": "toMouse", "dropdownDuration": 500 } },
    { "keys": "ctrl+5", "command": { "action": "globalSummon", "dropdownDuration": 500 } },
```
This commit is contained in:
Mike Griese 2021-05-17 07:57:08 -05:00 committed by GitHub
parent 6e11780ca6
commit 3866771b1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 271 additions and 29 deletions

View file

@ -1,7 +1,7 @@
--- ---
author: Mike Griese @zadjii-msft author: Mike Griese @zadjii-msft
created on: 2021-02-23 created on: 2021-02-23
last updated: 2021-04-21 last updated: 2021-05-13
issue id: #653 issue id: #653
--- ---
@ -132,7 +132,7 @@ the window. To try and satisfy all these scenarios, I'm proposing the following
two arguments to the `globalSummon` action: two arguments to the `globalSummon` action:
```json ```json
"monitor": "any"|"toCurrent"|"onCurrent"|int, "monitor": "any"|"toCurrent"|"toMouse"|"onCurrent"|int,
"desktop": "any"|"toCurrent"|"onCurrent" "desktop": "any"|"toCurrent"|"onCurrent"
``` ```
@ -141,8 +141,10 @@ 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 * `monitor`: This controls the monitor that the window will be summoned from/to
- `"any"`: Summon the MRU window, regardless of which monitor it's currently - `"any"`: Summon the MRU window, regardless of which monitor it's currently
on. on.
- `"toCurrent"`/omitted: (_default_): Summon the MRU window **TO** the current - `"toCurrent"`/omitted: (_default_): Summon the MRU window **TO** the monitor
monitor. with the current **foreground** window.
- `"toMouse"`: Summon the MRU window **TO** the monitor where the **mouse**
cursor is.
- `"onCurrent"`: Summon the MRU window **ALREADY ON** 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 - `int`: Summon the MRU window for the given monitor (as identified by the
"Identify" displays feature in the OS settings) "Identify" displays feature in the OS settings)
@ -193,16 +195,33 @@ Else:
</tr> </tr>
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
<tr> <tr>
<td><code>"toCurrent"</code><br> Summon the MRU window TO the current monitor</td> <td><code>"toCurrent"</code><br> Summon the MRU window TO the monitor with the foreground window</td>
<td>Go to the desktop the window is on, move to this monitor</td> <td>Go to the desktop the window is on, move to the monitor with the foreground window</td>
<td>Move the window to this desktop, move to this monitor</td> <td>Move the window to this desktop, move to the monitor with the foreground window</td>
<td> <td>
If there isn't one on this desktop: If there isn't one on this desktop:
* create a new one (on this monitor) * create a new one (on the monitor with the foreground window)
Else: Else:
* activate the one on this desktop, move to this window * activate the one on this desktop, move to the monitor with the foreground window
</td>
</tr>
<!-- ----------------------------------------------------------------------- -->
<tr>
<td>
<code>"toMouse"</code>
<sup><a href="#footnote-2">[2]</a></sup> <br>
Summon the MRU window TO the monitor with the mouse</td>
<td>Go to the desktop the window is on, move to the monitor with the mouse</td>
<td>Move the window to this desktop, move to the monitor with the mouse</td>
<td>
If there isn't one on this desktop:
* create a new one (on the monitor with the mouse)
Else:
* activate the one on this desktop, move to the monitor with the mouse
</td> </td>
</tr> </tr>
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
@ -673,6 +692,8 @@ aren't already included in this spec.
``` ```
That would allow the user some further customizations on the quake mode That would allow the user some further customizations on the quake mode
behaviors. behaviors.
- This was later converted to the idea in [#9992] - Add per-window-name global
settings
* Another proposed idea was a simplification of some of the summoning modes. `{ * Another proposed idea was a simplification of some of the summoning modes. `{
"monitor": "any", "desktop": "any" }` is a little long, and maybe not the most "monitor": "any", "desktop": "any" }` is a little long, and maybe not the most
apparent naming. Perhaps we could add another property like `summonMode` that apparent naming. Perhaps we could add another property like `summonMode` that
@ -698,9 +719,16 @@ 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, _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. not just the one that happened to be closed last.
<a name="footnote-2"><a>[2]: **Addenda, May 2021**: In the course of
implementation, it became apparent that there's an important UX difference
between summoning _to the monitor with the cursor_ vs _to the monitor with the
foreground window_. `"monitor": "toMouse"` was added as an option, to allow the
user to differentiate between the two behaviors.
[#653]: https://github.com/microsoft/terminal/issues/653 [#653]: https://github.com/microsoft/terminal/issues/653
[#766]: https://github.com/microsoft/terminal/issues/766 [#766]: https://github.com/microsoft/terminal/issues/766
[#5727]: https://github.com/microsoft/terminal/issues/5727 [#5727]: https://github.com/microsoft/terminal/issues/5727
[#9992]: https://github.com/microsoft/terminal/issues/9992
[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 [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 [Quake 3 sample]: https://youtu.be/ZmR6HQbuHPA?t=27

View file

@ -30,13 +30,21 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.DateTime ActivatedTime { get; }; Windows.Foundation.DateTime ActivatedTime { get; };
}; };
enum MonitorBehavior
{
InPlace,
ToCurrent,
ToMouse,
};
[default_interface] runtimeclass SummonWindowBehavior { [default_interface] runtimeclass SummonWindowBehavior {
SummonWindowBehavior(); SummonWindowBehavior();
Boolean MoveToCurrentDesktop; Boolean MoveToCurrentDesktop;
Boolean ToggleVisibility; Boolean ToggleVisibility;
UInt32 DropdownDuration; UInt32 DropdownDuration;
// Other options: MonitorBehavior ToMonitor;
// * CurrentMonitor
} }
interface IPeasant interface IPeasant

View file

@ -24,10 +24,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
WINRT_PROPERTY(bool, MoveToCurrentDesktop, true); WINRT_PROPERTY(bool, MoveToCurrentDesktop, true);
WINRT_PROPERTY(bool, ToggleVisibility, true); WINRT_PROPERTY(bool, ToggleVisibility, true);
WINRT_PROPERTY(uint32_t, DropdownDuration, 0); WINRT_PROPERTY(uint32_t, DropdownDuration, 0);
WINRT_PROPERTY(Remoting::MonitorBehavior, ToMonitor, Remoting::MonitorBehavior::ToCurrent);
public: public:
SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) : SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) :
_MoveToCurrentDesktop{ other.MoveToCurrentDesktop() }, _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() },
_ToMonitor{ other.ToMonitor() },
_DropdownDuration{ other.DropdownDuration() }, _DropdownDuration{ other.DropdownDuration() },
_ToggleVisibility{ other.ToggleVisibility() } {}; _ToggleVisibility{ other.ToggleVisibility() } {};
}; };

View file

@ -1158,11 +1158,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
GlobalSummonArgs() = default; GlobalSummonArgs() = default;
WINRT_PROPERTY(winrt::hstring, Name, L""); WINRT_PROPERTY(winrt::hstring, Name, L"");
WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent); WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent);
WINRT_PROPERTY(Model::MonitorBehavior, Monitor, Model::MonitorBehavior::ToMouse);
WINRT_PROPERTY(bool, ToggleVisibility, true); WINRT_PROPERTY(bool, ToggleVisibility, true);
WINRT_PROPERTY(uint32_t, DropdownDuration, 0); WINRT_PROPERTY(uint32_t, DropdownDuration, 0);
static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view DesktopKey{ "desktop" }; static constexpr std::string_view DesktopKey{ "desktop" };
static constexpr std::string_view MonitorKey{ "monitor" };
static constexpr std::string_view ToggleVisibilityKey{ "toggleVisibility" }; static constexpr std::string_view ToggleVisibilityKey{ "toggleVisibility" };
static constexpr std::string_view DropdownDurationKey{ "dropdownDuration" }; static constexpr std::string_view DropdownDurationKey{ "dropdownDuration" };
@ -1175,6 +1177,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ {
return otherAsUs->_Name == _Name && return otherAsUs->_Name == _Name &&
otherAsUs->_Desktop == _Desktop && otherAsUs->_Desktop == _Desktop &&
otherAsUs->_Monitor == _Monitor &&
otherAsUs->_DropdownDuration == _DropdownDuration && otherAsUs->_DropdownDuration == _DropdownDuration &&
otherAsUs->_ToggleVisibility == _ToggleVisibility; otherAsUs->_ToggleVisibility == _ToggleVisibility;
} }
@ -1186,6 +1189,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto args = winrt::make_self<GlobalSummonArgs>(); auto args = winrt::make_self<GlobalSummonArgs>();
JsonUtils::GetValueForKey(json, NameKey, args->_Name); JsonUtils::GetValueForKey(json, NameKey, args->_Name);
JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop); JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop);
JsonUtils::GetValueForKey(json, MonitorKey, args->_Monitor);
JsonUtils::GetValueForKey(json, DropdownDurationKey, args->_DropdownDuration); JsonUtils::GetValueForKey(json, DropdownDurationKey, args->_DropdownDuration);
JsonUtils::GetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility); JsonUtils::GetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility);
return { *args, {} }; return { *args, {} };
@ -1195,6 +1199,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto copy{ winrt::make_self<GlobalSummonArgs>() }; auto copy{ winrt::make_self<GlobalSummonArgs>() };
copy->_Name = _Name; copy->_Name = _Name;
copy->_Desktop = _Desktop; copy->_Desktop = _Desktop;
copy->_Monitor = _Monitor;
copy->_DropdownDuration = _DropdownDuration; copy->_DropdownDuration = _DropdownDuration;
copy->_ToggleVisibility = _ToggleVisibility; copy->_ToggleVisibility = _ToggleVisibility;
return *copy; return *copy;
@ -1213,7 +1218,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
} }
size_t Hash() const size_t Hash() const
{ {
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Name, _Desktop, _ToggleVisibility); return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Name, _Desktop, _Monitor, _DropdownDuration, _ToggleVisibility);
} }
}; };

View file

@ -92,6 +92,13 @@ namespace Microsoft.Terminal.Settings.Model
OnCurrent, OnCurrent,
}; };
enum MonitorBehavior
{
Any,
ToCurrent,
ToMouse,
};
[default_interface] runtimeclass NewTerminalArgs { [default_interface] runtimeclass NewTerminalArgs {
NewTerminalArgs(); NewTerminalArgs();
NewTerminalArgs(Int32 profileIndex); NewTerminalArgs(Int32 profileIndex);
@ -266,6 +273,7 @@ namespace Microsoft.Terminal.Settings.Model
{ {
String Name { get; }; String Name { get; };
DesktopBehavior Desktop { get; }; DesktopBehavior Desktop { get; };
MonitorBehavior Monitor { get; };
Boolean ToggleVisibility { get; }; Boolean ToggleVisibility { get; };
UInt32 DropdownDuration { get; }; UInt32 DropdownDuration { get; };
}; };

View file

@ -457,3 +457,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::DesktopBehavior)
pair_type{ "onCurrent", ValueType::OnCurrent }, pair_type{ "onCurrent", ValueType::OnCurrent },
}; };
}; };
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::MonitorBehavior)
{
JSON_MAPPINGS(3) = {
pair_type{ "any", ValueType::Any },
pair_type{ "toCurrent", ValueType::ToCurrent },
pair_type{ "toMouse", ValueType::ToMouse },
};
};

View file

@ -589,9 +589,13 @@ bool AppHost::HasWindow()
void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/,
Remoting::CommandlineArgs args) Remoting::CommandlineArgs args)
{ {
const Remoting::SummonWindowBehavior summonArgs{};
summonArgs.MoveToCurrentDesktop(false);
summonArgs.DropdownDuration(0);
summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace);
// Summon the window whenever we dispatch a commandline to it. This will // Summon the window whenever we dispatch a commandline to it. This will
// make it obvious when a new tab/pane is created in a window. // make it obvious when a new tab/pane is created in a window.
_window->SummonWindow(false, 0); _window->SummonWindow(summonArgs);
_logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory());
} }
@ -695,6 +699,19 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex)
args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility());
args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration());
switch (summonArgs.Monitor())
{
case Settings::Model::MonitorBehavior::Any:
args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace);
break;
case Settings::Model::MonitorBehavior::ToCurrent:
args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToCurrent);
break;
case Settings::Model::MonitorBehavior::ToMouse:
args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToMouse);
break;
}
_windowManager.SummonWindow(args); _windowManager.SummonWindow(args);
if (args.FoundMatch()) if (args.FoundMatch())
{ {
@ -776,7 +793,7 @@ bool AppHost::_LazyLoadDesktopManager()
void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const Remoting::SummonWindowBehavior& args) const Remoting::SummonWindowBehavior& args)
{ {
_window->SummonWindow(args.ToggleVisibility(), args.DropdownDuration()); _window->SummonWindow(args);
if (args != nullptr && args.MoveToCurrentDesktop()) if (args != nullptr && args.MoveToCurrentDesktop())
{ {

View file

@ -16,6 +16,7 @@ using namespace winrt::Windows::UI::Xaml::Hosting;
using namespace winrt::Windows::Foundation::Numerics; using namespace winrt::Windows::Foundation::Numerics;
using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal;
using namespace ::Microsoft::Console::Types; using namespace ::Microsoft::Console::Types;
#define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS" #define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS"
@ -1009,14 +1010,14 @@ void IslandWindow::SetGlobalHotkeys(const std::vector<winrt::Microsoft::Terminal
// - toggleVisibility: controls how we should behave when already in the foreground. // - toggleVisibility: controls how we should behave when already in the foreground.
// Return Value: // Return Value:
// - <none> // - <none>
winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility, const uint32_t dropdownDuration) winrt::fire_and_forget IslandWindow::SummonWindow(Remoting::SummonWindowBehavior args)
{ {
// On the foreground thread: // On the foreground thread:
co_await winrt::resume_foreground(_rootGrid.Dispatcher()); co_await winrt::resume_foreground(_rootGrid.Dispatcher());
uint32_t actualDropdownDuration = dropdownDuration; uint32_t actualDropdownDuration = args.DropdownDuration();
// If the user requested an animation, let's check if animations are enabled in the OS. // If the user requested an animation, let's check if animations are enabled in the OS.
if (dropdownDuration > 0) if (args.DropdownDuration() > 0)
{ {
BOOL animationsEnabled = TRUE; BOOL animationsEnabled = TRUE;
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0); SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
@ -1036,15 +1037,38 @@ winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility, c
// * If the user doesn't want to toggleVisibility, then just always try to // * If the user doesn't want to toggleVisibility, then just always try to
// activate. // activate.
// * If the user does want to toggleVisibility, then dismiss the window if // * If the user does want to toggleVisibility,
// we're the current foreground window. // - If we're the foreground window, ToMonitor == ToMouse, and the mouse is on the monitor we are
if (toggleVisibility && GetForegroundWindow() == _window.get()) // - activate the window
// - else
// - dismiss the window
if (args.ToggleVisibility() && GetForegroundWindow() == _window.get())
{ {
_globalDismissWindow(actualDropdownDuration); bool handled = false;
// They want to toggle the window when it is the FG window, and we are
// the FG window. However, if we're on a different monitor than the
// mouse, then we should move to that monitor instead of dismissing.
if (args.ToMonitor() == Remoting::MonitorBehavior::ToMouse)
{
const til::rectangle cursorMonitorRect{ _getMonitorForCursor().rcMonitor };
const til::rectangle currentMonitorRect{ _getMonitorForWindow(GetHandle()).rcMonitor };
if (cursorMonitorRect != currentMonitorRect)
{
// We're not on the same monitor as the mouse. Go to that monitor.
_globalActivateWindow(actualDropdownDuration, args.ToMonitor());
handled = true;
}
}
if (!handled)
{
_globalDismissWindow(actualDropdownDuration);
}
} }
else else
{ {
_globalActivateWindow(actualDropdownDuration); _globalActivateWindow(actualDropdownDuration, args.ToMonitor());
} }
} }
@ -1105,8 +1129,15 @@ void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool
SetWindowRgn(_interopWindowHandle, nullptr, true); SetWindowRgn(_interopWindowHandle, nullptr, true);
} }
void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration) void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration,
const Remoting::MonitorBehavior toMonitor)
{ {
// First, get the window that's currently in the foreground. We'll need
// _this_ window to be able to appear on top of. If we just use
// GetForegroundWindow afer the SetWindowPlacement call, _we_ will be the
// foreground window.
const auto oldForegroundWindow = GetForegroundWindow();
// First, restore the window. SetWindowPlacement has a fun undocumented // First, restore the window. SetWindowPlacement has a fun undocumented
// piece of functionality where it will restore the window position // piece of functionality where it will restore the window position
// _without_ the animation, so use that instead of ShowWindow(SW_RESTORE). // _without_ the animation, so use that instead of ShowWindow(SW_RESTORE).
@ -1116,6 +1147,9 @@ void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration)
wpc.showCmd = SW_RESTORE; wpc.showCmd = SW_RESTORE;
SetWindowPlacement(_window.get(), &wpc); SetWindowPlacement(_window.get(), &wpc);
// Possibly go to the monitor of the mouse / old foreground window.
_moveToMonitor(oldForegroundWindow, toMonitor);
// Now that we're visible, animate the dropdown. // Now that we're visible, animate the dropdown.
_doSlideAnimation(dropdownDuration, true); _doSlideAnimation(dropdownDuration, true);
} }
@ -1145,8 +1179,15 @@ void IslandWindow::_slideUpWindow(const uint32_t dropdownDuration)
// milliseconds. If 0, we won't perform a dropdown animation. // milliseconds. If 0, we won't perform a dropdown animation.
// Return Value: // Return Value:
// - <none> // - <none>
void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration) void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration,
const Remoting::MonitorBehavior toMonitor)
{ {
// First, get the window that's currently in the foreground. We'll need
// _this_ window to be able to appear on top of. If we just use
// GetForegroundWindow afer the SetWindowPlacement/ShowWindow call, _we_
// will be the foreground window.
const auto oldForegroundWindow = GetForegroundWindow();
// From: https://stackoverflow.com/a/59659421 // From: https://stackoverflow.com/a/59659421
// > The trick is to make windows think that our process and the target // > The trick is to make windows think that our process and the target
// > window (hwnd) are related by attaching the threads (using // > window (hwnd) are related by attaching the threads (using
@ -1158,16 +1199,19 @@ void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration)
{ {
if (dropdownDuration > 0) if (dropdownDuration > 0)
{ {
_dropdownWindow(dropdownDuration); _dropdownWindow(dropdownDuration, toMonitor);
} }
else else
{ {
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_RESTORE)); LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_RESTORE));
// Once we've been restored, throw us on the active monitor.
_moveToMonitor(oldForegroundWindow, toMonitor);
} }
} }
else else
{ {
const DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), nullptr); const DWORD windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr);
const DWORD currentThreadId = GetCurrentThreadId(); const DWORD currentThreadId = GetCurrentThreadId();
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true)); LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true));
@ -1181,6 +1225,9 @@ void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration)
// Activate the window too. This will force us to the virtual desktop this // Activate the window too. This will force us to the virtual desktop this
// window is on, if it's on another virtual desktop. // window is on, if it's on another virtual desktop.
LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get()));
// Throw us on the active monitor.
_moveToMonitor(oldForegroundWindow, toMonitor);
} }
} }
@ -1206,6 +1253,115 @@ void IslandWindow::_globalDismissWindow(const uint32_t dropdownDuration)
} }
} }
// Method Description:
// - Get the monitor the mouse cursor is currently on
// Arguments:
// - dropdownDuration: The duration to play the slide-up animation, in
// milliseconds. If 0, we won't perform a slide-up animation.
// Return Value:
// - The MONITORINFO for the monitor the mouse cursor is on
MONITORINFO IslandWindow::_getMonitorForCursor()
{
POINT p{};
GetCursorPos(&p);
// Get the monitor info for the window's current monitor.
MONITORINFO activeMonitor{};
activeMonitor.cbSize = sizeof(activeMonitor);
GetMonitorInfo(MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST), &activeMonitor);
return activeMonitor;
}
// Method Description:
// - Get the monitor for a given HWND
// Arguments:
// - <none>
// Return Value:
// - The MONITORINFO for the given HWND
MONITORINFO IslandWindow::_getMonitorForWindow(HWND foregroundWindow)
{
// Get the monitor info for the window's current monitor.
MONITORINFO activeMonitor{};
activeMonitor.cbSize = sizeof(activeMonitor);
GetMonitorInfo(MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTONEAREST), &activeMonitor);
return activeMonitor;
}
// Method Description:
// - Based on the value in toMonitor, move the window to the monitor of the
// given HWND, the monitor of the mouse pointer, or just leave it where it is.
// Arguments:
// - oldForegroundWindow: when toMonitor is ToCurrent, we'll move to the monitor
// of this HWND. Otherwise, this param is ignored.
// - toMonitor: Controls which monitor we should move to.
// Return Value:
// - <none>
void IslandWindow::_moveToMonitor(HWND oldForegroundWindow, Remoting::MonitorBehavior toMonitor)
{
if (toMonitor == Remoting::MonitorBehavior::ToCurrent)
{
_moveToMonitorOf(oldForegroundWindow);
}
else if (toMonitor == Remoting::MonitorBehavior::ToMouse)
{
_moveToMonitorOfMouse();
}
}
// Method Description:
// - Move our window to the monitor the mouse is on.
// Arguments:
// - <none>
// Return Value:
// - <none>
void IslandWindow::_moveToMonitorOfMouse()
{
_moveToMonitor(_getMonitorForCursor());
}
// Method Description:
// - Move our window to the monitor that the given HWND is on.
// Arguments:
// - <none>
// Return Value:
// - <none>
void IslandWindow::_moveToMonitorOf(HWND foregroundWindow)
{
_moveToMonitor(_getMonitorForWindow(foregroundWindow));
}
// Method Description:
// - Move our window to the given monitor. This will do nothing if we're already
// on that monitor.
// - We'll retain the same relative position on the new monitor as we had on the
// old monitor.
// Arguments:
// - activeMonitor: the monitor to move to.
// Return Value:
// - <none>
void IslandWindow::_moveToMonitor(const MONITORINFO activeMonitor)
{
// Get the monitor info for the window's current monitor.
const auto currentMonitor = _getMonitorForWindow(GetHandle());
const til::rectangle currentRect{ currentMonitor.rcMonitor };
const til::rectangle activeRect{ activeMonitor.rcMonitor };
if (currentRect != activeRect)
{
const til::rectangle currentWindowRect{ GetWindowRect() };
const til::point offset{ currentWindowRect.origin() - currentRect.origin() };
const til::point newOrigin{ activeRect.origin() + offset };
SetWindowPos(GetHandle(),
0,
newOrigin.x<int>(),
newOrigin.y<int>(),
currentWindowRect.width<int>(),
currentWindowRect.height<int>(),
SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
bool IslandWindow::IsQuakeWindow() const noexcept bool IslandWindow::IsQuakeWindow() const noexcept
{ {
return _isQuakeWindow; return _isQuakeWindow;

View file

@ -41,7 +41,7 @@ public:
void UnsetHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList); void UnsetHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList);
void SetGlobalHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList); void SetGlobalHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList);
winrt::fire_and_forget SummonWindow(const bool toggleVisibility, const uint32_t dropdownDuration); winrt::fire_and_forget SummonWindow(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);
bool IsQuakeWindow() const noexcept; bool IsQuakeWindow() const noexcept;
void IsQuakeWindow(bool isQuakeWindow) noexcept; void IsQuakeWindow(bool isQuakeWindow) noexcept;
@ -93,12 +93,21 @@ protected:
void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam); void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam);
long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize); long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize);
void _globalActivateWindow(const uint32_t dropdownDuration); void _globalActivateWindow(const uint32_t dropdownDuration,
void _dropdownWindow(const uint32_t dropdownDuration); const winrt::Microsoft::Terminal::Remoting::MonitorBehavior toMonitor);
void _dropdownWindow(const uint32_t dropdownDuration,
const winrt::Microsoft::Terminal::Remoting::MonitorBehavior toMonitor);
void _slideUpWindow(const uint32_t dropdownDuration); void _slideUpWindow(const uint32_t dropdownDuration);
void _doSlideAnimation(const uint32_t dropdownDuration, const bool down); void _doSlideAnimation(const uint32_t dropdownDuration, const bool down);
void _globalDismissWindow(const uint32_t dropdownDuration); void _globalDismissWindow(const uint32_t dropdownDuration);
static MONITORINFO _getMonitorForCursor();
static MONITORINFO _getMonitorForWindow(HWND foregroundWindow);
void _moveToMonitor(HWND foregroundWindow, const winrt::Microsoft::Terminal::Remoting::MonitorBehavior toMonitor);
void _moveToMonitorOfMouse();
void _moveToMonitorOf(HWND foregroundWindow);
void _moveToMonitor(const MONITORINFO activeMonitor);
bool _isQuakeWindow{ false }; bool _isQuakeWindow{ false };
void _enterQuakeMode(); void _enterQuakeMode();