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:
+11
-7
@@ -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 =
|
||||
|
||||
@@ -38,13 +38,14 @@
|
||||
#include <wx/log.h>
|
||||
#include <wx/regex.h>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
|
||||
///! The following environment variables will never be migrated from a previous version
|
||||
const wxRegEx versionedEnvVarRegex( wxS( "KICAD[0-9]+_[A-Z0-9_]+(_DIR)?" ) );
|
||||
|
||||
///! Update the schema version whenever a migration is required
|
||||
const int commonSchemaVersion = 4;
|
||||
const int commonSchemaVersion = 5;
|
||||
|
||||
COMMON_SETTINGS::~COMMON_SETTINGS() = default;
|
||||
|
||||
@@ -482,6 +483,7 @@ COMMON_SETTINGS::COMMON_SETTINGS() :
|
||||
registerMigration( 1, 2, std::bind( &COMMON_SETTINGS::migrateSchema1to2, this ) );
|
||||
registerMigration( 2, 3, std::bind( &COMMON_SETTINGS::migrateSchema2to3, this ) );
|
||||
registerMigration( 3, 4, std::bind( &COMMON_SETTINGS::migrateSchema3to4, this ) );
|
||||
registerMigration( 4, 5, std::bind( &COMMON_SETTINGS::migrateSchema4to5, this ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -661,6 +663,46 @@ bool COMMON_SETTINGS::migrateSchema3to4()
|
||||
}
|
||||
|
||||
|
||||
bool COMMON_SETTINGS::migrateSchema4to5()
|
||||
{
|
||||
try
|
||||
{
|
||||
nlohmann::json& controls = m_internals->At( "dialog" ).at( "controls" );
|
||||
|
||||
for( auto& [dlgKey, dlgVal] : controls.items() )
|
||||
{
|
||||
if( !dlgVal.is_object() )
|
||||
continue;
|
||||
|
||||
auto geoIt = dlgVal.find( "__geometry" );
|
||||
|
||||
if( geoIt == dlgVal.end() || !geoIt->is_object() )
|
||||
continue;
|
||||
|
||||
nlohmann::json& geom = *geoIt;
|
||||
|
||||
// Legacy values were stored in logical pixels. Convert to DIP using the
|
||||
// primary display's scale factor (best approximation without window context).
|
||||
int w = geom.value( "w", 0 );
|
||||
int h = geom.value( "h", 0 );
|
||||
|
||||
wxSize dipSize = wxWindow::ToDIP( wxSize( w, h ), nullptr );
|
||||
geom[ "w" ] = dipSize.x;
|
||||
geom[ "h" ] = dipSize.y;
|
||||
|
||||
geom.erase( "dip" );
|
||||
}
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
wxLogTrace( traceSettings,
|
||||
wxT( "COMMON_SETTINGS::Migrate 4->5: dialog.controls not found" ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg )
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
@@ -197,6 +197,7 @@ private:
|
||||
bool migrateSchema1to2();
|
||||
bool migrateSchema2to3();
|
||||
bool migrateSchema3to4();
|
||||
bool migrateSchema4to5();
|
||||
|
||||
struct LEGACY_3D_SEARCH_PATH
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"zoom_speed_auto": true
|
||||
},
|
||||
"meta": {
|
||||
"version": 4
|
||||
"version": 5
|
||||
},
|
||||
"package_manager": {
|
||||
"sash_pos": 380
|
||||
|
||||
Reference in New Issue
Block a user