remove stupid abstraction
This commit is contained in:
@@ -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{};
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 = "";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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>;
|
|
||||||
}
|
|
||||||
@@ -40,26 +40,4 @@ 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;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
settingsWindow.render();
|
|
||||||
about.render();
|
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();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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")) {
|
||||||
NFD::Guard guard;
|
if(ImGui::BeginTabItem("General")) {
|
||||||
NFD::UniquePath outPath;
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::InputText("Save Path", (char*)savesPath.c_str(), savesPath.length());
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Button("Pick...")) {
|
||||||
|
NFD::Guard guard;
|
||||||
|
NFD::UniquePath outPath;
|
||||||
|
|
||||||
auto result = NFD::PickFolder(outPath);
|
auto result = NFD::PickFolder(outPath);
|
||||||
if(result == NFD_CANCEL)
|
if(result == NFD_ERROR)
|
||||||
return;
|
Util::panic("Error: {}", NFD::GetError());
|
||||||
if(result == NFD_ERROR)
|
|
||||||
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabItem("Core")) {
|
||||||
|
if(cpuSettings.render()) {
|
||||||
|
if(cpuSettings.getModified())
|
||||||
|
applyEnabled = true;
|
||||||
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabItem("Audio")) {
|
||||||
|
if(audioSettings.render()) {
|
||||||
|
if(audioSettings.getModified())
|
||||||
|
applyEnabled = true;
|
||||||
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabBar();
|
||||||
}
|
}
|
||||||
}});
|
|
||||||
|
|
||||||
tabs.addTab({"CPU", [&]() {
|
ImGui::BeginDisabled(!applyEnabled);
|
||||||
if(cpuSettings.render()) {
|
if(ImGui::Button("Apply")) {
|
||||||
if(cpuSettings.getModified())
|
applyEnabled = false;
|
||||||
apply.setEnabled(true);
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
|
|
||||||
tabs.addTab({"Audio", [&]() {
|
|
||||||
if(audioSettings.render()) {
|
|
||||||
if(audioSettings.getModified())
|
|
||||||
apply.setEnabled(true);
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
|
|
||||||
apply.setEnabled(false);
|
|
||||||
|
|
||||||
popup.setFunc([&]() {
|
|
||||||
tabs.render();
|
|
||||||
|
|
||||||
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() {
|
return true;
|
||||||
if(popup.render())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
@@ -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"};
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user