Added WidgetText. Fixed some things. Updated README
This commit is contained in:
parent
58abb91c64
commit
56e07e1080
1
Makefile
1
Makefile
@ -2,6 +2,7 @@ src_files += main.cpp
|
||||
src_files += sdl_helpers.cpp
|
||||
|
||||
src_files += Widgets/Widget.cpp
|
||||
src_files += Widgets/WidgetText.cpp
|
||||
|
||||
all:
|
||||
g++ $(src_files) -Wall -lSDL2 -lSDL2_ttf -lSDL2_image -o trmnl_sdl
|
||||
|
||||
@ -3,17 +3,17 @@ A utility which can be used to create custom images for TRMNL devices via code
|
||||
|
||||
# How to write new visual stuff
|
||||
1. Inherit from `Widget` and then do your magic inside your own class.
|
||||
2. Add your widget builder in `main.cpp` inside `init builders`.
|
||||
3. Write your config file.
|
||||
2. Add your widget builder in `main.cpp` inside `init_builders`.
|
||||
3. Write your config file. Either named `config.json` or added as the first command line parameter.
|
||||
4. ???
|
||||
5. Profit
|
||||
|
||||
# ImageMagick commands
|
||||
### Convert image without dithering
|
||||
`magick input.png -monochrome -colors 2 -depth 1 -strip png:output.png`
|
||||
`convert trmnl.png -monochrome -colors 2 -depth 1 -strip png:output.png`
|
||||
|
||||
### Convert image with dithering
|
||||
`magick input.png -dither FloydSteinberg -remap pattern:gray50 -depth 1 -strip png:output.png`
|
||||
`convert trmnl.png -dither FloydSteinberg -remap pattern:gray50 -depth 1 -strip png:output.png`
|
||||
|
||||
# Notes
|
||||
json.hpp from nlohmann/json v3.11.2
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
class Widget
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
// Internal surface for drawing
|
||||
SDL_Surface* m_surface;
|
||||
|
||||
|
||||
223
Widgets/WidgetText.cpp
Normal file
223
Widgets/WidgetText.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#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)
|
||||
{
|
||||
txt_surface = TTF_RenderUTF8_Solid_Wrapped(font, text.c_str(), color, wrap_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
txt_surface = TTF_RenderUTF8_Solid(font, text.c_str(), color);
|
||||
}
|
||||
|
||||
return txt_surface;
|
||||
}
|
||||
|
||||
WidgetText::WidgetText(int width, int height, string text,
|
||||
TextFit fit, bool should_wrap,
|
||||
HorizontalAlign halign, VerticalAlign valign,
|
||||
SDL_Color text_color,
|
||||
int size, std::string font)
|
||||
: Widget(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)
|
||||
{
|
||||
if (nullptr != m_surface)
|
||||
{
|
||||
SDL_SetSurfaceBlendMode(m_surface, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
WidgetText::WidgetText(int width, int height, string text,
|
||||
TextFit fit, bool should_wrap,
|
||||
int size, string font)
|
||||
: Widget(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}
|
||||
{
|
||||
if (nullptr != m_surface)
|
||||
{
|
||||
SDL_SetSurfaceBlendMode(m_surface, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
WidgetText::WidgetText(int width, int height, string text,
|
||||
int size, string font)
|
||||
: Widget(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}
|
||||
{
|
||||
if (nullptr != m_surface)
|
||||
{
|
||||
SDL_SetSurfaceBlendMode(m_surface, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// 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 align = surface_align(m_surface, txt_surface, m_halign, m_valign);
|
||||
SDL_BlitSurface(txt_surface, NULL, m_surface, &align);
|
||||
SDL_FreeSurface(txt_surface);
|
||||
}
|
||||
|
||||
Widget* WidgetText::builder(const nlohmann::json& j)
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
string text = "";
|
||||
TextFit fit = FIT_NONE;
|
||||
bool should_wrap = false;
|
||||
HorizontalAlign halign = HALIGN_CENTER;
|
||||
VerticalAlign valign = VALIGN_CENTER;
|
||||
SDL_Color text_color = {.r = 0, .g = 0, .b = 0, .a = SDL_ALPHA_OPAQUE};
|
||||
int size = 0;
|
||||
string font = "";
|
||||
|
||||
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, "color", text_color);
|
||||
json_extract(j, "size", size);
|
||||
json_extract(j, "font", font);
|
||||
|
||||
if ((0 == width) || (0 == height) || (0 == size))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new WidgetText(width, height, text, fit, should_wrap, halign, valign, text_color, size, font);
|
||||
}
|
||||
71
Widgets/WidgetText.h
Normal file
71
Widgets/WidgetText.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef WIDGET_TEXT_H_
|
||||
#define WIDGET_TEXT_H_
|
||||
|
||||
#include "Widget.h"
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../sdl_helpers.h"
|
||||
#include "../json.hpp"
|
||||
|
||||
class WidgetText : public Widget
|
||||
{
|
||||
protected:
|
||||
// Text to display
|
||||
std::string m_text;
|
||||
|
||||
// Desired font file
|
||||
// Leave empty for default
|
||||
std::string m_font_file;
|
||||
|
||||
// Desired font size
|
||||
int m_size;
|
||||
|
||||
// Whether to fit text inside rectangle
|
||||
// FIT_SHRINK will try with the desired font size and shrink if needed
|
||||
// FIT_AUTO will with the desired font size and shrink or enlarge if needed
|
||||
// Default - FIT_NONE
|
||||
TextFit m_fit;
|
||||
|
||||
// Whether to wrap to multiple lines
|
||||
// Default - false
|
||||
bool m_should_wrap;
|
||||
|
||||
// Default - center
|
||||
HorizontalAlign m_halign;
|
||||
|
||||
// Default - center
|
||||
VerticalAlign m_valign;
|
||||
|
||||
// Default - black
|
||||
SDL_Color m_text_color;
|
||||
|
||||
public:
|
||||
WidgetText(int width, int height, std::string text,
|
||||
TextFit fit, bool should_wrap,
|
||||
HorizontalAlign halign, VerticalAlign valign,
|
||||
SDL_Color text_color,
|
||||
int size, std::string font = "");
|
||||
|
||||
WidgetText(int width, int height, std::string text,
|
||||
TextFit fit, bool should_wrap,
|
||||
int size, std::string font = "");
|
||||
|
||||
WidgetText(int width, int height, std::string text,
|
||||
int size, std::string font = "");
|
||||
|
||||
void set_text(const std::string& text);
|
||||
void set_font(const std::string& font_file);
|
||||
void set_font_size(int size);
|
||||
void set_fit(TextFit fit);
|
||||
void set_halign(HorizontalAlign halign);
|
||||
void set_valign(VerticalAlign valign);
|
||||
void set_color(SDL_Color text_color);
|
||||
|
||||
virtual void draw() override;
|
||||
|
||||
static Widget* builder(const nlohmann::json& j);
|
||||
};
|
||||
|
||||
#endif // WIDGET_TEXT_H_
|
||||
5
main.cpp
5
main.cpp
@ -11,6 +11,7 @@
|
||||
|
||||
#include "sdl_helpers.h"
|
||||
#include "Widgets/Widget.h"
|
||||
#include "Widgets/WidgetText.h"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
@ -21,7 +22,7 @@ using nlohmann::json;
|
||||
|
||||
void init_builders(map<string, Widget*(*)(const json&)>& widget_builders)
|
||||
{
|
||||
// widget_builders["name"] = &WidgetName::builder;
|
||||
widget_builders["text"] = &WidgetText::builder;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -107,7 +108,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
// Clear screen with white
|
||||
SDL_FillRect(main_surface, nullptr, SDL_MapRGBA(main_surface->format, 255, 255, 255, 255));
|
||||
SDL_FillRect(main_surface, nullptr, SDL_MapRGBA(main_surface->format, 255, 255, 255, SDL_ALPHA_OPAQUE));
|
||||
|
||||
// Draw and apply all widgets
|
||||
for (Widget* widget : widgets)
|
||||
|
||||
115
sdl_helpers.cpp
115
sdl_helpers.cpp
@ -11,6 +11,7 @@ using namespace std;
|
||||
using nlohmann::json;
|
||||
|
||||
map<pair<string, int>, TTF_Font*> font_map;
|
||||
string default_font_name = "font.ttf";
|
||||
|
||||
bool init_sdl()
|
||||
{
|
||||
@ -75,6 +76,49 @@ TTF_Font* get_font(int size, const string& filename)
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect surface_align(const SDL_Surface* base, const SDL_Surface* applied,
|
||||
HorizontalAlign halign, VerticalAlign valign)
|
||||
{
|
||||
SDL_Rect align = {.x = 0, .y = 0, .w = 0, .h = 0};
|
||||
|
||||
if ((nullptr == base) || (nullptr == applied))
|
||||
{
|
||||
return align;
|
||||
}
|
||||
|
||||
switch (halign)
|
||||
{
|
||||
case HALIGN_LEFT:
|
||||
align.x = 0;
|
||||
break;
|
||||
|
||||
case HALIGN_CENTER:
|
||||
align.x = base->w / 2 - applied->w / 2;
|
||||
break;
|
||||
|
||||
case HALIGN_RIGHT:
|
||||
align.x = base->w - applied->w;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (valign)
|
||||
{
|
||||
case VALIGN_TOP:
|
||||
align.y = 0;
|
||||
break;
|
||||
|
||||
case VALIGN_CENTER:
|
||||
align.y = base->h / 2 - applied->h / 2;
|
||||
break;
|
||||
|
||||
case VALIGN_BOTTOM:
|
||||
align.y = base->h - applied->h;
|
||||
break;
|
||||
}
|
||||
|
||||
return align;
|
||||
}
|
||||
|
||||
bool read_config_json(json& cfg, const string& filename, ostream* log)
|
||||
{
|
||||
ifstream cfg_file(filename);
|
||||
@ -120,3 +164,74 @@ void json_extract(const nlohmann::json& j, const string& key, int& out)
|
||||
out = j[key];
|
||||
}
|
||||
}
|
||||
|
||||
void json_extract(const nlohmann::json& j, const string& key, bool& out)
|
||||
{
|
||||
if (j.contains(key) && j[key].is_boolean())
|
||||
{
|
||||
out = j[key];
|
||||
}
|
||||
}
|
||||
|
||||
void json_extract(const json& j, const string& key, HorizontalAlign& out)
|
||||
{
|
||||
if (j.contains(key))
|
||||
{
|
||||
try
|
||||
{
|
||||
out = j[key].get<HorizontalAlign>();
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_extract(const json& j, const string& key, VerticalAlign& out)
|
||||
{
|
||||
if (j.contains(key))
|
||||
{
|
||||
try
|
||||
{
|
||||
out = j[key].get<VerticalAlign>();
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_extract(const json& j, const string& key, TextFit& out)
|
||||
{
|
||||
if (j.contains(key))
|
||||
{
|
||||
try
|
||||
{
|
||||
out = j[key].get<TextFit>();
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json_extract(const json& j, const string& key, SDL_Color& out)
|
||||
{
|
||||
if (j.contains(key) && j[key].is_object())
|
||||
{
|
||||
const json& j2 = j[key];
|
||||
|
||||
json_extract(j2, "r", out.r);
|
||||
json_extract(j2, "g", out.g);
|
||||
json_extract(j2, "b", out.b);
|
||||
json_extract(j2, "a", out.a);
|
||||
}
|
||||
}
|
||||
|
||||
void json_extract(const nlohmann::json& j, const string& key, Uint8& out)
|
||||
{
|
||||
if (j.contains(key) && j[key].is_number_unsigned())
|
||||
{
|
||||
out = j[key];
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,49 @@
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
std::string default_font_name = "font.ttf";
|
||||
extern std::string default_font_name;
|
||||
|
||||
enum HorizontalAlign
|
||||
{
|
||||
HALIGN_LEFT,
|
||||
HALIGN_CENTER,
|
||||
HALIGN_RIGHT
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(HorizontalAlign,
|
||||
{
|
||||
{HALIGN_LEFT, "left"},
|
||||
{HALIGN_CENTER, "center"},
|
||||
{HALIGN_RIGHT, "right"},
|
||||
})
|
||||
|
||||
enum VerticalAlign
|
||||
{
|
||||
VALIGN_TOP,
|
||||
VALIGN_CENTER,
|
||||
VALIGN_BOTTOM
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(VerticalAlign,
|
||||
{
|
||||
{VALIGN_TOP, "top"},
|
||||
{VALIGN_CENTER, "center"},
|
||||
{VALIGN_BOTTOM, "bottom"},
|
||||
})
|
||||
|
||||
enum TextFit
|
||||
{
|
||||
FIT_NONE,
|
||||
FIT_SHRINK,
|
||||
FIT_AUTO
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(TextFit,
|
||||
{
|
||||
{FIT_NONE, "none"},
|
||||
{FIT_SHRINK, "shrink"},
|
||||
{FIT_AUTO, "auto"},
|
||||
})
|
||||
|
||||
// Call this before everything
|
||||
// Prints its messages
|
||||
@ -21,6 +63,12 @@ void clean_sdl();
|
||||
// Can return NULL
|
||||
TTF_Font* get_font(int size, const std::string& filename = default_font_name);
|
||||
|
||||
// Returns a rect to use during bliting of 2 surfaces
|
||||
// base - surface on which the other is applied to
|
||||
// applied - the surface which will be applied to the other
|
||||
SDL_Rect surface_align(const SDL_Surface* base, const SDL_Surface* applied,
|
||||
HorizontalAlign halign, VerticalAlign valign);
|
||||
|
||||
// Reads the file and tries to parse a JSON file with comments
|
||||
// cfg - output json struct
|
||||
// filename - filepath to open and read
|
||||
@ -31,5 +79,11 @@ bool read_config_json(nlohmann::json& cfg, const std::string& filename, std::ost
|
||||
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, std::string& out);
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, int& out);
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, bool& out);
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, HorizontalAlign& out);
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, VerticalAlign& out);
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, TextFit& out);
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, SDL_Color& out);
|
||||
void json_extract(const nlohmann::json& j, const std::string& key, Uint8& out);
|
||||
|
||||
#endif // SDL_HELPERS_H_
|
||||
|
||||
Loading…
Reference in New Issue
Block a user