Don't generate any events from wxSpinCtrl and wxSpinCtrlDouble methods.

After the changes of r53758 wxMSW didn't generate any wxEVT_TEXT events but
this was still the case for the generic version (and hence for
wxSpinCtrlDouble under MSW too) and wasn't documented.

Fix all versions to avoid sending events for programmatic actions, add unit
tests checking this behaviour and document it.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74631 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2013-08-06 17:00:00 +00:00
parent 36a0190ebd
commit b736d59eb5
6 changed files with 115 additions and 37 deletions

View File

@@ -117,9 +117,15 @@ protected:
virtual void DoEnable(bool enable); virtual void DoEnable(bool enable);
#endif // __WXMSW__ #endif // __WXMSW__
enum SendEvent
{
SendEvent_None,
SendEvent_Text
};
// generic double valued functions // generic double valued functions
double DoGetValue() const { return m_value; } double DoGetValue() const { return m_value; }
bool DoSetValue(double val); bool DoSetValue(double val, SendEvent sendEvent);
void DoSetRange(double min_val, double max_val); void DoSetRange(double min_val, double max_val);
void DoSetIncrement(double inc); void DoSetIncrement(double inc);
@@ -129,7 +135,7 @@ protected:
// can also change the text control if its value is invalid // can also change the text control if its value is invalid
// //
// return true if our value has changed // return true if our value has changed
bool SyncSpinToText(); bool SyncSpinToText(SendEvent sendEvent);
// Send the correct event type // Send the correct event type
virtual void DoSendEvent() = 0; virtual void DoSendEvent() = 0;
@@ -201,7 +207,7 @@ public:
bool ok = wxTextCtrl::Create(parent, id, value, pos, size, style, bool ok = wxTextCtrl::Create(parent, id, value, pos, size, style,
wxDefaultValidator, name); wxDefaultValidator, name);
DoSetValue(initial); DoSetValue(initial, SendEvent_None);
return ok; return ok;
} }
@@ -237,9 +243,20 @@ protected:
return n; return n;
} }
bool DoSetValue(double val) bool DoSetValue(double val, SendEvent sendEvent)
{ {
wxTextCtrl::SetValue(wxString::Format(m_format.c_str(), val)); wxString str(wxString::Format(m_format, val));
switch ( sendEvent )
{
case SendEvent_None:
wxTextCtrl::ChangeValue(str);
break;
case SendEvent_Text:
wxTextCtrl::SetValue(str);
break;
}
return true; return true;
} }
void DoSetRange(double min_val, double max_val) void DoSetRange(double min_val, double max_val)
@@ -305,7 +322,7 @@ public:
// operations // operations
void SetValue(const wxString& value) void SetValue(const wxString& value)
{ wxSpinCtrlGenericBase::SetValue(value); } { wxSpinCtrlGenericBase::SetValue(value); }
void SetValue( int value ) { DoSetValue(value); } void SetValue( int value ) { DoSetValue(value, SendEvent_None); }
void SetRange( int minVal, int maxVal ) { DoSetRange(minVal, maxVal); } void SetRange( int minVal, int maxVal ) { DoSetRange(minVal, maxVal); }
void SetIncrement(int inc) { DoSetIncrement(inc); } void SetIncrement(int inc) { DoSetIncrement(inc); }
@@ -381,7 +398,7 @@ public:
// operations // operations
void SetValue(const wxString& value) void SetValue(const wxString& value)
{ wxSpinCtrlGenericBase::SetValue(value); } { wxSpinCtrlGenericBase::SetValue(value); }
void SetValue(double value) { DoSetValue(value); } 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) { DoSetIncrement(inc); }
void SetDigits(unsigned digits); void SetDigits(unsigned digits);

View File

@@ -179,12 +179,20 @@ public:
virtual void SetSelection(long from, long to); virtual void SetSelection(long from, long to);
/** /**
Sets the value of the spin control. Use the variant using int instead. Sets the value of the spin control.
It is recommended to use the overload taking an integer value instead.
Notice that, unlike wxTextCtrl::SetValue(), but like most of the other
setter methods in wxWidgets, calling this method does not generate any
events as events are only generated for the user actions.
*/ */
virtual void SetValue(const wxString& text); virtual void SetValue(const wxString& text);
/** /**
Sets the value of the spin control. Sets the value of the spin control.
Calling this method doesn't generate any @c wxEVT_SPINCTRL events.
*/ */
void SetValue(int value); void SetValue(int value);
}; };
@@ -318,12 +326,20 @@ public:
void SetRange(double minVal, double maxVal); void SetRange(double minVal, double maxVal);
/** /**
Sets the value of the spin control. Use the variant using double instead. Sets the value of the spin control.
It is recommended to use the overload taking a double value instead.
Notice that, unlike wxTextCtrl::SetValue(), but like most of the other
setter methods in wxWidgets, calling this method does not generate any
events as events are only generated for the user actions.
*/ */
virtual void SetValue(const wxString& text); virtual void SetValue(const wxString& text);
/** /**
Sets the value of the spin control. Sets the value of the spin control.
Calling this method doesn't generate any @c wxEVT_SPINCTRLDOUBLE events.
*/ */
void SetValue(double value); void SetValue(double value);
}; };

View File

@@ -406,7 +406,7 @@ void wxSpinCtrlGenericBase::OnSpinButton(wxSpinEvent& event)
// Sync the textctrl since the user expects that the button will modify // Sync the textctrl since the user expects that the button will modify
// what they see in the textctrl. // what they see in the textctrl.
SyncSpinToText(); SyncSpinToText(SendEvent_None);
int spin_value = event.GetPosition(); int spin_value = event.GetPosition();
double step = (event.GetEventType() == wxEVT_SCROLL_LINEUP) ? 1 : -1; double step = (event.GetEventType() == wxEVT_SCROLL_LINEUP) ? 1 : -1;
@@ -427,13 +427,14 @@ void wxSpinCtrlGenericBase::OnSpinButton(wxSpinEvent& event)
m_spin_value = spin_value; m_spin_value = spin_value;
if ( DoSetValue(value) ) // Notify about the change in wxTextCtrl too.
if ( DoSetValue(value, SendEvent_Text) )
DoSendEvent(); DoSendEvent();
} }
void wxSpinCtrlGenericBase::OnTextLostFocus(wxFocusEvent& event) void wxSpinCtrlGenericBase::OnTextLostFocus(wxFocusEvent& event)
{ {
SyncSpinToText(); SyncSpinToText(SendEvent_Text);
DoSendEvent(); DoSendEvent();
event.Skip(); event.Skip();
@@ -473,9 +474,10 @@ void wxSpinCtrlGenericBase::OnTextChar(wxKeyEvent& event)
value = AdjustToFitInRange(value); value = AdjustToFitInRange(value);
SyncSpinToText(); SyncSpinToText(SendEvent_None);
if ( DoSetValue(value) ) // No need to send event, it was already generated by wxTextCtrl itself.
if ( DoSetValue(value, SendEvent_None) )
DoSendEvent(); DoSendEvent();
} }
@@ -483,7 +485,7 @@ void wxSpinCtrlGenericBase::OnTextChar(wxKeyEvent& event)
// Textctrl functions // Textctrl functions
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool wxSpinCtrlGenericBase::SyncSpinToText() bool wxSpinCtrlGenericBase::SyncSpinToText(SendEvent sendEvent)
{ {
if ( !m_textCtrl || !m_textCtrl->IsModified() ) if ( !m_textCtrl || !m_textCtrl->IsModified() )
return false; return false;
@@ -505,7 +507,7 @@ bool wxSpinCtrlGenericBase::SyncSpinToText()
// we must always set the value here, even if it's equal to m_value, as // we must always set the value here, even if it's equal to m_value, as
// otherwise we could be left with an out of range value when leaving the // otherwise we could be left with an out of range value when leaving the
// text control and the current value is already m_max for example // text control and the current value is already m_max for example
return DoSetValue(textValue); return DoSetValue(textValue, sendEvent);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -519,16 +521,16 @@ void wxSpinCtrlGenericBase::SetValue(const wxString& text)
double val; double val;
if ( DoTextToValue(text, &val) && InRange(val) ) if ( DoTextToValue(text, &val) && InRange(val) )
{ {
DoSetValue(val); DoSetValue(val, SendEvent_None);
} }
else // not a number at all or out of range else // not a number at all or out of range
{ {
m_textCtrl->SetValue(text); m_textCtrl->ChangeValue(text);
m_textCtrl->SelectAll(); m_textCtrl->SelectAll();
} }
} }
bool wxSpinCtrlGenericBase::DoSetValue(double val) bool wxSpinCtrlGenericBase::DoSetValue(double val, SendEvent sendEvent)
{ {
wxCHECK_MSG( m_textCtrl, false, wxT("invalid call to wxSpinCtrl::SetValue") ); wxCHECK_MSG( m_textCtrl, false, wxT("invalid call to wxSpinCtrl::SetValue") );
@@ -556,7 +558,18 @@ bool wxSpinCtrlGenericBase::DoSetValue(double val)
{ {
if ( !DoTextToValue(str, &m_value ) ) // wysiwyg for textctrl if ( !DoTextToValue(str, &m_value ) ) // wysiwyg for textctrl
m_value = val; m_value = val;
m_textCtrl->SetValue( str );
switch ( sendEvent )
{
case SendEvent_None:
m_textCtrl->ChangeValue(str);
break;
case SendEvent_Text:
m_textCtrl->SetValue(str);
break;
}
m_textCtrl->SelectAll(); m_textCtrl->SelectAll();
m_textCtrl->DiscardEdits(); m_textCtrl->DiscardEdits();
return true; return true;
@@ -579,10 +592,10 @@ void wxSpinCtrlGenericBase::DoSetRange(double min, double max)
{ {
m_min = min; m_min = min;
if ( m_value < m_min ) if ( m_value < m_min )
DoSetValue(m_min); DoSetValue(m_min, SendEvent_None);
m_max = max; m_max = max;
if ( m_value > m_max ) if ( m_value > m_max )
DoSetValue(m_max); DoSetValue(m_max, SendEvent_None);
} }
void wxSpinCtrlGenericBase::DoSetIncrement(double inc) void wxSpinCtrlGenericBase::DoSetIncrement(double inc)
@@ -593,7 +606,7 @@ void wxSpinCtrlGenericBase::DoSetIncrement(double inc)
void wxSpinCtrlGenericBase::SetSnapToTicks(bool snap_to_ticks) void wxSpinCtrlGenericBase::SetSnapToTicks(bool snap_to_ticks)
{ {
m_snap_to_ticks = snap_to_ticks; m_snap_to_ticks = snap_to_ticks;
DoSetValue(m_value); DoSetValue(m_value, SendEvent_None);
} }
void wxSpinCtrlGenericBase::SetSelection(long from, long to) void wxSpinCtrlGenericBase::SetSelection(long from, long to)
@@ -628,7 +641,7 @@ bool wxSpinCtrl::SetBase(int base)
// ... but DoValueToText() after doing it. // ... but DoValueToText() after doing it.
if ( hasValidVal ) if ( hasValidVal )
m_textCtrl->SetValue(DoValueToText(val)); m_textCtrl->ChangeValue(DoValueToText(val));
return true; return true;
} }
@@ -708,7 +721,7 @@ void wxSpinCtrlDouble::SetDigits(unsigned digits)
m_format.Printf(wxT("%%0.%ulf"), digits); m_format.Printf(wxT("%%0.%ulf"), digits);
DoSetValue(m_value); DoSetValue(m_value, SendEvent_None);
} }
#endif // wxUSE_SPINBTN #endif // wxUSE_SPINBTN

View File

@@ -471,7 +471,9 @@ void wxSpinCtrlDouble::SetDigits(unsigned digits)
{ {
wxCHECK_RET( m_widget, "invalid spin button" ); wxCHECK_RET( m_widget, "invalid spin button" );
GtkDisableEvents();
gtk_spin_button_set_digits( GTK_SPIN_BUTTON(m_widget), digits); gtk_spin_button_set_digits( GTK_SPIN_BUTTON(m_widget), digits);
GtkEnableEvents();
} }
#endif // wxUSE_SPINCTRL #endif // wxUSE_SPINCTRL

View File

@@ -76,13 +76,15 @@ void SpinCtrlDoubleTestCase::NoEventsInCtor()
delete m_spin; delete m_spin;
m_spin = new wxSpinCtrlDouble; m_spin = new wxSpinCtrlDouble;
EventCounter updated(m_spin, wxEVT_SPINCTRLDOUBLE); EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE);
EventCounter updatedText(m_spin, wxEVT_TEXT);
m_spin->Create(parent, wxID_ANY, "", m_spin->Create(parent, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0, wxDefaultPosition, wxDefaultSize, 0,
0., 100., 17.); 0., 100., 17.);
CPPUNIT_ASSERT_EQUAL(0, updated.GetCount()); CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount());
CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount());
} }
void SpinCtrlDoubleTestCase::Arrows() void SpinCtrlDoubleTestCase::Arrows()
@@ -141,7 +143,21 @@ void SpinCtrlDoubleTestCase::Range()
CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetMin()); CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetMin());
CPPUNIT_ASSERT_EQUAL(100.0, m_spin->GetMax()); CPPUNIT_ASSERT_EQUAL(100.0, m_spin->GetMax());
//Test neagtive ranges // Test that the value is adjusted to be inside the new valid range but
// that this doesn't result in any events (as this is not something done by
// the user).
{
EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE);
EventCounter updatedText(m_spin, wxEVT_TEXT);
m_spin->SetRange(1., 10.);
CPPUNIT_ASSERT_EQUAL(1., m_spin->GetValue());
CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount());
CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount());
}
//Test negative ranges
m_spin->SetRange(-10.0, 10.0); m_spin->SetRange(-10.0, 10.0);
CPPUNIT_ASSERT_EQUAL(-10.0, m_spin->GetMin()); CPPUNIT_ASSERT_EQUAL(-10.0, m_spin->GetMin());
@@ -156,18 +172,23 @@ void SpinCtrlDoubleTestCase::Range()
void SpinCtrlDoubleTestCase::Value() void SpinCtrlDoubleTestCase::Value()
{ {
EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE);
EventCounter updatedText(m_spin, wxEVT_TEXT);
m_spin->SetDigits(2); m_spin->SetDigits(2);
m_spin->SetIncrement(0.1); m_spin->SetIncrement(0.1);
CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetValue());
m_spin->SetValue(50.0); m_spin->SetValue(50.0);
CPPUNIT_ASSERT_EQUAL(50.0, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(50.0, m_spin->GetValue());
m_spin->SetValue(49.1); m_spin->SetValue(49.1);
CPPUNIT_ASSERT_EQUAL(49.1, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(49.1, m_spin->GetValue());
// Calling SetValue() shouldn't have generated any events.
CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount());
CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount());
} }
void SpinCtrlDoubleTestCase::Increment() void SpinCtrlDoubleTestCase::Increment()

View File

@@ -106,13 +106,15 @@ void SpinCtrlTestCase::NoEventsInCtor()
delete m_spin; delete m_spin;
m_spin = new wxSpinCtrl; m_spin = new wxSpinCtrl;
EventCounter updated(m_spin, wxEVT_SPINCTRL); EventCounter updatedSpin(m_spin, wxEVT_SPINCTRL);
EventCounter updatedText(m_spin, wxEVT_TEXT);
m_spin->Create(parent, wxID_ANY, "", m_spin->Create(parent, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0, wxDefaultPosition, wxDefaultSize, 0,
0, 100, 17); 0, 100, 17);
CPPUNIT_ASSERT_EQUAL(0, updated.GetCount()); CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount());
CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount());
} }
void SpinCtrlTestCase::Arrows() void SpinCtrlTestCase::Arrows()
@@ -176,11 +178,14 @@ void SpinCtrlTestCase::Range()
// that this doesn't result in any events (as this is not something done by // that this doesn't result in any events (as this is not something done by
// the user). // the user).
{ {
EventCounter updated(m_spin, wxEVT_SPINCTRL); EventCounter updatedSpin(m_spin, wxEVT_SPINCTRL);
EventCounter updatedText(m_spin, wxEVT_TEXT);
m_spin->SetRange(1, 10); m_spin->SetRange(1, 10);
CPPUNIT_ASSERT_EQUAL(1, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(1, m_spin->GetValue());
CPPUNIT_ASSERT_EQUAL(0, updated.GetCount());
CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount());
CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount());
} }
//Test negative ranges //Test negative ranges
@@ -198,19 +203,23 @@ void SpinCtrlTestCase::Range()
void SpinCtrlTestCase::Value() void SpinCtrlTestCase::Value()
{ {
EventCounter updatedSpin(m_spin, wxEVT_SPINCTRL);
EventCounter updatedText(m_spin, wxEVT_TEXT);
CPPUNIT_ASSERT_EQUAL(0, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(0, m_spin->GetValue());
m_spin->SetValue(50); m_spin->SetValue(50);
CPPUNIT_ASSERT_EQUAL(50, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(50, m_spin->GetValue());
m_spin->SetValue(-10); m_spin->SetValue(-10);
CPPUNIT_ASSERT_EQUAL(0, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(0, m_spin->GetValue());
m_spin->SetValue(110); m_spin->SetValue(110);
CPPUNIT_ASSERT_EQUAL(100, m_spin->GetValue()); CPPUNIT_ASSERT_EQUAL(100, m_spin->GetValue());
// Calling SetValue() shouldn't have generated any events.
CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount());
CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount());
} }
#endif #endif