-
Notifications
You must be signed in to change notification settings - Fork 8.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add property to control dropdown speed of global summon #9977
Changes from 110 commits
3bef7bb
1f52d35
5a9cdc8
36539cf
27ace16
9a41647
5cabcfb
03bfc6e
590b9ff
0579b24
a3faed6
9c6eac4
0103331
b4fe1bf
d08e65c
e101efd
e1402d8
f978a9c
5939636
c088895
fa2df47
0f5c24f
921d915
00184e7
658db6b
977db46
bcbef34
813dbc6
0f0df5e
9fc2f0e
6537686
a75da0a
c02f25a
3e39ab9
52b2cb6
bc492f1
88ffc6f
be74b2e
2a7bc94
81c09d9
c34e4ce
5b8ace2
689c385
59deca1
f02969b
b2db317
d2a3438
a65f341
1c2f8e5
71f6b58
18d1a20
a41bee6
6e7ea61
18099d2
3e5d927
91b52d4
848682a
1dcb4cb
fee6473
342d3f2
5052d31
eff18d1
5c039ea
10779ca
25b31ff
7c2a514
ac8fef0
1fdb6b1
784ec73
b20222f
9d76c62
2a2f5cb
71577fc
f892752
4166afa
717db81
1b2e7a7
305e627
9f9eacb
f71c948
51617cf
2eec961
15a8a9c
8370789
4eb1d3a
c215a97
26d5194
91372ea
045a8db
c95a429
b5e0f2d
6c4238b
0abd963
643b860
84c1bd9
24bfbe6
4c2e6d2
aec2561
22036c2
173720f
b607a55
63b3658
5940f05
06cb41b
783be75
6c424f7
387d675
f190576
960e1fa
caeb17f
2d7ed8d
f4c9993
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ HIGHCONTRASTON | |
HIGHCONTRASTW | ||
hotkeys | ||
href | ||
hrgn | ||
IActivation | ||
IApp | ||
IAppearance | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(false); | ||
_window->SummonWindow(false, 0); | ||
_logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); | ||
} | ||
|
||
|
@@ -693,6 +693,7 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) | |
args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); | ||
args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); | ||
args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); | ||
args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); | ||
|
||
_windowManager.SummonWindow(args); | ||
if (args.FoundMatch()) | ||
|
@@ -775,7 +776,7 @@ bool AppHost::_LazyLoadDesktopManager() | |
void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, | ||
const Remoting::SummonWindowBehavior& args) | ||
{ | ||
_window->SummonWindow(args.ToggleVisibility()); | ||
_window->SummonWindow(args.ToggleVisibility(), args.DropdownDuration()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason not to just pass down the whole There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose not really There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not make IslandWindow more specific to Terminal. I'd prefer it to be less specific to Terminal IMO 😄 |
||
|
||
if (args != nullptr && args.MoveToCurrentDesktop()) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1007,34 +1007,143 @@ void IslandWindow::SetGlobalHotkeys(const std::vector<winrt::Microsoft::Terminal | |
// - toggleVisibility: controls how we should behave when already in the foreground. | ||
// Return Value: | ||
// - <none> | ||
winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility) | ||
winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility, const uint32_t dropdownDuration) | ||
{ | ||
// On the foreground thread: | ||
co_await winrt::resume_foreground(_rootGrid.Dispatcher()); | ||
|
||
uint32_t actualDropdownDuration = dropdownDuration; | ||
// If the user requested an animation, let's check if animations are enabled in the OS. | ||
if (dropdownDuration > 0) | ||
{ | ||
BOOL animationsEnabled = TRUE; | ||
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0); | ||
if (!animationsEnabled) | ||
{ | ||
// The OS has animations disabled - we should respect that and | ||
// disable the animation here. | ||
// | ||
// We're doing this here, rather than in _doSlideAnimation, to | ||
// preempt any other specific behavior that | ||
// _globalActivateWindow/_globalDismissWindow might do if they think | ||
// there should be an animation (like making the window appear with | ||
// SetWindowPlacement rather than ShowWindow) | ||
actualDropdownDuration = 0; | ||
} | ||
} | ||
|
||
// * 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(); | ||
_globalDismissWindow(actualDropdownDuration); | ||
} | ||
else | ||
{ | ||
_globalActivateWindow(); | ||
_globalActivateWindow(actualDropdownDuration); | ||
} | ||
} | ||
|
||
// Method Description: | ||
// - Helper for performing a sliding animation. This will animate our _Xaml | ||
// Island_, either growing down or shrinking up, using SetWindowRgn. | ||
// - This function does the entire animation on the main thread (the UI thread), | ||
// and **DOES NOT YIELD IT**. The window will be animating for the entire | ||
// duration of dropdownDuration. | ||
// - At the end of the animation, we'll reset the window region, so that it's as | ||
// if nothing occurred. | ||
// Arguments: | ||
// - dropdownDuration: The duration to play the animation, in milliseconds. If | ||
// 0, we won't perform a dropdown animation. | ||
// - down: if true, increase the height from top to bottom. otherwise, decrease | ||
// the height, from bottom to top. | ||
// Return Value: | ||
// - <none> | ||
void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool down) | ||
{ | ||
til::rectangle fullWindowSize{ GetWindowRect() }; | ||
const double fullHeight = fullWindowSize.height<double>(); | ||
|
||
const double animationDuration = dropdownDuration; // use floating-point math throughout | ||
const auto start = std::chrono::system_clock::now(); | ||
|
||
// Do at most dropdownDuration frames. After that, just bail straight to the | ||
// final state. | ||
for (uint32_t i = 0; i < dropdownDuration; i++) | ||
{ | ||
const auto end = std::chrono::system_clock::now(); | ||
const auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); | ||
const double dt = ::base::saturated_cast<double>(millis.count()); | ||
|
||
if (dt > animationDuration) | ||
{ | ||
break; | ||
} | ||
|
||
// If going down, increase the height over time. If going up, decrease the height. | ||
const double currentHeight = ::base::saturated_cast<double>( | ||
down ? ((dt / animationDuration) * fullHeight) : | ||
((1.0 - (dt / animationDuration)) * fullHeight)); | ||
|
||
wil::unique_hrgn rgn{ CreateRectRgn(0, | ||
0, | ||
fullWindowSize.width<int>(), | ||
::base::saturated_cast<int>(currentHeight)) }; | ||
|
||
SetWindowRgn(_interopWindowHandle, rgn.get(), true); | ||
|
||
// Go immediately into another frame. This prevents the window from | ||
// doing anything else (tearing our state). A Sleep() here will cause a | ||
// weird stutter, and causes the animation to not be as smooth. | ||
} | ||
|
||
// Reset the window. | ||
SetWindowRgn(_interopWindowHandle, nullptr, true); | ||
} | ||
|
||
void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration) | ||
{ | ||
// First, restore the window. SetWindowPlacement has a fun undocumented | ||
// piece of functionality where it will restore the window position | ||
// _without_ the animation, so use that instead of ShowWindow(SW_RESTORE). | ||
WINDOWPLACEMENT wpc; | ||
zadjii-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
wpc.length = sizeof(WINDOWPLACEMENT); | ||
GetWindowPlacement(_window.get(), &wpc); | ||
wpc.showCmd = SW_RESTORE; | ||
SetWindowPlacement(_window.get(), &wpc); | ||
|
||
// Now that we're visible, animate the dropdown. | ||
_doSlideAnimation(dropdownDuration, true); | ||
} | ||
|
||
void IslandWindow::_slideUpWindow(const uint32_t dropdownDuration) | ||
{ | ||
// First, animate the window sliding up. | ||
_doSlideAnimation(dropdownDuration, false); | ||
|
||
// Then, use SetWindowPlacement to minimize without the animation. | ||
WINDOWPLACEMENT wpc; | ||
zadjii-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
wpc.length = sizeof(WINDOWPLACEMENT); | ||
GetWindowPlacement(_window.get(), &wpc); | ||
wpc.showCmd = SW_MINIMIZE; | ||
SetWindowPlacement(_window.get(), &wpc); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two seem very similar. If you add a second param |
||
|
||
// 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 | ||
// window is on another desktop, the OS will switch to that desktop. | ||
// - If the window is minimized, and dropdownDuration is greater than 0, we'll | ||
// perform a "slide in" animation. We won't do this if the window is already | ||
// on the screen (since that seems silly). | ||
// Arguments: | ||
// - <none> | ||
// - dropdownDuration: The duration to play the dropdown animation, in | ||
// milliseconds. If 0, we won't perform a dropdown animation. | ||
// Return Value: | ||
// - <none> | ||
void IslandWindow::_globalActivateWindow() | ||
void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration) | ||
{ | ||
// From: https://stackoverflow.com/a/59659421 | ||
// > The trick is to make windows ‘think’ that our process and the target | ||
|
@@ -1045,34 +1154,54 @@ void IslandWindow::_globalActivateWindow() | |
// restore-down the window. | ||
if (IsIconic(_window.get())) | ||
{ | ||
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_RESTORE)); | ||
if (dropdownDuration > 0) | ||
{ | ||
_dropdownWindow(dropdownDuration); | ||
} | ||
else | ||
{ | ||
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_RESTORE)); | ||
} | ||
} | ||
else | ||
{ | ||
const DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), nullptr); | ||
const DWORD currentThreadId = GetCurrentThreadId(); | ||
|
||
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true)); | ||
// Just in case, add the thread detach as a scope_exit, to make _sure_ we do it. | ||
auto detachThread = wil::scope_exit([windowThreadProcessId, currentThreadId]() { | ||
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false)); | ||
}); | ||
LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); | ||
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW)); | ||
|
||
// Activate the window too. This will force us to the virtual desktop this | ||
// window is on, if it's on another virtual desktop. | ||
LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); | ||
} | ||
const DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), nullptr); | ||
const DWORD currentThreadId = GetCurrentThreadId(); | ||
|
||
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true)); | ||
// Just in case, add the thread detach as a scope_exit, to make _sure_ we do it. | ||
auto detachThread = wil::scope_exit([windowThreadProcessId, currentThreadId]() { | ||
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false)); | ||
}); | ||
LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); | ||
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW)); | ||
|
||
// Activate the window too. This will force us to the virtual desktop this | ||
// window is on, if it's on another virtual desktop. | ||
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 | ||
// - If dropdownDuration is greater than 0, we'll perform a "slide in" | ||
// animation, before minimizing the window. | ||
// Arguments: | ||
// - <none> | ||
// - dropdownDuration: The duration to play the slide-up animation, in | ||
// milliseconds. If 0, we won't perform a slide-up animation. | ||
// Return Value: | ||
// - <none> | ||
void IslandWindow::_globalDismissWindow() | ||
void IslandWindow::_globalDismissWindow(const uint32_t dropdownDuration) | ||
{ | ||
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_MINIMIZE)); | ||
if (dropdownDuration > 0) | ||
{ | ||
_slideUpWindow(dropdownDuration); | ||
} | ||
else | ||
{ | ||
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_MINIMIZE)); | ||
} | ||
} | ||
|
||
bool IslandWindow::IsQuakeWindow() const noexcept | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make the default value something nice to make it look relatively modern? I assume '0' means that it'll just pop up immediately and not animate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea so now we're getting into the subjective realm - IMO the normal min/maximize behavior looks better than the dropdown. The animation seems more gimmicky - good for literally "quake mode", but seems more annoying for the others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm gonna tap in @DHowett and @cinnamon-msft, since:
I'd feel more comfortable making dropdown the default if the whole window could animate. But since it can't then this almost feels more experimental. That's why I'm thinking we should leave the default
0
. It'll still play the normal Windows "minimize" and "restore" animations that a window would usually get with"dropdownDuration": 0
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think since the animation is requested, we can put it in for now. Then we can test it/play with it and if we don't like it we can just set it back to 0 in a follow up before 1.9?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol