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

优化表格控件单元格的多行编辑和显示效果
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/ temp/
bin/ bin/
.vs/ .vs/
demo/* demo/
*.lib *.lib

View File

@@ -76,7 +76,7 @@ void mainForm::OnNotify(Control* sender, EventArgs& args)
int rowCount = tableView->GetRowCount(); //总行数 int rowCount = tableView->GetRowCount(); //总行数
// 表格增加一行数据 // 表格增加一行数据
tableView->InsertRow(rowCount); 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", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr); freopen_s(&fp, "CONOUT$", "w", stderr);
// 初始化设置表格各项属性 // 初始化设置表格各项属性
TableView* tableView = (TableView*)FindControl("tableViewAdmin"); //获取表格控件 TableView* tableView = (TableView*)FindControl("tableViewAdmin"); //获取表格控件
if (tableView) { if (tableView) {
@@ -130,12 +131,14 @@ mainForm::mainForm() :LayeredWindow(1000, 750)
tableView->SetColumnType(5, ezui::CellType::CheckBox); tableView->SetColumnType(5, ezui::CellType::CheckBox);
tableView->SetColumnType(2, ezui::CellType::ComboBox); tableView->SetColumnType(2, ezui::CellType::ComboBox);
tableView->SetColumnComboItems(2, { L"默认", L"禁止" , L"验机" }); 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) { tableView->RightClick = [tableView](int row, int col) {
int pRow = tableView->GetHoverRow(); //当前行号 int pRow = tableView->GetHoverRow(); //当前行号
int pCol = tableView->GetHoverCol(); //当前列号 int pCol = tableView->GetHoverCol(); //当前列号
@@ -143,6 +146,7 @@ mainForm::mainForm() :LayeredWindow(1000, 750)
//std::cout << "单元格内容: " << celContent.ansi() << std::endl; //std::cout << "单元格内容: " << celContent.ansi() << std::endl;
std::cout << "当前列宽: " << tableView->GetColumnWidth(pCol) << std::endl; std::cout << "当前列宽: " << tableView->GetColumnWidth(pCol) << std::endl;
}; };
// 单元格编辑完成(编辑结束时触发,提供旧值与新值) // 单元格编辑完成(编辑结束时触发,提供旧值与新值)
tableView->CellEditFinished = [](int row, int col, const UIString& oldValue, const UIString& newValue) { tableView->CellEditFinished = [](int row, int col, const UIString& oldValue, const UIString& newValue) {
std::cout << "单元格内容: " << newValue.ansi() << ", " << oldValue.ansi() << std::endl; std::cout << "单元格内容: " << newValue.ansi() << ", " << oldValue.ansi() << std::endl;

View File

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

View File

@@ -11,7 +11,8 @@ namespace ezui {
VScrollBar m_vScrollbar; VScrollBar m_vScrollbar;
int m_lastWidth = 0; int m_lastWidth = 0;
int m_lastHeight = 0; int m_lastHeight = 0;
bool m_multiLine = false; bool m_autoWrap = false; // 是否允许文字超宽时自动换行
bool m_allowManualLineBreak = false; // 是否允许用户按Shift+Enter手动换行
std::wstring m_text;//文字 std::wstring m_text;//文字
Size m_fontBox; Size m_fontBox;
bool m_down = false;//是否具有焦点中 bool m_down = false;//是否具有焦点中
@@ -98,10 +99,12 @@ namespace ezui {
virtual ScrollBar* GetScrollBar()override; virtual ScrollBar* GetScrollBar()override;
//设置文字 //设置文字
void SetText(const UIString& text); void SetText(const UIString& text);
//是否多行显示 //是否多行显示兼容旧版仅调用autoWrap参数
bool IsMultiLine(); bool IsMultiLine();
//设置是否多行显示 //设置是否多行显示
void SetMultiLine(bool multiLine); // autoWrap: 文字超宽时是否自动换行
// allowManualLineBreak: 是否允许用户按Shift+Enter手动换行
void SetMultiLine(bool autoWrap, bool allowManualLineBreak = false);
//设置为是否只读 //设置为是否只读
void SetReadOnly(bool bReadOnly); 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 = new TextBox();
m_editBox->SetVisible(false); m_editBox->SetVisible(false);
m_editBox->SetMultiLine(true); // 设置为不自动换行但允许手动换行Shift+Enter
m_editBox->SetMultiLine(false, true);
// 为编辑框设置默认字体样式 // 为编辑框设置默认字体样式
m_editBox->Style.FontSize = m_cellFontSize; m_editBox->Style.FontSize = m_cellFontSize;
m_editBox->Style.FontFamily = L"Microsoft YaHei"; 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()) { if (m_editRow < (int)m_data.size() && m_editCol < (int)m_data[m_editRow].size()) {
m_data[m_editRow][m_editCol].Text = text; m_data[m_editRow][m_editCol].Text = text;
UpdateRowHeight(m_editRow); 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) { if (CellValueChanged) {
CellValueChanged(m_editRow, m_editCol, text); CellValueChanged(m_editRow, m_editCol, text);
} }
@@ -415,9 +426,10 @@ namespace ezui {
switch (colInfo.Type) { switch (colInfo.Type) {
case CellType::TextBox: case CellType::TextBox:
case CellType::ReadOnly: { case CellType::ReadOnly: {
// 绘制文本 // 绘制文本(禁用自动换行,只根据实际换行符换行)
font.Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
TextLayout layout(cellData.Text.unicode(), font, 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)); g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y + 1));
break; break;
} }
@@ -644,13 +656,15 @@ namespace ezui {
return 1; return 1;
} }
Font font(m_cellFontFamily, m_cellFontSize); // 由于单元格设置为不自动换行,只根据实际换行符计算行数
TextLayout layout(text.unicode(), font, SizeF(width, EZUI_FLOAT_MAX)); std::wstring wtext = text.unicode();
Size box = layout.GetFontBox(); int lines = 1;
for (wchar_t c : wtext) {
int fontHeight = m_cellFontSize + 4; if (c == L'\n') {
int lines = (box.Height + fontHeight - 1) / fontHeight; lines++;
return (std::max)(1, lines); }
}
return lines;
} }
void TableView::RefreshScrollBars() { void TableView::RefreshScrollBars() {

View File

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