remove stupid abstraction

This commit is contained in:
SimoZ64
2025-06-28 13:48:18 +02:00
parent 1be357e607
commit 4f618d35e0
20 changed files with 200 additions and 575 deletions

View File

@@ -67,6 +67,10 @@ struct RPC {
} }
FORCE_INLINE void Clear() { Discord_ClearPresence(); } FORCE_INLINE void Clear() { Discord_ClearPresence(); }
FORCE_INLINE void Shutdown() {
Clear();
Discord_Shutdown();
}
private: private:
DiscordEventHandlers handlers{}; DiscordEventHandlers handlers{};

View File

@@ -8,7 +8,6 @@ Core::Core() : cpu(std::make_unique<Interpreter>(parallel)) {}
void Core::Stop() { void Core::Stop() {
pause = true; pause = true;
romLoaded = false; romLoaded = false;
rom = {};
cpu->Reset(); cpu->Reset();
} }

View File

@@ -1,17 +1,18 @@
#include <AudioSettings.hpp> #include <AudioSettings.hpp>
#include <imgui.h>
AudioSettings::AudioSettings() { AudioSettings::AudioSettings() {
lockChannels.setChecked(Options::GetInstance().GetValue<bool>("audio", "lock")); lockChannels = Options::GetInstance().GetValue<bool>("audio", "lock");
volumeL.setValue(Options::GetInstance().GetValue<float>("audio", "volumeL") * 100); volumeL = Options::GetInstance().GetValue<float>("audio", "volumeL") * 100;
volumeR.setValue(Options::GetInstance().GetValue<float>("audio", "volumeR") * 100); volumeR = Options::GetInstance().GetValue<float>("audio", "volumeR") * 100;
} }
bool AudioSettings::render() { bool AudioSettings::render() {
if(lockChannels.render()) { if(ImGui::Checkbox("Lock channels:", &lockChannels)) {
auto isChecked = lockChannels.isChecked(); Options::GetInstance().SetValue("audio", "lock", lockChannels);
Options::GetInstance().SetValue("audio", "lock", isChecked); if(lockChannels) {
if (isChecked) { volumeR = volumeL;
volumeR.setValue(volumeL.getValue()); Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f);
} }
modified = true; modified = true;
@@ -19,12 +20,11 @@ bool AudioSettings::render() {
ImGui::SameLine(); ImGui::SameLine();
if(volumeL.render()) { if(ImGui::SliderFloat("Volume L", &volumeL, 0.f, 100.f, "%.2f")) {
float valueL = volumeL.getValue(); Options::GetInstance().SetValue("audio", "volumeL", volumeL / 100.f);
Options::GetInstance().SetValue("audio", "volumeL", float(valueL) / 100.f); if (lockChannels) {
if (lockChannels.isChecked()) { volumeR = volumeL;
volumeR.setValue(valueL); Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f);
Options::GetInstance().SetValue("audio", "volumeR", float(valueL) / 100.f);
} }
modified = true; modified = true;
@@ -32,13 +32,12 @@ bool AudioSettings::render() {
ImGui::SameLine(); ImGui::SameLine();
if(volumeR.render()) { ImGui::BeginDisabled(lockChannels);
if (!lockChannels.isChecked()) { if(ImGui::SliderFloat("Volume R", &volumeR, 0.f, 100.f, "%.2f")) {
Options::GetInstance().SetValue("audio", "volumeR", float(volumeR.getValue()) / 100.f); Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f);
}
modified = true; modified = true;
} }
ImGui::EndDisabled();
return modified; return modified;
} }

View File

@@ -1,14 +1,12 @@
#pragma once #pragma once
#include <Options.hpp> #include <Options.hpp>
#include <ImGuiImpl/Checkbox.hpp>
#include <ImGuiImpl/Slider.hpp>
class AudioSettings final { class AudioSettings final {
gui::Checkbox lockChannels{"Lock channels:", false}; bool lockChannels = false;
bool modified = false; bool modified = false;
public: public:
gui::SliderFloat volumeL{"Volume L", 0.f, 100.f, 0.f}; float volumeL{};
gui::SliderFloat volumeR{"Volume R", 0.f, 100.f, 0.f}; float volumeR{};
explicit AudioSettings(); explicit AudioSettings();
bool render(); bool render();
void setModified(bool v) { modified = v; } void setModified(bool v) { modified = v; }

View File

@@ -1,24 +1,50 @@
#include <CPUSettings.hpp> #include <CPUSettings.hpp>
#include <Options.hpp> #include <Options.hpp>
#include <log.hpp> #include <log.hpp>
#include <imgui.h>
CPUSettings::CPUSettings() { CPUSettings::CPUSettings() {
if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") { if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") {
cpuTypes.setCurrentIndex(1); selectedCpuTypeIndex = 1;
} else { } else {
cpuTypes.setCurrentIndex(0); selectedCpuTypeIndex = 0;
} }
} }
bool CPUSettings::render() { bool CPUSettings::render() {
if(cpuTypes.render()) { const char* items[] = {
if(cpuTypes.getCurrentIndex() == 0) { "Interpreter",
"Dynamic Recompiler"
};
const char* combo_preview_value = items[selectedCpuTypeIndex];
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
if(n == 1) // make JIT non-selectable but visible for now.
ImGui::BeginDisabled(true);
const bool is_selected = (selectedCpuTypeIndex == n);
if (ImGui::Selectable(items[n], is_selected)) {
selectedCpuTypeIndex = n;
modified = true;
}
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if (is_selected)
ImGui::SetItemDefaultFocus();
if(n == 1) // make JIT non-selectable but visible for now.
ImGui::EndDisabled();
}
ImGui::EndCombo();
}
if(modified) {
if(selectedCpuTypeIndex == 0) {
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter"); Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
} else { } else {
Util::panic("JIT should not be selectable??"); Util::panic("JIT should not be selectable??");
} }
modified = true;
} }
return modified; return modified;

View File

@@ -1,9 +1,8 @@
#pragma once #pragma once
#include <Options.hpp> #include <Options.hpp>
#include <ImGuiImpl/Combobox.hpp>
class CPUSettings final { class CPUSettings final {
gui::Combobox cpuTypes{"CPU type:", {{"Interpreter"}, {"Dynamic Recompiler", false}}}; int selectedCpuTypeIndex = 0;
bool modified = false; bool modified = false;
public: public:
bool render(); bool render();

View File

@@ -56,4 +56,5 @@ void EmuThread::Reset() const noexcept {
void EmuThread::Stop() const noexcept { void EmuThread::Stop() const noexcept {
Util::RPC::GetInstance().Update(Util::RPC::Idling); Util::RPC::GetInstance().Update(Util::RPC::Idling);
core->Stop(); core->Stop();
core->rom = {};
} }

View File

@@ -13,7 +13,9 @@ class EmuThread final {
bool started = false; bool started = false;
public: public:
explicit EmuThread(const std::shared_ptr<n64::Core> &, double &, RenderWidget &, SettingsWindow &) noexcept; explicit EmuThread(const std::shared_ptr<n64::Core> &, double &, RenderWidget &, SettingsWindow &) noexcept;
~EmuThread() {
Util::RPC::GetInstance().Shutdown();
}
void run() noexcept; void run() noexcept;
void TogglePause() const noexcept; void TogglePause() const noexcept;
void Reset() const noexcept; void Reset() const noexcept;

View File

@@ -1,16 +0,0 @@
#pragma once
#include <imgui.h>
#include <string>
namespace gui {
struct Checkbox {
Checkbox(const std::string& label, bool def = false) : val(def), label(label) {}
void setChecked(bool v) { val = v; }
bool render() { return ImGui::Checkbox(label.c_str(), &val); }
bool isChecked() { return val; }
private:
bool val = false;
std::string label = "";
};
}

View File

@@ -1,84 +0,0 @@
#pragma once
#include <imgui.h>
#include <string>
#include <ranges>
namespace gui {
struct ComboItem {
ComboItem(const std::string& label, bool enabled = true) : enabled(enabled), label(label) {}
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() const { return label; }
void setLabel(const std::string& text) { label = text; }
private:
bool enabled = true;
std::string label;
};
struct Combobox {
Combobox(const std::string& label, const std::vector<ComboItem>& items, const std::string& preview = "", bool enabled = true) : label(label), items(items), preview(preview), enabled(enabled) {}
void addItem(const ComboItem& item) {
if(std::find_if(items.begin(), items.end(),
[&item](const 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 = "";
if(preview != "") {
_preview = preview.c_str();
} else {
_preview = items[current_index].getLabel().c_str();
}
bool changed = false;
ImGui::BeginDisabled(!enabled);
if (ImGui::BeginCombo(label.c_str(), _preview)) {
for (int n = 0; n < items.size(); n++) {
const bool is_selected = ((current_index) == n);
if (items[n].render(is_selected))
{
current_index = n;
changed = true;
}
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::EndDisabled();
return changed;
}
void setEnabled(bool v) { enabled = v; }
bool isEnabled() { return enabled; }
const std::string& getCurrentlySelected() { return items[current_index].getLabel(); }
const int& getCurrentlySelectedIndex() { return current_index; }
void setCurrentIndex(int v) { current_index = v; }
int getCurrentIndex() { return current_index; }
private:
bool enabled = true;
std::vector<ComboItem> items;
std::string label, preview;
int current_index = 0;
};
}

View File

@@ -1,79 +0,0 @@
#pragma once
#include <imgui.h>
#include <string>
#include <ranges>
#include <functional>
namespace gui {
struct MenuItem {
MenuItem(const std::string& label, std::function<void()>&& func = nullptr, bool enabled = true) : label(label), exec(std::move(func)), enabled(enabled) {}
bool render() {
bool ret = false;
ImGui::BeginDisabled(!enabled);
if(ImGui::MenuItem(label.c_str())) {
if(exec)
exec();
ret = true;
}
ImGui::EndDisabled();
return ret;
}
void setLabel(const std::string& label) { this->label = label; }
const std::string& getLabel() { return label; }
void setFunc(std::function<void()>&& func) { exec = func; }
void setEnabled(bool v) { enabled = v; }
private:
bool enabled = true;
std::string label;
std::function<void()> exec;
};
struct Menu {
Menu(const std::string& label, const std::vector<MenuItem>& items = {}, bool enabled = true) : label(label), items(items), enabled(enabled) {}
void addMenuItem(const MenuItem& item) { items.push_back(item); }
bool render() {
bool ret = false;
ImGui::BeginDisabled(!enabled);
if(ImGui::BeginMenu(label.c_str())) {
for(auto& item : items) {
ret |= item.render();
}
ImGui::EndMenu();
}
ImGui::EndDisabled();
return ret;
}
void setEnabled(bool v) { enabled = v; }
private:
std::vector<MenuItem> items{};
std::string label{};
bool enabled = true;
};
template <bool main = false>
struct MenuBar {
MenuBar(bool enabled = true) : enabled(enabled) {}
void addMenu(const Menu& menu) { menus.push_back(menu); }
bool render() {
bool ret = false;
ImGui::BeginDisabled(!enabled);
auto beginMenuBar = main ? &ImGui::BeginMainMenuBar : &ImGui::BeginMenuBar;
auto endMenuBar = main ? &ImGui::EndMainMenuBar : &ImGui::EndMenuBar;
if(beginMenuBar()) {
for(auto& menu : menus) {
ret |= menu.render();
}
endMenuBar();
}
ImGui::EndDisabled();
return ret;
}
void setEnabled(bool v) { enabled = v; }
private:
bool enabled = true;
std::vector<Menu> menus{};
};
}

View File

@@ -1,53 +0,0 @@
#pragma once
#include <imgui.h>
#include <string>
#include <functional>
#include <utils/log.hpp>
namespace gui {
struct PopupWindow {
PopupWindow(const std::string& title, std::function<void()>&& func = nullptr, bool opened = false) : title(title), exec(func), opened(opened) {}
void setFunc(std::function<void()>&& func) { exec = func; }
void setOpened(bool v) { opened = v; }
void setOnClose(std::function<void()>&& func) { onClose = func; }
void setOnOpen(std::function<void()>&& func) { onOpen = func; }
bool render() {
if(!opened) {
if(onClose)
onClose();
return false;
}
if (!ImGui::IsPopupOpen(title.c_str()))
{
if(onOpen)
onOpen();
ImGui::OpenPopup(title.c_str());
}
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal(title.c_str(), &opened, ImGuiWindowFlags_AlwaysAutoResize))
{
if(exec)
exec();
ImGui::EndPopup();
return true;
}
return false;
}
private:
std::function<void()> exec;
std::function<void()> onClose;
std::function<void()> onOpen;
std::string title;
bool opened = false;
};
}

View File

@@ -1,34 +0,0 @@
#pragma once
#include <imgui.h>
#include <string>
namespace gui {
struct PushButton {
PushButton(const std::string& label, const std::string& name = "", bool enabled = true) : label(label), name(name), enabled(enabled) {}
bool render() {
if(name != "") {
ImGui::Text("%s", name.c_str());
ImGui::SameLine();
}
ImGui::BeginDisabled(!enabled);
bool ret = ImGui::Button(label.c_str());
ImGui::EndDisabled();
return ret;
}
void setEnabled(bool v) { enabled = v; }
bool getEnabled() { return enabled; }
const std::string& getName() { return name; }
void setName(const std::string& v) { name = v; }
const std::string& getLabel() { return label; }
void setLabel(const std::string& v) { label = v; }
private:
bool enabled = true;
std::string name;
std::string label;
};
}

View File

@@ -1,53 +0,0 @@
#pragma once
#include <imgui.h>
#include <string>
namespace gui {
template <typename T>
struct Slider {
Slider(const std::string& label, T min = 0, T max = 0, T initial_value = 0)
: val(initial_value), label(label), min(min), max(max) { }
void setValue(T v) { val = v; }
bool render() {
return ImGui::SliderScalarN(label.c_str(), type_to_imgui(), (void*)&val, 1, (void*)&min, (void*)&max);
}
T getValue() const { return val; }
private:
constexpr ImGuiDataType_ type_to_imgui() {
if constexpr(std::is_same_v<T, char>) {
return ImGuiDataType_S8;
} else if constexpr(std::is_same_v<T, unsigned char>) {
return ImGuiDataType_U8;
} else if constexpr(std::is_same_v<T, short>) {
return ImGuiDataType_S16;
} else if constexpr(std::is_same_v<T, unsigned short>) {
return ImGuiDataType_U16;
} else if constexpr(std::is_same_v<T, int>) {
return ImGuiDataType_S32;
} else if constexpr(std::is_same_v<T, unsigned int>) {
return ImGuiDataType_U32;
} else if constexpr(std::is_same_v<T, long long>) {
return ImGuiDataType_S64;
} else if constexpr(std::is_same_v<T, unsigned long long>) {
return ImGuiDataType_U64;
} else if constexpr(std::is_same_v<T, float>) {
return ImGuiDataType_Float;
} else if constexpr(std::is_same_v<T, double>) {
return ImGuiDataType_Double;
} else if constexpr(std::is_same_v<T, bool>) {
return ImGuiDataType_Bool;
} else if constexpr(std::is_same_v<T, char*>) {
return ImGuiDataType_String;
} else {
return ImGuiDataType_COUNT;
}
}
T val{}, min{}, max{};
std::string label = "";
};
using SliderFloat = Slider<float>;
using SliderInt = Slider<int>;
}

View File

@@ -41,25 +41,3 @@ inline void EndMainStatusBar()
End(); End();
} }
} }
namespace gui {
struct StatusBar {
StatusBar(std::function<void()>&& func = nullptr, bool enabled = true) : exec(func), enabled(enabled) {}
void setFunc(std::function<void()>&& func) { exec = func; }
bool render() {
if(ImGui::BeginMainStatusBar()) {
if(exec)
exec();
ImGui::EndMainStatusBar();
return true;
}
return false;
}
private:
std::function<void()> exec = nullptr;
bool enabled = true;
};
}

View File

@@ -1,52 +0,0 @@
#pragma once
#include <imgui.h>
#include <string>
#include <vector>
#include <functional>
namespace gui {
struct TabItem {
TabItem(const std::string& label, std::function<void()>&& func, bool enabled = true) : exec(std::move(func)), enabled(enabled), label(label) {}
bool render() {
bool ret = false;
ImGui::BeginDisabled(!enabled);
if(ImGui::BeginTabItem(label.c_str())) {
if(exec)
exec();
ImGui::EndTabItem();
ret = true;
}
ImGui::EndDisabled();
return ret;
}
private:
bool enabled = true;
std::string label;
std::function<void()> exec;
};
struct TabBar {
TabBar(const std::string& label, const std::vector<TabItem>& items = {}) : label(label), tabs(items) {}
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

@@ -1,8 +1,8 @@
#include <KaizenGui.hpp> #include <KaizenGui.hpp>
#include <nfd.hpp> #include <nfd.hpp>
#include <backend/Core.hpp> #include <backend/Core.hpp>
#include <ImGuiImpl/StatusBar.hpp>
#include <ImGuiImpl/GUI.hpp> #include <ImGuiImpl/GUI.hpp>
#include <ImGuiImpl/StatusBar.hpp>
#include <resources/gamecontrollerdb.h> #include <resources/gamecontrollerdb.h>
KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), core(std::make_shared<n64::Core>()), vulkanWidget(core, window.getHandle()), emuThread(core, fpsCounter, vulkanWidget, settingsWindow) { KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), core(std::make_shared<n64::Core>()), vulkanWidget(core, window.getHandle()), emuThread(core, fpsCounter, vulkanWidget, settingsWindow) {
@@ -10,82 +10,6 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), core(std::make_sha
SDL_InitSubSystem(SDL_INIT_GAMEPAD); SDL_InitSubSystem(SDL_INIT_GAMEPAD);
SDL_AddGamepadMapping(gamecontrollerdb_str); SDL_AddGamepadMapping(gamecontrollerdb_str);
actionPause.setFunc([&]() {
if(ImGui::IsItemClicked()) {
actionPause.setLabel(actionPause.getLabel() == "Pause" ? "Resume" : "Pause");
core->TogglePause();
}
});
actionStop.setFunc([&]() {
if(ImGui::IsItemClicked()) {
actionStop.setEnabled(false);
actionPause.setEnabled(false);
actionReset.setEnabled(false);
core->Stop();
}
});
about.setFunc([&]() {
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
ImGui::Text("to offer a friendly user experience and compatibility.");
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
if(ImGui::Button("OK")) {
about.setOpened(false);
ImGui::CloseCurrentPopup();
}
});
statusBar.setFunc([&]() {
/* TODO: for when I'll separate the GUI and the Core in two threads again
ImGui::Text("GUI FPS: %.2f, Emulation FPS: %s", ImGui::GetIO().Framerate,
fmt::format(fpsCounter > 0 ? fmt::runtime("{0:.2f}") : fmt::runtime("{1}"), fpsCounter, "Not playing").c_str());
*/
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
});
menuBar.addMenu({"File",
{
{"Open", [&]() {
NFD::Guard guard;
NFD::UniquePath path;
static const std::vector<nfdfilteritem_t> filterItems = {
{"Nintendo 64 rom archive", "rar,RAR,tar,TAR,zip,ZIP,7z,7Z"},
{"Nintendo 64 rom", "n64,z64,v64,N64,Z64,V64"}
};
auto result = NFD::OpenDialog(path, filterItems.data(), filterItems.size());
if(result == NFD_CANCEL) return;
if(result == NFD_ERROR)
Util::panic("Error: {}", NFD::GetError());
LoadROM(path.get());
}},
{"Exit", [&]() {
quit = true;
emuThread.Stop();
}}
}
});
menuBar.addMenu({
"Emulation", {
actionPause,
actionStop,
actionReset,
{"Settings", [&]() {
settingsWindow.popup.setOpened(true);
}},
}});
menuBar.addMenu({
"Help", {
{"About", [&]() {
about.setOpened(true);
}},
}});
} }
KaizenGui::~KaizenGui() { KaizenGui::~KaizenGui() {
@@ -190,10 +114,85 @@ void KaizenGui::HandleInput(SDL_Event event) {
void KaizenGui::RenderUI() { void KaizenGui::RenderUI() {
gui::StartFrame(); gui::StartFrame();
statusBar.render();
menuBar.render(); static bool actionsEnabled = false;
if(ImGui::BeginMainMenuBar()) {
if(ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Open")) {
NFD::Guard guard;
NFD::UniquePath path;
static const std::vector<nfdfilteritem_t> filterItems = {
{"Nintendo 64 rom archive", "rar,RAR,tar,TAR,zip,ZIP,7z,7Z"},
{"Nintendo 64 rom", "n64,z64,v64,N64,Z64,V64"}
};
auto result = NFD::OpenDialog(path, filterItems.data(), filterItems.size());
if(result == NFD_ERROR)
Util::panic("Error: {}", NFD::GetError());
if(result != NFD_CANCEL) {
LoadROM(path.get());
actionsEnabled = true;
}
}
if(ImGui::MenuItem("Exit")) {
quit = true;
emuThread.Stop();
}
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Emulation")) {
ImGui::BeginDisabled(!actionsEnabled);
if(ImGui::MenuItem(core->pause ? "Resume" : "Pause")) {
emuThread.TogglePause();
}
if(ImGui::MenuItem("Reset")) {
emuThread.Reset();
}
if(ImGui::MenuItem("Stop")) {
emuThread.Stop();
actionsEnabled = false;
}
ImGui::EndDisabled();
if(ImGui::MenuItem("Settings")) {
settingsWindow.render(); settingsWindow.render();
about.render(); }
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Help")) {
if(ImGui::MenuItem("About")) {
ImGui::OpenPopup("About Kaizen");
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("About Kaizen", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
ImGui::Text("to offer a friendly user experience and compatibility.");
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
if(ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
if(ImGui::BeginMainStatusBar()) {
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::EndMainStatusBar();
}
ImGui::Render(); ImGui::Render();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable){ if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable){
ImGui::UpdatePlatformWindows(); ImGui::UpdatePlatformWindows();
@@ -209,9 +208,6 @@ void KaizenGui::RenderUI() {
} }
void KaizenGui::LoadROM(const std::string &path) noexcept { void KaizenGui::LoadROM(const std::string &path) noexcept {
actionPause.setEnabled(true);
actionReset.setEnabled(true);
actionStop.setEnabled(true);
emuThread.core->LoadROM(path); emuThread.core->LoadROM(path);
const auto gameNameDB = emuThread.core->cpu->GetMem().rom.gameNameDB; const auto gameNameDB = emuThread.core->cpu->GetMem().rom.gameNameDB;
Util::RPC::GetInstance().Update(Util::RPC::Playing, gameNameDB); Util::RPC::GetInstance().Update(Util::RPC::Playing, gameNameDB);

View File

@@ -2,8 +2,6 @@
#include <RenderWidget.hpp> #include <RenderWidget.hpp>
#include <NativeWindow.hpp> #include <NativeWindow.hpp>
#include <Debugger.hpp> #include <Debugger.hpp>
#include <ImGuiImpl/Menu.hpp>
#include <ImGuiImpl/StatusBar.hpp>
#include <EmuThread.hpp> #include <EmuThread.hpp>
#include <Discord.hpp> #include <Discord.hpp>
#include <SDL3/SDL_gamepad.h> #include <SDL3/SDL_gamepad.h>
@@ -16,11 +14,6 @@ public:
double fpsCounter = -1.0; double fpsCounter = -1.0;
gui::MenuBar<true> menuBar;
gui::MenuItem actionPause{"Pause", nullptr, false}, actionStop{"Stop", nullptr, false}, actionReset{"Reset", nullptr, false};
gui::PopupWindow about{"About Kaizen"};
gui::StatusBar statusBar{};
std::shared_ptr<n64::Core> core; std::shared_ptr<n64::Core> core;
RenderWidget vulkanWidget; RenderWidget vulkanWidget;
SettingsWindow settingsWindow; SettingsWindow settingsWindow;

View File

@@ -3,66 +3,73 @@
#include <nfd.hpp> #include <nfd.hpp>
#include <log.hpp> #include <log.hpp>
#include <Options.hpp> #include <Options.hpp>
#include <imgui.h>
std::string savePath; std::string savePath;
SettingsWindow::SettingsWindow() { bool SettingsWindow::render() {
savesFolder.setName(fmt::format("Save path: {}", static bool applyEnabled = false;
Options::GetInstance().GetValue<std::string>("general", "savePath"))); ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
tabs.addTab({"General", [&]() { if(ImGui::BeginPopupModal("Settings")) {
if(savesFolder.render()) { if(ImGui::BeginTabBar("SettingsTabBar")) {
if(ImGui::BeginTabItem("General")) {
ImGui::BeginDisabled();
ImGui::InputText("Save Path", (char*)savesPath.c_str(), savesPath.length());
ImGui::EndDisabled();
ImGui::SameLine();
if(ImGui::Button("Pick...")) {
NFD::Guard guard; NFD::Guard guard;
NFD::UniquePath outPath; NFD::UniquePath outPath;
auto result = NFD::PickFolder(outPath); auto result = NFD::PickFolder(outPath);
if(result == NFD_CANCEL)
return;
if(result == NFD_ERROR) if(result == NFD_ERROR)
Util::panic("Error: {}", NFD::GetError()); Util::panic("Error: {}", NFD::GetError());
savesFolder.setName(fmt::format("Save path: {}", outPath.get())); if(result != NFD_CANCEL) {
Options::GetInstance().SetValue<std::string>("general", "savePath", outPath.get()); savesPath = outPath.get();
apply.setEnabled(true); Options::GetInstance().SetValue<std::string>("general", "savePath", savesPath);
applyEnabled = true;
}
}
ImGui::EndTabItem();
} }
}});
tabs.addTab({"CPU", [&]() { if(ImGui::BeginTabItem("Core")) {
if(cpuSettings.render()) { if(cpuSettings.render()) {
if(cpuSettings.getModified()) if(cpuSettings.getModified())
apply.setEnabled(true); applyEnabled = true;
}
ImGui::EndTabItem();
} }
}});
tabs.addTab({"Audio", [&]() { if(ImGui::BeginTabItem("Audio")) {
if(audioSettings.render()) { if(audioSettings.render()) {
if(audioSettings.getModified()) if(audioSettings.getModified())
apply.setEnabled(true); applyEnabled = true;
}
ImGui::EndTabItem();
} }
}});
apply.setEnabled(false); ImGui::EndTabBar();
}
popup.setFunc([&]() { ImGui::BeginDisabled(!applyEnabled);
tabs.render(); if(ImGui::Button("Apply")) {
applyEnabled = false;
if(apply.render()) {
apply.setEnabled(false);
Options::GetInstance().Apply(); Options::GetInstance().Apply();
} }
ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Cancel")) { if(ImGui::Button("Cancel")) {
popup.setOpened(false);
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
}); ImGui::EndPopup();
} }
bool SettingsWindow::render() {
if(popup.render())
return true; return true;
return false;
} }

View File

@@ -1,22 +1,16 @@
#pragma once #pragma once
#include <AudioSettings.hpp> #include <AudioSettings.hpp>
#include <CPUSettings.hpp> #include <CPUSettings.hpp>
#include <ImGuiImpl/TabBar.hpp>
#include <ImGuiImpl/PopupWindow.hpp>
#include <ImGuiImpl/PushButton.hpp>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <memory> #include <memory>
class SettingsWindow final { class SettingsWindow final {
gui::PushButton apply{"Apply", "", false}, savesFolder{"Open", "Save path: {}"};
CPUSettings cpuSettings; CPUSettings cpuSettings;
AudioSettings audioSettings; AudioSettings audioSettings;
std::string savesPath;
public: public:
gui::PopupWindow popup{"Emulator Settings"};
bool render(); bool render();
SettingsWindow(); SettingsWindow() = default;
[[nodiscard]] float getVolumeL() const { return audioSettings.volumeL.getValue() / 100.f; } [[nodiscard]] float getVolumeL() const { return audioSettings.volumeL / 100.f; }
[[nodiscard]] float getVolumeR() const { return audioSettings.volumeR.getValue() / 100.f; } [[nodiscard]] float getVolumeR() const { return audioSettings.volumeR / 100.f; }
gui::TabBar tabs{"SettingsTabs"};
}; };