Bill DeBlazz
Bill DeBlazz

Reputation: 1

How to get other widgets to respond to action on sibling in wxWidgets (C++)?

I'm trying to create a GUI with wxWidgets in C++, where the user can double click on the main panel to add and remove circles. When a user single clicks on one of these circles, I want it to toggle isSelected and change its appearance, as well as de-selecting all the other circles and changing their appearance.

To do this, I've made a custom widget inheriting wxWindow to control the appearance. I can get it to work where clicking on a CustomWidgetCircle element will make it selected, and clicking on the background panel WidgetPanel will deselect all, but I am not able to figure out how to get the WidgetPanel to respond and change its children's data by clicking on a CustomWidgetCircle.

Code excerpts below. Right now, dynamic bindings are happening in the respective constructors.

// CustomWidgetCircle.h
class CustomWidgetCircle : public wxWindow {
    public:
        CustomWidgetCircle(wxPanel* parent, wxString label, wxPoint location);
        bool isSelected = false;
    private:
        void OnMouseDown(wxMouseEvent& event);
        void OnPaintEvent(wxPaintEvent& event);
        void Render(wxDC& dc);
};

// CustomWidgetCircle.cpp

...
CustomWidgetCircle

CustomWidgetCircle::OnMouseDown(wxMouseEvent& event) {
    this->isSelected = !this->isSelected;
    Refresh();
}
...

// WidgetPanel.h
class WidgetPanel : public wxPanel {
    public:
        WidgetPanel(wxFrame* parent);
    private:
        void OnDoubleClick(wxMouseEvent& event);
        void OnMouseDown
        std::list<CustomWidgetCircle*> CustomWidgetCircleList;
};
// WidgetPanel.cpp
...
WidgetPanel::OnDoubleClick(wxMouseEvent& event) {
    CustomWidgetCircle* newCircle = new CustomWidgetCircle(this, "lbl", event.GetPosition());
    CustomWidgetCircleList.push_back(newCircle);
    Refresh();
}

WidgetPanel::OnMouseDown(wxMouseEvent& event) {
    for (auto circle_ptr : CustomWidgetCircleList) {
        circle_ptr->isSelected = false;
    }
    Refresh();
}
...

What I've tried:

  1. Using Skip() in the call to OnMouseDown on the child windows to pass the event up to the parent. Seems to do nothing.
  2. Only binding the OnMouseDown handler to the parent frame and calling GetEventObject() on the event arg in order to modify the child window that's been clicked on, but that won't work because it returns a wxObject so I can't access member methods / data for my custom class.
  3. Calling the OnMouseDown handler on the parent from the child window, but I can't work out how to make parent methods accessible from the child.
  4. Subclassing wxCommandEvent so that I can pass an event that propagates up the window hierarchy. I don't think I've worked out how to properly define and initialize custom events yet.

Fully admit I'm missing a fair amount on how event handling works, but I've been through the docs several times now and am no closer. If any of the above could work and I'm missing some details, please let me know. I can provide more code to make it reproducible but it's not a bug per se. Thanks in advance.

Upvotes: 0

Views: 67

Answers (1)

VZ.
VZ.

Reputation: 22688

wxWidgets event handling system is very flexible (perhaps too much so), so there are many possible solutions, but I'd start with the simplest one:

  1. Call child->Bind(wxEVT_LEFT_DOWN, &WidgetPanel::OnChildLeftDown, this) for each child.
  2. From OnChildLeftDown() handler deselect all the other children.
  3. And call event.Skip() to let the child handle the event too.

Upvotes: 0

Related Questions