first commit

This commit is contained in:
2026-01-02 19:20:35 +09:00
commit a10cb30c4a
94 changed files with 28609 additions and 0 deletions

View File

@@ -0,0 +1,361 @@
#include "tableoverlaywidget.h"
#include <QPropertyAnimation>
#include <QGraphicsOpacityEffect>
#include <QScrollBar>
#include <QDebug>
TableOverlayWidget::TableOverlayWidget(QWidget *parent)
: QWidget(parent), m_isVisible(false)
{
setupUI();
hide(); // 初始隐藏
}
void TableOverlayWidget::setupUI()
{
// 设置浮层属性
setAttribute(Qt::WA_StyledBackground);
setStyleSheet("TableOverlayWidget { background: transparent; }");
// 主布局
auto *mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
// 左侧标签栏滚动区域
m_tabScrollArea = new QScrollArea(this);
m_tabScrollArea->setFixedWidth(130);
m_tabScrollArea->setWidgetResizable(true);
m_tabScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_tabScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
m_tabScrollArea->setStyleSheet(
"QScrollArea { "
" background: #34495e; "
" border-right: 1px solid #2c3e50; "
" border: none; "
"}"
"QScrollBar:vertical { "
" background: #2c3e50; "
" width: 10px; "
" margin: 0px; "
"}"
"QScrollBar::handle:vertical { "
" background: #546e7a; "
" min-height: 20px; "
" border-radius: 5px; "
"}"
"QScrollBar::handle:vertical:hover { "
" background: #607d8b; "
"}"
"QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { "
" height: 0px; "
"}"
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { "
" background: none; "
"}");
// 标签栏容器
m_tabBar = new QWidget();
m_tabBar->setStyleSheet(
"QWidget { "
" background: #34495e; "
"}");
m_tabLayout = new QVBoxLayout(m_tabBar);
m_tabLayout->setContentsMargins(8, 12, 8, 12);
m_tabLayout->setSpacing(6);
m_tabLayout->setAlignment(Qt::AlignTop);
m_tabScrollArea->setWidget(m_tabBar);
mainLayout->addWidget(m_tabScrollArea);
// 内容容器(右侧)
m_container = new QWidget(this);
m_container->setStyleSheet(
"QWidget { "
" background: #f5f5f5; "
" border: none; "
" border-top-right-radius: 10px; "
" border-bottom-right-radius: 10px; "
"}");
auto *containerLayout = new QVBoxLayout(m_container);
containerLayout->setContentsMargins(0, 0, 0, 0);
containerLayout->setSpacing(0);
// 标题栏
auto *headerWidget = new QWidget(this);
headerWidget->setStyleSheet(
"QWidget { "
" background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #3498db, stop:1 #2980b9); "
" border-top-left-radius: 0px; "
" border-top-right-radius: 10px; "
"}");
auto *headerLayout = new QHBoxLayout(headerWidget);
headerLayout->setContentsMargins(20, 10, 10, 10);
headerLayout->setSpacing(8);
m_titleLabel = new QLabel("表格", this);
m_titleLabel->setStyleSheet(
"QLabel { "
" color: white; "
" font-size: 14pt; "
" font-weight: bold; "
" background: transparent; "
"}");
headerLayout->addWidget(m_titleLabel);
headerLayout->addStretch();
// 关闭按钮 - 正方形圆角,图标居中
m_closeBtn = new QPushButton("×", this);
m_closeBtn->setFixedSize(32, 32);
m_closeBtn->setCursor(Qt::PointingHandCursor);
m_closeBtn->setStyleSheet(
"QPushButton { "
" background: rgba(255,255,255,0.2); "
" color: white; "
" border: none; "
" border-radius: 6px; "
" font-size: 20px; "
" font-weight: normal; "
" text-align: center; "
" padding: 0px; "
" margin: 0px; "
"}"
"QPushButton:hover { background: rgba(255,255,255,0.35); }"
"QPushButton:pressed { background: rgba(255,255,255,0.5); }");
connect(m_closeBtn, &QPushButton::clicked, this, &TableOverlayWidget::onCloseButtonClicked);
headerLayout->addWidget(m_closeBtn);
containerLayout->addWidget(headerWidget);
// 内容堆栈
m_contentStack = new QStackedWidget(this);
m_contentStack->setStyleSheet(
"QStackedWidget { "
" background: white; "
" border: none; "
"}");
containerLayout->addWidget(m_contentStack, 1);
mainLayout->addWidget(m_container, 1);
// 设置默认大小占父容器的70%宽度90%高度)
setMinimumWidth(600);
setMaximumWidth(1200);
}
void TableOverlayWidget::addTable(const QString &tableId, const QString &tableName, QWidget *tableWidget)
{
if (m_tables.contains(tableId))
{
qDebug() << "Table already exists:" << tableId;
return;
}
TableTab tab;
tab.id = tableId;
tab.name = tableName;
tab.widget = tableWidget;
// 创建标签按钮
tab.tabButton = new QPushButton(tableName, m_tabBar);
tab.tabButton->setFixedHeight(48);
tab.tabButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
tab.tabButton->setProperty("tableId", tableId);
tab.tabButton->setCursor(Qt::PointingHandCursor);
tab.tabButton->setStyleSheet(
"QPushButton {"
" background: #455a64;"
" color: #ecf0f1;"
" border: none;"
" border-radius: 6px;"
" padding: 6px 6px;"
" font-size: 9.5pt;"
" font-weight: 500;"
" text-align: center;"
" border-left: 3px solid transparent;"
"}"
"QPushButton:hover {"
" background: #546e7a;"
" color: white;"
"}"
"QPushButton:checked {"
" background: #2980b9;"
" color: white;"
" font-weight: bold;"
" border-left: 3px solid #1976d2;"
"}"
"QPushButton:pressed {"
" background: #21618c;"
"}");
tab.tabButton->setCheckable(true);
connect(tab.tabButton, &QPushButton::clicked, this, &TableOverlayWidget::onTabButtonClicked);
// 添加到布局
m_tabLayout->addWidget(tab.tabButton);
// 添加到内容堆栈
m_contentStack->addWidget(tableWidget);
m_tables[tableId] = tab;
// 如果是第一个表格,自动显示
if (m_tables.size() == 1)
{
showTable(tableId);
}
}
void TableOverlayWidget::showTable(const QString &tableId)
{
if (!m_tables.contains(tableId))
{
qDebug() << "Table not found:" << tableId;
return;
}
// 取消之前的选中状态
if (!m_currentTableId.isEmpty() && m_tables.contains(m_currentTableId))
{
m_tables[m_currentTableId].tabButton->setChecked(false);
}
// 切换到新表格
m_currentTableId = tableId;
const TableTab &tab = m_tables[tableId];
tab.tabButton->setChecked(true);
m_contentStack->setCurrentWidget(tab.widget);
m_titleLabel->setText(tab.name);
emit tableChanged(tableId);
}
void TableOverlayWidget::removeTable(const QString &tableId)
{
if (!m_tables.contains(tableId))
return;
TableTab tab = m_tables.take(tableId);
// 移除按钮
m_tabLayout->removeWidget(tab.tabButton);
tab.tabButton->deleteLater();
// 移除内容
m_contentStack->removeWidget(tab.widget);
// 如果删除的是当前表格,切换到第一个
if (m_currentTableId == tableId && !m_tables.isEmpty())
{
showTable(m_tables.firstKey());
}
else if (m_tables.isEmpty())
{
m_currentTableId.clear();
hideOverlay();
}
}
void TableOverlayWidget::clearTables()
{
// 清除所有表格
for (const QString &tableId : m_tables.keys())
{
removeTable(tableId);
}
m_currentTableId.clear();
}
void TableOverlayWidget::showOverlay()
{
if (m_isVisible || m_tables.isEmpty())
return;
// 如果当前没有选中的表格,显示第一个
if (m_currentTableId.isEmpty() && !m_tables.isEmpty())
{
showTable(m_tables.firstKey());
}
m_isVisible = true;
m_container->show();
m_tabBar->show();
show();
animateShow();
}
void TableOverlayWidget::hideOverlay()
{
if (!m_isVisible)
return;
m_isVisible = false;
animateHide();
}
void TableOverlayWidget::toggleOverlay()
{
if (m_isVisible)
hideOverlay();
else
showOverlay();
}
void TableOverlayWidget::onTabButtonClicked()
{
QPushButton *btn = qobject_cast<QPushButton *>(sender());
if (!btn)
return;
QString tableId = btn->property("tableId").toString();
if (!tableId.isEmpty())
{
showTable(tableId);
}
}
void TableOverlayWidget::onCloseButtonClicked()
{
hideOverlay();
emit closed();
}
void TableOverlayWidget::updateTabButtons()
{
for (const TableTab &tab : m_tables)
{
tab.tabButton->setChecked(tab.id == m_currentTableId);
}
}
void TableOverlayWidget::animateShow()
{
// 淡入动画
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(this);
setGraphicsEffect(effect);
QPropertyAnimation *animation = new QPropertyAnimation(effect, "opacity");
animation->setDuration(200);
animation->setStartValue(0.0);
animation->setEndValue(1.0);
animation->setEasingCurve(QEasingCurve::OutCubic);
animation->start(QPropertyAnimation::DeleteWhenStopped);
}
void TableOverlayWidget::animateHide()
{
// 淡出动画
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(this);
setGraphicsEffect(effect);
QPropertyAnimation *animation = new QPropertyAnimation(effect, "opacity");
animation->setDuration(150);
animation->setStartValue(1.0);
animation->setEndValue(0.0);
animation->setEasingCurve(QEasingCurve::InCubic);
connect(animation, &QPropertyAnimation::finished, this, &TableOverlayWidget::hide);
animation->start(QPropertyAnimation::DeleteWhenStopped);
}