Files
Renderive/YSGraphic_Core/component/Table/Table_View.cpp
T
2026-06-16 13:33:27 +08:00

566 lines
20 KiB
C++

#include "Table_View.h"
#include <QPainter>
#include <set>
#include "../../GenerateMockData.h"
#include "Base_Table_Data.h"
#include "Base_Info.h"
namespace Psc {
/*
我的表格是一个存粹的painter 实现
这样的话 我任何东西都可以 通过 虚函数去实现
我可以虚函数 给这些所有
视觉上元素的拓展无非就是 位置,如何绘制,允许传来自定义参数, 允许嵌入自己的组件
所以我每一个视觉上的组件都要有这个信息
那么元素上的事件处理呢? 无非是鼠标 键盘
*/
QPoint Table_View::to_inside_pos(QPoint widget_pos) {
auto rect = get_view_rect();
return widget_pos + this->_view_pos - QPoint(rect.left(), rect.top());
}
QPoint Table_View::to_widget_pos(QPoint inside_pos) {
auto rect = get_view_rect();
return inside_pos - this->_view_pos + QPoint(rect.left(), rect.top());
}
QRect Table_View::get_view_rect() {
int left = row_info_w + row_info_inside_space + left_margin ;
int top = col_info_h + col_info_inside_space + top_margin;
int right = (v_bar.show ? static_cast<int>(v_bar.w) : 0) + right_margin;
int bottom = (h_bar.show ? static_cast<int>(h_bar.h) : 0) + bottom_margin;
QRect all_view_rect = QRect(left, top, width() - left - right, height() - top - bottom);
return all_view_rect;
}
Table_View::Table_View() {
setAttribute(Qt::WA_Hover);
setMouseTracking(true);
edit = new QTextEdit();
container = new Container(this);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
void Table_View::resizeEvent(QResizeEvent* event) {
QRect vr = get_view_rect();
h_bar.start = vr.x();
h_bar.resize(vr.width(), vr.width(), model->width());
v_bar.start = vr.y();
v_bar.resize(vr.height(), vr.height(), model->height());
container->setGeometry(vr);
QWidget::resizeEvent(event);
}
bool Table_View::eventFilter(QObject* obj, QEvent* event) {
if (event->type() == QEvent::Enter) {
setCursor(Qt::ArrowCursor);
}
return QObject::eventFilter(obj, event);
}
void Table_View::paintEvent(QPaintEvent* event) {
static QImage img(":/jian_hao.png");
static QImage img2(":/jia_hao.png");
static int space = 4;
QPainter painter(this);
// 视图位置转换为绝对位置
QRect cells_widget_rect = get_view_rect();
QPen pen(Qt::black, 1);
painter.setPen(pen);
painter.drawRect(cells_widget_rect.adjusted(-1, -1, 0, 0));
auto inside_sta_pos = to_inside_pos(cells_widget_rect.topLeft());
auto inside_end_pos = to_inside_pos(cells_widget_rect.bottomRight());
Pos x_sta = model->get_col(inside_sta_pos.rx());
Pos y_sta = model->get_row(inside_sta_pos.ry());
Pos x_end = model->get_col(inside_end_pos.rx());
Pos y_end = model->get_row(inside_end_pos.ry());
struct Paint_Cache {
int cell_abs_pos;
int index;
Base_Info* info;
};
// 计算横着每个格子起点的绝对位置
int xsi = x_sta.index;
int max_col_num = std::min(x_end.index - x_sta.index + 1, model->total_col());
int col_num = 0;
QVector<Paint_Cache> xs;
{
int cur_x = x_sta.cell_pos;
for (int i = 0; i < max_col_num; ++i)
{
auto cur = model->get_col_info(xsi + i);
if (cur->hide) continue;
xs.push_back({cur_x, i, cur});
cur_x += cur->len + model->col_space;
}
col_num = xs.size();
}
// 计算竖着每个格子起点的绝对位置
int ysi = y_sta.index;
int max_row_num = std::min(y_end.index - y_sta.index + 1, model->total_row());
int row_num = 0;
QVector<Paint_Cache> ys;
{
int cur_y = y_sta.cell_pos;
for (int i = 0; i < max_row_num; ++i)
{
auto cur = model->get_row_info(ysi + i);
if (cur->hide) continue;
ys.push_back({cur_y, i, cur});
cur_y += cur->len + model->row_space;
}
row_num = ys.size();
}
// 绘制表头 水平
{
painter.setPen(Qt::black);
int end_y = cells_widget_rect.y() - col_info_inside_space;
int sta_y = end_y - col_info_h;
{
QPoint s = QPoint(cells_widget_rect.x(), sta_y);
QPoint e = QPoint(cells_widget_rect.right(), end_y);
painter.setClipRect(QRect(s, e));
}
QVector<int> middle_xs(col_num + 1);
for (int i = 0; i < col_num; ++i)
{
auto& xd = xs[i];
int sta_x = to_widget_pos({xd.cell_abs_pos, 0}).rx();
int end_x = to_widget_pos({xd.cell_abs_pos + xd.info->len, 0}).rx();
if (i == 0) {
middle_xs[0] = sta_x - model->row_space / 2;
}
middle_xs[i + 1] = end_x + model->row_space / 2;
QRect re(QPoint(sta_x, sta_y), QPoint(end_x, end_y));
xd.info->paint(&painter, re);
}
{
QPoint s = QPoint(cells_widget_rect.x(), sta_y);
painter.setClipRect(QRect(s, cells_widget_rect.bottomRight()));
}
painter.setPen(Qt::black);
for (int i = 0; i < col_num; ++i)
{
int middle_x = middle_xs[i];
painter.drawLine(QPoint(middle_x, 0), QPoint(middle_x, height()));
}
}
// 绘制表头 竖直
{
painter.setPen(Qt::black);
int end_x = cells_widget_rect.x() - row_info_inside_space;
int sta_x = end_x - row_info_w;
{
QPoint s = QPoint(sta_x, cells_widget_rect.y());
QPoint e = QPoint(end_x, cells_widget_rect.bottom());
painter.setClipRect(QRect(s, e));
}
QVector<int> middle_ys(row_num);
for (int i = 0; i < row_num; ++i)
{
auto& yd = ys[i];
int sta_y = to_widget_pos({0, yd.cell_abs_pos}).ry();
int end_y = to_widget_pos({0, yd.cell_abs_pos + yd.info->len}).ry();
middle_ys[i] = end_y + model->col_space / 2;
QRect re(QPoint(sta_x, sta_y), QPoint(end_x, end_y));
yd.info->paint(&painter, re);
}
{
QPoint s = QPoint(sta_x, cells_widget_rect.y());
painter.setClipRect(QRect(s, cells_widget_rect.bottomRight()));
}
painter.setPen(Qt::black);
for (int i = 0; i < row_num; ++i)
{
int middle_y = middle_ys[i];
painter.drawLine(QPoint(0, middle_y), QPoint(width(), middle_y));
}
}
// 绘制滚动条
if (h_bar.show) {
h_bar.draw(&painter);
}
if (v_bar.show) {
v_bar.draw(&painter);
}
QSet<Base_Table_Data*> set;
// 绘制小方格
painter.setClipRect(cells_widget_rect);
painter.setPen(Qt::black);
for (auto& xd : xs)
{
for (auto& yd : ys)
{
int xi = xsi + xd.index;
int yi = ysi + yd.index;
auto cur_cell = model->get_table_data(xi, yi);
QPoint cur_inside_sta_pos;
QPoint cur_inside_end_pos;
auto show_cell = cur_cell;
bool show = true;
if (cur_cell->parent != nullptr && cur_cell->parent != cur_cell) {
auto r = model->find_parent(cur_cell->parent, xi, yi);
if (
r.col_index < xsi ||
r.row_index < ysi
) {
if (!set.contains(cur_cell->parent)) {
set.insert(cur_cell->parent);
int sxi = r.col_index;
int syi = r.row_index;
show = true;
auto info = model->get_span(sxi, syi);
int x = model->cell_x(sxi);
int y = model->cell_y(syi);
cur_inside_sta_pos = {x, y};
cur_inside_end_pos = {x + info.sw, y + info.sh};
show_cell = model->get_table_data(sxi, syi);
} else
{
show = false;
}
}
} else
{
auto info = model->get_span(xi, yi);
cur_inside_sta_pos = {xd.cell_abs_pos, yd.cell_abs_pos};
cur_inside_end_pos = {xd.cell_abs_pos + info.sw, yd.cell_abs_pos + info.sh};
}
QPoint cur_widget_sta_pos = to_widget_pos(cur_inside_sta_pos);
QPoint cur_widget_end_pos = to_widget_pos(cur_inside_end_pos);
QRect cur_view_rect = QRect(cur_widget_sta_pos, cur_widget_end_pos);
show_cell->update(show, this, &painter, cur_view_rect);
}
}
}
bool Table_View::event(QEvent* e) {
if (
e->type() == QEvent::MouseButtonPress ||
e->type() == QEvent::MouseMove ||
e->type() == QEvent::MouseButtonRelease ||
e->type() == QEvent::MouseButtonDblClick
) {
auto event = static_cast<QMouseEvent*>(e);
mouseEvent(event, event->type());
return true;
}
return QWidget::event(e);
}
void Table_View::leaveEvent(QEvent* event) {
QWidget::leaveEvent(event);
}
void Table_View::mouseEvent(QMouseEvent* event, QEvent::Type type) {
QRect cells_widget_rect = get_view_rect();
auto pos = event->pos();
auto abs_pos = to_inside_pos(pos);
Pos px = model->get_col(abs_pos.x());
Pos py = model->get_row(abs_pos.y());
bool drag_on_col_space = false;
bool drag_on_row_space = false;
Col_Info* cur_col{};
Row_Info* cur_row{};
if (model->total_col() != 0) {
cur_col = model->get_col_info(px.index);
if (use_drag_w && px.offset > cur_col->len && px.offset < cur_col->len + model->col_space) {
drag_on_col_space = true;
}
}
if (model->total_row() != 0) {
cur_row = model->get_row_info(py.index);
if (use_drag_h && py.offset > cur_row->len && py.offset < cur_row->len + model->row_space) {
drag_on_row_space = true;
}
}
bool on_h_slider = h_bar.get_slider_rect(width(), height()).contains(event->pos());
bool on_v_slider = v_bar.get_slider_rect(width(), height()).contains(event->pos());
bool on_cells_view = cells_widget_rect.contains(pos);
if (cur_col && cur_row) {
auto cur_cell = model->get_table_data(px.index, py.index);
auto parent_pos = model->find_parent(cur_cell->parent, px.index, py.index);
auto show_cell = model->get_table_data(parent_pos.col_index,parent_pos.row_index);
int sci = parent_pos.col_index;
int sri = parent_pos.row_index;
auto sx = model->cell_x(sci);
auto sy = model->cell_y(sri);
auto info = model->get_span(parent_pos.col_index, parent_pos.row_index);
QPoint s_sta = to_widget_pos({sx, sy});
QPoint s_end = to_widget_pos({sx + info.sw, sy + info.sh});
QRect srect = QRect(s_sta, s_end);
if (type == QEvent::MouseButtonDblClick)
{
if (on_cells_view && srect.contains(s_sta))
{
edit->setGeometry(srect);
prev_data = show_cell;
auto ttd = dynamic_cast<Table_Edit_Able*>(show_cell);
if (ttd) {
edit->setText(ttd->content);
edit->setParent(this);
edit->show();
}
}
}
}
if (type == QEvent::MouseButtonPress)
{
//qDebug() << "cur_cell " << cur_cell->content ;
//qDebug() << "cur_cell_parent "<< (cur_cell->parent ? cur_cell->parent->content : "nullptr");
edit->hide();
if (prev_data)
{
auto ttd = dynamic_cast<Table_Edit_Able*>(prev_data);
if (ttd) {
ttd->content = edit->toPlainText();
prev_data = nullptr;
update();
}
}
// qDebug() << log;
if (isCtrlPressed())
{
isDragging = true;
drag_type = Table_Move;
setCursor(Qt::OpenHandCursor);
} else if (on_h_slider)
{
isDragging = true;
drag_type = H_scrollBar;
setCursor(Qt::SizeHorCursor);
} else if (on_v_slider)
{
isDragging = true;
drag_type = V_scrollBar;
setCursor(Qt::SizeVerCursor);
} else if (drag_on_col_space)
{
isDragging = true;
drag_type = H_Expand;
col_expand = cur_col;
start_expand_len = col_expand->len;
setCursor(Qt::SplitHCursor);
} else if (drag_on_row_space)
{
isDragging = true;
drag_type = V_Expand;
row_expand = cur_row;
start_expand_len = row_expand->len;
setCursor(Qt::SplitVCursor);
} else
{
setCursor(Qt::ArrowCursor);
}
if (isDragging)
{
start_pos = event->pos();
save_pos = _view_pos;
}
} else if (type == QEvent::MouseButtonRelease)
{
isDragging = false;
if (drag_on_col_space)
{
setCursor(Qt::SplitHCursor);
} else if (drag_on_row_space)
{
setCursor(Qt::SplitVCursor);
} else if (on_h_slider)
{
setCursor(Qt::SizeHorCursor);
} else if (on_v_slider)
{
setCursor(Qt::SizeVerCursor);
} else
{
setCursor(Qt::ArrowCursor);
}
} else if (type == QEvent::MouseMove)
{
if (isCtrlPressed())
{
if (isDragging) {
setCursor(Qt::ClosedHandCursor);
} else
{
setCursor(Qt::OpenHandCursor);
}
} else
{
if (!isDragging)
{
if (drag_on_col_space)
{
setCursor(Qt::SplitHCursor);
} else if (drag_on_row_space)
{
setCursor(Qt::SplitVCursor);
} else if (on_h_slider)
{
setCursor(Qt::SizeHorCursor);
} else if (on_v_slider)
{
setCursor(Qt::SizeVerCursor);
} else
{
setCursor(Qt::ArrowCursor);
}
}
}
if (!isDragging) return;
QPoint cur_pos = event->pos();
int deltaX = cur_pos.x() - start_pos.x(); // 计算X轴的拖动距离
int deltaY = cur_pos.y() - start_pos.y(); // 计算Y轴的拖动距离
if (drag_type == Table_Move && isCtrlPressed())
{
int end_x = save_pos.x() - deltaX;
int end_y = save_pos.y() - deltaY;
_view_pos.setX(end_x);
_view_pos.setY(end_y);
h_bar.set_view_pos(_view_pos.rx());
v_bar.set_view_pos(_view_pos.ry());
} else if (drag_type == H_scrollBar)
{
int end_x = save_pos.x() + deltaX / h_bar.rate();
_view_pos.setX(end_x);
h_bar.set_view_pos(end_x);
} else if (drag_type == V_scrollBar)
{
int end_y = save_pos.y() + deltaY / v_bar.rate();
_view_pos.setY(end_y);
v_bar.set_view_pos(end_y);
} else if (drag_type == H_Expand)
{
col_expand->len = std::max(model->col_min_w, start_expand_len + deltaX);
} else if (drag_type == V_Expand)
{
row_expand->len = std::max(model->row_min_h, start_expand_len + deltaY);
}
update();
}
}
void Table_View::wheelEvent(QWheelEvent* event) {
int single_step = 12;
double v_steps = event->angleDelta().y() / 120;
v_bar.set_view_pos(v_bar.get_view_pos() - v_steps * single_step);
double h_steps = event->angleDelta().x() / 120;
h_bar.set_view_pos(h_bar.get_view_pos() - h_steps * single_step);
_view_pos.setX(h_bar.view_pos());
_view_pos.setY(v_bar.view_pos());
update();
}
}
#ifdef YC_USE_GTEST
#include <gtest/gtest.h>
TEST(Table, get_pos_test) {
QVector<Base_info*> info_list;
// 4 2 1
// 4 6 7
info_list.push_back(new Base_info(4, ""));
info_list.push_back(new Base_info(1, ""));
auto size = static_cast<int>(info_list.size());
auto d = info_list.data();
QVector<std::pair<Base_Table_Info::Pos, int>> combinedData = {
{Base_Table_Info::Pos{0, 0, -7}, -7},
{Base_Table_Info::Pos{0, 0, 4}, 4},
{Base_Table_Info::Pos{1, 6, 1}, 7},
{Base_Table_Info::Pos{1, 6, 2}, 8},
};
for (auto it : combinedData)
{
auto answer = it.first;
auto input = it.second;
auto pos = get_pos(d, size, 2, input, 0);
EXPECT_TRUE(pos == answer) << "输入:" << input << "\n"
<< "结果:" + pos.to_string() << "\n"
<< "期望:" + answer.to_string() << "\n";
}
}
// 测试 Matrix 类的基本操作
TEST(MatrixTest, BasicOperations) {
// 初始化一个 3x3 矩阵
Table<int> mat;
mat.data = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
// 测试 total_row 和 total_col
EXPECT_EQ(mat.total_row(), 3);
EXPECT_EQ(mat.total_col(), 3);
// 测试 at 方法
EXPECT_EQ(mat.at(0, 0), 1);
EXPECT_EQ(mat.at(1, 1), 5);
EXPECT_EQ(mat.at(2, 2), 9);
// 测试 row 方法
QVector<int> row_0 = mat.row(0);
EXPECT_EQ(row_0.size(), 3);
EXPECT_EQ(row_0[0], 1);
EXPECT_EQ(row_0[1], 2);
EXPECT_EQ(row_0[2], 3);
// 测试 col 方法
QVector<int> col_0 = mat.col(0);
EXPECT_EQ(col_0.size(), 3);
EXPECT_EQ(col_0[0], 1);
EXPECT_EQ(col_0[1], 4);
EXPECT_EQ(col_0[2], 7);
// 测试 sub_matrix 方法
Table<int> sub_mat = mat.sub_table(0, 0, 2, 2);
EXPECT_EQ(sub_mat.total_row(), 2);
EXPECT_EQ(sub_mat.total_col(), 2);
EXPECT_EQ(sub_mat.at(0, 0), 1);
EXPECT_EQ(sub_mat.at(1, 1), 5);
}
TEST(MatrixTest, SubMatrix) {
// 测试提取子矩阵
Table<int> mat;
mat.data = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Table<int> sub_mat = mat.sub_table(1, 1, 2, 2);
EXPECT_EQ(sub_mat.total_row(), 2);
EXPECT_EQ(sub_mat.total_col(), 2);
EXPECT_EQ(sub_mat.at(0, 0), 5);
EXPECT_EQ(sub_mat.at(0, 1), 6);
EXPECT_EQ(sub_mat.at(1, 0), 8);
EXPECT_EQ(sub_mat.at(1, 1), 9);
}
TEST(MatrixTest, EmptyMatrix) {
// 测试空矩阵
Table<int> mat;
EXPECT_EQ(mat.total_row(), 0);
EXPECT_EQ(mat.total_col(), 0);
}
TEST(MatrixTest, SingleElementMatrix) {
// 测试 1x1 矩阵
Table<int> mat;
mat.data = {{42}};
EXPECT_EQ(mat.total_row(), 1);
EXPECT_EQ(mat.total_col(), 1);
EXPECT_EQ(mat.at(0, 0), 42);
}
#include <QTextCodec>
#ifdef _WINDOWS
#include <Windows.h>
#endif
int main() {
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
#ifdef _WINDOWS
SetConsoleOutputCP(CP_UTF8);
#endif
testing::InitGoogleTest();
RUN_ALL_TESTS();
return 0;
}
#endif