#include "userwidget.h" #include #include #include #include #include UserWidget::UserWidget(UserManager* userManager, QWidget *parent) : QWidget(parent) , m_userManager(userManager) , m_currentFilter(FilterAll) , m_contextMenu(nullptr) { setupUI(); m_tableWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_tableWidget, &QTableWidget::customContextMenuRequested, this, &UserWidget::showContextMenu); setupContextMenu(); if (m_userManager) { connectSignals(); } m_refreshTimer = new QTimer(this); connect(m_refreshTimer, &QTimer::timeout, this, &UserWidget::onRefreshTimer); m_refreshTimer->start(30000); refreshData(); } UserWidget::~UserWidget() { if (m_refreshTimer) { m_refreshTimer->stop(); } } void UserWidget::setUserManager(UserManager* userManager) { if (m_userManager) { disconnectSignals(); } m_userManager = userManager; if (m_userManager) { connectSignals(); } refreshData(); } void UserWidget::connectSignals() { connect(m_userManager, &UserManager::userAdded, this, &UserWidget::onUserAdded); connect(m_userManager, &UserManager::userUpdated, this, &UserWidget::onUserUpdated); connect(m_userManager, &UserManager::userRemoved, this, &UserWidget::onUserRemoved); } void UserWidget::disconnectSignals() { disconnect(m_userManager, &UserManager::userAdded, this, &UserWidget::onUserAdded); disconnect(m_userManager, &UserManager::userUpdated, this, &UserWidget::onUserUpdated); disconnect(m_userManager, &UserManager::userRemoved, this, &UserWidget::onUserRemoved); } void UserWidget::setupUI() { // ------------------------------------------------------------ // 1. Глобальные настройки самого виджета // ------------------------------------------------------------ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setMinimumSize(400, 300); // гарантия, что виджет не сожмётся до нуля // ------------------------------------------------------------ // 2. Главный вертикальный layout // ------------------------------------------------------------ QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(5, 5, 5, 5); mainLayout->setSpacing(5); mainLayout->setSizeConstraint(QLayout::SetDefaultConstraint); // ------------------------------------------------------------ // 3. Панель инструментов (фильтр, поиск, кнопка обновления) // ------------------------------------------------------------ QHBoxLayout* toolbarLayout = new QHBoxLayout(); toolbarLayout->setSpacing(5); QLabel* filterLabel = new QLabel("Фильтр:", this); m_filterCombo = new QComboBox(this); m_filterCombo->addItem("Все зрители", FilterAll); m_filterCombo->addItem("Модераторы", FilterModerators); m_filterCombo->addItem("VIP", FilterVIPs); m_filterCombo->addItem("Подписчики", FilterSubscribers); m_filterCombo->addItem("Активные (10 мин)", FilterActive); m_filterCombo->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); connect(m_filterCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &UserWidget::onFilterChanged); QLabel* searchLabel = new QLabel("Поиск:", this); m_searchEdit = new QLineEdit(this); m_searchEdit->setPlaceholderText("Имя зрителя..."); m_searchEdit->setClearButtonEnabled(true); m_searchEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); connect(m_searchEdit, &QLineEdit::textChanged, this, &UserWidget::onSearchTextChanged); QPushButton* refreshBtn = new QPushButton("Обновить", this); refreshBtn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); refreshBtn->setMaximumWidth(100); connect(refreshBtn, &QPushButton::clicked, this, &UserWidget::onRefreshButtonClicked); toolbarLayout->addWidget(filterLabel); toolbarLayout->addWidget(m_filterCombo); toolbarLayout->addWidget(searchLabel); toolbarLayout->addWidget(m_searchEdit, 1); toolbarLayout->addStretch(); toolbarLayout->addWidget(refreshBtn); // ------------------------------------------------------------ // 4. Таблица пользователей // ------------------------------------------------------------ m_tableWidget = new QTableWidget(this); m_tableWidget->setColumnCount(6); m_tableWidget->setHorizontalHeaderLabels( QStringList() << "Имя" << "Сообщений" << "Статус" << "VIP" << "Подписчик" << "Последняя активность"); m_tableWidget->setSortingEnabled(true); m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); m_tableWidget->setAlternatingRowColors(true); m_tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_tableWidget->verticalHeader()->setVisible(false); // Политика размеров таблицы – активное расширение m_tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_tableWidget->setMinimumHeight(200); // минимальная высота для отображения хоть чего-то // Настройка ширины колонок (интерактивно, последняя растягивается) QHeaderView* header = m_tableWidget->horizontalHeader(); header->setStretchLastSection(true); header->setSectionResizeMode(QHeaderView::Interactive); // Начальные предпочтительные ширины m_tableWidget->setColumnWidth(0, 200); m_tableWidget->setColumnWidth(1, 80); m_tableWidget->setColumnWidth(2, 100); m_tableWidget->setColumnWidth(3, 50); m_tableWidget->setColumnWidth(4, 80); connect(m_tableWidget, &QTableWidget::cellDoubleClicked, this, &UserWidget::onUserDoubleClicked); // ------------------------------------------------------------ // 5. Панель статистики // ------------------------------------------------------------ QHBoxLayout* statsLayout = new QHBoxLayout(); m_statsLabel = new QLabel(this); m_statsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); statsLayout->addWidget(m_statsLabel); statsLayout->addStretch(); // ------------------------------------------------------------ // 6. Сборка интерфейса // ------------------------------------------------------------ mainLayout->addLayout(toolbarLayout); mainLayout->addWidget(m_tableWidget, 1); // растягиваем таблицу с коэффициентом 1 mainLayout->addLayout(statsLayout); setLayout(mainLayout); // Принудительно обновляем геометрию после установки layout updateGeometry(); } void UserWidget::refreshData() { if (!m_userManager) { m_tableWidget->setRowCount(0); m_statsLabel->setText("Менеджер пользователей не установлен"); return; } clearTable(); populateTable(); updateStatistics(); } void UserWidget::populateTable() { if (!m_userManager) return; QVector users; switch (m_currentFilter) { case FilterAll: for (const User& user : m_userManager->getAllUsers()) { users.append(const_cast(&user)); } break; case FilterModerators: users = m_userManager->getModerators(); break; case FilterVIPs: users = m_userManager->getVIPs(); break; case FilterSubscribers: users = m_userManager->getSubscribers(); break; case FilterActive: users = m_userManager->getActiveUsers(10); break; } if (!m_searchText.isEmpty()) { QString searchLower = m_searchText.toLower(); QVector filteredUsers; for (User* user : users) { if (user->displayName.toLower().contains(searchLower) || user->login.toLower().contains(searchLower)) { filteredUsers.append(user); } } users = filteredUsers; } m_tableWidget->setRowCount(users.size()); for (int i = 0; i < users.size(); ++i) { User* user = users[i]; QTableWidgetItem* nameItem = new QTableWidgetItem(user->displayName); nameItem->setData(Qt::UserRole, user->id); m_tableWidget->setItem(i, 0, nameItem); m_tableWidget->setItem(i, 1, new QTableWidgetItem(QString::number(user->messageCount))); QString status; if (user->isModerator) status = "👑 Модератор"; else if (user->isVIP) status = "⭐ VIP"; else if (user->isSubscriber) status = "💎 Подписчик"; else status = "👤 Зритель"; m_tableWidget->setItem(i, 2, new QTableWidgetItem(status)); QTableWidgetItem* vipItem = new QTableWidgetItem(user->isVIP ? "Да" : "Нет"); if (user->isVIP) vipItem->setForeground(Qt::darkGreen); m_tableWidget->setItem(i, 3, vipItem); QTableWidgetItem* subItem = new QTableWidgetItem(user->isSubscriber ? "Да" : "Нет"); if (user->isSubscriber) subItem->setForeground(Qt::darkBlue); m_tableWidget->setItem(i, 4, subItem); QString lastActive; if (user->lastMessageTime.isValid()) { QDateTime now = QDateTime::currentDateTime(); qint64 secs = user->lastMessageTime.secsTo(now); if (secs < 60) lastActive = "Только что"; else if (secs < 3600) lastActive = QString("%1 мин назад").arg(secs / 60); else if (secs < 86400) lastActive = QString("%1 ч назад").arg(secs / 3600); else lastActive = user->lastMessageTime.toString("dd.MM.yyyy hh:mm"); } else { lastActive = "Никогда"; } m_tableWidget->setItem(i, 5, new QTableWidgetItem(lastActive)); } // Автоподгон ширины колонок с ограничением m_tableWidget->resizeColumnsToContents(); for (int col = 0; col < m_tableWidget->columnCount() - 1; ++col) { if (m_tableWidget->columnWidth(col) > 300) { m_tableWidget->setColumnWidth(col, 300); } } } void UserWidget::updateStatistics() { if (!m_userManager) return; QString stats = QString("Зрителей: %1 | Сообщений: %2 | Модераторов: %3 | VIP: %4 | Подписчиков: %5") .arg(m_userManager->getUserCount()) .arg(m_userManager->getMessageCount()) .arg(m_userManager->getModerators().size()) .arg(m_userManager->getVIPs().size()) .arg(m_userManager->getSubscribers().size()); m_statsLabel->setText(stats); } void UserWidget::addUserToTable(const User &user) { int row = m_tableWidget->rowCount(); m_tableWidget->insertRow(row); QTableWidgetItem* nameItem = new QTableWidgetItem(user.displayName); nameItem->setData(Qt::UserRole, user.id); m_tableWidget->setItem(row, 0, nameItem); m_tableWidget->setItem(row, 1, new QTableWidgetItem(QString::number(user.messageCount))); QString status; if (user.isModerator) status = "👑 Модератор"; else if (user.isVIP) status = "⭐ VIP"; else if (user.isSubscriber) status = "💎 Подписчик"; else status = "👤 Зритель"; m_tableWidget->setItem(row, 2, new QTableWidgetItem(status)); QTableWidgetItem* vipItem = new QTableWidgetItem(user.isVIP ? "Да" : "Нет"); if (user.isVIP) vipItem->setForeground(Qt::darkGreen); m_tableWidget->setItem(row, 3, vipItem); QTableWidgetItem* subItem = new QTableWidgetItem(user.isSubscriber ? "Да" : "Нет"); if (user.isSubscriber) subItem->setForeground(Qt::darkBlue); m_tableWidget->setItem(row, 4, subItem); m_tableWidget->setItem(row, 5, new QTableWidgetItem("Только что")); updateStatistics(); } void UserWidget::updateUserInTable(const User &user) { for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->data(Qt::UserRole).toString() == user.id) { item->setText(user.displayName); m_tableWidget->item(row, 1)->setText(QString::number(user.messageCount)); QString status; if (user.isModerator) status = "👑 Модератор"; else if (user.isVIP) status = "⭐ VIP"; else if (user.isSubscriber) status = "💎 Подписчик"; else status = "👤 Зритель"; m_tableWidget->item(row, 2)->setText(status); m_tableWidget->item(row, 3)->setText(user.isVIP ? "Да" : "Нет"); m_tableWidget->item(row, 3)->setForeground(user.isVIP ? Qt::darkGreen : Qt::black); m_tableWidget->item(row, 4)->setText(user.isSubscriber ? "Да" : "Нет"); m_tableWidget->item(row, 4)->setForeground(user.isSubscriber ? Qt::darkBlue : Qt::black); QString lastActive; if (user.lastMessageTime.isValid()) { QDateTime now = QDateTime::currentDateTime(); qint64 secs = user.lastMessageTime.secsTo(now); if (secs < 60) lastActive = "Только что"; else if (secs < 3600) lastActive = QString("%1 мин назад").arg(secs / 60); else if (secs < 86400) lastActive = QString("%1 ч назад").arg(secs / 3600); else lastActive = user.lastMessageTime.toString("dd.MM hh:mm"); } m_tableWidget->item(row, 5)->setText(lastActive); break; } } updateStatistics(); } void UserWidget::removeUserFromTable(const QString &displayName) { for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->text() == displayName) { m_tableWidget->removeRow(row); updateStatistics(); break; } } } void UserWidget::clearTable() { m_tableWidget->setRowCount(0); } void UserWidget::onRefreshTimer() { if (!m_userManager) return; QDateTime now = QDateTime::currentDateTime(); for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* idItem = m_tableWidget->item(row, 0); if (idItem) { QString userId = idItem->data(Qt::UserRole).toString(); User* user = m_userManager->findUserById(userId); if (user && user->lastMessageTime.isValid()) { qint64 secs = user->lastMessageTime.secsTo(now); QString lastActive; if (secs < 60) lastActive = "Только что"; else if (secs < 3600) lastActive = QString("%1 мин назад").arg(secs / 60); else if (secs < 86400) lastActive = QString("%1 ч назад").arg(secs / 3600); else lastActive = user->lastMessageTime.toString("dd.MM hh:mm"); if (m_tableWidget->item(row, 5)) { m_tableWidget->item(row, 5)->setText(lastActive); } } } } } bool UserWidget::userMatchesFilter(const User* user) const { if (!user) return false; switch (m_currentFilter) { case FilterAll: break; case FilterModerators: if (!user->isModerator) return false; break; case FilterVIPs: if (!user->isVIP) return false; break; case FilterSubscribers: if (!user->isSubscriber) return false; break; case FilterActive: if (user->lastMessageTime.secsTo(QDateTime::currentDateTime()) > 10 * 60) return false; break; } if (!m_searchText.isEmpty()) { QString searchLower = m_searchText.toLower(); if (!user->displayName.toLower().contains(searchLower) && !user->login.toLower().contains(searchLower)) { return false; } } return true; } void UserWidget::onUserAdded(const User &user) { if (userMatchesFilter(&user)) { addUserToTable(user); } } void UserWidget::onUserUpdated(const User &user) { int row = findRowByUserId(user.id); if (row >= 0) { if (userMatchesFilter(&user)) { updateUserInTable(user); } else { removeUserFromTableByUserId(user.id); } } else { if (userMatchesFilter(&user)) { addUserToTable(user); } } } int UserWidget::findRowByUserId(const QString &userId) const { for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->data(Qt::UserRole).toString() == userId) { return row; } } return -1; } void UserWidget::removeUserFromTableByUserId(const QString &userId) { int row = findRowByUserId(userId); if (row >= 0) { m_tableWidget->removeRow(row); updateStatistics(); } } void UserWidget::onUserRemoved(const QString &userId, const QString &displayName) { Q_UNUSED(displayName); removeUserFromTableByUserId(userId); } void UserWidget::onUserDoubleClicked(int row, int column) { Q_UNUSED(column); if (!m_userManager) return; QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item) { QString userId = item->data(Qt::UserRole).toString(); User* user = m_userManager->findUserById(userId); if (user) { qDebug() << "Детали зрителя:" << "\nИмя:" << user->displayName << "\nID:" << user->id << "\nLogin:" << user->login << "\nСообщений:" << user->messageCount << "\nМодератор:" << user->isModerator << "\nVIP:" << user->isVIP << "\nПодписчик:" << user->isSubscriber << "\nПоследнее сообщение:" << user->lastMessageTime.toString(); } } } void UserWidget::onFilterChanged(int index) { m_currentFilter = static_cast(m_filterCombo->itemData(index).toInt()); refreshData(); } void UserWidget::onSearchTextChanged(const QString &text) { m_searchText = text; refreshData(); } void UserWidget::onRefreshButtonClicked() { refreshData(); } void UserWidget::setupContextMenu() { m_contextMenu = new QMenu(this); m_banAction = m_contextMenu->addAction("Забанить"); connect(m_banAction, &QAction::triggered, this, &UserWidget::onBanUser); m_timeoutAction = m_contextMenu->addAction("Таймаут (5 мин)"); connect(m_timeoutAction, &QAction::triggered, this, &UserWidget::onTimeoutUser); m_unbanAction = m_contextMenu->addAction("Разбанить"); connect(m_unbanAction, &QAction::triggered, this, &UserWidget::onUnbanUser); m_contextMenu->addSeparator(); m_setModAction = m_contextMenu->addAction("Сделать модератором"); connect(m_setModAction, &QAction::triggered, this, &UserWidget::onSetModerator); m_removeModAction = m_contextMenu->addAction("Забрать модератора"); connect(m_removeModAction, &QAction::triggered, this, &UserWidget::onRemoveModerator); m_contextMenu->addSeparator(); m_setVIPAction = m_contextMenu->addAction("Сделать VIP"); connect(m_setVIPAction, &QAction::triggered, this, &UserWidget::onSetVIP); m_removeVIPAction = m_contextMenu->addAction("Забрать VIP"); connect(m_removeVIPAction, &QAction::triggered, this, &UserWidget::onRemoveVIP); m_contextMenu->addSeparator(); m_infoAction = m_contextMenu->addAction("Информация"); connect(m_infoAction, &QAction::triggered, this, &UserWidget::onShowUserInfo); } void UserWidget::showContextMenu(const QPoint &pos) { QTableWidgetItem* item = m_tableWidget->itemAt(pos); if (!item) return; int row = item->row(); QTableWidgetItem* nameItem = m_tableWidget->item(row, 0); if (nameItem) { m_selectedUserId = nameItem->data(Qt::UserRole).toString(); m_selectedUserName = nameItem->text(); User* user = m_userManager->findUserById(m_selectedUserId); if (user) { m_setModAction->setEnabled(!user->isModerator); m_removeModAction->setEnabled(user->isModerator); m_setVIPAction->setEnabled(!user->isVIP); m_removeVIPAction->setEnabled(user->isVIP); m_contextMenu->exec(m_tableWidget->viewport()->mapToGlobal(pos)); } } } User* UserWidget::getSelectedUser() { if (!m_selectedUserId.isEmpty()) { return m_userManager->findUserById(m_selectedUserId); } return nullptr; } void UserWidget::onBanUser() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit banUserRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onTimeoutUser() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit timeoutUserRequested(m_selectedUserId, m_selectedUserName, 5); } } void UserWidget::onUnbanUser() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit unbanUserRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onSetModerator() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit setModeratorRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onRemoveModerator() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit removeModeratorRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onSetVIP() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit setVIPRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onRemoveVIP() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit removeVIPRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onShowUserInfo() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit userInfoRequested(m_selectedUserId, m_selectedUserName); } }