Store dialog geometry in DPI-independent pixels to prevent size ratcheting

On mixed-DPI multi-monitor setups, dialog sizes grow each time they are
reopened because logical pixel values change when a window moves between
monitors with different scaling factors. The std::max comparison during
restore guarantees monotonic growth since the DPI-scaled value always
exceeds the previously saved unscaled value.

Store width and height as DIP (device-independent pixels) using ToDIP()
on save and FromDIP() on restore. This makes the persisted size stable
regardless of which monitor the dialog was on when closed. A "dip" flag
in the JSON differentiates new DIP-format values from legacy logical
pixel values, which are converted in place on first load.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/20120
This commit is contained in:
Seth Hillbrand
2026-02-19 15:21:30 -08:00
parent 426d122b60
commit ee8b2bebce
4 changed files with 56 additions and 9 deletions
+11 -7
View File
@@ -387,6 +387,9 @@ bool DIALOG_SHIM::Show( bool show )
if( savedDialogRect.GetSize().x != 0 && savedDialogRect.GetSize().y != 0 )
{
// Convert saved DIP size to logical pixels for the current monitor
wxSize restoredSize = FromDIP( savedDialogRect.GetSize() );
if( m_useCalculatedSize )
{
SetSize( savedDialogRect.GetPosition().x, savedDialogRect.GetPosition().y,
@@ -395,8 +398,8 @@ bool DIALOG_SHIM::Show( bool show )
else
{
SetSize( savedDialogRect.GetPosition().x, savedDialogRect.GetPosition().y,
std::max( wxDialog::GetSize().x, savedDialogRect.GetSize().x ),
std::max( wxDialog::GetSize().y, savedDialogRect.GetSize().y ), 0 );
std::max( wxDialog::GetSize().x, restoredSize.x ),
std::max( wxDialog::GetSize().y, restoredSize.y ), 0 );
// Reset minimum size so the user can resize the dialog smaller than
// the saved size. We must clear the current minimum and invalidate
@@ -565,12 +568,13 @@ void DIALOG_SHIM::SaveControlState()
std::string dialogKey = m_hash_key.empty() ? getDialogKeyFromTitle( GetTitle() ) : m_hash_key;
std::map<std::string, nlohmann::json>& dlgMap = settings->CsInternals().m_dialogControlValues[ dialogKey ];
wxRect rect( GetPosition(), GetSize() );
wxPoint pos = GetPosition();
wxSize dipSize = ToDIP( GetSize() );
nlohmann::json geom;
geom[ "x" ] = rect.GetX();
geom[ "y" ] = rect.GetY();
geom[ "w" ] = rect.GetWidth();
geom[ "h" ] = rect.GetHeight();
geom[ "x" ] = pos.x;
geom[ "y" ] = pos.y;
geom[ "w" ] = dipSize.x;
geom[ "h" ] = dipSize.y;
dlgMap[ "__geometry" ] = geom;
std::function<void( wxWindow* )> saveFn =