#include "mainwidget.h" #include "./ui_mainwidget.h" #include #include #include "sessiondetailwidget.h" MainWidget *MainWidget::instance = nullptr; MainWidget *MainWidget::getInstance() { if(instance == nullptr) { //此处不传入参数,以桌面为父窗口 // instance = new MainWidget(); } return instance; } MainWidget::MainWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MainWidget) { ui->setupUi(this); // this->setStyleSheet("MainWidget {min-height: 6000px; }"); this->resize(QSize(1280, 800)); this->setWindowTitle("MyChat"); this->setWindowIcon(QIcon(":/resource/image/logo.png")); //初始化主窗口的样式布局 initMainWindow(); //初始化左侧窗口的样式布局 initLeftWindow(); //初始化中间窗口的样式布局 initMidWindow(); //初始化右侧窗口的样式布局 initRightWindow(); //初始化信号槽 initSignalSlot(); } MainWidget::~MainWidget() { delete ui; } void MainWidget::initMainWindow() { QHBoxLayout* layout = new QHBoxLayout(); //layout内部的元素间隔设置为0 layout->setSpacing(0); //layout内部元素四个方向边界的距离设置 layout->setContentsMargins(0, 0, 0, 0); this->setLayout(layout); windowLeft = new QWidget(); windowMid = new QWidget(); windowRight = new QWidget(); windowLeft->setFixedWidth(70); windowMid->setFixedWidth(320); windowRight->setMinimumWidth(600); windowLeft->setStyleSheet("QWidget { background-color: rgb(46, 46, 46); }"); windowMid->setStyleSheet("QWidget { background-color: rgb(247, 247, 247); }"); windowRight->setStyleSheet("QWidget { background-color: rgb(245, 245, 245); }"); layout->addWidget(windowLeft); layout->addWidget(windowMid); layout->addWidget(windowRight); } void MainWidget::initLeftWindow() { QVBoxLayout* layout = new QVBoxLayout(); layout->setSpacing(20); layout->setContentsMargins(0, 50, 0, 0); windowLeft->setLayout(layout); //添加用户头像 userAvatar = new QPushButton(); userAvatar->setFixedSize(45, 45); userAvatar->setIconSize(QSize(45, 45)); //由于要从网络获取头像信息,所以这里就不能再设置默认的头像了,;避免头像意外变化 //userAvatar->setIcon(QIcon(":/resource/image/defaultAv.png")); userAvatar->setStyleSheet("QPushButton {background-color: transparent; }"); layout->addWidget(userAvatar, 1, Qt::AlignTop | Qt::AlignCenter); //添加会话标签页按钮 sessionTabBtn = new QPushButton(); sessionTabBtn->setFixedSize(45, 45); sessionTabBtn->setIconSize(QSize(35, 35)); sessionTabBtn->setIcon(QIcon(":/resource/image/session_active.png")); sessionTabBtn->setStyleSheet("QPushButton {background-color: transparent; }"); layout->addWidget(sessionTabBtn, 1, Qt::AlignTop | Qt::AlignCenter); //添加好友标签页按钮 friendTabBtn = new QPushButton(); friendTabBtn->setFixedSize(45, 45); friendTabBtn->setIconSize(QSize(35, 35)); friendTabBtn->setIcon(QIcon(":/resource/image/friend_inactive.png")); friendTabBtn->setStyleSheet("QPushButton {background-color: transparent; }"); layout->addWidget(friendTabBtn, 1, Qt::AlignTop | Qt::AlignCenter); //添加好友申请标签页按钮 applyTabBtn = new QPushButton(); applyTabBtn->setFixedSize(45, 45); applyTabBtn->setIconSize(QSize(35, 35)); applyTabBtn->setIcon(QIcon(":/resource/image/apply_inactive.png")); applyTabBtn->setStyleSheet("QPushButton {background-color: transparent; }"); layout->addWidget(applyTabBtn, 1, Qt::AlignTop | Qt::AlignCenter); layout->addStretch(20); } void MainWidget::initMidWindow() { QGridLayout* layout = new QGridLayout(); //距离上方有20px,另外的三个方向都不要边距 layout->setContentsMargins(0, 20, 0, 0); layout->setSpacing(0); windowMid->setLayout(layout); searchEdit = new QLineEdit(); searchEdit->setFixedHeight(30); searchEdit->setPlaceholderText("搜索"); searchEdit->setStyleSheet("QLineEdit { border-radius: 5px; color: rgb(129, 129, 129); background-color: rgb(226, 226, 226); padding-left: 5px; }"); addFriendBtn = new QPushButton(); addFriendBtn->setFixedSize(30, 30); addFriendBtn->setIcon(QIcon(":/resource/image/cross.png")); QString style = R"(QPushButton { border-radius: 5px; background-color: rgb(226, 226, 226); } QPushButton::pressed { background-color: rgb(240, 240, 240); })"; addFriendBtn->setStyleSheet(style); sessionFriendArea = new SessionFriendArea(); //为了更加灵活的控制边距,只影响搜索框和按钮的这一行, //创建空白的widget填充到布局管理器上 QWidget* spacer1 = new QWidget(); spacer1->setFixedWidth(10); QWidget* spacer2 = new QWidget(); spacer2->setFixedWidth(10); QWidget* spacer3 = new QWidget(); spacer3->setFixedWidth(10); QWidget* spacer4 = new QWidget(); spacer4->setFixedHeight(10); layout->addWidget(spacer1, 0, 0); layout->addWidget(searchEdit, 0, 1); layout->addWidget(spacer2, 0, 2); layout->addWidget(addFriendBtn, 0 ,3); layout->addWidget(spacer3, 0, 4); layout->addWidget(spacer4, 1, 0, 1, 5); layout->addWidget(sessionFriendArea, 2, 0, 1, 5); } void MainWidget::initRightWindow() { //创建右侧窗口的布局管理器 QVBoxLayout* vlayout = new QVBoxLayout(); vlayout->setSpacing(0); vlayout->setContentsMargins(0, 0, 0, 0); vlayout->setAlignment(Qt::AlignTop); windowRight->setLayout(vlayout); //创建上方的标题栏 QWidget* titleWidget = new QWidget(); titleWidget->setFixedHeight(62); titleWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); titleWidget->setObjectName("titleWidget"); titleWidget->setStyleSheet(R"(#titleWidget { border-bottom: 2px solid rgb(231, 231, 231); border-left: 1px solid rgb(231, 231, 231); } )"); vlayout->addWidget(titleWidget); //给标题栏添加 label 和 button QHBoxLayout* hlayout = new QHBoxLayout(); hlayout->setSpacing(0); hlayout->setContentsMargins(10, 0, 10, 0); titleWidget->setLayout(hlayout); sessionTitleLabel = new QLabel(); sessionTitleLabel->setStyleSheet("QLabel { font-size: 22px; border-bottom: 1px solid rgb(230, 230, 230); }"); #if TEST_UI //为了测试仅填充从服务器获取的数据 sessionTitleLabel->setText("TEST TITLE"); #endif hlayout->addWidget(sessionTitleLabel); extraBtn = new QPushButton(); extraBtn->setFixedSize(30, 30); extraBtn->setIconSize(QSize(30, 30)); extraBtn->setIcon(QIcon(":/resource/image/more.png")); extraBtn->setStyleSheet(R"(QPushButton { border: none; background-color: transparent; } QPushButton:pressed { background-color: rgb(210, 210 ,210); } )"); hlayout->addWidget(extraBtn); //添加消息展示区 messageShowArea = new MessageShowArea(); vlayout->addWidget(messageShowArea); //添加消息编辑区 messageEditArea = new MessageEditArea(); //确保消息编辑区处于窗口正下方 vlayout->addWidget(messageEditArea, 0, Qt::AlignBottom); } void MainWidget::initSignalSlot() { model::DataCenter* dataCenter = model::DataCenter::getInstance(); ///////////////////////////////////// //连接信号槽,处理标签页切换 ///////////////////////////////////// connect(sessionTabBtn, &QPushButton::clicked, this, &MainWidget::switchTabToSession); connect(friendTabBtn, &QPushButton::clicked, this, &MainWidget::switchTabToFriend); connect(applyTabBtn, &QPushButton::clicked, this, &MainWidget::switchTabToApply); ///////////////////////////////////// // 点击自己的头像,弹出对话框显示个人的主页 ///////////////////////////////////// connect(userAvatar, &QPushButton::clicked, this, [=]()->void { SelfInfoWidget* selfInfoWidget = new SelfInfoWidget(this); selfInfoWidget->setStyleSheet("QDialog { background-color: rgb(245, 245, 245); }"); selfInfoWidget->exec(); //弹出模态对话框 //selfInfoWidget->show(); //弹出非模态对加框 }); ///////////////////////////////////// // 点击右上角的更多按钮,扩展会话的详细信息 ///////////////////////////////////// connect(extraBtn, &QPushButton::clicked, this, [=]() { //判定当前的会话是单聊还是群聊 #if TEST_GROUP_SESSION_DETAIL bool isSingleChat = false; //要根据当前选中的实际的会话来确定 #else bool isSingleChat = true; //要根据当前选中的实际的会话来确定 #endif if (isSingleChat) { //说明是单聊 SessionDetailWidget* sessionDetailWidget = new SessionDetailWidget(this); sessionDetailWidget->exec(); } else { //说明是群聊 GroupSessionDetailWidget* groupSessionDetailWidget = new GroupSessionDetailWidget(this); groupSessionDetailWidget->exec(); } }); ///////////////////////////////////// // 点击添加好友按钮,弹出添加好友的窗口 ///////////////////////////////////// connect(addFriendBtn, &QPushButton::clicked, this, [=]() { AddFriendDialog* addFriendDialog = new AddFriendDialog(this); addFriendDialog->exec(); }); ///////////////////////////////////// // 修改搜索框内容,设置到新弹出的输入框里面 ///////////////////////////////////// connect(searchEdit, &QLineEdit::textEdited, this, [=]() { const QString& searchKey = searchEdit->text(); AddFriendDialog* addFriendDialog = new AddFriendDialog(this); addFriendDialog->setSearchKey(searchKey); searchEdit->setText(""); addFriendDialog->exec(); }); ///////////////////////////////////// // 获取个人的信息 ///////////////////////////////////// //提供一个具体的方法,来获取到网络的数据 connect(dataCenter, &DataCenter::getMyselfDone, this, [=]() { //从DataCenter中拿到响应的结果myself,把里面的头像取出来,显示到界面上 //由于响应的逻辑已经执行完毕,说明从网络拿到了信息,则直接同步获取即可 auto myself = dataCenter->getMyselfsync(); userAvatar->setIcon(myself->avatar); }); dataCenter->getMyselfAsync(); ///////////////////////////////////// // 获取好友列表 ///////////////////////////////////// loadFriendList(); ///////////////////////////////////// // 获取会话列表 ///////////////////////////////////// loadSessionList(); ///////////////////////////////////// // 获取好友申请的列表 ///////////////////////////////////// loadApplyList(); } void MainWidget::switchTabToSession() { //记录当前切换到了哪个标签页 activeTab = SESSION_LIST; //调整图标显示情况 sessionTabBtn->setIcon(QIcon(":/resource/image/session_active.png")); friendTabBtn->setIcon(QIcon(":/resource/image/friend_inactive.png")); applyTabBtn->setIcon(QIcon(":/resource/image/apply_inactive.png")); // 在主窗口的中间部分,加载出会话列表数据 this->loadSessionList(); } void MainWidget::switchTabToFriend() { //记录当前切换到了哪个标签页 activeTab = FRIEND_LIST; //调整图标显示情况 sessionTabBtn->setIcon(QIcon(":/resource/image/session_inactive.png")); friendTabBtn->setIcon(QIcon(":/resource/image/friend_active.png")); applyTabBtn->setIcon(QIcon(":/resource/image/apply_inactive.png")); // 在主窗口的中间部分,加载出会话列表数据 this->loadFriendList(); } void MainWidget::switchTabToApply() { //记录当前切换到了哪个标签页 activeTab = APPLY_LIST; //调整图标显示情况 sessionTabBtn->setIcon(QIcon(":/resource/image/session_inactive.png")); friendTabBtn->setIcon(QIcon(":/resource/image/friend_inactive.png")); applyTabBtn->setIcon(QIcon(":/resource/image/apply_active.png")); // 在主窗口的中间部分,加载出会话列表数据 this->loadApplyList(); } //加载会话列表 void MainWidget::loadSessionList() { //会判定会话是否存在本地的DataCenter中,若存在,则直接构造界面 //若不存在则从服务器获取数据 DataCenter* dataCenter = DataCenter::getInstance(); if (dataCenter->getChatSessionList() != nullptr) { //本地内存中获取数据 LOG() << "本地已存在会话列表数据,更新加载本地会话"; updateChatSessionList(); } else { // 从网路中加载数据 LOG() << "本地会话列表数据不存在,正在从网络获取..."; connect(dataCenter, &DataCenter::getChatSessionListDone, this, &MainWidget::updateChatSessionList, Qt::UniqueConnection); dataCenter->getChatSessionListAsync(); } } //加载好友列表 void MainWidget::loadFriendList() { //好友列表数据是在DataCenter中存储的 //首先需要判定DataCenter中是否已经有数据了,如果有,则直接加载本地数据 //如果没有,则从服务器读取 DataCenter* dataCenter = DataCenter::getInstance(); if (dataCenter->getFriendList() != nullptr) { //从内存中加载数据 LOG() << "本地已存在好友列表数据,更新加载本地好友"; updateFriendList(); } else { //通过网络加载数据 通过这个参数确保信号槽不会别重复绑定 LOG() << "本地好友列表数据不存在,正在从网络获取..."; connect(dataCenter, &DataCenter::getFriendListDone, this, &MainWidget::updateFriendList, Qt::UniqueConnection); dataCenter->getFriendListAsync(); } } //加载申请列表 void MainWidget::loadApplyList() { //好友申请列表在DataCenter中存储 //同理首先需要判定DataCenter中是否已经有数据了,如果有,则直接加载本地数据 //如果没有,则从服务器读取 DataCenter* dataCenter = DataCenter::getInstance(); if (dataCenter->getApplyList() != nullptr) { //从内存中加载数据 LOG() << "本地已存在用户申请列表数据,更新加载申请列表"; updateApplyList(); } else { //通过网络加载数据 LOG() << "用户申请列表数据不存在,正在从网络获取..."; connect(dataCenter, &DataCenter::getApplyListDone, this, &MainWidget::updateApplyList, Qt::UniqueConnection); dataCenter->getApplyListAsync(); } } void MainWidget::updateFriendList() { if (activeTab != FRIEND_LIST) { //当前的标签页不是好友列表,就不渲染任何数据到界面上 return; } DataCenter* dataCenter = DataCenter::getInstance(); QList* friendList = dataCenter->getFriendList(); //清空一下之前的界面的数据 sessionFriendArea->clear(); //遍历好友列表,添加到界面上 for (const auto& f : *friendList) { sessionFriendArea->addItem(FriendItemType, f.userId, f.avatar, f.nickname, f.description); } } void MainWidget::updateChatSessionList() { if (activeTab != SESSION_LIST) { //当前的标签页不是会话列表,不渲染任何数据到界面上 return; } DataCenter* dataCenter = DataCenter::getInstance(); QList* chatSessionList = dataCenter->getChatSessionList(); sessionFriendArea->clear(); for (const auto& c : *chatSessionList) { switch (c.lastMessage.messageType) { case TEXT_TYPE: sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, c.lastMessage.content); break; case IMAGE_TYPE: sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, "[图片]"); break; case FILE_TYPE: sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, "[文件]"); break; case SPEECH_TYPE: sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, "[语音]"); break; default: LOG() << "updateSessionList 收到了错误的“最后一条消息的类型”"; break; } } } void MainWidget::updateApplyList() { if (activeTab != APPLY_LIST) { //当前的标签页不是“好友申请列表” return; } DataCenter* dataCenter = DataCenter::getInstance(); QList* applyList = dataCenter->getApplyList(); sessionFriendArea->clear(); for (const auto& u : *applyList) { //直接把这个UserInfo对象添加到界面之上,不涉及PB和自己结构的转换 //此处的UserInfo的desc不需要填写进来,好友的申请列表中, 不显示用户的标签 sessionFriendArea->addItem(ApplyItemType, u.userId, u.avatar, u.nickname, ""); } } void MainWidget::loadRecentMessage(const QString& chatSessionId) { //也是先判定,本地内存中是否已经存在对应的消息列表数据 //有的话直接显示到界面之上,没有的话从网络中获取 DataCenter* dataCenter = DataCenter::getInstance(); if (dataCenter->getRecentMessageList(chatSessionId) != nullptr) { //直接拿着本地数据更新界面 LOG() << "本地已存在好友聊天数据,更新加载好友聊天数据"; updateRecentMessage(chatSessionId); } else { //本地没有数据,从网络加载 LOG() << "好友聊天数据不存在,正在从网络获取..."; connect(dataCenter, &DataCenter::getRecentMessageListDone, this, &MainWidget::updateRecentMessage, Qt::UniqueConnection); dataCenter->getRecnetMessageListAsync(chatSessionId); } } void MainWidget::updateRecentMessage(const QString& chatSessionId) { //拿到该会话最近的消息列表 DataCenter* dataCenter = DataCenter::getInstance(); auto* recentMessageList = dataCenter->getRecentMessageList(chatSessionId); //清空原有界面上的消息列表 messageShowArea->clear(); //根据当前拿到的消息列表,显示到界面上 //此处把数据显示到界面上,可以使用头插也可以尾插 //对于消息列表来说,用户最先看到的应该是最近的消息,也就是末尾的消息 for (int i = recentMessageList->size() - 1; i >= 0; i--) { const Message& message = recentMessageList->at(i); bool isLeft = message.sender.userId != dataCenter->getMyselfsync()->userId; messageShowArea->addFrontMessage(isLeft, message); } //设置会话标题 ChatSessionInfo* chatSessionInfo = dataCenter->findChatSessionById(chatSessionId); if (chatSessionInfo != nullptr) { //把会话名称显示到界面之上 sessionTitleLabel->setText(chatSessionInfo->chatSessionName); } //保存当前选中的会话是哪个 dataCenter->setCurrentChatSessionId(chatSessionId); //自动把滚动条滚动到末尾 messageShowArea->scrollToEnd(); }