0
votes

I'm new to wxWidgets and sizers. I've created the following structure inside of a frame constructor

wxBoxSizer *frameSizer = new wxBoxSizer(wxHORIZONTAL);

wxBoxSizer *deckListLeftSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *deckListRightSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *logOutputSizer = new wxBoxSizer(wxVERTICAL);

// 10 inputs on left
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(this, -1, wxT(""), wxPoint(-1, -1), wxSize(-1, -1));
    deckInputCastables.push_back(textCtrl);
    deckListLeftSizer->Add(textCtrl, wxEXPAND );
}

// 10 inputs on right
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(this, -1, wxT(""), wxPoint(-1, -1), wxSize(-1, -1));
    deckInputLands.push_back(textCtrl);
    deckListRightSizer->Add(textCtrl, wxEXPAND );
}

logOutput = new wxStaticText(this, wxID_ANY, "Output Terminal", wxPoint(-1, -1), wxSize(-1, -1));
logOutput->SetBackgroundColour(*wxRED);
logOutputSizer->Add(logOutput);

frameSizer->Add(deckListLeftSizer, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5);
frameSizer->Add(deckListRightSizer, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5);
frameSizer->Add(logOutputSizer, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5);

frameSizer->Fit(this);
SetSizer(frameSizer);

This is what it looks like:

enter image description here

What I would like is the blue StaticText to fill its entire sizer, so it takes up the right 1/3 of the window, stretching vertically and horizontally. Also, each TextCtrl has stretched Vertically to fill its sizer, but not horizontally. I don't want any unused space when I adjust the frame size, so each textctrl expands vertically and horizontally. What am I doing wrong?

Thanks

2

2 Answers

2
votes

@GeoffL,

First - there is no blue Static Text only the Red one.

Second - wxALIGN_* options as you can see work only in the major direction, i.e. the one you pass to the wxBoxSizer() constructor.

What you probably want is (untested):

logOutput = new wxStaticText(this, wxID_ANY, "Output Terminal", wxPoint(-1, -1), wxSize(-1, -1));
logOutput->SetBackgroundColour(*wxRED);
logOutputSizer->Add(logOutput, 0, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0 );

wxEXPAND | wxALIGN_CENTER_VERTICAL will expand the sizer and center the control vertically.

You also omit wxALL option everywhere as it is not useful with the wxALIGN options present.

You can also omit wxPoint() and wxSize() parameters in control constructor as (-1, -1) are default values

2
votes

In addition to what Igor said above, I would add two more points. First, I would use wxSizerFlags when adding items to a sizer. I think it really helps clarify what all of the numbers and options used for adding the item are doing. Second, usually a sizer with just a single item such as logOutputSizer doesn't serve any purpose. I would just get rid of it.

With those two points in mind, to answer your question, it is necessary to use the proportion argument and the Expand method for the sizer flags to get the layout you want. For example:

wxBoxSizer *frameSizer = new wxBoxSizer(wxHORIZONTAL);

wxBoxSizer *deckListLeftSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *deckListRightSizer = new wxBoxSizer(wxVERTICAL);

// 10 inputs on left
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString);
    deckInputCastables.push_back(textCtrl);
    deckListLeftSizer->Add(textCtrl, wxSizerFlags(1).Expand());
}

// 10 inputs on right
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString);
    deckInputLands.push_back(textCtrl);
    deckListRightSizer->Add(textCtrl, wxSizerFlags(1).Expand());
}

logOutput = new wxStaticText(this, wxID_ANY, "Output Terminal");
logOutput->SetBackgroundColour(*wxRED);

frameSizer->Add(deckListLeftSizer, wxSizerFlags(1).Expand());
frameSizer->Add(deckListRightSizer, wxSizerFlags(1).Expand());
frameSizer->Add(logOutput, wxSizerFlags(1).Expand());

frameSizer->Fit(this);
SetSizer(frameSizer);

produces this output.

enter image description here

This is a start, but it bunches everything together with no separation. To add a little separation between the item, you can use the Border method for the sizer flags. However if you just use wxALL to add all borders for each item you will get several double borders. For example if an item has both a bottom border and the item below it has a top border, there will be a double border between the items. With that in mind, the following code will put a single border around each item:

wxBoxSizer *frameSizer = new wxBoxSizer(wxHORIZONTAL);

wxBoxSizer *deckListLeftSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *deckListRightSizer = new wxBoxSizer(wxVERTICAL);

// 10 inputs on left
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString);
    deckInputCastables.push_back(textCtrl);
    deckListLeftSizer->Add(textCtrl, wxSizerFlags(1).Expand().Border(wxBOTTOM));
}

// 10 inputs on right
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString);
    deckInputLands.push_back(textCtrl);
    deckListRightSizer->Add(textCtrl,  wxSizerFlags(1).Expand().Border(wxBOTTOM));
}

logOutput = new wxStaticText(this, wxID_ANY, "Output Terminal");
logOutput->SetBackgroundColour(*wxRED);

frameSizer->Add(deckListLeftSizer, wxSizerFlags(1).Expand().Border(wxTOP|wxLEFT|wxRIGHT));
frameSizer->Add(deckListRightSizer, wxSizerFlags(1).Expand().Border(wxTOP|wxRIGHT));
frameSizer->Add(logOutput, wxSizerFlags(1).Expand().Border(wxTOP|wxBOTTOM|wxRIGHT));

frameSizer->Fit(this);
SetSizer(frameSizer);

This produces a layout like this:

enter image description here

There are many different ways to rearrange the Border options to prevent double borders, and the example above is just 1 possibility.

Finally, it's often a good idea to use a panel to group all of the items in a frame. It provides a nicer looking background and lets you move between the controls with the tab key. Here's a final example showing this idea:

wxPanel* panel = new wxPanel(this,wxID_ANY);
wxBoxSizer *panelSizer = new wxBoxSizer(wxHORIZONTAL);

wxBoxSizer *deckListLeftSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *deckListRightSizer = new wxBoxSizer(wxVERTICAL);

// 10 inputs on left
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString);
    deckInputCastables.push_back(textCtrl);
    deckListLeftSizer->Add(textCtrl, wxSizerFlags(1).Expand().Border(wxBOTTOM));
}

// 10 inputs on right
for ( int i = 0; i < 10; i++ )
{
    wxTextCtrl *textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString);
    deckInputLands.push_back(textCtrl);
    deckListRightSizer->Add(textCtrl,  wxSizerFlags(1).Expand().Border(wxBOTTOM));
}

logOutput = new wxStaticText(panel, wxID_ANY, "Output Terminal");
logOutput->SetBackgroundColour(*wxRED);

panelSizer->Add(deckListLeftSizer, wxSizerFlags(1).Expand().Border(wxTOP|wxLEFT|wxRIGHT));
panelSizer->Add(deckListRightSizer, wxSizerFlags(1).Expand().Border(wxTOP|wxRIGHT));
panelSizer->Add(logOutput, wxSizerFlags(1).Expand().Border(wxTOP|wxBOTTOM|wxRIGHT));

panel->SetSizerAndFit(panelSizer);
this->Fit();

This produces this layout:

enter image description here

There's no need to use a sizer for the panel because when a frame only has a single child, that child is automatically expanded to fill all of the frame's space.