Skip to content

Commit

Permalink
Refactor fonts manager (#3401)
Browse files Browse the repository at this point in the history
* Refactor Fonts Manager

* Add function to change fonts

* fix

* CR Fix
  • Loading branch information
Grantim committed Sep 23, 2024
1 parent a0b765c commit ebc4c67
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 122 deletions.
212 changes: 97 additions & 115 deletions source/MRViewer/MRRibbonFontManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,55 @@
#include "MRViewer.h"
#include "MRRibbonMenu.h"
#include "MRPch/MRSpdlog.h"
#include "MRCommandLoop.h"
#include "backends/imgui_impl_opengl3.h"

namespace MR
{

static void loadFontChecked( const char* filename, float size_pixels, const ImFontConfig* font_cfg = nullptr, const ImWchar* glyph_ranges = nullptr )
static ImFont* loadFontChecked( const char* filename, float size_pixels, const ImFontConfig* font_cfg = nullptr, const ImWchar* glyph_ranges = nullptr )
{
if ( !ImGui::GetIO().Fonts->AddFontFromFileTTF( filename, size_pixels, font_cfg, glyph_ranges ) )
auto font = ImGui::GetIO().Fonts->AddFontFromFileTTF( filename, size_pixels, font_cfg, glyph_ranges );
if ( !font )
{
assert( false && "Failed to load font!" );
spdlog::error( "Failed to load font from `{}`.", filename );

ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF( droid_sans_compressed_data,
font =ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF( droid_sans_compressed_data,
droid_sans_compressed_size, size_pixels, font_cfg, glyph_ranges );
}
return font;
}

RibbonFontManager::RibbonFontManager()
{
fontPaths_ =
{
#ifndef __EMSCRIPTEN__
SystemPath::getFontsDirectory() / "NotoSansSC-Regular.otf",
#else
SystemPath::getFontsDirectory() / "NotoSans-Regular.ttf",
#endif
SystemPath::getFontsDirectory() / "NotoSans-SemiBold.ttf",
SystemPath::getFontsDirectory() / "NotoSansMono-Regular.ttf",
SystemPath::getFontsDirectory() / "fa-solid-900.ttf"
};
}

void RibbonFontManager::loadAllFonts( ImWchar* charRanges, float scaling )
{
fonts_ = {};
fonts_ = {
FontData{.fontFile = FontFile::Regular},
FontData{.fontFile = FontFile::Regular},
FontData{.fontFile = FontFile::SemiBold},
FontData{.fontFile = FontFile::Icons},
FontData{.fontFile = FontFile::Regular},
FontData{.fontFile = FontFile::SemiBold},
FontData{.fontFile = FontFile::SemiBold},
FontData{.fontFile = FontFile::Monospace}
};

updateFontsScaledOffset_( scaling );

const ImWchar iconRanges[] = { 0xe005, 0xf8ff, 0 };

Expand All @@ -50,7 +80,7 @@ void RibbonFontManager::loadAllFonts( ImWchar* charRanges, float scaling )

ImFont* RibbonFontManager::getFontByType( FontType type ) const
{
return fonts_[int( type )];
return fonts_[int( type )].fontPtr;
}

float RibbonFontManager::getFontSizeByType( FontType type )
Expand Down Expand Up @@ -80,11 +110,20 @@ float RibbonFontManager::getFontSizeByType( FontType type )

std::filesystem::path RibbonFontManager::getMenuFontPath() const
{
#ifndef __EMSCRIPTEN__
return SystemPath::getFontsDirectory() / "NotoSansSC-Regular.otf";
#else
return SystemPath::getFontsDirectory() / "NotoSans-Regular.ttf";
#endif
return fontPaths_[int( FontFile::Regular )];
}

void RibbonFontManager::setNewFontPaths( const FontFilePaths& paths )
{
fontPaths_ = paths;
if ( auto menu = getViewerInstance().getMenuPlugin() )
{
CommandLoop::appendCommand( [menu] ()
{
menu->reload_font();
ImGui_ImplOpenGL3_DestroyDeviceObjects(); // needed to update font
} );
}
}

ImFont* RibbonFontManager::getFontByTypeStatic( FontType type )
Expand All @@ -100,128 +139,71 @@ void RibbonFontManager::initFontManagerInstance( RibbonFontManager* ribbonFontMa
getFontManagerInstance_() = ribbonFontManager;
}

std::filesystem::path RibbonFontManager::getMenuLatinSemiBoldFontPath_() const
{
return getMenuFontPath().parent_path() / "NotoSans-SemiBold.ttf";
}

MR::RibbonFontManager*& RibbonFontManager::getFontManagerInstance_()
{
static RibbonFontManager* instance{ nullptr };
return instance;
}

void RibbonFontManager::loadFont_( FontType type, const ImWchar* ranges, float scaling )
void RibbonFontManager::updateFontsScaledOffset_( float scaling )
{
float fontSize = getFontSizeByType( type ) * scaling;

if ( type == FontType::Default )
ImGuiIO& io = ImGui::GetIO();
const ImWchar wRange[] = { 0x0057, 0x0057, 0 }; // `W` symbol
std::array<ImFont*, int( FontType::Count )> localFonts;
for ( int i = 0; i < int( FontType::Count ); ++i )
{
auto fontPath = getMenuFontPath();
ImFontConfig config;
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;
#ifndef __EMSCRIPTEN__
config.GlyphOffset = ImVec2( 0, -4 * scaling );
#else
config.GlyphOffset = ImVec2( 0, -3 * scaling );
#endif
loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
}
else if ( type == FontType::Icons )
{
ImFontConfig config;
config.GlyphMinAdvanceX = fontSize; // Use if you want to make the icon monospaced
auto fontPath = SystemPath::getFontsDirectory() / "fa-solid-900.ttf";
loadFontChecked( utf8string( fontPath ).c_str(), fontSize, &config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
}
else if ( type == FontType::Small )
{
auto fontPath = getMenuFontPath();
ImFontConfig config;
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;
#ifndef __EMSCRIPTEN__
config.GlyphOffset = ImVec2( 0, -3 * scaling );
#else
config.GlyphOffset = ImVec2( 0, -2 * scaling );
#endif
loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
}
else if ( type == FontType::SemiBold )
{
auto fontPath = getMenuLatinSemiBoldFontPath_();
ImFontConfig config;
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;
// "- 3 * scaling" eliminates shift of the font in order to render this font in text fields properly
config.GlyphOffset = ImVec2( 0, - 3 * scaling );
loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
}
else if ( type == FontType::Big )
{
auto fontPath = getMenuFontPath();
auto& font = fonts_[int( i )];
auto fontPath = fontPaths_[int( font.fontFile )];

ImFontConfig config;
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;
config.GlyphOffset = ImVec2( 0, -4 * scaling );
loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
if ( i != int( FontType::Icons ) )
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;

auto fontSize = getFontSizeByType( FontType( i ) ) * scaling;
localFonts[i] = io.Fonts->AddFontFromFileTTF( utf8string( fontPath ).c_str(), fontSize, &config, wRange );
}
else if ( type == FontType::BigSemiBold )
io.Fonts->Build();
for ( int i = 0; i < int( FontType::Count ); ++i )
{
auto fontPath = getMenuLatinSemiBoldFontPath_();
ImFontConfig config;
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;
config.GlyphOffset = ImVec2( 0, -4 * scaling );
loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
auto* lFont = localFonts[i];
if ( !lFont )
continue;
if ( lFont->Glyphs.size() != 1 )
continue;
const auto& glyph = lFont->Glyphs.back();

auto& fontRef = fonts_[int( i )];
auto fontSize = getFontSizeByType( FontType( i ) ) * scaling;
Box2f box;
box.include( Vector2f( glyph.X0, glyph.Y0 ) );
box.include( Vector2f( glyph.X1, glyph.Y1 ) );
fontRef.scaledOffset = 0.5f * ( Vector2f::diagonal( fontSize ) - box.size() ) - box.min;
fontRef.scaledOffset.x = std::floor( fontRef.scaledOffset.x );
fontRef.scaledOffset.y = std::round( fontRef.scaledOffset.y );
}
else if ( type == FontType::Headline )
io.Fonts->Clear();
}

void RibbonFontManager::loadFont_( FontType type, const ImWchar* ranges, float scaling )
{
float fontSize = getFontSizeByType( type ) * scaling;
auto& font = fonts_[int( type )];
auto fontPath = fontPaths_[int( font.fontFile )];

ImFontConfig config;
if ( type == FontType::Icons )
{
auto fontPath = getMenuLatinSemiBoldFontPath_();
ImFontConfig config;
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;
config.GlyphOffset = ImVec2( 0, -4 * scaling );
loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
config.GlyphMinAdvanceX = fontSize; // Use if you want to make the icon monospaced
}
else if ( type == FontType::Monospace )
else
{
auto fontPath = SystemPath::getFontsDirectory() / "NotoSansMono-Regular.ttf";
ImFontConfig config;
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bitmap;
config.GlyphOffset = ImVec2( 1 * scaling, -2 * scaling );
loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
fonts_[int( type )] = ImGui::GetIO().Fonts->Fonts.back();
config.GlyphOffset = ImVec2( font.scaledOffset );
}
}

void RibbonFontManager::loadDefaultFont_( float fontSize, float yOffset )
{
ImFontConfig config;
config.GlyphOffset = ImVec2( 0, yOffset );
#ifndef __EMSCRIPTEN__
config.OversampleH = 7;
config.OversampleV = 7;
#endif
ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF( droid_sans_compressed_data,
droid_sans_compressed_size, fontSize,
&config);
font.fontPtr = loadFontChecked(
utf8string( fontPath ).c_str(), fontSize,
&config, ranges );
}

void RibbonFontManager::addCustomGlyphs_( FontType font, float scaling, std::vector<CustomGlyph>& glyphs )
Expand All @@ -236,7 +218,7 @@ void RibbonFontManager::addCustomGlyphs_( FontType font, float scaling, std::vec
int height = int( std::floor( getFontSizeByType( font ) * scaling ) );
int width = int( std::round( height * relWidth ) );

int index = ImGui::GetIO().Fonts->AddCustomRectFontGlyph( fonts_[int( font )], ch, width, height, float( width ) );
int index = ImGui::GetIO().Fonts->AddCustomRectFontGlyph( fonts_[int( font )].fontPtr, ch, width, height, float( width ) );
auto renderWrapper = [index, func = std::move( render )]( unsigned char* texData, int texW )
{
const ImFontAtlasCustomRect* rect = ImGui::GetIO().Fonts->GetCustomRectByIndex(index);
Expand Down
39 changes: 32 additions & 7 deletions source/MRViewer/MRRibbonFontManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ class MRVIEWER_CLASS RibbonFontManager
Count
};

// Unique fonts that are used for different FontTypes
enum class FontFile
{
Regular,
SemiBold,
Monospace,
Icons,
Count
};

using FontFilePaths = std::array<std::filesystem::path, size_t( FontFile::Count )>;

MRVIEWER_API RibbonFontManager();

/// load all fonts using in ribbon menu
MRVIEWER_API void loadAllFonts( ImWchar* charRanges, float scaling );

Expand All @@ -36,6 +50,13 @@ class MRVIEWER_CLASS RibbonFontManager
/// get ribbon menu font path
MRVIEWER_API std::filesystem::path getMenuFontPath() const;

/// returns list of all font paths
const FontFilePaths& getAllFontPaths() const { return fontPaths_; }

/// sets new fonts paths
/// note that it will trigger reload font
MRVIEWER_API void setNewFontPaths( const FontFilePaths& paths );

/// get font by font type
/// (need to avoid dynamic cast menu to ribbon menu)
MRVIEWER_API static ImFont* getFontByTypeStatic( FontType type );
Expand All @@ -45,18 +66,22 @@ class MRVIEWER_CLASS RibbonFontManager
MRVIEWER_API static void initFontManagerInstance( RibbonFontManager* ribbonFontManager );

private:
std::array<ImFont*, size_t( FontType::Count )> fonts_{ nullptr,nullptr,nullptr,nullptr };

/// get ribbon latin menu font path
std::filesystem::path getMenuLatinSemiBoldFontPath_() const;
FontFilePaths fontPaths_;
struct FontData
{
FontFile fontFile{ FontFile::Regular }; // what file type to use for this font
Vector2f scaledOffset; // offset that is used for each glyph while creating atlas (updates in `updateFontsScaledOffset_`), should respect font size with scaling
ImFont* fontPtr{ nullptr }; // pointer to loaded font, nullptr means that font was not loaded
};
std::array<FontData, size_t( FontType::Count )> fonts_;

/// get pointer to instance of this class (if it exists)
static RibbonFontManager*& getFontManagerInstance_();

void loadFont_( FontType type, const ImWchar* ranges, float scaling );
/// calculates font glyph shift
void updateFontsScaledOffset_( float scaling );

/// load default font (droid_sans)
void loadDefaultFont_( float fontSize, float yOffset = 0.0f );
void loadFont_( FontType type, const ImWchar* ranges, float scaling );

struct CustomGlyph
{
Expand Down

0 comments on commit ebc4c67

Please sign in to comment.