Skip to content

Commit

Permalink
Make the window name _quake special (#9785)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request

This PR adds some special behavior to the window named "\_quake".
* When creating the quake window, it ignores "initialRows" and "initialCols" and opens on the top half of the monitor.
  - It uses `initialPosition` to determine which monitor this is
* It cannot be moved
* It can only be vertically resized on the bottom border.
* It's always in focus mode.
  - We should probably have an issue tracking "Allow showing tabs in focus mode"? Maybe?
  - This one element is maybe the one I'm least attached to

When renaming a window to "\_quake", it adopts all those behaviors as well. It does not exit focus mode when leaving QM, nor does it resize back. That seemed unnecessary. 

## References

* As spec'ed in #9274
* See also #8888

## PR Checklist
* [x] In the pursuit of #653 
* [x] I work here
* [ ] Tests added/passed
* [ ] Requires documentation to be updated, but I'm not gonna do any of that till quake mode is totally done. 

## Detailed Description of the Pull Request / Additional comments

Note that this doesn't do things like:
* dropdown
* global hotkey summon 
* summon to the current monitor 
* summon to the current desktop

I'm doing #653 _very_ piecemeal, to try and make the PRs less egregious.

## Validation Steps Performed

* validated that center on launch still works
* validated that QM works on different monitors based on `initialPosition`
* validated entering/exiting QM behaves as expected

## TODO!
* [ ] When snapping the quake window between desktops with <kbd>win+shift+arrow</kbd>, the window doesn't horizontally re-size to the new monitor dimensions. It should.
  • Loading branch information
zadjii-msft authored Apr 26, 2021
1 parent 3d09c7d commit dc66313
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 42 deletions.
6 changes: 6 additions & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1026,14 +1026,20 @@ hsl
hstr
hstring
hsv
HTBOTTOMLEFT
HTBOTTOMRIGHT
HTCAPTION
HTCLIENT
HTLEFT
htm
HTMAXBUTTON
HTMINBUTTON
html
HTMLTo
HTRIGHT
HTTOP
HTTOPLEFT
HTTOPRIGHT
hu
hungapp
HVP
Expand Down
18 changes: 16 additions & 2 deletions src/cascadia/TerminalApp/AppLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,21 @@ namespace winrt::TerminalApp::implementation
// launched _fullscreen_, toggle fullscreen mode. This will make sure
// that the window size is _first_ set up as something sensible, so
// leaving fullscreen returns to a reasonable size.
//
// We know at the start, that the root TerminalPage definitely isn't
// in focus nor fullscreen mode. So "Toggle" here will always work
// to "enable".
const auto launchMode = this->GetLaunchMode();
if (launchMode == LaunchMode::FullscreenMode)
if (IsQuakeWindow())
{
_root->ToggleFocusMode();
}
else if (launchMode == LaunchMode::FullscreenMode)
{
_root->ToggleFullscreen();
}
else if (launchMode == LaunchMode::FocusMode || launchMode == LaunchMode::MaximizedFocusMode)
else if (launchMode == LaunchMode::FocusMode ||
launchMode == LaunchMode::MaximizedFocusMode)
{
_root->ToggleFocusMode();
}
Expand Down Expand Up @@ -1439,4 +1448,9 @@ namespace winrt::TerminalApp::implementation
}
}

bool AppLogic::IsQuakeWindow() const noexcept
{
return _root->IsQuakeWindow();
}

}
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/AppLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ namespace winrt::TerminalApp::implementation
void WindowName(const winrt::hstring& name);
uint64_t WindowId();
void WindowId(const uint64_t& id);
bool IsQuakeWindow() const noexcept;

Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
bool CenterOnLaunch();
Expand Down Expand Up @@ -158,6 +159,7 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);

#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/AppLogic.idl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace TerminalApp
String WindowName;
UInt64 WindowId;
void RenameFailed();
Boolean IsQuakeWindow();

Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
Boolean CenterOnLaunch { get; };
Expand Down Expand Up @@ -85,5 +86,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
}
}
17 changes: 11 additions & 6 deletions src/cascadia/TerminalApp/TabManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,17 @@ namespace winrt::TerminalApp::implementation
(_tabs.Size() > 1) ||
_settings.GlobalSettings().AlwaysShowTabs());

// collapse/show the tabs themselves
_tabView.Visibility(isVisible ? Visibility::Visible : Visibility::Collapsed);

// collapse/show the row that the tabs are in.
// NaN is the special value XAML uses for "Auto" sizing.
_tabRow.Height(isVisible ? NAN : 0);
if (_tabView)
{
// collapse/show the tabs themselves
_tabView.Visibility(isVisible ? Visibility::Visible : Visibility::Collapsed);
}
if (_tabRow)
{
// collapse/show the row that the tabs are in.
// NaN is the special value XAML uses for "Auto" sizing.
_tabRow.Height(isVisible ? NAN : 0);
}
}

// Method Description:
Expand Down
34 changes: 31 additions & 3 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ namespace winrt
using IInspectable = Windows::Foundation::IInspectable;
}

static constexpr std::wstring_view QuakeWindowName{ L"_quake" };

namespace winrt::TerminalApp::implementation
{
TerminalPage::TerminalPage() :
Expand Down Expand Up @@ -2062,9 +2064,20 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::ToggleFocusMode()
{
_isInFocusMode = !_isInFocusMode;
_UpdateTabView();
_FocusModeChangedHandlers(*this, nullptr);
_SetFocusMode(!_isInFocusMode);
}

void TerminalPage::_SetFocusMode(const bool inFocusMode)
{
// If we're the quake window, we must always be in focus mode.
// Prevent leaving focus mode here.
const bool newInFocusMode = inFocusMode || IsQuakeWindow();
if (newInFocusMode != FocusMode())
{
_isInFocusMode = newInFocusMode;
_UpdateTabView();
_FocusModeChangedHandlers(*this, nullptr);
}
}

// Method Description:
Expand Down Expand Up @@ -2610,6 +2623,7 @@ namespace winrt::TerminalApp::implementation

winrt::fire_and_forget TerminalPage::WindowName(const winrt::hstring& value)
{
const bool oldIsQuakeMode = IsQuakeWindow();
const bool changed = _WindowName != value;
if (changed)
{
Expand All @@ -2631,6 +2645,16 @@ namespace winrt::TerminalApp::implementation
if (page->_startupState == StartupState::Initialized)
{
page->IdentifyWindow();

// If we're entering quake mode, or leaving it
if (IsQuakeWindow() != oldIsQuakeMode)
{
// If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode
// If we're entering Quake Mode from Focus Mode, then this will do nothing
// If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing
_SetFocusMode(true);
_IsQuakeWindowChangedHandlers(*this, nullptr);
}
}
}
}
Expand Down Expand Up @@ -2773,4 +2797,8 @@ namespace winrt::TerminalApp::implementation
}
}

bool TerminalPage::IsQuakeWindow() const noexcept
{
return WindowName() == QuakeWindowName;
}
}
4 changes: 4 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ namespace winrt::TerminalApp::implementation
void WindowId(const uint64_t& value);
winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept;
bool IsQuakeWindow() const noexcept;

WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);

Expand All @@ -122,6 +123,7 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(Initialized, IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs);
TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable);
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);

private:
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
Expand Down Expand Up @@ -337,6 +339,8 @@ namespace winrt::TerminalApp::implementation

void _UpdateTeachingTipTheme(winrt::Windows::UI::Xaml::FrameworkElement element);

void _SetFocusMode(const bool inFocusMode);

#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.idl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace TerminalApp
String WindowNameForDisplay { get; };
String WindowIdForDisplay { get; };
void RenameFailed();
Boolean IsQuakeWindow();

// We cannot use the default XAML APIs because we want to make sure
// that there's only one application-global dialog visible at a time,
Expand All @@ -54,5 +55,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
}
}
79 changes: 54 additions & 25 deletions src/cascadia/WindowsTerminal/AppHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ AppHost::AppHost() noexcept :
_window = std::make_unique<IslandWindow>();
}

// Update our own internal state tracking if we're in quake mode or not.
_IsQuakeWindowChanged(nullptr, nullptr);

// Tell the window to callback to us when it's about to handle a WM_CREATE
auto pfn = std::bind(&AppHost::_HandleCreateWindow,
this,
Expand Down Expand Up @@ -251,6 +254,7 @@ void AppHost::Initialize()
_logic.SetTaskbarProgress({ this, &AppHost::SetTaskbarProgress });
_logic.IdentifyWindowsRequested({ this, &AppHost::_IdentifyWindowsRequested });
_logic.RenameWindowRequested({ this, &AppHost::_RenameWindowRequested });
_logic.IsQuakeWindowChanged({ this, &AppHost::_IsQuakeWindowChanged });

_window->UpdateTitle(_logic.Title());

Expand Down Expand Up @@ -379,39 +383,58 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode

// Get the size of a window we'd need to host that client rect. This will
// add the titlebar space.
const auto nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix);
adjustedWidth = islandWidth + nonClientSize.cx;
adjustedHeight = islandHeight + nonClientSize.cy;
const til::size nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix);
adjustedWidth = islandWidth + nonClientSize.width<long>();
adjustedHeight = islandHeight + nonClientSize.height<long>();

til::size dimensions{ Utils::ClampToShortMax(adjustedWidth, 1),
Utils::ClampToShortMax(adjustedHeight, 1) };

// Find nearest monitor for the position that we've actually settled on
HMONITOR hMonNearest = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST);
MONITORINFO nearestMonitorInfo;
nearestMonitorInfo.cbSize = sizeof(MONITORINFO);
// Get monitor dimensions:
GetMonitorInfo(hMonNearest, &nearestMonitorInfo);
const til::size desktopDimensions{ gsl::narrow<short>(nearestMonitorInfo.rcWork.right - nearestMonitorInfo.rcWork.left),
gsl::narrow<short>(nearestMonitorInfo.rcWork.bottom - nearestMonitorInfo.rcWork.top) };

const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1),
Utils::ClampToShortMax(adjustedHeight, 1) };
til::point origin{ (proposedRect.left),
(proposedRect.top) };

if (centerOnLaunch)
if (_logic.IsQuakeWindow())
{
// If we just use rcWork by itself, we'll fail to account for the invisible
// space reserved for the resize handles. So retrieve that size here.
const til::size ncSize{ _window->GetTotalNonClientExclusiveSize(dpix) };
const til::size availableSpace = desktopDimensions + nonClientSize;

origin = til::point{
::base::ClampSub<long>(nearestMonitorInfo.rcWork.left, (nonClientSize.width() / 2)),
(nearestMonitorInfo.rcWork.top)
};
dimensions = til::size{
availableSpace.width(),
availableSpace.height() / 2
};
launchMode = LaunchMode::FocusMode;
}
else if (centerOnLaunch)
{
// Find nearest monitor for the position that we've actually settled on
HMONITOR hMonNearest = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST);
MONITORINFO nearestMonitorInfo;
nearestMonitorInfo.cbSize = sizeof(MONITORINFO);
// Get monitor dimensions:
GetMonitorInfo(hMonNearest, &nearestMonitorInfo);
const COORD desktopDimensions{ gsl::narrow<short>(nearestMonitorInfo.rcWork.right - nearestMonitorInfo.rcWork.left),
gsl::narrow<short>(nearestMonitorInfo.rcWork.bottom - nearestMonitorInfo.rcWork.top) };
// Move our proposed location into the center of that specific monitor.
proposedRect.left = nearestMonitorInfo.rcWork.left +
((desktopDimensions.X / 2) - (dimensions.X / 2));
proposedRect.top = nearestMonitorInfo.rcWork.top +
((desktopDimensions.Y / 2) - (dimensions.Y / 2));
origin = til::point{
(nearestMonitorInfo.rcWork.left + ((desktopDimensions.width() / 2) - (dimensions.width() / 2))),
(nearestMonitorInfo.rcWork.top + ((desktopDimensions.height() / 2) - (dimensions.height() / 2)))
};
}
const COORD origin{ gsl::narrow<short>(proposedRect.left),
gsl::narrow<short>(proposedRect.top) };

const auto newPos = Viewport::FromDimensions(origin, dimensions);
const til::rectangle newRect{ origin, dimensions };
bool succeeded = SetWindowPos(hwnd,
nullptr,
newPos.Left(),
newPos.Top(),
newPos.Width(),
newPos.Height(),
newRect.left<int>(),
newRect.top<int>(),
newRect.width<int>(),
newRect.height<int>(),
SWP_NOACTIVATE | SWP_NOZORDER);

// Refresh the dpi of HWND because the dpi where the window will launch may be different
Expand Down Expand Up @@ -674,3 +697,9 @@ winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Fou
}
}
}

void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->IsQuakeWindow(_logic.IsQuakeWindow());
}
3 changes: 3 additions & 0 deletions src/cascadia/WindowsTerminal/AppHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,7 @@ class AppHost
const winrt::TerminalApp::RenameWindowRequestedArgs args);

GUID _CurrentDesktopGuid();

void _IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
};
Loading

0 comments on commit dc66313

Please sign in to comment.