[office/kmymoney] kmymoney/views: Absorb manual column resizes on the side of the dragged separator
Thomas Baumgart <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 3d4134d4dc55c085bb3e5cc3efbeaf06c81340b5 by Thomas Baumgart, on behalf of Simone Iori.
Committed on 27/07/2026 at 19:15.
Pushed by tbaumgart into branch 'master'.
Absorb manual column resizes on the side of the dragged separator
LedgerView::adjustDetailColumn() keeps the columns filling the
viewport exactly by handing the whole difference to the detail column,
and it runs after every section resize — including the ones caused by
the user dragging a separator. Two consequences:
* the detail column cannot be widened with its right separator, since
the correction takes the added width off it again;
* dragging a separator to the right of the detail column moves the
detail column's edge instead of the separator, because the correction
lands on the other side of the drag.
Separators to the left of the detail column behave correctly already,
which is what makes the current behavior feel inconsistent.
With this change, while the user drags a separator the columns to its
right absorb the difference, cascading to the next one when a column
reaches its minimum width; the detail column remains the absorber in
every other case, such as the view being resized. The separator now
stays where it was dropped, and the columns keep filling the viewport
exactly as before, so no horizontal scrollbar appears.
Two notes for review:
* When several columns have to give way, the width is taken from the
nearest one first until it reaches its minimum, rather than
distributed proportionally over all of them. Both are defensible;
this one felt the most natural in use. See the discussion on the
bug report.
* SplitView carries a copy of the same logic with the memo column as
its absorber and has the same problem. I left it out of this change
on purpose so the approach can be settled on one focused diff first;
extending it afterwards is straightforward.
BUG: 510377
FIXED-IN: 5.2.3
M +59 -4 kmymoney/views/ledgerview.cpp
https://invent.kde.org/office/kmymoney/-/commit/3d4134d4dc55c085bb3e5cc3efbeaf06c81340b5
diff --git a/kmymoney/views/ledgerview.cpp b/kmymoney/views/ledgerview.cpp
index 9571e1b54..06c907ef7 100644
--- a/kmymoney/views/ledgerview.cpp
+++ b/kmymoney/views/ledgerview.cpp
@@ -91,6 +91,7 @@ public:
, infoMessage(new KMessageWidget(q))
, editor(nullptr)
, adjustableColumn(JournalModel::Column::Detail)
+ , userResizedColumn(-1)
, adjustingColumn(false)
, showValuesInverted(false)
, newTransactionPresent(false)
@@ -514,6 +515,38 @@ public:
}
}
+ /**
+ * Returns the columns that take up the difference between the width of
+ * all columns and the width of the viewport, in the order in which they
+ * shall be used.
+ *
+ * When the user drags a section separator, the columns to the right of
+ * the modified one absorb the change, so that the separator stays where
+ * it was dropped. In all other cases (e.g. the view itself is resized)
+ * the detail column takes the difference, as it always did.
+ */
+ QVector<int> absorberColumns() const
+ {
+ QVector<int> columns;
+ const auto header = q->horizontalHeader();
+
+ if (userResizedColumn >= 0) {
+ for (int visualIndex = header->visualIndex(userResizedColumn) + 1; visualIndex < header->count(); ++visualIndex) {
+ const auto logicalIndex = header->logicalIndex(visualIndex);
+ if (!header->isSectionHidden(logicalIndex) && (header->sectionResizeMode(logicalIndex) == QHeaderView::Interactive)) {
+ columns.append(logicalIndex);
+ }
+ }
+ }
+
+ // the detail column remains the last resort, e.g. when the columns
+ // to the right of the modified one cannot shrink any further
+ if (!columns.contains(adjustableColumn) && !header->isSectionHidden(adjustableColumn)) {
+ columns.append(adjustableColumn);
+ }
+ return columns;
+ }
+
void resetMaxLineCache()
{
auto m = q->LedgerView::model();
@@ -533,6 +566,7 @@ public:
TransactionEditorBase* editor;
QHash<const QAbstractItemModel*, QStyledItemDelegate*> delegates;
int adjustableColumn;
+ int userResizedColumn;
bool adjustingColumn;
bool showValuesInverted;
bool newTransactionPresent;
@@ -574,6 +608,11 @@ LedgerView::LedgerView(QWidget* parent)
// See LedgerView::resizeSection().
connect(horizontalHeader(), &QHeaderView::sectionResized, this, [&](int logicalIndex, int oldSize, int newSize) {
Q_EMIT sectionResized(this, d->columnSelector->configGroupName(), logicalIndex, oldSize, newSize);
+ // when the user drags a separator, the columns to the right of the
+ // modified one absorb the difference. See Private::absorberColumns().
+ const auto draggingSeparator =
+ (QApplication::mouseButtons() & Qt::LeftButton) && (horizontalHeader()->cursor().shape() == Qt::SplitHCursor);
+ d->userResizedColumn = draggingSeparator ? logicalIndex : -1;
QMetaObject::invokeMethod(this, "adjustDetailColumn", Qt::QueuedConnection, Q_ARG(int, viewport()->width()), Q_ARG(bool, false));
});
@@ -1391,16 +1430,32 @@ void LedgerView::adjustDetailColumn(int newViewportWidth, bool informOtherViews)
}
totalColumnWidth += header->sectionSize(i);
}
- const int delta = newViewportWidth - totalColumnWidth;
- const int newWidth = header->sectionSize(d->adjustableColumn) + delta;
- if (newWidth > 10) {
+ int delta = newViewportWidth - totalColumnWidth;
+ if (delta != 0) {
+ const int minimumWidth = qMax(header->minimumSectionSize(), 10);
QSignalBlocker blocker(header);
if (informOtherViews)
blocker.unblock();
- header->resizeSection(d->adjustableColumn, newWidth);
+
+ // hand the difference to the absorber columns one after the other
+ // until it is used up or none of them can take any more of it
+ const auto columns = d->absorberColumns();
+ for (const int column : columns) {
+ if (delta == 0) {
+ break;
+ }
+ const int currentWidth = header->sectionSize(column);
+ const int newWidth = qMax(minimumWidth, currentWidth + delta);
+ if (newWidth == currentWidth) {
+ continue;
+ }
+ header->resizeSection(column, newWidth);
+ delta -= newWidth - currentWidth;
+ }
}
// remember that we're done this time
+ d->userResizedColumn = -1;
d->adjustingColumn = false;
}