Qt Creator 插件开发(8):添加新的编辑器

Qt Creator 最基本的功能是一个文本编辑器。在此基础之上,Qt Creator 还提供了编辑 UI 文件、QRC 文件、PRO/PRI 文件以及 EXE/DLL/SO 文件的功能。

Qt Creator 二进制编辑器

从本节开始,我们将开始尝试开发一些实际的插件,理解如何为我们特有的文件格式提供编辑器。这里,我们选择 HTML 格式。我们的插件将使我们能够从本地文件系统中加载 HTML 文件,并且能够查看和编辑。下面就是我们的插件完成后的行为:

QtCreator HTML Editor

核心类和接口

为了支持新的编辑器类型,我们需要:

  • 实现一个插件类(实现Core::IPlugin接口),暴露出一个“编辑器工厂”。前面我们已经介绍过如何创建插件,实现Core::IPlugin接口。
  • 实现这个“编辑器工厂”,也就是Core::IEditorFactory接口。这个接口提供了帮助创建特定格式编辑器对象的函数。
  • 实现编辑器,也就是Core::IEditor接口。这个接口提供了用于辅助编辑一种文件格式(例如 HTML、ODF 等)的函数。编辑器必须提供访问它所要显示或者编辑的文件的函数。
  • 实现接口Core::IFile。该接口用于帮助数据加载或保存。

在后面的内容中,我们将依次了解上述各个接口的含义以及如何实现这些接口。

Core::IFile

Core::IFile接口将文件操作从用户界面抽象出来,提供用于加载和保存文件的虚函数(一般会以文件名作为参数)。我们可以将文件看做具有 mime-tyle、“modified” 和 “read-only” 等标记位的对象。Core::IFile接口在 src/plugins/coreplugin/ifile.h 中声明:

#ifndef IFILE_H
#define IFILE_H

#include "core_global.h"
#include <QtCore/QObject>

namespace Core {

class MimeType;

class CORE_EXPORT IFile : public QObject
{
    Q_OBJECT

public:
    // This enum must match the indexes of the reloadBehavior widget
    // in generalsettings.ui
    enum ReloadSetting {
        AlwaysAsk = 0,
        ReloadUnmodified = 1,
        IgnoreAll = 2
    };

    enum Utf8BomSetting {
        AlwaysAdd = 0,
        OnlyKeep = 1,
        AlwaysDelete = 2
    };

    enum ChangeTrigger {
        TriggerInternal,
        TriggerExternal
    };

    enum ChangeType {
        TypeContents,
        TypePermissions,
        TypeRemoved
    };

    enum ReloadBehavior {
        BehaviorAsk,
        BehaviorSilent
    };

    enum ReloadFlag {
        FlagReload,
        FlagIgnore
    };

    IFile(QObject *parent = 0) : QObject(parent) {}
    virtual ~IFile() {}

    virtual bool save(const QString &fileName = QString()) = 0;
    virtual QString fileName() const = 0;

    virtual QString defaultPath() const = 0;
    virtual QString suggestedFileName() const = 0;
    virtual QString mimeType() const = 0;

    virtual bool isModified() const = 0;
    virtual bool isReadOnly() const = 0;
    virtual bool isSaveAsAllowed() const = 0;

    virtual ReloadBehavior reloadBehavior(ChangeTrigger state,
                                          ChangeType type) const = 0;
    virtual void reload(ReloadFlag flag, ChangeType type) = 0;
    virtual void rename(const QString &newName) = 0;

    virtual void checkPermissions() {}

signals:
    void changed();

    void aboutToReload();
    void reloaded();
};

} // namespace Core

#endif // IFILE_H

或许你会问:“我们已经有QFile,作为文件操作的一种抽象,为什么又要设计一个IFile呢?”原因如下:

  • IFile关心的是以文件名为参数,将一个文件的内容加载进一个编辑器 editor 的行为,而QFile仅仅是将文件内容加载到一个QByteArray对象;
  • IFile需要在用户在编辑器中修改了文件内容的时候发出modified()信号,需要注意的是,此时的文件修改并没有写入磁盘;而QFile只有在文件内容写入磁盘时才会发出bytesWritten()信号;
  • IFile需要处理一个在磁盘被修改的文件如何重新加载到编辑器中,而QFile不需要处理这种问题

我们会在后面的章节中详细探讨如何实现Core::IFile接口。

Core::IEditor

Core::IEditor接口用于提供编辑不同类型文件的编辑器。这个接口位于 src/plugins/coreplugin/editormanager/ieditor.h 中:

#ifndef IEDITOR_H
#define IEDITOR_H

#include <coreplugin/core_global.h>
#include <coreplugin/icontext.h>
#include <QtCore/QMetaType>

namespace Core {

class IFile;

class CORE_EXPORT IEditor : public IContext
{
    Q_OBJECT
public:

    IEditor(QObject *parent = 0) : IContext(parent) {}
    virtual ~IEditor() {}

    virtual bool createNew(const QString &contents = QString()) = 0;
    virtual bool open(const QString &fileName = QString()) = 0;
    virtual IFile *file() = 0;
    virtual QString id() const = 0;
    virtual QString displayName() const = 0;
    virtual void setDisplayName(const QString &title) = 0;

    virtual bool duplicateSupported() const = 0;
    virtual IEditor *duplicate(QWidget *parent) = 0;

    virtual QByteArray saveState() const = 0;
    virtual bool restoreState(const QByteArray &state) = 0;

    virtual int currentLine() const { return 0; }
    virtual int currentColumn() const { return 0; }
    virtual void gotoLine(int line, int column = 0)
    { Q_UNUSED(line) Q_UNUSED(column) }

    virtual bool isTemporary() const = 0;

    virtual QWidget *toolBar() = 0;

    virtual QString preferredModeType() const { return QString(); }

signals:
    void changed();
};

} // namespace Core

Q_DECLARE_METATYPE(Core::IEditor*)

#endif // IEDITOR_H

Core::IEditor主要提供以下功能:

  • 一个编辑器组件(由Core::IEditor::widget()函数返回)。Qt Creator 使用这个组件显示需要编辑的文件的内容;
  • 实现Core::IFile接口的文件(由Core::IEditor::file()函数返回),Qt Creator 使用这个对象触发文件从磁盘加载以及保存到磁盘的操作;
  • 一个自定义的工具条 toolbar,Qt Creator 会在该编辑器可用时自动加载该工具条;
  • 在文件中光标的当前位置(Core::IEditor::currentLine()Core::IEditor::currentColumn()函数)
  • 需要显示在“打开文件”列表的名字。

我们可以通过下面的示意图来理解上面说的这几点:

Qt Creator Editor 结构

2 Comments

  1. kangaxx 2013年9月30日
    • 豆子 2013年10月1日

Leave a Reply