diff --git a/plugins/quickopen/quickopenplugin.h b/plugins/quickopen/quickopenplugin.h --- a/plugins/quickopen/quickopenplugin.h +++ b/plugins/quickopen/quickopenplugin.h @@ -50,7 +50,7 @@ ~QuickOpenPlugin() override; static QuickOpenPlugin* self(); - + // KDevelop::Plugin methods void unload() override; @@ -77,13 +77,10 @@ QSet fileSet() const override; - //Frees the model by closing active quickopen dialoags, and retuns whether successful. - bool freeModel(); - void createActionsForMainWindow( Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions ) override; QuickOpenLineEdit* createQuickOpenLineWidget(); - + KDevelop::IQuickOpenLine* createQuickOpenLine(const QStringList& scopes, const QStringList& type, QuickOpenType kind) override; public slots: void quickOpen(); @@ -116,18 +113,17 @@ QWidget* specialObjectNavigationWidget() const; bool jumpToSpecialObject(); void showQuickOpenWidget(const QStringList &items, const QStringList &scopes, bool preselectText); - + QuickOpenModel* m_model; class ProjectFileDataProvider* m_projectFileData; class ProjectItemDataProvider* m_projectItemData; class OpenFilesDataProvider* m_openFilesData; class DocumentationQuickOpenProvider* m_documentationItemData; class ActionsQuickOpenProvider* m_actionsItemData; QStringList lastUsedScopes; QStringList lastUsedItems; - + //We can only have one widget at a time, because we manipulate the model. - QPointer m_currentWidgetHandler; QAction* m_quickOpenDeclaration; QAction* m_quickOpenDefinition; }; @@ -139,28 +135,31 @@ public: explicit QuickOpenLineEdit(QuickOpenWidgetCreator* creator) ; ~QuickOpenLineEdit() override ; - - bool insideThis(QObject* object); + void showWithWidget(QuickOpenWidget* widget); - + void setDefaultText(const QString& text) override { m_defaultText = text; setPlaceholderText(m_defaultText); } + void configureWidget(); + void prepareWidgetGeometry(); private slots: - void activate() ; void deactivate() ; - void checkFocus(); - void widgetDestroyed(QObject*); + private: void focusInEvent(QFocusEvent* ev) override ; + void focusOutEvent(QFocusEvent * ) override; + void keyPressEvent(QKeyEvent * ev) override; bool eventFilter(QObject* obj, QEvent* e) override ; - void hideEvent(QHideEvent* ) override; - + QPointer m_widget; bool m_forceUpdate; QString m_defaultText; QuickOpenWidgetCreator* m_widgetCreator; + friend class QuickOpenPlugin; }; +QuickOpenModel* createOutlineModel(); + #endif // KDEVPLATFORM_PLUGIN_QUICKOPENPLUGIN_H diff --git a/plugins/quickopen/quickopenplugin.cpp b/plugins/quickopen/quickopenplugin.cpp --- a/plugins/quickopen/quickopenplugin.cpp +++ b/plugins/quickopen/quickopenplugin.cpp @@ -390,8 +390,6 @@ QuickOpenPlugin::~QuickOpenPlugin() { - freeModel(); - delete m_model; delete m_projectFileData; delete m_projectItemData; @@ -432,9 +430,6 @@ void QuickOpenPlugin::showQuickOpen(const QStringList& items) { - if(!freeModel()) - return; - QStringList initialItems = items; QStringList useScopes = lastUsedScopes; @@ -447,9 +442,6 @@ void QuickOpenPlugin::showQuickOpen( ModelTypes modes ) { - if(!freeModel()) - return; - QStringList initialItems; if( modes & Files || modes & OpenFiles ) initialItems << i18n("Files"); @@ -473,27 +465,23 @@ void QuickOpenPlugin::showQuickOpenWidget(const QStringList& items, const QStringList& scopes, bool preselectText) { - QuickOpenWidgetDialog* dialog = new QuickOpenWidgetDialog( i18n("Quick Open"), m_model, items, scopes ); - m_currentWidgetHandler = dialog; - if (preselectText) - { - KDevelop::IDocument *currentDoc = core()->documentController()->activeDocument(); - if (currentDoc && currentDoc->isTextDocument()) - { - QString preselected = currentDoc->textSelection().isEmpty() ? currentDoc->textWord() : currentDoc->textDocument()->text(currentDoc->textSelection()); - dialog->widget()->setPreselectedText(preselected); - } - } - - connect( dialog->widget(), &QuickOpenWidget::scopesChanged, this, &QuickOpenPlugin::storeScopes ); - //Not connecting itemsChanged to storeItems, as showQuickOpen doesn't use lastUsedItems and so shouldn't store item changes - //connect( dialog->widget(), SIGNAL(itemsChanged(QStringList)), this, SLOT(storeItems(QStringList)) ); - dialog->widget()->ui.itemsButton->setEnabled(false); - if(quickOpenLine()) { - quickOpenLine()->showWithWidget(dialog->widget()); - dialog->deleteLater(); + quickOpenLine()->setFocus(); }else{ + //TODO: Do we really need to present a dialog? + QuickOpenWidgetDialog* dialog = new QuickOpenWidgetDialog( i18n("Quick Open"), m_model, items, scopes ); + if (preselectText) + { + KDevelop::IDocument *currentDoc = core()->documentController()->activeDocument(); + if (currentDoc && currentDoc->isTextDocument()) + { + QString preselected = currentDoc->textSelection().isEmpty() ? currentDoc->textWord() : currentDoc->textDocument()->text(currentDoc->textSelection()); + dialog->widget()->setPreselectedText(preselected); + } + } + connect( dialog->widget(), &QuickOpenWidget::scopesChanged, this, &QuickOpenPlugin::storeScopes ); + connect( dialog->widget(), SIGNAL(itemsChanged(QStringList)), this, SLOT(storeItems(QStringList)) ); + dialog->widget()->ui.itemsButton->setEnabled(false); dialog->run(); } } @@ -675,15 +663,6 @@ core()->documentController()->openDocument(u.toUrl(), c); } -bool QuickOpenPlugin::freeModel() -{ - if(m_currentWidgetHandler) - delete m_currentWidgetHandler; - m_currentWidgetHandler = 0; - - return true; -} - void QuickOpenPlugin::nextFunction() { jumpToNearestFunction(NextFunction); @@ -754,49 +733,45 @@ qCDebug(PLUGIN_QUICKOPEN) << "No declaration to jump to"; } +QuickOpenModel* createOutlineModel() { + IDocument* doc = ICore::self()->documentController()->activeDocument(); + if(!doc) { + qCDebug(PLUGIN_QUICKOPEN) << "No active document"; + return nullptr; + } -struct CreateOutlineDialog { - CreateOutlineDialog() : dialog(0), cursorDecl(0), model(0) { - } - - void start() { - if(!QuickOpenPlugin::self()->freeModel()) - return; - - IDocument* doc = ICore::self()->documentController()->activeDocument(); - if(!doc) { - qCDebug(PLUGIN_QUICKOPEN) << "No active document"; - return; - } - - KDevelop::DUChainReadLocker lock( DUChain::lock() ); - - TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() ); - - if( !context ) { - qCDebug(PLUGIN_QUICKOPEN) << "Got no standard context"; - return; - } - - model = new QuickOpenModel(0); + KDevelop::DUChainReadLocker lock( DUChain::lock() ); - OutlineFilter filter(items); + TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() ); + if( !context ) { + qCDebug(PLUGIN_QUICKOPEN) << "Got no standard context"; + return nullptr; + } - DUChainUtils::collectItems( context, filter ); + auto model = new QuickOpenModel(0); + QList items; + OutlineFilter filter(items); + DUChainUtils::collectItems( context, filter ); - if(noHtmlDestriptionInOutline) { - for(int a = 0; a < items.size(); ++a) - items[a].m_noHtmlDestription = true; - } + if(noHtmlDestriptionInOutline) { + for(int a = 0; a < items.size(); ++a) + items[a].m_noHtmlDestription = true; + } - cursorDecl = cursorContextDeclaration(); + model->registerProvider( QStringList(), QStringList(), new DeclarationListDataProvider(QuickOpenPlugin::self(), items, true) ); + return model; +}; - model->registerProvider( QStringList(), QStringList(), new DeclarationListDataProvider(QuickOpenPlugin::self(), items, true) ); +struct CreateOutlineDialog { + CreateOutlineDialog() : dialog(0), cursorDecl(0), model(0) { + } + void start() { + auto model = createOutlineModel(); dialog = new QuickOpenWidgetDialog( i18n("Outline"), model, QStringList(), QStringList(), true ); - model->setParent(dialog->widget()); } + void finish() { //Select the declaration that contains the cursor if(cursorDecl && dialog) { @@ -859,25 +834,12 @@ void QuickOpenPlugin::quickOpenNavigateFunctions() { - CreateOutlineDialog create; - create.start(); - - if(!create.dialog) - return; - - m_currentWidgetHandler = create.dialog; - - QuickOpenLineEdit* line = quickOpenLine(QStringLiteral("Outline")); - if(!line) - line = quickOpenLine(); - - if(line) { - line->showWithWidget(create.dialog->widget()); - create.dialog->deleteLater(); - }else - create.dialog->run(); - - create.finish(); + auto outlineLineEdit = quickOpenLine(QStringLiteral("Outline")); + Q_ASSERT(outlineLineEdit); + if(outlineLineEdit->m_widget && outlineLineEdit->m_widget->m_model) { + outlineLineEdit->m_widget->setQuickOpenData(createOutlineModel(), QStringList(), QStringList()); + } + outlineLineEdit->setFocus(); } QuickOpenLineEdit::QuickOpenLineEdit(QuickOpenWidgetCreator* creator) : m_widget(0), m_forceUpdate(false), m_widgetCreator(creator) { @@ -897,82 +859,8 @@ delete m_widgetCreator; } -bool QuickOpenLineEdit::insideThis(QObject* object) { - while (object) - { - qCDebug(PLUGIN_QUICKOPEN) << object; - if (object == this || object == m_widget) - { - return true; - } - object = object->parent(); - } - return false; -} - -void QuickOpenLineEdit::widgetDestroyed(QObject* obj) -{ - Q_UNUSED(obj); - // need to use a queued connection here, because this function is called in ~QWidget! - // => QuickOpenWidget instance is half-destructed => connections are not yet cleared - // => clear() will trigger signals which will operate on the invalid QuickOpenWidget - // So, just wait until properly destructed - QMetaObject::invokeMethod(this, "deactivate", Qt::QueuedConnection); -} - -void QuickOpenLineEdit::showWithWidget(QuickOpenWidget* widget) +void QuickOpenLineEdit::prepareWidgetGeometry() { - connect(widget, &QuickOpenWidget::destroyed, this, &QuickOpenLineEdit::widgetDestroyed); - qCDebug(PLUGIN_QUICKOPEN) << "storing widget" << widget; - deactivate(); - if(m_widget) { - qCDebug(PLUGIN_QUICKOPEN) << "deleting" << m_widget; - delete m_widget; - } - m_widget = widget; - m_forceUpdate = true; - setFocus(); -} - -void QuickOpenLineEdit::focusInEvent(QFocusEvent* ev) { - QLineEdit::focusInEvent(ev); -// delete m_widget; - qCDebug(PLUGIN_QUICKOPEN) << "got focus"; - qCDebug(PLUGIN_QUICKOPEN) << "old widget" << m_widget << "force update:" << m_forceUpdate; - if (m_widget && !m_forceUpdate) - return; - - if (!m_forceUpdate && !QuickOpenPlugin::self()->freeModel()) { - deactivate(); - return; - } - - m_forceUpdate = false; - - if(!m_widget) - { - m_widget = m_widgetCreator->createWidget(); - if(!m_widget) { - deactivate(); - return; - } - } - - activate(); - - m_widget->showStandardButtons(false); - m_widget->showSearchField(false); - - m_widget->setParent(0, Qt::ToolTip); - m_widget->setFocusPolicy(Qt::NoFocus); - m_widget->setAlternativeSearchField(this); - - QuickOpenPlugin::self()->m_currentWidgetHandler = m_widget; - connect(m_widget.data(), &QuickOpenWidget::ready, this, &QuickOpenLineEdit::deactivate); - - connect( m_widget.data(), &QuickOpenWidget::scopesChanged, QuickOpenPlugin::self(), &QuickOpenPlugin::storeScopes ); - connect( m_widget.data(), &QuickOpenWidget::itemsChanged, QuickOpenPlugin::self(), &QuickOpenPlugin::storeItems ); - Q_ASSERT(m_widget->ui.searchLine == this); m_widget->prepareShow(); QRect widgetGeometry = QRect(mapToGlobal(QPoint(0, height())), mapToGlobal(QPoint(width(), height() + 400))); widgetGeometry.setWidth(700); ///@todo Waste less space @@ -984,17 +872,45 @@ widgetGeometry.moveBottom(mapToGlobal(QPoint(0, 0)).y()); } m_widget->setGeometry(widgetGeometry); +} + +void QuickOpenLineEdit::focusInEvent(QFocusEvent* ev) { + QLineEdit::focusInEvent(ev); + if(!m_widget) { + m_widget = m_widgetCreator->createWidget(); + m_widget->installEventFilter(this); + configureWidget(); + } + prepareWidgetGeometry(); m_widget->show(); +} - m_widgetCreator->widgetShown(); +void QuickOpenLineEdit::configureWidget() { + m_widget->setParent(0, Qt::ToolTip); + m_widget->setFocusPolicy(Qt::NoFocus); + m_widget->showStandardButtons(false); + m_widget->showSearchField(false); + m_widget->setAlternativeSearchField(this); + connect( m_widget, &QuickOpenWidget::ready, this, &QuickOpenLineEdit::deactivate); + connect( m_widget, &QuickOpenWidget::scopesChanged, QuickOpenPlugin::self(), &QuickOpenPlugin::storeScopes ); + connect( m_widget, &QuickOpenWidget::itemsChanged, QuickOpenPlugin::self(), &QuickOpenPlugin::storeItems ); } -void QuickOpenLineEdit::hideEvent(QHideEvent* ev) -{ - QWidget::hideEvent(ev); - if(m_widget) - QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); -// deactivate(); +void QuickOpenLineEdit::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Escape) { + deactivate(); + e->accept(); + clearFocus(); + } +} + +void QuickOpenLineEdit::focusOutEvent(QFocusEvent *ev){ + if (!m_widget->underMouse()) { + m_widget->hide(); + } + if (!m_widget->isVisible()) { + QLineEdit::focusOutEvent(ev); + } } bool QuickOpenLineEdit::eventFilter(QObject* obj, QEvent* e) { @@ -1012,6 +928,8 @@ break; case QEvent::WindowActivate: case QEvent::WindowDeactivate: + if (m_widget->underMouse()) + return true; qCDebug(PLUGIN_QUICKOPEN) << "closing because of window activation"; deactivate(); break; @@ -1026,66 +944,15 @@ break; } } - case QEvent::FocusIn: - if (dynamic_cast(obj)) { - QFocusEvent* focusEvent = dynamic_cast(e); - Q_ASSERT(focusEvent); - //Eat the focus event, keep the focus - qCDebug(PLUGIN_QUICKOPEN) << "focus change" << "inside this: " << insideThis(obj) << "this" << this << "obj" << obj; - if(obj == this) - return false; - - qCDebug(PLUGIN_QUICKOPEN) << "reason" << focusEvent->reason(); - if (focusEvent->reason() != Qt::MouseFocusReason && focusEvent->reason() != Qt::ActiveWindowFocusReason) { - QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); - return false; - } - if (!insideThis(obj)) - deactivate(); - } - break; default: break; } return false; } -void QuickOpenLineEdit::activate() { - qCDebug(PLUGIN_QUICKOPEN) << "activating"; - setText(QString()); - setStyleSheet(QString()); - qApp->installEventFilter(this); -} -void QuickOpenLineEdit::deactivate() { - qCDebug(PLUGIN_QUICKOPEN) << "deactivating"; - - clear(); - - if(m_widget || hasFocus()) - QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection); - - if (m_widget) - m_widget->deleteLater(); - - m_widget = 0; - qApp->removeEventFilter(this); - -} -void QuickOpenLineEdit::checkFocus() -{ - qCDebug(PLUGIN_QUICKOPEN) << "checking focus" << m_widget; - if(m_widget) { - if(isVisible() && !isHidden()) - setFocus(); - else - deactivate(); - }else{ - if (ICore::self()->documentController()->activeDocument()) - ICore::self()->documentController()->activateDocument(ICore::self()->documentController()->activeDocument()); - - //Make sure the focus is somewehre else, even if there is no active document - setEnabled(false); - setEnabled(true); +void QuickOpenLineEdit::deactivate() { + if (m_widget) { + m_widget->hide(); } } diff --git a/plugins/quickopen/quickopenwidget.h b/plugins/quickopen/quickopenwidget.h --- a/plugins/quickopen/quickopenwidget.h +++ b/plugins/quickopen/quickopenwidget.h @@ -53,6 +53,9 @@ //Shows OK + Cancel. By default they are hidden void showStandardButtons(bool show); void showSearchField(bool show); + void setQuickOpenData(QuickOpenModel* model, const QStringList& items, const QStringList& scopes, bool listOnly = false, bool noSearchField = false); + + void createItemsMenu(QPushButton *btn, const QStringList &items, const QStringList& allTypes); signals: void scopesChanged( const QStringList& scopes ); diff --git a/plugins/quickopen/quickopenwidget.cpp b/plugins/quickopen/quickopenwidget.cpp --- a/plugins/quickopen/quickopenwidget.cpp +++ b/plugins/quickopen/quickopenwidget.cpp @@ -66,76 +66,58 @@ ui.list->header()->hide(); ui.list->setRootIsDecorated( false ); ui.list->setVerticalScrollMode( QAbstractItemView::ScrollPerItem ); - - connect(ui.list->verticalScrollBar(), &QScrollBar::valueChanged, m_model, &QuickOpenModel::placeExpandingWidgets); - ui.searchLine->setFocus(); - ui.list->setItemDelegate( new QuickOpenDelegate( m_model, ui.list ) ); - - if(!listOnly) { - QStringList allTypes = m_model->allTypes(); - QStringList allScopes = m_model->allScopes(); - - QMenu* itemsMenu = new QMenu(this); - - foreach( const QString &type, allTypes ) - { - QAction* action = new QAction(type, itemsMenu); - action->setCheckable(true); - action->setChecked(initialItems.isEmpty() || initialItems.contains( type )); - connect( action, &QAction::toggled, this, &QuickOpenWidget::updateProviders, Qt::QueuedConnection ); - itemsMenu->addAction(action); - } - - ui.itemsButton->setMenu(itemsMenu); - - QMenu* scopesMenu = new QMenu(this); - - foreach( const QString &scope, allScopes ) - { - QAction* action = new QAction(scope, scopesMenu); - action->setCheckable(true); - action->setChecked(initialScopes.isEmpty() || initialScopes.contains( scope ) ); - - connect( action, &QAction::toggled, this, &QuickOpenWidget::updateProviders, Qt::QueuedConnection ); - scopesMenu->addAction(action); - } - - ui.scopesButton->setMenu(scopesMenu); - - }else{ - ui.list->setFocusPolicy(Qt::StrongFocus); - ui.scopesButton->hide(); - ui.itemsButton->hide(); - ui.label->hide(); - ui.label_2->hide(); - } - - showSearchField(!noSearchField); - ui.okButton->hide(); ui.cancelButton->hide(); - ui.searchLine->installEventFilter( this ); ui.list->installEventFilter( this ); ui.list->setFocusPolicy(Qt::NoFocus); ui.scopesButton->setFocusPolicy(Qt::NoFocus); ui.itemsButton->setFocusPolicy(Qt::NoFocus); - connect( ui.searchLine, &QLineEdit::textChanged, this, &QuickOpenWidget::textChanged ); - - connect( ui.list, &ExpandingTree::doubleClicked, this, &QuickOpenWidget::doubleClicked ); + setQuickOpenData(model, initialItems, initialScopes, listOnly, noSearchField); + connect(ui.list->verticalScrollBar(), &QScrollBar::valueChanged, m_model, &QuickOpenModel::placeExpandingWidgets); + connect(ui.searchLine, &QLineEdit::textChanged, this, &QuickOpenWidget::textChanged ); + connect(ui.list, &ExpandingTree::doubleClicked, this, &QuickOpenWidget::doubleClicked ); connect(ui.okButton, &QPushButton::clicked, this, &QuickOpenWidget::accept); connect(ui.okButton, &QPushButton::clicked, this, &QuickOpenWidget::ready); connect(ui.cancelButton, &QPushButton::clicked, this, &QuickOpenWidget::ready); +} +void QuickOpenWidget::setQuickOpenData(QuickOpenModel* model, const QStringList& items, const QStringList& scopes, bool listOnly, bool noSearchField) +{ + m_model = model; + if (listOnly) { +// ui.list->setFocusPolicy(Qt::StrongFocus); + ui.scopesButton->hide(); + ui.itemsButton->hide(); + ui.label->hide(); + ui.label_2->hide(); + } else { + createItemsMenu(ui.itemsButton, items, m_model->allTypes()); + createItemsMenu(ui.scopesButton, scopes, m_model->allScopes()); + } + showSearchField(!noSearchField); updateProviders(); updateTimerInterval(true); +} -// no need to call this, it's done by updateProviders already -// m_model->restart(); +void QuickOpenWidget::createItemsMenu(QPushButton *btn, const QStringList &items, const QStringList& allTypes) { + if (!btn->menu()) { + btn->setMenu(new QMenu(this)); + } + btn->menu()->clear(); + + foreach( const QString &type, allTypes ) + { + QAction* action = new QAction(type, btn->menu()); + action->setCheckable(true); + action->setChecked(items.isEmpty() || items.contains( type )); + connect( action, &QAction::toggled, this, &QuickOpenWidget::updateProviders, Qt::QueuedConnection ); + btn->menu()->addAction(action); + } } void QuickOpenWidget::showStandardButtons(bool show)