阅读QtCreator-Mode与ModeManager

微笑、不失礼 提交于 2019-12-10 15:39:05

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

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的控件布局如下:

image
树形结构从左到右,从上到下有序的布局。之所以FancyTabBar部分的颜色不同,是FancyTabWidget的paintEvent完成的。

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。

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之间的通信桥梁。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!