Finalize layout stage 2 fixes and refresh regression scenes
This commit is contained in:
@@ -1,9 +1,81 @@
|
||||
#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);; //默认白色背景
|
||||
@@ -13,11 +85,44 @@ Label::Label(int x, int y, std::string text, COLORREF textcolor, COLORREF bkColo
|
||||
: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)
|
||||
@@ -34,16 +139,6 @@ void Label::draw()
|
||||
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
||||
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
||||
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); //设置字体样式
|
||||
|
||||
const int newWidth = textwidth(text.c_str());
|
||||
const int newHeight = textheight(text.c_str());
|
||||
if (newWidth != this->width || newHeight != this->height)
|
||||
{
|
||||
if (hasSnap)
|
||||
discardBackground();
|
||||
this->width = newWidth;
|
||||
this->height = newHeight;
|
||||
}
|
||||
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
||||
saveBackground(this->x, this->y, this->width, this->height);
|
||||
// 恢复背景(清除旧内容)
|
||||
@@ -53,12 +148,24 @@ void Label::draw()
|
||||
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;
|
||||
@@ -74,5 +181,14 @@ void Label::setTextBkColor(COLORREF color)
|
||||
void Label::setText(std::string text)
|
||||
{
|
||||
this->text = text;
|
||||
this->dirty = true;
|
||||
refreshContentDrivenRuntimeGeometry();
|
||||
}
|
||||
|
||||
void Label::applyResolvedLayoutRect(const StellarX::ResolvedLayoutRect& rect)
|
||||
{
|
||||
// 通用布局器给出的 rect.width/rect.height 只代表“外部几何解算结果”。
|
||||
// Label 当前阶段的语义是内容驱动尺寸,因此这里只接位置,不接外部宽高,
|
||||
// 宽高继续由 refreshContentDrivenRuntimeGeometry() 按文本和字体样式刷新。
|
||||
refreshContentDrivenRuntimeGeometry();
|
||||
applyRuntimeRectDirect(rect.worldX, rect.worldY, this->width, this->height);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user