Require glyph drawing from ui::renderer & implement ui::label component

This commit is contained in:
Nikita Lisitsa 2024-08-07 15:45:19 +03:00
parent ab50b4c323
commit 611dc959da
6 changed files with 110 additions and 4 deletions

View file

@ -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)

View file

@ -0,0 +1,31 @@
#pragma once
#include <psemek/ui/label.hpp>
#include <psemek/ui/impl/component.hpp>
#include <psemek/react/source.hpp>
namespace psemek::ui::impl
{
struct label_base
: component
{
label_base();
react::value<struct size_constraints> size_constraints() const override;
void draw(renderer & renderer) override;
void update(label const & value);
private:
react::source<react::value<fonts::font *>> font_;
react::source<react::value<std::vector<fonts::shaped_glyph>>> glyphs_;
react::value<gfx::color_rgba> color_ = gfx::color_rgba{0, 0, 0, 255};
react::value<halignment> halign_ = halignment::center;
react::value<valignment> valign_ = valignment::center;
react::value<geom::vector<float, 2>> size_;
react::value<struct size_constraints> size_constraints_;
};
}

View file

@ -1,10 +1,16 @@
#pragma once
#include <psemek/fonts/font_v2.hpp>
#include <psemek/gfx/color.hpp>
#include <psemek/geom/box.hpp>
namespace psemek::ui::impl
{
struct renderer
{
virtual void draw_glyph(fonts::texture_type const & texture, geom::box<float, 2> const & position, geom::box<float, 2> const & texcoord, gfx::color_rgba const & color) = 0;
virtual ~renderer() {}
};

View file

@ -1,6 +1,8 @@
#pragma once
#include <psemek/ui/alignment.hpp>
#include <psemek/fonts/font_v2.hpp>
#include <psemek/gfx/color.hpp>
#include <psemek/react/value.hpp>
#include <string>
@ -11,9 +13,10 @@ namespace psemek::ui
struct label
{
react::value<std::string> text = {};
react::value<halignment> halign = {};
react::value<valignment> valign = {};
react::value<std::string> must_fit = {};
react::value<fonts::font *> font = {};
react::value<gfx::color_rgba> color = gfx::color_rgba{0, 0, 0, 255};
react::value<halignment> halign = halignment::center;
react::value<valignment> valign = valignment::center;
};
}

View file

@ -12,6 +12,7 @@
#include <psemek/ui/impl/extend_base.hpp>
#include <psemek/ui/impl/move_base.hpp>
#include <psemek/ui/impl/button_base.hpp>
#include <psemek/ui/impl/label_base.hpp>
namespace psemek::ui::impl
{
@ -29,6 +30,7 @@ namespace psemek::ui::impl
register_type<extend, impl::extend_base>();
register_type<move, impl::move_base>();
register_type<button, impl::button_base>();
register_type<label, impl::label_base>();
}
}

View file

@ -0,0 +1,64 @@
#include <psemek/ui/impl/label_base.hpp>
#include <psemek/react/map.hpp>
#include <psemek/react/join.hpp>
namespace psemek::ui::impl
{
label_base::label_base()
: font_(react::value<fonts::font *>(nullptr))
, glyphs_(react::value<std::vector<fonts::shaped_glyph>>(std::vector<fonts::shaped_glyph>{}))
, size_(react::map([](std::vector<fonts::shaped_glyph> const & glyphs, fonts::font * font){
if (!font)
return geom::vector{0.f, 0.f};
geom::box<float, 2> 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<float, 2> const & size){
geom::box<float, 2> 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<size_constraints> 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<float, 2> 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<fonts::shaped_glyph> {
if (!font)
return {};
return font->shape(str, {});
}, value.text, value.font));
color_ = value.color;
halign_ = value.halign;
valign_ = value.valign;
}
}