sysclk: add kip editing feature

This commit is contained in:
souldbminersmwc
2025-12-13 16:48:55 -05:00
parent b4abdf9b43
commit 3a2f113d25
26 changed files with 1252 additions and 1015 deletions

View File

@@ -25,154 +25,184 @@
*/
#include "freq_choice_gui.h"
#include "freq_choice_gui.h"
#include "../format.h"
#include "fatal_gui.h"
FreqChoiceGui::FreqChoiceGui(std::uint32_t selectedHz,
std::uint32_t* hzList,
std::uint32_t hzCount,
SysClkModule module,
FreqChoiceListener listener,
bool checkMax,
std::map<uint32_t, std::string> labels)
{
this->selectedHz = selectedHz;
this->hzList = hzList;
this->hzCount = hzCount;
this->module = module;
this->listener = listener;
this->checkMax = checkMax;
this->labels = labels; // NEW
this->configList = new SysClkConfigValueList {};
}
#include "../format.h"
#include "fatal_gui.h"
FreqChoiceGui::FreqChoiceGui(std::uint32_t selectedHz, std::uint32_t *hzList, std::uint32_t hzCount, SysClkModule module, FreqChoiceListener listener, bool checkMax)
{
this->selectedHz = selectedHz;
this->hzList = hzList;
this->hzCount = hzCount;
this->module = module;
this->listener = listener;
this->checkMax = checkMax;
this->configList = new SysClkConfigValueList {};
}
FreqChoiceGui::~FreqChoiceGui()
{
delete this->configList;
}
tsl::elm::ListItem* FreqChoiceGui::createFreqListItem(std::uint32_t hz, bool selected, int safety)
{
std::string text = formatListFreqHz(hz);
if (selected) text += " \uE14B";
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(text, "", false);
switch (safety)
{
case 0:
listItem->setTextColor(tsl::Color(255, 255, 255, 255));
listItem->setValueColor(tsl::Color(255, 255, 255, 255));
break;
case 1:
listItem->setTextColor(tsl::Color(255, 165, 0, 255));
listItem->setValueColor(tsl::Color(255, 165, 0, 255));
break;
case 2:
listItem->setTextColor(tsl::Color(255, 0, 0, 255));
listItem->setValueColor(tsl::Color(255, 0, 0, 255));
break;
}
listItem->setClickListener([this, hz](u64 keys)
{
if ((keys & HidNpadButton_A) == HidNpadButton_A && this->listener) {
if (this->listener(hz)) {
tsl::goBack();
}
return true;
}
return false;
});
return listItem;
}
void FreqChoiceGui::listUI()
{
sysclkIpcGetConfigValues(this->configList);
// Add CategoryHeader based on module
std::string moduleName = sysclkFormatModule(this->module, false);
this->listElement->addItem(new tsl::elm::CategoryHeader(moduleName));
this->listElement->addItem(this->createFreqListItem(0, this->selectedHz == 0, false));
std::uint32_t hz;
for (std::uint32_t i = 0; i < this->hzCount; i++)
{
hz = this->hzList[i];
uint32_t mhz = hz / 1000000;
// Skip 204 MHz exactly
if(checkMax && IsMariko()) {
if (this->configList->values[HocClkConfigValue_MarikoMaxCpuClock] < mhz && moduleName == "cpu") {
continue;
}
if (this->configList->values[HocClkConfigValue_MarikoMaxGpuClock] < mhz && moduleName == "gpu") {
continue;
}
if (this->configList->values[HocClkConfigValue_MarikoMaxMemClock] < mhz && moduleName == "mem") {
continue;
}
} else if (checkMax && IsErista()) {
if (this->configList->values[HocClkConfigValue_EristaMaxCpuClock] < mhz && moduleName == "cpu") {
continue;
}
if (this->configList->values[HocClkConfigValue_EristaMaxGpuClock] < mhz && moduleName == "gpu") {
continue;
}
if (this->configList->values[HocClkConfigValue_EristaMaxMemClock] < mhz && moduleName == "mem") {
continue;
}
}
if (moduleName == "mem" && mhz <= 600)
{
continue;
}
uint32_t unsafe_cpu;
uint32_t unsafe_gpu;
uint32_t danger_cpu;
uint32_t danger_gpu;
if (IsMariko())
{
unsafe_cpu = 1964;
unsafe_gpu = 1076;
danger_cpu = 2398;
danger_gpu = 1306;
}
else
{
unsafe_cpu = 1786;
unsafe_gpu = 922;
danger_cpu = 2092;
danger_gpu = 999;
}
tsl::elm::ListItem* FreqChoiceGui::createFreqListItem(std::uint32_t hz, bool selected, int safety)
{
std::string text = formatListFreqHz(hz);
if (selected)
text += " \uE14B";
if (moduleName == "cpu") {
if (mhz >= danger_cpu) {
this->listElement->addItem(this->createFreqListItem(hz, mhz == this->selectedHz / 1000000, 2));
continue;
}
if (mhz >= unsafe_cpu) {
this->listElement->addItem(this->createFreqListItem(hz, mhz == this->selectedHz / 1000000, 1));
continue;
}
if (mhz <= unsafe_cpu) {
this->listElement->addItem(this->createFreqListItem(hz, mhz == this->selectedHz / 1000000, 0));
// NEW: Right-side label
std::string rightText = "";
auto it = labels.find(hz);
if (it != labels.end())
rightText = it->second;
tsl::elm::ListItem* listItem =
new tsl::elm::ListItem(text, rightText, false);
switch (safety)
{
case 0:
listItem->setTextColor(tsl::Color(255, 255, 255, 255));
listItem->setValueColor(tsl::Color(255, 255, 255, 255));
break;
case 1:
listItem->setTextColor(tsl::Color(255, 165, 0, 255));
listItem->setValueColor(tsl::Color(255, 165, 0, 255));
break;
case 2:
listItem->setTextColor(tsl::Color(255, 0, 0, 255));
listItem->setValueColor(tsl::Color(255, 0, 0, 255));
break;
}
// Make annotation grey
if (!rightText.empty())
listItem->setValueColor(tsl::Color(180, 180, 180, 255));
listItem->setClickListener([this, hz](u64 keys)
{
if ((keys & HidNpadButton_A) == HidNpadButton_A && this->listener) {
if (this->listener(hz)) {
tsl::goBack();
}
return true;
}
return false;
});
return listItem;
}
void FreqChoiceGui::listUI()
{
sysclkIpcGetConfigValues(this->configList);
// Header based on CPU/GPU/MEM module
std::string moduleName = sysclkFormatModule(this->module, false);
this->listElement->addItem(new tsl::elm::CategoryHeader(moduleName));
// Default option
this->listElement->addItem(
this->createFreqListItem(0, this->selectedHz == 0, 0));
for (std::uint32_t i = 0; i < this->hzCount; i++)
{
std::uint32_t hz = this->hzList[i];
uint32_t mhz = hz / 1000000;
if (checkMax && IsMariko()) {
if (moduleName == "cpu" &&
this->configList->values[HocClkConfigValue_MarikoMaxCpuClock] < mhz)
continue;
}
} else if (moduleName == "gpu") {
if (mhz >= danger_gpu) {
this->listElement->addItem(this->createFreqListItem(hz, mhz == this->selectedHz / 1000000, 2));
continue;
}
if (mhz >= unsafe_gpu) {
this->listElement->addItem(this->createFreqListItem(hz, mhz == this->selectedHz / 1000000, 1));
continue;
}
if (mhz <= unsafe_gpu) {
this->listElement->addItem(this->createFreqListItem(hz, mhz == this->selectedHz / 1000000, 0));
if (moduleName == "gpu" &&
this->configList->values[HocClkConfigValue_MarikoMaxGpuClock] < mhz)
continue;
}
} else if (moduleName == "mem") {
this->listElement->addItem(this->createFreqListItem(hz, mhz == this->selectedHz / 1000000, 0));
if (moduleName == "mem" &&
this->configList->values[HocClkConfigValue_MarikoMaxMemClock] < mhz)
continue;
} else if (checkMax && IsErista()) {
if (moduleName == "cpu" &&
this->configList->values[HocClkConfigValue_EristaMaxCpuClock] < mhz)
continue;
if (moduleName == "gpu" &&
this->configList->values[HocClkConfigValue_EristaMaxGpuClock] < mhz)
continue;
if (moduleName == "mem" &&
this->configList->values[HocClkConfigValue_EristaMaxMemClock] < mhz)
continue;
}
if (moduleName == "mem" && mhz <= 600)
continue;
}
}
this->listElement->jumpToItem("", "");
}
uint32_t unsafe_cpu;
uint32_t unsafe_gpu;
uint32_t danger_cpu;
uint32_t danger_gpu;
if (IsMariko())
{
unsafe_cpu = 1964;
unsafe_gpu = 1076;
danger_cpu = 2398;
danger_gpu = 1306;
}
else
{
unsafe_cpu = 1786;
unsafe_gpu = 922;
danger_cpu = 2092;
danger_gpu = 999;
}
int safety = 0;
if (moduleName == "cpu") {
if (mhz >= danger_cpu)
safety = 2;
else if (mhz >= unsafe_cpu)
safety = 1;
else
safety = 0;
} else if (moduleName == "gpu") {
if (mhz >= danger_gpu)
safety = 2;
else if (mhz >= unsafe_gpu)
safety = 1;
else
safety = 0;
} else if (moduleName == "mem") {
safety = 0;
}
this->listElement->addItem(
this->createFreqListItem(
hz,
(mhz == this->selectedHz / 1000000),
safety
)
);
}
this->listElement->jumpToItem("", "");
}

View File

@@ -29,6 +29,7 @@
#include <list>
#include <functional>
#include <map>
#include "base_menu_gui.h"
using FreqChoiceListener = std::function<bool(std::uint32_t hz)>;
@@ -42,20 +43,24 @@ protected:
std::uint32_t selectedHz;
std::uint32_t* hzList;
std::uint32_t hzCount;
SysClkModule module; // added module
SysClkModule module;
FreqChoiceListener listener;
bool checkMax; // new member
bool checkMax;
std::map<uint32_t, std::string> labels;
tsl::elm::ListItem* createFreqListItem(std::uint32_t hz, bool selected, int safety);
public:
// Updated constructor with checkMaxValue
FreqChoiceGui(std::uint32_t selectedHz,
std::uint32_t* hzList,
std::uint32_t hzCount,
SysClkModule module,
FreqChoiceListener listener,
bool checkMax = true);
bool checkMax = true,
std::map<uint32_t, std::string> labels = {});
~FreqChoiceGui();
void listUI() override;
};
};

View File

@@ -59,7 +59,8 @@ void MiscGui::addConfigButton(SysClkConfigValue configVal,
const char* altName,
const ValueRange& range,
const std::string& categoryName,
const ValueThresholds* thresholds)
const ValueThresholds* thresholds,
const std::map<uint32_t, std::string>& labels)
{
const char* configName = altName ? altName : sysclkFormatConfigValue(configVal, true);
@@ -82,7 +83,7 @@ void MiscGui::addConfigButton(SysClkConfigValue configVal,
ValueThresholds thresholdsCopy = (thresholds ? *thresholds : ValueThresholds{});
listItem->setClickListener(
[this, configVal, range, categoryName, thresholdsCopy](u64 keys)
[this, configVal, range, categoryName, thresholdsCopy, labels](u64 keys)
{
if ((keys & HidNpadButton_A) == 0)
return false;
@@ -106,7 +107,8 @@ void MiscGui::addConfigButton(SysClkConfigValue configVal,
return true;
},
thresholdsCopy,
true
true,
labels // <── NEW
);
} else {
@@ -123,7 +125,10 @@ void MiscGui::addConfigButton(SysClkConfigValue configVal,
}
this->lastContextUpdate = armGetSystemTick();
return true;
}
},
ValueThresholds(),
false,
labels // <── NEW
);
}
@@ -135,7 +140,11 @@ void MiscGui::addConfigButton(SysClkConfigValue configVal,
this->configRanges[configVal] = range;
}
void MiscGui::addFreqButton(SysClkConfigValue configVal, const char* altName, SysClkModule module) {
void MiscGui::addFreqButton(SysClkConfigValue configVal,
const char* altName,
SysClkModule module,
const std::map<uint32_t, std::string>& labels)
{
const char* configName = altName ? altName : sysclkFormatConfigValue(configVal, true);
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(configName);
@@ -145,45 +154,48 @@ void MiscGui::addFreqButton(SysClkConfigValue configVal, const char* altName, Sy
snprintf(valueText, sizeof(valueText), "%lu MHz", currentMHz);
listItem->setValue(valueText);
listItem->setClickListener([this, configVal, module](u64 keys) {
if ((keys & HidNpadButton_A) == 0)
return false;
listItem->setClickListener(
[this, configVal, module, labels](u64 keys)
{
if ((keys & HidNpadButton_A) == 0)
return false;
std::uint32_t hzList[SYSCLK_FREQ_LIST_MAX];
std::uint32_t hzCount;
std::uint32_t hzList[SYSCLK_FREQ_LIST_MAX];
std::uint32_t hzCount;
Result rc = sysclkIpcGetFreqList(module, hzList, SYSCLK_FREQ_LIST_MAX, &hzCount);
if (R_FAILED(rc)) {
FatalGui::openWithResultCode("sysclkIpcGetFreqList", rc);
return false;
}
Result rc = sysclkIpcGetFreqList(module, hzList, SYSCLK_FREQ_LIST_MAX, &hzCount);
if (R_FAILED(rc)) {
FatalGui::openWithResultCode("sysclkIpcGetFreqList", rc);
return false;
}
std::uint32_t currentHz = this->configList->values[configVal] * 1'000'000;
std::uint32_t currentHz = this->configList->values[configVal] * 1'000'000;
tsl::changeTo<FreqChoiceGui>(
currentHz,
hzList,
hzCount,
module,
[this, configVal](std::uint32_t hz) {
tsl::changeTo<FreqChoiceGui>(
currentHz,
hzList,
hzCount,
module,
[this, configVal](std::uint32_t hz)
{
uint64_t mhz = hz / 1'000'000;
this->configList->values[configVal] = mhz;
uint64_t mhz = hz / 1'000'000;
this->configList->values[configVal] = mhz;
Result rc = sysclkIpcSetConfigValues(this->configList);
if (R_FAILED(rc)) {
FatalGui::openWithResultCode("sysclkIpcSetConfigValues", rc);
return false;
}
Result rc = sysclkIpcSetConfigValues(this->configList);
if (R_FAILED(rc)) {
FatalGui::openWithResultCode("sysclkIpcSetConfigValues", rc);
return false;
}
this->lastContextUpdate = armGetSystemTick();
return true;
},
false,
labels
);
this->lastContextUpdate = armGetSystemTick();
return true;
},
false
);
return true;
});
return true;
});
this->listElement->addItem(listItem);
this->configButtons[configVal] = listItem;
@@ -209,14 +221,20 @@ void MiscGui::listUI()
this->listElement->addItem(new tsl::elm::CategoryHeader("Experimental"));
addConfigToggle(HocClkConfigValue_ThermalThrottle, nullptr);
addConfigToggle(HocClkConfigValue_HandheldTDP, nullptr);
std::map<uint32_t, std::string> labels_pwr_r = {
{8600, "Official Rating"}
};
std::map<uint32_t, std::string> labels_pwr_l = {
{6400, "Official Rating"}
};
ValueThresholds tdpThresholds(8600, 9500);
addConfigButton(
HocClkConfigValue_HandheldTDPLimit,
"TDP Threshold",
ValueRange(5000, 10000, 100, "mW", 1),
"Power",
&tdpThresholds
&tdpThresholds,
labels_pwr_r
);
ValueThresholds tdpThresholdsLite(6400, 7500);
@@ -225,7 +243,8 @@ void MiscGui::listUI()
"Lite TDP Threshold",
ValueRange(4000, 8000, 100, "mW", 1),
"Power",
&tdpThresholdsLite
&tdpThresholdsLite,
labels_pwr_r
);
ValueThresholds throttleThresholds(70, 80);
@@ -236,16 +255,113 @@ void MiscGui::listUI()
"Temp",
&throttleThresholds
);
this->listElement->addItem(new tsl::elm::CategoryHeader("Max Clocks"));
std::map<uint32_t, std::string> cpu_freq_label_m = {
{612000000, "Sleep Mode"},
{1020000000, "Stock"},
{1224000000, "Dev OC"},
{1785000000, "Boost Mode"},
{1963000000, "Safe Max"},
{2397000000, "Unsafe Max"},
{2805000000, "Aboslute Max"},
};
std::map<uint32_t, std::string> cpu_freq_label_e = {
{612000000, "Sleep Mode"},
{1020000000, "Stock"},
{1224000000, "Dev OC"},
{1785000000, "Boost Mode & Safe Max"},
{2091000000, "Unsafe Max"},
{2295000000, "Aboslute Max"},
};
std::map<uint32_t, std::string> gpu_freq_label_e = {
{76800000, "Boost Mode"},
{307200000, "Handheld"},
{384000000, "Handheld"},
{460800000, "Handheld Safe Max"},
{768000000, "Docked"},
{844000000, "Safe Max"},
{998400000, "Unsafe Max"},
{1075200000, "Aboslute Max"},
};
std::map<uint32_t, std::string> gpu_freq_label_m = {
{76800000, "Boost Mode"},
{307200000, "Handheld"},
{384000000, "Handheld"},
{460800000, "Handheld"},
{614400000, "Handheld Safe Max"},
{768000000, "Docked"},
{1152200000, "Safe Max"},
{1305600000, "Unsafe Max"},
{1536000000, "Aboslute Max"},
};
std::map<uint32_t, std::string> emc_freq_label_e = {
{133120000, "Handheld"},
{160000000, "Docked & Safe Max"},
{213100000, "JEDEC Max"},
{236000000, "Absolute Max"},
};
std::map<uint32_t, std::string> emc_freq_label_m = {
{133120000, "Handheld"},
{160000000, "Docked"},
{186600000, "Safe Max (3733MT/s)"},
{213300000, "Safe Max (4266MT/s)"},
{320000000, "Absolute Max"},
};
if(IsMariko()) {
addFreqButton(HocClkConfigValue_MarikoMaxCpuClock, nullptr, SysClkModule_CPU);
addFreqButton(HocClkConfigValue_MarikoMaxGpuClock, nullptr, SysClkModule_GPU);
addFreqButton(HocClkConfigValue_MarikoMaxMemClock, nullptr, SysClkModule_MEM);
addFreqButton(HocClkConfigValue_MarikoMaxCpuClock, nullptr, SysClkModule_CPU, cpu_freq_label_m);
addFreqButton(HocClkConfigValue_MarikoMaxGpuClock, nullptr, SysClkModule_GPU, gpu_freq_label_m);
addFreqButton(HocClkConfigValue_MarikoMaxMemClock, nullptr, SysClkModule_MEM, emc_freq_label_m);
} else {
addFreqButton(HocClkConfigValue_EristaMaxCpuClock, nullptr, SysClkModule_CPU);
addFreqButton(HocClkConfigValue_EristaMaxGpuClock, nullptr, SysClkModule_GPU);
addFreqButton(HocClkConfigValue_EristaMaxMemClock, nullptr, SysClkModule_MEM);
addFreqButton(HocClkConfigValue_EristaMaxCpuClock, nullptr, SysClkModule_CPU, cpu_freq_label_e);
addFreqButton(HocClkConfigValue_EristaMaxGpuClock, nullptr, SysClkModule_GPU, gpu_freq_label_e);
addFreqButton(HocClkConfigValue_EristaMaxMemClock, nullptr, SysClkModule_MEM, emc_freq_label_e);
}
this->listElement->addItem(new tsl::elm::CategoryHeader("KIP"));
std::map<uint32_t, std::string> emc_voltage_label = {
{1100000, "Default (Mariko)"},
{1125000, "Default (Erista)"},
{1175000, "Rating"},
{1212500, "Safe Max"},
};
ValueThresholds vdd2Thresholds(1212500, 1250000);
addConfigButton(
KipConfigValue_commonEmcMemVolt,
"EMC VDD2 Voltage",
ValueRange(912500, 1350000, 12500, "mV", 1000),
"Voltage",
&vdd2Thresholds
);
tsl::elm::ListItem* saveBtn = new tsl::elm::ListItem("Save KIP Settings");
saveBtn->setClickListener([](u64 keys) {
if (keys & HidNpadButton_A) {
Result rc = hocClkIpcSetKipData();
if (R_FAILED(rc)) {
FatalGui::openWithResultCode("hocClkIpcSetKipData", rc);
return false;
}
return true;
}
return false;
});
this->listElement->addItem(saveBtn);
}
void MiscGui::refresh() {

View File

@@ -27,9 +27,12 @@ protected:
const char* altName,
const ValueRange& range,
const std::string& categoryName,
const ValueThresholds* thresholds = nullptr);
void addFreqButton(SysClkConfigValue configVal, const char* altName, SysClkModule module);
void updateConfigToggles();
const ValueThresholds* thresholds,
const std::map<uint32_t, std::string>& labels = {});
void addFreqButton(SysClkConfigValue configVal,
const char* altName,
SysClkModule module,
const std::map<uint32_t, std::string>& labels = {}); void updateConfigToggles();
tsl::elm::ToggleListItem* enabledToggle;
u8 frameCounter = 60;

View File

@@ -1,145 +1,121 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "value_choice_gui.h"
#include "../format.h"
#include "fatal_gui.h"
#include <sstream>
#include <iomanip>
ValueChoiceGui::ValueChoiceGui(std::uint32_t selectedValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds,
bool enableThresholds,
std::map<std::uint32_t, std::string> labels)
: selectedValue(selectedValue),
range(range),
categoryName(categoryName),
listener(listener),
thresholds(thresholds),
enableThresholds(enableThresholds),
labels(labels)
{
}
#include "value_choice_gui.h"
#include "../format.h"
#include "fatal_gui.h"
#include <sstream>
#include <iomanip>
ValueChoiceGui::~ValueChoiceGui()
{
}
ValueChoiceGui::ValueChoiceGui(std::uint32_t selectedValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds,
bool enableThresholds)
: selectedValue(selectedValue),
range(range),
categoryName(categoryName),
listener(listener),
thresholds(thresholds),
enableThresholds(enableThresholds)
{
}
std::string ValueChoiceGui::formatValue(std::uint32_t value)
{
std::ostringstream oss;
ValueChoiceGui::~ValueChoiceGui()
{
}
if (value == 0) {
return VALUE_DEFAULT_TEXT;
}
std::string ValueChoiceGui::formatValue(std::uint32_t value)
{
std::ostringstream oss;
double displayValue = static_cast<double>(value) / static_cast<double>(range.divisor);
if (value == 0) {
oss << std::fixed << std::setprecision(range.decimalPlaces) << displayValue;
return VALUE_DEFAULT_TEXT;
}
if (!range.suffix.empty()) {
oss << " " << range.suffix;
}
return oss.str();
}
std::uint32_t displayValue = value / range.divisor;
int ValueChoiceGui::getSafetyLevel(std::uint32_t value)
{
if (value > thresholds.danger) {
return 2;
}
if (value > thresholds.warning) {
return 1;
}
return 0;
}
oss << displayValue;
if (!range.suffix.empty()) {
oss << " " << range.suffix;
}
return oss.str();
}
tsl::elm::ListItem* ValueChoiceGui::createValueListItem(std::uint32_t value, bool selected, int safety)
{
std::string text = formatValue(value);
if (selected) {
text += " \uE14B";
}
int ValueChoiceGui::getSafetyLevel(std::uint32_t value)
{
if (!enableThresholds) {
return 0;
}
std::string rightText = "";
auto it = labels.find(value);
if (it != labels.end()) {
rightText = it->second;
}
std::uint32_t scaledValue = value / range.divisor;
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(text, rightText, false);
if (scaledValue > thresholds.danger) {
return 2;
}
if (scaledValue > thresholds.warning) {
return 1;
}
return 0;
}
switch (safety)
{
case 0:
listItem->setTextColor(tsl::Color(255, 255, 255, 255));
listItem->setValueColor(tsl::Color(255, 255, 255, 255));
break;
case 1:
listItem->setTextColor(tsl::Color(255, 165, 0, 255));
listItem->setValueColor(tsl::Color(255, 165, 0, 255));
break;
case 2:
listItem->setTextColor(tsl::Color(255, 0, 0, 255));
listItem->setValueColor(tsl::Color(255, 0, 0, 255));
break;
}
tsl::elm::ListItem* ValueChoiceGui::createValueListItem(std::uint32_t value, bool selected, int safety)
{
std::string text = formatValue(value);
if (selected) {
text += " \uE14B";
}
if (!rightText.empty())
listItem->setValueColor(tsl::Color(180, 180, 180, 255));
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(text, "", false);
listItem->setClickListener([this, value](u64 keys)
{
if ((keys & HidNpadButton_A) == HidNpadButton_A && this->listener) {
if (this->listener(value)) {
tsl::goBack();
}
return true;
}
return false;
});
switch (safety)
{
case 0:
listItem->setTextColor(tsl::Color(255, 255, 255, 255));
listItem->setValueColor(tsl::Color(255, 255, 255, 255));
break;
case 1:
listItem->setTextColor(tsl::Color(255, 165, 0, 255));
listItem->setValueColor(tsl::Color(255, 165, 0, 255));
break;
case 2:
listItem->setTextColor(tsl::Color(255, 0, 0, 255));
listItem->setValueColor(tsl::Color(255, 0, 0, 255));
break;
}
return listItem;
}
listItem->setClickListener([this, value](u64 keys)
{
if ((keys & HidNpadButton_A) == HidNpadButton_A && this->listener) {
void ValueChoiceGui::listUI()
{
if (!categoryName.empty()) {
this->listElement->addItem(new tsl::elm::CategoryHeader(categoryName));
}
if (this->listener(value)) {
tsl::goBack();
}
return true;
}
return false;
});
this->listElement->addItem(this->createValueListItem(0, this->selectedValue == 0, 0));
return listItem;
}
for (std::uint32_t value = range.min; value <= range.max; value += range.step)
{
int safety = getSafetyLevel(value);
bool selected = (value == this->selectedValue);
this->listElement->addItem(this->createValueListItem(value, selected, safety));
}
void ValueChoiceGui::listUI()
{
if (!categoryName.empty()) {
this->listElement->addItem(new tsl::elm::CategoryHeader(categoryName));
}
this->listElement->addItem(this->createValueListItem(0, this->selectedValue == 0, 0));
for (std::uint32_t value = range.min; value <= range.max; value += range.step)
{
int safety = getSafetyLevel(value);
bool selected = (value == this->selectedValue);
this->listElement->addItem(this->createValueListItem(value, selected, safety));
}
this->listElement->jumpToItem("", "\uE14B");
}
this->listElement->jumpToItem("", "\uE14B");
}

View File

@@ -25,62 +25,69 @@
*/
#pragma once
#pragma once
#include <list>
#include <functional>
#include <string>
#include "base_menu_gui.h"
using ValueChoiceListener = std::function<bool(std::uint32_t value)>;
#define VALUE_DEFAULT_TEXT "Default"
struct ValueRange {
std::uint32_t min;
std::uint32_t max;
std::uint32_t step;
std::string suffix;
std::uint32_t divisor; // Divide input values by this for display
// Default constructor
ValueRange() : min(0), max(0), step(1), suffix(""), divisor(1) {}
ValueRange(std::uint32_t min, std::uint32_t max, std::uint32_t step,
const std::string& suffix = "", std::uint32_t divisor = 1)
: min(min), max(max), step(step), suffix(suffix), divisor(divisor) {}
};
struct ValueThresholds {
std::uint32_t warning; // Values >= this show orange
std::uint32_t danger; // Values >= this show red
ValueThresholds(std::uint32_t warning = 0, std::uint32_t danger = 0)
: warning(warning), danger(danger) {}
};
class ValueChoiceGui : public BaseMenuGui
{
protected:
std::uint32_t selectedValue;
ValueRange range;
std::string categoryName;
ValueChoiceListener listener;
ValueThresholds thresholds;
bool enableThresholds;
tsl::elm::ListItem* createValueListItem(std::uint32_t value, bool selected, int safety);
std::string formatValue(std::uint32_t value);
int getSafetyLevel(std::uint32_t value);
public:
ValueChoiceGui(std::uint32_t selectedValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds = ValueThresholds(),
bool enableThresholds = false);
~ValueChoiceGui();
void listUI() override;
};
#include <list>
#include <functional>
#include <string>
#include <map>
#include "base_menu_gui.h"
using ValueChoiceListener = std::function<bool(std::uint32_t value)>;
#define VALUE_DEFAULT_TEXT "Default"
struct ValueRange {
std::uint32_t min;
std::uint32_t max;
std::uint32_t step;
std::string suffix;
std::uint32_t divisor;
int decimalPlaces;
ValueRange()
: min(0), max(0), step(1), suffix(""), divisor(1), decimalPlaces(0) {}
ValueRange(std::uint32_t min, std::uint32_t max, std::uint32_t step,
const std::string& suffix = "", std::uint32_t divisor = 1, int decimalPlaces = 0)
: min(min), max(max), step(step), suffix(suffix),
divisor(divisor), decimalPlaces(decimalPlaces) {}
};
struct ValueThresholds {
std::uint32_t warning;
std::uint32_t danger;
ValueThresholds(std::uint32_t warning = 0, std::uint32_t danger = 0)
: warning(warning), danger(danger) {}
};
class ValueChoiceGui : public BaseMenuGui
{
protected:
std::uint32_t selectedValue;
ValueRange range;
std::string categoryName;
ValueChoiceListener listener;
ValueThresholds thresholds;
bool enableThresholds;
// NEW — map of value → right-side text (like version numbers)
std::map<std::uint32_t, std::string> labels;
tsl::elm::ListItem* createValueListItem(std::uint32_t value, bool selected, int safety);
std::string formatValue(std::uint32_t value);
int getSafetyLevel(std::uint32_t value);
public:
ValueChoiceGui(std::uint32_t selectedValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds = ValueThresholds(),
bool enableThresholds = false,
std::map<std::uint32_t, std::string> labels = {});
~ValueChoiceGui();
void listUI() override;
};