C++ wxWidgets Sizer issue (beginner) - c++

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:
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

#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

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.
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:
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:
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.

Related

C++ wxWidgets Giant Gap in Sizers (beginner)

I'm using horizontal and vertical boxsizers to build my GUI, and so far I've had success just adding everything to sizers using expand on each sizer, then adjusting a min or max width/height of diff elements to get what I want. I've run into a strange problem though.
There are 2 gaps in the contents of my GUI i don't understand. Here is an image:
Gap #1
I don't understand gap #1 because it seems unnecessary. The entire GUI starts out with a vertical boxsizer with 2 elements. All the contents above the red StaticText go into the top part, and the red StaticText is contained in the second element.
Among the sub-sizers in the top strip, the most height that should be required is for the "Castables" and "Lands" columns of TextCtrls. This leads me to strange gap #2...
Gap #2
This gap was not here until i set a max height for the 4 buttons above it. I've drawn purple outlines in paint so you can see how the sizers fit together (or how they SHOULD fit together unless I've made a mistake). The gap is at the start of a vertical sizer that then contains some other sizers... Before i set the max heights for those 4 buttons, the height of those 4 buttons filled this gap area exactly. When i set the max height for them, I want all of the contents below to slide up, not down.
Here is all of my code in the constructor that builds the entire GUI
// mainPanel
wxPanel *mainPanel = new wxPanel(this, wxID_ANY);
// mainPanelSizer (vertical)
wxBoxSizer *mainPanelSizer = new wxBoxSizer(wxVERTICAL);
// progSizer (horizontal)
// contains program along top, and log window is along the bottom
// program contains 3 "columns"
wxBoxSizer *progSizer = new wxBoxSizer(wxHORIZONTAL);
mainPanelSizer->Add(progSizer, wxSizerFlags(1).Expand());
// 1. DECK
// deckSizer (horizontal)
wxBoxSizer *deckSizer = new wxBoxSizer(wxHORIZONTAL);
progSizer->Add(deckSizer, wxSizerFlags(1).Expand());
// castables column
wxBoxSizer *deckSizerCasts = new wxBoxSizer(wxVERTICAL);
deckSizer->Add(deckSizerCasts, wxSizerFlags(1).Expand());
// castables title
wxStaticText *deckCastsTitle = new wxStaticText(mainPanel, wxID_ANY, "Castables", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
deckCastsTitle->SetBackgroundColour(*wxYELLOW);
deckCastsTitle->SetMaxSize(wxSize(-1, 25));
deckSizerCasts->Add(deckCastsTitle, wxSizerFlags(1).Expand());
// castables content
for ( int i = 0; i < 25; i++ )
{
wxTextCtrl *textCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString);
textCtrl->SetMinSize(wxSize(200, -1));
textCtrl->SetMaxSize(wxSize(-1, 25));
deckInputCastables.push_back(textCtrl);
deckSizerCasts->Add(textCtrl, wxSizerFlags(1).Expand());
}
// lands column
wxBoxSizer *deckSizerLands = new wxBoxSizer(wxVERTICAL);
deckSizer->Add(deckSizerLands, wxSizerFlags(1).Expand());
// lands title
wxStaticText *deckLandsTitle = new wxStaticText(mainPanel, wxID_ANY, "Lands", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
deckLandsTitle->SetBackgroundColour(*wxGREEN);
deckLandsTitle->SetMaxSize(wxSize(-1, 25));
deckSizerLands->Add(deckLandsTitle, wxSizerFlags(1).Expand());
// lands content
for ( int i = 0; i < 25; i++ )
{
wxTextCtrl *textCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString);
textCtrl->SetMinSize(wxSize(200, -1));
textCtrl->SetMaxSize(wxSize(-1, 25));
deckInputLands.push_back(textCtrl);
deckSizerLands->Add(textCtrl, wxSizerFlags(1).Expand());
}
// 2. HAND
// handSizer (vertical)
wxBoxSizer *handSizer = new wxBoxSizer(wxVERTICAL);
progSizer->Add(handSizer, wxSizerFlags(1).Expand());
// hand title
wxStaticText *deckHandTitle = new wxStaticText(mainPanel, wxID_ANY, "Hand", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
deckHandTitle->SetBackgroundColour(*wxCYAN);
deckHandTitle->SetMaxSize(wxSize(-1, 25));
handSizer->Add(deckHandTitle, wxSizerFlags(1).Expand());
// hand content
for ( int i = 0; i < 7; i++ )
{
wxTextCtrl *textCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString);
textCtrl->SetMinSize(wxSize(200, -1));
textCtrl->SetMaxSize(wxSize(-1, 25));
handInputs.push_back(textCtrl);
handSizer->Add(textCtrl, wxSizerFlags(1).Expand());
}
// 3. OPTS
wxBoxSizer *optsSizer = new wxBoxSizer(wxVERTICAL);
progSizer->Add(optsSizer, wxSizerFlags(1).Expand());
// a. OPTS INSTRUCTIONS (horizontal)
// contains buttons for things can do,
// add or remove needs, reset all, or calculate
wxBoxSizer *optsInstructSizer = new wxBoxSizer(wxHORIZONTAL);
optsSizer->Add(optsInstructSizer, wxSizerFlags(1).Expand());
wxButton *addNeedButton = new wxButton(mainPanel, wxID_ANY, "+ Need");
//addNeedButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(addNeedButton, wxSizerFlags(1).Expand());
wxButton *removeNeedButton = new wxButton(mainPanel, wxID_ANY, "- Need");
//removeNeedButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(removeNeedButton, wxSizerFlags(1).Expand());
wxButton *resetAllButton = new wxButton(mainPanel, wxID_ANY, "Reset All");
//resetAllButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(resetAllButton, wxSizerFlags(1).Expand());
wxButton *calculateButton = new wxButton(mainPanel, wxID_ANY, "Calculate");
//calculateButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(calculateButton, wxSizerFlags(1).Expand());
// b. NEEDS WRAP SIZER
// contains all the needs
wxBoxSizer *needsWrapSizer = new wxBoxSizer(wxVERTICAL);
optsSizer->Add(needsWrapSizer, wxSizerFlags(1).Expand());
// b1. a single need we can copy to make more needs
wxBoxSizer *needsItemSizer = new wxBoxSizer(wxVERTICAL);
needsWrapSizer->Add(needsItemSizer, wxSizerFlags(1).Expand());
// each need has 3 horrizontal components
// i. filters check area
// we'll do 3 columns of filters so 12 possible.
// this is
wxBoxSizer *needsItemFiltersSizer = new wxBoxSizer(wxHORIZONTAL);
needsItemSizer->Add(needsItemFiltersSizer, wxSizerFlags(1).Expand());
// 4 filters per column: col 1
wxBoxSizer *needsItemFiltersCol1Sizer = new wxBoxSizer(wxVERTICAL);
needsItemFiltersSizer->Add(needsItemFiltersCol1Sizer, wxSizerFlags(1).Expand());
for ( int i = 0; i < 4; i++ )
{
wxCheckBox *filterCheck = new wxCheckBox(mainPanel, wxID_ANY, "filter " + wxString::Format(wxT("%d"), (int)(i + 1)));
filterCheck->SetBackgroundColour(*wxGREEN);
needsItemFiltersCol1Sizer->Add(filterCheck, wxSizerFlags(1).Expand());
}
// 4 filters per column: col 2
wxBoxSizer *needsItemFiltersCol2Sizer = new wxBoxSizer(wxVERTICAL);
needsItemFiltersSizer->Add(needsItemFiltersCol2Sizer, wxSizerFlags(1).Expand());
for ( int i = 4; i < 8; i++ )
{
wxCheckBox *filterCheck = new wxCheckBox(mainPanel, wxID_ANY, "filter " + wxString::Format(wxT("%d"), (int)(i + 1)));
needsItemFiltersCol2Sizer->Add(filterCheck, wxSizerFlags(1).Expand());
}
// 4 filters per column: col 3
wxBoxSizer *needsItemFiltersCol3Sizer = new wxBoxSizer(wxVERTICAL);
needsItemFiltersSizer->Add(needsItemFiltersCol3Sizer, wxSizerFlags(1).Expand());
for ( int i = 8; i < 12; i++ )
{
wxCheckBox *filterCheck = new wxCheckBox(mainPanel, wxID_ANY, "filter " + wxString::Format(wxT("%d"), (int)(i + 1)));
needsItemFiltersCol3Sizer->Add(filterCheck, wxSizerFlags(1).Expand());
}
// ii. build line (3 variations: land/land+fixing, specific card, and other
// should put in a ramp, but for now just do "land"
wxBoxSizer *needsItemBuildLineSizer = new wxBoxSizer(wxHORIZONTAL);
needsItemSizer->Add(needsItemBuildLineSizer, wxSizerFlags(1).Expand());
// line item -> "("
wxButton *needsItemOpenBrackButton = new wxButton(mainPanel, wxID_ANY, "(");
needsItemBuildLineSizer->Add(needsItemOpenBrackButton, wxSizerFlags(1).Expand());
// line item -> cards to draw combo box
wxComboBox *cardsToDrawCombo = new wxComboBox(mainPanel, wxID_ANY);
cardsToDrawCombo->Append("1 to Draw");
cardsToDrawCombo->Append("2 to Draw");
cardsToDrawCombo->Append("3 to Draw");
cardsToDrawCombo->Append("4 to Draw");
cardsToDrawCombo->Append("5 to Draw");
cardsToDrawCombo->Append("6 to Draw");
cardsToDrawCombo->Append("7 to Draw");
cardsToDrawCombo->Append("8 to Draw");
cardsToDrawCombo->Append("9 to Draw");
cardsToDrawCombo->Append("10 to Draw");
needsItemBuildLineSizer->Add(cardsToDrawCombo, wxSizerFlags(1).Expand());
// line item -> filters to apply (any/all)
wxComboBox *filtersApplyTypeCombo = new wxComboBox(mainPanel, wxID_ANY);
filtersApplyTypeCombo->Append("Any");
filtersApplyTypeCombo->Append("All");
needsItemBuildLineSizer->Add(filtersApplyTypeCombo, wxSizerFlags(1).Expand());
// line item -> cost colors combo
wxComboBox *filtersCostColorsCombo = new wxComboBox(mainPanel, wxID_ANY);
filtersCostColorsCombo->Append("Any");
filtersCostColorsCombo->Append("Black");
filtersCostColorsCombo->Append("Blue");
filtersCostColorsCombo->Append("Green");
filtersCostColorsCombo->Append("Red");
filtersCostColorsCombo->Append("White");
needsItemBuildLineSizer->Add(filtersCostColorsCombo, wxSizerFlags(1).Expand());
// line item -> cost amount combo
wxComboBox *filtersCostAmountCombo = new wxComboBox(mainPanel, wxID_ANY);
filtersCostAmountCombo->Append("<= 1");
filtersCostAmountCombo->Append("<= 2");
filtersCostAmountCombo->Append("<= 3");
filtersCostAmountCombo->Append("<= 4");
filtersCostAmountCombo->Append("<= 5");
filtersCostAmountCombo->Append("<= 6");
needsItemBuildLineSizer->Add(filtersCostAmountCombo, wxSizerFlags(1).Expand());
// line item -> ")"
wxButton *needsItemCloseBrackButton = new wxButton(mainPanel, wxID_ANY, ")");
needsItemBuildLineSizer->Add(needsItemCloseBrackButton , wxSizerFlags(1).Expand());
// line item -> "AND"
wxButton *needsItemAndButton = new wxButton(mainPanel, wxID_ANY, "AND");
needsItemBuildLineSizer->Add(needsItemAndButton, wxSizerFlags(1).Expand());
// line item -> "OR"
wxButton *needsItemOrButton = new wxButton(mainPanel, wxID_ANY, "OR");
needsItemBuildLineSizer->Add(needsItemOrButton, wxSizerFlags(1).Expand());
// iii. line buttons (reset, + row below, - this row
wxBoxSizer *needsItemButtonSizer = new wxBoxSizer(wxHORIZONTAL);
needsItemSizer->Add(needsItemButtonSizer, wxSizerFlags(1).Expand());
// buttons
wxButton *needsItemInlineAddButton = new wxButton(mainPanel, wxID_ANY, "+");
needsItemButtonSizer->Add(needsItemInlineAddButton, wxSizerFlags(1).Expand());
wxButton *needsItemInlineRemoveButton = new wxButton(mainPanel, wxID_ANY, "-");
needsItemButtonSizer->Add(needsItemInlineRemoveButton, wxSizerFlags(1).Expand());
wxButton *needsItemInlineResetButton = new wxButton(mainPanel, wxID_ANY, "R");
needsItemButtonSizer->Add(needsItemInlineResetButton, wxSizerFlags(1).Expand());
// b2. At the bottom of all needs is a
// test control that has the build string
wxBoxSizer *needsBuildDisplaySizer = new wxBoxSizer(wxVERTICAL);
needsWrapSizer->Add(needsBuildDisplaySizer, wxSizerFlags(1).Expand());
// outputSizer (horizontal)
wxStaticText *buildDisplay = new wxStaticText(mainPanel, wxID_ANY, "Build Display");
buildDisplay->SetBackgroundColour(*wxBLUE);
needsBuildDisplaySizer->Add(buildDisplay, wxSizerFlags(1).Expand());
// at the bottom of the main panel stretches the log output
wxBoxSizer *outputSizer = new wxBoxSizer(wxHORIZONTAL);
mainPanelSizer ->Add(outputSizer, wxSizerFlags(1).Expand());
// logOutput StaticText
logOutput = new wxStaticText(mainPanel, wxID_ANY, "Output Terminal");
logOutput->SetBackgroundColour(*wxRED);
outputSizer ->Add(logOutput, wxSizerFlags(1).Expand());
mainPanel->SetSizerAndFit(mainPanelSizer);
this->Fit();
Thanks for your help.
With the code you posted, I'm not seeing gap2. Instead, I'm seeing the buttons enlarged to fill the entire space. I guess you changed the code slightly after the image was created. But both the enlargement of the buttons and gap2 will have the same cause.
You have optsSizer which contains 2 other sizers optsInstructSizer and needsWrapSizer. Both of those sizers are added to optsSizer with proportion 1. That means that both optsInstructSizer and needsWrapSizer will be sized to have the same size. Since the contents of needsWrapSizer are much larger, that means that will have gap2 in the code that generated the image.
To solve this, simply add optsInstructSizer to optsSizer with proportion 0 like so:
optsSizer->Add(optsInstructSizer, wxSizerFlags(0).Expand());
or since 0 is the default value for the proportion argument, you can just use
optsSizer->Add(optsInstructSizer, wxSizerFlags().Expand());
(I always always use 0 even though it's not necessary to explicitly remind me that the item being added has proportion 0.) When an item is added to a sizer with proportion 0, it means that should be sized (verically in a vertical sizer or horizontally in a horizontal sizer) to its minimum size.
When you get rid of gap2, gap1 is removed for similar reasons. You have progSizer which contains handSizer and optsSizer. Both are added with proportion 1 which means they will have the same size. When gap2 is removed, the vertical size optsSizer shrinks which removes gap1 as well since handSizer has the same vertical size.

How can I make a sizer expand to the whole wxNotebook page?

I have a wxNotebook with two added pages. Each page uses a wxFlexGridSizer to manage the various widgets on each page.
The first page has three columns of widgets, the second page only two. The first page is wider than the second page.
The notebook is resized to the size of its widest page (the first one). However, on the second page I would like to have the sizer expand to fill the entire page, too. Instead, it just resizes to fit the widgets on that page.
So my question is:
How can I make the sizer of the second page expand to the whole notebook width, which is determined by the width of the first page?
I have tried calling the Layout() function of the parent wxNotebook, but that did not help.
Edit:
wxNotebook *notebook = new wxNotebook(this, wxID_ANY);
wxPanel* pageOne = new wxPanel(notebook, wxID_ANY);
wxFlexGridSizer* sizer = new wxFlexGridSizer(3);
wxSizerFlags flags = wxSizerFlags().Align(wxLEFT).Border(wxRIGHT, 5);
wxStaticText* label = new wxStaticText(pageOne, wxID_ANY, wxT("Label 1"));
sizer->Add(label, flags);
label = new wxStaticText(pageOne, wxID_ANY, wxT("Label 2"));
sizer->Add(label, flags);
wxSpinCtrlDouble* value = new wxSpinCtrlDouble(pageOne, wxID_ANY, wxT("50.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
sizer->Add(value, flags);
pageOne->SetSizer(sizer);
notebook->AddPage(pageOne, wxT("Page 1"));
wxPanel* pageTwo = new wxPanel(notebook, wxID_ANY);
sizer = new wxFlexGridSizer(2);
flags = wxSizerFlags().Align(wxLEFT).Border(wxRIGHT, 5);
label = new wxStaticText(pageTwo, wxID_ANY, wxT("Label 1"));
sizer->Add(label, flags);
value = new wxSpinCtrlDouble(pageTwo, wxID_ANY, wxT("50.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
sizer->Add(value, flags);
pageTwo->SetSizer(sizer);
notebook->AddPage(pageTwo, wxT("Page 2"));
topLevelSizer->Add(notebook);
this->SetSizerAndFit(topLevelSizer);
You just need to make 2 small changes. First with the flex grid sizer on the second page you need to call AddGrowableCol(1). This means that the second column can expand. (The indicies start at 0, so column 1 is the second column.)
Second, you need to use slightly different sizer flags when adding the spin control double. For example, something like
sizer->Add(value, wxSizerFlags(0).Expand().Border(wxRIGHT, 5));
Here's the snippet you posted with these two changes:
wxNotebook *notebook = new wxNotebook(this, wxID_ANY);
wxPanel* pageOne = new wxPanel(notebook, wxID_ANY);
wxFlexGridSizer* sizer = new wxFlexGridSizer(3);
wxSizerFlags flags = wxSizerFlags().Align(wxLEFT).Border(wxRIGHT, 5);
wxStaticText* label = new wxStaticText(pageOne, wxID_ANY, wxT("Label 1"));
sizer->Add(label, flags);
label = new wxStaticText(pageOne, wxID_ANY, wxT("Label 2"));
sizer->Add(label, flags);
wxSpinCtrlDouble* value = new wxSpinCtrlDouble(pageOne, wxID_ANY, wxT("50.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
sizer->Add(value, flags);
pageOne->SetSizer(sizer);
notebook->AddPage(pageOne, wxT("Page 1"));
wxPanel* pageTwo = new wxPanel(notebook, wxID_ANY);
sizer = new wxFlexGridSizer(2);
sizer->AddGrowableCol(1);
flags = wxSizerFlags().Align(wxLEFT).Border(wxRIGHT, 5);
label = new wxStaticText(pageTwo, wxID_ANY, wxT("Label 1"));
sizer->Add(label, flags);
value = new wxSpinCtrlDouble(pageTwo, wxID_ANY, wxT("50.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
sizer->Add(value, wxSizerFlags(0).Expand().Border(wxRIGHT, 5));
pageTwo->SetSizer(sizer);
notebook->AddPage(pageTwo, wxT("Page 2"));
topLevelSizer->Add(notebook);
will let the spin control expand to take up all the remaining space while matching the border from the first page.

wxWidgets Dynamically Create New Sizer after program creation, Not Rendering Properly (Newbie)

I have the following GUI, which is created with an empty sizer at the current location. The sizer is probably collapsed, as its proportion is 0, but i drew it like this so you can see it.
When you click the "+ Need" button, the program creates a new sizer with a bunch of buttons and controls, then adds it to this sizer. However they're not appearing where they should be. The new sizer and its contents is actually collapsed and in the top left corner as you can see here.
Which is obviously not what I want. However when I resize the window by just clicking and dragging one of the edges, everything pops into position:
I've tried calling every combination of ->Layout() and ->Fit() that I can think of but I just can't get it to work. This is the code that creates the GUI when program loads, called by the frame constructor. It creates "needs_sizer_wrap" which is the BoxSizer that starts out as empty and the new dynamic sizers get added to on button click. Note in the image it appears as "needsSizerWrap" but the code is actually "needs_sizer_wrap"
// creates the basic GUI
void MullSimple_2Frame::createGUI()
{
// mainPanel
main_panel = new wxPanel(this, wxID_ANY);
// mainPanelSizer (vertical)
wxBoxSizer *main_panel_sizer = new wxBoxSizer(wxVERTICAL);
// deckInfo (horizontal)
// deck data along top like deck name, deck id, deck set
wxBoxSizer *deck_info_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer->Add(deck_info_sizer, wxSizerFlags(0).Expand());
// DECK ID
// label
wxStaticText *label1 = new wxStaticText(main_panel, wxID_ANY, "DECK ID: ");
deck_info_sizer->Add(label1, wxSizerFlags(0).Expand());
// value
deck_info_id = new wxStaticText(main_panel, wxID_ANY, "-1");
deck_info_sizer->Add(deck_info_id, wxSizerFlags(0).Expand());
// DECK NAME
// label
wxStaticText *label2 = new wxStaticText(main_panel, wxID_ANY, ", NAME: ");
deck_info_sizer->Add(label2, wxSizerFlags(0).Expand());
// value
deck_info_name = new wxTextCtrl(main_panel, wxID_ANY, "");
deck_info_name->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_info_sizer->Add(deck_info_name, wxSizerFlags(0).Expand());
// DECK SET
// label
wxStaticText *label3 = new wxStaticText(main_panel, wxID_ANY, ", SET: ");
deck_info_sizer->Add(label3, wxSizerFlags(0).Expand());
// value
deck_info_set = new wxTextCtrl(main_panel, wxID_ANY, "");
deck_info_set->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_info_sizer->Add(deck_info_set, wxSizerFlags(0).Expand());
// DECK SAVED
// can't do stuff unless the deck is saved. saving allows us to set data_vars.
// label
wxStaticText *label4 = new wxStaticText(main_panel, wxID_ANY, ", SAVED: ");
deck_info_sizer->Add(label4, wxSizerFlags(0).Expand());
// value
deck_info_if_saved = new wxStaticText(main_panel, wxID_ANY, "FALSE");
deck_info_sizer->Add(deck_info_if_saved, wxSizerFlags(0).Expand());
// progSizer (horizontal)
// program contains 3 "columns"
wxBoxSizer *prog_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer->Add(prog_sizer, wxSizerFlags(1).Expand());
// PROGRAM COLUMN 1: DECK CASTABLES
// castables column
wxBoxSizer *deck_casts_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(deck_casts_sizer, wxSizerFlags(1).Expand());
// castables title
wxStaticText *deck_casts_title = new wxStaticText(main_panel, wxID_ANY, "Castables", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL);
deck_casts_title->SetBackgroundColour(*wxYELLOW);
deck_casts_sizer->Add(deck_casts_title, wxSizerFlags(0).Expand());
// castables content
for ( int i = 0; i < 25; i++ )
{
// need to set a size so that they're small enough to all fit on my crappy laptio
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
text_ctrl->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_castable_inputs.push_back(text_ctrl);
deck_casts_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 2: DECK LANDS
wxBoxSizer *deck_lands_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(deck_lands_sizer, wxSizerFlags(1).Expand());
// lands title
wxStaticText *deck_lands_title = new wxStaticText(main_panel, wxID_ANY, "Lands", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL);
deck_lands_title->SetBackgroundColour(*wxGREEN);
deck_lands_sizer->Add(deck_lands_title, wxSizerFlags(0).Expand());
// lands content
for ( int i = 0; i < 25; i++ )
{
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
text_ctrl->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_land_inputs.push_back(text_ctrl);
deck_lands_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 3: HAND
wxBoxSizer *hand_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(hand_sizer, wxSizerFlags(1).Expand());
// hand title
wxStaticText *hand_title = new wxStaticText(main_panel, wxID_ANY, "Hand", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL);
hand_title->SetBackgroundColour(*wxBLUE);
hand_sizer->Add(hand_title, wxSizerFlags(0).Expand());
// hand content
for ( int i = 0; i < 7; i++ )
{
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
hand_inputs.push_back(text_ctrl);
hand_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 4: MULL OPTS
wxBoxSizer *mull_opts_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(mull_opts_sizer, wxSizerFlags(3).Expand());
// MULL OPTS HORIZONTAL 1: MULL_OPTS_BUTTONS
wxBoxSizer *mull_opts_but_sizer = new wxBoxSizer(wxHORIZONTAL);
mull_opts_sizer->Add(mull_opts_but_sizer, wxSizerFlags(0).Expand());
// add need button
mull_opts_but_add_need = new wxButton(main_panel, wxID_ANY, "+ Need");
mull_opts_but_add_need->SetMinSize(wxSize(-1, 50));
mull_opts_but_add_need->Bind(wxEVT_BUTTON, &MullSimple_2Frame::addNeedGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_add_need, wxSizerFlags(1).Expand());
// remove need button
mull_opts_but_remove_need = new wxButton(main_panel, wxID_ANY, "- Need");
mull_opts_but_remove_need->SetMinSize(wxSize(-1, 50));
mull_opts_but_remove_need->Bind(wxEVT_BUTTON, &MullSimple_2Frame::removeNeedGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_remove_need, wxSizerFlags(1).Expand());
// reset all button
mull_opts_but_reset_needs = new wxButton(main_panel, wxID_ANY, "Reset All");
mull_opts_but_reset_needs->SetMinSize(wxSize(-1, 50));
mull_opts_but_reset_needs->Bind(wxEVT_BUTTON, &MullSimple_2Frame::resetNeedsGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_reset_needs, wxSizerFlags(1).Expand());
// calculate button
mull_opts_but_calculate = new wxButton(main_panel, wxID_ANY, "Calculate");
mull_opts_but_calculate->SetMinSize(wxSize(-1, 50));
mull_opts_but_calculate->Bind(wxEVT_BUTTON, &MullSimple_2Frame::calcNeedsGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_calculate, wxSizerFlags(1).Expand());
// needs sizer wrap, contains the needs, starts out empty
needs_sizer_wrap = new wxBoxSizer(wxVERTICAL);
mull_opts_sizer->Add(needs_sizer_wrap, wxSizerFlags(0).Expand());
// MULL OPTS HORIZONTAL 2: NEEDS (in a stack)
// Make on base need we can use as a model
// at the bottom of the main panel stretches the log output
// fixed height so set proportion 0
wxBoxSizer *output_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer ->Add(output_sizer, wxSizerFlags(0).Expand());
log_output = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE);
log_output->SetBackgroundColour(*wxYELLOW);
output_sizer ->Add(log_output, wxSizerFlags(1).Expand());
main_panel->SetSizerAndFit(main_panel_sizer);
this->Fit();
}
And this is the code that inserts the additional sizer when + need button is clicked
void MullSimple_2Frame::addNeedGUI()
{
wxBoxSizer *need_sizer = new wxBoxSizer(wxVERTICAL);
// NEED ITEM HORIZONTAL 1: grid of filters, 4 columns
// we'll put the same filters for every deck (for now),
// and no custom filter ability
std::vector<std::string> filters_in_deck;
filters_in_deck.push_back("Land");
...
// fixed 2 rows with 4 cols in each row
wxGridSizer *base_need_filters_sizer = new wxGridSizer(2, 4, 0, 0);
need_sizer->Add(base_need_filters_sizer, wxSizerFlags(0).Expand());
for ( unsigned int i = 0; i < filters_in_deck.size(); i++ )
{
wxCheckBox *filter_check = new wxCheckBox(this->main_panel, wxID_ANY, myHelpFxns::ToWxString(filters_in_deck[i]));
base_need_filters_sizer->Add(filter_check, wxSizerFlags(1).Expand());
}
// NEED ITEM HORIZONTAL 2: line options
wxBoxSizer *base_need_line_sizer = new wxBoxSizer(wxHORIZONTAL);
need_sizer->Add(base_need_line_sizer, wxSizerFlags(0).Expand());
// line item -> "("
wxButton *need_line_open_brack_but = new wxButton(this->main_panel, wxID_ANY, "(", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_open_brack_but, wxSizerFlags(0).Expand());
// line item -> apply filters logic (any/all)
wxComboBox *need_line_apply_filters_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_apply_filters_combo->Append("Any");
need_line_apply_filters_combo->Append("All");
base_need_line_sizer->Add(need_line_apply_filters_combo, wxSizerFlags(1).Expand());
// line item -> cards to draw combo box
wxComboBox *need_line_cards_draw_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_cards_draw_combo->Append("1 to Draw");
...
base_need_line_sizer->Add(need_line_cards_draw_combo, wxSizerFlags(1).Expand());
// line item -> source colors combo
wxComboBox *need_line_source_colors_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_source_colors_combo->Append("Any");
...
base_need_line_sizer->Add(need_line_source_colors_combo, wxSizerFlags(1).Expand());
// line item -> cost colors combo
wxComboBox *need_line_cost_colors_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_cost_colors_combo->Append("Any");
...
base_need_line_sizer->Add(need_line_cost_colors_combo, wxSizerFlags(1).Expand());
// line item -> cost amount combo
wxComboBox *need_line_cost_amount_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_cost_amount_combo->Append("Any");
...
base_need_line_sizer->Add(need_line_cost_amount_combo, wxSizerFlags(1).Expand());
// line item -> specific card input
wxTextCtrl *need_line_specific_card_input = new wxTextCtrl(this->main_panel, wxID_ANY, wxEmptyString);
base_need_line_sizer->Add(need_line_specific_card_input, wxSizerFlags(1).Expand());
// line item -> ")"
wxButton *need_line_close_brack_but = new wxButton(this->main_panel, wxID_ANY, ")", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_close_brack_but, wxSizerFlags(0).Expand());
// line item -> "AND"
wxButton *need_line_and_but = new wxButton(this->main_panel, wxID_ANY, "AND", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_and_but, wxSizerFlags(0).Expand());
// line item -> "OR"
wxButton *need_line_or_but = new wxButton(this->main_panel, wxID_ANY, "OR", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_or_but, wxSizerFlags(0).Expand());
// NEED ITEM HORIZONTAL 3: NEED INLINE BUTTONS
wxBoxSizer *base_need_buts_sizer = new wxBoxSizer(wxHORIZONTAL);
need_sizer->Add(base_need_buts_sizer, wxSizerFlags(0).Expand());
wxButton *need_but_add_need_after_but = new wxButton(this->main_panel, wxID_ANY, "+");
base_need_buts_sizer->Add(need_but_add_need_after_but, wxSizerFlags(0).Expand());
wxButton *need_but_remove_this_but = new wxButton(this->main_panel, wxID_ANY, "-");
base_need_buts_sizer->Add(need_but_remove_this_but, wxSizerFlags(0).Expand());
needs_sizer_wrap->Add(need_sizer, wxSizerFlags(0).Expand().Border(wxALL, 10));
//needs_sizer_wrap->Layout();
//needs_sizer_wrap->Fit(need_sizer);
}
I didn't realize this until just now, but according to the documentation when you call Layout on a top level window
Lays out the children using the window sizer or resizes the only child
of the window to cover its entire area.
This class overrides the base class Layout() method to check if this
window contains exactly one child – which is commonly the case, with
wxPanel being often created as the only child of wxTopLevelWindow –
and, if this is the case, resizes this child window to cover the
entire client area.
So I guess calling Layout on a frame with only 1 child window won't work since that call will only try to resize the panel but the panel doesn't change size so won't try to re-layout its new children.
What seems to work is adding something like
this->main_panel->Layout();
to the end of MullSimple_2Frame::addNeedGUI(). This forces a re-layout on the panel instead of the frame.
Alternately, according to the documentation you could just use a sizer the frame and only add main_panel to it. But as I said in another answer, usually a sizer with only a single window doesn't make much sense.

wxWidgets C++ panel is invisible

I am starting to practise my skills with wxWidgets and I wanted to add some panel with two different texts above this slider, but when I tried, the result is invisible:
MainWindow::MainWindow(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition) {
wxMenuBar *menubar;
wxMenu *file;
menubar = new wxMenuBar;
file = new wxMenu;
file->Append(wxID_OPEN, wxT("&Open"));
menubar->Append(file, wxT("&File"));
SetMenuBar(menubar);
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
this->SetSizer(vbox);
player_widget = new wxWindow(this, wxID_ANY);
player_widget->SetBackgroundColour(wxColour(wxT("black")));
vbox->Add(player_widget, 1, wxEXPAND | wxALIGN_TOP);
wxBoxSizer* bs = new wxBoxSizer(wxHORIZONTAL);
wxPanel* p1 = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(0,20));
p1->SetSizer(bs);
p1->Enable(true);
p1->Show(true);
vbox->Add(p1,0,wxEXPAND);
wxStaticText* text1 = new wxStaticText(this,11, "text 1");
wxStaticText* text2 = new wxStaticText(this,12, "text 2");
bs->Add(text1);
bs->Add(text2);
timeline = new wxSlider(this, myID_TIMELINE, 0, 0, TIMELINE_MAX);
vbox->Add(timeline, 0, wxEXPAND);
wxPanel *controlPanel = new wxPanel(this, wxID_ANY);
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
controlPanel->SetSizer(hbox);
vbox->Add(controlPanel, 0, wxEXPAND);
playpause_button = new wxButton(controlPanel, myID_PLAYPAUSE, wxT("Play"));
stop_button = new wxButton(controlPanel, myID_STOP, wxT("Stop"));
volume_slider = new wxSlider(controlPanel, myID_VOLUME, VOLUME_MAX, 0, VOLUME_MAX, wxDefaultPosition, wxSize(100, -1));
hbox->Add(playpause_button);
hbox->Add(stop_button);
hbox->AddStretchSpacer();
hbox->Add(volume_slider);
}
Any ideas what I did wrong?
Change parent of text1 and text2 to p1.
It is simpler to use just one panel that holds everything.
Something like this ( a complete program )
#include <wx/wx.h>
#include <wx/app.h>
class cApp : public wxApp
{
public:
virtual bool OnInit();
};
#define TIMELINE_MAX 100
#define VOLUME_MAX 100
enum {
myID_TIMELINE,
myID_PLAYPAUSE,
myID_STOP,
myID_VOLUME
};
class cFrame: public wxFrame
{
wxWindow * player_widget;
wxSlider * timeline;
wxSlider * volume_slider;
wxButton * playpause_button;
wxButton * stop_button;
public:
cFrame(wxFrame *frame, const wxString& title)
: wxFrame(frame, -1, title, wxPoint(-1,-1),wxSize(600,600))
{
wxMenuBar *menubar;
wxMenu *file;
menubar = new wxMenuBar;
file = new wxMenu;
file->Append(wxID_OPEN, wxT("&Open"));
menubar->Append(file, wxT("&File"));
SetMenuBar(menubar);
// define one panel to hold everything
// make it big enough to fill the frame
wxPanel* p1 = new wxPanel(this,wxID_ANY, wxPoint(-1,-1),wxSize(600,600) );
// top level sizer to hold everything
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
// add window widget at top
player_widget = new wxWindow(p1, wxID_ANY);
player_widget->SetBackgroundColour(wxColour(wxT("black")));
vbox->Add(player_widget, 1, wxEXPAND | wxALIGN_TOP);
// add some texts in a horizontal row
wxBoxSizer* bs = new wxBoxSizer(wxHORIZONTAL);
wxStaticText* text1 = new wxStaticText(p1,11, "text 1");
wxStaticText* text2 = new wxStaticText(p1,12, "text 2");
bs->Add(text1);
bs->Add(text2);
// add texts just below window widget
vbox->Add( bs );
// add slider below texts
timeline = new wxSlider(this, myID_TIMELINE, 0, 0, TIMELINE_MAX);
vbox->Add(timeline, 0, wxEXPAND);
// some more controls in a row
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
playpause_button = new wxButton(p1, myID_PLAYPAUSE, wxT("Play"));
stop_button = new wxButton(p1, myID_STOP, wxT("Stop"));
volume_slider = new wxSlider(p1, myID_VOLUME, VOLUME_MAX,
0, VOLUME_MAX, wxDefaultPosition, wxSize(100, -1));
hbox->Add(playpause_button);
hbox->Add(stop_button);
hbox->AddStretchSpacer();
hbox->Add(volume_slider);
// add controls below big slider
vbox->Add( hbox );
// make everything happen
SetSizer(vbox);
}
};
IMPLEMENT_APP(cApp);
bool cApp::OnInit()
{
cFrame* frame = new cFrame(0L, _("wx Starter"));
frame->Show();
return true;
}
This gives:
Here's the thing about computer programming: at some time you always have to settle down and write some code. All those applications, like wxFormBuilder, that promise to write code for you have to be abandoned at some point and you actually have to do some work ( Here is a link to more about this )

wxTextValidation doesn't copy data from wxTextCtrl to wxString variable

//ctor
test=new wxString[n];
starr=new wxStaticText *[n];
tcarr=new wxTextCtrl *[n];
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
wxStaticBox *st = new wxStaticBox(this, -1, wxT("test"),wxPoint(5, 5), wxSize(240, 150));
for(int i=0;i<n;i++){
starr[i] = new wxStaticText(this,-1,wxString::Format(wxT("test %i:"),i+1),wxPoint(20, 20+(i+1)*20));
tcarr[i] = new wxTextCtrl(this, 32000+i, wxT(""),wxPoint(95, 20+(i+1)*20),wxDefaultSize,0,wxTextValidator(wxFILTER_NUMERIC,&test[i]));
vbox->Add(starr[i],1);
vbox->Add(tcarr[i],1);
}
wxButton *okButton = new wxButton(this, wxID_OK, wxT("Save"),
wxDefaultPosition, wxSize(70, 30));
hbox->Add(okButton, 1);
vbox->Add(hbox, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, 10);
SetSizer(vbox);
Centre();
//header
private:
wxString *test;
wxStaticText **starr;
wxTextCtrl **tcarr;
wxTextValidation doesn't copy data from wxTextCtrl to wxString variable:
When I click wxID_OK button and check value on test[0..n] I get empty wxString in each, What is the problem?
Thanks.
By default wxValidator only copies the content of your control in the associated wxString when the control itself is "dismissed" (no idea whether this means destroyed or what). If you want to copy it as soon as the OK button is pressed, you have to do that explicitly. Try inserting this:
for (int i=0;i<n;i++) {
tcarr[i]->GetValidator()->TransferFromWindow();
wxMessageBox(test[i]); // just for debug
}
It worked fine for me (wxWidgets 3.0.2 on TDM-GCC-64).