diff --git a/libs/ui/include/psemek/ui/label.hpp b/libs/ui/include/psemek/ui/label.hpp index 382d94f8..c61ab154 100644 --- a/libs/ui/include/psemek/ui/label.hpp +++ b/libs/ui/include/psemek/ui/label.hpp @@ -84,6 +84,7 @@ namespace psemek::ui mutable std::optional cached_state_; void update_cached_state() const; + cached_state cached_state_for(geom::box const & bbox) const; }; } diff --git a/libs/ui/source/label.cpp b/libs/ui/source/label.cpp index 0758aabe..885565db 100644 --- a/libs/ui/source/label.cpp +++ b/libs/ui/source/label.cpp @@ -44,11 +44,11 @@ namespace psemek::ui geom::box label::size_constraints() const { - if (!cached_state_) - update_cached_state(); - static float const inf = std::numeric_limits::infinity(); - return {{{cached_state_->size[0], inf}, {cached_state_->size[1], inf}}}; + + auto state = cached_state_for({{{0.f, inf}, {0.f, inf}}}); + + return {{{state.size[0], inf}, {state.size[1], inf}}}; } void label::draw(painter & p) const @@ -82,15 +82,20 @@ namespace psemek::ui void label::update_cached_state() const { - cached_state_ = cached_state{}; + cached_state_ = cached_state_for(shape_.box); + } - if (text_.empty()) return; + label::cached_state label::cached_state_for(geom::box const & bbox) const + { + auto state = cached_state{}; + + if (text_.empty()) return state; auto st = style(); - if (!st) return; - if (!st->font) return; + if (!st) return state; + if (!st->font) return state; - cached_state_->font = st->font.get(); + state.font = st->font.get(); shape_options opts; opts.scale = st->text_scale; @@ -100,14 +105,17 @@ namespace psemek::ui for (auto const & g : glyphs) raw_bbox |= g.position; - std::size_t max_lines = 1; + std::size_t max_lines; switch (multiline_) { case multiline_mode::none: + max_lines = 1; break; case multiline_mode::minimize_lines: - max_lines = std::max(1.f, std::floor(shape_.box[1].length() / st->font->size()[1] / st->text_scale)); + max_lines = std::isfinite(bbox[1].length()) + ? std::max(1, std::floor(bbox[1].length() / st->font->size()[1] / st->text_scale)) + : std::numeric_limits::max(); break; case multiline_mode::minimize_area: throw std::runtime_error("multiline_mode::minimize_area is not supported yet"); @@ -136,7 +144,7 @@ namespace psemek::ui x_range |= glyphs[line_end].position[0]; - if (x_range.length() > shape_.box[0].length()) + if (x_range.length() > bbox[0].length()) break; ++line_end; @@ -190,37 +198,41 @@ namespace psemek::ui break; } + float max_line_size = 0.f; + for (std::size_t l = 0; l < lines.size(); ++l) { geom::interval x_range; for (std::size_t i = lines[l].first; i < lines[l].second; ++i) x_range |= glyphs[i].position[0]; + max_line_size = std::max(max_line_size, x_range.length()); + geom::vector offset; switch (halign_) { case halignment::left: - offset[0] = shape_.box[0].min - x_range.min; + offset[0] = bbox[0].min - x_range.min; break; case halignment::center: - offset[0] = shape_.box[0].center() - x_range.length() / 2.f - x_range.min; + offset[0] = bbox[0].center() - x_range.length() / 2.f - x_range.min; break; case halignment::right: - offset[0] = shape_.box[0].max - x_range.length() - x_range.min; + offset[0] = bbox[0].max - x_range.length() - x_range.min; break; } switch (valign_) { case valignment::top: - offset[1] = shape_.box[1].min + l * st->text_scale * st->font->size()[1]; + offset[1] = bbox[1].min + l * st->text_scale * st->font->size()[1]; break; case valignment::center: - offset[1] = shape_.box[1].center() + (l - lines.size() / 2.f) * st->text_scale * st->font->size()[1]; + offset[1] = bbox[1].center() + (l - lines.size() / 2.f) * st->text_scale * st->font->size()[1]; break; case valignment::bottom: - offset[1] = shape_.box[1].max + (l - lines.size() * 1.f) * st->text_scale * st->font->size()[1]; + offset[1] = bbox[1].max + (l - lines.size() * 1.f) * st->text_scale * st->font->size()[1]; break; } @@ -228,8 +240,11 @@ namespace psemek::ui glyphs[i].position += offset; } - cached_state_->glyphs = std::move(glyphs); - cached_state_->size = raw_bbox.dimensions(); + state.glyphs = std::move(glyphs); + state.size[0] = max_line_size; + state.size[1] = lines.size() * st->text_scale * st->font->size()[1]; + + return state; } }