146 lines
4.3 KiB
C++
146 lines
4.3 KiB
C++
#include <functional>
|
|
#include <memory>
|
|
#include <stack>
|
|
#include <stdexcept>
|
|
|
|
#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>
|
|
|
|
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)
|
|
: _data {ptr_or_throw<SDLError>(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_view file, const int size)
|
|
: Font {TTF_OpenFont(file.data(), size)}
|
|
{}
|
|
|
|
auto Font::hinting() const noexcept -> Font::Hinting
|
|
{
|
|
return static_cast<Hinting>(TTF_GetFontHinting(_data.get()));
|
|
}
|
|
|
|
auto Font::hinting(const Font::Hinting h) noexcept -> Font*
|
|
{
|
|
TTF_SetFontHinting(_data.get(), static_cast<int>(h));
|
|
return this;
|
|
}
|
|
|
|
auto Font::kerning() const noexcept -> bool
|
|
{
|
|
return TTF_GetFontKerning(_data.get()) != 0;
|
|
}
|
|
|
|
auto Font::kerning(const bool allowed) noexcept -> Font*
|
|
{
|
|
TTF_SetFontKerning(_data.get(), static_cast<int>(allowed));
|
|
return this;
|
|
}
|
|
|
|
auto Font::outline() const noexcept -> int
|
|
{
|
|
return TTF_GetFontOutline(_data.get());
|
|
}
|
|
|
|
auto Font::outline(const int size) noexcept -> Font*
|
|
{
|
|
TTF_SetFontOutline(_data.get(), size);
|
|
return this;
|
|
}
|
|
|
|
auto Font::render(const RenderMode m, const std::string_view str, const Color fg,
|
|
const Color bg) -> std::unique_ptr<SDL_Surface, Deleter>
|
|
{
|
|
const char* const c_str = str.empty() ? " " : str.data();
|
|
auto renderer = [bg, c_str, fg, m, this]() -> std::function<SDL_Surface*()> {
|
|
switch (m) {
|
|
case RenderMode::BLENDED:
|
|
return [fg, c_str, this]() {
|
|
return TTF_RenderUTF8_Blended(_data.get(), c_str, fg());
|
|
};
|
|
case RenderMode::SHADED:
|
|
return [bg, fg, c_str, this]() {
|
|
return TTF_RenderUTF8_Shaded(_data.get(), c_str, fg(), bg());
|
|
};
|
|
case RenderMode::SOLID:
|
|
return [fg, c_str, this]() {
|
|
return TTF_RenderUTF8_Solid(_data.get(), c_str, fg());
|
|
};
|
|
default:
|
|
throw std::logic_error("missing switch case.");
|
|
}
|
|
}();
|
|
|
|
return std::unique_ptr<SDL_Surface, Deleter>(ptr_or_throw<SDLError>(renderer()));
|
|
}
|
|
|
|
auto Font::style() const noexcept -> uint8_t
|
|
{
|
|
return TTF_GetFontStyle(_data.get());
|
|
}
|
|
|
|
auto Font::style(const uint8_t s) noexcept -> Font*
|
|
{
|
|
TTF_SetFontStyle(_data.get(), s);
|
|
return this;
|
|
}
|
|
|
|
auto Font::text_size(const std::string_view str) const noexcept -> Size
|
|
{
|
|
Size s {};
|
|
TTF_SizeUTF8(_data.get(), str.data(), &s.w, &s.h);
|
|
return s;
|
|
}
|
|
|
|
auto Font::find(const std::string_view pat) -> std::string
|
|
{
|
|
const auto conf = std::unique_ptr<FcConfig, Deleter>(
|
|
ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), "fontconfig init failed."));
|
|
|
|
const auto pattern = [&conf, pat]() {
|
|
auto pattern = std::unique_ptr<FcPattern, Deleter>(ptr_or_throw<FCError>(
|
|
FcNameParse(reinterpret_cast<const FcChar8*>(pat.data())),
|
|
"pattern parsing failed"));
|
|
|
|
success_or_throw<FCError, FcBool>(
|
|
FcConfigSubstitute(conf.get(), pattern.get(), FcMatchPattern),
|
|
"FcConfigSubstitute failed", [](auto code) { return code == FcTrue; });
|
|
return pattern;
|
|
}();
|
|
|
|
FcDefaultSubstitute(pattern.get());
|
|
|
|
std::string file_path = [&conf, &pattern]() {
|
|
FcResult res {};
|
|
const auto font = std::unique_ptr<FcPattern, Deleter>(ptr_or_throw<FCError>(
|
|
FcFontMatch(conf.get(), pattern.get(), &res), "no font found."));
|
|
|
|
if (FcChar8* file = nullptr;
|
|
FcPatternGetString(font.get(), FC_FILE, 0, &file) == FcResultMatch)
|
|
{
|
|
// "I know what I'm doing"
|
|
// NOLINTNEXTLINE
|
|
std::string _file_path {reinterpret_cast<char*>(file)};
|
|
return _file_path;
|
|
}
|
|
throw FCError("no font found.");
|
|
}();
|
|
|
|
return file_path;
|
|
}
|