Compare commits

..

5 Commits

Author SHA1 Message Date
Niklas Friesen
30e89fee71 CI: GitHub Actions build with .nro artifact (GHES-compatible)
All checks were successful
Build / Build (push) Successful in 30s
- devkitpro/devkita64 container; apt install nodejs for actions/checkout.
- upload-artifact@v3 (v4+ unsupported on GitHub Enterprise Server).
- Push/PR to main and master; workflow_dispatch.
- Makefile: TARGET_VERSION 1.0.0.

Made-with: Cursor
2026-03-30 20:17:50 +02:00
Niklas Friesen
b5296d6686 i18n: English/German from Switch system language
- Add I18n module: German when system language is DE, else English (ENUS/ENGB
  or any other language).
- Translate UI strings (tabs, settings, about, file browser, notifications,
  OC row titles, toggles Ein/Aus).
- Keep toggle/frequency/voltage list rows without subtitle descriptions
  like before.

Uses setGetSystemLanguage + setMakeLanguage; call I18n::init before
Application::init.

Made-with: Cursor
2026-03-30 20:09:38 +02:00
Niklas Friesen
b5ba2d71df UI: header icon + title; always show OC controls; About Logo cleanup
- Use gui_icon.png next to APP_TITLE in the applet header (replaces text-only header Logo).
- Simplify Logo to About tab only; drop unused HEADER style.
- Show frequency/voltage/toggle controls even when INI keys are absent (defaults until edited).
- Add resources/gui_icon.png to RomFS.

Made-with: Cursor
2026-03-30 19:57:12 +02:00
Niklas Friesen
a61fa67dca Makefile: use cp -rf for ROMFS merge (macOS/BSD compatibility)
BSD cp does not support GNU's -u flag, which broke the ROMFS target on macOS.

Made-with: Cursor
2026-03-30 19:32:53 +02:00
Niklas Friesen
90b3902311 Added gitignore 2026-03-30 19:32:39 +02:00
15 changed files with 437 additions and 107 deletions

41
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Build
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch:
jobs:
switch:
name: Build
runs-on: ubuntu-latest
container:
# Switch homebrew uses devkitA64 + libnx (not devkitARM)
image: devkitpro/devkita64:latest
permissions:
contents: read
steps:
# actions/checkout needs Node; devkitPro images omit it by default
- name: Install dependencies
run: apt-get update && apt-get install -y nodejs
- name: Checkout
uses: actions/checkout@v4
with:
clean: true
- name: Compile (Switch .nro)
env:
DEVKITPRO: /opt/devkitpro
run: make -j$(nproc)
# v3: GHES does not support upload-artifact v4+ (artifact v2 backend)
- name: Upload .nro artifact
uses: actions/upload-artifact@v3
with:
name: swr-ini-tool
path: swr-ini-tool.nro
if-no-files-found: error

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
# devkitPro / Switch build outputs
build.nx/
*.elf
*.nro
*.nacp
*.nso
*.npdm
*.nsp
*.lst
*.map
# editor / OS
.vscode/
.idea/
*~
*.bak
.DS_Store

View File

@@ -33,7 +33,7 @@ APP_RESOURCES := romfs:/
#---------------------------------------------------------------------------------
# version control constants
#---------------------------------------------------------------------------------
TARGET_VERSION := 0.1.0
TARGET_VERSION := 1.0.0
APP_VERSION := $(TARGET_VERSION)
#---------------------------------------------------------------------------------
@@ -159,8 +159,8 @@ all: $(BUILD)
$(ROMFS):
@[ -d $@ ] || mkdir -p $@
@echo Merging ROMFS...
@cp -ruf $(CURDIR)/$(BOREALIS_PATH)/resources/. $(CURDIR)/$(ROMFS)/borealis/
@cp -ruf $(CURDIR)/$(RESOURCES)/. $(CURDIR)/$(ROMFS)/
@cp -rf $(CURDIR)/$(BOREALIS_PATH)/resources/. $(CURDIR)/$(ROMFS)/borealis/
@cp -rf $(CURDIR)/$(RESOURCES)/. $(CURDIR)/$(ROMFS)/
$(BUILD): $(ROMFS)
@[ -d $@ ] || mkdir -p $@

BIN
resources/gui_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -4,18 +4,18 @@
*/
#include "about_tab.h"
#include "i18n.h"
#include "logo.h"
AboutTab::AboutTab()
{
// Logo
this->addView(new Logo(LogoStyle::ABOUT));
this->addView(new Logo());
// Subtitle
brls::Label *subTitle = new brls::Label(
brls::LabelStyle::REGULAR,
"Switchroot INI Configuration Editor\n"
"Edit your Linux, Android and Lakka OC settings without a PC!",
I18n::aboutSubtitle(),
true
);
subTitle->setHorizontalAlign(NVG_ALIGN_CENTER);
@@ -24,22 +24,17 @@ AboutTab::AboutTab()
// Copyright
brls::Label *copyright = new brls::Label(
brls::LabelStyle::DESCRIPTION,
"Licensed under GPL-3.0\n"
"Powered by Borealis UI framework\n"
"Based on the work of Switchroot\n"
"\u00A9 2026 NiklasCFW",
I18n::aboutCopyright(),
true
);
copyright->setHorizontalAlign(NVG_ALIGN_CENTER);
this->addView(copyright);
// Links
this->addView(new brls::Header("Links and Resources"));
this->addView(new brls::Header(I18n::headerLinksAndResources()));
brls::Label *links = new brls::Label(
brls::LabelStyle::SMALL,
"\uE016 NiklasCFW Docs for setup guides and documentation\n"
"\uE016 NiklasCFW's Discord server for support and community\n"
"\uE016 Source code available on the OmniNX GitHub",
I18n::aboutLinks(),
true
);
this->addView(links);

View File

@@ -4,6 +4,7 @@
*/
#include "file_browser.h"
#include "i18n.h"
#include <dirent.h>
#include <sys/stat.h>
@@ -78,7 +79,7 @@ std::vector<FileBrowser::DirEntry> FileBrowser::listDirectory(const std::string&
void FileBrowser::navigate(const std::string& dir)
{
this->currentDir = dir;
this->setTitle("Select INI \u2014 " + dir);
this->setTitle(std::string(I18n::fileBrowserTitlePrefix()) + dir);
// Create a fresh list (setContentView frees the old one)
this->list = new brls::List();
@@ -89,7 +90,7 @@ void FileBrowser::navigate(const std::string& dir)
if (entries.empty())
{
brls::ListItem* emptyItem = new brls::ListItem("No .ini files found in " + dir);
brls::ListItem* emptyItem = new brls::ListItem(I18n::fileBrowserNoIni(dir));
this->list->addView(emptyItem);
return;
}

264
src/i18n.cpp Normal file
View File

@@ -0,0 +1,264 @@
/*
SWR INI Tool - Switchroot INI Configuration Editor
Copyright (C) 2026 Switchroot
*/
#include "i18n.h"
#ifdef __SWITCH__
#include <switch.h>
#endif
namespace
{
bool gGerman = false;
#ifdef __SWITCH__
void detectSystemLanguage()
{
gGerman = false;
Result rc = setInitialize();
if (!R_SUCCEEDED(rc))
return;
u64 code = 0;
SetLanguage lang = SetLanguage_ENUS;
if (R_SUCCEEDED(setGetSystemLanguage(&code)) && R_SUCCEEDED(setMakeLanguage(code, &lang)))
{
if (lang == SetLanguage_DE)
gGerman = true;
}
setExit();
}
#endif
struct Triple
{
const char* key;
const char* en;
const char* de;
};
static const Triple OC_BOOL_LABEL[] = {
{"oc", "Overclocking", "Übertaktung"},
{"dvfsb", "CPU DVFS Boost", "CPU-DVFS-Boost"},
{"gpu_dvfsc", "GPU DVFS Scaling", "GPU-DVFS-Skalierung"},
{"usb3force", "Force USB 3.0", "USB 3.0 erzwingen"},
{"ddr200_enable", "DDR200 Enable", "DDR200 aktivieren"},
};
static const Triple OC_FREQ_LABEL[] = {
{"max_cpu_freq", "Max CPU Frequency", "Max. CPU-Taktfrequenz"},
{"max_gpu_freq", "Max GPU Frequency", "Max. GPU-Taktfrequenz"},
{"ram_oc", "RAM Frequency", "RAM-Taktfrequenz"},
};
static const Triple OC_VOLT_LABEL[] = {
{"ram_oc_vdd2", "RAM VDD2 Voltage", "RAM VDD2-Spannung"},
{"ram_oc_vddq", "RAM VDDQ Voltage", "RAM VDDQ-Spannung"},
};
const char* lookup(const Triple* table, size_t n, const std::string& key, const char* fallback)
{
for (size_t i = 0; i < n; i++)
{
if (key == table[i].key)
return gGerman ? table[i].de : table[i].en;
}
return fallback;
}
} // namespace
namespace I18n
{
void init()
{
#ifdef __SWITCH__
detectSystemLanguage();
#else
gGerman = false;
#endif
}
bool isGerman()
{
return gGerman;
}
const char* appTitle()
{
return gGerman ? "SWR INI-Tool" : "SWR INI Tool";
}
const char* tabSettings()
{
return gGerman ? "Einstellungen" : "Settings";
}
const char* tabAbout()
{
return gGerman ? "Über" : "About";
}
const char* wordOn()
{
return gGerman ? "Ein" : "ON";
}
const char* wordOff()
{
return gGerman ? "Aus" : "OFF";
}
std::string sectionEditLine(const std::string& section, const std::string& path)
{
if (gGerman)
return "Abschnitt [" + section + "] aus " + path;
return "Editing section [" + section + "] from " + path;
}
const char* headerToggleOptions()
{
return gGerman ? "Schalter" : "Toggle Options";
}
const char* headerFrequencySettings()
{
return gGerman ? "Frequenz" : "Frequency Settings";
}
const char* headerVoltageSettings()
{
return gGerman ? "Spannung" : "Voltage Settings";
}
const char* headerOther()
{
return gGerman ? "Sonstiges" : "Other";
}
const char* headerIniFilePaths()
{
return gGerman ? "INI-Dateipfade" : "INI File Paths";
}
const char* headerLinksAndResources()
{
return gGerman ? "Links und Infos" : "Links and Resources";
}
const char* errorListItemTitleIni()
{
return gGerman ? "\uE150 INI-Problem" : "\uE150 INI file not found";
}
std::string iniLoadErrorBody(const std::string& path)
{
if (gGerman)
return "Die Datei konnte nicht geladen werden:\n" + path + "\n\n"
"Prüfen Sie den Pfad in den Einstellungen.";
return "Could not load " + path + "\n\n"
"Make sure the INI file exists at the expected path.\n"
"You can configure paths in the Settings tab.";
}
std::string iniSectionErrorBody(const std::string& path, const std::string& osName)
{
if (gGerman)
return "Kein OS-Abschnitt in " + path + "\n\n"
"Die INI sollte z. B. einen Abschnitt ["
+ osName + " OC] enthalten.";
return "No OS section found in " + path + "\n\n"
"The INI file should contain a section like\n"
"[" + osName + " OC] with your configuration.";
}
const char* notifyConfigSaved()
{
return gGerman ? "\uE14B Konfiguration gespeichert" : "\uE14B Configuration saved";
}
const char* notifyConfigSaveError()
{
return gGerman ? "\uE150 Fehler beim Speichern!" : "\uE150 Error saving configuration!";
}
const char* notifyPathUpdated()
{
return gGerman ? "\uE14B Pfad aktualisiert" : "\uE14B Path updated";
}
const char* settingsIntro()
{
return gGerman ? "Wählen Sie, welche INI-Datei jeder Reiter liest und schreibt.\n"
"Änderungen gelten sofort."
: "Select which INI file each OS tab reads and writes.\n"
"Changes take effect immediately.";
}
const char* pathListDescriptionLakka()
{
return gGerman ? "Tippen, um eine INI-Datei zu wählen" : "Tap to browse for an INI file";
}
const char* fileBrowserTitlePrefix()
{
return gGerman ? "INI wählen \u2014 " : "Select INI \u2014 ";
}
std::string fileBrowserNoIni(const std::string& dir)
{
if (gGerman)
return "Keine .ini-Dateien in " + dir;
return "No .ini files found in " + dir;
}
const char* aboutSubtitle()
{
return gGerman ? "Switchroot INI-Konfiguration\n"
"Linux-, Android- und Lakka-OC ohne PC bearbeiten!"
: "Switchroot INI Configuration Editor\n"
"Edit your Linux, Android and Lakka OC settings without a PC!";
}
const char* aboutCopyright()
{
return gGerman ? "Lizenziert unter GPL-3.0\n"
"UI: Borealis\n"
"Basierend auf der Arbeit von Switchroot\n"
"\u00A9 2026 NiklasCFW"
: "Licensed under GPL-3.0\n"
"Powered by Borealis UI framework\n"
"Based on the work of Switchroot\n"
"\u00A9 2026 NiklasCFW";
}
const char* aboutLinks()
{
return gGerman ? "\uE016 NiklasCFW-Dokumentation: Anleitungen und Infos\n"
"\uE016 NiklasCFW-Discord: Fragen und Community\n"
"\uE016 Quellcode auf GitHub (OmniNX)"
: "\uE016 NiklasCFW Docs for setup guides and documentation\n"
"\uE016 NiklasCFW's Discord server for support and community\n"
"\uE016 Source code available on the OmniNX GitHub";
}
const char* ocBoolLabel(const std::string& key)
{
return lookup(OC_BOOL_LABEL, sizeof(OC_BOOL_LABEL) / sizeof(OC_BOOL_LABEL[0]), key, key.c_str());
}
const char* ocFreqLabel(const std::string& key)
{
return lookup(OC_FREQ_LABEL, sizeof(OC_FREQ_LABEL) / sizeof(OC_FREQ_LABEL[0]), key, key.c_str());
}
const char* ocVoltageLabel(const std::string& key)
{
return lookup(OC_VOLT_LABEL, sizeof(OC_VOLT_LABEL) / sizeof(OC_VOLT_LABEL[0]), key, key.c_str());
}
} // namespace I18n

57
src/i18n.h Normal file
View File

@@ -0,0 +1,57 @@
/*
SWR INI Tool - Switchroot INI Configuration Editor
Copyright (C) 2026 Switchroot
*/
#pragma once
#include <string>
namespace I18n
{
void init();
bool isGerman();
const char* appTitle();
const char* tabSettings();
const char* tabAbout();
const char* wordOn();
const char* wordOff();
std::string sectionEditLine(const std::string& section, const std::string& path);
const char* headerToggleOptions();
const char* headerFrequencySettings();
const char* headerVoltageSettings();
const char* headerOther();
const char* headerIniFilePaths();
const char* headerLinksAndResources();
const char* errorListItemTitleIni();
std::string iniLoadErrorBody(const std::string& path);
std::string iniSectionErrorBody(const std::string& path, const std::string& osName);
const char* notifyConfigSaved();
const char* notifyConfigSaveError();
const char* notifyPathUpdated();
const char* settingsIntro();
const char* pathListDescriptionLakka();
const char* fileBrowserTitlePrefix();
std::string fileBrowserNoIni(const std::string& dir);
const char* aboutSubtitle();
const char* aboutCopyright();
const char* aboutLinks();
const char* ocBoolLabel(const std::string& key);
const char* ocFreqLabel(const std::string& key);
const char* ocVoltageLabel(const std::string& key);
} // namespace I18n

View File

@@ -5,9 +5,9 @@
#include "logo.h"
Logo::Logo(LogoStyle style)
Logo::Logo()
{
this->logoLabel = new brls::Label(brls::LabelStyle::LIST_ITEM, "SWR", style == LogoStyle::ABOUT);
this->logoLabel = new brls::Label(brls::LabelStyle::LIST_ITEM, "SWR", true);
this->logoLabel->setParent(this);
int logoFont = brls::Application::findFont(LOGO_FONT_NAME);
@@ -16,35 +16,18 @@ Logo::Logo(LogoStyle style)
this->logoLabel->setFont(logoFont);
}
if (style == LogoStyle::ABOUT)
{
this->logoLabel->setFontSize(LOGO_ABOUT_FONT_SIZE);
this->logoLabel->setHorizontalAlign(NVG_ALIGN_CENTER);
}
if (style == LogoStyle::HEADER)
{
this->logoLabel->setFontSize(LOGO_HEADER_FONT_SIZE);
this->descLabel = new brls::Label(brls::LabelStyle::LIST_ITEM, "INI Tool");
this->descLabel->setParent(this);
this->descLabel->setFontSize(LOGO_DESC_FONT_SIZE);
}
}
Logo::~Logo()
{
delete this->logoLabel;
if (this->descLabel)
delete this->descLabel;
}
void Logo::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{
this->logoLabel->frame(ctx);
if (this->descLabel)
this->descLabel->frame(ctx);
}
void Logo::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
@@ -52,14 +35,4 @@ void Logo::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
this->logoLabel->setBoundaries(this->x, this->y + LOGO_OFFSET, this->width, this->height);
this->logoLabel->layout(vg, style, stash);
this->height = this->logoLabel->getHeight();
if (this->descLabel)
{
this->descLabel->layout(vg, style, stash);
this->descLabel->setBoundaries(
this->x + LOGO_HEADER_SPACING + this->logoLabel->getWidth(),
this->y + style->AppletFrame.titleOffset - 1,
this->descLabel->getWidth(),
height);
}
}

View File

@@ -9,30 +9,20 @@
#define APP_ASSET(p) APP_RESOURCES p
enum class LogoStyle
{
HEADER = 0,
ABOUT
};
#define LOGO_FONT_NAME "logo"
#define LOGO_FONT_PATH APP_ASSET("fira/FiraSans-Medium-rnx.ttf")
#define LOGO_HEADER_FONT_SIZE 45
#define LOGO_HEADER_SPACING 12
#define LOGO_ABOUT_FONT_SIZE 55
#define LOGO_DESC_FONT_SIZE 28
#define LOGO_OFFSET 2
class Logo : public brls::View
{
protected:
brls::Label* logoLabel = nullptr;
brls::Label* descLabel = nullptr;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
public:
Logo(LogoStyle style);
Logo();
virtual ~Logo();
};

View File

@@ -17,11 +17,14 @@
#include "main_frame.h"
#include "logo.h"
#include "app_config.h"
#include "i18n.h"
int main(int argc, char* argv[])
{
I18n::init();
// Init the app
if (!brls::Application::init("SWR INI Tool"))
if (!brls::Application::init(I18n::appTitle()))
{
brls::Logger::error("Unable to init Borealis application");
return EXIT_FAILURE;

View File

@@ -8,8 +8,9 @@
#include "os_config_tab.h"
#include "settings_tab.h"
#include "about_tab.h"
#include "logo.h"
#include "app_config.h"
#include "i18n.h"
#include "logo.h"
OsConfigTab* MainFrame::osTabs[(int)OsTarget::COUNT] = {};
@@ -17,8 +18,10 @@ MainFrame::MainFrame() : TabFrame()
{
AppConfig& cfg = AppConfig::get();
// Header logo
this->setIcon(new Logo(LogoStyle::HEADER));
this->setTitle(I18n::appTitle());
brls::Image* headerIcon = new brls::Image(APP_ASSET("gui_icon.png"));
headerIcon->setScaleType(brls::ImageScaleType::FIT);
this->setIcon(headerIcon);
// OS configuration tabs — paths from config
osTabs[(int)OsTarget::ANDROID] = new OsConfigTab("Android", cfg.getPath(OsTarget::ANDROID));
@@ -28,11 +31,11 @@ MainFrame::MainFrame() : TabFrame()
this->addTab("Android", osTabs[(int)OsTarget::ANDROID]);
this->addTab("Linux", osTabs[(int)OsTarget::LINUX]);
this->addTab("Lakka", osTabs[(int)OsTarget::LAKKA]);
this->addTab("Settings", new SettingsTab());
this->addTab(I18n::tabSettings(), new SettingsTab());
this->addSeparator();
this->addTab("About", new AboutTab());
this->addTab(I18n::tabAbout(), new AboutTab());
}
MainFrame::~MainFrame()

View File

@@ -4,6 +4,7 @@
*/
#include "os_config_tab.h"
#include "i18n.h"
#include "oc_defs.h"
#include <algorithm>
@@ -13,18 +14,14 @@ OsConfigTab::OsConfigTab(const std::string& osName, const std::string& iniPath)
{
if (!this->ini.load(iniPath))
{
buildErrorUI("Could not load " + iniPath + "\n\n"
"Make sure the INI file exists at the expected path.\n"
"You can configure paths in the Settings tab.");
buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniLoadErrorBody(iniPath));
return;
}
this->osSection = this->ini.findOsSection();
if (this->osSection.empty())
{
buildErrorUI("No OS section found in " + iniPath + "\n\n"
"The INI file should contain a section like\n"
"[" + osName + " OC] with your configuration.");
buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniSectionErrorBody(iniPath, osName));
return;
}
@@ -41,30 +38,26 @@ void OsConfigTab::reload(const std::string& newIniPath)
if (!this->ini.load(iniPath))
{
buildErrorUI("Could not load " + iniPath + "\n\n"
"Make sure the INI file exists at the expected path.\n"
"You can configure paths in the Settings tab.");
buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniLoadErrorBody(iniPath));
return;
}
this->osSection = this->ini.findOsSection();
if (this->osSection.empty())
{
buildErrorUI("No OS section found in " + iniPath + "\n\n"
"The INI file should contain a section like\n"
"[" + osName + " OC] with your configuration.");
buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniSectionErrorBody(iniPath, osName));
return;
}
buildUI();
}
void OsConfigTab::buildErrorUI(const std::string& message)
void OsConfigTab::buildErrorUI(const std::string& title, const std::string& message)
{
this->setSpacing(15);
// Use a ListItem (focusable) so borealis doesn't crash on controller nav
brls::ListItem *errorItem = new brls::ListItem("\uE150 INI file not found", message);
brls::ListItem *errorItem = new brls::ListItem(title, message);
this->addView(errorItem);
}
@@ -76,51 +69,44 @@ void OsConfigTab::buildUI()
// Section name info
brls::Label *sectionInfo = new brls::Label(
brls::LabelStyle::DESCRIPTION,
"Editing section [" + this->osSection + "] from " + this->iniPath,
I18n::sectionEditLine(this->osSection, this->iniPath),
true
);
this->addView(sectionInfo);
// ── Toggle Options ──
this->addView(new brls::Header("Toggle Options"));
this->addView(new brls::Header(I18n::headerToggleOptions()));
for (const auto& def : OC_BOOL_KEYS)
{
if (this->ini.hasKey(this->osSection, def.key))
{
bool val = this->ini.getBool(this->osSection, def.key);
addBooleanToggle(def.label, "", def.key, val);
}
bool val = this->ini.getBool(this->osSection, def.key, false);
addBooleanToggle(I18n::ocBoolLabel(def.key), "", def.key, val);
}
// ── Frequency Settings ──
brls::Rectangle* spacer1 = new brls::Rectangle(nvgRGBA(0, 0, 0, 0));
spacer1->setHeight(30);
this->addView(spacer1);
this->addView(new brls::Header("Frequency Settings"));
this->addView(new brls::Header(I18n::headerFrequencySettings()));
for (const auto& def : OC_FREQ_KEYS)
{
if (this->ini.hasKey(this->osSection, def.key))
{
uint32_t val = (uint32_t)this->ini.getInt(this->osSection, def.key, 0);
addFreqDropdown(def.label, "", def.key, val, def.options);
}
int defVal = def.options.empty() ? 0 : (int)def.options.front();
uint32_t val = (uint32_t)this->ini.getInt(this->osSection, def.key, defVal);
addFreqDropdown(I18n::ocFreqLabel(def.key), "", def.key, val, def.options);
}
// ── Voltage Settings ──
brls::Rectangle* spacer2 = new brls::Rectangle(nvgRGBA(0, 0, 0, 0));
spacer2->setHeight(30);
this->addView(spacer2);
this->addView(new brls::Header("Voltage Settings"));
this->addView(new brls::Header(I18n::headerVoltageSettings()));
for (const auto& def : OC_VOLTAGE_KEYS)
{
if (this->ini.hasKey(this->osSection, def.key))
{
uint32_t val = (uint32_t)this->ini.getInt(this->osSection, def.key, 0);
addVoltageDropdown(def.label, "", def.key, val, def.options);
}
int defVal = def.options.empty() ? 0 : (int)def.options.front();
uint32_t val = (uint32_t)this->ini.getInt(this->osSection, def.key, defVal);
addVoltageDropdown(I18n::ocVoltageLabel(def.key), "", def.key, val, def.options);
}
// ── Other Keys (read-only info) ──
@@ -148,7 +134,7 @@ void OsConfigTab::buildUI()
brls::Rectangle* spacerOther = new brls::Rectangle(nvgRGBA(0, 0, 0, 0));
spacerOther->setHeight(30);
this->addView(spacerOther);
this->addView(new brls::Header("Other"));
this->addView(new brls::Header(I18n::headerOther()));
hasOtherKeys = true;
}
@@ -163,11 +149,11 @@ void OsConfigTab::saveAndNotify()
{
if (this->ini.save())
{
brls::Application::notify("\uE14B Configuration saved");
brls::Application::notify(I18n::notifyConfigSaved());
}
else
{
brls::Application::notify("\uE150 Error saving configuration!");
brls::Application::notify(I18n::notifyConfigSaveError());
}
}
@@ -175,7 +161,7 @@ void OsConfigTab::addBooleanToggle(const std::string& label, const std::string&
const std::string& key, bool currentValue)
{
brls::ToggleListItem *toggle = new brls::ToggleListItem(
label, currentValue, description, "ON", "OFF"
label, currentValue, description, I18n::wordOn(), I18n::wordOff()
);
std::string keyCopy = key;

View File

@@ -23,7 +23,7 @@ private:
std::string osSection;
void buildUI();
void buildErrorUI(const std::string& message);
void buildErrorUI(const std::string& title, const std::string& message);
void saveAndNotify();
// Create a toggle for a boolean key

View File

@@ -5,6 +5,7 @@
#include "settings_tab.h"
#include "file_browser.h"
#include "i18n.h"
#include "main_frame.h"
#include "os_config_tab.h"
@@ -15,14 +16,13 @@ SettingsTab::SettingsTab()
brls::Label *info = new brls::Label(
brls::LabelStyle::DESCRIPTION,
"Select which INI file each OS tab reads and writes.\n"
"Changes take effect immediately.",
I18n::settingsIntro(),
true
);
this->addView(info);
// ── INI File Paths ──
this->addView(new brls::Header("INI File Paths"));
this->addView(new brls::Header(I18n::headerIniFilePaths()));
addPathItem(OsTarget::ANDROID);
addPathItem(OsTarget::LINUX);
@@ -35,7 +35,7 @@ void SettingsTab::addPathItem(OsTarget target)
std::string currentPath = cfg.getPath(target);
std::string label = std::string(AppConfig::getLabel(target)) + " INI";
std::string description = (target == OsTarget::LAKKA) ? "Tap to browse for an INI file" : "";
std::string description = (target == OsTarget::LAKKA) ? I18n::pathListDescriptionLakka() : "";
brls::ListItem* item = new brls::ListItem(label, description);
item->setValue(currentPath);
@@ -59,7 +59,7 @@ void SettingsTab::addPathItem(OsTarget target)
if (tab)
tab->reload(selectedPath);
brls::Application::notify("\uE14B Path updated");
brls::Application::notify(I18n::notifyPathUpdated());
}
);