wxWidgets icon indicating copy to clipboard operation
wxWidgets copied to clipboard

AUI pane title bar looks wrong in high DPI

Open vadz opened this issue 3 years ago • 4 comments

I wrote

I've also noticed another problem, not related to toolbars: the title bars are not laid out/drawn correctly after a DPI change, see e.g. "Tree Pane" in the sample.

but this is actually unrelated to the DPI change, it looks bad even initially, just running the sample in 2x scaling shows the problem, so extracting this one in a separate issue.

vadz avatar Apr 04 '23 20:04 vadz

I think this was fixed since then, at least I don't see any glaring problems any more. The title bar icons look bad in high DPI because we don't provide high resolution bitmaps for them but there is already #18959 for this.

vadz avatar Dec 26 '23 02:12 vadz

If I start auidemo at wxPoint(2000, 50), which places it on a 250% scale (virtual) screen on Windows 11, the title bars are too thin:

Screenshot 2024-05-26 013440

Moving it between screens then resizing a sash breaks it in other ways.

Versions: 3.2 / master

dsa-t avatar May 25 '24 22:05 dsa-t

Just to be clear: your primary monitor DPI is 100%, right?

vadz avatar Jun 08 '24 20:06 vadz

Just to be clear: your primary monitor DPI is 100%, right?

Yes.

dsa-t avatar Jun 08 '24 21:06 dsa-t

I have a real problem with debugging this, as I don't have a multi-monitor Windows development machine any more and I can't reproduce this otherwise, i.e. if I start the sample in 100% scaling and then switch it to 200%, it adjusts to it correctly (in master, at least).

Any help with debugging what's going on a real multi-monitor system would be very welcome!

vadz avatar Sep 24 '24 16:09 vadz

I can reproduce it. For me it doesn't happen on the DPI change, but when moving borders of a pane after the DPI change. Then the captions/headers get their original height back.

The problem is that wxAuiDefaultDockArt sizes are not Per-Monitor DPI aware: https://github.com/wxWidgets/wxWidgets/blob/597499316370a1d1b08e2e50bfc6ca0e85b84fc2/src/aui/dockart.cpp#L208-L213

They are initialized once, and not updated when the DPI changes. I think we should change these to be DPI unaware. And then wrap all the GetMetric() calls with FromDIP(). Something like the patch below. I'm not sure if used the correct pane/window/frame everywhere, but these seem the most correct.

patch
 samples/aui/auidemo.cpp  |  2 +-
 src/aui/dockart.cpp      | 16 ++++++++--------
 src/aui/floatpane.cpp    |  4 ++--
 src/aui/framemanager.cpp | 30 +++++++++++++++---------------
 src/aui/tabart.cpp       |  4 ++--
 5 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/samples/aui/auidemo.cpp b/samples/aui/auidemo.cpp
index a5df60252b..a3a7b1bc05 100644
--- a/samples/aui/auidemo.cpp
+++ b/samples/aui/auidemo.cpp
@@ -967,7 +967,7 @@ MyFrame::MyFrame(wxWindow* parent,
     wxWindow* wnd10 = CreateTextCtrl("This pane will prompt the user before hiding.");
 
     // Give this pane an icon, too, just for testing.
-    int iconSize = m_mgr.GetArtProvider()->GetMetric(wxAUI_DOCKART_CAPTION_SIZE);
+    int iconSize = FromDIP(m_mgr.GetArtProvider()->GetMetric(wxAUI_DOCKART_CAPTION_SIZE));
 
     // Make it even to use 16 pixel icons with default 17 caption height.
     iconSize &= ~1;
diff --git a/src/aui/dockart.cpp b/src/aui/dockart.cpp
index b39c9267c6..83a1e5fd2b 100644
--- a/src/aui/dockart.cpp
+++ b/src/aui/dockart.cpp
@@ -205,12 +205,12 @@ wxAuiDefaultDockArt::wxAuiDefaultDockArt()
 #elif defined(__WXGTK__)
     m_sashSize     = wxRendererNative::Get().GetSplitterParams(nullptr).widthSash;
 #else
-    m_sashSize     = wxWindow::FromDIP( 4, nullptr);
+    m_sashSize     = 4;
 #endif
-    m_captionSize  = wxWindow::FromDIP(17, nullptr);
+    m_captionSize  = 17;
     m_borderSize   = 1;
-    m_buttonSize   = wxWindow::FromDIP(14, nullptr);
-    m_gripperSize  = wxWindow::FromDIP( 9, nullptr);
+    m_buttonSize   = 14;
+    m_gripperSize  = 9;
     m_gradientType = wxAUI_GRADIENT_VERTICAL;
 
     InitBitmaps();
@@ -518,7 +518,7 @@ void wxAuiDefaultDockArt::DrawBorder(wxDC& dc, wxWindow* window, const wxRect& _
     dc.SetBrush(*wxTRANSPARENT_BRUSH);
 
     wxRect rect = _rect;
-    int i, border_width = GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
+    int i, border_width = window->FromDIP(GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE));
 
     if (pane.IsToolbar())
     {
@@ -640,11 +640,11 @@ void wxAuiDefaultDockArt::DrawCaption(wxDC& dc,
     clip_rect.width -= window->FromDIP(3); // text offset
     clip_rect.width -= window->FromDIP(2); // button padding
     if (pane.HasCloseButton())
-        clip_rect.width -= m_buttonSize;
+        clip_rect.width -= window->FromDIP(m_buttonSize);
     if (pane.HasPinButton())
-        clip_rect.width -= m_buttonSize;
+        clip_rect.width -= window->FromDIP(m_buttonSize);
     if (pane.HasMaximizeButton())
-        clip_rect.width -= m_buttonSize;
+        clip_rect.width -= window->FromDIP(m_buttonSize);
 
     wxString draw_text = wxAuiChopText(dc, text, clip_rect.width);
 
diff --git a/src/aui/floatpane.cpp b/src/aui/floatpane.cpp
index 5fe6f64bab..f9e38fad05 100644
--- a/src/aui/floatpane.cpp
+++ b/src/aui/floatpane.cpp
@@ -148,9 +148,9 @@ void wxAuiFloatingFrame::SetPaneWindow(const wxAuiPaneInfo& pane)
         if (m_ownerMgr && pane.HasGripper())
         {
             if (pane.HasGripperTop())
-                size.y += m_ownerMgr->m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE);
+                size.y += m_paneWindow->FromDIP(m_ownerMgr->m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE));
             else
-                size.x += m_ownerMgr->m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE);
+                size.x += m_paneWindow->FromDIP(m_ownerMgr->m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE));
         }
 
         SetClientSize(size);
diff --git a/src/aui/framemanager.cpp b/src/aui/framemanager.cpp
index 3951b56955..d972f40adf 100644
--- a/src/aui/framemanager.cpp
+++ b/src/aui/framemanager.cpp
@@ -1344,10 +1344,6 @@ void wxAuiManager::GetPanePositionsAndSizes(wxAuiDockInfo& dock,
                                             wxArrayInt& positions,
                                             wxArrayInt& sizes)
 {
-    int caption_size = m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE);
-    int pane_borderSize = m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
-    int gripperSize = m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE);
-
     positions.Empty();
     sizes.Empty();
 
@@ -1372,6 +1368,10 @@ void wxAuiManager::GetPanePositionsAndSizes(wxAuiDockInfo& dock,
     for (pane_i = 0; pane_i < pane_count; ++pane_i)
     {
         wxAuiPaneInfo& pane = *(dock.panes.Item(pane_i));
+        int caption_size = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE));
+        int pane_borderSize = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE));
+        int gripperSize = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE));
+
         positions.Add(pane.dock_pos);
         int size = 0;
 
@@ -1437,10 +1437,10 @@ void wxAuiManager::LayoutAddPane(wxSizer* cont,
     wxAuiDockUIPart part;
     wxSizerItem* sizer_item;
 
-    int caption_size = m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE);
-    int gripperSize = m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE);
-    int pane_borderSize = m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
-    int pane_button_size = m_art->GetMetric(wxAUI_DOCKART_PANE_BUTTON_SIZE);
+    int caption_size = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE));
+    int gripperSize = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_GRIPPER_SIZE));
+    int pane_borderSize = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE));
+    int pane_button_size = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_PANE_BUTTON_SIZE));
 
     // find out the orientation of the item (orientation for panes
     // is the same as the dock's orientation)
@@ -1619,7 +1619,7 @@ void wxAuiManager::LayoutAddDock(wxSizer* cont,
     wxSizerItem* sizer_item;
     wxAuiDockUIPart part;
 
-    int sashSize = m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE);
+    int sashSize = m_frame->FromDIP(m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE));
     int orientation = dock.IsHorizontal() ? wxHORIZONTAL : wxVERTICAL;
 
     // resizable bottom and right docks have a sash before them
@@ -1774,8 +1774,8 @@ wxSizer* wxAuiManager::LayoutAll(wxAuiPaneInfoArray& panes,
 {
     wxBoxSizer* container = new wxBoxSizer(wxVERTICAL);
 
-    int pane_borderSize = m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
-    int caption_size = m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE);
+    int pane_borderSize = m_frame->FromDIP(m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE));
+    int caption_size = m_frame->FromDIP(m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE));
     wxSize cli_size = m_frame->GetClientSize();
     int i, dock_count, pane_count;
 
@@ -3869,7 +3869,7 @@ bool wxAuiManager::DoEndResizeAction(wxMouseEvent& event)
     if (m_actionPart && m_actionPart->type==wxAuiDockUIPart::typeDockSizer)
     {
         // first, we must calculate the maximum size the dock may be
-        int sashSize = m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE);
+        int sashSize = m_frame->FromDIP(m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE));
 
         int used_width = 0, used_height = 0;
 
@@ -3961,9 +3961,9 @@ bool wxAuiManager::DoEndResizeAction(wxMouseEvent& event)
         int dock_pixels = 0;
         int new_pixsize = 0;
 
-        int caption_size = m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE);
-        int pane_borderSize = m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
-        int sashSize = m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE);
+        int caption_size = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE));
+        int pane_borderSize = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE));
+        int sashSize = pane.window->FromDIP(m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE));
 
         wxPoint new_pos(event.m_x - m_actionOffset.x,
             event.m_y - m_actionOffset.y);
diff --git a/src/aui/tabart.cpp b/src/aui/tabart.cpp
index 4feddd5878..c8eedd29d1 100644
--- a/src/aui/tabart.cpp
+++ b/src/aui/tabart.cpp
@@ -666,7 +666,7 @@ int wxAuiGenericTabArt::GetBorderWidth(wxWindow* wnd)
     {
         wxAuiDockArt* art = mgr->GetArtProvider();
         if (art)
-            return art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
+            return wnd->FromDIP(art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE));
     }
     return 1;
 }
@@ -1212,7 +1212,7 @@ int wxAuiSimpleTabArt::GetBorderWidth(wxWindow* wnd)
     {
        wxAuiDockArt*  art = mgr->GetArtProvider();
         if (art)
-            return art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
+            return wnd->FromDIP(art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE));
     }
     return 1;
 }

Another issue I see in these captions/headers is that the maximize and close button is a bit small at 150%. But I think this is because bitmap bundle doesn't create bitmaps for fractional scaling?

MaartenBent avatar Sep 25 '24 22:09 MaartenBent

Thanks for testing this!

I guess we indeed need to document that GetMetric() returns value in DIPs and apply the suggested changes, the only other alternative I see is to call it with wxWindow* parameter, but it doesn't seem to be obviously better. I'll (gratefully) let you turn your patch into a PR, but please let me know if I should do it myself.

For the buttons, I think you're right. Ideal would be to use SVGs for them...

vadz avatar Sep 26 '24 17:09 vadz

I have a real problem with debugging this, as I don't have a multi-monitor Windows development machine any more and I can't reproduce this otherwise

I've been using https://github.com/itsmikethetech/Virtual-Display-Driver and then viewed the virtual screen in OBS by adding it as a source then using "Fullscreen Projector (Preview)" mode.

dsa-t avatar Sep 26 '24 17:09 dsa-t

I've been using https://github.com/itsmikethetech/Virtual-Display-Driver and then viewed the virtual screen in OBS by adding it as a source then using "Fullscreen Projector (Preview)" mode.

Thanks for the hint, but I don't understand how does this work for you: where do you run OBS? On the same Windows machine? Or how do you get access to the virtual display from outside it?

vadz avatar Sep 26 '24 17:09 vadz

Yes, I run OBS on the same machine; the virtual monitor is added as a normal Display Capture source; in "Fullscreen Projector (Preview)" I choose the main monitor, so I see scaled virtual screen's content on the main monitor (Alt-Tab works if you want to put some windows above the preview).

"Fullscreen Projector (Preview)" is accessed by right-clicking in the preview area.

dsa-t avatar Sep 26 '24 18:09 dsa-t

I'll (gratefully) let you turn your patch into a PR

I created #24844

MaartenBent avatar Sep 28 '24 17:09 MaartenBent