wxSpinCtrl Enter should do what Tab does Topic is solved
-
- Ultimate wxWidgets Guru
- Posts: 539
- Joined: Fri May 22, 2009 8:52 am
- Location: Bremen, Germany
wxSpinCtrl Enter should do what Tab does
I have a wxSpinCtrl and want to leave the control when the user presses Enter, just like what happens when the user pressed Tab. The wxEVT_COMMAND_SPINCTRL_UPDATED should be triggered and the next control selected.
I am surprised that seems to be difficult. How can I achieve this?
I am surprised that seems to be difficult. How can I achieve this?
Re: wxSpinCtrl Enter should do what Tab does
Hi,
Do you have it inside wxFrame or wxDialog?
What does hapend when you press Enter?
And usual stanza:
wx version?
OS version?
Toolkit?
Thank you.
Do you have it inside wxFrame or wxDialog?
What does hapend when you press Enter?
And usual stanza:
wx version?
OS version?
Toolkit?
Thank you.
-
- Ultimate wxWidgets Guru
- Posts: 539
- Joined: Fri May 22, 2009 8:52 am
- Location: Bremen, Germany
Re: wxSpinCtrl Enter should do what Tab does
I have it in a wxPanel.
Nothing happens when I press Enter.
wxWidgets 3.1.3 on Windows 10 Pro with Visual Studio v142 build tools (10.0.18362.0).
Nothing happens when I press Enter.
wxWidgets 3.1.3 on Windows 10 Pro with Visual Studio v142 build tools (10.0.18362.0).
Re: wxSpinCtrl Enter should do what Tab does
Without trying, i'd say: Catch wxEVT_TEXT_ENTER event and call Navigate() in the event handler.
Use the source, Luke!
Re: wxSpinCtrl Enter should do what Tab does
Does the event handler get called? If not, did you set the wxTE_PROCESS_ENTER style flag?
If it does, try calling Navigate delayed using CallAfter:
If it does, try calling Navigate delayed using CallAfter:
Code: Select all
CallAfter( [this] {
Navigate();
});
Use the source, Luke!
Re: wxSpinCtrl Enter should do what Tab does
Seems to work fine for me with the master on MSW, did you create the spin control with wxTE_PROCESS_ENTER flag?
Code: Select all
#include <wx/wx.h>
#include <wx/spinctrl.h>
class MyFrame : public wxFrame
{
public:
MyFrame () : wxFrame(nullptr, wxID_ANY, "Test")
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainPanelSizer = new wxBoxSizer(wxVERTICAL);
wxSpinCtrl* spinCtrl = new wxSpinCtrl(mainPanel, wxID_ANY, "5", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER);
spinCtrl->SetRange(1, 10);
spinCtrl->Bind(wxEVT_TEXT_ENTER, &MyFrame::OnSpinCtrlTextEnter, this);
mainPanelSizer->Add(spinCtrl, 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);
}
private:
void OnSpinCtrlTextEnter(wxCommandEvent& evt)
{
wxWindow* win = dynamic_cast<wxWindow*>(evt.GetEventObject());
win->Navigate();
}
};
class MyApp : public wxApp
{
public:
bool OnInit() override
{
(new MyFrame())->Show();
return true;
}
}; wxIMPLEMENT_APP(MyApp);
As I tested now, it would not work for me if I just called Navigate() for the panel which is the parent of the spin control, it must be wxSpinCtrl's Navigate() as shown in the code above.
-
- Ultimate wxWidgets Guru
- Posts: 539
- Joined: Fri May 22, 2009 8:52 am
- Location: Bremen, Germany
Re: wxSpinCtrl Enter should do what Tab does
A little follow up question:
Can I somehow put this in a class that a lot of different spin controls can inherit from?
I would like to have inheriting wxSpinCtrl and wxSpinCtrlDouble with this onEnterKey functionality plus whatever they have to do when the value changes.
Can I somehow put this in a class that a lot of different spin controls can inherit from?
Code: Select all
class DECLDIR_CONF spinCtrlBasis {
public:
spinCtrlBasis(int winId){
getEvtHandler()->Connect(winId, wxEVT_TEXT_ENTER, wxCommandEventHandler(spinCtrlBasis::onEnterKey));
getEvtHandler()->Connect(winId, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler(spinCtrlBasis::handleValueChange));
}
private:
virtual void handleValueChange(wxCommandEvent& evt) = 0;
virtual wxEvtHandler *getEvtHandler() = 0;
void onEnterKey(wxCommandEvent& evt) {
wxWindow* win = dynamic_cast<wxWindow*>(evt.GetEventObject());
win->Navigate();
}
};
Re: wxSpinCtrl Enter should do what Tab does
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
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
I would investigate if it this is not possible to do that some other way, e.g. using validators.
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);
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);
-
- Ultimate wxWidgets Guru
- Posts: 539
- Joined: Fri May 22, 2009 8:52 am
- Location: Bremen, Germany
Re: wxSpinCtrl Enter should do what Tab does
That is okay, my program will always be limited to windows.
Really helpful answer, thank you! I also learned something new about how to use templates.