openMSX
ImGuiTrainer.cc
Go to the documentation of this file.
1#include "ImGuiTrainer.hh"
2
3#include "CustomFont.h"
4#include "ImGuiCpp.hh"
5#include "ImGuiManager.hh"
6#include "ImGuiUtils.hh"
7
8#include "StringOp.hh"
9#include "xrange.hh"
10
11#include <imgui_stdlib.h>
12
13#include <algorithm>
14
15namespace openmsx {
16
17using namespace std::literals;
18
19void ImGuiTrainer::save(ImGuiTextBuffer& buf)
20{
21 savePersistent(buf, *this, persistentElements);
22}
23
24void ImGuiTrainer::loadLine(std::string_view name, zstring_view value)
25{
26 loadOnePersistent(name, value, *this, persistentElements);
27}
28
29void ImGuiTrainer::paint(MSXMotherBoard* /*motherBoard*/)
30{
31 if (!show) return;
32
33 if (!trainers) {
35 // trainer.tcl script (actually lazy.tcl) has not yet been executed
36 return;
37 }
38 // Only query/sort this list once instead of on every frame.
39 // If loading fails, use an empty dict (don't keep on retying).
40 trainers = manager.execute(TclObject("trainer::load_trainers")).value_or(TclObject{});
41 // TODO use c++23 stride_view
42 gameNames = to_vector(view::transform(xrange(trainers->size() / 2), [&](auto i) {
43 return std::string(trainers->getListIndexUnchecked(2 * i).getString());
44 }));
45 ranges::sort(gameNames, StringOp::caseless{});
46 }
47 auto activeGame = manager.execute(makeTclList("set", "trainer::active_trainer")).value_or(TclObject{});
48 auto activeList = manager.execute(makeTclList("set", "trainer::items_active")).value_or(TclObject{});
49
50 std::optional<std::string> newGame;
51 std::optional<int> toggleItem;
52 bool all = false;
53 bool none = false;
54
55 ImGui::SetNextWindowSize(gl::vec2{28, 26} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
56 im::Window("Trainer Selector", &show, ImGuiWindowFlags_HorizontalScrollbar, [&]{
57 ImGui::TextUnformatted("Select Game:"sv);
58 zstring_view displayName = activeGame.getString();
59 if (displayName.empty()) displayName = "none";
60 bool useFilter = im::TreeNode("filter", [&]{
61 ImGui::InputText(ICON_IGFD_FILTER, &filterString);
62 HelpMarker("A list of substrings that must be part of the game name.\n"
63 "\n"
64 "For example: enter 'vamp' to search for 'Akumajyo Drakyula - Vampire Killer'.");
65 });
66 auto drawGameNames = [&](size_t num, auto getName) {
67 int selectedIdx = -1;
68 if (ImGui::IsWindowAppearing()) { // The ComboBox popup window
69 for (auto i : xrange(num)) {
70 if (getName(i) == displayName) {
71 selectedIdx = narrow<int>(i) + 1;
72 }
73 }
74 }
75 im::ListClipper(1 + num, selectedIdx, [&](int i) {
76 if (i == 0) {
77 if (ImGui::Selectable("none", displayName == "none")) {
78 newGame = "deactivate";
79 }
80 } else {
81 auto name = getName(size_t(i - 1));
82 if (ImGui::Selectable(name.c_str(), name == displayName)) {
83 newGame = name;
84 }
85 if (i == selectedIdx) {
86 ImGui::SetItemDefaultFocus();
87 }
88 }
89 });
90 };
91 auto getGameName = [&](size_t i) { return gameNames[i]; };
92 if (useFilter) {
93 auto indices = to_vector(xrange(gameNames.size()));
94 filterIndices(filterString, getGameName, indices);
95 im::ListBox("##game", {-FLT_MIN, 0.0f}, [&]{
96 drawGameNames(indices.size(), [&](size_t i) { return gameNames[indices[i]]; });
97 });
98 } else {
99 ImGui::SetNextItemWidth(-FLT_MIN);
100 im::Combo("##game", displayName.c_str(), [&]{
101 drawGameNames(gameNames.size(), getGameName);
102 });
103 }
104 ImGui::Separator();
105
106 im::Disabled(activeGame.getString().empty(), [&]{
107 ImGui::AlignTextToFramePadding();
108 ImGui::TextUnformatted("Select Cheats:"sv);
109 ImGui::SameLine();
110 all = ImGui::Button("All");
111 ImGui::SameLine();
112 none = ImGui::Button("None");
113
114 TclObject items = trainers->getOptionalDictValue(activeGame).value_or(TclObject{})
115 .getOptionalDictValue(TclObject("items")).value_or(TclObject{});
116 auto numItems = std::min(activeList.size(), items.size() / 2);
117 for (auto i : xrange(numItems)) {
118 bool active = activeList.getListIndexUnchecked(i).getOptionalBool().value_or(false);
119 auto name = items.getListIndexUnchecked(2 * i + 0).getString();
120 if (ImGui::Checkbox(name.c_str(), &active)) {
121 toggleItem = i;
122 }
123 }
124 });
125 });
126
127 if (newGame) {
128 manager.execute(makeTclList("trainer", *newGame));
129 } else if (toggleItem) {
130 manager.execute(makeTclList("trainer", activeGame, *toggleItem + 1));
131 } else if (all || none) {
132 auto cmd = makeTclList("trainer", activeGame);
133 for (auto i : xrange(activeList.size())) {
134 if (activeList.getListIndexUnchecked(i).getOptionalBool().value_or(false) == none) {
135 cmd.addListElement(i + 1);
136 }
137 }
138 manager.execute(cmd);
139 }
140}
141
142} // namespace openmsx
std::optional< TclObject > execute(TclObject command)
ImGuiManager & manager
Definition ImGuiPart.hh:30
void save(ImGuiTextBuffer &buf) override
void loadLine(std::string_view name, zstring_view value) override
void paint(MSXMotherBoard *motherBoard) override
bool isFullyStarted() const
Definition Reactor.hh:129
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr const char * c_str() const
constexpr auto empty() const
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:26
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:63
bool TreeNode(const char *label, ImGuiTreeNodeFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:302
void Combo(const char *label, const char *preview_value, ImGuiComboFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:289
void ListBox(const char *label, const ImVec2 &size, std::invocable<> auto next)
Definition ImGuiCpp.hh:328
void Disabled(bool b, std::invocable<> auto next)
Definition ImGuiCpp.hh:506
void ListClipper(size_t count, int forceIndex, float lineHeight, std::invocable< int > auto next)
Definition ImGuiCpp.hh:538
This file implemented 3 utility functions:
Definition Autofire.cc:11
void filterIndices(std::string_view filterString, GetName getName, std::vector< size_t > &indices)
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
void HelpMarker(std::string_view desc)
Definition ImGuiUtils.cc:23
TclObject makeTclList(Args &&... args)
Definition TclObject.hh:293
constexpr void sort(RandomAccessRange &&range)
Definition ranges.hh:51
size_t size(std::string_view utf8)
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
Definition stl.hh:278
constexpr auto xrange(T e)
Definition xrange.hh:132