Smallcase folders part 1
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
message(STATUS "··Configuring UI")
|
||||
|
||||
add_subdirectory(bar)
|
||||
add_subdirectory(text)
|
||||
add_subdirectory(header)
|
||||
add_subdirectory(hostblock)
|
||||
add_subdirectory(theme)
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "Display/UI/Bar/Bar.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace display::ui::bar
|
||||
{
|
||||
Bar::Bar(int width, int height, Orientation orientation, Style style)
|
||||
: width(width), height(height), orientation(orientation), style(style)
|
||||
{
|
||||
}
|
||||
|
||||
void Bar::draw(display::graphics::Framebuffer &framebuffer, int x, int y, float value)
|
||||
{
|
||||
value = std::clamp(value, 0.0f, 1.0f);
|
||||
|
||||
// 1. Рисуем фон
|
||||
framebuffer.fillRect(x, y, width, height, style.background);
|
||||
|
||||
// 2. Рисуем заполнение
|
||||
display::graphics::Color fillColor = valueToColor(value);
|
||||
|
||||
if (orientation == Orientation::Horizontal)
|
||||
{
|
||||
int fillWidth = static_cast<int>(width * value);
|
||||
framebuffer.fillRect(x, y, fillWidth, height, fillColor);
|
||||
}
|
||||
else // Vertical
|
||||
{
|
||||
int fillHeight = static_cast<int>(height * value);
|
||||
framebuffer.fillRect(x, y + (height - fillHeight), width, fillHeight, fillColor);
|
||||
}
|
||||
|
||||
// 3. Рисуем рамку
|
||||
if (style.drawBorder)
|
||||
{
|
||||
// можно добавить метод drawRect в Framebuffer
|
||||
framebuffer.drawRect(x, y, width, height, style.border);
|
||||
}
|
||||
}
|
||||
|
||||
display::graphics::Color Bar::valueToColor(float value)
|
||||
{
|
||||
value = std::clamp(value, 0.0f, 1.0f);
|
||||
if (value <= 0.5f)
|
||||
{
|
||||
float t = value / 0.5f;
|
||||
return display::graphics::Color{
|
||||
static_cast<uint8_t>(t * 255), // R: 0 → 255
|
||||
180, // G: остаётся 180
|
||||
0 // B
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
float t = (value - 0.5f) / 0.5f;
|
||||
return display::graphics::Color{
|
||||
255, // R: остаётся 255
|
||||
static_cast<uint8_t>(180 * (1.0f - t)), // G: 180 → 0
|
||||
0};
|
||||
}
|
||||
}
|
||||
} // namespace display::ui::bar
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "Display/Graphics/Framebuffer.h"
|
||||
#include "Display/Graphics/Color.h"
|
||||
#include "Display/UI/Bar/Orientation.h"
|
||||
#include "Display/UI/Bar/Style.h"
|
||||
|
||||
namespace display::ui::bar
|
||||
{
|
||||
class Bar
|
||||
{
|
||||
public:
|
||||
Bar(int width, int height, Orientation orientation = Orientation::Horizontal, Style style = {});
|
||||
|
||||
void draw(display::graphics::Framebuffer &framebuffer,
|
||||
int x, int y,
|
||||
float value); // 0.0 .. 1.0
|
||||
|
||||
private:
|
||||
int width;
|
||||
int height;
|
||||
Orientation orientation;
|
||||
Style style;
|
||||
|
||||
display::graphics::Color valueToColor(float value);
|
||||
};
|
||||
} // namespace display::ui::bar
|
||||
@@ -0,0 +1,17 @@
|
||||
message(STATUS "···Configuring Bar")
|
||||
|
||||
add_library(DisplayUIBar
|
||||
Bar.cpp
|
||||
)
|
||||
|
||||
add_library(display::ui::Bar ALIAS DisplayUIBar)
|
||||
|
||||
target_link_libraries(DisplayUIBar
|
||||
PUBLIC
|
||||
Display::Graphics
|
||||
)
|
||||
|
||||
target_include_directories(DisplayUIBar
|
||||
PUBLIC
|
||||
${INCLUDE_BASE_DIR}
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace display::ui::bar
|
||||
{
|
||||
enum class Orientation
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
};
|
||||
} // namespace display::ui::bar
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Display/Graphics/Color.h"
|
||||
#include "Display/UI/Theme/Theme.h"
|
||||
|
||||
namespace display::ui::bar
|
||||
{
|
||||
struct Style
|
||||
{
|
||||
display::graphics::Color background;
|
||||
display::graphics::Color fill;
|
||||
display::graphics::Color border;
|
||||
bool drawBorder;
|
||||
|
||||
Style(
|
||||
display::graphics::Color background = display::ui::theme::bar::BACKGROUND,
|
||||
display::graphics::Color fill = display::ui::theme::bar::FILL,
|
||||
display::graphics::Color border = display::ui::theme::bar::BORDER,
|
||||
bool drawBorder = true)
|
||||
: background(background),
|
||||
fill(fill),
|
||||
border(border),
|
||||
drawBorder(true)
|
||||
{
|
||||
}
|
||||
};
|
||||
} // namespace display::ui::bar
|
||||
@@ -0,0 +1,18 @@
|
||||
message(STATUS "···Configuring Header")
|
||||
|
||||
add_library(DisplayUIHeader
|
||||
Header.cpp
|
||||
)
|
||||
|
||||
add_library(display::ui::Header ALIAS DisplayUIHeader)
|
||||
|
||||
target_link_libraries(DisplayUIHeader
|
||||
PUBLIC
|
||||
Display::Graphics
|
||||
display::ui::Text
|
||||
)
|
||||
|
||||
target_include_directories(DisplayUIHeader
|
||||
PUBLIC
|
||||
${INCLUDE_BASE_DIR}
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "Display/UI/Header/Header.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "Display/UI/Bar/Orientation.h"
|
||||
#include "Display/UI/Bar/Style.h"
|
||||
#include "Display/UI/Theme/Theme.h"
|
||||
#include "Display/Graphics/Color.h"
|
||||
|
||||
namespace display::ui::header
|
||||
{
|
||||
Header::Header()
|
||||
{
|
||||
}
|
||||
|
||||
void Header::draw(
|
||||
display::graphics::Framebuffer &fb,
|
||||
display::ui::text::Renderer &text,
|
||||
int x, int y)
|
||||
{
|
||||
// ===== Block background =====
|
||||
fb.fillRect(x, y, HEADER_WIDTH, HEADER_HEIGHT, display::ui::theme::header::BACKGROUND);
|
||||
fb.drawRect(x, y, HEADER_WIDTH, HEADER_HEIGHT, display::ui::theme::header::BORDER);
|
||||
|
||||
int cursorY = y + PADDING;
|
||||
|
||||
text.drawTextOutlined(
|
||||
fb,
|
||||
x + PADDING + 120 - 2,
|
||||
cursorY + 8,
|
||||
getCurrentDateTime(),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{std::string(HEADER_FONT_NAME.begin(), HEADER_FONT_NAME.end()), HEADER_FONT_SIZE});
|
||||
}
|
||||
|
||||
std::string Header::getCurrentDateTime()
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm{};
|
||||
localtime_r(&t, &tm);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0')
|
||||
<< std::setw(2) << tm.tm_mday << '/'
|
||||
<< std::setw(2) << tm.tm_mon + 1 << '/'
|
||||
<< tm.tm_year + 1900 << ' '
|
||||
<< std::setw(2) << tm.tm_hour << ':'
|
||||
<< std::setw(2) << tm.tm_min << ':'
|
||||
<< std::setw(2) << tm.tm_sec;
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Display/Graphics/Framebuffer.h"
|
||||
#include "Display/Graphics/Color.h"
|
||||
#include "Display/UI/Text/Renderer.h"
|
||||
|
||||
namespace display::ui::header
|
||||
{
|
||||
// ===== Layout =====
|
||||
constexpr int HEADER_WIDTH = 240; // 116
|
||||
constexpr int HEADER_HEIGHT = 16; // 116
|
||||
constexpr int PADDING = 4; // 4
|
||||
constexpr std::string_view HEADER_FONT_NAME = "Pixel10";
|
||||
constexpr int HEADER_FONT_SIZE = 14; // 12
|
||||
|
||||
class Header
|
||||
{
|
||||
public:
|
||||
Header();
|
||||
|
||||
static constexpr int width() { return HEADER_WIDTH; }
|
||||
static constexpr int height() { return HEADER_HEIGHT; }
|
||||
|
||||
void draw(
|
||||
display::graphics::Framebuffer &fb,
|
||||
display::ui::text::Renderer &text,
|
||||
int x, int y);
|
||||
|
||||
private:
|
||||
std::string getCurrentDateTime();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
message(STATUS "···Configuring HostBlock")
|
||||
|
||||
add_library(DisplayUIHostBlock
|
||||
HostBlock.cpp
|
||||
)
|
||||
|
||||
add_library(display::ui::HostBlock ALIAS DisplayUIHostBlock)
|
||||
|
||||
target_link_libraries(DisplayUIHostBlock
|
||||
PUBLIC
|
||||
Display::Graphics
|
||||
display::ui::Text
|
||||
display::ui::Bar
|
||||
)
|
||||
|
||||
target_include_directories(DisplayUIHostBlock
|
||||
PUBLIC
|
||||
${INCLUDE_BASE_DIR}
|
||||
)
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "Display/UI/HostBlock/HostBlock.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Display/UI/Bar/Orientation.h"
|
||||
#include "Display/UI/Bar/Style.h"
|
||||
#include "Display/UI/Theme/Theme.h"
|
||||
#include "Display/Graphics/Color.h"
|
||||
#include "Display/UI/Text/Helpers.h"
|
||||
|
||||
namespace display::ui::hostblock
|
||||
{
|
||||
HostBlock::HostBlock()
|
||||
: cpuBar(
|
||||
CPU_BAR_WIDTH,
|
||||
CPU_BAR_HEIGHT,
|
||||
display::ui::bar::Orientation::Vertical,
|
||||
display::ui::bar::Style{display::graphics::Color{40, 40, 40}, display::graphics::Color{0, 180, 0}, display::graphics::Color{80, 80, 80}, true}),
|
||||
memBar(
|
||||
BLOCK_WIDTH - PADDING * 2,
|
||||
MEM_BAR_HEIGHT,
|
||||
display::ui::bar::Orientation::Horizontal,
|
||||
display::ui::bar::Style{display::graphics::Color{40, 40, 40}, display::graphics::Color{0, 120, 200}, display::graphics::Color{80, 80, 80}, true})
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
void HostBlock::draw(
|
||||
display::graphics::Framebuffer &fb,
|
||||
display::ui::text::Renderer &text,
|
||||
int x, int y,
|
||||
const std::string &hostname,
|
||||
const metrics::Host &metrics)
|
||||
{
|
||||
// ===== Block background =====
|
||||
fb.fillRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, display::ui::theme::hostblock::BACKGROUND);
|
||||
fb.drawRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, display::ui::theme::hostblock::BORDER);
|
||||
|
||||
int cursorY = y + PADDING;
|
||||
|
||||
// ===== Header =====
|
||||
fb.fillRect(
|
||||
x + PADDING,
|
||||
cursorY,
|
||||
BLOCK_WIDTH - PADDING * 2,
|
||||
HEADER_HEIGHT,
|
||||
display::ui::theme::hostblock::HEADER);
|
||||
|
||||
text.drawTextOutlined(
|
||||
fb,
|
||||
x + PADDING + 2,
|
||||
cursorY + HEADER_HEIGHT - 3,
|
||||
hostname,
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
// display::ui::text::Font{"LiberationSans-Regular", HEADER_FONT_SIZE});
|
||||
// display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
display::ui::text::Font{std::string(HEADER_FONT_NAME.begin(), HEADER_FONT_NAME.end()), HEADER_FONT_SIZE});
|
||||
|
||||
cursorY += HEADER_HEIGHT + SECTION_GAP;
|
||||
|
||||
// ===== CPU bars =====
|
||||
int cpuCount = std::min<int>(metrics.cpu.coreLoads.size(),
|
||||
CPU_MAX_PER_ROW * CPU_ROWS);
|
||||
|
||||
for (int i = 0; i < cpuCount; ++i)
|
||||
{
|
||||
int row = i / CPU_MAX_PER_ROW;
|
||||
int col = i % CPU_MAX_PER_ROW;
|
||||
|
||||
int bx = x + PADDING +
|
||||
col * (CPU_BAR_WIDTH + CPU_BAR_GAP);
|
||||
|
||||
int by = cursorY +
|
||||
row * (CPU_BAR_HEIGHT + CPU_BAR_GAP);
|
||||
|
||||
float value = std::clamp(metrics.cpu.coreLoads[i] / 100.0f, 0.0f, 1.0f);
|
||||
|
||||
cpuBar.draw(fb, bx, by, value);
|
||||
}
|
||||
|
||||
cursorY += CPU_ROWS * CPU_BAR_HEIGHT +
|
||||
(CPU_ROWS - 1) * CPU_BAR_GAP +
|
||||
SECTION_GAP;
|
||||
|
||||
// ===== Memory bar =====
|
||||
float memValue, swapValue = 0.0f;
|
||||
if (metrics.memory.mem_total > 0)
|
||||
memValue = metrics.memory.mem_used / metrics.memory.mem_total;
|
||||
|
||||
memBar.draw(
|
||||
fb,
|
||||
x + PADDING,
|
||||
cursorY,
|
||||
std::clamp(memValue, 0.0f, 1.0f));
|
||||
text.drawTextOutlined(fb,
|
||||
x + MEM_BAR_TEXT_PADDING_X,
|
||||
cursorY + 8,
|
||||
"M: " + display::ui::text::formatFloat(metrics.memory.mem_used / 1073741824) + "/" + display::ui::text::formatFloat(metrics.memory.mem_total / 1073741824),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
|
||||
cursorY += MEM_BAR_HEIGHT + SECTION_GAP;
|
||||
|
||||
if (metrics.memory.swap_total > 0)
|
||||
{
|
||||
swapValue = metrics.memory.swap_used / metrics.memory.swap_total;
|
||||
|
||||
memBar.draw(
|
||||
fb,
|
||||
x + PADDING,
|
||||
cursorY,
|
||||
std::clamp(swapValue, 0.0f, 1.0f));
|
||||
text.drawTextOutlined(fb,
|
||||
x + MEM_BAR_TEXT_PADDING_X,
|
||||
cursorY + 8,
|
||||
"S: " + display::ui::text::formatFloat(metrics.memory.swap_used / 1073741824) + "/" + display::ui::text::formatFloat(metrics.memory.swap_total / 1073741824),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
|
||||
cursorY += MEM_BAR_HEIGHT + SECTION_GAP;
|
||||
}
|
||||
|
||||
// ===== Disk bar =====
|
||||
int diskCount = metrics.disks.size();
|
||||
|
||||
for (int i = 0; i < diskCount; ++i)
|
||||
{
|
||||
int by = cursorY +
|
||||
i * (MEM_BAR_HEIGHT + SECTION_GAP);
|
||||
|
||||
float value = std::clamp(metrics.disks[i].used / metrics.disks[i].total, 0.0f, 1.0f);
|
||||
|
||||
memBar.draw(fb, x + PADDING, by, value);
|
||||
text.drawTextOutlined(fb,
|
||||
x + MEM_BAR_TEXT_PADDING_X,
|
||||
by + 8,
|
||||
metrics.disks[i].name + ": " + display::ui::text::formatFloat(metrics.disks[i].used / 1073741824) + "/" + display::ui::text::formatFloat(metrics.disks[i].total / 1073741824),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Display/Graphics/Framebuffer.h"
|
||||
#include "Display/Graphics/Color.h"
|
||||
#include "Display/UI/Text/Renderer.h"
|
||||
#include "Display/UI/Bar/Bar.h"
|
||||
#include "Metrics/Host.h"
|
||||
|
||||
namespace display::ui::hostblock
|
||||
{
|
||||
// ===== Layout =====
|
||||
constexpr int BLOCK_WIDTH = 118; // 118
|
||||
constexpr int BLOCK_HEIGHT = 146; // 146
|
||||
constexpr int PADDING = 5; // 5
|
||||
|
||||
constexpr int HEADER_HEIGHT = 12; // 12
|
||||
constexpr std::string_view HEADER_FONT_NAME = "PixelFive-Regular";
|
||||
constexpr int HEADER_FONT_SIZE = 5; // 10
|
||||
constexpr int SECTION_GAP = 4; // 4
|
||||
|
||||
// ===== CPU bars =====
|
||||
constexpr int CPU_BAR_WIDTH = 10; // 10
|
||||
constexpr int CPU_BAR_GAP = 4; // 4
|
||||
constexpr int CPU_BAR_HEIGHT = 16; // 16
|
||||
constexpr int CPU_MAX_PER_ROW = 8; // 8
|
||||
constexpr int CPU_ROWS = 2; // 2
|
||||
|
||||
// ===== Memory bar =====
|
||||
constexpr int MEM_BAR_HEIGHT = 11; // 8
|
||||
constexpr int MEM_BAR_TEXT_PADDING_X = PADDING + 3;
|
||||
|
||||
class HostBlock
|
||||
{
|
||||
public:
|
||||
HostBlock();
|
||||
|
||||
static constexpr int width() { return BLOCK_WIDTH; }
|
||||
static constexpr int height() { return BLOCK_HEIGHT; }
|
||||
|
||||
void draw(
|
||||
display::graphics::Framebuffer &fb,
|
||||
display::ui::text::Renderer &text,
|
||||
int x, int y,
|
||||
const std::string &hostname,
|
||||
const metrics::Host &metrics);
|
||||
|
||||
private:
|
||||
display::ui::bar::Bar cpuBar;
|
||||
display::ui::bar::Bar memBar;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
message(STATUS "···Configuring Text")
|
||||
|
||||
add_library(DisplayUIText
|
||||
Fonts.cpp
|
||||
Helpers.cpp
|
||||
Renderer.cpp
|
||||
)
|
||||
|
||||
|
||||
add_library(display::ui::Text ALIAS DisplayUIText)
|
||||
|
||||
find_package(Freetype REQUIRED)
|
||||
|
||||
target_link_libraries(DisplayUIText
|
||||
PRIVATE
|
||||
Display::Graphics
|
||||
Helpers::All
|
||||
${FREETYPE_LIBRARIES}
|
||||
)
|
||||
|
||||
# include-root общий
|
||||
target_include_directories(DisplayUIText
|
||||
PUBLIC
|
||||
${INCLUDE_BASE_DIR}
|
||||
${FREETYPE_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
struct Font
|
||||
{
|
||||
std::string name;
|
||||
int size; // размер шрифта
|
||||
bool operator==(const Font &other) const
|
||||
{
|
||||
return name == other.name && size == other.size;
|
||||
}
|
||||
};
|
||||
|
||||
struct FontHash
|
||||
{
|
||||
std::size_t operator()(const Font &k) const
|
||||
{
|
||||
return std::hash<std::string>()(k.name) ^ std::hash<int>()(k.size);
|
||||
}
|
||||
};
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "Display/UI/Text/Fonts.h"
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "Helpers/Paths.h"
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
struct Fonts::Library
|
||||
{
|
||||
FT_Library library = nullptr;
|
||||
};
|
||||
struct Fonts::Face
|
||||
{
|
||||
FT_Face face = nullptr;
|
||||
};
|
||||
|
||||
Fonts::Fonts()
|
||||
: library(std::make_unique<Library>())
|
||||
{
|
||||
if (FT_Init_FreeType(&library->library))
|
||||
throw std::runtime_error("FT_Init_FreeType failed");
|
||||
|
||||
loadAllFonts();
|
||||
}
|
||||
|
||||
Fonts::~Fonts()
|
||||
{
|
||||
for (auto &[k, v] : fonts)
|
||||
if (v->face)
|
||||
FT_Done_Face(v->face);
|
||||
if (library->library)
|
||||
FT_Done_FreeType(library->library);
|
||||
}
|
||||
|
||||
void Fonts::loadAllFonts()
|
||||
{
|
||||
loadFonts("/usr/share/fonts/truetype");
|
||||
loadFonts(helpers::initPaths().exeDir);
|
||||
}
|
||||
|
||||
void Fonts::loadFonts(const std::string &path)
|
||||
{
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
for (auto &p : std::filesystem::recursive_directory_iterator(path))
|
||||
{
|
||||
if (p.path().extension() == ".ttf" || p.path().extension() == ".otf")
|
||||
{
|
||||
std::string path = p.path().string();
|
||||
std::string filename = p.path().stem().string();
|
||||
|
||||
// Можно сразу создавать Fonts для стандартных размеров, например: 10,12,14,16,18,24
|
||||
for (int sz : {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 24, 36, 72})
|
||||
{
|
||||
Font key{filename, sz};
|
||||
|
||||
std::unique_ptr<Face> face = std::make_unique<Face>();
|
||||
if (FT_New_Face(library->library, path.c_str(), 0, &face->face))
|
||||
throw std::runtime_error("FT_New_Face failed");
|
||||
FT_Set_Pixel_Sizes(face->face, 0, sz);
|
||||
|
||||
fonts.emplace(key, std::move(face));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Glyph &Fonts::getGlyph(char ch, const Font &font)
|
||||
{
|
||||
|
||||
GlyphKey key{ch, font};
|
||||
|
||||
auto gph = glyphs.find(key);
|
||||
if (gph != glyphs.end())
|
||||
return gph->second;
|
||||
|
||||
auto fnt = fonts.find(font);
|
||||
if (fnt == fonts.end())
|
||||
{
|
||||
// Если нет, можно использовать какой-нибудь дефолтный шрифт
|
||||
fnt = fonts.begin();
|
||||
}
|
||||
|
||||
FT_Face face = fnt->second->face;
|
||||
|
||||
if (!face)
|
||||
throw std::runtime_error("Fonts not initialized!");
|
||||
if (FT_Load_Char(face, ch, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO))
|
||||
throw std::runtime_error("FT_Load_Char failed");
|
||||
FT_GlyphSlot g = face->glyph;
|
||||
|
||||
Glyph glyph;
|
||||
glyph.width = g->bitmap.width;
|
||||
glyph.height = g->bitmap.rows;
|
||||
glyph.bearingX = g->bitmap_left;
|
||||
glyph.bearingY = g->bitmap_top;
|
||||
glyph.advance = g->advance.x >> 6;
|
||||
// glyph.buffer.assign(g->bitmap.buffer, g->bitmap.buffer + g->bitmap.width * g->bitmap.rows);
|
||||
|
||||
glyph.buffer.resize(glyph.width * glyph.height, 0);
|
||||
|
||||
const FT_Bitmap &bm = g->bitmap;
|
||||
for (int y = 0; y < bm.rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < bm.width; ++x)
|
||||
{
|
||||
int byteIndex = y * bm.pitch + x / 8;
|
||||
int bitIndex = 7 - (x % 8);
|
||||
uint8_t bit = (bm.buffer[byteIndex] >> bitIndex) & 1;
|
||||
glyph.buffer[y * bm.width + x] = bit ? 255 : 0;
|
||||
}
|
||||
}
|
||||
return glyphs.emplace(key, std::move(glyph)).first->second;
|
||||
}
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "Display/UI/Text/Font.h"
|
||||
#include "Display/UI/Text/Glyph.h"
|
||||
#include "Display/UI/Text/GlyphKey.h"
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
class Fonts
|
||||
{
|
||||
public:
|
||||
Fonts();
|
||||
~Fonts();
|
||||
|
||||
const Glyph &getGlyph(char ch, const Font &key);
|
||||
|
||||
private:
|
||||
struct Library;
|
||||
struct Face;
|
||||
std::unique_ptr<Library> library;
|
||||
std::unordered_map<Font, std::unique_ptr<Face>, FontHash> fonts;
|
||||
std::unordered_map<GlyphKey, Glyph, GlyphKeyHash> glyphs;
|
||||
void loadAllFonts();
|
||||
void loadFonts(const std::string &path);
|
||||
};
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
struct Glyph
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int bearingX;
|
||||
int bearingY;
|
||||
int advance;
|
||||
std::vector<uint8_t> buffer; // grayscale bitmap
|
||||
};
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "Font.h"
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
struct GlyphKey
|
||||
{
|
||||
char ch;
|
||||
Font font;
|
||||
|
||||
bool operator==(const GlyphKey &other) const
|
||||
{
|
||||
return ch == other.ch && font == other.font;
|
||||
}
|
||||
};
|
||||
|
||||
struct GlyphKeyHash
|
||||
{
|
||||
std::size_t operator()(const GlyphKey &k) const
|
||||
{
|
||||
std::size_t h1 = std::hash<char>()(k.ch);
|
||||
std::size_t h2 = FontHash{}(k.font);
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
};
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "Display/UI/Text/Helpers.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
std::string formatFloat(float value, int decimals)
|
||||
{
|
||||
char buffer[64];
|
||||
std::snprintf(buffer, sizeof(buffer), "%.*f", decimals, value);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
std::string formatDouble(double value, int decimals)
|
||||
{
|
||||
char buffer[64];
|
||||
std::snprintf(buffer, sizeof(buffer), "%.*f", decimals, value);
|
||||
return std::string(buffer);
|
||||
}
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
std::string formatFloat(float value, int decimals = 1);
|
||||
std::string formatDouble(double value, int decimals = 1);
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "Display/UI/Text/Renderer.h"
|
||||
#include "Display/UI/Text/UTFDecoder.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
void Renderer::drawText(display::graphics::Framebuffer &fb, int x, int y,
|
||||
const std::string &text, const display::graphics::Color &color, const Font &font)
|
||||
{
|
||||
int penX = x;
|
||||
int baseline = y;
|
||||
|
||||
for (char ch : text)
|
||||
{
|
||||
const Glyph &g = fonts.getGlyph(ch, font);
|
||||
|
||||
for (int row = 0; row < g.height; ++row)
|
||||
{
|
||||
int py = baseline - g.bearingY + row;
|
||||
|
||||
for (int col = 0; col < g.width; ++col)
|
||||
{
|
||||
int px = penX + col + g.bearingX;
|
||||
|
||||
uint8_t alpha = g.buffer[row * g.width + col];
|
||||
if (!alpha)
|
||||
continue;
|
||||
|
||||
display::graphics::Color bg = fb.getPixel(px, py);
|
||||
display::graphics::Color out;
|
||||
out.r = (alpha * color.r + (255 - alpha) * bg.r) / 255;
|
||||
out.g = (alpha * color.g + (255 - alpha) * bg.g) / 255;
|
||||
out.b = (alpha * color.b + (255 - alpha) * bg.b) / 255;
|
||||
|
||||
fb.setPixel(px, py, out);
|
||||
}
|
||||
}
|
||||
|
||||
penX += g.advance;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::drawTextOutlined(display::graphics::Framebuffer &fb,
|
||||
int x, int y,
|
||||
const std::string &text,
|
||||
const display::graphics::Color &colorText,
|
||||
const display::graphics::Color &colorOutline,
|
||||
const Font &font)
|
||||
{
|
||||
// outline
|
||||
for (int dx = -1; dx <= 1; ++dx)
|
||||
for (int dy = -1; dy <= 1; ++dy)
|
||||
if (dx != 0 || dy != 0)
|
||||
drawText(fb, x + dx, y + dy, text, colorOutline, font);
|
||||
|
||||
// main text
|
||||
drawText(fb, x, y, text, colorText, font);
|
||||
}
|
||||
|
||||
/* int Renderer::measureWidth(const std::string &text)
|
||||
{
|
||||
int width = 0;
|
||||
for (char ch : text)
|
||||
{
|
||||
const Glyph &g = glyphCache.getGlyph(ch, face);
|
||||
width += g.advance;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
int Renderer::measureHeight(const std::string &text)
|
||||
{
|
||||
int height = 0;
|
||||
for (char ch : text)
|
||||
{
|
||||
const Glyph &g = glyphCache.getGlyph(ch, face);
|
||||
int glyphHeight = g.height - g.bearingY + g.bearingY; // можно уточнить по высоте глифа
|
||||
if (glyphHeight > height)
|
||||
height = glyphHeight;
|
||||
}
|
||||
return height;
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Display/Graphics/Framebuffer.h"
|
||||
#include "Display/UI/Text/Fonts.h"
|
||||
#include "Display/UI/Text/Font.h"
|
||||
#include "Display/Graphics/Color.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
void drawText(display::graphics::Framebuffer &fb, int x, int y, const std::string &text, const display::graphics::Color &color = display::graphics::Color{255, 255, 255}, const Font &font = {"LiberationSans-Regular", 12});
|
||||
void drawTextOutlined(display::graphics::Framebuffer &fb, int x, int y, const std::string &text, const display::graphics::Color &colorText = display::graphics::Color{255, 255, 255}, const display::graphics::Color &colorOutline = display::graphics::Color{0, 0, 0}, const Font &font = {"LiberationSans-Regular", 12});
|
||||
|
||||
// int measureWidth(const std::string &text);
|
||||
// int measureHeight(const std::string &text);
|
||||
|
||||
private:
|
||||
Fonts fonts;
|
||||
};
|
||||
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
|
||||
namespace display::ui::text
|
||||
{
|
||||
|
||||
inline bool utf8Next(std::string_view &s, uint32_t &codepoint)
|
||||
{
|
||||
if (s.empty())
|
||||
return false;
|
||||
|
||||
uint8_t c = s[0];
|
||||
|
||||
if ((c & 0x80) == 0)
|
||||
{
|
||||
codepoint = c;
|
||||
s.remove_prefix(1);
|
||||
}
|
||||
else if ((c & 0xE0) == 0xC0)
|
||||
{
|
||||
codepoint = ((c & 0x1F) << 6) | (s[1] & 0x3F);
|
||||
s.remove_prefix(2);
|
||||
}
|
||||
else if ((c & 0xF0) == 0xE0)
|
||||
{
|
||||
codepoint = ((c & 0x0F) << 12) |
|
||||
((s[1] & 0x3F) << 6) |
|
||||
(s[2] & 0x3F);
|
||||
s.remove_prefix(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace display::ui::text
|
||||
@@ -0,0 +1,15 @@
|
||||
message(STATUS "···Configuring Theme")
|
||||
|
||||
add_library(Theme INTERFACE)
|
||||
|
||||
add_library(display::ui::Theme ALIAS Theme)
|
||||
|
||||
target_link_libraries(Theme
|
||||
INTERFACE
|
||||
Display::Graphics
|
||||
)
|
||||
|
||||
target_include_directories(Theme
|
||||
INTERFACE
|
||||
${INCLUDE_BASE_DIR}
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "Display/Graphics/Color.h"
|
||||
|
||||
namespace display::ui::theme
|
||||
{
|
||||
namespace text
|
||||
{
|
||||
const display::graphics::Color TEXT{220, 220, 220};
|
||||
const display::graphics::Color OUTLINE{20, 20, 20};
|
||||
} // namespace text
|
||||
|
||||
namespace header
|
||||
{
|
||||
// ===== Header =====
|
||||
const display::graphics::Color BACKGROUND{20, 20, 20};
|
||||
const display::graphics::Color BORDER{60, 60, 60};
|
||||
const display::graphics::Color HEADER{30, 30, 30};
|
||||
} // namespace header
|
||||
|
||||
namespace hostblock
|
||||
{
|
||||
// ===== HostBlock =====
|
||||
const display::graphics::Color BACKGROUND{20, 20, 20};
|
||||
const display::graphics::Color BORDER{60, 60, 60};
|
||||
const display::graphics::Color HEADER{30, 30, 30};
|
||||
} // namespace hostblock
|
||||
|
||||
namespace bar
|
||||
{
|
||||
const display::graphics::Color BACKGROUND{30, 30, 30};
|
||||
const display::graphics::Color FILL{0, 180, 0};
|
||||
const display::graphics::Color BORDER{80, 80, 80};
|
||||
} // namespace bar
|
||||
} // namespace display::ui::theme
|
||||
Reference in New Issue
Block a user