Merge branch 'expose-radiogroup'

Add public functions for navigating in radio button groups.

Also introduce wxRadioButtonBase defining wxRadioButton API for all
ports.

See https://github.com/wxWidgets/wxWidgets/pull/2052
This commit is contained in:
Vadim Zeitlin
2020-09-24 00:11:15 +02:00
13 changed files with 321 additions and 201 deletions

View File

@@ -13,7 +13,7 @@
// wxRadioButton // wxRadioButton
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
class WXDLLIMPEXP_CORE wxRadioButton: public wxControl class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase
{ {
public: public:
wxRadioButton() { } wxRadioButton() { }
@@ -39,8 +39,8 @@ public:
const wxString& name = wxASCII_STR(wxRadioButtonNameStr) ); const wxString& name = wxASCII_STR(wxRadioButtonNameStr) );
virtual void SetLabel(const wxString& label) wxOVERRIDE; virtual void SetLabel(const wxString& label) wxOVERRIDE;
virtual void SetValue(bool val); virtual void SetValue(bool val) wxOVERRIDE;
virtual bool GetValue() const; virtual bool GetValue() const wxOVERRIDE;
static wxVisualAttributes static wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL);

View File

@@ -13,7 +13,7 @@
// wxRadioButton // wxRadioButton
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
class WXDLLIMPEXP_CORE wxRadioButton: public wxControl class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase
{ {
public: public:
wxRadioButton() { } wxRadioButton() { }

View File

@@ -11,7 +11,7 @@
#ifndef _WX_RADIOBUT_H_ #ifndef _WX_RADIOBUT_H_
#define _WX_RADIOBUT_H_ #define _WX_RADIOBUT_H_
class WXDLLIMPEXP_CORE wxRadioButton: public wxControl class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase
{ {
wxDECLARE_DYNAMIC_CLASS(wxRadioButton); wxDECLARE_DYNAMIC_CLASS(wxRadioButton);
public: public:

View File

@@ -13,7 +13,7 @@
#include "wx/msw/ownerdrawnbutton.h" #include "wx/msw/ownerdrawnbutton.h"
class WXDLLIMPEXP_CORE wxRadioButton : public wxMSWOwnerDrawnButton<wxControl> class WXDLLIMPEXP_CORE wxRadioButton : public wxMSWOwnerDrawnButton<wxRadioButtonBase>
{ {
public: public:
// ctors and creation functions // ctors and creation functions
@@ -43,8 +43,8 @@ public:
const wxString& name = wxASCII_STR(wxRadioButtonNameStr)); const wxString& name = wxASCII_STR(wxRadioButtonNameStr));
// implement the radio button interface // implement the radio button interface
virtual void SetValue(bool value); virtual void SetValue(bool value) wxOVERRIDE;
virtual bool GetValue() const; virtual bool GetValue() const wxOVERRIDE;
// implementation only from now on // implementation only from now on
virtual bool MSWCommand(WXUINT param, WXWORD id) wxOVERRIDE; virtual bool MSWCommand(WXUINT param, WXWORD id) wxOVERRIDE;

View File

@@ -11,7 +11,7 @@
#ifndef _WX_RADIOBUT_H_ #ifndef _WX_RADIOBUT_H_
#define _WX_RADIOBUT_H_ #define _WX_RADIOBUT_H_
class WXDLLIMPEXP_CORE wxRadioButton: public wxControl class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase
{ {
wxDECLARE_DYNAMIC_CLASS(wxRadioButton); wxDECLARE_DYNAMIC_CLASS(wxRadioButton);
@@ -35,8 +35,8 @@ public:
const wxValidator& validator = wxDefaultValidator, const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxASCII_STR(wxRadioButtonNameStr)); const wxString& name = wxASCII_STR(wxRadioButtonNameStr));
virtual void SetValue(bool val); virtual void SetValue(bool val) wxOVERRIDE;
virtual bool GetValue() const ; virtual bool GetValue() const wxOVERRIDE;
// implementation // implementation

View File

@@ -10,7 +10,7 @@
class QRadioButton; class QRadioButton;
class WXDLLIMPEXP_CORE wxRadioButton : public wxControl class WXDLLIMPEXP_CORE wxRadioButton : public wxRadioButtonBase
{ {
public: public:
wxRadioButton(); wxRadioButton();
@@ -32,8 +32,8 @@ public:
const wxValidator& validator = wxDefaultValidator, const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxASCII_STR(wxRadioButtonNameStr) ); const wxString& name = wxASCII_STR(wxRadioButtonNameStr) );
virtual void SetValue(bool value); virtual void SetValue(bool value) wxOVERRIDE;
virtual bool GetValue() const; virtual bool GetValue() const wxOVERRIDE;
virtual QWidget *GetHandle() const wxOVERRIDE; virtual QWidget *GetHandle() const wxOVERRIDE;

View File

@@ -15,23 +15,42 @@
#if wxUSE_RADIOBTN #if wxUSE_RADIOBTN
/*
There is no wxRadioButtonBase class as wxRadioButton interface is the same
as wxCheckBox(Base), but under some platforms wxRadioButton really
derives from wxCheckBox and on the others it doesn't.
The pseudo-declaration of wxRadioButtonBase would look like this:
class wxRadioButtonBase : public ...
{
public:
virtual void SetValue(bool value);
virtual bool GetValue() const;
};
*/
#include "wx/control.h" #include "wx/control.h"
class WXDLLIMPEXP_FWD_CORE wxRadioButton;
// TODO: In wxUniv, wxRadioButton must derive from wxCheckBox as it reuses
// much of its code. This should be fixed by refactoring wxCheckBox to allow
// this class to reuse its functionality without inheriting from it, but for
// now use this hack to allow the existing code to compile.
#ifdef __WXUNIVERSAL__
#include "wx/checkbox.h"
typedef wxCheckBox wxRadioButtonBaseBase;
#else
typedef wxControl wxRadioButtonBaseBase;
#endif
class WXDLLIMPEXP_CORE wxRadioButtonBase : public wxRadioButtonBaseBase
{
public:
wxRadioButtonBase() { }
// Methods to be implemented by the derived classes:
virtual void SetValue(bool value) = 0;
virtual bool GetValue() const = 0;
// Methods implemented by this class itself.
wxRadioButton* GetFirstInGroup() const;
wxRadioButton* GetLastInGroup() const;
wxRadioButton* GetPreviousInGroup() const;
wxRadioButton* GetNextInGroup() const;
private:
wxDECLARE_NO_COPY_CLASS(wxRadioButtonBase);
};
extern WXDLLIMPEXP_DATA_CORE(const char) wxRadioButtonNameStr[]; extern WXDLLIMPEXP_DATA_CORE(const char) wxRadioButtonNameStr[];
#if defined(__WXUNIVERSAL__) #if defined(__WXUNIVERSAL__)

View File

@@ -11,13 +11,11 @@
#ifndef _WX_UNIV_RADIOBUT_H_ #ifndef _WX_UNIV_RADIOBUT_H_
#define _WX_UNIV_RADIOBUT_H_ #define _WX_UNIV_RADIOBUT_H_
#include "wx/checkbox.h"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxRadioButton // wxRadioButton
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class WXDLLIMPEXP_CORE wxRadioButton : public wxCheckBox class WXDLLIMPEXP_CORE wxRadioButton : public wxRadioButtonBase
{ {
public: public:
// constructors // constructors
@@ -46,6 +44,10 @@ public:
const wxValidator& validator = wxDefaultValidator, const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxASCII_STR(wxRadioButtonNameStr)); const wxString& name = wxASCII_STR(wxRadioButtonNameStr));
// (re)implement pure virtuals from wxRadioButtonBase
virtual void SetValue(bool value) wxOVERRIDE { return wxCheckBox::SetValue(value); }
virtual bool GetValue() const wxOVERRIDE { return wxCheckBox::GetValue(); }
// override some base class methods // override some base class methods
virtual void ChangeValue(bool value) wxOVERRIDE; virtual void ChangeValue(bool value) wxOVERRIDE;

View File

@@ -12,18 +12,28 @@
mutually exclusive options. It has a text label next to a (usually) round mutually exclusive options. It has a text label next to a (usually) round
button. button.
You can create a group of mutually-exclusive radio buttons by specifying Radio buttons are typically used in groups of mutually-exclusive buttons,
@c wxRB_GROUP for the first in the group. The group ends when another i.e. exactly one of the buttons in the group is checked, and the other ones
radio button group is created, or there are no more radio buttons. are unchecked automatically. Such groups are created implicitly, but can
also be started explicitly by using @c wxRB_GROUP style: a button with this
style starts a new group and will become the initial selection in this
group. Alternatively, a radio button may be excluded from the group that it
would otherwise belong to by using @c wxRB_SINGLE style.
To find the other elements of the same radio button group, you can use
GetFirstInGroup(), GetPreviousInGroup(), GetNextInGroup() and
GetLastInGroup() functions.
@beginStyleTable @beginStyleTable
@style{wxRB_GROUP} @style{wxRB_GROUP}
Marks the beginning of a new group of radio buttons. Marks the beginning of a new group of radio buttons.
@style{wxRB_SINGLE} @style{wxRB_SINGLE}
In some circumstances, radio buttons that are not consecutive Creates a radio button which is not part of any radio button group.
siblings trigger a hang bug in Windows (only). If this happens, add When this style is used, no other radio buttons will be turned off
this style to mark the button as not belonging to a group, and automatically when this button is turned on and such behaviour will
implement the mutually-exclusive group behaviour yourself. need to be implemented manually, in the event handler for this
button.
@endStyleTable @endStyleTable
@beginEventEmissionTable{wxCommandEvent} @beginEventEmissionTable{wxCommandEvent}
@@ -119,5 +129,64 @@ public:
@true to check, @false to uncheck. @true to check, @false to uncheck.
*/ */
virtual void SetValue(bool value); virtual void SetValue(bool value);
/**
Returns the first button of the radio button group this button belongs
to.
For a radio button with @c wxRB_SINGLE style, this function returns this
button itself, as it is the only member of its group. Otherwise, the
function returns the closest previous radio button with @c wxRB_GROUP
style (which could still be this button itself) or the first radio
button in the same window.
The returned value is never @NULL.
@see GetPreviousInGroup(), GetNextInGroup(), GetLastInGroup()
@since 3.1.5
*/
wxRadioButton* GetFirstInGroup() const;
/**
Returns the last button of the radio button group this button belongs
to.
Similarly to GetFirstInGroup(), this function returns this button
itself if it has @c wxRB_SINGLE style. Otherwise, the function returns
the last button before the next button with @c wxRB_GROUP style or the
last radio button in the same window.
The returned value is never @NULL.
@see GetPreviousInGroup(), GetNextInGroup()
@since 3.1.5
*/
wxRadioButton* GetLastInGroup() const;
/**
Returns the previous radio button in the same group.
The return value is @NULL if there is no predecessor or if this button
has @c wxRB_SINGLE style.
@see GetFirstInGroup(), GetNextInGroup(), GetLastInGroup()
@since 3.1.5
*/
wxRadioButton* GetPreviousInGroup() const;
/**
Returns the next radio button in the same group.
The return value is @NULL if there is no successor or if this button
has @c wxRB_SINGLE style.
@see GetFirstInGroup(), GetPreviousInGroup(), GetLastInGroup()
@since 3.1.5
*/
wxRadioButton* GetNextInGroup() const;
}; };

View File

@@ -234,101 +234,19 @@ void wxControlContainer::SetLastFocus(wxWindow *win)
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// The following four functions are used to find other radio buttons // The following functions is used by wxSetFocusToChild()
// within the same group. Used by wxSetFocusToChild
// -------------------------------------------------------------------- // --------------------------------------------------------------------
#if wxUSE_RADIOBTN #if defined(USE_RADIOBTN_NAV)
wxRadioButton* wxGetPreviousButtonInGroup(wxRadioButton *btn) namespace
{ {
if ( btn->HasFlag(wxRB_GROUP) || btn->HasFlag(wxRB_SINGLE) )
return NULL;
const wxWindowList& siblings = btn->GetParent()->GetChildren(); wxRadioButton* wxGetSelectedButtonInGroup(const wxRadioButton *btn)
wxWindowList::compatibility_iterator nodeThis = siblings.Find(btn);
wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") );
// Iterate over all previous siblings until we find the next radio button
wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
wxRadioButton *prevBtn = 0;
while (nodeBefore)
{
prevBtn = wxDynamicCast(nodeBefore->GetData(), wxRadioButton);
if (prevBtn)
break;
nodeBefore = nodeBefore->GetPrevious();
}
if (!prevBtn || prevBtn->HasFlag(wxRB_SINGLE))
{
// no more buttons in group
return NULL;
}
return prevBtn;
}
wxRadioButton* wxGetNextButtonInGroup(wxRadioButton *btn)
{
if (btn->HasFlag(wxRB_SINGLE))
return NULL;
const wxWindowList& siblings = btn->GetParent()->GetChildren();
wxWindowList::compatibility_iterator nodeThis = siblings.Find(btn);
wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") );
// Iterate over all previous siblings until we find the next radio button
wxWindowList::compatibility_iterator nodeNext = nodeThis->GetNext();
wxRadioButton *nextBtn = 0;
while (nodeNext)
{
nextBtn = wxDynamicCast(nodeNext->GetData(), wxRadioButton);
if (nextBtn)
break;
nodeNext = nodeNext->GetNext();
}
if ( !nextBtn || nextBtn->HasFlag(wxRB_GROUP) || nextBtn->HasFlag(wxRB_SINGLE) )
{
// no more buttons or the first button of the next group
return NULL;
}
return nextBtn;
}
wxRadioButton* wxGetFirstButtonInGroup(wxRadioButton *btn)
{
while (true)
{
wxRadioButton* prevBtn = wxGetPreviousButtonInGroup(btn);
if (!prevBtn)
return btn;
btn = prevBtn;
}
}
wxRadioButton* wxGetLastButtonInGroup(wxRadioButton *btn)
{
while (true)
{
wxRadioButton* nextBtn = wxGetNextButtonInGroup(btn);
if (!nextBtn)
return btn;
btn = nextBtn;
}
}
wxRadioButton* wxGetSelectedButtonInGroup(wxRadioButton *btn)
{ {
// Find currently selected button // Find currently selected button
if (btn->GetValue()) if (btn->GetValue())
return btn; return const_cast<wxRadioButton*>(btn);
if (btn->HasFlag(wxRB_SINGLE)) if (btn->HasFlag(wxRB_SINGLE))
return NULL; return NULL;
@@ -336,19 +254,21 @@ wxRadioButton* wxGetSelectedButtonInGroup(wxRadioButton *btn)
wxRadioButton *selBtn; wxRadioButton *selBtn;
// First check all previous buttons // First check all previous buttons
for (selBtn = wxGetPreviousButtonInGroup(btn); selBtn; selBtn = wxGetPreviousButtonInGroup(selBtn)) for (selBtn = btn->GetPreviousInGroup(); selBtn; selBtn = selBtn->GetPreviousInGroup())
if (selBtn->GetValue()) if (selBtn->GetValue())
return selBtn; return selBtn;
// Now all following buttons // Now all following buttons
for (selBtn = wxGetNextButtonInGroup(btn); selBtn; selBtn = wxGetNextButtonInGroup(selBtn)) for (selBtn = btn->GetNextInGroup(); selBtn; selBtn = selBtn->GetNextInGroup())
if (selBtn->GetValue()) if (selBtn->GetValue())
return selBtn; return selBtn;
return NULL; return NULL;
} }
#endif // wxUSE_RADIOBTN } // anonymous namespace
#endif // USE_RADIOBTN_NAV
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Keyboard handling - this is the place where the TAB traversal logic is // Keyboard handling - this is the place where the TAB traversal logic is
@@ -468,7 +388,7 @@ void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event )
// If we are in a radio button group, start from the first item in the // If we are in a radio button group, start from the first item in the
// group // group
if ( event.IsFromTab() && wxIsKindOf(winFocus, wxRadioButton ) ) if ( event.IsFromTab() && wxIsKindOf(winFocus, wxRadioButton ) )
winFocus = wxGetFirstButtonInGroup((wxRadioButton*)winFocus); winFocus = static_cast<wxRadioButton*>(winFocus)->GetFirstInGroup();
#endif // USE_RADIOBTN_NAV #endif // USE_RADIOBTN_NAV
// ok, we found the focus - now is it our child? // ok, we found the focus - now is it our child?
start_node = children.Find( winFocus ); start_node = children.Find( winFocus );
@@ -586,20 +506,20 @@ void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event )
// find the correct radio button to focus // find the correct radio button to focus
if ( forward ) if ( forward )
{ {
child = wxGetNextButtonInGroup(lastBtn); child = lastBtn->GetNextInGroup();
if ( !child ) if ( !child )
{ {
// no next button in group, set it to the first button // no next button in group, set it to the first button
child = wxGetFirstButtonInGroup(lastBtn); child = lastBtn->GetFirstInGroup();
} }
} }
else else
{ {
child = wxGetPreviousButtonInGroup(lastBtn); child = lastBtn->GetPreviousInGroup();
if ( !child ) if ( !child )
{ {
// no previous button in group, set it to the last button // no previous button in group, set it to the last button
child = wxGetLastButtonInGroup(lastBtn); child = lastBtn->GetLastInGroup();
} }
} }

View File

@@ -92,4 +92,96 @@ wxCONSTRUCTOR_6( wxRadioButton, wxWindow*, Parent, wxWindowID, Id, \
wxString, Label, wxPoint, Position, wxSize, Size, long, WindowStyle ) wxString, Label, wxPoint, Position, wxSize, Size, long, WindowStyle )
// ----------------------------------------------------------------------------
// wxRadioButton group navigation
// ----------------------------------------------------------------------------
wxRadioButton* wxRadioButtonBase::GetFirstInGroup() const
{
wxRadioButton*
btn = static_cast<wxRadioButton*>(const_cast<wxRadioButtonBase*>(this));
while (true)
{
wxRadioButton* prevBtn = btn->GetPreviousInGroup();
if (!prevBtn)
return btn;
btn = prevBtn;
}
}
wxRadioButton* wxRadioButtonBase::GetLastInGroup() const
{
wxRadioButton*
btn = static_cast<wxRadioButton*>(const_cast<wxRadioButtonBase*>(this));
while (true)
{
wxRadioButton* nextBtn = btn->GetNextInGroup();
if (!nextBtn)
return btn;
btn = nextBtn;
}
}
wxRadioButton* wxRadioButtonBase::GetPreviousInGroup() const
{
if ( HasFlag(wxRB_GROUP) || HasFlag(wxRB_SINGLE) )
return NULL;
const wxWindowList& siblings = GetParent()->GetChildren();
wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") );
// Iterate over all previous siblings until we find the next radio button
wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
wxRadioButton *prevBtn = 0;
while (nodeBefore)
{
prevBtn = wxDynamicCast(nodeBefore->GetData(), wxRadioButton);
if (prevBtn)
break;
nodeBefore = nodeBefore->GetPrevious();
}
if (!prevBtn || prevBtn->HasFlag(wxRB_SINGLE))
{
// no more buttons in group
return NULL;
}
return prevBtn;
}
wxRadioButton* wxRadioButtonBase::GetNextInGroup() const
{
if ( HasFlag(wxRB_SINGLE) )
return NULL;
const wxWindowList& siblings = GetParent()->GetChildren();
wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") );
// Iterate over all previous siblings until we find the next radio button
wxWindowList::compatibility_iterator nodeNext = nodeThis->GetNext();
wxRadioButton *nextBtn = 0;
while (nodeNext)
{
nextBtn = wxDynamicCast(nodeNext->GetData(), wxRadioButton);
if (nextBtn)
break;
nodeNext = nodeNext->GetNext();
}
if ( !nextBtn || nextBtn->HasFlag(wxRB_GROUP) || nextBtn->HasFlag(wxRB_SINGLE) )
{
// no more buttons or the first button of the next group
return NULL;
}
return nextBtn;
}
#endif // wxUSE_RADIOBTN #endif // wxUSE_RADIOBTN

View File

@@ -27,40 +27,19 @@
#include "testableframe.h" #include "testableframe.h"
#include "testwindow.h" #include "testwindow.h"
class RadioButtonTestCase : public CppUnit::TestCase class RadioButtonTestCase
{ {
public: public:
RadioButtonTestCase() { } RadioButtonTestCase();
~RadioButtonTestCase();
void setUp() wxOVERRIDE;
void tearDown() wxOVERRIDE;
private:
CPPUNIT_TEST_SUITE( RadioButtonTestCase );
WXUISIM_TEST( Click );
CPPUNIT_TEST( Value );
CPPUNIT_TEST( Group );
CPPUNIT_TEST( Single );
CPPUNIT_TEST_SUITE_END();
void Click();
void Value();
void Group();
void Single();
protected:
wxRadioButton* m_radio; wxRadioButton* m_radio;
wxDECLARE_NO_COPY_CLASS(RadioButtonTestCase); wxDECLARE_NO_COPY_CLASS(RadioButtonTestCase);
}; };
// register in the unnamed registry so that these tests are run by default RadioButtonTestCase::RadioButtonTestCase()
CPPUNIT_TEST_SUITE_REGISTRATION( RadioButtonTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( RadioButtonTestCase,
"RadioButtonTestCase" );
void RadioButtonTestCase::setUp()
{ {
m_radio = new wxRadioButton(wxTheApp->GetTopWindow(), wxID_ANY, m_radio = new wxRadioButton(wxTheApp->GetTopWindow(), wxID_ANY,
"wxRadioButton"); "wxRadioButton");
@@ -68,12 +47,12 @@ void RadioButtonTestCase::setUp()
m_radio->Refresh(); m_radio->Refresh();
} }
void RadioButtonTestCase::tearDown() RadioButtonTestCase::~RadioButtonTestCase()
{ {
wxDELETE(m_radio); delete m_radio;
} }
void RadioButtonTestCase::Click() TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Click", "[radiobutton]")
{ {
// OS X doesn't support selecting a single radio button // OS X doesn't support selecting a single radio button
#if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__) #if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__)
@@ -87,87 +66,115 @@ void RadioButtonTestCase::Click()
wxYield(); wxYield();
CPPUNIT_ASSERT_EQUAL( 1, selected.GetCount() ); CHECK(selected.GetCount() == 1);
#endif #endif
} }
void RadioButtonTestCase::Value() TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Value", "[radiobutton]")
{ {
#ifndef __WXGTK__ #ifndef __WXGTK__
EventCounter selected(m_radio, wxEVT_RADIOBUTTON); EventCounter selected(m_radio, wxEVT_RADIOBUTTON);
m_radio->SetValue(true); m_radio->SetValue(true);
CPPUNIT_ASSERT(m_radio->GetValue()); CHECK(m_radio->GetValue());
m_radio->SetValue(false); m_radio->SetValue(false);
CPPUNIT_ASSERT(!m_radio->GetValue()); CHECK(!m_radio->GetValue());
CPPUNIT_ASSERT_EQUAL(0, selected.GetCount()); CHECK(selected.GetCount() == 0);
#endif #endif
} }
void RadioButtonTestCase::Group() TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Group", "[radiobutton]")
{ {
wxWindow* const parent = wxTheApp->GetTopWindow(); wxWindow* const parent = wxTheApp->GetTopWindow();
// Create two different radio groups. // Create two different radio groups.
wxRadioButton* g1radio0 = new wxRadioButton(parent, wxID_ANY, "radio 1.0", wxScopedPtr<wxRadioButton> g1radio0(new wxRadioButton(parent, wxID_ANY, "radio 1.0",
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
wxRB_GROUP); wxRB_GROUP));
wxRadioButton* g1radio1 = new wxRadioButton(parent, wxID_ANY, "radio 1.1"); wxScopedPtr<wxRadioButton> g1radio1(new wxRadioButton(parent, wxID_ANY, "radio 1.1"));
wxRadioButton* g2radio0 = new wxRadioButton(parent, wxID_ANY, "radio 2.0", wxScopedPtr<wxRadioButton> g2radio0(new wxRadioButton(parent, wxID_ANY, "radio 2.0",
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
wxRB_GROUP); wxRB_GROUP));
wxRadioButton* g2radio1 = new wxRadioButton(parent, wxID_ANY, "radio 2.1"); wxScopedPtr<wxRadioButton> g2radio1(new wxRadioButton(parent, wxID_ANY, "radio 2.1"));
// Check that having another control between radio buttons doesn't break // Check that having another control between radio buttons doesn't break
// grouping. // grouping.
wxStaticText* text = new wxStaticText(parent, wxID_ANY, "Label"); wxScopedPtr<wxStaticText> text(new wxStaticText(parent, wxID_ANY, "Label"));
wxRadioButton* g2radio2 = new wxRadioButton(parent, wxID_ANY, "radio 2.1"); wxScopedPtr<wxRadioButton> g2radio2(new wxRadioButton(parent, wxID_ANY, "radio 2.2"));
g1radio0->SetValue(true); g1radio0->SetValue(true);
g2radio0->SetValue(true); g2radio0->SetValue(true);
CPPUNIT_ASSERT(g1radio0->GetValue()); CHECK(g1radio0->GetValue());
CPPUNIT_ASSERT(!g1radio1->GetValue()); CHECK(!g1radio1->GetValue());
CPPUNIT_ASSERT(g2radio0->GetValue()); CHECK(g2radio0->GetValue());
CPPUNIT_ASSERT(!g2radio1->GetValue()); CHECK(!g2radio1->GetValue());
g1radio1->SetValue(true); g1radio1->SetValue(true);
g2radio1->SetValue(true); g2radio1->SetValue(true);
CPPUNIT_ASSERT(!g1radio0->GetValue()); CHECK(!g1radio0->GetValue());
CPPUNIT_ASSERT(g1radio1->GetValue()); CHECK(g1radio1->GetValue());
CPPUNIT_ASSERT(!g2radio0->GetValue()); CHECK(!g2radio0->GetValue());
CPPUNIT_ASSERT(g2radio1->GetValue()); CHECK(g2radio1->GetValue());
g2radio2->SetValue(true); g2radio2->SetValue(true);
CPPUNIT_ASSERT(!g2radio0->GetValue()); CHECK(!g2radio0->GetValue());
CPPUNIT_ASSERT(!g2radio1->GetValue()); CHECK(!g2radio1->GetValue());
CPPUNIT_ASSERT(g2radio2->GetValue()); CHECK(g2radio2->GetValue());
g1radio0->SetValue(true); g1radio0->SetValue(true);
g2radio0->SetValue(true); g2radio0->SetValue(true);
CPPUNIT_ASSERT(g1radio0->GetValue()); CHECK(g1radio0->GetValue());
CPPUNIT_ASSERT(!g1radio1->GetValue()); CHECK(!g1radio1->GetValue());
CPPUNIT_ASSERT(g2radio0->GetValue()); CHECK(g2radio0->GetValue());
CPPUNIT_ASSERT(!g2radio1->GetValue()); CHECK(!g2radio1->GetValue());
wxDELETE(g1radio0);
wxDELETE(g1radio1); // Check that group navigation functions behave as expected.
wxDELETE(g2radio0);
wxDELETE(g2radio1); // GetFirstInGroup()
wxDELETE(g2radio2); CHECK_SAME_WINDOW(g1radio0->GetFirstInGroup(), g1radio0);
wxDELETE(text); CHECK_SAME_WINDOW(g1radio1->GetFirstInGroup(), g1radio0);
CHECK_SAME_WINDOW(g2radio0->GetFirstInGroup(), g2radio0);
CHECK_SAME_WINDOW(g2radio1->GetFirstInGroup(), g2radio0);
CHECK_SAME_WINDOW(g2radio2->GetFirstInGroup(), g2radio0);
// GetLastInGroup()
CHECK_SAME_WINDOW(g1radio0->GetLastInGroup(), g1radio1);
CHECK_SAME_WINDOW(g1radio1->GetLastInGroup(), g1radio1);
CHECK_SAME_WINDOW(g2radio0->GetLastInGroup(), g2radio2);
CHECK_SAME_WINDOW(g2radio1->GetLastInGroup(), g2radio2);
CHECK_SAME_WINDOW(g2radio2->GetLastInGroup(), g2radio2);
// GetNextInGroup()
CHECK_SAME_WINDOW(g1radio0->GetNextInGroup(), g1radio1);
CHECK_SAME_WINDOW(g1radio1->GetNextInGroup(), NULL);
CHECK_SAME_WINDOW(g2radio0->GetNextInGroup(), g2radio1);
CHECK_SAME_WINDOW(g2radio1->GetNextInGroup(), g2radio2);
CHECK_SAME_WINDOW(g2radio2->GetNextInGroup(), NULL);
// GetPreviousInGroup()
CHECK_SAME_WINDOW(g1radio0->GetPreviousInGroup(), NULL);
CHECK_SAME_WINDOW(g1radio1->GetPreviousInGroup(), g1radio0);
CHECK_SAME_WINDOW(g2radio0->GetPreviousInGroup(), NULL);
CHECK_SAME_WINDOW(g2radio1->GetPreviousInGroup(), g2radio0);
CHECK_SAME_WINDOW(g2radio2->GetPreviousInGroup(), g2radio1);
} }
void RadioButtonTestCase::Single() TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Single", "[radiobutton]")
{ {
//Create a group of 2 buttons, having second button selected //Create a group of 2 buttons, having second button selected
wxScopedPtr<wxRadioButton> gradio0(new wxRadioButton(wxTheApp->GetTopWindow(), wxScopedPtr<wxRadioButton> gradio0(new wxRadioButton(wxTheApp->GetTopWindow(),
@@ -197,9 +204,15 @@ void RadioButtonTestCase::Single()
CHECK(gradio1->GetValue()); CHECK(gradio1->GetValue());
CHECK(ngradio->GetValue()); CHECK(ngradio->GetValue());
// Also check that navigation works as expected with "single" buttons.
CHECK_SAME_WINDOW(sradio->GetFirstInGroup(), sradio);
CHECK_SAME_WINDOW(sradio->GetLastInGroup(), sradio);
CHECK_SAME_WINDOW(sradio->GetPreviousInGroup(), NULL);
CHECK_SAME_WINDOW(sradio->GetNextInGroup(), NULL);
} }
TEST_CASE("wxRadioButton::Focus", "[radiobutton][focus]") TEST_CASE("RadioButton::Focus", "[radiobutton][focus]")
{ {
// Create a container panel just to be able to destroy all the windows // Create a container panel just to be able to destroy all the windows
// created here at once by simply destroying it. // created here at once by simply destroying it.

View File

@@ -9,6 +9,7 @@
#ifndef _WX_TESTS_TESTWINDOW_H_ #ifndef _WX_TESTS_TESTWINDOW_H_
#define _WX_TESTS_TESTWINDOW_H_ #define _WX_TESTS_TESTWINDOW_H_
#include "wx/scopedptr.h"
#include "wx/window.h" #include "wx/window.h"
// We need to wrap wxWindow* in a class as specializing StringMaker for // We need to wrap wxWindow* in a class as specializing StringMaker for
@@ -17,6 +18,8 @@ class wxWindowPtr
{ {
public: public:
explicit wxWindowPtr(wxWindow* win) : m_win(win) {} explicit wxWindowPtr(wxWindow* win) : m_win(win) {}
template <typename W>
explicit wxWindowPtr(const wxScopedPtr<W>& win) : m_win(win.get()) {}
wxString Dump() const wxString Dump() const
{ {
@@ -44,7 +47,9 @@ private:
// Macro providing more information about the current focus if comparison // Macro providing more information about the current focus if comparison
// fails. // fails.
#define CHECK_FOCUS_IS(w) CHECK(wxWindowPtr(wxWindow::FindFocus()) == wxWindowPtr(w)) #define CHECK_SAME_WINDOW(w1, w2) CHECK(wxWindowPtr(w1) == wxWindowPtr(w2))
#define CHECK_FOCUS_IS(w) CHECK_SAME_WINDOW(wxWindow::FindFocus(), w)
namespace Catch namespace Catch
{ {