Draft: qt: use expanded client area for csd on windows
~~We already have client shadow, resize handle, button logic in the interface for other platforms. We can use them on Windows like we do on other platforms.
Going this way allows us to eliminate a lot of code, which is good for maintenance. At the same time, with frameless window hint, Windows 11 22H2 can use native acrylic backdrop filter (without artifacts unlike when this flag is not set).
We also need to have the resize handle outside the interface content, because there are interactive controls that the resize handle prevents the user to interact with them. For example, it is hard or impossible to scroll the play queue, or control the seek bar when it reaches the window frame (when frameless window hint is not set).
The biggest disadvantage of this seems to be not having the "aero snapping" feature. In my opinion, fixing the resize handle issue is more important than having extra features that are not essential.
Another disadvantage is having no DWM transitions. I'm not sure how important that is.
System menu is also recently factorized, so the system menu is still functional with frameless window hint.~~
Close #28906 (closed).
Request review @chub.
Draft because I'm not sure if it is completely reliable. It needs some more testing. I also need to check that the Windows 11 22H2 native backdrop blur does not reach to the margin area.
Merge request reports
Activity
mentioned in issue #28906 (closed)
changed milestone to %4.0
added Component::Interface: Qt label
- Resolved by Fatih Uzunoğlu
I also need to check that the Windows 11 22H2 native backdrop blur does not reach to the margin area.
Windows 11 DWM applies the native backdrop effect to whole window, including the extend margins... Similar to recent behavior of KWin X11 (!6184 (merged)), but at least KWin provides way to specify an effect region unlike DWM (https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type). This is unfortunate:
I have tried adjusting the window mask (https://doc.qt.io/qt-6/qwindow.html#setMask) as we are doing on Wayland to tell the compositor not deliver mouse/touch input in the shadow region, but it did not work. I'm not sure what can be done about that on Windows.
As a side note, if I remember correctly I also tried that on X11, but it seems that setting a mask also clips the content (which is the usual behavior for X11, unlike Wayland).
added 4 commits
- 6c67c7b6 - qml: add and use `MainCtx::platformHandlesResizeWithCSD()`
- 91beeae9 - contrib: qtbase: add patch `Windows QPA: improve Qt::FramlessWindowHint windows`
- cd30c069 - qt: use frameless window hint for csd in `CompositorDirectComposition`
- 3d62e035 - qt: use frameless window hint for csd in `CompositorWin7`
Toggle commit listWith
Windows QPA: improve Qt::FramlessWindowHint windows
(https://github.com/qt/qtbase/commit/1a6ab689d5858e5997c989ce6c3574fb591438b0), frameless window hint has fewer limitations.This patch recently got reverted on Qt side because "Regression: FrameLessWindows have borders now when they should not.". Then reintroduced as a new flag as
Qt::ExpandedClientAreaHint
. For us, this is not too relevant at the moment, so we can probably use it.I could import the patch for
Qt::ExpandedClientAreaHint
instead, but I guess this would be a better idea because we are still using Qt 6.8, and that feature is supposed to come with Qt 6.9. When we use more recent Qt, we can switch usingQt::ExpandedClientAreaHint
fromQt::FramelessWindowHint
. I don't have strong opinion on this.Patch description:
From a24517a47071debf5e53815835fa021c0d411eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Keller?= <timothee.keller@qt.io> Date: Thu, 15 Jun 2023 23:15:52 +0200 Subject: [PATCH] Windows QPA: improve Qt::FramlessWindowHint windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, Qt::FramelessWindowHint windows lose functionalities that we might want them to keep. This patch attempts to address these by giving the frameless windows the following features: - Border-resizable: it is now possible to resize them by hovering over the edges of the (invisible) border. - Aero snap: While this patch does not enable dragging by clicking on non client areas, shortcuts like Win key + arrow keys let the user snap the windows as expected. - Animations/Shadows: minimizing/maximizing windows now has the typical animations, and the windows also have shadows. Change-Id: Icd7b671a6ac3dc300ba78378897b5dcae2432f76 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Regarding the resize handle, this patch does not fix resize handle being within the client area. Windows does not seem to send
WM_NCHITTEST
to all windows unconditionally, it seems to first do a hit test itself. If the window is beneath the cursor, then the window receives it from what I understood. Another way is to capture the mouse (SetCapture()
), but I'm not sure if we can do that.I will investigate more if we can have the resize handle outside the client area. But for now, that seems to be the only limitation of
FramelessWindowHint
thanks to that patch. I have already started using the patch here.I also need to check if this new
FramelessWindowHint
is problematic for the new Windows 11 native acrylic backdrop effect.I also need to check if this new
FramelessWindowHint
is problematic for the new Windows 11 native acrylic backdrop effect.I can notice initial white background artifact with Nvidia (like before !6297 (merged)), so I will probably need to check for
WS_POPUP
and enable the Win11 22H2 native backdrop effect if it is really "frameless". Otherwise, we can trigger an initial resize on the interface to clear the artifact and use native effect, which would make it possible to use it also when CSD is disabled.We need to know if without
WS_POPUP
the native effect works (besides the initial artifact that can be cleared) with different GPUs.Edited by Fatih UzunoğluI will investigate more if we can have the resize handle outside the client area
I have done more investigation on this during the weekend. There are some discussions on how to achieve that, this one provided some insight to me: https://github.com/melak47/BorderlessWindow/issues/11.
Restricting the client area in
WM_NCCALCSIZE
seems to be working, except it brings a prominent 1px (on Windows 10) and even bigger (on Windows 7) border. We can also not paint opaque color over that, because we have transparent parts, and also it is just a workaround and not a real fix.DWM seems to allow disabling painting in non-client region, but that also means no server side shadows, and possibly broken rounded corners.
There seems to be a undocumented way to disable the border (https://github.com/melak47/BorderlessWindow/issues/13#issuecomment-309154142), which we can use on Windows 10. But it reportedly is not available on Windows 7. So, I guess for the moment we can fix #28906 (closed) only for Windows 10+. I guess the thick border on Windows 7 would be disturbing.
An alternative could be using extended frame, and using our own shadow. But for that I need to use
ExpandedClientAreaHint
instead as setting custom margin does not work whenFramelessWindowHint
is set (I think Qt's patch should have adjusted that, because frameless window is not actually frameless with that patch, refer to why it was reverted), and I assume the window would not be rounded that way.In my opinion, we can go with the former approach: restrict the client area, and use undocumented
ACCENT_ENABLE_GRADIENT
to get rid of the border. The issue #28906 (closed) then would be limited to Windows 7, and 8 (?).By the way, starting with Windows 11 Build 22000, Windows seems to provide an official way to disable the border (https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute) via adjusting
DWMWA_BORDER_COLOR
.Lastly, I think we should really not do what Office or Visual Studio is reportedly doing:
Visual Studio 2012+ and Office 2013+ for example sidestep this by having multiple windows, a transparent window to which the shadow (or glow) is rendered (which also serves to capture the mouse and enable resizing outside the frame), and one or more "main" windows, with completely custom drawn title bars and window frames
I wonder what is Firefox or Thunderbird is doing, as it seems working fine there.
Some things I noticed while testing your branch
- I have some garbage content at the edge (1px or something)
- application buttons are doesn't scale with the application (they are really tiny here)
- application buttons are drawn over the application border, that's especially visible with windows 11 round corner
- application buttons are always drawn, so they remains visible in the playerview when the controlbare are hidden
- when resizing garbage is visible in the un-refreshed area, current master seems to show the the acrylic layer (surface has a uniform look, that's less disturbing)
- tiling menu is no longer available when hovering the maximize button
- no resize animation
Lastly, I think we should really not do what Office or Visual Studio is reportedly doing:
So more or less the Win7 compositor solution?
I have some garbage content at the edge (1px or something)
That occurs when the client size does not exceed to the window edges (
Windows: ExpandedClientAreaHint fixups
), but we need that for outside client area resizing that is handled by Windows. In a way, this can be considered extended frame, but Windows DWM adds a border.With the improved
FramelessWindowHint
(previous approachWindows QPA: improve Qt::FramlessWindowHint windows
), it was a proper border. I guess that was becauseWVR_REDRAW
was used. Qt's new approach does not seem to do that (https://github.com/qt/qtbase/commit/5feefd30734cd12753956819fc7c152be07c24cd#diff-3e09c31f0d04df4b66367538595a624aaa71179e67231c230835b76bdd79ca8bR1181 and https://github.com/qt/qtbase/commit/1a6ab689d5858e5997c989ce6c3574fb591438b0#diff-3e09c31f0d04df4b66367538595a624aaa71179e67231c230835b76bdd79ca8bR1134).We don't want a border there, even if it is a properly drawn border by the system compositor. But I think it is still better than resizing from inside.
If we do extension ourselves, by handling resize and shadow in the client like we do with X11 and Wayland, then we would need to handle rounded corners ourselves as well. With layered window, that might be possible but I'm not really inclined on going this way.
application buttons are doesn't scale with the application (they are really tiny here)
I also noticed that, probably should be reported to Qt. Maybe we should not let Qt to draw the buttons but use our Qt Quick buttons. I did not do that because when Qt draws them itself with GDI, it also appears to be handling the hit testing (Currently we are doing that in the application).
Worse, Qt's button handling is also off. I recently tried to fix that with our handling, ours is better than what Qt is doing. I don't think they properly tested
ExpandedClientAreaHint
at all. Sometimes the buttons trigger even when hovering, and the behavior does not match with Windows. Close button seems to trigger on release, minimize and maximize on press, and release within boundaries (standard Windows behavior) does not seem to be the case.Note that they started using
QPainter
3 days ago instead of Gdi: https://github.com/qt/qtbase/commit/438aa1524ee99fd636dc02a7181857ade71bb101. I have not imported that patch here yet, but it says that it respects the DPI, so I guess it would not be tiny anymore.application buttons are drawn over the application border, that's especially visible with windows 11 round corner
application buttons are always drawn, so they remains visible in the playerview when the controlbare are hidden
Maybe we should draw the buttons ourselves and patch Qt to not draw them, but adjust the size so that Qt handles the hit testing for them.
when resizing garbage is visible in the un-refreshed area, current master seems to show the the acrylic layer
I think this is also about
WVR_REDRAW
(only when the client size does not extend to the whole window, so essentially withWindows: ExpandedClientAreaHint fixups
). We can use it if that is the reason by patching Qt. Again, this was not a problem with theWindows QPA: improve Qt::FramlessWindowHint windows
but it had other issues.tiling menu is no longer available when hovering the maximize button
I noticed this, but have not investigated why. Qt seems to handle hit testing (https://github.com/qt/qtbase/blob/6e29a94b547fbafa69e97ac02aeb33edad63f2ae/src/plugins/platforms/windows/qwindowswindow.cpp#L3325), so this should work?
no resize animation
If you mean transitions, I have noticed this as well, but have not found out why. This was also fine with the
Windows QPA: improve Qt::FramlessWindowHint windows
. It usesWS_MINIMIZEBOX
andWS_MAXIMIZEBOX
which are necessary for DWM transitions (https://github.com/qt/qtbase/commit/1a6ab689d5858e5997c989ce6c3574fb591438b0#diff-3e09c31f0d04df4b66367538595a624aaa71179e67231c230835b76bdd79ca8bR816), but so does the newExpandedClientAreaHint
.There are other options there:
WS_THICKFRAME
,WS_CAPTION
. If standard Qt window does not use these, maybe we can set ourselves if they are necessary for transitions (I'm not sure).So more or less the Win7 compositor solution?
As far as I see, yes. If we really want to go that route, we should first consider extended frame (drawing shadow, buttons ourselves as well as handling the resize) and rounding the window corners ourselves with the layered window approach. Note that I'm not sure how layered top level window behaves when DirectComposition is involved, but theoretically it should work. I'm also not sure with
FramelessWindowHint
(which would be necessary with custom extended frame) we can have window transitions even if we setWS_MINIMIZEBOX
andWS_MAXIMIZEBOX
.Edited by Fatih UzunoğluIf you mean transitions, I have noticed this as well, but have not found out why.
This seems to be an issue when
Qt::WindowTitleHint
is not used, which is probably aboutWS_CAPTION
. I disabledQt::WindowTitleHint
because I did not want Qt (with the new expand client area) to draw the title itself.
added 5 commits
- 9c55019b - qml: add and use `MainCtx::platformHandlesTitleBarButtonsWithCSD()`
- a8c18b7e - qml: add and use `MainCtx::platformHandlesShadowsWithCSD()`
- e0cd6874 - contrib: qtbase: backport expanded client area support and have outside client area resize
- d7629a45 - qt: use expanded client area for csd in `CompositorDirectComposition`
- b1cc07d7 - qt: use expanded client area for csd in `CompositorWin7`
Toggle commit listadded 94 commits
-
b1cc07d7...cdd4b633 - 87 commits from branch
videolan:master
- 8d099916 - qt: consider window margins in `CompositorDCompositionAcrylicSurface`
- bcfddaf6 - qml: add and use `MainCtx::platformHandlesResizeWithCSD()`
- 0552ccf0 - qml: add and use `MainCtx::platformHandlesTitleBarButtonsWithCSD()`
- 272bf31a - qml: add and use `MainCtx::platformHandlesShadowsWithCSD()`
- 64bfb761 - contrib: qtbase: backport expanded client area support and have outside client area resize
- 03f66cf7 - qt: use expanded client area for csd in `CompositorDirectComposition`
- 1cdabfa2 - qt: use expanded client area for csd in `CompositorWin7`
Toggle commit list-
b1cc07d7...cdd4b633 - 87 commits from branch
I could import the patch for
Qt::ExpandedClientAreaHint
instead, but I guess this would be a better idea because we are still using Qt 6.8, and that feature is supposed to come with Qt 6.9. When we use more recent Qt, we can switch usingQt::ExpandedClientAreaHint
fromQt::FramelessWindowHint
. I don't have strong opinion on this.I decided to go that way instead.
Qt::FramelessWindowHint
is not properly integrated, there are existing code (such as invisible margins retrieval, or dark mode setting) that depend on frameless window (WS_POPUP
), but the patch that improves the frameless window behavior does not seem to adjust them. I could manually change the checks toWS_POPUP
instead ofFramelessWindowHint
, but I decided to not do that considering Qt also reverted that patch and that would cause issues with rebasing in the future.Now,
Qt::ExpandedClientAreaHint
is used instead. I have patched Qt myself to get the resize handle outside the client area. As said in the previous comment, there seems to be a border now:On Windows 10 at least, the border should appear dark if the color scheme is dark. I'm not sure why it is not the case, it may be a Qt bug. We can manually use the dwm function to adjust the dark mode if necessary.
I will test this now on Windows 11.
It seems similar to Windows 10, there is a thin white border
This seems to be because of using
DwmExtendFrameIntoClientArea()
(https://github.com/melak47/BorderlessWindow/issues/13#issuecomment-309234282). Qt windows have white background brush. However, I'm not sure whyDwmExtendFrameIntoClientArea()
is used, becauseWS_POPUP
is not used, so the window should have shadows and rounded corners.
Looks like @mstorsjo added a commit you're missing: https://github.com/qt/qtbase/commit/fbd8067a89ba7abf2a6d063a98edda9b19c9c749
mentioned in merge request !6562
mentioned in merge request !6586 (merged)