wxEVT_SET_FOCUS on Linux Topic is solved

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
gmapp
Earned a small fee
Earned a small fee
Posts: 10
Joined: Wed Jan 24, 2018 4:39 pm

wxEVT_SET_FOCUS on Linux

Post by gmapp » Wed Oct 16, 2019 9:05 am

I have a main frame that opens with a button a new frame. On Linux, I cannot detect when the second frame gets the focus. On Windows, it works fine.
a) Is there an error in my code?
b) Is it a known bug?
c) Is there a workaround?

Thanks for the help

Code: Select all

NewFrame::NewFrame(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size){
	//(*Initialize(NewFrame)
	Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
	SetClientSize(wxDefaultSize);
	Move(wxDefaultPosition);
	Connect(wxEVT_SET_FOCUS,(wxObjectEventFunction)&NewFrame::OnSetFocus);
	//*)
}

void NewFrame::OnSetFocus(wxFocusEvent& event){
  //wxMessageBox("abc");
  std::cout <<"focus" <<std::endl;
  event.Skip();
}
Linux Mint
wxWidgets 3.1.1-Linux-Unicode build
Code::Blocks + wxSmith

DavidHart
Site Admin
Site Admin
Posts: 3898
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxEVT_SET_FOCUS on Linux

Post by DavidHart » Wed Oct 16, 2019 11:42 am

Hi,

There are two possible issues here:
1) Focus events are not derived from wxCommandEvent, and so do not propagate to parent controls; at least they aren't supposed to, though nobody seems to have told Windows... So if there are child controls in NewFrame, any focus events they get will not be seen by your Connect() (btw, why not use the modern Bind(), which is less likely to fail silently).

2) wxFrames exist to 'frame' child controls, and are reluctant to receive focus themselves. So, even without child controls, NewFrame will probably not receive focus and so will not send the event.

I expect the posted code is just a minimal demonstration but, even if you really want an empty frame, it's best to give it a wxPanel as its sole child control (no need for a wxSizer). And if the frame does have child controls, that's still recommended: give that panel a sizer and do everything else inside it, parented by the sizer.

You can then Bind() the control (that panel, or one of its children) which is most likely actually to receive focus. If you want to catch the event in NewFrame, in NewFrame's constructor do: pChild->Bind( wxEVT_SET_FOCUS, &NewFrame::OnSetFocus, this );

Regards,

David

gmapp
Earned a small fee
Earned a small fee
Posts: 10
Joined: Wed Jan 24, 2018 4:39 pm

Re: wxEVT_SET_FOCUS on Linux

Post by gmapp » Thu Oct 17, 2019 9:54 am

Thanks for the reply David,

Yes, my codes are minimal problem demostrations. I don't need an empty frame.
I followed your tips and tried to use Bind for the main panel and for a button. The first, the most important for me, doesn't work.

Hier the source code

Code: Select all

NewFrame::NewFrame(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size){
	//(*Initialize(NewFrame)
	wxGridSizer* GridSizer1;

	Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("wxID_ANY"));
	Panel1 = new wxPanel(this, ID_PANEL1, wxPoint(168,168), wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
	GridSizer1 = new wxGridSizer(0, 1, 0, 0);
	StaticText1 = new wxStaticText(Panel1, ID_STATICTEXT1, _("Test"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
	GridSizer1->Add(StaticText1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	TextCtrl1 = new wxTextCtrl(Panel1, ID_TEXTCTRL1, _("Text"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_TEXTCTRL1"));
	GridSizer1->Add(TextCtrl1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	Button1 = new wxButton(Panel1, ID_BUTTON1, _("Label"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
	GridSizer1->Add(Button1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
	Panel1->SetSizer(GridSizer1);
	GridSizer1->Fit(Panel1);
	GridSizer1->SetSizeHints(Panel1);
	//*)

	Panel1->Bind(wxEVT_SET_FOCUS, &NewFrame::OnSetFocus, this);
	Button1->Bind(wxEVT_SET_FOCUS, &NewFrame::OnSetFocus, this);
}

DavidHart
Site Admin
Site Admin
Posts: 3898
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxEVT_SET_FOCUS on Linux

Post by DavidHart » Thu Oct 17, 2019 10:53 am

use Bind for the main panel and for a button. The first, the most important for me, doesn't work
You mean that the button Bind() does? That, or the textctrl, is what I'd expect to have focus. If you really must use the panel, you could try wxPanel::SetFocusIgnoringChildren.

But you should probably be not be trying to catch wxFocusEvents anyway; instead use wxActivateEvent.

gmapp
Earned a small fee
Earned a small fee
Posts: 10
Joined: Wed Jan 24, 2018 4:39 pm

Re: wxEVT_SET_FOCUS on Linux

Post by gmapp » Thu Oct 17, 2019 12:25 pm

You mean that the button Bind() does?
Yes, it worked.

But I tested the following and it sounds like a good workaround in my case.

Code: Select all

 BEGIN_EVENT_TABLE(NewFrame,wxFrame)
    //(*EventTable(NewFrame)
    EVT_ACTIVATE(NewFrame::OnSetFocus1)
    //*)
END_EVENT_TABLE()

void NewFrame::OnSetFocus1(wxActivateEvent& event){
  std::cout <<"focus1" <<std::endl;
  event.Skip();
}
If I swich the focus, clicking on the MainFrame and the NewFrame, the OnSetFocus1 runs! =D>

The solution is not perfect because it runs three times when I click on NewFrame, and also when I click on the MainFrame #-o

I tried to use:

Code: Select all

Panel1->Bind(wxEVT_ACTIVATE, &NewFrame::OnSetFocus1, this);
instead of:

Code: Select all

EVT_ACTIVATE(NewFrame::OnSetFocus1)
but it was not a good idea. OnSetFocus1 didn't run anymore.

Thanks

DavidHart
Site Admin
Site Admin
Posts: 3898
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxEVT_SET_FOCUS on Linux

Post by DavidHart » Thu Oct 17, 2019 1:18 pm

If I switch the focus, clicking on the MainFrame and the NewFrame, the OnSetFocus1 runs!
Good.
The solution is not perfect because it runs three times when I click on NewFrame, and also when I click on the MainFrame
I don't know why 3 times, but you can reduce the amount of spam by testing event->GetActive().
Panel1->Bind(wxEVT_ACTIVATE, &NewFrame::OnSetFocus1, this);
IIUC wxActiveEvents go to top-level windows, so that should have been:
Bind(wxEVT_ACTIVATE, &NewFrame::OnSetFocus1, this);

gmapp
Earned a small fee
Earned a small fee
Posts: 10
Joined: Wed Jan 24, 2018 4:39 pm

Re: wxEVT_SET_FOCUS on Linux

Post by gmapp » Tue Oct 22, 2019 1:30 pm

The event.GetActive() reduces the noise :D
Now the code works perfectly:
- when the NewFrame is created by the MainFrame button and receives the focus
- when I switch the focus using the keyboard
- correctly OnSetFocus1() doesn't run when I click/select the MainFrame

If I select the NewFrame using the mouse, or if I move (drag and drop) the frame the function runs two times but it is OK for me.

Thanks for the support :-)

Code: Select all

void NewFrame::OnSetFocus1(wxActivateEvent& event){
  if(event.GetActive()){
    std::cout <<"focus1" <<std::endl;
  }
  event.Skip();
}

Post Reply