閱讀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之間的通訊橋樑。

相關文章
相關標籤/搜索