File indexing completed on 2025-01-19 13:25:36
0001 /* 0002 SPDX-FileCopyrightText: 2022-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "messagelistviewbase.h" 0008 #include "model/messagesmodel.h" 0009 #include "room/plugins/plugintext.h" 0010 #include "room/plugins/plugintextinterface.h" 0011 #include "room/textpluginmanager.h" 0012 #include <QAbstractItemModel> 0013 #include <QApplication> 0014 #include <QClipboard> 0015 #include <QMouseEvent> 0016 #include <QScrollBar> 0017 0018 MessageListViewBase::MessageListViewBase(QWidget *parent) 0019 : QListView(parent) 0020 { 0021 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) 0022 setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::BottomEdge})); 0023 #endif 0024 setSelectionMode(QAbstractItemView::NoSelection); 0025 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0026 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); // nicer in case of huge messages 0027 setWordWrap(true); // so that the delegate sizeHint is called again when the width changes 0028 // only the lineedit takes focus 0029 setFocusPolicy(Qt::NoFocus); 0030 scrollToBottom(); 0031 setMouseTracking(true); 0032 0033 const QVector<PluginText *> plugins = TextPluginManager::self()->pluginsList(); 0034 for (PluginText *plugin : plugins) { 0035 connect(plugin, &PluginText::errorMessage, this, &MessageListViewBase::errorMessage); 0036 connect(plugin, &PluginText::successMessage, this, &MessageListViewBase::successMessage); 0037 mPluginTextInterface.append(plugin->createInterface(this)); 0038 } 0039 } 0040 0041 MessageListViewBase::~MessageListViewBase() 0042 { 0043 qDeleteAll(mPluginTextInterface); 0044 } 0045 0046 void MessageListViewBase::resizeEvent(QResizeEvent *ev) 0047 { 0048 QListView::resizeEvent(ev); 0049 0050 // Fix not being really at bottom when the view gets reduced by the header widget becoming taller 0051 checkIfAtBottom(); 0052 maybeScrollToBottom(); // this forces a layout in QAIV, which then changes the vbar max value 0053 updateVerticalPageStep(); 0054 Q_EMIT needToClearSizeHintCache(); 0055 } 0056 0057 void MessageListViewBase::checkIfAtBottom() 0058 { 0059 auto *vbar = verticalScrollBar(); 0060 mAtBottom = vbar->value() == vbar->maximum(); 0061 } 0062 0063 void MessageListViewBase::maybeScrollToBottom() 0064 { 0065 if (mAtBottom) { 0066 scrollToBottom(); 0067 } 0068 } 0069 0070 void MessageListViewBase::updateVerticalPageStep() 0071 { 0072 verticalScrollBar()->setPageStep(viewport()->height() * 3 / 4); 0073 } 0074 0075 void MessageListViewBase::handleMouseEvent(QMouseEvent *event) 0076 { 0077 const QPersistentModelIndex index = indexAt(event->pos()); 0078 if (index.isValid()) { 0079 // When the cursor hovers another message, hide/show the reaction icon accordingly 0080 if (mCurrentIndex != index) { 0081 if (mCurrentIndex.isValid()) { 0082 auto lastModel = const_cast<QAbstractItemModel *>(mCurrentIndex.model()); 0083 lastModel->setData(mCurrentIndex, false, MessagesModel::HoverHighLight); 0084 } 0085 mCurrentIndex = index; 0086 auto model = const_cast<QAbstractItemModel *>(mCurrentIndex.model()); 0087 model->setData(mCurrentIndex, true, MessagesModel::HoverHighLight); 0088 } 0089 0090 QStyleOptionViewItem options = listViewOptions(); 0091 options.rect = visualRect(mCurrentIndex); 0092 if (mouseEvent(event, options, mCurrentIndex)) { 0093 update(mCurrentIndex); 0094 } 0095 } 0096 } 0097 0098 void MessageListViewBase::mouseReleaseEvent(QMouseEvent *event) 0099 { 0100 handleMouseEvent(event); 0101 } 0102 0103 void MessageListViewBase::mouseDoubleClickEvent(QMouseEvent *event) 0104 { 0105 handleMouseEvent(event); 0106 } 0107 0108 void MessageListViewBase::mousePressEvent(QMouseEvent *event) 0109 { 0110 mPressedPosition = event->pos(); 0111 handleMouseEvent(event); 0112 } 0113 0114 void MessageListViewBase::mouseMoveEvent(QMouseEvent *event) 0115 { 0116 // Drag support 0117 const int distance = (event->pos() - mPressedPosition).manhattanLength(); 0118 if (distance > QApplication::startDragDistance()) { 0119 mPressedPosition = {}; 0120 const QPersistentModelIndex index = indexAt(event->pos()); 0121 if (index.isValid()) { 0122 QStyleOptionViewItem options = listViewOptions(); 0123 options.rect = visualRect(index); 0124 if (maybeStartDrag(event, options, index)) { 0125 return; 0126 } 0127 } 0128 } 0129 handleMouseEvent(event); 0130 } 0131 0132 void MessageListViewBase::leaveEvent(QEvent *event) 0133 { 0134 if (mCurrentIndex.isValid()) { 0135 auto lastModel = const_cast<QAbstractItemModel *>(mCurrentIndex.model()); 0136 lastModel->setData(mCurrentIndex, false, MessagesModel::HoverHighLight); 0137 mCurrentIndex = QPersistentModelIndex(); 0138 } 0139 QListView::leaveEvent(event); 0140 } 0141 0142 QStyleOptionViewItem MessageListViewBase::listViewOptions() const 0143 { 0144 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0145 return QListView::viewOptions(); 0146 #else 0147 QStyleOptionViewItem option; 0148 initViewItemOption(&option); 0149 return option; 0150 #endif 0151 } 0152 0153 bool MessageListViewBase::maybeStartDrag(QMouseEvent *event, const QStyleOptionViewItem &option, const QModelIndex &index) 0154 { 0155 Q_UNUSED(event); 0156 Q_UNUSED(option); 0157 Q_UNUSED(index); 0158 return false; 0159 } 0160 0161 bool MessageListViewBase::mouseEvent(QMouseEvent *event, const QStyleOptionViewItem &option, const QModelIndex &index) 0162 { 0163 Q_UNUSED(event); 0164 Q_UNUSED(option); 0165 Q_UNUSED(index); 0166 return false; 0167 } 0168 0169 void MessageListViewBase::addTextPlugins(QMenu *menu, const QString &selectedText) 0170 { 0171 for (PluginTextInterface *interface : std::as_const(mPluginTextInterface)) { 0172 interface->setSelectedText(selectedText); 0173 interface->addAction(menu); 0174 } 0175 } 0176 0177 QString MessageListViewBase::selectedText(const QModelIndex &index) 0178 { 0179 Q_UNUSED(index); 0180 return {}; 0181 } 0182 0183 void MessageListViewBase::copyMessageToClipboard(const QModelIndex &index) 0184 { 0185 const QString messageText = selectedText(index); 0186 if (messageText.isEmpty()) { 0187 return; 0188 } 0189 QClipboard *clip = QApplication::clipboard(); 0190 clip->setText(messageText, QClipboard::Clipboard); 0191 clip->setText(messageText, QClipboard::Selection); 0192 } 0193 0194 #include "moc_messagelistviewbase.cpp"