What's best way to implement this?

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
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7479
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

What's best way to implement this?

Post by ONEEYEMAN »

Hi,
I'm trying to get a following:

A class derived from wxPanel, which will display wxStaticBitmap and wxStaticText in the vertical box sizer.
The set of the panels should be displayed in the dialog.
Only one panel should be "selected" at a time.

A selection can be done by the mouse click anywhere on the pane, including the control it displays.
The "selection" will be indicated by changing the background color of the static text.

I am going to probably try to bind the mouse click in the dialog to the panel, but how to catch the clicks on the controls (static bitmap and static text)?
It looks like static bitmap does not react to mouse clicks.

Thank you.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: What's best way to implement this?

Post by doublemax »

At least under Windows i get mouse click events from both wxStaticBitmap and wxStaticText.

Code: Select all

::wxInitAllImageHandlers();
wxPanel *panel = new wxPanel(this, wxID_ANY);

wxBitmap bmp("d:\\test.png", wxBITMAP_TYPE_PNG);
wxStaticBitmap *sbmp = new wxStaticBitmap(panel, wxID_ANY, bmp, wxPoint(20,20));
sbmp->Bind(wxEVT_LEFT_DOWN, &MyFrame::OnMouse, this);

wxStaticText *st = new wxStaticText(panel, wxID_ANY, "click me", wxPoint(200, 20) );
st->Bind(wxEVT_LEFT_DOWN, &MyFrame::OnMouse, this);

// *********************

void MyFrame::OnMouse(wxMouseEvent &event)
{
  wxLogDebug("mouse event: %d %p", event.GetEventType(), event.GetEventObject() );
  event.Skip();
}
Use the source, Luke!
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7479
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: What's best way to implement this?

Post by ONEEYEMAN »

doublemax,
So I think all I need to do is to bind the left mouse click to static bitmap and call event,skip().
And then I bind the mouse click on the dialog, because I have to groups of those panels.

Is it what you meant?

Thank you.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: What's best way to implement this?

Post by doublemax »

ONEEYEMAN wrote: Sun Mar 10, 2019 8:26 pm doublemax,
So I think all I need to do is to bind the left mouse click to static bitmap and call event,skip().
And then I bind the mouse click on the dialog, because I have to groups of those panels.

Is it what you meant?

Thank you.
That's one way. But for proper encapsulation i would keep the mouse event handling inside the custom control and send a custom wxCommandEvent upwards that indicates that this panel got selected.
Use the source, Luke!
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7479
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: What's best way to implement this?

Post by ONEEYEMAN »

doublemax,
Apparently it looks like I was wrong.
So I have following:

wxDialog->wxPanel->MyPanel (with bitmap and label)

Inside MyPanel I bound mouse click to the bitmap and called event.Skip()
Now inside the dialog I bound the mouse click to the MyPanel and call the handler inside the dialog.

So while the MyPanel::OnBitmapClicked is called the dialog handler is not.

I will try to see what is going on. If nothing comes up I will probably follow your advice.

Thank you.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7479
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: What's best way to implement this?

Post by ONEEYEMAN »

doublemax,
NM, I think I got it.

Thank you.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7479
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: What's best way to implement this?

Post by ONEEYEMAN »

doublemax,
It works partially.

Code: Select all

MyPanel::MyPanel(wxWindow *parent, const wxBitmap &bitmap, const wxString &label) : wxPanel(parent)
{
    m_bitmap = new wxStaticBitmap( this, wxID_ANY, bitmap );
    m_label = new wxStaticText( this, wxID_ANY, label );
    do_layout();
    m_bitmap->Bind( wxEVT_LEFT_DOWN, &MyPanel::OnBitmapClicked, this );
}

void MyPanel::do_layout()
{
    wxBoxSizer *sizer1 = new wxBoxSizer( wxHORIZONTAL );
    wxBoxSizer *sizer2 = new wxBoxSizer( wxVERTICAL );
    wxBoxSizer *sizer3 = new wxBoxSizer( wxVERTICAL );
    sizer1->Add( 5, 5, 0, wxEXPAND, 0 );
    sizer3->Add( 5, 20, 0, wxEXPAND, 0 );
    sizer3->Add( m_bitmap, 0, wxEXPAND, 0 );
    sizer3->Add( 5, 5, wxEXPAND, 0 );
    sizer3->Add( m_label, 0, wxALIGN_CENTER_HORIZONTAL, 0 );
    sizer3->Add( 5, 20, wxEXPAND, 0 );
    sizer2->Add( sizer3, 0, wxEXPAND, 0 );
    sizer1->Add( sizer2, 0, wxEXPAND, 0 );
    sizer1->Add( 5, 5, 0, wxEXPAND, 0 );
    SetSizer( sizer1 );
}

void MyPanel::OnBitmapClick(wxMouseEvent &event)
{
    event.ResumePropagation( wxEVENT_PROPAGATE_MAX );
    event.SetEventObject( this );
    event.Skip();
}

void MyDialog::OnMyPanelClick(wxMouseEvent &event)
{
    bool found = false;
    for( int i = 0; i < 4 || !found; ++i )
    {
        if( event.GetEventObject () == m_panels[i] )
        {
            if( i + 1 != m_source )
            {
                m_panels[m_source - 1]->GetLabel()->SetBackgroundColour( m_panel->GetBackgroundColour() );
                m_source = i + 1;
                m_panels[m_source - 1]->GetLabel()->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
                found = true;
            }
        }
    }
    event.Skip();
}
This same code works properly on *nix/GTK, but doesn't work on Windows (8.1) with MSVC 2017 Community.

Any idea?

Thank you.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: What's best way to implement this?

Post by doublemax »

Usually mouse events don't propagate and i don't know if setting wxEVENT_PROPAGATE_MAX is enough to override that.

My idea was to send a (new) wxCommandEvent. You could also re-use an existing one that has a similar meaning, e.g. wxEVT_LISTBOX.

Code: Select all

void MyPanel::OnBitmapClick(wxMouseEvent &event)
{
  wxCommandEvent cmd(wxEVT_LISTBOX, GetId() );
  cmd.SetEventObject( this );
  GetEventHandler()->ProcessEvent(cmd);
}
This is of course a little dirty, as the event object is not a wxListBox, so code relying on that would crash. For a clean solution, define a new event type.
Use the source, Luke!
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7479
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: What's best way to implement this?

Post by ONEEYEMAN »

doublemax,
Calling Refresh() at the end of the dialog handler fixed it for Windows. Background color is now reflects properly.
Which makes me curious - shouldn't library do that?

I will try to build the widgets sample and check and maybe submit a patch/PR.

Thank you.
Post Reply