From: Troels on
Vadim Zeitlin wrote:
>I usually use EVT_IDLE handler if I need to do something "soon after"
>window is shown on screen. It's not ideal and I'd welcome any better
>ideas but this is what we have for now.

Excellent tip. Certainly belongs in the faq
http://www.wxwidgets.org/docs/faq.htm
if it isn't already there.

Below is shown a fancy method to handle the "soon after" situation in a
fairly clean way.
(http://wxforum.shadonet.com/viewtopic.php?p=49422#49422)

// wxIdleOnceEvtHandler by Troels K 2006

extern wxEventType wxEVT_IDLE_ONCE;
#define EVT_IDLE_ONCE(func) wx__DECLARE_EVT0(wxEVT_IDLE_ONCE,
wxIdleEventHandler(func))

class wxIdleOnceEvtHandler : public wxEvtHandler
{
public:
bool m_fired;
wxEvtHandler* m_target;
wxIdleOnceEvtHandler(wxEvtHandler* target) : m_target(target),
wxEvtHandler(), m_fired(false)
{
}
virtual bool ProcessEvent(wxEvent&);
};
....
wxEventType wxEVT_IDLE_ONCE = wxNewEventType();

bool wxIdleOnceEvtHandler::ProcessEvent(wxEvent& event)
{
if ((event.GetEventType() == wxEVT_IDLE) && !m_fired)
{
m_fired = true;
wxIdleEvent& idle = (wxIdleEvent&)event;
wxIdleEvent temp(idle);
temp.SetEventType(wxEVT_IDLE_ONCE);
wxPostEvent(m_target, temp);
}
return wxEvtHandler::ProcessEvent(event);
}

....

MyWindow::MyWindow()
{
m_idleonce = new wxIdleOnceEvtHandler(this);
PushEventHandler(m_idleonce);
}

MyWindow::~MyWindow()
{
RemoveEventHandler(m_idleonce);
delete m_idleonce;
}

BEGIN_EVENT_TABLE(MyWindow, wxScrolledWindow)
EVT_IDLE_ONCE(MyWindow::OnIdleOnce)
END_EVENT_TABLE()

void MyWindow::OnIdleOnce(wxIdleEvent&)
{
OnInitialUpdate(); // or whatever
}


Vadim Zeitlin wrote:
> On Thu, 21 Sep 2006 18:13:24 +0200 Volker Bartheld <dr_versaeg(a)freenet.de> wrote:
>
> VB> I wonder at what point of time in the life of a wxWindow,
> VB> wxScrolledWindow or wxPanel the "default" values for width and height
> VB> (in case of wxScrolledWindow also the virtual values) reflect the
> VB> reality.
>
> They always reflect the reality. It's just that initially they're wrong
> and the window is only sized correctly after it's laid out correctly.
>
> VB> What do I have to do to be sure that GetSize()'s return value matches
> VB> the actual screen size?
>
> I usually use EVT_IDLE handler if I need to do something "soon after"
> window is shown on screen. It's not ideal and I'd welcome any better ideas
> but this is what we have for now.
>
> VB> I think, this recipe would also hold true for
> VB> wxScrolledWindow::GetVirtualSize, right?
>
> I don't think so, this one should return the "correct" size always.
>
> Regards,
> VZ
>
From: Volker Bartheld on
Hi Troels!

> >I usually use EVT_IDLE handler if I need to do something "soon after"
> >window is shown on screen.
>Below is shown a fancy method to handle the "soon after" situation in a
>fairly clean way.
>(http://wxforum.shadonet.com/viewtopic.php?p=49422#49422)

Actually this is a totally cool thing to do and it casually helped me
solve - or at least dramatically reduce - the "stolen focus issue"
(check out Message-ID: <4rro90Fse3o8U1(a)mid.individual.net> for more
info) I mentioned in the other thread.

As it seems, neither overriding wxWindow::AcceptsFocus() nor
catching/handling wxActivateEvent and returning wxEvent::Skip(true) or
giving back the focus with
FindWindow(wxActivateEvent::GetID())->SetFocus() did the trick.

On wxMac and wxLin, I couldn't reproduce this behaviour, so a Win32-only
solution was acceptable. I decided to remember the HWND of the current
foreground window via the native ::GetForegroundWindow() function in the
c'tor and then resetting the foreground window to the current state in
the OnIdleOnce() handler via ::SetForegroundWindow(). Anyway and whoever
is interested, I'm posting the code snippet at the bottom of this reply.

But I still have a question: Why do you create a new event type plus
handler for it and push this into the wxWindow's list event handlers?
Wouldn't it be good enough to

DECLARE_EVENT_TYPE(wxEVT_IDLEONCE, -1)
DEFINE_EVENT_TYPE(wxEVT_IDLEONCE)

, and then just add it in the c'tor

wxCommandEvent e(wxEVT_IDLEONCE, GetId());
AddPendingEvent(e);

and grab it via

EVT_COMMAND(wxID_ANY, wxEVT_IDLEONCE, MyFrame::OnIdleOnce)

? OK, anybody could post this event an make the handler fire false
positive - is it that what you wanted to avoid?

Thanks a lot for your hints,

Volker


<nofocus_frame.cpp>
#define EVT_IDLE_ONCE(func) wx__DECLARE_EVT0(wxEVT_IDLE_ONCE, wxIdleEventHandler(func))
static wxEventType wxEVT_IDLE_ONCE=wxNewEventType();
class wxIdleOnceEvtHandler : public wxEvtHandler
{
public:
bool m_fired;
wxEvtHandler* m_target;
wxIdleOnceEvtHandler(wxEvtHandler* target) : m_target(target), wxEvtHandler(), m_fired(false) {}
virtual bool wxIdleOnceEvtHandler::ProcessEvent(wxEvent& event)
{
if((event.GetEventType()==wxEVT_IDLE) && !m_fired)
{
m_fired=true;
wxIdleEvent& idle=(wxIdleEvent&)event;
wxIdleEvent temp(idle);
temp.SetEventType(wxEVT_IDLE_ONCE);
wxPostEvent(m_target, temp);
}
return wxEvtHandler::ProcessEvent(event);
}
};

class CMyDialog : public wxFrame
{
DECLARE_DYNAMIC_CLASS(CMyDialog)
public:
CMyDialog::CMyDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize, long style=wxSIMPLE_BORDER|wxSTAY_ON_TOP|wxFRAME_NO_TASKBAR|wxSUNKEN_BORDER, const wxString& name=wxT("dialogBox"))
: wxFrame(parent, id, title, pos, size, style, name), m_idleonce(NULL), m_hWndFocus(NULL)
{
m_idleonce=new wxIdleOnceEvtHandler(this);
PushEventHandler(m_idleonce);

SetSizer(new wxBoxSizer(wxVERTICAL));
wxBoxSizer* pTextSizer=new wxBoxSizer(wxHORIZONTAL);
pTextSizer->Add(new wxTextCtrl(this, wxID_ANY, wxT("Text here...")), 0, wxALL, 4);
pTextSizer->AddStretchSpacer();
pTextSizer->Add(new wxButton(this, ID_OK, wxT("OK")), 0, wxALL, 4);
GetSizer()->Add(pTextSizer, 1, wxEXPAND | wxALL, 4);
Fit();
CenterOnScreen();
m_hWndFocus=::GetForegroundWindow();
Show();
}
CMyDialog::~CMyDialog()
{
RemoveEventHandler(m_idleonce);
delete m_idleonce;
}
void CMyDialog::OnOK(wxCommandEvent& event)
{
wxCommandEvent e(wxEVT_END_DIALOG, GetId());
GetParent()->AddPendingEvent(e);
Destroy();
}
void CMyDialog::OnIdleOnce(wxIdleEvent&) { ::SetForegroundWindow(m_hWndFocus); }
private:
DECLARE_EVENT_TABLE()
CMyDialog::CMyDialog() {}
wxIdleOnceEvtHandler* m_idleonce;
HWND m_hWndFocus;
};
IMPLEMENT_DYNAMIC_CLASS(CMyDialog, wxFrame)
BEGIN_EVENT_TABLE(CMyDialog, wxFrame)
EVT_BUTTON(ID_OK, CMyDialog::OnOK)
EVT_BUTTON(ID_CANCEL, CMyDialog::OnOK)
EVT_IDLE_ONCE(CMyDialog::OnIdleOnce)
END_EVENT_TABLE()
</nofocus_frame.cpp>

__
Mail replies to/an V B A R T H E L D at G M X dot D E
From: Troels on
Hi Volker

> But I still have a question: Why do you create a new event type plus
> handler for it and push this into the wxWindow's list event handlers?
> Wouldn't it be good enough to

It's to stand on the shoulders of EVT_IDLE, piggybacking it, letting the
idle mechanism make the decision of when things are stable. I should
think it's the safer way to do it, blessed by Vadim n all.
I've rearranged the code a little:
http://wxforum.shadonet.com/viewtopic.php?p=49422#49422
Now it itself takes care of calling RemoveEventHandler.

Greetings
Troels


Volker Bartheld wrote:
> Hi Troels!
>
>>> I usually use EVT_IDLE handler if I need to do something "soon after"
>>> window is shown on screen.
>> Below is shown a fancy method to handle the "soon after" situation in a
>> fairly clean way.
>> (http://wxforum.shadonet.com/viewtopic.php?p=49422#49422)
>
> Actually this is a totally cool thing to do and it casually helped me
> solve - or at least dramatically reduce - the "stolen focus issue"
> (check out Message-ID: <4rro90Fse3o8U1(a)mid.individual.net> for more
> info) I mentioned in the other thread.
>
> As it seems, neither overriding wxWindow::AcceptsFocus() nor
> catching/handling wxActivateEvent and returning wxEvent::Skip(true) or
> giving back the focus with
> FindWindow(wxActivateEvent::GetID())->SetFocus() did the trick.
>
> On wxMac and wxLin, I couldn't reproduce this behaviour, so a Win32-only
> solution was acceptable. I decided to remember the HWND of the current
> foreground window via the native ::GetForegroundWindow() function in the
> c'tor and then resetting the foreground window to the current state in
> the OnIdleOnce() handler via ::SetForegroundWindow(). Anyway and whoever
> is interested, I'm posting the code snippet at the bottom of this reply.
>
> But I still have a question: Why do you create a new event type plus
> handler for it and push this into the wxWindow's list event handlers?
> Wouldn't it be good enough to
>
> DECLARE_EVENT_TYPE(wxEVT_IDLEONCE, -1)
> DEFINE_EVENT_TYPE(wxEVT_IDLEONCE)
>
> , and then just add it in the c'tor
>
> wxCommandEvent e(wxEVT_IDLEONCE, GetId());
> AddPendingEvent(e);
>
> and grab it via
>
> EVT_COMMAND(wxID_ANY, wxEVT_IDLEONCE, MyFrame::OnIdleOnce)
>
> ? OK, anybody could post this event an make the handler fire false
> positive - is it that what you wanted to avoid?
>
> Thanks a lot for your hints,
>
> Volker
>
>
> <nofocus_frame.cpp>
> #define EVT_IDLE_ONCE(func) wx__DECLARE_EVT0(wxEVT_IDLE_ONCE, wxIdleEventHandler(func))
> static wxEventType wxEVT_IDLE_ONCE=wxNewEventType();
> class wxIdleOnceEvtHandler : public wxEvtHandler
> {
> public:
> bool m_fired;
> wxEvtHandler* m_target;
> wxIdleOnceEvtHandler(wxEvtHandler* target) : m_target(target), wxEvtHandler(), m_fired(false) {}
> virtual bool wxIdleOnceEvtHandler::ProcessEvent(wxEvent& event)
> {
> if((event.GetEventType()==wxEVT_IDLE) && !m_fired)
> {
> m_fired=true;
> wxIdleEvent& idle=(wxIdleEvent&)event;
> wxIdleEvent temp(idle);
> temp.SetEventType(wxEVT_IDLE_ONCE);
> wxPostEvent(m_target, temp);
> }
> return wxEvtHandler::ProcessEvent(event);
> }
> };
>
> class CMyDialog : public wxFrame
> {
> DECLARE_DYNAMIC_CLASS(CMyDialog)
> public:
> CMyDialog::CMyDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize, long style=wxSIMPLE_BORDER|wxSTAY_ON_TOP|wxFRAME_NO_TASKBAR|wxSUNKEN_BORDER, const wxString& name=wxT("dialogBox"))
> : wxFrame(parent, id, title, pos, size, style, name), m_idleonce(NULL), m_hWndFocus(NULL)
> {
> m_idleonce=new wxIdleOnceEvtHandler(this);
> PushEventHandler(m_idleonce);
>
> SetSizer(new wxBoxSizer(wxVERTICAL));
> wxBoxSizer* pTextSizer=new wxBoxSizer(wxHORIZONTAL);
> pTextSizer->Add(new wxTextCtrl(this, wxID_ANY, wxT("Text here...")), 0, wxALL, 4);
> pTextSizer->AddStretchSpacer();
> pTextSizer->Add(new wxButton(this, ID_OK, wxT("OK")), 0, wxALL, 4);
> GetSizer()->Add(pTextSizer, 1, wxEXPAND | wxALL, 4);
> Fit();
> CenterOnScreen();
> m_hWndFocus=::GetForegroundWindow();
> Show();
> }
> CMyDialog::~CMyDialog()
> {
> RemoveEventHandler(m_idleonce);
> delete m_idleonce;
> }
> void CMyDialog::OnOK(wxCommandEvent& event)
> {
> wxCommandEvent e(wxEVT_END_DIALOG, GetId());
> GetParent()->AddPendingEvent(e);
> Destroy();
> }
> void CMyDialog::OnIdleOnce(wxIdleEvent&) { ::SetForegroundWindow(m_hWndFocus); }
> private:
> DECLARE_EVENT_TABLE()
> CMyDialog::CMyDialog() {}
> wxIdleOnceEvtHandler* m_idleonce;
> HWND m_hWndFocus;
> };
> IMPLEMENT_DYNAMIC_CLASS(CMyDialog, wxFrame)
> BEGIN_EVENT_TABLE(CMyDialog, wxFrame)
> EVT_BUTTON(ID_OK, CMyDialog::OnOK)
> EVT_BUTTON(ID_CANCEL, CMyDialog::OnOK)
> EVT_IDLE_ONCE(CMyDialog::OnIdleOnce)
> END_EVENT_TABLE()
> </nofocus_frame.cpp>
>
> __
> Mail replies to/an V B A R T H E L D at G M X dot D E
From: Volker Bartheld on
Hi Troels!

> > But I still have a question: Why do you create a new event type plus
> > handler for it and push this into the wxWindow's list event handlers?
> > Wouldn't it be good enough to

>It's to stand on the shoulders of EVT_IDLE, piggybacking it, letting the
>idle mechanism make the decision of when things are stable.

OK, understood. That makes perfect sense.

>I've rearranged the code a little:
>http://wxforum.shadonet.com/viewtopic.php?p=49422#49422
>Now it itself takes care of calling RemoveEventHandler.

You might also want it to take care of automatically inserting itself in
the list of the parent's event handlers. Here's the code - it's
basically the same approach in the c'tor as you've taken it in the
d'tor. So just a m_idleonce=new wxIdleOnceEvtHandler(this); in the
parent's c'tor and a delete m_idleonce; in the d'tor should be enough.

<idleonce.cpp>
#define EVT_IDLE_ONCE(func) wx__DECLARE_EVT0(wxEVT_IDLE_ONCE, wxIdleEventHandler(func))
static wxEventType wxEVT_IDLE_ONCE=wxNewEventType();

/**
* define a new handler type that will deal with the wxEVT_IDLE event when it arrives in the queue for the *FIRST* time
*/
class wxIdleOnceEvtHandler : public wxEvtHandler
{
public:
wxEvtHandler* m_target;
wxIdleOnceEvtHandler::wxIdleOnceEvtHandler(wxEvtHandler* target) : m_target(target), wxEvtHandler()
{
if(wxIS_KIND_OF(m_target, wxWindow) && GetEvtHandlerEnabled()) ((wxWindow*)m_target)->PushEventHandler(this); // push event handler into parent's list
}
void wxIdleOnceEvtHandler::Stop(void)
{
if(wxIS_KIND_OF(m_target, wxWindow) && GetEvtHandlerEnabled()) ((wxWindow*)m_target)->RemoveEventHandler(this); // remove handler from parent's list
SetEvtHandlerEnabled(false);
} // void wxIdleOnceEvtHandler::Stop(void)
wxIdleOnceEvtHandler::~wxIdleOnceEvtHandler() { Stop(); }
bool wxIdleOnceEvtHandler::ProcessEvent(wxEvent& event) // event handle "hook"
{
if(wxEVT_IDLE==event.GetEventType() && GetEvtHandlerEnabled()) // is it the wxEVT_IDLE and it arrived for the first time (event handler still enabled)?
{
Stop(); // stop the handler
wxEvent* temp=event.Clone(); // duplicate the event
temp->SetEventType(wxEVT_IDLE_ONCE); // set correct event type
wxPostEvent(m_target, *temp); // and reinsert it into parent's queue
delete temp;
} // if(wxEVT_IDLE==event.GetEventType() && GetEvtHandlerEnabled())
return wxEvtHandler::ProcessEvent(event);
} // bool wxIdleOnceEvtHandler::ProcessEvent(wxEvent& event)
};
</idleonce.cpp>

HTH & happy coding,
Volker
__
Mail replies to/an V B A R T H E L D at G M X dot D E
From: Troels on
Hi again

> You might also want it to take care of automatically inserting itself
> in the list of the parent's event handlers. Here's the code

Right! I've rearranged the code again
http://wxforum.shadonet.com/viewtopic.php?p=49422#49422
Gotten rid of the casts by overloading the constructor, in order to
still be able to handle both window and non-window wxEvtHandler's.

Cheers!
Troels


Volker Bartheld wrote:
> Hi Troels!
>
>>> But I still have a question: Why do you create a new event type plus
>>> handler for it and push this into the wxWindow's list event handlers?
>>> Wouldn't it be good enough to
>
>> It's to stand on the shoulders of EVT_IDLE, piggybacking it, letting the
>> idle mechanism make the decision of when things are stable.
>
> OK, understood. That makes perfect sense.
>
>> I've rearranged the code a little:
>> http://wxforum.shadonet.com/viewtopic.php?p=49422#49422
>> Now it itself takes care of calling RemoveEventHandler.
>
> You might also want it to take care of automatically inserting itself in
> the list of the parent's event handlers. Here's the code - it's
> basically the same approach in the c'tor as you've taken it in the
> d'tor. So just a m_idleonce=new wxIdleOnceEvtHandler(this); in the
> parent's c'tor and a delete m_idleonce; in the d'tor should be enough.
>
> <idleonce.cpp>
> #define EVT_IDLE_ONCE(func) wx__DECLARE_EVT0(wxEVT_IDLE_ONCE, wxIdleEventHandler(func))
> static wxEventType wxEVT_IDLE_ONCE=wxNewEventType();
>
> /**
> * define a new handler type that will deal with the wxEVT_IDLE event when it arrives in the queue for the *FIRST* time
> */
> class wxIdleOnceEvtHandler : public wxEvtHandler
> {
> public:
> wxEvtHandler* m_target;
> wxIdleOnceEvtHandler::wxIdleOnceEvtHandler(wxEvtHandler* target) : m_target(target), wxEvtHandler()
> {
> if(wxIS_KIND_OF(m_target, wxWindow) && GetEvtHandlerEnabled()) ((wxWindow*)m_target)->PushEventHandler(this); // push event handler into parent's list
> }
> void wxIdleOnceEvtHandler::Stop(void)
> {
> if(wxIS_KIND_OF(m_target, wxWindow) && GetEvtHandlerEnabled()) ((wxWindow*)m_target)->RemoveEventHandler(this); // remove handler from parent's list
> SetEvtHandlerEnabled(false);
> } // void wxIdleOnceEvtHandler::Stop(void)
> wxIdleOnceEvtHandler::~wxIdleOnceEvtHandler() { Stop(); }
> bool wxIdleOnceEvtHandler::ProcessEvent(wxEvent& event) // event handle "hook"
> {
> if(wxEVT_IDLE==event.GetEventType() && GetEvtHandlerEnabled()) // is it the wxEVT_IDLE and it arrived for the first time (event handler still enabled)?
> {
> Stop(); // stop the handler
> wxEvent* temp=event.Clone(); // duplicate the event
> temp->SetEventType(wxEVT_IDLE_ONCE); // set correct event type
> wxPostEvent(m_target, *temp); // and reinsert it into parent's queue
> delete temp;
> } // if(wxEVT_IDLE==event.GetEventType() && GetEvtHandlerEnabled())
> return wxEvtHandler::ProcessEvent(event);
> } // bool wxIdleOnceEvtHandler::ProcessEvent(wxEvent& event)
> };
> </idleonce.cpp>
>
> HTH & happy coding,
> Volker
> __
> Mail replies to/an V B A R T H E L D at G M X dot D E