#include "mainwindow.h" #include "ui/login_dialog.h" #include #include #include #include #include #include #include namespace scar { MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("SCAR Chat"); resize(1200, 800); config_ = std::make_unique(); config_->load(); connection_ = std::make_unique(this); // Connect signals connect(connection_.get(), &ClientConnection::connected, this, &MainWindow::onConnected); connect(connection_.get(), &ClientConnection::disconnected, this, &MainWindow::onDisconnected); connect(connection_.get(), &ClientConnection::loginSuccess, this, &MainWindow::onLoginSuccess); connect(connection_.get(), &ClientConnection::loginFailed, this, &MainWindow::onLoginFailed); connect(connection_.get(), &ClientConnection::messageReceived, this, &MainWindow::onMessageReceived); setupUI(); applyDarkTheme(); createMenuBar(); setupStatusBar(); // Clock timer clockTimer_ = new QTimer(this); connect(clockTimer_, &QTimer::timeout, this, &MainWindow::updateClock); clockTimer_->start(1000); updateClock(); // Show login dialog QTimer::singleShot(100, this, &MainWindow::showLoginDialog); } MainWindow::~MainWindow() { if (config_) { config_->save(); } } void MainWindow::setupUI() { auto* centralWidget = new QWidget(this); setCentralWidget(centralWidget); auto* mainLayout = new QHBoxLayout(centralWidget); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0, 0, 0, 0); // Left splitter: User list and chat/video auto* leftSplitter = new QSplitter(Qt::Horizontal, this); // User list (left sidebar) userListWidget_ = new UserListWidget(this); userListWidget_->setMinimumWidth(200); userListWidget_->setMaximumWidth(300); leftSplitter->addWidget(userListWidget_); // Right splitter: Chat and video grid auto* rightSplitter = new QSplitter(Qt::Vertical, this); // Chat widget chatWidget_ = new ChatWidget(this); connect(chatWidget_, &ChatWidget::messageSent, this, &MainWindow::onSendMessage); rightSplitter->addWidget(chatWidget_); // Video grid widget videoGridWidget_ = new VideoGridWidget(this); videoGridWidget_->setMinimumHeight(200); rightSplitter->addWidget(videoGridWidget_); // Set initial sizes (60% chat, 40% video) rightSplitter->setStretchFactor(0, 60); rightSplitter->setStretchFactor(1, 40); leftSplitter->addWidget(rightSplitter); // Set initial sizes (20% users, 80% content) leftSplitter->setStretchFactor(0, 20); leftSplitter->setStretchFactor(1, 80); mainLayout->addWidget(leftSplitter); } void MainWindow::setupStatusBar() { // Connection status (left) connectionStatusLabel_ = new QLabel("Disconnected", this); connectionStatusLabel_->setStyleSheet("color: #ED4245; padding: 2px 10px;"); // Red statusBar()->addWidget(connectionStatusLabel_); // User count (left) userCountLabel_ = new QLabel("Users: 0", this); userCountLabel_->setStyleSheet("padding: 2px 10px;"); statusBar()->addWidget(userCountLabel_); // Spacer statusBar()->addWidget(new QWidget(this), 1); // Clock (center) clockLabel_ = new QLabel(this); clockLabel_->setStyleSheet("padding: 2px 10px;"); statusBar()->addPermanentWidget(clockLabel_); } void MainWindow::applyDarkTheme() { // Discord-inspired dark theme QString styleSheet = R"( QMainWindow { background-color: #36393F; } QWidget { background-color: #36393F; color: #DCDDDE; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; } QListWidget { background-color: #2F3136; border: none; outline: none; } QListWidget::item { padding: 5px; border-radius: 3px; } QListWidget::item:hover { background-color: #3A3C42; } QListWidget::item:selected { background-color: #5865F2; } QTextEdit { background-color: #40444B; border: none; color: #DCDDDE; } QLineEdit { background-color: #40444B; border: none; border-radius: 3px; padding: 8px; color: #DCDDDE; } QLineEdit:focus { background-color: #383A40; } QPushButton { background-color: #5865F2; border: none; border-radius: 3px; padding: 8px 16px; color: white; font-weight: bold; } QPushButton:hover { background-color: #4752C4; } QPushButton:pressed { background-color: #3C45A5; } QStatusBar { background-color: #202225; color: #B9BBBE; } QMenuBar { background-color: #202225; color: #DCDDDE; border-bottom: 1px solid #000; } QMenuBar::item:selected { background-color: #5865F2; } QMenu { background-color: #2F3136; color: #DCDDDE; border: 1px solid #202225; } QMenu::item:selected { background-color: #5865F2; } QSplitter::handle { background-color: #202225; } QDialog { background-color: #36393F; } QLabel { background-color: transparent; } QSpinBox { background-color: #40444B; border: none; border-radius: 3px; padding: 8px; color: #DCDDDE; } )"; setStyleSheet(styleSheet); } void MainWindow::createMenuBar() { auto* fileMenu = menuBar()->addMenu("&File"); auto* connectAction = fileMenu->addAction("&Connect"); connect(connectAction, &QAction::triggered, this, &MainWindow::showLoginDialog); auto* disconnectAction = fileMenu->addAction("&Disconnect"); connect(disconnectAction, &QAction::triggered, [this]() { connection_->disconnect(); }); fileMenu->addSeparator(); auto* exitAction = fileMenu->addAction("E&xit"); connect(exitAction, &QAction::triggered, this, &QMainWindow::close); auto* helpMenu = menuBar()->addMenu("&Help"); auto* aboutAction = helpMenu->addAction("&About"); connect(aboutAction, &QAction::triggered, [this]() { QMessageBox::about(this, "About SCAR Chat", "SCAR Chat v1.0.0\n\n" "A secure cross-platform chat application with video streaming.\n\n" "Built with C++20, Qt6, Boost.ASIO, and SQLite3."); }); } void MainWindow::showLoginDialog() { LoginDialog dialog( QString::fromStdString(config_->lastUsername()), QString::fromStdString(config_->lastServer()), config_->lastPort(), this ); if (dialog.exec() == QDialog::Accepted) { currentUsername_ = dialog.username(); config_->setLastUsername(dialog.username().toStdString()); config_->setLastServer(dialog.server().toStdString()); config_->setLastPort(dialog.port()); config_->save(); updateConnectionStatus("Connecting..."); connection_->connectToServer( dialog.server().toStdString(), dialog.port(), dialog.username().toStdString(), dialog.password().toStdString() ); } } void MainWindow::onConnected() { updateConnectionStatus("Connected"); } void MainWindow::onDisconnected() { updateConnectionStatus("Disconnected"); userListWidget_->clear(); updateUserCount(0); } void MainWindow::onLoginSuccess(const QString& token) { config_->setJwtToken(token.toStdString()); config_->save(); updateConnectionStatus("Authenticated"); // Add self to user list UserInfo self; self.username = currentUsername_; self.status = "Online"; userListWidget_->addUser(self); updateUserCount(userListWidget_->userCount()); // Load chat history chatWidget_->loadHistory(); } void MainWindow::onLoginFailed(const QString& error) { QMessageBox::critical(this, "Login Failed", error); updateConnectionStatus("Login failed"); } void MainWindow::onMessageReceived(const QString& sender, const QString& content) { chatWidget_->addMessage(sender, content, false); } void MainWindow::onSendMessage(const QString& content) { connection_->sendTextMessage(content.toStdString()); } void MainWindow::updateClock() { QString currentTime = QDateTime::currentDateTime().toString("hh:mm:ss"); clockLabel_->setText(currentTime); } void MainWindow::updateConnectionStatus(const QString& status) { connectionStatusLabel_->setText(status); // Color coding if (status.contains("Connected") || status.contains("Authenticated")) { connectionStatusLabel_->setStyleSheet("color: #43B581; padding: 2px 10px;"); // Green } else if (status.contains("Connecting")) { connectionStatusLabel_->setStyleSheet("color: #FAA61A; padding: 2px 10px;"); // Yellow } else { connectionStatusLabel_->setStyleSheet("color: #ED4245; padding: 2px 10px;"); // Red } } void MainWindow::updateUserCount(int count) { userCountLabel_->setText(QString("Users: %1").arg(count)); } } // namespace scar