Compare commits
13 Commits
0553927dad
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
e6591ce9be
|
|||
|
2a0334ba4b
|
|||
|
9867242098
|
|||
|
bacbf8a95b
|
|||
|
|
c43915a21e
|
||
|
|
9bb296f233
|
||
|
|
4c70f3440d
|
||
|
|
33f79d9f92
|
||
|
|
fe9fc4d69c
|
||
|
|
1772a3baff
|
||
|
|
243bf41e90
|
||
|
|
e1e410561c
|
||
|
|
0fe1509c6c
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,3 +35,4 @@
|
||||
build/*
|
||||
.vscode/*
|
||||
frame/src/Config.h
|
||||
.editorconfig
|
||||
|
||||
@@ -7,6 +7,8 @@ using json = nlohmann::json;
|
||||
|
||||
void PrintGlyph(sf::Font const& font, sf::Glyph const& g, unsigned int size);
|
||||
|
||||
void ExportSize(json& out, uint32_t size, sf::Font& font);
|
||||
|
||||
int main()
|
||||
{
|
||||
sf::Font font;
|
||||
@@ -16,46 +18,39 @@ int main()
|
||||
|
||||
fmt::print("Loaded {}\n", ok);
|
||||
|
||||
std::vector<char> chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
|
||||
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
|
||||
|
||||
fmt::print("{}\n", font.getInfo().family);
|
||||
fmt::print("Line Spacing: {}\n", font.getLineSpacing(14));
|
||||
|
||||
std::map<char, sf::Glyph> glyphs;
|
||||
|
||||
for(auto el : chars)
|
||||
{
|
||||
auto a = font.getGlyph(el, 14, false);
|
||||
|
||||
fmt::print("{}: w {} h {}\n",
|
||||
el,
|
||||
a.textureRect.width,
|
||||
a.textureRect.height);
|
||||
|
||||
fmt::print("- t: {} l: {} w: {} h: {}\n",
|
||||
a.bounds.top,
|
||||
a.bounds.left,
|
||||
a.bounds.width,
|
||||
a.bounds.height);
|
||||
|
||||
fmt::print("offset: {}\n\n", a.advance);
|
||||
|
||||
glyphs[el] = a;
|
||||
}
|
||||
|
||||
auto font_image = font.getTexture(14).copyToImage();
|
||||
auto size = font_image.getSize();
|
||||
|
||||
json export_font;
|
||||
export_font["name"] = font.getInfo().family;
|
||||
export_font["sizes"] = json::object();
|
||||
|
||||
auto& size_14 = export_font["sizes"]["14"];
|
||||
std::vector<uint32_t> sizes =
|
||||
{12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40, 100, 250};
|
||||
|
||||
size_14["LineSpacing"] = font.getLineSpacing(14);
|
||||
auto& Glyphs = size_14["Glyphs"];
|
||||
for(auto& size : sizes)
|
||||
{
|
||||
fmt::print("Export: {}\n", size);
|
||||
ExportSize(export_font["sizes"][fmt::format("{}", size)], size, font);
|
||||
}
|
||||
|
||||
std::fstream out(fmt::format("{}.json", font.getInfo().family),
|
||||
std::ios::out);
|
||||
out << export_font.dump();
|
||||
}
|
||||
|
||||
void ExportSize(json& out, uint32_t size, sf::Font& font)
|
||||
{
|
||||
std::map<char, sf::Glyph> glyphs;
|
||||
|
||||
for(uint8_t c = 32; c <= 126; ++c)
|
||||
{
|
||||
glyphs[(char)c] = font.getGlyph(c, size, false);
|
||||
}
|
||||
auto font_image = font.getTexture(size).copyToImage();
|
||||
|
||||
out["LineSpacing"] = font.getLineSpacing(size);
|
||||
auto& Glyphs = out["Glyphs"];
|
||||
|
||||
for(auto&& [c, g] : glyphs)
|
||||
{
|
||||
@@ -82,13 +77,6 @@ int main()
|
||||
|
||||
current["data"] = data;
|
||||
}
|
||||
|
||||
std::fstream out(fmt::format("{}.json", font.getInfo().family),
|
||||
std::ios::out);
|
||||
out << export_font.dump();
|
||||
|
||||
fmt::print("Size x: {} y: {}\n", size.x, size.y);
|
||||
font_image.saveToFile("Test.png");
|
||||
}
|
||||
|
||||
void PrintGlyph(sf::Font const& font, sf::Glyph const& g, unsigned int size)
|
||||
|
||||
@@ -2,23 +2,57 @@
|
||||
set(target frame)
|
||||
|
||||
configure_file (
|
||||
"${PROJECT_SOURCE_DIR}/frame/src/Config.h.in"
|
||||
"${PROJECT_SOURCE_DIR}/frame/src/Config.h"
|
||||
"${PROJECT_SOURCE_DIR}/frame/src/BuildConfig.h.in"
|
||||
"${PROJECT_SOURCE_DIR}/frame/src/BuildConfig.h"
|
||||
)
|
||||
|
||||
set(src
|
||||
src/main.cpp
|
||||
|
||||
src/Config.hpp
|
||||
src/Config.cpp
|
||||
|
||||
src/Size.hpp
|
||||
src/Vector.hpp
|
||||
src/Rect.hpp
|
||||
|
||||
src/Image.hpp
|
||||
src/Image.cpp
|
||||
|
||||
src/ScreenManager.hpp
|
||||
src/ScreenManager.cpp
|
||||
|
||||
src/ServiceLocator.hpp
|
||||
src/ServiceLocator.cpp
|
||||
|
||||
src/display/IDisplay.hpp
|
||||
src/display/Display.hpp
|
||||
src/display/Display.cpp
|
||||
|
||||
src/render/RenderTarget.hpp
|
||||
src/render/RenderTarget.cpp
|
||||
|
||||
src/font/Font.hpp
|
||||
src/font/Font.cpp
|
||||
src/font/Glyph.hpp
|
||||
src/font/Glyph.cpp
|
||||
src/font/FontRegistry.hpp
|
||||
src/font/FontRegistry.cpp
|
||||
|
||||
|
||||
src/widgets/Widget.hpp
|
||||
src/widgets/Widget.cpp
|
||||
src/widgets/ContainerWidget.hpp
|
||||
src/widgets/ContainerWidget.cpp
|
||||
src/widgets/WidgetRegistry.hpp
|
||||
src/widgets/WidgetRegistry.cpp
|
||||
src/widgets/clock/Digital.hpp
|
||||
src/widgets/clock/Digital.cpp
|
||||
src/widgets/clock/Analog.hpp
|
||||
src/widgets/clock/Analog.cpp
|
||||
src/widgets/clock/Date.hpp
|
||||
src/widgets/clock/Date.cpp
|
||||
|
||||
)
|
||||
|
||||
if (BUILD_EPD)
|
||||
@@ -38,6 +72,7 @@ endif (BUILD_VIRTUAL_DISPLAY)
|
||||
add_executable(${target} ${src})
|
||||
target_link_libraries(${target} PRIVATE
|
||||
fmt::fmt
|
||||
nlohmann_json::nlohmann_json
|
||||
)
|
||||
|
||||
if (BUILD_EPD)
|
||||
|
||||
2
frame/src/BuildConfig.h
Normal file
2
frame/src/BuildConfig.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#define BUILD_VIRTUAL_DISPLAY
|
||||
/* #undef BUILD_EPD */
|
||||
89
frame/src/Config.cpp
Normal file
89
frame/src/Config.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "Config.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#define PROPERTY(str, member) str.member = j.value(#member, str.member)
|
||||
#define PROPERTY_OPT(str, member, type) \
|
||||
str.member = \
|
||||
j.contains(#member) ? j[#member].get<type>() : std::optional<type>{};
|
||||
|
||||
#define OBJ(str, mem) \
|
||||
{ \
|
||||
# mem, str.mem \
|
||||
}
|
||||
|
||||
Config Config::Load()
|
||||
{
|
||||
fs::path const config_path{"config.json"};
|
||||
|
||||
if(fs::is_regular_file(config_path))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::fstream file(config_path.string(), std::ios::in);
|
||||
|
||||
return json::parse(file);
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
fmt::print("Error: {}\n", e.what());
|
||||
|
||||
// TODO: Create Error Display Widget
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("[INFO] Creating config file\n");
|
||||
Config config;
|
||||
std::fstream file(config_path.string(), std::ios::out);
|
||||
file << json(config).dump();
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// == to json ==
|
||||
void to_json(json& j, Config const& c)
|
||||
{
|
||||
j = {OBJ(c, font), OBJ(c, root)};
|
||||
}
|
||||
|
||||
void to_json(json& j, FontConfig const& fc)
|
||||
{
|
||||
j = {OBJ(fc, base_path), OBJ(fc, fonts)};
|
||||
}
|
||||
|
||||
void to_json(json& j, WidgetConfig const& w)
|
||||
{
|
||||
j = {OBJ(w, name), OBJ(w, parameters), OBJ(w, widgets)};
|
||||
|
||||
if(w.slot)
|
||||
{
|
||||
j["slot"] = w.slot.value();
|
||||
}
|
||||
}
|
||||
|
||||
// == from json ==
|
||||
void from_json(json const& j, Config& c)
|
||||
{
|
||||
PROPERTY(c, font);
|
||||
PROPERTY(c, root);
|
||||
}
|
||||
|
||||
void from_json(json const& j, FontConfig& fc)
|
||||
{
|
||||
PROPERTY(fc, base_path);
|
||||
PROPERTY(fc, fonts);
|
||||
}
|
||||
|
||||
void from_json(json const& j, WidgetConfig& w)
|
||||
{
|
||||
PROPERTY(w, name);
|
||||
PROPERTY(w, parameters);
|
||||
PROPERTY(w, widgets);
|
||||
PROPERTY_OPT(w, slot, int);
|
||||
}
|
||||
38
frame/src/Config.hpp
Normal file
38
frame/src/Config.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct FontConfig
|
||||
{
|
||||
std::string base_path = "fonts/";
|
||||
std::unordered_map<std::string, std::string> fonts{
|
||||
{"Fira Code", "FiraCode.json"}};
|
||||
};
|
||||
|
||||
struct WidgetConfig
|
||||
{
|
||||
std::string name = "DigitalClock";
|
||||
std::unordered_map<std::string, json> parameters = {};
|
||||
std::vector<WidgetConfig> widgets = {};
|
||||
std::optional<int> slot = {};
|
||||
};
|
||||
|
||||
struct Config
|
||||
{
|
||||
FontConfig font{};
|
||||
WidgetConfig root{};
|
||||
|
||||
static Config Load();
|
||||
};
|
||||
|
||||
void to_json(json& j, Config const& c);
|
||||
void to_json(json& j, FontConfig const& fc);
|
||||
void to_json(json& j, WidgetConfig const& w);
|
||||
|
||||
void from_json(json const& j, Config& c);
|
||||
void from_json(json const& j, FontConfig& fc);
|
||||
void from_json(json const& j, WidgetConfig& w);
|
||||
@@ -1,7 +1,11 @@
|
||||
#include "Image.hpp"
|
||||
|
||||
#include "Color.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
namespace frame
|
||||
{
|
||||
|
||||
@@ -36,6 +40,11 @@ namespace frame
|
||||
return mBuffer.at(toInternal(x, y));
|
||||
}
|
||||
|
||||
void Image::Clear(Color color)
|
||||
{
|
||||
memset(mBuffer.data(), color, mBuffer.size());
|
||||
}
|
||||
|
||||
void Image::operator=(Image const& image)
|
||||
{
|
||||
mHeight = image.mHeight;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
#include "Color.hpp"
|
||||
#include "Size.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace frame
|
||||
{
|
||||
|
||||
@@ -25,6 +27,8 @@ namespace frame
|
||||
uint8_t& at(uint32_t x, uint32_t y);
|
||||
uint8_t const& at(uint32_t x, uint32_t y) const;
|
||||
|
||||
void Clear(Color color);
|
||||
|
||||
auto getWidth() const
|
||||
{
|
||||
return mWidth;
|
||||
|
||||
15
frame/src/Rect.hpp
Normal file
15
frame/src/Rect.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace frame
|
||||
{
|
||||
|
||||
struct Rect
|
||||
{
|
||||
int32_t top;
|
||||
int32_t left;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
};
|
||||
} // namespace frame
|
||||
64
frame/src/ScreenManager.cpp
Normal file
64
frame/src/ScreenManager.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "ScreenManager.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace frame
|
||||
{
|
||||
|
||||
ScreenManager::ScreenManager(std::unique_ptr<display::IDisplay> pDisplay)
|
||||
: mDisplay(std::move(pDisplay))
|
||||
, mRenderTarget(mDisplay->getSize())
|
||||
{
|
||||
}
|
||||
|
||||
void ScreenManager::Update()
|
||||
{
|
||||
if(mRoot)
|
||||
{
|
||||
mRoot->Update();
|
||||
}
|
||||
if(mDisplay)
|
||||
{
|
||||
mDisplay->Update();
|
||||
if(!mDisplay->isOpen())
|
||||
{
|
||||
mIsRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenManager::Render()
|
||||
{
|
||||
if(mRoot)
|
||||
{
|
||||
mRenderTarget.Clear();
|
||||
|
||||
mRoot->setSize(mDisplay->getSize());
|
||||
mRoot->Render(mRenderTarget);
|
||||
|
||||
mDisplay->Display(mRenderTarget.getImage());
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenManager::MainLoop()
|
||||
{
|
||||
while(mIsRunning)
|
||||
{
|
||||
Update();
|
||||
|
||||
if(mRoot && mRoot->isDirty())
|
||||
{
|
||||
Render();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(mUpdateInterval);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenManager::setRoot(widgets::Widget::shared_ptr widget)
|
||||
{
|
||||
mRoot = widget;
|
||||
mRoot->ComputeChildSize(mDisplay->getSize());
|
||||
}
|
||||
|
||||
} // namespace frame
|
||||
36
frame/src/ScreenManager.hpp
Normal file
36
frame/src/ScreenManager.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "display/IDisplay.hpp"
|
||||
#include "render/RenderTarget.hpp"
|
||||
#include "widgets/Widget.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace frame
|
||||
{
|
||||
|
||||
class ScreenManager
|
||||
{
|
||||
std::unique_ptr<display::IDisplay> mDisplay;
|
||||
|
||||
render::RenderTarget mRenderTarget;
|
||||
widgets::Widget::shared_ptr mRoot;
|
||||
|
||||
std::chrono::duration<double> mUpdateInterval = 0.5s;
|
||||
|
||||
bool mIsRunning = true;
|
||||
|
||||
public:
|
||||
ScreenManager(std::unique_ptr<display::IDisplay> pDisplay);
|
||||
|
||||
void Update();
|
||||
void Render();
|
||||
|
||||
void MainLoop();
|
||||
|
||||
void setRoot(widgets::Widget::shared_ptr widget);
|
||||
};
|
||||
|
||||
} // namespace frame
|
||||
19
frame/src/ServiceLocator.cpp
Normal file
19
frame/src/ServiceLocator.cpp
Normal 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
|
||||
40
frame/src/ServiceLocator.hpp
Normal file
40
frame/src/ServiceLocator.hpp
Normal 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
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "Display.hpp"
|
||||
|
||||
#include "../Config.h"
|
||||
#include "../BuildConfig.h"
|
||||
|
||||
#ifdef BUILD_EPD
|
||||
# include "EPD_7in5_V2.hpp"
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace frame::display
|
||||
std::string mName;
|
||||
Size mSize;
|
||||
|
||||
bool mIsOpen = true;
|
||||
|
||||
public:
|
||||
IDisplay(std::string_view pName, Size pSize)
|
||||
: mName(pName)
|
||||
@@ -33,5 +35,15 @@ namespace frame::display
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
bool isOpen() const
|
||||
{
|
||||
return mIsOpen;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
mIsOpen = false;
|
||||
}
|
||||
};
|
||||
} // namespace frame::display
|
||||
|
||||
@@ -55,6 +55,10 @@ namespace frame::display
|
||||
sf::Event ev;
|
||||
while(window.pollEvent(ev))
|
||||
{
|
||||
if(ev.type == sf::Event::Closed)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,64 @@ namespace frame::font
|
||||
}
|
||||
}
|
||||
|
||||
Glyph const& Font::getGlyph(uint32_t size, char c) const
|
||||
{
|
||||
return sizes.at(size).at(c);
|
||||
}
|
||||
|
||||
uint32_t Font::getLength(std::string_view text, uint32_t size) const
|
||||
{
|
||||
uint32_t length = 0;
|
||||
|
||||
for(auto c : text)
|
||||
{
|
||||
auto const& g = getGlyph(size, c);
|
||||
length += g.advance;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
uint32_t Font::getHeight(std::string_view text, uint32_t size) const
|
||||
{
|
||||
uint32_t height = 0;
|
||||
|
||||
for(auto c : text)
|
||||
{
|
||||
auto const& g = getGlyph(size, c);
|
||||
height = std::max((uint32_t)g.height, height);
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
int32_t Font::getMaxYOffset(std::string_view text, uint32_t size) const
|
||||
{
|
||||
int32_t y_offset = 0;
|
||||
|
||||
for(auto c : text)
|
||||
{
|
||||
auto const& g = getGlyph(size, c);
|
||||
y_offset = std::min(g.y_offset, 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)
|
||||
{
|
||||
json file_data = json::parse(os);
|
||||
@@ -46,9 +104,4 @@ namespace frame::font
|
||||
}
|
||||
}
|
||||
|
||||
Glyph const& Font::getGlyph(uint32_t size, char c) const
|
||||
{
|
||||
return sizes.at(size).at(c);
|
||||
}
|
||||
|
||||
} // namespace frame::font
|
||||
@@ -1,20 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Rect.hpp"
|
||||
#include "Glyph.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
namespace frame::font
|
||||
{
|
||||
using Glyphs = std::map<char, Glyph>;
|
||||
using LineSpacing = std::map<uint8_t, uint32_t>;
|
||||
using LineSpacing = std::map<uint32_t, uint32_t>;
|
||||
|
||||
class Font
|
||||
{
|
||||
std::string name;
|
||||
std::map<uint8_t, Glyphs> sizes;
|
||||
std::map<uint32_t, Glyphs> sizes;
|
||||
LineSpacing lineSpacing;
|
||||
|
||||
public:
|
||||
@@ -23,6 +25,17 @@ namespace frame::font
|
||||
|
||||
Glyph const& getGlyph(uint32_t size, char c) const;
|
||||
|
||||
uint32_t getLength(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;
|
||||
|
||||
uint32_t getOptimalSize(std::string_view text, Rect rect) const;
|
||||
|
||||
uint32_t getLineSpacing(uint32_t size) const
|
||||
{
|
||||
return lineSpacing.at(size);
|
||||
}
|
||||
|
||||
protected:
|
||||
void LoadFromStream(std::istream& os);
|
||||
};
|
||||
|
||||
98
frame/src/font/FontRegistry.cpp
Normal file
98
frame/src/font/FontRegistry.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "FontRegistry.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
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& LocateRegistry()
|
||||
{
|
||||
return *Service::get<FontRegistry>();
|
||||
}
|
||||
|
||||
FontRegistry::FontRegistry()
|
||||
: IService("FontRegistry")
|
||||
{
|
||||
}
|
||||
|
||||
void FontRegistry::LoadFromConfig(FontConfig const& config)
|
||||
{
|
||||
fs::path base{config.base_path};
|
||||
|
||||
for(auto const& [name, file] : config.fonts)
|
||||
{
|
||||
Load(name, (base / file).string());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool FontRegistry::Load(std::string_view name, std::string_view path)
|
||||
{
|
||||
if(fonts.find(std::string{name}) != fonts.end())
|
||||
{
|
||||
fmt::print("Font {} allready loaded!\n", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
fmt::print("Loading font \"{}\": ", name);
|
||||
auto ptr = Font::LoadFromFile(path);
|
||||
|
||||
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
|
||||
31
frame/src/font/FontRegistry.hpp
Normal file
31
frame/src/font/FontRegistry.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Config.hpp"
|
||||
#include "../ServiceLocator.hpp"
|
||||
#include "Font.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace frame::font
|
||||
{
|
||||
class FontRegistry;
|
||||
|
||||
std::shared_ptr<Font> GetFont(std::string_view name);
|
||||
bool LoadFont(std::string_view name);
|
||||
FontRegistry& LocateRegistry();
|
||||
|
||||
class FontRegistry : public IService
|
||||
{
|
||||
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
|
||||
|
||||
public:
|
||||
FontRegistry();
|
||||
|
||||
void LoadFromConfig(FontConfig const& config);
|
||||
bool Load(std::string_view name);
|
||||
bool Load(std::string_view name, std::string_view path);
|
||||
std::shared_ptr<Font> Get(std::string_view name);
|
||||
};
|
||||
|
||||
} // namespace frame::font
|
||||
@@ -13,9 +13,9 @@ namespace frame::font
|
||||
Image image;
|
||||
uint8_t height;
|
||||
uint8_t width;
|
||||
int8_t x_offset;
|
||||
int8_t y_offset;
|
||||
int8_t advance;
|
||||
int32_t x_offset;
|
||||
int32_t y_offset;
|
||||
int32_t advance;
|
||||
|
||||
public:
|
||||
Glyph() = default;
|
||||
|
||||
@@ -6,10 +6,53 @@
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#include "Config.hpp"
|
||||
#include "ScreenManager.hpp"
|
||||
#include "display/Display.hpp"
|
||||
#include "font/Font.hpp"
|
||||
#include "render/RenderTarget.hpp"
|
||||
#include "font/FontRegistry.hpp"
|
||||
//#include "render/RenderTarget.hpp"
|
||||
//#include "widgets/clock/Analog.hpp"
|
||||
#include "widgets/ContainerWidget.hpp"
|
||||
#include "widgets/WidgetRegistry.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
// == == Config == ==
|
||||
auto config = Config::Load();
|
||||
|
||||
// == == Display == ==
|
||||
auto display = frame::display::Create();
|
||||
|
||||
if(!display)
|
||||
{
|
||||
fmt::print("Error: Not display driver!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
display->Init();
|
||||
|
||||
// frame::font::LoadFont("Fira Code");
|
||||
frame::font::LocateRegistry().LoadFromConfig(config.font);
|
||||
|
||||
frame::ScreenManager screen(std::move(display));
|
||||
|
||||
auto const& widgets = frame::Service::get<frame::widgets::WidgetRegistry>();
|
||||
|
||||
// std::shared_ptr<frame::widgets::ContainerWidget> con =
|
||||
// std::static_pointer_cast<frame::widgets::ContainerWidget>(
|
||||
// widgets->Create("AnalogClock"));
|
||||
|
||||
// con->setSlot(0, widgets->Create("DigitalClock"));
|
||||
|
||||
// con->setSlot(1, widgets->Create("Date"));
|
||||
|
||||
auto con = frame::widgets::LocateRegistry().BuildFromConfig(config.root);
|
||||
screen.setRoot(con);
|
||||
|
||||
screen.MainLoop();
|
||||
}
|
||||
|
||||
/*
|
||||
int main()
|
||||
{
|
||||
constexpr double pi = 3.14159;
|
||||
@@ -65,15 +108,109 @@ int main()
|
||||
{110, (int)display->getSize().height - 10},
|
||||
5);
|
||||
|
||||
target.DrawText("abcdefghijklmnopqrstuvwxyz", {100, 50}, *font, 14);
|
||||
target.DrawText("Hallo Welt!", {100, 50}, *font, 14);
|
||||
target.DrawText("Hallo Welt!", {50, 100}, *font, 30);
|
||||
|
||||
display->Display(target.getImage());
|
||||
|
||||
std::this_thread::sleep_for(10s);
|
||||
|
||||
target.Clear();
|
||||
|
||||
frame::Vector pos = display->getSize() / 2;
|
||||
|
||||
auto text = "10:30";
|
||||
|
||||
pos.x -= font->getLength(text, 250) / 2;
|
||||
pos.y += font->getHeight(text, 250) / 2;
|
||||
pos.y -= font->getMaxYOffset(text, 250) + font->getHeight(text, 250);
|
||||
|
||||
// target.DrawTextGlyphBounds(text, pos, *font, 250);
|
||||
// target.DrawLine({0, (int32_t)pos.y},
|
||||
// {(int32_t)display->getSize().width, pos.y});
|
||||
|
||||
auto rect = frame::Rect{0,
|
||||
0,
|
||||
(int)display->getSize().width,
|
||||
(int)display->getSize().height};
|
||||
|
||||
target.DrawText(rect, text, *font, 250);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect, text, *font, 250, frame::AlignHorizontal::CENTER);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect, text, *font, 250, frame::AlignHorizontal::RIGHT);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect,
|
||||
text,
|
||||
*font,
|
||||
250,
|
||||
frame::AlignHorizontal::LEFT,
|
||||
frame::AlignVertical::CENTER);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect,
|
||||
text,
|
||||
*font,
|
||||
250,
|
||||
frame::AlignHorizontal::CENTER,
|
||||
frame::AlignVertical::CENTER);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect,
|
||||
text,
|
||||
*font,
|
||||
250,
|
||||
frame::AlignHorizontal::RIGHT,
|
||||
frame::AlignVertical::CENTER);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect,
|
||||
text,
|
||||
*font,
|
||||
250,
|
||||
frame::AlignHorizontal::LEFT,
|
||||
frame::AlignVertical::BOTTOM);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect,
|
||||
text,
|
||||
*font,
|
||||
250,
|
||||
frame::AlignHorizontal::CENTER,
|
||||
frame::AlignVertical::BOTTOM);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
target.Clear();
|
||||
target.DrawText(rect,
|
||||
text,
|
||||
*font,
|
||||
250,
|
||||
frame::AlignHorizontal::RIGHT,
|
||||
frame::AlignVertical::BOTTOM);
|
||||
display->Display(target.getImage());
|
||||
std::this_thread::sleep_for(2s);
|
||||
|
||||
display->Clear(frame::display::Color::WHITE);
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
#include <EPD_7in5_V2.h>
|
||||
|
||||
|
||||
@@ -7,15 +7,25 @@ namespace frame::render
|
||||
|
||||
RenderTarget::RenderTarget(Size size)
|
||||
: image(size)
|
||||
, root_size{0, 0, (int)size.width, (int)size.height}
|
||||
{
|
||||
}
|
||||
|
||||
void RenderTarget::DrawPixel(Vector const& position, Color color)
|
||||
{
|
||||
if(position.x < 0 || position.y < 0 || position.x >= image.getWidth()
|
||||
|| position.y >= image.getHeight())
|
||||
auto current = getCurrentSize();
|
||||
|
||||
if(position.x < 0 || position.y < 0 || position.x >= current.width
|
||||
|| position.y >= current.height
|
||||
|| (position.x + current.left) >= root_size.width
|
||||
|| (position.y + current.top) >= root_size.height)
|
||||
return;
|
||||
image.at(position.x, position.y) = 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,
|
||||
@@ -77,12 +87,16 @@ namespace frame::render
|
||||
|
||||
void RenderTarget::DrawCircle(Vector const& center,
|
||||
int32_t radius,
|
||||
int32_t line,
|
||||
Color color)
|
||||
{
|
||||
int32_t const r_2 = radius * radius;
|
||||
Vector pos{0, -radius};
|
||||
|
||||
DrawPointsMirrorCircle(center, pos, color);
|
||||
for(auto i = 0; i < line; ++i)
|
||||
{
|
||||
DrawPointsMirrorCircle(center, {pos.x, pos.y + i}, color);
|
||||
}
|
||||
|
||||
while(pos.x <= radius / std::sqrt(2.f))
|
||||
{
|
||||
@@ -102,7 +116,10 @@ namespace frame::render
|
||||
E += 2 * pos.x - 2 * pos.y + 5;
|
||||
}
|
||||
|
||||
DrawPointsMirrorCircle(center, pos, color);
|
||||
for(auto i = 0; i < line; ++i)
|
||||
{
|
||||
DrawPointsMirrorCircle(center, {pos.x, pos.y + i}, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +159,15 @@ namespace frame::render
|
||||
{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)
|
||||
{
|
||||
auto pos = topLeft;
|
||||
@@ -173,7 +199,7 @@ namespace frame::render
|
||||
void RenderTarget::DrawText(std::string_view pText,
|
||||
Vector pCenterLineStart,
|
||||
font::Font const& pFont,
|
||||
uint8_t size)
|
||||
uint32_t size)
|
||||
{
|
||||
auto pos = pCenterLineStart;
|
||||
for(auto c : pText)
|
||||
@@ -182,6 +208,97 @@ namespace frame::render
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTarget::DrawText(Rect rect,
|
||||
std::string pText,
|
||||
font::Font const& pFont,
|
||||
uint32_t size,
|
||||
AlignHorizontal alignHorizontal,
|
||||
AlignVertical alignVertical)
|
||||
{
|
||||
auto const lineSpacing = pFont.getLineSpacing(size);
|
||||
auto const lineSpacing_3 = lineSpacing / 3;
|
||||
|
||||
Vector start{rect.left, rect.top};
|
||||
auto text_height = pFont.getHeight(pText, size);
|
||||
auto length = pFont.getLength(pText, size);
|
||||
auto y_offset = pFont.getMaxYOffset(pText, size);
|
||||
|
||||
switch(alignVertical)
|
||||
{
|
||||
case AlignVertical::TOP: start.y -= y_offset; break;
|
||||
case AlignVertical::CENTER:
|
||||
start.y += rect.height / 2;
|
||||
start.y += text_height / 2;
|
||||
start.y -= y_offset + text_height;
|
||||
break;
|
||||
case AlignVertical::BOTTOM:
|
||||
start.y += rect.height;
|
||||
start.y -= text_height + y_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(alignHorizontal)
|
||||
{
|
||||
case AlignHorizontal::CENTER:
|
||||
start.x += rect.width / 2;
|
||||
start.x -= length / 2;
|
||||
break;
|
||||
|
||||
case AlignHorizontal::RIGHT: start.x += rect.width - length; break;
|
||||
}
|
||||
|
||||
DrawText(pText, start, pFont, size);
|
||||
}
|
||||
|
||||
void RenderTarget::DrawTextGlyphBounds(std::string_view pText,
|
||||
Vector pCenterLineStart,
|
||||
font::Font const& pFont,
|
||||
uint32_t size)
|
||||
{
|
||||
auto pos = pCenterLineStart;
|
||||
|
||||
auto lineSpacing = pFont.getLineSpacing(size);
|
||||
auto _3 = lineSpacing / 3;
|
||||
for(auto c : pText)
|
||||
{
|
||||
auto const& g = pFont.getGlyph(size, c);
|
||||
DrawRect({int32_t(pos.x), int32_t(pos.y - _3 * 2)},
|
||||
{int32_t(pos.x + g.advance), int32_t(pos.y + _3)},
|
||||
1);
|
||||
|
||||
pos.x += g.advance;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTarget::Clear(Color color)
|
||||
{
|
||||
image.Clear(color);
|
||||
}
|
||||
|
||||
Rect RenderTarget::getCurrentSize() const
|
||||
{
|
||||
if(Scissor.size() == 0)
|
||||
{
|
||||
return root_size;
|
||||
}
|
||||
return Scissor.top();
|
||||
}
|
||||
|
||||
void RenderTarget::pushViewport(Rect rect)
|
||||
{
|
||||
auto current = getCurrentSize();
|
||||
|
||||
Scissor.push({current.top + rect.top,
|
||||
current.left + rect.left,
|
||||
rect.width,
|
||||
rect.height});
|
||||
}
|
||||
|
||||
void RenderTarget::popViewport()
|
||||
{
|
||||
Scissor.pop();
|
||||
}
|
||||
|
||||
void RenderTarget::DrawPointsMirrorCircle(Vector const& center,
|
||||
Vector const& pos,
|
||||
Color color)
|
||||
|
||||
@@ -2,9 +2,18 @@
|
||||
|
||||
#include "../Color.hpp"
|
||||
#include "../Image.hpp"
|
||||
#include "../Rect.hpp"
|
||||
#include "../Vector.hpp"
|
||||
#include "../font/Font.hpp"
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace frame
|
||||
{
|
||||
enum class AlignVertical { TOP, CENTER, BOTTOM };
|
||||
enum class AlignHorizontal { LEFT, CENTER, RIGHT };
|
||||
} // namespace frame
|
||||
|
||||
namespace frame::render
|
||||
{
|
||||
|
||||
@@ -12,6 +21,11 @@ namespace frame::render
|
||||
{
|
||||
Image image;
|
||||
|
||||
Rect root_size;
|
||||
std::stack<Rect> Scissor;
|
||||
|
||||
bool invert = false;
|
||||
|
||||
public:
|
||||
RenderTarget(Size size);
|
||||
|
||||
@@ -23,6 +37,7 @@ namespace frame::render
|
||||
|
||||
void DrawCircle(Vector const& center,
|
||||
int32_t radius,
|
||||
int32_t line,
|
||||
Color color = BLACK);
|
||||
|
||||
void DrawRectFilled(Vector const& TopLeft,
|
||||
@@ -34,6 +49,8 @@ namespace frame::render
|
||||
uint8_t LineWidth,
|
||||
Color color = BLACK);
|
||||
|
||||
void DrawRect(Rect const& rect, uint8_t LineWidth, Color color = BLACK);
|
||||
|
||||
void DrawImage(Vector topLeft, Image const& image);
|
||||
|
||||
Vector DrawGlyph(Vector centerLine,
|
||||
@@ -43,13 +60,36 @@ namespace frame::render
|
||||
void DrawText(std::string_view pText,
|
||||
Vector pCenterLineStart,
|
||||
font::Font const& pFont,
|
||||
uint8_t size);
|
||||
uint32_t size);
|
||||
|
||||
void DrawText(Rect rect,
|
||||
std::string pText,
|
||||
font::Font const& pFont,
|
||||
uint32_t size,
|
||||
AlignHorizontal alignHorizontal = AlignHorizontal::LEFT,
|
||||
AlignVertical alignVertical = AlignVertical::TOP);
|
||||
|
||||
void DrawTextGlyphBounds(std::string_view pText,
|
||||
Vector pCenterLineStart,
|
||||
font::Font const& pFont,
|
||||
uint32_t size);
|
||||
|
||||
void Clear(Color color = WHITE);
|
||||
|
||||
Image const& getImage()
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
Rect getCurrentSize() const;
|
||||
void pushViewport(Rect rect);
|
||||
void popViewport();
|
||||
|
||||
void setInvert(bool pInvert)
|
||||
{
|
||||
invert = pInvert;
|
||||
}
|
||||
|
||||
private:
|
||||
void DrawPointsMirrorCircle(Vector const& center,
|
||||
Vector const& pos,
|
||||
|
||||
35
frame/src/widgets/ContainerWidget.cpp
Normal file
35
frame/src/widgets/ContainerWidget.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "ContainerWidget.hpp"
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
ContainerWidget::ContainerWidget(size_t slots)
|
||||
{
|
||||
widgets.resize(slots);
|
||||
maxSlots = slots;
|
||||
}
|
||||
|
||||
Widget::shared_ptr ContainerWidget::getSlot(size_t slot) const
|
||||
{
|
||||
if(slot >= maxSlots)
|
||||
return nullptr;
|
||||
return widgets[slot];
|
||||
}
|
||||
|
||||
void ContainerWidget::setSlot(size_t slot, Widget::shared_ptr ptr)
|
||||
{
|
||||
ptr->setParent(this);
|
||||
widgets[slot] = ptr;
|
||||
}
|
||||
|
||||
void ContainerWidget::UpdateWidgets()
|
||||
{
|
||||
for(auto& el : widgets)
|
||||
{
|
||||
if(el)
|
||||
{
|
||||
el->Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace frame::widgets
|
||||
23
frame/src/widgets/ContainerWidget.hpp
Normal file
23
frame/src/widgets/ContainerWidget.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
class ContainerWidget : public Widget
|
||||
{
|
||||
std::vector<Widget::shared_ptr> widgets;
|
||||
size_t maxSlots = 0;
|
||||
|
||||
public:
|
||||
ContainerWidget(size_t slots);
|
||||
|
||||
Widget::shared_ptr getSlot(size_t slot) const;
|
||||
void setSlot(size_t slot, Widget::shared_ptr);
|
||||
|
||||
void UpdateWidgets();
|
||||
};
|
||||
|
||||
} // namespace frame::widgets
|
||||
56
frame/src/widgets/Widget.cpp
Normal file
56
frame/src/widgets/Widget.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "Widget.hpp"
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
Widget::week_ptr Widget::getParent() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void Widget::setParent(week_ptr ptr)
|
||||
{
|
||||
mParent = ptr;
|
||||
}
|
||||
|
||||
bool Widget::isDirty() const
|
||||
{
|
||||
return mDirty;
|
||||
}
|
||||
|
||||
void Widget::setSize(Vector size)
|
||||
{
|
||||
mSize = size;
|
||||
ComputeChildSize(size);
|
||||
}
|
||||
|
||||
Vector const& Widget::getSize() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
void Widget::setPosition(Vector size)
|
||||
{
|
||||
mRelativePosition = size;
|
||||
}
|
||||
|
||||
Vector Widget::getPosition() const
|
||||
{
|
||||
return mRelativePosition;
|
||||
}
|
||||
|
||||
void Widget::setDirty()
|
||||
{
|
||||
mDirty = true;
|
||||
if(mParent)
|
||||
{
|
||||
mParent->setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setClear()
|
||||
{
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
} // namespace frame::widgets
|
||||
44
frame/src/widgets/Widget.hpp
Normal file
44
frame/src/widgets/Widget.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "../Rect.hpp"
|
||||
#include "../render/RenderTarget.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
class Widget
|
||||
{
|
||||
public:
|
||||
using shared_ptr = std::shared_ptr<Widget>;
|
||||
using week_ptr = Widget*;
|
||||
|
||||
public:
|
||||
virtual ~Widget() = default;
|
||||
|
||||
virtual void Update() = 0;
|
||||
virtual void Render(render::RenderTarget& rt) = 0;
|
||||
virtual void ComputeChildSize(Vector size){};
|
||||
|
||||
week_ptr getParent() const;
|
||||
void setParent(week_ptr ptr);
|
||||
|
||||
void setSize(Vector size);
|
||||
Vector const& getSize() const;
|
||||
|
||||
void setPosition(Vector size);
|
||||
Vector getPosition() const;
|
||||
|
||||
bool isDirty() const;
|
||||
void setDirty();
|
||||
void setClear();
|
||||
|
||||
private:
|
||||
week_ptr mParent = nullptr;
|
||||
bool mDirty = true;
|
||||
|
||||
Vector mRelativePosition;
|
||||
Vector mSize;
|
||||
};
|
||||
|
||||
} // namespace frame::widgets
|
||||
43
frame/src/widgets/WidgetRegistry.cpp
Normal file
43
frame/src/widgets/WidgetRegistry.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "WidgetRegistry.hpp"
|
||||
|
||||
#include "ContainerWidget.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();
|
||||
}
|
||||
|
||||
Widget::shared_ptr WidgetRegistry::BuildFromConfig(WidgetConfig const& w)
|
||||
{
|
||||
fmt::print("INFO: Creating widget {} \n", w.name);
|
||||
auto widget = Create(w.name);
|
||||
|
||||
auto container = std::dynamic_pointer_cast<ContainerWidget>(widget);
|
||||
if(container)
|
||||
{
|
||||
int counter = 0;
|
||||
for(auto const& el : w.widgets)
|
||||
{
|
||||
int slot = el.slot ? el.slot.value() : counter;
|
||||
|
||||
container->setSlot(slot, BuildFromConfig(el));
|
||||
}
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
|
||||
} // namespace frame::widgets
|
||||
59
frame/src/widgets/WidgetRegistry.hpp
Normal file
59
frame/src/widgets/WidgetRegistry.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "../Config.hpp"
|
||||
#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;
|
||||
|
||||
Widget::shared_ptr BuildFromConfig(WidgetConfig const& w);
|
||||
};
|
||||
|
||||
inline WidgetRegistry& LocateRegistry()
|
||||
{
|
||||
return *Service::get<frame::widgets::WidgetRegistry>();
|
||||
}
|
||||
|
||||
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
|
||||
180
frame/src/widgets/clock/Analog.cpp
Normal file
180
frame/src/widgets/clock/Analog.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#include "Analog.hpp"
|
||||
|
||||
#include "../../font/FontRegistry.hpp"
|
||||
|
||||
#include <ctime>
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
constexpr double pi = 3.14159;
|
||||
|
||||
#include "../WidgetRegistry.hpp"
|
||||
|
||||
REGISTER_WIDGET(frame::widgets::AnalogClock, "AnalogClock");
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
Widget::shared_ptr AnalogClock::Create()
|
||||
{
|
||||
return std::make_shared<AnalogClock>();
|
||||
}
|
||||
|
||||
AnalogClock::AnalogClock()
|
||||
: ContainerWidget(2)
|
||||
{
|
||||
}
|
||||
|
||||
void AnalogClock::Update()
|
||||
{
|
||||
auto const now = std::chrono::system_clock::now();
|
||||
auto const min = std::chrono::floor<std::chrono::minutes>(now);
|
||||
|
||||
if(last_time != min)
|
||||
{
|
||||
setDirty();
|
||||
last_time = min;
|
||||
}
|
||||
|
||||
UpdateWidgets();
|
||||
}
|
||||
|
||||
void AnalogClock::Render(render::RenderTarget& rt)
|
||||
{
|
||||
auto const size = rt.getCurrentSize();
|
||||
auto const min = std::min(size.height, size.width);
|
||||
|
||||
auto const radius = (min / 2) - 10;
|
||||
|
||||
auto const center = Vector{size.width / 2, size.height / 2};
|
||||
|
||||
rt.DrawCircle(center, radius, 4);
|
||||
rt.DrawCircle(center, 6, 4);
|
||||
|
||||
auto step = 360 / 60;
|
||||
|
||||
for(int i = 0; i < 60; ++i)
|
||||
{
|
||||
double const x = std::cos(step * i * pi / 180.f);
|
||||
double const y = std::sin(step * i * pi / 180.f);
|
||||
|
||||
auto lineLength = 10;
|
||||
if(i % 5 == 0)
|
||||
{
|
||||
lineLength = 20;
|
||||
if(i % 3 == 0)
|
||||
{
|
||||
lineLength = 50;
|
||||
}
|
||||
}
|
||||
|
||||
auto const fromCenter = radius - lineLength;
|
||||
|
||||
rt.DrawLine({int32_t(x * (radius - 1) + center.x),
|
||||
int32_t(y * (radius - 1) + center.y)},
|
||||
{int32_t(x * fromCenter + center.x),
|
||||
int32_t(y * fromCenter + center.y)});
|
||||
}
|
||||
|
||||
// pls c++ 20
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(last_time);
|
||||
auto c_time = std::localtime(&t);
|
||||
|
||||
// Render Pointer Minute
|
||||
|
||||
auto const min_steps = 360 / 60;
|
||||
auto const min_rad = (min_steps * c_time->tm_min - 90) * pi / 180.f;
|
||||
|
||||
rt.DrawLine(center,
|
||||
{center.x + int32_t(std::cos(min_rad) * radius * 0.8),
|
||||
center.y + int32_t(std::sin(min_rad) * radius * 0.8)});
|
||||
|
||||
// Render Pointer Hour
|
||||
|
||||
auto const hour_steps = 360 / 12;
|
||||
auto hour_deg = (hour_steps * c_time->tm_hour - 90);
|
||||
auto const normalied_min = (c_time->tm_min / 59.0);
|
||||
hour_deg += int32_t(normalied_min * hour_steps);
|
||||
|
||||
auto const hour_rad = hour_deg * pi / 180.f;
|
||||
|
||||
rt.DrawLine(center,
|
||||
{center.x + int32_t(std::cos(hour_rad) * radius * 0.6),
|
||||
center.y + int32_t(std::sin(hour_rad) * radius * 0.6)});
|
||||
|
||||
setClear();
|
||||
|
||||
// == Widgets ==
|
||||
|
||||
auto widget_center_1 = center;
|
||||
widget_center_1.y -= radius / 2;
|
||||
|
||||
auto widget_center_2 = center;
|
||||
widget_center_2.y += radius / 2;
|
||||
|
||||
auto const widget_height_h = radius / 4;
|
||||
auto const widget_width_h = radius / 2;
|
||||
|
||||
Rect widget_box_1{widget_center_1.y - widget_height_h,
|
||||
widget_center_1.x - widget_width_h,
|
||||
widget_width_h * 2,
|
||||
widget_height_h * 2};
|
||||
|
||||
Rect widget_box_2{widget_center_2.y - widget_height_h,
|
||||
widget_center_2.x - widget_width_h,
|
||||
widget_width_h * 2,
|
||||
widget_height_h * 2};
|
||||
|
||||
// Top
|
||||
|
||||
auto top = getSlot(0);
|
||||
if(top)
|
||||
{
|
||||
top->setSize({widget_box_1.width, widget_box_1.height});
|
||||
rt.pushViewport(widget_box_1);
|
||||
top->Render(rt);
|
||||
rt.popViewport();
|
||||
}
|
||||
|
||||
auto bottom = getSlot(1);
|
||||
if(bottom)
|
||||
{
|
||||
bottom->setSize({widget_box_2.width, widget_box_2.height});
|
||||
rt.pushViewport(widget_box_2);
|
||||
bottom->Render(rt);
|
||||
rt.popViewport();
|
||||
}
|
||||
|
||||
// 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
|
||||
24
frame/src/widgets/clock/Analog.hpp
Normal file
24
frame/src/widgets/clock/Analog.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ContainerWidget.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
class AnalogClock : public ContainerWidget
|
||||
{
|
||||
using Time = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
Time last_time;
|
||||
|
||||
public:
|
||||
static Widget::shared_ptr Create();
|
||||
|
||||
AnalogClock();
|
||||
|
||||
void Update() override;
|
||||
void Render(render::RenderTarget& rt) override;
|
||||
};
|
||||
} // namespace frame::widgets
|
||||
56
frame/src/widgets/clock/Date.cpp
Normal file
56
frame/src/widgets/clock/Date.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "Date.hpp"
|
||||
|
||||
REGISTER_WIDGET(frame::widgets::Date, "Date");
|
||||
|
||||
#include "../../font/FontRegistry.hpp"
|
||||
|
||||
#include <ctime>
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
Widget::shared_ptr Date::Create()
|
||||
{
|
||||
return std::make_shared<Date>();
|
||||
}
|
||||
|
||||
void Date::Update()
|
||||
{
|
||||
auto const now = std::chrono::system_clock::now();
|
||||
auto const min = std::chrono::floor<std::chrono::minutes>(now);
|
||||
|
||||
if(last_time != min)
|
||||
{
|
||||
setDirty();
|
||||
last_time = min;
|
||||
}
|
||||
}
|
||||
|
||||
void Date::Render(render::RenderTarget& rt)
|
||||
{
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(last_time);
|
||||
|
||||
auto const& size = getSize();
|
||||
Rect draw{0, 0, size.x, size.y};
|
||||
|
||||
auto text = fmt::format("{:%d.%m.%Y}", fmt::localtime(t));
|
||||
|
||||
auto font = font::GetFont("Fira Code");
|
||||
|
||||
auto font_size = font->getOptimalSize(text, draw);
|
||||
|
||||
rt.setInvert(true);
|
||||
rt.DrawText(draw,
|
||||
text,
|
||||
*font,
|
||||
font_size,
|
||||
AlignHorizontal::CENTER,
|
||||
AlignVertical::CENTER);
|
||||
rt.setInvert(false);
|
||||
|
||||
setClear();
|
||||
}
|
||||
|
||||
} // namespace frame::widgets
|
||||
22
frame/src/widgets/clock/Date.hpp
Normal file
22
frame/src/widgets/clock/Date.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Widget.hpp"
|
||||
#include "../WidgetRegistry.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
class Date : public Widget
|
||||
{
|
||||
using Time = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
Time last_time;
|
||||
|
||||
public:
|
||||
static Widget::shared_ptr Create();
|
||||
|
||||
void Update() override;
|
||||
void Render(render::RenderTarget& rt) override;
|
||||
};
|
||||
} // namespace frame::widgets
|
||||
60
frame/src/widgets/clock/Digital.cpp
Normal file
60
frame/src/widgets/clock/Digital.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "Digital.hpp"
|
||||
|
||||
REGISTER_WIDGET(frame::widgets::DigitalClock, "DigitalClock");
|
||||
|
||||
#include "../../font/FontRegistry.hpp"
|
||||
|
||||
#include <ctime>
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
Widget::shared_ptr DigitalClock::Create()
|
||||
{
|
||||
return std::make_shared<DigitalClock>();
|
||||
}
|
||||
|
||||
void DigitalClock::Update()
|
||||
{
|
||||
auto const now = std::chrono::system_clock::now();
|
||||
auto const min = std::chrono::floor<std::chrono::minutes>(now);
|
||||
|
||||
if(last_time != min)
|
||||
{
|
||||
setDirty();
|
||||
last_time = min;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalClock::Render(render::RenderTarget& rt)
|
||||
{
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(last_time);
|
||||
auto c_time = std::localtime(&t);
|
||||
|
||||
auto const& size = getSize();
|
||||
Rect draw{0, 0, size.x, size.y};
|
||||
|
||||
auto text =
|
||||
fmt::format("{:0>2}:{:0>2}", c_time->tm_hour, c_time->tm_min);
|
||||
|
||||
auto font = font::GetFont("Fira Code");
|
||||
|
||||
auto font_size = font->getOptimalSize(text, draw);
|
||||
|
||||
rt.setInvert(true);
|
||||
|
||||
rt.DrawText(draw,
|
||||
text,
|
||||
*font,
|
||||
font_size,
|
||||
AlignHorizontal::CENTER,
|
||||
AlignVertical::CENTER);
|
||||
|
||||
rt.setInvert(false);
|
||||
|
||||
setClear();
|
||||
}
|
||||
|
||||
} // namespace frame::widgets
|
||||
24
frame/src/widgets/clock/Digital.hpp
Normal file
24
frame/src/widgets/clock/Digital.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "../Widget.hpp"
|
||||
#include "../WidgetRegistry.hpp"
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
class DigitalClock : public Widget
|
||||
{
|
||||
using Time = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
Time last_time;
|
||||
|
||||
public:
|
||||
static Widget::shared_ptr Create();
|
||||
|
||||
void Update() override;
|
||||
void Render(render::RenderTarget& rt) override;
|
||||
};
|
||||
|
||||
} // namespace frame::widgets
|
||||
Reference in New Issue
Block a user