terminal/src/cascadia/WindowsTerminal/IslandWindow.h
Mike Griese 6e11780ca6
Add property to control dropdown speed of global summon (#9977)
## Summary of the Pull Request

Adds the `dropdownDuration` property to `globalSummon`. This controls how fast the window appears on the screen when summoned from minimized. It similarly controls the speed for sliding out of view when the window is dismissed with `"toggleVisibility": true`.

`dropdownDuration` specifies the duration in **milliseconds**. This defaults to `0` for `globalSummon`, and defaults to `200` for `quakeMode`. 200 was picked because, according to [`AnimateWindow`](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-animatewindow): 

>  Typically, an animation takes 200 milliseconds to play.

Do note that you won't be able to interact with the window during the animation! Input sent during the dropdown will arrive at the end of the animation, but input sent during the slide-up _won't_. Avoid setting this to large values!

The gifs are in Teams. 

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

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

## Detailed Description of the Pull Request / Additional comments

I had the following previously in the doc comments, but it feels better in the PR body:

- This was chosen because it was easier to implement and generally nicer than:
  * `AnimateWindow`, which would show the window borders for the duration of
    the animation, and occasionally just plain not work. Additionally, for
    `AnimateWindow` to work, the window much not be visible, so we'd need to
    first restore the window, then hide it, then animate it. That would flash
    the taskbar.
  * `SetWindowRgn` on the root HWND, which caused the xaml content to shift to
    the left, and caused a black bar to be drawn on the right of the window.
    Presumably, `SetWindowRgn` and `DwmExtendFrameIntoClientArea` did not play
    well with each other.
  * `SetWindowPos(..., SWP_NOSENDCHANGING)`, which worked the absolute best for
    longer animations, and is the closest to the actual implementation of
    `AnimateWindow`. This would resize the ROOT window, without sending resizes
    to the XAML island, allowing the content to _not_ reflow. but for a
    duration of 200ms, would only ever display ~2 frames. That's basically
    not even animation anymore, it's now just an "appear". Since that's how
    long the default animation is, if felt silly to have it basically not
    work by default.
- If a future reader would like to implement this better, **they should feel
  free to**, and not mistake my notes here as expertise. These are research
  notes into the dark and terrible land that is Win32 programming. I'm no expert. 

## Validation Steps Performed

This is the blob of json I'm testing with these days:

```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+3", "command": { "action": "globalSummon", "desktop": "onCurrent" } },
        { "keys": "ctrl+4", "command": { "action": "globalSummon", "desktop": "any" } },
```

* <kbd>ctrl+\`</kbd> will summon the quake window with a _quick_ animation
* <kbd>ctrl+2</kbd> will summon the window with a  s l o w  animation
2021-05-17 07:28:46 -05:00

114 lines
4.6 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "BaseWindow.h"
#include <winrt/TerminalApp.h>
#include "../../cascadia/inc/cppwinrt_utils.h"
class IslandWindow :
public BaseWindow<IslandWindow>
{
public:
IslandWindow() noexcept;
virtual ~IslandWindow() override;
virtual void MakeWindow() noexcept;
void Close();
virtual void OnSize(const UINT width, const UINT height);
[[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override;
void OnResize(const UINT width, const UINT height) override;
void OnMinimize() override;
void OnRestore() override;
virtual void OnAppInitialized();
virtual void SetContent(winrt::Windows::UI::Xaml::UIElement content);
virtual void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
virtual SIZE GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept;
virtual void Initialize();
void SetCreateCallback(std::function<void(const HWND, const RECT, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode)> pfn) noexcept;
void SetSnapDimensionCallback(std::function<float(bool widthOrHeight, float dimension)> pfn) noexcept;
void FocusModeChanged(const bool focusMode);
void FullscreenChanged(const bool fullscreen);
void SetAlwaysOnTop(const bool alwaysOnTop);
void FlashTaskbar();
void SetTaskbarProgress(const size_t state, const size_t progress);
void UnsetHotkeys(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);
bool IsQuakeWindow() const noexcept;
void IsQuakeWindow(bool isQuakeWindow) noexcept;
DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
WINRT_CALLBACK(WindowActivated, winrt::delegate<void()>);
WINRT_CALLBACK(HotkeyPressed, winrt::delegate<void(long)>);
protected:
void ForceResize()
{
// Do a quick resize to force the island to paint
const auto size = GetPhysicalSize();
OnSize(size.cx, size.cy);
}
HWND _interopWindowHandle;
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source;
winrt::Windows::UI::Xaml::Controls::Grid _rootGrid;
std::function<void(const HWND, const RECT, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode)> _pfnCreateCallback;
std::function<float(bool, float)> _pfnSnapDimensionCallback;
void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept;
[[nodiscard]] LRESULT _OnSizing(const WPARAM wParam, const LPARAM lParam);
[[nodiscard]] LRESULT _OnMoving(const WPARAM wParam, const LPARAM lParam);
bool _borderless{ false };
bool _alwaysOnTop{ false };
bool _fullscreen{ false };
bool _fWasMaximizedBeforeFullscreen{ false };
RECT _rcWindowBeforeFullscreen;
RECT _rcWorkBeforeFullscreen;
UINT _dpiBeforeFullscreen;
virtual void _SetIsBorderless(const bool borderlessEnabled);
virtual void _SetIsFullscreen(const bool fullscreenEnabled);
void _RestoreFullscreenPosition(const RECT rcWork);
void _SetFullscreenPosition(const RECT rcMonitor, const RECT rcWork);
LONG _getDesiredWindowStyle() const;
wil::com_ptr<ITaskbarList3> _taskbar;
void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam);
long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize);
void _globalActivateWindow(const uint32_t dropdownDuration);
void _dropdownWindow(const uint32_t dropdownDuration);
void _slideUpWindow(const uint32_t dropdownDuration);
void _doSlideAnimation(const uint32_t dropdownDuration, const bool down);
void _globalDismissWindow(const uint32_t dropdownDuration);
bool _isQuakeWindow{ false };
void _enterQuakeMode();
private:
// This minimum width allows for width the tabs fit
static constexpr long minimumWidth = 460L;
// We run with no height requirement for client area,
// though the total height will take into account the non-client area
// and the requirements of components hosted in the client area
static constexpr long minimumHeight = 0L;
};