Refactor the event processing code to add ProcessEventLocally().
This new method can be used to only process the event in this handler or any handlers connected to it (unlike ProcessEventHere() which doesn't follow the chain at all), without propagating the event upwards (unlike ProcessEvent()). Unfortunately implementing this required a field to wxEvent but there doesn't seem to be any other way to do what we need. There should be no user-visible changes after this commit, it just paves the way for the upcoming fixes. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64261 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
parent
255ea4a702
commit
3cbb9df817
@ -996,6 +996,11 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is also used only internally by ProcessEvent() to check if it
|
||||||
|
// should process the event normally or only restrict the search for the
|
||||||
|
// event handler to this object itself.
|
||||||
|
bool ShouldProcessHereOnly() const { return m_processHereOnly; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
wxObject* m_eventObject;
|
wxObject* m_eventObject;
|
||||||
wxEventType m_eventType;
|
wxEventType m_eventType;
|
||||||
@ -1020,6 +1025,11 @@ protected:
|
|||||||
// once for this event
|
// once for this event
|
||||||
bool m_wasProcessed;
|
bool m_wasProcessed;
|
||||||
|
|
||||||
|
// this flag is used by ProcessEventLocally() to prevent ProcessEvent()
|
||||||
|
// from doing its usual stuff and force it to just call ProcessEventHere()
|
||||||
|
// instead, see the comment there explaining why is this needed
|
||||||
|
bool m_processHereOnly;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
wxEvent(const wxEvent&); // for implementing Clone()
|
wxEvent(const wxEvent&); // for implementing Clone()
|
||||||
wxEvent& operator=(const wxEvent&); // for derived classes operator=()
|
wxEvent& operator=(const wxEvent&); // for derived classes operator=()
|
||||||
@ -1028,6 +1038,10 @@ private:
|
|||||||
// it needs to access our m_propagationLevel
|
// it needs to access our m_propagationLevel
|
||||||
friend class WXDLLIMPEXP_FWD_BASE wxPropagateOnce;
|
friend class WXDLLIMPEXP_FWD_BASE wxPropagateOnce;
|
||||||
|
|
||||||
|
// and this one needs to access our m_processHereOnly
|
||||||
|
friend class WXDLLIMPEXP_FWD_BASE wxEventProcessHereOnly;
|
||||||
|
|
||||||
|
|
||||||
DECLARE_ABSTRACT_CLASS(wxEvent)
|
DECLARE_ABSTRACT_CLASS(wxEvent)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1079,6 +1093,32 @@ private:
|
|||||||
wxDECLARE_NO_COPY_CLASS(wxPropagateOnce);
|
wxDECLARE_NO_COPY_CLASS(wxPropagateOnce);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A helper used by ProcessEventLocally() to restrict the event processing
|
||||||
|
// to this handler only.
|
||||||
|
class WXDLLIMPEXP_BASE wxEventProcessHereOnly
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
wxEventProcessHereOnly(wxEvent& event) : m_event(event)
|
||||||
|
{
|
||||||
|
// This would be unexpected and would also restore the wrong value in
|
||||||
|
// this class dtor so if even does happen legitimately we'd need to
|
||||||
|
// store the value in ctor and restore it in dtor.
|
||||||
|
wxASSERT_MSG( !m_event.m_processHereOnly,
|
||||||
|
"shouldn't be used twice for the same event" );
|
||||||
|
|
||||||
|
m_event.m_processHereOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~wxEventProcessHereOnly()
|
||||||
|
{
|
||||||
|
m_event.m_processHereOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxEvent& m_event;
|
||||||
|
|
||||||
|
wxDECLARE_NO_COPY_CLASS(wxEventProcessHereOnly);
|
||||||
|
};
|
||||||
|
|
||||||
#if wxUSE_GUI
|
#if wxUSE_GUI
|
||||||
|
|
||||||
@ -2966,6 +3006,20 @@ public:
|
|||||||
bool SafelyProcessEvent(wxEvent& event);
|
bool SafelyProcessEvent(wxEvent& event);
|
||||||
// NOTE: uses ProcessEvent()
|
// NOTE: uses ProcessEvent()
|
||||||
|
|
||||||
|
// This method tries to process the event in this event handler, including
|
||||||
|
// any preprocessing done by TryBefore() and all the handlers chained to
|
||||||
|
// it, but excluding the post-processing done in TryAfter().
|
||||||
|
//
|
||||||
|
// It is meant to be called from ProcessEvent() only and is not virtual,
|
||||||
|
// additional event handlers can be hooked into the normal event processing
|
||||||
|
// logic using TryBefore() and TryAfter() hooks.
|
||||||
|
//
|
||||||
|
// You can also call it yourself to forward an event to another handler but
|
||||||
|
// without propagating it upwards if it's unhandled (this is usually
|
||||||
|
// unwanted when forwarding as the original handler would already do it if
|
||||||
|
// needed normally).
|
||||||
|
bool ProcessEventLocally(wxEvent& event);
|
||||||
|
|
||||||
// Schedule the given event to be processed later. It takes ownership of
|
// Schedule the given event to be processed later. It takes ownership of
|
||||||
// the event pointer, i.e. it will be deleted later. This is safe to call
|
// the event pointer, i.e. it will be deleted later. This is safe to call
|
||||||
// from multiple threads although you still need to ensure that wxString
|
// from multiple threads although you still need to ensure that wxString
|
||||||
@ -3180,9 +3234,8 @@ public:
|
|||||||
|
|
||||||
// The method tries to process the event in this event handler.
|
// The method tries to process the event in this event handler.
|
||||||
//
|
//
|
||||||
// It is meant to be called from ProcessEvent() only and is not virtual,
|
// It is called from ProcessEventLocally() and normally shouldn't be called
|
||||||
// additional event handlers can be hooked into the normal event processing
|
// directly as doing it would ignore any chained event handlers.
|
||||||
// logic using TryBefore() and TryAfter() hooks.
|
|
||||||
bool ProcessEventHere(wxEvent& event);
|
bool ProcessEventHere(wxEvent& event);
|
||||||
|
|
||||||
|
|
||||||
@ -3274,6 +3327,9 @@ private:
|
|||||||
// pass the event to wxTheApp instance, called from TryAfter()
|
// pass the event to wxTheApp instance, called from TryAfter()
|
||||||
bool DoTryApp(wxEvent& event);
|
bool DoTryApp(wxEvent& event);
|
||||||
|
|
||||||
|
// try to process events in all handlers chained to this one
|
||||||
|
bool DoTryChain(wxEvent& event);
|
||||||
|
|
||||||
DECLARE_DYNAMIC_CLASS_NO_COPY(wxEvtHandler)
|
DECLARE_DYNAMIC_CLASS_NO_COPY(wxEvtHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -489,8 +489,8 @@ public:
|
|||||||
processed, ProcessEvent() on wxTheApp object is called as the last
|
processed, ProcessEvent() on wxTheApp object is called as the last
|
||||||
step.
|
step.
|
||||||
|
|
||||||
Notice that steps (3)-(5) are performed in ProcessEventHere() which is
|
Notice that steps (2)-(6) are performed in ProcessEventLocally()
|
||||||
called by this function.
|
which is called by this function.
|
||||||
|
|
||||||
@param event
|
@param event
|
||||||
Event to process.
|
Event to process.
|
||||||
@ -502,14 +502,43 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool ProcessEvent(wxEvent& event);
|
virtual bool ProcessEvent(wxEvent& event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Try to process the event in this handler and all those chained to it.
|
||||||
|
|
||||||
|
As explained in ProcessEvent() documentation, the event handlers may be
|
||||||
|
chained in a doubly-linked list. This function tries to process the
|
||||||
|
event in this handler (including performing any pre-processing done in
|
||||||
|
TryBefore(), e.g. applying validators) and all those following it in
|
||||||
|
the chain until the event is processed or the chain is exhausted.
|
||||||
|
|
||||||
|
This function is called from ProcessEvent() and, in turn, calls
|
||||||
|
ProcessEventHere() for each handler in turn. It is not virtual and so
|
||||||
|
cannot be overridden but can, and should, be called to forward an event
|
||||||
|
to another handler instead of ProcessEvent() which would result in a
|
||||||
|
duplicate call to TryAfter(), e.g. resulting in all unprocessed events
|
||||||
|
being sent to the application object multiple times.
|
||||||
|
|
||||||
|
@since 2.9.1
|
||||||
|
|
||||||
|
@param event
|
||||||
|
Event to process.
|
||||||
|
@return
|
||||||
|
@true if this handler of one of those chained to it processed the
|
||||||
|
event.
|
||||||
|
*/
|
||||||
|
bool ProcessEventLocally(wxEvent& event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Try to process the event in this event handler.
|
Try to process the event in this event handler.
|
||||||
|
|
||||||
This method is called from ProcessEvent(), please see the detailed
|
This method is called from ProcessEventLocally() and thus,
|
||||||
description of the event processing logic there.
|
indirectly, from ProcessEvent(), please see the detailed description of
|
||||||
|
the event processing logic there.
|
||||||
|
|
||||||
It is @em not virtual and so may not be overridden.
|
It is @em not virtual and so may not be overridden.
|
||||||
|
|
||||||
|
@since 2.9.1
|
||||||
|
|
||||||
@param event
|
@param event
|
||||||
Event to process.
|
Event to process.
|
||||||
@return
|
@return
|
||||||
|
@ -366,6 +366,7 @@ wxEvent::wxEvent(int theId, wxEventType commandType)
|
|||||||
m_isCommandEvent = false;
|
m_isCommandEvent = false;
|
||||||
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
|
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
|
||||||
m_wasProcessed = false;
|
m_wasProcessed = false;
|
||||||
|
m_processHereOnly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxEvent::wxEvent(const wxEvent& src)
|
wxEvent::wxEvent(const wxEvent& src)
|
||||||
@ -379,6 +380,7 @@ wxEvent::wxEvent(const wxEvent& src)
|
|||||||
, m_skipped(src.m_skipped)
|
, m_skipped(src.m_skipped)
|
||||||
, m_isCommandEvent(src.m_isCommandEvent)
|
, m_isCommandEvent(src.m_isCommandEvent)
|
||||||
, m_wasProcessed(false)
|
, m_wasProcessed(false)
|
||||||
|
, m_processHereOnly(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,7 +397,7 @@ wxEvent& wxEvent::operator=(const wxEvent& src)
|
|||||||
m_skipped = src.m_skipped;
|
m_skipped = src.m_skipped;
|
||||||
m_isCommandEvent = src.m_isCommandEvent;
|
m_isCommandEvent = src.m_isCommandEvent;
|
||||||
|
|
||||||
// don't change m_wasProcessed
|
// don't change m_wasProcessed nor m_processHereOnly
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -1330,6 +1332,18 @@ bool wxEvtHandler::TryBefore(wxEvent& event)
|
|||||||
|
|
||||||
bool wxEvtHandler::TryAfter(wxEvent& event)
|
bool wxEvtHandler::TryAfter(wxEvent& event)
|
||||||
{
|
{
|
||||||
|
// We only want to pass the window to the application object once even if
|
||||||
|
// there are several chained handlers. Ensure that this is what happens by
|
||||||
|
// only calling DoTryApp() if there is no next handler (which would do it).
|
||||||
|
//
|
||||||
|
// Notice that, unlike simply calling TryAfter() on the last handler in the
|
||||||
|
// chain only from ProcessEvent(), this also works with wxWindow object in
|
||||||
|
// the middle of the chain: its overridden TryAfter() will still be called
|
||||||
|
// and propagate the event upwards the window hierarchy even if it's not
|
||||||
|
// the last one in the chain (which, admittedly, shouldn't happen often).
|
||||||
|
if ( GetNextHandler() )
|
||||||
|
return GetNextHandler()->TryAfter(event);
|
||||||
|
|
||||||
#if WXWIN_COMPATIBILITY_2_8
|
#if WXWIN_COMPATIBILITY_2_8
|
||||||
// as above, call the old virtual function for compatibility
|
// as above, call the old virtual function for compatibility
|
||||||
return TryParent(event);
|
return TryParent(event);
|
||||||
@ -1340,11 +1354,12 @@ bool wxEvtHandler::TryAfter(wxEvent& event)
|
|||||||
|
|
||||||
bool wxEvtHandler::ProcessEvent(wxEvent& event)
|
bool wxEvtHandler::ProcessEvent(wxEvent& event)
|
||||||
{
|
{
|
||||||
// allow the application to hook into event processing
|
// The very first thing we do is to allow the application to hook into
|
||||||
|
// event processing in order to globally pre-process all events.
|
||||||
//
|
//
|
||||||
// note that we should only do it if we're the first event handler called
|
// Note that we should only do it if we're the first event handler called
|
||||||
// to avoid calling FilterEvent() multiple times as the event goes through
|
// to avoid calling FilterEvent() multiple times as the event goes through
|
||||||
// the event handler chain and possibly upwards the window hierarchy
|
// the event handler chain and possibly upwards the window hierarchy.
|
||||||
if ( !event.WasProcessed() )
|
if ( !event.WasProcessed() )
|
||||||
{
|
{
|
||||||
if ( wxTheApp )
|
if ( wxTheApp )
|
||||||
@ -1361,22 +1376,72 @@ bool wxEvtHandler::ProcessEvent(wxEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try the hooks which should be called before our own handlers
|
// Short circuit the event processing logic if we're requested to process
|
||||||
|
// this event in this handler only, see DoTryChain() for more details.
|
||||||
|
if ( event.ShouldProcessHereOnly() )
|
||||||
|
return ProcessEventHere(event);
|
||||||
|
|
||||||
|
|
||||||
|
// Try to process the event in this handler itself.
|
||||||
|
if ( ProcessEventLocally(event) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// If we still didn't find a handler, propagate the event upwards the
|
||||||
|
// window chain and/or to the application object.
|
||||||
|
if ( TryAfter(event) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
// No handler found anywhere, bail out.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxEvtHandler::ProcessEventLocally(wxEvent& event)
|
||||||
|
{
|
||||||
|
// First try the hooks which should be called before our own handlers
|
||||||
if ( TryBefore(event) )
|
if ( TryBefore(event) )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Then try this handler itself, notice that we should not call
|
||||||
|
// ProcessEvent() on this one as we're already called from it, which
|
||||||
|
// explains why we do it here and not in DoTryChain()
|
||||||
if ( ProcessEventHere(event) )
|
if ( ProcessEventHere(event) )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// pass the event to the next handler, notice that we shouldn't call
|
// Finally try the event handlers chained to this one,
|
||||||
// TryAfter() even if it doesn't handle the event as the last handler in
|
if ( DoTryChain(event) )
|
||||||
// the chain will do it
|
return true;
|
||||||
if ( GetNextHandler() )
|
|
||||||
return GetNextHandler()->ProcessEvent(event);
|
|
||||||
|
|
||||||
// propagate the event upwards the window chain and/or to the application
|
// And return false to indicate that we didn't find any handler at this
|
||||||
// object if it wasn't processed at this level
|
// level.
|
||||||
return TryAfter(event);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxEvtHandler::DoTryChain(wxEvent& event)
|
||||||
|
{
|
||||||
|
for ( wxEvtHandler *h = GetNextHandler(); h; h = h->GetNextHandler() )
|
||||||
|
{
|
||||||
|
// We need to process this event at the level of this handler only
|
||||||
|
// right now, the pre-/post-processing was either already done by
|
||||||
|
// ProcessEvent() from which we were called or will be done by it when
|
||||||
|
// we return.
|
||||||
|
//
|
||||||
|
// However we must call ProcessEvent() and not ProcessEventHere()
|
||||||
|
// because the existing code (including some in wxWidgets itself)
|
||||||
|
// expects the overridden ProcessEvent() in its custom event handlers
|
||||||
|
// pushed on a window to be called.
|
||||||
|
//
|
||||||
|
// So we must call ProcessEvent() but it must not do what it usually
|
||||||
|
// does. To resolve this paradox we pass a special "process here only"
|
||||||
|
// flag to ProcessEvent() via the event object itself. This ensures
|
||||||
|
// that if our own, base class, version is called, it will just call
|
||||||
|
// ProcessEventHere() and won't do anything else, just as we want it to.
|
||||||
|
wxEventProcessHereOnly processHereOnly(event);
|
||||||
|
if ( h->ProcessEvent(event) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxEvtHandler::ProcessEventHere(wxEvent& event)
|
bool wxEvtHandler::ProcessEventHere(wxEvent& event)
|
||||||
|
Loading…
Reference in New Issue
Block a user