- Add expanded address icon picker with search, bottom-aligned actions, and improved modal sizing - Embed a pickaxe icon font subset and wire it into typography/address icon rendering - Track view-only shielded addresses and prevent sends from non-spendable z-addresses - Improve address transfer dialog sizing, max amount handling, and text clipping - Tune main header layout values in ui.toml - Update README, codebase overview, and third-party license documentation
501 lines
19 KiB
C++
501 lines
19 KiB
C++
// DragonX Wallet - ImGui Edition
|
||
// Copyright 2024-2026 The Hush Developers
|
||
// Released under the GPLv3
|
||
|
||
#include "typography.h"
|
||
#include "../layout.h"
|
||
#include "../schema/ui_schema.h"
|
||
#include <cstring>
|
||
#include <cstdio>
|
||
#include <cctype>
|
||
#include <string>
|
||
|
||
// Embedded font data (via INCBIN – assembler .incbin directive)
|
||
#include "../embedded/embedded_fonts.h" // g_ubuntu_*_data/size, g_material_icons_*
|
||
#include "../embedded/IconsMaterialDesign.h" // Icon codepoint defines
|
||
#include "../../util/logger.h"
|
||
|
||
namespace dragonx {
|
||
namespace ui {
|
||
namespace material {
|
||
|
||
// ============================================================================
|
||
// Type Scale Specifications
|
||
// ============================================================================
|
||
// Font sizes are configured in ui/layout.h for easy global adjustment.
|
||
// Letter spacing, line height and text-transform are loaded from ui.toml
|
||
// via the UISchema typography section.
|
||
// Font sizes come from UISchema (JSON-configurable) via Layout:: accessors.
|
||
|
||
const TypeSpec* Typography::getTypeSpecs()
|
||
{
|
||
// Rebuild each call to pick up hot-reloaded values
|
||
static TypeSpec specs[kNumStyles];
|
||
|
||
// Helper: read typography entry from UISchema
|
||
auto& S = schema::UI();
|
||
auto tspec = [&](const char* key, float defLS, float defLH, bool defUpper) -> TypeSpec {
|
||
auto e = S.drawElement("typography", key);
|
||
float ls = e.getFloat("letter-spacing", defLS);
|
||
float lh = e.getFloat("line-height", defLH);
|
||
// text-transform stored as extraColors (string map)
|
||
bool upper = defUpper;
|
||
auto it = e.extraColors.find("text-transform");
|
||
if (it != e.extraColors.end()) {
|
||
upper = (it->second == "uppercase");
|
||
}
|
||
return TypeSpec{0.0f, ls, lh, upper};
|
||
};
|
||
|
||
// Font size comes from Layout:: accessors; typography from UISchema
|
||
auto ts0 = tspec("h1", -1.0f, 1.167f, false);
|
||
auto ts1 = tspec("h2", -0.5f, 1.2f, false);
|
||
auto ts2 = tspec("h3", 0.0f, 1.167f, false);
|
||
auto ts3 = tspec("h4", 0.25f, 1.235f, false);
|
||
auto ts4 = tspec("h5", 0.0f, 1.334f, false);
|
||
auto ts5 = tspec("h6", 0.15f, 1.6f, false);
|
||
auto ts6 = tspec("subtitle1", 0.15f, 1.75f, false);
|
||
auto ts7 = tspec("subtitle2", 0.1f, 1.57f, false);
|
||
auto ts8 = tspec("body1", 0.5f, 1.5f, false);
|
||
auto ts9 = tspec("body2", 0.25f, 1.43f, false);
|
||
auto ts10 = tspec("button", 1.25f, 1.75f, true);
|
||
auto ts11 = tspec("caption", 0.4f, 1.66f, false);
|
||
auto ts12 = tspec("overline", 1.5f, 2.66f, true);
|
||
|
||
specs[0] = { Layout::kFontH1(), ts0.letterSpacing, ts0.lineHeight, ts0.uppercase };
|
||
specs[1] = { Layout::kFontH2(), ts1.letterSpacing, ts1.lineHeight, ts1.uppercase };
|
||
specs[2] = { Layout::kFontH3(), ts2.letterSpacing, ts2.lineHeight, ts2.uppercase };
|
||
specs[3] = { Layout::kFontH4(), ts3.letterSpacing, ts3.lineHeight, ts3.uppercase };
|
||
specs[4] = { Layout::kFontH5(), ts4.letterSpacing, ts4.lineHeight, ts4.uppercase };
|
||
specs[5] = { Layout::kFontH6(), ts5.letterSpacing, ts5.lineHeight, ts5.uppercase };
|
||
specs[6] = { Layout::kFontSubtitle1(), ts6.letterSpacing, ts6.lineHeight, ts6.uppercase };
|
||
specs[7] = { Layout::kFontSubtitle2(), ts7.letterSpacing, ts7.lineHeight, ts7.uppercase };
|
||
specs[8] = { Layout::kFontBody1(), ts8.letterSpacing, ts8.lineHeight, ts8.uppercase };
|
||
specs[9] = { Layout::kFontBody2(), ts9.letterSpacing, ts9.lineHeight, ts9.uppercase };
|
||
specs[10] = { Layout::kFontButton(), ts10.letterSpacing, ts10.lineHeight, ts10.uppercase };
|
||
specs[11] = { Layout::kFontCaption(), ts11.letterSpacing, ts11.lineHeight, ts11.uppercase };
|
||
specs[12] = { Layout::kFontOverline(), ts12.letterSpacing, ts12.lineHeight, ts12.uppercase };
|
||
specs[13] = { Layout::kFontButtonSm(), ts10.letterSpacing, ts10.lineHeight, ts10.uppercase };
|
||
specs[14] = { Layout::kFontButtonLg(), ts10.letterSpacing, ts10.lineHeight, ts10.uppercase };
|
||
return specs;
|
||
}
|
||
|
||
// ============================================================================
|
||
// Typography Implementation
|
||
// ============================================================================
|
||
|
||
Typography& Typography::instance()
|
||
{
|
||
static Typography s_instance;
|
||
return s_instance;
|
||
}
|
||
|
||
bool Typography::reload(ImGuiIO& io, float dpiScale)
|
||
{
|
||
if (!loaded_) {
|
||
return load(io, dpiScale);
|
||
}
|
||
DEBUG_LOGF("Typography: Reloading fonts for DPI scale %.2f\n", dpiScale);
|
||
// Clear existing fonts and reload
|
||
io.Fonts->Clear();
|
||
loaded_ = false;
|
||
for (int i = 0; i < kNumStyles; ++i) fonts_[i] = nullptr;
|
||
for (int i = 0; i < kNumIconSizes; ++i) iconFonts_[i] = nullptr;
|
||
for (int i = 0; i < kNumIconSizes; ++i) pickaxeFonts_[i] = nullptr;
|
||
return load(io, dpiScale);
|
||
}
|
||
|
||
bool Typography::load(ImGuiIO& io, float dpiScale)
|
||
{
|
||
if (loaded_) {
|
||
DEBUG_LOGF("Typography: Already loaded\n");
|
||
return true;
|
||
}
|
||
|
||
dpiScale_ = dpiScale;
|
||
|
||
// Multiply by dpiScale so fonts are the correct physical size.
|
||
// On Windows Per-Monitor DPI v2, SDL3 coordinates are physical pixels
|
||
// and DisplayFramebufferScale is 1.0 (no automatic upscaling).
|
||
// The window is resized by dpiScale in main.cpp so that fonts at
|
||
// size*dpiScale fit proportionally (no overflow).
|
||
// Layout::userFontScale() is the user-chosen accessibility multiplier
|
||
// (1.0–3.0) persisted in Settings; it makes glyphs physically larger
|
||
// without any bitmap up-scaling (sharp at every size).
|
||
float scale = dpiScale * Layout::kFontScale() * Layout::userFontScale();
|
||
DEBUG_LOGF("Typography: Loading Material Design type scale (DPI: %.2f, fontScale: %.2f, userFontScale: %.2f, combined: %.2f)\n",
|
||
dpiScale, Layout::kFontScale(), Layout::userFontScale(), scale);
|
||
|
||
// For ImGui, we need to load fonts at specific pixel sizes.
|
||
// Font sizes come from Layout:: accessors (backed by UISchema JSON)
|
||
|
||
// Load fonts in order of TypeStyle enum
|
||
|
||
// H1: Light
|
||
fonts_[0] = loadFont(io, kWeightLight, Layout::kFontH1() * scale, "H1");
|
||
|
||
// H2: Light
|
||
fonts_[1] = loadFont(io, kWeightLight, Layout::kFontH2() * scale, "H2");
|
||
|
||
// H3: Regular
|
||
fonts_[2] = loadFont(io, kWeightRegular, Layout::kFontH3() * scale, "H3");
|
||
|
||
// H4: Regular
|
||
fonts_[3] = loadFont(io, kWeightRegular, Layout::kFontH4() * scale, "H4");
|
||
|
||
// H5: Regular
|
||
fonts_[4] = loadFont(io, kWeightRegular, Layout::kFontH5() * scale, "H5");
|
||
|
||
// H6: Medium
|
||
fonts_[5] = loadFont(io, kWeightMedium, Layout::kFontH6() * scale, "H6");
|
||
|
||
// Subtitle1: Regular
|
||
fonts_[6] = loadFont(io, kWeightRegular, Layout::kFontSubtitle1() * scale, "Subtitle1");
|
||
|
||
// Subtitle2: Medium
|
||
fonts_[7] = loadFont(io, kWeightMedium, Layout::kFontSubtitle2() * scale, "Subtitle2");
|
||
|
||
// Body1: Regular (shares with Subtitle1 if same size)
|
||
if (Layout::kFontBody1() == Layout::kFontSubtitle1()) {
|
||
fonts_[8] = fonts_[6]; // Reuse Subtitle1 font
|
||
} else {
|
||
fonts_[8] = loadFont(io, kWeightRegular, Layout::kFontBody1() * scale, "Body1");
|
||
}
|
||
|
||
// Body2: Regular
|
||
fonts_[9] = loadFont(io, kWeightRegular, Layout::kFontBody2() * scale, "Body2");
|
||
|
||
// Button: Medium (shares with Subtitle2 if same size)
|
||
if (Layout::kFontButton() == Layout::kFontSubtitle2()) {
|
||
fonts_[10] = fonts_[7]; // Reuse Subtitle2 font
|
||
} else {
|
||
fonts_[10] = loadFont(io, kWeightMedium, Layout::kFontButton() * scale, "Button");
|
||
}
|
||
|
||
// Caption: Regular
|
||
fonts_[11] = loadFont(io, kWeightRegular, Layout::kFontCaption() * scale, "Caption");
|
||
|
||
// Overline: Regular
|
||
fonts_[12] = loadFont(io, kWeightRegular, Layout::kFontOverline() * scale, "Overline");
|
||
|
||
// ButtonSm: Medium
|
||
fonts_[13] = loadFont(io, kWeightMedium, Layout::kFontButtonSm() * scale, "ButtonSm");
|
||
|
||
// ButtonLg: Medium
|
||
fonts_[14] = loadFont(io, kWeightMedium, Layout::kFontButtonLg() * scale, "ButtonLg");
|
||
|
||
// --- Icon fonts at multiple sizes ---
|
||
// These are standalone fonts (not merged) so we can PushFont(iconXxx) for icon rendering.
|
||
iconFonts_[0] = loadIconFont(io, 14.0f * scale, "IconSmall");
|
||
iconFonts_[1] = loadIconFont(io, 18.0f * scale, "IconMed");
|
||
iconFonts_[2] = loadIconFont(io, 24.0f * scale, "IconLarge");
|
||
iconFonts_[3] = loadIconFont(io, 40.0f * scale, "IconXL");
|
||
|
||
// Load a one-glyph MDI subset for pickaxe. The glyph is remapped onto a
|
||
// BMP private-use codepoint so it remains renderable with 16-bit ImWchar.
|
||
pickaxeFonts_[0] = loadPickaxeFont(io, 14.0f * scale, "PickaxeSmall");
|
||
pickaxeFonts_[1] = loadPickaxeFont(io, 18.0f * scale, "PickaxeMed");
|
||
pickaxeFonts_[2] = loadPickaxeFont(io, 24.0f * scale, "PickaxeLarge");
|
||
pickaxeFonts_[3] = loadPickaxeFont(io, 40.0f * scale, "PickaxeXL");
|
||
|
||
// Verify all fonts loaded
|
||
bool allLoaded = true;
|
||
for (int i = 0; i < kNumStyles; ++i) {
|
||
if (!fonts_[i]) {
|
||
DEBUG_LOGF("Typography: Warning - Font for style %d not loaded\n", i);
|
||
fonts_[i] = io.Fonts->Fonts.Size > 0 ? io.Fonts->Fonts[0] : nullptr;
|
||
allLoaded = false;
|
||
}
|
||
}
|
||
|
||
// Set Body1 as the default font so that bare ImGui::Text() calls
|
||
// (status bar, dialogs, notifications, etc.) render at Body1 size
|
||
// instead of H1 (the first font loaded).
|
||
io.FontDefault = fonts_[static_cast<int>(TypeStyle::Body1)];
|
||
|
||
loaded_ = true;
|
||
Layout::setFontAtlasScale(Layout::userFontScale());
|
||
DEBUG_LOGF("Typography: Loaded %d font styles (default=Body1, atlasScale=%.2f)\n",
|
||
allLoaded ? kNumStyles : -1, Layout::fontAtlasScale());
|
||
|
||
return allLoaded;
|
||
}
|
||
|
||
ImFont* Typography::loadFont(ImGuiIO& io, int weight, float size, const char* name)
|
||
{
|
||
// Select font data based on weight
|
||
const unsigned char* fontData = nullptr;
|
||
unsigned int fontDataLen = 0;
|
||
|
||
switch (weight) {
|
||
case kWeightLight:
|
||
fontData = g_ubuntu_light_data;
|
||
fontDataLen = g_ubuntu_light_size;
|
||
break;
|
||
case kWeightMedium:
|
||
fontData = g_ubuntu_medium_data;
|
||
fontDataLen = g_ubuntu_medium_size;
|
||
break;
|
||
case kWeightRegular:
|
||
default:
|
||
fontData = g_ubuntu_regular_data;
|
||
fontDataLen = g_ubuntu_regular_size;
|
||
break;
|
||
}
|
||
|
||
// ImGui needs to own a copy of the font data
|
||
void* fontDataCopy = IM_ALLOC(fontDataLen);
|
||
memcpy(fontDataCopy, fontData, fontDataLen);
|
||
|
||
ImFontConfig cfg;
|
||
cfg.FontDataOwnedByAtlas = true; // ImGui will free this
|
||
cfg.OversampleH = 2;
|
||
cfg.OversampleV = 1;
|
||
cfg.PixelSnapH = true;
|
||
|
||
// Include default ASCII + Latin, Latin Extended (for Spanish/multilingual),
|
||
// Cyrillic (for Russian), Greek, plus arrows (⇄ U+21C4),
|
||
// math (≈ U+2248), general punctuation (— U+2014, … U+2026, etc.)
|
||
static const ImWchar glyphRanges[] = {
|
||
0x0020, 0x00FF, // Basic Latin + Latin-1 Supplement
|
||
0x0100, 0x024F, // Latin Extended-A + Latin Extended-B (Spanish, etc.)
|
||
0x0370, 0x03FF, // Greek and Coptic
|
||
0x0400, 0x04FF, // Cyrillic (Russian, Ukrainian, etc.)
|
||
0x0500, 0x052F, // Cyrillic Supplement
|
||
0x2000, 0x206F, // General Punctuation (em dash, ellipsis, etc.)
|
||
0x2190, 0x21FF, // Arrows (includes ⇄ U+21C4, ↻ U+21BB)
|
||
0x2200, 0x22FF, // Mathematical Operators (includes ≈ U+2248)
|
||
0x2600, 0x26FF, // Miscellaneous Symbols (includes ⛏ U+26CF)
|
||
0,
|
||
};
|
||
cfg.GlyphRanges = glyphRanges;
|
||
|
||
// Create a unique name for this font variant
|
||
snprintf(cfg.Name, sizeof(cfg.Name), "Ubuntu %s %.0fpx",
|
||
weight == kWeightLight ? "Light" :
|
||
weight == kWeightMedium ? "Medium" : "Regular",
|
||
size);
|
||
|
||
ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, fontDataLen, size, &cfg);
|
||
|
||
if (font) {
|
||
DEBUG_LOGF("Typography: Loaded %s (%.0fpx) as '%s'\n", name, size, cfg.Name);
|
||
|
||
// Merge CJK fallback glyphs (Chinese/Japanese/Korean) from subset font
|
||
if (g_noto_cjk_subset_size > 0) {
|
||
void* cjkCopy = IM_ALLOC(g_noto_cjk_subset_size);
|
||
memcpy(cjkCopy, g_noto_cjk_subset_data, g_noto_cjk_subset_size);
|
||
|
||
ImFontConfig cjkCfg;
|
||
cjkCfg.FontDataOwnedByAtlas = true;
|
||
cjkCfg.MergeMode = true; // merge into the font we just loaded
|
||
cjkCfg.OversampleH = 1;
|
||
cjkCfg.OversampleV = 1;
|
||
cjkCfg.PixelSnapH = true;
|
||
cjkCfg.GlyphMinAdvanceX = 0;
|
||
// CJK Unified Ideographs + Hiragana + Katakana + Hangul + fullwidth punctuation
|
||
static const ImWchar cjkRanges[] = {
|
||
0x2E80, 0x2FDF, // CJK Radicals
|
||
0x3000, 0x30FF, // CJK Symbols, Hiragana, Katakana
|
||
0x3100, 0x312F, // Bopomofo
|
||
0x31F0, 0x31FF, // Katakana Extensions
|
||
0x3400, 0x4DBF, // CJK Extension A
|
||
0x4E00, 0x9FFF, // CJK Unified Ideographs
|
||
0xAC00, 0xD7AF, // Hangul Syllables
|
||
0xFF00, 0xFFEF, // Fullwidth Forms
|
||
0,
|
||
};
|
||
cjkCfg.GlyphRanges = cjkRanges;
|
||
snprintf(cjkCfg.Name, sizeof(cjkCfg.Name), "NotoSansCJK %.0fpx (merge)", size);
|
||
|
||
ImFont* mergeResult = io.Fonts->AddFontFromMemoryTTF(cjkCopy, g_noto_cjk_subset_size, size, &cjkCfg);
|
||
if (mergeResult) {
|
||
DEBUG_LOGF("Typography: Merged CJK (%u bytes) into %s OK\n",
|
||
g_noto_cjk_subset_size, name);
|
||
} else {
|
||
DEBUG_LOGF("Typography: WARNING — CJK merge FAILED for %s (size=%u)\n",
|
||
name, g_noto_cjk_subset_size);
|
||
}
|
||
}
|
||
} else {
|
||
DEBUG_LOGF("Typography: Failed to load %s\n", name);
|
||
IM_FREE(fontDataCopy);
|
||
}
|
||
|
||
return font;
|
||
}
|
||
|
||
ImFont* Typography::loadIconFont(ImGuiIO& io, float size, const char* name)
|
||
{
|
||
// ImGui needs to own a copy of the font data
|
||
void* fontDataCopy = IM_ALLOC(g_material_icons_size);
|
||
memcpy(fontDataCopy, g_material_icons_data, g_material_icons_size);
|
||
|
||
ImFontConfig cfg;
|
||
cfg.FontDataOwnedByAtlas = true;
|
||
cfg.OversampleH = 2;
|
||
cfg.OversampleV = 1;
|
||
cfg.PixelSnapH = true;
|
||
cfg.MergeMode = false; // standalone icon font
|
||
cfg.GlyphMinAdvanceX = size; // monospace icons
|
||
|
||
static const ImWchar iconRanges[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 };
|
||
cfg.GlyphRanges = iconRanges;
|
||
|
||
snprintf(cfg.Name, sizeof(cfg.Name), "MDIcons %.0fpx", size);
|
||
|
||
ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, g_material_icons_size, size, &cfg);
|
||
if (font) {
|
||
DEBUG_LOGF("Typography: Loaded icon font %s (%.0fpx)\n", name, size);
|
||
} else {
|
||
DEBUG_LOGF("Typography: Failed to load icon font %s\n", name);
|
||
IM_FREE(fontDataCopy);
|
||
}
|
||
return font;
|
||
}
|
||
|
||
ImFont* Typography::loadPickaxeFont(ImGuiIO& io, float size, const char* name)
|
||
{
|
||
if (g_mdi_pickaxe_subset_size == 0) {
|
||
DEBUG_LOGF("Typography: Pickaxe subset font is empty\n");
|
||
return nullptr;
|
||
}
|
||
|
||
void* fontDataCopy = IM_ALLOC(g_mdi_pickaxe_subset_size);
|
||
memcpy(fontDataCopy, g_mdi_pickaxe_subset_data, g_mdi_pickaxe_subset_size);
|
||
|
||
ImFontConfig cfg;
|
||
cfg.FontDataOwnedByAtlas = true;
|
||
cfg.OversampleH = 2;
|
||
cfg.OversampleV = 1;
|
||
cfg.PixelSnapH = true;
|
||
cfg.MergeMode = false;
|
||
cfg.GlyphMinAdvanceX = size;
|
||
|
||
static const ImWchar pickaxeRange[] = {
|
||
Typography::kPickaxeCodepoint,
|
||
Typography::kPickaxeCodepoint,
|
||
0,
|
||
};
|
||
cfg.GlyphRanges = pickaxeRange;
|
||
|
||
snprintf(cfg.Name, sizeof(cfg.Name), "MDIPickaxe %.0fpx", size);
|
||
|
||
ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, g_mdi_pickaxe_subset_size, size, &cfg);
|
||
if (font) {
|
||
DEBUG_LOGF("Typography: Loaded pickaxe font %s (%.0fpx)\n", name, size);
|
||
} else {
|
||
DEBUG_LOGF("Typography: Failed to load pickaxe font %s\n", name);
|
||
IM_FREE(fontDataCopy);
|
||
}
|
||
return font;
|
||
}
|
||
|
||
ImFont* Typography::pickaxeFontForSize(float size) const
|
||
{
|
||
if (size <= 15.0f) return pickaxeSmall();
|
||
if (size <= 20.0f) return pickaxeMed();
|
||
if (size <= 32.0f) return pickaxeLarge();
|
||
return pickaxeXL();
|
||
}
|
||
|
||
ImFont* Typography::getFont(TypeStyle style) const
|
||
{
|
||
int index = static_cast<int>(style);
|
||
if (index >= 0 && index < kNumStyles && fonts_[index]) {
|
||
return fonts_[index];
|
||
}
|
||
// Return default font if not found
|
||
return ImGui::GetIO().Fonts->Fonts.Size > 0 ?
|
||
ImGui::GetIO().Fonts->Fonts[0] : nullptr;
|
||
}
|
||
|
||
const TypeSpec& Typography::getSpec(TypeStyle style) const
|
||
{
|
||
const TypeSpec* specs = getTypeSpecs();
|
||
int index = static_cast<int>(style);
|
||
if (index >= 0 && index < kNumStyles) {
|
||
return specs[index];
|
||
}
|
||
// Return Body1 as default
|
||
return specs[static_cast<int>(TypeStyle::Body1)];
|
||
}
|
||
|
||
void Typography::pushFont(TypeStyle style) const
|
||
{
|
||
ImGui::PushFont(getFont(style));
|
||
}
|
||
|
||
void Typography::popFont() const
|
||
{
|
||
ImGui::PopFont();
|
||
}
|
||
|
||
// ============================================================================
|
||
// Text Rendering Helpers
|
||
// ============================================================================
|
||
|
||
void Typography::text(TypeStyle style, const char* text) const
|
||
{
|
||
const TypeSpec& spec = getSpec(style);
|
||
pushFont(style);
|
||
|
||
if (spec.uppercase) {
|
||
// Convert to uppercase
|
||
std::string upper;
|
||
upper.reserve(strlen(text));
|
||
for (const char* p = text; *p; ++p) {
|
||
upper += static_cast<char>(std::toupper(static_cast<unsigned char>(*p)));
|
||
}
|
||
ImGui::TextUnformatted(upper.c_str());
|
||
} else {
|
||
ImGui::TextUnformatted(text);
|
||
}
|
||
|
||
popFont();
|
||
}
|
||
|
||
void Typography::textWrapped(TypeStyle style, const char* text) const
|
||
{
|
||
const TypeSpec& spec = getSpec(style);
|
||
pushFont(style);
|
||
|
||
if (spec.uppercase) {
|
||
std::string upper;
|
||
upper.reserve(strlen(text));
|
||
for (const char* p = text; *p; ++p) {
|
||
upper += static_cast<char>(std::toupper(static_cast<unsigned char>(*p)));
|
||
}
|
||
ImGui::TextWrapped("%s", upper.c_str());
|
||
} else {
|
||
ImGui::TextWrapped("%s", text);
|
||
}
|
||
|
||
popFont();
|
||
}
|
||
|
||
void Typography::textColored(TypeStyle style, ImU32 color, const char* text) const
|
||
{
|
||
const TypeSpec& spec = getSpec(style);
|
||
pushFont(style);
|
||
|
||
ImVec4 colorVec = ImGui::ColorConvertU32ToFloat4(color);
|
||
|
||
if (spec.uppercase) {
|
||
std::string upper;
|
||
upper.reserve(strlen(text));
|
||
for (const char* p = text; *p; ++p) {
|
||
upper += static_cast<char>(std::toupper(static_cast<unsigned char>(*p)));
|
||
}
|
||
ImGui::TextColored(colorVec, "%s", upper.c_str());
|
||
} else {
|
||
ImGui::TextColored(colorVec, "%s", text);
|
||
}
|
||
|
||
popFont();
|
||
}
|
||
|
||
} // namespace material
|
||
} // namespace ui
|
||
} // namespace dragonx
|