more wrappers and helpers!
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(kaizen)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (WIN32)
|
||||
add_compile_definitions(NOMINMAX)
|
||||
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_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(REMOVE
|
||||
|
||||
@@ -15,7 +15,7 @@ bool CPUSettings::render() {
|
||||
if(cpuTypes.getCurrentIndex() == 0) {
|
||||
JSONSetField(settings, "cpu", "type", "interpreter");
|
||||
} else {
|
||||
Util::panic("JIT is unfinished and currently not supported!");
|
||||
Util::panic("JIT should not be selectable??");
|
||||
}
|
||||
|
||||
modified = true;
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include <ImGuiImpl/Combobox.hpp>
|
||||
|
||||
class CPUSettings final {
|
||||
gui::Combobox cpuTypes{"CPU type:", {"Interpreter" //, "Dynamic Recompiler"
|
||||
}};
|
||||
gui::Combobox cpuTypes{"CPU type:", {{"Interpreter"}, {"Dynamic Recompiler", false}}};
|
||||
bool modified = false;
|
||||
public:
|
||||
bool render();
|
||||
explicit CPUSettings(nlohmann::json &);
|
||||
void setModified(bool v) { modified = v; }
|
||||
bool getModified() { return modified; }
|
||||
nlohmann::json &settings;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,40 @@
|
||||
#pragma once
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ranges>
|
||||
|
||||
namespace gui {
|
||||
struct Combobox {
|
||||
Combobox(std::string label, std::vector<std::string> items, std::string preview = "", bool enabled = true) : label(label), items(items), preview(preview), enabled(enabled) {}
|
||||
struct ComboItem {
|
||||
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() {
|
||||
const char* _preview = "";
|
||||
@@ -15,7 +42,7 @@ struct Combobox {
|
||||
if(preview != "") {
|
||||
_preview = preview.c_str();
|
||||
} else {
|
||||
_preview = items[current_index].c_str();
|
||||
_preview = items[current_index].getLabel().c_str();
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
@@ -24,7 +51,7 @@ struct Combobox {
|
||||
if (ImGui::BeginCombo(label.c_str(), _preview)) {
|
||||
for (int n = 0; n < items.size(); 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;
|
||||
changed = true;
|
||||
@@ -41,12 +68,13 @@ struct Combobox {
|
||||
}
|
||||
|
||||
void setEnabled(bool v) { enabled = v; }
|
||||
bool isEnabled() { return enabled; }
|
||||
|
||||
void setCurrentIndex(int v) { current_index = v; }
|
||||
int getCurrentIndex() { return current_index; }
|
||||
private:
|
||||
bool enabled = true;
|
||||
std::vector<std::string>& items;
|
||||
std::vector<ComboItem> items;
|
||||
std::string label, preview;
|
||||
int current_index = 0;
|
||||
};
|
||||
|
||||
@@ -21,10 +21,10 @@ struct PushButton {
|
||||
void setEnabled(bool v) { enabled = v; }
|
||||
bool getEnabled() { return enabled; }
|
||||
|
||||
std::string& getName() const { return name; }
|
||||
const std::string& getName() { return name; }
|
||||
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; }
|
||||
private:
|
||||
bool enabled = true;
|
||||
|
||||
54
src/frontend/ImGuiImpl/TabBar.hpp
Normal file
54
src/frontend/ImGuiImpl/TabBar.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -2,13 +2,14 @@
|
||||
#include <log.hpp>
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_keycode.h>
|
||||
|
||||
InputSettings::InputSettings(nlohmann::json &settings) : settings(settings) {
|
||||
for(auto& kb : kbButtons) {
|
||||
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
|
||||
connect(devices.get(), &QComboBox::currentTextChanged, this, [&](const QString &text) {
|
||||
@@ -51,16 +52,16 @@ void InputSettings::keyPressEvent(QKeyEvent *e) {
|
||||
if (grabbing) {
|
||||
const auto k = QKeySequence(e->key()).toString().toStdString();
|
||||
JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k);
|
||||
kbButtons[whichGrabbing]->setText(k.c_str());
|
||||
devices->setEnabled(true);
|
||||
kbButtons[whichGrabbing].setText(k.c_str());
|
||||
devices.setEnabled(true);
|
||||
for (const auto &kbButton : kbButtons) {
|
||||
kbButton->setEnabled(true);
|
||||
kbButton.setEnabled(true);
|
||||
}
|
||||
|
||||
grabbing = false;
|
||||
whichGrabbing = -1;
|
||||
|
||||
if (devices->currentText() == "Keyboard/Mouse") {
|
||||
if (devices.currentText() == "Keyboard/Mouse") {
|
||||
releaseKeyboard();
|
||||
emit modified();
|
||||
}
|
||||
@@ -73,16 +74,15 @@ std::array<SDL_Keycode, 18> InputSettings::GetMappedKeys() {
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (const auto& kb : kbButtons) {
|
||||
for (auto& kb : kbButtons) {
|
||||
ret[i++] = SDL_GetKeyFromName(kb.getLabel().c_str());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*TODO: RECREATE THIS IN SDL
|
||||
void InputSettings::QueryDevices() noexcept {
|
||||
if (!devices->isEnabled())
|
||||
if (!devices.isEnabled())
|
||||
return;
|
||||
|
||||
SDL_Event e;
|
||||
@@ -102,17 +102,17 @@ void InputSettings::QueryDevices() noexcept {
|
||||
if (!gamepadIndexes.contains(index)) {
|
||||
gamepadIndexes[index] = name;
|
||||
}
|
||||
devices->addItem(name);
|
||||
devices.addItem({name});
|
||||
} else if (serial) {
|
||||
if (!gamepadIndexes.contains(index)) {
|
||||
gamepadIndexes[index] = serial;
|
||||
}
|
||||
devices->addItem(serial);
|
||||
devices.addItem({serial});
|
||||
} else if (path) {
|
||||
if (!gamepadIndexes.contains(index)) {
|
||||
gamepadIndexes[index] = path;
|
||||
}
|
||||
devices->addItem(path);
|
||||
devices.addItem({path});
|
||||
}
|
||||
|
||||
SDL_CloseGamepad(gamepad);
|
||||
@@ -123,13 +123,14 @@ void InputSettings::QueryDevices() noexcept {
|
||||
const auto index = e.gdevice.which;
|
||||
|
||||
if (gamepadIndexes.contains(index))
|
||||
devices->removeItem(devices->findText(gamepadIndexes[index].c_str()));
|
||||
devices.removeItem(gamepadIndexes[index]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void InputSettings::PollGamepad() noexcept {
|
||||
if (!selectedDeviceIsNotKeyboard)
|
||||
return;
|
||||
@@ -141,16 +142,16 @@ void InputSettings::PollGamepad() noexcept {
|
||||
{
|
||||
const auto k = SDL_GetGamepadStringForButton(static_cast<SDL_GamepadButton>(e.gbutton.button));
|
||||
JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k);
|
||||
kbButtons[whichGrabbing]->setText(k);
|
||||
devices->setEnabled(true);
|
||||
kbButtons[whichGrabbing].setText(k);
|
||||
devices.setEnabled(true);
|
||||
for (const auto &kbButton : kbButtons) {
|
||||
kbButton->setEnabled(true);
|
||||
kbButton.setEnabled(true);
|
||||
}
|
||||
|
||||
grabbing = false;
|
||||
whichGrabbing = -1;
|
||||
|
||||
emit modified();
|
||||
modified = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <unordered_map>
|
||||
#include <ImGuiImpl/PushButton.hpp>
|
||||
#include <ImGuiImpl/Combobox.hpp>
|
||||
#include <SDL3/SDL_keycode.h>
|
||||
|
||||
class InputSettings final {
|
||||
bool grabbing = false;
|
||||
@@ -34,6 +35,7 @@ class InputSettings final {
|
||||
};
|
||||
|
||||
gui::Combobox devices{"Device:", {}};
|
||||
bool modified = false;
|
||||
|
||||
//std::unique_ptr<QHBoxLayout> AB = std::make_unique<QHBoxLayout>();
|
||||
//std::unique_ptr<QHBoxLayout> ZStart = std::make_unique<QHBoxLayout>();
|
||||
@@ -56,6 +58,8 @@ public:
|
||||
bool render();
|
||||
bool selectedDeviceIsNotKeyboard = false;
|
||||
explicit InputSettings(nlohmann::json &);
|
||||
void setModified(bool v) { modified = v; }
|
||||
bool getModified() { return modified; }
|
||||
nlohmann::json &settings;
|
||||
std::array<SDL_Keycode, 18> GetMappedKeys();
|
||||
};
|
||||
|
||||
@@ -28,6 +28,18 @@ void KaizenGui::ConnectMainWindowSignalsToSlots() noexcept {
|
||||
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 {
|
||||
mainWindow->actionPause->setEnabled(true);
|
||||
mainWindow->actionReset->setEnabled(true);
|
||||
|
||||
@@ -7,66 +7,70 @@ SettingsWindow::SettingsWindow() {
|
||||
settings = JSONOpenOrCreate("resources/settings.json");
|
||||
|
||||
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();
|
||||
|
||||
savesFolder.setName(fmt::format(savesFolder.getName(), savePath));
|
||||
|
||||
connect(folderBtn.get(), &QPushButton::pressed, this, [&]() {
|
||||
savePath = QFileDialog::getExistingDirectory(this, tr("Select directory")).toStdString();
|
||||
folderLabel->setText(fmt::format("{}", savePath).c_str());
|
||||
JSONSetField(settings, "general", "savePath", savePath);
|
||||
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();
|
||||
tabs.addTab({"General", [](gui::TabItem* tab, void* userData) {
|
||||
SettingsWindow* sW = (SettingsWindow*)userData;
|
||||
if(sW->savesFolder.render()) {
|
||||
// TODO: HANDLE FILE DIALOG savePath = QFileDialog::getExistingDirectory(this, tr("Select directory")).toStdString();
|
||||
sW->savesFolder.setName(fmt::format(sW->savesFolder.getName(), savePath));
|
||||
JSONSetField(sW->settings, "general", "savePath", savePath);
|
||||
sW->apply.setEnabled(true);
|
||||
}
|
||||
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");
|
||||
file << settings;
|
||||
file.close();
|
||||
});
|
||||
}
|
||||
|
||||
connect(cancel.get(), &QPushButton::pressed, this, &QWidget::hide);
|
||||
|
||||
buttonsLayout->addWidget(apply.get());
|
||||
buttonsLayout->addWidget(cancel.get());
|
||||
mainLayout->addWidget(tabs.get());
|
||||
mainLayout->addLayout(buttonsLayout.get());
|
||||
setLayout(mainLayout.get());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2,10 +2,15 @@
|
||||
#include <AudioSettings.hpp>
|
||||
#include <CPUSettings.hpp>
|
||||
#include <InputSettings.hpp>
|
||||
#include <ImGuiImpl/TabBar.hpp>
|
||||
#include <SDL3/SDL_keycode.h>
|
||||
#include <memory>
|
||||
|
||||
class SettingsWindow final {
|
||||
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> apply = std::make_unique<QPushButton>("Apply");
|
||||
//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<QHBoxLayout> buttonsLayout = std::make_unique<QHBoxLayout>();
|
||||
public:
|
||||
bool render() {} // TODO
|
||||
bool render();
|
||||
SettingsWindow();
|
||||
[[nodiscard]] float getVolumeL() const { return audioSettings.volumeL.getValue() / 100.f; }
|
||||
[[nodiscard]] float getVolumeR() const { return audioSettings.volumeR.getValue() / 100.f; }
|
||||
|
||||
std::array<SDL_Keycode, 18> keyMap{};
|
||||
nlohmann::json settings;
|
||||
CPUSettings cpuSettings{settings};
|
||||
AudioSettings audioSettings{settings};
|
||||
InputSettings inputSettings{settings};
|
||||
gui::TabBar tabs{"SettingsTabs"};
|
||||
//std::unique_ptr<QWidget> generalSettings{};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user