I do not know if subclassing the controls for that is the best idea. E.g, in wxFormBuilder support for custom controls, even those deriving from the supported ones with the same public interface is not the best.
Seeing as wxSpinCtrl and wxSpinCtrl double do not have a common "spin" ancestor, I would probably consider using the curiously recurring template pattern. The basic implementation of a spin control handling <Enter> could look like this
Code: Select all
#include <wx/wx.h>
#include <wx/spinctrl.h>
template <typename BaseSpinCtrl>
class wxCommonSpinCtrlHandlingEnter : public BaseSpinCtrl
{
public:
wxCommonSpinCtrlHandlingEnter(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& value = wxEmptyString)
: BaseSpinCtrl(parent, id, value, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER)
{
Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&){ Navigate(); });
}
};
typedef wxCommonSpinCtrlHandlingEnter<wxSpinCtrl> wxSpinCtrlHandlingEnter;
typedef wxCommonSpinCtrlHandlingEnter<wxSpinCtrlDouble> wxSpinCtrlDoubleHandlingEnter;
class MyFrame : public wxFrame
{
public:
MyFrame () : wxFrame(nullptr, wxID_ANY, "Test")
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainPanelSizer = new wxBoxSizer(wxVERTICAL);
auto spinCtrl = new wxSpinCtrlHandlingEnter(mainPanel, wxID_ANY, "5");
spinCtrl->SetRange(1, 10);
auto spinCtrlDouble = new wxSpinCtrlDoubleHandlingEnter(mainPanel, wxID_ANY, "5");
spinCtrlDouble->SetRange(1, 10);
mainPanelSizer->Add(spinCtrl, wxSizerFlags().Expand().DoubleBorder());
mainPanelSizer->Add(spinCtrlDouble, wxSizerFlags().Expand().DoubleBorder());
mainPanelSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY, "blah"), wxSizerFlags().Expand().DoubleBorder());
mainPanelSizer->Add(new wxButton(mainPanel, wxID_ANY, "Button"), wxSizerFlags().Expand().DoubleBorder());
mainPanel->SetSizer(mainPanelSizer);
}
};
class MyApp : public wxApp
{
public:
bool OnInit() override
{
(new MyFrame())->Show();
return true;
}
}; wxIMPLEMENT_APP(MyApp);
BTW, the official documentation for wxSpinCtrlDouble, unlike that for wxSpinCtrl, has nothing about processing enter. On MSW it works (the control is a generic one combining wxTextCtrl and wxSpinCtrl) but it may not work elsewhere.
EDIT
When using virtual functions is required, it gets tricky and the CRTP may not be that useful. The best I could come with was
Code: Select all
#include <wx/wx.h>
#include <wx/spinctrl.h>
#include <type_traits>
template <typename BaseSpinCtrl>
class wxCommonSpinCtrlHandlingEnter : public BaseSpinCtrl
{
public:
wxCommonSpinCtrlHandlingEnter(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& value = wxEmptyString)
: BaseSpinCtrl(parent, id, value, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER)
{
Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&){ Navigate(); });
if ( std::is_same<BaseSpinCtrl, wxSpinCtrl>::value )
{
Bind(wxEVT_SPINCTRL, [this](wxSpinEvent& evt){ ProcessValue(evt.GetPosition()); });
}
else
{
Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent& evt){ ProcessValue(evt.GetValue()); });
}
}
protected:
virtual void ProcessValue(int value)
{
wxLogMessage("Processing INT value: %d", value);
}
virtual void ProcessValue(double value)
{
wxLogMessage("Processing DOUBLE value: %g", value);
}
};
typedef wxCommonSpinCtrlHandlingEnter<wxSpinCtrl> wxSpinCtrlHandlingEnter;
typedef wxCommonSpinCtrlHandlingEnter<wxSpinCtrlDouble> wxSpinCtrlDoubleHandlingEnter;
class MySpinCtrlHandlingEnter : public wxSpinCtrlHandlingEnter
{
public:
MySpinCtrlHandlingEnter(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& value = wxEmptyString)
: wxSpinCtrlHandlingEnter(parent, id, value) {}
protected:
void ProcessValue(int value) override
{
wxLogMessage("Hello from MySpinCtrlHandlingEnter::ProcessValue value: %d", value);
}
};
class MyFrame : public wxFrame
{
public:
MyFrame () : wxFrame(nullptr, wxID_ANY, "Test", wxDefaultPosition, wxSize(600, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainPanelSizer = new wxBoxSizer(wxVERTICAL);
auto spinCtrl = new wxSpinCtrlHandlingEnter(mainPanel, wxID_ANY, "5");
spinCtrl->SetRange(1, 10);
mainPanelSizer->Add(spinCtrl, wxSizerFlags().Expand().DoubleBorder());
auto spinCtrlDouble = new wxSpinCtrlDoubleHandlingEnter(mainPanel, wxID_ANY, "5");
spinCtrlDouble->SetRange(1, 10);
mainPanelSizer->Add(spinCtrlDouble, wxSizerFlags().Expand().DoubleBorder());
auto mySpinCtrl = new MySpinCtrlHandlingEnter(mainPanel, wxID_ANY, "5");
mySpinCtrl->SetRange(1, 10);
mainPanelSizer->Add(mySpinCtrl, wxSizerFlags().Expand().DoubleBorder());
wxTextCtrl* logCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));
wxLog::DisableTimestamp();
mainPanelSizer->Add(logCtrl, wxSizerFlags().Proportion(1).Expand().DoubleBorder());
mainPanel->SetSizer(mainPanelSizer);
}
};
class MyApp : public wxApp
{
public:
bool OnInit() override
{
(new MyFrame())->Show();
return true;
}
}; wxIMPLEMENT_APP(MyApp);
I would investigate if it this is not possible to do that some other way, e.g. using validators.