Files
EzUI/sources/TableView.cpp
2026-02-20 15:02:30 +08:00

2311 lines
53 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "TableView.h"
namespace ezui
{
TableView::TableView(Object *parentObject) : Control(parentObject)
{
Init();
}
TableView::~TableView()
{
EndEdit(false);
// 注意m_editBox、m_editCombo、m_editCheck 作为子控件,由父控件自动管理
// 不需要手动 delete否则会导致双重释放
m_editBox = nullptr;
m_editCombo = nullptr;
m_editCheck = nullptr;
}
void TableView::Init()
{
// 初始化垂直滚动条
m_vScrollBar.SetWidth(10);
m_vScrollBar.Parent = this;
m_vScrollBar.OffsetCallback = [this](int offset)
{
this->OffsetY(offset);
};
// 初始化水平滚动条
m_hScrollBar.SetHeight(10);
m_hScrollBar.Parent = this;
m_hScrollBar.OffsetCallback = [this](int offset)
{
this->OffsetX(offset);
};
// 创建编辑控件(初始隐藏)
m_editBox = new TextBox();
m_editBox->SetVisible(false);
// 设置为不自动换行但允许手动换行Shift+Enter
m_editBox->SetMultiLine(false, true);
// 为编辑框设置默认字体样式
m_editBox->Style.FontSize = m_cellFontSize;
m_editBox->Style.FontFamily = L"Microsoft YaHei";
// 添加到子控件列表,这样才能正确处理事件和绘制
this->Add(m_editBox);
m_editBox->TextChanged = [this](const UIString &text)
{
if (m_editing && m_editRow >= 0 && m_editCol >= 0)
{
// 实时更新数据
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);
// }
}
}
};
}
int TableView::GetContentWidth() const
{
int width = m_firstColumnWidth;
for (const auto &col : m_columns)
{
width += col.Width;
}
return width;
}
int TableView::GetContentHeight() const
{
int height = m_headerHeight;
for (int h : m_rowHeights)
{
height += h;
}
return height;
}
int TableView::GetColumnX(int colIndex) const
{
int x = m_firstColumnWidth - m_scrollOffsetX;
for (int i = 0; i < colIndex && i < (int)m_columns.size(); ++i)
{
x += m_columns[i].Width;
}
return x;
}
int TableView::GetRowY(int rowIndex) const
{
int y = m_headerHeight - m_scrollOffsetY;
for (int i = 0; i < rowIndex && i < (int)m_rowHeights.size(); ++i)
{
y += m_rowHeights[i];
}
return y;
}
bool TableView::HitTestCell(const Point &pt, int *outRow, int *outCol) const
{
// 检查是否在表头区域
if (pt.Y < m_headerHeight)
{
*outRow = -1;
// 检查是否在第一列
if (pt.X < m_firstColumnWidth)
{
*outCol = -1;
return true;
}
// 检查是否在数据列
int x = m_firstColumnWidth;
for (int i = 0; i < (int)m_columns.size(); ++i)
{
if (pt.X >= x - m_scrollOffsetX && pt.X < x - m_scrollOffsetX + m_columns[i].Width)
{
*outCol = i;
return true;
}
x += m_columns[i].Width;
}
return false;
}
// 检查行
int y = m_headerHeight;
for (int row = 0; row < (int)m_rowHeights.size(); ++row)
{
int rowY = y - m_scrollOffsetY;
if (pt.Y >= rowY && pt.Y < rowY + m_rowHeights[row])
{
*outRow = row;
// 检查列
if (pt.X < m_firstColumnWidth)
{
*outCol = -1; // 第一列
return true;
}
int x = m_firstColumnWidth;
for (int col = 0; col < (int)m_columns.size(); ++col)
{
int colX = x - m_scrollOffsetX;
if (pt.X >= colX && pt.X < colX + m_columns[col].Width)
{
*outCol = col;
return true;
}
x += m_columns[col].Width;
}
return false;
}
y += m_rowHeights[row];
}
*outRow = -1;
*outCol = -1;
return false;
}
int TableView::HitTestColumnBorder(const Point &pt) const
{
const int borderHitWidth = 5; // 边界检测宽度
// 只在表头区域检测
if (pt.Y >= m_headerHeight)
{
return -2; // 返回-2表示不在表头区域不应该显示拖动光标
}
// 检测第一列右边界
int x = m_firstColumnWidth;
if (std::abs(pt.X - x) <= borderHitWidth)
{
return -1; // 返回-1表示第一列边界特殊处理
}
// 检测数据列边界
for (int i = 0; i < (int)m_columns.size(); ++i)
{
x += m_columns[i].Width;
int borderX = x - m_scrollOffsetX;
if (std::abs(pt.X - borderX) <= borderHitWidth)
{
return i;
}
}
return -2; // 未命中任何边界
}
bool TableView::HitTestVScrollBar(const Point &pt)
{
if (!m_vScrollBar.IsVisible())
{
return false;
}
Rect scrollRect = m_vScrollBar.GetRect();
return scrollRect.Contains(pt);
}
bool TableView::HitTestHScrollBar(const Point &pt)
{
if (!m_hScrollBar.IsVisible())
{
return false;
}
Rect scrollRect = m_hScrollBar.GetRect();
return scrollRect.Contains(pt);
}
void TableView::DrawHeader(PaintEventArgs &args)
{
auto &g = args.Graphics;
// 绘制表头背景
Rect headerRect(0, 0, Width(), m_headerHeight);
g.SetColor(m_headerBackColor);
g.FillRectangle(RectF(headerRect));
// 绘制第一列表头
Rect firstColRect(0, 0, m_firstColumnWidth, m_headerHeight);
g.SetColor(m_headerBackColor);
g.FillRectangle(RectF(firstColRect));
// 如果第一列是 CheckBox 类型,绘制全选复选框
if (m_firstColumnType == FirstColumnType::CheckBox)
{
int checkSize = (std::min)(m_headerHeight - 8, 16);
int checkX = (m_firstColumnWidth - checkSize) / 2;
int checkY = (m_headerHeight - checkSize) / 2;
Rect checkRect(checkX, checkY, checkSize, checkSize);
// 绘制复选框边框
g.SetColor(m_cellBorderColor);
g.DrawRectangle(RectF(checkRect));
// 如果全选,绘制勾选标记
if (m_headerSelectAll)
{
g.SetColor(m_headerForeColor);
// 简单的勾选线
PointF p1(checkX + 3, checkY + checkSize / 2);
PointF p2(checkX + checkSize / 3, checkY + checkSize - 4);
PointF p3(checkX + checkSize - 3, checkY + 4);
g.DrawLine(p1, p2);
g.DrawLine(p2, p3);
}
}
else if (m_firstColumnType == FirstColumnType::Index)
{
// 绘制 "#" 或序号标题
Font font(m_cellFontFamily, m_cellFontSize);
g.SetFont(font);
g.SetColor(m_headerForeColor);
TextLayout layout(L"#", font, SizeF(m_firstColumnWidth, m_headerHeight), TextAlign::MiddleCenter);
g.DrawTextLayout(layout, PointF(0, 0));
}
// 绘制第一列边框
if (m_cellBorderStyle != StrokeStyle::None)
{
g.SetColor(m_cellBorderColor);
g.DrawRectangle(RectF(firstColRect));
}
int headerClipWidth = Width() - m_firstColumnWidth;
if (headerClipWidth > 0)
{
g.PushAxisAlignedClip(RectF((float)m_firstColumnWidth, 0.0f, (float)headerClipWidth, (float)m_headerHeight));
}
// 绘制数据列表头
int x = m_firstColumnWidth - m_scrollOffsetX;
for (int i = 0; i < (int)m_columns.size(); ++i)
{
const auto &col = m_columns[i];
// 裁剪到可见区域
if (x + col.Width <= m_firstColumnWidth || x >= Width())
{
x += col.Width;
continue;
}
Rect colRect(x, 0, col.Width, m_headerHeight);
// 绘制表头文字
Font font(m_cellFontFamily, m_cellFontSize);
g.SetFont(font);
g.SetColor(m_headerForeColor);
// 添加排序指示器
std::wstring headerText = col.HeaderText.unicode();
if (col.CurrentSort == SortOrder::Ascending)
{
headerText = headerText + L"";
}
else if (col.CurrentSort == SortOrder::Descending)
{
headerText = headerText + L"";
}
TextLayout layout(headerText, font, SizeF(col.Width - 4, m_headerHeight), TextAlign::MiddleCenter);
g.DrawTextLayout(layout, PointF(x + 2, 0));
// 绘制边框
if (m_cellBorderStyle != StrokeStyle::None)
{
g.SetColor(m_cellBorderColor);
g.DrawRectangle(RectF(colRect));
}
x += col.Width;
}
if (headerClipWidth > 0)
{
g.PopAxisAlignedClip();
}
}
void TableView::DrawCells(PaintEventArgs &args)
{
auto &g = args.Graphics;
int startY = m_headerHeight;
for (int row = 0; row < (int)m_data.size(); ++row)
{
int rowY = startY + GetRowY(row) - m_headerHeight;
int rowHeight = m_rowHeights[row];
// 跳过不可见行
if (rowY + rowHeight <= m_headerHeight || rowY >= Height())
{
continue;
}
// 绘制第一列
Rect firstColRect(0, rowY, m_firstColumnWidth, rowHeight);
DrawFirstColumn(args, row, firstColRect);
int rowClipWidth = Width() - m_firstColumnWidth;
if (rowClipWidth > 0)
{
g.PushAxisAlignedClip(RectF((float)m_firstColumnWidth, (float)rowY, (float)rowClipWidth, (float)rowHeight));
}
// 绘制数据列
int x = m_firstColumnWidth - m_scrollOffsetX;
for (int col = 0; col < (int)m_columns.size(); ++col)
{
int colWidth = m_columns[col].Width;
// 跳过不可见列
if (x + colWidth <= m_firstColumnWidth || x >= Width())
{
x += colWidth;
continue;
}
Rect cellRect(x, rowY, colWidth, rowHeight);
DrawCell(args, row, col, cellRect);
x += colWidth;
}
if (rowClipWidth > 0)
{
g.PopAxisAlignedClip();
}
}
}
void TableView::DrawFirstColumn(PaintEventArgs &args, int row, const Rect &cellRect)
{
auto &g = args.Graphics;
// 获取行选中状态背景色
Color backColor = m_cellBackColor;
if (m_firstColumnType == FirstColumnType::CheckBox && row < (int)m_rowChecked.size() && m_rowChecked[row])
{
backColor = SelectedRowBackColor;
}
// 如果第一列不是 CheckBox 且当前行被选中,使用选中行背景色
if (m_firstColumnType != FirstColumnType::CheckBox && row == m_selectedRow)
{
backColor = SelectedRowBackColor;
}
// 绘制背景
g.SetColor(backColor);
g.FillRectangle(RectF(cellRect));
// 根据第一列类型绘制内容
if (m_firstColumnType == FirstColumnType::Index)
{
// 绘制序号
Font font(m_cellFontFamily, m_cellFontSize);
g.SetFont(font);
g.SetColor(m_cellForeColor);
std::wstring indexText = std::to_wstring(row + 1); // 从1开始
TextLayout layout(indexText, font, SizeF(m_firstColumnWidth - 4, cellRect.Height), TextAlign::MiddleCenter);
g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y));
}
else if (m_firstColumnType == FirstColumnType::CheckBox)
{
// 绘制复选框
int checkSize = (std::min)(cellRect.Height - 8, 16);
int checkX = cellRect.X + (m_firstColumnWidth - checkSize) / 2;
int checkY = cellRect.Y + (cellRect.Height - checkSize) / 2;
Rect checkRect(checkX, checkY, checkSize, checkSize);
g.SetColor(m_cellBorderColor);
g.DrawRectangle(RectF(checkRect));
if (row < (int)m_rowChecked.size() && m_rowChecked[row])
{
g.SetColor(m_cellForeColor);
PointF p1(checkX + 3, checkY + checkSize / 2);
PointF p2(checkX + checkSize / 3, checkY + checkSize - 4);
PointF p3(checkX + checkSize - 3, checkY + 4);
g.DrawLine(p1, p2);
g.DrawLine(p2, p3);
}
}
// TextBox 类型第一列留空
// 绘制边框
if (m_cellBorderStyle != StrokeStyle::None)
{
g.SetColor(m_cellBorderColor);
g.DrawRectangle(RectF(cellRect));
}
}
void TableView::DrawCell(PaintEventArgs &args, int row, int col, const Rect &cellRect)
{
auto &g = args.Graphics;
if (row >= (int)m_data.size() || col >= (int)m_data[row].size())
{
return;
}
const auto &cellData = m_data[row][col];
const auto &colInfo = m_columns[col];
// 确定背景色
Color backColor = m_cellBackColor;
// 如果第一列是 CheckBox 且选中,使用选中行背景色
if (m_firstColumnType == FirstColumnType::CheckBox && row < (int)m_rowChecked.size() && m_rowChecked[row])
{
backColor = SelectedRowBackColor;
}
// 如果第一列不是 CheckBox 且当前行被选中,使用选中行背景色
if (m_firstColumnType != FirstColumnType::CheckBox && row == m_selectedRow)
{
backColor = SelectedRowBackColor;
}
// 如果单元格有独立背景色,使用独立设置(优先级高)
if (cellData.Style.HasBackColor)
{
backColor = cellData.Style.BackColor;
}
// 如果正在编辑此单元格,跳过内容绘制(只绘制背景),让 TextBox 绘制内容
bool isEditing = m_editing && m_editRow == row && m_editCol == col;
// 绘制背景(编辑时也需要绘制,但使用白色以匹配 TextBox
if (isEditing)
{
g.SetColor(Color::White);
}
else
{
g.SetColor(backColor);
}
g.FillRectangle(RectF(cellRect));
// 如果正在编辑此单元格,跳过内容绘制
if (isEditing)
{
return;
}
// 裁剪到单元格区域,避免内容溢出
g.PushAxisAlignedClip(RectF(cellRect));
// 确定前景色
Color foreColor = m_cellForeColor;
if (cellData.Style.HasForeColor)
{
foreColor = cellData.Style.ForeColor;
}
// 根据单元格类型绘制内容
Font font(m_cellFontFamily, m_cellFontSize);
g.SetFont(font);
g.SetColor(foreColor);
// 获取对齐方式
TextAlign textAlign = GetCellTextAlign(row, col);
switch (colInfo.Type)
{
case CellType::TextBox:
case CellType::ReadOnly:
{
// 绘制文本(禁用自动换行,只根据实际换行符换行)
font.Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
// 使用单元格实际高度这样Middle和Bottom对齐才能正确显示
TextLayout layout(cellData.Text.unicode(), font,
SizeF(cellRect.Width - 4, cellRect.Height - 2), textAlign);
g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y + 1));
break;
}
case CellType::CheckBox:
{
// 绘制复选框
int checkSize = (std::min)(cellRect.Height - 8, 16);
int checkX = cellRect.X + (cellRect.Width - checkSize) / 2;
int checkY = cellRect.Y + (cellRect.Height - checkSize) / 2;
Rect checkRect(checkX, checkY, checkSize, checkSize);
g.SetColor(m_cellBorderColor);
g.DrawRectangle(RectF(checkRect));
if (cellData.Checked)
{
g.SetColor(foreColor);
PointF p1(checkX + 3, checkY + checkSize / 2);
PointF p2(checkX + checkSize / 3, checkY + checkSize - 4);
PointF p3(checkX + checkSize - 3, checkY + 4);
g.DrawLine(p1, p2);
g.DrawLine(p2, p3);
}
break;
}
case CellType::ComboBox:
{
// 绘制下拉框文本
UIString displayText;
if (cellData.ComboIndex >= 0 && cellData.ComboIndex < (int)colInfo.ComboItems.size())
{
displayText = colInfo.ComboItems[cellData.ComboIndex];
}
TextLayout layout(displayText.unicode(), font,
SizeF(cellRect.Width - 20, cellRect.Height - 2), TextAlign::MiddleLeft);
g.DrawTextLayout(layout, PointF(cellRect.X + 2, cellRect.Y + 1));
// 绘制下拉箭头
int arrowSize = 8;
int arrowX = cellRect.GetRight() - arrowSize - 4;
int arrowY = cellRect.Y + (cellRect.Height - arrowSize / 2) / 2;
PointF ap1(arrowX, arrowY);
PointF ap2(arrowX + arrowSize, arrowY);
PointF ap3(arrowX + arrowSize / 2, arrowY + arrowSize / 2);
g.SetColor(foreColor);
g.DrawLine(ap1, ap3);
g.DrawLine(ap2, ap3);
break;
}
}
g.PopAxisAlignedClip();
// 绘制边框
StrokeStyle borderStyle = m_cellBorderStyle;
Color borderColor = m_cellBorderColor;
if (cellData.Style.HasBorderStyle)
{
borderStyle = cellData.Style.BorderStyle;
}
if (cellData.Style.HasBorderColor)
{
borderColor = cellData.Style.BorderColor;
}
if (borderStyle != StrokeStyle::None)
{
g.SetColor(borderColor);
// TODO: 支持虚线样式
g.DrawRectangle(RectF(cellRect));
}
}
void TableView::BeginEdit(int row, int col)
{
if (row < 0 || col < 0 || row >= (int)m_data.size() || col >= (int)m_columns.size())
{
return;
}
const auto &colInfo = m_columns[col];
// ReadOnly 类型不能编辑
if (colInfo.Type == CellType::ReadOnly)
{
return;
}
EndEdit(true); // 结束之前的编辑
// 保存编辑前的原始值
if (colInfo.Type == CellType::TextBox)
{
m_editOriginalValue = m_data[row][col].Text;
}
else if (colInfo.Type == CellType::CheckBox)
{
m_editOriginalValue = m_data[row][col].Checked ? L"true" : L"false";
}
else if (colInfo.Type == CellType::ComboBox)
{
int idx = m_data[row][col].ComboIndex;
if (idx >= 0 && idx < (int)colInfo.ComboItems.size())
{
m_editOriginalValue = colInfo.ComboItems[idx];
}
else
{
m_editOriginalValue = L"";
}
}
m_editing = true;
m_editRow = row;
m_editCol = col;
// 计算单元格位置
int x = GetColumnX(col);
int y = GetRowY(row);
int width = colInfo.Width;
int height = m_rowHeights[row];
Rect editRect(x, y, width, height);
switch (colInfo.Type)
{
case CellType::TextBox:
{
// 确保 TextBox 字体与单元格字体一致
m_editBox->Style.FontSize = m_cellFontSize;
m_editBox->Style.FontFamily = m_cellFontFamily;
m_editBox->SetRect(editRect);
m_editBox->SetText(m_data[row][col].Text);
m_editBox->SetVisible(true);
// 确保 TextBox 获得正确的窗口句柄(从 TableView 继承)
m_editBox->SetHwnd(this->Hwnd());
// 触发焦点事件(这会启动光标闪烁计时器)
m_editBox->SendEvent(FocusEventArgs(m_editBox));
Invalidate();
break;
}
case CellType::CheckBox:
{
// 直接切换状态
m_data[row][col].Checked = !m_data[row][col].Checked;
m_editing = false;
m_editRow = -1;
m_editCol = -1;
if (CellValueChanged)
{
CellValueChanged(row, col, m_data[row][col].Checked ? L"true" : L"false");
}
Invalidate();
break;
}
case CellType::ComboBox:
{
// TODO: 显示下拉列表
// 暂时简化处理,循环选择
int nextIndex = (m_data[row][col].ComboIndex + 1) % (std::max)(1, (int)colInfo.ComboItems.size());
m_data[row][col].ComboIndex = nextIndex;
m_editing = false;
m_editRow = -1;
m_editCol = -1;
UIString newValue;
if (nextIndex >= 0 && nextIndex < (int)colInfo.ComboItems.size())
{
newValue = colInfo.ComboItems[nextIndex];
if (CellValueChanged)
{
CellValueChanged(row, col, newValue);
}
}
Invalidate();
break;
}
default:
m_editing = false;
m_editRow = -1;
m_editCol = -1;
break;
}
}
void TableView::EndEdit(bool save)
{
if (!m_editing)
{
return;
}
int editRow = m_editRow;
int editCol = m_editCol;
UIString newValue;
if (save && editRow >= 0 && editCol >= 0)
{
if (editRow < (int)m_data.size() && editCol < (int)m_data[editRow].size())
{
const auto &colInfo = m_columns[editCol];
if (colInfo.Type == CellType::TextBox)
{
newValue = m_editBox->GetText();
m_data[editRow][editCol].Text = newValue;
UpdateRowHeight(editRow);
// 触发编辑完成回调
// if (CellEditFinished && newValue != m_editOriginalValue)
// {
// CellEditFinished(editRow, editCol, m_editOriginalValue, newValue);
// }
// 触发内容更改回调
if (CellValueChanged && newValue != m_editOriginalValue)
{
CellValueChanged(editRow, editCol, newValue);
}
}
}
}
m_editBox->SetVisible(false);
m_editing = false;
m_editRow = -1;
m_editCol = -1;
Invalidate();
}
void TableView::UpdateRowHeight(int row)
{
if (row < 0 || row >= (int)m_data.size())
{
return;
}
int maxHeight = m_defaultRowHeight;
// 遍历该行所有单元格,计算最大需要的高度
for (int col = 0; col < (int)m_data[row].size() && col < (int)m_columns.size(); ++col)
{
const auto &cellData = m_data[row][col];
const auto &colInfo = m_columns[col];
if (colInfo.Type == CellType::TextBox || colInfo.Type == CellType::ReadOnly)
{
int lines = CalculateTextLines(cellData.Text, colInfo.Width - 4);
int neededHeight = lines * (m_cellFontSize + 4) + 8;
if (neededHeight > maxHeight)
{
maxHeight = neededHeight;
}
}
}
if (row < (int)m_rowHeights.size())
{
m_rowHeights[row] = maxHeight;
}
RefreshScrollBars();
Invalidate();
}
int TableView::CalculateTextLines(const UIString &text, int width) const
{
if (text.empty() || width <= 0)
{
return 1;
}
// 由于单元格设置为不自动换行,只根据实际换行符计算行数
std::wstring wtext = text.unicode();
int lines = 1;
for (wchar_t c : wtext)
{
if (c == L'\n')
{
lines++;
}
}
return lines;
}
void TableView::RefreshScrollBars()
{
int contentWidth = GetContentWidth();
int contentHeight = GetContentHeight();
// 可视区域大小(初步计算,不考虑滚动条)
int viewWidth = Width();
int viewHeight = Height() - m_headerHeight;
// 判断是否需要滚动条
bool needVScroll = (contentHeight - m_headerHeight) > viewHeight;
bool needHScroll = contentWidth > viewWidth;
// 如果需要垂直滚动条,水平可视区域要减去滚动条宽度
if (needVScroll)
{
viewWidth -= m_vScrollBar.Width();
// 重新判断是否需要水平滚动条
needHScroll = contentWidth > viewWidth;
}
// 如果需要水平滚动条,垂直可视区域要减去滚动条高度
if (needHScroll)
{
viewHeight -= m_hScrollBar.Height();
// 重新判断是否需要垂直滚动条
if (!needVScroll)
{
needVScroll = (contentHeight - m_headerHeight) > viewHeight;
if (needVScroll)
{
viewWidth -= m_vScrollBar.Width();
}
}
}
// 设置垂直滚动条
if (needVScroll)
{
m_vScrollBar.SetVisible(true);
int vScrollHeight = Height() - m_headerHeight - (needHScroll ? m_hScrollBar.Height() : 0);
m_vScrollBar.SetRect(Rect(Width() - m_vScrollBar.Width(), m_headerHeight,
m_vScrollBar.Width(), vScrollHeight));
}
else
{
m_vScrollBar.SetVisible(false);
m_scrollOffsetY = 0;
}
// 设置水平滚动条
if (needHScroll)
{
m_hScrollBar.SetVisible(true);
int hScrollWidth = Width() - m_firstColumnWidth - (needVScroll ? m_vScrollBar.Width() : 0);
m_hScrollBar.SetRect(Rect(m_firstColumnWidth, Height() - m_hScrollBar.Height(),
hScrollWidth, m_hScrollBar.Height()));
}
else
{
m_hScrollBar.SetVisible(false);
m_scrollOffsetX = 0;
}
m_vScrollBar.RefreshScroll();
m_hScrollBar.RefreshScroll();
}
void TableView::OffsetX(int offset)
{
m_scrollOffsetX = -offset;
// 如果正在编辑,更新编辑框位置
if (m_editing && m_editBox->IsVisible() && m_editRow >= 0 && m_editCol >= 0)
{
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));
}
}
Invalidate();
}
void TableView::OffsetY(int offset)
{
m_scrollOffsetY = -offset;
// 如果正在编辑,更新编辑框位置
if (m_editing && m_editBox->IsVisible() && m_editRow >= 0 && m_editCol >= 0)
{
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));
}
}
Invalidate();
}
void TableView::DoSort(int colIndex)
{
if (colIndex < 0 || colIndex >= (int)m_columns.size())
{
return;
}
// 更新排序状态
SortOrder newOrder = SortOrder::Ascending;
if (m_columns[colIndex].CurrentSort == SortOrder::Ascending)
{
newOrder = SortOrder::Descending;
}
else if (m_columns[colIndex].CurrentSort == SortOrder::Descending)
{
newOrder = SortOrder::None;
}
// 重置其他列的排序状态
for (auto &col : m_columns)
{
col.CurrentSort = SortOrder::None;
}
m_columns[colIndex].CurrentSort = newOrder;
if (newOrder == SortOrder::None)
{
Invalidate();
return;
}
// 创建索引数组进行排序
std::vector<int> indices(m_data.size());
for (int i = 0; i < (int)indices.size(); ++i)
{
indices[i] = i;
}
// 排序
std::sort(indices.begin(), indices.end(), [this, colIndex, newOrder](int a, int b)
{
UIString textA, textB;
if (colIndex < (int)m_data[a].size()) {
textA = m_data[a][colIndex].Text;
}
if (colIndex < (int)m_data[b].size()) {
textB = m_data[b][colIndex].Text;
}
if (newOrder == SortOrder::Ascending) {
return textA < textB;
} else {
return textA > textB;
} });
// 重新排列数据
std::vector<std::vector<CellData>> newData(m_data.size());
std::vector<int> newRowHeights(m_rowHeights.size());
std::vector<bool> newRowChecked(m_rowChecked.size());
std::vector<TextAlign> newRowTextAlign(m_rowTextAlign.size());
for (int i = 0; i < (int)indices.size(); ++i)
{
int oldIndex = indices[i];
newData[i] = std::move(m_data[oldIndex]);
if (oldIndex < (int)m_rowHeights.size())
{
newRowHeights[i] = m_rowHeights[oldIndex];
}
if (oldIndex < (int)m_rowChecked.size())
{
newRowChecked[i] = m_rowChecked[oldIndex];
}
if (oldIndex < (int)m_rowTextAlign.size())
{
newRowTextAlign[i] = m_rowTextAlign[oldIndex];
}
}
m_data = std::move(newData);
m_rowHeights = std::move(newRowHeights);
m_rowChecked = std::move(newRowChecked);
m_rowTextAlign = std::move(newRowTextAlign);
Invalidate();
}
// ============ 重写的虚函数 ============
void TableView::OnPaint(PaintEventArgs &args)
{
__super::OnPaint(args);
// 绘制单元格(先绘制,这样表头会覆盖在上面)
DrawCells(args);
// 绘制表头
DrawHeader(args);
// 绘制滚动条
if (m_vScrollBar.IsVisible())
{
m_vScrollBar.SendEvent(args);
}
if (m_hScrollBar.IsVisible())
{
m_hScrollBar.SendEvent(args);
}
// 绘制编辑控件(放在最后确保在最上层)
if (m_editBox->IsVisible())
{
m_editBox->SendEvent(args);
}
}
void TableView::OnChildPaint(PaintEventArgs &args)
{
// 重写子控件绘制,跳过 m_editBox因为我们在 OnPaint 中单独处理)
// 其他子控件正常绘制
ViewControls.clear();
Rect rect(0, 0, Width(), Height());
for (auto &it : GetControls())
{
if (it == m_editBox)
{
continue; // 跳过编辑框,由 OnPaint 单独处理
}
if (rect.IntersectsWith(it->GetRect()))
{
ViewControls.push_back(it);
}
it->SendEvent(args);
}
}
void TableView::OnLayout()
{
// 设置滚动条位置
m_vScrollBar.SetRect(Rect(Width() - m_vScrollBar.Width(), m_headerHeight,
m_vScrollBar.Width(), Height() - m_headerHeight - (m_hScrollBar.IsVisible() ? m_hScrollBar.Height() : 0)));
m_hScrollBar.SetRect(Rect(m_firstColumnWidth, Height() - m_hScrollBar.Height(),
Width() - m_firstColumnWidth - (m_vScrollBar.IsVisible() ? m_vScrollBar.Width() : 0), m_hScrollBar.Height()));
RefreshScrollBars();
__super::OnLayout();
}
void TableView::OnMouseMove(const MouseEventArgs &args)
{
__super::OnMouseMove(args);
// 转发事件给编辑框(用于文本选择)
if (m_editBox->IsVisible())
{
Rect editRect = m_editBox->GetRect();
m_editBox->SendEvent(MouseEventArgs(Event::OnMouseMove,
Point(args.Location.X - editRect.X, args.Location.Y - editRect.Y),
args.Button, args.ZDelta));
}
// 转发事件给滚动条
if (m_vScrollBar.IsVisible())
{
m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseMove,
Point(args.Location.X - m_vScrollBar.X(), args.Location.Y - m_vScrollBar.Y()),
args.Button, args.ZDelta));
}
if (m_hScrollBar.IsVisible())
{
m_hScrollBar.SendEvent(MouseEventArgs(Event::OnMouseMove,
Point(args.Location.X - m_hScrollBar.X(), args.Location.Y - m_hScrollBar.Y()),
args.Button, args.ZDelta));
}
// 列宽拖动
if (m_draggingColumn)
{
int delta = args.Location.X - m_dragStartX;
int newWidth = (std::max)(20, m_dragStartWidth + delta);
if (m_dragColumnIndex == -1)
{
// 拖动第一列
m_firstColumnWidth = newWidth;
}
else if (m_dragColumnIndex >= 0 && m_dragColumnIndex < (int)m_columns.size())
{
m_columns[m_dragColumnIndex].Width = newWidth;
}
RefreshScrollBars();
Invalidate();
return;
}
// 检测鼠标是否在列边界上
int borderCol = HitTestColumnBorder(args.Location);
if (borderCol >= -1)
{ // -1 表示第一列边界
// 设置调整大小的光标
Style.Cursor = LoadCursor(Cursor::SIZEWE);
}
else
{
Style.Cursor = LoadCursor(Cursor::ARROW);
}
// 更新悬停状态
int row, col;
if (HitTestCell(args.Location, &row, &col))
{
if (m_hoverRow != row || m_hoverCol != col)
{
m_hoverRow = row;
m_hoverCol = col;
Invalidate();
}
}
}
void TableView::OnMouseDown(const MouseEventArgs &args)
{
__super::OnMouseDown(args);
// 右键单击
if (args.Button == MouseButton::Right)
{
int row, col;
if (HitTestCell(args.Location, &row, &col))
{
if (RightClick)
{
RightClick(row, col);
}
}
return;
}
if (args.Button != MouseButton::Left)
{
return;
}
// 检查是否点击在编辑框上
if (m_editBox->IsVisible())
{
Rect editRect = m_editBox->GetRect();
if (editRect.Contains(args.Location))
{
m_editBox->SendEvent(MouseEventArgs(Event::OnMouseDown,
Point(args.Location.X - editRect.X, args.Location.Y - editRect.Y),
args.Button, args.ZDelta));
return;
}
}
// 检查是否点击在滚动条上
if (HitTestVScrollBar(args.Location))
{
m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseDown,
Point(args.Location.X - m_vScrollBar.X(), args.Location.Y - m_vScrollBar.Y()),
args.Button, args.ZDelta));
return;
}
if (HitTestHScrollBar(args.Location))
{
m_hScrollBar.SendEvent(MouseEventArgs(Event::OnMouseDown,
Point(args.Location.X - m_hScrollBar.X(), args.Location.Y - m_hScrollBar.Y()),
args.Button, args.ZDelta));
return;
}
// 检测是否点击在列边界上
int borderCol = HitTestColumnBorder(args.Location);
if (borderCol >= -1)
{
m_draggingColumn = true;
m_dragColumnIndex = borderCol;
m_dragStartX = args.Location.X;
if (borderCol == -1)
{
m_dragStartWidth = m_firstColumnWidth;
}
else
{
m_dragStartWidth = m_columns[borderCol].Width;
}
return;
}
int row, col;
if (!HitTestCell(args.Location, &row, &col))
{
EndEdit(true);
return;
}
// 点击表头
if (row == -1)
{
if (col == -1)
{
// 点击第一列表头(全选)
if (m_firstColumnType == FirstColumnType::CheckBox)
{
m_headerSelectAll = !m_headerSelectAll;
for (int i = 0; i < (int)m_rowChecked.size(); ++i)
{
m_rowChecked[i] = m_headerSelectAll;
}
Invalidate();
}
}
else
{
// 点击数据列表头(排序)
DoSort(col);
}
return;
}
// 点击第一列
if (col == -1)
{
if (m_firstColumnType == FirstColumnType::CheckBox && row < (int)m_rowChecked.size())
{
// CheckBox 模式:切换该行的选中状态
m_rowChecked[row] = !m_rowChecked[row];
// 更新全选状态
bool allChecked = true;
for (bool checked : m_rowChecked)
{
if (!checked)
{
allChecked = false;
break;
}
}
m_headerSelectAll = allChecked;
Invalidate();
}
else if (m_firstColumnType != FirstColumnType::CheckBox)
{
// 非 CheckBox 模式:单击选中当前行
m_selectedRow = row;
Invalidate();
}
return;
}
// 点击数据单元格
// 获取单元格类型
CellType cellType = CellType::TextBox;
if (col >= 0 && col < (int)m_columns.size())
{
cellType = m_columns[col].Type;
}
// CheckBox 和 ComboBox 单击就触发
if (cellType == CellType::CheckBox || cellType == CellType::ComboBox)
{
BeginEdit(row, col);
m_lastClickTime = 0;
m_lastClickRow = -1;
m_lastClickCol = -1;
return;
}
// TextBox 类型需要双击才进入编辑
ULONGLONG currentTime = ::GetTickCount64();
if (currentTime - m_lastClickTime < 300 &&
m_lastClickRow == row && m_lastClickCol == col)
{
// 双击,开始编辑
BeginEdit(row, col);
m_lastClickTime = 0; // 重置,避免三击触发
m_lastClickRow = -1;
m_lastClickCol = -1;
}
else
{
// 单击,记录时间和位置
m_lastClickTime = currentTime;
m_lastClickRow = row;
m_lastClickCol = col;
EndEdit(true);
}
}
void TableView::OnMouseUp(const MouseEventArgs &args)
{
__super::OnMouseUp(args);
m_draggingColumn = false;
// 转发事件给编辑框
if (m_editBox->IsVisible())
{
Rect editRect = m_editBox->GetRect();
m_editBox->SendEvent(MouseEventArgs(Event::OnMouseUp,
Point(args.Location.X - editRect.X, args.Location.Y - editRect.Y),
args.Button, args.ZDelta));
}
// 转发事件给滚动条
if (m_vScrollBar.IsVisible())
{
m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseUp,
Point(args.Location.X - m_vScrollBar.X(), args.Location.Y - m_vScrollBar.Y()),
args.Button, args.ZDelta));
}
if (m_hScrollBar.IsVisible())
{
m_hScrollBar.SendEvent(MouseEventArgs(Event::OnMouseUp,
Point(args.Location.X - m_hScrollBar.X(), args.Location.Y - m_hScrollBar.Y()),
args.Button, args.ZDelta));
}
}
void TableView::OnMouseDoubleClick(const MouseEventArgs &args)
{
__super::OnMouseDoubleClick(args);
int row, col;
if (HitTestCell(args.Location, &row, &col) && row >= 0 && col >= 0)
{
BeginEdit(row, col);
}
}
void TableView::OnMouseWheel(const MouseEventArgs &args)
{
__super::OnMouseWheel(args);
if (m_vScrollBar.IsVisible())
{
m_vScrollBar.SendEvent(MouseEventArgs(Event::OnMouseWheel, args.Location, args.Button, args.ZDelta));
}
}
void TableView::OnMouseLeave(const MouseEventArgs &args)
{
__super::OnMouseLeave(args);
m_hoverRow = -1;
m_hoverCol = -1;
Style.Cursor = LoadCursor(Cursor::ARROW);
}
void TableView::OnSize(const SizeEventArgs &args)
{
__super::OnSize(args);
RefreshScrollBars();
}
void TableView::OnKeyDown(const KeyboardEventArgs &args)
{
__super::OnKeyDown(args);
// 如果编辑框可见,将键盘事件转发给编辑框
if (m_editBox->IsVisible())
{
m_editBox->SendEvent(args);
}
if (args.wParam == VK_ESCAPE)
{
EndEdit(false);
}
else if (args.wParam == VK_RETURN && !m_editing)
{
// 如果不是多行编辑,回车可以结束编辑
}
}
void TableView::OnKeyChar(const KeyboardEventArgs &args)
{
__super::OnKeyChar(args);
// 如果编辑框可见,将字符输入事件转发给编辑框
if (m_editBox->IsVisible())
{
m_editBox->SendEvent(args);
}
}
// ============ 公共接口实现 ============
void TableView::SetHeaders(const std::vector<UIString> &headers)
{
m_columns.clear();
for (const auto &header : headers)
{
ColumnInfo col;
col.HeaderText = header;
col.Width = 100;
m_columns.push_back(col);
}
// 更新现有数据的列数
for (auto &row : m_data)
{
row.resize(m_columns.size());
}
RefreshScrollBars();
Invalidate();
}
int TableView::GetColumnCount() const
{
return (int)m_columns.size();
}
void TableView::SetHeaderHeight(int height)
{
m_headerHeight = (std::max)(20, height);
RefreshScrollBars();
Invalidate();
}
int TableView::GetHeaderHeight() const
{
return m_headerHeight;
}
void TableView::SetFirstColumnType(FirstColumnType type)
{
m_firstColumnType = type;
// 如果切换到 CheckBox 类型,初始化选中状态
if (type == FirstColumnType::CheckBox)
{
m_rowChecked.resize(m_data.size(), false);
}
Invalidate();
}
FirstColumnType TableView::GetFirstColumnType() const
{
return m_firstColumnType;
}
void TableView::SetFirstColumnWidth(int width)
{
m_firstColumnWidth = (std::max)(20, width);
RefreshScrollBars();
Invalidate();
}
int TableView::GetFirstColumnWidth() const
{
return m_firstColumnWidth;
}
void TableView::SetColumnWidth(int colIndex, int width)
{
if (colIndex >= 0 && colIndex < (int)m_columns.size())
{
m_columns[colIndex].Width = (std::max)(20, width);
RefreshScrollBars();
Invalidate();
}
}
int TableView::GetColumnWidth(int colIndex) const
{
if (colIndex >= 0 && colIndex < (int)m_columns.size())
{
return m_columns[colIndex].Width;
}
return 0;
}
const std::vector<int> TableView::GetColumnWidths() const
{
std::vector<int> widths;
for (const auto &col : m_columns)
{
widths.push_back(col.Width);
}
return widths;
}
void TableView::SetColumnType(int colIndex, CellType type)
{
if (colIndex >= 0 && colIndex < (int)m_columns.size())
{
m_columns[colIndex].Type = type;
Invalidate();
}
}
CellType TableView::GetColumnType(int colIndex) const
{
if (colIndex >= 0 && colIndex < (int)m_columns.size())
{
return m_columns[colIndex].Type;
}
return CellType::TextBox;
}
void TableView::SetColumnComboItems(int colIndex, const std::vector<UIString> &items)
{
if (colIndex >= 0 && colIndex < (int)m_columns.size())
{
m_columns[colIndex].ComboItems = items;
}
}
int TableView::GetRowCount() const
{
return (int)m_data.size();
}
void TableView::AddRow()
{
std::vector<CellData> newRow(m_columns.size());
m_data.push_back(newRow);
m_rowHeights.push_back(m_defaultRowHeight);
m_rowTextAlign.push_back(m_cellTextAlign); // 添加默认对齐
if (m_firstColumnType == FirstColumnType::CheckBox)
{
m_rowChecked.push_back(false);
}
RefreshScrollBars();
Invalidate();
}
void TableView::InsertRow(int rowIndex)
{
if (rowIndex < 0)
rowIndex = 0;
if (rowIndex > (int)m_data.size())
rowIndex = (int)m_data.size();
std::vector<CellData> newRow(m_columns.size());
m_data.insert(m_data.begin() + rowIndex, newRow);
m_rowHeights.insert(m_rowHeights.begin() + rowIndex, m_defaultRowHeight);
// 确保rowTextAlign大小与数据一致
while (m_rowTextAlign.size() < m_data.size())
{
m_rowTextAlign.push_back(m_cellTextAlign);
}
if (rowIndex < (int)m_rowTextAlign.size())
{
m_rowTextAlign.insert(m_rowTextAlign.begin() + rowIndex, m_cellTextAlign);
}
if (m_firstColumnType == FirstColumnType::CheckBox)
{
m_rowChecked.insert(m_rowChecked.begin() + rowIndex, false);
}
RefreshScrollBars();
Invalidate();
}
void TableView::RemoveRow(int rowIndex)
{
if (rowIndex >= 0 && rowIndex < (int)m_data.size())
{
m_data.erase(m_data.begin() + rowIndex);
m_rowHeights.erase(m_rowHeights.begin() + rowIndex);
if (rowIndex < (int)m_rowTextAlign.size())
{
m_rowTextAlign.erase(m_rowTextAlign.begin() + rowIndex);
}
if (rowIndex < (int)m_rowChecked.size())
{
m_rowChecked.erase(m_rowChecked.begin() + rowIndex);
}
RefreshScrollBars();
Invalidate();
}
}
void TableView::ClearRows()
{
m_data.clear();
m_rowHeights.clear();
m_rowChecked.clear();
m_rowTextAlign.clear();
m_headerSelectAll = false;
m_scrollOffsetX = 0;
m_scrollOffsetY = 0;
RefreshScrollBars();
Invalidate();
}
void TableView::AddColumn(const UIString &headerText, int width)
{
ColumnInfo col;
col.HeaderText = headerText;
col.Width = (std::max)(20, width);
m_columns.push_back(col);
// 为所有行添加新列的数据
for (auto &row : m_data)
{
row.push_back(CellData());
}
RefreshScrollBars();
Invalidate();
}
void TableView::InsertColumn(int colIndex, const UIString &headerText, int width)
{
if (colIndex < 0)
colIndex = 0;
if (colIndex > (int)m_columns.size())
colIndex = (int)m_columns.size();
ColumnInfo col;
col.HeaderText = headerText;
col.Width = (std::max)(20, width);
m_columns.insert(m_columns.begin() + colIndex, col);
// 为所有行插入新列的数据
for (auto &row : m_data)
{
row.insert(row.begin() + colIndex, CellData());
}
RefreshScrollBars();
Invalidate();
}
void TableView::RemoveColumn(int colIndex)
{
if (colIndex >= 0 && colIndex < (int)m_columns.size())
{
m_columns.erase(m_columns.begin() + colIndex);
// 从所有行删除该列的数据
for (auto &row : m_data)
{
if (colIndex < (int)row.size())
{
row.erase(row.begin() + colIndex);
}
}
RefreshScrollBars();
Invalidate();
}
}
int TableView::GetRowHeight(int rowIndex) const
{
if (rowIndex >= 0 && rowIndex < (int)m_rowHeights.size())
{
return m_rowHeights[rowIndex];
}
return m_defaultRowHeight;
}
const std::vector<int> TableView::GetRowHeights() const
{
return m_rowHeights;
}
void TableView::SetDefaultRowHeight(int height)
{
m_defaultRowHeight = (std::max)(20, height);
}
void TableView::SetData(int row, int col, const UIString &value)
{
// 自动扩展行
while (row >= (int)m_data.size())
{
AddRow();
}
// 确保列足够
if (col >= 0 && col < (int)m_columns.size())
{
if (col >= (int)m_data[row].size())
{
m_data[row].resize(m_columns.size());
}
m_data[row][col].Text = value;
UpdateRowHeight(row);
// if (CellValueChanged) {
// CellValueChanged(row, col, value);
// }
}
}
UIString TableView::GetData(int row, int col) const
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
return m_data[row][col].Text;
}
return UIString();
}
void TableView::SetRowData(int row, const std::vector<UIString> &values)
{
// 自动扩展行
while (row >= (int)m_data.size())
{
AddRow();
}
for (int col = 0; col < (int)values.size() && col < (int)m_columns.size(); ++col)
{
if (col >= (int)m_data[row].size())
{
m_data[row].resize(m_columns.size());
}
m_data[row][col].Text = values[col];
}
UpdateRowHeight(row);
}
std::vector<UIString> TableView::GetRowData(int row) const
{
std::vector<UIString> result;
if (row >= 0 && row < (int)m_data.size())
{
for (const auto &cell : m_data[row])
{
result.push_back(cell.Text);
}
}
return result;
}
std::vector<UIString> TableView::GetColData(int col) const
{
std::vector<UIString> result;
if (col >= 0 && col < (int)m_columns.size())
{
for (const auto &row : m_data)
{
if (col < (int)row.size())
{
result.push_back(row[col].Text);
}
else
{
result.push_back(UIString());
}
}
}
return result;
}
void TableView::SetAllData(const std::vector<std::vector<UIString>> &data)
{
ClearRows();
for (const auto &rowData : data)
{
AddRow();
int row = (int)m_data.size() - 1;
for (int col = 0; col < (int)rowData.size() && col < (int)m_columns.size(); ++col)
{
m_data[row][col].Text = rowData[col];
}
UpdateRowHeight(row);
}
}
std::vector<std::vector<UIString>> TableView::GetAllData() const
{
std::vector<std::vector<UIString>> result;
for (int row = 0; row < (int)m_data.size(); ++row)
{
result.push_back(GetRowData(row));
}
return result;
}
void TableView::SetCellChecked(int row, int col, bool checked)
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
m_data[row][col].Checked = checked;
Invalidate();
}
}
bool TableView::GetCellChecked(int row, int col) const
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
return m_data[row][col].Checked;
}
return false;
}
void TableView::SetCellComboIndex(int row, int col, int index)
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
m_data[row][col].ComboIndex = index;
Invalidate();
}
}
int TableView::GetCellComboIndex(int row, int col) const
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
return m_data[row][col].ComboIndex;
}
return -1;
}
void TableView::SetRowChecked(int row, bool checked)
{
if (m_firstColumnType == FirstColumnType::CheckBox &&
row >= 0 && row < (int)m_rowChecked.size())
{
m_rowChecked[row] = checked;
// 更新全选状态
bool allChecked = true;
for (bool c : m_rowChecked)
{
if (!c)
{
allChecked = false;
break;
}
}
m_headerSelectAll = allChecked;
Invalidate();
}
}
bool TableView::GetRowChecked(int row) const
{
if (row >= 0 && row < (int)m_rowChecked.size())
{
return m_rowChecked[row];
}
return false;
}
std::vector<int> TableView::GetCheckedRows() const
{
std::vector<int> result;
for (int i = 0; i < (int)m_rowChecked.size(); ++i)
{
if (m_rowChecked[i])
{
result.push_back(i);
}
}
return result;
}
int TableView::GetSelectedRow() const
{
return m_selectedRow;
}
void TableView::SetSelectedRow(int row)
{
if (row >= -1 && row < (int)m_data.size())
{
m_selectedRow = row;
Invalidate();
}
}
void TableView::ClearSelection()
{
m_selectedRow = -1;
Invalidate();
}
void TableView::SelectAll()
{
if (m_firstColumnType == FirstColumnType::CheckBox)
{
m_headerSelectAll = true;
for (int i = 0; i < (int)m_rowChecked.size(); ++i)
{
m_rowChecked[i] = true;
}
Invalidate();
}
}
void TableView::DeselectAll()
{
if (m_firstColumnType == FirstColumnType::CheckBox)
{
m_headerSelectAll = false;
for (int i = 0; i < (int)m_rowChecked.size(); ++i)
{
m_rowChecked[i] = false;
}
Invalidate();
}
}
void TableView::SetCellBorderSize(int size)
{
m_cellBorderSize = (std::max)(0, size);
Invalidate();
}
void TableView::SetCellBorderStyle(StrokeStyle style)
{
m_cellBorderStyle = style;
Invalidate();
}
void TableView::SetCellBorderColor(const Color &color)
{
m_cellBorderColor = color;
Invalidate();
}
void TableView::SetCellBackColor(const Color &color)
{
m_cellBackColor = color;
Invalidate();
}
void TableView::SetCellForeColor(const Color &color)
{
m_cellForeColor = color;
Invalidate();
}
void TableView::SetCellFontSize(int size)
{
m_cellFontSize = (std::max)(8, size);
// 同步更新编辑框字体
m_editBox->Style.FontSize = m_cellFontSize;
// 更新所有行高
for (int i = 0; i < (int)m_data.size(); ++i)
{
UpdateRowHeight(i);
}
}
void TableView::SetCellFontFamily(const std::wstring &fontFamily)
{
m_cellFontFamily = fontFamily;
// 同步更新编辑框字体
m_editBox->Style.FontFamily = m_cellFontFamily;
// 更新所有行高
for (int i = 0; i < (int)m_data.size(); ++i)
{
UpdateRowHeight(i);
}
}
void TableView::SetCellStyle(int row, int col, const CellStyle &style)
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
m_data[row][col].Style = style;
Invalidate();
}
}
CellStyle TableView::GetCellStyle(int row, int col) const
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
return m_data[row][col].Style;
}
return CellStyle();
}
void TableView::ResetCellStyle(int row, int col)
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
m_data[row][col].Style.Reset();
Invalidate();
}
}
void TableView::SetHeaderBackColor(const Color &color)
{
m_headerBackColor = color;
Invalidate();
}
void TableView::SetHeaderForeColor(const Color &color)
{
m_headerForeColor = color;
Invalidate();
}
void TableView::SetDefaultTextAlign(TextAlign align)
{
m_cellTextAlign = align;
Invalidate();
}
void TableView::SetColumnTextAlign(int colIndex, TextAlign align)
{
if (colIndex >= 0 && colIndex < (int)m_columns.size())
{
m_columns[colIndex].CellTextAlign = align;
Invalidate();
}
}
void TableView::SetRowTextAlign(int rowIndex, TextAlign align)
{
// 自动扩展
while (rowIndex >= (int)m_rowTextAlign.size())
{
m_rowTextAlign.push_back(m_cellTextAlign); // 使用默认值
}
if (rowIndex >= 0 && rowIndex < (int)m_rowTextAlign.size())
{
m_rowTextAlign[rowIndex] = align;
Invalidate();
}
}
void TableView::SetCellTextAlign(int row, int col, TextAlign align)
{
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
m_data[row][col].Style.SetTextAlign(align);
Invalidate();
}
}
TextAlign TableView::GetCellTextAlign(int row, int col) const
{
// 优先级:单元格 > 行 > 列 > 默认
if (row >= 0 && row < (int)m_data.size() &&
col >= 0 && col < (int)m_data[row].size())
{
// 单元格级别
if (m_data[row][col].Style.HasTextAlign)
{
return m_data[row][col].Style.Align;
}
}
// 行级别(只有当行索引在 m_rowTextAlign 范围内才使用)
if (row >= 0 && row < (int)m_rowTextAlign.size())
{
// 这里应该检查是否真的设置过,但为了兼容性,我们假设设置过就使用
// 如果数组已扩展,就使用该行的对齐方式
return m_rowTextAlign[row];
}
// 列级别
if (col >= 0 && col < (int)m_columns.size())
{
return m_columns[col].CellTextAlign;
}
// 默认值
return m_cellTextAlign;
}
int TableView::GetHoverRow() const
{
return m_hoverRow;
}
int TableView::GetHoverCol() const
{
return m_hoverCol;
}
void TableView::GetHoverCell(int *outRow, int *outCol) const
{
if (outRow)
*outRow = m_hoverRow;
if (outCol)
*outCol = m_hoverCol;
}
const Size &TableView::GetContentSize()
{
// 计算内容大小供滚动条使用
m_contentSize.Width = GetContentWidth();
m_contentSize.Height = GetContentHeight();
return m_contentSize;
}
ScrollBar *TableView::GetVScrollBar()
{
return &m_vScrollBar;
}
ScrollBar *TableView::GetHScrollBar()
{
return &m_hScrollBar;
}
void TableView::SetAttribute(const UIString &key, const UIString &value)
{
__super::SetAttribute(key, value);
// 辅助函数:解析整数
auto parseInt = [](const UIString &v) -> int
{
UIString tmp = v;
ui_text::Replace(&tmp, "px", "");
tmp = tmp.trim();
return std::atoi(tmp.c_str());
};
// 辅助函数:解析颜色
auto parseColor = [](const UIString &v) -> Color
{
bool isGood = false;
return Color::Make(v, &isGood);
};
do
{
// 表头高度
if (key == "header-height" || key == "headerheight")
{
SetHeaderHeight(parseInt(value));
break;
}
// 第一列类型
if (key == "first-column-type" || key == "firstcolumntype")
{
if (value == "index" || value == "number")
{
SetFirstColumnType(FirstColumnType::Index);
}
else if (value == "checkbox" || value == "check")
{
SetFirstColumnType(FirstColumnType::CheckBox);
}
else
{
SetFirstColumnType(FirstColumnType::TextBox);
}
break;
}
// 第一列宽度
if (key == "first-column-width" || key == "firstcolumnwidth")
{
SetFirstColumnWidth(parseInt(value));
break;
}
// 默认行高
if (key == "default-row-height" || key == "defaultrowheight" || key == "row-height" || key == "rowheight")
{
SetDefaultRowHeight(parseInt(value));
break;
}
// 单元格边框大小
if (key == "cell-border-size" || key == "cellbordersize" || key == "border-size" || key == "bordersize")
{
SetCellBorderSize(parseInt(value));
break;
}
// 单元格边框样式
if (key == "cell-border-style" || key == "cellborderstyle" || key == "border-style" || key == "borderstyle")
{
if (value == "solid")
{
SetCellBorderStyle(StrokeStyle::Solid);
}
else if (value == "dash" || value == "dashed")
{
SetCellBorderStyle(StrokeStyle::Dash);
}
else if (value == "none")
{
SetCellBorderStyle(StrokeStyle::None);
}
break;
}
// 单元格边框颜色
if (key == "cell-border-color" || key == "cellbordercolor" || key == "border-color" || key == "bordercolor")
{
SetCellBorderColor(parseColor(value));
break;
}
// 单元格背景颜色
if (key == "cell-back-color" || key == "cellbackcolor" || key == "cell-background" || key == "cellbackground")
{
SetCellBackColor(parseColor(value));
break;
}
// 单元格前景颜色(文字颜色)
if (key == "cell-fore-color" || key == "cellforecolor" || key == "cell-color" || key == "cellcolor")
{
SetCellForeColor(parseColor(value));
break;
}
// 单元格字体大小
if (key == "cell-font-size" || key == "cellfontsize")
{
SetCellFontSize(parseInt(value));
break;
}
// 单元格字体
if (key == "cell-font-family" || key == "cellfontfamily" || key == "cell-font" || key == "cellfont")
{
SetCellFontFamily(value.unicode());
break;
}
// 表头背景颜色
if (key == "header-back-color" || key == "headerbackcolor" || key == "header-background" || key == "headerbackground")
{
SetHeaderBackColor(parseColor(value));
break;
}
// 表头前景颜色(文字颜色)
if (key == "header-fore-color" || key == "headerforecolor" || key == "header-color" || key == "headercolor")
{
SetHeaderForeColor(parseColor(value));
break;
}
// 选中行背景颜色
if (key == "selected-row-back-color" || key == "selectedrowbackcolor" || key == "selected-color" || key == "selectedcolor")
{
SelectedRowBackColor = parseColor(value);
Invalidate();
break;
}
// 表头文字(逗号分隔)
if (key == "headers" || key == "columns")
{
auto headers = value.split(",");
std::vector<UIString> headerList;
for (auto &h : headers)
{
headerList.push_back(h.trim());
}
SetHeaders(headerList);
break;
}
} while (false);
}
}