more wrappers and helpers!

This commit is contained in:
SimoZ64
2025-04-29 22:40:07 +02:00
parent b319255178
commit 730f5bed24
11 changed files with 194 additions and 89 deletions

View File

@@ -1,9 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
project(kaizen) project(kaizen)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (WIN32) if (WIN32)
add_compile_definitions(NOMINMAX) add_compile_definitions(NOMINMAX)
endif () endif ()
@@ -129,6 +126,7 @@ add_executable(kaizen
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags discord-rpc fmt mio nlohmann_json parallel-rdp capstone backend) target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags discord-rpc fmt mio nlohmann_json parallel-rdp capstone backend)
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED) target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
set_target_properties(kaizen PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON)
file(COPY ../../resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/) file(COPY ../../resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/)
file(REMOVE file(REMOVE

View File

@@ -15,7 +15,7 @@ bool CPUSettings::render() {
if(cpuTypes.getCurrentIndex() == 0) { if(cpuTypes.getCurrentIndex() == 0) {
JSONSetField(settings, "cpu", "type", "interpreter"); JSONSetField(settings, "cpu", "type", "interpreter");
} else { } else {
Util::panic("JIT is unfinished and currently not supported!"); Util::panic("JIT should not be selectable??");
} }
modified = true; modified = true;

View File

@@ -3,11 +3,12 @@
#include <ImGuiImpl/Combobox.hpp> #include <ImGuiImpl/Combobox.hpp>
class CPUSettings final { class CPUSettings final {
gui::Combobox cpuTypes{"CPU type:", {"Interpreter" //, "Dynamic Recompiler" gui::Combobox cpuTypes{"CPU type:", {{"Interpreter"}, {"Dynamic Recompiler", false}}};
}};
bool modified = false; bool modified = false;
public: public:
bool render(); bool render();
explicit CPUSettings(nlohmann::json &); explicit CPUSettings(nlohmann::json &);
void setModified(bool v) { modified = v; }
bool getModified() { return modified; }
nlohmann::json &settings; nlohmann::json &settings;
}; };

View File

@@ -1,13 +1,40 @@
#pragma once #pragma once
#include <imgui.h> #include <imgui.h>
#include <string> #include <string>
#include <vector> #include <ranges>
namespace gui { namespace gui {
struct Combobox { struct ComboItem {
Combobox(std::string label, std::vector<std::string> items, std::string preview = "", bool enabled = true) : label(label), items(items), preview(preview), enabled(enabled) {} ComboItem(std::string label, bool enabled = true) : enabled(enabled) {}
void addItem(const std::string& text) { items.push_back(text); } bool render(bool is_selected) {
ImGui::BeginDisabled(!enabled);
bool ret = ImGui::Selectable(label.c_str(), is_selected);
ImGui::EndDisabled();
return ret;
}
const std::string& getLabel() { return label; }
void setLabel(const std::string& text) { label = text; }
private:
bool enabled = true;
std::string label;
};
struct Combobox {
Combobox(std::string label, std::vector<ComboItem> items, std::string preview = "", bool enabled = true) : label(label), items(items), preview(preview), enabled(enabled) {}
void addItem(ComboItem& item) {
if(std::find(items.begin(), items.end(),
[&item](ComboItem& a) { return a.getLabel() == item.getLabel(); }) != items.end())
return;
items.push_back(item);
}
void removeItem(const std::string& item) {
std::erase_if(items, [&item](ComboItem a) { return a.getLabel() == item; });
}
bool render() { bool render() {
const char* _preview = ""; const char* _preview = "";
@@ -15,7 +42,7 @@ struct Combobox {
if(preview != "") { if(preview != "") {
_preview = preview.c_str(); _preview = preview.c_str();
} else { } else {
_preview = items[current_index].c_str(); _preview = items[current_index].getLabel().c_str();
} }
bool changed = false; bool changed = false;
@@ -24,7 +51,7 @@ struct Combobox {
if (ImGui::BeginCombo(label.c_str(), _preview)) { if (ImGui::BeginCombo(label.c_str(), _preview)) {
for (int n = 0; n < items.size(); n++) { for (int n = 0; n < items.size(); n++) {
const bool is_selected = ((current_index) == n); const bool is_selected = ((current_index) == n);
if (ImGui::Selectable(items[n].c_str(), is_selected)) if (items[n].render(is_selected))
{ {
current_index = n; current_index = n;
changed = true; changed = true;
@@ -41,12 +68,13 @@ struct Combobox {
} }
void setEnabled(bool v) { enabled = v; } void setEnabled(bool v) { enabled = v; }
bool isEnabled() { return enabled; }
void setCurrentIndex(int v) { current_index = v; } void setCurrentIndex(int v) { current_index = v; }
int getCurrentIndex() { return current_index; } int getCurrentIndex() { return current_index; }
private: private:
bool enabled = true; bool enabled = true;
std::vector<std::string>& items; std::vector<ComboItem> items;
std::string label, preview; std::string label, preview;
int current_index = 0; int current_index = 0;
}; };

View File

@@ -21,10 +21,10 @@ struct PushButton {
void setEnabled(bool v) { enabled = v; } void setEnabled(bool v) { enabled = v; }
bool getEnabled() { return enabled; } bool getEnabled() { return enabled; }
std::string& getName() const { return name; } const std::string& getName() { return name; }
void setName(const std::string& v) { name = v; } void setName(const std::string& v) { name = v; }
std::string& getLabel() const { return label; } const std::string& getLabel() { return label; }
void setLabel(const std::string& v) { label = v; } void setLabel(const std::string& v) { label = v; }
private: private:
bool enabled = true; bool enabled = true;

View File

@@ -0,0 +1,54 @@
#pragma once
#include <imgui.h>
#include <string>
#include <vector>
namespace gui {
struct TabItem {
TabItem(std::string label, void(*func)(TabItem*, void* userData) = nullptr, void* userData = nullptr, bool enabled = true) : exec(func), enabled(enabled), userData(userData) {}
bool render() {
bool ret = false;
ImGui::BeginDisabled(!enabled);
if(ImGui::BeginTabItem(label.c_str())) {
if(exec)
exec(this, userData);
ImGui::EndTabItem();
ret = true;
}
ImGui::EndDisabled();
return ret;
}
private:
bool enabled = true;
std::string label;
void(*exec)(TabItem*, void* userData);
void* userData;
};
struct TabBar {
TabBar(std::string label) : label(label) {}
void addTab(TabItem tabItem) {
tabs.push_back(tabItem);
}
bool render() {
bool ret = false;
ImGui::BeginDisabled(!enabled);
if(ImGui::BeginTabBar(label.c_str())) {
ret = true;
for(auto& tab : tabs) {
ret &= tab.render();
}
ImGui::EndTabBar();
}
ImGui::EndDisabled();
return ret;
}
private:
bool enabled = true;
std::string label;
std::vector<TabItem> tabs;
};
}

View File

@@ -2,13 +2,14 @@
#include <log.hpp> #include <log.hpp>
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h> #include <SDL3/SDL_init.h>
#include <SDL3/SDL_keycode.h>
InputSettings::InputSettings(nlohmann::json &settings) : settings(settings) { InputSettings::InputSettings(nlohmann::json &settings) : settings(settings) {
for(auto& kb : kbButtons) { for(auto& kb : kbButtons) {
kb.setLabel(JSONGetField<std::string>(settings, "input", kb.getName())); kb.setLabel(JSONGetField<std::string>(settings, "input", kb.getName()));
} }
devices.addItem("Keyboard/Mouse"); devices.addItem({"Keyboard/Mouse"});
/* TODO: GAMEPAD STUFF IDK HOW TO HANDLE YET /* TODO: GAMEPAD STUFF IDK HOW TO HANDLE YET
connect(devices.get(), &QComboBox::currentTextChanged, this, [&](const QString &text) { connect(devices.get(), &QComboBox::currentTextChanged, this, [&](const QString &text) {
@@ -51,16 +52,16 @@ void InputSettings::keyPressEvent(QKeyEvent *e) {
if (grabbing) { if (grabbing) {
const auto k = QKeySequence(e->key()).toString().toStdString(); const auto k = QKeySequence(e->key()).toString().toStdString();
JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k); JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k);
kbButtons[whichGrabbing]->setText(k.c_str()); kbButtons[whichGrabbing].setText(k.c_str());
devices->setEnabled(true); devices.setEnabled(true);
for (const auto &kbButton : kbButtons) { for (const auto &kbButton : kbButtons) {
kbButton->setEnabled(true); kbButton.setEnabled(true);
} }
grabbing = false; grabbing = false;
whichGrabbing = -1; whichGrabbing = -1;
if (devices->currentText() == "Keyboard/Mouse") { if (devices.currentText() == "Keyboard/Mouse") {
releaseKeyboard(); releaseKeyboard();
emit modified(); emit modified();
} }
@@ -73,16 +74,15 @@ std::array<SDL_Keycode, 18> InputSettings::GetMappedKeys() {
int i = 0; int i = 0;
for (const auto& kb : kbButtons) { for (auto& kb : kbButtons) {
ret[i++] = SDL_GetKeyFromName(kb.getLabel().c_str()); ret[i++] = SDL_GetKeyFromName(kb.getLabel().c_str());
} }
return ret; return ret;
} }
/*TODO: RECREATE THIS IN SDL
void InputSettings::QueryDevices() noexcept { void InputSettings::QueryDevices() noexcept {
if (!devices->isEnabled()) if (!devices.isEnabled())
return; return;
SDL_Event e; SDL_Event e;
@@ -102,17 +102,17 @@ void InputSettings::QueryDevices() noexcept {
if (!gamepadIndexes.contains(index)) { if (!gamepadIndexes.contains(index)) {
gamepadIndexes[index] = name; gamepadIndexes[index] = name;
} }
devices->addItem(name); devices.addItem({name});
} else if (serial) { } else if (serial) {
if (!gamepadIndexes.contains(index)) { if (!gamepadIndexes.contains(index)) {
gamepadIndexes[index] = serial; gamepadIndexes[index] = serial;
} }
devices->addItem(serial); devices.addItem({serial});
} else if (path) { } else if (path) {
if (!gamepadIndexes.contains(index)) { if (!gamepadIndexes.contains(index)) {
gamepadIndexes[index] = path; gamepadIndexes[index] = path;
} }
devices->addItem(path); devices.addItem({path});
} }
SDL_CloseGamepad(gamepad); SDL_CloseGamepad(gamepad);
@@ -123,13 +123,14 @@ void InputSettings::QueryDevices() noexcept {
const auto index = e.gdevice.which; const auto index = e.gdevice.which;
if (gamepadIndexes.contains(index)) if (gamepadIndexes.contains(index))
devices->removeItem(devices->findText(gamepadIndexes[index].c_str())); devices.removeItem(gamepadIndexes[index]);
} }
break; break;
} }
} }
} }
/*
void InputSettings::PollGamepad() noexcept { void InputSettings::PollGamepad() noexcept {
if (!selectedDeviceIsNotKeyboard) if (!selectedDeviceIsNotKeyboard)
return; return;
@@ -141,16 +142,16 @@ void InputSettings::PollGamepad() noexcept {
{ {
const auto k = SDL_GetGamepadStringForButton(static_cast<SDL_GamepadButton>(e.gbutton.button)); const auto k = SDL_GetGamepadStringForButton(static_cast<SDL_GamepadButton>(e.gbutton.button));
JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k); JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k);
kbButtons[whichGrabbing]->setText(k); kbButtons[whichGrabbing].setText(k);
devices->setEnabled(true); devices.setEnabled(true);
for (const auto &kbButton : kbButtons) { for (const auto &kbButton : kbButtons) {
kbButton->setEnabled(true); kbButton.setEnabled(true);
} }
grabbing = false; grabbing = false;
whichGrabbing = -1; whichGrabbing = -1;
emit modified(); modified = true;
} }
break; break;
default: default:

View File

@@ -3,6 +3,7 @@
#include <unordered_map> #include <unordered_map>
#include <ImGuiImpl/PushButton.hpp> #include <ImGuiImpl/PushButton.hpp>
#include <ImGuiImpl/Combobox.hpp> #include <ImGuiImpl/Combobox.hpp>
#include <SDL3/SDL_keycode.h>
class InputSettings final { class InputSettings final {
bool grabbing = false; bool grabbing = false;
@@ -34,6 +35,7 @@ class InputSettings final {
}; };
gui::Combobox devices{"Device:", {}}; gui::Combobox devices{"Device:", {}};
bool modified = false;
//std::unique_ptr<QHBoxLayout> AB = std::make_unique<QHBoxLayout>(); //std::unique_ptr<QHBoxLayout> AB = std::make_unique<QHBoxLayout>();
//std::unique_ptr<QHBoxLayout> ZStart = std::make_unique<QHBoxLayout>(); //std::unique_ptr<QHBoxLayout> ZStart = std::make_unique<QHBoxLayout>();
@@ -56,6 +58,8 @@ public:
bool render(); bool render();
bool selectedDeviceIsNotKeyboard = false; bool selectedDeviceIsNotKeyboard = false;
explicit InputSettings(nlohmann::json &); explicit InputSettings(nlohmann::json &);
void setModified(bool v) { modified = v; }
bool getModified() { return modified; }
nlohmann::json &settings; nlohmann::json &settings;
std::array<SDL_Keycode, 18> GetMappedKeys(); std::array<SDL_Keycode, 18> GetMappedKeys();
}; };

View File

@@ -28,6 +28,18 @@ void KaizenGui::ConnectMainWindowSignalsToSlots() noexcept {
connect(mainWindow.get(), &MainWindow::Pause, emuThread.get(), &EmuThread::TogglePause); connect(mainWindow.get(), &MainWindow::Pause, emuThread.get(), &EmuThread::TogglePause);
} }
int KaizenGui::run() {
bool inputForEmu = true;
if(settingsWindow.render()) {
inputForEmu = false;
}
mainWindow.render();
return 0;
}
void KaizenGui::LoadROM(const QString &path) const noexcept { void KaizenGui::LoadROM(const QString &path) const noexcept {
mainWindow->actionPause->setEnabled(true); mainWindow->actionPause->setEnabled(true);
mainWindow->actionReset->setEnabled(true); mainWindow->actionReset->setEnabled(true);

View File

@@ -7,66 +7,70 @@ SettingsWindow::SettingsWindow() {
settings = JSONOpenOrCreate("resources/settings.json"); settings = JSONOpenOrCreate("resources/settings.json");
savePath = JSONGetField<std::string>(settings, "general", "savePath"); savePath = JSONGetField<std::string>(settings, "general", "savePath");
if (objectName().isEmpty())
setObjectName("Settings");
resize(500, 400);
setWindowTitle("Settings");
cpuSettings = std::make_unique<CPUSettings>(settings);
audioSettings = std::make_unique<AudioSettings>(settings);
inputSettings = std::make_unique<InputSettings>(settings);
generalSettings = std::make_unique<QWidget>();
keyMap = inputSettings.GetMappedKeys(); keyMap = inputSettings.GetMappedKeys();
savesFolder.setName(fmt::format(savesFolder.getName(), savePath)); savesFolder.setName(fmt::format(savesFolder.getName(), savePath));
connect(folderBtn.get(), &QPushButton::pressed, this, [&]() { tabs.addTab({"General", [](gui::TabItem* tab, void* userData) {
savePath = QFileDialog::getExistingDirectory(this, tr("Select directory")).toStdString(); SettingsWindow* sW = (SettingsWindow*)userData;
folderLabel->setText(fmt::format("{}", savePath).c_str()); if(sW->savesFolder.render()) {
JSONSetField(settings, "general", "savePath", savePath); // TODO: HANDLE FILE DIALOG savePath = QFileDialog::getExistingDirectory(this, tr("Select directory")).toStdString();
apply->setEnabled(true); sW->savesFolder.setName(fmt::format(sW->savesFolder.getName(), savePath));
}); JSONSetField(sW->settings, "general", "savePath", savePath);
sW->apply.setEnabled(true);
generalLayout->addWidget(folderLabelPrefix.get());
generalLayout->addWidget(folderLabel.get());
generalLayout->addStretch();
generalLayout->addWidget(folderBtn.get());
generalLayoutV->addLayout(generalLayout.get());
generalLayoutV->addStretch();
generalSettings->setLayout(generalLayoutV.get());
tabs->addTab(generalSettings.get(), tr("General"));
tabs->addTab(cpuSettings.get(), tr("CPU"));
tabs->addTab(audioSettings.get(), tr("Audio"));
tabs->addTab(inputSettings.get(), tr("Input"));
apply->setEnabled(false);
connect(cpuSettings.get(), &CPUSettings::modified, this, [&]() { apply->setEnabled(true); });
connect(audioSettings.get(), &AudioSettings::modified, this, [&]() { apply->setEnabled(true); });
connect(inputSettings.get(), &InputSettings::modified, this, [&]() { apply->setEnabled(true); });
connect(apply.get(), &QPushButton::pressed, this, [&]() {
auto newMap = inputSettings->GetMappedKeys();
if (!std::ranges::equal(keyMap, newMap)) {
keyMap = newMap;
emit regrabKeyboard();
} }
apply->setEnabled(false); }, this});
tabs.addTab({"CPU", [](gui::TabItem* tab, void* userData) {
SettingsWindow* sW = (SettingsWindow*)userData;
CPUSettings& cS = sW->cpuSettings;
gui::PushButton& apply = sW->apply;
if(cS.render()) {
if(cS.getModified())
sW->apply.setEnabled(true);
}
}, this});
tabs.addTab({"Audio", [](gui::TabItem* tab, void* userData) {
SettingsWindow* sW = (SettingsWindow*)userData;
AudioSettings& aS = sW->audioSettings;
gui::PushButton& apply = sW->apply;
if(aS.render()) {
if(aS.getModified())
apply.setEnabled(true);
}
}, this});
tabs.addTab({"Input", [](gui::TabItem* tab, void* userData) {
SettingsWindow* sW = (SettingsWindow*)userData;
sW->inputSettings.render();
InputSettings& iS = sW->inputSettings;
gui::PushButton& apply = sW->apply;
if(iS.render()) {
if(iS.getModified())
apply.setEnabled(true);
}
}, this});
apply.setEnabled(false);
}
bool SettingsWindow::render() {
tabs.render();
if(apply.render()) {
auto newMap = inputSettings.GetMappedKeys();
if (keyMap != newMap)
keyMap = newMap;
apply.setEnabled(false);
std::ofstream file("resources/settings.json"); std::ofstream file("resources/settings.json");
file << settings; file << settings;
file.close(); file.close();
}); }
connect(cancel.get(), &QPushButton::pressed, this, &QWidget::hide); return false;
}
buttonsLayout->addWidget(apply.get());
buttonsLayout->addWidget(cancel.get());
mainLayout->addWidget(tabs.get());
mainLayout->addLayout(buttonsLayout.get());
setLayout(mainLayout.get());
}

View File

@@ -2,10 +2,15 @@
#include <AudioSettings.hpp> #include <AudioSettings.hpp>
#include <CPUSettings.hpp> #include <CPUSettings.hpp>
#include <InputSettings.hpp> #include <InputSettings.hpp>
#include <ImGuiImpl/TabBar.hpp>
#include <SDL3/SDL_keycode.h>
#include <memory> #include <memory>
class SettingsWindow final { class SettingsWindow final {
gui::PushButton cancel{"Cancel"}, apply{"Apply", "", false}, savesFolder{"Open", "Save path: {}"}; gui::PushButton cancel{"Cancel"}, apply{"Apply", "", false}, savesFolder{"Open", "Save path: {}"};
CPUSettings cpuSettings{settings};
AudioSettings audioSettings{settings};
InputSettings inputSettings{settings};
//std::unique_ptr<QPushButton> cancel = std::make_unique<QPushButton>("Cancel"); //std::unique_ptr<QPushButton> cancel = std::make_unique<QPushButton>("Cancel");
//std::unique_ptr<QPushButton> apply = std::make_unique<QPushButton>("Apply"); //std::unique_ptr<QPushButton> apply = std::make_unique<QPushButton>("Apply");
//std::unique_ptr<QFileIconProvider> iconProv = std::make_unique<QFileIconProvider>(); //std::unique_ptr<QFileIconProvider> iconProv = std::make_unique<QFileIconProvider>();
@@ -18,15 +23,13 @@ class SettingsWindow final {
//std::unique_ptr<QVBoxLayout> mainLayout = std::make_unique<QVBoxLayout>(); //std::unique_ptr<QVBoxLayout> mainLayout = std::make_unique<QVBoxLayout>();
//std::unique_ptr<QHBoxLayout> buttonsLayout = std::make_unique<QHBoxLayout>(); //std::unique_ptr<QHBoxLayout> buttonsLayout = std::make_unique<QHBoxLayout>();
public: public:
bool render() {} // TODO bool render();
SettingsWindow(); SettingsWindow();
[[nodiscard]] float getVolumeL() const { return audioSettings.volumeL.getValue() / 100.f; } [[nodiscard]] float getVolumeL() const { return audioSettings.volumeL.getValue() / 100.f; }
[[nodiscard]] float getVolumeR() const { return audioSettings.volumeR.getValue() / 100.f; } [[nodiscard]] float getVolumeR() const { return audioSettings.volumeR.getValue() / 100.f; }
std::array<SDL_Keycode, 18> keyMap{}; std::array<SDL_Keycode, 18> keyMap{};
nlohmann::json settings; nlohmann::json settings;
CPUSettings cpuSettings{settings}; gui::TabBar tabs{"SettingsTabs"};
AudioSettings audioSettings{settings};
InputSettings inputSettings{settings};
//std::unique_ptr<QWidget> generalSettings{}; //std::unique_ptr<QWidget> generalSettings{};
}; };