// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once #include "imgui.h" #include namespace dragonx { namespace ui { namespace material { // ============================================================================ // Material Design Type Scale // ============================================================================ // Based on https://m2.material.io/design/typography/the-type-system.html // // The type scale is a combination of 13 styles that are supported by the // type system. It contains reusable categories of text, each with an // intended application and meaning. /** * @brief Material Design typography style identifiers */ enum class TypeStyle { H1, // 96sp Light - Large display headers H2, // 60sp Light - Display headers H3, // 48sp Regular - Section titles H4, // 34sp Regular - Section titles H5, // 24sp Regular - Card titles, dialog titles H6, // 20sp Medium - Section headers, subtitles Subtitle1, // 16sp Regular - Secondary information Subtitle2, // 14sp Medium - Secondary information Body1, // 16sp Regular - Primary body text Body2, // 14sp Regular - Secondary body text Button, // 14sp Medium - Button labels (uppercase) Caption, // 12sp Regular - Timestamps, labels Overline, // 10sp Regular - Overline labels (uppercase) ButtonSm, // Small button labels ButtonLg // Large button labels }; /** * @brief Typography specification for a type style */ struct TypeSpec { float size; // Font size in sp (scaled pixels) float letterSpacing; // Letter spacing in px (applied after scaling) float lineHeight; // Line height multiplier bool uppercase; // Whether text should be uppercase // Font weight is handled by which font is used (Light/Regular/Medium) }; /** * @brief Typography system managing Material Design fonts * * Loads Ubuntu fonts in Light, Regular, and Medium weights at multiple * sizes to implement the Material Design type scale. * * Usage: * // Initialize during startup (after ImGui context) * dragonx::ui::material::Typography::instance().load(io); * * // Use fonts throughout the application * ImGui::PushFont(Typography::instance().getFont(TypeStyle::H5)); * ImGui::Text("Card Title"); * ImGui::PopFont(); * * // Or use convenience functions * Typography::instance().pushFont(TypeStyle::Body1); * ImGui::TextWrapped("Body text here..."); * Typography::instance().popFont(); */ class Typography { public: /** * @brief Get the singleton instance */ static Typography& instance(); /** * @brief Load all fonts for the type scale * * Must be called after ImGui context is created but before first frame. * * @param io ImGui IO reference * @param dpiScale DPI scale factor (default 1.0) * @return true if fonts loaded successfully */ bool load(ImGuiIO& io, float dpiScale = 1.0f); /** * @brief Reload fonts at a new DPI scale * * Call when the display scale changes (e.g., window moved to a different * DPI monitor). Clears the existing font atlas and reloads all fonts. * * @param io ImGui IO reference * @param dpiScale New DPI scale factor * @return true if fonts reloaded successfully */ bool reload(ImGuiIO& io, float dpiScale); /** * @brief Check if typography system is loaded */ bool isLoaded() const { return loaded_; } /** * @brief Get the current DPI scale */ float getDpiScale() const { return dpiScale_; } /** * @brief Get font for a type style * * @param style The Material Design type style * @return ImFont pointer (never null, returns default if not loaded) */ ImFont* getFont(TypeStyle style) const; /** * @brief Get the spec for a type style * * @param style The Material Design type style * @return TypeSpec with size, spacing, and line height */ const TypeSpec& getSpec(TypeStyle style) const; /** * @brief Push font for a type style * * Convenience wrapper around ImGui::PushFont(getFont(style)) */ void pushFont(TypeStyle style) const; /** * @brief Pop the current font * * Convenience wrapper around ImGui::PopFont() */ void popFont() const; // ======================================================================== // Font Accessors (for common cases) // ======================================================================== // Headers ImFont* h1() const { return getFont(TypeStyle::H1); } ImFont* h2() const { return getFont(TypeStyle::H2); } ImFont* h3() const { return getFont(TypeStyle::H3); } ImFont* h4() const { return getFont(TypeStyle::H4); } ImFont* h5() const { return getFont(TypeStyle::H5); } ImFont* h6() const { return getFont(TypeStyle::H6); } // Body text ImFont* subtitle1() const { return getFont(TypeStyle::Subtitle1); } ImFont* subtitle2() const { return getFont(TypeStyle::Subtitle2); } ImFont* body1() const { return getFont(TypeStyle::Body1); } ImFont* body2() const { return getFont(TypeStyle::Body2); } // UI elements ImFont* button() const { return getFont(TypeStyle::Button); } ImFont* buttonSm() const { return getFont(TypeStyle::ButtonSm); } ImFont* buttonLg() const { return getFont(TypeStyle::ButtonLg); } ImFont* caption() const { return getFont(TypeStyle::Caption); } ImFont* overline() const { return getFont(TypeStyle::Overline); } // Icon fonts — Material Design Icons merged at specific pixel sizes // Use these when you need to render an icon at a specific size via AddText/ImGui::Text. // Common sizes: iconSmall (14px), iconMed (18px), iconLarge (24px). ImFont* iconSmall() const { return iconFonts_[0] ? iconFonts_[0] : getFont(TypeStyle::Body2); } ImFont* iconMed() const { return iconFonts_[1] ? iconFonts_[1] : getFont(TypeStyle::Body1); } ImFont* iconLarge() const { return iconFonts_[2] ? iconFonts_[2] : getFont(TypeStyle::H5); } ImFont* iconXL() const { return iconFonts_[3] ? iconFonts_[3] : getFont(TypeStyle::H3); } ImFont* pickaxeSmall() const { return pickaxeFonts_[0] ? pickaxeFonts_[0] : iconSmall(); } ImFont* pickaxeMed() const { return pickaxeFonts_[1] ? pickaxeFonts_[1] : iconMed(); } ImFont* pickaxeLarge() const { return pickaxeFonts_[2] ? pickaxeFonts_[2] : iconLarge(); } ImFont* pickaxeXL() const { return pickaxeFonts_[3] ? pickaxeFonts_[3] : iconXL(); } ImFont* pickaxeFontForSize(float size) const; static constexpr ImWchar kPickaxeCodepoint = 0xE001; /** * @brief Resolve a font name string to ImFont* * @param name Font name like "button", "button-sm", "button-lg", "h4", "body1" * @return ImFont* pointer, or nullptr if name is empty/unknown */ ImFont* resolveByName(const std::string& name) const { if (name.empty()) return nullptr; if (name == "h1") return h1(); if (name == "h2") return h2(); if (name == "h3") return h3(); if (name == "h4") return h4(); if (name == "h5") return h5(); if (name == "h6") return h6(); if (name == "subtitle1") return subtitle1(); if (name == "subtitle2") return subtitle2(); if (name == "body1") return body1(); if (name == "body2") return body2(); if (name == "button") return button(); if (name == "button-sm") return buttonSm(); if (name == "button-lg") return buttonLg(); if (name == "caption") return caption(); if (name == "overline") return overline(); return nullptr; } // ======================================================================== // Text Rendering Helpers // ======================================================================== /** * @brief Render text with a specific type style * * Handles font push/pop and optional uppercase transformation. * * @param style The type style to use * @param text The text to render */ void text(TypeStyle style, const char* text) const; /** * @brief Render wrapped text with a specific type style * * @param style The type style to use * @param text The text to render */ void textWrapped(TypeStyle style, const char* text) const; /** * @brief Render colored text with a specific type style * * @param style The type style to use * @param color Text color * @param text The text to render */ void textColored(TypeStyle style, ImU32 color, const char* text) const; private: Typography() = default; ~Typography() = default; Typography(const Typography&) = delete; Typography& operator=(const Typography&) = delete; // Load fonts at a specific size with a specific weight ImFont* loadFont(ImGuiIO& io, int weight, float size, const char* name); // Font weight constants static constexpr int kWeightLight = 300; static constexpr int kWeightRegular = 400; static constexpr int kWeightMedium = 500; bool loaded_ = false; float dpiScale_ = 1.0f; // Fonts for each type style ImFont* fonts_[15] = {}; // Icon fonts at different sizes: [0]=small(14), [1]=med(18), [2]=large(24), [3]=xl(40) ImFont* iconFonts_[4] = {}; ImFont* pickaxeFonts_[4] = {}; static constexpr int kNumIconSizes = 4; // Load an icon-only font at a specific pixel size ImFont* loadIconFont(ImGuiIO& io, float size, const char* name); ImFont* loadPickaxeFont(ImGuiIO& io, float size, const char* name); // Type specifications static const TypeSpec* getTypeSpecs(); static constexpr int kNumStyles = 15; }; // ============================================================================ // Convenience Macros (Optional) // ============================================================================ // Scoped font push/pop using RAII class ScopedFont { public: explicit ScopedFont(TypeStyle style) { Typography::instance().pushFont(style); } ~ScopedFont() { Typography::instance().popFont(); } }; // Usage: MATERIAL_FONT(H5) { ImGui::Text("Title"); } #define MATERIAL_FONT(style) \ if (dragonx::ui::material::ScopedFont _font{dragonx::ui::material::TypeStyle::style}; true) } // namespace material } // namespace ui } // namespace dragonx