Compare commits

...

2 Commits

Author SHA1 Message Date
Simon Hardt
fe9fc4d69c add: Font registry 2022-03-06 20:15:57 +01:00
Simon Hardt
1772a3baff add: Widget registry 2022-03-06 19:05:37 +01:00
14 changed files with 320 additions and 8 deletions

1
.gitignore vendored
View File

@@ -35,3 +35,4 @@
build/* build/*
.vscode/* .vscode/*
frame/src/Config.h frame/src/Config.h
.editorconfig

View File

@@ -19,6 +19,9 @@ set(src
src/ScreenManager.hpp src/ScreenManager.hpp
src/ScreenManager.cpp src/ScreenManager.cpp
src/ServiceLocator.hpp
src/ServiceLocator.cpp
src/display/IDisplay.hpp src/display/IDisplay.hpp
src/display/Display.hpp src/display/Display.hpp
src/display/Display.cpp src/display/Display.cpp
@@ -30,9 +33,14 @@ set(src
src/font/Font.cpp src/font/Font.cpp
src/font/Glyph.hpp src/font/Glyph.hpp
src/font/Glyph.cpp src/font/Glyph.cpp
src/font/FontRegistry.hpp
src/font/FontRegistry.cpp
src/widgets/Widget.hpp src/widgets/Widget.hpp
src/widgets/Widget.cpp src/widgets/Widget.cpp
src/widgets/WidgetRegistry.hpp
src/widgets/WidgetRegistry.cpp
src/widgets/clock/Digital.hpp src/widgets/clock/Digital.hpp
src/widgets/clock/Digital.cpp src/widgets/clock/Digital.cpp
src/widgets/clock/Analog.hpp src/widgets/clock/Analog.hpp

View File

@@ -0,0 +1,19 @@
#include "ServiceLocator.hpp"
namespace frame
{
IService::IService(std::string_view name)
: name(name)
{
}
std::string_view IService::getName() const
{
return name;
}
std::unordered_map<std::type_index, std::shared_ptr<IService>>
Service::Services = {};
} // namespace frame

View File

@@ -0,0 +1,40 @@
#pragma once
#include <memory>
#include <string>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
namespace frame
{
class IService
{
std::string name;
public:
IService(std::string_view name);
virtual ~IService() = default;
std::string_view getName() const;
};
class Service
{
static std::unordered_map<std::type_index, std::shared_ptr<IService>>
Services;
public:
template<class SERVICE>
static std::shared_ptr<SERVICE> get()
{
auto const type = std::type_index(typeid(SERVICE));
if(Services.find(type) == Services.end())
{
Services[type] = std::make_shared<SERVICE>();
}
return std::static_pointer_cast<SERVICE>(Services[type]);
}
};
} // namespace frame

View File

@@ -65,6 +65,23 @@ namespace frame::font
return y_offset; return y_offset;
} }
uint32_t Font::getOptimalSize(std::string_view text, Rect rect) const
{
auto best_size = 0;
for(auto const& [size, value] : sizes)
{
auto width = getLength(text, size);
auto height = getHeight(text, size);
if(rect.width < width || rect.height < height)
break;
else
best_size = size;
}
return best_size;
}
void Font::LoadFromStream(std::istream& os) void Font::LoadFromStream(std::istream& os)
{ {
json file_data = json::parse(os); json file_data = json::parse(os);

View File

@@ -1,11 +1,13 @@
#pragma once #pragma once
#include "../Rect.hpp"
#include "Glyph.hpp" #include "Glyph.hpp"
#include <map> #include <map>
#include <memory> #include <memory>
#include <string_view> #include <string_view>
namespace frame::font namespace frame::font
{ {
using Glyphs = std::map<char, Glyph>; using Glyphs = std::map<char, Glyph>;
@@ -27,6 +29,8 @@ namespace frame::font
uint32_t getHeight(std::string_view text, uint32_t size) const; uint32_t getHeight(std::string_view text, uint32_t size) const;
int32_t getMaxYOffset(std::string_view text, uint32_t size) const; int32_t getMaxYOffset(std::string_view text, uint32_t size) const;
uint32_t getOptimalSize(std::string_view text, Rect rect) const;
uint32_t getLineSpacing(uint32_t size) const uint32_t getLineSpacing(uint32_t size) const
{ {
return lineSpacing.at(size); return lineSpacing.at(size);

View File

@@ -0,0 +1,57 @@
#include "FontRegistry.hpp"
#include <fmt/format.h>
namespace frame::font
{
std::shared_ptr<Font> GetFont(std::string_view name)
{
return Service::get<FontRegistry>()->Get(name);
}
bool LoadFont(std::string_view name)
{
return Service::get<FontRegistry>()->Load(name);
}
FontRegistry::FontRegistry()
: IService("FontRegistry")
{
}
bool FontRegistry::Load(std::string_view name)
{
if(fonts.find(std::string{name}) != fonts.end())
{
fmt::print("Font {} allready loaded!\n", name);
return true;
} else
{
fmt::print("Loading font \"{}\": ", name);
auto ptr = Font::LoadFromFile(fmt::format("{}.json", name));
if(ptr == nullptr)
{
fmt::print("Error not found!\n");
} else
{
fonts[std::string{name}] = ptr;
fmt::print("OK\n");
return true;
}
}
return false;
}
std::shared_ptr<Font> FontRegistry::Get(std::string_view name)
{
auto it = fonts.find({std::string{name}});
if(it == fonts.end())
{
if(!Load(name))
return nullptr;
it = fonts.find({std::string{name}});
}
return it->second;
}
} // namespace frame::font

View File

@@ -0,0 +1,26 @@
#pragma once
#include "../ServiceLocator.hpp"
#include "Font.hpp"
#include <memory>
#include <unordered_map>
namespace frame::font
{
std::shared_ptr<Font> GetFont(std::string_view name);
bool LoadFont(std::string_view name);
class FontRegistry : public IService
{
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
public:
FontRegistry();
bool Load(std::string_view name);
std::shared_ptr<Font> Get(std::string_view name);
};
} // namespace frame::font

View File

@@ -8,9 +8,10 @@ using namespace std::chrono_literals;
#include "ScreenManager.hpp" #include "ScreenManager.hpp"
#include "display/Display.hpp" #include "display/Display.hpp"
#include "font/Font.hpp" #include "font/FontRegistry.hpp"
#include "render/RenderTarget.hpp" //#include "render/RenderTarget.hpp"
#include "widgets/clock/Analog.hpp" //#include "widgets/clock/Analog.hpp"
#include "widgets/WidgetRegistry.hpp"
int main() int main()
{ {
@@ -23,9 +24,13 @@ int main()
display->Init(); display->Init();
frame::font::LoadFont("Fira Code");
frame::ScreenManager screen(std::move(display)); frame::ScreenManager screen(std::move(display));
screen.setRoot(frame::widgets::AnalogClock::Create()); auto const& widgets = frame::Service::get<frame::widgets::WidgetRegistry>();
screen.setRoot(widgets->Create("AnalogClock"));
screen.MainLoop(); screen.MainLoop();
} }

View File

@@ -20,7 +20,12 @@ namespace frame::render
|| (position.x + current.left) >= root_size.width || (position.x + current.left) >= root_size.width
|| (position.y + current.top) >= root_size.height) || (position.y + current.top) >= root_size.height)
return; return;
image.at(position.x + current.left, position.y + current.top) = color; if(!invert)
image.at(position.x + current.left, position.y + current.top) =
color;
else
image.at(position.x + current.left, position.y + current.top) ^=
color;
} }
void RenderTarget::DrawLine(Vector const& start, void RenderTarget::DrawLine(Vector const& start,
@@ -154,6 +159,15 @@ namespace frame::render
{BottomRight.x, BottomRight.y - lw}); {BottomRight.x, BottomRight.y - lw});
} }
void
RenderTarget::DrawRect(Rect const& rect, uint8_t LineWidth, Color color)
{
DrawRect(Vector{rect.left, rect.top},
Vector{rect.left + rect.width, rect.top + rect.height},
LineWidth,
color);
}
void RenderTarget::DrawImage(Vector topLeft, Image const& image) void RenderTarget::DrawImage(Vector topLeft, Image const& image)
{ {
auto pos = topLeft; auto pos = topLeft;
@@ -204,7 +218,7 @@ namespace frame::render
auto const lineSpacing = pFont.getLineSpacing(size); auto const lineSpacing = pFont.getLineSpacing(size);
auto const lineSpacing_3 = lineSpacing / 3; auto const lineSpacing_3 = lineSpacing / 3;
Vector start{rect.top, rect.left}; Vector start{rect.left, rect.top};
auto text_height = pFont.getHeight(pText, size); auto text_height = pFont.getHeight(pText, size);
auto length = pFont.getLength(pText, size); auto length = pFont.getLength(pText, size);
auto y_offset = pFont.getMaxYOffset(pText, size); auto y_offset = pFont.getMaxYOffset(pText, size);

View File

@@ -24,6 +24,8 @@ namespace frame::render
Rect root_size; Rect root_size;
std::stack<Rect> Scissor; std::stack<Rect> Scissor;
bool invert = false;
public: public:
RenderTarget(Size size); RenderTarget(Size size);
@@ -47,6 +49,8 @@ namespace frame::render
uint8_t LineWidth, uint8_t LineWidth,
Color color = BLACK); Color color = BLACK);
void DrawRect(Rect const& rect, uint8_t LineWidth, Color color = BLACK);
void DrawImage(Vector topLeft, Image const& image); void DrawImage(Vector topLeft, Image const& image);
Vector DrawGlyph(Vector centerLine, Vector DrawGlyph(Vector centerLine,
@@ -81,6 +85,11 @@ namespace frame::render
void pushViewport(Rect rect); void pushViewport(Rect rect);
void popViewport(); void popViewport();
void setInvert(bool pInvert)
{
invert = pInvert;
}
private: private:
void DrawPointsMirrorCircle(Vector const& center, void DrawPointsMirrorCircle(Vector const& center,
Vector const& pos, Vector const& pos,

View File

@@ -0,0 +1,22 @@
#include "WidgetRegistry.hpp"
namespace frame::widgets
{
WidgetRegistry::WidgetRegistry()
: IService("WidgetRegistry")
{
}
Widget::shared_ptr WidgetRegistry::Create(std::string_view name) const
{
auto it = mWidgets.find(std::string{name});
if(it == mWidgets.end())
{
fmt::print("[ERROR] Widget {} not found!\n", name);
return nullptr;
}
return it->second();
}
} // namespace frame::widgets

View File

@@ -0,0 +1,52 @@
#pragma once
#include "../ServiceLocator.hpp"
#include "Widget.hpp"
#include <fmt/format.h>
#include <functional>
#include <string>
#include <unordered_map>
#define REGISTER_WIDGET(TYPE, NAME) \
static bool registered = \
frame::Service::get<frame::widgets::WidgetRegistry>()->Register<TYPE>( \
NAME)
namespace frame::widgets
{
class WidgetRegistry : public IService
{
using Creator = std::function<Widget::shared_ptr()>;
std::unordered_map<std::string, Creator> mWidgets;
public:
WidgetRegistry();
template<class WIDGET>
bool Register(std::string_view name);
Widget::shared_ptr Create(std::string_view name) const;
};
template<class WIDGET>
bool WidgetRegistry::Register(std::string_view name)
{
auto const name_string = std::string{name};
if(mWidgets.find(name_string) != mWidgets.end())
{
fmt::print("Widget {} already registered!\n", name);
return true;
}
mWidgets[name_string] = []() -> Widget::shared_ptr {
return WIDGET::Create();
};
fmt::print("Widget {} registered!\n", name);
return true;
}
} // namespace frame::widgets

View File

@@ -1,10 +1,17 @@
#include "Analog.hpp" #include "Analog.hpp"
#include "../../font/FontRegistry.hpp"
#include <ctime> #include <ctime>
#include <fmt/chrono.h>
#include <fmt/format.h> #include <fmt/format.h>
constexpr double pi = 3.14159; constexpr double pi = 3.14159;
#include "../WidgetRegistry.hpp"
REGISTER_WIDGET(frame::widgets::AnalogClock, "AnalogClock");
namespace frame::widgets namespace frame::widgets
{ {
@@ -72,8 +79,8 @@ namespace frame::widgets
auto const min_rad = (min_steps * c_time->tm_min - 90) * pi / 180.f; auto const min_rad = (min_steps * c_time->tm_min - 90) * pi / 180.f;
rt.DrawLine(center, rt.DrawLine(center,
{center.x + int32_t(std::cos(min_rad) * radius), {center.x + int32_t(std::cos(min_rad) * radius * 0.8),
center.y + int32_t(std::sin(min_rad) * radius)}); center.y + int32_t(std::sin(min_rad) * radius * 0.8)});
// Render Pointer Hour // Render Pointer Hour
@@ -89,6 +96,37 @@ namespace frame::widgets
center.y + int32_t(std::sin(hour_rad) * radius * 0.6)}); center.y + int32_t(std::sin(hour_rad) * radius * 0.6)});
setClear(); setClear();
// Text
auto font = font::GetFont("Fira Code");
if(!font)
return;
auto text = fmt::format("{:%d.%m.%Y}", fmt::localtime(t));
auto text_center = center;
text_center.y += radius / 2;
auto const text_height_h = radius / 4;
auto const text_width_h = radius / 2;
Rect text_box{text_center.y - text_height_h,
text_center.x - text_width_h,
text_width_h * 2,
text_height_h * 2};
auto font_size = font->getOptimalSize(text, text_box);
rt.setInvert(true);
rt.DrawText(text_box,
text,
*font,
font_size,
AlignHorizontal::CENTER,
AlignVertical::CENTER);
rt.setInvert(false);
} }
} // namespace frame::widgets } // namespace frame::widgets