159 lines
4.4 KiB
C++
159 lines
4.4 KiB
C++
#include <functional>
|
|
#include <stack>
|
|
|
|
#include <fontconfig/fontconfig.h>
|
|
|
|
#include <basic_widgets/core/error_helper.hpp>
|
|
#include <basic_widgets/core/font.hpp>
|
|
#include <basic_widgets/core/type/fc_error.hpp>
|
|
#include <basic_widgets/core/type/sdl_error.hpp>
|
|
|
|
using namespace bwidgets;
|
|
|
|
const Color Font::default_color_bg {255, 255, 255, SDL_ALPHA_OPAQUE};
|
|
const Color Font::default_color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
|
|
|
|
Font::Font(TTF_Font* f)
|
|
: Wrapper {ptr_or_throw<SDLError>(f), [f](TTF_Font*) noexcept { TTF_CloseFont(f); }},
|
|
ascent {TTF_FontAscent(f)},
|
|
descent {TTF_FontDescent(f)},
|
|
faces {TTF_FontFaces(f)},
|
|
family_name {TTF_FontFaceFamilyName(f)},
|
|
fixed_width {TTF_FontFaceIsFixedWidth(f) != 0},
|
|
height {TTF_FontHeight(f)},
|
|
line_skip {TTF_FontLineSkip(f)},
|
|
style_name {TTF_FontFaceStyleName(f)}
|
|
{}
|
|
|
|
Font::Font(const std::string& file, int size) : Font {TTF_OpenFont(file.c_str(), size)}
|
|
{}
|
|
|
|
auto Font::hinting() const noexcept -> Font::Hinting
|
|
{
|
|
return (Hinting)TTF_GetFontHinting(_data());
|
|
}
|
|
|
|
auto Font::hinting(const Font::Hinting h) noexcept -> Font*
|
|
{
|
|
TTF_SetFontHinting(_data(), (int)h);
|
|
return this;
|
|
}
|
|
|
|
auto Font::kerning() const noexcept -> bool
|
|
{
|
|
return TTF_GetFontKerning(_data()) != 0;
|
|
}
|
|
|
|
auto Font::kerning(const bool allowed) noexcept -> Font*
|
|
{
|
|
TTF_SetFontKerning(_data(), static_cast<int>(allowed));
|
|
return this;
|
|
}
|
|
|
|
auto Font::outline() const noexcept -> int
|
|
{
|
|
return TTF_GetFontOutline(_data());
|
|
}
|
|
|
|
auto Font::outline(const int size) noexcept -> Font*
|
|
{
|
|
TTF_SetFontOutline(_data(), size);
|
|
return this;
|
|
}
|
|
|
|
auto Font::render(const RenderMode m, const std::string& str, const Color& fg,
|
|
const Color& bg) -> SDL_Surface*
|
|
{
|
|
std::function<SDL_Surface*()> renderer;
|
|
const char* c_str = str.empty() ? " " : str.c_str();
|
|
|
|
switch (m) {
|
|
case RenderMode::BLENDED:
|
|
renderer = [&fg, c_str, this]() {
|
|
return TTF_RenderUTF8_Blended(_data(), c_str, fg());
|
|
};
|
|
break;
|
|
case RenderMode::SHADED:
|
|
renderer = [&bg, &fg, c_str, this]() {
|
|
return TTF_RenderUTF8_Shaded(_data(), c_str, fg(), bg());
|
|
};
|
|
break;
|
|
case RenderMode::SOLID:
|
|
renderer = [&fg, c_str, this]() {
|
|
return TTF_RenderUTF8_Solid(_data(), c_str, fg());
|
|
};
|
|
break;
|
|
}
|
|
|
|
return ptr_or_throw<SDLError>(renderer());
|
|
}
|
|
|
|
auto Font::style() const noexcept -> uint8_t
|
|
{
|
|
return TTF_GetFontStyle(_data());
|
|
}
|
|
|
|
auto Font::style(const uint8_t s) noexcept -> Font*
|
|
{
|
|
TTF_SetFontStyle(_data(), s);
|
|
return this;
|
|
}
|
|
|
|
auto Font::text_size(const std::string& str) const noexcept -> Size
|
|
{
|
|
Size s {};
|
|
TTF_SizeUTF8(_data(), str.c_str(), &s.w, &s.h);
|
|
return s;
|
|
}
|
|
|
|
auto Font::find(const std::string& pat) -> std::string
|
|
{
|
|
std::stack<std::function<void()>> cleaners;
|
|
|
|
FcConfig* conf = ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), "init failed");
|
|
cleaners.emplace([conf]() { FcConfigDestroy(conf); });
|
|
|
|
FcPattern* pattern = nullptr;
|
|
try {
|
|
pattern = ptr_or_throw<FCError>(FcNameParse((FcChar8*)pat.c_str()),
|
|
"pattern parsing failed");
|
|
cleaners.emplace([pattern]() { FcPatternDestroy(pattern); });
|
|
|
|
// Don't check for error because it looks like it returns an error code when used
|
|
// as the actual code is. But neverless things doesn't work well without calling
|
|
// it.
|
|
/* success_or_throw<FCError, FcBool>( */
|
|
FcConfigSubstitute(conf, pattern,
|
|
FcMatchPattern); //, "FcConfigSubstitute failed",
|
|
/* [](auto code) { return code == FcFalse; }); */
|
|
} catch (const FCError& e) {
|
|
while (!cleaners.empty()) {
|
|
cleaners.top()();
|
|
cleaners.pop();
|
|
}
|
|
throw e;
|
|
}
|
|
|
|
FcDefaultSubstitute(pattern);
|
|
|
|
std::string file_path;
|
|
FcResult res {};
|
|
FcPattern* font = FcFontMatch(conf, pattern, &res);
|
|
if (font != nullptr) {
|
|
FcChar8* file = nullptr;
|
|
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {
|
|
file_path = (char*)file;
|
|
}
|
|
FcPatternDestroy(font);
|
|
}
|
|
|
|
while (!cleaners.empty()) {
|
|
cleaners.top()();
|
|
cleaners.pop();
|
|
}
|
|
|
|
if (file_path.empty()) throw FCError {"no font found"};
|
|
|
|
return file_path;
|
|
}
|