diff --git a/.github/actions/spelling/dictionary/apis.txt b/.github/actions/spelling/dictionary/apis.txt index f5812a01b..dc413e88b 100644 --- a/.github/actions/spelling/dictionary/apis.txt +++ b/.github/actions/spelling/dictionary/apis.txt @@ -47,6 +47,7 @@ ICustom IDialog IDirect IExplorer +IFACEMETHOD IFile IInheritable IMap diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index d7b58b73b..c4f267222 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -134,7 +134,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Peasant_Summon", TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), - TraceLoggingUInt64(localCopy->MoveToCurrentDesktop(), "MoveToCurrentDesktop", "true if we should move to the current desktop"), + TraceLoggingUInt64(localCopy.MoveToCurrentDesktop(), "MoveToCurrentDesktop", "true if we should move to the current desktop"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); _SummonRequestedHandlers(*this, localCopy); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index af74cf3d0..90ff1ab45 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -33,6 +33,7 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass SummonWindowBehavior { SummonWindowBehavior(); Boolean MoveToCurrentDesktop; + Boolean ToggleVisibility; // Other options: // * CurrentMonitor } diff --git a/src/cascadia/Remoting/SummonWindowBehavior.h b/src/cascadia/Remoting/SummonWindowBehavior.h index c460ef988..d26a21aff 100644 --- a/src/cascadia/Remoting/SummonWindowBehavior.h +++ b/src/cascadia/Remoting/SummonWindowBehavior.h @@ -22,10 +22,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation public: SummonWindowBehavior() = default; WINRT_PROPERTY(bool, MoveToCurrentDesktop, true); + WINRT_PROPERTY(bool, ToggleVisibility, true); public: SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) : - _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() } {}; + _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() }, + _ToggleVisibility{ other.ToggleVisibility() } {}; }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 3074db1b2..ee6998a61 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -1045,9 +1045,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GlobalSummonArgs() = default; WINRT_PROPERTY(winrt::hstring, Name, L""); WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent); + WINRT_PROPERTY(bool, ToggleVisibility, true); static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view DesktopKey{ "desktop" }; + static constexpr std::string_view ToggleVisibilityKey{ "toggleVisibility" }; public: hstring GenerateName() const; @@ -1057,7 +1059,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (auto otherAsUs = other.try_as()) { return otherAsUs->_Name == _Name && - otherAsUs->_Desktop == _Desktop; + otherAsUs->_Desktop == _Desktop && + otherAsUs->_ToggleVisibility == _ToggleVisibility; } return false; }; @@ -1067,6 +1070,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, NameKey, args->_Name); JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop); + JsonUtils::GetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility); return { *args, {} }; } IActionArgs Copy() const @@ -1074,6 +1078,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto copy{ winrt::make_self() }; copy->_Name = _Name; copy->_Desktop = _Desktop; + copy->_ToggleVisibility = _ToggleVisibility; return *copy; } // SPECIAL! This deserializer creates a GlobalSummonArgs with the diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index abf52e50b..527da24ef 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -264,5 +264,6 @@ namespace Microsoft.Terminal.Settings.Model { String Name { get; }; DesktopBehavior Desktop { get; }; + Boolean ToggleVisibility { get; }; }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 5fa03dbef..3d585b21c 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -38,17 +38,20 @@ namespace RemotingUnitTests { struct MockDesktopManager : implements { - HRESULT GetWindowDesktopId(HWND /*topLevelWindow*/, GUID* /*desktopId*/) + IFACEMETHOD(GetWindowDesktopId) + (HWND /*topLevelWindow*/, GUID* /*desktopId*/) { VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests."); return E_FAIL; } - HRESULT MoveWindowToDesktop(HWND /*topLevelWindow*/, REFGUID /*desktopId*/) + IFACEMETHOD(MoveWindowToDesktop) + (HWND /*topLevelWindow*/, REFGUID /*desktopId*/) { VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests."); return E_FAIL; } - HRESULT IsWindowOnCurrentVirtualDesktop(HWND topLevelWindow, BOOL* onCurrentDesktop) + IFACEMETHOD(IsWindowOnCurrentVirtualDesktop) + (HWND topLevelWindow, BOOL* onCurrentDesktop) { if (pfnIsWindowOnCurrentVirtualDesktop) { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 3430e69ba..5b7ca216f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -591,7 +591,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se { // 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. - _window->SummonWindow(); + _window->SummonWindow(false); _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } @@ -692,6 +692,7 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); + args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); _windowManager.SummonWindow(args); if (args.FoundMatch()) @@ -774,7 +775,7 @@ bool AppHost::_LazyLoadDesktopManager() void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::SummonWindowBehavior& args) { - _window->SummonWindow(); + _window->SummonWindow(args.ToggleVisibility()); if (args != nullptr && args.MoveToCurrentDesktop()) { diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index a4c249a5b..a842ec6c2 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -999,6 +999,33 @@ void IslandWindow::SetGlobalHotkeys(const std::vector +winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility) +{ + // On the foreground thread: + co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + + // * If the user doesn't want to toggleVisibility, then just always try to + // activate. + // * If the user does want to toggleVisibility, then dismiss the window if + // we're the current foreground window. + if (toggleVisibility && GetForegroundWindow() == _window.get()) + { + _globalDismissWindow(); + } + else + { + _globalActivateWindow(); + } +} + // Method Description: // - Force activate this window. This method will bring us to the foreground and // activate us. If the window is minimized, it will restore the window. If the @@ -1007,11 +1034,8 @@ void IslandWindow::SetGlobalHotkeys(const std::vector // Return Value: // - -winrt::fire_and_forget IslandWindow::SummonWindow() +void IslandWindow::_globalActivateWindow() { - // On the foreground thread: - co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - // From: https://stackoverflow.com/a/59659421 // > The trick is to make windows ‘think’ that our process and the target // > window (hwnd) are related by attaching the threads (using @@ -1039,6 +1063,18 @@ winrt::fire_and_forget IslandWindow::SummonWindow() LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); } +// Method Description: +// - Minimize the window. This is called when the window is summoned, but is +// already active +// Arguments: +// - +// Return Value: +// - +void IslandWindow::_globalDismissWindow() +{ + LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_MINIMIZE)); +} + bool IslandWindow::IsQuakeWindow() const noexcept { return _isQuakeWindow; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 29897f111..f73ecd5e6 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -41,7 +41,7 @@ public: void UnsetHotkeys(const std::vector& hotkeyList); void SetGlobalHotkeys(const std::vector& hotkeyList); - winrt::fire_and_forget SummonWindow(); + winrt::fire_and_forget SummonWindow(const bool toggleVisibility); bool IsQuakeWindow() const noexcept; void IsQuakeWindow(bool isQuakeWindow) noexcept; @@ -93,6 +93,9 @@ protected: void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam); long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize); + void _globalActivateWindow(); + void _globalDismissWindow(); + bool _isQuakeWindow{ false }; void _enterQuakeMode();