系统设计

模块设计

好友申请模块

主要组件:ClickedBtn、QScrollArea、CustomizeEdit、ClickedOnceLabel

使用的自定义组件:CustomizeEdit、ClickedBtn、QScrollArea、、ClickedOnceLabel

模块运行逻辑

chatdialog_ui.png
聊天主界面中将search_edit内容改变时发出信号,绑定若文本不为空执行槽函数ShowSearch,用于显示搜索列表。
connect(ui->search_edit,&QLineEdit::textChanged,this,&ChatDialog::slot_text_changed);

ShowSearch函数功能
 
void ChatDialog::ShowSearch(bool bsearch)
{
    if(bsearch){
        ui->chat_user_list->hide();
        ui->con_user_list->hide();
        ui->search_list->show();
        _mode = ChatUIMode::SearchMode;
    }else if(_state == ChatUIMode::ChatMode){
        ui->chat_user_list->show();
        ui->con_user_list->hide();
        ui->search_list->hide();
        _mode = ChatUIMode::ChatMode;
    }else if(_state == ChatUIMode::ContactMode){
        ui->chat_user_list->hide();
        ui->search_list->hide();
        ui->con_user_list->show();
        _mode = ChatUIMode::ContactMode;
    }

SearchList的构造函数中,实现了添加搜索的AddUserItem 以 ListItemBase 为父类 ,QListWidgetBase为基类

SearchList查询添加好友的Item
 
void SearchList::addTipItem()
{
    auto *invalid_item = new QWidget();
    QListWidgetItem *item_tmp = new QListWidgetItem;
    //qDebug()<<"chat_user_wid sizeHint is " << chat_user_wid->sizeHint();
    item_tmp->setSizeHint(QSize(250,10));
    this->addItem(item_tmp);
    invalid_item->setObjectName("invalid_item");
    this->setItemWidget(item_tmp, invalid_item);
    item_tmp->setFlags(item_tmp->flags() & ~Qt::ItemIsSelectable);
auto *add_user_item = new AddUserItem();
QListWidgetItem *item = new QListWidgetItem;
//qDebug()<<"chat_user_wid sizeHint is " << chat_user_wid->sizeHint();
item->setSizeHint(add_user_item->sizeHint());
this->addItem(item);
this->setItemWidget(item, add_user_item);

}

当点击Searchlist时,出发点击槽函数,并且判断类型。

QListWidgetItem的类型
 
//自定义QListWidgetItem的几种类型
enum ListItemType{
    CHAT_USER_ITEM, //聊天用户
    CONTACT_USER_ITEM, //联系人用户
    SEARCH_USER_ITEM, //搜索到的用户
    ADD_USER_TIP_ITEM, //提示添加用户
    INVALID_ITEM,  //不可点击条目
    GROUP_TIP_ITEM, //分组提示条目
    LINE_ITEM,  //分割线
    APPLY_FRIEND_ITEM, //好友申请
};
用于创建添加好友界面的slot
 
void SearchList::slot_item_clicked(QListWidgetItem *item)
{
    QWidget *widget = this->itemWidget(item); // 获取自定义widget对象
    if(!widget){
        qDebug()<< "slot item clicked widget is nullptr";
        return;
    }
// 对自定义widget进行操作, 将item 转化为基类ListItemBase
ListItemBase *customItem = qobject_cast<ListItemBase*>(widget);
if(!customItem){
    qDebug()<< "slot item clicked widget is nullptr";
    return;
}

auto itemType = customItem->GetItemType();
if(itemType == ListItemType::INVALID_ITEM){
    qDebug()<< "slot invalid item clicked ";
    return;
}

if(itemType == ListItemType::ADD_USER_TIP_ITEM){
	
	
	//生成对应的查找信息的界面QDialog
    _find_dlg = std::make_shared<FindSuccessDlg>(this);


	//该处为自定义,后期改写为服务器获取
    auto si = std::make_shared<SearchInfo>					(0,"wzq","wzq","wzq",0,":C:/Users/12291/Pictures/icon/add_friend_normal.png");
    
    //使用强制转换
    dynamic_pointer_cast<FindSuccessDlg>(_find_dlg)->SetSearchInfo(si);
    
    //显示
    _find_dlg->show();

    // dynamic_pointer_cast<FindSuccessDlg>(_find_dlg)->SetSearchInfo(si);

    // dynamic_pointer_cast<FindSuccessDlg>(_find_dlg)->SetSearchInfo(si);
    // if (_send_pending) {
    //     return;
    // }

    // if (!_search_edit) {
    //     return;
    // }
    // waitPending(true);
    // auto search_edit = dynamic_cast<CustomizeEdit*>(_search_edit);
    // auto uid_str = search_edit->text();
    // //此处发送请求给server
    // QJsonObject jsonObj;
    // jsonObj["uid"] = uid_str;

    // QJsonDocument doc(jsonObj);
    // QByteArray jsonData = doc.toJson(QJsonDocument::Compact);

    // //发送tcp请求给chat server
    // emit TcpMgr::GetInstance()->sig_send_data(ReqId::ID_SEARCH_USER_REQ, jsonData);
    return;
}

//清除弹出框
CloseFindDlg();

}

在上述代码执行后会显示界面。

AddFriend_1.png

弹出显示对应信息之后,点击FindSuccessDlg的add_friend_btn触发槽函数用于显示添加好友界面。

on_add_friend_btn_clicked函数
 
void FindSuccessDlg::on_add_friend_btn_clicked()
{
	//隐藏当前界面(添加好友)
    this->hide();
    //弹出加好友界面(详细信息)
    auto applyFriend = new ApplyFriend(_parent);
    applyFriend->SetSearchInfo(_si);
//QDialog 类的成员函数。将当前窗口设置为模态窗口,当此窗口显示时,用户不能与其他窗口进行交互,直到这个窗口被关闭或隐藏。
applyFriend->setModal(true);
applyFriend->show();

}

## CustomizeEdit applyFriend为一个设计师界面类型,用于显示添加好友时的详细信息。点击确认发送请求给服务器。

AddSuccess.png

组件设计

CustomizeEdit

功能描述

CustomizeEdit 为QLineEdit 的自定义提升类,相对于QlineEdit 添加焦点检测、设置最大长度

customizeedit.h
#ifndef CUSTOMIZEEDIT_H
#define CUSTOMIZEEDIT_H
#include <QLineEdit>
#include <QDebug>

class CustomizeEdit: public QLineEdit
{
    Q_OBJECT
public:
    CustomizeEdit(QWidget *parent = nullptr);
    void SetMaxLength(int maxLen);
protected:
    void focusOutEvent(QFocusEvent *event) override
    {
        // 执行失去焦点时的处理逻辑
        //qDebug() << "CustomizeEdit focusout";
        // 调用基类的focusOutEvent()方法,保证基类的行为得到执行
        QLineEdit::focusOutEvent(event);
        //发送失去焦点得信号
        emit sig_foucus_out();
    }
private:
    void limitTextLength(QString text) {
        if(_max_len <= 0){
            return;
        }

        QByteArray byteArray = text.toUtf8();

        if (byteArray.size() > _max_len) {
            byteArray = byteArray.left(_max_len);
            this->setText(QString::fromUtf8(byteArray));
        }
    }

    int _max_len;
signals:
    void sig_foucus_out();
};

#endif // CUSTOMIZEEDIT_H
customizeedit.cpp
#include "customizeedit.h"

CustomizeEdit::CustomizeEdit(QWidget *parent):QLineEdit (parent),_max_len(0)
{
    connect(this, &QLineEdit::textChanged, this, &CustomizeEdit::limitTextLength);
}

void CustomizeEdit::SetMaxLength(int maxLen)
{
    _max_len = maxLen;
}

ClickLabel

功能描述

ClickLabel 为 QLabel的自定义提升类,相对于QlineEdit添加鼠标按下、释放、进入、进出的事件处理鼠标对应状态显示的切换

clicklabel.h
#ifndef CLICKEDLABEL_H
#define CLICKEDLABEL_H
#include <QLabel>
#include "global.h"

class ClickedLabel:public QLabel
{
    Q_OBJECT
public:
    ClickedLabel(QWidget* parent);
    virtual void mousePressEvent(QMouseEvent *ev) override;
    virtual void mouseReleaseEvent(QMouseEvent *ev) override;
    virtual void enterEvent(QEnterEvent *event) override;
    virtual void leaveEvent(QEvent* event) override;
    void SetState(QString normal="", QString hover="", QString press="",
                  QString select="", QString select_hover="", QString select_press="");
	
    ClickLbState GetCurState();
    bool SetCurState(ClickLbState state);
    void ResetNormalState();
protected:

private:
    QString _normal;
    QString _normal_hover;
    QString _normal_press;

    QString _selected;
    QString _selected_hover;
    QString _selected_press;

    ClickLbState _curstate;
signals:
    void clicked(QString,ClickLbState);

};

#endif // CLICKEDLABEL_H
    
    
clicklabel.cpp
#include "clickedlabel.h"
#include <QMouseEvent>
ClickedLabel::ClickedLabel(QWidget* parent):QLabel (parent),_curstate(ClickLbState::Normal)
{
    this->setCursor(Qt::PointingHandCursor);
}


// 处理鼠标点击事件
void ClickedLabel::mousePressEvent(QMouseEvent* event)  {


    if (event->button() == Qt::LeftButton) {
        if(_curstate == ClickLbState::Normal){
            qDebug()<<"clicked , change to selected hover: "<< _selected_hover;
            _curstate = ClickLbState::Selected;
            setProperty("state",_selected_hover);
            repolish(this);
            update();

        }else{
            qDebug()<<"clicked , change to normal hover: "<< _normal_hover;
            _curstate = ClickLbState::Normal;
            setProperty("state",_normal_hover);
            repolish(this);
            update();
        }
        // emit clicked();
        return;
    }
    // 调用基类的mousePressEvent以保证正常的事件处理
    QLabel::mousePressEvent(event);
}

void ClickedLabel::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        if(_curstate == ClickLbState::Normal){
            // qDebug()<<"ReleaseEvent , change to normal hover: "<< _normal_hover;
            setProperty("state",_selected_press);
            repolish(this);
            update();

        }else{
            //  qDebug()<<"ReleaseEvent , change to select hover: "<< _selected_hover;
            setProperty("state",_normal_press);
            repolish(this);
            update();
        }
        emit clicked(this->text(),_curstate);
        return;
    }
    // 调用基类的mousePressEvent以保证正常的事件处理
    QLabel::mousePressEvent(event);
}

// 处理鼠标悬停进入事件
void ClickedLabel::enterEvent(QEnterEvent* event) {
    // 在这里处理鼠标悬停进入的逻辑
    if(_curstate == ClickLbState::Normal){
        qDebug()<<"enter , change to normal hover: "<< _normal_hover;
        setProperty("state",_normal_hover);
        repolish(this);
        update();

    }else{
        qDebug()<<"enter , change to selected hover: "<< _selected_hover;
        setProperty("state",_selected_hover);
        repolish(this);
        update();
    }

    QLabel::enterEvent(event);
}

// 处理鼠标悬停离开事件
void ClickedLabel::leaveEvent(QEvent* event){
    // 在这里处理鼠标悬停离开的逻辑
    if(_curstate == ClickLbState::Normal){
        qDebug()<<"leave , change to normal : "<< _normal;
        setProperty("state",_normal);
        repolish(this);
        update();

    }else{
        qDebug()<<"leave , change to normal hover: "<< _selected;
        setProperty("state",_selected);
        repolish(this);
        update();
    }
    QLabel::leaveEvent(event);
}


void ClickedLabel::SetState(QString normal, QString hover, QString press,
                            QString select, QString select_hover, QString select_press)
{
    _normal = normal;
    _normal_hover = hover;
    _normal_press = press;

    _selected = select;
    _selected_hover = select_hover;
    _selected_press = select_press;

    setProperty("state",normal);
    repolish(this);
}

ClickLbState ClickedLabel::GetCurState(){
    return _curstate;
}

bool ClickedLabel::SetCurState(ClickLbState state)
{
    _curstate = state;
    if(_curstate == ClickLbState::Normal)
    {
        setProperty("state",_normal);
        repolish(this);
    }
    else if(_curstate == ClickLbState::Selected)
    {
        setProperty("state",_selected);
        repolish(this);
    }

    return true;
}

void ClickedLabel::ResetNormalState()
{
    _curstate = ClickLbState::Normal;

    setProperty("state",_normal);
    repolish(this);

}