File indexing completed on 2024-12-01 04:36:37
0001 /* 0002 SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "completionlistview.h" 0008 0009 #include <QApplication> 0010 #include <QDebug> 0011 #include <QKeyEvent> 0012 #include <QScreen> 0013 #include <QScrollBar> 0014 0015 CompletionListView::CompletionListView() 0016 : QListView() 0017 { 0018 setUniformItemSizes(true); 0019 setWindowFlag(Qt::Popup); 0020 hide(); 0021 0022 connect(this, &QListView::clicked, this, &CompletionListView::complete); 0023 } 0024 0025 CompletionListView::~CompletionListView() = default; 0026 0027 void CompletionListView::setTextWidget(QWidget *textWidget) 0028 { 0029 mTextWidget = textWidget; 0030 const Qt::FocusPolicy origPolicy = textWidget->focusPolicy(); 0031 setFocusPolicy(Qt::NoFocus); 0032 textWidget->setFocusPolicy(origPolicy); 0033 setFocusProxy(textWidget); 0034 } 0035 0036 // Typically called with one of the 3 models returned by InputTextManager: 0037 // inputCompleterModel() for users and channels, emojiCompleterModel(), or commandModel() 0038 void CompletionListView::setModel(QAbstractItemModel *model) 0039 { 0040 QAbstractItemModel *oldModel = QListView::model(); 0041 if (model != oldModel) { 0042 if (oldModel) { 0043 disconnect(oldModel, &QAbstractItemModel::rowsInserted, this, &CompletionListView::slotCompletionAvailable); 0044 disconnect(oldModel, &QAbstractItemModel::rowsRemoved, this, &CompletionListView::slotCompletionAvailable); 0045 } 0046 connect(model, &QAbstractItemModel::rowsInserted, this, &CompletionListView::slotCompletionAvailable); 0047 connect(model, &QAbstractItemModel::rowsRemoved, this, &CompletionListView::slotCompletionAvailable); 0048 QListView::setModel(model); 0049 } 0050 } 0051 0052 void CompletionListView::keyPressEvent(QKeyEvent *event) 0053 { 0054 const int key = event->key(); 0055 if (key == Qt::Key_Escape) { 0056 hide(); 0057 event->accept(); 0058 return; 0059 } else if (key == Qt::Key_Return || key == Qt::Key_Enter) { 0060 Q_EMIT complete(currentIndex()); 0061 event->accept(); 0062 return; 0063 } else if (key != Qt::Key_Down && key != Qt::Key_Up && key != Qt::Key_PageDown && key != Qt::Key_PageUp && key != Qt::Key_Home && key != Qt::Key_End 0064 && key != Qt::Key_Left && key != Qt::Key_Right) { 0065 // send keypresses to the linedit 0066 qApp->sendEvent(mTextWidget, event); 0067 return; 0068 } 0069 0070 QListView::keyPressEvent(event); 0071 } 0072 0073 bool CompletionListView::event(QEvent *event) 0074 { 0075 // QAbstractScrollArea::event doesn't call QWidget::mousePressEvent, which leads to 0076 // https://bugs.kde.org/show_bug.cgi?id=434473 when making the scrollarea a Qt::Popup 0077 if (event->type() == QEvent::MouseButtonPress) { 0078 QWidget::mousePressEvent(static_cast<QMouseEvent *>(event)); 0079 } 0080 return QListView::event(event); 0081 } 0082 0083 void CompletionListView::slotCompletionAvailable() 0084 { 0085 const int rowCount = model()->rowCount(); 0086 if (rowCount == 0) { 0087 hide(); 0088 return; 0089 } 0090 const int maxVisibleItems = 15; 0091 0092 // Not entirely unlike QCompletionPrivate::showPopup 0093 const QRect screenRect = mTextWidget->screen()->availableGeometry(); 0094 int h = (sizeHintForRow(0) * qMin(maxVisibleItems, rowCount) + 3) + 3; 0095 QScrollBar *hsb = horizontalScrollBar(); 0096 if (hsb && hsb->isVisible()) { 0097 h += horizontalScrollBar()->sizeHint().height(); 0098 } 0099 0100 const int rh = mTextWidget->height(); 0101 QPoint pos = mTextWidget->mapToGlobal(QPoint(0, rh - 2)); 0102 int w = mTextWidget->width(); 0103 0104 if (w > screenRect.width()) { 0105 w = screenRect.width(); 0106 } 0107 if ((pos.x() + w) > (screenRect.x() + screenRect.width())) { 0108 pos.setX(screenRect.x() + screenRect.width() - w); 0109 } 0110 if (pos.x() < screenRect.x()) { 0111 pos.setX(screenRect.x()); 0112 } 0113 0114 int top = pos.y() - rh - screenRect.top() + 2; 0115 int bottom = screenRect.bottom() - pos.y(); 0116 h = qMax(h, minimumHeight()); 0117 if (h > bottom) { 0118 h = qMin(qMax(top, bottom), h); 0119 0120 if (top > bottom) { 0121 pos.setY(pos.y() - h - rh + 2); 0122 } 0123 } 0124 0125 // qDebug() << "showing at" << pos << "size" << w << "x" << h; 0126 setGeometry(pos.x(), pos.y(), w, h); 0127 0128 if (!isVisible()) { 0129 if (!currentIndex().isValid()) { 0130 setCurrentIndex(model()->index(0, 0)); 0131 } 0132 show(); 0133 } 0134 } 0135 0136 #include "moc_completionlistview.cpp"