diff --git a/include/wx/generic/spinctlg.h b/include/wx/generic/spinctlg.h index 8f3e5ecc92..64d9cb95c5 100644 --- a/include/wx/generic/spinctlg.h +++ b/include/wx/generic/spinctlg.h @@ -382,13 +382,7 @@ public: long style = wxSP_ARROW_KEYS, double min = 0, double max = 100, double initial = 0, double inc = 1, - const wxString& name = wxT("wxSpinCtrlDouble")) - { - DetermineDigits(inc); - return wxSpinCtrlGenericBase::Create(parent, id, value, pos, size, - style, min, max, initial, - inc, name); - } + const wxString& name = wxT("wxSpinCtrlDouble")); // accessors double GetValue(wxSPINCTRL_GETVALUE_FIX) const { return DoGetValue(); } @@ -402,7 +396,7 @@ public: { wxSpinCtrlGenericBase::SetValue(value); } void SetValue(double value) { DoSetValue(value, SendEvent_None); } void SetRange(double minVal, double maxVal) { DoSetRange(minVal, maxVal); } - void SetIncrement(double inc) { DoSetIncrement(inc); } + void SetIncrement(double inc); void SetDigits(unsigned digits); // We don't implement bases support for floating point numbers, this is not @@ -416,7 +410,6 @@ protected: virtual bool DoTextToValue(const wxString& text, double *val) wxOVERRIDE; virtual wxString DoValueToText(double val) wxOVERRIDE; virtual void ResetTextValidator() wxOVERRIDE; - void DetermineDigits(double inc); unsigned m_digits; @@ -424,10 +417,16 @@ private: // Common part of all ctors. void Init() { - m_digits = 0; - m_format = wxASCII_STR("%0.0f"); + DoSetDigits(0); } + // Just set the number of digits and the format unconditionally. + void DoSetDigits(unsigned digits); + + // Call DoSetDigits() and update the appearance. + void DoSetDigitsAndUpdate(unsigned digits); + + wxString m_format; wxDECLARE_DYNAMIC_CLASS(wxSpinCtrlDouble); diff --git a/include/wx/gtk/spinctrl.h b/include/wx/gtk/spinctrl.h index 267bd94de5..5fb19b4162 100644 --- a/include/wx/gtk/spinctrl.h +++ b/include/wx/gtk/spinctrl.h @@ -243,7 +243,7 @@ public: void SetValue(const wxString& value) wxOVERRIDE { wxSpinCtrlGTKBase::SetValue(value); } // visibility problem w/ gcc void SetValue(double value) { DoSetValue(value); } void SetRange(double minVal, double maxVal) { DoSetRange(minVal, maxVal); } - void SetIncrement(double inc) { DoSetIncrement(inc); } + void SetIncrement(double inc); void SetDigits(unsigned digits); virtual int GetBase() const wxOVERRIDE { return 10; } diff --git a/include/wx/private/spinctrl.h b/include/wx/private/spinctrl.h index e0a59db1cc..66fdfb6e8e 100644 --- a/include/wx/private/spinctrl.h +++ b/include/wx/private/spinctrl.h @@ -28,6 +28,14 @@ extern wxSize GetBestSize(const wxControl* spin, int minVal, int maxVal, int bas // Helper function to check if given combination of range and base is valid. extern bool IsBaseCompatibleWithRange(int minVal, int maxVal, int base); + +// Maximum number of digits returned by DetermineDigits(). +const unsigned SPINCTRLDBL_MAX_DIGITS = 20; + +// Return the number of digits required to show the numbers using the +// specified increment without loss of precision. +extern unsigned DetermineDigits(double inc); + } // namespace wxSpinCtrlImpl #endif // _WX_PRIVATE_SPINCTRL_H_ diff --git a/interface/wx/spinctrl.h b/interface/wx/spinctrl.h index 2ee73557a1..bb41ccd909 100644 --- a/interface/wx/spinctrl.h +++ b/interface/wx/spinctrl.h @@ -354,8 +354,15 @@ public: /** Sets the increment value. - @note You may also need to change the precision of the value - using SetDigits(). + + Using this method changes the number of digits used by the control to + at least match the value of @a inc, e.g. using the increment of @c 0.01 + sets the number of digits to 2 if it had been less than 2 before. + However it doesn't change the number of digits if it had been already + high enough. + + In any case, you may call SetDigits() explicitly to override the + automatic determination of the number of digits. */ void SetIncrement(double inc); diff --git a/src/common/spinctrlcmn.cpp b/src/common/spinctrlcmn.cpp index ac3c562843..956fcbf212 100644 --- a/src/common/spinctrlcmn.cpp +++ b/src/common/spinctrlcmn.cpp @@ -26,6 +26,8 @@ #include "wx/private/spinctrl.h" +#include + wxDEFINE_EVENT(wxEVT_SPINCTRL, wxSpinEvent); wxDEFINE_EVENT(wxEVT_SPINCTRLDOUBLE, wxSpinDoubleEvent); @@ -140,4 +142,19 @@ bool wxSpinCtrlImpl::IsBaseCompatibleWithRange(int minVal, int maxVal, int base) return base == 10 || (minVal >= 0 && maxVal >= 0); } +unsigned wxSpinCtrlImpl::DetermineDigits(double inc) +{ + // TODO-C++11: Use std::modf() to get the fractional part. + inc = fabs(inc); + inc -= static_cast(inc); + if ( inc > 0.0 ) + { + return wxMin(SPINCTRLDBL_MAX_DIGITS, -static_cast(floor(log10(inc)))); + } + else + { + return 0; + } +} + #endif // wxUSE_SPINCTRL diff --git a/src/generic/spinctlg.cpp b/src/generic/spinctlg.cpp index 7255045e24..ae47212210 100644 --- a/src/generic/spinctlg.cpp +++ b/src/generic/spinctlg.cpp @@ -723,10 +723,26 @@ void wxSpinCtrl::ResetTextValidator() // wxSpinCtrlDouble //----------------------------------------------------------------------------- -#define SPINCTRLDBL_MAX_DIGITS 20 - wxIMPLEMENT_DYNAMIC_CLASS(wxSpinCtrlDouble, wxSpinCtrlGenericBase); +bool +wxSpinCtrlDouble::Create(wxWindow *parent, + wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + long style, + double min, double max, double initial, + double inc, + const wxString& name) +{ + DoSetDigits(wxSpinCtrlImpl::DetermineDigits(inc)); + + return wxSpinCtrlGenericBase::Create(parent, id, value, pos, size, + style, min, max, initial, + inc, name); +} + void wxSpinCtrlDouble::DoSendEvent() { wxSpinDoubleEvent event( wxEVT_SPINCTRLDOUBLE, GetId()); @@ -746,16 +762,36 @@ wxString wxSpinCtrlDouble::DoValueToText(double val) return wxString::Format(m_format, val); } +void wxSpinCtrlDouble::SetIncrement(double inc) +{ + if ( inc == m_increment ) + return; + + DoSetIncrement(inc); + + const unsigned digits = wxSpinCtrlImpl::DetermineDigits(inc); + + // We don't decrease the number of digits here, as this is unnecessary and + // could be undesirable, but we do increase it if the current number is not + // high enough to show the numbers without losing precision. + if ( digits > m_digits ) + DoSetDigitsAndUpdate(digits); +} + void wxSpinCtrlDouble::SetDigits(unsigned digits) { - wxCHECK_RET( digits <= SPINCTRLDBL_MAX_DIGITS, "too many digits for wxSpinCtrlDouble" ); + wxCHECK_RET( digits <= wxSpinCtrlImpl::SPINCTRLDBL_MAX_DIGITS, + "too many digits for wxSpinCtrlDouble" ); if ( digits == m_digits ) return; - m_digits = digits; + DoSetDigitsAndUpdate(digits); +} - m_format.Printf(wxT("%%0.%ulf"), digits); +void wxSpinCtrlDouble::DoSetDigitsAndUpdate(unsigned digits) +{ + DoSetDigits(digits); ResetTextValidator(); m_textCtrl->InvalidateBestSize(); @@ -763,6 +799,13 @@ void wxSpinCtrlDouble::SetDigits(unsigned digits) DoSetValue(m_value, SendEvent_None); } +void wxSpinCtrlDouble::DoSetDigits(unsigned digits) +{ + m_digits = digits; + + m_format.Printf(wxT("%%0.%ulf"), digits); +} + void wxSpinCtrlDouble::ResetTextValidator() { #if wxUSE_VALIDATORS @@ -772,16 +815,6 @@ void wxSpinCtrlDouble::ResetTextValidator() #endif // wxUSE_VALIDATORS } -void wxSpinCtrlDouble::DetermineDigits(double inc) -{ - inc = fabs(inc); - if ( inc > 0.0 && inc < 1.0 ) - { - m_digits = wxMin(SPINCTRLDBL_MAX_DIGITS, -static_cast(floor(log10(inc)))); - m_format.Printf("%%0.%ulf", m_digits); - } -} - #endif // wxUSE_SPINBTN #endif // !wxPort-with-native-spinctrl diff --git a/src/gtk/spinctrl.cpp b/src/gtk/spinctrl.cpp index 637fb60592..c327734e8a 100644 --- a/src/gtk/spinctrl.cpp +++ b/src/gtk/spinctrl.cpp @@ -643,6 +643,18 @@ void wxSpinCtrlDouble::GtkSetEntryWidth() gtk_entry_set_width_chars(GTK_ENTRY(m_widget), wxMax(lenMin, lenMax)); } +void wxSpinCtrlDouble::SetIncrement(double inc) +{ + DoSetIncrement(inc); + + const unsigned digits = wxSpinCtrlImpl::DetermineDigits(inc); + + // Increase the number of digits, if necessary, to show all numbers that + // can be obtained by using the new increment without loss of precision. + if ( digits > GetDigits() ) + SetDigits(digits); +} + unsigned wxSpinCtrlDouble::GetDigits() const { wxCHECK_MSG( m_widget, 0, "invalid spin button" ); diff --git a/tests/controls/spinctrldbltest.cpp b/tests/controls/spinctrldbltest.cpp index 1e834b1915..c31e83aa84 100644 --- a/tests/controls/spinctrldbltest.cpp +++ b/tests/controls/spinctrldbltest.cpp @@ -217,18 +217,47 @@ TEST_CASE_METHOD(SpinCtrlDoubleTestCase, TEST_CASE_METHOD(SpinCtrlDoubleTestCase, "SpinCtrlDouble::Digits", "[spinctrl][spinctrldouble]") { - m_spin->SetDigits(5); + // Setting increment should adjust the number of digits shown to be big + // enough to show numbers with the corresponding granularity. + m_spin->SetIncrement(0.1); + m_spin->SetValue(1.23456789); + CHECK( m_spin->GetTextValue() == "1.2" ); + m_spin->SetIncrement(0.01); + m_spin->SetValue(1.23456789); + CHECK( m_spin->GetTextValue() == "1.23" ); + + m_spin->SetDigits(5); CHECK( m_spin->GetDigits() == 5 ); + m_spin->SetValue(1.23456789); + CHECK( m_spin->GetTextValue() == "1.23457" ); + + // The number of digits shouldn't (implicitly) decrease however. + m_spin->SetIncrement(0.001); + m_spin->SetValue(1.23456789); + CHECK( m_spin->GetTextValue() == "1.23457" ); + + // Check that using increment greater than 1 also works. + m_spin->SetDigits(0); + m_spin->SetIncrement(2.5); + m_spin->SetValue(7.5); + CHECK( m_spin->GetTextValue() == "7.5" ); } static inline unsigned int GetInitialDigits(double inc) { - wxSpinCtrlDouble* sc = new wxSpinCtrlDouble(wxTheApp->GetTopWindow(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, - 0, 50, 0, inc); - unsigned int digits = sc->GetDigits(); - delete sc; - return digits; + wxScopedPtr sc(new wxSpinCtrlDouble + ( + wxTheApp->GetTopWindow(), + wxID_ANY, + wxEmptyString, + wxDefaultPosition, + wxDefaultSize, + wxSP_ARROW_KEYS, + 0, 50, 0, + inc + )); + return sc->GetDigits(); } TEST_CASE("SpinCtrlDouble::InitialDigits", "[spinctrldouble][initialdigits]")