doobieshroom
doobieshroom

Reputation: 1

wxWidgets: Implementing selection of objects on the panel

I want to implement object selection by allowing users to create a rectangular area using the mouse. I'm creating a panel TPanel that inherits from wxWindow to handle certain events and perform redrawing. Then I add a wxWrapSizer* imageWrapper to it, into which I add ImageContainer elements in std::vector<ImageContainer*> imageContent from std::vector<wxImage> images MyFrame.cpp:

TPanel leftPanel = new TPanel(menuSplitter, &cManager, wxID_ANY, wxDefaultPosition, wxSize(600, 494));

wxWrapSizer imageWrapper = new wxWrapSizer(wxHORIZONTAL);
leftPanel->SetSizer(imageWrapper);

for (auto& img : images) {
    auto image = new ImageContainer(leftPanel, &img, &cManager, wxID_ANY, wxDefaultPosition, wxSize(120, 100));

    imageContent.push_back(image);
    imageWrapper->Add(image, 0, wxALL, FromDIP(10));
}

So I track the area of selection I need TPanel.h / TPanel.cpp:

class TPanel : public wxScrolled<wxWindow> {
public:
    TPanel(wxWindow* parent, ColorManager* clrM, wxWindowID winid = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL | wxNO_BORDER,  const wxString& name = wxASCII_STR(wxPanelNameStr));

private:
    wxPoint startSel, endSel;
    ColorManager* colorManager;
    bool isSelect = false;
// ... and other minor details
};

void TPanel::OnMouseLeftDown(wxMouseEvent& evt) {
    if (!isSelect) {
        CaptureMouse();
        isSelect = true;
        startSel = ScreenToClient(wxGetMousePosition());
    }
}

void TPanel::OnMouseLeftUp(wxMouseEvent& evt) {
    if (isSelect) {
        ReleaseMouse();
        isSelect = false;
        Refresh();
    }
}

void TPanel::OnMouseMove(wxMouseEvent& evt) {
    if (isSelect) {
        endSel = ScreenToClient(wxGetMousePosition());
        Refresh();
    }
}

void TPanel::OnMouseLost(wxMouseCaptureLostEvent& evt) {
    ReleaseMouse();
    isSelect = false;
    Refresh();
}

void TPanel::OnSize(wxSizeEvent& event) {
    wxSize size = GetClientSize();
    this->SetVirtualSize(size);
    event.Skip();
}

Ok, I can draw this area directly on the panel, but then it will be displayed under the elements of imageContent. What I want is to display it above all child windows of the panel, within its bounds.

  1. I tried initializing a wxWindow in the constructor of TPanel to create a transparent window above and draw the selection on it. The window was indeed created above the child elements, but wxBG_STYLE_TRANSPARENT didn't provide any transparency.
  2. Next, I tried to find information on methods of rendering graphics using device context. There is too little information and examples about wxOverlay for me to understand how to use it properly. Among methods like wxClientDC, wxWindowDC, wxScreenDC, I managed to achieve flickering drawing only with wxScreenDC. In other cases, I couldn't get the area to display at all.

upd. For now I have chosen a workaround. The TPanel generates a selection start event, which MyFrame handles in MultiSelection(). Here, the intersection of the rectangular stroke area with the wxRect of control is checked and the area that needs to be painted over the child element is passed. The method works, but I'm not sure it works the same way in large commercial programs or operating systems.

void MyFrame::MultiSelection(wxCommandEvent& evt) {
    wxRect selectRC = wxRect(leftPanel->strSel, leftPanel->endSel);

    for (auto& item : imageContent) {
        if (item->GetRect().Intersects(selectRC)) {
            item->isMark = true;
            item->isSelect = true;
            item->selectRC = wxRect(selectRC.GetX() - item->GetPosition().x, selectRC.GetY() - item->GetPosition().y,
                selectRC.GetWidth(), selectRC.GetHeight());
        }
        else {
            item->isMark = false;
            item->isSelect = false;
        }
    }
}

Upvotes: 0

Views: 81

Answers (1)

Xaviou
Xaviou

Reputation: 309

You should have a look at wxOverlay

Upvotes: 0

Related Questions