┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/kitemviews/private
diff options
context:
space:
mode:
authorFelix Ernst <[email protected]>2024-12-29 11:42:22 +0000
committerFelix Ernst <[email protected]>2024-12-29 11:42:22 +0000
commit95542a389112491abf3a31c338e7d78f7785f48e (patch)
tree1a4ece8deef6626c4649538fbf22acdee43114cb /src/kitemviews/private
parent3696213ccbbe27e9ef3fc85eb97dd32fd669066f (diff)
Mirror details view mode for right-to-left languages
This commit implements mirroring of the details view mode for right-to- left languages. This is the last of the Dolphin view modes which did not adapt to right-to-left languages correctly. Implementation-wise this is mostly about adapting the math so all the information is placed correctly no matter the view mode or layout direction. While most of the view actually changes the painting code for right-to-left languages, for the column header I decided to keep the logic left-to-right and instead reverse the order of the role columns. To implement this mirroring I needed to rework quite a bit of logic, so I used the opportunity to fix some bugs/behaviur quirks: - Left and right padding is now saved and restored separately instead of only saving the left padding - Changing the right padding no longer disables "automatic column resizing". - The grip handles for column resizing can now be grabbed when near the grip handle instead of only allowing grabbing when slightly to the left of the grip. - Role column headers now only show a hover highlight effect when the mouse cursor is actually above that role and not above the grip handle or the padding. - There is now a soft-boarder when shrinking the right padding so shrinking the padding "below zero width" will no longer immediately clear automatic resize behaviour. So now it is possible to simply remove the right padding by resizing it to zero width. BUG: 449211 BUG: 495942 # Acknowledgement This work is part of a my project funded through the NGI0 Entrust Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme, under the aegis of DG Communications Networks, Content and Technology.
Diffstat (limited to 'src/kitemviews/private')
-rw-r--r--src/kitemviews/private/kitemlistheaderwidget.cpp324
-rw-r--r--src/kitemviews/private/kitemlistheaderwidget.h37
2 files changed, 206 insertions, 155 deletions
diff --git a/src/kitemviews/private/kitemlistheaderwidget.cpp b/src/kitemviews/private/kitemlistheaderwidget.cpp
index 02a4f939d..3dc82ad6b 100644
--- a/src/kitemviews/private/kitemlistheaderwidget.cpp
+++ b/src/kitemviews/private/kitemlistheaderwidget.cpp
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]>
+ * SPDX-FileCopyrightText: 2022, 2024 Felix Ernst <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
@@ -12,18 +13,44 @@
#include <QPainter>
#include <QStyleOptionHeader>
+namespace
+{
+/**
+ * @returns a list which has a reversed order of elements compared to @a list.
+ */
+QList<QByteArray> reversed(const QList<QByteArray> list)
+{
+ QList<QByteArray> reversedList;
+ for (auto i = list.rbegin(); i != list.rend(); i++) {
+ reversedList.emplaceBack(*i);
+ }
+ return reversedList;
+};
+
+/**
+ * @returns the index of the column for the name/text of items. This depends on the layoutDirection() and column count of @a itemListHeaderWidget.
+ */
+int nameColumnIndex(const KItemListHeaderWidget *itemListHeaderWidget)
+{
+ if (itemListHeaderWidget->layoutDirection() == Qt::LeftToRight) {
+ return 0;
+ }
+ return itemListHeaderWidget->columns().count() - 1;
+};
+}
+
KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget *parent)
: QGraphicsWidget(parent)
, m_automaticColumnResizing(true)
, m_model(nullptr)
, m_offset(0)
- , m_sidePadding(0)
+ , m_leftPadding(0)
+ , m_rightPadding(0)
, m_columns()
, m_columnWidths()
, m_preferredColumnWidths()
, m_hoveredIndex(-1)
, m_pressedRoleIndex(-1)
- , m_roleOperation(NoRoleOperation)
, m_pressedMousePos()
, m_movingRole()
{
@@ -82,13 +109,13 @@ void KItemListHeaderWidget::setColumns(const QList<QByteArray> &roles)
}
}
- m_columns = roles;
+ m_columns = layoutDirection() == Qt::LeftToRight ? roles : reversed(roles);
update();
}
QList<QByteArray> KItemListHeaderWidget::columns() const
{
- return m_columns;
+ return layoutDirection() == Qt::LeftToRight ? m_columns : reversed(m_columns);
}
void KItemListHeaderWidget::setColumnWidth(const QByteArray &role, qreal width)
@@ -132,18 +159,35 @@ qreal KItemListHeaderWidget::offset() const
return m_offset;
}
-void KItemListHeaderWidget::setSidePadding(qreal width)
+void KItemListHeaderWidget::setSidePadding(qreal leftPaddingWidth, qreal rightPaddingWidth)
{
- if (m_sidePadding != width) {
- m_sidePadding = width;
- Q_EMIT sidePaddingChanged(width);
- update();
+ bool changed = false;
+ if (m_leftPadding != leftPaddingWidth) {
+ m_leftPadding = leftPaddingWidth;
+ changed = true;
}
+
+ if (m_rightPadding != rightPaddingWidth) {
+ m_rightPadding = rightPaddingWidth;
+ changed = true;
+ }
+
+ if (!changed) {
+ return;
+ }
+
+ Q_EMIT sidePaddingChanged(leftPaddingWidth, rightPaddingWidth);
+ update();
+}
+
+qreal KItemListHeaderWidget::leftPadding() const
+{
+ return m_leftPadding;
}
-qreal KItemListHeaderWidget::sidePadding() const
+qreal KItemListHeaderWidget::rightPadding() const
{
- return m_sidePadding;
+ return m_rightPadding;
}
qreal KItemListHeaderWidget::minimumColumnWidth() const
@@ -165,7 +209,7 @@ void KItemListHeaderWidget::paint(QPainter *painter, const QStyleOptionGraphicsI
painter->setFont(font());
painter->setPen(palette().text().color());
- qreal x = -m_offset + m_sidePadding;
+ qreal x = -m_offset + m_leftPadding + unusedSpace();
int orderIndex = 0;
for (const QByteArray &role : std::as_const(m_columns)) {
const qreal roleWidth = m_columnWidths.value(role);
@@ -176,7 +220,6 @@ void KItemListHeaderWidget::paint(QPainter *painter, const QStyleOptionGraphicsI
}
if (!m_movingRole.pixmap.isNull()) {
- Q_ASSERT(m_roleOperation == MoveRoleOperation);
painter->drawPixmap(m_movingRole.x, 0, m_movingRole.pixmap);
}
}
@@ -185,11 +228,9 @@ void KItemListHeaderWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() & Qt::LeftButton) {
m_pressedMousePos = event->pos();
- if (isAbovePaddingGrip(m_pressedMousePos, PaddingGrip::Leading)) {
- m_roleOperation = ResizePaddingColumnOperation;
- } else {
+ m_pressedGrip = isAboveResizeGrip(m_pressedMousePos);
+ if (!m_pressedGrip) {
updatePressedRoleIndex(event->pos());
- m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ? ResizeRoleOperation : NoRoleOperation;
}
event->accept();
} else {
@@ -201,12 +242,15 @@ void KItemListHeaderWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsWidget::mouseReleaseEvent(event);
- if (m_pressedRoleIndex == -1) {
- return;
- }
-
- switch (m_roleOperation) {
- case NoRoleOperation: {
+ if (m_pressedGrip) {
+ // Emitting a column width change removes automatic column resizing, so we do not emit if only the padding is being changed.
+ // Eception: In mouseMoveEvent() we also resize the last column if the right padding is at zero but the user still quickly resizes beyond the screen
+ // boarder. Such a resize "of the right padding" is let through when automatic column resizing was disabled by that resize.
+ if (m_pressedGrip->roleToTheLeft != "leftPadding" && (m_pressedGrip->roleToTheRight != "rightPadding" || !m_automaticColumnResizing)) {
+ const qreal currentWidth = m_columnWidths.value(m_pressedGrip->roleToTheLeft);
+ Q_EMIT columnWidthChangeFinished(m_pressedGrip->roleToTheLeft, currentWidth);
+ }
+ } else if (m_pressedRoleIndex != -1 && m_movingRole.index == -1) {
// Only a click has been done and no moving or resizing has been started
const QByteArray sortRole = m_model->sortRole();
const int sortRoleIndex = m_columns.indexOf(sortRole);
@@ -229,29 +273,15 @@ void KItemListHeaderWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
Q_EMIT sortOrderChanged(Qt::AscendingOrder, Qt::DescendingOrder);
}
}
- break;
}
- case ResizeRoleOperation: {
- const QByteArray pressedRole = m_columns[m_pressedRoleIndex];
- const qreal currentWidth = m_columnWidths.value(pressedRole);
- Q_EMIT columnWidthChangeFinished(pressedRole, currentWidth);
- break;
- }
-
- case MoveRoleOperation:
- m_movingRole.pixmap = QPixmap();
- m_movingRole.x = 0;
- m_movingRole.xDec = 0;
- m_movingRole.index = -1;
- break;
-
- default:
- break;
- }
+ m_movingRole.pixmap = QPixmap();
+ m_movingRole.x = 0;
+ m_movingRole.xDec = 0;
+ m_movingRole.index = -1;
+ m_pressedGrip = std::nullopt;
m_pressedRoleIndex = -1;
- m_roleOperation = NoRoleOperation;
update();
QApplication::restoreOverrideCursor();
@@ -261,69 +291,51 @@ void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsWidget::mouseMoveEvent(event);
- switch (m_roleOperation) {
- case NoRoleOperation:
- if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
- // A role gets dragged by the user. Create a pixmap of the role that will get
- // synchronized on each further mouse-move-event with the mouse-position.
- m_roleOperation = MoveRoleOperation;
- const int roleIndex = roleIndexAt(m_pressedMousePos);
- m_movingRole.index = roleIndex;
- if (roleIndex == 0) {
- // TODO: It should be configurable whether moving the first role is allowed.
- // In the context of Dolphin this is not required, however this should be
- // changed if KItemViews are used in a more generic way.
- QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor));
- } else {
- m_movingRole.pixmap = createRolePixmap(roleIndex);
+ if (m_pressedGrip) {
+ if (m_pressedGrip->roleToTheLeft == "leftPadding") {
+ qreal currentWidth = m_leftPadding;
+ currentWidth += event->pos().x() - event->lastPos().x();
+ m_leftPadding = qMax(0.0, currentWidth);
- qreal roleX = -m_offset + m_sidePadding;
- for (int i = 0; i < roleIndex; ++i) {
- const QByteArray role = m_columns[i];
- roleX += m_columnWidths.value(role);
- }
+ update();
+ Q_EMIT sidePaddingChanged(m_leftPadding, m_rightPadding);
+ return;
+ }
+
+ if (m_pressedGrip->roleToTheRight == "rightPadding") {
+ qreal currentWidth = m_rightPadding;
+ currentWidth -= event->pos().x() - event->lastPos().x();
+ m_rightPadding = qMax(0.0, currentWidth);
- m_movingRole.xDec = event->pos().x() - roleX;
- m_movingRole.x = roleX;
- update();
+ update();
+ Q_EMIT sidePaddingChanged(m_leftPadding, m_rightPadding);
+ if (m_rightPadding > 0.0) {
+ return;
+ }
+ // Continue so resizing of the last column beyond the view width is possible.
+ if (currentWidth > -10) {
+ return; // Automatic column resizing is valuable, so we don't want to give it up just for a few pixels of extra width for the rightmost column.
}
+ m_automaticColumnResizing = false;
}
- break;
-
- case ResizeRoleOperation: {
- const QByteArray pressedRole = m_columns[m_pressedRoleIndex];
- qreal previousWidth = m_columnWidths.value(pressedRole);
+ qreal previousWidth = m_columnWidths.value(m_pressedGrip->roleToTheLeft);
qreal currentWidth = previousWidth;
currentWidth += event->pos().x() - event->lastPos().x();
currentWidth = qMax(minimumColumnWidth(), currentWidth);
- m_columnWidths.insert(pressedRole, currentWidth);
+ m_columnWidths.insert(m_pressedGrip->roleToTheLeft, currentWidth);
update();
- Q_EMIT columnWidthChanged(pressedRole, currentWidth, previousWidth);
- break;
- }
-
- case ResizePaddingColumnOperation: {
- qreal currentWidth = m_sidePadding;
- currentWidth += event->pos().x() - event->lastPos().x();
- currentWidth = qMax(0.0, currentWidth);
-
- m_sidePadding = currentWidth;
-
- update();
-
- Q_EMIT sidePaddingChanged(currentWidth);
-
- break;
+ Q_EMIT columnWidthChanged(m_pressedGrip->roleToTheLeft, currentWidth, previousWidth);
+ return;
}
- case MoveRoleOperation: {
+ if (m_movingRole.index != -1) {
// TODO: It should be configurable whether moving the first role is allowed.
// In the context of Dolphin this is not required, however this should be
// changed if KItemViews are used in a more generic way.
- if (m_movingRole.index > 0) {
+ if (m_movingRole.index != nameColumnIndex(this)) {
m_movingRole.x = event->pos().x() - m_movingRole.xDec;
update();
@@ -332,16 +344,42 @@ void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
const QByteArray role = m_columns[m_movingRole.index];
const int previousIndex = m_movingRole.index;
m_movingRole.index = targetIndex;
- Q_EMIT columnMoved(role, targetIndex, previousIndex);
+ if (layoutDirection() == Qt::LeftToRight) {
+ Q_EMIT columnMoved(role, targetIndex, previousIndex);
+ } else {
+ Q_EMIT columnMoved(role, m_columns.count() - 1 - targetIndex, m_columns.count() - 1 - previousIndex);
+ }
m_movingRole.xDec = event->pos().x() - roleXPosition(role);
}
}
- break;
+ return;
}
- default:
- break;
+ if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
+ // A role gets dragged by the user. Create a pixmap of the role that will get
+ // synchronized on each further mouse-move-event with the mouse-position.
+ const int roleIndex = roleIndexAt(m_pressedMousePos);
+ m_movingRole.index = roleIndex;
+ if (roleIndex == nameColumnIndex(this)) {
+ // TODO: It should be configurable whether moving the first role is allowed.
+ // In the context of Dolphin this is not required, however this should be
+ // changed if KItemViews are used in a more generic way.
+ QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor));
+ return;
+ }
+
+ m_movingRole.pixmap = createRolePixmap(roleIndex);
+
+ qreal roleX = -m_offset + m_leftPadding + unusedSpace();
+ for (int i = 0; i < roleIndex; ++i) {
+ const QByteArray role = m_columns[i];
+ roleX += m_columnWidths.value(role);
+ }
+
+ m_movingRole.xDec = event->pos().x() - roleX;
+ m_movingRole.x = roleX;
+ update();
}
}
@@ -349,17 +387,17 @@ void KItemListHeaderWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *even
{
QGraphicsItem::mouseDoubleClickEvent(event);
- const int roleIndex = roleIndexAt(event->pos());
- if (roleIndex >= 0 && isAboveRoleGrip(event->pos(), roleIndex)) {
- const QByteArray role = m_columns.at(roleIndex);
+ const std::optional<Grip> doubleClickedGrip = isAboveResizeGrip(event->pos());
+ if (!doubleClickedGrip || doubleClickedGrip->roleToTheLeft.isEmpty()) {
+ return;
+ }
- qreal previousWidth = columnWidth(role);
- setColumnWidth(role, preferredColumnWidth(role));
- qreal currentWidth = columnWidth(role);
+ qreal previousWidth = columnWidth(doubleClickedGrip->roleToTheLeft);
+ setColumnWidth(doubleClickedGrip->roleToTheLeft, preferredColumnWidth(doubleClickedGrip->roleToTheLeft));
+ qreal currentWidth = columnWidth(doubleClickedGrip->roleToTheLeft);
- Q_EMIT columnWidthChanged(role, currentWidth, previousWidth);
- Q_EMIT columnWidthChangeFinished(role, currentWidth);
- }
+ Q_EMIT columnWidthChanged(doubleClickedGrip->roleToTheLeft, currentWidth, previousWidth);
+ Q_EMIT columnWidthChangeFinished(doubleClickedGrip->roleToTheLeft, currentWidth);
}
void KItemListHeaderWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
@@ -384,8 +422,7 @@ void KItemListHeaderWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
const QPointF &pos = event->pos();
updateHoveredIndex(pos);
- if ((m_hoveredIndex >= 0 && isAboveRoleGrip(pos, m_hoveredIndex)) || isAbovePaddingGrip(pos, PaddingGrip::Leading)
- || isAbovePaddingGrip(pos, PaddingGrip::Trailing)) {
+ if (isAboveResizeGrip(pos)) {
setCursor(Qt::SplitHCursor);
} else {
unsetCursor();
@@ -408,14 +445,9 @@ void KItemListHeaderWidget::slotSortOrderChanged(Qt::SortOrder current, Qt::Sort
void KItemListHeaderWidget::paintRole(QPainter *painter, const QByteArray &role, const QRectF &rect, int orderIndex, QWidget *widget) const
{
- const auto direction = widget ? widget->layoutDirection() : qApp->layoutDirection();
-
// The following code is based on the code from QHeaderView::paintSection().
// SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies).
QStyleOptionHeader option;
- option.direction = direction;
- option.textAlignment = direction == Qt::LeftToRight ? Qt::AlignLeft : Qt::AlignRight;
-
option.section = orderIndex;
option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
if (isEnabled()) {
@@ -453,7 +485,7 @@ void KItemListHeaderWidget::paintRole(QPainter *painter, const QByteArray &role,
if (m_columns.count() == 1) {
option.position = QStyleOptionHeader::Middle;
paintPadding(0, QRectF(0.0, 0.0, rect.left(), rect.height()), QStyleOptionHeader::Beginning);
- paintPadding(1, QRectF(rect.left(), 0.0, size().width() - rect.left(), rect.height()), QStyleOptionHeader::End);
+ paintPadding(1, QRectF(rect.right(), 0.0, size().width() - rect.right(), rect.height()), QStyleOptionHeader::End);
} else if (orderIndex == 0) {
// Paint the header for the first column; check if there is some empty space to the left which needs to be filled.
if (rect.left() > 0) {
@@ -466,7 +498,7 @@ void KItemListHeaderWidget::paintRole(QPainter *painter, const QByteArray &role,
// Paint the header for the last column; check if there is some empty space to the right which needs to be filled.
if (rect.right() < size().width()) {
option.position = QStyleOptionHeader::Middle;
- paintPadding(m_columns.count(), QRectF(rect.left(), 0.0, size().width() - rect.left(), rect.height()), QStyleOptionHeader::End);
+ paintPadding(m_columns.count(), QRectF(rect.right(), 0.0, size().width() - rect.right(), rect.height()), QStyleOptionHeader::End);
} else {
option.position = QStyleOptionHeader::End;
}
@@ -488,7 +520,7 @@ void KItemListHeaderWidget::updatePressedRoleIndex(const QPointF &pos)
void KItemListHeaderWidget::updateHoveredIndex(const QPointF &pos)
{
- const int hoverIndex = roleIndexAt(pos);
+ const int hoverIndex = isAboveResizeGrip(pos) ? -1 : roleIndexAt(pos);
if (m_hoveredIndex != hoverIndex) {
if (m_hoveredIndex != -1) {
@@ -504,50 +536,43 @@ void KItemListHeaderWidget::updateHoveredIndex(const QPointF &pos)
int KItemListHeaderWidget::roleIndexAt(const QPointF &pos) const
{
- int index = -1;
+ qreal x = -m_offset + m_leftPadding + unusedSpace();
+ if (pos.x() < x) {
+ return -1;
+ }
- qreal x = -m_offset + m_sidePadding;
+ int index = -1;
for (const QByteArray &role : std::as_const(m_columns)) {
++index;
x += m_columnWidths.value(role);
if (pos.x() <= x) {
- break;
+ return index;
}
}
- return index;
+ return -1;
}
-bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF &pos, int roleIndex) const
+std::optional<const KItemListHeaderWidget::Grip> KItemListHeaderWidget::isAboveResizeGrip(const QPointF &position) const
{
- qreal x = -m_offset + m_sidePadding;
- for (int i = 0; i <= roleIndex; ++i) {
- const QByteArray role = m_columns[i];
- x += m_columnWidths.value(role);
- }
-
- const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
- return pos.x() >= (x - grip) && pos.x() <= x;
-}
+ qreal x = -m_offset + m_leftPadding + unusedSpace();
+ const int gripWidthTolerance = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
-bool KItemListHeaderWidget::isAbovePaddingGrip(const QPointF &pos, PaddingGrip paddingGrip) const
-{
- const qreal lx = -m_offset + m_sidePadding;
- const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
+ if (x - gripWidthTolerance < position.x() && position.x() < x + gripWidthTolerance) {
+ return std::optional{Grip{"leftPadding", m_columns[0]}};
+ }
- switch (paddingGrip) {
- case Leading:
- return pos.x() >= (lx - grip) && pos.x() <= lx;
- case Trailing: {
- qreal rx = lx;
- for (const QByteArray &role : std::as_const(m_columns)) {
- rx += m_columnWidths.value(role);
+ for (int i = 0; i < m_columns.count(); ++i) {
+ const QByteArray role = m_columns[i];
+ x += m_columnWidths.value(role);
+ if (x - gripWidthTolerance < position.x() && position.x() < x + gripWidthTolerance) {
+ if (i + 1 < m_columns.count()) {
+ return std::optional{Grip{m_columns[i], m_columns[i + 1]}};
+ }
+ return std::optional{Grip{m_columns[i], "rightPadding"}};
}
- return pos.x() >= (rx - grip) && pos.x() <= rx;
- }
- default:
- return false;
}
+ return std::nullopt;
}
QPixmap KItemListHeaderWidget::createRolePixmap(int roleIndex) const
@@ -581,7 +606,7 @@ int KItemListHeaderWidget::targetOfMovingRole() const
const int movingRight = movingLeft + movingWidth - 1;
int targetIndex = 0;
- qreal targetLeft = -m_offset + m_sidePadding;
+ qreal targetLeft = -m_offset + m_leftPadding + unusedSpace();
while (targetIndex < m_columns.count()) {
const QByteArray role = m_columns[targetIndex];
const qreal targetWidth = m_columnWidths.value(role);
@@ -603,7 +628,7 @@ int KItemListHeaderWidget::targetOfMovingRole() const
qreal KItemListHeaderWidget::roleXPosition(const QByteArray &role) const
{
- qreal x = -m_offset + m_sidePadding;
+ qreal x = -m_offset + m_leftPadding + unusedSpace();
for (const QByteArray &visibleRole : std::as_const(m_columns)) {
if (visibleRole == role) {
return x;
@@ -615,4 +640,17 @@ qreal KItemListHeaderWidget::roleXPosition(const QByteArray &role) const
return -1;
}
+qreal KItemListHeaderWidget::unusedSpace() const
+{
+ if (layoutDirection() == Qt::LeftToRight) {
+ return 0;
+ }
+ int unusedSpace = size().width() - m_leftPadding - m_rightPadding;
+ for (int i = 0; i < m_columns.count(); ++i) {
+ const QByteArray role = m_columns[i];
+ unusedSpace -= m_columnWidths.value(role);
+ }
+ return qMax(unusedSpace, 0);
+}
+
#include "moc_kitemlistheaderwidget.cpp"
diff --git a/src/kitemviews/private/kitemlistheaderwidget.h b/src/kitemviews/private/kitemlistheaderwidget.h
index a522fa3a2..42dfda503 100644
--- a/src/kitemviews/private/kitemlistheaderwidget.h
+++ b/src/kitemviews/private/kitemlistheaderwidget.h
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]>
+ * SPDX-FileCopyrightText: 2022, 2024 Felix Ernst <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
@@ -50,8 +51,9 @@ public:
void setOffset(qreal offset);
qreal offset() const;
- void setSidePadding(qreal width);
- qreal sidePadding() const;
+ void setSidePadding(qreal leftPaddingWidth, qreal rightPaddingWidth);
+ qreal leftPadding() const;
+ qreal rightPadding() const;
qreal minimumColumnWidth() const;
@@ -64,7 +66,7 @@ Q_SIGNALS:
*/
void columnWidthChanged(const QByteArray &role, qreal currentWidth, qreal previousWidth);
- void sidePaddingChanged(qreal width);
+ void sidePaddingChanged(qreal leftPaddingWidth, qreal rightPaddingWidth);
/**
* Is emitted if the user has released the mouse button after adjusting the
@@ -110,9 +112,9 @@ private Q_SLOTS:
void slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
private:
- enum PaddingGrip {
- Leading,
- Trailing,
+ struct Grip {
+ QByteArray roleToTheLeft;
+ QByteArray roleToTheRight;
};
void paintRole(QPainter *painter, const QByteArray &role, const QRectF &rect, int orderIndex, QWidget *widget = nullptr) const;
@@ -120,8 +122,13 @@ private:
void updatePressedRoleIndex(const QPointF &pos);
void updateHoveredIndex(const QPointF &pos);
int roleIndexAt(const QPointF &pos) const;
- bool isAboveRoleGrip(const QPointF &pos, int roleIndex) const;
- bool isAbovePaddingGrip(const QPointF &pos, PaddingGrip paddingGrip) const;
+
+ /**
+ * @returns std::nullopt if none of the resize grips is below @p position.
+ * Otherwise returns a Grip defined by the two roles on each side of @p position.
+ * @note If the Grip is between a padding and a role, a class-specific "leftPadding" or "rightPadding" role is used.
+ */
+ std::optional<const Grip> isAboveResizeGrip(const QPointF &position) const;
/**
* Creates a pixmap of the role with the index \a roleIndex that is shown
@@ -140,20 +147,26 @@ private:
*/
qreal roleXPosition(const QByteArray &role) const;
-private:
- enum RoleOperation { NoRoleOperation, ResizeRoleOperation, ResizePaddingColumnOperation, MoveRoleOperation };
+ /**
+ * @returns 0 for left-to-right layoutDirection().
+ * Otherwise returns the space that is left over when space is distributed between padding and role columns.
+ * Used to make the column headers stay above their information columns for right-to-left layout directions.
+ */
+ qreal unusedSpace() const;
+private:
bool m_automaticColumnResizing;
KItemModelBase *m_model;
qreal m_offset;
- qreal m_sidePadding;
+ qreal m_leftPadding;
+ qreal m_rightPadding;
QList<QByteArray> m_columns;
QHash<QByteArray, qreal> m_columnWidths;
QHash<QByteArray, qreal> m_preferredColumnWidths;
int m_hoveredIndex;
+ std::optional<Grip> m_pressedGrip;
int m_pressedRoleIndex;
- RoleOperation m_roleOperation;
QPointF m_pressedMousePos;
struct MovingRole {