EventHandlers chain

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

EventHandlers chain

Post by rudolfninja »

Hi all,
Firstly I'll describe the problem I faced with, and then the questions I have.
The problem (for now tested only on Windows platform):
I've got control from this topic. The control is placed on custom dialog (which is inherited from wxDialog). This dialog is opened from tray widget (custom class inherited from wxTaskBarIcon). When I open the dialog with wsCustomDateTimePicker (from the topic mentioned above) and then close the app trough the tray widget option I see the following error:
datetimepickerpopup.png
datetimepickerpopup.png (46.36 KiB) Viewed 657 times
I added following code in wxCustomDateTimePickerPopup destructor:

Code: Select all

if (GetEventHandler() != nullptr && GetEventHandler() != this)
{
    RemoveEventHandler(GetEventHandler());
}
This doesn't change situation significantly. Now I see the following error in the described case:
topLevelWindow.png
topLevelWindow.png (47.6 KiB) Viewed 657 times
As I found in the Internet, there is no general solution for the problem. So I'd like to understand the mechanism of EventHandlers chain to figure out the reason of the assert and fix it.
Could you please describe the basics of EventHandlers chain and help to understand why I see this assert and how to fix it?

Thanks.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: EventHandlers chain

Post by PB »

I believe you hardly find the better overview of event handling than the official one
https://docs.wxwidgets.org/trunk/overview_events.html

Is the error caused by the application being launched from the tray, i.e., it is not reproducible from the minimal sample?

What unusual, event-wise, do you do in your dialog / control (I do not remember doing anything like that from the linked thread)?
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: EventHandlers chain

Post by rudolfninja »

I didn't manage to reproduce the problem in minimal sample, to the error is caused by the tray app. I guess, that custom dialog might be the reason.
Unfortunately I cant provide full code of the custom dialog, so some snippets from there:

Code: Select all

wxCustomDialog::wxCustomDialog(wxWindow* parent, const wxString& title, const IdType& id, long dialogStyle /*= 0*/, long buttonsStyle /*= wxOK*/):
  m_identifier(id)
, m_dialogContent(nullptr)
, m_buttonsStyle(buttonsStyle)
, m_processor(nullptr)
{
    SetBackgroundColour(StylesFactory::BackgroundColor);
    SetForegroundColour(StylesFactory::ForegroundColor);

    Create(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, DialogStyle | dialogStyle);

#if defined(BUILD_WINDOWS)
    HWND hWnd = reinterpret_cast<HWND>(GetHandle());
    ::SetClassLong(hWnd, GCL_STYLE, ::GetClassLong(hWnd, GCL_STYLE) | CS_DROPSHADOW);
    auto style = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
    SetWindowLong(hWnd, GWL_EXSTYLE, style | WS_EX_LAYERED);
    SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
#endif

    Bind(wxEVT_PAINT, &wxCustomDialog::OnPaint, this);
    Bind(wxEVT_SIZE, &wxCustomDialog::OnSize, this);
    Bind(wxEVT_CLOSE_WINDOW, &wxCustomDialog::OnCloseWindow, this);
    Bind(wxEVT_BUTTON, &wxCustomDialog::OnOk, this, wxID_OK);
    Bind(wxEVT_BUTTON, &wxCustomDialog::OnCancel, this, wxID_CANCEL);
    Bind(wxEVT_BUTTON, &wxCustomDialog::OnCancel, this, wxID_CLOSE);
    Bind(wxEVT_BUTTON, &wxCustomDialog::OnYes, this, wxID_YES);
    Bind(wxEVT_BUTTON, &wxCustomDialog::OnNo, this, wxID_NO);
}

Code: Select all

void wxCustomDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
{
    NegativeReturnCode();
    CloseDialog(GetReturnCode());
}

void wxCustomDialog::CloseDialog(std::int32_t result)
{
    if (IsModal())
    {
        EndModal(result);
    }
    else
    {
        auto handler = m_processor ? m_processor.get() : this;

        SetReturnCode(result);
        EndDialog(result);
        wxWindowModalDialogEvent closeDialog(wxEVT_WINDOW_MODAL_DIALOG_CLOSED);
        closeDialog.SetEventObject(this);
        wxPostEvent(handler, closeDialog);
    }

    if (!IsModal())
    {
        Destroy();
    }
}
There is no any extra event-related code in the control (except those that is already present in the linked thread).
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: EventHandlers chain

Post by PB »

Well, to me it seems obvious that the event posting code in CloseDialog() is suspect. Are you sure handler is still valid at the time of processing the event?
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: EventHandlers chain

Post by rudolfninja »

Handler of wxCustomDateTimePicker? Destructor of the wxCustomDateTimePicker control is called before the CloseDialog method.
I just set BP inside of CloseDialog, but I even didn't stop there. The assert dialog occurs earlier then this method is called. So the reason is somewhere else.
The problem occurs on wxCustomDateTimePickerPopup::~wxCustomDateTimePickerPopup()
If I call GetEventHandler from ~wxCustomDateTimePickerPopup() it returns wxComboPopupEventHandler. If I remove this event handler (by calling

Code: Select all

RemoveEventHandler(GetEventHandler())
), then the following event handler will be equal to this (I guess that this is wxPanel event handler from which wxCustomDateTimePickerPopup is inherited).
And the assert occurs inside of wxPanel (from which wxCustomDateTimePickerPopup is inherited) destructor. But I can't understand why

Another UPD:
I found that when I reproduce the case, destructor of the wxCustomDateTimePickerPopup object is called before the destructor of the wxCustomDateTimePicker object and I see the assert. According to the call stack from the first screenshot, destructor of the wxCustomDateTimePickerPopup is called from wxWindowBase::DestroyChildren. I guess that this function is called for wxCustomDateTimePicker object, due to wxCustomDateTimePickerPopup is a child of wxCustomDateTimePicker, Destroy() is called for the popup object firstly and then the destructor is called.
If I just close the dialog using 'X' button, then destructor of the wxCustomDateTimePicker object is called first and then destructor of the wxCustomDateTimePickerPopup object and everything is OK then.
So quest for now is to understand why.
Post Reply