feat: add a new awesome feature

This commit is contained in:
Ysm-04
2025-09-22 15:08:49 +08:00
parent bd4588731b
commit 9f2b175b17
25 changed files with 1962 additions and 616 deletions

View File

@@ -1,4 +1,4 @@
#include "StellarX/Button.h"
#include "Button.h"
Button::Button(int x, int y, int width, int height, const std::string text, StellarX::ButtonMode mode, StellarX::ControlShape shape)
: Control(x, y, width, height)
@@ -52,7 +52,6 @@ void Button::draw()
}
else
{
// 确保点击状态的颜色正确显示
// 点击状态优先级最高,然后是悬停状态,最后是默认状态
if (click)
setfillcolor(buttonTrueColor);
@@ -77,7 +76,7 @@ void Button::draw()
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); //设置字体样式
}
//设置按钮填充模式
setfillstyle(buttonFillMode, (int)buttonFillIma, buttonFileIMAGE);
setfillstyle((int)buttonFillMode, (int)buttonFillIma, buttonFileIMAGE);
//获取字符串像素高度和宽度
if ((this->oldtext_width != this->text_width || this->oldtext_height != this->text_height)
@@ -132,10 +131,11 @@ void Button::draw()
}
// 处理鼠标事件,检测点击和悬停状态
// 根据按钮模式和形状进行不同的处理
void Button::handleEvent(const ExMessage& msg)
bool Button::handleEvent(const ExMessage& msg)
{
bool oldHover = hover;
bool oldClick = click;
bool consume = false;//是否消耗事件
// 检测悬停状态(根据不同形状)
switch (shape)
@@ -163,13 +163,15 @@ void Button::handleEvent(const ExMessage& msg)
{
click = true;
dirty = true;
consume = true;
}
else if (mode == StellarX::ButtonMode::TOGGLE)
{
// TOGGLE模式在鼠标释放时处理
}
}
// 处理鼠标释放事件
// NORMAL 模式:鼠标在按钮上释放时才触发点击回调,如果移出区域则取消点击状态。
// TOGGLE 模式:在释放时切换状态,并触发相应的开/关回调。
else if (msg.message == WM_LBUTTONUP && hover && mode != StellarX::ButtonMode::DISABLED)
{
if (mode == StellarX::ButtonMode::NORMAL && click)
@@ -177,7 +179,8 @@ void Button::handleEvent(const ExMessage& msg)
if (onClickCallback) onClickCallback();
click = false;
dirty = true;
// 使用新的flushmessage函数刷新消息队列:cite[2]:cite[3]
consume = true;
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
flushmessage(EX_MOUSE | EX_KEY);
}
else if (mode == StellarX::ButtonMode::TOGGLE)
@@ -186,11 +189,13 @@ void Button::handleEvent(const ExMessage& msg)
if (click && onToggleOnCallback) onToggleOnCallback();
else if (!click && onToggleOffCallback) onToggleOffCallback();
dirty = true;
// 使用新的flushmessage函数刷新消息队列:cite[2]:cite[3]
consume = true;
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
flushmessage(EX_MOUSE | EX_KEY);
}
}
// 处理鼠标移出区域的情况
else if (msg.message == WM_MOUSEMOVE)
{
if (!hover && mode == StellarX::ButtonMode::NORMAL && click)
@@ -215,6 +220,7 @@ void Button::handleEvent(const ExMessage& msg)
{
draw();
}
return consume;
}
void Button::setOnClickListener(const std::function<void()>&& callback)
@@ -237,14 +243,17 @@ void Button::setbuttonMode(StellarX::ButtonMode mode)
this->mode = mode;
}
int Button::setROUND_RECTANGLEwidth(int width)
void Button::setROUND_RECTANGLEwidth(int width)
{
return rouRectangleSize.ROUND_RECTANGLEwidth = width;
rouRectangleSize.ROUND_RECTANGLEwidth = width;
this->dirty = true; // 标记需要重绘
}
int Button::setROUND_RECTANGLEheight(int height)
void Button::setROUND_RECTANGLEheight(int height)
{
return rouRectangleSize.ROUND_RECTANGLEheight = height;
rouRectangleSize.ROUND_RECTANGLEheight = height;
this->dirty = true; // 标记需要重绘
}
bool Button::isClicked() const
@@ -252,26 +261,41 @@ bool Button::isClicked() const
return this->click;
}
void Button::setFillMode(int mode)
void Button::setFillMode(StellarX::FillMode mode)
{
buttonFillMode = mode;
this->dirty = true; // 标记需要重绘
}
void Button::setFillIma(StellarX::FillStyle ima)
{
buttonFillIma = ima;
this->dirty = true;
}
void Button::setFillIma(std::string imaNAme)
{
if (buttonFileIMAGE)
{
delete buttonFileIMAGE;
buttonFileIMAGE = nullptr;
}
buttonFileIMAGE = new IMAGE;
loadimage(buttonFileIMAGE, imaNAme.c_str(),width-x,height-y);
loadimage(buttonFileIMAGE, imaNAme.c_str(),width,height);
this->dirty = true;
}
void Button::setButtonBorder(COLORREF Border)
{
buttonBorderColor = Border;
buttonBorderColor = Border;
this->dirty = true;
}
void Button::setButtonFalseColor(COLORREF color)
{
this->buttonFalseColor = color;
this->dirty = true;
}
void Button::setButtonText(const char* text)
@@ -279,6 +303,7 @@ void Button::setButtonText(const char* text)
this->text = std::string(text);
this->text_width = textwidth(LPCTSTR(this->text.c_str()));
this->text_height = textheight(LPCTSTR(this->text.c_str()));
this->dirty = true;
}
void Button::setButtonText(std::string text)
@@ -292,6 +317,7 @@ void Button::setButtonText(std::string text)
void Button::setButtonShape(StellarX::ControlShape shape)
{
this->shape = shape;
this->dirty = true;
}
@@ -315,7 +341,7 @@ StellarX::ControlShape Button::getButtonShape() const
return this->shape;
}
int Button::getFillMode() const
StellarX::FillMode Button::getFillMode() const
{
return this->buttonFillMode;
}
@@ -345,6 +371,26 @@ StellarX::ControlText Button::getButtonTextStyle() const
return this->textStyle;
}
int Button::getButtonWidth() const
{
return this->width;
}
int Button::getButtonHeight() const
{
return this->height;
}
int Button::getButtonX() const
{
return this->x;
}
int Button::getButtonY() const
{
return this->y;
}
bool Button::isMouseInCircle(int mouseX, int mouseY, int x, int y, int radius)
{
@@ -357,10 +403,10 @@ bool Button::isMouseInCircle(int mouseX, int mouseY, int x, int y, int radius)
bool Button::isMouseInEllipse(int mouseX, int mouseY, int x, int y, int width, int height)
{
int centerX = (x + width) / 2;
int centerY = (y + height) / 2;
int majorAxis = (width - x) / 2;
int minorAxis = (height - y) / 2;
int centerX = (x + width) / 2;
int centerY = (y + height) / 2;
int majorAxis = (width - x) / 2;
int minorAxis = (height - y) / 2;
double dx = mouseX - centerX;
double dy = mouseY - centerY;
double normalizedDistance = (dx * dx) / (majorAxis * majorAxis) + (dy * dy) / (minorAxis * minorAxis);

View File

@@ -1,10 +1,21 @@
#include "StellarX/Canvas.h"
#include "Canvas.h"
void Canvas::clearAllControls()
{
controls.clear();
}
Canvas::Canvas()
:Control(0,0,100,100)
{
}
Canvas::Canvas(int x, int y, int width, int height)
:Control(x, y, width, height) {}
void Canvas::draw()
{
if (!dirty)return;
saveStyle();
setlinecolor(canvasBorderClor);//设置线色
@@ -34,18 +45,21 @@ void Canvas::draw()
restoreStyle();
dirty = false; //标记画布不需要重绘
}
void Canvas::handleEvent(const ExMessage& msg)
bool Canvas::handleEvent(const ExMessage& msg)
{
for (auto& control : controls) {
control->handleEvent(msg);
}
for (auto& control : controls)
if (control->handleEvent(msg))
return true;//事件被消费短路传递立即返回true 否则返回false
return false;
}
void Canvas::addControl(std::unique_ptr<Control> control)
{
controls.push_back(std::move(control));
dirty = true;
}
void Canvas::setShape(StellarX::ControlShape shape)
@@ -57,12 +71,14 @@ void Canvas::setShape(StellarX::ControlShape shape)
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;
}
}
@@ -70,26 +86,31 @@ void Canvas::setShape(StellarX::ControlShape shape)
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;
}

View File

@@ -1,4 +1,5 @@
#include "StellarX/Control.h"
#include "Control.h"
#include<assert.h>
StellarX::ControlText& StellarX::ControlText::operator=(const ControlText& text)
{
@@ -45,20 +46,21 @@ bool StellarX::ControlText::operator!=(const ControlText& text)
// 在控件绘制前调用,确保不会影响全局绘图状态
void Control::saveStyle()
{
gettextstyle(&currentFont); // 获取当前字体样式
currentColor = gettextcolor(); // 获取当前字体颜色
currentBorderColor = getlinecolor(); //保存当前边框颜色
gettextstyle(currentFont); // 获取当前字体样式
*currentColor = gettextcolor(); // 获取当前字体颜色
*currentBorderColor = getlinecolor(); //保存当前边框颜色
getlinestyle(currentLineStyle); //保存当前线型
currentBkColor = getfillcolor(); //保存当前填充色
*currentBkColor = getfillcolor(); //保存当前填充色
}
// 恢复之前保存的绘图状态
// 在控件绘制完成后调用,恢复全局绘图状态
void Control::restoreStyle()
{
settextstyle(&currentFont); // 恢复默认字体样式
settextcolor(currentColor); // 恢复默认字体颜色
setfillcolor(currentBkColor);
settextstyle(currentFont); // 恢复默认字体样式
settextcolor(*currentColor); // 恢复默认字体颜色
setfillcolor(*currentBkColor);
setlinestyle(currentLineStyle);
setlinecolor(currentBorderColor);
setlinecolor(*currentBorderColor);
setfillstyle(BS_SOLID);//恢复填充
}

673
src/Dialog.cpp Normal file
View File

@@ -0,0 +1,673 @@
#include "Dialog.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)
{
initializeDialog();
}
Dialog::~Dialog()
{
}
void Dialog::draw()
{
if(!isVisible)
{
// 如果对话框不可见且需要清理,执行清理
if (pendingCleanup && !isCleaning)
{
performDelayedCleanup();
}
return;
}
// 如果需要初始化,则执行初始化
if (needsInitialization && isVisible)
{
initDialogSize();
needsInitialization = false;
}
if (dirty && isVisible)
{
// 保存当前绘图状态
saveStyle();
// 保存背景(仅在第一次绘制时)
if (saveBkImage == nullptr) {
saveBkX = x - BorderWidth;
saveBkY = y - BorderWidth;
saveBkWidth = width + 2 * BorderWidth;
saveBkHeight = height + 2 * BorderWidth;
saveBkImage = new IMAGE(saveBkWidth, saveBkHeight);
getimage(saveBkImage, saveBkX, saveBkY, saveBkWidth, saveBkHeight);
}
Canvas::setBorderColor(this->borderColor);
Canvas::setLinewidth(this->BorderWidth);
Canvas::setCanvasBkColor(this->backgroundColor);
Canvas::setShape(StellarX::ControlShape::ROUND_RECTANGLE);
//设置所有控件为脏状态
for(auto& c :this->controls)
c->setDirty(true);
Canvas::draw();
//绘制消息文本
settextcolor(textStyle.color);
//设置字体样式
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
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;
if (!isVisible)
{
if (pendingCleanup && !isCleaning)
{
performDelayedCleanup();
}
return false;
}
// 如果正在清理或标记为待清理,则不处理事件
if (pendingCleanup || isCleaning)
return false;
// 模态对话框:点击对话框外部区域时,发出提示音(\a)并吞噬该事件,不允许操作背景内容。
if (modal && msg.message == WM_LBUTTONUP &&
(msg.x < x || msg.x > x + width || msg.y < y || msg.y > y + height))
{
std::cout << "\a" << std::endl;
// 模态对话框不允许点击外部区域
return true;
}
// 将事件传递给子控件处理
if (!consume)
consume = Canvas::handleEvent(msg);
// 每次事件处理后检查是否需要执行延迟清理
if (pendingCleanup && !isCleaning)
performDelayedCleanup();
return consume;
}
void Dialog::SetTitle(const std::string& title)
{
this->titleText = title;
if (this->title)
{
this->title->setText(title);
}
dirty = true;
}
void Dialog::SetMessage(const std::string& message)
{
this->message = message;
splitMessageLines();
getTextSize();
dirty = true;
}
void Dialog::SetType(StellarX::MessageBoxType type)
{
this->type = type;
// 重新初始化按钮
initButtons();
dirty = 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::getModal() const
{
return modal;
}
bool Dialog::IsVisible() const
{
return isVisible;
}
bool Dialog::model() const
{
return modal;
}
void Dialog::Show()
{
if (pendingCleanup)
performDelayedCleanup();
isVisible = true;
dirty = true;
needsInitialization = true;
close = false;
shouldClose = false;
if (modal)
{
// 模态对话框需要阻塞当前线程直到对话框关闭
while (isVisible && !close)
{
// 处理消息
ExMessage msg;
if (peekmessage(&msg, EX_MOUSE | EX_KEY))
{
handleEvent(msg);
// 检查是否需要关闭
if (shouldClose)
{
Close();
break;
}
}
// 重绘
if (dirty)
{
draw();
FlushBatchDraw();
}
// 避免CPU占用过高
Sleep(10);
}
// 模态对话框关闭后执行清理
if (pendingCleanup && !isCleaning)
{
performDelayedCleanup();
}
}
else
{
// 非模态对话框只需标记为可见,由主循环处理
dirty = true;
}
}
void Dialog::Close()
{
if (!isVisible) return;
isVisible = false;
close = true;
dirty = true;
pendingCleanup = true; // 只标记需要清理,不立即执行
auto& c = hWnd.getControls();
for(auto& control:c)
control->setDirty(true);
// 工厂模式下非模态触发回调 返回结果
if (resultCallback&& !modal)
resultCallback(this->result);
}
void Dialog::initButtons()
{
controls.clear();
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->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()->getButtonX() + okButton.get()->getButtonWidth() + buttonMargin),
okButton.get()->getButtonY(),
"取消"
);
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()->getButtonX() + yesButton.get()->getButtonWidth() + buttonMargin),
yesButton.get()->getButtonY(),
""
);
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()->getButtonX() + yesButton.get()->getButtonWidth() + buttonMargin,
yesButton.get()->getButtonY(),
""
);
noButton->setOnClickListener([this]()
{
this->SetResult(StellarX::MessageBoxResult::No);
this->hWnd.dialogClose = true;
this->Close(); });
auto cancelButton = createDialogButton(
noButton.get()->getButtonX() + noButton.get()->getButtonWidth() + buttonMargin,
noButton.get()->getButtonY(),
"取消"
);
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()->getButtonX() + retryButton.get()->getButtonWidth() + buttonMargin,
retryButton.get()->getButtonY(),
"取消"
);
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()->getButtonX() + abortButton.get()->getButtonWidth() + buttonMargin,
abortButton.get()->getButtonY(),
"重试"
);
retryButton->setOnClickListener([this]()
{
this->SetResult(StellarX::MessageBoxResult::Retry);
this->hWnd.dialogClose = true;
this->Close();
});
auto ignoreButton = createDialogButton(
retryButton.get()->getButtonX() + retryButton.get()->getButtonWidth() + buttonMargin,
retryButton.get()->getButtonY(),
"忽略"
);
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<Button>
(
(this->x + this->width - closeButtonWidth) - 3, (this->y+3), closeButtonWidth-1, closeButtonHeight,
"×", // 按钮文本
RGB(255, 0, 0), // 按钮被点击颜色
this->canvasBkClor, // 按钮背景颜色
RGB(255, 0, 0), // 按钮被悬停颜色
StellarX::ButtonMode::NORMAL,
StellarX::ControlShape::B_RECTANGLE
);
but.get()->setButtonFalseColor(this->backgroundColor);
but->setOnClickListener([this]() {
this->SetResult(StellarX::MessageBoxResult::Cancel);
this->hWnd.dialogClose = true;
this->Close(); });
this->closeButton = but.get();
this->addControl(std::move(but));
}
void Dialog::initTitle()
{
this->title = std::make_unique<Label>(this->x+5,this->y+5,titleText,textStyle.color);
title->setTextdisap(true);
title->textStyle = this->textStyle;
this->addControl(std::move(title));
}
void Dialog::splitMessageLines()
{
lines.clear(); // 先清空现有的行
std::string currentLine;
for (size_t i = 0; i < message.length(); i++) {
// 处理 换行符 \r\n \n \r
if (i + 1 < message.length() && (message[i] == '\r' || message[i] == '\n')||(message[i] == '\r' && message[i+1] == '\n'))
{
if (!currentLine.empty()) {
lines.push_back(currentLine);
currentLine.clear();
}
if (message[i] == '\r' && message[i + 1] == '\n')
i++;
continue;
}
currentLine += message[i];
}
// 添加最后一行(如果有内容)
if (!currentLine.empty()) {
lines.push_back(currentLine);
}
// 如果消息为空,至少添加一个空行
if (lines.empty()) {
lines.push_back("");
}
}
void Dialog::getTextSize()
{
saveStyle();
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
for (auto text : lines)
{
int w = textwidth(LPCTSTR(text.c_str()));
int h = textheight(LPCTSTR(text.c_str()));
if (this->textHeight < h)
this->textHeight = h;
if (this->textWidth < w)
this->textWidth = w;
}
restoreStyle();
}
// 计算逻辑:对话框宽度取【文本区域最大宽度】和【按钮区域总宽度】中的较大值。
// 对话框高度 = 标题栏 + 文本区 + 按钮区 + 各种间距。
void Dialog::initDialogSize()
{
splitMessageLines(); // 分割消息行
getTextSize(); // 获取文本最大尺寸
// 获取功能按钮数量
switch (this->type)
{
case StellarX::MessageBoxType::OK: // 只有确定按钮
buttonNum = 1;
break;
case StellarX::MessageBoxType::OKCancel: // 确定和取消按钮
case StellarX::MessageBoxType::YesNo: // 是和否按钮
case StellarX::MessageBoxType::RetryCancel: // 重试和取消按钮
buttonNum = 2;
break;
case StellarX::MessageBoxType::YesNoCancel: // 是、否和取消按钮
case StellarX::MessageBoxType::AbortRetryIgnore: // 中止、重试和忽略按钮
buttonNum = 3;
break;
}
// 计算按钮区域宽度
int buttonAreaWidth = buttonNum * functionButtonWidth +
(buttonNum > 0 ? (buttonNum +1) * buttonMargin : 0);
// 计算文本区域宽度(包括边距)
int textAreaWidth = textWidth + textToBorderMargin * 2 ;
// 对话框宽度取两者中的较大值,并确保最小宽度
this->width = buttonAreaWidth > textAreaWidth ? buttonAreaWidth : textAreaWidth;
this->width = this->width > 200 ? this->width : 200;
// 计算对话框高度
// 高度 = 标题栏高度 + 文本区域高度 + 按钮区域高度 + 间距
int textAreaHeight = textHeight * (int)lines.size() + 5*((int)lines.size()-1); // 文本行高+行间距
this->height = closeButtonHeight + // 标题栏高度
titleToTextMargin + // 标题到文本的间距
textAreaHeight + // 文本区域高度
buttonAreaHeight; // 按钮区域高度
// 居中定位对话框
this->x = (hWnd.getWidth() - this->width) / 2;
this->y = (hWnd.getHeight() - this->height) / 2;
//this->textStyle.nWidth = 10;
this->textStyle.nHeight = 20;
initButtons(); // 初始化按钮
initTitle(); // 初始化标题标签
initCloseButton(); // 初始化关闭按钮
}
void Dialog::initializeDialog()
{
needsInitialization = true;
pendingCleanup = false;
isCleaning = false;
close = false;
isVisible = false;
}
// 延迟清理策略:由于对话框绘制时保存了背景快照,必须在对话框隐藏后、
// 所有控件析构前恢复背景,否则会导致背景图像被错误覆盖。
// 此方法在对话框不可见且被标记为待清理时由 draw() 或 handleEvent() 调用。
void Dialog::performDelayedCleanup()
{
if (isCleaning) return;
isCleaning = true;
// 清除所有控件
controls.clear();
// 重置指针
closeButton = nullptr;
title.reset();
// 释放背景图像资源
if (saveBkImage)
{
// 恢复背景
putimage(saveBkX, saveBkY, saveBkImage);
delete saveBkImage;
saveBkImage = nullptr;
}
// 重置状态
needsInitialization = true;
pendingCleanup = false;
isCleaning = false;
shouldClose = false;
}
void Dialog::SetResultCallback(std::function<void(StellarX::MessageBoxResult)> cb)
{
resultCallback = std::move(cb);
}
std::string Dialog::GetCaption() const
{
return titleText;
}
void Dialog::clearControls()
{
controls.clear();
// 重置按钮指针
closeButton = nullptr;
title.reset(); // 释放标题资源
}
std::unique_ptr<Button> Dialog::createDialogButton(int x, int y, const std::string& text)
{
auto btn = std::make_unique<Button>(
x, y, functionButtonWidth, functionButtonHeight,
text,
buttonTrueColor, // 点击色
buttonFalseColor, // 背景色
buttonHoverColor, // 悬停色
StellarX::ButtonMode::NORMAL,
StellarX::ControlShape::RECTANGLE
);
return btn;
}

32
src/MessageBox.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include "MessageBox.h"
namespace StellarX
{
MessageBoxResult MessageBox::ShowModal(Window& wnd,const std::string& text,const std::string& caption,
MessageBoxType type)
{
Dialog dlg(wnd, caption, text, type, true); // 模态
dlg.Show();
return dlg.GetResult();
}
void MessageBox::ShowAsync(Window& wnd,const std::string& text,const std::string& caption,MessageBoxType type,
std::function<void(MessageBoxResult)> onResult)
{
//去重,如果窗口内已有相同的对话框被触发,则不再创建
if (wnd.hasNonModalDialogWithCaption(caption))
{
std::cout << "\a" << std::endl;
return;
}
auto dlg = std::make_unique<Dialog>(wnd, caption, text, type, false); // 非模态
Dialog* dlgPtr = dlg.get();
// 设置回调
if (onResult)
dlgPtr->SetResultCallback(std::move(onResult));
// 交给 Window 管理生命周期
wnd.addDialog(std::move(dlg));
dlgPtr->Show();
}
}

View File

@@ -1,4 +1,4 @@
#include "StellarX/Label.h"
#include "Label.h"
Label::Label()
:Control(0, 0, 0, 0)
@@ -18,38 +18,46 @@ Label::Label(int x, int y, std::string text, COLORREF textcolor, COLORREF bkColo
void Label::draw()
{
saveStyle();
if (textBkDisap)
setbkmode(TRANSPARENT); //设置背景透明
else
if(dirty)
{
setbkmode(OPAQUE); //设置背景不透明
setbkcolor(textBkColor); //设置背景颜色
saveStyle();
if (textBkDisap)
setbkmode(TRANSPARENT); //设置背景透明
else
{
setbkmode(OPAQUE); //设置背景不透明
setbkcolor(textBkColor); //设置背景颜色
}
settextcolor(textColor);
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); //设置字体样式
outtextxy(x, y, LPCTSTR(text.c_str()));
restoreStyle();
dirty = false;
}
settextcolor(textColor);
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); //设置字体样式
outtextxy(x,y, LPCTSTR(text.c_str()));
restoreStyle();
}
void Label::setTextdisap(bool key)
{
textBkDisap = key;
this->dirty = true;
}
void Label::setTextColor(COLORREF color)
{
textColor = color;
this->dirty = true;
}
void Label::setTextBkColor(COLORREF color)
{
textBkColor = color;
this->dirty = true;
}
void Label::setText(std::string text)
{
this->text = text;
this->dirty = true;
}

View File

@@ -1,4 +1,4 @@
#include "StellarX/Table.h"
#include "Table.h"
// 绘制表格的当前页
// 使用双循环绘制行和列,考虑分页偏移
void Table::drawTable()
@@ -7,9 +7,9 @@ void Table::drawTable()
dY = uY;
uY = dY + lineHeights.at(0) + 10;
for (int i = (currentPage * rowsPerPage - rowsPerPage); i < (currentPage*rowsPerPage) && i < data.size(); i++)
for (size_t i = (currentPage * rowsPerPage - rowsPerPage); i < (currentPage*rowsPerPage) && i < data.size(); i++)
{
for (int j = 0; j < data[i].size(); j++)
for (size_t j = 0; j < data[i].size(); j++)
{
uX = dX + colWidths.at(j) + 20;
fillrectangle(dX, dY, uX, uY);
@@ -28,7 +28,7 @@ void Table::drawHeader()
{
uY = dY + lineHeights.at(0) + 10;
for(int i = 0; i < headers.size(); i++)
for(size_t i = 0; i < headers.size(); i++)
{
uX = dX + colWidths.at(i) + 20;
fillrectangle(dX, dY, uX, uY);
@@ -38,9 +38,8 @@ void Table::drawHeader()
}
// 初始化文本宽度和高度计算
// 遍历所有数据和表头,计算每列的最大宽度和行高
// 此方法在数据变更时自动调用
// 遍历所有数据单元和表头,计算每列的最大宽度和每行的最大高度,
// 为后续绘制表格单元格提供尺寸依据。此计算在数据变更时自动触发。
void Table::initTextWaH()
{
this->colWidths.resize(this->headers.size());
@@ -48,9 +47,9 @@ void Table::initTextWaH()
int width = 0;
int height = 0;
//计算数据尺寸
for (int i = 0; i < data.size(); i++)
for (size_t i = 0; i < data.size(); i++)
{
for (int j = 0; j < data[i].size(); j++)
for (size_t j = 0; j < data[i].size(); j++)
{
width = textwidth(LPCTSTR(data[i][j].c_str()));
height = textheight(LPCTSTR(data[i][j].c_str()));
@@ -61,7 +60,7 @@ void Table::initTextWaH()
}
}
for (int i = 0; i < this->headers.size(); i++)
for (size_t i = 0; i < this->headers.size(); i++)
{
width = textwidth(LPCTSTR(headers[i].c_str()));
height = textheight(LPCTSTR(headers[i].c_str()));
@@ -73,7 +72,7 @@ void Table::initTextWaH()
// 计算表格总宽度和高度
this->width = 0;
for (int i = 0; i < colWidths.size(); i++)
for (size_t i = 0; i < colWidths.size(); i++)
this->width += colWidths.at(i) + 20;
LINESTYLE currentStyle;
@@ -81,6 +80,9 @@ void Table::initTextWaH()
this->height = lineHeights.at(0) * (rowsPerPage + 1) + rowsPerPage * 10+20 ; // 表头+数据行+页码区域
// 由于表格绘制会覆盖原有区域,需要先保存背景以便在下次绘制前恢复,
// 避免重叠绘制造成的残影问题。
// 如果背景图像不存在或尺寸不匹配,创建或重新创建
if (saveBkImage == nullptr) {
saveBkImage = new IMAGE(width, height);
@@ -95,11 +97,15 @@ void Table::initButton()
{
int x1, x2;
int y1, y2;
x1 = pX - 70;
x1 = pX - 72;
x2 = pX + textwidth(LPCTSTR(pageNumtext.c_str())) + 10;
y1 = y2 = pY;
this->prevButton = new Button(x1, y1, 60, textheight(LPCTSTR(pageNumtext.c_str())), "上一页", RGB(0, 0, 0), RGB(255, 255, 255));
this->nextButton = new Button(x2, y2, 60, textheight(LPCTSTR(pageNumtext.c_str())), "下一页", RGB(0, 0, 0), RGB(255, 255, 255));
this->prevButton = new Button(x1, y1, 62, textheight(LPCTSTR(pageNumtext.c_str())), "上一页", RGB(0, 0, 0), RGB(255, 255, 255));
this->nextButton = new Button(x2, y2, 62, textheight(LPCTSTR(pageNumtext.c_str())), "下一页", RGB(0, 0, 0), RGB(255, 255, 255));
this->prevButton->textStyle = this->textStyle;
this->nextButton->textStyle = this->textStyle;
this->prevButton->setFillMode(tableFillMode);
this->nextButton->setFillMode(tableFillMode);
prevButton->setOnClickListener([this]()
{if (this->currentPage > 1)
{
@@ -120,23 +126,25 @@ void Table::initButton()
void Table::initPageNum()
{
if (0 == pY)
pY = uY + lineHeights.at(0) * rowsPerPage + rowsPerPage * 10+10;
for (int i = 0; i < colWidths.size(); i++)
pY = uY + lineHeights.at(0) * rowsPerPage + rowsPerPage * 10+20;
for (size_t i = 0; i < colWidths.size(); i++)
this->pX += colWidths.at(i) + 20;
this->pX -= textwidth(LPCTSTR(pageNumtext.c_str()));
this->pX /= 2;
this->pX += x;
this->pageNum = new Label(this->pX, pY, pageNumtext);
//pageNum->setTextdisap(true);
pageNum->textStyle = this->textStyle;
if (StellarX::FillMode::Null == tableFillMode)
pageNum->setTextdisap(true);
}
void Table::drawPageNum()
{
if (nullptr == pageNum)
initPageNum();
pageNumtext = std::to_string(currentPage);
pageNumtext += "页/第";
pageNumtext = "";
pageNumtext+= std::to_string(currentPage);
pageNumtext += "页/共";
pageNumtext += std::to_string(totalPages);
pageNumtext += "";
pageNum->setText(pageNumtext);
@@ -154,9 +162,8 @@ void Table::drawButton()
}
Table::Table(int x, int y)
:Control(x, y, 0,0)
{
//this->saveBkImage = new IMAGE(this->width,this->height);
:Control(x, y, 0, 0)
{
}
Table::~Table()
@@ -200,12 +207,15 @@ void Table::draw()
isNeedCellSize = false;
}
// 绘制表格之前捕获背景
// 只有在第一次绘制或者尺寸变化时才需要重新捕获背景
// 优化性能:仅在首次绘制表格尺寸变化时捕获背景图像。
// 由于表格绘制会覆盖原有区域,需要先保存背景以便在下次绘制前恢复,
// 避免重叠绘制造成的残影问题。
static bool firstDraw = true;
if (firstDraw || isNeedDrawHeaders) {
if (firstDraw || isNeedDrawHeaders)
{
// 确保在绘制任何表格内容之前捕获背景
if (saveBkImage) {
if (saveBkImage)
{
// 临时恢复样式,确保捕获正确的背景
restoreStyle();
if(tableBorderWidth>1)
@@ -229,7 +239,8 @@ void Table::draw()
}
// 恢复背景(清除旧内容)
if (saveBkImage) {
if (saveBkImage)
{
if (tableBorderWidth > 1)
putimage(this->x - tableBorderWidth, this->y - tableBorderWidth, saveBkImage);
else
@@ -260,15 +271,18 @@ void Table::draw()
}
}
void Table::handleEvent(const ExMessage& msg)
bool Table::handleEvent(const ExMessage& msg)
{
bool consume = false;
if(!this->isShowPageButton)
return;
return consume;
else
{
prevButton->handleEvent(msg);
nextButton->handleEvent(msg);
consume = prevButton->handleEvent(msg);
if (!consume)
consume = nextButton->handleEvent(msg);
}
return consume;
}
void Table::setHeaders(std::initializer_list<std::string> headers)
@@ -281,21 +295,32 @@ void Table::setHeaders(std::initializer_list<std::string> headers)
dirty = true;
}
void Table::setData(const std::vector<std::string>& data)
void Table::setData( std::vector<std::string> data)
{
if (data.size() < headers.size())
for (int i = 0; data.size() <= headers.size(); i++)
data.push_back("");
this->data.push_back(data);
totalPages = (this->data.size() + rowsPerPage - 1) / rowsPerPage;
totalPages = ((int)this->data.size() + rowsPerPage - 1) / rowsPerPage;
if (totalPages < 1)
totalPages = 1;
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
dirty = true;
}
void Table::setData(const std::initializer_list<std::vector<std::string>>& data)
void Table::setData( std::initializer_list<std::vector<std::string>> data)
{
for (auto lis : data)
this->data.push_back(lis);
totalPages = (this->data.size() + rowsPerPage - 1) / rowsPerPage;
if (lis.size() < headers.size())
{
for (size_t i = lis.size(); i< headers.size(); i++)
lis.push_back("");
this->data.push_back(lis);
}
else
this->data.push_back(lis);
totalPages = ((int)this->data.size() + rowsPerPage - 1) / rowsPerPage;
if (totalPages < 1)
totalPages = 1;
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
@@ -305,7 +330,7 @@ void Table::setData(const std::initializer_list<std::vector<std::string>>& data)
void Table::setRowsPerPage(int rows)
{
this->rowsPerPage = rows;
totalPages = (data.size() + rowsPerPage - 1) / rowsPerPage;
totalPages = ((int)data.size() + rowsPerPage - 1) / rowsPerPage;
if (totalPages < 1)
totalPages = 1;
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
@@ -315,16 +340,19 @@ void Table::setRowsPerPage(int rows)
void Table::showPageButton(bool isShow)
{
this->isShowPageButton = isShow;
this->dirty = true;
}
void Table::setTableBorder(COLORREF color)
{
this->tableBorderClor = color;
this->dirty = true;
}
void Table::setTableBk(COLORREF color)
{
this->tableBkClor = color;
this->dirty = true;
}
void Table::setTableFillMode(StellarX::FillMode mode)
@@ -333,16 +361,30 @@ void Table::setTableFillMode(StellarX::FillMode mode)
this->tableFillMode = mode;
else
this->tableFillMode = StellarX::FillMode::Solid;
this->prevButton->textStyle = this->textStyle;
this->nextButton->textStyle = this->textStyle;
this->prevButton->setFillMode(tableFillMode);
this->nextButton->setFillMode(tableFillMode);
if (StellarX::FillMode::Null == tableFillMode)
pageNum->setTextdisap(true);
this->prevButton->setDirty(true);
this->nextButton->setDirty(true);
this->prevButton->setDirty(true);
this->nextButton->setDirty(true);
this->dirty = true;
}
void Table::setTableLineStyle(StellarX::LineStyle style)
{
this->tableLineStyle = style;
this->dirty = true;
}
void Table::setTableBorderWidth(int width)
{
this->tableBorderWidth = width;
this->dirty = true;
}
int Table::getCurrentPage() const
@@ -352,7 +394,7 @@ int Table::getCurrentPage() const
int Table::getTotalPages() const
{
return this->totalPages;;
return this->totalPages;
}
int Table::getRowsPerPage() const

View File

@@ -1,5 +1,5 @@
// TextBox.cpp
#include "StellarX/TextBox.h"
#include "TextBox.h"
TextBox::TextBox(int x, int y, int width, int height, std::string text, StellarX::TextBoxmode mode, StellarX::ControlShape shape)
@@ -48,16 +48,17 @@ void TextBox::draw()
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
break;
}
restoreStyle();
dirty = false; //标记不需要重绘
}
restoreStyle();
dirty = false; //标记不需要重绘
}
void TextBox::handleEvent(const ExMessage& msg)
bool TextBox::handleEvent(const ExMessage& msg)
{
bool hover = false;
bool oldClick = click;
bool consume = false;
switch (shape)
{
@@ -66,17 +67,22 @@ void TextBox::handleEvent(const ExMessage& msg)
case StellarX::ControlShape::ROUND_RECTANGLE:
case StellarX::ControlShape::B_ROUND_RECTANGLE:
hover = (msg.x > x && msg.x < (x + width) && msg.y > y && msg.y < (y + height));//判断鼠标是否在矩形按钮内
consume = false;
break;
}
if (hover && msg.message == WM_LBUTTONUP)
{
click = true;
if(StellarX::TextBoxmode::INPUT_MODE == mode)
dirty = InputBox(LPTSTR(text.c_str()), maxCharLen,"输入框",NULL,text.c_str(), NULL ,NULL,false);
{
dirty = InputBox(LPTSTR(text.c_str()), (int)maxCharLen, "输入框", NULL, text.c_str(), NULL, NULL, false);
consume = true;
}
else if (StellarX::TextBoxmode::READONLY_MODE == mode)
{
dirty = false;
InputBox(NULL, maxCharLen, "输出框(输入无效!)", NULL, text.c_str(), NULL, NULL, false);
InputBox(NULL, (int)maxCharLen, "输出框(输入无效!)", NULL, text.c_str(), NULL, NULL, false);
consume = true;
}
flushmessage(EX_MOUSE | EX_KEY);
}
@@ -85,17 +91,20 @@ void TextBox::handleEvent(const ExMessage& msg)
if (click)
click = false;
return consume;
}
void TextBox::setMode(StellarX::TextBoxmode mode)
{
this->mode = mode;
this->dirty = true;
}
void TextBox::setMaxCharLen(int len)
void TextBox::setMaxCharLen(size_t len)
{
if (len > 0)
maxCharLen = len;
this->dirty = true;
}
void TextBox::setTextBoxshape(StellarX::ControlShape shape)
@@ -107,12 +116,14 @@ void TextBox::setTextBoxshape(StellarX::ControlShape shape)
case StellarX::ControlShape::ROUND_RECTANGLE:
case StellarX::ControlShape::B_ROUND_RECTANGLE:
this->shape = shape;
this->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;
this->dirty = true;
break;
}
}
@@ -120,11 +131,13 @@ void TextBox::setTextBoxshape(StellarX::ControlShape shape)
void TextBox::setTextBoxBorder(COLORREF color)
{
textBoxBorderClor = color;
this->dirty = true;
}
void TextBox::setTextBoxBk(COLORREF color)
{
textBoxBkClor = color;
this->dirty = true;
}
std::string TextBox::getText() const

View File

@@ -1,4 +1,5 @@
#include "StellarX/Window.h"
#include "Window.h"
#include"Dialog.h"
Window::Window(int width, int height, int mode)
{
@@ -59,24 +60,93 @@ void Window::draw(std::string pImgFile)
BeginBatchDraw(); // 开始批量绘制
for (auto& control : controls)
control->draw();
for(auto& d: dialogs)
d->draw();
EndBatchDraw(); // 结束批量绘制
}
// 运行主事件循环,处理用户输入和窗口消息
// 此方法会阻塞直到窗口关闭
// 主消息循环优先级:对话框 > 普通控件。
// 重绘策略:为保证视觉一致性,每次有对话框状态变化(打开/关闭)时,
// 会强制重绘所有控件。先绘制普通控件,再绘制对话框(确保对话框在最上层)。
void Window::runEventLoop()
{
ExMessage msg;
bool running = true;
while (running) {
msg = getmessage(EX_MOUSE | EX_KEY);
if (msg.message == WM_CLOSE) {
running = false;
continue;
while (running)
{
bool consume = false;// 是否处理了消息
// 处理所有消息
if (peekmessage(&msg, EX_MOUSE | EX_KEY | EX_WINDOW, true))
{
if (msg.message == WM_CLOSE)
{
running = false;
break;
}
// 优先处理对话框事件
for (auto& d : dialogs)
{
if (d->IsVisible() && !d->model())
consume = d->handleEvent(msg);
if (consume)
break;
}
if(!consume)
for (auto& c : controls)
{
consume = c->handleEvent(msg);
if (consume)
break;
}
}
for (auto& c : controls)
c->handleEvent(msg);
flushmessage(EX_MOUSE |EX_KEY |EX_CHAR|EX_WINDOW);
//如果有对话框打开或者关闭强制重绘
bool needredraw = false;
for (auto& d : dialogs)
{
needredraw = d->IsVisible();
if (needredraw)break;
}
if (needredraw || dialogClose)
{
// 对话框关闭后,需要手动合成一个鼠标移动消息并分发给所有普通控件,
// 以便它们能及时更新悬停状态hover否则悬停状态可能保持错误状态。
// 先把当前鼠标位置转换为客户区坐标,并合成一次 WM_MOUSEMOVE先分发给控件更新 hover 状态
POINT pt;
if (GetCursorPos(&pt))
{
ScreenToClient(this->hWnd, &pt);
ExMessage mm;
mm.message = WM_MOUSEMOVE;
mm.x =(short) pt.x;
mm.y =(short) pt.y;
// 只分发给 window 层控件(因为 dialog 已经关闭或即将关闭)
for (auto& c : controls)
c->handleEvent(mm);
}
BeginBatchDraw();
// 先绘制普通控件
for (auto& c : controls)
c->draw();
// 然后绘制对话框(确保对话框在最上层)
for (auto& d : dialogs)
{
if (!d->model() && d->IsVisible())
d->setDirty(true);
d->draw();
}
EndBatchDraw();
needredraw = false;
}
// 避免CPU占用过高
Sleep(10);
}
}
@@ -108,6 +178,59 @@ void Window::addControl(std::unique_ptr<Control> control)
this->controls.push_back(std::move(control));
}
void Window::addDialog(std::unique_ptr<Control> dialogs)
{
this->dialogs.push_back(std::move(dialogs));
}
bool Window::hasNonModalDialogWithCaption(const std::string& caption) const {
for (const auto& dptr : dialogs) {
if (!dptr) continue;
// 只检查 Dialog 类型的控件
Dialog* d = dynamic_cast<Dialog*>(dptr.get());
//检查是否有非模态对话框可见,并且消息内容一致
if (d && d->IsVisible() && !d->model() && d->GetCaption() == caption)
return true;
}
return false;
}
HWND Window::getHwnd() const
{
return hWnd;
}
int Window::getWidth() const
{
return this->width;
}
int Window::getHeight() const
{
return this->height;
}
std::string Window::getHeadline() const
{
return this->headline;
}
COLORREF Window::getBkcolor() const
{
return this->wBkcolor;
}
IMAGE* Window::getBkImage() const
{
return this->background;
}
std::vector<std::unique_ptr<Control>>& Window::getControls()
{
return this->controls;
}