Related
I'm getting a very mysterious error that just makes no sense to me. I'm using CodeBlocks with a wxWidgets project. Inside the frame constructor, i create an object of the class fileManager. I then call a fileManager method that passes a TextCtrl from the GUI to fileManager for storage, so I'll be able to access the TextCtrl from within fileManager. The TextCtrl is called log_output. When I do this, i set a pointer in fileManager to the TextCtrl called local_log_output. Then when I try to call a different fileManager method and use that pointer in the method, the program crashes.
The mysterious part is the following. If this is my frame constructor:
MullSimple_3Frame::MullSimple_3Frame(wxFrame *frame, const wxString& title)
: wxFrame(frame, -1, title, wxPoint(0, 0) )
{
iniGUI();
log_output->SetValue("HUH?");
FileManager fileManager;
fileManager.setLogOutput(log_output);
std::map<int, std::string> prog_vars_deck_pairs = fileManager.getMenuPairs( "deck", "" );
}
the program works fine and does not crash. The fileManager.getMenuPairs method is called fine, which uses local_log_output. If however I move the call to fileManager.getMenuPairs to a seperate function in the frame, then call that function like so:
MullSimple_3Frame::MullSimple_3Frame(wxFrame *frame, const wxString& title)
: wxFrame(frame, -1, title, wxPoint(0, 0) )
{
iniGUI();
log_output->SetValue("HUH?");
FileManager fileManager;
fileManager.setLogOutput(log_output);
testMethod();
}
void MullSimple_3Frame::testMethod()
{
std::map<int, std::string> prog_vars_deck_pairs = fileManager.getMenuPairs( "deck", "" );
}
The program crashes on the call to testMethod, and it stops on the line in getMenuPairs where I try to set the value of local_log_output.
The only thing I can think of is maybe the order that I'm declaring variables/functions in the frame constructor header matters? What other possible difference could there be where its not crashing in the first case but is crashing in the second case?
This is the header file for the frame
#ifndef MULLSIMPLE_2MAIN_H
#define MULLSIMPLE_2MAIN_H
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <vector>
#include "MullSimple_3App.h"
#include "non-wx/my_help_fxns.h"
#include "non-wx/file_manager.h"
#include "non-wx/data_vars.h"
#include "non-wx/simulator.h"
class MullSimple_3Frame: public wxFrame
{
public:
// constructor and destructor
MullSimple_3Frame(wxFrame *frame, const wxString& title);
~MullSimple_3Frame();
// logging output to terminal at bottom of gui
wxTextCtrl *log_output;
// reloads the deck load menu
void menuDeckOptsReload();
// just for trying to solve problem
void testMethod();
// VARIABLES
int unique_wx_id = 1000;
int curr_need_id = 0;
int last_active_need_row_id = -1;
std::vector<int> existing_needs_ids;
std::vector<wxBoxSizer*> existing_needs_sizers;
std::vector<need_layout_struct> existing_needs_items;
std::vector<std::vector<int>> existing_needs_widget_ids;
// WX GUI OBJECTS
wxPanel *main_panel;
wxMenu *deck_options_menu;
wxMenu *load_deck_sub_menu;
wxMenu *mull_options_menu;
wxMenu *mull_load_all_sub_menu;
wxMenu *mull_load_this_sub_menu;
wxStaticText *deck_info_id;
wxTextCtrl *deck_info_name;
wxTextCtrl *deck_info_set;
wxStaticText *deck_info_if_saved;
wxStaticText *mull_info_id;
wxTextCtrl *mull_info_name;
wxStaticText *mull_info_if_saved;
wxStaticText *deck_casts_title;
wxStaticText *deck_lands_title;
std::vector<wxTextCtrl*> deck_castable_inputs;
std::vector<wxButton*> deck_castable_info_buts;
std::vector<wxTextCtrl*> deck_land_inputs;
std::vector<wxButton*> deck_land_info_buts;
std::vector<wxTextCtrl*> hand_inputs;
wxButton *mull_opts_but_add_need;
wxButton *mull_opts_but_remove_need;
wxButton *mull_opts_but_reset_needs;
wxButton *mull_opts_but_simulate;
wxBoxSizer *needs_sizer_wrap;
// EVENT HANDLERS
void eMenuDeckOptsNew(wxCommandEvent &event);
void eMenuDeckOptsSave(wxCommandEvent &event);
void eMenuDeckOptsLoad(wxCommandEvent &event);
void eMenuMullOptsNew(wxCommandEvent &event);
void eMenuMullOptsSave(wxCommandEvent &event);
void eMenuMullOptsLoad(wxCommandEvent &event);
void eMenuHandOptsClear(wxCommandEvent &event);
// FUNCTIONS
void iniMenu();
void menuMullOptsReload();
void iniGUI();
void resetDeckInfo();
void resetDeckInputs();
void resetDeckInfoButs();
void resetHandInputs();
void resetMullInfo();
void resetMullInputs();
void deckCountCards();
bool deckCheckSaved();
void deckSortInputs(std::vector<std::string> deck_castable_strings, std::vector<std::string> deck_land_strings );
void deckUpdateInfoButs();
bool mullCheckSaved();
void mullAddNeed(int insert_after_count);
void mullNeedLineProcessRadiosAndChecks(need_layout_struct this_need_layout, std::string filter_class );
// CLASS REFERENCES
// file manager can save and load data
FileManager fileManager;
// data vars holds info about the cards in deck
DataVars dataVars;
// simulator sets up and runs sims
Simulator simulator;
private:
// we don't use the event table for handling events, instead we use
// connect for menu items and bind for controls, but still declare the table
DECLARE_EVENT_TABLE()
};
#endif // MULLSIMPLE_2MAIN_H
And here is the file manager header file:
#ifndef FILE_MANAGER_H_INCLUDED
#define FILE_MANAGER_H_INCLUDED
#include <vector>
#include <map>
#include "prog_structs.h"
class FileManager
{
private:
public:
FileManager();
// pointer to the gui log_output
wxTextCtrl *local_log_output;
// passed log_output from gui, sets a pointer to it for access within this class
void setLogOutput(wxTextCtrl *log_output);
// gets id/name pairs for saved decks to populate menu items
std::map<int, std::string> getMenuPairs(
std::string type, // "deck" or "mull"
std::string deck_id // only needed for mull, if we want only mull data related to a specific deck
);
};
#endif // FILE_MANAGER_H_INCLUDED
And the file manager .cpp file
#include <sstream>
#include <fstream>
#include <filesystem>
#include <map>
#include "file_manager.h"
#include "my_help_fxns.h"
FileManager::FileManager()
{
}
void FileManager::setLogOutput(wxTextCtrl *log_output)
{
local_log_output = log_output;
}
std::map<int, std::string> FileManager::getMenuPairs( std::string type, std::string deck_id )
{
// PROGRAM IS CRASHING HERE, WHY I DON"T KNOW
local_log_output->SetValue("PRINTING B...");
std::map<int, std::string> menu_pairs;
return menu_pairs;
}
EDIT: iniGUI()
// mainPanel
main_panel = new wxPanel(this, wxID_ANY);
// mainPanelSizer (vertical)
wxBoxSizer *main_panel_sizer = new wxBoxSizer(wxVERTICAL);
// info sizer
// deck info (id, name etc)
// mull info (id, name etc)
wxBoxSizer *info_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer->Add(info_sizer, wxSizerFlags(0).Expand());
// deckInfo (horizontal)
// deck data along top like deck name, deck id, deck set
wxBoxSizer *deck_info_sizer = new wxBoxSizer(wxHORIZONTAL);
info_sizer->Add(deck_info_sizer, wxSizerFlags(1).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_3Frame::eDeckInfoChangeValue, 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_3Frame::eDeckInfoChangeValue, this);
deck_info_set->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::eDeckInfoBlurSet, 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());
wxBoxSizer *mull_info_sizer = new wxBoxSizer(wxHORIZONTAL);
info_sizer->Add(mull_info_sizer, wxSizerFlags(1).Expand());
// MULL ID
// label
wxStaticText *label5 = new wxStaticText(main_panel, wxID_ANY, "MULL ID: ");
mull_info_sizer->Add(label5, wxSizerFlags(0).Expand());
// value
mull_info_id = new wxStaticText(main_panel, wxID_ANY, "-1");
mull_info_sizer->Add(mull_info_id, wxSizerFlags(0).Expand());
// MULL NAME
// label
wxStaticText *label6 = new wxStaticText(main_panel, wxID_ANY, ", NAME: ");
mull_info_sizer->Add(label6, wxSizerFlags(0).Expand());
// value
mull_info_name = new wxTextCtrl(main_panel, wxID_ANY, "");
mull_info_name->Bind(wxEVT_TEXT, &MullSimple_3Frame::eMullInfoChangeValue, this);
mull_info_sizer->Add(mull_info_name, wxSizerFlags(0).Expand());
// MULL SAVED
// can't do stuff unless the deck is saved. saving allows us to set data_vars.
// label
wxStaticText *label7 = new wxStaticText(main_panel, wxID_ANY, ", SAVED: ");
mull_info_sizer->Add(label7, wxSizerFlags(0).Expand());
// value
mull_info_if_saved = new wxStaticText(main_panel, wxID_ANY, "FALSE");
mull_info_sizer->Add(mull_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
deck_casts_title = new wxStaticText(main_panel, wxID_ANY, "Castables (0)", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL|wxST_NO_AUTORESIZE);
deck_casts_title->SetBackgroundColour(*wxYELLOW);
deck_casts_sizer->Add(deck_casts_title, wxSizerFlags(0).Expand());
// castables content
// before we had just a single input arranged inside the sizer, but now i think
// we want each row itself to be a sizer, with a button and an input
for ( int i = 0; i < 25; i++ )
{
wxBoxSizer *card_item_sizer = new wxBoxSizer(wxHORIZONTAL);
deck_casts_sizer->Add(card_item_sizer, wxSizerFlags(1).Expand());
// input
this->unique_wx_id++;
wxTextCtrl *card_input = new wxTextCtrl(main_panel, this->unique_wx_id, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
//card_input->Bind(wxEVT_TEXT, &MullSimple_3Frame::GUIDeckCardsChangeValue, this);
//card_input->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::GUICardsChangeAutoComplete, this);
card_input->SetMinSize(wxSize(100, -1));
deck_castable_inputs.push_back(card_input);
card_item_sizer->Add(card_input, wxSizerFlags(1).Expand());
// button
this->unique_wx_id++;
wxButton *card_info_but = new wxButton(this->main_panel, this->unique_wx_id, "", wxDefaultPosition, wxSize(50, -1));
card_info_but->SetName("deck_cast_info_but_" + myHelpFxns::ToWxString(std::to_string(i + 1)));
//card_info_but->Bind(wxEVT_BUTTON, &MullSimple_3Frame::GUIDeckPrintCardInfo, this);
deck_castable_info_buts.push_back(card_info_but);
card_item_sizer->Add(card_info_but, 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
deck_lands_title = new wxStaticText(main_panel, wxID_ANY, "Lands (0)", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL|wxST_NO_AUTORESIZE);
deck_lands_title->SetBackgroundColour(*wxGREEN);
deck_lands_sizer->Add(deck_lands_title, wxSizerFlags(0).Expand());
// lands content
for ( int i = 0; i < 25; i++ )
{
wxBoxSizer *card_item_sizer = new wxBoxSizer(wxHORIZONTAL);
deck_lands_sizer->Add(card_item_sizer, wxSizerFlags(1).Expand());
// input
this->unique_wx_id++;
wxTextCtrl *card_input = new wxTextCtrl(main_panel, this->unique_wx_id, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
//card_input->Bind(wxEVT_TEXT, &MullSimple_3Frame::GUIDeckCardsChangeValue, this);
//card_input->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::GUICardsChangeAutoComplete, this);
card_input->SetMinSize(wxSize(100, -1));
deck_land_inputs.push_back(card_input);
card_item_sizer->Add(card_input, wxSizerFlags(1).Expand());
// button
this->unique_wx_id++;
wxButton *card_info_but = new wxButton(this->main_panel, this->unique_wx_id, "", wxDefaultPosition, wxSize(50, -1));
card_info_but->SetName("deck_land_info_but_" + myHelpFxns::ToWxString(std::to_string(i + 1)));
//card_info_but->Bind(wxEVT_BUTTON, &MullSimple_3Frame::GUIDeckPrintCardInfo, this);
deck_land_info_buts.push_back(card_info_but);
card_item_sizer->Add(card_info_but, 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++ )
{
this->unique_wx_id++;
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, this->unique_wx_id, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
//text_ctrl->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::GUICardsChangeAutoComplete, this);
text_ctrl->SetMinSize(wxSize(100, -1));
hand_inputs.push_back(text_ctrl);
hand_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 4: MULL OPTS
// we'll set proportion 0 here and use the mull_opts_buttons to
// set a fixed width for the sizer
wxBoxSizer *mull_opts_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(mull_opts_sizer, wxSizerFlags(0).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(200, 50));
//mull_opts_but_add_need->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButAdd, 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(200, 50));
//mull_opts_but_remove_need->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButRemove, 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(200, 50));
//mull_opts_but_reset_needs->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButReset, this);
mull_opts_but_sizer->Add(mull_opts_but_reset_needs, wxSizerFlags(1).Expand());
// calculate button
mull_opts_but_simulate = new wxButton(main_panel, wxID_ANY, "Simulate");
mull_opts_but_simulate->SetMinSize(wxSize(200, 50));
//mull_opts_but_simulate->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButSimulate, this);
mull_opts_but_sizer->Add(mull_opts_but_simulate, 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, 200), wxTE_MULTILINE);
log_output->SetBackgroundColour(*wxYELLOW);
output_sizer ->Add(log_output, wxSizerFlags(1).Expand());
// TAB ORDER CHANGES
// tab order is annoying, no way to simply disable tabbing into an element.
// so for things i don't want to tab into easily, i use this to put them after the log_ouput
// which should be last in the panel
mull_info_name->MoveAfterInTabOrder(log_output); // mull info name input for the mull set
for ( unsigned int i = 0; i < deck_castable_info_buts.size(); i++ )
{
deck_castable_info_buts[i]->MoveAfterInTabOrder(log_output); // info buttons for each card (castables)
}
for ( unsigned int i = 0; i < deck_land_info_buts.size(); i++ )
{
deck_land_info_buts[i]->MoveAfterInTabOrder(log_output); // info buttons for each card (lands)
}
main_panel->SetSizerAndFit(main_panel_sizer);
this->Fit();
}
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.
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.
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.
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 )