wxDialog.ShowModal vs wxMessageDialog.ShowModal Topic is solved

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

wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by rudolfninja »

Hi guys,
Could anyone explain the difference between these two methods?
Why I'm asking. I've got custom button with overloaded OnPaint function.

.h file

Code: Select all

#pragma once

#include <memory>
#include <wx/wx.h>

#include "wxCustomControl.h"

class wxCustomButtonDrawerInterface;

class wxCustomButton: public wxCustomControl
{
public:
    wxCustomButton(wxWindow* parent, wxWindowID id, std::unique_ptr<wxCustomButtonDrawerInterface>&& drawer, const wxSize& size = wxDefaultSize);
    virtual ~wxCustomButton();

    bool Enable(bool enable /* = true */) override;
    void SetLabel(const wxString& rsLabel) override;

protected:
    void DrawInternal(wxDC& dc, wxWindow* drawer) override;
    void DrawBackground(wxDC& dc, const wxRect& rect) override;
    wxSize DoGetBestSize() const override;

public:
    void OnSize(wxSizeEvent& event);
    void OnMouseEnter(wxMouseEvent& event);
    void OnMouseLeave(wxMouseEvent& event);
    void OnMouseDown(wxMouseEvent& event);
    void OnMouseUp(wxMouseEvent& event);
    void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
    void OnButton(wxMouseEvent& event);
protected:
    virtual void OnButtonPressed(); 

private:
    bool m_pressed;
    bool m_hovered;

private:
    std::unique_ptr<wxCustomButtonDrawerInterface> m_drawer;
};
.cpp:

Code: Select all

#include "stdafx.h"

#include "wxCustomButton.h"
#include "wxUtils.h"

#include "wxCustomButtonDrawerInterface.h"
#include "wxDrawUtils.h"

namespace {
wxCursor handCursor = wxCursor(wxCURSOR_HAND);
}

wxCustomButton::wxCustomButton(wxWindow* parent, wxWindowID id, std::unique_ptr<wxCustomButtonDrawerInterface>&& drawer, const wxSize& size)
: wxCustomControl(parent, id, wxDefaultPosition, size)
, m_pressed(false)
, m_hovered(false)
, m_drawer(std::move(drawer))
{
    m_drawer->SetParent(this);

    Bind(wxEVT_SIZE, &wxCustomButton::OnSize, this);
    Bind(wxEVT_MOUSE_CAPTURE_LOST, &wxCustomButton::OnMouseCaptureLost, this);
    Bind(wxEVT_ENTER_WINDOW, &wxCustomButton::OnMouseEnter, this);
    Bind(wxEVT_LEAVE_WINDOW, &wxCustomButton::OnMouseLeave, this);
    Bind(wxEVT_LEFT_DOWN, &wxCustomButton::OnMouseDown, this);
    Bind(wxEVT_LEFT_UP, &wxCustomButton::OnMouseUp, this);

    SetCursor(handCursor);
}

wxCustomButton::~wxCustomButton()
{}

bool wxCustomButton::Enable(bool enable /* = true */)
{
    auto result = wxWindowBase::Enable(enable);
    if (result)
    {
        Refresh();
        Update();
    }
    return result;
}

void wxCustomButton::SetLabel(const wxString& rsLabel)
{
    wxCustomControl::SetLabel(rsLabel);
    Refresh();
}

void wxCustomButton::DrawInternal(wxDC& dc, wxWindow* drawer)
{
    wxRect rect = GetControlRect(drawer);

    auto state = ButtonState::Default;
    if (IsEnabled())
    {
        if (m_pressed)
        {
            state = ButtonState::Pressed;
        }
        else if (m_hovered)
        {
            state = ButtonState::Hovered;
        }
    }
    else
    {
        state = ButtonState::Disabled;
    }
    
    m_drawer->Draw(dc, rect, state);
}

void wxCustomButton::DrawBackground(wxDC& dc, const wxRect& rect)
{}

wxSize wxCustomButton::DoGetBestSize() const
{
    auto size = m_drawer->CalculateBestSize();
    if (size == wxDefaultSize)
    {
        size = wxButton::GetDefaultSize();
    }
    return size;
}

void wxCustomButton::OnSize(wxSizeEvent& event)
{
    Layout();
}

void wxCustomButton::OnMouseEnter(wxMouseEvent&)
{
    m_hovered = true;
    Refresh();
}

void wxCustomButton::OnMouseLeave(wxMouseEvent&)
{
    m_hovered = false;
    m_pressed = false;
    Refresh();
}

void wxCustomButton::OnMouseDown(wxMouseEvent&)
{
    if (!m_pressed)
    {
        m_pressed = true;

        Refresh();
        Update();
    }
}

void wxCustomButton::OnMouseUp(wxMouseEvent&)
{
    if (m_pressed)
    {
        m_pressed = false;

        Refresh();
        Update();
        OnButtonPressed();
    }
}

void wxCustomButton::OnMouseCaptureLost(wxMouseCaptureLostEvent& event)
{
    m_pressed = false;
    m_hovered = false;
    if (HasCapture())
    {
        ReleaseMouse();
    }
    Refresh();
}

void wxCustomButton::OnButtonPressed()
{
    auto event = new wxCommandEvent(wxEVT_BUTTON, GetId());
    wxQueueEvent(this, event);
}

void wxCustomButton::OnButton(wxMouseEvent& event)
{
    m_pressed = true;
    Refresh();
    Update();
    OnButtonPressed();
    m_pressed = false;
}
The painting itself takes place inside of m_drawer. OnPaint if overloaded inside of wxCustomControl (inherited from wxControl) and calls void wxCustomButton::DrawInternal. The appearance of the button depends on it's state (enabled or disabled).
Inside of wxEVT_BUTTON handler some checks are executed and message dialog box is shown in case of error. The problem is in following:
When I create dialog like

Code: Select all

wxDialog dlg(m_parent, wxID_ANY, APP_TITLE_MSG);
dlg.ShowModal();
The when I close the dialog, the button is painted as it is in disabled state though it is not (if I put mouse on it, it repaints as it should).
When I show error message as

Code: Select all

       
 wxMessageDialog dlg(m_parent, APP_TITLE_MSG, PASSWORD_REQUIRED, wxOK | wxICON_ERROR);
 dlg.ShowModal();
Then button isn't disabled and everything works OK.
So the main question is why the behavior differs and how to make it work with wxDialog.
I think, that in case of wxDialog dialog is shown before wxCustomButton::DrawInternal is called. Thus IsEnabled() returns false (because IsEnabled() checks if parent is enabled as well) and button is painted as disabled. How to fix my code to make it work properly with wxDialog?

P.S. wxWidgets 3.1.1; OS: Windows 8.1
P.P.S. The whole code (with entire inheritance model) is too complex so some classes (that I believe doesn't needed here) isn't present here.

Thanks.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by doublemax »

First thing you need to check: In the "bad" case, does the button refresh properly if you force a redraw, e.g. by moving another window accross? If yes, it's just a redraw problem. If not it means that its internal state is wrong.

In any case i would put some debug output in certain methods of the control in order to see if they are all called when expected, especially Enable().
Use the source, Luke!
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by rudolfninja »

doublemax wrote:First thing you need to check: In the "bad" case, does the button refresh properly if you force a redraw, e.g. by moving another window accross? If yes, it's just a redraw problem. If not it means that its internal state is wrong.
If I move mouse on the button, then it redraws correctly? Or do I need check the way you mentioned (moving another window accross?)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by doublemax »

If I move mouse on the button, then it redraws correctly? Or do I need check the way you mentioned (moving another window accross?)
Moving the mouse over the button triggers a new state change. But for narrowing down the problem it's important to know if it's just a redraw problem or if the internal state of the button is wrong.
Use the source, Luke!
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by rudolfninja »

No, it is not just redraw problem. If a overlap the button with another dialog, then nothing changes, the button still looks disabled.
The problem indeed might be in button state logic. I think, that drawing process is executed when modal dialog already has been shown, thus IsEnabled() returns false because parent window is disabled.
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by rudolfninja »

I noticed, that button's OnPaint event isn't triggered when I close message box. Looks like this is the reason. Should this event be triggered and why it is not (if it should)?
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by doublemax »

I noticed, that button's OnPaint event isn't triggered when I close message box.
Does the message box cover the button? If yes, there should be a paint event, otherwise it wouldn't be redrawn.

If not, at least the Enable() method should be called where you perform a repaint yourself.

Check if any button method gets called when the message box closes.
Looks like this is the reason
If that was the reason, forcing a redraw by moving another window accross would have fixed it.
Use the source, Luke!
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by rudolfninja »

doublemax wrote:Does the message box cover the button? If yes, there should be a paint event, otherwise it wouldn't be redrawn.
No, message box doesn't cover the button.
doublemax wrote:Check if any button method gets called when the message box closes.
bool wxCustomButton::Enable(bool enable /* = true */) method isn't called when the message box closes.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by doublemax »

I'm out of ideas. I'd need a compiling version of your custom button to test this myself.
Use the source, Luke!
rudolfninja
Earned some good credits
Earned some good credits
Posts: 107
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: wxDialog.ShowModal vs wxMessageDialog.ShowModal

Post by rudolfninja »

Thanks to doublemax it was possible to find the solution.
The solution here was to override DoEnable method inside of custom button:

Code: Select all

void wxCustomButton::DoEnable(bool flag)
{
    Refresh();
    wxControl::DoEnable(flag);
}
Post Reply