继续阅读QtCreator源码,本文主要分析在QtCreator中Mode的管理方式以及其接口设计方法。
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_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的控件布局如下:
树形结构从左到右,从上到下有序的布局。之所以FancyTabBar部分的颜色不同,是FancyTabWidget的paintEvent完成的。
EditorManager
继承于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。
EditorManager中的另一个重要函数是
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之间的通信桥梁。
来源:oschina
链接:https://my.oschina.net/u/247612/blog/73916