I tried to debug a dialog whose layout is completely broken after moving it from a monitor with 200% DPI scaling to a monitor with 100% DPI scaling.
The cause seems to be the use of wxSizer::SetMinSize().
At that point I realized that I actually don't know how wx{Sizer,Window}::Set{Min,Max}Size() is intended to work in regard to per-monitor DPI awareness.
Therefore, I looked in the documentation for such information, but could not find any.
I would therefore kindly ask you to add information to the documentation in this regard.
Let me illustrate my trouble in understanding with a concrete example.
wxSizer::SetMinSize() or wxWindow::SetMinSize() is called for some wxSizer's or wxControl's.Layout() is called, it will cause a broken layout, right?The wxSizer's and wxWindow's might internally store specified minimum/maximum sizes in DIPs, which would avoid such trouble.
But I can't find any information about that in the documentation.
I checked:
wxSizer::SetMinSize()wxWindow::SetMinSize()If it's just me, which is quite possible, please point me to the relevant section.
Thanks in advance!
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you are subscribed to this thread.![]()
There probably isn't any documentation on this. But the minimum size of windows and sizers should be scaled automatically to the new DPI when a DPI-changed event occurs, in
https://github.com/wxWidgets/wxWidgets/blob/294a364b487a3f1cf9b0469e4dde7b82ff9bbfbe/src/msw/window.cpp#L5054
and https://github.com/wxWidgets/wxWidgets/blob/294a364b487a3f1cf9b0469e4dde7b82ff9bbfbe/src/msw/window.cpp#L5015
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you are subscribed to this thread.![]()
Many thanks for your informative response.
That is great news for me.
If it works automatically, it might not be necessary to document it explicitly.
On the other hand, this information can be very helpful if something is not working as expected, to know how it is supposed to work.
However, I am not sure if it actually works as intended in every case at the moment.
Therefore, I have a few more questions.
Shouldn't wxGridSizer's horizontal and vertical gap (GetHGap(), GetVGap()) also be scaled automatically to the new DPI?
Maybe I am missing something, but in UpdateSizerOnDPIChange() only the border and the minimum size are being scaled.
Why does code like the following behave so differently when a minimum size is set or not?
wxDPIChangedEvent is sent after the minimum size of all child windows and sizers has been adjusted to the new DPI.wxDPIChangedEvent handler, the wxGridSizer returns still the initially set minimum size.diff -r -u -p a/samples/layout/layout.cpp b/samples/layout/layout.cpp --- a/samples/layout/layout.cpp 2026-05-26 12:20:44.094518000 +0200 +++ b/samples/layout/layout.cpp 2026-05-26 12:38:14.367767400 +0200 @@ -70,6 +70,8 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(LAYOUT_TEST_SET_MINIMAL, MyFrame::TestSetMinimal) EVT_MENU(LAYOUT_TEST_NESTED, MyFrame::TestNested) EVT_MENU(LAYOUT_TEST_WRAP, MyFrame::TestWrap) + + EVT_DPI_CHANGED(MyFrame::OnDPIChanged) wxEND_EVENT_TABLE() // Define my frame constructor @@ -116,6 +118,51 @@ MyFrame::MyFrame() wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL ); + const int border = FromDIP(5); + + for (int i = 0; i < 2; i++) + { + topsizer->AddSpacer( FromDIP(10) ); + + const bool withMinSize = (i == 1); + wxString statlabel = withMinSize ? "wxGridSizer's with a specified minimum size" + : "wxGridSizer's without minimum size"; + wxStaticBoxSizer *statsizer = new wxStaticBoxSizer(wxHORIZONTAL, p, statlabel); + topsizer->Add(statsizer, 0, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxRIGHT|wxBOTTOM, border); + + wxBoxSizer *bs = new wxBoxSizer(wxHORIZONTAL); + statsizer->Add(bs, 0, 0, 0); + + wxGridSizer *gsL = new wxGridSizer(1, border, border); + if (withMinSize) + m_gridSizerWithMinSize = gsL; + bs->Add(gsL, 0, wxLEFT, border); + wxGridSizer *gsR = new wxGridSizer(1, border, border); + bs->Add(gsR, 0, wxLEFT|wxRIGHT, border); + + wxStaticText *itemL1 = new wxStaticText(p, wxID_STATIC, "A_long_text_on_the_left:"); + gsL->Add(itemL1, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL); + + wxStaticText *itemR1 = new wxStaticText(p, wxID_STATIC, "Text1_right"); + gsR->Add(itemR1, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL); + + wxStaticText *itemL2 = new wxStaticText(p, wxID_STATIC, "Text2_left:"); + gsL->Add(itemL2, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL); + + wxStaticText *itemR2 = new wxStaticText(p, wxID_STATIC, "A_long_text_on_the_right"); + gsR->Add(itemR2, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL); + + if ( withMinSize ) + { + gsL->SetMinSize( gsL->CalcMin() ); + gsR->SetMinSize( gsR->CalcMin() ); + + wxLogStatus(this, "Minimum width: %d (at %d DPI)", + m_gridSizerWithMinSize->GetMinSize().GetWidth(), GetDPI().x ); + } + } + +#if 0 // 1) top: create wxStaticText with minimum size equal to its default size topsizer->Add( new wxStaticText( p, wxID_ANY, "An explanation (wxALIGN_RIGHT)." ), @@ -179,7 +226,7 @@ MyFrame::MyFrame() wxSizerFlags().Border(wxALL, FromDIP(7))); topsizer->Add(button_box, wxSizerFlags().Center()); - +#endif p->SetSizer( topsizer ); // don't allow frame to get smaller than what the sizers tell it and also set @@ -187,6 +234,14 @@ MyFrame::MyFrame() topsizer->SetSizeHints( this ); } +void MyFrame::OnDPIChanged(wxDPIChangedEvent& event) +{ + wxLogStatus(this, "Minimum width: %d (at %d DPI)", + m_gridSizerWithMinSize->GetMinSize().GetWidth(), GetDPI().x ); + + event.Skip(); +} + void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(true); diff -r -u -p a/samples/layout/layout.h b/samples/layout/layout.h --- a/samples/layout/layout.h 2026-05-26 12:20:44.094518000 +0200 +++ b/samples/layout/layout.h 2026-05-26 12:23:43.955863800 +0200 @@ -32,6 +32,10 @@ public: void OnAbout(wxCommandEvent& event); void OnQuit(wxCommandEvent& event); + void OnDPIChanged(wxDPIChangedEvent& event); + + wxGridSizer *m_gridSizerWithMinSize; + private: wxDECLARE_EVENT_TABLE(); };
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you are subscribed to this thread.![]()
@vadz, should the title of this ticket be changed, as the problem described above is significantly more serious than a possibly incomplete documentation?
Or would it be best to create a separate ticket for the problem? TIA!
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
Sorry, I'm not ignoring this, just didn't have time to look at/debug it/understand what's going on here yet and I didn't want to write anything potentially wrong before doing this to avoid increasing the total amount of confusion. I do hope to do it soon.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
BTW, I think that the answer to the question "Why does code like the following behave so differently when a minimum size is set or not?" is given in this comment.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
I don't know what should/could be documented here, basically the min size is expressed in logical pixels, just as all the other sizes. And it is supposed to be scaled on DPI change, but this didn't actually happen because wxSizer::m_minSize is different from wxSizerItem::m_minSize which the code in UpdateSizerOnDPIChange() updated. I've changed it to do this now in 3a1bcdd which I pushed to #26604.
Shouldn't
wxGridSizer's horizontal and vertical gap (GetHGap(),GetVGap()) also be scaled automatically to the new DPI?
Yes, they should, and I did this in 0245dea now.
Do you still see any problems with these commits?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
These commits work perfectly fine. Thanks!
Now that it works correctly automatically, it's probably not necessary to document anything.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
I don't know what should/could be documented here, basically the min size is expressed in logical pixels, just as all the other sizes.
@vadz, I have always thought that all numbers in sizes we pass to wxWindow/wxSizer are in physical pixels? I could not find information about which pixels are used anywhere in the docs (e.g., window sizing or sizers overview). The only information I could find was in the High DPI Overview and wxWindow::FromDIP(), where it is stated that hard-coded pixels are physical and need to be wrapped in FromDIP() (which has unfortunate innate problem of not working in the ctor call)?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
No, all the numbers are in logical pixels — which is basically the definition of LPs, in fact: "logical pixels" are the ones that wx functions take. They may be the same as physical pixels or same as DI pixels, which is the whole source of confusion.
Frankly, all this was a mistake :-( We should have just changed to taking DIPs and damn the compatibility. But it's clearly too late to do it by now, as usual...
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
Sorry, my mistake. I somehow missed the whole wxWidgets documentation section on this: https://docs.wxwidgets.org/latest/overview_high_dpi.html#high_dpi_pixel_types
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()