Files
StellarX-kaifa/Control.cpp
T

289 lines
8.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "Control.h"
#include "SxLog.h"
#include<assert.h>
#include "Window.h"
StellarX::ControlText& StellarX::ControlText::operator=(const ControlText& text)
{
{
nHeight = text.nHeight;
nWidth = text.nWidth;
lpszFace = text.lpszFace;
color = text.color;
nEscapement = text.nEscapement;
nOrientation = text.nOrientation;
nWeight = text.nWeight;
bItalic = text.bItalic;
bUnderline = text.bUnderline;
bStrikeOut = text.bStrikeOut;
return *this;
}
}
bool StellarX::ControlText::operator!=(const ControlText& text)
{
if(nHeight != text.nHeight)
return true;
else if (nWidth != text.nWidth)
return true;
else if (lpszFace != text.lpszFace)
return true;
else if (color != text.color)
return true;
else if (nEscapement != text.nEscapement)
return true;
else if (nOrientation != text.nOrientation)
return true;
else if (nWeight != text.nWeight)
return true;
else if (bItalic != text.bItalic)
return true;
else if (bUnderline != text.bUnderline)
return true;
else if (bStrikeOut != text.bStrikeOut)
return true;
return false;
}
void Control::setIsVisible(bool show)
{
SX_LOGD("Control") << SX_T("重置可见状态: id=", "setIsVisible: id=")
<< id
<< " show=" << (show ? 1 : 0);
if (this->show == show)
return;
this->show = show;
this->dirty = true;
if (!show)
{
// 隐藏:擦除自己在屏幕上的内容,并释放快照
discardBackground();
return;
}
// 显示:不在这里 requestRepaint(避免父容器快照未就绪时子控件抢跑 draw,污染快照)
// 仅向上标脏,让事件收口阶段由容器统一重绘。
if (parent)
parent->setDirty(true);
}
void Control::onWindowResize()
{
SX_LOGD("Layout") << SX_T("尺寸变化:id=", "onWindowResize: id=") << id
<< SX_T(" -> 丢背景快照 + 标脏", " -> discardSnap + dirty");
// 自己:丢快照 + 标脏
invalidateBackgroundSnapshot();
setDirty(true);
}
void Control::setLayoutMode(StellarX::LayoutMode layoutMode_)
{
this->layoutMode = layoutMode_;
}
void Control::setAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2)
{
this->anchor_1 = anchor_1;
this->anchor_2 = anchor_2;
}
StellarX::Anchor Control::getAnchor_1() const
{
return this->anchor_1;
}
StellarX::Anchor Control::getAnchor_2() const
{
return this->anchor_2;
}
StellarX::LayoutMode Control::getLayoutMode() const
{
return this->layoutMode;
}
// 保存当前的绘图状态(字体、颜色、线型等)
// 在控件绘制前调用,确保不会影响全局绘图状态
void Control::saveStyle()
{
gettextstyle(&currentFont); // 获取当前字体样式
currentColor = gettextcolor(); // 获取当前字体颜色
currentBorderColor = getlinecolor(); //保存当前边框颜色
getlinestyle(&currentLineStyle); //保存当前线型
currentBkColor = getfillcolor(); //保存当前填充色
}
// 恢复之前保存的绘图状态
// 在控件绘制完成后调用,恢复全局绘图状态
void Control::restoreStyle()
{
settextstyle(&currentFont); // 恢复默认字体样式
settextcolor(currentColor); // 恢复默认字体颜色
setfillcolor(currentBkColor);
setlinestyle(&currentLineStyle);
setlinecolor(currentBorderColor);
setfillstyle(BS_SOLID);//恢复填充
}
void Control::requestRepaint(Control* parent)
{
if (shouldDeferManagedRepaint())
{
// 托管路径:当前正在 Window 的事件分发阶段,不能立即绘制;
// 这里只登记 source,真正的 root 选择由 Window 在 requestManagedRepaint 中完成。
if (auto* host = getHostWindow())
host->requestManagedRepaint(this);
return;
}
// 说明:
// - 常规路径:子控件调用 requestRepaint(this->parent),然后 parent 负责局部重绘(Canvas/TabControl override
// - 兜底路径:如果某个“容器控件”没 override requestRepaint,就会出现 parent==this 的递归风险
// 此时我们改为向更上层冒泡,直到根重绘。
if (parent == this)
{
SX_LOGW("Dirty")
<< SX_T("requestRepaint(默认容器兜底):id=", "requestRepaint(default-container-fallback): id=")
<< id
<< SX_T("parent==this,向上层 parent 继续冒泡", " parent==this, bubble to upper parent");
if (this->parent) this->parent->requestRepaint(this->parent);
else onRequestRepaintAsRoot();
return;
}
SX_LOG_TRACE("Dirty") << SX_T("请求重绘:id=","requestRepaint: id=") << id << " parent=" << (parent ? parent->getId() : "null");
if (parent) parent->requestRepaint(parent); // 交给容器处理(容器可局部重绘)
else onRequestRepaintAsRoot(); // 根兜底
}
void Control::onRequestRepaintAsRoot()
{
if (shouldDeferManagedRepaint())
{
// 即使已经冒泡到 root,只要还在托管分发期,也不能直接绘制;
// 仍然回到 Window 做统一提交。
if (auto* host = getHostWindow())
host->requestManagedRepaint(this);
return;
}
SX_LOG_TRACE("Dirty")
<< SX_T("触发根重绘:id=", "onRequestRepaintAsRoot: id=") << id
<< SX_T("(从根节点开始重画)", " (root repaint)");
discardBackground();
setDirty(true);
draw(); // 只有“无父”时才允许立即画,不会被谁覆盖
}
bool Control::shouldDeferManagedRepaint() const
{
Window* host = getHostWindow();
return host && host->isManagedDispatchActive();
}
// 获取宿主 Window
// - 顶层控件由 Window/addDialog 直接注入;
// - 子控件没有直接注入时,沿 parent 链向上回溯即可。
Window* Control::getHostWindow() const
{
if (hostWindow)
return hostWindow;
return parent ? parent->getHostWindow() : nullptr;
}
// 托管重绘 root 选择规则:
// - 对于直接挂在 Window 下的控件,root 就是它自己;
// - 对于嵌套在 Canvas/TabControl/Dialog 内的子控件,沿 parent 向上找到与宿主 Window 同属一棵树的最上层控件。
Control* Control::getManagedRepaintRoot()
{
Control* root = this;
Window* host = getHostWindow();
while (root->parent && root->parent->getHostWindow() == host)
root = root->parent;
return root;
}
RECT Control::getBoundsRect() const
{
RECT rc{};
rc.left = x;
rc.top = y;
rc.right = x + width;
rc.bottom = y + height;
return rc;
}
bool Control::canCommitManagedPartialRepaint() const
{
// 基类默认不承诺自己能安全做局部提交;
// 只有 Canvas / TabControl / Dialog 这类“拥有完整背景语义”的 root 才会 override 为 true。
return false;
}
void Control::commitManagedRepaint()
{
if (!show)
return;
// 基类兜底:如果没有更具体的容器实现,就按根级重绘处理。
if (dirty)
onRequestRepaintAsRoot();
}
void Control::saveBackground(int x, int y, int w, int h)
{
if (w <= 0 || h <= 0) return;
saveBkX = x; saveBkY = y; saveWidth = w; saveHeight = h;
if (saveBkImage)
{
//尺寸变了才重建,避免反复 new/delete
if (saveBkImage->getwidth() != w || saveBkImage->getheight() != h)
{
SX_LOGD("Snap") <<SX_T("重新保存背景快照:id=", "saveBackground rebuild: id=") << id << " size=(" << w << "x" << h << ")";
saveBkImage.reset();
}
}
else
SX_LOGD("Snap") << SX_T("保存背景快照:id=", "saveBackground rebuild: id=") << id << " size=(" << w << "x" << h << ")";
if (!saveBkImage) saveBkImage = std::make_unique<IMAGE>(w, h);
SetWorkingImage(nullptr); // ★抓屏幕
getimage(saveBkImage.get(), x, y, w, h);
hasSnap = true;
}
void Control::restBackground()
{
if (!hasSnap || !saveBkImage) return;
// 直接回贴屏幕(与抓取一致)
SetWorkingImage(nullptr);
putimage(saveBkX, saveBkY, saveBkImage.get());
}
void Control::discardBackground()
{
if (saveBkImage)
{
restBackground();
SX_LOGD("Snap") << SX_T("丢弃背景快照:id=","discardBackground: id=") << id << " hasSnap=" << (hasSnap ? 1 : 0);
saveBkImage.reset();
}
hasSnap = false; saveWidth = saveHeight = 0;
}
void Control::invalidateBackgroundSnapshot()
{
if (saveBkImage)
{
SX_LOGD("Snap") << SX_T("作废背景快照:id=", "invalidateBackgroundSnapshot: id=") << id
<< " hasSnap=" << (hasSnap ? 1 : 0);
saveBkImage.reset();
}
hasSnap = false;
saveBkX = saveBkY = 0;
saveWidth = saveHeight = 0;
}