195 lines
5.4 KiB
C++
195 lines
5.4 KiB
C++
#include "Label.h"
|
||
|
||
namespace
|
||
{
|
||
// Label 的内容尺寸刷新不依赖 EasyX 当前绘图上下文,
|
||
// 直接使用 GDI 依据“文本 + 字体样式”测量宽高。
|
||
// 这样无论是文本变化、字体样式变化,还是控件在窗口正式绘制前进入 dirty,
|
||
// 都可以先把运行态尺寸收口好,而不必等到 draw() 再决定几何。
|
||
void MeasureLabelText(const std::string& text, const StellarX::ControlText& style, int& width, int& height)
|
||
{
|
||
width = 0;
|
||
height = 0;
|
||
|
||
HDC hdc = CreateCompatibleDC(nullptr);
|
||
if (!hdc)
|
||
return;
|
||
|
||
LOGFONTA fontDesc{};
|
||
fontDesc.lfHeight = style.nHeight;
|
||
fontDesc.lfWidth = style.nWidth;
|
||
fontDesc.lfEscapement = style.nEscapement;
|
||
fontDesc.lfOrientation = style.nOrientation;
|
||
fontDesc.lfWeight = style.nWeight;
|
||
fontDesc.lfItalic = style.bItalic ? TRUE : FALSE;
|
||
fontDesc.lfUnderline = style.bUnderline ? TRUE : FALSE;
|
||
fontDesc.lfStrikeOut = style.bStrikeOut ? TRUE : FALSE;
|
||
fontDesc.lfCharSet = DEFAULT_CHARSET;
|
||
fontDesc.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
||
fontDesc.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||
fontDesc.lfQuality = DEFAULT_QUALITY;
|
||
fontDesc.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
||
lstrcpynA(fontDesc.lfFaceName, style.lpszFace ? style.lpszFace : "微软雅黑", LF_FACESIZE);
|
||
|
||
HFONT font = CreateFontIndirectA(&fontDesc);
|
||
HFONT oldFont = nullptr;
|
||
if (font)
|
||
oldFont = (HFONT)SelectObject(hdc, font);
|
||
|
||
if (text.empty())
|
||
{
|
||
TEXTMETRICA tm{};
|
||
if (GetTextMetricsA(hdc, &tm))
|
||
height = tm.tmHeight;
|
||
}
|
||
else
|
||
{
|
||
SIZE size{};
|
||
if (GetTextExtentPoint32A(hdc, text.c_str(), (int)text.size(), &size))
|
||
{
|
||
width = size.cx;
|
||
height = size.cy;
|
||
}
|
||
if (height == 0)
|
||
{
|
||
TEXTMETRICA tm{};
|
||
if (GetTextMetricsA(hdc, &tm))
|
||
height = tm.tmHeight;
|
||
}
|
||
}
|
||
|
||
if (font)
|
||
{
|
||
if (oldFont)
|
||
SelectObject(hdc, oldFont);
|
||
DeleteObject(font);
|
||
}
|
||
DeleteDC(hdc);
|
||
}
|
||
}
|
||
|
||
Label::Label()
|
||
:Control(0, 0, 0, 0)
|
||
{
|
||
this->id = "Label";
|
||
// Label 当前阶段明确走“内容驱动尺寸”语义:
|
||
// 外部布局器只安置位置,不通过 Stretch 改写它的宽高。
|
||
this->layoutCapability.allowStretchX = false;
|
||
this->layoutCapability.allowStretchY = false;
|
||
this->text = "默认标签";
|
||
textStyle.color = RGB(0, 0, 0);
|
||
textBkColor = RGB(255, 255, 255);; //默认白色背景
|
||
}
|
||
|
||
Label::Label(int x, int y, std::string text, COLORREF textcolor, COLORREF bkColor)
|
||
:Control(x, y, 0, 0)
|
||
{
|
||
this->id = "Label";
|
||
this->layoutCapability.allowStretchX = false;
|
||
this->layoutCapability.allowStretchY = false;
|
||
this->text = text;
|
||
textStyle.color = textcolor;
|
||
textBkColor = bkColor; //默认白色背景
|
||
}
|
||
|
||
void Label::refreshContentDrivenRuntimeGeometry()
|
||
{
|
||
// Label 的尺寸来源于“当前文本 + 当前字体样式”。
|
||
// 这里只更新运行态 width/height,不自动回写 localWidth/localHeight,
|
||
// 避免内容变化把设计基线偷偷带偏。
|
||
if (contentMeasureValid &&
|
||
lastMeasuredText == text &&
|
||
!(lastMeasuredStyle != textStyle))
|
||
{
|
||
return;
|
||
}
|
||
|
||
int newWidth = 0;
|
||
int newHeight = 0;
|
||
MeasureLabelText(text, textStyle, newWidth, newHeight);
|
||
|
||
const bool sizeChanged = (newWidth != this->width) || (newHeight != this->height);
|
||
if (sizeChanged && hasSnap)
|
||
{
|
||
// 运行态尺寸变化时,先恢复旧背景,避免文本缩短后旧像素残留在屏幕上。
|
||
discardBackground();
|
||
}
|
||
|
||
this->width = newWidth;
|
||
this->height = newHeight;
|
||
lastMeasuredText = text;
|
||
lastMeasuredStyle = textStyle;
|
||
contentMeasureValid = true;
|
||
dirty = true;
|
||
}
|
||
|
||
void Label::draw()
|
||
{
|
||
if (dirty && show)
|
||
{
|
||
saveStyle();
|
||
if (textBkDisap)
|
||
setbkmode(TRANSPARENT); //设置背景透明
|
||
else
|
||
{
|
||
setbkmode(OPAQUE); //设置背景不透明
|
||
setbkcolor(textBkColor); //设置背景颜色
|
||
}
|
||
settextcolor(textStyle.color);
|
||
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
||
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
||
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); //设置字体样式
|
||
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
||
saveBackground(this->x, this->y, this->width, this->height);
|
||
// 恢复背景(清除旧内容)
|
||
restBackground();
|
||
outtextxy(x, y, LPCTSTR(text.c_str()));
|
||
restoreStyle();
|
||
dirty = false;
|
||
}
|
||
}
|
||
|
||
void Label::hide()
|
||
{
|
||
discardBackground(); // 还原并释放快照
|
||
dirty = false;
|
||
}
|
||
|
||
void Label::setDirty(bool dirty)
|
||
{
|
||
if (dirty)
|
||
{
|
||
// 只要 Label 进入脏状态,就在绘制前把内容驱动尺寸同步到运行态。
|
||
// 这样文本变化和字体样式变化都会在 draw() 之前完成几何刷新。
|
||
refreshContentDrivenRuntimeGeometry();
|
||
}
|
||
this->dirty = dirty;
|
||
}
|
||
|
||
void Label::setTextdisap(bool key)
|
||
{
|
||
textBkDisap = key;
|
||
this->dirty = true;
|
||
}
|
||
|
||
void Label::setTextBkColor(COLORREF color)
|
||
{
|
||
textBkColor = color;
|
||
this->dirty = true;
|
||
}
|
||
|
||
void Label::setText(std::string text)
|
||
{
|
||
this->text = text;
|
||
refreshContentDrivenRuntimeGeometry();
|
||
}
|
||
|
||
void Label::applyResolvedLayoutRect(const StellarX::ResolvedLayoutRect& rect)
|
||
{
|
||
// 通用布局器给出的 rect.width/rect.height 只代表“外部几何解算结果”。
|
||
// Label 当前阶段的语义是内容驱动尺寸,因此这里只接位置,不接外部宽高,
|
||
// 宽高继续由 refreshContentDrivenRuntimeGeometry() 按文本和字体样式刷新。
|
||
refreshContentDrivenRuntimeGeometry();
|
||
applyRuntimeRectDirect(rect.worldX, rect.worldY, this->width, this->height);
|
||
}
|