wxWrapSizer incorrect layout (Issue #23352)

25 views
Skip to first unread message

lszl84

unread,
Mar 15, 2023, 12:05:38 PM3/15/23
to wx-...@googlegroups.com, Subscribed

Description

Here's a window with a splitter and a wrap sizer. To reproduce the problem, move the sash until drawing artifacts appear.

#include <wx/wx.h>
#include <wx/wrapsizer.h>
#include <wx/splitter.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
    MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size);
};

bool MyApp::OnInit()
{
    MyFrame *frame = new MyFrame("Hello World", wxDefaultPosition, wxDefaultSize);
    frame->Show(true);
    return true;
}

MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size)
    : wxFrame(nullptr, wxID_ANY, title, pos, size)
{
    wxSplitterWindow *splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_BORDER | wxSP_LIVE_UPDATE);

    splitter->SetMinimumPaneSize(FromDIP(150));

    auto controlsPanel = new wxScrolled<wxPanel>(splitter);
    controlsPanel->SetScrollRate(0, FromDIP(10));

    auto controlsSizer = new wxBoxSizer(wxVERTICAL);
    auto text = new wxStaticText(controlsPanel, wxID_ANY, "Panes:");

    controlsSizer->Add(text, 0, wxALL, FromDIP(5));

    auto paneSizer = new wxWrapSizer(wxHORIZONTAL);

    for (int i = 0; i < 10; i++)
    {
        auto p = new wxPanel(controlsPanel, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(45, 45)));
        p->SetBackgroundColour(*wxRED);
        paneSizer->Add(p, 0, wxRIGHT | wxBOTTOM, FromDIP(3));
    }

    controlsSizer->Add(paneSizer, 0, wxALL, FromDIP(5));

    controlsPanel->SetSizer(controlsSizer);

    auto rightPanel = new wxPanel(splitter);

    splitter->SplitVertically(controlsPanel, rightPanel);
    splitter->SetSashPosition(FromDIP(220));

    this->SetSize(FromDIP(800), FromDIP(400));
    this->SetMinSize({FromDIP(400), FromDIP(200)});
}

This mainly reproduces on Linux:

Screenshot 2023-03-15 at 16 39 58

With custom controls instead of wxPanel things may get even more ugly:

#include <wx/wx.h>
#include <wx/wrapsizer.h>
#include <wx/splitter.h>

#include <wx/graphics.h>
#include <wx/dcbuffer.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
    MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size);
};

bool MyApp::OnInit()
{
    MyFrame *frame = new MyFrame("Hello World", wxDefaultPosition, wxDefaultSize);
    frame->Show(true);
    return true;
}

class SelectablePane : public wxWindow
{
public:
    SelectablePane(wxWindow *parent, wxWindowID id, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize);

    wxSize DoGetBestSize() const override
    {
        return FromDIP(wxSize(45, 45));
    }

    bool selected = false;

private:
    void OnPaint(wxPaintEvent &evt);
};

MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size)
    : wxFrame(nullptr, wxID_ANY, title, pos, size)
{
    wxSplitterWindow *splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_BORDER | wxSP_LIVE_UPDATE);

    splitter->SetMinimumPaneSize(FromDIP(150));

    auto controlsPanel = new wxScrolled<wxPanel>(splitter);
    controlsPanel->SetScrollRate(0, FromDIP(10));

    auto controlsSizer = new wxBoxSizer(wxVERTICAL);
    auto text = new wxStaticText(controlsPanel, wxID_ANY, "Panes:");

    controlsSizer->Add(text, 0, wxALL, FromDIP(5));

    auto paneSizer = new wxWrapSizer(wxHORIZONTAL);

    for (int i = 0; i < 15; i++)
    {
        auto p = new SelectablePane(controlsPanel, wxID_ANY);
        paneSizer->Add(p, 0, wxRIGHT | wxBOTTOM, FromDIP(3));
    }

    controlsSizer->Add(paneSizer, 0, wxALL, FromDIP(5));

    controlsPanel->SetSizer(controlsSizer);

    auto rightPanel = new wxPanel(splitter);

    splitter->SplitVertically(controlsPanel, rightPanel);
    splitter->SetSashPosition(FromDIP(220));

    this->SetSize(FromDIP(800), FromDIP(400));
    this->SetMinSize({FromDIP(400), FromDIP(200)});
}

// wxFULL_REPAINT_ON_RESIZE needed for Windows
SelectablePane::SelectablePane(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size)
    : wxWindow(parent, id, pos, size, wxFULL_REPAINT_ON_RESIZE)
{
    this->SetBackgroundStyle(wxBG_STYLE_PAINT); // needed for windows

    this->Bind(wxEVT_PAINT, &SelectablePane::OnPaint, this);
}

void SelectablePane::OnPaint(wxPaintEvent &evt)
{
    wxAutoBufferedPaintDC dc(this);

    dc.Clear();
    auto gc = wxGraphicsContext::Create(dc);

    if (gc)
    {
        wxRect selectionRect{0, 0, this->GetSize().GetWidth(), this->GetSize().GetHeight()};
        selectionRect.Deflate(FromDIP(1));

        const auto roundness = 5;

        gc->SetPen(*wxYELLOW_PEN);
        gc->SetBrush(*wxBLACK_BRUSH);

        gc->DrawRoundedRectangle(selectionRect.GetX(), selectionRect.GetY(), selectionRect.GetWidth(), selectionRect.GetHeight(), roundness);

        gc->SetPen(*wxWHITE_PEN);
        gc->SetBrush(*wxWHITE_BRUSH);

        auto pw = FromDIP(8);

        gc->DrawEllipse(selectionRect.GetX() + selectionRect.GetWidth() / 2 - pw / 2, selectionRect.GetY() + selectionRect.GetHeight() / 2 - pw / 2, pw, pw);

        delete gc;
    }
}

Screenshot 2023-03-15 at 16 50 41

It's more difficult to reproduce it on Mac or Windows. The worst behavior I could get was not wrapping (without any drawing artifacts):

Screenshot 2023-03-15 at 16 52 32

Platform and version information

  • wxWidgets version 3.2.2
  • wxWidgets port you use: wxGTK (also Mac/Windows, see description)
  • OS and its version: Ubuntu 22.10.
  • GTK version: 3.24.34
  • Which GDK backend is used: Wayland (reproduces on X11 too)
  • Current theme:

—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23352@github.com>

lszl84

unread,
May 13, 2023, 3:39:31 AM5/13/23
to wx-...@googlegroups.com, Subscribed

Here's a simpler app that reproduces this bug every time on Mac, Windows, and Linux. The layout is simple:

  • controlsSizer (vertical wxBoxSizer)
    • hideButton,
    • showButton,
    • the first groupSizer (vertical wxBoxSizer)
      • static text describing the group,
      • paneSizer (horizontal wxWrapSizer)
        • red square wxPanels
    • the second groupSizer (vertical wxBoxSizer)
      • static text describing the group,
      • paneSizer (horizontal wxWrapSizer)
        • red square wxPanels

The idea is to control the visibility of the last group with the hide/show buttons.

Problems:

  1. After running the app, the red panels are not visible at all:
Screenshot 2023-05-13 at 09 34 18

You need to resize the window a bit, then the panels appear, and wxWrapSizers work as expected... until we play around with the hide/show buttons.

  1. After increasing the width of the window, clicking the hide button, changing the size back to the original and hiding the show button, the last group is not laid out correctly:
Screenshot 2023-05-13 at 09 34 22 Screenshot 2023-05-13 at 09 36 59 Screenshot 2023-05-13 at 09 37 01 Screenshot 2023-05-13 at 09 37 10
#include <wx/wx.h>
#include <wx/wrapsizer.h>
#include <wx/splitter.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
    MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size);
};

bool MyApp::OnInit()
{
    MyFrame *frame = new MyFrame("Hello World", wxDefaultPosition, wxDefaultSize);
    frame->Show(true);
    return true;
}

MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size)
    : wxFrame(nullptr
, wxID_ANY, title, pos, size)
{
    auto controlsPanel = new wxPanel(this);
    
auto controlsSizer = new wxBoxSizer
(wxVERTICAL);

    auto hideButton = new wxButton(controlsPanel, wxID_ANY, "Hide");
    controlsSizer->Add(hideButton, 0, wxALL, FromDIP(5));

    auto showButton = new wxButton(controlsPanel, wxID_ANY, "Show");
    controlsSizer->Add(showButton, 0, wxALL, FromDIP(5));

    wxSizer *sizerToHide = nullptr;

    for (int g = 0; g < 2; g++)
    {
        auto groupSizer = new wxBoxSizer(wxVERTICAL);

        
auto text = new wxStaticText(controlsPanel, wxID_ANY, "Panes:"
);
        groupSizer->Add(text, 0, wxALL, FromDIP(5));

        
auto paneSizer = new wxWrapSizer(wxHORIZONTAL);

        for (int i = 0; i < 10; i++)
        {
            auto p = new wxPanel(controlsPanel, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(45, 45)));
            p->SetBackgroundColour(*wxRED);
            paneSizer->Add(p, 0, wxRIGHT | wxBOTTOM, FromDIP(3
));
        }

        groupSizer->Add(paneSizer, 0, wxALL, FromDIP(5));

        controlsSizer->Add(groupSizer, 0, wxEXPAND);

        sizerToHide = groupSizer;
    }

    controlsPanel->SetSizer(controlsSizer);

    this->SetSize(FromDIP(200), FromDIP(700));
    this->SetMinSize({FromDIP(100), FromDIP(200)});

    showButton->Bind(wxEVT_BUTTON, [controlsSizer, sizerToHide, this](wxCommandEvent &event)
                     {
                         controlsSizer->Show(sizerToHide, true, true);
                         controlsSizer->Layout(); });

    hideButton->Bind(wxEVT_BUTTON, [controlsSizer, sizerToHide](wxCommandEvent &event)
                     {
                         controlsSizer->Hide(sizerToHide, true);
                         controlsSizer->Layout(); });
}

—
Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23352/1546587264@github.com>

VZ

unread,
May 25, 2023, 7:01:20 PM5/25/23
to wx-...@googlegroups.com, Subscribed

FWIW this definitely doesn't look right, but I don't much about wxWrapSizer and just have no time to debug it right now. If anybody could look into this, it would be really great...

—
Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23352/1563610781@github.com>

Reply all
Reply to author
Forward
0 new messages