Index: src/filewidgets/kdiroperatordetailview.cpp =================================================================== --- src/filewidgets/kdiroperatordetailview.cpp +++ src/filewidgets/kdiroperatordetailview.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -32,6 +33,88 @@ #include #include +class KDirHeaderView : public QHeaderView +{ + Q_OBJECT +public: + explicit KDirHeaderView(KDirOperatorDetailView *view, Qt::Orientation orientation, QWidget *parent = nullptr) + : QHeaderView(orientation, parent), + q(view), + m_minimalNameColumnWidth(-1), + m_setInteractiveResizeMode(false), + m_checkMinimalNameColumnWidth(false), + m_narrowMode(false) + { + for (int i = 0 ; i < KDirModel::ColumnCount ; ++i) { + m_currentColumnWidth[i] = -1; + } + auto pw = view->parentWidget(); + for (int pn = 0; pw && pn < 3; ++pn) { + pw = pw->parentWidget(); + } + m_isFileWidget = qobject_cast(pw); + // install the section resize handler + connect(this, &QHeaderView::sectionResized, this, &KDirHeaderView::sectionResizeHandler); + } + + void resetWidthTracking(bool all) + { + m_setInteractiveResizeMode = false; + m_checkMinimalNameColumnWidth = true; + if (all) { + for (int i = 0 ; i < KDirModel::ColumnCount ; ++i) { + m_currentColumnWidth[i] = -1; + } + m_minimalNameColumnWidth = -1; + } + } + + void setMinimumNameColumnWidth() + { + if (m_minimalNameColumnWidth < 0) { + m_minimalNameColumnWidth = sectionSize(2) * 1.33 + 0.5; + } + } + + virtual QSize sectionSizeFromContents(int logicalIndex) const + { + auto size = QHeaderView::sectionSizeFromContents(logicalIndex); + if (logicalIndex == 0 && size.width() < m_minimalNameColumnWidth) { + size.setWidth(m_minimalNameColumnWidth); + } + return size; + } + + void sectionResizeHandler(int column, int, int newSize) + { + if (newSize > 0 && model()->rowCount() > 0) { + if (column == 0 && newSize < m_minimalNameColumnWidth) { + resizeSection(0, m_minimalNameColumnWidth); + } else { + // store the new width; note that we may be called multiple times + // for columns in Stretch mode, with increasingly accurate sizes. + m_currentColumnWidth[column] = newSize; + } + // for some reason making a selective ResizeMode setting here + // has no effect; we need to set interactive mode on all sections + // at a later stage. Queue that now, and don't touch any + // section settings directly. + m_setInteractiveResizeMode = true; + // also set this flag here because we may be called even if the + // user isn't resizing the parent widget. + m_checkMinimalNameColumnWidth = true; + } + } + + KDirOperatorDetailView* q; + int m_currentColumnWidth[KDirModel::ColumnCount]; + int m_minimalNameColumnWidth; + bool m_setInteractiveResizeMode; + bool m_checkMinimalNameColumnWidth; + bool m_isFileWidget; + bool m_narrowMode; +}; + KDirOperatorDetailView::KDirOperatorDetailView(QWidget *parent) : QTreeView(parent), m_hideDetailColumns(false) @@ -44,6 +127,8 @@ setEditTriggers(QAbstractItemView::NoEditTriggers); setVerticalScrollMode(QListView::ScrollPerPixel); setHorizontalScrollMode(QListView::ScrollPerPixel); + const auto currentHeader = header(); + setHeader(new KDirHeaderView(this, currentHeader->orientation(), currentHeader->parentWidget())); } KDirOperatorDetailView::~KDirOperatorDetailView() @@ -85,27 +170,102 @@ bool KDirOperatorDetailView::event(QEvent *event) { - if (event->type() == QEvent::Polish) { - QHeaderView *headerView = header(); - headerView->setSectionResizeMode(0, QHeaderView::Stretch); - headerView->setSectionResizeMode(1, QHeaderView::ResizeToContents); - headerView->setSectionResizeMode(2, QHeaderView::ResizeToContents); - headerView->setStretchLastSection(false); - headerView->setSectionsMovable(false); - - setColumnHidden(KDirModel::Size, m_hideDetailColumns); - setColumnHidden(KDirModel::ModifiedTime, m_hideDetailColumns); - hideColumn(KDirModel::Type); - hideColumn(KDirModel::Permissions); - hideColumn(KDirModel::Owner); - hideColumn(KDirModel::Group); - } else if (event->type() == QEvent::UpdateRequest) { - // A wheel movement will scroll 4 items - if (model()->rowCount()) { - verticalScrollBar()->setSingleStep((sizeHintForRow(0) / 3) * 4); - } - } + KDirHeaderView *headerView = qobject_cast(header()); + switch (auto type = event->type()) { + case QEvent::Resize: + case QEvent::Polish: + if (type == QEvent::Polish) { + // the polish event seems to arrive only once during our lifetime so + // this is a good moment for some JIT initialisation. + headerView->resetWidthTracking(true); + + } else { + headerView->resetWidthTracking(false); + } + headerView->setSectionResizeMode(0, QHeaderView::Stretch); + headerView->setSectionResizeMode(1, QHeaderView::ResizeToContents); + headerView->setSectionResizeMode(2, QHeaderView::ResizeToContents); + headerView->setStretchLastSection(false); + headerView->setSectionsMovable(false); + setColumnHidden(KDirModel::Size, m_hideDetailColumns); + setColumnHidden(KDirModel::ModifiedTime, m_hideDetailColumns); + hideColumn(KDirModel::Type); + hideColumn(KDirModel::Permissions); + hideColumn(KDirModel::Owner); + hideColumn(KDirModel::Group); + if (type == QEvent::Resize && !headerView->m_isFileWidget) { + // force an explicit resize here for column 0; without that, + // this column could be resized to and shown at a width less + // than m_minimalNameColumnWidth. This can happen outside of + // our control when resizing Kate's file browser sidebar in + // detailed tree view. It doesn't happen in KFileWidgets + // (open/save dialogs) which are common enough to detect them + // and skip this potentially expensive operation. + resizeColumnToContents(0); + } + break; + case QEvent::UpdateRequest: + // A wheel movement will scroll 4 items + if (model()->rowCount()) { + verticalScrollBar()->setSingleStep((sizeHintForRow(0) / 3) * 4); + } + break; + case QEvent::Paint: + // event analysis confirms that the 1st paint event arrives after all headers have + // had a size set, so we can now set the definitive section size explicitly and + // activate interactive mode. The results is that section size is set once according + // to the principles defined above (Polish case) but can then be adapted by the user, + // for instance when navigating to a different directory. + + // define a minimum width for the name column when we have the 1st valid reference, + // here a third more than the width of the date column. + if (headerView->m_setInteractiveResizeMode) { + headerView->setMinimumNameColumnWidth(); + headerView->setSectionResizeMode(QHeaderView::Interactive); + headerView->setSectionsMovable(true); + // we only do this once after receiving a Polish or Resize event, + // both of which reset the column size mode. + headerView->m_setInteractiveResizeMode = false; + } + if (headerView->m_checkMinimalNameColumnWidth) { + // compensation for very narrow views: impose a minimum name column width and + // reduce letterspacing by a small amount. NB: here we use sectionSize() to obtain + // current widths. + if (headerView->sectionSize(0) > 0) { + if (headerView->sectionSize(0) < headerView->m_minimalNameColumnWidth) { + headerView->m_checkMinimalNameColumnWidth = false; + headerView->m_currentColumnWidth[0] = headerView->m_minimalNameColumnWidth; + headerView->m_narrowMode = true; + } else if (headerView->m_narrowMode) { + // sometimes Qt finds a better (larger) width for the name column even + // when in narrow mode; use it. + if (headerView->sectionSize(0) > headerView->m_minimalNameColumnWidth) { + headerView->m_minimalNameColumnWidth = headerView->sectionSize(0); + qDebug() << "KDirOperatorDetailView: narrow mode off - minimalNameColumnWidth now" + << headerView->m_minimalNameColumnWidth; + } + headerView->m_narrowMode = false; + } + } + } + // set the values we want to set explicitly + for (int i = 0 ; i < KDirModel::ColumnCount ; ++i) { + if (headerView->m_currentColumnWidth[i] > 0) { + headerView->setSectionResizeMode(i, QHeaderView::Interactive); + headerView->resizeSection(i, headerView->m_currentColumnWidth[i]); + } + } + if (headerView->m_currentColumnWidth[0] > 0 && headerView->sectionResizeMode(0) != QHeaderView::Interactive) { + qWarning() << "!!!"; + } + // if ever we get still non-resizable sections we could make them resizable + // here if columns 1-3 have been resized explicitly. + break; + default: + // noop + break; + } return QTreeView::event(event); } @@ -133,3 +293,5 @@ { QTreeView::currentChanged(current, previous); } + +#include "kdiroperatordetailview.moc"