362 lines
9.2 KiB
C++
362 lines
9.2 KiB
C++
#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);
|
||
}
|