How to prevent a wxTextCtrl from loosing focus?
How to prevent a wxTextCtrl from loosing focus?
I want that the user can't "leave" a wxTextCtrl until the value inside it is "valid".
Which means that what i want is to prevent a wxTextCtrl to loose the Focus, no matter what causes it (mouse, keystrokes).
How can i obtain this?
*EDIT:
While the whole application is a fullscreen app, i don't want to prevent the user to go to another app; all i need i that the focused wxTextCtrl stay focused while my app is active. The user should not be able to anything else than typing on the text or press ESC or ENTER
Which means that what i want is to prevent a wxTextCtrl to loose the Focus, no matter what causes it (mouse, keystrokes).
How can i obtain this?
*EDIT:
While the whole application is a fullscreen app, i don't want to prevent the user to go to another app; all i need i that the focused wxTextCtrl stay focused while my app is active. The user should not be able to anything else than typing on the text or press ESC or ENTER
Re: How to prevent a wxTextCtrl from loosing focus?
TBH, I consider such behaviour not only rather unusual but also quite user hostile.
Anyway, have you tried to handle wxEVT_KILL_FOCUS and when needed, from this handler (making sure to call wxEvent::Skip()), use CallAfter() (this is needed) where you return the focus to the control using SetFocus().
Anyway, have you tried to handle wxEVT_KILL_FOCUS and when needed, from this handler (making sure to call wxEvent::Skip()), use CallAfter() (this is needed) where you return the focus to the control using SetFocus().
Re: How to prevent a wxTextCtrl from loosing focus?
Why there is need to use CallAfter? Code below works for me:PB wrote: Anyway, have you tried to handle wxEVT_KILL_FOCUS and when needed, from this handler (making sure to call wxEvent::Skip()), use CallAfter() (this is needed) where you return the focus to the control using SetFocus().
Code: Select all
#include <wx/wx.h>
class MyFrame : public wxFrame
{
public:
MyFrame() : wxFrame(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(600, 400))
{
wxBoxSizer* Sizer = new wxBoxSizer( wxHORIZONTAL );
m_textCtrl1 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_textCtrl1->Bind(wxEVT_KILL_FOCUS,[&](wxFocusEvent& event)
{
if(m_textCtrl1->GetValue() != "stop")
m_textCtrl1->SetFocus();
else
event.Skip();
});
Sizer->Add( m_textCtrl1, 0, wxALL, 5 );
m_textCtrl2 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
Sizer->Add( m_textCtrl2, 0, wxALL, 5 );
this->SetSizer( Sizer );
this->Layout();
this->Centre( wxBOTH );
}
protected:
wxTextCtrl* m_textCtrl1;
wxTextCtrl* m_textCtrl2;
};
class MyApp : public wxApp
{
public:
bool OnInit()
{
(new MyFrame)->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
Re: How to prevent a wxTextCtrl from loosing focus?
wxWidgets docs for wxFocusEvent has thisKvaz1r wrote:Why there is need to use CallAfter?
I believe CallAfter() is the modern equivalent of the delayed action mechanism using wxIdleEvent.Also note that wxEVT_KILL_FOCUS handler must not call wxWindow::SetFocus() as this, again, is not supported by all native controls. If you need to do this, consider using the Delayed Action Mechanism described in wxIdleEvent documentation.
It seems to work but handling focus is tricky and what works one one platform may not work on another. Here's the version with CallAfter():
Code: Select all
#include <wx/wx.h>
class MyDialog : public wxDialog
{
public:
MyDialog () : wxDialog(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(800, 600))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
mainSizer->Add(new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Expand().Border());
mainSizer->Add(new wxStaticText(this, wxID_ANY, "The next wxTextCtrl must contain \"ABC\""),
wxSizerFlags().Expand().Border(wxTOP | wxLEFT));
wxTextCtrl* textCtrl = new wxTextCtrl(this, wxID_ANY);
textCtrl->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);
mainSizer->Add(textCtrl, wxSizerFlags().Expand().Border());
mainSizer->Add(new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Expand().Border());
mainSizer->Add(new wxButton(this, wxID_ANY, "Button"), wxSizerFlags().Expand().Border());
wxTextCtrl* logCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));
mainSizer->Add(logCtrl, wxSizerFlags().Expand().Border().Proportion(1));
SetSizer(mainSizer);
}
private:
void OnKillFocus(wxFocusEvent& event)
{
event.Skip();
wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>(event.GetEventObject());
const wxString value = textCtrl->GetValue();
if ( value != "ABC" )
{
wxLogMessage("\"%s\" is not a valid value!", value);
CallAfter([textCtrl] { textCtrl->SetFocus(); } );
}
else
{
wxLogMessage("Value is valid!");
}
}
};
class MyApp : public wxApp
{
public:
bool OnInit()
{
MyDialog().ShowModal();
return false;
}
}; wxIMPLEMENT_APP(MyApp);
Re: How to prevent a wxTextCtrl from loosing focus?
Right, thanks for the explanation.PB wrote: wxWidgets docs for wxFocusEvent has thisI believe CallAfter() is the modern equivalent of the delayed action mechanism using wxIdleEvent.Also note that wxEVT_KILL_FOCUS handler must not call wxWindow::SetFocus() as this, again, is not supported by all native controls. If you need to do this, consider using the Delayed Action Mechanism described in wxIdleEvent documentation.
Re: How to prevent a wxTextCtrl from loosing focus?
The code created by Kvaz1r and myself is very similar and thus shares the same bug. At least on MSW, it crashes when the "protected" wxTextCtrl has focus and you try to exit the program by closing its window.
Therefore it seems that such situation must be handled, perhaps by disconnecting the event handler when the control is being destroyed.
Therefore it seems that such situation must be handled, perhaps by disconnecting the event handler when the control is being destroyed.
Re: How to prevent a wxTextCtrl from loosing focus?
Wow, thanks.
I'll try your suggestions right now.
The value of the textctrl will be sent to the device, and the user have to finish one "edit action" before do anything else.
The edit action ends (and the data sent) when the user aborts (ESC), or confirms (ENTER) a validated value.
Problems are:
I'll try your suggestions right now.
Kvaz1r wrote:Btw, I also don't see advantages for user in this behaviour.
This is true, and desired: the app is the control interface (running on a BBB with a touchscreen) for some devices on an automatic machine.PB wrote:TBH, I consider such behaviour not only rather unusual but also quite user hostile.
The value of the textctrl will be sent to the device, and the user have to finish one "edit action" before do anything else.
The edit action ends (and the data sent) when the user aborts (ESC), or confirms (ENTER) a validated value.
Problems are:
- there's multiple textctrl per page, multiple pages, and we want that each "edit action" ends correctly (the beginning of the edit requires the device to stop doing real-time tasks)
- Boss don't like popup/modal msgbox for data entry
Re: How to prevent a wxTextCtrl from loosing focus?
Adding an identical textctrl (all my textctrl behave the same) fires an infinite loop of Set/KillFocus.PB wrote: It seems to work but handling focus is tricky and what works one one platform may not work on another. Here's the version with CallAfter():
My solution is to store the pointer to the ctrl that will gain and immediatly loose the focus to make it skip the validation.
I don't like this so much.
Do you have better ideas?
Code: Select all
#include <wx/wx.h>
class MyDialog : public wxDialog
{
private:
wxWindow* SkipFocusCtrl;
public:
MyDialog () : wxDialog(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(800, 600))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
mainSizer->Add(new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Expand().Border());
mainSizer->Add(new wxStaticText(this, wxID_ANY, "The next wxTextCtrl must contain \"ABC\""),
wxSizerFlags().Expand().Border(wxTOP | wxLEFT));
wxTextCtrl* textCtrl1 = new wxTextCtrl(this, wxID_ANY);
textCtrl1->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);
mainSizer->Add(textCtrl1, wxSizerFlags().Expand().Border());
textCtrl1->SetValue("123");
wxTextCtrl* textCtrl2 = new wxTextCtrl(this, wxID_ANY);
textCtrl2->Bind(wxEVT_KILL_FOCUS, &MyDialog::OnKillFocus, this);
mainSizer->Add(textCtrl2, wxSizerFlags().Expand().Border());
textCtrl2->SetValue("456");
mainSizer->Add(new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Expand().Border());
mainSizer->Add(new wxButton(this, wxID_ANY, "Button"), wxSizerFlags().Expand().Border());
wxTextCtrl* logCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));
mainSizer->Add(logCtrl, wxSizerFlags().Expand().Border().Proportion(1));
SetSizer(mainSizer);
SkipFocusCtrl = nullptr;
}
private:
void OnKillFocus(wxFocusEvent& event)
{
event.Skip();
wxWindow* nextFocus = event.GetWindow();
wxObject* currFocus = event.GetEventObject();
wxLogMessage("%p is loosing focus for %p", currFocus, nextFocus);
if (SkipFocusCtrl==currFocus) {
wxLogMessage("Skipping focus control");
SkipFocusCtrl = nullptr;
return;
}
wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>(currFocus);
const wxString value = textCtrl->GetValue();
if ( value != "ABC" )
{
wxLogMessage("\"%s\" is not a valid value!", value);
SkipFocusCtrl = nextFocus;
CallAfter([textCtrl] { textCtrl->SetFocus(); } );
}
else
{
wxLogMessage("Value is valid!");
}
}
};
class MyApp : public wxApp
{
public:
bool OnInit()
{
MyDialog().ShowModal();
return false;
}
};
wxIMPLEMENT_APP(MyApp);
Re: How to prevent a wxTextCtrl from loosing focus?
Well, I am not surprised. You have multiple invalid text controls in the code and the condition that an invalid control must not lose focus.Parduz wrote:Adding an identical textctrl (all my textctrl behave the same) fires an infinite loop of Set/KillFocus.
In other words:
What the code actually does is not preventing losing focus, the control loses the focus but tries to get it back. However, in your case the text control from which the focus needs to be taken back is invalid as well and thus wants to take focus back as well. This is a situation which must be dealt with. I assumed it was clear that the code I posted is just a technical proof of concept showing a possible scenario.
In a real world application, the handling needs to be more complex, depending on the form design and logic.
It is also possible that this way is not the way to approach the problem at all, but I am not sure which other ways there are.
EDIT
Sorry, I have somehow missed that you do know why the infinite loop happens, so my explanation was not needed.
Re: How to prevent a wxTextCtrl from loosing focus?
Hi,
If the user still needs to either confirm with pressing Enter or deny with pressing ESC, why not leave this and do the check on the (presumably default) button click handler?
Or whatever Enter action is handled by.
This will be more natural...
Thank you.
If the user still needs to either confirm with pressing Enter or deny with pressing ESC, why not leave this and do the check on the (presumably default) button click handler?
Or whatever Enter action is handled by.
This will be more natural...
Thank you.
Re: How to prevent a wxTextCtrl from loosing focus?
'cause (as example) the user could press a "change page" button, an action that should send a series of command to the device to exit from the edit mode, switch page, notifiy the new page to the device, get all the data, etc...ONEEYEMAN wrote:Hi,
If the user still needs to either confirm with pressing Enter or deny with pressing ESC, why not leave this and do the check on the (presumably default) button click handler?
Or whatever Enter action is handled by.
This will be more natural...
It's feasible but it involve a whole lot of management, timing issue (that device is SLOW and the communication with it is via serial, etc) so SEEMS to me that forcing the user to finish what he's doing is better and simpler.
Re: How to prevent a wxTextCtrl from loosing focus?
I'm still not 100% sure how your GUI works, i'll assume something like this:
- there are several pages with text controls (any maybe other controls)
- in order for a certain function to get executed, all the controls on all pages must have valid data
In that case, i would solve it like this:
When the user clicks "execute" or "next page", the content of the text controls is checked. If they are not valid, display a wxRichToolTip pointing at the first text control with wrong data *and* give focus to that text control. I think it's better to show a tooltip instead of a message box, because it doesn't require an additional action by the user.
Additionally, you could mark all text controls that have invalid values with a different colored background or border.
- there are several pages with text controls (any maybe other controls)
- in order for a certain function to get executed, all the controls on all pages must have valid data
In that case, i would solve it like this:
When the user clicks "execute" or "next page", the content of the text controls is checked. If they are not valid, display a wxRichToolTip pointing at the first text control with wrong data *and* give focus to that text control. I think it's better to show a tooltip instead of a message box, because it doesn't require an additional action by the user.
Additionally, you could mark all text controls that have invalid values with a different colored background or border.
Use the source, Luke!
Re: How to prevent a wxTextCtrl from loosing focus?
This is true, but the real situation is much more complex (or messed up, if you want) and until prooven impossible or just unbelievable hard, i'll stick to the focus managementdoublemax wrote:I'm still not 100% sure how your GUI works, i'll assume something like this:
- there are several pages with text controls (any maybe other controls)
- in order for a certain function to get executed, all the controls on all pages must have valid data
In that case, i would solve it like this:
When the user clicks "execute" or "next page", the content of the text controls is checked. If they are not valid, display a wxRichToolTip pointing at the first text control with wrong data *and* give focus to that text control. I think it's better to show a tooltip instead of a message box, because it doesn't require an additional action by the user.
Additionally, you could mark all text controls that have invalid values with a different colored background or border.