From b667d6f3524a6a71748cddb179aeadda71cceeae Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 22 Jun 2026 22:48:27 +0300 Subject: [PATCH] tools/chatgui: Qt5 GUI chat app stub with tray, channel list, message bubbles --- .gitignore | 1 + tools/chatgui/CMakeLists.txt | 27 +++ tools/chatgui/build.bat | 42 +++++ tools/chatgui/build.sh | 44 +++++ tools/chatgui/resources/bg.jpg | Bin 0 -> 85615 bytes tools/chatgui/src/accountlist.cpp | 69 +++++++ tools/chatgui/src/accountlist.h | 16 ++ tools/chatgui/src/channeldelegate.cpp | 74 ++++++++ tools/chatgui/src/channeldelegate.h | 20 ++ tools/chatgui/src/channellist.cpp | 82 +++++++++ tools/chatgui/src/channellist.h | 19 ++ tools/chatgui/src/chatview.cpp | 34 ++++ tools/chatgui/src/chatview.h | 19 ++ tools/chatgui/src/main.cpp | 13 ++ tools/chatgui/src/mainwindow.cpp | 83 +++++++++ tools/chatgui/src/mainwindow.h | 32 ++++ tools/chatgui/src/messagedelegate.cpp | 251 ++++++++++++++++++++++++++ tools/chatgui/src/messagedelegate.h | 67 +++++++ tools/chatgui/src/messagelist.cpp | 179 ++++++++++++++++++ tools/chatgui/src/messagelist.h | 17 ++ 20 files changed, 1089 insertions(+) create mode 100644 tools/chatgui/CMakeLists.txt create mode 100644 tools/chatgui/build.bat create mode 100755 tools/chatgui/build.sh create mode 100644 tools/chatgui/resources/bg.jpg create mode 100644 tools/chatgui/src/accountlist.cpp create mode 100644 tools/chatgui/src/accountlist.h create mode 100644 tools/chatgui/src/channeldelegate.cpp create mode 100644 tools/chatgui/src/channeldelegate.h create mode 100644 tools/chatgui/src/channellist.cpp create mode 100644 tools/chatgui/src/channellist.h create mode 100644 tools/chatgui/src/chatview.cpp create mode 100644 tools/chatgui/src/chatview.h create mode 100644 tools/chatgui/src/main.cpp create mode 100644 tools/chatgui/src/mainwindow.cpp create mode 100644 tools/chatgui/src/mainwindow.h create mode 100644 tools/chatgui/src/messagedelegate.cpp create mode 100644 tools/chatgui/src/messagedelegate.h create mode 100644 tools/chatgui/src/messagelist.cpp create mode 100644 tools/chatgui/src/messagelist.h diff --git a/.gitignore b/.gitignore index 0595b084..e5aa1a81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Build directory (out-of-tree build) /build/ +tools/chatgui/build/ # Build outputs *.o diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt new file mode 100644 index 00000000..37d10ebd --- /dev/null +++ b/tools/chatgui/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.16) +project(chatgui VERSION 0.1.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_AUTOMOC ON) + +find_package(Qt5 COMPONENTS Widgets QUIET) +if(NOT Qt5_FOUND) + find_package(Qt6 COMPONENTS Widgets REQUIRED) + set(QT_LIBS Qt6::Widgets) +else() + set(QT_LIBS Qt5::Widgets) +endif() + +add_executable(chatgui + src/main.cpp + src/mainwindow.cpp + src/channellist.cpp + src/channeldelegate.cpp + src/chatview.cpp + src/messagedelegate.cpp + src/messagelist.cpp + src/accountlist.cpp +) + +target_link_libraries(chatgui PRIVATE ${QT_LIBS}) diff --git a/tools/chatgui/build.bat b/tools/chatgui/build.bat new file mode 100644 index 00000000..15c76363 --- /dev/null +++ b/tools/chatgui/build.bat @@ -0,0 +1,42 @@ +@echo off +pushd "%~dp0\..\.." + +echo ChatGUI Build Script for MSYS2 UCRT64 +echo =========================================== +echo. + +set MSYS2_PATH=C:\msys64 +if not "%MSYS2_ROOT%"=="" set MSYS2_PATH=%MSYS2_ROOT% +if not "%MSYS2%"=="" set MSYS2_PATH=%MSYS2% + +if not exist "%MSYS2_PATH%\msys2_shell.cmd" ( + echo [ERROR] MSYS2 not found at %MSYS2_PATH% + echo Please install MSYS2 from https://www.msys2.org/ + echo or set MSYS2_ROOT environment variable. + pause + exit /b 1 +) + +echo [INFO] MSYS2 found at: %MSYS2_PATH% +echo [INFO] Starting build... +echo. + +set "CURRENT_DIR=%CD%" + +call "%MSYS2_PATH%\msys2_shell.cmd" -ucrt64 -here -defterm -no-start -c "cd \"%CURRENT_DIR%/tools/chatgui\" && bash build.sh 2>&1 | tee build_win.log" + +set BUILD_RESULT=%ERRORLEVEL% + +echo. +echo =========================================== +if %BUILD_RESULT% == 0 ( + echo [SUCCESS] Build completed! + echo [INFO] Executable: tools\chatgui\build\chatgui.exe +) else ( + echo [FAILED] Build failed with error code: %BUILD_RESULT% + echo [INFO] Check tools\chatgui\build_win.log for details +) +echo =========================================== +echo. + +pause diff --git a/tools/chatgui/build.sh b/tools/chatgui/build.sh new file mode 100755 index 00000000..83d2794d --- /dev/null +++ b/tools/chatgui/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -eo pipefail + +LOG_FILE="build_win.log" +> "$LOG_FILE" + +log() { echo "$1"; echo "$1" >> "$LOG_FILE"; } + +log "================================" +log "ChatGUI Build Script (MSYS2 UCRT64)" +log "================================" +log "" + +SRC_DIR="$(cd "$(dirname "$0")" && pwd)" +BUILD_DIR="$SRC_DIR/build" + +log "Source: $SRC_DIR" +log "Build: $BUILD_DIR" +log "" + +log "[1/3] Configuring CMake..." +cmake -S "$SRC_DIR" -B "$BUILD_DIR" -G "MSYS Makefiles" 2>&1 | tee -a "$LOG_FILE" +test ${PIPESTATUS[0]} -eq 0 || { log "CMake configure FAILED"; exit 1; } + +log "" +log "[2/3] Building..." +cmake --build "$BUILD_DIR" 2>&1 | tee -a "$LOG_FILE" +test ${PIPESTATUS[0]} -eq 0 || { log "Build FAILED"; exit 1; } + +log "" +log "[3/3] Verifying..." +EXE="$BUILD_DIR/chatgui.exe" +if [ -f "$EXE" ]; then + SIZE=$(du -h "$EXE" | cut -f1) + log " OK: chatgui.exe ($SIZE)" + log "" + log "================================" + log "Build SUCCESS" + log "================================" + log "Executable: $EXE" +else + log " FAIL: chatgui.exe not found" + exit 1 +fi diff --git a/tools/chatgui/resources/bg.jpg b/tools/chatgui/resources/bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5b95d805380b3d95bea90c7dabfd91cd5bbec06c GIT binary patch literal 85615 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

B`(z9YBUDgnb;QPg8h! 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 +#include +#include +#include + +static QPixmap makeAvatar(const QColor &color, const QString &letter) { + QPixmap pix(24, 24); + pix.fill(Qt::transparent); + QPainter p(&pix); + p.setRenderHint(QPainter::Antialiasing); + p.setBrush(color); + p.setPen(Qt::NoPen); + p.drawEllipse(0, 0, 24, 24); + p.setPen(Qt::white); + QFont f = p.font(); + f.setBold(true); + f.setPointSize(10); + p.setFont(f); + p.drawText(QRect(0, 0, 24, 24), Qt::AlignCenter, letter); + p.end(); + return pix; +} + +AccountList::AccountList(QWidget *parent) + : QWidget(parent) + , m_listView(new QListView(this)) + , m_model(new QStandardItemModel(this)) +{ + auto *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + auto *header = new QLabel("Accounts", this); + header->setAlignment(Qt::AlignCenter); + header->setStyleSheet("font-weight: bold; padding: 8px; background: palette(window);"); + layout->addWidget(header); + + m_listView->setModel(m_model); + m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_listView->setSelectionMode(QAbstractItemView::SingleSelection); + m_listView->setIconSize(QSize(24, 24)); + m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + layout->addWidget(m_listView); + + populateStubData(); +} + +void AccountList::populateStubData() { + struct { + const char *name; + QColor color; + const char *letter; + } accounts[] = { + {"Alice", QColor(74, 144, 226), "A"}, + {"Bob", QColor(126, 211, 33), "B"}, + {"Carol", QColor(245, 166, 35), "C"}, + {"Dave", QColor(144, 19, 254), "D"}, + {"Eve", QColor(0, 184, 148), "E"}, + }; + + for (const auto &a : accounts) { + auto *item = new QStandardItem(); + item->setText(a.name); + item->setIcon(QIcon(makeAvatar(a.color, a.letter))); + m_model->appendRow(item); + } +} diff --git a/tools/chatgui/src/accountlist.h b/tools/chatgui/src/accountlist.h new file mode 100644 index 00000000..7fc1cfc0 --- /dev/null +++ b/tools/chatgui/src/accountlist.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +class AccountList : public QWidget { + Q_OBJECT +public: + explicit AccountList(QWidget *parent = nullptr); + +private: + QListView *m_listView; + QStandardItemModel *m_model; + void populateStubData(); +}; diff --git a/tools/chatgui/src/channeldelegate.cpp b/tools/chatgui/src/channeldelegate.cpp new file mode 100644 index 00000000..daefcb56 --- /dev/null +++ b/tools/chatgui/src/channeldelegate.cpp @@ -0,0 +1,74 @@ +#include "channeldelegate.h" +#include +#include + +ChannelDelegate::ChannelDelegate(QObject *parent) + : QStyledItemDelegate(parent) {} + +void ChannelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + painter->save(); + + if (option.state & QStyle::State_Selected) { + painter->fillRect(option.rect, option.palette.highlight()); + } else if (option.state & QStyle::State_MouseOver) { + painter->fillRect(option.rect, option.palette.light()); + } + + const int margin = 8; + const int iconSize = 32; + QRect iconRect(option.rect.left() + margin, + option.rect.top() + (option.rect.height() - iconSize) / 2, + iconSize, iconSize); + + QIcon icon = qvariant_cast(index.data(ChannelIconRole)); + if (!icon.isNull()) { + icon.paint(painter, iconRect); + } + + QRect textRect(iconRect.right() + margin, option.rect.top() + margin, + option.rect.right() - iconRect.right() - 2 * margin, + option.rect.height() - 2 * margin); + + QString name = index.data(ChannelNameRole).toString(); + QFont nameFont = option.font; + nameFont.setBold(true); + nameFont.setPointSize(nameFont.pointSize() + 1); + painter->setFont(nameFont); + + QColor textColor = (option.state & QStyle::State_Selected) + ? option.palette.highlightedText().color() + : option.palette.text().color(); + painter->setPen(textColor); + + QRect nameRect = textRect; + nameRect.setHeight(QFontMetrics(nameFont).height()); + painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignTop, + painter->fontMetrics().elidedText(name, Qt::ElideRight, nameRect.width())); + + QString lastMsg = index.data(ChannelLastMessageRole).toString(); + if (!lastMsg.isEmpty()) { + QFont msgFont = option.font; + msgFont.setPointSize(msgFont.pointSize() - 1); + painter->setFont(msgFont); + + QColor msgColor = (option.state & QStyle::State_Selected) + ? option.palette.highlightedText().color().darker(120) + : QColor(128, 128, 128); + painter->setPen(msgColor); + + QRect msgRect(textRect.left(), nameRect.bottom() + 4, + textRect.width(), option.rect.bottom() - nameRect.bottom() - margin); + QString elidedMsg = painter->fontMetrics().elidedText(lastMsg, Qt::ElideRight, msgRect.width()); + painter->drawText(msgRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, elidedMsg); + } + + painter->restore(); +} + +QSize ChannelDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const { + Q_UNUSED(option); + Q_UNUSED(index); + return QSize(200, 56); +} diff --git a/tools/chatgui/src/channeldelegate.h b/tools/chatgui/src/channeldelegate.h new file mode 100644 index 00000000..8bfa7324 --- /dev/null +++ b/tools/chatgui/src/channeldelegate.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +enum ChannelDataRole { + ChannelNameRole = Qt::DisplayRole, + ChannelIconRole = Qt::DecorationRole, + ChannelLastMessageRole = Qt::UserRole + 1 +}; + +class ChannelDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + explicit ChannelDelegate(QObject *parent = nullptr); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const override; +}; diff --git a/tools/chatgui/src/channellist.cpp b/tools/chatgui/src/channellist.cpp new file mode 100644 index 00000000..7885b1da --- /dev/null +++ b/tools/chatgui/src/channellist.cpp @@ -0,0 +1,82 @@ +#include "channellist.h" +#include "channeldelegate.h" +#include +#include +#include +#include + +static QIcon makeChannelIcon(const QColor &color, const QString &letter) { + QPixmap pix(32, 32); + pix.fill(Qt::transparent); + QPainter p(&pix); + p.setRenderHint(QPainter::Antialiasing); + p.setBrush(color); + p.setPen(Qt::NoPen); + p.drawRoundedRect(0, 0, 32, 32, 8, 8); + p.setPen(Qt::white); + QFont f = p.font(); + f.setBold(true); + f.setPointSize(14); + p.setFont(f); + p.drawText(QRect(0, 0, 32, 32), Qt::AlignCenter, letter); + p.end(); + return QIcon(pix); +} + +ChannelList::ChannelList(QWidget *parent) + : QWidget(parent) + , m_listView(new QListView(this)) + , m_model(new QStandardItemModel(this)) +{ + auto *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + auto *header = new QLabel("Channels", this); + header->setAlignment(Qt::AlignCenter); + header->setStyleSheet("font-weight: bold; padding: 8px; background: palette(window);"); + layout->addWidget(header); + + m_listView->setModel(m_model); + m_listView->setItemDelegate(new ChannelDelegate(this)); + m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_listView->setSelectionMode(QAbstractItemView::SingleSelection); + m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + layout->addWidget(m_listView); + + connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged, + this, [this](const QModelIndex ¤t, const QModelIndex &) { + if (current.isValid()) { + emit channelSelected(current.row()); + } + }); + + populateStubData(); +} + +void ChannelList::populateStubData() { + struct { + const char *name; + const char *lastMsg; + QColor color; + const char *letter; + } channels[] = { + {"#general", "Alice: Sounds good, let's deploy tomorrow morning.", QColor(74, 144, 226), "G"}, + {"#random", "Bob: Anyone seen the latest episode? It was amazing!", QColor(126, 211, 33), "R"}, + {"#dev-team", "Carol: I've pushed the fix for ETCP-42 to staging.", QColor(245, 166, 35), "D"}, + {"#announcements","Admin: Maintenance scheduled for Sunday 02:00 UTC.", QColor(208, 2, 27), "A"}, + {"#support", "Dave: Can someone help me configure the routing table? The BGP session keeps dropping after the handshake completes. I've checked the logs.", QColor(144, 19, 254), "S"}, + {"#offtopic", "Eve: Check out this new keyboard — Cherry MX Blue, 60%.", QColor(0, 184, 148), "O"}, + }; + + for (const auto &ch : channels) { + auto *item = new QStandardItem(); + item->setData(ch.name, ChannelNameRole); + item->setData(QString::fromUtf8(ch.lastMsg), ChannelLastMessageRole); + item->setData(makeChannelIcon(ch.color, ch.letter), ChannelIconRole); + m_model->appendRow(item); + } + + m_listView->setCurrentIndex(m_model->index(0, 0)); +} diff --git a/tools/chatgui/src/channellist.h b/tools/chatgui/src/channellist.h new file mode 100644 index 00000000..6f81f9eb --- /dev/null +++ b/tools/chatgui/src/channellist.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +class ChannelList : public QWidget { + Q_OBJECT +public: + explicit ChannelList(QWidget *parent = nullptr); + +signals: + void channelSelected(int index); + +private: + QListView *m_listView; + QStandardItemModel *m_model; + void populateStubData(); +}; diff --git a/tools/chatgui/src/chatview.cpp b/tools/chatgui/src/chatview.cpp new file mode 100644 index 00000000..cb4dbb68 --- /dev/null +++ b/tools/chatgui/src/chatview.cpp @@ -0,0 +1,34 @@ +#include "chatview.h" +#include +#include + +ChatView::ChatView(QWidget *parent) + : QListView(parent) +{ +} + +void ChatView::setBackgroundImage(const QString &path) { + if (!m_background.load(path)) { + qWarning() << "ChatView: failed to load background" << path; + return; + } + viewport()->update(); +} + +void ChatView::resizeEvent(QResizeEvent *event) { + QListView::resizeEvent(event); + scheduleDelayedItemsLayout(); +} + +void ChatView::scrollContentsBy(int dx, int dy) { + QListView::scrollContentsBy(dx, dy); + viewport()->update(); +} + +void ChatView::paintEvent(QPaintEvent *event) { + if (!m_background.isNull()) { + QPainter p(viewport()); + p.drawTiledPixmap(viewport()->rect(), m_background); + } + QListView::paintEvent(event); +} diff --git a/tools/chatgui/src/chatview.h b/tools/chatgui/src/chatview.h new file mode 100644 index 00000000..3de619b6 --- /dev/null +++ b/tools/chatgui/src/chatview.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class ChatView : public QListView { + Q_OBJECT +public: + explicit ChatView(QWidget *parent = nullptr); + + void setBackgroundImage(const QString &path); + +protected: + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void scrollContentsBy(int dx, int dy) override; + +private: + QPixmap m_background; +}; diff --git a/tools/chatgui/src/main.cpp b/tools/chatgui/src/main.cpp new file mode 100644 index 00000000..5df4f1f9 --- /dev/null +++ b/tools/chatgui/src/main.cpp @@ -0,0 +1,13 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + app.setApplicationName("Chat"); + app.setQuitOnLastWindowClosed(false); + + MainWindow w; + w.show(); + + return app.exec(); +} diff --git a/tools/chatgui/src/mainwindow.cpp b/tools/chatgui/src/mainwindow.cpp new file mode 100644 index 00000000..88dcdd20 --- /dev/null +++ b/tools/chatgui/src/mainwindow.cpp @@ -0,0 +1,83 @@ +#include "mainwindow.h" +#include "channellist.h" +#include "messagelist.h" +#include "accountlist.h" +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , m_channelList(nullptr) + , m_messageList(nullptr) + , m_accountList(nullptr) + , m_trayIcon(nullptr) + , m_trayMenu(nullptr) +{ + setWindowTitle("Chat"); + resize(900, 600); + + setupUi(); + setupTray(); +} + +void MainWindow::setupUi() { + auto *splitter = new QSplitter(Qt::Horizontal, this); + + m_channelList = new ChannelList(splitter); + m_messageList = new MessageList(splitter); + m_accountList = new AccountList(splitter); + + splitter->addWidget(m_channelList); + splitter->addWidget(m_messageList); + splitter->addWidget(m_accountList); + + splitter->setStretchFactor(0, 2); + splitter->setStretchFactor(1, 5); + splitter->setStretchFactor(2, 1); + + splitter->setSizes({220, 480, 180}); + + setCentralWidget(splitter); +} + +void MainWindow::setupTray() { + m_trayIcon = new QSystemTrayIcon(this); + m_trayIcon->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon)); + m_trayIcon->setToolTip("Chat"); + + m_trayMenu = new QMenu(this); + QAction *showAction = m_trayMenu->addAction("Show"); + connect(showAction, &QAction::triggered, this, [this]() { + show(); + raise(); + activateWindow(); + }); + + QAction *quitAction = m_trayMenu->addAction("Quit"); + connect(quitAction, &QAction::triggered, qApp, &QApplication::quit); + + m_trayIcon->setContextMenu(m_trayMenu); + + connect(m_trayIcon, &QSystemTrayIcon::activated, + this, &MainWindow::onTrayActivated); + + m_trayIcon->show(); +} + +void MainWindow::closeEvent(QCloseEvent *event) { + hide(); + m_trayIcon->showMessage("Chat", "Application minimized to tray.", + QSystemTrayIcon::Information, 2000); + event->ignore(); +} + +void MainWindow::onTrayActivated(QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::Trigger || + reason == QSystemTrayIcon::DoubleClick) { + show(); + raise(); + activateWindow(); + } +} diff --git a/tools/chatgui/src/mainwindow.h b/tools/chatgui/src/mainwindow.h new file mode 100644 index 00000000..bc2401d3 --- /dev/null +++ b/tools/chatgui/src/mainwindow.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +class ChannelList; +class MessageList; +class AccountList; + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow() override = default; + +protected: + void closeEvent(QCloseEvent *event) override; + +private slots: + void onTrayActivated(QSystemTrayIcon::ActivationReason reason); + +private: + void setupUi(); + void setupTray(); + + ChannelList *m_channelList; + MessageList *m_messageList; + AccountList *m_accountList; + QSystemTrayIcon *m_trayIcon; + QMenu *m_trayMenu; +}; diff --git a/tools/chatgui/src/messagedelegate.cpp b/tools/chatgui/src/messagedelegate.cpp new file mode 100644 index 00000000..ae0d5b4d --- /dev/null +++ b/tools/chatgui/src/messagedelegate.cpp @@ -0,0 +1,251 @@ +#include "messagedelegate.h" +#include +#include +#include +#include +#include + +MessageDelegate::MessageDelegate(QObject *parent) + : QStyledItemDelegate(parent) {} + +QSize MessageDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const { + Layout L = calcLayout(option, index); + return QSize(L.viewWidth, L.totalHeight); +} + +static int measureTextHeight(const QFont &font, const QString &text, int width) { + if (text.isEmpty() || width <= 0) return 0; + QRect r = QFontMetrics(font).boundingRect(QRect(0, 0, width, 100000), + Qt::AlignLeft | Qt::TextWordWrap, text); + return r.height(); +} + +MessageDelegate::Layout MessageDelegate::calcLayout( + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Layout L{}; + L.viewWidth = 300; + if (option.widget) { + auto *v = qobject_cast(option.widget); + if (v) L.viewWidth = v->viewport()->width(); + } + if (L.viewWidth <= 0) L.viewWidth = 300; + + bool hasQuote = index.data(MsgHasQuoteRole).toBool(); + QString text = index.data(MsgTextRole).toString(); + QString qText = hasQuote ? index.data(MsgQuoteTextRole).toString() : QString(); + + int bubbleW = L.viewWidth - kMarginH - kAvatar - kGap - kMarginH; + if (bubbleW < 100) bubbleW = 100; + L.bubbleWidth = bubbleW; + + int textW = bubbleW - 2 * kPadH; + + int qH = 0; + if (hasQuote) { + int qTextW = bubbleW - 2 * kPadH - kQuoteBarW - kQuotePadH; + int qTextH = measureTextHeight(option.font, qText, qTextW); + int qOneLineH = QFontMetrics(option.font).height(); + qH = kQuotePadV + qOneLineH + (qText.isEmpty() ? 0 : 2 + qTextH) + kQuotePadV; + } + + int textH = measureTextHeight(option.font, text, textW); + int bubbleH = kPadTop + (hasQuote ? qH + kQuoteGap : 0) + textH + kStatusH + kPadBot; + L.totalHeight = qMax(bubbleH, kAvatar) + kItemGap; + + int contentLeft = kMarginH + kAvatar + kGap; + + L.bubbleRect = QRect(contentLeft, 0, bubbleW, bubbleH); + + int contentTop = kPadTop; + + if (hasQuote) { + L.quoteRect = QRect(contentLeft + kPadH, contentTop, bubbleW - 2 * kPadH, qH); + L.quoteBarRect = QRect(L.quoteRect.left(), L.quoteRect.top(), + kQuoteBarW, qH); + int tx = L.quoteBarRect.right() + kQuotePadH; + L.quoteAuthorRect = QRect(tx, L.quoteRect.top() + kQuotePadV, + L.quoteRect.width() - kQuoteBarW - kQuotePadH, + QFontMetrics(option.font).height()); + L.quoteTextRect = QRect(tx, + L.quoteAuthorRect.bottom() + 2, + L.quoteAuthorRect.width(), + qH - kQuotePadV - QFontMetrics(option.font).height() - 2 - kQuotePadV); + contentTop += qH + kQuoteGap; + } + + L.textRect = QRect(contentLeft + kPadH, contentTop, + textW, textH); + L.statusRect = QRect(contentLeft + kPadH, + contentTop + textH + 2, + textW, kStatusH); + + int avatarY = L.bubbleRect.bottom() - kAvatar; + L.avatarRect = QRect(kMarginH, avatarY, kAvatar, kAvatar); + + return L; +} + +void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + painter->save(); + painter->translate(option.rect.topLeft()); + painter->setClipRect(QRect(QPoint(0, 0), option.rect.size())); + + Layout L = calcLayout(option, index); + + drawBubble(painter, L); + + if (index.data(MsgHasQuoteRole).toBool()) { + drawQuote(painter, L, index, option); + } + + drawAvatar(painter, L, index); + + painter->setPen(option.palette.text().color()); + QFont textFont = option.font; + textFont.setPointSize(textFont.pointSize()); + painter->setFont(textFont); + painter->drawText(L.textRect, Qt::AlignLeft | Qt::TextWordWrap, + index.data(MsgTextRole).toString()); + + drawStatusBar(painter, L, index, option); + + painter->restore(); +} + +void MessageDelegate::drawQuote(QPainter *p, const Layout &L, + const QModelIndex &index, + const QStyleOptionViewItem &opt) const { + p->save(); + + p->setPen(Qt::NoPen); + p->setBrush(QColor(0xFF, 0xEE, 0xEE)); + p->drawRoundedRect(L.quoteRect, 5, 5); + + p->setPen(Qt::NoPen); + p->setBrush(QColor(0xE0, 0x40, 0x40)); + p->drawRect(L.quoteBarRect); + + QFont boldFont = opt.font; + boldFont.setBold(true); + p->setFont(boldFont); + p->setPen(QColor(0xE0, 0x40, 0x40)); + p->drawText(L.quoteAuthorRect, Qt::AlignLeft | Qt::AlignVCenter, + index.data(MsgQuoteAuthorRole).toString()); + + p->setFont(opt.font); + p->setPen(QColor(0x99, 0x55, 0x55)); + p->drawText(L.quoteTextRect, Qt::AlignLeft | Qt::TextWordWrap, + index.data(MsgQuoteTextRole).toString()); + + p->restore(); +} + +void MessageDelegate::drawBubble(QPainter *p, const Layout &L) const { + p->save(); + p->setRenderHint(QPainter::Antialiasing); + + QRectF b = L.bubbleRect; + qreal r = kRadius; + qreal t = kTail; + + QPainterPath body; + body.addRoundedRect(b, r, r); + + QPainterPath tail; + qreal tx = b.left(); + qreal ty = b.bottom(); + tail.moveTo(tx, ty - r); + tail.lineTo(tx - t, ty - t / 2.0); + tail.lineTo(tx + r, ty); + tail.closeSubpath(); + + QPainterPath combined = body.united(tail); + + p->setPen(QPen(QColor(0xE0, 0xE0, 0xE0), 0.5)); + p->setBrush(QColor(255, 255, 255)); + p->drawPath(combined); + + p->restore(); +} + +void MessageDelegate::drawStatusBar(QPainter *p, const Layout &L, + const QModelIndex &index, + const QStyleOptionViewItem &opt) const { + p->save(); + + QRect r = L.statusRect; + + QVariant reactionsVar = index.data(MsgReactionsRole); + QVariantList reactions = reactionsVar.toList(); + + int rx = r.left(); + if (!reactions.isEmpty()) { + for (const QVariant &rv : reactions) { + QVariantMap rm = rv.toMap(); + QString emoji = rm["emoji"].toString(); + int count = rm["count"].toInt(); + QVariant colorsVar = rm["colors"]; + QList colors = colorsVar.value>(); + + if (count < 4 && !colors.isEmpty()) { + int iconSize = 16; + for (int i = colors.size() - 1; i >= 0; --i) { + int cx = rx + i * 11; + p->setPen(Qt::NoPen); + p->setBrush(colors[i]); + p->drawEllipse(cx, r.top() + 1, iconSize, iconSize); + } + rx += colors.size() * 11 + 8; + } else { + QFont emojiFont = opt.font; + emojiFont.setPointSize(emojiFont.pointSize() - 1); + p->setFont(emojiFont); + p->setPen(opt.palette.text().color()); + QRect emojiRect(rx, r.top(), 20, r.height()); + p->drawText(emojiRect, Qt::AlignLeft | Qt::AlignVCenter, emoji); + rx += 20; + + QFont countFont = opt.font; + countFont.setPointSize(countFont.pointSize() - 2); + p->setFont(countFont); + p->setPen(QColor(0x80, 0x80, 0x80)); + QRect cntRect(rx, r.top(), 24, r.height()); + p->drawText(cntRect, Qt::AlignLeft | Qt::AlignVCenter, + QString::number(count)); + rx = cntRect.right() + 8; + } + } + } + + QFont smallFont = opt.font; + smallFont.setPointSize(smallFont.pointSize() - 2); + p->setFont(smallFont); + p->setPen(QColor(0x80, 0x80, 0x80)); + + QString statusText; + if (index.data(MsgEditedRole).toBool()) { + statusText = QString::fromUtf8("изменено "); + } + statusText += index.data(MsgTimeRole).toString(); + p->drawText(QRect(r.left(), r.top(), r.width(), r.height()), + Qt::AlignRight | Qt::AlignVCenter, statusText); + + p->restore(); +} + +void MessageDelegate::drawAvatar(QPainter *p, const Layout &L, + const QModelIndex &index) const { + QPixmap av = index.data(MsgAvatarRole).value(); + if (av.isNull()) return; + + p->save(); + p->setRenderHint(QPainter::Antialiasing); + QPainterPath clip; + clip.addEllipse(L.avatarRect); + p->setClipPath(clip); + p->drawPixmap(L.avatarRect, av); + p->restore(); +} diff --git a/tools/chatgui/src/messagedelegate.h b/tools/chatgui/src/messagedelegate.h new file mode 100644 index 00000000..68d33326 --- /dev/null +++ b/tools/chatgui/src/messagedelegate.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +enum MessageDataRole { + MsgAuthorRole = Qt::UserRole + 1, + MsgTextRole = Qt::UserRole + 2, + MsgTimeRole = Qt::UserRole + 3, + MsgEditedRole = Qt::UserRole + 4, + MsgAvatarRole = Qt::UserRole + 5, + MsgHasQuoteRole = Qt::UserRole + 6, + MsgQuoteAuthorRole = Qt::UserRole + 7, + MsgQuoteTextRole = Qt::UserRole + 8, + MsgReactionsRole = Qt::UserRole + 9, +}; + +class MessageDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + explicit MessageDelegate(QObject *parent = nullptr); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + +private: + struct Layout { + int viewWidth; + int bubbleWidth; + QRect avatarRect; + QRect bubbleRect; + QRect quoteRect; + QRect quoteBarRect; + QRect quoteAuthorRect; + QRect quoteTextRect; + QRect textRect; + QRect statusRect; + int totalHeight; + }; + + Layout calcLayout(const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void drawQuote(QPainter *p, const Layout &L, + const QModelIndex &index, const QStyleOptionViewItem &opt) const; + void drawBubble(QPainter *p, const Layout &L) const; + void drawStatusBar(QPainter *p, const Layout &L, + const QModelIndex &index, const QStyleOptionViewItem &opt) const; + void drawAvatar(QPainter *p, const Layout &L, + const QModelIndex &index) const; + + static constexpr int kAvatar = 36; + static constexpr int kGap = 8; + static constexpr int kRadius = 10; + static constexpr int kPadH = 12; + static constexpr int kPadTop = 8; + static constexpr int kPadBot = 6; + static constexpr int kTail = 6; + static constexpr int kMarginH = 8; + static constexpr int kItemGap = 3; + static constexpr int kQuoteGap = 4; + static constexpr int kQuotePadH = 6; + static constexpr int kQuotePadV = 6; + static constexpr int kQuoteBarW = 2; + static constexpr int kQuoteAv = 16; + static constexpr int kStatusH = 18; +}; diff --git a/tools/chatgui/src/messagelist.cpp b/tools/chatgui/src/messagelist.cpp new file mode 100644 index 00000000..5c18dc8e --- /dev/null +++ b/tools/chatgui/src/messagelist.cpp @@ -0,0 +1,179 @@ +#include "messagelist.h" +#include "chatview.h" +#include "messagedelegate.h" +#include +#include +#include + +static QPixmap makeAvatar(const QColor &color, const QString &letter, int size = 36) { + QPixmap pix(size, size); + pix.fill(Qt::transparent); + QPainter p(&pix); + p.setRenderHint(QPainter::Antialiasing); + p.setBrush(color); + p.setPen(Qt::NoPen); + p.drawEllipse(0, 0, size, size); + p.setPen(Qt::white); + QFont f = p.font(); + f.setBold(true); + f.setPointSize(size / 3); + p.setFont(f); + p.drawText(QRect(0, 0, size, size), Qt::AlignCenter, letter); + p.end(); + return pix; +} + +static QVariant makeReaction(const QString &emoji, int count, + const QList &colors = {}) { + QVariantMap m; + m["emoji"] = emoji; + m["count"] = count; + if (!colors.isEmpty()) + m["colors"] = QVariant::fromValue(colors); + return m; +} + +MessageList::MessageList(QWidget *parent) + : QWidget(parent) + , m_view(new ChatView(this)) + , m_model(new QStandardItemModel(this)) +{ + auto *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + m_view->setModel(m_model); + m_view->setItemDelegate(new MessageDelegate(this)); + m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_view->setSelectionMode(QAbstractItemView::NoSelection); + m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_view->setFrameShape(QFrame::NoFrame); + m_view->setBackgroundImage("../resources/bg.jpg"); + layout->addWidget(m_view); + + populateStubData(); +} + +static void setMsg(QStandardItem *item, + const QString &author, const QString &text, const QString &time, + const QPixmap &avatar, bool edited, + bool hasQuote, const QString &qAuthor, const QString &qText, + const QVariantList &reactions) { + item->setData(author, MsgAuthorRole); + item->setData(text, MsgTextRole); + item->setData(time, MsgTimeRole); + item->setData(edited, MsgEditedRole); + item->setData(QVariant::fromValue(avatar), MsgAvatarRole); + item->setData(hasQuote, MsgHasQuoteRole); + if (hasQuote) { + item->setData(qAuthor, MsgQuoteAuthorRole); + item->setData(qText, MsgQuoteTextRole); + } + if (!reactions.isEmpty()) + item->setData(reactions, MsgReactionsRole); +} + +void MessageList::populateStubData() { + QColor cAlice(74, 144, 226), cBob(126, 211, 33), cCarol(245, 166, 35); + QColor cDave(144, 19, 254), cEve(0, 184, 148); + auto avAlice = makeAvatar(cAlice, "A"); + auto avBob = makeAvatar(cBob, "B"); + auto avCarol = makeAvatar(cCarol, "C"); + auto avDave = makeAvatar(cDave, "D"); + + auto *m0 = new QStandardItem(); + setMsg(m0, + QString::fromUtf8("Alice"), + QString::fromUtf8("Hey team! How's the deployment going?"), + "10:15", avAlice, false, + false, QString(), QString(), + {}); + m_model->appendRow(m0); + + auto *m1 = new QStandardItem(); + setMsg(m1, + QString::fromUtf8("Bob"), + QString::fromUtf8("All green on staging. Running the final integration tests now."), + "10:17", avBob, false, + false, QString(), QString(), + QVariantList{makeReaction(QString::fromUtf8("\xF0\x9F\x91\x8D"), 2, + {cAlice, cCarol})}); + m_model->appendRow(m1); + + auto *m2 = new QStandardItem(); + setMsg(m2, + QString::fromUtf8("Carol"), + QString::fromUtf8("I've pushed the ETCP-42 fix to staging. BGP handshake should be stable now. Please review the patch when you get a chance."), + "10:22", avCarol, true, + false, QString(), QString(), + QVariantList{ + makeReaction(QString::fromUtf8("\xF0\x9F\x91\x8D"), 3, + {cAlice, cBob, cDave}), + makeReaction(QString::fromUtf8("\xE2\x9D\xA4\xEF\xB8\x8F"), 1, + {cAlice}) + }); + m_model->appendRow(m2); + + auto *m3 = new QStandardItem(); + setMsg(m3, + QString::fromUtf8("Dave"), + QString::fromUtf8("Wait — the BGP session is still flapping after the handshake. I'm seeing the keepalive timer fire but no route updates come through. Logs attached."), + "10:25", avDave, false, + true, + QString::fromUtf8("Carol"), + QString::fromUtf8("BGP handshake should be stable now"), + QVariantList{ + makeReaction(QString::fromUtf8("\xF0\x9F\x98\xAE"), 1, + {cBob}) + }); + m_model->appendRow(m3); + + auto *m4 = new QStandardItem(); + setMsg(m4, + QString::fromUtf8("Alice"), + QString::fromUtf8("Reviewed the patch. LGTM. Let's merge and deploy to production tomorrow morning."), + "10:28", avAlice, false, + false, QString(), QString(), + QVariantList{ + makeReaction(QString::fromUtf8("\xF0\x9F\x91\x8D"), 5), + makeReaction(QString::fromUtf8("\xF0\x9F\x8E\x89"), 2, + {cBob, cEve}) + }); + m_model->appendRow(m4); + + auto *m5 = new QStandardItem(); + setMsg(m5, + QString::fromUtf8("Bob"), + QString::fromUtf8("Can you share the full logs for the BGP session? I'll dig into the keepalive timer logic."), + "10:30", avBob, false, + true, + QString::fromUtf8("Dave"), + QString::fromUtf8("keepalive timer fire but no route updates"), + {}); + m_model->appendRow(m5); + + auto *m6 = new QStandardItem(); + setMsg(m6, + QString::fromUtf8("Carol"), + QString::fromUtf8("Sounds good. Let's coordinate in #dev-team. I'll prepare the release notes."), + "10:33", avCarol, false, + false, QString(), QString(), + {}); + m_model->appendRow(m6); + + auto *m7 = new QStandardItem(); + setMsg(m7, + QString::fromUtf8("Alice"), + QString::fromUtf8("Deploy scheduled for tomorrow 10:00 UTC. All features are frozen — no more merges to main."), + "10:35", avAlice, false, + false, QString(), QString(), + QVariantList{ + makeReaction(QString::fromUtf8("\xF0\x9F\x9A\x80"), 3, + {cBob, cCarol, cDave}), + makeReaction(QString::fromUtf8("\xE2\x9D\xA4\xEF\xB8\x8F"), 7) + }); + m_model->appendRow(m7); + + m_view->scrollToBottom(); +} diff --git a/tools/chatgui/src/messagelist.h b/tools/chatgui/src/messagelist.h new file mode 100644 index 00000000..cd37eae3 --- /dev/null +++ b/tools/chatgui/src/messagelist.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +class ChatView; + +class MessageList : public QWidget { + Q_OBJECT +public: + explicit MessageList(QWidget *parent = nullptr); + +private: + ChatView *m_view; + QStandardItemModel *m_model; + void populateStubData(); +};