File indexing completed on 2024-05-05 12:22:13
0001 /* 0002 SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "kateargumenthinttree.h" 0008 0009 #include "expandingtree/expandingwidgetmodel.h" 0010 #include "kateargumenthintmodel.h" 0011 #include "katecompletiondelegate.h" 0012 #include "katecompletionwidget.h" 0013 #include "kateview.h" 0014 0015 #include <QApplication> 0016 #include <QHeaderView> 0017 #include <QModelIndex> 0018 #include <QPainter> 0019 #include <QScreen> 0020 #include <QScrollBar> 0021 0022 #include <KWindowSystem> 0023 0024 class ArgumentHintDelegate : public KateCompletionDelegate 0025 { 0026 public: 0027 using KateCompletionDelegate::KateCompletionDelegate; 0028 0029 protected: 0030 bool editorEvent(QEvent *event, QAbstractItemModel *, const QStyleOptionViewItem &, const QModelIndex &index) override 0031 { 0032 if (event->type() == QEvent::MouseButtonRelease) { 0033 event->accept(); 0034 model()->setExpanded(index, !model()->isExpanded(index)); 0035 0036 return true; 0037 } else { 0038 event->ignore(); 0039 } 0040 0041 return false; 0042 } 0043 0044 KateArgumentHintTree *tree() const 0045 { 0046 return static_cast<KateArgumentHintTree *>(parent()); 0047 } 0048 0049 KateArgumentHintModel *model() const 0050 { 0051 auto tree = this->tree(); 0052 return tree->model(); 0053 } 0054 0055 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override 0056 { 0057 QSize s = QStyledItemDelegate::sizeHint(option, index); 0058 if (model()->isExpanded(index) && model()->expandingWidget(index)) { 0059 QWidget *widget = model()->expandingWidget(index); 0060 QSize widgetSize = widget->size(); 0061 0062 s.setHeight(widgetSize.height() + s.height() 0063 + 10); // 10 is the sum that must match exactly the offsets used in ExpandingWidgetModel::placeExpandingWidget 0064 } 0065 return s; 0066 } 0067 0068 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override 0069 { 0070 if (index.column() == 0) { 0071 model()->placeExpandingWidget(index); 0072 } 0073 // paint the text at top if there is a widget below 0074 m_alignTop = model()->isExpanded(index); 0075 0076 KateCompletionDelegate::paint(painter, option, index); 0077 } 0078 }; 0079 0080 KateArgumentHintTree::KateArgumentHintTree(KateCompletionWidget *parent) 0081 : QTreeView(parent) 0082 , m_parent(parent) // Do not use the completion-widget as widget-parent, because the argument-hint-tree will be rendered separately 0083 { 0084 setFrameStyle(QFrame::Box | QFrame::Raised); 0085 0086 setUniformRowHeights(false); 0087 header()->setMinimumSectionSize(0); 0088 0089 setFocusPolicy(Qt::NoFocus); 0090 setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); 0091 setUniformRowHeights(false); 0092 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); 0093 header()->hide(); 0094 setRootIsDecorated(false); 0095 setIndentation(0); 0096 setAllColumnsShowFocus(true); 0097 setAlternatingRowColors(true); 0098 setItemDelegate(new ArgumentHintDelegate(this)); 0099 } 0100 0101 void KateArgumentHintTree::clearCompletion() 0102 { 0103 setCurrentIndex(QModelIndex()); 0104 } 0105 0106 KateArgumentHintModel *KateArgumentHintTree::model() const 0107 { 0108 return m_parent->argumentHintModel(); 0109 } 0110 0111 void KateArgumentHintTree::paintEvent(QPaintEvent *event) 0112 { 0113 QTreeView::paintEvent(event); 0114 updateGeometry(); ///@todo delay this. It is needed here, because visualRect(...) returns an invalid rect in updateGeometry before the content is painted 0115 } 0116 0117 void KateArgumentHintTree::rowsInserted(const QModelIndex &parent, int start, int end) 0118 { 0119 QTreeView::rowsInserted(parent, start, end); 0120 updateGeometry(); 0121 } 0122 0123 int KateArgumentHintTree::sizeHintForColumn(int column) const 0124 { 0125 return QTreeView::sizeHintForColumn(column); 0126 } 0127 0128 unsigned int KateArgumentHintTree::rowHeight(const QModelIndex &index) const 0129 { 0130 uint max = sizeHintForIndex(index).height(); 0131 0132 for (int a = 0; a < index.model()->columnCount(index.parent()); ++a) { 0133 QModelIndex i = index.sibling(index.row(), a); 0134 uint cSize = sizeHintForIndex(i).height(); 0135 if (cSize > max) { 0136 max = cSize; 0137 } 0138 } 0139 return max; 0140 } 0141 0142 void KateArgumentHintTree::updateGeometry(QRect geom) 0143 { 0144 // Avoid recursive calls of updateGeometry 0145 static bool updatingGeometry = false; 0146 if (updatingGeometry) { 0147 return; 0148 } 0149 updatingGeometry = true; 0150 0151 if (model()->rowCount(QModelIndex()) == 0) { 0152 /* qCDebug(LOG_KTE) << "KateArgumentHintTree:: empty model";*/ 0153 hide(); 0154 setGeometry(geom); 0155 updatingGeometry = false; 0156 return; 0157 } 0158 0159 int bottom = geom.bottom(); 0160 int totalWidth = std::max(geom.width(), resizeColumns()); 0161 int totalHeight = 0; 0162 for (int a = 0; a < model()->rowCount(QModelIndex()); ++a) { 0163 QModelIndex index(model()->index(a, 0)); 0164 totalHeight += rowHeight(index); 0165 for (int b = 0; b < model()->rowCount(index); ++b) { 0166 QModelIndex childIndex = model()->index(b, 0, index); 0167 totalHeight += rowHeight(childIndex); 0168 } 0169 } 0170 0171 totalHeight += frameWidth() * 2; 0172 0173 geom.setHeight(totalHeight); 0174 0175 geom.moveBottom(bottom); 0176 // if( totalWidth > geom.width() ) 0177 geom.setWidth(totalWidth); 0178 0179 bool enableScrollBars = false; 0180 0181 // Resize and move so it fits the screen horizontally 0182 const auto screenGeometry = m_parent->view()->screen()->availableGeometry(); 0183 const int maxWidth = (screenGeometry.width() * 3) / 4; 0184 if (geom.width() > maxWidth) { 0185 geom.setWidth(maxWidth); 0186 geom.setHeight(geom.height() + horizontalScrollBar()->height() + 2); 0187 geom.moveBottom(bottom); 0188 enableScrollBars = true; 0189 } 0190 0191 bool resized = false; 0192 if (!KWindowSystem::isPlatformWayland()) { 0193 if (geom.right() > screenGeometry.right()) { 0194 geom.moveRight(screenGeometry.right()); 0195 } 0196 0197 if (geom.left() < screenGeometry.left()) { 0198 geom.moveLeft(screenGeometry.left()); 0199 } 0200 0201 // Resize and move so it fits the screen vertically 0202 if (geom.top() < screenGeometry.top()) { 0203 const int offset = screenGeometry.top() - geom.top(); 0204 geom.setBottom(geom.bottom() - offset); 0205 geom.moveTo(geom.left(), screenGeometry.top()); 0206 resized = true; 0207 } 0208 } 0209 0210 if (geom != geometry()) { 0211 setUpdatesEnabled(false); 0212 setAnimated(false); 0213 0214 setHorizontalScrollBarPolicy(enableScrollBars ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff); 0215 0216 /* qCDebug(LOG_KTE) << "KateArgumentHintTree::updateGeometry: updating geometry to " << geom;*/ 0217 setGeometry(geom); 0218 0219 if (resized && currentIndex().isValid()) { 0220 scrollTo(currentIndex()); 0221 } 0222 0223 setUpdatesEnabled(true); 0224 } 0225 0226 updatingGeometry = false; 0227 } 0228 0229 int KateArgumentHintTree::resizeColumns() 0230 { 0231 int totalSize = 0; 0232 for (int a = 0; a < header()->count(); a++) { 0233 int columnSize = sizeHintForColumn(a); 0234 setColumnWidth(a, columnSize); 0235 totalSize += columnSize; 0236 } 0237 return totalSize; 0238 } 0239 0240 void KateArgumentHintTree::updateGeometry() 0241 { 0242 updateGeometry(geometry()); 0243 } 0244 0245 bool KateArgumentHintTree::nextCompletion() 0246 { 0247 QModelIndex current; 0248 QModelIndex firstCurrent = currentIndex(); 0249 0250 do { 0251 QModelIndex oldCurrent = currentIndex(); 0252 0253 current = moveCursor(MoveDown, Qt::NoModifier); 0254 0255 if (current != oldCurrent && current.isValid()) { 0256 setCurrentIndex(current); 0257 0258 } else { 0259 if (firstCurrent.isValid()) { 0260 setCurrentIndex(firstCurrent); 0261 } 0262 return false; 0263 } 0264 0265 } while (!model()->indexIsItem(current)); 0266 0267 return true; 0268 } 0269 0270 bool KateArgumentHintTree::previousCompletion() 0271 { 0272 QModelIndex current; 0273 QModelIndex firstCurrent = currentIndex(); 0274 0275 do { 0276 QModelIndex oldCurrent = currentIndex(); 0277 0278 current = moveCursor(MoveUp, Qt::NoModifier); 0279 0280 if (current != oldCurrent && current.isValid()) { 0281 setCurrentIndex(current); 0282 0283 } else { 0284 if (firstCurrent.isValid()) { 0285 setCurrentIndex(firstCurrent); 0286 } 0287 return false; 0288 } 0289 0290 } while (!model()->indexIsItem(current)); 0291 0292 return true; 0293 } 0294 0295 bool KateArgumentHintTree::pageDown() 0296 { 0297 QModelIndex old = currentIndex(); 0298 QModelIndex current = moveCursor(MovePageDown, Qt::NoModifier); 0299 0300 if (current.isValid()) { 0301 setCurrentIndex(current); 0302 if (!model()->indexIsItem(current)) { 0303 if (!nextCompletion()) { 0304 previousCompletion(); 0305 } 0306 } 0307 } 0308 0309 return current != old; 0310 } 0311 0312 bool KateArgumentHintTree::pageUp() 0313 { 0314 QModelIndex old = currentIndex(); 0315 QModelIndex current = moveCursor(MovePageUp, Qt::NoModifier); 0316 0317 if (current.isValid()) { 0318 setCurrentIndex(current); 0319 if (!model()->indexIsItem(current)) { 0320 if (!previousCompletion()) { 0321 nextCompletion(); 0322 } 0323 } 0324 } 0325 return current != old; 0326 } 0327 0328 void KateArgumentHintTree::top() 0329 { 0330 QModelIndex current = moveCursor(MoveHome, Qt::NoModifier); 0331 setCurrentIndex(current); 0332 0333 if (current.isValid()) { 0334 setCurrentIndex(current); 0335 if (!model()->indexIsItem(current)) { 0336 nextCompletion(); 0337 } 0338 } 0339 } 0340 0341 void KateArgumentHintTree::bottom() 0342 { 0343 QModelIndex current = moveCursor(MoveEnd, Qt::NoModifier); 0344 setCurrentIndex(current); 0345 0346 if (current.isValid()) { 0347 setCurrentIndex(current); 0348 if (!model()->indexIsItem(current)) { 0349 previousCompletion(); 0350 } 0351 } 0352 } 0353 0354 #include "moc_kateargumenthinttree.cpp"