Simplify DPI logic (#829)

* Simply DPI logic

* Apply PR comments

* Update src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp

Co-Authored-By: Dustin L. Howett (MSFT) <duhowett@microsoft.com>

* Add comments

* Update src/cascadia/WindowsTerminal/BaseWindow.h

Co-Authored-By: Dustin L. Howett (MSFT) <duhowett@microsoft.com>

* Apply PR feedback
This commit is contained in:
Oscar Calvo 2019-05-20 12:49:28 -07:00 committed by msftbot[bot]
parent 9f4ad6d1ce
commit 37ea2dce48
4 changed files with 68 additions and 114 deletions

View File

@ -89,7 +89,6 @@ public:
}
case CM_UPDATE_TITLE:
{
SetWindowTextW(_window, _title.c_str());
break;
}
@ -115,30 +114,67 @@ public:
SWP_NOZORDER | SWP_NOACTIVATE);
_currentDpi = uDpi;
NewScale(uDpi);
}
_inDpiChange = false;
return 0;
}
virtual void NewScale(UINT dpi) = 0;
virtual void OnResize(const UINT width, const UINT height) = 0;
virtual void OnMinimize() = 0;
virtual void OnRestore() = 0;
RECT GetWindowRect() const
RECT GetWindowRect() const noexcept
{
RECT rc = { 0 };
::GetWindowRect(_window, &rc);
return rc;
}
HWND GetHandle() noexcept
HWND GetHandle() const noexcept
{
return _window;
};
float GetCurrentDpiScale() const noexcept
{
const auto dpi = ::GetDpiForWindow(_window);
const auto scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
return scale;
}
//// Gets the physical size of the client area of the HWND in _window
SIZE GetPhysicalSize() const noexcept
{
RECT rect = {};
GetClientRect(_window, &rect);
const auto windowsWidth = rect.right - rect.left;
const auto windowsHeight = rect.bottom - rect.top;
return SIZE{ windowsWidth, windowsHeight };
}
//// Gets the logical (in DIPs) size of a physical size specified by the parameter physicalSize
//// Remarks:
//// XAML coordinate system is always in Display Indepenent Pixels (a.k.a DIPs or Logical). However Win32 GDI (because of legacy reasons)
//// in DPI mode "Per-Monitor and Per-Monitor (V2) DPI Awareness" is always in physical pixels.
//// The formula to transform is:
//// logical = (physical / dpi) + 0.5 // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75
//// See also:
//// https://docs.microsoft.com/en-us/windows/desktop/LearnWin32/dpi-and-device-independent-pixels
//// https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows#per-monitor-and-per-monitor-v2-dpi-awareness
winrt::Windows::Foundation::Size GetLogicalSize(const SIZE physicalSize) const noexcept
{
const auto dpi = GetCurrentDpiScale();
// 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75
const auto logicalWidth = (physicalSize.cx / dpi) + 0.5f;
const auto logicalHeigth = (physicalSize.cy / dpi) + 0.5f;
return winrt::Windows::Foundation::Size(logicalWidth, logicalHeigth);
}
winrt::Windows::Foundation::Size GetLogicalSize() const noexcept
{
return GetLogicalSize(GetPhysicalSize());
}
// Method Description:
// - Sends a message to our message loop to update the title of the window.
// Arguments:

View File

@ -17,10 +17,7 @@ using namespace ::Microsoft::Console::Types;
#define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS"
IslandWindow::IslandWindow() noexcept :
_currentWidth{ 0 },
_currentHeight{ 0 },
_interopWindowHandle{ nullptr },
_scale{ nullptr },
_rootGrid{ nullptr },
_source{ nullptr },
_pfnCreateCallback{ nullptr }
@ -126,40 +123,25 @@ void IslandWindow::Initialize()
// stash the child interop handle so we can resize it when the main hwnd is resized
interop->get_WindowHandle(&_interopWindowHandle);
if (!initialized)
{
_InitXamlContent();
}
_rootGrid = winrt::Windows::UI::Xaml::Controls::Grid();
_source.Content(_rootGrid);
// Do a quick resize to force the island to paint
OnSize();
}
void IslandWindow::_InitXamlContent()
{
// setup a root grid that will be used to apply DPI scaling
winrt::Windows::UI::Xaml::Media::ScaleTransform dpiScaleTransform;
winrt::Windows::UI::Xaml::Controls::Grid dpiAdjustmentGrid;
const auto dpi = GetDpiForWindow(_window);
const double scale = double(dpi) / double(USER_DEFAULT_SCREEN_DPI);
_rootGrid = dpiAdjustmentGrid;
_scale = dpiScaleTransform;
_scale.ScaleX(scale);
_scale.ScaleY(scale);
}
void IslandWindow::OnSize()
{
const auto physicalSize = GetPhysicalSize();
// update the interop window size
SetWindowPos(_interopWindowHandle, 0, 0, 0, _currentWidth, _currentHeight, SWP_SHOWWINDOW);
_rootGrid.Width(_currentWidth);
_rootGrid.Height(_currentHeight);
SetWindowPos(_interopWindowHandle, 0, 0, 0, physicalSize.cx, physicalSize.cy, SWP_SHOWWINDOW);
if (_rootGrid)
{
const auto size = GetLogicalSize();
_rootGrid.Width(size.Width);
_rootGrid.Height(size.Height);
}
}
LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
@ -186,63 +168,6 @@ LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LP
return base_type::MessageHandler(message, wparam, lparam);
}
// Method Description:
// - Called when the DPI of this window changes. Updates the XAML content sizing to match the client area of our window.
// Arguments:
// - dpi: new DPI to use. The default is 96, as defined by USER_DEFAULT_SCREEN_DPI.
// Return Value:
// - <none>
void IslandWindow::NewScale(UINT dpi)
{
const double scaleFactor = static_cast<double>(dpi) / static_cast<double>(USER_DEFAULT_SCREEN_DPI);
if (_scale != nullptr)
{
_scale.ScaleX(scaleFactor);
_scale.ScaleY(scaleFactor);
}
ApplyCorrection(scaleFactor);
}
// Method Description:
// - This method updates the padding that exists off the edge of the window to
// make sure to keep the XAML content size the same as the actual window size.
// Arguments:
// - scaleFactor: the DPI scaling multiplier to use. for a dpi of 96, this would
// be 1, for 144, this would be 1.5.
// Return Value:
// - <none>
void IslandWindow::ApplyCorrection(double scaleFactor)
{
// Get the dimensions of the XAML content grid.
const auto realWidth = _rootGrid.Width();
const auto realHeight = _rootGrid.Height();
// Scale those dimensions by our dpi scaling. This is how big the XAML
// content thinks it should be.
const auto dpiAwareWidth = realWidth * scaleFactor;
const auto dpiAwareHeight = realHeight * scaleFactor;
// Get the difference between what xaml thinks and the actual client area
// of our window.
const auto deltaX = dpiAwareWidth - realWidth;
const auto deltaY = dpiAwareHeight - realHeight;
// correct for the scaling we applied above
const auto dividedDeltaX = deltaX / scaleFactor;
const auto dividedDeltaY = deltaY / scaleFactor;
const double rightCorrection = dividedDeltaX;
const double bottomCorrection = dividedDeltaY;
// Apply padding to the root grid, so that it's content is the same size as
// our actual window size.
// Without this, XAML content will seem to spill off the side/bottom of the window
_rootGrid.Padding(Xaml::ThicknessHelper::FromLengths(0, 0, rightCorrection, bottomCorrection));
}
// Method Description:
// - Called when the window has been resized (or maximized)
// Arguments:
@ -250,13 +175,7 @@ void IslandWindow::ApplyCorrection(double scaleFactor)
// - height: the new height of the window _in pixels_
void IslandWindow::OnResize(const UINT width, const UINT height)
{
_currentWidth = width;
_currentHeight = height;
if (nullptr != _rootGrid)
{
OnSize();
ApplyCorrection(_scale.ScaleX());
}
OnSize();
}
// Method Description:
@ -276,6 +195,5 @@ void IslandWindow::OnRestore()
void IslandWindow::SetRootContent(winrt::Windows::UI::Xaml::UIElement content)
{
_rootGrid.Children().Clear();
ApplyCorrection(_scale.ScaleX());
_rootGrid.Children().Append(content);
}

View File

@ -17,8 +17,6 @@ public:
virtual void OnSize();
virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override;
void ApplyCorrection(double scaleFactor);
void NewScale(UINT dpi) override;
void OnResize(const UINT width, const UINT height) override;
void OnMinimize() override;
void OnRestore() override;
@ -29,18 +27,13 @@ public:
void SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept;
protected:
unsigned int _currentWidth;
unsigned int _currentHeight;
HWND _interopWindowHandle;
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source;
winrt::Windows::UI::Xaml::Media::ScaleTransform _scale;
winrt::Windows::UI::Xaml::Controls::Grid _rootGrid;
std::function<void(const HWND, const RECT)> _pfnCreateCallback;
void _InitXamlContent();
void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept;
};

View File

@ -68,7 +68,6 @@ void NonClientIslandWindow::Initialize()
void NonClientIslandWindow::SetNonClientContent(winrt::Windows::UI::Xaml::UIElement content)
{
_nonClientRootGrid.Children().Clear();
ApplyCorrection(_scale.ScaleX());
_nonClientRootGrid.Children().Append(content);
}
@ -91,13 +90,15 @@ void NonClientIslandWindow::SetNonClientHeight(const int contentHeight) noexcept
// content, in window coordinates.
Viewport NonClientIslandWindow::GetTitlebarContentArea() const noexcept
{
const auto dpi = GetDpiForWindow(_window);
const double scale = static_cast<double>(dpi) / static_cast<double>(USER_DEFAULT_SCREEN_DPI);
const auto scale = GetCurrentDpiScale();
const auto titlebarContentHeight = _titlebarUnscaledContentHeight * scale;
const auto titlebarMarginRight = _titlebarMarginRight;
auto titlebarWidth = _currentWidth - (_windowMarginSides + titlebarMarginRight);
const auto physicalSize = GetPhysicalSize();
const auto clientWidth = physicalSize.cx;
auto titlebarWidth = clientWidth - (_windowMarginSides + titlebarMarginRight);
// Adjust for maximized margins
titlebarWidth -= (_maximizedMargins.cxLeftWidth + _maximizedMargins.cxRightWidth);
@ -133,8 +134,9 @@ Viewport NonClientIslandWindow::GetClientContentArea() const noexcept
COORD clientOrigin = { static_cast<short>(margins.cxLeftWidth),
static_cast<short>(margins.cyTopHeight) };
auto clientWidth = _currentWidth;
auto clientHeight = _currentHeight;
const auto physicalSize = GetPhysicalSize();
auto clientWidth = physicalSize.cx;
auto clientHeight = physicalSize.cy;
// If we're maximized, we don't want to use the frame as our margins,
// instead we want to use the margins from the maximization. If we included
@ -177,8 +179,13 @@ void NonClientIslandWindow::OnSize()
clientArea.Height(),
SWP_SHOWWINDOW);
_rootGrid.Width(clientArea.Width());
_rootGrid.Height(clientArea.Height());
if (_rootGrid)
{
const SIZE physicalSize{ clientArea.Width(), clientArea.Height() };
const auto logicalSize = GetLogicalSize(physicalSize);
_rootGrid.Width(logicalSize.Width);
_rootGrid.Height(logicalSize.Height);
}
// update the interop window size
SetWindowPos(_nonClientInteropWindowHandle, 0,