#include "Table.h" #include "SxLog.h" namespace { std::vector NormalizeTableRow(std::vector row, size_t headerCount) { if (headerCount == 0) return row; if (row.size() > headerCount) row.resize(headerCount); else if (row.size() < headerCount) row.resize(headerCount, ""); return row; } int CalculateTotalPages(size_t rowCount, int rowsPerPage) { if (rowsPerPage < 1) rowsPerPage = 1; const int total = static_cast((rowCount + static_cast(rowsPerPage) - 1) / static_cast(rowsPerPage)); return total > 0 ? total : 1; } } // 绘制表格的当前页 // 使用双循环绘制行和列,考虑分页偏移 void Table::drawTable() { if (lineHeights.empty() || colWidths.empty()) return; const int border = tableBorderWidth > 0 ? tableBorderWidth : 0; // 表体从“表头之下”开始 dX = x + border; dY = y + border + lineHeights.at(0) + TABLE_HEADER_EXTRA; // 表头高度 uY = dY + lineHeights.at(0) + TABLE_ROW_EXTRA; size_t startRow = (currentPage - 1) * rowsPerPage; size_t endRow = startRow + (size_t)rowsPerPage < data.size() ? startRow + (size_t)rowsPerPage : data.size(); for (size_t i = startRow; i < endRow; ++i) { for (size_t j = 0; j < data[i].size(); ++j) { uX = dX + colWidths.at(j) + TABLE_COL_GAP; fillrectangle(dX, dY, uX, uY); outtextxy(dX + TABLE_PAD_X, dY + TABLE_PAD_Y, LPCTSTR(data[i][j].c_str())); dX += colWidths.at(j) + TABLE_COL_GAP; } dX = x + border; dY = uY; uY = dY + lineHeights.at(0) + TABLE_ROW_EXTRA; } } void Table::drawHeader() { if (headers.empty() || lineHeights.empty() || colWidths.empty()) return; const int border = tableBorderWidth > 0 ? tableBorderWidth : 0; // 内容区原点 = x+border, y+border dX = x + border; dY = y + border; uY = dY + lineHeights.at(0) + TABLE_HEADER_EXTRA; for (size_t i = 0; i < headers.size(); i++) { uX = dX + colWidths.at(i) + TABLE_COL_GAP; // 注意这里是 +20,和表体一致 fillrectangle(dX, dY, uX, uY); outtextxy(dX + TABLE_PAD_X, dY + TABLE_PAD_Y, LPCTSTR(headers[i].c_str())); dX += colWidths.at(i) + TABLE_COL_GAP; // 列间距 20 } } // 遍历所有数据单元和表头,计算每列的最大宽度和每行的最大高度, // 为后续绘制表格单元格提供尺寸依据。此计算在数据变更时自动触发。 void Table::initTextWaH() { // 和绘制一致的单元内边距 const int padX = TABLE_PAD_X; // 左右 padding const int padY = TABLE_PAD_Y; // 上下 padding const int colGap = TABLE_COL_GAP; // 列间距 const int border = tableBorderWidth > 0 ? tableBorderWidth : 0; size_t maxCols = headers.size(); for (const auto& row : data) if (row.size() > maxCols) maxCols = row.size(); // 统计每列最大文本宽 & 每列最大行高(包含数据 + 表头) colWidths.assign(maxCols, 0); lineHeights.assign(maxCols, 0); // 先看数据 for (size_t i = 0; i < data.size(); ++i) { for (size_t j = 0; j < data[i].size(); ++j) { const int w = textwidth(LPCTSTR(data[i][j].c_str())); const int h = textheight(LPCTSTR(data[i][j].c_str())); if (w > colWidths[j]) colWidths[j] = w; if (h > lineHeights[j]) lineHeights[j] = h; } } // 再用表头更新(谁大取谁) for (size_t j = 0; j < headers.size(); ++j) { const int w = textwidth(LPCTSTR(headers[j].c_str())); const int h = textheight(LPCTSTR(headers[j].c_str())); if (w > colWidths[j]) colWidths[j] = w; if (h > lineHeights[j]) lineHeights[j] = h; } // 用“所有列的最大行高”作为一行的基准高度 int maxLineH = 0; for (int h : lineHeights) if (h > maxLineH) maxLineH = h; if (maxLineH == 0) maxLineH = textheight(LPCTSTR("A")); if (rowsPerPage < 1) rowsPerPage = 1; // 列宽包含左右 padding:在计算完最大文本宽度后,加上 2*padX 作为单元格内边距 for (size_t j = 0; j < colWidths.size(); ++j) { colWidths[j] += 2 * padX; } // 表内容总宽 = Σ(列宽 + 列间距) int contentW = 0; for (size_t j = 0; j < colWidths.size(); ++j) contentW += colWidths[j] + colGap; // 表头高 & 行高(与 drawHeader/drawTable 内部一致:+上下 padding) const int headerH = maxLineH + 2 * padY; const int rowH = maxLineH + 2 * padY; const int rowsH = rowH * rowsPerPage; // 页脚: const int pageTextH = textheight(LPCTSTR(pageNumtext.c_str())); const int btnTextH = textheight(LPCTSTR("上一页")); const int btnPadV = TABLE_BTN_TEXT_PAD_V; const int btnH = btnTextH + 2 * btnPadV; const int footerPad = TABLE_FOOTER_PAD; const int footerH = (pageTextH > btnH ? pageTextH : btnH) + footerPad; // 最终表宽/高:内容 + 对称边框 this->width = contentW + (border << 1); this->height = headerH + rowsH + footerH + (border << 1); // 这是 Table 自身受控的“结构尺寸 -> 设计基线”刷新点: // 表格总宽高本来就由表头、表体、页脚和分页按钮共同决定, // 因此这里允许同步 localWidth/localHeight,作为后续锚点解算参考。 const bool baselineChanged = (this->localWidth != this->width) || (this->localHeight != this->height); this->localWidth = this->width; this->localHeight = this->height; if (baselineChanged) { SX_LOGD("Layout") << SX_T("Table 刷新结构设计基线:id=", "Table refresh structural design rect: id=") << id << SX_T(" size=(", " size=(") << this->width << "," << this->height << ")"; } } void Table::initButton() { const int gap = TABLE_BTN_GAP; const int padH = TABLE_BTN_PAD_H; const int padV = TABLE_BTN_PAD_V; // 按钮垂直内边距 int pageW = textwidth(LPCTSTR(pageNumtext.c_str())); int lblH = textheight(LPCTSTR(pageNumtext.c_str())); // 统一按钮尺寸(用按钮文字自身宽高 + padding) int prevW = textwidth(LPCTSTR(TABLE_STR_PREV)) + padH * 2; int nextW = textwidth(LPCTSTR(TABLE_STR_NEXT)) + padH * 2; int btnH = lblH + padV * 2; // 基于“页码标签”的矩形来摆放: // prev 在页码左侧 gap 处;next 在右侧 gap 处;Y 对齐 pY int prevX = pX - gap - prevW; int nextX = pX + pageW + gap; int btnY = pY; // 和页码同一基线 if (!prevButton) prevButton = std::make_unique