File 2003-better-quick-open-window.patch of Package kate

diff --git a/apps/lib/katemainwindow.cpp b/apps/lib/katemainwindow.cpp
index 1e292c3084746f93eeba15e391d6437df4592017..180fdb4c0c3aaf67c158bcf87903a93ff9edd2d2 100644
--- a/apps/lib/katemainwindow.cpp
+++ b/apps/lib/katemainwindow.cpp
@@ -1614,8 +1614,6 @@ void KateMainWindow::slotQuickOpen()
      * show quick open and pass focus to it
      */
     KateQuickOpen *quickOpen = new KateQuickOpen(this);
-    centralWidget()->setFocusProxy(quickOpen);
-    quickOpen->raise();
     quickOpen->show();
 }
 
diff --git a/apps/lib/quickopen/katequickopen.cpp b/apps/lib/quickopen/katequickopen.cpp
index 6960ed52f3a00294f1fa14c215d06682a16ae41c..161ed9d3ba7554639958f12473630c570a97d57c 100644
--- a/apps/lib/quickopen/katequickopen.cpp
+++ b/apps/lib/quickopen/katequickopen.cpp
@@ -20,16 +20,18 @@
 
 #include <QCoreApplication>
 #include <QEvent>
+#include <QGraphicsEffect>
+#include <QLabel>
 #include <QPainter>
 #include <QPointer>
 #include <QSortFilterProxyModel>
+#include <QStatusBar>
 #include <QStyledItemDelegate>
 #include <QToolBar>
 #include <QTreeView>
 
 #include <drawing_utils.h>
 #include <kfts_fuzzy_match.h>
-#include <qgraphicseffect.h>
 
 class QuickOpenFilterProxyModel final : public QSortFilterProxyModel
 {
@@ -194,8 +196,7 @@ public:
         }
 
         QTextCharFormat fmt;
-        fmt.setForeground(options.palette.link().color());
-        fmt.setFontWeight(QFont::Bold);
+        fmt.setForeground(options.palette.link());
 
         const int nameLen = name.length();
         // space between name and path
@@ -215,15 +216,11 @@ public:
                 auto nameFormats = kfts::get_fuzzy_match_formats(m_filterString, name, 0, fmt);
                 formats.append(nameFormats);
             }
-            QTextCharFormat boldFmt;
-            boldFmt.setFontWeight(QFont::Bold);
-            boldFmt.setFontPointSize(options.font.pointSize() - 1);
-            pathFormats = kfts::get_fuzzy_match_formats(m_filterString, path, nameLen + space, boldFmt);
+            pathFormats = kfts::get_fuzzy_match_formats(m_filterString, path, nameLen + space, fmt);
         }
 
         QTextCharFormat gray;
-        gray.setForeground(Qt::gray);
-        gray.setFontPointSize(options.font.pointSize() - 1);
+        gray.setForeground(option.palette.placeholderText());
         formats.append({.start = nameLen + space, .length = static_cast<int>(path.length()), .format = gray});
         if (!pathFormats.isEmpty()) {
             formats.append(pathFormats);
@@ -232,14 +229,11 @@ public:
         painter->save();
 
         // paint background
-        if (option.state & QStyle::State_Selected) {
-            painter->fillRect(option.rect, option.palette.highlight());
-        } else {
-            painter->fillRect(option.rect, option.palette.base());
-        }
+        QStyle *style = option.widget->style();
+        style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
 
         options.text = QString(); // clear old text
-        options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
+        style->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
 
         // space for icon
         painter->translate(25, 0);
@@ -269,23 +263,17 @@ private:
 Q_DECLARE_METATYPE(QPointer<KTextEditor::Document>)
 
 KateQuickOpen::KateQuickOpen(KateMainWindow *mainWindow)
-    : QFrame(mainWindow)
+    : QFrame(mainWindow, Qt::Popup)
     , m_mainWindow(mainWindow)
 {
+    setAttribute(Qt::WA_DeleteOnClose);
+    setAttribute(Qt::WA_TranslucentBackground);
     setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
     setProperty("_breeze_force_frame", true);
-
-    QGraphicsDropShadowEffect *e = new QGraphicsDropShadowEffect(this);
-    e->setColor(palette().color(QPalette::Dark));
-    e->setOffset(0, 4);
-    e->setBlurRadius(8);
-    setGraphicsEffect(e);
-
-    // handle resizing
-    mainWindow->installEventFilter(this);
+    setProperty("_KDE_NET_WM_FORCE_SHADOW", true);
 
     // ensure the components have some proper frame
-    QVBoxLayout *layout = new QVBoxLayout();
+    QVBoxLayout *layout = new QVBoxLayout;
     layout->setSpacing(0);
     layout->setContentsMargins(QMargins());
     setLayout(layout);
@@ -295,104 +283,118 @@ KateQuickOpen::KateQuickOpen(KateMainWindow *mainWindow)
     m_inputLine->addAction(QIcon::fromTheme(QStringLiteral("search")), QLineEdit::LeadingPosition);
     m_inputLine->setFrame(false);
     m_inputLine->setTextMargins(QMargins() + style()->pixelMetric(QStyle::PM_ButtonMargin));
-    setFocusProxy(m_inputLine);
-
     layout->addWidget(m_inputLine);
+    setFocusProxy(m_inputLine);
 
     m_listView = new QTreeView(this);
-    layout->addWidget(m_listView, 1);
-    m_listView->setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags(Qt::TopEdge)));
+    m_listView->setFocusPolicy(Qt::NoFocus);
     m_listView->setTextElideMode(Qt::ElideLeft);
     m_listView->setUniformRowHeights(true);
+    m_listView->setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags(Qt::TopEdge)));
+    layout->addWidget(m_listView);
 
     m_model = new KateQuickOpenModel(this);
     m_styleDelegate = new QuickOpenStyleDelegate(this);
     m_listView->setItemDelegate(m_styleDelegate);
 
-    connect(m_inputLine, &QuickOpenLineEdit::returnPressed, this, &KateQuickOpen::slotReturnPressed);
     connect(m_inputLine, &QuickOpenLineEdit::listModeChanged, this, &KateQuickOpen::slotListModeChanged);
     connect(m_inputLine, &QuickOpenLineEdit::filterModeChanged, this, &KateQuickOpen::setFilterMode);
 
     connect(m_listView, &QTreeView::activated, this, &KateQuickOpen::slotReturnPressed);
-    connect(m_listView, &QTreeView::clicked, this, &KateQuickOpen::slotReturnPressed); // for single click
+    connect(m_listView, &QTreeView::clicked, m_listView, &QTreeView::activated); // for single click
 
     m_inputLine->installEventFilter(this);
-    m_listView->installEventFilter(this);
+
     m_listView->setHeaderHidden(true);
     m_listView->setRootIsDecorated(false);
     m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-    m_listView->setModel(m_model);
 
-    connect(m_inputLine, &QuickOpenLineEdit::textChanged, this, [this](const QString &text) {
-        // We init the proxy model when there is something to filter
-        bool didFilter = false;
-        if (!m_proxyModel) {
-            m_proxyModel = new QuickOpenFilterProxyModel(this);
-            m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
-            m_proxyModel->setFilterMode(m_inputLine->filterMode());
-            didFilter = m_proxyModel->setFilterText(text);
-            m_proxyModel->setSourceModel(m_model);
-            m_listView->setModel(m_proxyModel);
+    m_proxyModel = new QuickOpenFilterProxyModel(this);
+    m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+    m_proxyModel->setFilterMode(m_inputLine->filterMode());
+    m_proxyModel->setSourceModel(m_model);
+    m_listView->setModel(m_proxyModel);
+
+    QLabel *placeholderLabel = new QLabel;
+    placeholderLabel->setAlignment(Qt::AlignCenter);
+    placeholderLabel->setTextInteractionFlags(Qt::NoTextInteraction);
+    placeholderLabel->setWordWrap(true);
+    // To match the size of a level 2 Heading/KTitleWidget
+    QFont placeholderLabelFont = placeholderLabel->font();
+    placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3));
+    placeholderLabel->setFont(placeholderLabelFont);
+    // Match opacity of QML placeholder label component
+    QGraphicsOpacityEffect *opacityEffect = new QGraphicsOpacityEffect(placeholderLabel);
+    opacityEffect->setOpacity(0.5);
+    placeholderLabel->setGraphicsEffect(opacityEffect);
+
+    QHBoxLayout *placeholderLayout = new QHBoxLayout;
+    placeholderLayout->addWidget(placeholderLabel);
+    m_listView->setLayout(placeholderLayout);
+
+    connect(m_proxyModel, &QuickOpenFilterProxyModel::modelReset, this, [this, placeholderLabel]() {
+        if (m_proxyModel->rowCount() > 0) {
+            placeholderLabel->hide();
         } else {
-            didFilter = m_proxyModel->setFilterText(text);
+            if (m_model->rowCount() == 0) {
+                placeholderLabel->setText(i18n("No items to display"));
+            } else {
+                placeholderLabel->setText(i18n("No items matching the filter"));
+            }
+            placeholderLabel->show();
         }
-        if (didFilter) {
+    });
+
+    connect(m_inputLine, &QuickOpenLineEdit::textChanged, this, [this](const QString &text) {
+        if (m_proxyModel->setFilterText(text)) {
             m_styleDelegate->setFilterString(text);
             m_listView->viewport()->update();
             reselectFirst();
         }
     });
 
-    setHidden(true);
-
     setFilterMode();
     m_model->setListMode(m_inputLine->listMode());
 
     // fill stuff
-    updateState();
+    refreshModel();
 }
 
 bool KateQuickOpen::eventFilter(QObject *obj, QEvent *event)
 {
-    // catch key presses + shortcut overrides to allow to have ESC as application wide shortcut, too, see bug 409856
-    if (event->type() == QEvent::KeyPress || event->type() == QEvent::ShortcutOverride) {
-        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
-        if (obj == m_inputLine) {
-            const bool forward2list = (keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down) || (keyEvent->key() == Qt::Key_PageUp)
-                || (keyEvent->key() == Qt::Key_PageDown);
-            if (forward2list) {
-                QCoreApplication::sendEvent(m_listView, event);
-                return true;
-            }
-
-        } else if (obj == m_listView) {
-            const bool forward2input = (keyEvent->key() != Qt::Key_Up) && (keyEvent->key() != Qt::Key_Down) && (keyEvent->key() != Qt::Key_PageUp)
-                && (keyEvent->key() != Qt::Key_PageDown) && (keyEvent->key() != Qt::Key_Tab) && (keyEvent->key() != Qt::Key_Backtab);
-            if (forward2input) {
-                QCoreApplication::sendEvent(m_inputLine, event);
-                return true;
-            }
+    if (obj == m_inputLine && event->type() == QEvent::KeyPress) {
+        const QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
+        if (keyEvent->modifiers() & Qt::MetaModifier) {
+            return true;
         }
 
-        if (keyEvent->key() == Qt::Key_Escape) {
-            hide();
-            deleteLater();
+        switch (keyEvent->key()) {
+        case Qt::Key_Up:
+        case Qt::Key_Down:
+        case Qt::Key_PageUp:
+        case Qt::Key_PageDown:
+        case Qt::Key_Enter:
+        case Qt::Key_Return:
+            QCoreApplication::sendEvent(m_listView, event);
+            return true;
+        case Qt::Key_Escape:
+            close();
             return true;
+        default:
+            break;
         }
     }
 
-    if (event->type() == QEvent::FocusOut && !(m_inputLine->hasFocus() || m_listView->hasFocus())) {
-        hide();
-        deleteLater();
-        return true;
-    }
+    return QWidget::eventFilter(obj, event);
+}
 
-    // handle resizing
-    if (m_mainWindow == obj && event->type() == QEvent::Resize) {
-        updateViewGeometry();
-    }
+void KateQuickOpen::showEvent(QShowEvent *event)
+{
+    adjustSizeAndPosition();
 
-    return QWidget::eventFilter(obj, event);
+    QWidget::showEvent(event);
+
+    setFocus(Qt::PopupFocusReason);
 }
 
 void KateQuickOpen::reselectFirst()
@@ -407,14 +409,10 @@ void KateQuickOpen::reselectFirst()
     m_listView->setCurrentIndex(index);
 }
 
-void KateQuickOpen::updateState()
+void KateQuickOpen::refreshModel()
 {
     m_model->refresh(m_mainWindow);
     reselectFirst();
-
-    updateViewGeometry();
-    show();
-    setFocus();
 }
 
 void KateQuickOpen::slotReturnPressed()
@@ -427,15 +425,23 @@ void KateQuickOpen::slotReturnPressed()
 
     // either get view via document pointer or url
     const QModelIndex index = m_listView->currentIndex();
-    KTextEditor::View *view = nullptr;
+    if (!index.isValid()) {
+        return;
+    }
+
+    KTextEditor::View *view;
     if (auto doc = index.data(KateQuickOpenModel::Document).value<KTextEditor::Document *>()) {
         view = m_mainWindow->activateView(doc);
     } else {
         view = m_mainWindow->wrapper()->openUrl(index.data(Qt::UserRole).toUrl());
     }
 
+    if (!view) {
+        return;
+    }
+
     const auto strs = m_inputLine->text().split(QLatin1Char(':'));
-    if (view && strs.count() > 1) {
+    if (strs.count() > 1) {
         // helper to convert String => Number
         auto stringToInt = [](const QString &s) {
             bool ok = false;
@@ -460,22 +466,19 @@ void KateQuickOpen::slotReturnPressed()
         }
     }
 
-    hide();
-    deleteLater();
+    close();
 
     m_mainWindow->slotWindowActivated();
 
     // store the new position in location history
-    if (view) {
-        vm->addPositionToHistory(view->document()->url(), view->cursorPosition());
-    }
+    vm->addPositionToHistory(view->document()->url(), view->cursorPosition());
 }
 
 void KateQuickOpen::slotListModeChanged(KateQuickOpenModel::List mode)
 {
     m_model->setListMode(mode);
     // this changes things again, needs refresh, let's go all the way
-    updateState();
+    refreshModel();
 }
 
 void KateQuickOpen::setFilterMode()
@@ -489,7 +492,7 @@ void KateQuickOpen::setFilterMode()
     m_listView->viewport()->update();
 }
 
-static QRect getQuickOpenBoundingRect(QMainWindow *mainWindow)
+static QRect getQuickOpenBoundingRect(KateMainWindow *mainWindow)
 {
     QRect boundingRect = mainWindow->contentsRect();
 
@@ -500,6 +503,13 @@ static QRect getQuickOpenBoundingRect(QMainWindow *mainWindow)
         }
     }
 
+    // exclude the status bar from the bounding rect
+    if (const QStatusBar *statusBar = mainWindow->findChild<QStatusBar *>()) {
+        if (!statusBar->isHidden()) {
+            boundingRect.setBottom(boundingRect.bottom() - statusBar->height());
+        }
+    }
+
     // exclude any undocked toolbar from the bounding rect
     const QList<QToolBar *> toolBars = mainWindow->findChildren<QToolBar *>();
     for (QToolBar *toolBar : toolBars) {
@@ -507,15 +517,28 @@ static QRect getQuickOpenBoundingRect(QMainWindow *mainWindow)
             continue;
         }
 
-        if (mainWindow->toolBarArea(toolBar) == Qt::TopToolBarArea) {
+        switch (mainWindow->toolBarArea(toolBar)) {
+        case Qt::TopToolBarArea:
             boundingRect.setTop(std::max(boundingRect.top(), toolBar->geometry().bottom()));
+            break;
+        case Qt::RightToolBarArea:
+            boundingRect.setRight(std::min(boundingRect.right(), toolBar->geometry().left()));
+            break;
+        case Qt::BottomToolBarArea:
+            boundingRect.setBottom(std::min(boundingRect.bottom(), toolBar->geometry().top()));
+            break;
+        case Qt::LeftToolBarArea:
+            boundingRect.setLeft(std::max(boundingRect.left(), toolBar->geometry().right()));
+            break;
+        default:
+            break;
         }
     }
 
-    return boundingRect;
+    return boundingRect.translated(mainWindow->geometry().topLeft());
 }
 
-void KateQuickOpen::updateViewGeometry()
+void KateQuickOpen::adjustSizeAndPosition()
 {
     const QRect boundingRect = getQuickOpenBoundingRect(m_mainWindow);
 
@@ -527,15 +550,13 @@ void KateQuickOpen::updateViewGeometry()
     const int maxHeight = boundingRect.height();
     const int preferredHeight = maxHeight / 2;
 
-    const QSize size{std::min(maxWidth, std::max(preferredWidth, minWidth)), std::min(maxHeight, std::max(preferredHeight, minHeight))};
+    const QSize size(std::min(maxWidth, std::max(preferredWidth, minWidth)), std::min(maxHeight, std::max(preferredHeight, minHeight)));
 
-    // resize() doesn't work here, so use setFixedSize() instead
-    setFixedSize(size);
+    resize(size);
 
     // set the position to the top-center of the parent
     // just below the menubar/toolbar (if any)
-    const QPoint position{boundingRect.center().x() - size.width() / 2, boundingRect.y() + 6};
-    move(position);
+    move(boundingRect.center().x() - size.width() / 2, boundingRect.y() + 6);
 }
 
 #include "moc_katequickopen.cpp"
diff --git a/apps/lib/quickopen/katequickopen.h b/apps/lib/quickopen/katequickopen.h
index 592b3c81d0b0e79ae405473675689ef8755263c2..c800b7a678484ed808c3ab3d1f74c65764d6f0ca 100644
--- a/apps/lib/quickopen/katequickopen.h
+++ b/apps/lib/quickopen/katequickopen.h
@@ -27,14 +27,14 @@ public:
     KateQuickOpen(KateMainWindow *mainWindow);
 
     /**
-     * update state
+     * refresh model
      * will fill model with current open documents, project documents, ...
      */
-    void updateState();
-    void updateViewGeometry();
+    void refreshModel();
 
 protected:
     bool eventFilter(QObject *obj, QEvent *event) override;
+    void showEvent(QShowEvent *event) override;
 
 private:
     void reselectFirst();
@@ -50,6 +50,8 @@ private:
     void setFilterMode();
 
 private:
+    void adjustSizeAndPosition();
+
     KateMainWindow *m_mainWindow;
     QTreeView *m_listView;
     QuickOpenLineEdit *m_inputLine;
diff --git a/apps/lib/quickopen/katequickopenmodel.cpp b/apps/lib/quickopen/katequickopenmodel.cpp
index 83d17848d1080b2369bb3a9bca3e26fdee43050f..87d49bf9f7537b969e808bea2763d8bacf7c235f 100644
--- a/apps/lib/quickopen/katequickopenmodel.cpp
+++ b/apps/lib/quickopen/katequickopenmodel.cpp
@@ -54,14 +54,6 @@ QVariant KateQuickOpenModel::data(const QModelIndex &idx, int role) const
         const auto &path = entry.filePath;
         return path.startsWith(m_projectBase) ? path.mid(m_projectBase.size()) : path;
     }
-    case Qt::FontRole: {
-        if (entry.document) {
-            QFont font;
-            font.setBold(true);
-            return font;
-        }
-        return {};
-    }
     case Qt::DecorationRole:
         return QIcon::fromTheme(QMimeDatabase().mimeTypeForFile(entry.fileName, QMimeDatabase::MatchExtension).iconName());
     case Qt::UserRole:
openSUSE Build Service is sponsored by