diff --git a/libstarlight/source/starlight/Application.cpp b/libstarlight/source/starlight/Application.cpp index fe2989f..138ee80 100644 --- a/libstarlight/source/starlight/Application.cpp +++ b/libstarlight/source/starlight/Application.cpp @@ -69,6 +69,7 @@ void Application::_init() { romfsInit(); ConfigManager::Init(); RenderCore::Open(); + ThemeManager::Init(); touchScreen = std::make_shared(); topScreen = std::make_shared(); @@ -83,6 +84,7 @@ void Application::_end() { forms.clear(); // not sure why, but not doing this results in a data abort if any forms are active + ThemeManager::End(); RenderCore::Close(); ConfigManager::End(); } diff --git a/libstarlight/source/starlight/ConfigManager.cpp b/libstarlight/source/starlight/ConfigManager.cpp index be8faf5..a97e79b 100644 --- a/libstarlight/source/starlight/ConfigManager.cpp +++ b/libstarlight/source/starlight/ConfigManager.cpp @@ -68,6 +68,7 @@ std::unordered_map> ConfigManager::cfg; void ConfigManager::Init() { //FSHelper::AssertDirPath("sdmc:/.starlight/config/app/" + Application::AppName()); + Get("user").autoSave = true; } void ConfigManager::End() { diff --git a/libstarlight/source/starlight/ConfigManager.h b/libstarlight/source/starlight/ConfigManager.h index 853d28e..40b65c7 100644 --- a/libstarlight/source/starlight/ConfigManager.h +++ b/libstarlight/source/starlight/ConfigManager.h @@ -4,7 +4,8 @@ #include #include -#include "starlight/_incLib/json_fwd.hpp" +//#include "starlight/_incLib/json_fwd.hpp" +#include "starlight/_incLib/json.hpp" #include "starlight/util/Path.h" @@ -26,6 +27,22 @@ namespace starlight { void Save(); nlohmann::json& Json() { return *json; } + + template + T Get(const std::string& path, T defVal, bool writeDefault = false) { + auto jp = nlohmann::json::json_pointer(path); + auto& js = (*json)[jp]; + if (js.is_null()) { + if (writeDefault) js = defVal; + return defVal; + } + return js; + } + + template + void Set(const std::string& path, T val) { + (*json)[nlohmann::json::json_pointer(path)] = val; + } }; class ConfigManager { diff --git a/libstarlight/source/starlight/InputManager.cpp b/libstarlight/source/starlight/InputManager.cpp index 5351134..1984ca4 100644 --- a/libstarlight/source/starlight/InputManager.cpp +++ b/libstarlight/source/starlight/InputManager.cpp @@ -58,11 +58,11 @@ void InputManager::Update() { touchLast = touchNow; hidTouchRead(&tp); - if (Held(Keys::TOUCH)) touchNow = Vector2(tp.px, tp.py); + if (Held(Keys::Touch)) touchNow = Vector2(tp.px, tp.py); - if (Pressed(Keys::TOUCH)) touchStart = touchLast = touchNow; + if (Pressed(Keys::Touch)) touchStart = touchLast = touchNow; - if (!Held(Keys::TOUCH) && !Released(Keys::TOUCH)) touchTime = 0; + if (!Held(Keys::Touch) && !Released(Keys::Touch)) touchTime = 0; else touchTime++; } diff --git a/libstarlight/source/starlight/InputManager.h b/libstarlight/source/starlight/InputManager.h index fedac27..c7a3a99 100644 --- a/libstarlight/source/starlight/InputManager.h +++ b/libstarlight/source/starlight/InputManager.h @@ -7,40 +7,40 @@ #include "starlight/ui/UIElement.h" -// borrow this from ctrulib +// based on ctrulib's enum #ifndef BIT #define BIT(n) (1U<<(n)) #endif enum class Keys : unsigned int { - A = BIT(0), ///< A - B = BIT(1), ///< B - SELECT = BIT(2), ///< Select - START = BIT(3), ///< Start - DRIGHT = BIT(4), ///< D-Pad Right - DLEFT = BIT(5), ///< D-Pad Left - DUP = BIT(6), ///< D-Pad Up - DDOWN = BIT(7), ///< D-Pad Down - R = BIT(8), ///< R - L = BIT(9), ///< L - X = BIT(10), ///< X - Y = BIT(11), ///< Y - ZL = BIT(14), ///< ZL (New 3DS only) - ZR = BIT(15), ///< ZR (New 3DS only) - TOUCH = BIT(20), ///< Touch (Not actually provided by HID) - CSTICK_RIGHT = BIT(24), ///< C-Stick Right (New 3DS only) - CSTICK_LEFT = BIT(25), ///< C-Stick Left (New 3DS only) - CSTICK_UP = BIT(26), ///< C-Stick Up (New 3DS only) - CSTICK_DOWN = BIT(27), ///< C-Stick Down (New 3DS only) - CPAD_RIGHT = BIT(28), ///< Circle Pad Right - CPAD_LEFT = BIT(29), ///< Circle Pad Left - CPAD_UP = BIT(30), ///< Circle Pad Up - CPAD_DOWN = BIT(31), ///< Circle Pad Down + A = BIT(0), ///< A + B = BIT(1), ///< B + Select = BIT(2), ///< Select + Start = BIT(3), ///< Start + DPadRight = BIT(4), ///< D-Pad Right + DPadLeft = BIT(5), ///< D-Pad Left + DPadUp = BIT(6), ///< D-Pad Up + DPadDown = BIT(7), ///< D-Pad Down + R = BIT(8), ///< R + L = BIT(9), ///< L + X = BIT(10), ///< X + Y = BIT(11), ///< Y + ZL = BIT(14), ///< ZL (New 3DS only) + ZR = BIT(15), ///< ZR (New 3DS only) + Touch = BIT(20), ///< Touch (Not actually provided by HID) + CStickRight = BIT(24), ///< C-Stick Right (New 3DS only) + CStickLeft = BIT(25), ///< C-Stick Left (New 3DS only) + CStickUp = BIT(26), ///< C-Stick Up (New 3DS only) + CStickDown = BIT(27), ///< C-Stick Down (New 3DS only) + CPadRight = BIT(28), ///< Circle Pad Right + CPadLeft = BIT(29), ///< Circle Pad Left + CPadUp = BIT(30), ///< Circle Pad Up + CPadDown = BIT(31), ///< Circle Pad Down // Generic catch-all directions - UP = DUP | CPAD_UP, ///< D-Pad Up or Circle Pad Up - DOWN = DDOWN | CPAD_DOWN, ///< D-Pad Down or Circle Pad Down - LEFT = DLEFT | CPAD_LEFT, ///< D-Pad Left or Circle Pad Left - RIGHT = DRIGHT | CPAD_RIGHT, ///< D-Pad Right or Circle Pad Right + Up = DPadUp | CPadUp, ///< D-Pad Up or Circle Pad Up + Down = DPadDown | CPadDown, ///< D-Pad Down or Circle Pad Down + Left = DPadLeft | CPadLeft, ///< D-Pad Left or Circle Pad Left + Right = DPadRight | CPadRight, ///< D-Pad Right or Circle Pad Right }; namespace starlight { diff --git a/libstarlight/source/starlight/ThemeManager.cpp b/libstarlight/source/starlight/ThemeManager.cpp index 02a4131..ccce7a7 100644 --- a/libstarlight/source/starlight/ThemeManager.cpp +++ b/libstarlight/source/starlight/ThemeManager.cpp @@ -1,3 +1,6 @@ +#include "ThemeManager.h" +#include "starlight/gfx/ThemeRef.h" + #include #include #include @@ -8,8 +11,7 @@ #include "starlight/_incLib/lodepng.h" #include "starlight/_incLib/json.hpp" -#include "ThemeManager.h" -#include "starlight/gfx/ThemeRef.h" +#include "starlight/ConfigManager.h" #include "starlight/gfx/DrawableImage.h" #include "starlight/gfx/DrawableNinePatch.h" @@ -19,6 +21,8 @@ #include "starlight/gfx/RenderCore.h" #include "starlight/gfx/BitmapFont.h" +#include "starlight/util/JsonConversions.h" + using std::string; using std::shared_ptr; using std::make_shared; @@ -26,8 +30,13 @@ using std::make_shared; using nlohmann::json; using starlight::Vector2; +using starlight::VRect; +using starlight::Color; + +using starlight::util::Path; + +using starlight::ConfigManager; -using starlight::ThemeManager; using starlight::gfx::Drawable; using starlight::gfx::Font; using starlight::gfx::ThemeRef; @@ -40,6 +49,10 @@ using starlight::gfx::RenderCore; using starlight::gfx::CTexture; using starlight::gfx::BitmapFont; +using starlight::ThemeInfo; +using starlight::ThemeManager; +using starlight::TextConfig; + namespace { inline int NextPow2(unsigned int x) { --x; @@ -118,10 +131,51 @@ namespace { } } +ThemeInfo::ThemeInfo(const std::string& name) : ThemeInfo(Path(".starlight/themes").Combine(name), name) { + //ConfigManager::Get("user").Json()["log"].push_back(basePath); +} + +ThemeInfo::ThemeInfo(const Path& path, const std::string& name) { + this->name = name; + basePath = path; + meta = std::make_shared(); + metrics = std::make_shared(); + if (!basePath.IsDirectory()) return; // don't bother trying to load stuff + { // using... + std::ifstream load = basePath.Combine("meta.json").OpenI(); + if (load.good()) load >> *meta; + load = basePath.Combine("metrics.json").OpenI(); + if (load.good()) load >> *metrics; + } +} + +std::list ThemeManager::themeData; std::unordered_map> ThemeManager::drawables; std::unordered_map> ThemeManager::fonts; std::list> ThemeManager::tq; +void ThemeManager::Init() { + auto& usercfg = ConfigManager::Get("user"); + + auto themeName = usercfg.Get("/theme", "default", true); + + ThemeInfo thm = ThemeInfo(themeName); + if (!thm.basePath.IsDirectory()) thm = ThemeInfo("default"); // fall back on default if not found + // ...and if "default" doesn't exist, fall back on a standard location in romfs: + if (!thm.basePath.IsDirectory()) thm = ThemeInfo(Path("romfs:/.fallback_theme", "FALLBACK")); + themeData.push_back(thm); + + while (!(*thm.meta)["fallback"].is_null()) { // follow fallback chain + std::string fbn = (*thm.meta)["fallback"]; + thm = ThemeInfo(fbn); + themeData.push_back(thm); + } +} + +void ThemeManager::End() { + +} + ThemeRef ThemeManager::GetAsset(const std::string& name) { auto const& itr = drawables.find(name); if (itr == drawables.end()) { @@ -201,37 +255,96 @@ void ThemeManager::LoadProc() { } string ThemeManager::ResolveAssetPath(const string& id) { - struct stat buf; - string path(id.length() + 64, ' '); // preallocate buffer space + //struct stat buf; + //string path(id.length() + 64, ' '); // preallocate buffer space static const string pfxLocal = "app:/"; - if (id.compare(0, pfxLocal.length(), pfxLocal)) { + if (id.compare(0, pfxLocal.length(), pfxLocal) == 0) { // app-local asset // check if present in theme/app/[appname]/, else check in romfs } else { // theme asset; check in each theme from selected to most-fallback + for (auto thm : themeData) { + Path p = thm.basePath.Combine(id+".json"); + if (p.IsFile()) return p; + p = thm.basePath.Combine(id+".png"); + if (p.IsFile()) return p; + } } - path.clear(); path.append("romfs:/"); path.append(id); path.append(".json"); + /*path.clear(); path.append("romfs:/"); path.append(id); path.append(".json"); printf("attempt: %s\n", path.c_str()); if (stat(path.c_str(), &buf) == 0) return path; path.erase(path.end()-5, path.end()); path.append(".png"); printf("attempt: %s\n", path.c_str()); - if (stat(path.c_str(), &buf) == 0) return path; + if (stat(path.c_str(), &buf) == 0) return path;//*/ return string(); } -string ThemeManager::ResolveFontPath(const string& id) { // this needs redone, but whatever - struct stat buf; - string path(id.length() + 64, ' '); // preallocate buffer space - path.clear(); path.append("romfs:/fonts/"); path.append(id); path.append(".json"); - printf("attempt: %s\n", path.c_str()); - if (stat(path.c_str(), &buf) == 0) return path; - path.erase(path.end()-5, path.end()); path.append(".png"); - printf("attempt: %s\n", path.c_str()); - if (stat(path.c_str(), &buf) == 0) return path; +string ThemeManager::ResolveFontPath(const string& id) { // there we go, nice and simple + for (auto thm : themeData) { + Path p = thm.basePath.Combine("fonts").Combine(id+".json"); + if (p.IsFile()) return p; + } return string(); } + +json& ThemeManager::GetMetric(const string& path) { + json::json_pointer jp(path); + for (auto& t : themeData) { + json& j = (*t.metrics)[jp]; + if (!j.is_null()) { + if (j.is_object()) { + auto& jr = j["_redir"]; + if (!jr.is_null()) return GetMetric(jr); + } + return j; + } + } + static json jx({}); + return jx; // well, here's a null json +} + +template +T ThemeManager::GetMetric(const std::string& path, const T& defaultValue) { + //try { + json& j = GetMetric(path); + if (j.is_null()) return defaultValue; + return j; + /*} catch (std::exception& e) { + return defaultValue; + }//*/ +} + +TextConfig::TextConfig(const std::string& fontName, Color text, Color border) { + font = ThemeManager::GetFont(fontName); + textColor = text; borderColor = border; +} + +void TextConfig::Print(Vector2 position, std::string& text, Vector2 justification) + { font->Print(position, text, 1, textColor, justification, borderColor); } +void TextConfig::Print(VRect rect, std::string& text, Vector2 justification) + { font->Print(rect, text, 1, textColor, justification, borderColor); } + +namespace starlight { // todo: expose these in the header + void to_json(nlohmann::json& j, const TextConfig& tc) { + // todo: implement this + } + void from_json(const nlohmann::json& j, TextConfig& tc) { + if (j.is_object()) { + tc.font = ThemeManager::GetFont(j.value("font", "default.12")); + tc.textColor = j.value("textColor", Color::white); + tc.borderColor = j.value("borderColor", Color::transparent); + } + // + } +} + +template Vector2 ThemeManager::GetMetric(const std::string&, const Vector2&); +template VRect ThemeManager::GetMetric(const std::string&, const VRect&); +template Color ThemeManager::GetMetric(const std::string&, const Color&); + +template starlight::TextConfig ThemeManager::GetMetric(const std::string&, const TextConfig&); diff --git a/libstarlight/source/starlight/ThemeManager.h b/libstarlight/source/starlight/ThemeManager.h index ade598a..8a28421 100644 --- a/libstarlight/source/starlight/ThemeManager.h +++ b/libstarlight/source/starlight/ThemeManager.h @@ -4,8 +4,13 @@ #include #include #include +#include #include +#include "starlight/_incLib/json_fwd.hpp" + +#include "starlight/util/Path.h" + #include "starlight/gfx/Drawable.h" #include "starlight/gfx/Font.h" @@ -15,6 +20,20 @@ namespace starlight { template class ThemeRefContainer; template class ThemeRef; } + + struct ThemeInfo { + public: + std::string name; + util::Path basePath; + + std::shared_ptr meta; + std::shared_ptr metrics; + + ThemeInfo() = default; + ThemeInfo(const std::string& name); + ThemeInfo(const util::Path& path, const std::string& name = ""); + ~ThemeInfo() = default; + }; class ThemeManager { template @@ -24,6 +43,8 @@ namespace starlight { static std::unordered_map> fonts; static std::list> tq; protected: + static std::list themeData; + static void Fulfill(gfx::ThemeRefContainer& ref); static void Fulfill(gfx::ThemeRefContainer& ref); @@ -31,6 +52,9 @@ namespace starlight { public: ThemeManager() = delete; // "static" class + static void Init(); + static void End(); + static gfx::ThemeRef GetAsset(const std::string& name); static gfx::ThemeRef GetFont(const std::string& name); @@ -38,8 +62,28 @@ namespace starlight { static std::string ResolveAssetPath(const std::string& id); static std::string ResolveFontPath(const std::string& id); + + static inline std::string GetThemeName() { return themeData.front().name; } + static nlohmann::json& GetMetric(const std::string& path); + template static T GetMetric(const std::string& path, const T& defaultValue); }; } // post-include dependency #include "starlight/gfx/ThemeRef.h" + +// and some metrics types depending on ThemeRef +namespace starlight { + struct TextConfig { + gfx::ThemeRef font = ThemeManager::GetFont("default.12"); + Color textColor = Color::white; + Color borderColor = Color::transparent; + + TextConfig() = default; + TextConfig(const std::string& fontName, Color text, Color border = Color::transparent); + ~TextConfig() = default; + + void Print(Vector2 position, std::string& text, Vector2 justification = Vector2::zero); + void Print(VRect rect, std::string& text, Vector2 justification = Vector2::zero); + }; +} diff --git a/libstarlight/source/starlight/_incLib/json.hpp b/libstarlight/source/starlight/_incLib/json.hpp index b9c2348..042520f 100644 --- a/libstarlight/source/starlight/_incLib/json.hpp +++ b/libstarlight/source/starlight/_incLib/json.hpp @@ -12898,6 +12898,9 @@ basic_json_parser_74: } /// @} + + // ZP EDIT: JSON EXTENSIONS + #include "json_extensions.hpp" }; ///////////// diff --git a/libstarlight/source/starlight/_incLib/json_extensions.hpp b/libstarlight/source/starlight/_incLib/json_extensions.hpp new file mode 100644 index 0000000..310a7de --- /dev/null +++ b/libstarlight/source/starlight/_incLib/json_extensions.hpp @@ -0,0 +1,10 @@ +// libstarlight extensions for nlohmann::json (include nested within basic_json) + +// try_get: reads out if applicable, else leaves the target alone +void try_get(int& v) { if (is_number()) v = get(); } +void try_get(float& v) { if (is_number()) v = get(); } +void try_get(std::string& v) { if (is_string()) v = get(); } + +/*bool has(std::string& idx) const { + return find(idx) != end(); +}*/ diff --git a/libstarlight/source/starlight/datatypes/Color.cpp b/libstarlight/source/starlight/datatypes/Color.cpp index b643781..32d1e04 100644 --- a/libstarlight/source/starlight/datatypes/Color.cpp +++ b/libstarlight/source/starlight/datatypes/Color.cpp @@ -1,15 +1,9 @@ #include "Color.h" +#include "starlight/_incLib/json.hpp" + using starlight::Color; -// -//#define RGBA8(r, g, b, a) ((((a)&0xFF)<<24) | (((b)&0xFF)<<16) | (((g)&0xFF)<<8) | (((r)&0xFF)<<0)) - -//#define RGBA8_GET_R(c) (((c) >> 0) & 0xFF) -//#define RGBA8_GET_G(c) (((c) >> 8) & 0xFF) -//#define RGBA8_GET_B(c) (((c) >> 16) & 0xFF) -//#define RGBA8_GET_A(c) (((c) >> 24) & 0xFF) - const Color Color::transparent = Color(0.0f, 0.0f, 0.0f, 0.0f); const Color Color::white = Color(1.0f, 1.0f, 1.0f); const Color Color::black = Color(0.0f, 0.0f, 0.0f); diff --git a/libstarlight/source/starlight/dialog/MessageBox.cpp b/libstarlight/source/starlight/dialog/MessageBox.cpp index e45288f..ef3e040 100644 --- a/libstarlight/source/starlight/dialog/MessageBox.cpp +++ b/libstarlight/source/starlight/dialog/MessageBox.cpp @@ -22,13 +22,13 @@ MessageBox::MessageBox(Mode m, const std::string& msg, std::function priority = 10; eOnSelect = onSelect; - VRect boxArea = VRect(160, 120, 0, 0).Expand(Vector2(300, 200)*.5); + VRect boxArea = VRect(160, 120, 0, 0).Expand(Vector2(240, 160)*.5); auto bg = std::make_shared(boxArea, "decorations/panel.bg"); touchScreen->Add(bg); - auto scroll = std::make_shared(boxArea.Expand(-8, -8).TopEdge(200-16-8-32)); + auto scroll = std::make_shared(boxArea.Expand(-8, -8).TopEdge(boxArea.size.y - 16 - 32 - 8)); touchScreen->Add(scroll); - auto label = std::make_shared