#include "MainPanel.h" #include "utils/iconutils.h" // Qt Core #include #include #include #include #include #include #include #include // Qt Widgets #include #include #include #include #include #include // Third Party #include #include #include // Project Includes #include "AVPlayer/avplayerwidget.h" #include "api/roomapi.h" #include "appevent.h" #include "network/websocketclient.h" #include "themesettingswidget.h" #include "widgets/bubbletip.h" #include "widgets/chatView/chatwindow.h" #include "widgets/framelessbase.h" #include "widgets/functionbutton.h" #include "widgets/maskoverlay.h" #include "widgets/recorderwidget.h" #include "widgets/statswidget.h" #include "widgets/userprofilewidget.h" #include MainPanel::MainPanel(QWidget *parent) : TWidget(parent) , chatView(nullptr) { setAttribute(Qt::WA_StyledBackground, true); // ========== 初始化播放控制组件 ========== initPlaybackControls(); // ========== 初始化聊天相关组件 ========== initChatComponents(); // ========== 初始化布局组件 ========== initLayoutComponents(); // ========== 初始化功能按钮 ========== initFunctionButtons(); // ========== 连接信号槽 ========== connectSignals(); // ========== 初始化音频设备 ========== initAudioDeviceSelectors(); } void MainPanel::initPlaybackControls() { m_debounceTimer = new QTimer(this); m_debounceTimer->setInterval(500); m_debounceTimer->setSingleShot(true); connect(m_debounceTimer, &QTimer::timeout, this, &MainPanel::handleDebouncedPlay); } void MainPanel::initChatComponents() { webSocketClient = new WebSocketClient(this); // 启用WebSocket自动重连,提升聊天室稳定性 if (webSocketClient) { webSocketClient->setAutoReconnect(true); } chatView = new ChatWindow(webSocketClient); chatView->setMinimumWidth(400); // 防御:明确不随关闭销毁,避免父窗口关闭时误删 chatView->setAttribute(Qt::WA_DeleteOnClose, false); // 连接聊天窗口关闭请求信号 connect(chatView, &ChatWindow::windowCloseRequested, this, &MainPanel::onChatWindowCloseRequested); } void MainPanel::initLayoutComponents() { // 创建右侧面板 m_rightWidget = new QWidget; QVBoxLayout *vbox = new QVBoxLayout(m_rightWidget); vbox->setContentsMargins(0, 0, 0, 0); // 创建聊天窗口容器 m_chatContainer = new QWidget(m_rightWidget); QVBoxLayout *chatLayout = new QVBoxLayout(m_chatContainer); chatLayout->setContentsMargins(0, 0, 0, 0); chatLayout->addWidget(chatView); vbox->addWidget(m_chatContainer, 1); // 创建主分割器 splitter = new QSplitter(Qt::Horizontal, this); playerContainer = new QWidget(this); splitter->addWidget(playerContainer); splitter->addWidget(m_rightWidget); splitter->setStretchFactor(0, 60); splitter->setStretchFactor(1, 30); this->mainLayout()->addWidget(splitter, 1); this->mainLayout()->setContentsMargins(0, 0, 0, 0); this->mainLayout()->setSpacing(0); // 为playerContainer设置初始布局 QVBoxLayout *playerLayout = new QVBoxLayout(playerContainer); playerLayout->setContentsMargins(0, 0, 0, 0); } void MainPanel::initFunctionButtons() { buttonGroup = new PopoverButtonGroup(Qt::Horizontal, playerContainer); // 添加功能按钮 // FunctionButton *settingsBtn = new FunctionButton(IconUtils::createSettingsIcon(), "设置", this); // // 移除 Popover 箭头,改为直接打开设置窗口 // buttonGroup->addButton(settingsBtn, nullptr); // connect(settingsBtn, &QPushButton::clicked, this, &MainPanel::onSettingsButtonClicked); // 移除:搜索按钮(暂不使用) // FunctionButton *searchBtn = new FunctionButton(IconUtils::createSearchIcon(), "搜索", this); // Popover *searchPopover = new Popover(this); // buttonGroup->addButton(searchBtn, searchPopover); FunctionButton *userBtn = new FunctionButton(IconUtils::createUserIcon(), "用户", this); Popover *userPopover = new Popover(this); // 构建用户Popover内容:提供“退出”操作 { QWidget *userContent = new QWidget(userPopover); QVBoxLayout *userLayout = new QVBoxLayout(userContent); userLayout->setContentsMargins(8, 8, 8, 8); userLayout->setSpacing(8); QPushButton *logoutBtn = new QPushButton(tr("退出"), userContent); logoutBtn->setMinimumWidth(120); connect(logoutBtn, &QPushButton::clicked, this, [this]() { emit logoutClicked(); }); userLayout->addWidget(logoutBtn); userPopover->setContentWidget(userContent); } buttonGroup->addButton(userBtn, userPopover); // 添加音频设备选择按钮 FunctionButton *audioDeviceBtn = new FunctionButton(IconUtils::createAudioDeviceIcon(), "音频设备", this); Popover *audioDevicePopover = new Popover(this); // // 使用解耦合版本的音频设备选择器 // m_audioDeviceSelectorDecoupled = new AudioDeviceSelectorIconDecoupled(this); // audioDevicePopover->setContentWidget(m_audioDeviceSelectorDecoupled); // 使用 RecorderAudioWidget 作为 Popover 内容(麦克风 + 扬声器) QWidget *audioContent = new QWidget(audioDevicePopover); QVBoxLayout *audioLayout = new QVBoxLayout(audioContent); audioLayout->setContentsMargins(8, 8, 8, 8); audioLayout->setSpacing(8); // 麦克风区域 QLabel *micTitle = new QLabel(tr("麦克风"), audioContent); micTitle->setStyleSheet("font-weight:600;"); m_micWidget = new QComboBox(audioContent); m_micWidget->setEditable(false); audioLayout->addWidget(micTitle); audioLayout->addWidget(m_micWidget); // 扬声器区域 QLabel *speakerTitle = new QLabel(tr("扬声器"), audioContent); speakerTitle->setStyleSheet("font-weight:600;"); m_speakerWidget = new QComboBox(audioContent); m_speakerWidget->setEditable(false); audioLayout->addWidget(speakerTitle); audioLayout->addWidget(m_speakerWidget); // 视频编码器区域 QLabel *encoderTitle = new QLabel(tr("视频编码器"), audioContent); encoderTitle->setStyleSheet("font-weight:600;"); m_encoderWidget = new QComboBox(audioContent); m_encoderWidget->setEditable(false); audioLayout->addWidget(encoderTitle); audioLayout->addWidget(m_encoderWidget); audioDevicePopover->setContentWidget(audioContent); buttonGroup->addButton(audioDeviceBtn, audioDevicePopover); // 新增:独立的推流按钮(无弹层) m_streamButton = new FunctionButton(IconUtils::createStreamIcon(), tr("推流"), this); buttonGroup->addButton(m_streamButton); // 新增:聊天按钮(推流:显示/隐藏;非推流:弹出/嵌入) m_chatButton = new FunctionButton(IconUtils::createChatIcon(), tr("聊天"), this); buttonGroup->addButton(m_chatButton); // 移除:执行操作按钮(暂不使用) // FunctionButton *actionButton = new FunctionButton(IconUtils::createSettingsIcon(), // "执行操作", // this); // buttonGroup->addButton(actionButton, nullptr); // 将buttonGroup添加到playerContainer的布局中 QVBoxLayout *playerLayout = qobject_cast(playerContainer->layout()); if (!playerLayout) { playerLayout = new QVBoxLayout(playerContainer); playerLayout->setContentsMargins(0, 0, 0, 0); } playerLayout->addStretch(1); // 添加弹性空间,将buttonGroup推到底部 playerLayout->addWidget(buttonGroup, 0); // 添加buttonGroup到底部,不拉伸 buttonGroup->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // 使用固定大小策略 } void MainPanel::connectSignals() { // 连接功能按钮信号 connect(m_streamButton, &QPushButton::clicked, this, &MainPanel::onStreamButtonClicked); connect(m_chatButton, &QPushButton::clicked, this, &MainPanel::onChatButtonClicked); // 连接WebSocket相关信号 // 已移除与 UserProfileWidget 相关的在线状态更新 // connect(webSocketClient, &WebSocketClient::statsUpdate, statsWidget, &StatsWidget::updateStats); // 暂时移除统计在主面板的更新 connect(webSocketClient, &WebSocketClient::liveStatus, this, [this](const QString &msg) { // 这里可以处理 liveStatus 相关逻辑 QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(msg.toUtf8(), &err); if (err.error != QJsonParseError::NoError || !doc.isObject()) { qDebug() << "[MainPanel] liveStatus: 解析失败" << err.errorString(); return; } QJsonObject obj = doc.object(); int liveStatus = obj.value("liveStatus").toInt(0); // 默认-1 if (liveStatus == 1) { qDebug() << "[MainPanel] liveStatus: 直播中" << chatView; if (chatView) { const QString id = webSocketClient->roomId(); // 使用防抖机制处理频繁的请求 m_pendingRoomId = id; m_debounceTimer->start(); // 重新开始计时,如果在500ms内再次收到请求,会重置定时器 } } }); // 初始化音频设备列表 initAudioDeviceSelectors(); } MainPanel::~MainPanel() { if (m_avPlayerStandalone) { m_avPlayerStandalone->deleteLater(); m_avPlayerStandalone = nullptr; } if (m_recorderStandalone) { m_recorderStandalone->deleteLater(); m_recorderStandalone = nullptr; } } void MainPanel::setRole(const QStringList &roleList) { bool isRec = roleList.contains("role.admin") || roleList.contains("role.recorder") || roleList.contains("录制"); if (isRec) { auto rec = new RecorderWidget(this); setPlayerWidget(rec); } else { auto av = new AVPlayerWidget(this); setPlayerWidget(av); } if (m_streamButton) { m_streamButton->setVisible(isRec); m_streamButton->setText(tr("推流")); } } void MainPanel::setPushRoomId(const QString &id) { if (!webSocketClient) return; // 初始化聊天室连接 webSocketClient->connectToRoom(id); if (chatView) { chatView->initWebsocket(id); } // 若当前是播放器,使用防抖播放 m_pendingRoomId = id; m_debounceTimer->start(); } void MainPanel::setPlayerWidget(QWidget *newPlayer) { if (!newPlayer) return; if (!playerContainer) return; if (playerWidget) { playerWidget->deleteLater(); playerWidget = nullptr; } playerWidget = newPlayer; // 将新播放器添加到容器布局 if (auto layout = qobject_cast(playerContainer->layout())) { layout->insertWidget(0, playerWidget, 1); } else { auto layout2 = new QVBoxLayout(playerContainer); layout2->setContentsMargins(0, 0, 0, 0); layout2->setSpacing(0); layout2->addWidget(playerWidget, 1); } // 更新推流按钮可见性 if (m_streamButton) { const bool isRec = qobject_cast(playerWidget) != nullptr; m_streamButton->setVisible(isRec); if (!isRec) { m_isStreaming = false; m_streamButton->setText(tr("推流")); } } // 连接 RecorderWidget 的推流信号,保持 UI 状态同步 if (auto rec = qobject_cast(playerWidget)) { connect(rec, &RecorderWidget::streamingStarted, this, [this, rec]() { m_isStreaming = true; if (m_streamButton) m_streamButton->setText(tr("停止推流")); // 进入极简模式 if (QWidget *tlw = window()) { tlw->setUpdatesEnabled(false); } rec->hidePreview(); if (chatView) chatView->hide(); if (m_rightWidget) m_rightWidget->hide(); if (splitter) splitter->hide(); // 使用自定义标题的紧凑浮窗承载工具栏 if (!m_compactFrame) { m_compactFrame = new TMainWindow(); m_compactFrame->setWindowTitle(tr("共享控制")); m_compactFrame->setWindowFlag(Qt::Tool, true); // m_compactFrame->setAttribute(Qt::WA_DeleteOnClose); if (buttonGroup) { if (auto layout = qobject_cast(playerContainer->layout())) { layout->removeWidget(buttonGroup); } auto container = new QWidget(m_compactFrame); auto lay = new QHBoxLayout(container); lay->setContentsMargins(12, 8, 12, 8); lay->setSpacing(10); buttonGroup->setParent(container); lay->addWidget(buttonGroup); m_compactFrame->setCentralWidget(container); // 使用 sizeHint 直接设置初始尺寸,避免反复 adjustSize 带来的重算 const QSize hint = buttonGroup->sizeHint(); int w = qMax(360, hint.width() + 24); int h = qMax(80, hint.height() + 16); int titleH = 0; if (auto mw = m_compactFrame->menuWidget()) { mw->adjustSize(); titleH = mw->sizeHint().height(); } m_compactFrame->setMinimumSize(w, h + titleH); m_compactFrame->resize(w, h + titleH); } } m_compactFrame->show(); m_compactFrame->raise(); m_compactMode = true; // 主窗口保持原几何但隐藏 if (QWidget *tlw = window()) { m_savedWindowGeometry = tlw->geometry(); tlw->hide(); } }); connect(rec, &RecorderWidget::streamingStopped, this, [this, rec]() { m_isStreaming = false; if (m_streamButton) m_streamButton->setText(tr("推流")); // 退出极简模式 if (QWidget *tlw = window()) { tlw->setUpdatesEnabled(false); } rec->showPreview(); if (splitter) splitter->show(); if (m_rightWidget) m_rightWidget->show(); // 销毁紧凑浮窗并将 buttonGroup 归还 if (m_compactFrame) { if (buttonGroup) { buttonGroup->hide(); buttonGroup->setParent(playerContainer); if (auto layout = qobject_cast(playerContainer->layout())) { layout->addWidget(buttonGroup, 0); } buttonGroup->show(); } m_compactFrame->close(); m_compactFrame->deleteLater(); m_compactFrame = nullptr; } m_compactMode = false; // 恢复主窗口显示 if (QWidget *tlw = window()) { tlw->setGeometry(m_savedWindowGeometry); tlw->show(); tlw->raise(); tlw->setUpdatesEnabled(true); } showChatEmbedded(); if (m_chatButton) m_chatButton->setText(tr("聊天")); applyModeLayout(); // 恢复布局为非推流状态 }); } // 根据当前模式(录制/播放)应用一次自适应布局 applyModeLayout(); } void MainPanel::handleDebouncedPlay() { // 在定时器触发时执行播放逻辑 const QString id = m_pendingRoomId; if (id.isEmpty()) return; qDebug() << "[MainPanel] Debounced startPlay for room" << id; AVPlayerWidget *av = qobject_cast(playerWidget); if (av) { av->stopPlay(); av->setPlayRoomId(id); av->startPlay(); return; } RecorderWidget *rec = qobject_cast(playerWidget); if (rec) { // rec->startLive(); // - RecorderWidget::Settings s = rec->m_settings; // 需要通过公共接口设置,避免直接访问成员 } } void MainPanel::initAudioDeviceSelectors() { // TODO: 枚举音频设备、填充 m_micWidget 和 m_speakerWidget;枚举编码器填充 m_encoderWidget } void MainPanel::showRecorderStandalone() { if (m_recorderFrame) { m_recorderFrame->raise(); m_recorderFrame->activateWindow(); return; } m_recorderFrame = new TMainWindow(); m_recorderFrame->setAttribute(Qt::WA_DeleteOnClose); m_recorderFrame->setWindowTitle(tr("录制器")); m_recorderStandalone = new RecorderWidget(m_recorderFrame); m_recorderFrame->setCentralWidget(m_recorderStandalone); m_recorderFrame->resize(960, 600); m_recorderFrame->show(); connect(m_recorderFrame, &QObject::destroyed, this, [this]() { m_recorderFrame = nullptr; m_recorderStandalone = nullptr; }); } void MainPanel::showPlayerStandalone() { if (m_playerFrame) { m_playerFrame->raise(); m_playerFrame->activateWindow(); return; } m_playerFrame = new TMainWindow(); m_playerFrame->setAttribute(Qt::WA_DeleteOnClose); m_playerFrame->setWindowTitle(tr("播放器")); m_avPlayerStandalone = new AVPlayerWidget(m_playerFrame); m_playerFrame->setCentralWidget(m_avPlayerStandalone); m_playerFrame->resize(960, 600); m_playerFrame->show(); connect(m_playerFrame, &QObject::destroyed, this, [this]() { m_playerFrame = nullptr; m_avPlayerStandalone = nullptr; }); } void MainPanel::showChatStandalone() { if (!chatView) return; if (m_isStreaming) return; // 推流时保持仅浮窗 // - if (m_chatFrame) { // - m_chatFrame->raise(); // - m_chatFrame->activateWindow(); // - return; // - } if (m_chatFrame) { // 确保 chatView 已正确作为中央部件挂载 if (m_chatFrame->centralWidget() != chatView && chatView) { chatView->hide(); if (chatView->parent() != m_chatFrame) { chatView->setParent(m_chatFrame); } m_chatFrame->setCentralWidget(chatView); chatView->show(); } if (!m_chatFrame->isVisible()) m_chatFrame->show(); m_chatFrame->raise(); m_chatFrame->activateWindow(); return; } // 从嵌入容器移除 if (m_chatContainer) { if (auto l = qobject_cast(m_chatContainer->layout())) { l->removeWidget(chatView); } } m_chatFrame = new TMainWindow(); m_chatFrame->setAttribute(Qt::WA_DeleteOnClose); m_chatFrame->setWindowTitle(tr("聊天")); m_chatFrame->installEventFilter(this); chatView->setParent(m_chatFrame); m_chatFrame->setCentralWidget(chatView); m_chatFrame->resize(380, 540); m_chatFrame->show(); // - connect(m_chatFrame, &QObject::destroyed, this, [this]() { // - m_chatFrame = nullptr; // - onChatWindowCloseRequested(); // - }); connect(m_chatFrame, &QObject::destroyed, this, [this]() { m_chatFrame = nullptr; }); } void MainPanel::showChatEmbedded() { if (!chatView || !m_chatContainer) return; // 如存在独立窗口包装,先将 chatView 移回容器再关闭窗口,避免被父窗口销毁 if (m_chatFrame) { // 断开临时回调,避免 destroyed 中的副作用 disconnect(m_chatFrame, nullptr, this, nullptr); if (chatView->parent() == m_chatFrame) { // 使用 takeCentralWidget 而非 setCentralWidget(nullptr) 避免误删中央部件 if (m_chatFrame->centralWidget()) { QWidget *w = m_chatFrame->takeCentralWidget(); Q_UNUSED(w); } } chatView->hide(); chatView->setParent(m_chatContainer); chatView->setWindowTitle(QString()); if (auto l = qobject_cast(m_chatContainer->layout())) { if (l->indexOf(chatView) < 0) l->addWidget(chatView); } else { auto l2 = new QVBoxLayout(m_chatContainer); l2->setContentsMargins(0, 0, 0, 0); l2->addWidget(chatView); } chatView->show(); // 现在安全地关闭独立窗口 m_chatFrame->close(); m_chatFrame = nullptr; } else { // 无独立窗口,仅确保嵌入 chatView->hide(); chatView->setParent(m_chatContainer); chatView->setWindowTitle(QString()); if (auto l = qobject_cast(m_chatContainer->layout())) { if (l->indexOf(chatView) < 0) l->addWidget(chatView); } else { auto l2 = new QVBoxLayout(m_chatContainer); l2->setContentsMargins(0, 0, 0, 0); l2->addWidget(chatView); } chatView->show(); } } void MainPanel::onRecordButtonClicked() { // TODO: 录制控制逻辑 } void MainPanel::onStreamButtonClicked() { RecorderWidget *rec = qobject_cast(playerWidget); if (!rec) { qDebug() << "[MainPanel] 当前不处于录制器模式,无法推流"; return; } if (!m_isStreaming) { rec->startStreaming(); // 成功与否由信号驱动 UI } else { rec->stopStreaming(); } } void MainPanel::onChatWindowCloseRequested() { // 如果处在推流状态,认为是“隐藏聊天”而不是关闭程序 if (m_isStreaming) { // 显式隐藏独立聊天窗口,统一行为到 eventFilter 的隐藏逻辑 if (m_chatFrame && m_chatFrame->isVisible()) { m_chatFrame->hide(); } if (m_chatButton) m_chatButton->setText(tr("显示聊天")); return; // ChatWindow 本身不要销毁 } // 非推流:回归嵌入显示 showChatEmbedded(); if (m_chatButton) m_chatButton->setText(tr("聊天")); applyModeLayout(); } void MainPanel::onChatButtonClicked() { if (!chatView) return; if (m_isStreaming) { // 推流时:只在独立聊天窗口上 显示/隐藏 切换,避免频繁 reparent if (!m_chatFrame) { // 如果当前在嵌入容器里,先从布局移除 if (m_chatContainer) { if (auto l = qobject_cast(m_chatContainer->layout())) { l->removeWidget(chatView); } } m_chatFrame = new TMainWindow(); m_chatFrame->setAttribute(Qt::WA_DeleteOnClose); m_chatFrame->setWindowTitle(tr("聊天")); m_chatFrame->installEventFilter(this); chatView->setParent(m_chatFrame); m_chatFrame->setCentralWidget(chatView); chatView->show(); m_chatFrame->resize(380, 540); m_chatFrame->show(); m_chatFrame->raise(); m_chatFrame->activateWindow(); connect(m_chatFrame, &QObject::destroyed, this, [this]() { m_chatFrame = nullptr; }); if (m_chatButton) m_chatButton->setText(tr("隐藏聊天")); return; // 结束推流分支处理 } else { // 已有独立窗口:切换显示/隐藏 if (chatView->parent() != m_chatFrame) { chatView->setParent(m_chatFrame); if (m_chatFrame->centralWidget() != chatView) m_chatFrame->setCentralWidget(chatView); } chatView->show(); if (m_chatFrame->isVisible()) { m_chatFrame->hide(); if (m_chatButton) m_chatButton->setText(tr("显示聊天")); } else { m_chatFrame->show(); m_chatFrame->raise(); m_chatFrame->activateWindow(); if (m_chatButton) m_chatButton->setText(tr("隐藏聊天")); } return; // 结束推流分支处理 } } // 非推流:在弹出与嵌入间切换 if (m_chatFrame) { // 已经是弹出状态,切回嵌入 showChatEmbedded(); if (m_chatButton) m_chatButton->setText(tr("聊天")); applyModeLayout(); } else { // 目前为嵌入状态,弹出为独立窗口 showChatStandalone(); if (m_chatButton) m_chatButton->setText(tr("嵌入聊天")); applyModeLayout(); } } // ===== 浮动工具栏 ===== void MainPanel::showFloatingToolbar() { // 改为使用 m_compactFrame 承载,无需再在主窗口中悬浮 if (!m_compactFrame) return; m_compactFrame->show(); m_compactFrame->raise(); } void MainPanel::hideFloatingToolbar() { if (m_compactFrame) m_compactFrame->hide(); } // 新增:根据模式与窗口大小自适应布局 void MainPanel::applyModeLayout() { // 若控件未初始化,直接返回 if (!splitter || !playerContainer) return; // 推流期间,主窗口处于隐藏/极简状态,这里只保证分割区域隐藏 if (m_isStreaming) { if (m_rightWidget) m_rightWidget->hide(); splitter->hide(); return; } const bool isRecorder = qobject_cast(playerWidget) != nullptr; const bool isPlayer = qobject_cast(playerWidget) != nullptr; // 保证分割窗口可见,用于布局 splitter->show(); const int panelW = width() > 0 ? width() : 1200; if (isPlayer) { // 播放模式:根据聊天是否嵌入来决定右侧面板显示 const bool embeddedChat = (chatView && m_chatContainer && chatView->parent() == m_chatContainer); if (embeddedChat) { if (m_rightWidget) m_rightWidget->show(); int chatW = panelW / 3; chatW = qBound(300, chatW, 420); const int leftW = qMax(0, panelW - chatW); QList sizes; sizes << leftW << chatW; splitter->setSizes(sizes); } else { if (m_rightWidget) m_rightWidget->hide(); QList sizes; sizes << panelW << 0; splitter->setSizes(sizes); } return; } // 录制模式(非推流):展示右侧面板,根据聊天当前状态决定布局 if (m_rightWidget) m_rightWidget->show(); // 检查聊天当前是否为嵌入状态 const bool embeddedChat = (chatView && m_chatContainer && chatView->parent() == m_chatContainer); if (embeddedChat) { // 聊天为嵌入状态,设置分割布局 int chatW = panelW / 3; chatW = qBound(300, chatW, 420); const int leftW = qMax(0, panelW - chatW); QList sizes; sizes << leftW << chatW; splitter->setSizes(sizes); } else { // 聊天为独立窗口状态,隐藏右侧面板 if (m_rightWidget) m_rightWidget->hide(); QList sizes; sizes << panelW << 0; splitter->setSizes(sizes); } } void MainPanel::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); // 随窗口大小调整左右比例 applyModeLayout(); } bool MainPanel::eventFilter(QObject *watched, QEvent *event) { // 拦截聊天独立窗口的关闭事件 if (watched == m_chatFrame && event->type() == QEvent::Close) { // 推流期间:关闭操作改为隐藏窗口,避免销毁与频繁 reparent 导致异常 if (m_isStreaming) { event->ignore(); if (m_chatFrame) { m_chatFrame->hide(); } if (m_chatButton) m_chatButton->setText(tr("显示聊天")); return true; // 事件已处理,不再继续关闭 } // 非推流:允许关闭,但先把 chatView 放回嵌入容器,避免被父窗口销毁 if (chatView && m_chatContainer && chatView->parent() == m_chatFrame) { // 取走中央部件,避免 setCentralWidget(nullptr) 触发潜在删除 if (m_chatFrame->centralWidget()) { QWidget *w = m_chatFrame->takeCentralWidget(); Q_UNUSED(w); } chatView->hide(); chatView->setParent(m_chatContainer); chatView->setWindowTitle(QString()); if (auto l = qobject_cast(m_chatContainer->layout())) { if (l->indexOf(chatView) < 0) l->addWidget(chatView); } else { auto l2 = new QVBoxLayout(m_chatContainer); l2->setContentsMargins(0, 0, 0, 0); l2->addWidget(chatView); } chatView->show(); if (m_chatButton) m_chatButton->setText(tr("聊天")); applyModeLayout(); } return false; // 继续关闭流程 } return QWidget::eventFilter(watched, event); }