Delete OpaqueStruct and use directly fancy pointers now that I know

how to set the deleter correctly.
This commit is contained in:
Andrea Blankenstijn 2021-08-26 18:59:52 +02:00
parent 5f341149fa
commit d8f56e0e5f
11 changed files with 139 additions and 135 deletions

View File

@ -4,6 +4,7 @@
#include <functional>
#include <iostream>
#include <basic_widgets/core/type/deleter.hpp>
#include <basic_widgets/w/widget_factory.hpp>
template<typename T>
@ -26,14 +27,15 @@ void run_example(
auto font =
std::make_shared<bwidgets::Font>(bwidgets::Font::find("Monospace"),
16); // NOLINT(readability-magic-numbers)
auto win = bwidgets::OpaqueStruct<SDL_Window>(
auto win = std::unique_ptr<SDL_Window, bwidgets::Deleter>(
bwidgets::ptr_or_throw<bwidgets::SDLError>(SDL_CreateWindow(
"basic_widgets example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
size_init.w, size_init.h,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_UTILITY)),
[](auto ptr) noexcept { SDL_DestroyWindow(ptr); });
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_UTILITY)));
auto renderer =
std::make_shared<bwidgets::Renderer>(win(), -1, SDL_RENDERER_ACCELERATED);
std::make_shared<bwidgets::Renderer>(win.get(), -1, SDL_RENDERER_ACCELERATED);
renderer->blend_mode(SDL_BLENDMODE_BLEND);
auto layout = bwidgets::create_horizontal_layout();

View File

@ -1,19 +1,22 @@
#ifndef BWIDGETS_FONT_HPP
#define BWIDGETS_FONT_HPP
#include <memory>
#include <string>
#include <SDL_ttf.h>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/core/type/opaque_struct.hpp>
#include <basic_widgets/core/type/deleter.hpp>
#include <basic_widgets/core/type/size.hpp>
namespace bwidgets
{
// Wrap TTF_Font from SDL_ttf and provide a font finding function.
class Font final : OpaqueStruct<TTF_Font>::Wrapper
class Font final
{
const std::unique_ptr<TTF_Font, Deleter> _data;
public:
enum struct Hinting
{
@ -67,7 +70,8 @@ namespace bwidgets
[[nodiscard]] auto outline() const noexcept -> int;
auto outline(int) noexcept -> Font*;
auto render(RenderMode, std::string_view, Color fg = default_color_fg,
Color bg = default_color_bg) -> OpaqueStruct<SDL_Surface>;
Color bg = default_color_bg)
-> std::unique_ptr<SDL_Surface, Deleter>;
[[nodiscard]] auto style() const noexcept -> uint8_t;
auto style(uint8_t) noexcept -> Font*;
[[nodiscard]] auto text_size(std::string_view) const noexcept -> Size;

View File

@ -1,13 +1,14 @@
#ifndef BWIDGETS_RENDERER_HPP
#define BWIDGETS_RENDERER_HPP
#include <memory>
#include <span>
#include <SDL_render.h>
#include <basic_widgets/core/error_helper.hpp>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/core/type/opaque_struct.hpp>
#include <basic_widgets/core/type/deleter.hpp>
#include <basic_widgets/core/type/size.hpp>
namespace bwidgets
@ -15,10 +16,12 @@ namespace bwidgets
class Texture;
// Wrap some of SDL's 2D accelerated rendering API.
class Renderer final : OpaqueStruct<SDL_Renderer>::Wrapper
class Renderer final
{
friend Texture;
const std::unique_ptr<SDL_Renderer, Deleter> _data;
static auto _info(SDL_Renderer* r) -> SDL_RendererInfo
{
SDL_RendererInfo info;

View File

@ -1,19 +1,21 @@
#ifndef BWIDGETS_TEXTURE_HPP
#define BWIDGETS_TEXTURE_HPP
#include <memory>
#include <SDL_pixels.h>
#include <SDL_render.h>
#include <basic_widgets/core/error_helper.hpp>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/core/type/opaque_struct.hpp>
#include <basic_widgets/core/type/deleter.hpp>
namespace bwidgets
{
class Renderer;
// Wrap most of the texture functions of SDL 2D rendering API.
class Texture final : OpaqueStruct<SDL_Texture>::Wrapper
class Texture final
{
friend Renderer;
@ -25,7 +27,8 @@ namespace bwidgets
int w, h;
};
const Attr _attributes;
const Attr _attributes;
const std::unique_ptr<SDL_Texture, Deleter> _data;
public:
explicit Texture(SDL_Texture*);

View File

@ -0,0 +1,51 @@
#ifndef BWIDGETS_OPAQUE_STRUCT_HPP
#define BWIDGETS_OPAQUE_STRUCT_HPP
#include <fontconfig/fontconfig.h>
#include <SDL_render.h>
#include <SDL_surface.h>
#include <SDL_ttf.h>
namespace bwidgets
{
// Deleter type for fancy pointers.
struct Deleter
{
void operator()(FcConfig* ptr)
{
FcConfigDestroy(ptr);
}
void operator()(FcPattern* ptr)
{
FcPatternDestroy(ptr);
}
void operator()(SDL_Renderer* ptr)
{
SDL_DestroyRenderer(ptr);
}
void operator()(SDL_Surface* ptr)
{
SDL_FreeSurface(ptr);
}
void operator()(SDL_Texture* ptr)
{
SDL_DestroyTexture(ptr);
}
void operator()(SDL_Window* ptr)
{
SDL_DestroyWindow(ptr);
}
void operator()(TTF_Font* ptr)
{
TTF_CloseFont(ptr);
}
};
}
#endif

View File

@ -1,53 +0,0 @@
#ifndef BWIDGETS_OPAQUE_STRUCT_HPP
#define BWIDGETS_OPAQUE_STRUCT_HPP
#include <functional>
namespace bwidgets
{
// Wrap opaque structures to be able to use a destructor to free them.
template<typename T>
class OpaqueStruct
{
using Deleter = std::function<void(T*)>;
const Deleter _deleter;
T* const _c_pod;
public:
// Construct an instance managing resource pointed by ptr using Deleter d to free
// it on wrapper destruction.
OpaqueStruct(T* ptr, Deleter d) : _deleter {std::move(d)}, _c_pod {ptr} {}
OpaqueStruct(const OpaqueStruct&) = delete;
OpaqueStruct(OpaqueStruct&&) = delete;
virtual ~OpaqueStruct() noexcept
{
_deleter(_c_pod);
};
// Get the pointer to the wrapped opaque struct.
[[nodiscard]] auto* operator()() const
{
return _c_pod;
}
auto operator=(const OpaqueStruct&) = delete;
auto operator=(OpaqueStruct&&) = delete;
// Base class for defining custom wrappers.
class Wrapper
{
public:
// Forward args to OpaqueStruct ctor.
Wrapper(T* ptr, Deleter d) : _data(ptr, std::move(d)) {}
protected:
OpaqueStruct _data;
};
};
}
#endif

View File

@ -12,7 +12,6 @@ namespace bwidgets
SDL_ALPHA_OPAQUE};
inline static const Color default_font_color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
FontHandler() noexcept = default;
FontHandler(const FontHandler&) = delete;
FontHandler(FontHandler&&) = delete;
auto operator=(const FontHandler&) = delete;
@ -26,6 +25,9 @@ namespace bwidgets
virtual void font_color_bg(Color c) = 0;
// Set font foreground (text) color.
virtual void font_color_fg(Color c) = 0;
protected:
FontHandler() noexcept = default;
};
}

View File

@ -15,7 +15,7 @@ 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); }},
: _data {ptr_or_throw<SDLError>(f)},
ascent {TTF_FontAscent(f)},
descent {TTF_FontDescent(f)},
faces {TTF_FontFaces(f)},
@ -32,115 +32,110 @@ Font::Font(const std::string_view file, const int size)
auto Font::hinting() const noexcept -> Font::Hinting
{
return static_cast<Hinting>(TTF_GetFontHinting(_data()));
return static_cast<Hinting>(TTF_GetFontHinting(_data.get()));
}
auto Font::hinting(const Font::Hinting h) noexcept -> Font*
{
TTF_SetFontHinting(_data(), static_cast<int>(h));
TTF_SetFontHinting(_data.get(), static_cast<int>(h));
return this;
}
auto Font::kerning() const noexcept -> bool
{
return TTF_GetFontKerning(_data()) != 0;
return TTF_GetFontKerning(_data.get()) != 0;
}
auto Font::kerning(const bool allowed) noexcept -> Font*
{
TTF_SetFontKerning(_data(), static_cast<int>(allowed));
TTF_SetFontKerning(_data.get(), static_cast<int>(allowed));
return this;
}
auto Font::outline() const noexcept -> int
{
return TTF_GetFontOutline(_data());
return TTF_GetFontOutline(_data.get());
}
auto Font::outline(const int size) noexcept -> Font*
{
TTF_SetFontOutline(_data(), size);
TTF_SetFontOutline(_data.get(), size);
return this;
}
auto Font::render(const RenderMode m, const std::string_view str, const Color fg,
const Color bg) -> OpaqueStruct<SDL_Surface>
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(), c_str, fg());
return TTF_RenderUTF8_Blended(_data.get(), c_str, fg());
};
case RenderMode::SHADED:
return [bg, fg, c_str, this]() {
return TTF_RenderUTF8_Shaded(_data(), c_str, fg(), bg());
return TTF_RenderUTF8_Shaded(_data.get(), c_str, fg(), bg());
};
case RenderMode::SOLID:
return [fg, c_str, this]() {
return TTF_RenderUTF8_Solid(_data(), c_str, fg());
return TTF_RenderUTF8_Solid(_data.get(), c_str, fg());
};
default:
throw std::logic_error("missing switch case.");
}
}();
return {ptr_or_throw<SDLError>(renderer()), [](auto* ptr) { SDL_FreeSurface(ptr); }};
return std::unique_ptr<SDL_Surface, Deleter>(ptr_or_throw<SDLError>(renderer()));
}
auto Font::style() const noexcept -> uint8_t
{
return TTF_GetFontStyle(_data());
return TTF_GetFontStyle(_data.get());
}
auto Font::style(const uint8_t s) noexcept -> Font*
{
TTF_SetFontStyle(_data(), s);
TTF_SetFontStyle(_data.get(), s);
return this;
}
auto Font::text_size(const std::string_view str) const noexcept -> Size
{
Size s {};
TTF_SizeUTF8(_data(), str.data(), &s.w, &s.h);
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::make_unique<OpaqueStruct<FcConfig>>(
ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), "init failed"),
[](auto* ptr) { FcConfigDestroy(ptr); });
const auto conf = std::unique_ptr<FcConfig, Deleter>(
ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), "fontconfig init failed."));
const auto pattern = [&conf, pat]() {
auto pattern = std::make_unique<OpaqueStruct<FcPattern>>(
ptr_or_throw<FCError>(
FcNameParse(reinterpret_cast<const FcChar8*>(pat.data())),
"pattern parsing failed"),
[](auto* ptr) { FcPatternDestroy(ptr); });
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)(), (*pattern)(), FcMatchPattern),
FcConfigSubstitute(conf.get(), pattern.get(), FcMatchPattern),
"FcConfigSubstitute failed", [](auto code) { return code == FcTrue; });
return pattern;
}();
FcDefaultSubstitute((*pattern)());
FcDefaultSubstitute(pattern.get());
std::string file_path = [&conf, &pattern]() {
FcResult res {};
const auto font = std::make_unique<OpaqueStruct<FcPattern>>(
ptr_or_throw<FCError>(FcFontMatch((*conf)(), (*pattern)(), &res),
"no font found."),
[](auto* ptr) { FcPatternDestroy(ptr); });
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)(), FC_FILE, 0, &file) == FcResultMatch)
FcPatternGetString(font.get(), FC_FILE, 0, &file) == FcResultMatch)
{
// "I know what I'm doing"
// NOLINTNEXTLINE
std::string _file_path {reinterpret_cast<char*>(file)};
FcPatternDestroy((*font)());
return _file_path;
}
throw FCError("no font found.");

View File

@ -3,34 +3,31 @@
using namespace bwidgets;
Renderer::Renderer(SDL_Renderer* r)
: Wrapper {ptr_or_throw<SDLError>(r),
[r](SDL_Renderer*) noexcept { SDL_DestroyRenderer(r); }},
info {_info(r)}
Renderer::Renderer(SDL_Renderer* r) : _data {ptr_or_throw<SDLError>(r)}, info {_info(r)}
{}
Renderer::Renderer(SDL_Window* w, const int index, const uint32_t flags = 0)
: Renderer {ptr_or_throw<SDLError>(SDL_CreateRenderer(w, index, flags))}
: Renderer {SDL_CreateRenderer(w, index, flags)}
{}
auto Renderer::blend_mode() -> SDL_BlendMode
{
SDL_BlendMode mode {};
success_or_throw<SDLError>(SDL_GetRenderDrawBlendMode(_data(), &mode));
success_or_throw<SDLError>(SDL_GetRenderDrawBlendMode(_data.get(), &mode));
return mode;
}
auto Renderer::blend_mode(const SDL_BlendMode mode) -> Renderer*
{
success_or_throw<SDLError>(SDL_SetRenderDrawBlendMode(_data(), mode));
success_or_throw<SDLError>(SDL_SetRenderDrawBlendMode(_data.get(), mode));
return this;
}
auto Renderer::clear() -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderClear(_data()));
success_or_throw<SDLError>(SDL_RenderClear(_data.get()));
return this;
}
@ -38,7 +35,7 @@ auto Renderer::clear() -> Renderer*
auto Renderer::copy(const Texture& t, const SDL_Rect* const src,
const SDL_Rect* const dst) -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderCopy(_data(), t._data(), src, dst));
success_or_throw<SDLError>(SDL_RenderCopy(_data.get(), t._data.get(), src, dst));
return this;
}
@ -47,7 +44,7 @@ auto Renderer::draw_color() -> Color
{
Color c;
success_or_throw<SDLError>(
SDL_GetRenderDrawColor(_data(), &c().r, &c().g, &c().b, &c().a));
SDL_GetRenderDrawColor(_data.get(), &c().r, &c().g, &c().b, &c().a));
return c;
}
@ -55,14 +52,14 @@ auto Renderer::draw_color() -> Color
auto Renderer::draw_color(const Color c) -> Renderer*
{
success_or_throw<SDLError>(
SDL_SetRenderDrawColor(_data(), c().r, c().g, c().b, c().a));
SDL_SetRenderDrawColor(_data.get(), c().r, c().g, c().b, c().a));
return this;
}
auto Renderer::draw_line(const SDL_Point a, const SDL_Point b) -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderDrawLine(_data(), a.x, a.y, b.x, b.y));
success_or_throw<SDLError>(SDL_RenderDrawLine(_data.get(), a.x, a.y, b.x, b.y));
return this;
}
@ -70,14 +67,14 @@ auto Renderer::draw_line(const SDL_Point a, const SDL_Point b) -> Renderer*
auto Renderer::draw_lines(const std::span<SDL_Point> pts) -> Renderer*
{
success_or_throw<SDLError>(
SDL_RenderDrawLines(_data(), pts.data(), (int)pts.size()));
SDL_RenderDrawLines(_data.get(), pts.data(), (int)pts.size()));
return this;
}
auto Renderer::draw_point(const SDL_Point p) -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderDrawPoint(_data(), p.x, p.y));
success_or_throw<SDLError>(SDL_RenderDrawPoint(_data.get(), p.x, p.y));
return this;
}
@ -85,7 +82,7 @@ auto Renderer::draw_point(const SDL_Point p) -> Renderer*
auto Renderer::draw_points(const std::span<SDL_Point> pts) -> Renderer*
{
success_or_throw<SDLError>(
SDL_RenderDrawPoints(_data(), pts.data(), (int)pts.size()));
SDL_RenderDrawPoints(_data.get(), pts.data(), (int)pts.size()));
return this;
}
@ -98,7 +95,7 @@ auto Renderer::draw_rect(const SDL_Rect* const r) -> Renderer*
// The second corner is missing a pixel.
if (r != nullptr)
viewport({vp.x + r->x, vp.y + r->y, r->w - 1, r->h - 1}); // crop extra pixel
success_or_throw<SDLError>(SDL_RenderDrawRect(_data(), nullptr));
success_or_throw<SDLError>(SDL_RenderDrawRect(_data.get(), nullptr));
// add missing pixel. works sometimes…
if (r != nullptr) draw_point({r->w - 1, r->h - 1});
else draw_point({vp.w - 1, vp.h - 1});
@ -110,21 +107,23 @@ auto Renderer::draw_rect(const SDL_Rect* const r) -> Renderer*
auto Renderer::draw_rects(const std::span<SDL_Rect> rs) -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderDrawRects(_data(), rs.data(), (int)rs.size()));
success_or_throw<SDLError>(
SDL_RenderDrawRects(_data.get(), rs.data(), (int)rs.size()));
return this;
}
auto Renderer::fill_rect(const SDL_Rect* const r) -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderFillRect(_data(), r));
success_or_throw<SDLError>(SDL_RenderFillRect(_data.get(), r));
return this;
}
auto Renderer::fill_rects(const std::span<SDL_Rect> rs) -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderFillRects(_data(), rs.data(), (int)rs.size()));
success_or_throw<SDLError>(
SDL_RenderFillRects(_data.get(), rs.data(), (int)rs.size()));
return this;
}
@ -132,27 +131,27 @@ auto Renderer::fill_rects(const std::span<SDL_Rect> rs) -> Renderer*
auto Renderer::output_size() -> Size
{
Size s {};
success_or_throw<SDLError>(SDL_GetRendererOutputSize(_data(), &s.w, &s.h));
success_or_throw<SDLError>(SDL_GetRendererOutputSize(_data.get(), &s.w, &s.h));
return s;
}
void Renderer::present() noexcept
{
SDL_RenderPresent(_data());
SDL_RenderPresent(_data.get());
}
auto Renderer::viewport() noexcept -> SDL_Rect
{
SDL_Rect vp;
SDL_RenderGetViewport(_data(), &vp);
SDL_RenderGetViewport(_data.get(), &vp);
return vp;
}
auto Renderer::viewport(const SDL_Rect* const vp) -> Renderer*
{
success_or_throw<SDLError>(SDL_RenderSetViewport(_data(), vp));
success_or_throw<SDLError>(SDL_RenderSetViewport(_data.get(), vp));
return this;
}

View File

@ -4,18 +4,16 @@
using namespace bwidgets;
Texture::Texture(SDL_Texture* t)
: Wrapper {ptr_or_throw<SDLError>(t),
[t](SDL_Texture*) noexcept { SDL_DestroyTexture(t); }},
_attributes(attributes(t))
: _attributes(attributes(t)), _data {ptr_or_throw<SDLError>(t)}
{}
Texture::Texture(const Renderer& r, const SDL_PixelFormatEnum f,
const SDL_TextureAccess a, int w, int h)
: Texture {SDL_CreateTexture(r._data(), f, a, w, h)}
: Texture {SDL_CreateTexture(r._data.get(), f, a, w, h)}
{}
Texture::Texture(const Renderer& r, SDL_Surface* const s)
: Texture {SDL_CreateTextureFromSurface(r._data(), s)}
: Texture {SDL_CreateTextureFromSurface(r._data.get(), s)}
{}
Texture::~Texture() noexcept
@ -27,14 +25,14 @@ Texture::~Texture() noexcept
auto Texture::alpha_mode() -> uint8_t
{
uint8_t mode = 0;
success_or_throw<SDLError>(SDL_GetTextureAlphaMod(_data(), &mode));
success_or_throw<SDLError>(SDL_GetTextureAlphaMod(_data.get(), &mode));
return mode;
}
auto Texture::alpha_mode(const uint8_t m) -> Texture*
{
success_or_throw<SDLError>(SDL_SetTextureAlphaMod(_data(), m));
success_or_throw<SDLError>(SDL_SetTextureAlphaMod(_data.get(), m));
return this;
}
@ -42,14 +40,14 @@ auto Texture::alpha_mode(const uint8_t m) -> Texture*
auto Texture::blend_mode() -> SDL_BlendMode
{
SDL_BlendMode mode {};
success_or_throw<SDLError>(SDL_GetTextureBlendMode(_data(), &mode));
success_or_throw<SDLError>(SDL_GetTextureBlendMode(_data.get(), &mode));
return mode;
}
auto Texture::blend_mode(const SDL_BlendMode m) -> Texture*
{
success_or_throw<SDLError>(SDL_SetTextureBlendMode(_data(), m));
success_or_throw<SDLError>(SDL_SetTextureBlendMode(_data.get(), m));
return this;
}
@ -58,14 +56,14 @@ auto Texture::color_mode() -> Color
{
Color mode;
success_or_throw<SDLError>(
SDL_GetTextureColorMod(_data(), &mode().r, &mode().g, &mode().b));
SDL_GetTextureColorMod(_data.get(), &mode().r, &mode().g, &mode().b));
return mode;
}
auto Texture::color_mode(Color m) -> Texture*
{
success_or_throw<SDLError>(SDL_SetTextureColorMod(_data(), m().r, m().g, m().b));
success_or_throw<SDLError>(SDL_SetTextureColorMod(_data.get(), m().r, m().g, m().b));
return this;
}
@ -73,14 +71,14 @@ auto Texture::color_mode(Color m) -> Texture*
auto Texture::scale_mode() -> SDL_ScaleMode
{
SDL_ScaleMode mode {};
success_or_throw<SDLError>(SDL_GetTextureScaleMode(_data(), &mode));
success_or_throw<SDLError>(SDL_GetTextureScaleMode(_data.get(), &mode));
return mode;
}
auto Texture::scale_mode(const SDL_ScaleMode m) -> Texture*
{
success_or_throw<SDLError>(SDL_SetTextureScaleMode(_data(), m));
success_or_throw<SDLError>(SDL_SetTextureScaleMode(_data.get(), m));
return this;
}
@ -88,7 +86,7 @@ auto Texture::scale_mode(const SDL_ScaleMode m) -> Texture*
auto Texture::update(const SDL_Rect* const r, const void* const pixels, const int pitch)
-> Texture*
{
success_or_throw<SDLError>(SDL_UpdateTexture(_data(), r, pixels, pitch));
success_or_throw<SDLError>(SDL_UpdateTexture(_data.get(), r, pixels, pitch));
return this;
}

View File

@ -118,5 +118,5 @@ void CaptionImpl::_handle_texture_update()
return _font->render(_render_mode, _text, _font_color_fg);
}
}();
_text_texture = std::make_shared<Texture>(*_renderer, s());
_text_texture = std::make_shared<Texture>(*_renderer, s.get());
}