diff --git a/documentation/documentationfindwidget.h b/documentation/documentationfindwidget.h --- a/documentation/documentationfindwidget.h +++ b/documentation/documentationfindwidget.h @@ -42,15 +42,34 @@ explicit DocumentationFindWidget(QWidget* parent = nullptr); virtual ~DocumentationFindWidget(); - void showEvent ( QShowEvent* ) override; - - private Q_SLOTS: + void hideEvent(QHideEvent* event) override; + + public Q_SLOTS: void startSearch(); + + private Q_SLOTS: void searchNext(); void searchPrevious(); - + void emitDataChanged(); + Q_SIGNALS: - void newSearch(const QString& text, KDevelop::DocumentationFindWidget::FindOptions); + /** + * Emitted when the user requests a search + * @param text text to search in documentation + * @param options MatchCase being set or empty flags (Next/Previous unused here) + */ + void searchRequested(const QString& text, KDevelop::DocumentationFindWidget::FindOptions options); + /** + * Emitted when the user edits the search field or changes the case-sensitivity checkbox. + * Allows documentation views capable of live searches to show live results while the user types. + * @param text current text in search text field + * @param options MatchCase being set or empty flags (Next/Previous unused here) + */ + void searchDataChanged(const QString& text, KDevelop::DocumentationFindWidget::FindOptions options); + /** + * Emitted when the search tool view is closed, so no more search hits should be displayed. + */ + void searchFinished(); private: Ui::FindWidget* m_ui; diff --git a/documentation/documentationfindwidget.cpp b/documentation/documentationfindwidget.cpp --- a/documentation/documentationfindwidget.cpp +++ b/documentation/documentationfindwidget.cpp @@ -29,16 +29,17 @@ m_ui = new Ui::FindWidget; m_ui->setupUi(this); - m_ui->hideButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); - m_ui->nextButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down-search"))); - m_ui->previousButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up-search"))); - + connect(m_ui->findText, &QLineEdit::textEdited, + this, &DocumentationFindWidget::emitDataChanged); + connect(m_ui->matchCase, &QAbstractButton::toggled, + this, &DocumentationFindWidget::emitDataChanged); connect(m_ui->findText, &QLineEdit::returnPressed, this, &DocumentationFindWidget::searchNext); - connect(m_ui->nextButton, &QPushButton::clicked, + connect(m_ui->nextButton, &QToolButton::clicked, this, &DocumentationFindWidget::searchNext); - connect(m_ui->previousButton, &QPushButton::clicked, + connect(m_ui->previousButton, &QToolButton::clicked, this, &DocumentationFindWidget::searchPrevious); + // TODO: disable next/previous buttons if no (more) search hits, color coding in text field } DocumentationFindWidget::~DocumentationFindWidget() @@ -49,19 +50,19 @@ void KDevelop::DocumentationFindWidget::searchNext() { FindOptions opts=Next; - if(m_ui->matchCase->checkState()==Qt::Checked) + if(m_ui->matchCase->isChecked()) opts |= MatchCase; - emit newSearch(m_ui->findText->text(), opts); + emit searchRequested(m_ui->findText->text(), opts); } void KDevelop::DocumentationFindWidget::searchPrevious() { FindOptions opts=Previous; - if(m_ui->matchCase->checkState()==Qt::Checked) + if(m_ui->matchCase->isChecked()) opts |= MatchCase; - emit newSearch(m_ui->findText->text(), opts); + emit searchRequested(m_ui->findText->text(), opts); } void KDevelop::DocumentationFindWidget::startSearch() @@ -71,7 +72,17 @@ m_ui->findText->setFocus(); } -void KDevelop::DocumentationFindWidget::showEvent(QShowEvent* e) +void DocumentationFindWidget::emitDataChanged() +{ + FindOptions opts; + if (m_ui->matchCase->isChecked()) + opts |= MatchCase; + + emit searchDataChanged(m_ui->findText->text(), opts); +} + +void KDevelop::DocumentationFindWidget::hideEvent(QHideEvent* event) { - QWidget::showEvent(e); + emit searchFinished(); + QWidget::hideEvent(event); } diff --git a/documentation/documentationfindwidget.ui b/documentation/documentationfindwidget.ui --- a/documentation/documentationfindwidget.ui +++ b/documentation/documentationfindwidget.ui @@ -17,10 +17,20 @@ + + 0 + + + 0 + + + 0 + - - ... + + + .. true @@ -36,29 +46,56 @@ + + true + true - - - Next + + + Jump to next match + + + + .. + + + true - - - Previous + + + Jump to previous match + + + + .. + + + true - - - Match case + + + Match case sensitive + + + + .. + + + true + + + true diff --git a/documentation/documentationview.h b/documentation/documentationview.h --- a/documentation/documentationview.h +++ b/documentation/documentationview.h @@ -34,7 +34,6 @@ class QLineEdit; class ProvidersModel; class QComboBox; -class KToolBar; class KDEVPLATFORMDOCUMENTATION_EXPORT DocumentationView : public QWidget { @@ -44,6 +43,9 @@ void showDocumentation(const KDevelop::IDocumentation::Ptr& doc); +public: + QList contextMenuActions() const; + public Q_SLOTS: void initialize(); @@ -57,13 +59,14 @@ void showHome(); private: + void setupActions(); void updateView(); void returnPressed(); - KToolBar* mActions; QAction* mForward; QAction* mBack; QAction* mFind; + QAction* mHomeAction; QLineEdit* mIdentifiers; QList< KDevelop::IDocumentation::Ptr > mHistory; QList< KDevelop::IDocumentation::Ptr >::iterator mCurrent; diff --git a/documentation/documentationview.cpp b/documentation/documentationview.cpp --- a/documentation/documentationview.cpp +++ b/documentation/documentationview.cpp @@ -19,16 +19,16 @@ #include "documentationview.h" +#include #include #include #include #include #include -#include #include #include +#include -#include #include #include @@ -51,26 +51,54 @@ layout()->setMargin(0); layout()->setSpacing(0); - //TODO: clean this up, simply use addAction as that will create a toolbar automatically + mFindDoc = new DocumentationFindWidget; + mFindDoc->hide(); + + // insert placeholder widget at location of doc view + layout()->addWidget(new QWidget(this)); + layout()->addWidget(mFindDoc); + + setupActions(); + + mCurrent = mHistory.end(); + + setFocusProxy(mIdentifiers); + + QMetaObject::invokeMethod(this, "initialize", Qt::QueuedConnection); +} + +void DocumentationView::setupActions() +{ // use custom QAction's with createWidget for mProviders and mIdentifiers - mActions = new KToolBar(this); // set window title so the QAction from QToolBar::toggleViewAction gets a proper name set - mActions->setWindowTitle(i18n("Documentation Tool Bar")); - mActions->setToolButtonStyle(Qt::ToolButtonIconOnly); - int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); - mActions->setIconSize(QSize(iconSize, iconSize)); + mBack = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Back"), this); + mBack->setEnabled(false); + connect(mBack, &QAction::triggered, this, &DocumentationView::browseBack); + addAction(mBack); - mFindDoc = new DocumentationFindWidget; - mFindDoc->hide(); + mForward = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Forward"), this); + mForward->setEnabled(false); + connect(mForward, &QAction::triggered, this, &DocumentationView::browseForward); + addAction(mForward); + + mFind = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Find"), this); + connect(mFind, &QAction::triggered, mFindDoc, &DocumentationFindWidget::startSearch); + addAction(mFind); + + QAction* separator = new QAction(this); + separator->setSeparator(true); + addAction(separator); + + mHomeAction = new QAction(QIcon::fromTheme(QStringLiteral("go-home")), i18n("Home"), this); + connect(mHomeAction, &QAction::triggered, this, &DocumentationView::showHome); + addAction(mHomeAction); - mBack = mActions->addAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Back")); - mForward = mActions->addAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Forward")); - mFind = mActions->addAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Find"), mFindDoc, SLOT(startSearch())); - mActions->addSeparator(); - mActions->addAction(QIcon::fromTheme(QStringLiteral("go-home")), i18n("Home"), this, SLOT(showHome())); - mProviders = new QComboBox(mActions); + mProviders = new QComboBox(this); + auto provicersAction = new QWidgetAction(this); + provicersAction->setDefaultWidget(mProviders); + addAction(provicersAction); - mIdentifiers = new QLineEdit(mActions); + mIdentifiers = new QLineEdit(this); mIdentifiers->setClearButtonEnabled(true); mIdentifiers->setPlaceholderText(i18n("Search...")); mIdentifiers->setCompleter(new QCompleter(mIdentifiers)); @@ -82,24 +110,18 @@ connect(mIdentifiers->completer(), static_cast(&QCompleter::activated), this, &DocumentationView::changedSelection); connect(mIdentifiers, &QLineEdit::returnPressed, this, &DocumentationView::returnPressed); + auto identifiersAction = new QWidgetAction(this); + identifiersAction->setDefaultWidget(mIdentifiers); + addAction(identifiersAction); - QWidget::setTabOrder(mProviders, mIdentifiers); - mActions->addWidget(mProviders); - mActions->addWidget(mIdentifiers); - - mBack->setEnabled(false); - mForward->setEnabled(false); - connect(mBack, &QAction::triggered, this, &DocumentationView::browseBack); - connect(mForward, &QAction::triggered, this, &DocumentationView::browseForward); - mCurrent = mHistory.end(); - - layout()->addWidget(mActions); - layout()->addWidget(new QWidget(this)); - layout()->addWidget(mFindDoc); - - setFocusProxy(mIdentifiers); + auto closeFindBarShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); + closeFindBarShortcut->setContext(Qt::WidgetWithChildrenShortcut); + connect(closeFindBarShortcut, &QShortcut::activated, mFindDoc, &QWidget::hide); +} - QMetaObject::invokeMethod(this, "initialize", Qt::QueuedConnection); +QList DocumentationView::contextMenuActions() const +{ + return {mBack, mForward, mFind, mHomeAction}; // TODO: also show providers } void DocumentationView::initialize() @@ -228,7 +250,7 @@ mIdentifiers->setText((*mCurrent)->name()); mIdentifiers->completer()->setCompletionPrefix((*mCurrent)->name()); - QLayoutItem* lastview = layout()->takeAt(1); + QLayoutItem* lastview = layout()->takeAt(0); Q_ASSERT(lastview); if (lastview->widget()->parent() == this) { @@ -247,7 +269,7 @@ mFindDoc->hide(); } - QLayoutItem* findWidget = layout()->takeAt(1); + QLayoutItem* findWidget = layout()->takeAt(0); layout()->addWidget(w); layout()->addItem(findWidget); } diff --git a/documentation/standarddocumentationview.h b/documentation/standarddocumentationview.h --- a/documentation/standarddocumentationview.h +++ b/documentation/standarddocumentationview.h @@ -76,6 +76,8 @@ * Search for @p text in the documentation view. */ void search(const QString& text, KDevelop::DocumentationFindWidget::FindOptions options); + void searchLive(const QString& text, KDevelop::DocumentationFindWidget::FindOptions options); + void finishSearch(); /** * Updates the contents, in case it was initialized with a documentation instance, diff --git a/documentation/standarddocumentationview.cpp b/documentation/standarddocumentationview.cpp --- a/documentation/standarddocumentationview.cpp +++ b/documentation/standarddocumentationview.cpp @@ -111,12 +111,16 @@ : QWidget(parent) , d(new StandardDocumentationViewPrivate) { - setLayout(new QVBoxLayout(this)); + auto mainLayout = new QVBoxLayout(this); + mainLayout->setMargin(0); + setLayout(mainLayout); d->init(this); - layout()->addWidget(d->m_view); + mainLayout->addWidget(d->m_view); findWidget->setEnabled(true); - connect(findWidget, &DocumentationFindWidget::newSearch, this, &StandardDocumentationView::search); + connect(findWidget, &DocumentationFindWidget::searchRequested, this, &StandardDocumentationView::search); + connect(findWidget, &DocumentationFindWidget::searchDataChanged, this, &StandardDocumentationView::searchLive); + connect(findWidget, &DocumentationFindWidget::searchFinished, this, &StandardDocumentationView::finishSearch); #ifdef USE_QTWEBKIT QFont sansSerifFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); @@ -179,6 +183,36 @@ d->m_view->page()->findText(text, ff); } +void StandardDocumentationView::searchLive(const QString& text, DocumentationFindWidget::FindOptions options) +{ +#ifdef USE_QTWEBKIT + typedef QWebPage WebkitThing; +#else + typedef QWebEnginePage WebkitThing; +#endif + WebkitThing::FindFlags findFlags; + + if (options & DocumentationFindWidget::MatchCase) + findFlags |= WebkitThing::FindCaseSensitively; + + // calling with changed text with added or removed chars at end will result in current + // selection kept, if also matching new text + // behaviour on changed case sensitivity though is advancing to next match even if current + // would be still matching. as there is no control about currently shown match, nothing + // we can do about it. thankfully case sensitivity does not happen too often, so should + // not be too grave UX + // at least with webengine 5.9.1 there is a bug when switching from no-casesensitivy to + // casesensitivity, that global matches are not updated and the ones with non-matching casing + // still active. no workaround so far. + d->m_view->page()->findText(text, findFlags); +} + +void StandardDocumentationView::finishSearch() +{ + // passing emptry string to reset search, as told in API docs + d->m_view->page()->findText(QString()); +} + void StandardDocumentationView::initZoom(const QString& configSubGroup) { Q_ASSERT_X(!d->m_zoomController, "StandardDocumentationView::initZoom", "Can not initZoom a second time."); diff --git a/shell/documentationcontroller.cpp b/shell/documentationcontroller.cpp --- a/shell/documentationcontroller.cpp +++ b/shell/documentationcontroller.cpp @@ -102,6 +102,12 @@ Qt::DockWidgetArea defaultPosition() override { return Qt::RightDockWidgetArea; } QString id() const override { return QStringLiteral("org.kdevelop.DocumentationView"); } + QList contextMenuActions(QWidget* viewWidget) const override + { + auto documentationViewWidget = qobject_cast(viewWidget); + Q_ASSERT(documentationViewWidget); + return documentationViewWidget->contextMenuActions(); + } private: QScopedPointer m_providersModel;