繼續閱讀QtCreator源碼,本文主要分析在QtCreator中Mode的管理方式以及其接口設計方法。windows
Mode顧名思義叫作模式,在Qt Creator中有歡迎模式,編輯模式(EditMode),設計模式(DesignMode),調試模式,幫助模式。在覈心插件中只有兩個模式就是EditMode和DesignMode,本文僅分析EditMode,其餘能夠相似推廣開來。在UI上的表示就是IDE最左邊的Tab工具欄。以下圖所示:
固然從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_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用於建立最左邊的工具欄,它包括FancyTabBar和CornerWidget兩個部分,同時它還管理着一個StackedLayout和狀態欄,用於顯示在不一樣模式下的不一樣Widget佈局。FancyTabWidget的控件佈局以下:app
樹形結構從左到右,從上到下有序的佈局。之因此FancyTabBar部分的顏色不一樣,是FancyTabWidget的paintEvent完成的。ide
繼承於QWidget,在MainWindow的構造函數中建立實例,在extensionInitialized()中初始化。該類完成「文件」菜單項中的保存、另存爲等菜單項的添加,並添加關閉按鈕以及相應的快捷鍵。
能夠看到「向前/後」、關閉等按鈕的插函數都在該類中。在該類中還有一個重要的類是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。
插件
一個Mode包含顯示名稱,圖標,優先級,id,類型,狀態(Enabled or not).全部的Mode都須要繼承於該類。
它的初始化以下:
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之間的通訊橋樑。