阅读QtCreator-Mode与ModeManager

继续阅读QtCreator源码,本文主要分析在QtCreator中Mode的管理方式以及其接口设计方法。windows

Mode顾名思义叫作模式,在Qt Creator中有欢迎模式,编辑模式(EditMode),设计模式(DesignMode),调试模式,帮助模式。在核心插件中只有两个模式就是EditMode和DesignMode,本文仅分析EditMode,其余能够相似推广开来。在UI上的表示就是IDE最左边的Tab工具栏。以下图所示:
image
固然从CorePlugin入手,其类中有两个成员 设计模式

MainWindow *m_mainWindow;
    EditMode *m_editMode;

相关的函数是
bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
{
    parseArguments(arguments);
    const bool success = m_mainWindow->init(errorMessage);
    if (success) {
        m_editMode = new EditMode;
        addObject(m_editMode);
        ModeManager::activateMode(m_editMode->id());
        m_designMode = new DesignMode;
    }
    return success;
}

也就是在初始时的模式应该是m_editMode.将m_editMode放到对象池当中,以后能够随时访问。接下来看MainWindow类的Mode相关内容,它有三个比较重要的成员变量:EditorManager *m_editorManager; ModeManager *m_modeManager;  FancyTabWidget *m_modeStack;
在MainWindow的构造函数中建立了这四个变量。
m_modeStack = new FancyTabWidget(this);
    m_modeManager = new ModeManager(this, m_modeStack);
    m_modeManager->addWidget(m_progressManager->progressView());
    m_statusBarManager = new StatusBarManager(this);
    m_messageManager = new MessageManager;
    m_editorManager = new EditorManager(this);
    m_editorManager->hide();

FancyTabWidget

FancyTabWidget用于建立最左边的工具栏,它包括FancyTabBar和CornerWidget两个部分,同时它还管理着一个StackedLayout和状态栏,用于显示在不一样模式下的不一样Widget布局。FancyTabWidget的控件布局以下:app

image
树形结构从左到右,从上到下有序的布局。之因此FancyTabBar部分的颜色不一样,是FancyTabWidget的paintEvent完成的。ide

EditorManager

继承于QWidget,在MainWindow的构造函数中建立实例,在extensionInitialized()中初始化。该类完成“文件”菜单项中的保存、另存为等菜单项的添加,并添加关闭按钮以及相应的快捷键。
image
能够看到“向前/后”、关闭等按钮的插函数都在该类中。在该类中还有一个重要的类是EditorClosingCoreListener,继承与ICoreListener。ICoreListener提供一些钩子(或者说相应函数)响应核心插件发出的事件。任何继承与ICoreListener的类的变量的接口函数返回false,整个过程(如关闭过程)就会终止。在使用时,须要将这些变量添加到对象池当中,并要在析构的时候从对象池移出。
在Qt中Model/View方式被使用的淋漓精致,能够说EditorManager就一个View。OpenEditorsModel继承于QAbstractItemModel,实现相应的虚方法就能够了。这里的columnCount和rowCount分别返回2和editor数量。在这个model中数据存放在Entry列表中,每一个Entry存放文件名、id、显示标题等信息。Entry列表中以文件名做为key(也就是说,文件名相同的Editor将会合并,替换掉旧的)。
在OpenEditorModel中能够看到有两个这样的东东:QList<OpenEditorsModel::Entry> m_editors;函数

QList<IEditor *> m_duplicateEditors;
我暂时尚未理解出这个duplicate有什么做用?望高手解答。
在editormanager.cpp中有一个EditorManagerPlaceHolder(继承与QWidget)是用于根据当前模式进行布局。该类的功能很弱,就是隐藏当前Mode,show新的Mode。一个EditorManagerPlaceHolder对应一个m_mode,Mode发生改变,全部的Holder都将会收到currentModeChanged(newMode)信号。在Holder类中有一个静态变量EditorManagerPlaceHolder m_current,记录当前状态。那么响应信号中要作的就是若是响应函数在m_current中,就把EditorManager先隐藏掉,在Holder的模式是newMode的状况下把EditorManager添加到布局当中去,而后显示(这里显然是EditorManager的布局已经改变了),在EditorManager中有一个自动保存的定时器m_autoSaveTimer,而后调用autoSave()让IDocument类去完成保存。接着看下面的代码: 工具

void EditorManager::init()
{
    d->m_coreListener = new EditorClosingCoreListener(this);
    ExtensionSystem::PluginManager::addObject(d->m_coreListener);

    d->m_openEditorsFactory = new OpenEditorsViewFactory();
    ExtensionSystem::PluginManager::addObject(d->m_openEditorsFactory);
    VariableManager *vm = VariableManager::instance();
    vm->registerVariable(kCurrentDocumentFilePath,
        tr("Full path of the current document including file name."));
    vm->registerVariable(kCurrentDocumentPath,
        tr("Full path of the current document excluding file name."));
    vm->registerVariable(kCurrentDocumentXPos,
        tr("X-coordinate of the current editor's upper left corner, relative to screen."));
    vm->registerVariable(kCurrentDocumentYPos,
        tr("Y-coordinate of the current editor's upper left corner, relative to screen."));
    connect(vm, SIGNAL(variableUpdateRequested(QByteArray)),
            this, SLOT(updateVariable(QByteArray)));
			
}

这个初始化中完成coreListener的添加,并注册一些变量(其实本质上就是QMap的key-value)。
接下来看setCurrentEditor()。首先应该知道EditorView提供了鼠标的位置历史,那么当设置新的editor时就须要考虑是否保留以前记录的鼠标位置。更新Action,更新windows title。以外还有一个叫setCurrentView的函数。这个就是以前提到的修改布局。
布局

void EditorManager::emptyView(Core::Internal::EditorView *view)
{
    if (!view)
        return;

    QList<IEditor *> editors = view->editors();
    foreach (IEditor *editor, editors) {
        if (!d->m_editorModel->isDuplicate(editor)) {
            editors.removeAll(editor);
            view->removeEditor(editor);
            continue;
        }
        emit editorAboutToClose(editor);
        removeEditor(editor);
        view->removeEditor(editor);
    }
    emit editorsClosed(editors);
    foreach (IEditor *editor, editors) {
        delete editor;
    }
}

能够看到,清空View的方法是从View中移除全部的Editor。
有了清空View,还有关闭View。关闭View就是关闭该View的全部的IEditor,而后放上Split Window的其余View。this

EditorManager中的另外一个重要函数是.net

Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)。将当前分个窗口中有editor的窗口先将editor移除,而后添加到这个view参数当中。当调用activateEditor时就须要先将view和editor结合起来(调用placeEditor)便可,而后设置当前Editor为editor。
插件

IMode

一个Mode包含显示名称,图标,优先级,id,类型,状态(Enabled or not).全部的Mode都须要继承于该类。

ModeManager

它的初始化以下:

ModeManager::ModeManager(Internal::MainWindow *mainWindow,
                         Internal::FancyTabWidget *modeStack)
{
    m_instance = this;
    d = new ModeManagerPrivate();
    d->m_mainWindow = mainWindow;
    d->m_modeStack = modeStack;
    d->m_signalMapper = new QSignalMapper(this);
    d->m_oldCurrent = -1;
    d->m_actionBar = new Internal::FancyActionBar(modeStack);
    d->m_modeStack->addCornerWidget(d->m_actionBar);

    connect(d->m_modeStack, SIGNAL(currentAboutToShow(int)), SLOT(currentTabAboutToChange(int)));
    connect(d->m_modeStack, SIGNAL(currentChanged(int)), SLOT(currentTabChanged(int)));
    connect(d->m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotActivateMode(int)));
}

能够看到当FancyTabWidget的Tab发生变化时,就会调用currentTabAboutToChange(int)和currentTabChanged();而这两个函数又会发出 currentModeAboutToChange(mode)和currentModeChanged(mode, oldMode)两个信号,这些信号由全部的Mode来响应,也就是说ModeManager提供了FancyTabWidget和Mode之间的通讯桥梁。

相关文章
相关标签/搜索