aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Ziller <eike.ziller@qt.io>2017-06-16 10:24:26 +0200
committerEike Ziller <eike.ziller@qt.io>2017-06-23 13:38:07 +0000
commite133ee89286b289b047a53422caf4443c2d4881c (patch)
treef0c824f0e43547df7c15ae8fd17a986726a4e574
parent3a45d763ca0ca61d5e86a91dc3d141cd6ea45726 (diff)
Separate locator input widget from result list & popup
The input field may not care whether the result list is actually in a popup or not. Change-Id: Ia15f9a32441243de458e4e55d2daef6204b9dd59 Reviewed-by: David Schulz <david.schulz@qt.io>
-rw-r--r--src/plugins/coreplugin/locator/locator.cpp1
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.cpp311
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.h42
3 files changed, 240 insertions, 114 deletions
diff --git a/src/plugins/coreplugin/locator/locator.cpp b/src/plugins/coreplugin/locator/locator.cpp
index 74009035e8..3fd604bd7c 100644
--- a/src/plugins/coreplugin/locator/locator.cpp
+++ b/src/plugins/coreplugin/locator/locator.cpp
@@ -104,6 +104,7 @@ void Locator::initialize(CorePlugin *corePlugin, const QStringList &, QString *)
mtools->addAction(cmd);
auto locatorWidget = new LocatorWidget(this);
+ new LocatorPopup(locatorWidget, locatorWidget); // child of locatorWidget
StatusBarWidget *view = new StatusBarWidget;
view->setWidget(locatorWidget);
view->setContext(Context("LocatorWidget"));
diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp
index 84c1c73ce0..2b513367c5 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.cpp
+++ b/src/plugins/coreplugin/locator/locatorwidget.cpp
@@ -102,38 +102,16 @@ class CompletionList : public Utils::TreeView
public:
CompletionList(QWidget *parent = 0);
- void resize();
+ void setModel(QAbstractItemModel *model);
+
void resizeHeaders();
- QSize preferredSize() const { return m_preferredSize; }
- void focusOutEvent (QFocusEvent *event) {
- if (event->reason() == Qt::ActiveWindowFocusReason)
- hide();
- QTreeView::focusOutEvent(event);
- }
+ void next();
+ void previous();
- void next() {
- int index = currentIndex().row();
- ++index;
- if (index >= model()->rowCount(QModelIndex())) {
- // wrap
- index = 0;
- }
- setCurrentIndex(model()->index(index, 0));
- }
+ void showCurrentItemToolTip();
- void previous() {
- int index = currentIndex().row();
- --index;
- if (index < 0) {
- // wrap
- index = model()->rowCount(QModelIndex()) - 1;
- }
- setCurrentIndex(model()->index(index, 0));
- }
-
-private:
- QSize m_preferredSize;
+ void keyPressEvent(QKeyEvent *event);
};
// =========== LocatorModel ===========
@@ -239,46 +217,200 @@ CompletionList::CompletionList(QWidget *parent)
header()->setStretchLastSection(true);
// This is too slow when done on all results
//header()->setSectionResizeMode(QHeaderView::ResizeToContents);
- setWindowFlags(Qt::ToolTip);
if (Utils::HostOsInfo::isMacHost()) {
if (horizontalScrollBar())
horizontalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
if (verticalScrollBar())
verticalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
}
+ const QStyleOptionViewItem &option = viewOptions();
+ const QSize shint = itemDelegate()->sizeHint(option, QModelIndex());
+ setFixedHeight(shint.height() * 17 + frameWidth() * 2);
}
-void CompletionList::resize()
+void CompletionList::setModel(QAbstractItemModel *newModel)
{
- const QStyleOptionViewItem &option = viewOptions();
- const QSize shint = itemDelegate()->sizeHint(option, model()->index(0, 0));
- const QSize windowSize = ICore::mainWindow()->size();
+ if (model()) {
+ disconnect(model(), &QAbstractItemModel::columnsInserted,
+ this, &CompletionList::resizeHeaders);
+ }
+ QTreeView::setModel(newModel);
+ if (newModel) {
+ connect(newModel, &QAbstractItemModel::columnsInserted,
+ this, &CompletionList::resizeHeaders);
+ }
+}
- const int width = qMax(730, windowSize.width() * 2 / 3);
- m_preferredSize = QSize(width, shint.height() * 17 + frameWidth() * 2);
- QTreeView::resize(m_preferredSize);
- resizeHeaders();
+void LocatorPopup::resize()
+{
+ static const int MIN_WIDTH = 730;
+ const QSize windowSize = m_window ? m_window->size() : QSize(MIN_WIDTH, 0);
+
+ const int width = qMax(MIN_WIDTH, windowSize.width() * 2 / 3);
+ m_preferredSize = QSize(width, sizeHint().height());
+ QWidget::resize(m_preferredSize);
+ m_tree->resizeHeaders();
+}
+
+QSize LocatorPopup::preferredSize() const
+{
+ return m_preferredSize;
+}
+
+void LocatorPopup::updateWindow()
+{
+ QWidget *w = parentWidget() ? parentWidget()->window() : nullptr;
+ if (m_window != w) {
+ if (m_window)
+ m_window->removeEventFilter(this);
+ m_window = w;
+ if (m_window)
+ m_window->installEventFilter(this);
+ }
+}
+
+bool LocatorPopup::event(QEvent *event)
+{
+ if (event->type() == QEvent::ParentChange)
+ updateWindow();
+ return QWidget::event(event);
+}
+
+bool LocatorPopup::eventFilter(QObject *watched, QEvent *event)
+{
+ if (watched == m_window && event->type() == QEvent::Resize)
+ resize();
+ return QWidget::eventFilter(watched, event);
+}
+
+void LocatorPopup::showPopup()
+{
+ QTC_ASSERT(parentWidget(), return);
+ const QSize size = preferredSize();
+ const QRect rect(parentWidget()->mapToGlobal(QPoint(0, -size.height())), size);
+ setGeometry(rect);
+ show();
}
void CompletionList::resizeHeaders()
{
- header()->resizeSection(0, m_preferredSize.width() / 2);
+ header()->resizeSection(0, width() / 2);
header()->resizeSection(1, 0); // last section is auto resized because of stretchLastSection
}
+LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
+ : QWidget(parent),
+ m_tree(new CompletionList(this))
+{
+ setWindowFlags(Qt::ToolTip);
+
+ m_tree->setFrameStyle(QFrame::NoFrame);
+ m_tree->setModel(locatorWidget->model());
+
+ auto layout = new QVBoxLayout;
+ layout->setSizeConstraint(QLayout::SetMinimumSize);
+ setLayout(layout);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->setSpacing(0);
+ layout->addWidget(m_tree);
+
+ connect(locatorWidget, &LocatorWidget::parentChanged, this, &LocatorPopup::updateWindow);
+ connect(locatorWidget, &LocatorWidget::showPopup, this, &LocatorPopup::showPopup);
+ connect(locatorWidget, &LocatorWidget::hidePopup, this, &LocatorPopup::hide);
+ connect(locatorWidget, &LocatorWidget::selectRow, m_tree, [this](int row) {
+ m_tree->setCurrentIndex(m_tree->model()->index(row, 0));
+ });
+ connect(locatorWidget, &LocatorWidget::showCurrentItemToolTip,
+ m_tree, &CompletionList::showCurrentItemToolTip);
+ connect(locatorWidget, &LocatorWidget::handleKey, this, [this](QKeyEvent *keyEvent) {
+ QApplication::sendEvent(m_tree, keyEvent);
+ }, Qt::DirectConnection); // must be handled directly before event is deleted
+ connect(m_tree, &QAbstractItemView::activated, locatorWidget,
+ [this, locatorWidget](const QModelIndex &index) {
+ if (isVisible())
+ locatorWidget->scheduleAcceptEntry(index);
+ });
+
+ resize();
+}
+
+CompletionList *LocatorPopup::completionList() const
+{
+ return m_tree;
+}
+
+void LocatorPopup::focusOutEvent(QFocusEvent *event) {
+ if (event->reason() == Qt::ActiveWindowFocusReason)
+ hide();
+ QWidget::focusOutEvent(event);
+}
+
+void CompletionList::next() {
+ int index = currentIndex().row();
+ ++index;
+ if (index >= model()->rowCount(QModelIndex())) {
+ // wrap
+ index = 0;
+ }
+ setCurrentIndex(model()->index(index, 0));
+}
+
+void CompletionList::previous() {
+ int index = currentIndex().row();
+ --index;
+ if (index < 0) {
+ // wrap
+ index = model()->rowCount(QModelIndex()) - 1;
+ }
+ setCurrentIndex(model()->index(index, 0));
+}
+
+void CompletionList::showCurrentItemToolTip()
+{
+ QTC_ASSERT(model(), return);
+ if (!isVisible())
+ return;
+ const QModelIndex index = currentIndex();
+ if (index.isValid()) {
+ QToolTip::showText(mapToGlobal(pos() + visualRect(index).topRight()),
+ model()->data(index, Qt::ToolTipRole).toString());
+ }
+}
+
+void CompletionList::keyPressEvent(QKeyEvent *event)
+{
+ switch (event->key()) {
+ case Qt::Key_Tab:
+ case Qt::Key_Down:
+ next();
+ return;
+ case Qt::Key_Backtab:
+ case Qt::Key_Up:
+ previous();
+ return;
+ case Qt::Key_P:
+ case Qt::Key_N:
+ if (event->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (event->key() == Qt::Key_P)
+ previous();
+ else
+ next();
+ return;
+ }
+ break;
+ }
+ Utils::TreeView::keyPressEvent(event);
+}
+
// =========== LocatorWidget ===========
LocatorWidget::LocatorWidget(Locator *locator) :
m_locatorModel(new LocatorModel(this)),
- m_completionList(new CompletionList(this)),
m_filterMenu(new QMenu(this)),
m_refreshAction(new QAction(tr("Refresh"), this)),
m_configureAction(new QAction(ICore::msgShowOptionsDialog(), this)),
m_fileLineEdit(new Utils::FancyLineEdit)
{
- // Explicitly hide the completion list popup.
- m_completionList->hide();
-
setAttribute(Qt::WA_Hover);
setFocusProxy(m_fileLineEdit);
resize(200, 90);
@@ -306,11 +438,6 @@ LocatorWidget::LocatorWidget(Locator *locator) :
m_fileLineEdit->installEventFilter(this);
this->installEventFilter(this);
- m_completionList->setModel(m_locatorModel);
- m_completionList->resize();
- connect(m_locatorModel, &QAbstractItemModel::columnsInserted,
- m_completionList, &CompletionList::resizeHeaders);
-
m_filterMenu->addAction(m_refreshAction);
m_filterMenu->addAction(m_configureAction);
@@ -320,9 +447,7 @@ LocatorWidget::LocatorWidget(Locator *locator) :
locator, [locator]() { locator->refresh(); });
connect(m_configureAction, &QAction::triggered, this, &LocatorWidget::showConfigureDialog);
connect(m_fileLineEdit, &QLineEdit::textChanged,
- this, &LocatorWidget::showPopup);
- connect(m_completionList, &QAbstractItemView::activated,
- this, &LocatorWidget::scheduleAcceptEntry);
+ this, &LocatorWidget::showPopupDelayed);
m_entriesWatcher = new QFutureWatcher<LocatorFilterEntry>(this);
connect(m_entriesWatcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt,
@@ -398,34 +523,30 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch (keyEvent->key()) {
- case Qt::Key_Up:
- case Qt::Key_Down:
case Qt::Key_PageUp:
case Qt::Key_PageDown:
- showCompletionList();
- QApplication::sendEvent(m_completionList, event);
+ case Qt::Key_Down:
+ case Qt::Key_Tab:
+ case Qt::Key_Up:
+ case Qt::Key_Backtab:
+ emit showPopup();
+ emit handleKey(keyEvent);
return true;
case Qt::Key_Home:
case Qt::Key_End:
if (Utils::HostOsInfo::isMacHost()
!= (keyEvent->modifiers() == Qt::KeyboardModifiers(Qt::ControlModifier))) {
- showCompletionList();
- QApplication::sendEvent(m_completionList, event);
+ emit showPopup();
+ emit handleKey(keyEvent);
return true;
}
break;
case Qt::Key_Enter:
case Qt::Key_Return:
- QApplication::sendEvent(m_completionList, event);
+ emit handleKey(keyEvent);
return true;
case Qt::Key_Escape:
- m_completionList->hide();
- return true;
- case Qt::Key_Tab:
- m_completionList->next();
- return true;
- case Qt::Key_Backtab:
- m_completionList->previous();
+ emit hidePopup();
return true;
case Qt::Key_Alt:
if (keyEvent->modifiers() == Qt::AltModifier) {
@@ -435,12 +556,9 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
break;
case Qt::Key_P:
case Qt::Key_N:
- if (keyEvent->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier()))
- {
- if (keyEvent->key() == Qt::Key_P)
- m_completionList->previous();
- else
- m_completionList->next();
+ if (keyEvent->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ emit showPopup();
+ emit handleKey(keyEvent);
return true;
}
break;
@@ -451,35 +569,20 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (m_possibleToolTipRequest) {
m_possibleToolTipRequest = false;
- if (m_completionList->isVisible()
- && (keyEvent->key() == Qt::Key_Alt)
+ if ((keyEvent->key() == Qt::Key_Alt)
&& (keyEvent->modifiers() == Qt::NoModifier)) {
- const QModelIndex index = m_completionList->currentIndex();
- if (index.isValid()) {
- QToolTip::showText(m_completionList->pos() + m_completionList->visualRect(index).topRight(),
- m_locatorModel->data(index, Qt::ToolTipRole).toString());
- return true;
- }
+ emit showCurrentItemToolTip();
+ return true;
}
}
} else if (obj == m_fileLineEdit && event->type() == QEvent::FocusOut) {
- QFocusEvent *fev = static_cast<QFocusEvent *>(event);
- if (fev->reason() != Qt::ActiveWindowFocusReason || !m_completionList->isActiveWindow())
- m_completionList->hide();
+ emit hidePopup();
} else if (obj == m_fileLineEdit && event->type() == QEvent::FocusIn) {
QFocusEvent *fev = static_cast<QFocusEvent *>(event);
if (fev->reason() != Qt::ActiveWindowFocusReason)
showPopupNow();
- } else if (obj == m_window && event->type() == QEvent::Resize) {
- m_completionList->resize();
} else if (obj == this && event->type() == QEvent::ParentChange) {
- if (m_window != window()) {
- if (m_window)
- m_window->removeEventFilter(this);
- m_window = window();
- if (m_window)
- m_window->installEventFilter(this);
- }
+ emit parentChanged();
} else if (obj == this && event->type() == QEvent::ShortcutOverride) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
switch (ke->key()) {
@@ -508,16 +611,7 @@ void LocatorWidget::setFocusToCurrentMode()
ModeManager::setFocusToCurrentMode();
}
-void LocatorWidget::showCompletionList()
-{
- const int border = m_completionList->frameWidth();
- const QSize size = m_completionList->preferredSize();
- const QRect rect(mapToGlobal(QPoint(-border, -size.height() - border)), size);
- m_completionList->setGeometry(rect);
- m_completionList->show();
-}
-
-void LocatorWidget::showPopup()
+void LocatorWidget::showPopupDelayed()
{
m_updateRequested = true;
m_showPopupTimer.start();
@@ -527,7 +621,7 @@ void LocatorWidget::showPopupNow()
{
m_showPopupTimer.stop();
updateCompletionList(m_fileLineEdit->text());
- showCompletionList();
+ emit showPopup();
}
QList<ILocatorFilter *> LocatorWidget::filtersFor(const QString &text, QString &searchText)
@@ -633,9 +727,7 @@ void LocatorWidget::scheduleAcceptEntry(const QModelIndex &index)
void LocatorWidget::acceptEntry(int row)
{
- if (!m_completionList->isVisible())
- return;
- if (row >= m_locatorModel->rowCount())
+ if (row < 0 || row >= m_locatorModel->rowCount())
return;
const QModelIndex index = m_locatorModel->index(row, 0);
if (!index.isValid())
@@ -647,7 +739,7 @@ void LocatorWidget::acceptEntry(int row)
int selectionLength = 0;
entry.filter->accept(entry, &newText, &selectionStart, &selectionLength);
if (newText.isEmpty()) {
- m_completionList->hide();
+ emit hidePopup();
m_fileLineEdit->clearFocus();
} else {
showText(newText, selectionStart, selectionLength);
@@ -660,7 +752,7 @@ void LocatorWidget::showText(const QString &text, int selectionStart, int select
m_fileLineEdit->setText(text);
m_fileLineEdit->setFocus();
showPopupNow();
- ICore::raiseWindow(m_window);
+ ICore::raiseWindow(window());
if (selectionStart >= 0) {
m_fileLineEdit->setSelection(selectionStart, selectionLength);
@@ -676,6 +768,11 @@ QString LocatorWidget::currentText() const
return m_fileLineEdit->text();
}
+QAbstractItemModel *LocatorWidget::model() const
+{
+ return m_locatorModel;
+}
+
void LocatorWidget::showConfigureDialog()
{
ICore::showOptionsDialog(Constants::FILTER_OPTIONS_PAGE);
@@ -693,7 +790,7 @@ void LocatorWidget::addSearchResults(int firstIndex, int endIndex)
entries.append(m_entriesWatcher->resultAt(i));
m_locatorModel->addEntries(entries);
if (selectFirst) {
- m_completionList->setCurrentIndex(m_locatorModel->index(0, 0));
+ emit selectRow(0);
if (m_rowRequestedForAccept >= 0)
m_rowRequestedForAccept = 0;
}
diff --git a/src/plugins/coreplugin/locator/locatorwidget.h b/src/plugins/coreplugin/locator/locatorwidget.h
index 8a053e135f..29fa810ea9 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.h
+++ b/src/plugins/coreplugin/locator/locatorwidget.h
@@ -54,33 +54,40 @@ class LocatorWidget
public:
explicit LocatorWidget(Locator *locator);
- void updateFilterList();
-
void showText(const QString &text, int selectionStart = -1, int selectionLength = 0);
QString currentText() const;
+ QAbstractItemModel *model() const;
void updatePlaceholderText(Command *command);
-private:
+ void scheduleAcceptEntry(const QModelIndex &index);
+
+signals:
+ void showCurrentItemToolTip();
+ void hidePopup();
+ void selectRow(int row);
+ void handleKey(QKeyEvent *keyEvent); // only use with DirectConnection, event is deleted
+ void parentChanged();
void showPopup();
+
+private:
+ void showPopupDelayed();
void showPopupNow();
void acceptEntry(int row);
void showConfigureDialog();
void addSearchResults(int firstIndex, int endIndex);
void handleSearchFinished();
- void scheduleAcceptEntry(const QModelIndex &index);
void setFocusToCurrentMode();
+ void updateFilterList();
bool eventFilter(QObject *obj, QEvent *event);
- void showCompletionList();
void updateCompletionList(const QString &text);
QList<ILocatorFilter*> filtersFor(const QString &text, QString &searchText);
void setProgressIndicatorVisible(bool visible);
LocatorModel *m_locatorModel;
- CompletionList *m_completionList;
QMenu *m_filterMenu;
QAction *m_refreshAction;
QAction *m_configureAction;
@@ -93,9 +100,30 @@ private:
bool m_possibleToolTipRequest = false;
int m_rowRequestedForAccept = -1;
QWidget *m_progressIndicator;
- QPointer<QWidget> m_window;
QTimer m_showProgressTimer;
};
+class LocatorPopup : public QWidget
+{
+public:
+ LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent = 0);
+
+ CompletionList *completionList() const;
+
+ void focusOutEvent (QFocusEvent *event) override;
+ void resize();
+ QSize preferredSize() const;
+ bool event(QEvent *event) override;
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
+private:
+ void showPopup();
+
+ CompletionList *m_tree;
+ QSize m_preferredSize;
+ QPointer<QWidget> m_window;
+ void updateWindow();
+};
+
} // namespace Internal
} // namespace Core