diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index 873d301f..fe7055f1 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -3,6 +3,6 @@ file(GLOB_RECURSE PSEMEK_UI_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "sour psemek_add_library(psemek-ui ${PSEMEK_UI_HEADERS} ${PSEMEK_UI_SOURCES}) target_include_directories(psemek-ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries(psemek-ui PUBLIC psemek-util psemek-react psemek-geom psemek-async psemek-app) +target_link_libraries(psemek-ui PUBLIC psemek-util psemek-react psemek-geom psemek-fonts psemek-async psemek-app) psemek_glob_tests(psemek-ui tests) diff --git a/libs/ui/include/psemek/ui/impl/label_base.hpp b/libs/ui/include/psemek/ui/impl/label_base.hpp new file mode 100644 index 00000000..24ee89f5 --- /dev/null +++ b/libs/ui/include/psemek/ui/impl/label_base.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace psemek::ui::impl +{ + + struct label_base + : component + { + label_base(); + + react::value size_constraints() const override; + + void draw(renderer & renderer) override; + + void update(label const & value); + + private: + react::source> font_; + react::source>> glyphs_; + react::value color_ = gfx::color_rgba{0, 0, 0, 255}; + react::value halign_ = halignment::center; + react::value valign_ = valignment::center; + react::value> size_; + react::value size_constraints_; + }; + +} diff --git a/libs/ui/include/psemek/ui/impl/renderer.hpp b/libs/ui/include/psemek/ui/impl/renderer.hpp index bb2077b6..115f0f8a 100644 --- a/libs/ui/include/psemek/ui/impl/renderer.hpp +++ b/libs/ui/include/psemek/ui/impl/renderer.hpp @@ -1,10 +1,16 @@ #pragma once +#include +#include +#include + namespace psemek::ui::impl { struct renderer { + virtual void draw_glyph(fonts::texture_type const & texture, geom::box const & position, geom::box const & texcoord, gfx::color_rgba const & color) = 0; + virtual ~renderer() {} }; diff --git a/libs/ui/include/psemek/ui/label.hpp b/libs/ui/include/psemek/ui/label.hpp index 938ab748..120c169c 100644 --- a/libs/ui/include/psemek/ui/label.hpp +++ b/libs/ui/include/psemek/ui/label.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include @@ -11,9 +13,10 @@ namespace psemek::ui struct label { react::value text = {}; - react::value halign = {}; - react::value valign = {}; - react::value must_fit = {}; + react::value font = {}; + react::value color = gfx::color_rgba{0, 0, 0, 255}; + react::value halign = halignment::center; + react::value valign = valignment::center; }; } diff --git a/libs/ui/source/impl/default_component_factory.cpp b/libs/ui/source/impl/default_component_factory.cpp index 8e45e730..6817eef7 100644 --- a/libs/ui/source/impl/default_component_factory.cpp +++ b/libs/ui/source/impl/default_component_factory.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace psemek::ui::impl { @@ -29,6 +30,7 @@ namespace psemek::ui::impl register_type(); register_type(); register_type(); + register_type(); } } diff --git a/libs/ui/source/impl/label_base.cpp b/libs/ui/source/impl/label_base.cpp new file mode 100644 index 00000000..1e430e73 --- /dev/null +++ b/libs/ui/source/impl/label_base.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +namespace psemek::ui::impl +{ + + label_base::label_base() + : font_(react::value(nullptr)) + , glyphs_(react::value>(std::vector{})) + , size_(react::map([](std::vector const & glyphs, fonts::font * font){ + if (!font) + return geom::vector{0.f, 0.f}; + geom::box bbox; + for (auto const & glyph : glyphs) + bbox |= glyph.position; + return geom::vector{bbox[0].length(), font->size()[1]}; + }, react::join(glyphs_), react::join(font_))) + , size_constraints_(react::map([](geom::vector const & size){ + geom::box result; + result[0].min = size[0]; + result[0].max = size_constraints::infinity; + result[1].min = size[1]; + result[1].max = size_constraints::infinity; + return impl::size_constraints{result}; + }, size_)) + {} + + react::value label_base::size_constraints() const + { + return size_constraints_; + } + + void label_base::draw(renderer & renderer) + { + if (!glyphs_ || !*glyphs_) + return; + + auto const size = *size_; + auto const shape = this->shape(); + + geom::vector origin; + origin[0] = std::round(geom::lerp(shape[0].min, shape[0].max - size[0], lerp_factor(*halign_))); + origin[1] = std::round(geom::lerp(shape[1].min, shape[1].max - size[1], lerp_factor(*valign_))); + origin[1] += std::round((size[1] + (**font_)->xheight()) / 2.f); + + for (auto const & glyph : **glyphs_) + renderer.draw_glyph(*glyph.texture, glyph.position + origin, glyph.texcoords, *color_); + } + + void label_base::update(label const & value) + { + font_.set(value.font); + glyphs_.set(react::map([](std::string const & str, fonts::font * font) -> std::vector { + if (!font) + return {}; + return font->shape(str, {}); + }, value.text, value.font)); + color_ = value.color; + halign_ = value.halign; + valign_ = value.valign; + } + +}