Opened 8 years ago

Last modified 8 years ago

#11569 confirmed enhancement

Allow only showing windows once the initial layout is done

Reported by: jdog Owned by:
Priority: normal Milestone:
Component: wxMSW Version:
Keywords: layout flicker Cc:
Blocked By: Blocking:
Patch: no

Description

I've run into this problem when adding widgets dynamically:

Right after a widget is created ("new wxThisOrThat ...") it is immediately visible. Even if I on the very next line put it in a sizer and re-layout the window before returning, the widget will flicker by in the top left corner. This is not a big problem, but it looks bad.

I can get around it by freezing the containing widget until it has been placed in a sizer.

Could it be solved by either adding a flag such as "INITIALLY_HIDDEN", or not displaying any widgets until next iteration of the main loop (that is, not until the method creating the widget has returned)?

I've verified this on wxMSW 2.8.10, in debug mode. I've not tested in release mode. I've not seen the problem with wxGTK.

Here is a minimal sample that demonstrates the error:

class FlickerTest : public wxFrame
{
public:
    FlickerTest (wxWindow* parent)
        : wxFrame (parent, -1, _T("Flicker test"))
    {
        button = new wxButton (this, -1, _T("Add"));

        szr = new wxBoxSizer (wxVERTICAL);
        szr->Add (button, 0, wxEXPAND | wxTOP, 64);
        SetSizer (szr);
        szr->SetSizeHints (this);

        SetBackgroundColour (*wxRED);

        button->Connect (-1,
                         wxEVT_COMMAND_BUTTON_CLICKED,
                         wxCommandEventHandler (FlickerTest::OnButton),
                         NULL,
                         this);
    }

    void OnButton (wxCommandEvent& e)
    {
        wxStaticText* b = new wxStaticText (this, -1, _T("Hello"), wxDefaultPosition, wxSize (64, 64));

        /* Place a break point here and be surprised!               */
        /* The wxStaticText will be visible in the top left corner. */

        b->SetBackgroundColour (*wxBLUE);
        szr->Add (b);
        szr->Layout ();
        szr->SetSizeHints (this);
    }

    wxSizer*    szr;
    wxButton*   button;
};

Change History (7)

comment:1 Changed 8 years ago by vadz

  • Keywords layout flicker added
  • Status changed from new to confirmed
  • Summary changed from Widgets visible before they are added to sizer to Allow only showing windows once the initial layout is done
  • Type changed from defect to enhancement

I'm not aware of any foolproof way to postpone showing the widget until it is positioned in the correct location unfortunately. Any patches or even ideas about how to achieve this would certainly be welcome on wx-dev.

In the meanwhile, notice that you can create a window hidden, it's just done in a somewhat non-obvious way:

wxStaticText* b = new wxStaticText; // default ctor used here
b->Hide();
b->Create(this, -1, "Hello");
...
b->Show();

comment:2 Changed 8 years ago by jdog

Do you think it would be a bad idea to add a style flag such as wxHIDDEN?

I don't think there's any absolute rights or wrongs here, but I was a bit surprised that the UI was updated before returning to the main loop.

comment:3 Changed 8 years ago by vadz

I think it would indeed be a bad idea because creating the window in hidden state can already be achieved without using the flag (yes, it's ugly, but OTOH it needs to be done relatively rarely) and we should avoid adding new flags when it's possible to do without them. Having such flag would also give us two ways to show/hide the window (explicitly call the functions as now or toggle the flag) which would be needlessly confusing.

comment:4 Changed 8 years ago by jdog

#10748 is related, or same.

comment:5 follow-up: Changed 8 years ago by vadz

It's not quite the same as #10748 is about doing it manually while this one is about automatically doing the right thing. But they're indeed related, thanks for the reminder about the other bug.

Unfortunately I still don't really know how to implement either of them...

comment:6 in reply to: ↑ 5 ; follow-up: Changed 8 years ago by Infinity77

Replying to vadz:

It's not quite the same as #10748 is about doing it manually while this one is about automatically doing the right thing. But they're indeed related, thanks for the reminder about the other bug.

Unfortunately I still don't really know how to implement either of them...

How about something like this (pseudo-code) for #10748:

Inside a standard constructor (not a 2-phase one):

wxWindowBase::wxWindowBase()

if parent and parent.IsFrozen() and WXMSW:

m_some_global_wxwindow_variable_msw_frozen = True

window->Hide();
window->Create(this, id, pos, size, style);

void wxWindowBase::Thaw()

if m_some_global_wxwindow_variable_msw_frozen:

window->Show();
m_some_global_wxwindow_variable_msw_frozen = False

Sorry for the mix of pseudo-code, Python and C++, I know next to nothing about wxWidgets internals and C++.

Andrea.

comment:7 in reply to: ↑ 6 Changed 8 years ago by vadz

Replying to Infinity77:

Replying to vadz:

It's not quite the same as #10748 is about doing it manually while this one is about automatically doing the right thing. But they're indeed related, thanks for the reminder about the other bug.

Unfortunately I still don't really know how to implement either of them...

How about something like this (pseudo-code) for #10748:

Inside a standard constructor (not a 2-phase one):

wxWindowBase::wxWindowBase()

if parent and parent.IsFrozen() and WXMSW:

m_some_global_wxwindow_variable_msw_frozen = True

window->Hide();
window->Create(this, id, pos, size, style);

The problem is that if you hide the window from here, you need to show it from somewhere else. But if you simply show all hidden children when thawing a parent window, you risk showing the children which were hidden by the user code and were not supposed to be shown so you need to have 2 different kinds of "hidden": "hidden by user" and "hidden by wx". This will almost certainly result in more troubles...

Also, I'd really like this to work automatically, having to call Freeze() and Thaw() manually is ugly.

void wxWindowBase::Thaw()

if m_some_global_wxwindow_variable_msw_frozen:

window->Show();
m_some_global_wxwindow_variable_msw_frozen = False

Sorry for the mix of pseudo-code, Python and C++, I know next to nothing about wxWidgets internals and C++.

FWIW it would be slightly more readable if you used Trac triple curly braces around it.

Note: See TracTickets for help on using tickets.