Skip to content

Commit

Permalink
Option set dialog for large sets
Browse files Browse the repository at this point in the history
  • Loading branch information
hatkirby committed Feb 20, 2024
1 parent 32e1a3f commit e8f6edf
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 55 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_executable(ap_wizard
src/random_choice_dialog.cc
src/random_range_dialog.cc
src/util.cc
src/option_set_dialog.cc
)
set_property(TARGET ap_wizard PROPERTY CXX_STANDARD 20)
set_property(TARGET ap_wizard PROPERTY CXX_STANDARD_REQUIRED ON)
Expand Down
27 changes: 19 additions & 8 deletions src/game_definition.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <set>

#include "util.h"

Expand All @@ -13,20 +14,30 @@ GameDefinitions::GameDefinitions() {
for (const auto& [game_name, game_data] : all_games.items()) {
std::vector<OptionDefinition> options;

DoubleMap<std::string> game_items;
std::set<std::string> sorted_game_items;
for (const auto& item_name : game_data["itemGroups"]) {
game_items.Append(item_name);
sorted_game_items.insert(item_name);
}
for (const auto& item_name : game_data["items"]) {
game_items.Append(item_name);
sorted_game_items.insert(item_name);
}

DoubleMap<std::string> game_locations;
DoubleMap<std::string> game_items;
for (const std::string& game_item : sorted_game_items) {
game_items.Append(game_item);
}

std::set<std::string> sorted_game_locations;
for (const auto& location_name : game_data["locationGroups"]) {
game_locations.Append(location_name);
sorted_game_locations.insert(location_name);
}
for (const auto& location_name : game_data["locations"]) {
game_locations.Append(location_name);
sorted_game_locations.insert(location_name);
}

DoubleMap<std::string> game_locations;
for (const std::string& game_location : sorted_game_locations) {
game_locations.Append(game_location);
}

for (const auto& [option_name, option_data] :
Expand Down Expand Up @@ -61,13 +72,13 @@ GameDefinitions::GameDefinitions() {
option.set_type = kCustomSet;

for (const auto& choice : option_data["options"]) {
option.choices.Append(choice, choice);
option.custom_set.Append(choice);
option.default_value.set_values.push_back(false);
}

for (const auto& default_value : option_data["defaultValue"]) {
option.default_value
.set_values[option.choices.GetKeyId(default_value)] = true;
.set_values[option.custom_set.GetId(default_value)] = true;
}
} else if (option_data["type"] == "items-set") {
option.type = kSetOption;
Expand Down
2 changes: 2 additions & 0 deletions src/game_definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ struct OptionDefinition {
OrderedBijection<int, std::string> value_names; // value, display name

SetType set_type = kUNKNOWN_SET_TYPE;
DoubleMap<std::string> custom_set;

OrderedBijection<std::string, std::string> choices; // id, display name

OptionValue default_value;
Expand Down
185 changes: 185 additions & 0 deletions src/option_set_dialog.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#include "option_set_dialog.h"

#include "double_map.h"
#include "util.h"

OptionSetDialog::OptionSetDialog(const Game* game,
const std::string& option_name,
const OptionValue& option_value)
: wxDialog(nullptr, wxID_ANY, "Value Picker"),
game_(game),
option_definition_(&game->GetOption(option_name)) {
// Initialize the form.
wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL);

wxPanel* desc_panel = new wxPanel(this, wxID_ANY);
wxStaticBoxSizer* desc_sizer =
new wxStaticBoxSizer(wxVERTICAL, desc_panel, "Option Description");

wxStaticText* option_header =
new wxStaticText(desc_sizer->GetStaticBox(), wxID_ANY, "");
option_header->SetFont(option_header->GetFont().Bold());

wxStaticText* option_description =
new wxStaticText(desc_sizer->GetStaticBox(), wxID_ANY, "");

desc_sizer->Add(option_header, wxSizerFlags().Expand());
desc_sizer->AddSpacer(10);
desc_sizer->Add(option_description, wxSizerFlags().Expand());
desc_panel->SetSizer(desc_sizer);

top_sizer->Add(desc_panel, wxSizerFlags().DoubleBorder().Expand());

// Set up the source list
wxPanel* lists_panel = new wxPanel(this, wxID_ANY);
wxStaticBoxSizer* lists_sizer =
new wxStaticBoxSizer(wxHORIZONTAL, lists_panel, "Option Values");

source_filter_ = new wxTextCtrl(lists_sizer->GetStaticBox(), wxID_ANY);
source_filter_->Bind(wxEVT_TEXT, &OptionSetDialog::OnFilterEdited, this);

source_list_ = new wxListView(lists_sizer->GetStaticBox(), wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
UpdateSourceList();

wxBoxSizer* filter_sizer = new wxBoxSizer(wxHORIZONTAL);
filter_sizer->Add(
new wxStaticText(lists_sizer->GetStaticBox(), wxID_ANY, "Filter:"),
wxSizerFlags().Center());
filter_sizer->AddSpacer(10);
filter_sizer->Add(source_filter_, wxSizerFlags().Proportion(1).Expand());

wxButton* add_btn =
new wxButton(lists_sizer->GetStaticBox(), wxID_ANY, "Add");
add_btn->Bind(wxEVT_BUTTON, &OptionSetDialog::OnAddClicked, this);

wxBoxSizer* left_sizer = new wxBoxSizer(wxVERTICAL);
left_sizer->Add(filter_sizer, wxSizerFlags().Expand());
left_sizer->AddSpacer(10);
left_sizer->Add(source_list_, wxSizerFlags().Proportion(1).Expand());
left_sizer->AddSpacer(10);
left_sizer->Add(add_btn, wxSizerFlags().Center());

lists_sizer->Add(left_sizer,
wxSizerFlags().DoubleBorder().Proportion(1).Expand());

// Set up the chosen list
chosen_list_ = new wxDataViewListCtrl(lists_sizer->GetStaticBox(), wxID_ANY);
chosen_list_->AppendTextColumn("Value");

const DoubleMap<std::string>& option_set =
GetOptionSetElements(*game_, option_name);
for (int i = 0; i < option_value.set_values.size(); i++) {
if (option_value.set_values.at(i)) {
std::string str_val = option_set.GetValue(i);

wxVector<wxVariant> data;
data.push_back(wxVariant(str_val));
chosen_list_->AppendItem(data);

picked_.insert(str_val);
}
}

wxButton* remove_btn =
new wxButton(lists_sizer->GetStaticBox(), wxID_ANY, "Remove");
remove_btn->Bind(wxEVT_BUTTON, &OptionSetDialog::OnRemoveClicked, this);

wxBoxSizer* right_sizer = new wxBoxSizer(wxVERTICAL);
right_sizer->Add(chosen_list_, wxSizerFlags().Proportion(1).Expand());
right_sizer->AddSpacer(10);
right_sizer->Add(remove_btn, wxSizerFlags().Center());

lists_sizer->Add(right_sizer,
wxSizerFlags().DoubleBorder().Proportion(1).Expand());

lists_panel->SetSizerAndFit(lists_sizer);
top_sizer->Add(lists_panel, wxSizerFlags().DoubleBorder().Expand());
top_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Expand());

// Finish up the form.
SetSizer(top_sizer);
Layout();
SetMinSize(GetSize());
Fit();

int width = option_header->GetClientSize().GetWidth();
option_header->SetLabel(option_definition_->display_name);
option_header->Wrap(width);

option_description->SetLabel(option_definition_->description);
option_description->Wrap(width);

Fit();
CentreOnParent();

UpdateSourceList();
}

OptionValue OptionSetDialog::GetOptionValue() const {
const DoubleMap<std::string>& option_set =
GetOptionSetElements(*game_, option_definition_->name);

OptionValue option_value;
option_value.set_values.resize(option_set.size());

for (const std::string& name : picked_) {
option_value.set_values[option_set.GetId(name)] = true;
}

return option_value;
}

void OptionSetDialog::UpdateSourceList() {
source_list_->ClearAll();
source_list_->AppendColumn("Value");

const DoubleMap<std::string>& option_set =
GetOptionSetElements(*game_, option_definition_->name);

int i = 0;
for (const std::string& list_item : option_set.GetList()) {
if (!source_filter_->GetValue().IsEmpty()) {
wxString wx_list = wxString(list_item).Lower();
if (wx_list.Find(source_filter_->GetValue().Lower()) == wxNOT_FOUND) {
continue;
}
}

source_list_->InsertItem(i, list_item);
i++;
}

source_list_->SetColumnWidth(0, wxLIST_AUTOSIZE);
}

void OptionSetDialog::OnFilterEdited(wxCommandEvent&) { UpdateSourceList(); }

void OptionSetDialog::OnAddClicked(wxCommandEvent& event) {
long selection = source_list_->GetFirstSelected();
if (selection == -1) {
return;
}

wxString selected_text = source_list_->GetItemText(selection, 0);
if (picked_.count(selected_text.ToStdString())) {
return;
}

wxVector<wxVariant> data;
data.push_back(wxVariant(selected_text));
chosen_list_->AppendItem(data);

picked_.insert(selected_text.ToStdString());
}

void OptionSetDialog::OnRemoveClicked(wxCommandEvent& event) {
int selection = chosen_list_->GetSelectedRow();
if (selection == wxNOT_FOUND) {
return;
}

picked_.erase(chosen_list_->GetTextValue(selection, 0).ToStdString());
chosen_list_->DeleteItem(selection);
}
41 changes: 41 additions & 0 deletions src/option_set_dialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef OPTION_SET_DIALOG_H_9F0D48DA
#define OPTION_SET_DIALOG_H_9F0D48DA

#include <wx/wxprec.h>

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <wx/dataview.h>
#include <wx/listctrl.h>

#include <set>
#include <string>

#include "game_definition.h"

class OptionSetDialog : public wxDialog {
public:
OptionSetDialog(const Game* game, const std::string& option_name,
const OptionValue& option_value);

OptionValue GetOptionValue() const;

private:
void UpdateSourceList();

void OnFilterEdited(wxCommandEvent& event);
void OnAddClicked(wxCommandEvent& event);
void OnRemoveClicked(wxCommandEvent& event);

const Game* game_;
const OptionDefinition* option_definition_;
wxListView* source_list_;
wxTextCtrl* source_filter_;
wxDataViewListCtrl* chosen_list_;

std::set<std::string> picked_;
};

#endif /* end of include guard: OPTION_SET_DIALOG_H_9F0D48DA */
15 changes: 15 additions & 0 deletions src/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,18 @@ std::string RandomOptionValueToString(const OptionValue& option_value) {

return random_str.str();
}

const DoubleMap<std::string>& GetOptionSetElements(
const Game& game, const std::string& option_name) {
const OptionDefinition& game_option = game.GetOption(option_name);

if (game_option.set_type == kCustomSet) {
return game_option.custom_set;
} else if (game_option.set_type == kItemSet) {
return game.GetItems();
} else if (game_option.set_type == kLocationSet) {
return game.GetLocations();
}

throw std::invalid_argument("Invalid option set type.");
}
4 changes: 4 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <iterator>
#include <string>

#include "double_map.h"
#include "game_definition.h"

template <class OutputIterator>
Expand Down Expand Up @@ -37,4 +38,7 @@ OptionValue GetRandomOptionValueFromString(std::string descriptor);

std::string RandomOptionValueToString(const OptionValue& option_value);

const DoubleMap<std::string>& GetOptionSetElements(
const Game& game, const std::string& option_name);

#endif /* end of include guard: UTIL_H_84145E76 */
Loading

0 comments on commit e8f6edf

Please sign in to comment.