Files
Horizon-OC/Source/Horizon-OC-Monitor/source/modes/Configurator.hpp
2026-03-13 19:46:47 +01:00

2241 lines
89 KiB
C++

/*
* Mode-Specific Configuration Settings
*
* Based on actual settings structures, each mode only shows applicable settings:
*
* Mini Mode: Refresh Rate, Colors (background, focus_background, separator, category, text),
* Toggles, Font Sizes, Elements, DTC Format
*
* Micro Mode: Refresh Rate, Colors (background, separator, category, text), Toggles,
* Font Sizes, Elements, Text Alignment, Vertical Position (Top/Bottom only), DTC Format
*
* Full Mode: Refresh Rate, Toggles (show_real_freqs, show_deltas, etc.),
* Horizontal Position (Left/Right only) - NO colors, fonts, or elements
*
* FPS Counter: Refresh Rate, Colors (background, text only), Font Sizes,
* Horizontal/Vertical Position
*
* FPS Graph: Refresh Rate, Colors (8 graph-specific colors), Toggles (show_info only),
* Horizontal/Vertical Position - NO fonts
*
* Game Resolutions: Refresh Rate, Colors (background, category, text only),
* Horizontal/Vertical Position - NO toggles, fonts, or elements
*/
#pragma once
#include <tesla.hpp>
#include "../Utils.hpp"
#include <unordered_set>
// External variables for navigation
extern std::string jumpItemName;
extern std::string jumpItemValue;
extern std::atomic<bool> jumpItemExactMatch;
static tsl::elm::ListItem* lastSelectedListItem;
// Forward declarations
class ConfiguratorOverlay;
class RefreshRateConfig;
class FontSizeConfig;
class FontSizeSelector;
class ColorConfig;
class ColorSelector;
class AlphaSelector;
class ShowConfig;
class TogglesConfig;
class DTCFormatConfig;
// Helper functions for color manipulation
inline std::string extractColorWithoutAlpha(const std::string& rgba) {
if (rgba.length() >= 5 && rgba[0] == '#') {
return rgba.substr(0, 4); // Return #RGB without alpha
}
return rgba;
}
inline std::string extractAlphaFromColor(const std::string& rgba) {
if (rgba.length() == 5 && rgba[0] == '#') {
return std::string(1, rgba[4]); // Return just the alpha character
}
return "9"; // Default alpha
}
inline std::string setAlphaInColor(const std::string& rgba, char alpha) {
if (rgba.length() >= 4 && rgba[0] == '#') {
std::string result = rgba.substr(0, 4); // Get #RGB
result += alpha; // Add new alpha
return result;
}
return rgba;
}
// Alpha Selector for background colors
class AlphaSelector : public tsl::Gui {
private:
std::string modeName;
std::string colorKey;
std::string title;
bool isMiniMode;
bool isMicroMode;
bool isFPSCounterMode;
bool isFPSGraphMode;
bool isGameResolutionsMode;
public:
AlphaSelector(const std::string& mode, const std::string& key, const std::string& displayTitle)
: modeName(mode), colorKey(key), title(displayTitle) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFPSCounterMode = (mode == "FPS Counter");
isFPSGraphMode = (mode == "FPS Graph");
isGameResolutionsMode = (mode == "Game Resolutions");
}
~AlphaSelector() {
lastSelectedListItem = nullptr;
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader(title));
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
else if (isGameResolutionsMode) section = "game_resolutions";
// Get current color value and extract alpha
std::string currentColor = ult::parseValueFromIniSection(configIniPath, section, colorKey);
if (currentColor.empty()) {
currentColor = "#0009"; // Default
}
std::string currentAlpha = extractAlphaFromColor(currentColor);
// Alpha options
static const std::vector<std::pair<std::string, char>> alphaOptions = {
{"Transparent", '0'},
{"10%", '1'},
{"20%", '3'},
{"30%", '4'},
{"40%", '6'},
{"50%", '8'},
{"60%", '9'},
{"70%", 'B'},
{"80%", 'C'},
{"90%", 'E'},
{"Opaque", 'F'}
};
for (const auto& option : alphaOptions) {
auto* alphaItem = new tsl::elm::ListItem(option.first);
if (currentAlpha[0] == option.second) {
alphaItem->setValue(ult::CHECKMARK_SYMBOL);
lastSelectedListItem = alphaItem;
}
alphaItem->setClickListener([this, alphaItem, option, section, currentColor](uint64_t keys) {
if (keys & KEY_A) {
// Get current color and update only the alpha
std::string color = ult::parseValueFromIniSection(configIniPath, section, colorKey);
if (color.empty()) color = "#0009";
std::string newColor = setAlphaInColor(color, option.second);
ult::setIniFileValue(configIniPath, section, colorKey, newColor);
alphaItem->setValue(ult::CHECKMARK_SYMBOL);
if (lastSelectedListItem && lastSelectedListItem != alphaItem) {
lastSelectedListItem->setValue("");
}
lastSelectedListItem = alphaItem;
return true;
}
return false;
});
list->addItem(alphaItem);
}
list->jumpToItem("", ult::CHECKMARK_SYMBOL, false);
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Alpha");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
jumpItemName = title;
jumpItemValue = "";
jumpItemExactMatch = false;
tsl::swapTo<ColorConfig>(SwapDepth(2), modeName);
return true;
}
return false;
}
};
// Define available DTC format options
static const std::vector<std::pair<std::string, std::string>> dtcFormats = {
// Special
{"Pretty", "%a, %b %d"+ult::DIVIDER_SYMBOL+"%I:%M %p"},
{"Compact", "%Y%m%d"+ult::DIVIDER_SYMBOL+"%H:%M:%S"},
{"FileSafe", "%Y-%m-%d"+ult::DIVIDER_SYMBOL+"%H-%M-%S"},
{"Day+Time", "%a"+ult::DIVIDER_SYMBOL+"%H:%M"},
// Datetime (default included here)
{"Date+Time(s)", "%m-%d-%Y"+ult::DIVIDER_SYMBOL+"%H:%M:%S"}, // default
{"Date+Time AM/PM", "%m-%d-%Y"+ult::DIVIDER_SYMBOL+"%I:%M %p"},
{"Date+Time(s) AM/PM", "%m-%d-%Y"+ult::DIVIDER_SYMBOL+"%I:%M:%S %p"},
{"Date+Time EU", "%d/%m/%Y"+ult::DIVIDER_SYMBOL+"%H:%M"},
{"Date+Time EU AM/PM", "%d/%m/%Y"+ult::DIVIDER_SYMBOL+"%I:%M %p"},
{"Date+Time(s) EU AM/PM", "%d/%m/%Y"+ult::DIVIDER_SYMBOL+"%I:%M:%S %p"},
{"Date+Time ISO", "%Y-%m-%dT"+ult::DIVIDER_SYMBOL+"%H:%M:%S"},
// Time only
{"Time 24h", "%H:%M"},
{"Time AM/PM", "%I:%M %p"},
{"Time(s) 24h", "%H:%M:%S"},
{"Time(s) AM/PM", "%I:%M:%S %p"},
// Date only
{"Date US", "%m-%d-%Y"},
{"Date EU", "%d/%m/%Y"},
{"Date ISO", "%Y-%m-%d"},
{"Date Short", "%m/%d/%y"}
};
// DTC Format Configuration (Mini/Micro only)
class DTCFormatConfig : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isMicroMode;
public:
DTCFormatConfig(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
}
~DTCFormatConfig() {
lastSelectedListItem = nullptr;
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("DTC Format"));
const std::string section = isMiniMode ? "mini" : "micro";
std::string currentValue = ult::parseValueFromIniSection(configIniPath, section, "dtc_format");
// Handle default values
if (currentValue.empty()) {
currentValue = isMiniMode ? "%m-%d-%Y"+ult::DIVIDER_SYMBOL+"%H:%M:%S" : "%H:%M:%S";
}
for (const auto& format : dtcFormats) {
auto* formatItem = new tsl::elm::ListItem(format.first);
//formatItem->setValue(format.second);
if (format.second == currentValue) {
formatItem->setValue(ult::CHECKMARK_SYMBOL);
lastSelectedListItem = formatItem;
}
formatItem->setClickListener([this, formatItem, format, section](uint64_t keys) {
if (keys & KEY_A) {
ult::setIniFileValue(configIniPath, section, "dtc_format", format.second);
formatItem->setValue(ult::CHECKMARK_SYMBOL);
if (lastSelectedListItem && lastSelectedListItem != formatItem) {
lastSelectedListItem->setValue("");
}
lastSelectedListItem = formatItem;
return true;
}
return false;
});
list->addItem(formatItem);
}
// Jump to currently selected item
list->jumpToItem("", ult::CHECKMARK_SYMBOL, false);
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "DTC Format");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
jumpItemName = "DTC Format";
jumpItemValue = "";
jumpItemExactMatch = false;
tsl::swapTo<ConfiguratorOverlay>(SwapDepth(2), modeName);
return true;
}
return false;
}
};
// Toggles Configuration
class TogglesConfig : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isMicroMode;
bool isFullMode;
bool isFPSGraphMode;
bool isGameResolutionsMode;
bool isFPSCounterMode;
public:
TogglesConfig(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFullMode = (mode == "Full");
isFPSGraphMode = (mode == "FPS Graph");
isGameResolutionsMode = (mode == "Game Resolutions");
isFPSCounterMode = (mode == "FPS Counter");
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("Toggles"));
if (isFPSGraphMode) {
// FPS Graph: show_info and disable_screenshots
auto* showInfo = new tsl::elm::ToggleListItem("Info", getCurrentShowInfo());
showInfo->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "fps-graph", "show_info", state ? "true" : "false");
});
list->addItem(showInfo);
auto* dynamicColors = new tsl::elm::ToggleListItem("Use Dynamic Colors", getCurrentUseDynamicColors());
dynamicColors->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "fps-graph", "use_dynamic_colors", state ? "true" : "false");
});
list->addItem(dynamicColors);
auto* disableScreenshots = new tsl::elm::ToggleListItem("Disable Screenshots", getCurrentDisableScreenshots("fps-graph"));
disableScreenshots->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "fps-graph", "disable_screenshots", state ? "true" : "false");
});
list->addItem(disableScreenshots);
} else if (isFullMode) {
// Full mode: specific full toggles
auto* realFreqs = new tsl::elm::ToggleListItem("Real Freqs", getCurrentShowRealFreqs());
realFreqs->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "full", "show_real_freqs", state ? "true" : "false");
});
list->addItem(realFreqs);
auto* showDeltas = new tsl::elm::ToggleListItem("Deltas", getCurrentShowDeltas());
showDeltas->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "full", "show_deltas", state ? "true" : "false");
});
list->addItem(showDeltas);
auto* targetFreqs = new tsl::elm::ToggleListItem("Target Freqs", getCurrentShowTargetFreqs());
targetFreqs->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "full", "show_target_freqs", state ? "true" : "false");
});
list->addItem(targetFreqs);
auto* showFPS = new tsl::elm::ToggleListItem("FPS", getCurrentShowFPS());
showFPS->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "full", "show_fps", state ? "true" : "false");
});
list->addItem(showFPS);
auto* showRES = new tsl::elm::ToggleListItem("RES", getCurrentShowRES());
showRES->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "full", "show_res", state ? "true" : "false");
});
list->addItem(showRES);
auto* showRDSD = new tsl::elm::ToggleListItem("Read Speed", getCurrentShowRDSD());
showRDSD->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "full", "show_read_speed", state ? "true" : "false");
});
list->addItem(showRDSD);
auto* dynamicColors = new tsl::elm::ToggleListItem("Use Dynamic Colors", getCurrentUseDynamicColors());
dynamicColors->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "fps-graph", "use_dynamic_colors", state ? "true" : "false");
});
list->addItem(dynamicColors);
auto* disableScreenshots = new tsl::elm::ToggleListItem("Disable Screenshots", getCurrentDisableScreenshots("full"));
disableScreenshots->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "full", "disable_screenshots", state ? "true" : "false");
});
list->addItem(disableScreenshots);
} else if (isMiniMode || isMicroMode) {
// Mini/Micro modes: shared toggles
const std::string section = isMiniMode ? "mini" : "micro";
auto* realFreqs = new tsl::elm::ToggleListItem("Real Frequencies", getCurrentRealFreqs());
realFreqs->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "real_freqs", state ? "true" : "false");
});
list->addItem(realFreqs);
auto* realVolts = new tsl::elm::ToggleListItem("Real Voltages", getCurrentRealVolts());
realVolts->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "real_volts", state ? "true" : "false");
});
list->addItem(realVolts);
auto* showFullCPU = new tsl::elm::ToggleListItem("Full CPU", getCurrentShowFullCPU());
showFullCPU->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_full_cpu", state ? "true" : "false");
});
list->addItem(showFullCPU);
auto* showVDDQ = new tsl::elm::ToggleListItem("VDDQ", getCurrentShowVDDQ());
showVDDQ->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_vddq", state ? "true" : "false");
});
list->addItem(showVDDQ);
auto* showVDD2 = new tsl::elm::ToggleListItem("VDD2", getCurrentShowVDD2());
showVDD2->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_vdd2", state ? "true" : "false");
});
list->addItem(showVDD2);
auto* showFullRes = new tsl::elm::ToggleListItem("Full Resolution", getCurrentShowFullRes());
showFullRes->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_full_res", state ? "true" : "false");
});
list->addItem(showFullRes);
auto* socVoltage = new tsl::elm::ToggleListItem("SOC Voltage", getCurrentShowSOCVoltage());
socVoltage->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_soc_voltage", state ? "true" : "false");
});
list->addItem(socVoltage);
if (isMiniMode) {
auto* PartLoadCPUGPU = new tsl::elm::ToggleListItem("RAM Load CPU/GPU", getCurrentShowRAMLoadCPUGPU());
PartLoadCPUGPU->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_RAM_load_CPU_GPU", state ? "true" : "false");
});
list->addItem(PartLoadCPUGPU);
}
if (isMiniMode || isMicroMode) {
auto* invertBatteryDisplay = new tsl::elm::ToggleListItem("Invert Battery Display", getCurrentInvertBatteryDisplay());
invertBatteryDisplay->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "invert_battery_display", state ? "true" : "false");
});
list->addItem(invertBatteryDisplay);
}
auto* dtcSymbol = new tsl::elm::ToggleListItem("Use DTC Symbol", getCurrentUseDTCSymbol());
dtcSymbol->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "use_dtc_symbol", state ? "true" : "false");
});
list->addItem(dtcSymbol);
auto* dynamicColors = new tsl::elm::ToggleListItem("Use Dynamic Colors", getCurrentUseDynamicColors());
dynamicColors->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "use_dynamic_colors", state ? "true" : "false");
});
list->addItem(dynamicColors);
auto* disableScreenshots = new tsl::elm::ToggleListItem("Disable Screenshots", getCurrentDisableScreenshots(section));
disableScreenshots->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "disable_screenshots", state ? "true" : "false");
});
list->addItem(disableScreenshots);
auto* sleepExit = new tsl::elm::ToggleListItem("Sleep Exit", getCurrentSleepExit(section));
sleepExit->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "sleep_exit", state ? "true" : "false");
});
list->addItem(sleepExit);
} else if (isGameResolutionsMode) {
// Game Resolutions mode: only disable_screenshots
auto* disableScreenshots = new tsl::elm::ToggleListItem("Disable Screenshots", getCurrentDisableScreenshots("game_resolutions"));
disableScreenshots->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "game_resolutions", "disable_screenshots", state ? "true" : "false");
});
list->addItem(disableScreenshots);
} else if (isFPSCounterMode) {
// FPS Counter mode: only disable_screenshots
auto* integerCounter = new tsl::elm::ToggleListItem("Use Integer Counter", getCurrentUseIntegerCounter("fps-counter"));
integerCounter->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "fps-counter", "use_integer_counter", state ? "true" : "false");
});
list->addItem(integerCounter);
// FPS Counter mode: only disable_screenshots
auto* disableScreenshots = new tsl::elm::ToggleListItem("Disable Screenshots", getCurrentDisableScreenshots("fps-counter"));
disableScreenshots->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "fps-counter", "disable_screenshots", state ? "true" : "false");
});
list->addItem(disableScreenshots);
}
list->jumpToItem(jumpItemName, jumpItemValue, jumpItemExactMatch);
{
jumpItemName = "";
jumpItemValue = "";
jumpItemExactMatch = false;
}
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Configuration");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
tsl::goBack();
return true;
}
return false;
}
private:
// Helper methods for getting current toggle states
bool getCurrentShowInfo() {
std::string value = ult::parseValueFromIniSection(configIniPath, "fps-graph", "show_info");
if (value.empty()) return true;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentRealFreqs() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "real_freqs");
if (value.empty()) return true;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentRealVolts() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "real_volts");
if (value.empty()) return true;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentShowFullCPU() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "show_full_cpu");
if (value.empty()) return false;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentShowVDDQ() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "show_vddq");
if (value.empty()) return false;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentShowVDD2() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "show_vdd2");
if (value.empty()) return true;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentShowFullRes() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "show_full_res");
if (value.empty()) return true; // Default: true for mini, false for micro
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentShowSOCVoltage() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "show_soc_voltage");
if (value.empty()) return false; // Default: false for mini, true for micro
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentShowRAMLoadCPUGPU() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "show_RAM_load_CPU_GPU");
if (value.empty()) return false; // Default: false for mini, true for micro
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentInvertBatteryDisplay() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "invert_battery_display");
if (value.empty()) return isMiniMode ? true : false; // Default: false for mini, true for micro
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentUseDTCSymbol() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "use_dtc_symbol");
if (value.empty()) return true;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentUseDynamicColors() {
const std::string section = isFPSGraphMode? "fps-graph" : (isMiniMode ? "mini" : "micro");
std::string value = ult::parseValueFromIniSection(configIniPath, section, "use_dynamic_colors");
if (value.empty()) return true;
convertToUpper(value);
return value == "TRUE";
}
bool getCurrentUseIntegerCounter(const std::string& section) {
std::string value = ult::parseValueFromIniSection(configIniPath, section, "use_integer_counter");
if (value.empty()) return false; // Default is false (screenshots enabled)
convertToUpper(value);
return value != "FALSE"; // True if not explicitly "FALSE"
}
bool getCurrentDisableScreenshots(const std::string& section) {
std::string value = ult::parseValueFromIniSection(configIniPath, section, "disable_screenshots");
if (value.empty()) return false; // Default is false (screenshots enabled)
convertToUpper(value);
return value != "FALSE"; // True if not explicitly "FALSE"
}
bool getCurrentSleepExit(const std::string& section) {
std::string value = ult::parseValueFromIniSection(configIniPath, section, "sleep_exit");
if (value.empty()) return false;
convertToUpper(value);
return value != "FALSE"; // True if not explicitly "FALSE"
}
// Full mode toggle helpers
bool getCurrentShowRealFreqs() {
std::string value = ult::parseValueFromIniSection(configIniPath, "full", "show_real_freqs");
if (value.empty()) return true;
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentShowDeltas() {
std::string value = ult::parseValueFromIniSection(configIniPath, "full", "show_deltas");
if (value.empty()) return true;
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentShowTargetFreqs() {
std::string value = ult::parseValueFromIniSection(configIniPath, "full", "show_target_freqs");
if (value.empty()) return true;
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentShowFPS() {
std::string value = ult::parseValueFromIniSection(configIniPath, "full", "show_fps");
if (value.empty()) return true;
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentShowRES() {
std::string value = ult::parseValueFromIniSection(configIniPath, "full", "show_res");
if (value.empty()) return true;
convertToUpper(value);
return value != "FALSE";
}
bool getCurrentShowRDSD() {
std::string value = ult::parseValueFromIniSection(configIniPath, "full", "show_read_speed");
if (value.empty()) return true;
convertToUpper(value);
return value != "FALSE";
}
};
// Refresh Rate Configuration
class RefreshRateConfig : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isMicroMode;
bool isFullMode;
bool isGameResolutionsMode;
bool isFPSCounterMode;
bool isFPSGraphMode;
int currentRate;
public:
RefreshRateConfig(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFullMode = (mode == "Full");
isGameResolutionsMode = (mode == "Game Resolutions");
isFPSCounterMode = (mode == "FPS Counter");
isFPSGraphMode = (mode == "FPS Graph");
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFullMode) section = "full";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
const std::string value = ult::parseValueFromIniSection(configIniPath, section, "refresh_rate");
int defaultRate = (isGameResolutionsMode) ? 10 : ((isFPSCounterMode || isFPSGraphMode) ? 30 : 1);
currentRate = value.empty() ? defaultRate : std::clamp(atoi(value.c_str()), 1, 60);
}
~RefreshRateConfig() {
lastSelectedListItem = nullptr;
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("Refresh Rate"));
static const std::vector<int> rates = {1, 2, 3, 5, 10, 15, 30, 60};
for (int rate : rates) {
auto* rateItem = new tsl::elm::ListItem(std::to_string(rate) + " Hz");
if (rate == currentRate) {
rateItem->setValue(ult::CHECKMARK_SYMBOL);
lastSelectedListItem = rateItem;
}
rateItem->setClickListener([this, rateItem, rate](uint64_t keys) {
if (keys & KEY_A) {
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFullMode) section = "full";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
ult::setIniFileValue(configIniPath, section, "refresh_rate", std::to_string(rate));
rateItem->setValue(ult::CHECKMARK_SYMBOL);
if (lastSelectedListItem && rateItem != lastSelectedListItem)
lastSelectedListItem->setValue("");
lastSelectedListItem = rateItem;
return true;
}
return false;
});
list->addItem(rateItem);
}
list->jumpToItem("", ult::CHECKMARK_SYMBOL, false);
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Configuration");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
jumpItemName = "Refresh Rate";
jumpItemValue = "";
jumpItemExactMatch = false;
tsl::swapTo<ConfiguratorOverlay>(SwapDepth(2), modeName);
return true;
}
return false;
}
};
// Frame Padding Configuration (Mini only)
class FramePaddingConfig : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isGameResolutionsMode;
bool isFPSCounterMode;
bool isFPSGraphMode;
int currentPadding;
public:
FramePaddingConfig(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isGameResolutionsMode = (mode == "Game Resolutions");
isFPSCounterMode = (mode == "FPS Counter");
isFPSGraphMode = (mode == "FPS Graph");
std::string section;
if (isMiniMode) section = "mini";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
const std::string value = ult::parseValueFromIniSection(configIniPath, section, "frame_padding");
currentPadding = value.empty() ? 10 : std::clamp(atoi(value.c_str()), 0, 14);
}
~FramePaddingConfig() {
lastSelectedListItem = nullptr;
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("Frame Padding"));
std::string section;
if (isMiniMode) section = "mini";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
static const std::vector<int> paddingValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
for (int padding : paddingValues) {
auto* paddingItem = new tsl::elm::ListItem(std::to_string(padding) + " px");
if (padding == currentPadding) {
paddingItem->setValue(ult::CHECKMARK_SYMBOL);
lastSelectedListItem = paddingItem;
}
paddingItem->setClickListener([this, paddingItem, padding, section](uint64_t keys) {
if (keys & KEY_A) {
ult::setIniFileValue(configIniPath, section, "frame_padding", std::to_string(padding));
paddingItem->setValue(ult::CHECKMARK_SYMBOL);
if (lastSelectedListItem && paddingItem != lastSelectedListItem)
lastSelectedListItem->setValue("");
lastSelectedListItem = paddingItem;
return true;
}
return false;
});
list->addItem(paddingItem);
}
list->jumpToItem("", ult::CHECKMARK_SYMBOL, false);
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Configuration");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
jumpItemName = "Frame Padding";
jumpItemValue = "";
jumpItemExactMatch = false;
tsl::swapTo<ConfiguratorOverlay>(SwapDepth(2), modeName);
return true;
}
return false;
}
};
// Font Size Selector
class FontSizeSelector : public tsl::Gui {
private:
std::string modeName;
std::string fontType;
bool isMiniMode;
bool isMicroMode;
bool isFPSCounterMode;
std::string title;
public:
FontSizeSelector(const std::string& mode, const std::string& type)
: modeName(mode), fontType(type) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFPSCounterMode = (mode == "FPS Counter");
title = fontType;
title[0] = std::toupper(title[0]);
title += " Font Size";
}
~FontSizeSelector() {
lastSelectedListItem = nullptr;
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader(title));
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFPSCounterMode) section = "fps-counter";
const std::string keyName = fontType + "_font_size";
const std::string currentValue = ult::parseValueFromIniSection(configIniPath, section, keyName);
int defaultSize = isFPSCounterMode ? 40 : 15;
const int currentSize = currentValue.empty() ? defaultSize : atoi(currentValue.c_str());
// Font size range depends on mode
int minSize = 8;
int maxSize;
if (isFPSCounterMode) maxSize = 150;
else if (isMiniMode) maxSize = 22;
else maxSize = 18; // Micro mode
for (int size = minSize; size <= maxSize; size++) {
auto* sizeItem = new tsl::elm::ListItem(std::to_string(size) + " pt");
if (size == currentSize) {
sizeItem->setValue(ult::CHECKMARK_SYMBOL);
lastSelectedListItem = sizeItem;
}
sizeItem->setClickListener([this, sizeItem, size, keyName, section](uint64_t keys) {
if (keys & KEY_A) {
ult::setIniFileValue(configIniPath, section, keyName, std::to_string(size));
sizeItem->setValue(ult::CHECKMARK_SYMBOL);
if (lastSelectedListItem && lastSelectedListItem != sizeItem)
lastSelectedListItem->setValue("");
lastSelectedListItem = sizeItem;
return true;
}
return false;
});
list->addItem(sizeItem);
}
list->jumpToItem("", ult::CHECKMARK_SYMBOL, false);
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Font Sizes");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
jumpItemName = title;
jumpItemValue = "";
jumpItemExactMatch = false;
tsl::swapTo<FontSizeConfig>(SwapDepth(2), modeName);
return true;
}
return false;
}
};
// Font Size Configuration
class FontSizeConfig : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isMicroMode;
bool isFPSCounterMode;
public:
FontSizeConfig(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFPSCounterMode = (mode == "FPS Counter");
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("Font Sizes"));
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFPSCounterMode) section = "fps-counter";
const std::string handheldValue = ult::parseValueFromIniSection(configIniPath, section, "handheld_font_size");
const std::string dockedValue = ult::parseValueFromIniSection(configIniPath, section, "docked_font_size");
int defaultSize = isFPSCounterMode ? 40 : 15;
const int handheldSize = handheldValue.empty() ? defaultSize : atoi(handheldValue.c_str());
const int dockedSize = dockedValue.empty() ? defaultSize : atoi(dockedValue.c_str());
auto* handheldItem = new tsl::elm::ListItem("Handheld Font Size");
handheldItem->setValue(std::to_string(handheldSize) + " pt");
handheldItem->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<FontSizeSelector>(modeName, "handheld");
return true;
}
return false;
});
list->addItem(handheldItem);
auto* dockedItem = new tsl::elm::ListItem("Docked Font Size");
dockedItem->setValue(std::to_string(dockedSize) + " pt");
dockedItem->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<FontSizeSelector>(modeName, "docked");
return true;
}
return false;
});
list->addItem(dockedItem);
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Configuration");
rootFrame->setContent(list);
list->jumpToItem(jumpItemName, jumpItemValue, jumpItemExactMatch);
{
jumpItemName = "";
jumpItemValue = "";
jumpItemExactMatch = false;
}
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
tsl::goBack();
return true;
}
return false;
}
};
// Color Selector
class ColorSelector : public tsl::Gui {
private:
std::string modeName;
std::string modeTitle;
std::string colorKey;
std::string defaultValue;
bool isMiniMode;
bool isMicroMode;
bool isFullMode;
bool isGameResolutionsMode;
bool isFPSCounterMode;
bool isFPSGraphMode;
bool isBackgroundColor;
bool isTextBasedColor;
public:
ColorSelector(const std::string& mode, const std::string& title, const std::string& key, const std::string& def)
: modeName(mode), modeTitle(title), colorKey(key), defaultValue(def) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFullMode = (mode == "Full");
isGameResolutionsMode = (mode == "Game Resolutions");
isFPSCounterMode = (mode == "FPS Counter");
isFPSGraphMode = (mode == "FPS Graph");
// Determine if this is a background color or text-based color
isBackgroundColor = (key == "background_color" || key == "focus_background_color" ||
(isFPSGraphMode && (key == "fps_counter_color" || key == "dashed_line_color")));
isTextBasedColor = (key == "text_color" || key == "separator_color" || key == "cat_color" ||
(isFPSGraphMode && (key == "border_color" || key == "max_fps_text_color" ||
key == "min_fps_text_color" || key == "main_line_color" ||
key == "rounded_line_color" || key == "perfect_line_color")));
}
~ColorSelector() {
lastSelectedListItem = nullptr;
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader(modeTitle));
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFullMode) section = "full";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
std::string currentValue = ult::parseValueFromIniSection(configIniPath, section, colorKey);
if (currentValue.empty()) currentValue = defaultValue;
// Extract the color without alpha for comparison (for backgrounds and text colors)
std::string currentColorWithoutAlpha = extractColorWithoutAlpha(currentValue);
// Updated colors list with comprehensive color palette
static const std::vector<std::pair<std::string, std::string>> colors = {
// Grays & Basics
{"Black", "#000F"},
{"Dark Gray", "#333F"},
{"Gray", "#444F"},
{"Light Gray", "#888F"},
{"Silver", "#CCCF"},
{"White", "#FFFF"},
// Reds
{"Dark Red", "#800F"},
{"Red", "#F00F"},
{"Light Red", "#F88F"},
{"Pink", "#F8AF"},
// Greens
{"Dark Green", "#080F"},
{"Green", "#0F0F"},
{"Lime Green", "#0C0F"},
{"Light Green", "#8F8F"},
// Blues
{"Dark Blue", "#003F"},
{"Blue", "#00FF"},
{"Light Blue", "#2DFF"},
{"Sky Blue", "#8CFF"},
// Purples
{"Dark Purple", "#808F"},
{"Purple", "#80FF"},
{"Light Purple", "#C8FF"},
{"Violet", "#A0FF"},
// Yellows & Oranges
{"Orange", "#F80F"},
{"Yellow", "#FF0F"},
{"Light Yellow", "#FFCF"},
// Cyans & Teals
{"Teal", "#088F"},
{"Cyan", "#0FFF"},
{"Light Cyan", "#8FFF"},
// Magentas & Pinks
{"Magenta", "#F0FF"},
{"Hot Pink", "#F8CF"},
// Browns
{"Brown", "#840F"},
{"Light Brown", "#A86F"}
};
std::string _jumpItemValue;
for (const auto& color : colors) {
auto* colorItem = new tsl::elm::ListItem(color.first);
// For display, show the color code based on type
std::string displayValue;
if (isTextBasedColor || isBackgroundColor) {
// For ALL text-based colors AND background colors, show without the alpha
displayValue = extractColorWithoutAlpha(color.second);
} else {
// For any remaining FPS Graph colors (shouldn't happen now), keep original behavior
displayValue = color.second;
}
colorItem->setValue(displayValue);
// Check if this is the selected color
bool isSelected = false;
if (isBackgroundColor || isTextBasedColor) {
// For background and text colors, compare without alpha
isSelected = (extractColorWithoutAlpha(color.second) == currentColorWithoutAlpha);
} else {
// For any remaining FPS Graph colors (shouldn't happen now)
isSelected = (color.second == currentValue);
}
if (isSelected) {
colorItem->setValue(displayValue + " " + ult::CHECKMARK_SYMBOL);
lastSelectedListItem = colorItem;
_jumpItemValue = displayValue + " " + ult::CHECKMARK_SYMBOL;
}
colorItem->setClickListener([this, colorItem, color, section, displayValue](uint64_t keys) {
if (keys & KEY_A) {
std::string valueToSave = color.second;
if (isBackgroundColor) {
// For background colors, preserve existing alpha
std::string existingColor = ult::parseValueFromIniSection(configIniPath, section, colorKey);
if (!existingColor.empty() && existingColor.length() == 5) {
char existingAlpha = existingColor[4];
valueToSave = setAlphaInColor(color.second, existingAlpha);
}
} else if (isTextBasedColor) {
// For text-based colors, ensure alpha is always F
valueToSave = setAlphaInColor(color.second, 'F');
}
// For any remaining FPS Graph colors (shouldn't happen now), use as-is
ult::setIniFileValue(configIniPath, section, colorKey, valueToSave);
// Update the UI - clear old checkmark and set new one
if (lastSelectedListItem && lastSelectedListItem != colorItem) {
// Get the display value for the old selected item
std::string oldDisplayValue;
for (const auto& c : colors) {
if (lastSelectedListItem->getText() == c.first) {
if (isTextBasedColor || isBackgroundColor) {
oldDisplayValue = extractColorWithoutAlpha(c.second);
} else {
oldDisplayValue = c.second;
}
break;
}
}
lastSelectedListItem->setValue(oldDisplayValue);
}
// Set new checkmark
colorItem->setValue(displayValue + " " + ult::CHECKMARK_SYMBOL);
lastSelectedListItem = colorItem;
return true;
}
return false;
});
list->addItem(colorItem);
}
list->jumpToItem("", _jumpItemValue, false);
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Colors");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
jumpItemName = modeTitle;
jumpItemValue = "";
jumpItemExactMatch = false;
tsl::swapTo<ColorConfig>(SwapDepth(2), modeName);
return true;
}
return false;
}
};
// Color Configuration
class ColorConfig : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isMicroMode;
bool isFullMode;
bool isGameResolutionsMode;
bool isFPSCounterMode;
bool isFPSGraphMode;
public:
ColorConfig(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFullMode = (mode == "Full");
isGameResolutionsMode = (mode == "Game Resolutions");
isFPSCounterMode = (mode == "FPS Counter");
isFPSGraphMode = (mode == "FPS Graph");
// Full mode should never access color configuration
//if (isFullMode) {
// // This should not happen, but if it does, go back
// tsl::goBack();
//}
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("Colors"));
auto getCurrentColor = [this](const std::string& key, const std::string& def) {
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFullMode) section = "full";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
std::string value = ult::parseValueFromIniSection(configIniPath, section, key);
return value.empty() ? def : value;
};
auto getColorName = [](const std::string& hexColor) -> std::string {
// Extract RGB without alpha for comparison
std::string rgb = hexColor;
if (hexColor.length() == 5 && hexColor[0] == '#') {
rgb = hexColor.substr(0, 4);
}
// Map of hex colors to names (RGB only, no alpha)
static const std::map<std::string, std::string> colorNames = {
// Grays & Basics
{"#000", "Black"},
{"#333", "Dark Gray"},
{"#444", "Gray"},
{"#888", "Light Gray"},
{"#CCC", "Silver"},
{"#FFF", "White"},
// Reds
{"#800", "Dark Red"},
{"#F00", "Red"},
{"#F88", "Light Red"},
{"#F8A", "Pink"},
// Greens
{"#080", "Dark Green"},
{"#0F0", "Green"},
{"#0C0", "Lime Green"},
{"#8F8", "Light Green"},
// Blues
{"#003", "Dark Blue"},
{"#00F", "Blue"},
{"#2DF", "Light Blue"},
{"#8CF", "Sky Blue"},
// Purples
{"#808", "Dark Purple"},
{"#80F", "Purple"},
{"#C8F", "Light Purple"},
{"#A0F", "Violet"},
// Yellows & Oranges
{"#F80", "Orange"},
{"#FF0", "Yellow"},
{"#FFC", "Light Yellow"},
// Cyans & Teals
{"#088", "Teal"},
{"#0FF", "Cyan"},
{"#8FF", "Light Cyan"},
// Magentas & Pinks
{"#F0F", "Magenta"},
{"#F8C", "Hot Pink"},
// Browns
{"#840", "Brown"},
{"#A86", "Light Brown"}
};
auto it = colorNames.find(rgb);
if (it != colorNames.end()) {
// Special case for black/transparent disambiguation
if (rgb == "#000" && hexColor.length() == 5) {
char alpha = hexColor[4];
if (alpha == '0') return "Transparent";
else return "Black";
}
return it->second;
}
return rgb; // Return hex if no name found
};
auto getAlphaPercentage = [](const std::string& color) -> std::string {
if (color.length() == 5 && color[0] == '#') {
char alpha = color[4];
switch(alpha) {
case '0': return "0%";
case '1': return "10%";
case '3': return "20%";
case '4': return "30%";
case '6': return "40%";
case '8': return "50%";
case '9': return "60%";
case 'B': case 'b': return "70%";
case 'C': case 'c': return "80%";
case 'E': case 'e': return "90%";
case 'F': case 'f': return "100%";
default: return "60%";
}
}
return "60%";
};
if (!isFullMode) {
// Background Color (all modes)
auto* bgColor = new tsl::elm::ListItem("Background Color");
std::string bgDefault = "#0009";
std::string bgCurrentColor = getCurrentColor("background_color", bgDefault);
// Display color name instead of hex
bgColor->setValue(getColorName(bgCurrentColor));
bgColor->setClickListener([this, bgDefault](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Background Color", "background_color", bgDefault);
return true;
}
return false;
});
list->addItem(bgColor);
// Background Alpha (new)
auto* bgAlpha = new tsl::elm::ListItem("Background Alpha");
bgAlpha->setValue(getAlphaPercentage(bgCurrentColor));
bgAlpha->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<AlphaSelector>(modeName, "background_color", "Background Alpha");
return true;
}
return false;
});
list->addItem(bgAlpha);
if (isMiniMode || isFPSCounterMode || isFPSGraphMode || isGameResolutionsMode) {
// Mini mode: has focus background
auto* focusBgColor = new tsl::elm::ListItem("Focus Color");
std::string focusCurrentColor = getCurrentColor("focus_background_color", "#000F");
focusBgColor->setValue(getColorName(focusCurrentColor));
focusBgColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Focus Color", "focus_background_color", "#000F");
return true;
}
return false;
});
list->addItem(focusBgColor);
// Focus Alpha (new)
auto* focusAlpha = new tsl::elm::ListItem("Focus Alpha");
focusAlpha->setValue(getAlphaPercentage(focusCurrentColor));
focusAlpha->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<AlphaSelector>(modeName, "focus_background_color", "Focus Alpha");
return true;
}
return false;
});
list->addItem(focusAlpha);
}
}
// Text Color (all modes)
auto* textColor = new tsl::elm::ListItem("Text Color");
std::string textCurrentColor = getCurrentColor("text_color", "#FFFF");
// Display color name for text colors
textColor->setValue(getColorName(textCurrentColor));
textColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Text Color", "text_color", "#FFFF");
return true;
}
return false;
});
list->addItem(textColor);
if (isFPSGraphMode) {
// FPS Graph specific colors
struct ColorSetting {
std::string name;
std::string key;
std::string defaultVal;
bool isBackgroundType; // true for colors that allow alpha adjustment
};
// Game Resolutions: only category color (no separator)
auto* catColor = new tsl::elm::ListItem("Category Color");
catColor->setValue(getColorName(getCurrentColor("cat_color", "#0F0F")));
catColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Category Color", "cat_color", "#0F0F");
return true;
}
return false;
});
list->addItem(catColor);
static const std::vector<ColorSetting> fpsGraphColors = {
{"FPS Counter", "fps_counter_color", "#888C", true}, // background type
{"Border", "border_color", "#2DFF", false}, // text type
{"Dashed Line", "dashed_line_color", "#8888", true}, // background type
{"Max FPS Text", "max_fps_text_color", "#FFFF", false}, // text type
{"Min FPS Text", "min_fps_text_color", "#FFFF", false}, // text type
{"Main Line", "main_line_color", "#FFFF", false}, // text type
{"Rounded Line", "rounded_line_color", "#F0FF", false}, // text type
{"Perfect Line", "perfect_line_color", "#0C0F", false} // text type
};
for (const auto& color : fpsGraphColors) {
auto* colorItem = new tsl::elm::ListItem(color.name + " Color");
const std::string currentVal = getCurrentColor(color.key, color.defaultVal);
if (color.isBackgroundType) {
// For background-type colors, show color name
colorItem->setValue(getColorName(currentVal));
} else {
// For text-type colors, show color name
colorItem->setValue(getColorName(currentVal));
}
colorItem->setClickListener([this, color](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, color.name, color.key, color.defaultVal);
return true;
}
return false;
});
list->addItem(colorItem);
// Add alpha selector for background-type colors
if (color.isBackgroundType) {
auto* alphaItem = new tsl::elm::ListItem(color.name + " Alpha");
alphaItem->setValue(getAlphaPercentage(currentVal));
alphaItem->setClickListener([this, color](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<AlphaSelector>(modeName, color.key, color.name + " Alpha");
return true;
}
return false;
});
list->addItem(alphaItem);
}
}
} else if (isFullMode) {
auto* catColor1 = new tsl::elm::ListItem("Category Color 1");
// Display color name for category colors
catColor1->setValue(getColorName(getCurrentColor("cat_color_1", "#8FFF")));
catColor1->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Category Color 1", "cat_color_1", "#8FFF");
return true;
}
return false;
});
list->addItem(catColor1);
auto* catColor2 = new tsl::elm::ListItem("Category Color 2");
// Display color name for category colors
catColor2->setValue(getColorName(getCurrentColor("cat_color_2", "#2DFF")));
catColor2->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Category Color 2", "cat_color_2", "#2DFF");
return true;
}
return false;
});
list->addItem(catColor2);
auto* sepColor = new tsl::elm::ListItem("Separator Color");
// Display color name for separator colors
sepColor->setValue(getColorName(getCurrentColor("separator_color", "#888F")));
sepColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Separator Color", "separator_color", "#888F");
return true;
}
return false;
});
list->addItem(sepColor);
} else if (isMiniMode) {
auto* catColor = new tsl::elm::ListItem("Category Color");
// Display color name for category colors
catColor->setValue(getColorName(getCurrentColor("cat_color", "#2DFF")));
catColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Category Color", "cat_color", "#2DFF");
return true;
}
return false;
});
list->addItem(catColor);
auto* sepColor = new tsl::elm::ListItem("Separator Color");
// Display color name for separator colors
sepColor->setValue(getColorName(getCurrentColor("separator_color", "#888F")));
sepColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Separator Color", "separator_color", "#888F");
return true;
}
return false;
});
list->addItem(sepColor);
} else if (isMicroMode) {
auto* catColor = new tsl::elm::ListItem("Category Color");
catColor->setValue(getColorName(getCurrentColor("cat_color", "#2DFF")));
catColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Category Color", "cat_color", "#2DFF");
return true;
}
return false;
});
list->addItem(catColor);
// Micro mode: separator and category colors (no focus background like Mini)
auto* sepColor = new tsl::elm::ListItem("Separator Color");
sepColor->setValue(getColorName(getCurrentColor("separator_color", "#888F")));
sepColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Separator Color", "separator_color", "#888F");
return true;
}
return false;
});
list->addItem(sepColor);
} else if (isGameResolutionsMode) {
// Game Resolutions: only category color (no separator)
auto* catColor = new tsl::elm::ListItem("Category Color");
catColor->setValue(getColorName(getCurrentColor("cat_color", "#2DFF")));
catColor->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorSelector>(modeName, "Category Color", "cat_color", "#2DFF");
return true;
}
return false;
});
list->addItem(catColor);
}
// FPS Counter mode: only background and text colors (already added above)
// Full mode: NO color settings at all (excluded from this function)
list->jumpToItem(jumpItemName, jumpItemValue, jumpItemExactMatch);
{
jumpItemName = "";
jumpItemValue = "";
jumpItemExactMatch = false;
}
//list->disableCaching();
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Configuration");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
tsl::goBack();
return true;
}
return false;
}
};
// Show Configuration (Mini/Micro only)
class ShowConfig : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isMicroMode;
std::vector<std::string> elementOrder;
std::unordered_set<std::string> enabledElements;
public:
ShowConfig(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("Elements " + ult::DIVIDER_SYMBOL + " \uE0E3 Move Down " + ult::DIVIDER_SYMBOL + " \uE0E2 Move Up"));
const std::string section = isMiniMode ? "mini" : "micro";
std::string showValue = ult::parseValueFromIniSection(configIniPath, section, "show");
std::string orderValue = ult::parseValueFromIniSection(configIniPath, section, "element_order");
if (showValue.empty()) {
showValue = isMiniMode ? "DTC+BAT+CPU+GPU+RAM+TMP+FPS+RES" : "FPS+CPU+GPU+RAM+SOC+BAT+DTC";
}
convertToUpper(showValue);
enabledElements.clear();
ult::StringStream ss(showValue);
std::string item;
while (ss.getline(item, '+')) {
if (!item.empty()) {
enabledElements.insert(item);
}
}
elementOrder.clear();
if (!orderValue.empty()) {
convertToUpper(orderValue);
ult::StringStream orderSS(orderValue);
std::string orderItem;
while (orderSS.getline(orderItem, '+')) {
if (!orderItem.empty()) {
elementOrder.push_back(orderItem);
}
}
} else {
ult::StringStream ss2(showValue);
while (ss2.getline(item, '+')) {
if (!item.empty()) {
elementOrder.push_back(item);
}
}
}
static constexpr std::string_view miniElements[] = {
"DTC","BAT","CPU","GPU","RAM","MEM","READ","SOC","TMP","FPS","RES"
};
static constexpr std::string_view microElements[] = {
"FPS","CPU","GPU","RAM","READ","SOC","TMP","RES","BAT","DTC"
};
// Use span or array reference instead of pointer
const auto* allElements = isMiniMode ? miniElements : microElements;
const size_t allElementsSize = isMiniMode ? std::size(miniElements) : std::size(microElements);
elementOrder.clear();
if (!orderValue.empty()) {
convertToUpper(orderValue);
ult::StringStream orderSS(orderValue);
std::string orderItem;
while (orderSS.getline(orderItem, '+')) {
if (!orderItem.empty()) {
elementOrder.push_back(orderItem);
}
}
} else {
// Initialize with allElements order instead
for (size_t i = 0; i < allElementsSize; i++) {
auto elem = allElements[i];
if (!isMiniMode && elem == "MEM")
continue;
elementOrder.emplace_back(elem);
}
}
for (size_t i = 0; i < elementOrder.size(); i++) {
const std::string& element = elementOrder[i];
const bool isEnabled = enabledElements.find(element) != enabledElements.end();
auto* elementItem = new tsl::elm::ListItem(element);
elementItem->setValue(isEnabled ? ult::ON : ult::OFF, !isEnabled);
elementItem->setClickListener([this, elementItem, element](uint64_t keys) {
static bool hasNotTriggeredAnimation = false;
if (hasNotTriggeredAnimation) {
elementItem->triggerClickAnimation();
hasNotTriggeredAnimation = false;
}
if (keys & KEY_A) {
const bool currentlyEnabled = enabledElements.find(element) != enabledElements.end();
if (currentlyEnabled) {
enabledElements.erase(element);
} else {
enabledElements.insert(element);
}
updateShowAndOrder();
jumpItemName = element;
jumpItemValue = "";
jumpItemExactMatch = true;
hasNotTriggeredAnimation = true;
tsl::swapTo<ShowConfig>(SwapDepth(1), modeName);
return true;
}
else if (keys & KEY_Y || keys & KEY_X) {
size_t currentPos = 0;
for (size_t j = 0; j < elementOrder.size(); j++) {
if (elementOrder[j] == element) {
currentPos = j;
break;
}
}
if (keys & KEY_X) {
if (currentPos > 0) {
std::swap(elementOrder[currentPos], elementOrder[currentPos - 1]);
} else {
const std::string temp = elementOrder[0];
for (size_t j = 0; j < elementOrder.size() - 1; j++) {
elementOrder[j] = elementOrder[j + 1];
}
elementOrder[elementOrder.size() - 1] = temp;
}
triggerRumbleClick.store(true, std::memory_order_release);
triggerMoveSound.store(true, std::memory_order_release);
} else if (keys & KEY_Y) {
if (currentPos < elementOrder.size() - 1) {
std::swap(elementOrder[currentPos], elementOrder[currentPos + 1]);
} else {
const std::string temp = elementOrder[elementOrder.size() - 1];
for (size_t j = elementOrder.size() - 1; j > 0; j--) {
elementOrder[j] = elementOrder[j - 1];
}
elementOrder[0] = temp;
}
triggerRumbleClick.store(true, std::memory_order_release);
triggerMoveSound.store(true, std::memory_order_release);
}
updateShowAndOrder();
jumpItemName = element;
jumpItemValue = "";
jumpItemExactMatch = true;
tsl::swapTo<ShowConfig>(SwapDepth(1), modeName);
return true;
}
return false;
});
list->addItem(elementItem);
}
list->jumpToItem(jumpItemName, jumpItemValue, jumpItemExactMatch);
{
jumpItemName = "";
jumpItemValue = "";
jumpItemExactMatch = false;
}
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", "Configuration");
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
tsl::goBack();
return true;
}
return false;
}
private:
void updateShowAndOrder() {
std::string newShowValue;
std::string newOrderValue;
bool showFirst = true;
bool orderFirst = true;
for (const std::string& element : elementOrder) {
if (!orderFirst) {
newOrderValue += "+";
}
newOrderValue += element;
orderFirst = false;
if (enabledElements.find(element) != enabledElements.end()) {
if (!showFirst) {
newShowValue += "+";
}
newShowValue += element;
showFirst = false;
}
}
const std::string section = isMiniMode ? "mini" : "micro";
ult::setIniFileValue(configIniPath, section, "show", newShowValue);
ult::setIniFileValue(configIniPath, section, "element_order", newOrderValue);
}
};
// Main Configurator
class ConfiguratorOverlay : public tsl::Gui {
private:
std::string modeName;
bool isMiniMode;
bool isMicroMode;
bool isFullMode;
bool isGameResolutionsMode;
bool isFPSCounterMode;
bool isFPSGraphMode;
public:
ConfiguratorOverlay(const std::string& mode) : modeName(mode) {
isMiniMode = (mode == "Mini");
isMicroMode = (mode == "Micro");
isFullMode = (mode == "Full");
isGameResolutionsMode = (mode == "Game Resolutions");
isFPSCounterMode = (mode == "FPS Counter");
isFPSGraphMode = (mode == "FPS Graph");
}
virtual tsl::elm::Element* createUI() override {
auto* list = new tsl::elm::List();
list->addItem(new tsl::elm::CategoryHeader("Configuration"));
// 5. Elements (Mini/Micro only)
if (isMiniMode || isMicroMode) {
auto* showSettings = new tsl::elm::ListItem("Elements");
showSettings->setValue(ult::DROPDOWN_SYMBOL);
showSettings->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ShowConfig>(modeName);
return true;
}
return false;
});
list->addItem(showSettings);
}
// 3. Toggles (All modes)
//if (isMiniMode || isMicroMode || isFullMode || isFPSGraphMode) {
auto* toggles = new tsl::elm::ListItem("Toggles");
toggles->setValue(ult::DROPDOWN_SYMBOL);
toggles->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<TogglesConfig>(modeName);
return true;
}
return false;
});
list->addItem(toggles);
//}
// 2. Colors (not Full mode - it has no color settings)
//if (!isFullMode) {
auto* colors = new tsl::elm::ListItem("Colors");
colors->setValue(ult::DROPDOWN_SYMBOL);
colors->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<ColorConfig>(modeName);
return true;
}
return false;
});
list->addItem(colors);
//}
// 4. Font Sizes (Mini/Micro/FPS Counter only)
if (isMiniMode || isMicroMode || isFPSCounterMode) {
auto* fontSizes = new tsl::elm::ListItem("Font Sizes");
fontSizes->setValue(ult::DROPDOWN_SYMBOL);
fontSizes->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<FontSizeConfig>(modeName);
return true;
}
return false;
});
list->addItem(fontSizes);
}
// 1. Refresh Rate (all modes)
auto* refreshRate = new tsl::elm::ListItem("Refresh Rate");
refreshRate->setValue(std::to_string(getCurrentRefreshRate()) + " Hz");
refreshRate->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<RefreshRateConfig>(modeName);
return true;
}
return false;
});
list->addItem(refreshRate);
// 6. DTC Format (Mini/Micro only) - NEW ADDITION
if (isMiniMode || isMicroMode) {
auto* dtcFormat = new tsl::elm::ListItem("DTC Format");
dtcFormat->setValue(getCurrentDTCFormat());
dtcFormat->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<DTCFormatConfig>(modeName);
return true;
}
return false;
});
list->addItem(dtcFormat);
}
// 7. Frame Padding (Mini only) - NEW ADDITION
if (isMiniMode || isGameResolutionsMode || isFPSCounterMode || isFPSGraphMode) {
auto* framePadding = new tsl::elm::ListItem("Frame Padding");
framePadding->setValue(std::to_string(getCurrentFramePadding()) + " px");
framePadding->setClickListener([this](uint64_t keys) {
if (keys & KEY_A) {
tsl::changeTo<FramePaddingConfig>(modeName);
return true;
}
return false;
});
list->addItem(framePadding);
}
// 7. Mode-specific positioning settings
if (isMicroMode) {
// Text Alignment for Micro
auto* textAlign = new tsl::elm::ListItem("Text Alignment");
textAlign->setValue(getCurrentTextAlign());
textAlign->setClickListener([this, textAlign](uint64_t keys) {
if (keys & KEY_A) {
const std::string next = cycleTextAlign();
textAlign->setValue(next);
return true;
}
return false;
});
list->addItem(textAlign);
// Vertical Position for Micro (Top/Bottom only)
auto* layerPos = new tsl::elm::ListItem("Vertical Position");
layerPos->setValue(getCurrentLayerPosBottom());
layerPos->setClickListener([this, layerPos](uint64_t keys) {
if (keys & KEY_A) {
const std::string next = cycleLayerPosBottom();
layerPos->setValue(next);
return true;
}
return false;
});
list->addItem(layerPos);
} else if (isFullMode) {
// Horizontal Position for Full (Left/Right only)
auto* layerPos = new tsl::elm::ListItem("Horizontal Position");
layerPos->setValue(getCurrentLayerPosRight());
layerPos->setClickListener([this, layerPos](uint64_t keys) {
if (keys & KEY_A) {
const std::string next = cycleLayerPosRight();
layerPos->setValue(next);
return true;
}
return false;
});
list->addItem(layerPos);
//} else if (isGameResolutionsMode || isFPSCounterMode || isFPSGraphMode) {
// // Both horizontal and vertical positioning
// auto* layerPosH = new tsl::elm::ListItem("Horizontal Position");
// layerPosH->setValue(getCurrentLayerPosRight());
// layerPosH->setClickListener([this, layerPosH](uint64_t keys) {
// if (keys & KEY_A) {
// const std::string next = cycleLayerPosRight();
// layerPosH->setValue(next);
// return true;
// }
// return false;
// });
// list->addItem(layerPosH);
//
// auto* layerPosV = new tsl::elm::ListItem("Vertical Position");
// layerPosV->setValue(getCurrentLayerPosBottom());
// layerPosV->setClickListener([this, layerPosV](uint64_t keys) {
// if (keys & KEY_A) {
// const std::string next = cycleLayerPosBottom();
// layerPosV->setValue(next);
// return true;
// }
// return false;
// });
// list->addItem(layerPosV);
}
list->jumpToItem(jumpItemName, jumpItemValue, jumpItemExactMatch.load(std::memory_order_acquire));
{
jumpItemName = "";
jumpItemValue = "";
jumpItemExactMatch = false;
}
tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Horizon OC Monitor", modeName);
rootFrame->setContent(list);
return rootFrame;
}
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysDown & KEY_B) {
triggerRumbleDoubleClick.store(true, std::memory_order_release);
triggerExitSound.store(true, std::memory_order_release);
lastSelectedItem = modeName;
tsl::swapTo<MainMenu>();
return true;
}
return false;
}
private:
int getCurrentRefreshRate() {
std::string section;
if (isMiniMode) section = "mini";
else if (isMicroMode) section = "micro";
else if (isFullMode) section = "full";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "refresh_rate");
int defaultRate = (isGameResolutionsMode) ? 10 : ((isFPSCounterMode || isFPSGraphMode) ? 30 : 1);
return value.empty() ? defaultRate : atoi(value.c_str());
}
// NEW METHOD: Get current DTC format for display
std::string getCurrentDTCFormat() {
if (isMiniMode || isMicroMode) {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "dtc_format");
// Handle defaults properly
if (value.empty()) {
value = isMiniMode ? "%m-%d-%Y"+ult::DIVIDER_SYMBOL+"%H:%M:%S" : "%H:%M:%S";
}
// Convert format string to display name
return getDTCFormatName(value);
}
return "";
}
// Helper function to convert format string to display name
std::string getDTCFormatName(const std::string& formatStr) {
for (const auto& format : dtcFormats) {
if (format.second == formatStr) {
return format.first;
}
}
// Return the format string itself if no match found
return formatStr;
}
int getCurrentFramePadding() {
std::string section;
if (isMiniMode) section = "mini";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
else return 10;
std::string value = ult::parseValueFromIniSection(configIniPath, section, "frame_padding");
return value.empty() ? 10 : atoi(value.c_str());
}
std::string getCurrentTextAlign() {
if (isMicroMode) {
std::string value = ult::parseValueFromIniSection(configIniPath, "micro", "text_align");
convertToUpper(value);
if (value == "LEFT") return "Left";
if (value == "RIGHT") return "Right";
return "Center";
}
return "";
}
std::string getCurrentLayerPosRight() {
std::string section;
if (isFullMode) section = "full";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "layer_width_align");
convertToUpper(value);
if (isFullMode) {
// Full mode: only Left and Right allowed
if (value == "RIGHT") return "Right";
return "Left";
} else {
// Other modes: allow Center
if (value == "RIGHT") return "Right";
if (value == "CENTER") return "Center";
return "Left";
}
}
std::string getCurrentLayerPosBottom() {
std::string section;
if (isMicroMode) section = "micro";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "layer_height_align");
convertToUpper(value);
if (isMicroMode) {
// Micro mode: only Top and Bottom allowed
if (value == "BOTTOM") return "Bottom";
return "Top";
} else {
// Other modes: allow Center
if (value == "BOTTOM") return "Bottom";
if (value == "CENTER") return "Center";
return "Top";
}
}
std::string cycleTextAlign() {
if (isMicroMode) {
const std::string current = getCurrentTextAlign();
std::string next;
if (current == "Left") next = "Center";
else if (current == "Center") next = "Right";
else if (current == "Right") next = "Left";
else next = "Center";
ult::setIniFileValue(configIniPath, "micro", "text_align", next);
return next;
}
return "";
}
std::string cycleLayerPosRight() {
std::string section;
if (isFullMode) section = "full";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
const std::string current = getCurrentLayerPosRight();
std::string next;
if (isFullMode) {
// Full mode: only Left and Right
if (current == "Left") next = "Right";
else next = "Left";
} else {
// Other modes: Left -> Center -> Right -> Left
if (current == "Left") next = "Center";
else if (current == "Center") next = "Right";
else next = "Left";
}
const std::string value = (next == "Right") ? "right" : (next == "Center" ? "center" : "left");
ult::setIniFileValue(configIniPath, section, "layer_width_align", value);
return next;
}
std::string cycleLayerPosBottom() {
std::string section;
if (isMicroMode) section = "micro";
else if (isGameResolutionsMode) section = "game_resolutions";
else if (isFPSCounterMode) section = "fps-counter";
else if (isFPSGraphMode) section = "fps-graph";
const std::string current = getCurrentLayerPosBottom();
std::string next;
if (isMicroMode) {
// Micro mode: only Top and Bottom
if (current == "Top") next = "Bottom";
else next = "Top";
} else {
// Other modes: Top -> Center -> Bottom -> Top
if (current == "Top") next = "Center";
else if (current == "Center") next = "Bottom";
else next = "Top";
}
const std::string value = (next == "Bottom") ? "bottom" : (next == "Center" ? "center" : "top");
ult::setIniFileValue(configIniPath, section, "layer_height_align", value);
return next;
}
};