优化编辑框控件的多行编辑和显示;

优化表格控件单元格的多行编辑和显示效果
This commit is contained in:
睿 安
2026-01-28 22:27:21 +08:00
parent 4fe4749826
commit 814f42120c
18 changed files with 78 additions and 43 deletions

2
.gitignore vendored
View File

@@ -5,5 +5,5 @@ _bin/
temp/
bin/
.vs/
demo/*
demo/
*.lib

View File

@@ -76,7 +76,7 @@ void mainForm::OnNotify(Control* sender, EventArgs& args)
int rowCount = tableView->GetRowCount(); //总行数
// 表格增加一行数据
tableView->InsertRow(rowCount);
tableView->SetRowData(rowCount, { L"uid" + std::to_wstring(rowCount), L"192.168.200.131" , L"默认"});
tableView->SetRowData(rowCount, { L"uid" + std::to_wstring(rowCount), L"192.168.200.131\n127.0.0" , L"", L"2026-02-25"});
// 获取表格指定位置数据
}
@@ -123,6 +123,7 @@ mainForm::mainForm() :LayeredWindow(1000, 750)
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
// 初始化设置表格各项属性
TableView* tableView = (TableView*)FindControl("tableViewAdmin"); //获取表格控件
if (tableView) {
@@ -130,12 +131,14 @@ mainForm::mainForm() :LayeredWindow(1000, 750)
tableView->SetColumnType(5, ezui::CellType::CheckBox);
tableView->SetColumnType(2, ezui::CellType::ComboBox);
tableView->SetColumnComboItems(2, { L"默认", L"禁止" , L"验机" });
//设置列宽
//std::vector<int> withs = {80, 100};
std::vector<int> withs = {60, 120, 50, 90, 85, 85, 100, 70, 70, 80, 80, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90};
for(auto i=0;i< withs.size();i++)
tableView->SetColumnWidth(i, withs[i]);
// 鼠标右键单击的回调
// 鼠标右键单击的回调
tableView->RightClick = [tableView](int row, int col) {
int pRow = tableView->GetHoverRow(); //当前行号
int pCol = tableView->GetHoverCol(); //当前列号
@@ -143,6 +146,7 @@ mainForm::mainForm() :LayeredWindow(1000, 750)
//std::cout << "单元格内容: " << celContent.ansi() << std::endl;
std::cout << "当前列宽: " << tableView->GetColumnWidth(pCol) << std::endl;
};
// 单元格编辑完成(编辑结束时触发,提供旧值与新值)
tableView->CellEditFinished = [](int row, int col, const UIString& oldValue, const UIString& newValue) {
std::cout << "单元格内容: " << newValue.ansi() << ", " << oldValue.ansi() << std::endl;

View File

@@ -11,7 +11,8 @@ namespace ezui {
VScrollBar m_vScrollbar;
int m_lastWidth = 0;
int m_lastHeight = 0;
bool m_multiLine = false;
bool m_autoWrap = false; // 是否允许文字超宽时自动换行
bool m_allowManualLineBreak = false; // 是否允许用户按Shift+Enter手动换行
std::wstring m_text;//文字
Size m_fontBox;
bool m_down = false;//是否具有焦点中
@@ -98,10 +99,12 @@ namespace ezui {
virtual ScrollBar* GetScrollBar()override;
//设置文字
void SetText(const UIString& text);
//是否多行显示
//是否多行显示兼容旧版仅调用autoWrap参数
bool IsMultiLine();
//设置是否多行显示
void SetMultiLine(bool multiLine);
// autoWrap: 文字超宽时是否自动换行
// allowManualLineBreak: 是否允许用户按Shift+Enter手动换行
void SetMultiLine(bool autoWrap, bool allowManualLineBreak = false);
//设置为是否只读
void SetReadOnly(bool bReadOnly);
//是否为只读

View File

@@ -11,7 +11,8 @@ namespace ezui {
VScrollBar m_vScrollbar;
int m_lastWidth = 0;
int m_lastHeight = 0;
bool m_multiLine = false;
bool m_autoWrap = false; // 是否允许文字超宽时自动换行
bool m_allowManualLineBreak = false; // 是否允许用户按Shift+Enter手动换行
std::wstring m_text;//文字
Size m_fontBox;
bool m_down = false;//是否具有焦点中
@@ -98,10 +99,12 @@ namespace ezui {
virtual ScrollBar* GetScrollBar()override;
//设置文字
void SetText(const UIString& text);
//是否多行显示
//是否多行显示兼容旧版仅调用autoWrap参数
bool IsMultiLine();
//设置是否多行显示
void SetMultiLine(bool multiLine);
// autoWrap: 文字超宽时是否自动换行
// allowManualLineBreak: 是否允许用户按Shift+Enter手动换行
void SetMultiLine(bool autoWrap, bool allowManualLineBreak = false);
//设置为是否只读
void SetReadOnly(bool bReadOnly);
//是否为只读

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -33,7 +33,8 @@ namespace ezui {
// 创建编辑控件(初始隐藏)
m_editBox = new TextBox();
m_editBox->SetVisible(false);
m_editBox->SetMultiLine(true);
// 设置为不自动换行但允许手动换行Shift+Enter
m_editBox->SetMultiLine(false, true);
// 为编辑框设置默认字体样式
m_editBox->Style.FontSize = m_cellFontSize;
m_editBox->Style.FontFamily = L"Microsoft YaHei";
@@ -45,6 +46,16 @@ namespace ezui {
if (m_editRow < (int)m_data.size() && m_editCol < (int)m_data[m_editRow].size()) {
m_data[m_editRow][m_editCol].Text = text;
UpdateRowHeight(m_editRow);
// 同步更新编辑框的高度以匹配新的行高
if (m_editCol < (int)m_columns.size() && m_editRow < (int)m_rowHeights.size()) {
int x = GetColumnX(m_editCol);
int y = GetRowY(m_editRow);
int width = m_columns[m_editCol].Width;
int height = m_rowHeights[m_editRow]; // 使用更新后的行高
m_editBox->SetRect(Rect(x, y, width, height));
}
if (CellValueChanged) {
CellValueChanged(m_editRow, m_editCol, text);
}
@@ -415,9 +426,10 @@ namespace ezui {
switch (colInfo.Type) {
case CellType::TextBox:
case CellType::ReadOnly: {
// 绘制文本
// 绘制文本(禁用自动换行,只根据实际换行符换行)
font.Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
TextLayout layout(cellData.Text.unicode(), font,
SizeF(cellRect.Width - 4, cellRect.Height - 2), TextAlign::MiddleLeft);
SizeF(cellRect.Width - 4, EZUI_FLOAT_MAX), TextAlign::TopLeft);
g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y + 1));
break;
}
@@ -644,13 +656,15 @@ namespace ezui {
return 1;
}
Font font(m_cellFontFamily, m_cellFontSize);
TextLayout layout(text.unicode(), font, SizeF(width, EZUI_FLOAT_MAX));
Size box = layout.GetFontBox();
int fontHeight = m_cellFontSize + 4;
int lines = (box.Height + fontHeight - 1) / fontHeight;
return (std::max)(1, lines);
// 由于单元格设置为不自动换行,只根据实际换行符计算行数
std::wstring wtext = text.unicode();
int lines = 1;
for (wchar_t c : wtext) {
if (c == L'\n') {
lines++;
}
}
return lines;
}
void TableView::RefreshScrollBars() {

View File

@@ -235,8 +235,8 @@ namespace ezui {
std::wstring wBuf;
bool bRet = ezui::GetClipboardData(&wBuf, Hwnd());
UIString u8Str(wBuf);
if (!m_multiLine) {
//行编辑框不允许有换行符
if (!m_allowManualLineBreak) {
//不允许手动换行时不允许有换行符
ui_text::Replace(&u8Str, "\r", "");
ui_text::Replace(&u8Str, "\n", "");
}
@@ -263,8 +263,8 @@ namespace ezui {
WPARAM wParam = arg.wParam;
LPARAM lParam = arg.lParam;
//判断是否按下shift+enter
if (IsMultiLine() && (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_RETURN) {
//判断是否按下shift+enter(仅当允许手动换行时)
if (m_allowManualLineBreak && (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_RETURN) {
InsertUnicode(L"\n");//插入换行符
Analysis();//分析字符串
Invalidate();//刷新
@@ -374,7 +374,7 @@ namespace ezui {
int innerW = std::max(0, Width() - (m_padLeft + m_padRight));
int innerH = std::max(0, Height() - (m_padTop + m_padBottom));
if (!m_multiLine) {//单行编辑框
if (!m_autoWrap && !m_allowManualLineBreak) {//单行编辑框(两者都不允许)
m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
bool bAlignLeft = (int(this->TextAlign) & int(HAlign::Left));
float width = bAlignLeft ? EZUI_FLOAT_MAX : (float)innerW;
@@ -394,8 +394,13 @@ namespace ezui {
m_scrollX = innerW - m_fontBox.Width;
}
}
else {//多行编辑框
m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
else {//多行编辑框(至少允许一种换行方式)
// 根据 m_autoWrap 决定是否启用自动换行
if (m_autoWrap) {
m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
} else {
m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
}
m_textLayout = new TextLayout(*drawText, *m_font, SizeF{ (float)innerW, EZUI_FLOAT_MAX }, TextAlign::TopLeft);
m_fontBox = m_textLayout->GetFontBox();
}
@@ -403,13 +408,13 @@ namespace ezui {
delete drawText;
}
// 内容尺寸:多行时加入 padding 以保证最后一行不被裁掉,单行保持原逻辑
if (m_multiLine) {
if (m_autoWrap || m_allowManualLineBreak) {
this->SetContentSize({ m_fontBox.Width + m_padLeft + m_padRight, m_fontBox.Height + m_padTop + m_padBottom });
}
else {
this->SetContentSize({ m_fontBox.Width , m_fontBox.Height });
}
if (m_multiLine) {
if (m_autoWrap || m_allowManualLineBreak) {
this->GetScrollBar()->RefreshScroll();
}
BuildCare();
@@ -431,7 +436,7 @@ namespace ezui {
m_careRect.Height = m_textLayout->GetFontHeight();
m_careRect.Width = 1 * this->GetScale();
if (!m_multiLine) {
if (!m_autoWrap && !m_allowManualLineBreak) {
// 使光标保持在可视内区(考虑 padding
int innerW = std::max(0, Width() - (m_padLeft + m_padRight));
int caretDrawX = m_careRect.X + m_scrollX; // 布局坐标 + 滚动
@@ -487,7 +492,7 @@ namespace ezui {
void TextBox::OnMouseWheel(const MouseEventArgs& arg)
{
__super::OnMouseWheel(arg);
if (!m_multiLine) {//单行
if (!m_autoWrap && !m_allowManualLineBreak) {//单行
int innerW = std::max(0, Width() - (m_padLeft + m_padRight));
int textWidth = m_fontBox.Width;
if (arg.ZDelta > 0 && textWidth > innerW) {
@@ -522,15 +527,15 @@ namespace ezui {
m_scrollX = 0;
m_scrollY = 0;
m_selectRects.clear();
if (!m_multiLine && Height() != m_lastHeight) {
if ((!m_autoWrap && !m_allowManualLineBreak) && Height() != m_lastHeight) {
m_lastHeight = Height();
Analysis();
}
if (m_multiLine && Width() != m_lastWidth) {
if ((m_autoWrap || m_allowManualLineBreak) && Width() != m_lastWidth) {
m_lastWidth = Width();
Analysis();
}
this->SetContentSize({ m_fontBox.Width ,m_multiLine ? m_fontBox.Height : Height() });
this->SetContentSize({ m_fontBox.Width ,(m_autoWrap || m_allowManualLineBreak) ? m_fontBox.Height : Height() });
this->GetScrollBar()->RefreshScroll();
this->EndLayout();
}
@@ -555,7 +560,7 @@ namespace ezui {
BuildSelectedRect();
if (!m_multiLine) {//单行
if (!m_autoWrap && !m_allowManualLineBreak) {//单行
// 计算去掉左 padding 的鼠标相对文本区域坐标
int innerW = std::max(0, Width() - (m_padLeft + m_padRight));
int textWidth = m_fontBox.Width;
@@ -620,12 +625,13 @@ namespace ezui {
}
bool TextBox::IsMultiLine()
{
return m_multiLine;
return m_autoWrap || m_allowManualLineBreak;
}
void TextBox::SetMultiLine(bool multiLine)
void TextBox::SetMultiLine(bool autoWrap, bool allowManualLineBreak)
{
if (this->m_multiLine != multiLine) {
this->m_multiLine = multiLine;
if (this->m_autoWrap != autoWrap || this->m_allowManualLineBreak != allowManualLineBreak) {
this->m_autoWrap = autoWrap;
this->m_allowManualLineBreak = allowManualLineBreak;
Analysis();
}
}
@@ -751,11 +757,14 @@ namespace ezui {
}
if (key == "multiline") {
if (value == "true") {
this->m_multiLine = true;
// 兼容旧版:multiline=true 表示自动换行+手动换行
this->m_autoWrap = true;
this->m_allowManualLineBreak = true;
break;
}
if (value == "false") {
this->m_multiLine = false;
this->m_autoWrap = false;
this->m_allowManualLineBreak = false;
break;
}
}
@@ -786,7 +795,9 @@ namespace ezui {
placeholderColor.SetA(fontColor.GetA() * 0.6);
e.Graphics.SetColor(placeholderColor);
RectF ph((float)m_padLeft, (float)m_padTop, (float)Width() - (float)(m_padLeft + m_padRight), (float)Height() - (float)(m_padTop + m_padBottom));
e.Graphics.DrawString(m_placeholder, ph, m_multiLine ? TextAlign::TopLeft : this->TextAlign);
Font phFont(fontFamily, fontSize);
e.Graphics.SetFont(phFont);
e.Graphics.DrawString(m_placeholder, ph, (m_autoWrap || m_allowManualLineBreak) ? TextAlign::TopLeft : this->TextAlign);
}
}