dynamic vertical scroll bar and automatic resizing 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
McClain
In need of some credit
In need of some credit
Posts: 4
Joined: Tue Feb 06, 2018 12:04 pm

dynamic vertical scroll bar and automatic resizing

Post by McClain »

Dear all,

I have a simple resizable dialog in which I can add (vertically) or clear some controls by clicking "Add" or "Clear" button.
When the number of controls in my vertical sizer becomes more than 4, I would like to display a verticall scrollbar so that, only 4 controls are shown at any time.

Here is a simple example. All works fine without any scroll bar.
But if I uncomment the 2 lines SetScrollRate in order to activate the vertical scrolling feature, my scrolling area becomes too smal and no button is displayed ! I have tried a lot of different stuff such as FitInside, Layout, Size Hints etc... but nothing works :-(

Could you please help me to add the correct instructions in this code ?
Thanks in advance !

Code: Select all

#include <wx/wx.h>

// g++ -o dialog main.cpp `/usr/bin/wx-config --cxxflags --libs`

class MyDialog : public wxDialog {

public:

  void on_add(wxCommandEvent& /*event*/) {
    int nb = _button_box->GetItemCount();

    // Activate vertical scroll bar up to 4 buttons
    //if (nb == 4) _button_panel->SetScrollRate(0, 20);

    wxButton* but = new wxButton(_button_panel, -1, wxString::Format(wxT("Button %d"), nb+1));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxALL, 5);

    refresh();
  }

  void on_clear(wxCommandEvent& /*event*/) {
    _button_box->Clear(true);

    // Deactivate vertical scroll bar
    //_button_panel->SetScrollRate(0, 0);

    refresh();
  }
  
  void refresh() {
    _global_box->SetSizeHints(this);
  }

  MyDialog() : wxDialog(NULL, -1, wxT("Test Dialog"),
                        wxDefaultPosition, wxSize(-1,-1),
                        wxCAPTION | wxRESIZE_BORDER) {

    _global_box = new wxBoxSizer(wxVERTICAL);

    wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
    wxButton* add = new wxButton(this, 1, wxT("Add"), wxDefaultPosition, wxSize(60 , -1));
    hbox->Add(add, 0, wxLEFT, 5) ;
    wxButton* clear = new wxButton(this, 2, wxT("Clear"), wxDefaultPosition, wxSize(60 , -1));
    hbox->Add(clear, 0, wxLEFT, 5) ;

    _global_box->Add(hbox, 0, wxALIGN_CENTER | wxEXPAND | wxALL, 5) ;

    _button_panel = new wxScrolledWindow(this, -1, wxDefaultPosition, wxSize(-1,-1), wxBORDER_SIMPLE);
    _button_box = new wxBoxSizer(wxVERTICAL);    
    wxButton* but = new wxButton(_button_panel, -1, wxT("Button 1"));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxALL, 5);
    but = new wxButton(_button_panel, -1, wxT("Button 2"));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxALL, 5);
    _button_panel->SetSizer(_button_box);

    _global_box->Add(_button_panel, 0, wxALIGN_CENTER | wxEXPAND | wxALL, 5);

    wxSizer* buttons = CreateButtonSizer(wxOK | wxCANCEL);

    _global_box->Add(buttons, 0, wxALIGN_CENTER | wxALL, 5);

    SetSizer(_global_box);

    Connect(1, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)
       &MyDialog::on_add) ;
    Connect(2, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)
       &MyDialog::on_clear) ;

    refresh();
  }

private:
  wxBoxSizer*       _global_box;
  wxBoxSizer*       _button_box;
  wxScrolledWindow* _button_panel;
};

class MyApp: public wxApp {

 public:

  bool OnInit() {
    MyDialog *dialog = new MyDialog();
    dialog->ShowModal();
    return(false);
  }
};

IMPLEMENT_APP(MyApp)
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7458
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: dynamic vertical scroll bar and automatic resizing

Post by ONEEYEMAN »

Hi,
Check the scroll sample provided with the library - this is exactly what one of the options does.

Thank you.
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: dynamic vertical scroll bar and automatic resizing

Post by doublemax »

Code: Select all

#include <wx/wx.h>

// g++ -o dialog main.cpp `/usr/bin/wx-config --cxxflags --libs`

class MyDialog : public wxDialog {

public:

  void on_add(wxCommandEvent& /*event*/) {
    int nb = _button_box->GetItemCount();

    wxButton* but = new wxButton(_button_panel, -1, wxString::Format(wxT("Button %d"), nb+1));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxALL, 5);

    refresh();
  }

  void on_clear(wxCommandEvent& /*event*/) {
    _button_box->Clear(true);
    refresh();
  }
 
  void refresh() {
    _top_sizer->Layout();
  }

  MyDialog() : wxDialog(NULL, -1, wxT("Test Dialog"),
                        wxDefaultPosition, wxSize(-1,-1),
                        wxCAPTION | wxRESIZE_BORDER) {

    wxPanel *panel = new wxPanel(this, wxID_ANY);

    _top_sizer = new wxBoxSizer(wxVERTICAL);

    wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
    wxButton* add = new wxButton(panel, 1, wxT("Add"), wxDefaultPosition, wxSize(60 , -1));
    hbox->Add(add, 0, wxLEFT, 5) ;
    wxButton* clear = new wxButton(panel, 2, wxT("Clear"), wxDefaultPosition, wxSize(60 , -1));
    hbox->Add(clear, 0, wxLEFT, 5) ;

    _top_sizer->Add(hbox, 0, wxEXPAND | wxALL, 5) ;

    _button_panel = new wxScrolledWindow(panel, -1, wxDefaultPosition, wxSize(-1,-1), wxBORDER_SIMPLE);
    _button_panel->SetScrollRate(16,16);
    //_button_panel->SetMaxClientSize( wxSize(-1, 4 * 32) );    // 4 * estimated button height

    _button_box = new wxBoxSizer(wxVERTICAL);   
    wxButton* but = new wxButton(_button_panel, -1, wxT("Button 1"));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxALL, 5);
    but = new wxButton(_button_panel, -1, wxT("Button 2"));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxALL, 5);
    _button_panel->SetSizer(_button_box);

    _top_sizer->Add(_button_panel, 1, wxALIGN_CENTER | wxEXPAND | wxALL, 5);

    wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
    hsizer->Add( new wxButton(panel, wxID_OK, "OK"), 0, wxALL, 4 );
    hsizer->Add( new wxButton(panel, wxID_CANCEL, "Cancel"), 0, wxALL, 4 );
    _top_sizer->Add( hsizer, 0, wxEXPAND, 0 );

    panel->SetSizer(_top_sizer);

    Connect(1, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&MyDialog::on_add) ;
    Connect(2, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&MyDialog::on_clear) ;

    refresh();
  }

private:
  wxBoxSizer*       _top_sizer;
  wxBoxSizer*       _button_box;
  wxScrolledWindow* _button_panel;
};

class MyApp: public wxApp {

 public:

  bool OnInit() {
    MyDialog *dialog = new MyDialog();
    dialog->ShowModal();
    return(false);
  }
};

IMPLEMENT_APP(MyApp)
From a user's point of view, i think you should not limit the number of visible buttons. If the user chooses to make the window bigger, he should see more content. Comment in the line with SetMaxClientSize().
Use the source, Luke!
McClain
In need of some credit
In need of some credit
Posts: 4
Joined: Tue Feb 06, 2018 12:04 pm

Re: dynamic vertical scroll bar and automatic resizing

Post by McClain »

Thank you very much for your help !
I have modified your working code and my buggy one in order to make them the more identical as possible.
Then, I have identified several reasons explaining my problem. All of them must be fixed to make my initial code working properly.

1/ Change the proportion of 0 by 1 when adding button panel into the global sizer
2/ Use Layout method instead of SetSizeHints of the global sizer in my refresh method
3/ Customize the maximum client size using SetMaxClientSize for limiting the height of the button panel

I have new additional questions please related to your code:
- Is there a good reason to add a global panel container when building dialogs ?
- Does the method CreateButtonSizer must be avoided ?

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

Re: dynamic vertical scroll bar and automatic resizing

Post by PB »

McClain wrote: I have new additional questions please related to your code:
- Is there a good reason to add a global panel container when building dialogs ?
- Does the method CreateButtonSizer must be avoided ?
I believe that you unfortunately cannot use Create(Std)ButtonSizer() when a dialog has a "main" panel. There is no way to specify the buttons' parent (i.e., the main panel); the method creates the buttons as children of the dialog which is of course wrong...
McClain
In need of some credit
In need of some credit
Posts: 4
Joined: Tue Feb 06, 2018 12:04 pm

Re: dynamic vertical scroll bar and automatic resizing

Post by McClain »

Yes, that seems legit ;o)
So the question remains: Why using a global container panel when you already have a parent dialog ?

Now, I have still a pb and I cannot figure out to fix it.
Here is the code from doublemax but without horizontal scroll bar (horizontal scroll rate = 0) and bigger buttons (width = 800).
How can I force my window to automatically resize horizontally so that buttons will always be totally visible ?
I'm sure this is a simple question, but I have tried a lot of SetMinSize, Fit, FitInside, SetAutoLayout etc...
But nothing works ;-(

Thanks in advance !

Code: Select all

#include <wx/wx.h>

// g++ -o dialog main.cpp `/usr/bin/wx-config --cxxflags --libs`

class MyDialog : public wxDialog {

public:

  void on_add(wxCommandEvent& /*event*/) {
    int nb = _button_box->GetItemCount();

    wxButton* but = new wxButton(_button_panel, -1, wxString::Format(wxT("Button %d"), nb+1), wxDefaultPosition, wxSize(800 , -1));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxEXPAND | wxALL, 5);

    refresh();
  }

  void on_clear(wxCommandEvent& /*event*/) {
    _button_box->Clear(true);
    refresh();
  }
 
  void refresh() {
    _top_sizer->Layout();
  }

  MyDialog() : wxDialog(NULL, -1, wxT("Test Dialog"),
                        wxDefaultPosition, wxSize(-1,-1),
                        wxCAPTION | wxRESIZE_BORDER) {

    wxPanel *panel = new wxPanel(this, wxID_ANY);

    _top_sizer = new wxBoxSizer(wxVERTICAL);

    wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
    wxButton* add = new wxButton(panel, 1, wxT("Add"), wxDefaultPosition, wxSize(60 , -1));
    hbox->Add(add, 0, wxLEFT, 5) ;
    wxButton* clear = new wxButton(panel, 2, wxT("Clear"), wxDefaultPosition, wxSize(60 , -1));
    hbox->Add(clear, 0, wxLEFT, 5) ;

    _top_sizer->Add(hbox, 0, wxEXPAND | wxALL, 5) ;

    _button_panel = new wxScrolledWindow(panel, -1, wxDefaultPosition, wxSize(-1,-1), wxBORDER_SIMPLE);
    _button_panel->SetScrollRate(0,16);
    //_button_panel->SetMaxClientSize( wxSize(-1, 4 * 32) );    // 4 * estimated button height

    _button_box = new wxBoxSizer(wxVERTICAL);   
    wxButton* but = new wxButton(_button_panel, -1, wxT("Button 1"), wxDefaultPosition, wxSize(800 , -1));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxEXPAND | wxALL, 5);
    but = new wxButton(_button_panel, -1, wxT("Button 2"), wxDefaultPosition, wxSize(800 , -1));
    _button_box->Add(but, 0, wxALIGN_CENTER | wxEXPAND | wxALL, 5);
    _button_panel->SetSizer(_button_box);

    _top_sizer->Add(_button_panel, 1, wxALIGN_CENTER | wxEXPAND | wxALL, 5);

    wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
    hsizer->Add( new wxButton(panel, wxID_OK, "OK"), 0, wxALL, 4 );
    hsizer->Add( new wxButton(panel, wxID_CANCEL, "Cancel"), 0, wxALL, 4 );
    _top_sizer->Add( hsizer, 0, wxEXPAND, 0 );

    panel->SetSizer(_top_sizer);

    Connect(1, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&MyDialog::on_add) ;
    Connect(2, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&MyDialog::on_clear) ;

    refresh();
  }

private:
  wxBoxSizer*       _top_sizer;
  wxBoxSizer*       _button_box;
  wxScrolledWindow* _button_panel;
};

class MyApp: public wxApp {

 public:

  bool OnInit() {
    MyDialog *dialog = new MyDialog();
    dialog->ShowModal();
    return(false);
  }
};

IMPLEMENT_APP(MyApp)
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7458
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: dynamic vertical scroll bar and automatic resizing

Post by ONEEYEMAN »

Hi,
Using wxPanel gives you TABbing for free.
Therefore you can have a main sizer then put wxPanel on it and then use wxPanel as a parent for all GUI elements.

BTW, you absolutely can use wxStdButtonSizer with wxDialog or wxPanel.

Thank you.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: dynamic vertical scroll bar and automatic resizing

Post by PB »

ONEEYEMAN wrote: Therefore you can have a main sizer then put wxPanel on it and then use wxPanel as a parent for all GUI elements.

BTW, you absolutely can use wxStdButtonSizer with wxDialog or wxPanel.
Just curious, what is the advantage of putting the main panel into a sizer, i.e. having a one-item sizer instead of just using a wxPanel which will be automatically fully expanded as the only child? wxFormbuilder forcing you to use the sizer there is one of my pet peeves...

Assuming that by wxStdButtonSizer you meant wxStdDialogButtonSizer, did anyone said you cannot use it? For the record, I never talked about the sizer, just the convenience wxDialog method.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7458
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: dynamic vertical scroll bar and automatic resizing

Post by ONEEYEMAN »

Hi,
You can't create a modal wxPanel as ShowModal() is a member of wxDialog.
And using sizer for the wxPanel ensures that if you have a non-trivial layout it will be handled properly. And of course its not just wxFB - wxGlade do that as well.

Thank you.
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: dynamic vertical scroll bar and automatic resizing

Post by doublemax »

Code: Select all

  void refresh() {
    _top_sizer->Layout();

    // if you want the main window to grow automatically if there are more buttons, comment in this line
    //_button_panel->SetMinClientSize( _button_box->CalcMin() );
    
    wxSize min_size = _top_sizer->CalcMin();
    wxSize act_size = GetClientSize();

    if( act_size.x < min_size.x ) act_size.x = min_size.x;
    if( act_size.y < min_size.y ) act_size.y = min_size.y;

    SetMinClientSize( min_size );
    SetClientSize( act_size );
  }
This code is a little more complicated, because it creates the behavior that i personally would want as a user.

In many cases just SetSizeHints() does the trick, but SetSizeHints() also sets the current size to the minimum size, which i don't want in many cases.
Use the source, Luke!
McClain
In need of some credit
In need of some credit
Posts: 4
Joined: Tue Feb 06, 2018 12:04 pm

Re: dynamic vertical scroll bar and automatic resizing

Post by McClain »

Thank you so much !!!
You have definitely fixed all my issues !

Since a long time, I'm fighting with sizers and scroll bars.
Now, I have a good tiny example for my future dialogs.

Thank you !
Post Reply