文本区域在屏幕上显示文本。 文本区域的文本在大小、颜色、自定义字体、动态文本等方面是完全可以配置的
#ifndef TOUCHGFX_TEXTAREA_HPP
#define TOUCHGFX_TEXTAREA_HPP
#include <touchgfx/Font.hpp>
#include <touchgfx/TextProvider.hpp>
#include <touchgfx/TypedText.hpp>
#include <touchgfx/Unicode.hpp>
#include <touchgfx/hal/Types.hpp>
#include <touchgfx/widgets/Widget.hpp>
namespace touchgfx
{
/* 文本区域 */
class TextArea : public Widget
{
public:
/* 构造函数 */
TextArea()
: Widget(), typedText(TYPED_TEXT_INVALID), color(0), linespace(0), alpha(255), indentation(0), rotation(TEXT_ROTATE_0), wideTextAction(WIDE_TEXT_NONE), boundingArea()
{
}
// 设置宽度,并重新计算文本区域的边界
virtual void setWidth(int16_t width)
{
Widget::setWidth(width);
boundingArea = calculateBoundingArea();
}
// 设置高度,并重新计算文本区域的边界
virtual void setHeight(int16_t height)
{
Widget::setHeight(height);
boundingArea = calculateBoundingArea();
}
/* 获取可以保证为实心的(不透明的)最大矩形 */
virtual Rect getSolidRect() const
{
return Rect();
}
/* 设置文本颜色 */
FORCE_INLINE_FUNCTION void setColor(colortype newColor)
{
color = newColor;
}
/* 获取文本颜色 */
FORCE_INLINE_FUNCTION colortype getColor() const
{
return color;
}
/* 设置透明度 */
virtual void setAlpha(uint8_t newAlpha)
{
alpha = newAlpha;
}
/* 获取透明度值 */
uint8_t getAlpha() const
{
return alpha;
}
// 调整TextArea的y坐标,使文本的基线(而不是TextArea)位于指定的y值
virtual void setBaselineY(int16_t baselineY)
{
setY(baselineY - getTypedText().getFont()->getBaseline());
}
// 调整TextArea的x和y坐标,使文本的基线位于指定的y值,x坐标将被用作TextArea的x坐标
virtual void setXBaselineY(int16_t x, int16_t baselineY)
{
setX(x);
setBaselineY(baselineY);
}
// 设置TextArea的行间距。设置更大的值将增加行之间的距离。可以设置负值使行(部分)重叠
FORCE_INLINE_FUNCTION void setLinespacing(int16_t space)
{
linespace = space;
boundingArea = calculateBoundingArea();
}
// 获取TextArea的行间距。如果没有设置行间距,则行间距为0
FORCE_INLINE_FUNCTION int16_t getLinespacing() const
{
return linespace;
}
// 设置文本的缩进。这对于斜体字体非常有用,可以防止某些字符(如"j"和"g")在左侧下方延伸到前一个字符下面
FORCE_INLINE_FUNCTION void setIndentation(uint8_t indent)
{
indentation = indent;
boundingArea = calculateBoundingArea();
}
// 获取文本的缩进
FORCE_INLINE_FUNCTION uint8_t getIndentation()
{
return indentation;
}
/* 获取文本对齐方式 */
virtual Alignment getAlignment() const;
/* 获取文本高度 */
virtual int16_t getTextHeight() const;
/* 获取文本宽度 */
virtual uint16_t getTextWidth() const;
// 绘制文本区域
virtual void draw(const Rect& area) const;
/* 设置文本资源。如果之前没有设置过大小,TextArea将被调整以适应新的TypedText */
void setTypedText(const TypedText& t);
/* 获取文本资源 */
const TypedText& getTypedText() const
{
return typedText;
}
/* 设置文本的旋转角度 */
FORCE_INLINE_FUNCTION void setRotation(const TextRotation textRotation)
{
rotation = textRotation;
boundingArea = calculateBoundingArea();
}
/* 获取文本旋转角度 */
TextRotation getRotation() const
{
return rotation;
}
/* 根据文本尺寸和旋转方向调整控件尺寸 */
void resizeToCurrentText();
/* 根据对齐方式、旋转方向和尺寸调整控件尺寸和位置以适应文本 */
void resizeToCurrentTextWithAlignment();
/* 根据文本高度和旋转方向调整文本尺寸以适应文本 */
void resizeHeightToCurrentText();
/* 根据文本高度调整和旋转方向文本尺寸和位置以适应文本 */
void resizeHeightToCurrentTextWithRotation();
// 设置文本行过宽时的处理方式。并重新计算文本边界区域
// 默认情况下,只有当文本中存在手动插入的换行符时,文本行才会换行。
// 如果启用了换行,并且文本将占用比TextArea更多的行数,
// 则在最后一行的末尾添加一个省略号(通常为…)以表示某些文本被省略。
// 用于省略的字符取自文本样式表
FORCE_INLINE_FUNCTION void setWideTextAction(WideTextAction action)
{
wideTextAction = action;
boundingArea = calculateBoundingArea();
}
// 获取之前通过文本过宽处理方式
WideTextAction getWideTextAction() const
{
return wideTextAction;
}
// 根据文本格式和可变参数计算文本所需的总高度。
// 格式字符串中的<placeholder>占位符数量必须与后面的可变参数数量相匹配
virtual int16_t calculateTextHeight(const Unicode::UnicodeChar* format, ...) const;
// 获取TypedText中的第一个占位符。
// 如果此文本区域有占位符,则返回指向第一个占位符的指针,否则返回0
virtual const Unicode::UnicodeChar* getWildcard1() const
{
return 0;
}
// 获取TypedText中的第二个占位符。
// 如果此文本区域有两个占位符,则返回指向第二个占位符的指针,否则返回0
virtual const Unicode::UnicodeChar* getWildcard2() const
{
return 0;
}
//内容重新绘制
virtual void invalidateContent() const;
protected:
/* 刚好报文文本的边界区域 */
class BoundingArea
{
public:
// 使用给定的包围矩形和包含的文本初始化BoundingArea类的新实例
BoundingArea(const Rect& boundingRect, const Unicode::UnicodeChar* containedText)
: rect(boundingRect), text(containedText)
{
}
// 初始化一个默认为无效的BoundingArea类的新实例
BoundingArea() : rect(Rect(0, 0, -1, -1)), text(0)
{
}
// 获取包围矩形
Rect getRect() const
{
return rect;
}
// 查询包围区域是否有效
bool isValid(const Unicode::UnicodeChar* currentText) const
{
return (rect.height >= 0 && rect.width >= 0 && text == currentText);
}
private:
Rect rect;
const Unicode::UnicodeChar* text;
};
// 计算此文本区域的最小包围矩形,并将其与包含的文本相关联,以获得包围区域。
// 注意:根据对齐方式和旋转角度调整包围矩形
virtual TextArea::BoundingArea calculateBoundingArea() const;
TypedText typedText; //文本资源
colortype color; //颜色
int16_t linespace; //行间距
uint8_t alpha; //透明度
uint8_t indentation; //缩进
TextRotation rotation; //旋转角度
WideTextAction wideTextAction; //过宽处理
static const uint16_t newLine = 10; //换行值
BoundingArea boundingArea; //此文本区域的包围区域
};
}
#endif
#include <stdarg.h>
#include <touchgfx/hal/HAL.hpp>
#include <touchgfx/lcd/LCD.hpp>
#include <touchgfx/widgets/TextArea.hpp>
namespace touchgfx
{
/* 获取文本对齐方式 */
Alignment TextArea::getAlignment() const
{
if(typedText.hasValidId())
{
return typedText.getAlignment();
}
return LEFT;
}
/* 获取文本高度:考虑行数 */
int16_t TextArea::getTextHeight() const
{
return typedText.hasValidId() ? calculateTextHeight(typedText.getText(), getWildcard1(), getWildcard2()) : 0;
}
/* 获取文本宽度 */
uint16_t TextArea::getTextWidth() const
{
return typedText.hasValidId() ? typedText.getFont()->getStringWidth(typedText.getTextDirection(), typedText.getText(), getWildcard1(), getWildcard2()) : 0;
}
// 绘制文本区域
void TextArea::draw(const Rect& area) const
{
if (typedText.hasValidId())
{
/* 边界矩形 */
Rect rectToDraw = area;
if(typedText.hasValidId() && boundingArea.isValid(typedText.getText()))
{
rectToDraw &= boundingArea.getRect();
}
if (!rectToDraw.isEmpty())
{
/* 绘制指定的Unicode字符串。在新行处换行 */
const Font* fontToDraw = typedText.getFont();
if (fontToDraw != 0)
{
const LCD::StringVisuals visuals(fontToDraw, color, alpha, getAlignment(), linespace, rotation, typedText.getTextDirection(), indentation, wideTextAction);
HAL::lcd().drawString(getAbsoluteRect(), rectToDraw, visuals, typedText.getText(), getWildcard1(), getWildcard2());
}
}
}
}
/* 设置文本资源 */
void TextArea::setTypedText(const TypedText& t)
{
/* 设置文本资源 */
typedText = t;
/* 根据文本尺寸调整控件尺寸 */
if (getWidth() == 0 && getHeight() == 0)
{
resizeToCurrentText();
}
/* 重新计算刚好包围文本的边界区域 */
boundingArea = calculateBoundingArea();
}
/* 根据文本尺寸和旋转方向调整控件尺寸 */
void TextArea::resizeToCurrentText()
{
if (typedText.hasValidId())
{
const uint16_t w = getTextWidth();
const uint16_t h = getTextHeight();
if (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180)
{
setWidthHeight(w, h);
}
else
{
setWidthHeight(h, w);
}
}
}
/* 根据对齐方式、旋转方向和尺寸调整控件尺寸和位置以适应文本 */
void TextArea::resizeToCurrentTextWithAlignment()
{
if (typedText.hasValidId())
{
const Alignment alignment = getAlignment();
const uint16_t text_width = getTextWidth();
const uint16_t text_height = getTextHeight();
/* 0/180度 */
if (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180)
{
/* 文本不靠左放 */
if (!((rotation == TEXT_ROTATE_0 && alignment == LEFT) || (rotation == TEXT_ROTATE_180 && alignment == RIGHT)))
{
const uint16_t old_width = getWidth();
const uint16_t old_x = getX();
if (alignment == CENTER) //居中
{
setX(old_x + (old_width - text_width) / 2); //调整控件位置
}
else //靠右放
{
setX(old_x + (old_width - text_width)); //调整控件位置
}
}
if (rotation == TEXT_ROTATE_180)
{
const uint16_t old_height = getHeight();
const uint16_t old_y = getY();
setY(old_y + (old_height - text_height));
}
setWidthHeight(text_width, text_height);
}
else
{
// 90+left or 270+right places text at the same y coordinate
if (!((rotation == TEXT_ROTATE_90 && alignment == LEFT) || (rotation == TEXT_ROTATE_270 && alignment == RIGHT)))
{
const uint16_t old_height = getHeight();
const uint16_t old_y = getY();
if (alignment == CENTER)
{
setY(old_y + (old_height - text_width) / 2);
}
else
{
setY(old_y + (old_height - text_width));
}
}
if (rotation == TEXT_ROTATE_90)
{
const uint16_t old_width = getWidth();
const uint16_t old_x = getX();
setX(old_x + (old_width - text_height));
}
setWidthHeight(text_height, text_width);
}
}
}
/* 根据文本高度和旋转方向调整文本尺寸以适应文本 */
void TextArea::resizeHeightToCurrentText()
{
if (typedText.hasValidId())
{
const uint16_t h = getTextHeight();
if (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180)
{
setHeight(h);
}
else
{
setWidth(h);
}
}
}
/* 根据文本高度调整和旋转方向文本尺寸和位置以适应文本 */
void TextArea::resizeHeightToCurrentTextWithRotation()
{
if (typedText.hasValidId())
{
const uint16_t h = getTextHeight();
switch (rotation)
{
case TEXT_ROTATE_0:
setHeight(h);
break;
case TEXT_ROTATE_90:
setX(rect.right() - h);
setWidth(h);
break;
case TEXT_ROTATE_180:
setY(rect.bottom() - h);
setHeight(h);
break;
case TEXT_ROTATE_270:
setWidth(h);
break;
}
}
}
/* 计算文本高度:考虑换行 */
int16_t TextArea::calculateTextHeight(const Unicode::UnicodeChar *format, ...) const
{
if(!typedText.hasValidId())
{
return 0;
}
va_list pArg;
va_start(pArg, format);
/* 获取字体和字体高度 */
const Font *fontToDraw = typedText.getFont();
const int16_t textHeight = fontToDraw->getHeight();
/* 获取给定文本的行数,同时考虑换行 */
TextProvider textProvider;
textProvider.initialize(format, pArg, fontToDraw->getGSUBTable(), fontToDraw->getContextualFormsTable());
const int16_t numLines = LCD::getNumLines(textProvider, wideTextAction, typedText.getTextDirection(), typedText.getFont(), getWidth() - indentation);
va_end(pArg);
/* 计算高度 */
return textHeight + linespace > 0 ? numLines * textHeight + (numLines - 1) * linespace : (numLines > 0 ? textHeight : 0);
}
void TextArea::invalidateContent() const
{
if (alpha == 0 || !typedText.hasValidId() || rect.isEmpty())
{
return;
}
if (boundingArea.isValid(typedText.getText()))
{
Rect boundingRect = boundingArea.getRect();
invalidateRect(boundingRect);
return;
}
invalidate();
}
/* 计算刚好包围文本的边界区域 */
TextArea::BoundingArea TextArea::calculateBoundingArea() const
{
if(!typedText.hasValidId())
{
return TextArea::BoundingArea(); // Return Invalid BoundingArea
}
const Font* fontToDraw = typedText.getFont();
const Unicode::UnicodeChar* textToDraw = typedText.getText();
const int16_t fontHeight = fontToDraw->getHeight();
const int16_t lineHeight = fontHeight + linespace;
int16_t width = 0;
uint16_t numberOfLines = 0;
/* 计算最大行宽 */
if (wideTextAction == WIDE_TEXT_NONE)
{
TextProvider textProvider;
textProvider.initialize(textToDraw, fontToDraw->getGSUBTable(), fontToDraw->getContextualFormsTable(), getWildcard1(), getWildcard2());
int16_t widgetRectHeight = (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180) ? getHeight() : getWidth();
do
{
const uint16_t lineWidth = LCD::stringWidth(textProvider, *(fontToDraw), 0x7FFF, typedText.getTextDirection());
width = MAX(width, lineWidth);
numberOfLines++;
widgetRectHeight -= lineHeight;
}while (!textProvider.endOfString() && widgetRectHeight + fontToDraw->getPixelsAboveTop() > 0);
}
else
{
TextProvider wideTextProvider;
wideTextProvider.initialize(textToDraw, fontToDraw->getGSUBTable(), fontToDraw->getContextualFormsTable(), getWildcard1(), getWildcard2());
const int16_t widgetRectWidth = (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180) ? getWidth() : getHeight();
int16_t widgetRectHeight = (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180) ? getHeight() : getWidth();
LCD::WideTextInternalStruct wtis(wideTextProvider, widgetRectWidth - indentation, widgetRectHeight, typedText.getTextDirection(), fontToDraw, linespace, wideTextAction);
// Iterate through each line, find the longest line width and sum up the total height of the bounding rectangle
do
{
wtis.scanStringLengthForLine();
const uint16_t lineWidth = wtis.getLineWidth();
width = MAX(width, lineWidth);
numberOfLines++;
widgetRectHeight -= lineHeight;
// Keep reading until end of string, ellipsis inserted or next line completely invisible.
} while (wtis.getCurrChar() != 0 && !wtis.ellipsisAtEndOfLine() && widgetRectHeight + fontToDraw->getPixelsAboveTop() > 0);
}
/* 计算最大高度 */
int16_t height = (numberOfLines * lineHeight) - linespace;
height = MAX(height, fontHeight) + fontToDraw->getPixelsBelowBottom();
/* 边界矩形 */
Rect boundingRect(0, 0, width, height);
/* 根据对齐方式调整边界矩形 */
const int16_t areaWidth = (rotation == TEXT_ROTATE_0 || rotation == TEXT_ROTATE_180) ? getWidth() : getHeight();
switch (getAlignment())
{
default:
case LEFT:
boundingRect.x = indentation;
break;
case CENTER:
boundingRect.x = ((areaWidth - boundingRect.width) / 2);
break;
case RIGHT:
boundingRect.x = areaWidth - (boundingRect.width + indentation);
break;
}
/* 根据字体左右预留像素调整边界矩形 */
const uint8_t maxPixelsLeft = fontToDraw->getMaxPixelsLeft();
const uint8_t maxPixelsRight = fontToDraw->getMaxPixelsRight();
boundingRect.x -= maxPixelsLeft;
boundingRect.width += (maxPixelsLeft + maxPixelsRight);
/* 根据文本旋转方向调整边界矩形 */
switch (rotation)
{
case TEXT_ROTATE_0:
break;
case TEXT_ROTATE_90:
boundingRect = Rect(getWidth() - boundingRect.bottom(), boundingRect.x, boundingRect.height, boundingRect.width);
break;
case TEXT_ROTATE_180:
boundingRect = Rect(getWidth() - boundingRect.right(), getHeight() - boundingRect.bottom(), boundingRect.width, boundingRect.height);
break;
case TEXT_ROTATE_270:
boundingRect = Rect(boundingRect.y, getHeight() - boundingRect.right(), boundingRect.height, boundingRect.width);
break;
}
return TextArea::BoundingArea(boundingRect, typedText.getText());
}
}
控件组
文本区域位于TouchGFX Designer中的Miscellaneous控件组中。
TouchGFX Designer中的文本区域
属性
TouchGFX Designer中文本区域的属性。
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
位置 | X和Y指定控件左上角相对于其父的位置。 W和H指定控件的宽度和高度。 自动调整大小指定是否根据文本输入自动设置控件的大小。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
文本 | ID指定使用的文本。 如果控件使用自动生成的文本,ID将显示“自动生成”。. 翻译指定要显示的文本内容。 字体排印指定文本的格式。 对齐指定文本的水平对齐方式。 最多可以为动态文本输入创建两个通配符,表示为‘<tag>’,其中‘tag’可以是任意字符串。 如需详细了解关于文本配置的信息,请参阅“文本与字体”一节。 |
外观 | 颜色指定所显示文本的颜色。 Alpha指定控件的透明度。 控件Alpha值的范围是0到255。 0表示完全透明,255表示不透明。 行距指定行之间的间距。 文本旋转设置文本的旋转角度。 |