Finalize layout stage 2 fixes and refresh regression scenes

This commit is contained in:
Codex
2026-04-16 11:40:39 +08:00
parent b7ad960518
commit 738cf035bb
20 changed files with 1470 additions and 308 deletions
+128 -12
View File
@@ -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);
}