Refactor layout pipeline, add KEY5 regression, and fix tooltip hide
This commit is contained in:
+51
-184
@@ -32,26 +32,54 @@ Canvas::Canvas(int x, int y, int width, int height)
|
||||
this->id = "Canvas";
|
||||
}
|
||||
|
||||
void Canvas::relayoutManagedChildren()
|
||||
{
|
||||
// Canvas 负责子控件从“父局部设计矩形”到“当前世界矩形”的转换。
|
||||
// 当 Canvas 自己的位置或尺寸变化后,所有受它管理的子控件都要重新走一次统一解算。
|
||||
for (auto& child : controls)
|
||||
{
|
||||
const StellarX::ResolvedLayoutRect rect =
|
||||
child->resolveLayoutRect(localWidth, localHeight, x, y, width, height);
|
||||
child->applyResolvedLayoutRect(rect);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::setX(int x)
|
||||
{
|
||||
this->x = x;
|
||||
for (auto& c : controls)
|
||||
{
|
||||
c->onWindowResize();
|
||||
c->setX(c->getLocalX() + this->x);
|
||||
}
|
||||
dirty = true;
|
||||
// 公开 setter 在 Canvas 上不能再视为“单纯改自己的 x”:
|
||||
// 一旦容器移动,子控件的世界坐标也必须整体重算。
|
||||
applyRuntimeRectDirect(x, y, width, height);
|
||||
relayoutManagedChildren();
|
||||
onWindowResize();
|
||||
}
|
||||
|
||||
void Canvas::setY(int y)
|
||||
{
|
||||
this->y = y;
|
||||
for (auto& c : controls)
|
||||
{
|
||||
c->onWindowResize();
|
||||
c->setY(c->getLocalY() + this->y);
|
||||
}
|
||||
dirty = true;
|
||||
applyRuntimeRectDirect(this->x, y, width, height);
|
||||
relayoutManagedChildren();
|
||||
onWindowResize();
|
||||
}
|
||||
|
||||
void Canvas::setWidth(int width)
|
||||
{
|
||||
applyRuntimeRectDirect(x, y, width, height);
|
||||
relayoutManagedChildren();
|
||||
onWindowResize();
|
||||
}
|
||||
|
||||
void Canvas::setHeight(int height)
|
||||
{
|
||||
applyRuntimeRectDirect(x, y, width, height);
|
||||
relayoutManagedChildren();
|
||||
onWindowResize();
|
||||
}
|
||||
|
||||
void Canvas::applyResolvedLayoutRect(const StellarX::ResolvedLayoutRect& rect)
|
||||
{
|
||||
// 统一解算器已经给出当前运行态世界矩形;
|
||||
// Canvas 在应用自身矩形后,还必须继续刷新全部子控件。
|
||||
applyRuntimeRectDirect(rect.worldX, rect.worldY, rect.width, rect.height);
|
||||
relayoutManagedChildren();
|
||||
}
|
||||
|
||||
void Canvas::clearAllControls()
|
||||
@@ -173,11 +201,12 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
||||
|
||||
void Canvas::addControl(std::unique_ptr<Control> control)
|
||||
{
|
||||
|
||||
//坐标转化
|
||||
control->setX(control->getLocalX() + this->x);
|
||||
control->setY(control->getLocalY() + this->y);
|
||||
control->setParent(this);
|
||||
// 新子控件加入容器时,立刻按“当前容器运行态矩形”解算一次,
|
||||
// 避免后续第一次 draw / resize 前 world 坐标仍停留在设计态。
|
||||
const StellarX::ResolvedLayoutRect rect =
|
||||
control->resolveLayoutRect(localWidth, localHeight, this->x, this->y, this->width, this->height);
|
||||
control->applyResolvedLayoutRect(rect);
|
||||
SX_LOGI("Canvas")
|
||||
<< SX_T("添加子控件:父=Canvas 子id=", "addControl: parent=Canvas childId=")
|
||||
<< control->getId()
|
||||
@@ -264,173 +293,11 @@ void Canvas::setDirty(bool dirty)
|
||||
|
||||
void Canvas::onWindowResize()
|
||||
{
|
||||
// 首先处理自身的快照等逻辑
|
||||
// resize 语义已收口:
|
||||
// Canvas 不再在这里重新解算布局,只负责丢快照、标脏,并向子控件传播“环境已变化”。
|
||||
Control::onWindowResize();
|
||||
|
||||
// 记录父容器原始尺寸(用于计算子控件的右/下边距)
|
||||
int origParentW = this->localWidth;
|
||||
int origParentH = this->localHeight;
|
||||
|
||||
// 当前容器的新尺寸
|
||||
int finalW = this->width;
|
||||
int finalH = this->height;
|
||||
|
||||
// 当前容器的新坐标(全局坐标)
|
||||
int parentX = this->x;
|
||||
int parentY = this->y;
|
||||
|
||||
// 调整每个子控件在 AnchorToEdges 模式下的位置与尺寸
|
||||
for (auto& ch : controls)
|
||||
{
|
||||
// Only adjust when using anchor-to-edges layout
|
||||
if (ch->getLayoutMode() == StellarX::LayoutMode::AnchorToEdges)
|
||||
{
|
||||
// Determine whether this child is a Table; tables keep their height constant
|
||||
bool isTable = (dynamic_cast<Table*>(ch.get()) != nullptr);
|
||||
|
||||
// Unpack anchors
|
||||
auto a1 = ch->getAnchor_1();
|
||||
auto a2 = ch->getAnchor_2();
|
||||
|
||||
bool anchorLeft = (a1 == StellarX::Anchor::Left || a2 == StellarX::Anchor::Left);
|
||||
bool anchorRight = (a1 == StellarX::Anchor::Right || a2 == StellarX::Anchor::Right);
|
||||
bool anchorTop = (a1 == StellarX::Anchor::Top || a2 == StellarX::Anchor::Top);
|
||||
bool anchorBottom = (a1 == StellarX::Anchor::Bottom || a2 == StellarX::Anchor::Bottom);
|
||||
|
||||
// If it's a table, treat as anchored left and right horizontally and anchored top vertically by default.
|
||||
if (isTable)
|
||||
{
|
||||
anchorLeft = true;
|
||||
anchorRight = true;
|
||||
// If no explicit vertical anchor was provided, default to top.
|
||||
if (!(anchorTop || anchorBottom))
|
||||
{
|
||||
anchorTop = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute new X and width
|
||||
int newX = ch->getX();
|
||||
int newWidth = ch->getWidth();
|
||||
if (anchorLeft && anchorRight)
|
||||
{
|
||||
// Scale horizontally relative to parent's size.
|
||||
if (origParentW > 0)
|
||||
{
|
||||
// Maintain proportional position and size based on original local values.
|
||||
double scaleW = static_cast<double>(finalW) / static_cast<double>(origParentW);
|
||||
newX = parentX + static_cast<int>(ch->getLocalX() * scaleW + 0.5);
|
||||
newWidth = static_cast<int>(ch->getLocalWidth() * scaleW + 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: keep original
|
||||
newX = parentX + ch->getLocalX();
|
||||
newWidth = ch->getLocalWidth();
|
||||
}
|
||||
}
|
||||
else if (anchorLeft && !anchorRight)
|
||||
{
|
||||
// Only left anchored: keep original width and left margin.
|
||||
newWidth = ch->getLocalWidth();
|
||||
newX = parentX + ch->getLocalX();
|
||||
}
|
||||
else if (!anchorLeft && anchorRight)
|
||||
{
|
||||
// Only right anchored: keep original width and right margin.
|
||||
newWidth = ch->getLocalWidth();
|
||||
int origRightDist = origParentW - (ch->getLocalX() + ch->getLocalWidth());
|
||||
newX = parentX + finalW - origRightDist - newWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No horizontal anchor: position relative to parent's left and width unchanged.
|
||||
newWidth = ch->getLocalWidth();
|
||||
newX = parentX + ch->getLocalX();
|
||||
}
|
||||
ch->setX(newX);
|
||||
ch->setWidth(newWidth);
|
||||
|
||||
// Compute new Y and height
|
||||
int newY = ch->getY();
|
||||
int newHeight = ch->getHeight();
|
||||
if (isTable)
|
||||
{
|
||||
// Table: Height remains constant; adjust Y based on anchors.
|
||||
newHeight = ch->getLocalHeight();
|
||||
if (anchorTop && anchorBottom)
|
||||
{
|
||||
// If both top and bottom anchored, scale Y but keep height.
|
||||
if (origParentH > 0)
|
||||
{
|
||||
double scaleH = static_cast<double>(finalH) / static_cast<double>(origParentH);
|
||||
newY = parentY + static_cast<int>(ch->getLocalY() * scaleH + 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
}
|
||||
else if (anchorTop && !anchorBottom)
|
||||
{
|
||||
// Top anchored only
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
else if (!anchorTop && anchorBottom)
|
||||
{
|
||||
// Bottom anchored only
|
||||
int origBottomDist = origParentH - (ch->getLocalY() + ch->getLocalHeight());
|
||||
newY = parentY + finalH - origBottomDist - newHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No vertical anchor: default to top
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anchorTop && anchorBottom)
|
||||
{
|
||||
// Scale vertically relative to parent's size.
|
||||
if (origParentH > 0)
|
||||
{
|
||||
double scaleH = static_cast<double>(finalH) / static_cast<double>(origParentH);
|
||||
newY = parentY + static_cast<int>(ch->getLocalY() * scaleH + 0.5);
|
||||
newHeight = static_cast<int>(ch->getLocalHeight() * scaleH + 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
newY = parentY + ch->getLocalY();
|
||||
newHeight = ch->getLocalHeight();
|
||||
}
|
||||
}
|
||||
else if (anchorTop && !anchorBottom)
|
||||
{
|
||||
// Top anchored only: keep height constant
|
||||
newHeight = ch->getLocalHeight();
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
else if (!anchorTop && anchorBottom)
|
||||
{
|
||||
// Bottom anchored only: keep height and adjust Y relative to bottom
|
||||
newHeight = ch->getLocalHeight();
|
||||
int origBottomDist = origParentH - (ch->getLocalY() + ch->getLocalHeight());
|
||||
newY = parentY + finalH - origBottomDist - newHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No vertical anchor: position relative to parent's top, height constant.
|
||||
newHeight = ch->getLocalHeight();
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
}
|
||||
ch->setY(newY);
|
||||
ch->setHeight(newHeight);
|
||||
}
|
||||
// Always forward the window resize event to the child (recursively).
|
||||
ch->onWindowResize();
|
||||
}
|
||||
for (auto& child : controls)
|
||||
child->onWindowResize();
|
||||
}
|
||||
|
||||
void Canvas::requestRepaint(Control* parent)
|
||||
|
||||
Reference in New Issue
Block a user