Opened 5 years ago

Closed 3 months ago

#10956 closed defect (fixed)

Problems with wxStatusBar size calculations in wxMSW

Reported by: webmasterpdx Owned by: VZ
Priority: normal Milestone:
Component: wxMSW Version: dev-latest
Keywords: wxStatusBar creation message handler Cc:
Blocked By: Blocking:
Patch: yes

Description

If a status bar is created first and then, for example, a menu added, Then after that the status bar is added to the wxFrame.
When done in that order, the wxStatusBar is created at runtime too wide (almost half the window height).

If I move the wxStatusBar creation to below the menu (or whatever window content there is) right before it is added to the wxFrame, then the statusbar is displayed correctly.

FYI: I am using wxDevCpp on Windows Vista.

Attachments (3)

statusbar-handler.patch download (784 bytes) - added by awi 9 months ago.
Patch changing message handler.
statusbar-frame-pos.patch download (343 bytes) - added by awi 9 months ago.
Reset statusbar height on attaching to the frame.
statusbar-size.png download (9.1 KB) - added by awi 9 months ago.
Nonstandard sizing of statusbar

Download all attachments as: .zip

Change History (15)

comment:1 Changed 5 years ago by vadz

  • Status changed from new to infoneeded_new

Could you please specify the wx port you use in the component field ("base" is for the non-GUI stuff) and provide a patch to the minimal sample reproducing the problem?

Thanks!

comment:2 Changed 5 years ago by webmasterpdx

  • Component changed from base to wxMSW
  • Status changed from infoneeded_new to new

comment:3 Changed 5 years ago by webmasterpdx

This is a sample from part of the code....look at the text "*THIS LINE" below where one line
is commented out and moved to below the menu creation to fix the problem.....


void testwizardFrm::CreateGUIControls()
{

Do not add custom code between
GUI Items Creation Start and GUI Items Creation End
wxDev-C++ designer will remove them.
Add the custom code before or after the blocks
GUI Items Creation Start


statusbar = new WxStatusBar(this, ID_STATUSBAR); *THIS LINE moved to below

mainmenu = new wxMenuBar();
wxMenu *ID_MNU_FILE_1001_Mnu_Obj = new wxMenu(0);
ID_MNU_FILE_1001_Mnu_Obj->Append(ID_MNU_WIZARD_1002, wxT("&Wizard\tAlt-W"), wxT("Starts First Test Wizard"), wxITEM_NORMAL);
ID_MNU_FILE_1001_Mnu_Obj->Append(ID_MNU_WIZARD2_1003, wxT("Wizard &2\tALT-2"), wxT("Start 2nd test wizard"), wxITEM_NORMAL);
ID_MNU_FILE_1001_Mnu_Obj->Append(ID_MNU_TEST_1004, wxT("&Test\tAlt-T"), wxT("Used to test various features."), wxITEM_NORMAL);
ID_MNU_FILE_1001_Mnu_Obj->Append(ID_MNU_ACTIVATE_1005, wxT("&Activate\tALT-A"), wxT("Activate test dialog"), wxITEM_NORMAL);
mainmenu->Append(ID_MNU_FILE_1001_Mnu_Obj, wxT("&File"));
SetMenuBar(mainmenu);

statusbar = new wxStatusBar(this, ID_STATUSBAR); *THIS LINE is where it's moved to

SetStatusBar(statusbar);
SetTitle(wxT("testwizard"));
SetIcon(wxNullIcon);
SetSize(8,8,600,400);
Center();


GUI Items Creation End


comment:4 Changed 5 years ago by vadz

  • Status changed from new to infoneeded_new

Sorry, I can't test/debug this code without the rest of the program so this is not useful. Please make a patch reproducing the problem in the minimal sample as explained above.

Thanks!

Changed 9 months ago by awi

Patch changing message handler.

comment:5 Changed 9 months ago by awi

  • Keywords creation message handler added
  • Patch set
  • Status changed from infoneeded_new to new
  • Summary changed from Release 2.8.8 Bug related to wxStatusBar to Bug related to wxStatusBar
  • Version changed from 2.8.8 to dev-latest

Test case to reproduce the issue:

  • samples/minimal/minimal.cpp

    old new  
    163163    menuBar->Append(fileMenu, "&File"); 
    164164    menuBar->Append(helpMenu, "&Help"); 
    165165 
     166    wxStatusBar *statusbar = new wxStatusBar(this); 
    166167    // ... and attach this menu bar to the frame 
    167168    SetMenuBar(menuBar); 
    168169#endif // wxUSE_MENUS 
    169170 
     171    SetStatusBar(statusbar); 
    170172#if wxUSE_STATUSBAR 
    171173    // create a status bar just for fun (by default with 1 pane only) 
    172     CreateStatusBar(2); 
     174//    CreateStatusBar(2); 
    173175    SetStatusText("Welcome to wxWidgets!"); 
    174176#endif // wxUSE_STATUSBAR 
    175177} 

When menu is being added to the frame then as a first message there is sent to the child windows WM_SIZE message (when width and height cover all client area) and then WM_WINDOWPOSCHANGING massage.
The problem seems to happen due to the WM_WINDOWPOSCHANGING massage handler implementation in wxStatusBar. Currently, in this handler the proposed value on input is simply overridden with actual size of status bar.
If status bar already exists and SetMenu() is called then this size covers all client area which was set by the preceding WM_SIZE message and hence this wrong size is propagated further on.
Message flow looks as follows:

OK:
* SetMenuBar
* CreateStatusBar
WM_SIZE: 0 x 0
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 23
WM_SIZE: 50 x 23
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 50 x 23
WM_SIZE: 384 x 23
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 23

WRONG:
* new StatusBar
WM_SIZE: 0 x 0
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 23
WM_SIZE: 50 x 23
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 50 x 23
* SetMenuBar
WM_SIZE: 384 x 192
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 192
* SetStatusBar

Generally, it seems that WM_WINDOWPOSCHANGING handler which only overrides proposed size with actual one is superfluous. If we resign from this handler and let the system to do its job then everything works fine.

FIXED:
* new StatusBar
WM_SIZE: 0 x 0
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 23
WM_SIZE: 50 x 23
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 50 x 23
WM_SIZE: 384 x 23
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 23
* SetMenuBar
WM_SIZE: 384 x 192
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 192
WM_SIZE: 384 x 23
WM_WINDOWPOSCHANGING: 384 x 23	currSize: 384 x 23
* SetStatusBar

Patch removing WM_WINDOWPOSCHANGING handler is attached (attachment:ticket:10956:statusbar-handler.patch)

comment:6 Changed 9 months ago by vadz

  • Status changed from new to confirmed
  • Summary changed from Bug related to wxStatusBar to Problems with wxStatusBar size calculations in wxMSW

This code was added as part of r35351 which was a fix for #7298. And while the code does look suspicious and is almost certainly wrong, reverting it breaks the sizer example patch from that ticker, so it looks like it's working around some other bug somewhere else... If you could please debug it to see what's really going on there, it'd be great, but I don't think we should apply this patch in the current state.

Thanks again!

comment:7 Changed 9 months ago by awi

It seems that the way WM_WINDOWSPOSCHANGING is currently handled is necessary to enforce the statusbar to work in a nonstandard way e.g. inside the sizers where statusbar not always occupies full width of the parent window.
The problem with regard to sizing is that both sizers and menubar (SetMenu() function) communicate with statusbar via standard WM_SIZE message and it is not possible to distinguish whether nonstandard position or size is demanded by sizer or by menubar.
It would be difficult to change this hack without introducing additional code (and variables) so it rather must stay as is.

One possible workaround to override nonstandard statusbar size (especially its height) set by SetMenu() is to reset status bar height to the default value when it is attached to the frame in wxFrameBase::SetStatusbar() (which in turn calls wxFrame::PositionStatusBar() ).
Patch applying this approach is attached (attachment:ticket:10956:statusbar-frame-pos.patch).

Extended test case with sizers taken from #7298 below.

--- wxWidgets-trunk/samples/minimal/minimal.cpp	2014-03-30
+++ wxWidgets-work/samples/minimal/minimal.cpp	2014-04-02
@@ -65,10 +65,13 @@
     MyFrame(const wxString& title);
 
     // event handlers (these functions should _not_ be virtual)
+    void OnSize(wxSizeEvent& event); 
     void OnQuit(wxCommandEvent& event);
     void OnAbout(wxCommandEvent& event);
 
 private:
+    wxStatusBar *m_sbns; 
+
     // any class wishing to process wxWidgets events must use this macro
     wxDECLARE_EVENT_TABLE();
 };
@@ -97,6 +100,7 @@
 // handlers) which process them. It can be also done at run-time, but for the
 // simple menu events like this the static method is much simpler.
 wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
+    EVT_SIZE(MyFrame::OnSize) 
     EVT_MENU(Minimal_Quit,  MyFrame::OnQuit)
     EVT_MENU(Minimal_About, MyFrame::OnAbout)
 wxEND_EVENT_TABLE()
@@ -143,8 +147,10 @@
 
 // frame constructor
 MyFrame::MyFrame(const wxString& title)
-       : wxFrame(NULL, wxID_ANY, title)
+       : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(500, 500)) 
 {
+    m_sbns = NULL;
+
     // set the frame icon
     SetIcon(wxICON(sample));
 
@@ -163,20 +169,83 @@
     menuBar->Append(fileMenu, "&File");
     menuBar->Append(helpMenu, "&Help");
 
+    wxStatusBar *statusbar = new wxStatusBar(this);
     // ... and attach this menu bar to the frame
     SetMenuBar(menuBar);
 #endif // wxUSE_MENUS
 
+    SetStatusBar(statusbar);
 #if wxUSE_STATUSBAR
     // create a status bar just for fun (by default with 1 pane only)
-    CreateStatusBar(2);
+//    CreateStatusBar(2);
     SetStatusText("Welcome to wxWidgets!");
 #endif // wxUSE_STATUSBAR
+    wxPanel *p = new wxPanel(this);  
+    wxPanel *pns = new wxPanel(p, -1, wxPoint(50, 50), wxSize(500, 500));  
+    pns->SetBackgroundStyle(wxBG_STYLE_COLOUR);  
+    pns->SetBackgroundColour(*wxRED);  
+
+    wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);  
+    for ( int i = 0; i < 2; ++i )  
+    {  
+        wxBoxSizer *s = new wxBoxSizer(wxHORIZONTAL);  
+        wxButton *b = new wxButton(this, -1, _T("Button"));  
+        wxStatusBar *sb = new wxStatusBar(this, -1, wxST_SIZEGRIP);  
+        sb->SetStatusText(wxString::Format(_T("StatusBar %d"), i));  
+        sb->SetFieldsCount(3);  
+
+        if ( i % 2 == 0 )  
+        {  
+            s->Add(b, 0, 0);  
+            s->Add(sb, 1, wxGROW);  
+        }  
+        else  
+        {  
+            s->Add(sb, 1, wxGROW);  
+            s->Add(b, 0, 0);  
+        }  
+        sizer->Add(s, 0, wxGROW);  
+    }  
+    sizer->Add(p, 1, wxGROW);  
+    for ( int i = 0; i < 2; ++i )  
+    {  
+        wxBoxSizer *s = new wxBoxSizer(wxHORIZONTAL);
+        wxButton *b = new wxButton(this, -1, _T("Button"));
+        wxStatusBar *sb = new wxStatusBar(this, -1, wxST_SIZEGRIP);
+        sb->SetStatusText(wxString::Format(_T("StatusBar %d"), i));
+        sb->SetFieldsCount(3);
+
+        if ( i % 2 == 0 )
+        {
+            s->Add(b, 0, 0);
+            s->Add(sb, 1, wxGROW);
+        }
+        else
+        {
+            s->Add(sb, 1, wxGROW);
+            s->Add(b, 0, 0);
+        }
+
+        sizer->Add(s, 0, wxGROW);
+    }
+    SetSizer(sizer);
+
+    m_sbns = new wxStatusBar(pns, -1, wxST_SIZEGRIP);
+    m_sbns->SetSize(50, 50, 200, 100);
+    m_sbns->SetStatusText(_T("No sizer StatusBar"));
+    m_sbns->SetFieldsCount(3);
 }
 
 
 // event handlers
 
+void MyFrame::OnSize(wxSizeEvent& event)  
+{  
+    if ( m_sbns )  
+        m_sbns->SetSize(event.GetSize().GetWidth() - 200, 20);  
+    event.Skip();  
+}  
+
 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
 {
     // true is to force the frame to close

Changed 9 months ago by awi

Reset statusbar height on attaching to the frame.

comment:8 follow-up: Changed 9 months ago by vadz

I have nothing against the latest patch and will apply it, but I wonder if we could also tweak just the width, but not the height, in WM_WINDOWPOSCHANGING handler? This would seem to be a slightly more elegant solution -- if it works.

Changed 9 months ago by awi

Nonstandard sizing of statusbar

comment:9 in reply to: ↑ 8 Changed 9 months ago by awi

Replying to vadz:

I have nothing against the latest patch and will apply it, but I wonder if we could also tweak just the width, but not the height, in WM_WINDOWPOSCHANGING handler? This would seem to be a slightly more elegant solution -- if it works.

Simplifying 'WM_WINDOWPOSCHANGING' handler would be the best option but sizers may also enforce nonstandard height of statusbar:
Nonstandard sizing of statusbar

I think that resetting main statusbar to the default height when it is attached to the frame is the less invasive option in these circumstances.

comment:10 Changed 8 months ago by VZ

  • Owner set to VZ
  • Resolution set to fixed
  • Status changed from confirmed to closed

In 76417:

Fix wrong wxStatusBar height in wxMSW in some circumstances.

Creating the status bar before the menu bar but associating it with the frame
after creating the menu bar resulted in a status bar of completely wrong height.
Fix this by enforcing the default height on the status bar when it's attached
to the frame.

Closes #10956.

comment:11 Changed 8 months ago by vadz

  • Resolution changed from fixed to port to stable
  • Status changed from closed to portneeded

Thanks again for the testing and explanation! I've applied the patch to the trunk only for now as I'm a bit afraid of backporting it to 3.0 immediately, but this could be done later if no problems are found with this.

comment:12 Changed 3 months ago by VZ

  • Resolution changed from port to stable to fixed
  • Status changed from portneeded to closed

In 77949:

Fix wrong wxStatusBar height in wxMSW in some circumstances.

Creating the status bar before the menu bar but associating it with the frame
after creating the menu bar resulted in a status bar of completely wrong height.
Fix this by enforcing the default height on the status bar when it's attached
to the frame.

Closes #10956.

[This is the backport of r76417 from trunk.]

Note: See TracTickets for help on using tickets.