Featured image of post UOS20文管dde-file-manager添加徽标实践

UOS20文管dde-file-manager添加徽标实践

本文档主要介绍了两种方法实现国产操作系统UOS20的文件资源管理器dde-file-manager添加徽标(角标)。由于dfm的版本众多且不同版本之间的接口差异较大,因此本文所谈及的接口于dfm的版本密切相关,更多实时接口信息还请关注dfm官方文档和源码。

Intro

值得说明的是,在本文中,角标、徽标、emblem、emblemicon表示的意思是一致的。与本文相关的关键字有:

emblem 徽标 角标 metadata::emblems overlayicons 图标覆盖 SVN角标

文件资源管理器 文管 file manager

caja nautilus nemo peony dde-file-manager explorer.exe

我想,但凡你用过SVN就应该清楚上述的关键字在描述些什么,是的,我最近在研究在各个系统下实现SVN图标效果的方案。虽是在不同的系统实现徽标效果,但一般而言实现方案大体上是类似的:

  • 找到系统的文件资源管理器,比如UOS20承袭自deepin,其文管为dde-file-manager

  • 找到文管提供的添加徽标接口,一般而言都是以插件的形式注册加载

Interface

本文在此叙述了两个接口以实现徽标的添加功能(因不同的版本接口也不一样)。

DFMGenericPlugin

dde-file-manager ~ (此接口虽说会被删减,但在持续更新的版本中均发现仍然是保留的)

depends: libdde-file-manager-dev

此接口为我在调研期间,浏览开发者论坛查阅得于此链接link:不来体验体验深度文件管理器的坚果云插件么?-论坛-深度科技 ,遂找到了项目linuxdeepin/dde-file-manager-integration并结合dde-file-manager源码以理解此接口。

在dde-file-manager源码DFileViewHelperPrivate::init()中可见初始化了插件DFMGenericFactory::createAll(QStringLiteral("fileinfo/additionalIcon")).

在源码DFileViewHelperPrivate::getAdditionalIconByPlugins中可以看到

invokeMethod(object, "fileAdditionalIcon"...槽函数。结合具体实现章节的代码,此接口并不难理解。

DFMExtEmblemIconPlugin

dde-file-manager >= 5.5.10 (10 Dec 2021)

depends: libdfm-extension-dev

👀 关于此接口何时开始被支持,可以结合项目的tag和commit以及系统的发布日志,发现此接口从5.5.10开始支持。

https://github.com/linuxdeepin/dde-file-manager/commits/5.5.10/src/dde-file-manager-extension

此部分详细信息可参与github项目

DFMExtEmblemIconPlugin类是第三方开发者应该继承并实现的接口类,并且在元接口dfm_extesion_emblem中返回派生的类对象,接口定义在头文件dfmextemblemiconplugin.h` 中。需要特别注意的是,需要提前将角标资源图片文件安装到系统主题中,规则遵循 freedesktop 规范。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
extern "C" void dfm_extension_initialize();
extern "C" void dfm_extension_shutdown();
extern "C" DFMExtEmblemIconPlugin *dfm_extension_emblem();

/////////////////////////////////////////////////////////////
class DFMExtEmblemIconPluginPrivate;
class DFMExtEmblemIconPlugin
{
    DFM_DISABLE_COPY(DFMExtEmblemIconPlugin)
public:
    using IconsType = std::vector<std::string>;
    using EmblemIcons = std::function<IconsType(const std::string &)>;
    using LocationEmblemIcons = std::function<DFMExtEmblem(const std::string &, int)>;

public:
    DFMExtEmblemIconPlugin();
    ~DFMExtEmblemIconPlugin();

    // Note: If the corner mark set by emblemIcons conflicts with the corner mark position set by locationEmblemIcons,
    // the conflict position will only display the corner mark set by locationEmblemIcons
    DFM_FAKE_VIRTUAL IconsType emblemIcons(const std::string &fileUrl) const;
    void registerEmblemIcons(const EmblemIcons &func);

    DFM_FAKE_VIRTUAL DFMExtEmblem locationEmblemIcons(const std::string &fileUrl, int systemIconCount) const;
    void registerLocationEmblemIcons(const LocationEmblemIcons &func);

private:
    DFMExtEmblemIconPluginPrivate *d { nullptr };
};
dfm_extension_initialize插件初始化入口函数,插件被加载后将首先调用它
dfm_extension_shutdown插件释放的函数,当插件被卸载前将会调用它
dfm_extesion_menu返回右键菜单扩展对象的函数,该对象的类由第三方开发者实现
dfm_extesion_emblem返回角标对象的函数,该对象的类由第三方开发者实现
名称简介
emblemIcons文管主动调用,传入文件路径,返回被安装的角标图片名称列表
registerEmblemIconsemblemIcons 接口的注册函数,第三方开发者主动注册
locationEmblemIcons文管主动调用,传入文件路径和这个文件的现有系统角标数量,如果这个值达到了4,那么就位置绘制扩展角标了
registerLocationEmblemIconslocationEmblemIcons 接口的注册函数,第三方开发者主动注册

Dev & Impl

OS: UOS20-1050-PRO

dde-file-manage:5.6.12

github: GitHub - pinkkmin/dfm-examples

Project

DFMGenericPlugin

⚠️ 我在浏览论坛时,发现有人提到此接口在某些版本的dfm可能引起崩溃(见上述链接内容),但我在实践的UOS20版本中并未发现,因此具体在不同版本的dfm实践时还需多多测试。

1
2
3
4
5
6
7
dfm-emblem-example-plugin.pro
dfmgenericpluginobject.cpp
dfmgenericpluginobject.h
emblem-default.png
generic.json
main.cpp
res.qrc

DFMExtEmblemIconPlugin

👊 我实践此接口时(2022-09-02)并没有找到可参考的文档或示例,所以我邮件请教了dfm的开发者。他提供了一份有用的文档(见docs/dfm-extension-doc(answerofdeveloper).md)可以参考。

⚠️应当小心此文档的时效性,还是以官方的最新文档以及github仓库信息优先。

1
2
3
4
5
emblems
  + basketball.png
ext-emblem-example.pro
ext-emblem-impl.cpp
res.qrc

Coding

DFMGenericPlugin

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// dfmgenericpluginobject.h
#include <QObject>
#include <QIcon>
#include <QQueue>
#include <dabstractfileinfo.h>
#include "dde-file-manager-extension/emblemicon/dfmextemblemiconplugin.h"

QT_BEGIN_NAMESPACE
class QTcpSocket;
class QUdpSocket;
QT_END_NAMESPACE

class DFMGenericPluginObject : public QObject
{
 Q_OBJECT

public:
 explicit DFMGenericPluginObject(QObject *parent = 0);

public slots:
 QList<QIcon> fileAdditionalIcon(const DAbstractFileInfoPointer &fileInfo);
};

#endif // DFMGENERICPLUGINOBJECT_H
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// dfmgenericpluginobject.h
#include "dfmgenericpluginobject.h"
#include <QWidget>

DFMGenericPluginObject::DFMGenericPluginObject(QObject *parent)
    : QObject(parent)
{
}

QList<QIcon> DFMGenericPluginObject::fileAdditionalIcon(const DAbstractFileInfoPointer &fileInfo)
{
    QList<QIcon> list;

     list << QIcon("://emblem-default.png");
    return list;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// dfmgenericpluginobject.cpp

#include <dfmgenericplugin.h>
#include "dfmgenericpluginobject.h"

DFM_USE_NAMESPACE

class PluginMain : public DFMGenericPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID DFMGenericFactoryInterface_iid FILE "generic.json")
public:
    QObject *create(const QString &key)
    {
        if (key != "fileinfo/additionalIcon")
            return Q_NULLPTR;

        return new DFMGenericPluginObject();
    }
};

#include "main.moc"

DFMExtEmblemIconPlugin

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <QMessageLogger>
#include <QDebug>
#include <QString>
#include <iostream>

#include "dfm-extension.h"
#include "emblemicon/dfmextemblem.h"
#include "emblemicon/dfmextemblemiconplugin.h"
#include "emblemicon/dfmextemblemiconlayout.h"

USING_DFMEXT_NAMESPACE

// 全局徽标插件单例
static DFMExtEmblemIconPlugin *kEmblemPlugin = nullptr;

// bind functions
DFMExtEmblemIconPlugin::EmblemIcons emblemIconFunc;
DFMExtEmblemIconPlugin::LocationEmblemIcons localEmblemFunc;


std::vector<std::string> demo_emblemIcons(const std::string &fileUrl)
{
   std::vector<std::string> icons;

    std::cout<<"########################## emblem-example: fileUrl: "<<fileUrl<<std::endl;

    if(fileUrl.empty()) return icons;

   // demo 使用qrc资源
   icons.push_back("://emblems/basketball.png");

   // demo 系统主题默认的emblem
   // /usr/lib/icons/xxx/48x48/emblems/emblem-default.png
   icons.push_back("emblem-default");

   return icons;
}


DFMExtEmblem demo_locationEmblemIcons(const std::string &fileUrl, int systemIconCount)
{
    std::string emblempath="://emblems/basketball.png";
    DFMExtEmblemIconLayout dsmemblem(DFMExtEmblemIconLayout::LocationType::BottomRight,emblempath);

    std::vector<DFMExtEmblemIconLayout> iconPaths;
    iconPaths.push_back(dsmemblem);

    DFMExtEmblem emblems;
    emblems.setEmblem(iconPaths);

    return emblems;
}


extern "C" void dfm_extension_initiliaze()
{
    qInfo()<<"########################## emblem-example: dfm_extension_initiliaze.....";

    if (!kEmblemPlugin)
    {
        kEmblemPlugin = new DFMExtEmblemIconPlugin;

// 方式一:据悉,此接口为旧接口。但实际效果比localEmblem要好很多
        emblemIconFunc = demo_emblemIcons;
        kEmblemPlugin->registerEmblemIcons(emblemIconFunc);

// 方式二:此接口指定了位置,当位置冲突时,徽标可能会不显示
//        localEmblemFunc = demo_locationEmblemIcons;
//        kEmblemPlugin->registerLocationEmblemIcons(localEmblemFunc);
    }
}

extern "C"  void dfm_extension_shutdown()
{
    qInfo()<<"########################## emblem-example: dfm_extension_shutdown.....";

    if (kEmblemPlugin)
    {
        delete kEmblemPlugin;
        kEmblemPlugin = nullptr;
    }
}


extern "C" DFMExtEmblemIconPlugin *dfm_extension_emblem()
{
    qInfo()<<"################# dfm_extension_emblem.......";
    return kEmblemPlugin;
}

如上,这只是一份示例代码。建议使用C++继承对应的基类实现该功能,见官方文档。

Demo

将编译的动态库放置于以下目录:

对于DFMGenericPlugin接口,该目录为/usr/lib/x86_64-linux-gnu/dde-file-manager/plugins/generics/

对于DFMExtEmblemIconPlugin接口,改目录为/usr/lib/x86_64-linux-gnu/dde-file-manager/plugins/extensions/

然后,重启dde-file-manager.由于dfm有守护进程的存在,因而直接执行sudo kill -9 dde-file-manager可能并不会重新加载插件动态库,徽标也就无法奏效。因而,需要先杀死dfm的守护进程然后在重启之。

1
2
3
4
5
6
7
8
# install DFMGenericPlugin
mkdir -p /usr/lib/x86_64-linux-gnu/dde-file-manager/plugins/generics/
cp libemblem-example-plugin.so /usr/lib/x86_64-linux-gnu/dde-file-manager/plugins/generics/

# install DFMExtEmblemIconPlugin
mkdir -p /usr/lib/x86_64-linux-gnu/dde-file-manager/plugins/extensions/
cp ./out/libext-menu-example.so /usr/lib/x86_64-linux-gnu/dde-file-manager/plugins/extensions/
cp libext-emblem-demo.so /usr/lib/x86_64-linux-gnu/dde-file-manager/plugins/extensions/

DFMGenericPlugin

dde-file-manager

desktop

DFMExtEmblemIconPlugin

desktop

dde-file-manager

Finally

有一个特别无语的事情…..UOS提供的dfm-extension-dev包里面的头文件是错误的…如下,如果不仔细看的话…..插件就永远起不来….

看看最后两个函数的定义…..wtf?单词拼错了…..

1
2
3
4
extern "C" void dfm_extension_initiliaze();
extern "C" void dfm_extension_shutdown();
extern "C" DFMExtMenuPlugin *dfm_extesion_menu();
extern "C" DFMExtEmblemIconPlugin *dfm_extesion_emblem();

Reference

不来体验体验深度文件管理器的坚果云插件么?-论坛-深度科技 (deepin.org)

linuxdeepin/dde-file-manager-integration

哦吼是一首歌。
Built with Hugo
Theme Stack designed by Jimmy