作者:小 琛
欢迎转载,请标明出处

需求场景

思路描述

  1. 利用Qt提供的类:QListWidget、QListWidgetItem,其中QListWidget为列表类,QListWidgetItem可以理解为每一项的数据类
  2. 原生的QListWidget一定不能满足我们的需求,因此我们构建一个类继承自QListWidget,在子类中重新定义ui风格
  3. 在2中,我们做到了自定义列表,但列表内的具体每一项内容仍然是原生的,因此我们可以自定义一个QWidget,这个widget可以根据我们具体的场景绘制,在插入的时候,使用我们的自定义QWidget作为每一项内容
  4. QListWidgetItem是列表插入必不可少的,并且我们制作列表时,一定会用到一些数据,也可以用它来存储
    QT——使用QListWidget、QListWidgetItem、QWidget实现自定义管理列表-LMLPHP

Qt模块

QListWidget

下面是一些QListWidget的常见用法:

  1. 添加列表项

可以使用addItem()方法添加一个列表项,可以设置列表项的文本和图标:

QListWidget *listWidget = new QListWidget(this);

// 添加纯文本列表项
listWidget->addItem("Item 1");

// 添加带图标的列表项
QListWidgetItem *item2 = new QListWidgetItem(QIcon(":/images/icon.png"), "Item 2");
listWidget->addItem(item2);

// 添加自定义控件
QLabel *label = new QLabel("Item 3");
QListWidgetItem *item3 = new QListWidgetItem();
listWidget->addItem(item3);
listWidget->setItemWidget(item3, label);
  1. 获取当前选中的列表项

可以使用currentItem()方法获取当前选中的列表项:

QListWidgetItem *item = listWidget->currentItem();
if (item) {
    qDebug() << "Selected item: " << item->text();
}
  1. 设置列表项的状态

可以使用setCheckState()方法设置列表项的状态,使得列表项可以被选中或未选中:

QListWidgetItem *item = new QListWidgetItem("Item 1");
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // 开启用户可选中的标志
item->setCheckState(Qt::Unchecked); // 初始为未选中状态

listWidget->addItem(item);
  1. 删除列表项

可以使用takeItem()方法删除一个列表项:

QListWidgetItem *item = listWidget->currentItem();
if (item) {
    listWidget->takeItem(listWidget->row(item)); // 删除当前选中的列表项
}

QListWidgetItem

下面是一些常见的QListWidgetItem的用法:

  1. 设置列表项的文本和图标

可以使用setText()方法和setIcon()方法设置列表项的文本和图标:

QListWidgetItem *item = new QListWidgetItem();
item->setText("Item 1");
item->setIcon(QIcon(":/images/icon.png"));
listWidget->addItem(item);
  1. 获取列表项的文本和图标

可以使用text()方法和icon()方法获取列表项的文本和图标:

QListWidgetItem *item = listWidget->currentItem();
if (item) {
    QString text = item->text();
    QIcon icon = item->icon();
}
  1. 设置列表项的状态

可以使用setCheckState()方法设置列表项的状态,使得列表项可以被选中或未选中:

QListWidgetItem *item = new QListWidgetItem();
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // 开启用户可选中的标志
item->setCheckState(Qt::Unchecked); // 初始为未选中状态
listWidget->addItem(item);
  1. 获取列表项的状态

可以使用checkState()方法获取列表项的状态,返回值为Qt::CheckState枚举类型,表示列表项的选中状态:

QListWidgetItem *item = listWidget->currentItem();
if (item) {
    Qt::CheckState state = item->checkState();
}
  1. 自定义列表项

可以使用QListWidgetItem的方法setItemWidget()方法设置自定义控件作为列表项:

QLabel *label = new QLabel("Custom Item");
QListWidgetItem *item = new QListWidgetItem();
listWidget->addItem(item);
listWidget->setItemWidget(item, label);

自定义QWidget配合QListWidget

例子:实现一个json文件管理窗口

注意的点:

  1. 下面的例子,并不能直接运行,因为包含了很多我自定义的控件,仅限于了解整个结构
  2. 整个设计:JsonItemWidget是我的自定义widget,里面包含了我定义每一项的具体内容,其中的控件被触发时绑定信号,JsonItemWidget再绑定widget中的信号,实现事件的抛;JsonListWidget是列表项,修改了很多属性来实现我想要的ui效果;JsonListWidgetItem是自定义的Item,其中预留了operate >,该接口可以完成自定义排序功能;插入的时候,调用setItemWidget接口完成

JsonListWidget.h

#pragma once

#include <QListWidget>
#include "JsonItemDataDef.h"

class JsonListItem;
class QListWidgetItem;
class JsonListWidget : public QListWidget {
    Q_OBJECT
public:
    explicit JsonListWidget(QWidget* parent);
    ~JsonListWidget();
    
    void addJson(const JsonItemData& JsonData);
    void addJsonWithFlicker(const JsonItemData& JsonData);
    void updateJsonName(const QString& oldName, const QString& newName);
    void updateJsonInfo(const QString& name, const JsonItemData& data);
    void deleteJsonWithName(const QString& name);
    bool haveJsonByName(const QString& name);
    
private:
    QListWidgetItem* findWidgetItemByName(const QString& name);

signals:
    void sigOpenJsonFileFolder();
    void sigJsonRun(const QString& name);
    void sigItemNameChanged(const QString& oldName, const QString& newName);
    void sigItemDelete(const QString& name);
    void sigItemSetting(const QString& name);

protected:
    virtual bool eventFilter(QObject* object, QEvent* event) override;

};

JsonListWidget.cpp

#include "JsonListWidget.h"
#include "JsonListItem.h"
#include "NemuColor.h"
#include "AppUtil.h"
#include <QPainter>
#include <QDateTime>
#include <QEvent>
#include <QTimer>

enum ItemRole {
    kRoleIndex = Qt::UserRole + 1,
    kRoleName,
    kRoleDate,
    kRoleTime,
    kRoleScreen,
};

JsonListWidget::JsonListWidget(QWidget *parent) : QListWidget(parent) {
    setFrameShape(QFrame::NoFrame);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    viewport()->setContentsMargins(0, 0, 0, 0);

    setStyleSheet(
        QString("QListWidget{background-color:%1;border: 1px solid %2; border-bottom-left-radius: %3px; "
                "border-bottom-right-radius: %4px; outline:0px;}"
                "QListWidget::item:hover{background-color:%5;}"
                "QListWidget::item:selected{border:none;}"
                "QScrollBar:vertical{width:4px; background:transparent; padding:0px; border:0px;}"
                "QScrollBar::handle:vertical:hover,QScrollBar::handle:vertical:pressed{background:%7;}"
                "QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical{background:transparent;border:0px;}"
                "QScrollBar::add-line:vertical,QScrollBar::sub-line:vertical{background:transparent;border:0px;}")
            .arg(NemuUiLib::NemuColor::getGrey8().name(QColor::HexArgb))
            .arg(AppUtil::isWin11() ? "#44464b" : "#192027")
            .arg(AppUtil::isWin11() ? 8 : 0)
            .arg(AppUtil::isWin11() ? 8 : 0)
            .arg(NemuUiLib::NemuColor::getFill1().name(QColor::HexArgb))
            .arg(NemuUiLib::NemuColor::getFill2().name(QColor::HexArgb))
            .arg(NemuUiLib::NemuColor::getGrey3().name(QColor::HexArgb)));

    if (auto widget = findChild<QWidget *>("qt_scrollarea_vcontainer")) {
        widget->installEventFilter(this);
    }
    setViewMode(ListMode);
}

JsonListWidget::~JsonListWidget() {}

void JsonListWidget::addJson(const JsonItemData &JsonData) {
    if (haveJsonByName(JsonData.name_)) {
        return;
    }
    JsonListItem *item = new JsonListItem(this);
    connect(item, &JsonListItem::sigItemOpenJsonFileFolder, this, &JsonListWidget::sigOpenJsonFileFolder);
    connect(item, &JsonListItem::sigItemJsonRun, this, &JsonListWidget::sigJsonRun);
    connect(item, &JsonListItem::sigNameChanged, this, &JsonListWidget::sigItemNameChanged);
    connect(item, &JsonListItem::sigDeleteButtonClicked, this, &JsonListWidget::sigItemDelete);
    connect(item, &JsonListItem::sigSettingButtonClicked, this, &JsonListWidget::sigItemSetting);

    item->setJsonName(JsonData.name_);
    item->setJsonTime(JsonData.time_);
    item->setJsonDate(JsonData.date_);
    item->setJsonScreen(JsonData.screen_);

    auto listItem = new QListWidgetItem(this);
    listItem->setData(kRoleIndex, JsonData.index_);
    listItem->setData(kRoleName, JsonData.name_);
    listItem->setData(kRoleScreen, JsonData.screen_);
    listItem->setData(kRoleTime, JsonData.time_);
    listItem->setData(kRoleDate, JsonData.date_);
    listItem->setSizeHint(item->size());
    insertItem(0, listItem);
    setItemWidget(listItem, item);
}

void JsonListWidget::addJsonWithFlicker(const JsonItemData &JsonData) {
    if (haveJsonByName(JsonData.name_)) {
        return;
    }
    JsonListItem *item = new JsonListItem(this);
    connect(item, &JsonListItem::sigItemOpenJsonFileFolder, this, &JsonListWidget::sigOpenJsonFileFolder);
    connect(item, &JsonListItem::sigItemJsonRun, this, &JsonListWidget::sigJsonRun);
    connect(item, &JsonListItem::sigNameChanged, this, &JsonListWidget::sigItemNameChanged);
    connect(item, &JsonListItem::sigDeleteButtonClicked, this, &JsonListWidget::sigItemDelete);
    connect(item, &JsonListItem::sigSettingButtonClicked, this, &JsonListWidget::sigItemSetting);

    item->setJsonName(JsonData.name_);
    item->setJsonTime(JsonData.time_);
    item->setJsonDate(JsonData.date_);
    item->setJsonScreen(JsonData.screen_);

    auto listItem = new QListWidgetItem();
    listItem->setData(kRoleIndex, JsonData.index_);
    listItem->setData(kRoleName, JsonData.name_);
    listItem->setData(kRoleScreen, JsonData.screen_);
    listItem->setData(kRoleTime, JsonData.time_);
    listItem->setData(kRoleDate, JsonData.date_);
    listItem->setSizeHint(item->size());
    insertItem(0, listItem);
    setItemWidget(listItem, item);
    update();
    listItem->setBackgroundColor(NemuUiLib::NemuColor::getFill1());
    QTimer::singleShot(1000, [listItem]() { listItem->setBackgroundColor(NemuUiLib::NemuColor::getGrey8()); });
}

bool JsonListWidget::haveJsonByName(const QString& name) {
    bool ret = false;
    for (int row = 0; row < count(); ++row) {
        auto widgetItem = item(row);
        if (!widgetItem) {
            continue;
        }

        if (name != widgetItem->data(kRoleName).toString()) {
            continue;
        }

        ret = true;
        break;
    }
    return ret;
}


void JsonListWidget::deleteJsonWithName(const QString& name) {
    int listCount = count();
    for (int row = 0; row < listCount; ++row) {
        auto widgetItem = item(row);
        if (!widgetItem) {
            continue;
        }

        if (name != widgetItem->data(kRoleName).toString()) {
            continue;
        }

        takeItem(row);
        break;
    }
}

void JsonListWidget::updateJsonName(const QString &oldName, const QString &newName) {
    if (auto widgeItem = findWidgetItemByName(oldName)) {
        widgeItem->setData(kRoleName, newName);

        if (auto widget = qobject_cast<JsonListItem *>(itemWidget(widgeItem))) {
            widget->setJsonName(newName);
        }
    }
}

void JsonListWidget::updateJsonInfo(const QString &name, const JsonItemData &data) {
    if (auto widgeItem = findWidgetItemByName(name)) {
        widgeItem->setData(kRoleIndex, data.index_);
        widgeItem->setData(kRoleName, name);
        widgeItem->setData(kRoleDate, data.date_);
        widgeItem->setData(kRoleTime, data.time_);
        widgeItem->setData(kRoleTime, data.screen_);

        if (auto widget = qobject_cast<JsonListItem *>(itemWidget(widgeItem))) {
            widget->setIndex(data.index_);
            widget->setJsonName(name);
            widget->setJsonDate(data.date_);
            widget->setJsonTime(data.time_);
            widget->setJsonScreen(data.screen_);
        }
    }
}

QListWidgetItem *JsonListWidget::findWidgetItemByName(const QString &name) {
    int listCount = count();
    for (int row = 0; row < listCount; ++row) {
        auto widgetItem = item(row);
        if (!widgetItem) {
            continue;
        }

        if (name != widgetItem->data(kRoleName).toString()) {
            continue;
        }

        return widgetItem;
    }
    return nullptr;
}

bool JsonListWidget::eventFilter(QObject *object, QEvent *event) {
    // scrollbar设置位置的时机比较多,无法全面覆盖,只能在产生move事件之后再进行调整
    auto widget = findChild<QWidget *>("qt_scrollarea_vcontainer");
    if (widget && widget == object && event->type() == QEvent::Move) {
        QPoint pos = widget->pos();
        if (pos.x() && (pos.x() == rect().width() - widget->width())) {
            QRect rc1 = widget->geometry();
            pos -= QPoint(4, 0);
            widget->move(pos);
        }
    }

    return QListWidget::eventFilter(object, event);
}

JsonItemWidget.h

#pragma once

#include <QWidget>
#include "JsonItemDataDef.h"

class QStackedWidget;
class QStackedLayout;
class QLabel;
class QLineEdit;
class QVBoxLayout;
class QPushButton;
class RecorderPushButton18;
class RecorderTextLabel;
class JsonListItem : public QWidget {
    Q_OBJECT
public:
    explicit JsonListItem(QWidget* parent);
    ~JsonListItem();

    void setIndex(const QString& index);

    void setScriptName(const QString& name);

    void setScriptDate(const QString& date);
    void setScriptTime(const QString& time);

    void setScriptScreen(const QString& screen);

    void setModifyTime(quint64 time);

signals:
    void sigCheckStateChanged(const QString& index, Qt::CheckState state);
    void sigItemOpenScriptFileFolder();
    void sigItemScriptRun(const QString& name);
    void sigNameChanged(const QString& oldName, const QString& newName);
    void sigDeleteButtonClicked(const QString& name);
    void sigSettingButtonClicked(const QString& name);

protected:
    virtual void enterEvent(QEvent* event) override;
    virtual void leaveEvent(QEvent* event) override;
    virtual void focusOutEvent(QFocusEvent* event) override;
    bool eventFilter(QObject* object, QEvent* event) override;

private:
    QWidget* initLeftInfoWidget();
    QWidget* initRightButtonWidget();
    void ensureRenameEdit();
    void elidedName(NemuUiLib::NemuRichText1* label, const QString& name);
    void showNameLabel();
    void showRenameEdit();

private:
    QString index_;
    QString name_;
    QString time_;
    QString screen_;
    QLable* nameLabel_;
    QLable* dateLabel_;
    QLabel* timeLabel_;
    QLbale* screenLabel_;
    QLineEdit* renameEdit_;
    QPushButton* openFolderButton_;
    QPushButton* settingButton_;
    QPushButton* deleteButton_;
    QVBoxLayout* centerLayout_;
    bool mouseEntered_;

    QPushButton* operatorButton_;

};

JsonItemWidget.cpp

#include "JsonListItem.h"
#include "../../common/RecorderPushButton.h"
#include "../../common/RecorderTextLabel.h"
#include "nemu-ui-lib/control/NemuImage.h"
#include "nemu-ui-lib/control/NemuRichText.h"
#include "nemu-ui-lib/control/NemuCheckBox.h"
#include "nemu-ui-lib/control/NemuLine.h"
#include "nemu-ui-lib/control/NemuLineEdit.h"
#include "nemu-ui-lib/font/NemuFont.h"
#include "nemu-ui-lib/color/NemuColor.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QStackedWidget>
#include <QStackedLayout>
#include <QEvent>
#include <QMouseEvent>
#include <QValidator>
#include <QRegularExpression>

JsonListItem::JsonListItem(QWidget* parent)
    : QWidget(parent),
      nameLabel_(nullptr),
      renameEdit_(nullptr),
      openFolderButton_(nullptr),
      settingButton_(nullptr),
      deleteButton_(nullptr),
      operatorButton_(nullptr),
      mouseEntered_(false) {
    setFixedSize(800, 80);
    centerLayout_ = new QVBoxLayout(this);
    centerLayout_->setAlignment(Qt::AlignCenter);
    centerLayout_->setSpacing(0);
    centerLayout_->setContentsMargins(0, 0, 0, 0);

    auto mainLayout = new QHBoxLayout();
    mainLayout->setContentsMargins(40, 18, 40, 16);
    mainLayout->setAlignment(Qt::AlignCenter);
    mainLayout->setSpacing(0);

    installEventFilter(this);
    mainLayout->addWidget(initLeftInfoWidget());
    mainLayout->addWidget(initRightButtonWidget(), 0, Qt::AlignVCenter);

    const auto line = new Line2({720, 1}, {40, 0, 40, 0}, this); 

    //centerLayout_->addStretch();
    centerLayout_->addLayout(mainLayout);
    //centerLayout_->addStretch();

    centerLayout_->addWidget(line, 0, Qt::AlignBottom);
    adjustSize();
}

JsonListItem::~JsonListItem() {
    if (renameEdit_) {
        renameEdit_->removeEventFilter(this);
    }
    removeEventFilter(this);
}

QWidget* JsonListItem::initLeftInfoWidget() {
    auto widget = new QWidget(this);

    auto centerLayout = new QVBoxLayout(widget);
    centerLayout->setAlignment(Qt::AlignTop);
    centerLayout->setSpacing(0);
    centerLayout->setContentsMargins(0, 0, 0, 0);

    nameLabel_ = new NemuUiLib::NemuRichText1(300, getFont14(), QString(), widget);
    //nameLabel_->installEventFilter(this);
    nameLabel_->setFixedHeight(24);
    nameLabel_->document()->setDocumentMargin(0);
    nameLabel_->setStyleSheet(
        QString("QTextBrowser{color: %1; border: none; background-color: transparent}").arg(getWhite1().name(QColor::HexArgb)));
    centerLayout->addWidget(nameLabel_, 0, Qt::AlignTop | Qt::AlignLeft);
    QSpacerItem* spacer = new QSpacerItem(0, 4, QSizePolicy::Expanding, QSizePolicy::Fixed);
    centerLayout->addItem(spacer);

    auto dateAndScrenLayout = new QHBoxLayout();
    dateAndScrenLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    dateAndScrenLayout->setSpacing(0);
    dateAndScrenLayout->setContentsMargins(0, 0, 0, 0);

    auto dataImage = new Image1(QSize(16, 16), ":/resources/images/ic_calendar.svg", widget);
    dateAndScrenLayout->addWidget(dataImage);
    dateAndScrenLayout->addSpacing(2);

    dateLabel_ = new RichText2(QString(), getFont12(), widget);

    dateAndScrenLayout->addWidget(dateLabel_);

    dateAndScrenLayout->addSpacing(4);

    timeLabel_ = new Text2(QString(), getFont12(), widget);

    dateAndScrenLayout->addWidget(timeLabel_);

    dateAndScrenLayout->addSpacing(16);

    auto screenImage = new NemuImage1(QSize(16, 16), ":/resources/images/ic_resolution.svg", widget);
    dateAndScrenLayout->addWidget(screenImage);

    dateAndScrenLayout->addSpacing(2);

    screenLabel_ = new RichText2(QString(), getFont12(), widget);
    screenLabel_->setFixedWidth(55);
    dateAndScrenLayout->addWidget(screenLabel_);

    centerLayout->addLayout(dateAndScrenLayout);
    return widget;
}

QWidget* JsonListItem::initRightButtonWidget() {
    auto operatorWidget = new QWidget(this);

    auto operatorLayout = new QHBoxLayout(operatorWidget);
    operatorLayout->setContentsMargins(6, 0, 0, 0);
    operatorLayout->setSpacing(20);

    openFolderButton_ = new QPushButton(this);
    openFolderButton_->setFixedSize(16, 16);
    openFolderButton_->setToolTip(tr("File location"));
    openFolderButton_->setStyleSheet(
        QString("QPushButton{border-image: url(:/resources/images/ic_folder location_normal.svg);}\
                     QPushButton:hover{border-image: url(:/resources/images/ic_folder location_hover.svg);}\
                     QPushButton:pressed{border-image: url(:/resources/images/ic_folder location_press.svg);}\
                     QPushButton:disabled{border-image: url(:/resources/images/ic_folder location_disable.svg);}"
                "QToolTip {"
                "color: %1;"
                "background-color: %2;"
                "qproperty-shadow: none;"
                "border: 0px solid #2A82DA;"
                "}")
            .arg(getWhite1().name(QColor::HexArgb))
            .arg(getGrey10().name(QColor::HexArgb)));
    connect(openFolderButton_, &QPushButton::clicked, this, [this]() { 
        emit sigItemOpenJsonFileFolder(); 
        });
    operatorLayout->addWidget(openFolderButton_);

    settingButton_ = new QPushButton(this);
    settingButton_->setFixedSize(16, 16);
    settingButton_->setToolTip(tr("execution setting"));
    settingButton_->setStyleSheet(QString("QPushButton{border-image: url(:/resources/images/ic_setting_normal.svg);}\
                     QPushButton:hover{border-image: url(:/resources/images/ic_setting_hover.svg);}\
                     QPushButton:pressed{border-image: url(:/resources/images/ic_setting_pressed.svg);}\
                     QPushButton:disabled{border-image: url(:/resources/images/ic_setting_disabled.svg);}"
                                          "QToolTip {"
                                          "qproperty-shadow: none;"
                                          "color: %1;"
                                          "background-color: %2;"
                                          "border: 0px solid #2A82DA;"
                                          "}")
                                      .arg(getWhite1().name(QColor::HexArgb))
                                      .arg(getGrey10().name(QColor::HexArgb)));
    connect(settingButton_, &QPushButton::clicked, this, [this]() { emit sigSettingButtonClicked(name_); });
    operatorLayout->addWidget(settingButton_);

    deleteButton_ = new QPushButton(this);
    deleteButton_->setFixedSize(16, 16);
    deleteButton_->setToolTip(tr("delete"));
    deleteButton_->setStyleSheet(QString("QPushButton{border-image: url(:/resources/images/ic_delete_normal.svg);}\
                     QPushButton:hover{border-image: url(:/resources/images/ic_delete_hover.svg);}\
                     QPushButton:pressed{border-image: url(:/resources/images/ic_delete_pressed.svg);}\
                     QPushButton:disabled{border-image: url(:/resources/images/ic_delete_disabled.svg);}"
                                         "QToolTip {"
                                         "color: %1;"
                                         "background-color: %2;"
                                         "qproperty-shadow: none;"
                                         "border: 0px solid #2A82DA;"
                                         "}")
                                     .arg(getWhite1().name(QColor::HexArgb))
                                     .arg(getGrey10().name(QColor::HexArgb)));
    connect(deleteButton_, &QPushButton::clicked, this, [this]() { emit sigDeleteButtonClicked(name_); });
    operatorLayout->addWidget(deleteButton_);

    operatorButton_ = new RecorderPushButton18(this);
    operatorButton_->setFixedSize(48, 24);
    operatorButton_->setIcon(QIcon(":/resources/images//ic_start_up.svg"));
    operatorButton_->setStateColor(NemuUiLib::NemuColor::getBrand1(), getBrand2(),
                                   NemuUiLib::NemuColor::getBrand1());
    operatorButton_->setToolTip(tr("execute"));
    operatorButton_->setStyleSheet(QString("QToolTip {"
                                           "color: %1;"
                                           "background-color: %2;"
                                           "border: 0px solid #2A82DA;"
                                           "}")
                                       .arg(getWhite1().name(QColor::HexArgb))
                                       .arg(getGrey10().name(QColor::HexArgb)));
    operatorButton_->setVisible(true);
    connect(operatorButton_, &QPushButton::clicked, this, [this]() { sigItemJsonRun(name_);
    });
    operatorLayout->addWidget(operatorButton_);


    return operatorWidget;
}

void JsonListItem::setIndex(const QString& index) { index_ = index; }

void JsonListItem::setJsonName(const QString& name) {
    name_ = name;
    if (renameEdit_ && renameEdit_->isVisible()) {
        return;
    }

    elidedName(nameLabel_, name);
}

void JsonListItem::setJsonDate(const QString& date) { 
    time_ = date; 
    const auto dateHtmlText = QString("<p align=\"left\" style=\"color: %1;\">%2</p>")
                                  .arg(getGrey2().name(QColor::HexArgb))
                                  .arg(date);
    dateLabel_->updateText(dateHtmlText);
    dateLabel_->setFixedWidth(dateLabel_->adaptiveWidth(date, getFont12()));
}

void JsonListItem::setJsonTime(const QString& time) {
    time_ = time;
    const auto dateHtmlText = QString("<p align=\"left\" style=\"color: %1;\">%2</p>")
                                  .arg(NemuUiLib::NemuColor::getGrey2().name(QColor::HexArgb))
                                  .arg(time);
    timeLabel_->updateText(dateHtmlText);
    timeLabel_->setFixedWidth(timeLabel_->adaptiveWidth(time, getFont12()));
}

void JsonListItem::setJsonScreen(const QString& screen) {
    screen_ = screen; 
    const auto screenHtmlText = QString("<p align=\"left\" style=\"color: %1;\">%2</p>")
                                    .arg(getGrey2().name(QColor::HexArgb))
                                    .arg(screen_);
    screenLabel_->updateText(screenHtmlText);
    screenLabel_->setFixedWidth(timeLabel_->adaptiveWidth(screen, getFont12()));
}

void JsonListItem::setModifyTime(quint64 time) {}

void JsonListItem::enterEvent(QEvent* event) {

    QWidget::enterEvent(event);
}

void JsonListItem::leaveEvent(QEvent* event) {
    if (renameEdit_ && renameEdit_->isVisible()) {
        if (!renameEdit_->hasFocus()) {
            renameEdit_->setVisible(false);
            elidedName(nameLabel_, name_);
        }
    }
    QWidget::leaveEvent(event);
}

void JsonListItem::focusOutEvent(QFocusEvent* event) {
    if (renameEdit_) {
        renameEdit_->setVisible(false);
    }
    QWidget::focusOutEvent(event);
}

void JsonListItem::showNameLabel() {
    renameEdit_->setVisible(false);
    elidedName(nameLabel_, name_);
}

void JsonListItem::showRenameEdit() {
    nameLabel_->clear();
    auto pos = nameLabel_->geometry();
    ensureRenameEdit();
    QSize sz = renameEdit_->size();
    renameEdit_->setGeometry(pos.x() + 40, pos.y() + 18, sz.width(), sz.height());
    renameEdit_->setText(name_);
    renameEdit_->setVisible(true);
}

bool JsonListItem::eventFilter(QObject* object, QEvent* event) {
    if (object == renameEdit_) {
       if (event->type() == QEvent::FocusOut && !mouseEntered_) {
            showNameLabel();
        }
    } else if (object == this) {
        if (event->type() == QEvent::Enter) {
            mouseEntered_ = true;
            if (renameEdit_ && renameEdit_->isVisible()) {
                if (!renameEdit_->hasFocus()) {
                    showNameLabel();
                }
            } else {
                showRenameEdit();
            }
        } else if (event->type() == QEvent::Leave) {
            mouseEntered_ = false;
            if (renameEdit_ && !renameEdit_->hasFocus()) {
                showNameLabel();
            }
        }
    } else if (object != renameEdit_) {
        if (renameEdit_ && !renameEdit_->hasFocus()) {
            showNameLabel();
        }
    }

    return QWidget::eventFilter(object, event);
}

void JsonListItem::ensureRenameEdit() {
    if (renameEdit_) {
        return;
    }

    renameEdit_ = new QLineEdit(this);
    renameEdit_->setContextMenuPolicy(Qt::NoContextMenu);
    renameEdit_->setMaxLength(40);
    renameEdit_->setValidator(
        new QRegularExpressionValidator(QRegularExpression("^((?!\\\\|\\/|:|\\*|\\?|\"|<|>|\\|).)*$"), this));
    renameEdit_->setFixedSize(300, 24);
    renameEdit_->setFont(NemuUiLib::NemuFont::getFont12());
    renameEdit_->installEventFilter(this);
    renameEdit_->setStyleSheet(
        QString("QLineEdit{border-width:1px; border-style:solid; border-color:%1; padding-left:8px; padding-right:8px;"
                "background-color: %1; color: %2; border-radius:2px;}"
                "QLineEdit:hover{border-width:1px; border-style:solid; border-color:%3; background-color:%3;}"
                "QLineEdit:focus{border-width:1px; border-style:solid; border-color:%4; background-color:%5; }")
            .arg(getFill1().name(QColor::HexArgb))
            .arg(getWhite1().name(QColor::HexArgb))
            .arg(getFill2().name(QColor::HexArgb))
            .arg(getBrand1().name(QColor::HexArgb))
            .arg(getFill5().name(QColor::HexArgb)));
    connect(renameEdit_, &QLineEdit::editingFinished, this, [this]() {
        QString newName = renameEdit_->text();
        if (!newName.isEmpty() && newName != name_) {
            emit sigNameChanged(name_, newName);
        }
    });
    connect(renameEdit_, &QLineEdit::returnPressed, this, [this]() {
        QString newName = renameEdit_->text();
        if (!newName.isEmpty() && newName != name_) {
            emit sigNameChanged(name_, newName);
        }
        renameEdit_->setVisible(false);
        elidedName(nameLabel_, name_);
    });
}

void JsonListItem::elidedName(QLabel* label, const QString& name) {
    QFontMetrics fontWidth(label->font());
    int contentWidth = label->width() - label->document()->documentMargin() * 2;
    label->setText(fontWidth.elidedText(name, Qt::ElideRight, contentWidth));
    label->setFixedHeight(24);
}

JsonListWidgetItem.h

#pragma once

#include <QListWidgetItem>

enum ItemRole {
    kRoleId = Qt::UserRole + 1,
    kRoleIndex,
    kRoleNumber,
    kRoleMainFlag,
    kRoleStatus,
    kRoleErrorCode,
    kRoleTime,
    kRoleName,
};

class JsonWidgetItem : public QListWidgetItem {
public:
    virtual bool operator<(const QListWidgetItem &other) const override;

private:
    bool checked_;
};

JsonListWidgetItem.cpp

#include "JsonWidgetItem.h"
#include "JsonWidget.h"
#include <QListWidget>
#include <shlwapi.h>
#include "PlayerItemDataDef.h"

JsonWidgetItem::JsonWidgetItem(QListWidget *listview) : QListWidgetItem(listview), checked_(false) {}

JsonWidgetItem::~JsonWidgetItem() {}

bool JsonWidgetItem::operator<(const QListWidgetItem &other) const {
    if (auto listView = qobject_cast<JsonWidget *>(listWidget())) {
        if (listView->sortFactor() == kFactorNumber) {
            return data(kRoleNumber).toInt() < other.data(kRoleNumber).toInt();
        } else if (listView->sortFactor() == kFactorName) {
            return StrCmpLogicalW((PCWSTR)data(kRoleName).toString().utf16(),
                                  (PCWSTR)other.data(kRoleName).toString().utf16()) == -1;
        } else if (listView->sortFactor() == kFactorTime) {
            if (data(kRoleMainFlag).toBool()) {
                return true;
            } else if (other.data(kRoleMainFlag).toBool()) {
                return false;
            } else {
                return data(kRoleTime).toULongLong() < other.data(kRoleTime).toULongLong();
            }
        }
    }

    return data(kRoleNumber).toString().compare(other.data(kRoleNumber).toString()) < 0;
}

07-03 16:07