terminal/src/cascadia/TerminalApp/MinMaxCloseControl.cpp
2021-11-16 13:18:22 -06:00

241 lines
10 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "MinMaxCloseControl.h"
#include "MinMaxCloseControl.g.cpp"
#include <LibraryResources.h>
using namespace winrt::Windows::UI::Xaml;
namespace winrt::TerminalApp::implementation
{
static void closeToolTipForButton(const Controls::Button& button)
{
if (auto tt{ Controls::ToolTipService::GetToolTip(button) })
{
if (auto tooltip{ tt.try_as<Controls::ToolTip>() })
{
tooltip.IsOpen(false);
}
}
}
MinMaxCloseControl::MinMaxCloseControl()
{
// Get our dispatcher. This will get us the same dispatcher as
// Dispatcher(), but it's a DispatcherQueue, so we can use it with
// ThrottledFunc
auto dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
InitializeComponent();
// Get the tooltip hover time from the system, or default to 400ms
// (which should be the default, see:
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-trackmouseevent#remarks)
unsigned int hoverTimeoutMillis{ 400 };
LOG_IF_WIN32_BOOL_FALSE(SystemParametersInfoW(SPI_GETMOUSEHOVERTIME, 0, &hoverTimeoutMillis, 0));
const auto toolTipInterval = std::chrono::milliseconds(hoverTimeoutMillis);
// Create a ThrottledFunc for opening the tooltip after the hover
// timeout. If we hover another button, we should make sure to call
// Run() with the new button. Calling `_displayToolTip.Run(nullptr)`,
// which will cause us to not display a tooltip, which is used when we
// leave the control entirely.
_displayToolTip = std::make_shared<ThrottledFuncTrailing<Controls::Button>>(
dispatcher,
toolTipInterval,
[weakThis = get_weak()](Controls::Button button) {
// If we provide a button, then open the tooltip on that button.
// We can "dismiss" this throttled func by calling it with null,
// which will cause us to do nothing at the end of the timeout
// instead.
if (button)
{
if (auto tt{ Controls::ToolTipService::GetToolTip(button) })
{
if (auto tooltip{ tt.try_as<Controls::ToolTip>() })
{
tooltip.IsOpen(true);
}
}
}
});
}
// These event handlers simply forward each buttons click events up to the
// events we've exposed.
void MinMaxCloseControl::_MinimizeClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
RoutedEventArgs const& e)
{
_MinimizeClickHandlers(*this, e);
}
void MinMaxCloseControl::_MaximizeClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
RoutedEventArgs const& e)
{
_MaximizeClickHandlers(*this, e);
}
void MinMaxCloseControl::_CloseClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
RoutedEventArgs const& e)
{
_CloseClickHandlers(*this, e);
}
void MinMaxCloseControl::SetWindowVisualState(WindowVisualState visualState)
{
// Look up the heights we should use for the caption buttons from our
// XAML resources. "CaptionButtonHeightWindowed" and
// "CaptionButtonHeightMaximized" define the size we should use for the
// caption buttons height for the windowed and maximized states,
// respectively.
//
// use C++11 magic statics to make sure we only do this once.
static auto heights = [this]() {
const auto res = Resources();
const auto windowedHeightKey = winrt::box_value(L"CaptionButtonHeightWindowed");
const auto maximizedHeightKey = winrt::box_value(L"CaptionButtonHeightMaximized");
auto windowedHeight = 0.0;
auto maximizedHeight = 0.0;
if (res.HasKey(windowedHeightKey))
{
const auto valFromResources = res.Lookup(windowedHeightKey);
windowedHeight = winrt::unbox_value_or<double>(valFromResources, 0.0);
}
if (res.HasKey(maximizedHeightKey))
{
const auto valFromResources = res.Lookup(maximizedHeightKey);
maximizedHeight = winrt::unbox_value_or<double>(valFromResources, 0.0);
}
return std::tuple<double, double>{ windowedHeight, maximizedHeight };
}();
static const auto windowedHeight = std::get<0>(heights);
static const auto maximizedHeight = std::get<1>(heights);
switch (visualState)
{
case WindowVisualState::WindowVisualStateMaximized:
VisualStateManager::GoToState(MaximizeButton(), L"WindowStateMaximized", false);
MinimizeButton().Height(maximizedHeight);
MaximizeButton().Height(maximizedHeight);
CloseButton().Height(maximizedHeight);
MaximizeToolTip().Text(RS_(L"WindowRestoreDownButtonToolTip"));
break;
case WindowVisualState::WindowVisualStateNormal:
case WindowVisualState::WindowVisualStateIconified:
default:
VisualStateManager::GoToState(MaximizeButton(), L"WindowStateNormal", false);
MinimizeButton().Height(windowedHeight);
MaximizeButton().Height(windowedHeight);
CloseButton().Height(windowedHeight);
MaximizeToolTip().Text(RS_(L"WindowMaximizeButtonToolTip"));
break;
}
}
// Method Description:
// - Called when the mouse hovers a button.
// - Transition that button to `PointerOver`
// - run the throttled func with this button, to display the tooltip after
// a timeout
// - dismiss any open tooltips on other buttons.
// Arguments:
// - button: the button that was hovered
void MinMaxCloseControl::HoverButton(CaptionButton button)
{
// Keep track of the button that's been pressed. we get a mouse move
// message when we open the tooltip. If we move the mouse on top of this
// button, that we've already pressed, then no need to move to the
// "hovered" state, we should stay in the pressed state.
if (_lastPressedButton && _lastPressedButton.value() == button)
{
return;
}
switch (button)
{
// Make sure to use true for the useTransitions parameter, to
// animate the fade in/out transition between colors.
case CaptionButton::Minimize:
VisualStateManager::GoToState(MinimizeButton(), L"PointerOver", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
_displayToolTip->Run(MinimizeButton());
closeToolTipForButton(MaximizeButton());
closeToolTipForButton(CloseButton());
break;
case CaptionButton::Maximize:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"PointerOver", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
closeToolTipForButton(MinimizeButton());
_displayToolTip->Run(MaximizeButton());
closeToolTipForButton(CloseButton());
break;
case CaptionButton::Close:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"PointerOver", true);
closeToolTipForButton(MinimizeButton());
closeToolTipForButton(MaximizeButton());
_displayToolTip->Run(CloseButton());
break;
}
}
// Method Description:
// - Called when the mouse presses down on a button. NOT when it is
// released. That's handled one level above, in
// TitleBarControl::ReleaseButtons
// - Transition that button to `Pressed`
// Arguments:
// - button: the button that was pressed
void MinMaxCloseControl::PressButton(CaptionButton button)
{
switch (button)
{
case CaptionButton::Minimize:
VisualStateManager::GoToState(MinimizeButton(), L"Pressed", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
break;
case CaptionButton::Maximize:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Pressed", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
break;
case CaptionButton::Close:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Pressed", true);
break;
}
_lastPressedButton = button;
}
// Method Description:
// - Called when buttons are no longer hovered or pressed. Return them all
// to the normal state, and dismiss the tooltips.
void MinMaxCloseControl::ReleaseButtons()
{
_displayToolTip->Run(nullptr);
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
closeToolTipForButton(MinimizeButton());
closeToolTipForButton(MaximizeButton());
closeToolTipForButton(CloseButton());
_lastPressedButton = std::nullopt;
}
}