penguins  1.0.0
new_game_dialog.cc
Go to the documentation of this file.
1 #include "gui/new_game_dialog.hh"
2 #include "gui/game_state.hh"
3 #include "utils.h"
4 #include <wx/choice.h>
5 #include <wx/event.h>
6 #include <wx/gdicmn.h>
7 #include <wx/math.h>
8 #include <wx/sizer.h>
9 #include <wx/spinbutt.h>
10 #include <wx/spinctrl.h>
11 #include <wx/statline.h>
12 #include <wx/stattext.h>
13 #include <wx/string.h>
14 #include <wx/textctrl.h>
15 #include <wx/version.h>
16 #ifdef __WXGTK__
17 #include <wx/artprov.h>
18 #include <wx/bmpbuttn.h>
19 // IWYU pragma: no_include <wx/bmpbndl.h>
20 #else
21 #include <wx/button.h>
22 #endif
23 
25 : wxDialog(parent, id, "New game options", wxDefaultPosition, wxDefaultSize) {
28 
29 #if wxCHECK_VERSION(3, 1, 4)
31 #else
32  float spacing = wxSizerFlags::GetDefaultBorder();
33 #endif
34 
35  this->options_grid = new wxFlexGridSizer(
36  /* cols */ 2,
37  /* vgap */ wxRound(spacing),
38  /* hgap */ wxRound(spacing * 3)
39  );
40 
41  this->width_input =
42  this->create_number_option("Board width:", wxID_ANY, 1, 1000, DEFAULT_BOARD_WIDTH);
44  this->height_input =
45  this->create_number_option("Board height:", wxID_ANY, 1, 1000, DEFAULT_BOARD_HEIGHT);
47 
48  wxString board_gen_types[BOARD_GEN_MAX] = {};
49  board_gen_types[BOARD_GEN_RANDOM] = "Random";
50  board_gen_types[BOARD_GEN_ISLAND] = "Island";
52  "Board generation type:", wxID_ANY, WXSIZEOF(board_gen_types), board_gen_types
53  );
55 
57  "Penguins per player:", wxID_ANY, 1, 10, DEFAULT_PENGUINS_PER_PLAYER
58  );
59  this->players_number_input =
60  this->create_number_option("Number of players:", wxID_ANY, 2, 5, DEFAULT_NUMBER_OF_PLAYERS);
62 
63  this->players_grid = new wxFlexGridSizer(
64  /* cols */ 3,
65  /* vgap */ wxRound(spacing),
66  /* hgap */ wxRound(spacing / 2)
67  );
68  this->players_grid->AddGrowableCol(0, 1);
69 
70  this->add_new_player_row(true);
71  for (int i = 0; i < this->players_number_input->GetValue(); i++) {
72  this->add_new_player_row();
73  }
74 
75  auto grids_vbox = new wxBoxSizer(wxVERTICAL);
76  grids_vbox->Add(this->options_grid, wxSizerFlags().Expand().DoubleBorder(wxBOTTOM));
77  grids_vbox->Add(this->players_grid, wxSizerFlags().Expand());
78 
79  auto outer_vbox = new wxBoxSizer(wxVERTICAL);
80  wxSizerFlags grids_vbox_sizer_flags = wxSizerFlags().Expand().DoubleBorder(wxALL);
81 #ifdef __WXOSX__
82  grids_vbox_sizer_flags.Border(wxALL, wxRound(spacing * 4));
83 #endif
84  outer_vbox->Add(grids_vbox, grids_vbox_sizer_flags);
86  this->SetEscapeId(wxID_CLOSE);
87  outer_vbox->Add(new wxStaticLine(this), wxSizerFlags().Expand());
88  wxSizerFlags buttons_sizer_flags = wxSizerFlags().Expand().DoubleBorder(wxALL);
89 #ifdef __WXGTK__
90  buttons_sizer_flags.DoubleBorder(wxTOP | wxBOTTOM);
91 #endif
92  outer_vbox->Add(this->buttons_sizer, buttons_sizer_flags);
93  this->SetSizerAndFit(outer_vbox);
94 
95  this->width_input->SetFocus();
96 }
97 
99  // <https://docs.wxwidgets.org/3.0/classwx_window_destroy_event.html#details>
100  this->SendDestroyEvent();
101 }
102 
104  auto label = new wxStaticText(this, input->GetId(), label_str);
105  this->options_grid->Add(label, wxSizerFlags().Centre().Left());
106  this->options_grid->Add(input, wxSizerFlags().Expand());
107  return input;
108 }
109 
111  const wxString& label, wxWindowID id, int min, int max, int initial
112 ) {
113  auto input = new wxSpinCtrl(
114  this, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, initial
115  );
116  this->add_option(label, input);
117  return input;
118 }
119 
121  const wxString& label, wxWindowID id, int n, const wxString choices[]
122 ) {
123  auto input = new wxChoice(this, id, wxDefaultPosition, wxDefaultSize, n, choices);
124  this->add_option(label, input);
125  return input;
126 }
127 
129  this->Layout();
130  this->GetSizer()->SetSizeHints(this);
131 }
132 
134  return this->width_input->GetValue();
135 }
136 
138  return this->height_input->GetValue();
139 }
140 
142  return static_cast<BoardGenType>(this->board_gen_input->GetSelection());
143 }
144 
146  return this->penguins_input->GetValue();
147 }
148 
150  return this->player_rows.size();
151 }
152 
154  return this->player_rows.at(index).name_input->GetValue();
155 }
156 
158  return static_cast<PlayerType>(this->player_rows.at(index).type_input->GetSelection());
159 }
160 
162  size_t len = this->player_rows.size();
163  if (count < len) {
164  for (size_t i = len - 1; i >= count; i--) {
165  this->delete_player_row(i);
166  }
167  } else if (count > len) {
168  for (size_t i = len; i < count; i++) {
169  this->add_new_player_row();
170  }
171  }
172  this->update_new_player_row();
173 }
174 
176  bool show = this->player_rows.size() < size_t(this->players_number_input->GetMax());
177  this->new_player_row.name_input->Show(show);
178  this->new_player_row.type_input->Show(show);
180  wxSizerItem* delete_btn_item = this->players_grid->GetItem(this->new_player_row.delete_btn);
181  delete_btn_item->SetFlag(
182  change_bit(delete_btn_item->GetFlag(), wxRESERVE_SPACE_EVEN_IF_HIDDEN, show)
183  );
184 }
185 
187  auto grid = this->players_grid;
188 
189  if (!initial) {
190  this->player_rows.push_back(this->new_player_row);
191  this->realize_player_row(this->player_rows.size() - 1);
192  }
193 
194  auto name_input = new wxTextCtrl(
196  );
197  name_input->Bind(wxEVT_TEXT, &NewGameDialog::on_player_name_input, this);
199  name_input->SetHint("Add a new player...");
200  grid->Add(name_input, wxSizerFlags().Expand());
201 
202  wxString player_types[PLAYER_TYPE_MAX];
203  player_types[PLAYER_NORMAL] = "Normal";
204  player_types[PLAYER_BOT] = "Bot";
205  auto type_input =
207  type_input->Select(PLAYER_NORMAL);
208  grid->Add(type_input, wxSizerFlags().Expand());
209 
210 #if defined(__WXGTK__)
211  auto delete_btn = new wxBitmapButton(this, wxID_ANY, wxArtProvider::GetIcon(wxART_CROSS_MARK));
212 #elif defined(__WXOSX__)
213  auto delete_btn =
215 #else
216  auto delete_btn =
218 #endif
219  delete_btn->Bind(wxEVT_BUTTON, &NewGameDialog::on_player_delete_clicked, this);
220  delete_btn->Hide();
221  grid->Add(delete_btn, wxSizerFlags().Expand());
222 
223  this->new_player_row = { type_input, name_input, delete_btn };
224  this->update_new_player_row();
225 }
226 
228  PlayerRowWidgets& row = this->player_rows.at(index);
229  row.name_input->SetHint(wxString::Format("Name of player %zd", index + 1));
230  row.delete_btn->Show();
231 }
232 
234  PlayerRowWidgets& row = this->player_rows.at(index);
235  row.name_input->Destroy();
236  row.type_input->Destroy();
237  row.delete_btn->Destroy();
238  this->player_rows.erase(this->player_rows.begin() + index);
239 }
240 
241 void NewGameDialog::on_ok(wxCommandEvent& WXUNUSED(event)) {
242  if (this->player_rows.empty()) {
243  return;
244  }
245  for (auto& row : this->player_rows) {
246  if (row.name_input->IsEmpty()) {
247  row.name_input->SetFocus();
248  return;
249  }
250  }
251  this->EndModal(wxID_OK);
252 }
253 
254 void NewGameDialog::on_close(wxCommandEvent& WXUNUSED(event)) {
255  this->EndModal(wxID_CLOSE);
256 }
257 
259  if (event.GetEventObject() == this->new_player_row.name_input) {
260  // Try to set the input value first, this will perform the range checks.
261  this->players_number_input->SetValue(this->player_rows.size() + 1);
263  this->update_layout();
264  }
265 }
266 
268  for (size_t i = 0; i < this->player_rows.size(); i++) {
269  if (this->player_rows.at(i).name_input == event.GetEventObject()) {
270  for (size_t j = i + 1; j < this->player_rows.size(); j++) {
271  PlayerRowWidgets& row = this->player_rows.at(j);
272  if (row.name_input->IsEmpty()) {
273  row.name_input->SetFocus();
274  return;
275  }
276  }
278  return;
279  }
280  }
281  event.Skip();
282 }
283 
285  width_was_changed = true;
286  if (!height_was_changed) {
287  this->height_input->SetValue(this->width_input->GetValue());
288  }
289 }
290 
292  height_was_changed = true;
293  if (!width_was_changed) {
294  this->width_input->SetValue(this->height_input->GetValue());
295  }
296 }
297 
299  for (size_t i = 0; i < this->player_rows.size(); i++) {
300  if (this->player_rows.at(i).delete_btn == event.GetEventObject()) {
301  if (this->player_rows.size() > size_t(this->players_number_input->GetMin())) {
302  this->delete_player_row(i);
303  this->players_number_input->SetValue(this->player_rows.size());
304  for (size_t j = 0; j < this->player_rows.size(); j++) {
305  this->realize_player_row(j);
306  }
307  this->update_new_player_row();
308  this->update_layout();
309  }
310  return;
311  }
312  }
313  event.Skip();
314 }
315 
318  this->update_layout();
319 }
320 
322  NewGameDialog* dialog = this->Get();
323  int value;
324  wxString str_value;
325  if (this->RestoreValue("board_width", &value)) dialog->width_input->SetValue(value);
326  if (this->RestoreValue("board_height", &value)) dialog->height_input->SetValue(value);
327  if (this->RestoreValue("board_gen_type", &value) && 0 <= value && value < BOARD_GEN_MAX) {
328  dialog->board_gen_input->Select(value);
329  }
330  if (this->RestoreValue("penguins_per_player", &value)) dialog->penguins_input->SetValue(value);
331  if (this->RestoreValue("players_count", &value)) {
332  dialog->players_number_input->SetValue(value);
334  for (int i = 0; i < int(dialog->player_rows.size()); i++) {
335  PlayerRowWidgets& row = dialog->player_rows.at(i);
336  if (this->RestoreValue(wxString::Format("player_%d_name", i), &str_value)) {
337  row.name_input->SetValue(str_value);
338  }
339  if (this->RestoreValue(wxString::Format("player_%d_type", i), &value) && 0 <= value && value < PLAYER_TYPE_MAX) {
340  row.type_input->Select(value);
341  }
342  }
343  }
344  dialog->update_layout();
345  return true;
346 }
347 
349  NewGameDialog* dialog = this->Get();
350  this->SaveValue("board_width", dialog->width_input->GetValue());
351  this->SaveValue("board_height", dialog->height_input->GetValue());
352  this->SaveValue("board_gen_type", dialog->board_gen_input->GetSelection());
353  this->SaveValue("penguins_per_player", dialog->penguins_input->GetValue());
354  this->SaveValue("players_count", dialog->players_number_input->GetValue());
355  for (int i = 0; i < int(dialog->player_rows.size()); i++) {
356  PlayerRowWidgets& row = dialog->player_rows.at(i);
357  this->SaveValue(wxString::Format("player_%d_name", i), row.name_input->GetValue());
358  this->SaveValue(wxString::Format("player_%d_type", i), row.type_input->GetSelection());
359  }
360 }
#define wxBU_EXACTFIT
const char * wxART_CROSS_MARK
virtual void Save() const override
virtual bool Restore() override
wxFlexGridSizer * options_grid
static const int DEFAULT_NUMBER_OF_PLAYERS
void delete_player_row(size_t index)
wxChoice * create_choice_option(const wxString &label, wxWindowID id, int n, const wxString choices[])
NewGameDialog(wxWindow *parent, wxWindowID id)
wxSpinCtrl * players_number_input
wxWindow * add_option(const wxString &label, wxWindow *input)
wxSpinCtrl * width_input
static const int DEFAULT_BOARD_WIDTH
void on_player_delete_clicked(wxCommandEvent &event)
virtual ~NewGameDialog()
wxString get_player_name(size_t index) const
void realize_player_row(size_t index)
static const int DEFAULT_PENGUINS_PER_PLAYER
int get_board_width() const
int get_penguins_per_player() const
BoardGenType get_board_gen_type() const
int get_board_height() const
void on_player_name_input(wxCommandEvent &event)
PlayerType get_player_type(size_t index) const
void on_close(wxCommandEvent &event)
void on_board_width_input(wxSpinEvent &event)
wxSpinCtrl * create_number_option(const wxString &label, wxWindowID id, int min, int max, int initial)
void on_board_height_input(wxSpinEvent &event)
void on_player_name_enter_pressed(wxCommandEvent &event)
wxVector< PlayerRowWidgets > player_rows
void on_players_number_input(wxSpinEvent &event)
wxFlexGridSizer * players_grid
void on_ok(wxCommandEvent &event)
wxSpinCtrl * penguins_input
void set_player_rows_count(size_t count)
PlayerRowWidgets new_player_row
void update_new_player_row()
wxStdDialogButtonSizer * buttons_sizer
static const int DEFAULT_BOARD_HEIGHT
wxSpinCtrl * height_input
wxChoice * board_gen_input
size_t get_number_of_players() const
void add_new_player_row(bool initial=false)
static wxIcon GetIcon(const wxArtID &id, const wxArtClient &client=wxART_OTHER, const wxSize &size=wxDefaultSize)
virtual int GetSelection() const
void SetEscapeId(int id)
void Centre(int direction=wxBOTH)
virtual void EndModal(int retCode)
wxStdDialogButtonSizer * CreateStdDialogButtonSizer(long flags)
wxObject * GetEventObject() const
void Bind(const EventTag &eventType, Functor functor, int id=wxID_ANY, int lastId=wxID_ANY, wxObject *userData=NULL)
void AddGrowableCol(size_t idx, int proportion=0)
bool RestoreValue(const wxString &name, T *value)
static int GetDefaultBorder()
wxSizerFlags & Border(int direction, int borderinpixels)
static float GetDefaultBorderFractional()
wxSizerFlags & Expand()
wxSizerFlags & DoubleBorder(int direction=wxALL)
wxSizerItem * Add(wxWindow *window, const wxSizerFlags &flags)
void SetSizeHints(wxWindow *window)
wxSizerItem * GetItem(wxWindow *window, bool recursive=false)
int GetFlag() const
void SetFlag(int flag)
virtual void SetValue(const wxString &text)
int GetMax() const
int GetValue() const
static wxString Format(const wxString &format,...)
virtual wxString GetValue() const
virtual bool SetHint(const wxString &hint)
virtual void SetValue(const wxString &value)
virtual bool IsEmpty() const
virtual bool Layout()
wxSizer * GetSizer() const
void SetSizerAndFit(wxSizer *sizer, bool deleteOld=true)
virtual void SetFocus()
virtual bool Destroy()
wxWindowID GetId() const
void SendDestroyEvent()
virtual bool Show(bool show=true)
virtual bool IsShown() const
#define wxSP_ARROW_KEYS
#define wxCLOSE
wxVERTICAL
wxRESERVE_SPACE_EVEN_IF_HIDDEN
#define wxOK
wxALL
wxBOTTOM
wxTOP
wxID_ANY
wxID_CLOSE
wxID_OK
BoardGenType
Definition: game_state.hh:5
@ BOARD_GEN_ISLAND
Definition: game_state.hh:7
@ BOARD_GEN_RANDOM
Definition: game_state.hh:6
@ BOARD_GEN_MAX
Definition: game_state.hh:8
PlayerType
Definition: game_state.hh:11
@ PLAYER_NORMAL
Definition: game_state.hh:12
@ PLAYER_BOT
Definition: game_state.hh:13
@ PLAYER_TYPE_MAX
Definition: game_state.hh:14
const wxSize wxDefaultSize
const wxPoint wxDefaultPosition
wxEventType wxEVT_BUTTON
int wxRound(T x)
wxString wxEmptyString
wxEventType wxEVT_SPINCTRL
wxEventType wxEVT_TEXT_ENTER
wxEventType wxEVT_TEXT
#define wxTE_PROCESS_ENTER
#define change_bit(num, bit, val)
Definition: utils.h:108
int wxWindowID