Files
MuditaOS/module-gui/gui/widgets/Label.cpp
Maciej Janicki e6fdf0e22c [EGD-5333] Change renderer to follow command design pattern
Changes draw command implementation to properly follow
command design pattern. All drawing commands have been
moved to separate inheriting draw commands from renderer.

Other changes:
- New draw methods overloads have been added to pixel renderer.
 Now pixel rendering methods are in one class.
- Simplified draw commands naming.
- Changed variable naming in draw commands to be more verbose.
- Changed {x,y} pairs to Points where possible.
2021-02-03 16:24:48 +01:00

298 lines
8.8 KiB
C++

// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "log/log.hpp"
#include "utf8/UTF8.hpp"
#include "../core/DrawCommand.hpp"
#include "Label.hpp"
#include <Style.hpp>
#include <cassert>
#include "TextConstants.hpp"
#include <FontManager.hpp>
#include <RawFont.hpp>
namespace gui
{
Label::Label()
: Rect(), text{""}, textDisplayed{""}, charDrawableCount{0}, stringPixelWidth{0}, textColor{0, 0}, font{nullptr}
{
setFont(style::window::font::medium);
}
Label::Label(
Item *parent, const uint32_t &x, const uint32_t &y, const uint32_t &w, const uint32_t &h, const UTF8 &newText)
: Rect{parent, x, y, w, h},
text(newText), textDisplayed{""}, charDrawableCount{0}, stringPixelWidth{0}, textColor{0, 0}
{
setFont(style::window::font::medium);
}
Label::Label(Item *parent, meta::Label label) : Label(parent, label.x, label.y, label.w, label.h, label.text)
{
setPenFocusWidth(style::window::default_border_no_focus_w);
setPenWidth(style::window::default_border_no_focus_w);
setFont(label.font);
setEllipsis(Ellipsis::Right);
setAlignment(label.align);
setRadius(label.radius);
setEdges(label.edges);
}
void Label::calculateDisplayText()
{
uint32_t availableSpace = widgetArea.w;
if (font == nullptr) {
LOG_ERROR("No font loaded!");
return;
}
charDrawableCount = font->getCharCountInSpace(text, availableSpace);
textArea.w = font->getPixelWidth(text.substr(0, charDrawableCount));
textDisplayed = text;
/// do not try to draw text::newline in label
if (text.length() > 0 && text[text.length() - 1] == text::newline) {
textDisplayed.removeChar(textDisplayed.length() - 1);
}
textDisplayed = font->getTextWithElipsis(textDisplayed, availableSpace, ellipsis);
stringPixelWidth = font->getPixelWidth(textDisplayed, 0, textDisplayed.length());
textArea.h = font->info.line_height;
// calculate vertical position of text
switch (alignment.vertical) {
case (Alignment::Vertical::Center):
textArea.y = (widgetArea.h - font->info.line_height) / 2 + font->info.base;
break;
case Alignment::Vertical::Top:
textArea.y = font->info.base + padding.top;
break;
case Alignment::Vertical::Bottom:
textArea.y = widgetArea.h - font->info.line_height + font->info.base - padding.bottom;
break;
default:
break;
}
switch (alignment.horizontal) {
case (Alignment::Horizontal::Center):
textArea.x = (widgetArea.w - textArea.w) / 2;
break;
case Alignment::Horizontal::Left:
textArea.x = padding.left;
break;
case Alignment::Horizontal::Right:
textArea.x = widgetArea.w - textArea.w - padding.right;
break;
default:
break;
}
// if dots mode is disabled and line mode is enabled calculate positiona and width of the line
if ((ellipsis != Ellipsis::None) && (lineMode) && (lineFront != nullptr)) {
uint32_t spaceWidth = font->getCharPixelWidth(' ');
int32_t lineW = availableSpace - stringPixelWidth;
uint32_t lineY = textArea.y - font->getCharPixelHeight('a') / 2;
if (lineW < 0)
lineW = 0;
lineFront->setVisible(true);
lineBack->setVisible(true);
// both lines are visible
switch (alignment.horizontal) {
case (Alignment::Horizontal::Center):
lineFront->setPosition(0, lineY);
lineFront->setSize(lineW / 2 - spaceWidth, 2);
lineBack->setPosition(lineW / 2 + stringPixelWidth + spaceWidth, lineY);
lineBack->setSize(lineW / 2 - spaceWidth, 2);
break;
case Alignment::Horizontal::Right:
lineFront->setPosition(0, lineY);
lineFront->setSize(lineW - spaceWidth - padding.right, 2);
lineBack->setVisible(false);
break;
case Alignment::Horizontal::Left:
lineBack->setPosition(stringPixelWidth + spaceWidth + padding.left, lineY);
lineBack->setSize(lineW - spaceWidth - padding.left, 2);
lineFront->setVisible(false);
break;
default:
break;
}
}
}
void Label::setText(const UTF8 &text)
{
this->text = text;
auto fits = textFitsIn(text, area(Area::Normal).w);
if (!fits) {
fitTextIn(text);
}
calculateDisplayText();
}
Label::Fits Label::textFitsIn(const UTF8 &text, uint32_t width)
{
Fits fits;
if (font == nullptr) {
fits.fits = false;
}
else {
auto cnt = font->getCharCountInSpace(text, area(Area::Max).w);
fits.fits = cnt == text.length();
fits.space_consumed = font->getPixelWidth(text);
}
return fits;
}
void Label::fitTextIn(const UTF8 &text)
{
Fits fits = textFitsIn(text, area(Item::Area::Max).w);
if (fits) {
setSize(fits.space_consumed, getHeight());
}
else {
setSize(area(Item::Area::Max).w, getHeight());
}
}
void Label::clear()
{
this->text = UTF8("");
calculateDisplayText();
}
const UTF8 &Label::getText() const noexcept
{
return text;
}
unsigned int Label::getTextLength() const noexcept
{
return text.length();
}
void Label::setAlignment(const Alignment &value)
{
this->alignment = value;
calculateDisplayText();
}
void Label::setPadding(const Padding &padding)
{
this->padding = padding;
calculateDisplayText();
}
void Label::setEllipsis(Ellipsis ellipsis)
{
this->ellipsis = ellipsis;
calculateDisplayText();
}
void Label::setLineMode(const bool &val)
{
// if line mode is disable remove the line if it was previously created
if (val == false) {
if (lineFront != nullptr) {
this->removeWidget(lineFront);
this->removeWidget(lineBack);
delete lineFront;
delete lineBack;
lineFront = nullptr;
lineBack = nullptr;
}
}
else {
if (lineFront == nullptr) {
lineFront = new Rect(this, 0, 0, 0, 0);
lineBack = new Rect(this, 0, 0, 0, 0);
}
}
calculateDisplayText();
}
void Label::buildDrawListImplementation(std::list<Command> &commands)
{
Rect::buildDrawListImplementation(commands);
if (font != nullptr) {
auto cmd = std::make_unique<DrawText>();
cmd->str = textDisplayed;
cmd->fontID = font->id;
cmd->color = textColor;
cmd->origin = {drawArea.x, drawArea.y};
cmd->width = drawArea.w;
cmd->height = drawArea.h;
cmd->textOrigin = {textArea.x, textArea.y};
cmd->textHeight = textArea.h;
cmd->areaX = widgetArea.x;
cmd->areaY = widgetArea.y;
cmd->areaW = widgetArea.w;
cmd->areaH = widgetArea.h;
commands.emplace_back(std::move(cmd));
}
}
bool Label::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
{
calculateDisplayText();
return true;
}
void Label::setFont(const UTF8 &fontName)
{
RawFont *newFont = FontManager::getInstance().getFont(fontName);
setFont(newFont);
}
void Label::setFont(RawFont *font)
{
this->font = font;
if (font != nullptr) {
calculateDisplayText();
}
}
RawFont *Label::getFont() const noexcept
{
return font;
}
void Label::setTextColor(Color color)
{
textColor = color;
}
uint32_t Label::getTextNeedSpace() const noexcept
{
if (font == nullptr) {
return 0;
}
return font->getPixelWidth(text);
}
uint32_t Label::getTextHeight() const noexcept
{
if (font == nullptr) {
return 0;
}
return font->info.line_height;
}
uint32_t Label::getTextWidth() const noexcept
{
return textArea.w;
}
void Label::accept(GuiVisitor &visitor)
{
visitor.visit(*this);
}
} /* namespace gui */