File indexing completed on 2024-04-28 15:30:18

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"