255 lines
7.4 KiB
C++
255 lines
7.4 KiB
C++
// TextBox.cpp
|
|
#include "TextBox.h"
|
|
#include "SxLog.h"
|
|
|
|
TextBox::TextBox(int x, int y, int width, int height, std::string text, StellarX::TextBoxmode mode, StellarX::ControlShape shape)
|
|
:Control(x, y, width, height), text(text), mode(mode), shape(shape)
|
|
{
|
|
this->id = "TextBox";
|
|
// TextBox 当前阶段的默认布局策略:
|
|
// - 水平方向允许通过锚点语义拉伸
|
|
// - 垂直方向当前实现保持固定尺寸,不在本轮引入随高度变化的视觉自适应
|
|
this->layoutCapability.allowStretchX = true;
|
|
this->layoutCapability.allowStretchY = false;
|
|
}
|
|
|
|
void TextBox::draw()
|
|
{
|
|
if (dirty && show)
|
|
{
|
|
saveStyle();
|
|
setfillcolor(textBoxBkClor);
|
|
setlinecolor(textBoxBorderClor);
|
|
if (textStyle.nHeight > height)
|
|
textStyle.nHeight = height;
|
|
if (textStyle.nWidth > width)
|
|
textStyle.nWidth = width;
|
|
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
|
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
|
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
|
|
|
settextcolor(textStyle.color);
|
|
setbkmode(TRANSPARENT);
|
|
|
|
int text_width = 0;
|
|
int text_height = 0;
|
|
std::string pwdText;
|
|
std::string displayText; // 用于显示的文本(可能被截断)
|
|
bool isTextTruncated = false; // 标记文本是否被截断
|
|
|
|
if (StellarX::TextBoxmode::PASSWORD_MODE == mode)
|
|
{
|
|
for (size_t i = 0; i < text.size(); ++i)
|
|
pwdText += '*';
|
|
displayText = pwdText;
|
|
}
|
|
else
|
|
{
|
|
displayText = text;
|
|
}
|
|
|
|
// 计算可用宽度(留出左右边距)
|
|
int availableWidth = width - 20; // 左右各10像素边距
|
|
|
|
// 截断文本以适应可用宽度
|
|
int currentWidth = textwidth(LPCTSTR(displayText.c_str()));
|
|
if (currentWidth > availableWidth && availableWidth > 0)
|
|
{
|
|
// 需要截断文本,预留空间放置省略号
|
|
int ellipsisWidth = textwidth("...");
|
|
int truncatedWidth = availableWidth - ellipsisWidth;
|
|
|
|
std::string truncatedText = displayText;
|
|
while (truncatedText.size() > 0 && textwidth(LPCTSTR(truncatedText.c_str())) > truncatedWidth)
|
|
{
|
|
truncatedText.pop_back();
|
|
}
|
|
displayText = truncatedText + "...";
|
|
isTextTruncated = true;
|
|
currentWidth = textwidth(LPCTSTR(displayText.c_str()));
|
|
}
|
|
|
|
text_width = currentWidth;
|
|
text_height = textheight(LPCTSTR(displayText.c_str()));
|
|
|
|
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
|
saveBackground(this->x, this->y, this->width, this->height);
|
|
// 恢复背景(清除旧内容)
|
|
restBackground();
|
|
//根据形状绘制
|
|
switch (shape)
|
|
{
|
|
case StellarX::ControlShape::RECTANGLE:
|
|
fillrectangle(x, y, x + width, y + height);//有边框填充矩形
|
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
|
break;
|
|
case StellarX::ControlShape::B_RECTANGLE:
|
|
solidrectangle(x, y, x + width, y + height);//无边框填充矩形
|
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
|
break;
|
|
case StellarX::ControlShape::ROUND_RECTANGLE:
|
|
fillroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//有边框填充圆角矩形
|
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
|
break;
|
|
case StellarX::ControlShape::B_ROUND_RECTANGLE:
|
|
solidroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//无边框填充圆角矩形
|
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
|
break;
|
|
}
|
|
restoreStyle();
|
|
dirty = false; //标记不需要重绘
|
|
}
|
|
}
|
|
|
|
bool TextBox::handleEvent(const ExMessage& msg)
|
|
{
|
|
if (!show) return false;
|
|
resetEventVisualChanged();
|
|
|
|
bool hover = false;
|
|
bool oldClick = click;
|
|
bool consume = false;
|
|
const bool isMouseMessage =
|
|
msg.message == WM_MOUSEMOVE ||
|
|
msg.message == WM_LBUTTONDOWN ||
|
|
msg.message == WM_LBUTTONUP;
|
|
|
|
if (isMouseMessage)
|
|
{
|
|
switch (shape)
|
|
{
|
|
case StellarX::ControlShape::RECTANGLE:
|
|
case StellarX::ControlShape::B_RECTANGLE:
|
|
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));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hover && msg.message == WM_LBUTTONUP)
|
|
{
|
|
click = true;
|
|
|
|
const size_t oldLen = text.size();
|
|
SX_LOGI("TextBox") << SX_T("激活:id=","activate: id=") << id << " mode=" << (int)mode << " oldLen=" << oldLen;
|
|
|
|
if (StellarX::TextBoxmode::INPUT_MODE == mode)
|
|
{
|
|
std::vector<char> temp(maxCharLen + 1, '\0');
|
|
dirty = InputBox(temp.data(), (int)maxCharLen + 1, "输入框", NULL, text.c_str(), NULL, NULL, false);
|
|
if (dirty) text = temp.data();
|
|
consume = true;
|
|
}
|
|
else if (StellarX::TextBoxmode::READONLY_MODE == mode)
|
|
{
|
|
dirty = false;
|
|
InputBox(NULL, (int)maxCharLen, "输出框(输入无效!)", NULL, text.c_str(), NULL, NULL, false);
|
|
consume = true;
|
|
}
|
|
else if (StellarX::TextBoxmode::PASSWORD_MODE == mode)
|
|
{
|
|
std::vector<char> temp(maxCharLen + 1, '\0');
|
|
// 不记录明文,只记录长度变化
|
|
dirty = InputBox(temp.data(), (int)maxCharLen + 1, "输入框\n不可见输入,覆盖即可", NULL, NULL, NULL, NULL, false);
|
|
if (dirty) text = temp.data();
|
|
consume = true;
|
|
}
|
|
|
|
if (dirty)
|
|
{
|
|
SX_LOGI("TextBox") << SX_T("文本已更改: id=","text changed: id=") << id
|
|
<< " oldLen=" << oldLen << " newLen=" << text.size();
|
|
}
|
|
else
|
|
{
|
|
SX_LOGD("TextBox") << SX_T("文本无变化:id=","no change: id=") << id;
|
|
}
|
|
}
|
|
|
|
if (dirty)
|
|
requestRepaint(parent);
|
|
markEventVisualChanged(dirty);
|
|
|
|
if (click)
|
|
click = false;
|
|
|
|
return consume;
|
|
}
|
|
|
|
|
|
void TextBox::setMode(StellarX::TextBoxmode mode)
|
|
{
|
|
this->mode = mode;
|
|
this->dirty = true;
|
|
}
|
|
|
|
void TextBox::setMaxCharLen(size_t len)
|
|
{
|
|
if (len > 0)
|
|
maxCharLen = len;
|
|
this->dirty = true;
|
|
}
|
|
|
|
void TextBox::setTextBoxshape(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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
void TextBox::setTextBoxBorder(COLORREF color)
|
|
{
|
|
textBoxBorderClor = color;
|
|
this->dirty = true;
|
|
}
|
|
|
|
void TextBox::setTextBoxBk(COLORREF color)
|
|
{
|
|
textBoxBkClor = color;
|
|
this->dirty = true;
|
|
}
|
|
|
|
void TextBox::setText(std::string text)
|
|
{
|
|
if(text == this->text)
|
|
return; // 文本未改变,无需更新和重绘
|
|
if (text.size() > maxCharLen)
|
|
text = text.substr(0, maxCharLen);
|
|
this->text = text;
|
|
this->dirty = true; // 标记需要重绘,不论是否窗口图形上下文是否已初始化,等第一次绘制时由窗口真正调用 draw() 来重绘显示文本
|
|
|
|
//有父控件时请求父控件重绘,无父控件时直接重绘,确保文本更新后界面正确刷新显示
|
|
if (nullptr != parent)
|
|
{
|
|
//通过hasSnap是否持有有效快照,判断控件是否已经绘制过,避免在控件未绘制前/窗口图形上下文未初始化调用draw()导致的错误
|
|
if (hasSnap)
|
|
requestRepaint(parent);
|
|
}
|
|
else
|
|
if (hasSnap)
|
|
draw();
|
|
|
|
}
|
|
|
|
std::string TextBox::getText() const
|
|
{
|
|
return this->text;
|
|
}
|