AUI pane title bar looks wrong in high DPI
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.
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.
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:
Moving it between screens then resizing a sash breaks it in other ways.
Versions: 3.2 / master
Just to be clear: your primary monitor DPI is 100%, right?
Just to be clear: your primary monitor DPI is 100%, right?
Yes.
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!
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?
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...
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.
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?
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.
I'll (gratefully) let you turn your patch into a PR
I created #24844