diff --git a/include/wx/generic/spinctlg.h b/include/wx/generic/spinctlg.h index bcb589ef15..8f3e5ecc92 100644 --- a/include/wx/generic/spinctlg.h +++ b/include/wx/generic/spinctlg.h @@ -60,6 +60,7 @@ public: virtual ~wxSpinCtrlGenericBase(); // accessors + virtual wxString GetTextValue() const wxOVERRIDE; // T GetValue() const // T GetMin() const // T GetMax() const diff --git a/include/wx/gtk/spinctrl.h b/include/wx/gtk/spinctrl.h index 0db4cb0b39..267bd94de5 100644 --- a/include/wx/gtk/spinctrl.h +++ b/include/wx/gtk/spinctrl.h @@ -35,6 +35,7 @@ public: // wxSpinCtrl(Double) methods call DoXXX functions of the same name // accessors + virtual wxString GetTextValue() const wxOVERRIDE; // T GetValue() const // T GetMin() const // T GetMax() const @@ -57,7 +58,26 @@ public: // implementation void OnChar( wxKeyEvent &event ); + + // These values map to the possible return values of "input" GTK signal but + // are more readable and type-safe. + enum GTKInputResult + { + GTKInput_Error = -1, + GTKInput_Default, + GTKInput_Converted + }; + + virtual GTKInputResult GTKInput(double* value) const = 0; + virtual bool GTKOutput(wxString* text) const = 0; + + virtual void GTKValueChanged() = 0; + void GTKTextChanged(); + protected: + wxSpinCtrlGTKBase(); + ~wxSpinCtrlGTKBase(); + double DoGetValue() const; double DoGetMin() const; double DoGetMax() const; @@ -82,6 +102,26 @@ protected: // override this and return true. virtual bool UseGTKStyleBase() const wxOVERRIDE { return true; } + // Set m_textOverride to use the given text instead of the numeric value. + void GTKSetTextOverride(const wxString& text); + + // Reset the override and changing the value to correspond to the + // previously overridden numeric value. + void GTKResetTextOverride(); + + // Just reset the override, without touching the value, returning true if + // we did it. In most cases, the function above should be used instead. + bool GTKResetTextOverrideOnly(); + +private: + // This function does _not_ take into account m_textOverride, so it is + // private and normally shouldn't be used -- use DoGetValue() instead. + double GTKGetValue() const; + + // Non-null when the text value is different from the numeric value. + class wxSpinCtrlGTKTextOverride* m_textOverride; + + friend class wxSpinCtrlEventDisabler; wxDECLARE_EVENT_TABLE(); @@ -137,6 +177,10 @@ public: virtual int GetBase() const wxOVERRIDE { return m_base; } virtual bool SetBase(int base) wxOVERRIDE; + virtual GTKInputResult GTKInput(double* value) const wxOVERRIDE; + virtual bool GTKOutput(wxString* text) const wxOVERRIDE; + virtual void GTKValueChanged() wxOVERRIDE; + protected: virtual void GtkSetEntryWidth() wxOVERRIDE; @@ -205,6 +249,10 @@ public: virtual int GetBase() const wxOVERRIDE { return 10; } virtual bool SetBase(int WXUNUSED(base)) wxOVERRIDE { return false; } + virtual GTKInputResult GTKInput(double* value) const wxOVERRIDE; + virtual bool GTKOutput(wxString* text) const wxOVERRIDE; + virtual void GTKValueChanged() wxOVERRIDE; + protected: virtual void GtkSetEntryWidth() wxOVERRIDE; diff --git a/include/wx/gtk1/spinctrl.h b/include/wx/gtk1/spinctrl.h index 45a7473ca0..f037be2ebe 100644 --- a/include/wx/gtk1/spinctrl.h +++ b/include/wx/gtk1/spinctrl.h @@ -48,6 +48,7 @@ public: void SetValue(const wxString& text); void SetSelection(long from, long to); + virtual wxString GetTextValue() const; virtual int GetValue() const; virtual void SetValue( int value ); virtual void SetRange( int minVal, int maxVal ); diff --git a/include/wx/msw/spinctrl.h b/include/wx/msw/spinctrl.h index 491d9d99bd..b222089165 100644 --- a/include/wx/msw/spinctrl.h +++ b/include/wx/msw/spinctrl.h @@ -62,6 +62,7 @@ public: void SetSelection(long from, long to); // wxSpinCtrlBase methods + virtual wxString GetTextValue() const; virtual int GetBase() const; virtual bool SetBase(int base); diff --git a/include/wx/qt/spinctrl.h b/include/wx/qt/spinctrl.h index 9355ff9fc6..c6f0ca5954 100644 --- a/include/wx/qt/spinctrl.h +++ b/include/wx/qt/spinctrl.h @@ -29,6 +29,7 @@ public: T min, T max, T initial, T inc, const wxString& name ); + virtual wxString GetTextValue() const wxOVERRIDE; virtual void SetValue(const wxString&) wxOVERRIDE {} virtual void SetSnapToTicks(bool snap_to_ticks) wxOVERRIDE; diff --git a/include/wx/spinctrl.h b/include/wx/spinctrl.h index 775672f97b..42d2af59c4 100644 --- a/include/wx/spinctrl.h +++ b/include/wx/spinctrl.h @@ -35,6 +35,7 @@ public: wxSpinCtrlBase() {} // accessor functions that derived classes are expected to have + virtual wxString GetTextValue() const = 0; // T GetValue() const // T GetMin() const // T GetMax() const diff --git a/interface/wx/spinctrl.h b/interface/wx/spinctrl.h index 98cc2bd189..55353ef86d 100644 --- a/interface/wx/spinctrl.h +++ b/interface/wx/spinctrl.h @@ -133,6 +133,13 @@ public: */ int GetMin() const; + /** + Returns the text in the text entry part of the control. + + @since 3.1.6 + */ + wxString GetTextValue() const; + /** Gets the value of the spin control. */ @@ -190,6 +197,10 @@ public: Sets the value of the spin control. It is recommended to use the overload taking an integer value instead. + The behaviour of this function when @a text doesn't represent a valid + number currently differs between the platforms, however passing an + empty string does clear the text part contents, without affecting the + value returned by GetValue(), under all of them. Notice that, unlike wxTextCtrl::SetValue(), but like most of the other setter methods in wxWidgets, calling this method does not generate any @@ -243,6 +254,14 @@ public: /** Constructor, creating and showing a spin control. + If @a value is non-empty, it will be shown in the text entry part of + the control and if it has numeric value, the initial numeric value of + the control, as returned by GetValue() will also be determined by it + instead of by @a initial. Hence, it only makes sense to specify @a + initial if @a value is an empty string or is not convertible to a + number, otherwise @a initial is simply ignored and the number specified + by @a value is used. + @param parent Parent window. Must not be @NULL. @param value @@ -315,6 +334,13 @@ public: */ double GetMin() const; + /** + Returns the text in the text entry part of the control. + + @since 3.1.6 + */ + wxString GetTextValue() const; + /** Gets the value of the spin control. */ @@ -342,6 +368,10 @@ public: Sets the value of the spin control. It is recommended to use the overload taking a double value instead. + The behaviour of this function when @a text doesn't represent a valid + number currently differs between the platforms, however + passing an empty string does clear the text part contents, without + affecting the value returned by GetValue(), under all of them. Notice that, unlike wxTextCtrl::SetValue(), but like most of the other setter methods in wxWidgets, calling this method does not generate any diff --git a/samples/widgets/spinbtn.cpp b/samples/widgets/spinbtn.cpp index 70006a3a21..609fc36ada 100644 --- a/samples/widgets/spinbtn.cpp +++ b/samples/widgets/spinbtn.cpp @@ -483,6 +483,14 @@ void SpinBtnWidgetsPage::OnButtonSetBase(wxCommandEvent& WXUNUSED(event)) void SpinBtnWidgetsPage::OnButtonSetValue(wxCommandEvent& WXUNUSED(event)) { + if ( m_textValue->IsEmpty() ) + { + m_spinctrl->SetValue( wxEmptyString ); + m_spinctrldbl->SetValue( wxEmptyString ); + + return; + } + long val; if ( !m_textValue->GetValue().ToLong(&val) || !IsValidValue(val) ) { @@ -499,7 +507,8 @@ void SpinBtnWidgetsPage::OnButtonSetValue(wxCommandEvent& WXUNUSED(event)) void SpinBtnWidgetsPage::OnUpdateUIValueButton(wxUpdateUIEvent& event) { long val; - event.Enable( m_textValue->GetValue().ToLong(&val) && IsValidValue(val) ); + event.Enable( m_textValue->IsEmpty() || + ( m_textValue->GetValue().ToLong(&val) && IsValidValue(val) ) ); } void SpinBtnWidgetsPage::OnUpdateUIMinMaxButton(wxUpdateUIEvent& event) diff --git a/src/generic/spinctlg.cpp b/src/generic/spinctlg.cpp index 28393d38fa..aa99410c35 100644 --- a/src/generic/spinctlg.cpp +++ b/src/generic/spinctlg.cpp @@ -506,6 +506,11 @@ bool wxSpinCtrlGenericBase::SyncSpinToText(SendEvent sendEvent) // changing value and range // ---------------------------------------------------------------------------- +wxString wxSpinCtrlGenericBase::GetTextValue() const +{ + return m_textCtrl ? m_textCtrl->GetValue() : wxString(); +} + void wxSpinCtrlGenericBase::SetValue(const wxString& text) { wxCHECK_RET( m_textCtrl, wxT("invalid call to wxSpinCtrl::SetValue") ); diff --git a/src/gtk/spinctrl.cpp b/src/gtk/spinctrl.cpp index 14793ec74c..b9f72eda28 100644 --- a/src/gtk/spinctrl.cpp +++ b/src/gtk/spinctrl.cpp @@ -37,27 +37,12 @@ extern bool g_blockEventsOnDrag; extern "C" { static void -gtk_value_changed(GtkSpinButton* spinbutton, wxSpinCtrlGTKBase* win) +gtk_value_changed(GtkSpinButton*, wxSpinCtrlGTKBase* win) { if (g_blockEventsOnDrag) return; - if (wxIsKindOf(win, wxSpinCtrl)) - { - wxSpinEvent event(wxEVT_SPINCTRL, win->GetId()); - event.SetEventObject( win ); - event.SetPosition(static_cast(win)->GetValue()); - event.SetString(gtk_entry_get_text(GTK_ENTRY(spinbutton))); - win->HandleWindowEvent( event ); - } - else // wxIsKindOf(win, wxSpinCtrlDouble) - { - wxSpinDoubleEvent event( wxEVT_SPINCTRLDOUBLE, win->GetId()); - event.SetEventObject( win ); - event.SetValue(static_cast(win)->GetValue()); - event.SetString(gtk_entry_get_text(GTK_ENTRY(spinbutton))); - win->HandleWindowEvent( event ); - } + win->GTKValueChanged(); } } @@ -67,16 +52,53 @@ gtk_value_changed(GtkSpinButton* spinbutton, wxSpinCtrlGTKBase* win) extern "C" { static void -gtk_changed(GtkSpinButton* spinbutton, wxSpinCtrl* win) +gtk_changed(GtkSpinButton*, wxSpinCtrl* win) { - wxCommandEvent event( wxEVT_TEXT, win->GetId() ); - event.SetEventObject( win ); - event.SetString(gtk_entry_get_text(GTK_ENTRY(spinbutton))); - event.SetInt(win->GetValue()); - win->HandleWindowEvent( event ); + win->GTKTextChanged(); } } +//----------------------------------------------------------------------------- +// "input" and "output" +//----------------------------------------------------------------------------- + +extern "C" +{ + +static gint +wx_gtk_spin_input(GtkSpinButton*, gdouble* val, wxSpinCtrlGTKBase* win) +{ + switch ( win->GTKInput(val) ) + { + case wxSpinCtrl::GTKInput_Error: + return GTK_INPUT_ERROR; + + case wxSpinCtrl::GTKInput_Default: + return FALSE; + + case wxSpinCtrl::GTKInput_Converted: + return TRUE; + } + + wxFAIL_MSG("unreachable"); + return FALSE; +} + +static gboolean +wx_gtk_spin_output(GtkSpinButton* spin, wxSpinCtrlGTKBase* win) +{ + wxString text; + if ( !win->GTKOutput(&text) ) + return FALSE; + + if ( text != win->GetTextValue() ) + gtk_entry_set_text(GTK_ENTRY(spin), text.utf8_str()); + + return TRUE; +} + +} // extern "C" + // ---------------------------------------------------------------------------- // wxSpinCtrlEventDisabler: helper to temporarily disable GTK+ events // ---------------------------------------------------------------------------- @@ -101,6 +123,22 @@ private: wxDECLARE_NO_COPY_CLASS(wxSpinCtrlEventDisabler); }; +// ---------------------------------------------------------------------------- +// wxSpinCtrlGTKTextOverride: extra data for using a separate string value +// ---------------------------------------------------------------------------- + +class wxSpinCtrlGTKTextOverride +{ +public: + wxSpinCtrlGTKTextOverride() + : m_value(0.0) + { + } + + wxString m_text; + double m_value; +}; + //----------------------------------------------------------------------------- // wxSpinCtrlGTKBase //----------------------------------------------------------------------------- @@ -109,6 +147,62 @@ wxBEGIN_EVENT_TABLE(wxSpinCtrlGTKBase, wxSpinCtrlBase) EVT_CHAR(wxSpinCtrlGTKBase::OnChar) wxEND_EVENT_TABLE() +wxSpinCtrlGTKBase::wxSpinCtrlGTKBase() + : m_textOverride(NULL) +{ +} + +wxSpinCtrlGTKBase::~wxSpinCtrlGTKBase() +{ + delete m_textOverride; +} + +void wxSpinCtrlGTKBase::GTKSetTextOverride(const wxString& text) +{ + if ( !m_textOverride ) + { + // Remember the original numeric value, that we're going to keep using + // it while this override is valid, and do it before initializing + // m_textOverride as our own "input" handler called from GTKGetValue() + // would use it if it were non-null. + const double value = GTKGetValue(); + + m_textOverride = new wxSpinCtrlGTKTextOverride(); + m_textOverride->m_value = value; + } + //else: No need to change the value, it stays the same anyhow. + + // Update the text in any case. + m_textOverride->m_text = text; +} + +bool wxSpinCtrlGTKBase::GTKResetTextOverrideOnly() +{ + if ( !m_textOverride ) + return false; + + delete m_textOverride; + m_textOverride = NULL; + + return true; +} + +void wxSpinCtrlGTKBase::GTKResetTextOverride() +{ + if ( !GTKResetTextOverrideOnly() ) + return; + + // Update the text part to reflect the numeric value now that we don't + // override it any longer, otherwise we'd keep showing the old one because + // the text is updated by GTK before "value" is generated. + wxSpinCtrlEventDisabler disable(this); + gtk_spin_button_set_value + ( + GTK_SPIN_BUTTON(m_widget), + gtk_spin_button_get_value(GTK_SPIN_BUTTON(m_widget)) + ); +} + bool wxSpinCtrlGTKBase::Create(wxWindow *parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, @@ -146,6 +240,9 @@ bool wxSpinCtrlGTKBase::Create(wxWindow *parent, wxWindowID id, g_signal_connect_after(m_widget, "value_changed", G_CALLBACK(gtk_value_changed), this); g_signal_connect_after(m_widget, "changed", G_CALLBACK(gtk_changed), this); + g_signal_connect(m_widget, "input", G_CALLBACK(wx_gtk_spin_input), this); + g_signal_connect(m_widget, "output", G_CALLBACK(wx_gtk_spin_output), this); + m_parent->DoAddChild( this ); PostCreation(size); @@ -159,6 +256,15 @@ bool wxSpinCtrlGTKBase::Create(wxWindow *parent, wxWindowID id, } double wxSpinCtrlGTKBase::DoGetValue() const +{ + // While using a text override, the text value is fixed by the program and + // shouldn't be used, just return the numeric value we had had before, as + // the text override is reset whenever it changes, so it must not have + // changed yet. + return m_textOverride ? m_textOverride->m_value : GTKGetValue(); +} + +double wxSpinCtrlGTKBase::GTKGetValue() const { wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") ); @@ -214,6 +320,13 @@ double wxSpinCtrlGTKBase::DoGetIncrement() const return inc; } +wxString wxSpinCtrlGTKBase::GetTextValue() const +{ + wxCHECK_MSG(m_widget, wxEmptyString, "invalid spin button"); + + return wxGTK_CONV_BACK(gtk_entry_get_text( GTK_ENTRY(m_widget) )); +} + bool wxSpinCtrlGTKBase::GetSnapToTicks() const { wxCHECK_MSG(m_widget, false, "invalid spin button"); @@ -233,7 +346,12 @@ void wxSpinCtrlGTKBase::SetValue( const wxString& value ) return; } - // invalid number - set text as is (wxMSW compatible) + // invalid number - set text as is (wxMSW compatible) and remember that it + // is set to avoid overwriting it later, which is notably important when + // we're called before the window is realized as the default "realize" + // handler will call our "output" handler + GTKSetTextOverride(value); + wxSpinCtrlEventDisabler disable(this); gtk_entry_set_text( GTK_ENTRY(m_widget), wxGTK_CONV( value ) ); } @@ -242,6 +360,8 @@ void wxSpinCtrlGTKBase::DoSetValue( double value ) { wxCHECK_RET( (m_widget != NULL), wxT("invalid spin button") ); + GTKResetTextOverride(); + wxSpinCtrlEventDisabler disable(this); gtk_spin_button_set_value( GTK_SPIN_BUTTON(m_widget), value); } @@ -336,9 +456,7 @@ void wxSpinCtrlGTKBase::OnChar( wxKeyEvent &event ) { wxCommandEvent evt( wxEVT_TEXT_ENTER, m_windowId ); evt.SetEventObject(this); - GtkSpinButton *gsb = GTK_SPIN_BUTTON(m_widget); - wxString val = wxGTK_CONV_BACK( gtk_entry_get_text( &gsb->entry ) ); - evt.SetString( val ); + evt.SetString(GetTextValue()); if (HandleWindowEvent(evt)) return; } @@ -390,46 +508,46 @@ wxSpinCtrlGTKBase::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) return GetDefaultAttributesFromGTKWidget(gtk_spin_button_new_with_range(0, 100, 1), true); } +wxSpinCtrlGTKBase::GTKInputResult wxSpinCtrlGTKBase::GTKInput(double* value) const +{ + if ( m_textOverride ) + { + *value = m_textOverride->m_value; + return GTKInput_Converted; + } + + return GTKInput_Default; +} + +bool wxSpinCtrlGTKBase::GTKOutput(wxString* text) const +{ + if ( m_textOverride ) + { + *text = m_textOverride->m_text; + return true; + } + + return false; +} + +void wxSpinCtrlGTKBase::GTKTextChanged() +{ + // We can't use GTKResetTextOverride() itself here because it would also + // reset the value and we do not want this to happen -- the value is being + // changed to correspond to the new text. + GTKResetTextOverrideOnly(); + + wxCommandEvent event( wxEVT_TEXT, GetId() ); + event.SetEventObject( this ); + event.SetString(GetTextValue()); + event.SetInt(static_cast(DoGetValue())); + HandleWindowEvent( event ); +} + //----------------------------------------------------------------------------- // wxSpinCtrl //----------------------------------------------------------------------------- -extern "C" -{ - -static gboolean -wx_gtk_spin_input(GtkSpinButton* spin, gdouble* val, wxSpinCtrl* win) -{ - // We might use g_ascii_strtoll() here but it's 2.12+ only, so use our own - // wxString function even if this requires an extra conversion. - const wxString - text(wxString::FromUTF8(gtk_entry_get_text(GTK_ENTRY(spin)))); - - long lval; - if ( !text.ToLong(&lval, win->GetBase()) ) - return FALSE; - - *val = lval; - - return TRUE; -} - -static gint -wx_gtk_spin_output(GtkSpinButton* spin, wxSpinCtrl* win) -{ - const gint val = gtk_spin_button_get_value_as_int(spin); - - gtk_entry_set_text - ( - GTK_ENTRY(spin), - wxSpinCtrlImpl::FormatAsHex(val, win->GetMax()).utf8_str() - ); - - return TRUE; -} - -} // extern "C" - void wxSpinCtrl::GtkSetEntryWidth() { const int minVal = static_cast(DoGetMin()); @@ -462,23 +580,6 @@ bool wxSpinCtrl::SetBase(int base) // We need to be able to enter letters for any base greater than 10. gtk_spin_button_set_numeric( GTK_SPIN_BUTTON(m_widget), m_base <= 10 ); - if ( m_base != 10 ) - { - g_signal_connect( m_widget, "input", - G_CALLBACK(wx_gtk_spin_input), this); - g_signal_connect( m_widget, "output", - G_CALLBACK(wx_gtk_spin_output), this); - } - else - { - g_signal_handlers_disconnect_by_func(m_widget, - (gpointer)wx_gtk_spin_input, - this); - g_signal_handlers_disconnect_by_func(m_widget, - (gpointer)wx_gtk_spin_output, - this); - } - InvalidateBestSize(); GtkSetEntryWidth(); @@ -489,6 +590,61 @@ bool wxSpinCtrl::SetBase(int base) return true; } +wxSpinCtrl::GTKInputResult wxSpinCtrl::GTKInput(double* value) const +{ + GTKInputResult res = wxSpinCtrlGTKBase::GTKInput(value); + if ( res != GTKInput_Default ) + return res; + + // Don't override the default logic unless really needed. + if ( GetBase() == 10 ) + return GTKInput_Default; + + long lval; + if ( !GetTextValue().ToLong(&lval, GetBase()) ) + return GTKInput_Error; + + *value = lval; + + return GTKInput_Converted; +} + +bool wxSpinCtrl::GTKOutput(wxString* text) const +{ + if ( wxSpinCtrlGTKBase::GTKOutput(text) ) + return true; + + switch ( GetBase() ) + { + default: + wxFAIL_MSG("unsupported base"); + wxFALLTHROUGH; + + case 10: + // Don't override the default output format unless really needed. + return false; + + case 16: + const gint val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(m_widget)); + + *text = wxSpinCtrlImpl::FormatAsHex(val, GetMax()); + break; + } + + return true; +} + +void wxSpinCtrl::GTKValueChanged() +{ + GTKResetTextOverride(); + + wxSpinEvent event(wxEVT_SPINCTRL, GetId()); + event.SetEventObject( this ); + event.SetPosition(GetValue()); + event.SetString(GetTextValue()); + HandleWindowEvent( event ); +} + //----------------------------------------------------------------------------- // wxSpinCtrlDouble //----------------------------------------------------------------------------- @@ -523,4 +679,28 @@ void wxSpinCtrlDouble::SetDigits(unsigned digits) GtkSetEntryWidth(); } +wxSpinCtrl::GTKInputResult wxSpinCtrlDouble::GTKInput(double* value) const +{ + return wxSpinCtrlGTKBase::GTKInput(value); +} + +bool wxSpinCtrlDouble::GTKOutput(wxString* text) const +{ + if ( wxSpinCtrlGTKBase::GTKOutput(text) ) + return true; + + return false; +} + +void wxSpinCtrlDouble::GTKValueChanged() +{ + GTKResetTextOverride(); + + wxSpinDoubleEvent event( wxEVT_SPINCTRLDOUBLE, GetId()); + event.SetEventObject( this ); + event.SetValue(GetValue()); + event.SetString(GetTextValue()); + HandleWindowEvent( event ); +} + #endif // wxUSE_SPINCTRL diff --git a/src/gtk1/spinctrl.cpp b/src/gtk1/spinctrl.cpp index 98934ec791..0483b21d33 100644 --- a/src/gtk1/spinctrl.cpp +++ b/src/gtk1/spinctrl.cpp @@ -203,6 +203,13 @@ int wxSpinCtrl::GetMax() const return (int)ceil(m_adjust->upper); } +wxString wxSpinCtrl::GetTextValue() const +{ + wxCHECK_MSG(m_widget, wxEmptyString, "invalid spin button"); + + return gtk_entry_get_text( GTK_ENTRY(m_widget) ); +} + int wxSpinCtrl::GetValue() const { wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") ); diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index f4c60ba158..9cad282bcc 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -442,6 +442,11 @@ bool wxSpinCtrl::SetBase(int base) // wxTextCtrl-like methods // ---------------------------------------------------------------------------- +wxString wxSpinCtrl::GetTextValue() const +{ + return wxGetWindowText(m_hwndBuddy); +} + void wxSpinCtrl::SetValue(const wxString& text) { if ( !::SetWindowText(GetBuddyHwnd(), text.c_str()) ) diff --git a/src/qt/spinctrl.cpp b/src/qt/spinctrl.cpp index 1e67adc6af..d1c4b628f4 100644 --- a/src/qt/spinctrl.cpp +++ b/src/qt/spinctrl.cpp @@ -58,6 +58,12 @@ bool wxSpinCtrlQt< T, Widget >::Create( wxWindow *parent, wxWindowID id, return QtCreateControl( parent, id, pos, size, style, wxDefaultValidator, name ); } +template< typename T, typename Widget > +wxString wxSpinCtrlQt< T, Widget >::GetTextValue() const +{ + return wxQtConvertString(m_qtSpinBox->text()); +} + template< typename T, typename Widget > void wxSpinCtrlQt< T, Widget >::SetValue( T val ) { diff --git a/tests/controls/spinctrldbltest.cpp b/tests/controls/spinctrldbltest.cpp index 77668b75ed..890659b298 100644 --- a/tests/controls/spinctrldbltest.cpp +++ b/tests/controls/spinctrldbltest.cpp @@ -19,76 +19,59 @@ #include "wx/uiaction.h" #include "wx/spinctrl.h" -class SpinCtrlDoubleTestCase : public CppUnit::TestCase +class SpinCtrlDoubleTestCase { public: - SpinCtrlDoubleTestCase() { } + SpinCtrlDoubleTestCase(int style = wxSP_ARROW_KEYS) + : m_spin(new wxSpinCtrlDouble(wxTheApp->GetTopWindow(), wxID_ANY, "", + wxDefaultPosition, wxDefaultSize, + style)) + { + } - void setUp() wxOVERRIDE; - void tearDown() wxOVERRIDE; + ~SpinCtrlDoubleTestCase() + { + delete m_spin; + } -private: - CPPUNIT_TEST_SUITE( SpinCtrlDoubleTestCase ); - CPPUNIT_TEST( NoEventsInCtor ); - WXUISIM_TEST( Arrows ); - WXUISIM_TEST( Wrap ); - CPPUNIT_TEST( Range ); - CPPUNIT_TEST( Value ); - WXUISIM_TEST( Increment ); - CPPUNIT_TEST( Digits ); - CPPUNIT_TEST_SUITE_END(); - - void NoEventsInCtor(); - void Arrows(); - void Wrap(); - void Range(); - void Value(); - void Increment(); - void Digits(); - - wxSpinCtrlDouble* m_spin; +protected: + wxSpinCtrlDouble* const m_spin; wxDECLARE_NO_COPY_CLASS(SpinCtrlDoubleTestCase); }; -// register in the unnamed registry so that these tests are run by default -CPPUNIT_TEST_SUITE_REGISTRATION( SpinCtrlDoubleTestCase ); - -// also include in its own registry so that these tests can be run alone -CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( SpinCtrlDoubleTestCase, "SpinCtrlDoubleTestCase" ); - -void SpinCtrlDoubleTestCase::setUp() +class SpinCtrlDoubleTestCaseWrap : public SpinCtrlDoubleTestCase { - m_spin = new wxSpinCtrlDouble(wxTheApp->GetTopWindow()); -} +public: + SpinCtrlDoubleTestCaseWrap() + : SpinCtrlDoubleTestCase(wxSP_ARROW_KEYS | wxSP_WRAP) + { + } +}; -void SpinCtrlDoubleTestCase::tearDown() -{ - wxDELETE(m_spin); -} -void SpinCtrlDoubleTestCase::NoEventsInCtor() +TEST_CASE("SpinCtrlDouble::NoEventsInCtor", "[spinctrl][spinctrldouble]") { // Verify that creating the control does not generate any events. This is // unexpected and shouldn't happen. - wxWindow* const parent = m_spin->GetParent(); - delete m_spin; - m_spin = new wxSpinCtrlDouble; + wxSpinCtrlDouble *m_spin = new wxSpinCtrlDouble; EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE); EventCounter updatedText(m_spin, wxEVT_TEXT); - m_spin->Create(parent, wxID_ANY, "", + m_spin->Create(wxTheApp->GetTopWindow(), wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, 0., 100., 17.); - CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); - CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); + CHECK( updatedSpin.GetCount() == 0 ); + CHECK( updatedText.GetCount() == 0 ); } -void SpinCtrlDoubleTestCase::Arrows() -{ #if wxUSE_UIACTIONSIMULATOR + +TEST_CASE_METHOD(SpinCtrlDoubleTestCase, + "SpinCtrlDouble::Arrows", "[spinctrl][spinctrldouble]") +{ EventCounter updated(m_spin, wxEVT_SPINCTRLDOUBLE); wxUIActionSimulator sim; @@ -99,26 +82,20 @@ void SpinCtrlDoubleTestCase::Arrows() sim.Char(WXK_UP); wxYield(); - CPPUNIT_ASSERT_EQUAL(1, updated.GetCount()); - CPPUNIT_ASSERT_EQUAL(1.0, m_spin->GetValue()); + CHECK( updated.GetCount() == 1 ); + CHECK( m_spin->GetValue() == 1.0 ); updated.Clear(); sim.Char(WXK_DOWN); wxYield(); - CPPUNIT_ASSERT_EQUAL(1, updated.GetCount()); - CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetValue()); -#endif + CHECK( updated.GetCount() == 1 ); + CHECK( m_spin->GetValue() == 0.0 ); } -void SpinCtrlDoubleTestCase::Wrap() +TEST_CASE_METHOD(SpinCtrlDoubleTestCaseWrap, + "SpinCtrlDouble::Wrap", "[spinctrl][spinctrldouble]") { -#if wxUSE_UIACTIONSIMULATOR - wxDELETE(m_spin); - m_spin = new wxSpinCtrlDouble(wxTheApp->GetTopWindow(), wxID_ANY, "", - wxDefaultPosition, wxDefaultSize, - wxSP_ARROW_KEYS | wxSP_WRAP); - wxUIActionSimulator sim; m_spin->SetFocus(); @@ -128,20 +105,21 @@ void SpinCtrlDoubleTestCase::Wrap() wxYield(); - CPPUNIT_ASSERT_EQUAL(100.0, m_spin->GetValue()); + CHECK( m_spin->GetValue() == 100.0 ); sim.Char(WXK_UP); wxYield(); - CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetValue()); -#endif + CHECK( m_spin->GetValue() == 0.0 ); } +#endif // wxUSE_UIACTIONSIMULATOR -void SpinCtrlDoubleTestCase::Range() +TEST_CASE_METHOD(SpinCtrlDoubleTestCase, + "SpinCtrlDouble::Range", "[spinctrl][spinctrldouble]") { - CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetMin()); - CPPUNIT_ASSERT_EQUAL(100.0, m_spin->GetMax()); + CHECK( m_spin->GetMin() == 0.0 ); + CHECK( m_spin->GetMax() == 100.0 ); // 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 @@ -151,26 +129,27 @@ void SpinCtrlDoubleTestCase::Range() EventCounter updatedText(m_spin, wxEVT_TEXT); m_spin->SetRange(1., 10.); - CPPUNIT_ASSERT_EQUAL(1., m_spin->GetValue()); + CHECK( m_spin->GetValue() == 1. ); - CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); - CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); + CHECK( updatedSpin.GetCount() == 0 ); + CHECK( updatedText.GetCount() == 0 ); } //Test negative ranges m_spin->SetRange(-10.0, 10.0); - CPPUNIT_ASSERT_EQUAL(-10.0, m_spin->GetMin()); - CPPUNIT_ASSERT_EQUAL(10.0, m_spin->GetMax()); + CHECK( m_spin->GetMin() == -10.0 ); + CHECK( m_spin->GetMax() == 10.0 ); //Test backwards ranges m_spin->SetRange(75.0, 50.0); - CPPUNIT_ASSERT_EQUAL(75.0, m_spin->GetMin()); - CPPUNIT_ASSERT_EQUAL(50.0, m_spin->GetMax()); + CHECK( m_spin->GetMin() == 75.0 ); + CHECK( m_spin->GetMax() == 50.0 ); } -void SpinCtrlDoubleTestCase::Value() +TEST_CASE_METHOD(SpinCtrlDoubleTestCase, + "SpinCtrlDouble::Value", "[spinctrl][spinctrldouble]") { EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE); EventCounter updatedText(m_spin, wxEVT_TEXT); @@ -178,28 +157,41 @@ void SpinCtrlDoubleTestCase::Value() m_spin->SetDigits(2); m_spin->SetIncrement(0.1); - CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetValue()); + CHECK( m_spin->GetValue() == 0.0 ); m_spin->SetValue(50.0); - CPPUNIT_ASSERT_EQUAL(50.0, m_spin->GetValue()); + CHECK( m_spin->GetValue() == 50.0 ); m_spin->SetValue(49.1); - CPPUNIT_ASSERT_EQUAL(49.1, m_spin->GetValue()); + CHECK( m_spin->GetValue() == 49.1 ); // Calling SetValue() shouldn't have generated any events. - CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); - CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); + CHECK( updatedSpin.GetCount() == 0 ); + CHECK( updatedText.GetCount() == 0 ); + + // Also test that setting the text value works. + CHECK( m_spin->GetTextValue() == "49.10" ); + + m_spin->SetValue("57.30"); + CHECK( m_spin->GetTextValue() == "57.30" ); + CHECK( m_spin->GetValue() == 57.3 ); + + m_spin->SetValue(""); + CHECK( m_spin->GetTextValue() == "" ); + CHECK( m_spin->GetValue() == 57.3 ); } -void SpinCtrlDoubleTestCase::Increment() -{ #if wxUSE_UIACTIONSIMULATOR - CPPUNIT_ASSERT_EQUAL(1.0, m_spin->GetIncrement()); + +TEST_CASE_METHOD(SpinCtrlDoubleTestCase, + "SpinCtrlDouble::Increment", "[spinctrl][spinctrldouble]") +{ + CHECK( m_spin->GetIncrement() == 1.0 ); m_spin->SetDigits(1); m_spin->SetIncrement(0.1); - CPPUNIT_ASSERT_EQUAL(0.1, m_spin->GetIncrement()); + CHECK( m_spin->GetIncrement() == 0.1 ); wxUIActionSimulator sim; @@ -210,15 +202,17 @@ void SpinCtrlDoubleTestCase::Increment() wxYield(); - CPPUNIT_ASSERT_EQUAL(0.1, m_spin->GetValue()); -#endif + CHECK( m_spin->GetValue() == 0.1 ); } -void SpinCtrlDoubleTestCase::Digits() +#endif // wxUSE_UIACTIONSIMULATOR + +TEST_CASE_METHOD(SpinCtrlDoubleTestCase, + "SpinCtrlDouble::Digits", "[spinctrl][spinctrldouble]") { m_spin->SetDigits(5); - CPPUNIT_ASSERT_EQUAL(5, m_spin->GetDigits()); + CHECK( m_spin->GetDigits() == 5 ); } static inline unsigned int GetInitialDigits(double inc) @@ -230,7 +224,7 @@ static inline unsigned int GetInitialDigits(double inc) return digits; } -TEST_CASE("SpinCtrlDoubleTestCase::InitialDigits", "[spinctrldouble][initialdigits]") +TEST_CASE("SpinCtrlDouble::InitialDigits", "[spinctrldouble][initialdigits]") { REQUIRE(GetInitialDigits(15) == 0); REQUIRE(GetInitialDigits(10) == 0); diff --git a/tests/controls/spinctrltest.cpp b/tests/controls/spinctrltest.cpp index 1fcc8a1788..7591857281 100644 --- a/tests/controls/spinctrltest.cpp +++ b/tests/controls/spinctrltest.cpp @@ -268,6 +268,17 @@ TEST_CASE_METHOD(SpinCtrlTestCase2, "SpinCtrl::Value", "[spinctrl]") // Calling SetValue() shouldn't have generated any events. CHECK(updatedSpin.GetCount() == 0); CHECK(updatedText.GetCount() == 0); + + // Also test that setting the text value works. + CHECK( m_spin->GetTextValue() == "100" ); + + m_spin->SetValue("57"); + CHECK( m_spin->GetTextValue() == "57" ); + CHECK( m_spin->GetValue() == 57 ); + + m_spin->SetValue(""); + CHECK( m_spin->GetTextValue() == "" ); + CHECK( m_spin->GetValue() == 57 ); } TEST_CASE_METHOD(SpinCtrlTestCase2, "SpinCtrl::Base", "[spinctrl]")