#include "Dialog.h" #include "SxLog.h" Dialog::Dialog(Window& h, std::string text, std::string message, StellarX::MessageBoxType type, bool modal) : Canvas(), message(message), type(type), modal(modal), hWnd(h), titleText(text) { this->id = "Dialog"; setHostWindow(&hWnd); show = false; } Dialog::~Dialog() { } void Dialog::draw() { if (!show) { // 如果对话框不可见且需要清理,执行清理 if (pendingCleanup && !isCleaning) { performDelayedCleanup(); } return; } // 如果需要初始化,则执行初始化 if (needsInitialization && show) { initDialogSize(); needsInitialization = false; } if (dirty && show) { // 保存当前绘图状态 saveStyle(); Canvas::setBorderColor(this->borderColor); Canvas::setLinewidth(BorderWidth); Canvas::setCanvasBkColor(this->backgroundColor); Canvas::setShape(StellarX::ControlShape::ROUND_RECTANGLE); Canvas::draw(); //绘制消息文本 settextcolor(textStyle.color); setbkmode(TRANSPARENT); //设置字体样式 settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace, textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight, textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); outtextxy(x + 5, y + 5, LPCTSTR(titleText.c_str())); int ty = y + closeButtonHeight + titleToTextMargin; // 文本起始Y坐标 for (auto& line : lines) { int tx = this->x + ((this->width - textwidth(line.c_str())) / 2); // 文本起始X坐标 outtextxy(tx, ty, LPCTSTR(line.c_str())); ty = ty + textheight(LPCTSTR(line.c_str())) + 5; // 每行文本高度加5像素间距 } // 恢复绘图状态 restoreStyle(); dirty = false; } } bool Dialog::handleEvent(const ExMessage& msg) { bool consume = false; resetEventVisualChanged(); if (!show) { if (pendingCleanup && !isCleaning) { performDelayedCleanup(); } return false; } // 如果正在清理或标记为待清理,则不处理事件 if (pendingCleanup || isCleaning) return false; const bool isMouseMessage = msg.message == WM_MOUSEMOVE || msg.message == WM_LBUTTONDOWN || msg.message == WM_LBUTTONUP; const bool insideDialog = msg.x >= x && msg.x <= x + width && msg.y >= y && msg.y <= y + height; // 模态对话框区域外鼠标事件不允许落到底层背景。 if (modal && isMouseMessage && !insideDialog) { if (msg.message == WM_LBUTTONUP) std::cout << "\a" << std::endl; return true; } // 将事件传递给子控件处理 if (!consume) consume = Canvas::handleEvent(msg); // 对话框矩形范围内的鼠标事件一律由对话框吞掉,避免穿透到底层控件。 if (isMouseMessage && insideDialog) consume = true; // 每次事件处理后检查是否需要执行延迟清理 if (pendingCleanup && !isCleaning) performDelayedCleanup(); return consume; } void Dialog::SetTitle(const std::string& title) { this->titleText = title; invalidateLayout(true); } void Dialog::SetMessage(const std::string& message) { this->message = message; invalidateLayout(true); } void Dialog::SetType(StellarX::MessageBoxType type) { this->type = type; invalidateLayout(true); } void Dialog::SetModal(bool modal) { this->modal = modal; } void Dialog::SetResult(StellarX::MessageBoxResult result) { this->result = result; } StellarX::MessageBoxResult Dialog::GetResult() const { return this->result; } bool Dialog::model() const { return modal; } void Dialog::Show() { if (pendingCleanup) performDelayedCleanup(); SX_LOGI("Dialog") << SX_T("对话框弹出:是否模态=","Dialog::Show: modal=") << (modal ? 1 : 0); show = true; dirty = true; needsInitialization = true; hWnd.dialogOpen = true;// 通知窗口有对话框打开 if (modal) { // 模态对话框需要阻塞当前线程直到对话框关闭 // 记录当前窗口客户区尺寸,供轮询对比 RECT rc0; GetClientRect(hWnd.getHwnd(), &rc0); int lastW = rc0.right - rc0.left; int lastH = rc0.bottom - rc0.top; while (show) { // ① 轮询窗口尺寸(不依赖 WM_SIZE) RECT rc; GetClientRect(hWnd.getHwnd(), &rc); const int cw = rc.right - rc.left; const int ch = rc.bottom - rc.top; if (cw != lastW || ch != lastH) { lastW = cw; lastH = ch; SX_LOGD("Resize") <draw(); // 注意:不要 requestRepaint(parent),只画自己 EndBatchDraw(); dirty = false; } Sleep(10); } if (pendingCleanup && !isCleaning) performDelayedCleanup(); } else // 非模态对话框只需标记为可见,由主循环处理 dirty = true; } void Dialog::Close() { if (!show) return; show = false; dirty = true; pendingCleanup = true; // 只标记需要清理,不立即执行 // 工厂模式下非模态触发回调 返回结果 if (resultCallback && !modal) resultCallback(this->result); } void Dialog::recenterInHostWindow() { if (!show) return; // 尚未完成首次初始化时,保持“延迟初始化”语义,由首次 draw 统一创建子控件。 if (needsInitialization || width <= 0 || height <= 0) { dirty = true; return; } const int newX = (hWnd.getWidth() - width) / 2; const int newY = (hWnd.getHeight() - height) / 2; invalidateBackgroundSnapshot(); x = newX; y = newY; rebuildChrome(); dirty = true; } void Dialog::initButtons() { switch (this->type) { case StellarX::MessageBoxType::OK: // 只有确定按钮 { auto okbutton = createDialogButton((this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin * (buttonNum - 1))) / 2), ((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2), "确定" ); okbutton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::OK); this->hWnd.dialogClose = true; this->Close(); }); okbutton->textStyle = this->textStyle; this->addControl(std::move(okbutton)); } break; case StellarX::MessageBoxType::OKCancel: // 确定和取消按钮 { auto okButton = createDialogButton( (this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin * (buttonNum - 1))) / 2), ((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2), "确定" ); okButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::OK); this->hWnd.dialogClose = true; this->Close(); }); auto cancelButton = createDialogButton( (okButton.get()->getX() + okButton.get()->getWidth() + buttonMargin), okButton.get()->getY(), "取消" ); cancelButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Cancel); this->hWnd.dialogClose = true; this->Close(); }); okButton->textStyle = this->textStyle; cancelButton->textStyle = this->textStyle; this->addControl(std::move(okButton)); this->addControl(std::move(cancelButton)); } break; case StellarX::MessageBoxType::YesNo: // 是和否按钮 { auto yesButton = createDialogButton( (this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin * (buttonNum - 1))) / 2), ((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2), "是" ); yesButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Yes); this->hWnd.dialogClose = true; this->Close(); }); auto noButton = createDialogButton( (yesButton.get()->getX() + yesButton.get()->getWidth() + buttonMargin), yesButton.get()->getY(), "否" ); noButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::No); this->hWnd.dialogClose = true; this->Close(); }); yesButton->textStyle = this->textStyle; noButton->textStyle = this->textStyle; this->addControl(std::move(yesButton)); this->addControl(std::move(noButton)); } break; case StellarX::MessageBoxType::YesNoCancel: // 是、否和取消按钮 { auto yesButton = createDialogButton( (this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin * (buttonNum - 1))) / 2), ((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2), "是" ); yesButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Yes); this->hWnd.dialogClose = true; this->Close(); }); auto noButton = createDialogButton( yesButton.get()->getX() + yesButton.get()->getWidth() + buttonMargin, yesButton.get()->getY(), "否" ); noButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::No); this->hWnd.dialogClose = true; this->Close(); }); auto cancelButton = createDialogButton( noButton.get()->getX() + noButton.get()->getWidth() + buttonMargin, noButton.get()->getY(), "取消" ); cancelButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Cancel); this->hWnd.dialogClose = true; this->Close(); }); yesButton->textStyle = this->textStyle; noButton->textStyle = this->textStyle; cancelButton->textStyle = this->textStyle; this->addControl(std::move(yesButton)); this->addControl(std::move(noButton)); this->addControl(std::move(cancelButton)); } break; case StellarX::MessageBoxType::RetryCancel: // 重试和取消按钮 { auto retryButton = createDialogButton( (this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin * (buttonNum - 1))) / 2), ((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2), "重试" ); retryButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Retry); this->hWnd.dialogClose = true; this->Close(); }); auto cancelButton = createDialogButton( retryButton.get()->getX() + retryButton.get()->getWidth() + buttonMargin, retryButton.get()->getY(), "取消" ); cancelButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Cancel); this->hWnd.dialogClose = true; this->Close(); }); retryButton->textStyle = this->textStyle; cancelButton->textStyle = this->textStyle; this->addControl(std::move(retryButton)); this->addControl(std::move(cancelButton)); } break; case StellarX::MessageBoxType::AbortRetryIgnore: // 中止、重试和忽略按钮 { auto abortButton = createDialogButton( (this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin * (buttonNum - 1))) / 2), ((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2), "中止" ); abortButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Abort); this->hWnd.dialogClose = true; this->Close(); }); auto retryButton = createDialogButton( abortButton.get()->getX() + abortButton.get()->getWidth() + buttonMargin, abortButton.get()->getY(), "重试" ); retryButton->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Retry); this->hWnd.dialogClose = true; this->Close(); }); auto ignoreButton = createDialogButton( retryButton.get()->getX() + retryButton.get()->getWidth() + buttonMargin, retryButton.get()->getY(), "忽略" ); ignoreButton.get()->setOnClickListener([this]() { this->SetResult(StellarX::MessageBoxResult::Ignore); this->hWnd.dialogClose = true; this->Close(); }); abortButton->textStyle = this->textStyle; retryButton->textStyle = this->textStyle; ignoreButton->textStyle = this->textStyle; this->addControl(std::move(abortButton)); this->addControl(std::move(retryButton)); this->addControl(std::move(ignoreButton)); } break; } } void Dialog::initCloseButton() { //初始化关闭按钮 auto but = std::make_unique