Added glyph ranges manager for ImGuiPresenter, added ability to use glyph ranges for Inspector's font (#1936)

* Added glyph manager for ImGuiPresenter

* renamed glyph ranges constants

* for static code conduct

* for code of conduct 2

* removed some duplication and better format

* removed extra space
This commit is contained in:
Purple 2024-05-29 21:56:03 +06:00 committed by GitHub
parent ccf31892a0
commit eb0e6ef50d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 254 additions and 31 deletions

View File

@ -16,6 +16,7 @@
NS_AX_EXT_BEGIN
namespace
{
uint32_t fourccValue(std::string_view str)
@ -367,17 +368,13 @@ void ImGuiPresenter::loadCustomFonts(void* ud)
auto contentZoomFactor = thiz->_contentZoomFactor;
for (auto& fontInfo : thiz->_fontsInfoMap)
{
const ImWchar* imChars = nullptr;
switch (fontInfo.second.glyphRange)
{
case CHS_GLYPH_RANGE::GENERAL:
imChars = imFonts->GetGlyphRangesChineseSimplifiedCommon();
break;
case CHS_GLYPH_RANGE::FULL:
imChars = imFonts->GetGlyphRangesChineseFull();
break;
default:;
}
auto& imChars = fontInfo.second.glyphRanges;
// if the user has explicitly called `removeGlyphRanges` or replaced with new ranges
if (imChars && !thiz->_usedGlyphRanges.contains((uintptr_t)imChars))
imChars = nullptr;
if (imChars == nullptr && thiz->_glyphRanges.contains(fontInfo.second.glyphRangesId))
imChars = thiz->_glyphRanges.at(fontInfo.second.glyphRangesId).data();
auto fontData = FileUtils::getInstance()->getDataFromFile(fontInfo.first);
AXASSERT(!fontData.isNull(), "Cannot load font for IMGUI");
@ -388,6 +385,8 @@ void ImGuiPresenter::loadCustomFonts(void* ud)
imFonts->AddFontFromMemoryTTF(buffer, bufferSize, fontInfo.second.fontSize * contentZoomFactor, nullptr,
imChars);
}
// the temporary bucket gets emptied out
thiz->_eraseGlyphRanges.clear();
}
float ImGuiPresenter::enableDPIScale(float userScale)
@ -424,11 +423,54 @@ void ImGuiPresenter::setViewResolution(float width, float height)
ImGui_ImplAx_SetViewResolution(width, height);
}
void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, CHS_GLYPH_RANGE glyphRange)
void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, GLYPH_RANGES glyphRange)
{
addGlyphRanges(glyphRange);
std::string_view glyphId = getGlyphRangesId(glyphRange);
addFont(fontFile, fontSize, glyphId);
}
void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, std::string_view glyphRangeId)
{
auto it = _glyphRanges.find(glyphRangeId);
if (it == _glyphRanges.end())
{
addFont(fontFile, fontSize, std::vector<ImWchar>(0));
return;
}
if (FileUtils::getInstance()->isFileExistInternal(fontFile))
{
ImWchar* imChars = it->second.data();
bool isDirty = _fontsInfoMap.emplace(fontFile, FontInfo{fontSize, imChars, std::string(glyphRangeId)}).second;
isDirty |=
_usedGlyphRanges.emplace((uintptr_t)imChars).second || _fontsInfoMap.at(fontFile).glyphRanges != imChars;
if (isDirty)
ImGui_ImplAx_SetDeviceObjectsDirty();
}
}
void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, const std::vector<ImWchar>& glyphRanges)
{
addFont(fontFile, fontSize, fontFile, glyphRanges);
}
void ImGuiPresenter::addFont(std::string_view fontFile,
float fontSize,
std::string_view glyphRangesId,
const std::vector<ImWchar>& glyphRanges)
{
if (FileUtils::getInstance()->isFileExistInternal(fontFile))
{
if (_fontsInfoMap.emplace(fontFile, FontInfo{fontSize, glyphRange}).second)
ImWchar* imChars = nullptr;
if (!glyphRanges.empty())
imChars = addGlyphRanges(glyphRangesId, glyphRanges);
bool isDirty = _fontsInfoMap.emplace(fontFile, FontInfo{fontSize, imChars, std::string(glyphRangesId)}).second;
isDirty |= imChars && (_usedGlyphRanges.emplace((uintptr_t)imChars).second ||
_fontsInfoMap.at(fontFile).glyphRanges != imChars);
if (isDirty)
ImGui_ImplAx_SetDeviceObjectsDirty();
}
}
@ -853,18 +895,110 @@ void ImGuiPresenter::setLabelColor(Label* label, ImGuiCol col)
setLabelColor(label, ImGui::GetStyleColorVec4(col));
}
ImWchar* ImGuiPresenter::addGlyphRanges(GLYPH_RANGES glyphRange)
{
static std::unordered_map<GLYPH_RANGES, size_t> _glyph_ranges_size;
auto imFonts = ImGui::GetIO().Fonts;
const ImWchar* imChars;
switch (glyphRange)
{
case GLYPH_RANGES::DEFAULT:
imChars = imFonts->GetGlyphRangesDefault();
break;
case GLYPH_RANGES::GREEK:
imChars = imFonts->GetGlyphRangesGreek();
break;
case GLYPH_RANGES::KOREAN:
imChars = imFonts->GetGlyphRangesKorean();
break;
case GLYPH_RANGES::CHINESE_GENERAL:
imChars = imFonts->GetGlyphRangesChineseSimplifiedCommon();
break;
case GLYPH_RANGES::CHINESE_FULL:
imChars = imFonts->GetGlyphRangesChineseFull();
break;
case GLYPH_RANGES::JAPANESE:
imChars = imFonts->GetGlyphRangesJapanese();
break;
case GLYPH_RANGES::CYRILLIC:
imChars = imFonts->GetGlyphRangesCyrillic();
break;
case GLYPH_RANGES::THAI:
imChars = imFonts->GetGlyphRangesThai();
break;
case GLYPH_RANGES::VIETNAMESE:
imChars = imFonts->GetGlyphRangesVietnamese();
break;
default:
return nullptr;
}
size_t imCharsSize = 0;
if (_glyph_ranges_size.contains(glyphRange))
imCharsSize = _glyph_ranges_size[glyphRange];
else
{
// must always end with 0
while (imChars[imCharsSize] != 0)
imCharsSize++;
imCharsSize += 1;
_glyph_ranges_size[glyphRange] = imCharsSize;
}
auto glyphId = getGlyphRangesId(glyphRange);
return addGlyphRanges(glyphId, std::vector<ImWchar>(imChars, imChars + imCharsSize));
}
ImWchar* ImGuiPresenter::addGlyphRanges(std::string_view key, const std::vector<ImWchar>& ranges)
{
auto it = glyphRanges.find(key);
// the pointer must be persistant, do not replace
if (it != glyphRanges.end())
return it->second.data();
it = glyphRanges.emplace(key, ranges).first; // glyphRanges[key] = ranges;
auto it = _glyphRanges.find(key);
// store in our temporary bucket if already exists...
if (it != _glyphRanges.end())
{
// so that `loadCustomFonts` can look for the new "replaced" glyph ranges
_usedGlyphRanges.erase((uintptr_t)it->second.data());
// probably automatically gets *moved* but to make our intention more clear
_eraseGlyphRanges.push_back(std::move(it->second));
}
it = _glyphRanges.emplace(key, ranges).first; // _glyphRanges[key] = ranges;
if (ranges.empty())
it->second.push_back(0);
// the `addFont` will call `ImGui_ImplAx_SetDeviceObjectsDirty` if everything is okay,
// no need to call it if no font is using the glyph ranges...
return it->second.data();
}
void ImGuiPresenter::removeGlyphRanges(std::string_view key)
{
auto removeGlyphRange = _glyphRanges.find(key);
if (removeGlyphRange == _glyphRanges.end())
return;
auto usedCount = _usedGlyphRanges.size();
_usedGlyphRanges.erase((uintptr_t)removeGlyphRange->second.data());
_eraseGlyphRanges.push_back(std::move(removeGlyphRange->second));
_glyphRanges.erase(key);
// update the fonts to not use the glyph ranges since user wants to remove it for some reason..
if (_usedGlyphRanges.size() != usedCount)
ImGui_ImplAx_SetDeviceObjectsDirty();
}
void ImGuiPresenter::clearGlyphRanges()
{
auto usedCount = _usedGlyphRanges.size();
for (auto& glyphRange : _glyphRanges)
{
_usedGlyphRanges.erase((uintptr_t)glyphRange.second.data());
_eraseGlyphRanges.push_back(std::move(glyphRange.second));
}
_glyphRanges.clear();
if (_usedGlyphRanges.size() != usedCount)
ImGui_ImplAx_SetDeviceObjectsDirty();
}
void ImGuiPresenter::mergeFontGlyphs(ImFont* dst, ImFont* src, ImWchar start, ImWchar end)
{
if (!dst || !src || start > end)
@ -901,4 +1035,31 @@ int ImGuiPresenter::getCCRefId(Object* p)
return (int)hash;
}
std::string_view ImGuiPresenter::getGlyphRangesId(GLYPH_RANGES glyphRanges)
{
switch (glyphRanges)
{
case GLYPH_RANGES::DEFAULT:
return GLYPH_RANGES_DEFAULT_ID;
case GLYPH_RANGES::GREEK:
return GLYPH_RANGES_GREEK_ID;
case GLYPH_RANGES::KOREAN:
return GLYPH_RANGES_KOREAN_ID;
case GLYPH_RANGES::CHINESE_GENERAL:
return GLYPH_RANGES_CHINESE_GENERAL_ID;
case GLYPH_RANGES::CHINESE_FULL:
return GLYPH_RANGES_CHINESE_FULL_ID;
case GLYPH_RANGES::JAPANESE:
return GLYPH_RANGES_JAPANESE_ID;
case GLYPH_RANGES::CYRILLIC:
return GLYPH_RANGES_CYRILLIC_ID;
case GLYPH_RANGES::THAI:
return GLYPH_RANGES_THAI_ID;
case GLYPH_RANGES::VIETNAMESE:
return GLYPH_RANGES_VIETNAMESE_ID;
default:
return "";
}
}
NS_AX_EXT_END

View File

@ -20,13 +20,33 @@ class ImGuiPresenter
void cleanup();
public:
enum class CHS_GLYPH_RANGE
inline static const std::string_view GLYPH_RANGES_DEFAULT_ID = "__DEFAULT_GLYPH__";
inline static const std::string_view GLYPH_RANGES_GREEK_ID = "__GREEK_GLYPH__";
inline static const std::string_view GLYPH_RANGES_KOREAN_ID = "__KOREAN_GLYPH__";
inline static const std::string_view GLYPH_RANGES_CHINESE_GENERAL_ID = "__CHINESE_SIMPLIFIED_COMMON_GLYPH__";
inline static const std::string_view GLYPH_RANGES_CHINESE_FULL_ID = "__CHINESE_FULL_GLYPH__";
inline static const std::string_view GLYPH_RANGES_JAPANESE_ID = "__JAPANESE_GLYPH__";
inline static const std::string_view GLYPH_RANGES_CYRILLIC_ID = "__CYRILLIC_GLYPH__";
inline static const std::string_view GLYPH_RANGES_THAI_ID = "__THAI_GLYPH__";
inline static const std::string_view GLYPH_RANGES_VIETNAMESE_ID = "__VIETNAMESE_GLYPH__";
// predefined glyph ranges by imgui
enum class GLYPH_RANGES
{
NONE,
GENERAL,
FULL
DEFAULT,
GREEK,
KOREAN,
CHINESE_GENERAL,
CHINESE_FULL,
JAPANESE,
CYRILLIC,
THAI,
VIETNAMESE
};
static std::string_view getGlyphRangesId(GLYPH_RANGES glyphRanges);
enum
{
DEFAULT_FONT_SIZE = 13 // see imgui.cpp
@ -57,8 +77,30 @@ public:
/// <param name="fontFile"></param>
/// <param name="glyphRange"></param>
void addFont(std::string_view fontFile,
float fontSize = DEFAULT_FONT_SIZE,
CHS_GLYPH_RANGE glyphRange = CHS_GLYPH_RANGE::NONE);
float fontSize = DEFAULT_FONT_SIZE,
GLYPH_RANGES glyphRange = GLYPH_RANGES::NONE);
/// <summary>
/// Add ImGui font with contentZoomFactor and use pre-existing glyph range for the specified font
/// </summary>
/// <param name="fontFile"></param>
/// <param name="glyphRanges">The glyph range vector must end with 0 and it should be included in the size</param>
void addFont(std::string_view fontFile, float fontSize, std::string_view glyphRangesId);
/// <summary>
/// Add ImGui font with contentZoomFactor and use specified custom glyph range for the specified font
/// </summary>
/// <param name="fontFile"></param>
/// <param name="glyphRange">The glyph range vector must end with 0 and it should be included in the size</param>
void addFont(std::string_view fontFile, float fontSize, const std::vector<ImWchar>& glyphRanges);
/// <summary>
/// Add ImGui font with contentZoomFactor and use custom glyph range and specify a custom id
/// </summary>
/// <param name="fontFile"></param>
/// <param name="glyphRangesId">Custom Lookup Id</param>
/// <param name="glyphRanges">The glyph range vector must end with 0 and it should be included in the size</param>
void addFont(std::string_view fontFile,
float fontSize,
std::string_view glyphRangesId,
const std::vector<ImWchar>& glyphRanges);
void removeFont(std::string_view fontFile);
void clearFonts();
@ -124,7 +166,10 @@ public:
static void setLabelColor(Label* label, bool disabled = false);
static void setLabelColor(Label* label, ImGuiCol col);
ImWchar* addGlyphRanges(GLYPH_RANGES glyphRange);
ImWchar* addGlyphRanges(std::string_view key, const std::vector<ImWchar>& ranges);
void removeGlyphRanges(std::string_view key);
void clearGlyphRanges();
static void mergeFontGlyphs(ImFont* dst, ImFont* src, ImWchar start, ImWchar end);
int getCCRefId(Object* p);
@ -151,7 +196,11 @@ private:
std::unordered_map<Object*, int> usedCCRefIdMap;
// cocos objects should be retained until next frame
Vector<Object*> usedCCRef;
hlookup::string_map<std::vector<ImWchar>> glyphRanges;
hlookup::string_map<std::vector<ImWchar>> _glyphRanges;
std::unordered_set<uintptr_t> _usedGlyphRanges; // there should be one intance of "each glyph ranges"
// temporarily stores the current erased/replaced ranges, gets cleared in the next `loadCustomFonts` interation
std::vector<std::vector<ImWchar>> _eraseGlyphRanges;
float _contentZoomFactor = 1.0f;
@ -162,7 +211,8 @@ private:
struct FontInfo
{
float fontSize;
CHS_GLYPH_RANGE glyphRange;
ImWchar* glyphRanges;
std::string glyphRangesId;
};
hlookup::string_map<FontInfo> _fontsInfoMap;

View File

@ -9,6 +9,9 @@ Sync from https://github.com/Xrysnow/cocos2d-x-imgui and do a little changes
* Use ```FOURCC``` for key of ImGui render loop
* Add dpi scale support, use ```ImGuiPresenter::getInstance()->enableDPIScale();```
* Easy font manager, stable API ```addFont,removeFont,clearFonts``` to manage ImGui fonts, with ImGui API, very hard to do correctly.
* Easy to add/use custom or imgui pre-defined glyph ranges ```ImGuiPresenter::GLYPH_RANGES,addGlyphRanges,removeGlyphRanges```
and then specify the glyph ranges id while calling `addFont`
to use those with specific font.
## How to use
```cpp
@ -24,7 +27,7 @@ public:
ImGuiPresenter::getInstance()->addFont(R"(C:\Windows\Fonts\msyh.ttc)");
/* For Simplified Chinese support, please use:
ImGuiPresenter::getInstance()->addFont(R"(C:\Windows\Fonts\msyh.ttc)", ImGuiPresenter::DEFAULT_FONT_SIZE,
ImGuiPresenter::CHS_GLYPH_RANGE::GENERAL);
ImGuiPresenter::GLYPH_RANGES::CHINESE_GENERAL);
*/
ImGuiPresenter::getInstance()->enableDPIScale(); // enable dpi scale for 4K display support, depends at least one valid ttf/ttc font was added.
ImGuiPresenter::getInstance()->addRenderLoop("#im01", AX_CALLBACK_0(GameScene::onImGuiDraw, this), this);

View File

@ -177,9 +177,15 @@ void Inspector::setFontSize(float fontSize)
_fontSize = fontSize;
}
void Inspector::setFontGlyphId(std::string_view glyphId)
{
_fontGlyphId = std::string(glyphId);
}
void Inspector::init()
{
_fontPath = "fonts/arial.ttf";
_fontSize = ImGuiPresenter::DEFAULT_FONT_SIZE;
addPropertyHandler("__NODE__", std::make_unique<InspectorNodePropertyHandler>());
addPropertyHandler("__SPRITE__", std::make_unique<InspectorSpritePropertyHandler>());
@ -399,7 +405,7 @@ void Inspector::openForScene(Scene* target)
}
auto* presenter = ImGuiPresenter::getInstance();
presenter->addFont(FileUtils::getInstance()->fullPathForFilename(_fontPath), _fontSize);
presenter->addFont(FileUtils::getInstance()->fullPathForFilename(_fontPath), _fontSize, _fontGlyphId);
presenter->enableDPIScale();
presenter->addRenderLoop("#insp", AX_CALLBACK_0(Inspector::mainLoop , this), target);
}

View File

@ -65,10 +65,12 @@ class Inspector
void setAutoAddToScenes(bool autoAdd);
std::string_view getFontPath() { return _fontPath; }
float getFontSize() { return _fontSize; }
std::string_view getFontPath() const { return _fontPath; }
float getFontSize() const { return _fontSize; }
std::string_view getFontGlyphId() const { return _fontGlyphId; }
void setFontPath(std::string_view fontPath);
void setFontSize(float fontSize);
void setFontGlyphId(std::string_view glyphId);
private:
void init();
@ -86,7 +88,8 @@ class Inspector
bool _autoAddToScenes = false;
std::string _fontPath;
float _fontSize = 13; // ImGuiPresenter.h
float _fontSize;
std::string _fontGlyphId;
};
NS_AX_EXT_END

View File

@ -221,7 +221,7 @@ void SDFGen::open(ax::Scene* scene)
ImGuiPresenter::getInstance()->addFont(defaultFontFile);
/* For Simplified Chinese support, please use:
ImGuiPresenter::getInstance()->addFont(R"(C:\Windows\Fonts\msyh.ttc)", ImGuiPresenter::DEFAULT_FONT_SIZE,
ImGuiPresenter::CHS_GLYPH_RANGE::GENERAL);
ImGuiPresenter::GLYPH_RANGES::CHINESE_GENERAL);
*/
ImGuiPresenter::getInstance()->enableDPIScale(); // enable dpi scale for 4K display support, depends at least one
// valid ttf/ttc font was added.