Stationary background with wxScrolled 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
AmadeusK525
Knows some wx things
Knows some wx things
Posts: 38
Joined: Wed Aug 19, 2020 12:04 am

Stationary background with wxScrolled

Post by AmadeusK525 » Sun Apr 11, 2021 2:22 am

Hello!
I know this has been asked (and solved before), but I just can't figure this out. I'm able to draw what I want and it works fine, I've gotten rid of the flicker and everything, but when scrolling, everything painted gets offset by a bit and only returns to normal when scrolling stops (actually when thumbtrack is released. If I scroll without a thumbtrack it stays offset when scrolling stops). I don't know how to fix this. This is the code:

Code: Select all

void OverviewPanel::OnPatchNotesPaint(wxPaintEvent& event) {
	wxAutoBufferedPaintDC dc(m_patchNotesWindow);
	dc.SetUserScale(m_bgScale, m_bgScale);

	dc.DrawBitmap(m_background, m_sbgx, m_sbgy);

	wxGCDC gdc(dc);
	gdc.GetGraphicsContext()->Scale(1.0 / m_bgScale, 1.0 / m_bgScale);
	gdc.SetPen(wxPen(wxColour(90, 70, 60), 10));
	gdc.SetBrush(wxBrush(wxColour(102, 83, 67, 100)));
	gdc.DrawRoundedRectangle(wxPoint(0, 0), m_patchNotesWindow->GetClientSize(), 10.0);
}
I've of course made sure that m_sbgx and m_sbgy aren't changing when scrolling, they only change on resize. Even if I hardcode every position, it still happens. I've set the background style to wxBG_STYLE_PAINT.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2984
Joined: Sun Jan 03, 2010 5:45 pm

Re: Stationary background with wxScrolled

Post by PB » Sun Apr 11, 2021 9:10 am

Is m_patchNotesWindow a wxScrolled? I thought that when drawing with wxScrolled, you should follow this:
wxWidgets docs wrote:You have the option of handling the OnPaint handler or overriding the wxScrolled::OnDraw() function, which is passed a pre-scrolled device context (prepared by wxScrolled::DoPrepareDC()).

If you don't wish to calculate your own scrolling, you must call DoPrepareDC() when not drawing from within OnDraw(), to set the device origin for the device context according to the current scroll position.
For example, the code below (simplified from this), does the opposite of yours: It scrolls the bitmap but keeps the text overlay at the top left corner:

Code: Select all

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

    wxPoint      offset = GetViewStart();
    int          pixelsPerUnitX = 0, pixelsPerUnitY = 0;
    wxStopWatch  stopWatch;

    stopWatch.Start();

    DoPrepareDC(dc);

    dc.DrawBitmap(m_bitmap, 0, 0, false);

    GetScrollPixelsPerUnit(&pixelsPerUnitX, &pixelsPerUnitY);
    offset.x *= pixelsPerUnitX; offset.y *= pixelsPerUnitY;

    // Draw info "overlay", always at the top left corner of the window
    // regardless of how the bitmap is scrolled.
    const long            drawTime = stopWatch.Time();

    dc.DrawText(wxString::Format("GetCVBitmap: %ld ms\nConvertCVtoWXBitmap: %ld ms\nDrawWXBitmap: %ld ms\n",
        m_timeGetCVBitmap, m_timeConvertBitmap, drawTime),
        offset);
}
I also had to call EnableScrolling(false, false); to prevent drawing artifacts...

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

Re: Stationary background with wxScrolled

Post by doublemax » Sun Apr 11, 2021 9:45 am

Try adding the wxFULL_REPAINT_ON_RESIZE window style flag. Despite its name, it sometimes fixed redraw issues in scrolled windows, even when they're happening while scrolling, not resizing.
Use the source, Luke!

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2984
Joined: Sun Jan 03, 2010 5:45 pm

Re: Stationary background with wxScrolled

Post by PB » Sun Apr 11, 2021 10:33 am

When looking into the topic, on Windows I encountered something I think may be a bug (Windows 10, wxWidgets master)?

The code draws a logo in the top left corner, which is not scrolled with the controls. When I do not call EnableScrolling(false, false), there are drawing artifacts. This is expected because Windows will just move the window area including the bitmap. So EnableScrolling(false, false) has to be called.

However when EnableScrolling(false, false) is called, the controls are not properly redrawn after the scrolling. They scrolling result shows only after I resize the frame. I find this unexpected, I believe calling EnableScrolling() should not have any effect on this. The issue is present regardless of binding the paint event or creating m_scrolled with wxFULL_REPAINT_ON_RESIZE, only EnableScrolling() call affects this.

Code: Select all

#include <wx/wx.h>
#include <wx/artprov.h>
#include <wx/scrolwin.h>

class MyFrame: public wxFrame
{
public:
    MyFrame() : wxFrame (nullptr, wxID_ANY, "Test")
    {
        wxSizer* scrolledSizer = new wxBoxSizer(wxVERTICAL);

        m_scrolled = new wxScrolledWindow(this);
        for ( size_t i = 0; i < 50; ++i )
            scrolledSizer->Add(new wxButton(m_scrolled, wxID_ANY, wxString::Format("Button %zu", i)), wxSizerFlags().Center().Border());
        m_scrolled->SetSizer(scrolledSizer);
        m_scrolled->FitInside();
        m_scrolled->SetScrollRate(16, 16);
#if 1
        m_scrolled->EnableScrolling(false, false);
#endif
        m_scrolled->Bind(wxEVT_PAINT, &MyFrame::OnPaintScrolled, this);

        m_logoBitmap = wxArtProvider::GetBitmap(wxART_INFORMATION);
    }
private:
    wxBitmap          m_logoBitmap;
    wxScrolledWindow* m_scrolled;

    void OnPaintScrolled(wxPaintEvent&)
    {
        wxPaintDC dc(m_scrolled);

        dc.DrawBitmap(m_logoBitmap, 0, 0);
    }
};

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        (new MyFrame())->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
EDIT
The same happens on Ubuntu and reading EnableScrolling() docs, I may have misunderstood what it does. I thought it basically only speeds-up drawing by reusing the scrolled part of the window and drawing only the newly uncovered one.

EDIT2
On Ubuntu, there are no artifacts even without calling EnableScrolling(false, false). I guess it does not do what ScrollWindowEx() does on MSW, i.e., reusing the scrolled part?

EDIT3
It was bugging me so I asked in wx-users: https://groups.google.com/u/1/g/wx-users/c/9Ww1ACmJG4k

EDIT4
Based on the answer from the "main" wxWidgets developer, this may not be possible:
https://groups.google.com/g/wx-users/c/ ... B9FPMIBQAJ

AmadeusK525
Knows some wx things
Knows some wx things
Posts: 38
Joined: Wed Aug 19, 2020 12:04 am

Re: Stationary background with wxScrolled

Post by AmadeusK525 » Sun Apr 11, 2021 11:38 pm

Hey,
Turns out calling EnableScrolling(false, false) got rid of the issue. What problems were you having with it? I now am able to keep the background stationary while I move only the text on top of it. There are no visible artifacts and drawing is pretty fast. Haven't test on Ubuntu though. But I calculate everything manually, I don't use any controls, so maybe that's why I haven't run into any issues.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2984
Joined: Sun Jan 03, 2010 5:45 pm

Re: Stationary background with wxScrolled

Post by PB » Mon Apr 12, 2021 5:54 am

AmadeusK525 wrote:
Sun Apr 11, 2021 11:38 pm
Turns out calling EnableScrolling(false, false) got rid of the issue. What problems were you having with it?
As I wrote above, updating position of controls.

Post Reply