Merge branch 'spinctrl-digits'
Improve setting the number of digits in wxSpinCtrlDouble and make it consistent on all platforms. See https://github.com/wxWidgets/wxWidgets/pull/2348 Closes #17085.
This commit is contained in:
@@ -382,13 +382,7 @@ public:
|
|||||||
long style = wxSP_ARROW_KEYS,
|
long style = wxSP_ARROW_KEYS,
|
||||||
double min = 0, double max = 100, double initial = 0,
|
double min = 0, double max = 100, double initial = 0,
|
||||||
double inc = 1,
|
double inc = 1,
|
||||||
const wxString& name = wxT("wxSpinCtrlDouble"))
|
const wxString& name = wxT("wxSpinCtrlDouble"));
|
||||||
{
|
|
||||||
DetermineDigits(inc);
|
|
||||||
return wxSpinCtrlGenericBase::Create(parent, id, value, pos, size,
|
|
||||||
style, min, max, initial,
|
|
||||||
inc, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// accessors
|
// accessors
|
||||||
double GetValue(wxSPINCTRL_GETVALUE_FIX) const { return DoGetValue(); }
|
double GetValue(wxSPINCTRL_GETVALUE_FIX) const { return DoGetValue(); }
|
||||||
@@ -402,7 +396,7 @@ public:
|
|||||||
{ wxSpinCtrlGenericBase::SetValue(value); }
|
{ wxSpinCtrlGenericBase::SetValue(value); }
|
||||||
void SetValue(double value) { DoSetValue(value, SendEvent_None); }
|
void SetValue(double value) { DoSetValue(value, SendEvent_None); }
|
||||||
void SetRange(double minVal, double maxVal) { DoSetRange(minVal, maxVal); }
|
void SetRange(double minVal, double maxVal) { DoSetRange(minVal, maxVal); }
|
||||||
void SetIncrement(double inc) { DoSetIncrement(inc); }
|
void SetIncrement(double inc);
|
||||||
void SetDigits(unsigned digits);
|
void SetDigits(unsigned digits);
|
||||||
|
|
||||||
// We don't implement bases support for floating point numbers, this is not
|
// 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 bool DoTextToValue(const wxString& text, double *val) wxOVERRIDE;
|
||||||
virtual wxString DoValueToText(double val) wxOVERRIDE;
|
virtual wxString DoValueToText(double val) wxOVERRIDE;
|
||||||
virtual void ResetTextValidator() wxOVERRIDE;
|
virtual void ResetTextValidator() wxOVERRIDE;
|
||||||
void DetermineDigits(double inc);
|
|
||||||
|
|
||||||
unsigned m_digits;
|
unsigned m_digits;
|
||||||
|
|
||||||
@@ -424,10 +417,16 @@ private:
|
|||||||
// Common part of all ctors.
|
// Common part of all ctors.
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
m_digits = 0;
|
DoSetDigits(0);
|
||||||
m_format = wxASCII_STR("%0.0f");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
wxString m_format;
|
||||||
|
|
||||||
wxDECLARE_DYNAMIC_CLASS(wxSpinCtrlDouble);
|
wxDECLARE_DYNAMIC_CLASS(wxSpinCtrlDouble);
|
||||||
|
@@ -243,7 +243,7 @@ public:
|
|||||||
void SetValue(const wxString& value) wxOVERRIDE { wxSpinCtrlGTKBase::SetValue(value); } // visibility problem w/ gcc
|
void SetValue(const wxString& value) wxOVERRIDE { wxSpinCtrlGTKBase::SetValue(value); } // visibility problem w/ gcc
|
||||||
void SetValue(double value) { DoSetValue(value); }
|
void SetValue(double value) { DoSetValue(value); }
|
||||||
void SetRange(double minVal, double maxVal) { DoSetRange(minVal, maxVal); }
|
void SetRange(double minVal, double maxVal) { DoSetRange(minVal, maxVal); }
|
||||||
void SetIncrement(double inc) { DoSetIncrement(inc); }
|
void SetIncrement(double inc);
|
||||||
void SetDigits(unsigned digits);
|
void SetDigits(unsigned digits);
|
||||||
|
|
||||||
virtual int GetBase() const wxOVERRIDE { return 10; }
|
virtual int GetBase() const wxOVERRIDE { return 10; }
|
||||||
|
@@ -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.
|
// Helper function to check if given combination of range and base is valid.
|
||||||
extern bool IsBaseCompatibleWithRange(int minVal, int maxVal, int base);
|
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
|
} // namespace wxSpinCtrlImpl
|
||||||
|
|
||||||
#endif // _WX_PRIVATE_SPINCTRL_H_
|
#endif // _WX_PRIVATE_SPINCTRL_H_
|
||||||
|
@@ -354,8 +354,15 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Sets the increment value.
|
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);
|
void SetIncrement(double inc);
|
||||||
|
|
||||||
|
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#include "wx/private/spinctrl.h"
|
#include "wx/private/spinctrl.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
wxDEFINE_EVENT(wxEVT_SPINCTRL, wxSpinEvent);
|
wxDEFINE_EVENT(wxEVT_SPINCTRL, wxSpinEvent);
|
||||||
wxDEFINE_EVENT(wxEVT_SPINCTRLDOUBLE, wxSpinDoubleEvent);
|
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);
|
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<int>(inc);
|
||||||
|
if ( inc > 0.0 )
|
||||||
|
{
|
||||||
|
return wxMin(SPINCTRLDBL_MAX_DIGITS, -static_cast<int>(floor(log10(inc))));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // wxUSE_SPINCTRL
|
#endif // wxUSE_SPINCTRL
|
||||||
|
@@ -723,10 +723,26 @@ void wxSpinCtrl::ResetTextValidator()
|
|||||||
// wxSpinCtrlDouble
|
// wxSpinCtrlDouble
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#define SPINCTRLDBL_MAX_DIGITS 20
|
|
||||||
|
|
||||||
wxIMPLEMENT_DYNAMIC_CLASS(wxSpinCtrlDouble, wxSpinCtrlGenericBase);
|
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()
|
void wxSpinCtrlDouble::DoSendEvent()
|
||||||
{
|
{
|
||||||
wxSpinDoubleEvent event( wxEVT_SPINCTRLDOUBLE, GetId());
|
wxSpinDoubleEvent event( wxEVT_SPINCTRLDOUBLE, GetId());
|
||||||
@@ -746,16 +762,36 @@ wxString wxSpinCtrlDouble::DoValueToText(double val)
|
|||||||
return wxString::Format(m_format, 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)
|
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 )
|
if ( digits == m_digits )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_digits = digits;
|
DoSetDigitsAndUpdate(digits);
|
||||||
|
}
|
||||||
|
|
||||||
m_format.Printf(wxT("%%0.%ulf"), digits);
|
void wxSpinCtrlDouble::DoSetDigitsAndUpdate(unsigned digits)
|
||||||
|
{
|
||||||
|
DoSetDigits(digits);
|
||||||
|
|
||||||
ResetTextValidator();
|
ResetTextValidator();
|
||||||
m_textCtrl->InvalidateBestSize();
|
m_textCtrl->InvalidateBestSize();
|
||||||
@@ -763,6 +799,13 @@ void wxSpinCtrlDouble::SetDigits(unsigned digits)
|
|||||||
DoSetValue(m_value, SendEvent_None);
|
DoSetValue(m_value, SendEvent_None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxSpinCtrlDouble::DoSetDigits(unsigned digits)
|
||||||
|
{
|
||||||
|
m_digits = digits;
|
||||||
|
|
||||||
|
m_format.Printf(wxT("%%0.%ulf"), digits);
|
||||||
|
}
|
||||||
|
|
||||||
void wxSpinCtrlDouble::ResetTextValidator()
|
void wxSpinCtrlDouble::ResetTextValidator()
|
||||||
{
|
{
|
||||||
#if wxUSE_VALIDATORS
|
#if wxUSE_VALIDATORS
|
||||||
@@ -772,16 +815,6 @@ void wxSpinCtrlDouble::ResetTextValidator()
|
|||||||
#endif // wxUSE_VALIDATORS
|
#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<int>(floor(log10(inc))));
|
|
||||||
m_format.Printf("%%0.%ulf", m_digits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // wxUSE_SPINBTN
|
#endif // wxUSE_SPINBTN
|
||||||
|
|
||||||
#endif // !wxPort-with-native-spinctrl
|
#endif // !wxPort-with-native-spinctrl
|
||||||
|
@@ -643,6 +643,18 @@ void wxSpinCtrlDouble::GtkSetEntryWidth()
|
|||||||
gtk_entry_set_width_chars(GTK_ENTRY(m_widget), wxMax(lenMin, lenMax));
|
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
|
unsigned wxSpinCtrlDouble::GetDigits() const
|
||||||
{
|
{
|
||||||
wxCHECK_MSG( m_widget, 0, "invalid spin button" );
|
wxCHECK_MSG( m_widget, 0, "invalid spin button" );
|
||||||
|
@@ -217,18 +217,47 @@ TEST_CASE_METHOD(SpinCtrlDoubleTestCase,
|
|||||||
TEST_CASE_METHOD(SpinCtrlDoubleTestCase,
|
TEST_CASE_METHOD(SpinCtrlDoubleTestCase,
|
||||||
"SpinCtrlDouble::Digits", "[spinctrl][spinctrldouble]")
|
"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 );
|
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)
|
static inline unsigned int GetInitialDigits(double inc)
|
||||||
{
|
{
|
||||||
wxSpinCtrlDouble* sc = new wxSpinCtrlDouble(wxTheApp->GetTopWindow(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS,
|
wxScopedPtr<wxSpinCtrlDouble> sc(new wxSpinCtrlDouble
|
||||||
0, 50, 0, inc);
|
(
|
||||||
unsigned int digits = sc->GetDigits();
|
wxTheApp->GetTopWindow(),
|
||||||
delete sc;
|
wxID_ANY,
|
||||||
return digits;
|
wxEmptyString,
|
||||||
|
wxDefaultPosition,
|
||||||
|
wxDefaultSize,
|
||||||
|
wxSP_ARROW_KEYS,
|
||||||
|
0, 50, 0,
|
||||||
|
inc
|
||||||
|
));
|
||||||
|
return sc->GetDigits();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("SpinCtrlDouble::InitialDigits", "[spinctrldouble][initialdigits]")
|
TEST_CASE("SpinCtrlDouble::InitialDigits", "[spinctrldouble][initialdigits]")
|
||||||
|
Reference in New Issue
Block a user