From 1be43ed67b7c7e75e6909e56d52b73f6a19fb68a Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Thu, 3 Oct 2019 00:39:12 +0700 Subject: [PATCH] Improve best size determination for wxSpinCtrl Use the range and the base to determine the widest value string and use it to calculate the best size. --- include/wx/msw/spinctrl.h | 3 ++ include/wx/spinctrl.h | 5 ++++ src/common/spinctrlcmn.cpp | 13 +++++++++ src/msw/spinctrl.cpp | 59 ++++++++++++++++---------------------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/include/wx/msw/spinctrl.h b/include/wx/msw/spinctrl.h index 2152ca828d..0e3f05c132 100644 --- a/include/wx/msw/spinctrl.h +++ b/include/wx/msw/spinctrl.h @@ -168,6 +168,9 @@ private: // (up-down control) and the text control (buddy window). int GetOverlap() const; + // Calculate the best size for the number with the given number of digits. + wxSize GetBestSizeFromDigitsCount(int digitsCount) const; + wxDECLARE_DYNAMIC_CLASS(wxSpinCtrl); wxDECLARE_EVENT_TABLE(); wxDECLARE_NO_COPY_CLASS(wxSpinCtrl); diff --git a/include/wx/spinctrl.h b/include/wx/spinctrl.h index 4df89ff178..0051737fbc 100644 --- a/include/wx/spinctrl.h +++ b/include/wx/spinctrl.h @@ -143,6 +143,11 @@ namespace wxPrivate // string containing hexadecimal representation of the given number. extern wxString wxSpinCtrlFormatAsHex(long val, long maxVal); +// The helper function to determine the best size for the given control. +// We can't implement this function in the wxSpinCtrlBase because MSW implementation +// of wxSpinCtrl is derived from wxSpinButton but uses the same algorithm. +extern wxSize wxSpinCtrlGetBestSize(const wxControl* spin, int minVal, int maxVal, int base); + } // namespace wxPrivate // old wxEVT_COMMAND_* constants diff --git a/src/common/spinctrlcmn.cpp b/src/common/spinctrlcmn.cpp index 87b9f64776..9598ef829d 100644 --- a/src/common/spinctrlcmn.cpp +++ b/src/common/spinctrlcmn.cpp @@ -115,4 +115,17 @@ wxString wxPrivate::wxSpinCtrlFormatAsHex(long val, long maxVal) return text; } +wxSize wxPrivate::wxSpinCtrlGetBestSize(const wxControl* spin, + int minVal, int maxVal, int base) +{ + const int lenMin = (base == 16 ? + wxSpinCtrlFormatAsHex(minVal, maxVal) : + wxString::Format("%d", minVal)).length(); + const int lenMax = (base == 16 ? + wxSpinCtrlFormatAsHex(maxVal, maxVal) : + wxString::Format("%d", maxVal)).length(); + const wxString largestString('8', wxMax(lenMin, lenMax)); + return spin->GetSizeFromText(largestString); +} + #endif // wxUSE_SPINCTRL diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index f76ad61968..11fa10ee83 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -348,39 +348,6 @@ bool wxSpinCtrl::Create(wxWindow *parent, if (!m_hasFont) SetFont(GetDefaultAttributes().font); - // Finally deal with the size: notice that this can only be done now both - // windows are created and the text one is set up as buddy because - // UDM_SETBUDDY changes its size using some unknown algorithm, so setting - // the sizes earlier is useless. - const int bestSpinWidth = wxSpinButton::DoGetBestSize().x; - const int effectiveSpinWidth = bestSpinWidth - GetOverlap(); - wxSize sizeCtrl(size); - if ( sizeCtrl.x <= 0 ) - { - // DEFAULT_ITEM_WIDTH is the default width for the text control - sizeCtrl.x = FromDIP(DEFAULT_ITEM_WIDTH) + effectiveSpinWidth; - } - else if ( sizeCtrl.x <= effectiveSpinWidth ) - { - wxLogDebug(wxS("wxSpinCtrl \"%s\": initial width %d is too small, ") - wxS("at least %d pixels needed."), - name, size.x, effectiveSpinWidth); - } - - // adjust an invalid height for text control - if ( sizeCtrl.y <= 0 ) - { - int cx, cy; - wxGetCharSize(GetHWND(), &cx, &cy, GetFont()); - - sizeCtrl.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy); - } - - // This will call our DoMoveWindow() and lay out the windows correctly. - SetInitialSize(sizeCtrl); - - (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW); - // If the initial text value is actually a number, it overrides the // "initial" argument specified later. long initialFromText; @@ -400,6 +367,22 @@ bool wxSpinCtrl::Create(wxWindow *parent, SetValue(value); m_blockEvent = false; + // Finally deal with the size: notice that this can only be done now both + // windows are created and the text one is set up as buddy because + // UDM_SETBUDDY changes its size using some unknown algorithm, so setting + // the sizes earlier is useless. Do it after setting the range and the base + // because GetBestSize() uses them. + if ( size.x > 0 && size.x < GetBestSize().x ) + { + wxLogDebug(wxS("wxSpinCtrl \"%s\": initial width %d is too small, ") + wxS("at least %d pixels needed."), + name, size.x, GetBestSize().x); + } + + SetInitialSize(size); + + (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW); + return true; } @@ -439,10 +422,16 @@ bool wxSpinCtrl::SetBase(int base) if ( !::SendMessage(GetHwnd(), UDM_SETBASE, base, 0) ) return false; + // DoGetBestSize uses the base. + InvalidateBestSize(); + // Whether we need to be able enter "x" or not influences whether we should // use ES_NUMBER for the buddy control. UpdateBuddyStyle(); + // Update the displayed text after changing the base it uses. + SetValue(GetValue()); + return true; } @@ -559,6 +548,8 @@ void wxSpinCtrl::SetRange(int minVal, int maxVal) wxSpinButton::SetRange(minVal, maxVal); + InvalidateBestSize(); + UpdateBuddyStyle(); } @@ -748,7 +739,7 @@ int wxSpinCtrl::GetOverlap() const wxSize wxSpinCtrl::DoGetBestSize() const { - return DoGetSizeFromTextSize(FromDIP(DEFAULT_ITEM_WIDTH)); + return wxPrivate::wxSpinCtrlGetBestSize(this, GetMin(), GetMax(), GetBase()); } wxSize wxSpinCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const