QT6 C++ 高仿QQ -- 12. 好友通知列表,接受or拒绝,好友列表protobuf消息,自定义QListWidgetItem根据类型删除不必要的item控件

好友通知列表效果

好友通知列表
有新的好友通知,如QQ效果,右侧新好友右侧会显示有多少好友通知没有处理的数量。

protobuf消息

好友通知列表

//好友通知消息
message FriendNotice
{
    string          notice_id       = 1;        //通知id
    int64           initiator_code  = 2;        //发起者code
    int64           invitee_code    = 3;        //被邀请者code
    UserInfo        friend_info     = 4;        //朋友信息
    EInviteStatus   invite_status   = 5;        //邀请状态
    string          remark          = 6;        //邀请时备注
    string          message         = 7;        //留言
    uint64          created_time    = 8;        //创建时间
    EReadStatus     initiator_status = 9;       //发起者读取状态
    EReadStatus     invitee_status   = 10;      //被邀请者的读取状态   //客户端使用
    EInviteType     invite_type      = 11;      //邀请类型
}
//server ---> client 服务器主动下发好友通知信息
message FriendNoticeList
{
    repeated FriendNotice   notices         = 1;       //朋友通知消息
}


接受好友邀请

//client <---> server 接受好友申请
message FriendAddAccept
{
    uint64              request_id          = 1;        //请求序列
    string              notice_id           = 2;        //好友通知id
    bool                accept              = 3;        //接受还是拒绝
}

好友列表响应

//server ---> client 好友列表响应
message FriendListResponse
{
    repeated    UserFriend       friends	= 1;	    //此用户的所有朋友
}

好友通知列表显示

好友通知列表类设计

/*****************************************
 * 作者: Chris
 * 日期: 2024-04-12
 * 功能:朋友通知列表
 * ***************************************/
#ifndef FRIEND_NOTICE_LIST__H
#define FRIEND_NOTICE_LIST__H

#include "ui_friend_notice_list.h"

#include "model/user_info.h"
#include "model/friend_notice.h"

#include "widget/friend_item_control.h"     //朋友列表item控件

#include "proto/common.pb.h"
#include "proto/host_internal.pb.h"

#include <QWidget>
#include <QListWidget>
#include <QListWidgetItem>
#include <QPushButton>


namespace Ui
{
    class FriendNoticeList;
}

//联系人树节点数据自定义
struct FriendNoticeItemData
{
    bool    group;          //是否为分组
    uint64_t code;          //分组id
    uint64_t parentCode;    //父级code
};

Q_DECLARE_METATYPE(FriendNoticeItemData);


class FriendNoticeControl;
class FriendNoticeList : public QWidget
{
    Q_OBJECT

public:
    explicit FriendNoticeList(const std::shared_ptr<client::IpcThread>& ipcThread, QWidget* parent = nullptr);
    ~FriendNoticeList();

    //刷新朋友通知列表
    void onFriendNoticeList(const proto::FriendNoticeList& friendNoticeList);

    //更新某个朋友个人信息
    void onFriendInfoResponse(const proto::FriendInfoResponse& response);

    //更新某个朋友的通知信息, 或许邀请状态发生了改变
    void onFriendNoticeResponse(const proto::FriendNoticeResponse& response);

    //接受好友邀请的响应
    void onFriendAcceptResponse(const proto::FriendAcceptResponse& response);

    //设置用户信息
    void setUserInfo(const UserInfoPtr& userInfo) { userInfo_ = userInfo; }

signals:

    //UI线程信号, ---> ipc thread
    void sendMessageToIpc(proto::internal::UiToService& message);
    void newFriendNotice(uint32_t count);

public slots:                    //添加好友请求

    //void requestSearchFriend();		//发起搜好友的请求

protected:
    //void resizeEvent(QResizeEvent* e) override; //尺寸变化

private:
    void updateListWidget();
    void clearWidgets(QLayout* layout);

    void initWindow();                                  //初始化窗体
    void initButtonIcon();

    Ui::FriendNoticeList* ui = new Ui::FriendNoticeList;

    //----------------- 自定义控件 ------------------------------
    QListWidgetItem* currentItem_;      //当前的项

    QList<QAbstractButton*> btns_;	    //按钮s
    QList<int> btnIcons_;			    //按钮的ICONs

    std::shared_ptr<client::IpcThread>  ipcThread_;

    UserInfoPtr                         userInfo_ = nullptr;    //用户信息

    std::vector<base::FriendNotice>     notices_;               //朋友通知列表
    std::vector<FriendNoticeControl*>   noticeControlList_;     //设备列表

};

#endif // FRIEND_NOTICE_LIST__H


//朋友通知列表处理
void FriendNoticeList::onFriendNoticeList(const proto::FriendNoticeList& friendNoticeList)
{
    notices_.clear();
    int count = friendNoticeList.notices_size();
    uint32_t newNoticeCount = 0;
    LOG(LS_INFO) << "onFriendNoticeList friend notice count: " << count;
    for (int i = 0; i < count; i++) {
        base::FriendNotice notice = base::FriendNotice::kInvalidNotice;
        notice = base::FriendNotice::parseFrom(friendNoticeList.notices(i));
        if (friendNoticeList.notices(i).invite_type() == proto::EInviteType::INVITEE && friendNoticeList.notices(i).invitee_status() == proto::EReadStatus::READ_WAIT)
            newNoticeCount++;
        else if (friendNoticeList.notices(i).invite_type() == proto::EInviteType::INITIATOR && friendNoticeList.notices(i).initiator_status() == proto::EReadStatus::READ_WAIT)
            newNoticeCount++;
        notices_.emplace_back(std::move(notice));
    }

    //更新好友通知新消息数量
    emit newFriendNotice(newNoticeCount);

    ui->listWidget->clear();
    noticeControlList_.clear();
    update();
    updateListWidget();
}


void FriendNoticeList::updateListWidget()
{
    LOG(LS_INFO) << "updateListWidget noticeControlList_ size: " << noticeControlList_.size() << ", listWidget size: " << ui->listWidget->count();

    //将设备添加到设备列表中
    size_t listCout = notices_.size();
    if (listCout <= 0) {
        return;
    }

    for (int i = 0; i < listCout; i++) {
        FriendNoticeControl* noticeControl = new FriendNoticeControl(this, i, notices_[i].inviteType);
        noticeControl->setFriendNotice(notices_[i]);
        //noticeControl->setFixedHeight(60);
        noticeControl->update();

        //接受邀请成为好友
        connect(noticeControl, &FriendNoticeControl::friendAddFeedback, this, [&](int index, bool result) {
            if (index >= notices_.size())
                return;

            proto::internal::UiToService message;
            proto::FriendAddAccept* accept = message.mutable_friend_add_accept();
            accept->set_request_id(base::Random::number64());
            accept->set_notice_id(notices_[index].noticeId);
            accept->set_accept(result);

            emit sendMessageToIpc(message);
            });

        noticeControlList_.emplace_back(noticeControl);

        //关键代码
        QListWidgetItem* newItem = new QListWidgetItem();       //创建一个newItem
        newItem->setSizeHint(QSize(80, 80));  //每次改变Item的高度
        ui->listWidget->addItem(newItem);
        ui->listWidget->setItemWidget(newItem, noticeControl);
    }
    update();
}


//初始化窗体
void FriendNoticeList::initWindow()
{
    //1. 标题
    ui->titleLabel->setText(tr("New friend notice"));

    //2. 过滤显示
    ui->combox->addItem(tr("Wait"), proto::EInviteStatus::INVITE_WAIT);
    ui->combox->addItem(tr("Agreed"), proto::EInviteStatus::INVITE_AGREED);
    ui->combox->addItem(tr("Reject"), proto::EInviteStatus::INVITE_REJECT);
    ui->combox->setStyleSheet("QComboBox QAbstractItemView { background-color: white; }");

    //3. 按钮样式-- 字体图标/颜色/hover和checked样式等
    initButtonIcon();

    //4. listWidget样式设置, 去掉默认的hover和选中的背景色改变
    ui->listWidget->setStyleSheet("QListWidget{border: none; background-color: transparent; }"
        "QListWidget::Item:hover{background:transparent; }"
        "QListWidget::item:selected{background:transparent; }"
        //"QListWidget::item:selected:!active{border-width:0px; background:lightgreen; }"
    );
}


//初始化工具条按钮的图标, 样式设置等
void FriendNoticeList::initButtonIcon()
{
    //字体图标: fa-trash-o 0xf104 黑色
    ui->btnClear->setText("");

    btnIcons_ << 0xf014;
    btns_ << ui->btnClear;

    //左侧导航样式,可以设置各种牛逼的参数,超级棒
    IconHelper::StyleColor styleColor;
    styleColor.iconSize = 18;
    styleColor.iconWidth = 25;
    styleColor.iconHeight = 25;
    styleColor.position = "left";

    styleColor.borderColor = "#F5F5F5";
    styleColor.borderWidth = 0;

    styleColor.hoverTextColor = "#4169E1";  //hover的颜色---#4169E1 royalblue 蓝色
    styleColor.hoverBgColor = "#F5F5F5";    //背景色---#F5F5F5 wihtesmoke 烟白色

    styleColor.normalBgColor = "#F5F5F5";   //背景色---#F5F5F5 wihtesmoke 烟白色
    styleColor.normalTextColor = "#000000"; //正常字体颜色: #000000 黑色

    styleColor.checkedBgColor = "#F5F5F5";      //选中的背景---烟白色
    styleColor.checkedTextColor = "#4169E1";    //选中的字体---#4169E1 royalblue 蓝色

    //styleColor.setColor(QUIConfig::NormalColorEnd, QUIConfig::TextColor, QUIConfig::PanelColor, QUIConfig::TextColor);
     //1. 正常的背景色:  #F5F5F5 wihtesmoke 烟白色; 2. 正常的字体图标和文字的颜色, #000000 黑色; 3. 整个按钮的颜色, 与背景同色; 4. 选中或按下的字体颜色 #4169E1 royalblue 蓝色
    styleColor.setColor("#F5F5F5", "#000000", "#F5F5F5", "#4169E1");
    IconHelper::setStyle(this, btns_, btnIcons_, styleColor);   //将widget中所有按钮都设置成了同样的样式

    //设置为无边框
    ui->btnClear->setStyleSheet("border:none");
}

自定义QListWidgetItem展示好友通知信息,根据通知类型删除不必要的QWidget item

/*****************************************
 * 作者: Chris
 * 日期: 2024-03-19
 * 功能:好友通知QListWidgetItem控件
 * ***************************************/
#ifndef FRIEND_NOTICE_CONTROL__H
#define FRIEND_NOTICE_CONTROL__H

#include "ui_friend_notice_control.h"

#include "model/user_info.h"
#include "model/friend_notice.h"

#include "proto/common.pb.h"

#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
#include <QEvent>

namespace Ui
{
    class FriendNoticeControl;
}

class UserInfoDialog;

class FriendNoticeControl : public QWidget
{
    Q_OBJECT

public:
    explicit FriendNoticeControl(QWidget* parent, int index, proto::EInviteType type);
    ~FriendNoticeControl();

    void setFriendNotice(const base::FriendNotice& notice);
    void setSelected(bool selected);    //选中状态设置

private slots:

signals:
    void clicked(int index);            //设备控件被选择信号
    void friendAddFeedback(int index, bool agree);
    void requestFriendInfo(int index);  //点击头像主动请求下friend信息


protected:
    virtual void mousePressEvent(QMouseEvent* event);//设备左键选中重写
    bool eventFilter(QObject* object, QEvent* event) override;

private:

    void setHeaderImage(const std::string& headerImage);

    void onHeaderClicked();           //点击头像和人名, 弹出显示个人信息

    Ui::FriendNoticeControl* ui = new Ui::FriendNoticeControl();

    std::unique_ptr <UserInfoDialog> userInfoDialog_ = nullptr;
    int index_;

    base::UserInfo userInfo_;

};

#endif // FRIEND_NOTICE_CONTROL__H


 // 构造函数
FriendNoticeControl::FriendNoticeControl(QWidget* parent, int index, proto::EInviteType type) :
    QWidget(parent),
    index_(index)
{
    ui->setupUi(this);

    setStyleSheet("background-color: white; border-radius: 12px;"
        //    "QPushButton#btnAgree, QPushButton#btnReject{background: transparent; border: 1px solid lightgray; border-radius: 5px; height: 20px;}"
    );

    if (type == proto::EInviteType::INVITEE) {
        //同意删除按钮
        ui->btnAgree->setCheckable(true);
        ui->btnAgree->setCursor(Qt::PointingHandCursor);  //鼠标hover状态改变鼠标形状为小手
        ui->btnReject->setCheckable(true);
        ui->btnReject->setCursor(Qt::PointingHandCursor);  //鼠标hover状态改变鼠标形状为小手

        connect(ui->btnAgree, &QPushButton::clicked, this, [&]() {
            emit friendAddFeedback(index_, true);
            });
        connect(ui->btnReject, &QPushButton::clicked, this, [&]() {
            emit friendAddFeedback(index_, false);
            });
    }
    else {
        //主动邀请, 删除同意和拒绝按钮
        QList<QPushButton*> btns = this->findChildren<QPushButton*>();
        foreach(QPushButton * btn, btns)
        {
            if ("btnAgree" == btn->objectName() || "btnReject" == btn->objectName()) {
                btn->deleteLater();
            }
        }
    }

    ui->nickNameLabel->setStyleSheet("color: royalblue");
    ui->headerLabel->installEventFilter(this);  //1.给label安装事件过滤器
    ui->nickNameLabel->installEventFilter(this);  //1.给label安装事件过滤器

    ui->createdDateLabel->setStyleSheet("color: silver");

}

// 析构函数
FriendNoticeControl::~FriendNoticeControl()
{
    delete ui;
}


//设置好友通知
void FriendNoticeControl::setFriendNotice(const base::FriendNotice& notice)
{
    //保存用户信息
    userInfo_ = notice.friendInfo;

    //头像
    setHeaderImage(notice.friendInfo.headerImage);

    //昵称
    ui->nickNameLabel->setText(QString::fromStdU16String(notice.friendInfo.nickName));

    //状态不是wait则删除两个按钮
    if (notice.inviteStatus != proto::EInviteStatus::INVITE_WAIT) {
        //主动邀请, 删除同意和拒绝按钮
        QList<QPushButton*> btns = this->findChildren<QPushButton*>();
        foreach(QPushButton * btn, btns)
        {
            if ("btnAgree" == btn->objectName() || "btnReject" == btn->objectName()) {
                btn->deleteLater();
            }
        }
    }

    //邀请信息
    QString gender;
    if (notice.friendInfo.gender == proto::GenderType::MALE)
        gender = tr("his");
    else
        gender = tr("her");

    QString inviteInfo;
    if (notice.inviteType == proto::EInviteType::INITIATOR) {   //主动邀请
        if (notice.inviteStatus == proto::EInviteStatus::INVITE_WAIT) {
            inviteInfo += tr(" is verifying your friend request");
            ui->waitLabel->setText(tr("Wait"));
        }
        else if (notice.inviteStatus == proto::EInviteStatus::INVITE_AGREED) {
            inviteInfo += tr(" agreed to your friend request");
            ui->waitLabel->setText(tr("Agreed"));
        }
        else if (notice.inviteStatus == proto::EInviteStatus::INVITE_REJECT) {
            inviteInfo += tr(" rejected your friend request");
            ui->waitLabel->setText(tr("Rejected"));
        }
    }
    else if (notice.inviteType == proto::EInviteType::INVITEE) {  //被邀请
        inviteInfo += tr(" request to add as a friend");

        //被邀请者如果状态为wait则删除等待label
        if (notice.inviteStatus == proto::EInviteStatus::INVITE_WAIT) {
            QList<QLabel*> labels = this->findChildren<QLabel*>();
            foreach(QLabel * label, labels)
            {
                if ("waitLabel" == label->objectName()) {
                    label->deleteLater();
                }
            }
        }
        else if (notice.inviteStatus == proto::EInviteStatus::INVITE_AGREED) {
            ui->waitLabel->setText(tr("Agreed"));
        }
        else if (notice.inviteStatus == proto::EInviteStatus::INVITE_REJECT) {
            ui->waitLabel->setText(tr("Rejected"));
        }
    }
    else {

    }

    ui->inviteInfoLabel->setText(inviteInfo);

    //创建时间
    QDateTime createdTime = QDateTime::fromMSecsSinceEpoch(notice.createdTime);
    ui->createdDateLabel->setText(createdTime.toString("yyyy-MM-dd HH:mm:ss"));

    //预留消息
    ui->messageLabel->setText(QString::fromStdU16String(notice.message));
}

//头像按钮点击
void FriendNoticeControl::onHeaderClicked()
{
    //emit requestFriendInfo(index_);

    // 获取按钮的位置和大小
    QRect buttonRect = ui->headerLabel->geometry();

    // 计算对话框的位置
    QPoint dialogPos = ui->headerLabel->mapToGlobal(buttonRect.bottomLeft());
    dialogPos.setX(dialogPos.x() + 60);
    dialogPos.setY(dialogPos.y());

    //创建智能指针的user类
    UserInfoPtr user = std::make_shared<base::UserInfo>(userInfo_);

    userInfoDialog_.reset(new UserInfoDialog(dialogPos, user, true, true, this));

    userInfoDialog_->setModal(false);
    userInfoDialog_->setFixedSize(QSize(400, 350));

    //dlg.setUserInfo(userInfo_.serialize());

    userInfoDialog_->setGeometry(QRect(dialogPos, QSize(400, 350))); // 宽度200,高度100

    //非模态对话框自动关闭
    connect(userInfoDialog_.get(), &UserInfoDialog::hideDialog, this, [&]() {
        if (userInfoDialog_)
            userInfoDialog_->hide();
        });

    this->installEventFilter(userInfoDialog_.get());
    userInfoDialog_->show();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chrisLee_sz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值