#include "Canvas.h" Canvas::Canvas() :Control(0, 0, 100, 100) { this->id = "Canvas"; } Canvas::Canvas(int x, int y, int width, int height) :Control(x, y, width, height) { this->id = "Canvas"; } void Canvas::setX(int x) { this->x = x; for (auto& c : controls) { c->onWindowResize(); c->setX(c->getLocalX() + this->x); } dirty = true; } void Canvas::setY(int y) { this->y = y; for (auto& c : controls) { c->onWindowResize(); c->setY(c->getLocalY() + this->y); } dirty = true; } void Canvas::clearAllControls() { controls.clear(); } void Canvas::draw() { if (!dirty || !show) { for (auto& control : controls) if (auto c = dynamic_cast(control.get())) c->draw(); return; } saveStyle(); setlinecolor(canvasBorderClor);//设置线色 if(StellarX::FillMode::Null != canvasFillMode) setfillcolor(canvasBkClor);//设置填充色 setfillstyle((int)canvasFillMode);//设置填充模式 setlinestyle((int)canvasLineStyle, canvaslinewidth); // 在绘制画布之前,先恢复并更新背景快照: // 1. 如果已有快照,则先回贴旧快照以清除之前的内容。 // 2. 当坐标或尺寸变化,或缓存图像无效时,丢弃旧快照并重新抓取新的背景。 int margin = canvaslinewidth > 1 ? canvaslinewidth : 1; if (hasSnap) { // 恢复旧快照,清除上一次绘制 restBackground(); // 如果位置或尺寸变了,或没有有效缓存,则重新抓取 if (!saveBkImage || saveBkX != this->x - margin || saveBkY != this->y - margin || saveWidth != this->width + margin * 2 || saveHeight != this->height + margin * 2) { discardBackground(); saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2); } } else { // 首次绘制或没有快照时直接抓取背景 saveBackground(this->x- margin, this->y- margin, this->width + margin*2, this->height + margin*2); } // 再次恢复最新快照,确保绘制区域干净 restBackground(); //根据画布形状绘制 switch (shape) { case StellarX::ControlShape::RECTANGLE: fillrectangle(x,y,x+width,y+height);//有边框填充矩形 break; case StellarX::ControlShape::B_RECTANGLE: solidrectangle(x, y, x + width, y + height);//无边框填充矩形 break; case StellarX::ControlShape::ROUND_RECTANGLE: fillroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//有边框填充圆角矩形 break; case StellarX::ControlShape::B_ROUND_RECTANGLE: solidroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//无边框填充圆角矩形 break; } // 绘制所有子控件 for (auto& control : controls) { control->setDirty(true); control->draw(); } restoreStyle(); dirty = false; //标记画布不需要重绘 } bool Canvas::handleEvent(const ExMessage& msg) { if (!show)return false; bool consumed = false; bool anyDirty = false; for (auto it = controls.rbegin(); it != controls.rend(); ++it) { consumed |= it->get()->handleEvent(msg); if (it->get()->isDirty()) anyDirty = true; } if (anyDirty) requestRepaint(parent); return consumed; } void Canvas::addControl(std::unique_ptr control) { //坐标转化 control->setX(control->getLocalX() + this->x); control->setY(control->getLocalY() + this->y); control->setParent(this); controls.push_back(std::move(control)); dirty = true; } void Canvas::setShape(StellarX::ControlShape shape) { switch (shape) { case StellarX::ControlShape::RECTANGLE: case StellarX::ControlShape::B_RECTANGLE: case StellarX::ControlShape::ROUND_RECTANGLE: case StellarX::ControlShape::B_ROUND_RECTANGLE: this->shape = shape; dirty = true; break; case StellarX::ControlShape::CIRCLE: case StellarX::ControlShape::B_CIRCLE: case StellarX::ControlShape::ELLIPSE: case StellarX::ControlShape::B_ELLIPSE: this->shape = StellarX::ControlShape::RECTANGLE; dirty = true; break; } } void Canvas::setCanvasfillMode(StellarX::FillMode mode) { this->canvasFillMode = mode; dirty = true; } void Canvas::setBorderColor(COLORREF color) { this->canvasBorderClor = color; dirty = true; } void Canvas::setCanvasBkColor(COLORREF color) { this->canvasBkClor = color; dirty = true; } void Canvas::setCanvasLineStyle(StellarX::LineStyle style) { this->canvasLineStyle = style; dirty = true; } void Canvas::setLinewidth(int width) { this->canvaslinewidth = width; dirty = true; } void Canvas::setIsVisible(bool visible) { this->show = visible; dirty = true; for (auto& control : controls) { control->setIsVisible(visible); control->setDirty(true); } if (!visible) this->updateBackground(); } void Canvas::setDirty(bool dirty) { this->dirty = dirty; for(auto& control : controls) control->setDirty(dirty); } void Canvas::onWindowResize() { // 首先处理自身的快照等逻辑 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(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(finalW) / static_cast(origParentW); newX = parentX + static_cast(ch->getLocalX() * scaleW + 0.5); newWidth = static_cast(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(finalH) / static_cast(origParentH); newY = parentY + static_cast(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(finalH) / static_cast(origParentH); newY = parentY + static_cast(ch->getLocalY() * scaleH + 0.5); newHeight = static_cast(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(); } } void Canvas::requestRepaint(Control* parent) { if (this == parent) { for (auto& control : controls) if (control->isDirty() && control->IsVisible()) control->draw(); } else onRequestRepaintAsRoot(); }