Compare commits

...

7 commits

Author SHA1 Message Date
Mike Griese ac44b6b392 Start working on de-duping some of this work 2019-07-10 07:00:12 -05:00
Mike Griese 5fd41919e9 Add more comments and cleanup 2019-07-09 15:49:05 -05:00
Mike Griese aad9f56d83 heyo this works.
I'm not entirely sure why. But if we only update the titlebar drag region when
  that actually changes, it's a _lot_ smoother. I'm not super happy with the
  duplicated work in _UpdateDragRegion and OnSize, but checking this in in case
  I can't figure that out.
2019-07-09 15:18:03 -05:00
Mike Griese 22e102386b I don't know if this affects anything. 2019-07-09 14:04:45 -05:00
Mike Griese bf239b6de0 Still getting a black xaml islands area (the HRGN) when we switch to high DPI 2019-07-09 14:04:24 -05:00
Mike Griese 0bc9969361 The window style was _not_ important 2019-07-09 09:16:32 -05:00
Mike Griese 15a1f48254 This definitely works for getting shadow, pointy corners back
Don't do anything in NCPAINT. If you do, you have to do everything. But the
  whole point of DwmExtendFrameIntoClientArea is to let you paint the NC area in
  your normal paint. So just do that dummy.

  * This doesn't transition across monitors.
  * This has a window style change I think is wrong.
  * I'm not sure the margins change is important.
2019-07-09 09:11:25 -05:00
3 changed files with 160 additions and 56 deletions

View file

@ -89,6 +89,7 @@ public:
// do nothing.
break;
}
break;
}
case CM_UPDATE_TITLE:
{

View file

@ -34,10 +34,17 @@ NonClientIslandWindow::~NonClientIslandWindow()
{
}
void NonClientIslandWindow::OnDragBarSizeChanged(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::SizeChangedEventArgs eventArgs)
// Method Description:
// - Called when the app's size changes. When that happens, the size of the drag
// bar may have changed. If it has, we'll need to update the WindowRgn of the
// interop window.
// Arguments:
// - <unused>
// Return Value:
// - <none>
void NonClientIslandWindow::OnDragBarSizeChanged(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Windows::UI::Xaml::SizeChangedEventArgs /*eventArgs*/)
{
InvalidateRect(NULL, NULL, TRUE);
ForceResize();
_UpdateDragRegion();
}
void NonClientIslandWindow::OnAppInitialized(winrt::TerminalApp::App app)
@ -78,11 +85,94 @@ void NonClientIslandWindow::OnSize(const UINT width, const UINT height)
return;
}
const auto scale = GetCurrentDpiScale();
const auto dpi = ::GetDpiForWindow(_window.get());
// const auto scale = GetCurrentDpiScale();
// const auto dpi = ::GetDpiForWindow(_window.get());
// const auto dragY = ::GetSystemMetricsForDpi(SM_CYDRAG, dpi);
// const auto dragX = ::GetSystemMetricsForDpi(SM_CXDRAG, dpi);
// // 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
// // the left&right sides of the frame in this calculation while maximized,
// // you' have a few pixels of the window border on the sides while maximized,
// // which most apps do not have.
// const auto bordersWidth = _isMaximized ?
// (_maximizedMargins.cxLeftWidth + _maximizedMargins.cxRightWidth) :
// (dragX * 2);
// const auto bordersHeight = _isMaximized ?
// (_maximizedMargins.cyBottomHeight + _maximizedMargins.cyTopHeight) :
// (dragY * 2);
// const auto windowsWidth = width - bordersWidth;
// const auto windowsHeight = height - bordersHeight;
// const auto xPos = _isMaximized ? _maximizedMargins.cxLeftWidth : dragX;
// const auto yPos = _isMaximized ? _maximizedMargins.cyTopHeight : dragY;
_UpdateInternalMetrics();
const auto windowsWidth = RECT_WIDTH(&_clientArea);
const auto windowsHeight = RECT_HEIGHT(&_clientArea);
if (_rootGrid)
{
const auto scale = GetCurrentDpiScale();
winrt::Windows::Foundation::Size size{ (windowsWidth / scale) + 0.5f, (windowsHeight / scale) + 0.5f };
_rootGrid.Height(size.Height);
_rootGrid.Width(size.Width);
_rootGrid.Measure(size);
winrt::Windows::Foundation::Rect finalRect{};
_rootGrid.Arrange(finalRect);
}
winrt::check_bool(SetWindowPos(_interopWindowHandle, HWND_TOP, _clientArea.left, _clientArea.top, windowsWidth, windowsHeight, SWP_SHOWWINDOW));
}
// Method Description:
// - Update the region of our window that is the draggable area. This happens in
// response to a OnDragBarSizeChanged event. We'll calculate the areas of the
// window that we want to display XAML content in, and set the window region
// of our child xaml-island window to that region. That way, the parent window
// will still get NCHITTEST'ed _outside_ the XAML content area, for things
// like dragging and resizing.
// Arguments:
// - <none>
// Return Value:
// - <none>
void NonClientIslandWindow::_UpdateDragRegion()
{
if (_dragBar)
{
const auto dragBarRect = GetDragAreaRect();
const auto nonClientHeight = dragBarRect.bottom - dragBarRect.top;
const auto windowsWidth = RECT_WIDTH(&_clientArea);
const auto windowsHeight = RECT_HEIGHT(&_clientArea);
auto nonClientRegion = wil::unique_hrgn(CreateRectRgn(0, 0, 0, 0));
auto nonClientLeftRegion = wil::unique_hrgn(CreateRectRgn(0, 0, dragBarRect.left, nonClientHeight));
auto nonClientRightRegion = wil::unique_hrgn(CreateRectRgn(dragBarRect.right, 0, windowsWidth, nonClientHeight));
winrt::check_bool(CombineRgn(nonClientRegion.get(), nonClientLeftRegion.get(), nonClientRightRegion.get(), RGN_OR));
_dragBarRegion = wil::unique_hrgn(CreateRectRgn(0, 0, 0, 0));
auto clientRegion = wil::unique_hrgn(CreateRectRgn(0, nonClientHeight, windowsWidth, windowsHeight));
winrt::check_bool(CombineRgn(_dragBarRegion.get(), nonClientRegion.get(), clientRegion.get(), RGN_OR));
winrt::check_bool(SetWindowRgn(_interopWindowHandle, _dragBarRegion.get(), true));
}
}
void NonClientIslandWindow::_UpdateInternalMetrics()
{
// TODO:GH#1897 This is largely duplicated from OnSize, and we should do
// better than that.
_windowRect = GetWindowRect();
const auto width = _windowRect.right - _windowRect.left;
const auto height = _windowRect.bottom - _windowRect.top;
// const auto scale = GetCurrentDpiScale();
const auto dpi = _currentDpi; // ::GetDpiForWindow(_window.get());
const auto dragY = ::GetSystemMetricsForDpi(SM_CYDRAG, dpi);
const auto dragX = ::GetSystemMetricsForDpi(SM_CXDRAG, dpi);
_dragSize = { dragX, dragY };
// 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
@ -95,41 +185,21 @@ void NonClientIslandWindow::OnSize(const UINT width, const UINT height)
const auto bordersHeight = _isMaximized ?
(_maximizedMargins.cyBottomHeight + _maximizedMargins.cyTopHeight) :
(dragY * 2);
_borderSize = { bordersWidth, bordersHeight };
const auto windowsWidth = width - bordersWidth;
const auto windowsHeight = height - bordersHeight;
const auto xPos = _isMaximized ? _maximizedMargins.cxLeftWidth : dragX;
const auto yPos = _isMaximized ? _maximizedMargins.cyTopHeight : dragY;
winrt::check_bool(SetWindowPos(_interopWindowHandle, HWND_BOTTOM, xPos, yPos, windowsWidth, windowsHeight, SWP_SHOWWINDOW));
if (_rootGrid)
{
winrt::Windows::Foundation::Size size{ (windowsWidth / scale) + 0.5f, (windowsHeight / scale) + 0.5f };
_rootGrid.Height(size.Height);
_rootGrid.Width(size.Width);
_rootGrid.Measure(size);
winrt::Windows::Foundation::Rect finalRect{};
_rootGrid.Arrange(finalRect);
}
if (_dragBar)
{
const auto dragBarRect = GetDragAreaRect();
const auto nonClientHeight = dragBarRect.bottom - dragBarRect.top;
auto nonClientRegion = wil::unique_hrgn(CreateRectRgn(0, 0, 0, 0));
auto nonClientLeftRegion = wil::unique_hrgn(CreateRectRgn(0, 0, dragBarRect.left, nonClientHeight));
auto nonClientRightRegion = wil::unique_hrgn(CreateRectRgn(dragBarRect.right, 0, windowsWidth, nonClientHeight));
winrt::check_bool(CombineRgn(nonClientRegion.get(), nonClientLeftRegion.get(), nonClientRightRegion.get(), RGN_OR));
_dragBarRegion = wil::unique_hrgn(CreateRectRgn(0, 0, 0, 0));
auto clientRegion = wil::unique_hrgn(CreateRectRgn(0, nonClientHeight, windowsWidth, windowsHeight));
winrt::check_bool(CombineRgn(_dragBarRegion.get(), nonClientRegion.get(), clientRegion.get(), RGN_OR));
winrt::check_bool(SetWindowRgn(_interopWindowHandle, _dragBarRegion.get(), true));
}
winrt::check_hresult(_UpdateFrameMargins());
// TODO
// Wait this isn't the client area. This is just the area inside the
// borders. (which yea is all our client area but still)
// I think I want to keep this structure, but maybe rename it
_clientArea.left = xPos;
_clientArea.top = yPos;
_clientArea.right = xPos + windowsWidth;
_clientArea.bottom = yPos + windowsHeight;
}
// Method Description:
@ -229,10 +299,13 @@ MARGINS NonClientIslandWindow::GetFrameMargins() const noexcept
// - the HRESULT returned by DwmExtendFrameIntoClientArea.
[[nodiscard]] HRESULT NonClientIslandWindow::_UpdateFrameMargins() const noexcept
{
// Get the size of the borders we want to use. The sides and bottom will
// just be big enough for resizing, but the top will be as big as we need
// for the non-client content.
MARGINS margins = GetFrameMargins();
// Set frame margines with just a single pixel on the bottom. We don't
// really want a window frame at all - we're drawing all of it. We
// especially don't want a top margin - that's where the caption buttons
// are, and we're drawing those. So just set a single pixel on the bottom,
// because the method won't work with {0}.
MARGINS margins = { 0, 0, 0, 1 };
// Extend the frame into the client area.
return DwmExtendFrameIntoClientArea(_window.get(), &margins);
}
@ -386,61 +459,82 @@ RECT NonClientIslandWindow::GetMaxWindowRectInPixels(const RECT* const prcSugges
}
break;
}
case WM_EXITSIZEMOVE:
{
ForceResize();
break;
}
case WM_NCACTIVATE:
case WM_NCPAINT:
case WM_PAINT:
{
if (!_dragBar)
{
return 0;
}
const auto hdc = wil::GetDC(_window.get());
PAINTSTRUCT ps{ 0 };
const auto hdc = wil::BeginPaint(_window.get(), &ps);
if (hdc.get())
{
// Todo this shit can get pulled too
const auto scale = GetCurrentDpiScale();
const auto dpi = ::GetDpiForWindow(_window.get());
// Get the dimensions of the drag borders for the sides of the window.
const auto dragY = ::GetSystemMetricsForDpi(SM_CYDRAG, dpi);
const auto dragX = ::GetSystemMetricsForDpi(SM_CXDRAG, dpi);
const auto xPos = _isMaximized ? _maximizedMargins.cxLeftWidth : dragX;
const auto yPos = _isMaximized ? _maximizedMargins.cyTopHeight : dragY;
// Create brush for borders, titlebar color.
const auto backgroundBrush = _dragBar.Background();
const auto backgroundSolidBrush = backgroundBrush.as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>();
const auto backgroundColor = backgroundSolidBrush.Color();
const auto color = RGB(backgroundColor.R, backgroundColor.G, backgroundColor.B);
_backgroundBrush = wil::unique_hbrush(CreateSolidBrush(color));
// Fill in the area between the non-client content and the caption buttons.
RECT dragBarRect = GetDragAreaRect();
// dragBarRect.left += xPos;
// dragBarRect.right += xPos;
// dragBarRect.bottom += yPos;
// dragBarRect.top += yPos;
// This would paint the _entire_ window area, but I think we want to
// be more precise about it (in case we want different titlebar and
// border colors)
dragBarRect.left = 0;
dragBarRect.top = 0;
dragBarRect.right = RECT_WIDTH(&_windowRect);
dragBarRect.bottom = RECT_HEIGHT(&_windowRect);
::FillRect(hdc.get(), &dragBarRect, _backgroundBrush.get());
RECT windowRect = {};
::GetWindowRect(_window.get(), &windowRect);
const auto cx = windowRect.right - windowRect.left;
const auto cy = windowRect.bottom - windowRect.top;
RECT clientRect = { 0, 0, cx, yPos };
::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
// // Draw the top window border
// RECT clientRect = { 0, 0, cx, yPos };
// ::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
clientRect = { 0, 0, xPos, cy };
::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
// // Draw the left window border
// clientRect = { 0, 0, xPos, cy };
// ::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
clientRect = { 0, cy - yPos, cx, cy };
::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
// // Draw the bottom window border
// clientRect = { 0, cy - yPos, cx, cy };
// ::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
clientRect = { cx - xPos, 0, cx, cy };
::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
RECT dragBarRect = GetDragAreaRect();
dragBarRect.left += xPos;
dragBarRect.right += xPos;
dragBarRect.bottom += yPos;
dragBarRect.top += yPos;
::FillRect(hdc.get(), &dragBarRect, _backgroundBrush.get());
// // Draw the right window border
// clientRect = { cx - xPos, 0, cx, cy };
// ::FillRect(hdc.get(), &clientRect, _backgroundBrush.get());
}
return 0;
}
case WM_LBUTTONDOWN:
{
POINT point1 = {};

View file

@ -46,6 +46,13 @@ private:
bool _isMaximized;
winrt::Windows::UI::Xaml::Controls::Border _dragBar{ nullptr };
// TODO: These should be Viewport's
RECT _windowRect{ 0 };
SIZE _dragSize{ 0 };
SIZE _borderSize{ 0 };
RECT _clientArea{ 0 };
// RECT _titlebarArea{ 0 };
RECT GetDragAreaRect() const noexcept;
[[nodiscard]] LRESULT HitTestNCA(POINT ptMouse) const noexcept;
@ -54,6 +61,8 @@ private:
void _HandleActivateWindow();
bool _HandleWindowPosChanging(WINDOWPOS* const windowPos);
void _UpdateDragRegion();
void _UpdateInternalMetrics();
void OnDragBarSizeChanged(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::SizeChangedEventArgs eventArgs);