| 1 |
#include "ImGuiConnector.hh" |
| 2 |
|
| 3 |
#include "ImGuiCpp.hh" |
| 4 |
#include "ImGuiManager.hh" |
| 5 |
#include "ImGuiUtils.hh" |
| 6 |
|
| 7 |
#include "Connector.hh" |
| 8 |
#include "MSXMotherBoard.hh" |
| 9 |
#include "Pluggable.hh" |
| 10 |
#include "PluggingController.hh" |
| 11 |
|
| 12 |
#include "ImGuiPluggable.hh" |
| 13 |
|
| 14 |
#include <imgui.h> |
| 15 |
|
| 16 |
#include <algorithm> |
| 17 |
|
| 18 |
using namespace std::literals; |
| 19 |
|
| 20 |
namespace openmsx { |
| 21 |
|
| 22 |
[[nodiscard]] static zstring_view pluggableToGuiString(zstring_view pluggable) |
| 23 |
{ |
| 24 |
if (pluggable == "msxjoystick1") return "MSX joystick 1"; |
| 25 |
if (pluggable == "msxjoystick2") return "MSX joystick 2"; |
| 26 |
if (pluggable == "joymega1") return "JoyMega controller 1"; |
| 27 |
if (pluggable == "joymega2") return "JoyMega controller 2"; |
| 28 |
if (pluggable == "joyhandle1") return "Panasonic FS-JH1 Joy Handle 1"; |
| 29 |
if (pluggable == "joyhandle2") return "Panasonic FS-JH1 Joy Handle 2"; |
| 30 |
if (pluggable == "arkanoidpad") return "Arkanoid Vaus paddle"; |
| 31 |
if (pluggable == "ninjatap") return "Ninja Tap"; |
| 32 |
if (pluggable == "cassetteplayer") return "Tape deck"; |
| 33 |
if (pluggable == "simpl") return "SIMPL"; |
| 34 |
if (pluggable == "msx-printer") return "MSX printer"; |
| 35 |
if (pluggable == "epson-printer") return "Epson printer"; |
| 36 |
if (pluggable == "MSXPlotter") return "MSX plotter"; |
| 37 |
if (pluggable == "tetris2-protection") return "Tetris II SE dongle"; |
| 38 |
if (pluggable == "circuit-designer-rd-dongle") return "Circuit Designer RD dongle"; |
| 39 |
return pluggable; |
| 40 |
} |
| 41 |
|
| 42 |
void ImGuiConnector::showMenu(MSXMotherBoard* motherBoard) |
| 43 |
{ |
| 44 |
im::Menu("Connectors", motherBoard != nullptr, [&]{ |
| 45 |
showPluggables(motherBoard->getPluggingController(), Mode::COMBO); |
| 46 |
}); |
| 47 |
} |
| 48 |
|
| 49 |
void ImGuiConnector::paintPluggableSelectables(PluggingController& controller, const Connector& connector) |
| 50 |
{ |
| 51 |
const auto& currentPluggable = connector.getPlugged(); |
| 52 |
const auto& connectorName = connector.getName(); |
| 53 |
if (!currentPluggable.getName().empty()) { |
| 54 |
if (ImGui::Selectable("[unplug]")) { |
| 55 |
manager.executeDelayed(makeTclList("unplug", connectorName)); |
| 56 |
} |
| 57 |
} |
| 58 |
auto connectorClass = connector.getClass(); |
| 59 |
for (const auto& plug : controller.getPluggables()) { |
| 60 |
if (plug->getClass() != connectorClass) continue; |
| 61 |
auto plugName = plug->getName(); |
| 62 |
bool selected = plug.get() == ¤tPluggable; |
| 63 |
int flags = !selected && plug->getConnector() ? ImGuiSelectableFlags_Disabled : 0; |
| 64 |
if (ImGui::Selectable(pluggableToGuiString(plugName).c_str(), selected, flags)) { |
| 65 |
manager.executeDelayed(makeTclList("plug", connectorName, plugName)); |
| 66 |
} |
| 67 |
simpleToolTip(plug->getDescription()); |
| 68 |
if (selected) { |
| 69 |
ImGui::SetItemDefaultFocus(); |
| 70 |
} |
| 71 |
} |
| 72 |
} |
| 73 |
|
| 74 |
void ImGuiConnector::paintImGuiExtra(Pluggable& pluggable) |
| 75 |
{ |
| 76 |
if (auto* extra = dynamic_cast<ImGuiPluggable*>(&pluggable)) { |
| 77 |
extra->handleImGuiExtraMenuItems(); |
| 78 |
} |
| 79 |
} |
| 80 |
|
| 81 |
void ImGuiConnector::showPluggables(PluggingController& controller, Mode mode) |
| 82 |
{ |
| 83 |
im::Table("##shared-table", 2, [&]{ |
| 84 |
ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthFixed); |
| 85 |
ImGui::TableSetupColumn("Value"); |
| 86 |
for (const auto* connector : controller.getConnectors()) { |
| 87 |
if (ImGui::TableNextColumn()) { |
| 88 |
ImGui::TextUnformatted(connector->getDescription()); |
| 89 |
} |
| 90 |
if (ImGui::TableNextColumn()) { |
| 91 |
auto& currentPluggable = connector->getPlugged(); |
| 92 |
const auto& plugName = currentPluggable.getName(); |
| 93 |
const auto& connectorName = connector->getName(); |
| 94 |
using enum ImGuiConnector::Mode; |
| 95 |
switch (mode) { |
| 96 |
case VIEW: |
| 97 |
ImGui::TextUnformatted(plugName.empty() ? "(empty)"sv : pluggableToGuiString(plugName)); |
| 98 |
simpleToolTip(currentPluggable.getDescription()); |
| 99 |
break; |
| 100 |
case COMBO: |
| 101 |
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12.0f); |
| 102 |
im::Combo(tmpStrCat("##", connectorName).c_str(), pluggableToGuiString(plugName).c_str(), [&]{ |
| 103 |
paintPluggableSelectables(controller, *connector); |
| 104 |
}); |
| 105 |
simpleToolTip(currentPluggable.getDescription()); |
| 106 |
paintImGuiExtra(currentPluggable); |
| 107 |
break; |
| 108 |
case SUBMENU: |
| 109 |
if (plugName.empty() && std::ranges::none_of(controller.getPluggables(), [&](const auto& plug) { |
| 110 |
return plug->getClass() == connector->getClass(); })) { |
| 111 |
|
| 112 |
ImGui::TextUnformatted("(empty)"sv); |
| 113 |
break; |
| 114 |
} |
| 115 |
im::Menu(tmpStrCat(strCat_if(plugName.empty(), "(empty)##", connectorName).else_(STRCAT_LAZY(pluggableToGuiString(plugName)))).c_str(), [&] { |
| 116 |
paintPluggableSelectables(controller, *connector); |
| 117 |
}); |
| 118 |
paintImGuiExtra(currentPluggable); |
| 119 |
break; |
| 120 |
default: |
| 121 |
UNREACHABLE; |
| 122 |
} |
| 123 |
} |
| 124 |
} |
| 125 |
}); |
| 126 |
} |
| 127 |
|
| 128 |
|
| 129 |
} |