313 lines
6.2 KiB
C++
313 lines
6.2 KiB
C++
#include "WidgetText.h"
|
|
|
|
using std::string;
|
|
|
|
static SDL_Surface* render_text(bool should_wrap, TTF_Font* font, const string& text, SDL_Color color, Uint32 wrap_size)
|
|
{
|
|
SDL_Surface* txt_surface = nullptr;
|
|
if (!should_wrap)
|
|
{
|
|
// When wrap_size is 0 - the text only wraps on newlines
|
|
wrap_size = 0;
|
|
}
|
|
txt_surface = TTF_RenderUTF8_Solid_Wrapped(font, text.c_str(), color, wrap_size);
|
|
|
|
return txt_surface;
|
|
}
|
|
|
|
static SDL_Rect find_visible_area(const SDL_Surface* txt_surface)
|
|
{
|
|
int x_start = -1;
|
|
int x_end = -1;
|
|
int y_start = -1;
|
|
int y_end = -1;
|
|
|
|
for (int y = 0; y < txt_surface->h; ++y)
|
|
{
|
|
bool found = false;
|
|
for (int x = 0; x < txt_surface->w; ++x)
|
|
{
|
|
if (1 == (static_cast<Uint8*>(txt_surface->pixels)[y * txt_surface->pitch + x]))
|
|
{
|
|
if (-1 == x_start)
|
|
{
|
|
x_start = x;
|
|
}
|
|
else
|
|
{
|
|
if (x < x_start)
|
|
{
|
|
x_start = x;
|
|
}
|
|
}
|
|
|
|
if (-1 == x_end)
|
|
{
|
|
x_end = x;
|
|
}
|
|
else
|
|
{
|
|
if (x > x_end)
|
|
{
|
|
x_end = x;
|
|
}
|
|
}
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
if (-1 == y_start)
|
|
{
|
|
y_start = y;
|
|
}
|
|
y_end = y;
|
|
}
|
|
}
|
|
SDL_Rect result = {.x = 0, .y = 0, .w = 0, .h = 0};
|
|
if ((-1 != x_start) && (-1 != x_end))
|
|
{
|
|
result.x = x_start;
|
|
result.w = x_end - x_start + 1;
|
|
}
|
|
|
|
if ((-1 != y_start) && (-1 != y_end))
|
|
{
|
|
result.y = y_start;
|
|
result.h = y_end - y_start + 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
WidgetText::WidgetText(int x, int y, int width, int height, string text,
|
|
TextFit fit, bool should_wrap,
|
|
HorizontalAlign halign, VerticalAlign valign,
|
|
bool halign_via_visible, bool valign_via_visible,
|
|
SDL_Color text_color,
|
|
int size, std::string font)
|
|
: Widget(x, y, width, height),
|
|
m_text(text),
|
|
m_font_file(font),
|
|
m_size(size),
|
|
m_fit(fit),
|
|
m_should_wrap(should_wrap),
|
|
m_halign(halign),
|
|
m_valign(valign),
|
|
m_text_color(text_color),
|
|
m_halign_via_visible(halign_via_visible),
|
|
m_valign_via_visible(valign_via_visible)
|
|
{
|
|
}
|
|
|
|
WidgetText::WidgetText(int x, int y, int width, int height, string text,
|
|
TextFit fit, bool should_wrap,
|
|
int size, string font)
|
|
: Widget(x, y, width, height),
|
|
m_text(text),
|
|
m_font_file(font),
|
|
m_size(size),
|
|
m_fit(fit),
|
|
m_should_wrap(should_wrap),
|
|
m_halign(HALIGN_CENTER),
|
|
m_valign(VALIGN_CENTER),
|
|
m_text_color{.r = 0, .g = 0, .b = 0, .a = SDL_ALPHA_OPAQUE},
|
|
m_halign_via_visible(true),
|
|
m_valign_via_visible(true)
|
|
{
|
|
}
|
|
|
|
WidgetText::WidgetText(int x, int y, int width, int height, string text,
|
|
int size, string font)
|
|
: Widget(x, y, width, height),
|
|
m_text(text),
|
|
m_font_file(font),
|
|
m_size(size),
|
|
m_fit(FIT_NONE),
|
|
m_halign(HALIGN_CENTER),
|
|
m_valign(VALIGN_CENTER),
|
|
m_text_color{.r = 0, .g = 0, .b = 0, .a = SDL_ALPHA_OPAQUE},
|
|
m_halign_via_visible(true),
|
|
m_valign_via_visible(true)
|
|
{
|
|
}
|
|
|
|
void WidgetText::set_text(const string& text)
|
|
{
|
|
m_text = text;
|
|
}
|
|
|
|
void WidgetText::set_font(const string& font_file)
|
|
{
|
|
m_font_file = font_file;
|
|
}
|
|
|
|
void WidgetText::set_font_size(int size)
|
|
{
|
|
m_size = size;
|
|
}
|
|
|
|
void WidgetText::set_fit(TextFit fit)
|
|
{
|
|
m_fit = fit;
|
|
}
|
|
|
|
void WidgetText::set_halign(HorizontalAlign halign)
|
|
{
|
|
m_halign = halign;
|
|
}
|
|
|
|
void WidgetText::set_valign(VerticalAlign valign)
|
|
{
|
|
m_valign = valign;
|
|
}
|
|
|
|
void WidgetText::set_color(SDL_Color text_color)
|
|
{
|
|
m_text_color = text_color;
|
|
}
|
|
|
|
void WidgetText::draw()
|
|
{
|
|
if (nullptr == m_surface)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Clear surface
|
|
SDL_FillRect(m_surface, nullptr, SDL_MapRGBA(m_surface->format, 255, 255, 255, SDL_ALPHA_TRANSPARENT));
|
|
|
|
string font_name = m_font_file;
|
|
if (font_name.empty())
|
|
{
|
|
font_name = default_font_name;
|
|
}
|
|
|
|
TTF_Font* font = get_font(m_size, font_name);
|
|
if (nullptr == font)
|
|
{
|
|
// TODO: Message printing
|
|
return;
|
|
}
|
|
|
|
// Render text
|
|
SDL_Surface* txt_surface = render_text(m_should_wrap, font, m_text, m_text_color, m_rect.w);
|
|
|
|
if (nullptr == txt_surface)
|
|
{
|
|
// TODO: Message printing
|
|
return;
|
|
}
|
|
|
|
// Check if text fits
|
|
if (m_fit != FIT_NONE)
|
|
{
|
|
double x_scale;
|
|
double y_scale;
|
|
|
|
x_scale = m_surface->w;
|
|
x_scale /= txt_surface->w;
|
|
|
|
y_scale = m_surface->h;
|
|
y_scale /= txt_surface->h;
|
|
|
|
// Find the scale needed to shrink or enlarge with
|
|
double min_scale = x_scale;
|
|
if (y_scale < min_scale)
|
|
{
|
|
min_scale = y_scale;
|
|
}
|
|
|
|
// Do not scale up if only shrinkage is allowed
|
|
if ((min_scale > 1.0) && (FIT_SHRINK == m_fit))
|
|
{
|
|
min_scale = 1.0;
|
|
}
|
|
|
|
// Find new font size
|
|
int new_text_size = m_size;
|
|
new_text_size *= min_scale;
|
|
|
|
// Re-render text
|
|
if (new_text_size != m_size)
|
|
{
|
|
SDL_FreeSurface(txt_surface);
|
|
font = get_font(new_text_size, font_name);
|
|
if (nullptr == font)
|
|
{
|
|
// TODO: Message printing
|
|
return;
|
|
}
|
|
txt_surface = render_text(m_should_wrap, font, m_text, m_text_color, m_rect.w);
|
|
if (nullptr == txt_surface)
|
|
{
|
|
// TODO: Message printing
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have the final rendered text surface
|
|
SDL_Rect hint = find_visible_area(txt_surface);
|
|
if (!m_halign_via_visible)
|
|
{
|
|
hint.x = 0;
|
|
hint.w = txt_surface->w;
|
|
}
|
|
|
|
if (!m_valign_via_visible)
|
|
{
|
|
hint.y = 0;
|
|
hint.h = txt_surface->h;
|
|
}
|
|
|
|
SDL_Rect align = surface_align(m_surface, txt_surface, m_halign, m_valign, &hint);
|
|
SDL_BlitSurface(txt_surface, NULL, m_surface, &align);
|
|
SDL_FreeSurface(txt_surface);
|
|
}
|
|
|
|
std::unique_ptr<Widget> WidgetText::builder(const nlohmann::json& j)
|
|
{
|
|
int x = 0;
|
|
int y = 0;
|
|
int width = 0;
|
|
int height = 0;
|
|
string text = "";
|
|
TextFit fit = FIT_NONE;
|
|
bool should_wrap = false;
|
|
HorizontalAlign halign = HALIGN_CENTER;
|
|
VerticalAlign valign = VALIGN_CENTER;
|
|
bool halign_via_visible = true;
|
|
bool valign_via_visible = true;
|
|
SDL_Color text_color = {.r = 0, .g = 0, .b = 0, .a = SDL_ALPHA_OPAQUE};
|
|
int size = 0;
|
|
string font = "";
|
|
|
|
json_extract(j, "x", x);
|
|
json_extract(j, "y", y);
|
|
json_extract(j, "width", width);
|
|
json_extract(j, "height", height);
|
|
json_extract(j, "text", text);
|
|
json_extract(j, "fit", fit);
|
|
json_extract(j, "should_wrap", should_wrap);
|
|
json_extract(j, "halign", halign);
|
|
json_extract(j, "valign", valign);
|
|
json_extract(j, "halign_via_visible", halign_via_visible);
|
|
json_extract(j, "valign_via_visible", valign_via_visible);
|
|
json_extract(j, "color", text_color);
|
|
json_extract(j, "size", size);
|
|
json_extract(j, "font", font);
|
|
|
|
if ((0 == width) || (0 == height) || (0 == size))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return std::make_unique<WidgetText>(x, y, width, height,
|
|
text, fit, should_wrap,
|
|
halign, valign,
|
|
halign_via_visible, valign_via_visible,
|
|
text_color, size, font);
|
|
}
|