]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kitemlistheaderwidget.cpp
GIT_SILENT Sync po/docbooks with svn
[dolphin.git] / src / kitemviews / private / kitemlistheaderwidget.cpp
1 /*
2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2022, 2024 Felix Ernst <felixernst@kde.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "kitemlistheaderwidget.h"
9 #include "kitemviews/kitemmodelbase.h"
10
11 #include <QApplication>
12 #include <QGraphicsSceneHoverEvent>
13 #include <QPainter>
14 #include <QStyleOptionHeader>
15
16 namespace
17 {
18 /**
19 * @returns a list which has a reversed order of elements compared to @a list.
20 */
21 QList<QByteArray> reversed(const QList<QByteArray> list)
22 {
23 QList<QByteArray> reversedList;
24 for (auto i = list.rbegin(); i != list.rend(); i++) {
25 reversedList.emplaceBack(*i);
26 }
27 return reversedList;
28 };
29
30 /**
31 * @returns the index of the column for the name/text of items. This depends on the layoutDirection() and column count of @a itemListHeaderWidget.
32 */
33 int nameColumnIndex(const KItemListHeaderWidget *itemListHeaderWidget)
34 {
35 if (itemListHeaderWidget->layoutDirection() == Qt::LeftToRight) {
36 return 0;
37 }
38 return itemListHeaderWidget->columns().count() - 1;
39 };
40 }
41
42 KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget *parent)
43 : QGraphicsWidget(parent)
44 , m_automaticColumnResizing(true)
45 , m_model(nullptr)
46 , m_offset(0)
47 , m_leftPadding(0)
48 , m_rightPadding(0)
49 , m_columns()
50 , m_columnWidths()
51 , m_preferredColumnWidths()
52 , m_hoveredIndex(-1)
53 , m_pressedRoleIndex(-1)
54 , m_pressedMousePos()
55 , m_movingRole()
56 {
57 m_movingRole.x = 0;
58 m_movingRole.xDec = 0;
59 m_movingRole.index = -1;
60
61 setAcceptHoverEvents(true);
62 // TODO update when font changes at runtime
63 setFont(QApplication::font("QHeaderView"));
64 }
65
66 KItemListHeaderWidget::~KItemListHeaderWidget()
67 {
68 }
69
70 void KItemListHeaderWidget::setModel(KItemModelBase *model)
71 {
72 if (m_model == model) {
73 return;
74 }
75
76 if (m_model) {
77 disconnect(m_model, &KItemModelBase::sortRoleChanged, this, &KItemListHeaderWidget::slotSortRoleChanged);
78 disconnect(m_model, &KItemModelBase::sortOrderChanged, this, &KItemListHeaderWidget::slotSortOrderChanged);
79 }
80
81 m_model = model;
82
83 if (m_model) {
84 connect(m_model, &KItemModelBase::sortRoleChanged, this, &KItemListHeaderWidget::slotSortRoleChanged);
85 connect(m_model, &KItemModelBase::sortOrderChanged, this, &KItemListHeaderWidget::slotSortOrderChanged);
86 }
87 }
88
89 KItemModelBase *KItemListHeaderWidget::model() const
90 {
91 return m_model;
92 }
93
94 void KItemListHeaderWidget::setAutomaticColumnResizing(bool automatic)
95 {
96 m_automaticColumnResizing = automatic;
97 }
98
99 bool KItemListHeaderWidget::automaticColumnResizing() const
100 {
101 return m_automaticColumnResizing;
102 }
103
104 void KItemListHeaderWidget::setColumns(const QList<QByteArray> &roles)
105 {
106 for (const QByteArray &role : roles) {
107 if (!m_columnWidths.contains(role)) {
108 m_preferredColumnWidths.remove(role);
109 }
110 }
111
112 m_columns = layoutDirection() == Qt::LeftToRight ? roles : reversed(roles);
113 update();
114 }
115
116 QList<QByteArray> KItemListHeaderWidget::columns() const
117 {
118 return layoutDirection() == Qt::LeftToRight ? m_columns : reversed(m_columns);
119 }
120
121 void KItemListHeaderWidget::setColumnWidth(const QByteArray &role, qreal width)
122 {
123 const qreal minWidth = minimumColumnWidth();
124 if (width < minWidth) {
125 width = minWidth;
126 }
127
128 if (m_columnWidths.value(role) != width) {
129 m_columnWidths.insert(role, width);
130 update();
131 }
132 }
133
134 qreal KItemListHeaderWidget::columnWidth(const QByteArray &role) const
135 {
136 return m_columnWidths.value(role);
137 }
138
139 void KItemListHeaderWidget::setPreferredColumnWidth(const QByteArray &role, qreal width)
140 {
141 m_preferredColumnWidths.insert(role, width);
142 }
143
144 qreal KItemListHeaderWidget::preferredColumnWidth(const QByteArray &role) const
145 {
146 return m_preferredColumnWidths.value(role);
147 }
148
149 void KItemListHeaderWidget::setOffset(qreal offset)
150 {
151 if (m_offset != offset) {
152 m_offset = offset;
153 update();
154 }
155 }
156
157 qreal KItemListHeaderWidget::offset() const
158 {
159 return m_offset;
160 }
161
162 void KItemListHeaderWidget::setSidePadding(qreal leftPaddingWidth, qreal rightPaddingWidth)
163 {
164 bool changed = false;
165 if (m_leftPadding != leftPaddingWidth) {
166 m_leftPadding = leftPaddingWidth;
167 changed = true;
168 }
169
170 if (m_rightPadding != rightPaddingWidth) {
171 m_rightPadding = rightPaddingWidth;
172 changed = true;
173 }
174
175 if (!changed) {
176 return;
177 }
178
179 Q_EMIT sidePaddingChanged(leftPaddingWidth, rightPaddingWidth);
180 update();
181 }
182
183 qreal KItemListHeaderWidget::leftPadding() const
184 {
185 return m_leftPadding;
186 }
187
188 qreal KItemListHeaderWidget::rightPadding() const
189 {
190 return m_rightPadding;
191 }
192
193 qreal KItemListHeaderWidget::minimumColumnWidth() const
194 {
195 QFontMetricsF fontMetrics(font());
196 return fontMetrics.height() * 4;
197 }
198
199 void KItemListHeaderWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
200 {
201 Q_UNUSED(option)
202 Q_UNUSED(widget)
203
204 if (!m_model) {
205 return;
206 }
207
208 // Draw roles
209 painter->setFont(font());
210 painter->setPen(palette().text().color());
211
212 qreal x = -m_offset + m_leftPadding + unusedSpace();
213 int orderIndex = 0;
214 for (const QByteArray &role : std::as_const(m_columns)) {
215 const qreal roleWidth = m_columnWidths.value(role);
216 const QRectF rect(x, 0, roleWidth, size().height());
217 paintRole(painter, role, rect, orderIndex, widget);
218 x += roleWidth;
219 ++orderIndex;
220 }
221
222 if (!m_movingRole.pixmap.isNull()) {
223 painter->drawPixmap(m_movingRole.x, 0, m_movingRole.pixmap);
224 }
225 }
226
227 void KItemListHeaderWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
228 {
229 if (event->button() & Qt::LeftButton) {
230 m_pressedMousePos = event->pos();
231 m_pressedGrip = isAboveResizeGrip(m_pressedMousePos);
232 if (!m_pressedGrip) {
233 updatePressedRoleIndex(event->pos());
234 }
235 event->accept();
236 } else {
237 event->ignore();
238 }
239 }
240
241 void KItemListHeaderWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
242 {
243 QGraphicsWidget::mouseReleaseEvent(event);
244
245 if (m_pressedGrip) {
246 // Emitting a column width change removes automatic column resizing, so we do not emit if only the padding is being changed.
247 // 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
248 // boarder. Such a resize "of the right padding" is let through when automatic column resizing was disabled by that resize.
249 if (m_pressedGrip->roleToTheLeft != "leftPadding" && (m_pressedGrip->roleToTheRight != "rightPadding" || !m_automaticColumnResizing)) {
250 const qreal currentWidth = m_columnWidths.value(m_pressedGrip->roleToTheLeft);
251 Q_EMIT columnWidthChangeFinished(m_pressedGrip->roleToTheLeft, currentWidth);
252 }
253 } else if (m_pressedRoleIndex != -1 && m_movingRole.index == -1) {
254 // Only a click has been done and no moving or resizing has been started
255 const QByteArray sortRole = m_model->sortRole();
256 const int sortRoleIndex = m_columns.indexOf(sortRole);
257 if (m_pressedRoleIndex == sortRoleIndex) {
258 // Toggle the sort order
259 const Qt::SortOrder previous = m_model->sortOrder();
260 const Qt::SortOrder current = (m_model->sortOrder() == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder;
261 m_model->setSortOrder(current);
262 Q_EMIT sortOrderChanged(current, previous);
263 } else {
264 // Change the sort role and reset to the ascending order
265 const QByteArray previous = m_model->sortRole();
266 const QByteArray current = m_columns[m_pressedRoleIndex];
267 const bool resetSortOrder = m_model->sortOrder() == Qt::DescendingOrder;
268 m_model->setSortRole(current, !resetSortOrder);
269 Q_EMIT sortRoleChanged(current, previous);
270
271 if (resetSortOrder) {
272 m_model->setSortOrder(Qt::AscendingOrder);
273 Q_EMIT sortOrderChanged(Qt::AscendingOrder, Qt::DescendingOrder);
274 }
275 }
276 }
277
278 m_movingRole.pixmap = QPixmap();
279 m_movingRole.x = 0;
280 m_movingRole.xDec = 0;
281 m_movingRole.index = -1;
282
283 m_pressedGrip = std::nullopt;
284 m_pressedRoleIndex = -1;
285 update();
286
287 QApplication::restoreOverrideCursor();
288 }
289
290 void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
291 {
292 QGraphicsWidget::mouseMoveEvent(event);
293
294 if (m_pressedGrip) {
295 if (m_pressedGrip->roleToTheLeft == "leftPadding") {
296 qreal currentWidth = m_leftPadding;
297 currentWidth += event->pos().x() - event->lastPos().x();
298 m_leftPadding = qMax(0.0, currentWidth);
299
300 update();
301 Q_EMIT sidePaddingChanged(m_leftPadding, m_rightPadding);
302 return;
303 }
304
305 if (m_pressedGrip->roleToTheRight == "rightPadding") {
306 qreal currentWidth = m_rightPadding;
307 currentWidth -= event->pos().x() - event->lastPos().x();
308 m_rightPadding = qMax(0.0, currentWidth);
309
310 update();
311 Q_EMIT sidePaddingChanged(m_leftPadding, m_rightPadding);
312 if (m_rightPadding > 0.0) {
313 return;
314 }
315 // Continue so resizing of the last column beyond the view width is possible.
316 if (currentWidth > -10) {
317 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.
318 }
319 m_automaticColumnResizing = false;
320 }
321
322 qreal previousWidth = m_columnWidths.value(m_pressedGrip->roleToTheLeft);
323 qreal currentWidth = previousWidth;
324 currentWidth += event->pos().x() - event->lastPos().x();
325 currentWidth = qMax(minimumColumnWidth(), currentWidth);
326
327 m_columnWidths.insert(m_pressedGrip->roleToTheLeft, currentWidth);
328 update();
329
330 Q_EMIT columnWidthChanged(m_pressedGrip->roleToTheLeft, currentWidth, previousWidth);
331 return;
332 }
333
334 if (m_movingRole.index != -1) {
335 // TODO: It should be configurable whether moving the first role is allowed.
336 // In the context of Dolphin this is not required, however this should be
337 // changed if KItemViews are used in a more generic way.
338 if (m_movingRole.index != nameColumnIndex(this)) {
339 m_movingRole.x = event->pos().x() - m_movingRole.xDec;
340 update();
341
342 const int targetIndex = targetOfMovingRole();
343 if (targetIndex > 0 && targetIndex != m_movingRole.index) {
344 const QByteArray role = m_columns[m_movingRole.index];
345 const int previousIndex = m_movingRole.index;
346 m_movingRole.index = targetIndex;
347 if (layoutDirection() == Qt::LeftToRight) {
348 Q_EMIT columnMoved(role, targetIndex, previousIndex);
349 } else {
350 Q_EMIT columnMoved(role, m_columns.count() - 1 - targetIndex, m_columns.count() - 1 - previousIndex);
351 }
352
353 m_movingRole.xDec = event->pos().x() - roleXPosition(role);
354 }
355 }
356 return;
357 }
358
359 if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
360 // A role gets dragged by the user. Create a pixmap of the role that will get
361 // synchronized on each further mouse-move-event with the mouse-position.
362 const int roleIndex = roleIndexAt(m_pressedMousePos);
363 m_movingRole.index = roleIndex;
364 if (roleIndex == nameColumnIndex(this)) {
365 // TODO: It should be configurable whether moving the first role is allowed.
366 // In the context of Dolphin this is not required, however this should be
367 // changed if KItemViews are used in a more generic way.
368 QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor));
369 return;
370 }
371
372 m_movingRole.pixmap = createRolePixmap(roleIndex);
373
374 qreal roleX = -m_offset + m_leftPadding + unusedSpace();
375 for (int i = 0; i < roleIndex; ++i) {
376 const QByteArray role = m_columns[i];
377 roleX += m_columnWidths.value(role);
378 }
379
380 m_movingRole.xDec = event->pos().x() - roleX;
381 m_movingRole.x = roleX;
382 update();
383 }
384 }
385
386 void KItemListHeaderWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
387 {
388 QGraphicsItem::mouseDoubleClickEvent(event);
389
390 const std::optional<Grip> doubleClickedGrip = isAboveResizeGrip(event->pos());
391 if (!doubleClickedGrip || doubleClickedGrip->roleToTheLeft.isEmpty()) {
392 return;
393 }
394
395 qreal previousWidth = columnWidth(doubleClickedGrip->roleToTheLeft);
396 setColumnWidth(doubleClickedGrip->roleToTheLeft, preferredColumnWidth(doubleClickedGrip->roleToTheLeft));
397 qreal currentWidth = columnWidth(doubleClickedGrip->roleToTheLeft);
398
399 Q_EMIT columnWidthChanged(doubleClickedGrip->roleToTheLeft, currentWidth, previousWidth);
400 Q_EMIT columnWidthChangeFinished(doubleClickedGrip->roleToTheLeft, currentWidth);
401 }
402
403 void KItemListHeaderWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
404 {
405 QGraphicsWidget::hoverEnterEvent(event);
406 updateHoveredIndex(event->pos());
407 }
408
409 void KItemListHeaderWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
410 {
411 QGraphicsWidget::hoverLeaveEvent(event);
412 if (m_hoveredIndex != -1) {
413 Q_EMIT columnUnHovered(m_hoveredIndex);
414 m_hoveredIndex = -1;
415 update();
416 }
417 }
418
419 void KItemListHeaderWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
420 {
421 QGraphicsWidget::hoverMoveEvent(event);
422
423 const QPointF &pos = event->pos();
424 updateHoveredIndex(pos);
425 if (isAboveResizeGrip(pos)) {
426 setCursor(Qt::SplitHCursor);
427 } else {
428 unsetCursor();
429 }
430 }
431
432 void KItemListHeaderWidget::slotSortRoleChanged(const QByteArray &current, const QByteArray &previous)
433 {
434 Q_UNUSED(current)
435 Q_UNUSED(previous)
436 update();
437 }
438
439 void KItemListHeaderWidget::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
440 {
441 Q_UNUSED(current)
442 Q_UNUSED(previous)
443 update();
444 }
445
446 void KItemListHeaderWidget::paintRole(QPainter *painter, const QByteArray &role, const QRectF &rect, int orderIndex, QWidget *widget) const
447 {
448 // The following code is based on the code from QHeaderView::paintSection().
449 // SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies).
450 QStyleOptionHeader option;
451 option.section = orderIndex;
452 option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
453 if (isEnabled()) {
454 option.state |= QStyle::State_Enabled;
455 }
456 if (window() && window()->isActiveWindow()) {
457 option.state |= QStyle::State_Active;
458 }
459 if (m_hoveredIndex == orderIndex) {
460 option.state |= QStyle::State_MouseOver;
461 }
462 if (m_pressedRoleIndex == orderIndex) {
463 option.state |= QStyle::State_Sunken;
464 }
465 if (m_model->sortRole() == role) {
466 option.sortIndicator = (m_model->sortOrder() == Qt::AscendingOrder) ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
467 }
468 option.rect = rect.toRect();
469 option.orientation = Qt::Horizontal;
470 option.selectedPosition = QStyleOptionHeader::NotAdjacent;
471 option.text = m_model->roleDescription(role);
472
473 // First we paint any potential empty (padding) space on left and/or right of this role's column.
474 const auto paintPadding = [&](int section, const QRectF &rect, const QStyleOptionHeader::SectionPosition &pos) {
475 QStyleOptionHeader padding;
476 padding.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
477 padding.section = section;
478 padding.sortIndicator = QStyleOptionHeader::None;
479 padding.rect = rect.toRect();
480 padding.position = pos;
481 padding.text = QString();
482 style()->drawControl(QStyle::CE_Header, &padding, painter, widget);
483 };
484
485 if (m_columns.count() == 1) {
486 option.position = QStyleOptionHeader::Middle;
487 paintPadding(0, QRectF(0.0, 0.0, rect.left(), rect.height()), QStyleOptionHeader::Beginning);
488 paintPadding(1, QRectF(rect.right(), 0.0, size().width() - rect.right(), rect.height()), QStyleOptionHeader::End);
489 } else if (orderIndex == 0) {
490 // Paint the header for the first column; check if there is some empty space to the left which needs to be filled.
491 if (rect.left() > 0) {
492 option.position = QStyleOptionHeader::Middle;
493 paintPadding(0, QRectF(0.0, 0.0, rect.left(), rect.height()), QStyleOptionHeader::Beginning);
494 } else {
495 option.position = QStyleOptionHeader::Beginning;
496 }
497 } else if (orderIndex == m_columns.count() - 1) {
498 // Paint the header for the last column; check if there is some empty space to the right which needs to be filled.
499 if (rect.right() < size().width()) {
500 option.position = QStyleOptionHeader::Middle;
501 paintPadding(m_columns.count(), QRectF(rect.right(), 0.0, size().width() - rect.right(), rect.height()), QStyleOptionHeader::End);
502 } else {
503 option.position = QStyleOptionHeader::End;
504 }
505 } else {
506 option.position = QStyleOptionHeader::Middle;
507 }
508
509 style()->drawControl(QStyle::CE_Header, &option, painter, widget);
510 }
511
512 void KItemListHeaderWidget::updatePressedRoleIndex(const QPointF &pos)
513 {
514 const int pressedIndex = roleIndexAt(pos);
515 if (m_pressedRoleIndex != pressedIndex) {
516 m_pressedRoleIndex = pressedIndex;
517 update();
518 }
519 }
520
521 void KItemListHeaderWidget::updateHoveredIndex(const QPointF &pos)
522 {
523 const int hoverIndex = isAboveResizeGrip(pos) ? -1 : roleIndexAt(pos);
524
525 if (m_hoveredIndex != hoverIndex) {
526 if (m_hoveredIndex != -1) {
527 Q_EMIT columnUnHovered(m_hoveredIndex);
528 }
529 m_hoveredIndex = hoverIndex;
530 if (m_hoveredIndex != -1) {
531 Q_EMIT columnHovered(m_hoveredIndex);
532 }
533 update();
534 }
535 }
536
537 int KItemListHeaderWidget::roleIndexAt(const QPointF &pos) const
538 {
539 qreal x = -m_offset + m_leftPadding + unusedSpace();
540 if (pos.x() < x) {
541 return -1;
542 }
543
544 int index = -1;
545 for (const QByteArray &role : std::as_const(m_columns)) {
546 ++index;
547 x += m_columnWidths.value(role);
548 if (pos.x() <= x) {
549 return index;
550 }
551 }
552
553 return -1;
554 }
555
556 std::optional<const KItemListHeaderWidget::Grip> KItemListHeaderWidget::isAboveResizeGrip(const QPointF &position) const
557 {
558 qreal x = -m_offset + m_leftPadding + unusedSpace();
559 const int gripWidthTolerance = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
560
561 if (x - gripWidthTolerance < position.x() && position.x() < x + gripWidthTolerance) {
562 return std::optional{Grip{"leftPadding", m_columns[0]}};
563 }
564
565 for (int i = 0; i < m_columns.count(); ++i) {
566 const QByteArray role = m_columns[i];
567 x += m_columnWidths.value(role);
568 if (x - gripWidthTolerance < position.x() && position.x() < x + gripWidthTolerance) {
569 if (i + 1 < m_columns.count()) {
570 return std::optional{Grip{m_columns[i], m_columns[i + 1]}};
571 }
572 return std::optional{Grip{m_columns[i], "rightPadding"}};
573 }
574 }
575 return std::nullopt;
576 }
577
578 QPixmap KItemListHeaderWidget::createRolePixmap(int roleIndex) const
579 {
580 const QByteArray role = m_columns[roleIndex];
581 const qreal roleWidth = m_columnWidths.value(role);
582 const QRect rect(0, 0, roleWidth, size().height());
583
584 QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied);
585
586 QPainter painter(&image);
587 paintRole(&painter, role, rect, roleIndex);
588
589 // Apply a highlighting-color
590 const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
591 QColor highlightColor = palette().color(group, QPalette::Highlight);
592 highlightColor.setAlpha(64);
593 painter.fillRect(rect, highlightColor);
594
595 // Make the image transparent
596 painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
597 painter.fillRect(0, 0, image.width(), image.height(), QColor(0, 0, 0, 192));
598
599 return QPixmap::fromImage(image);
600 }
601
602 int KItemListHeaderWidget::targetOfMovingRole() const
603 {
604 const int movingWidth = m_movingRole.pixmap.width();
605 const int movingLeft = m_movingRole.x;
606 const int movingRight = movingLeft + movingWidth - 1;
607
608 int targetIndex = 0;
609 qreal targetLeft = -m_offset + m_leftPadding + unusedSpace();
610 while (targetIndex < m_columns.count()) {
611 const QByteArray role = m_columns[targetIndex];
612 const qreal targetWidth = m_columnWidths.value(role);
613 const qreal targetRight = targetLeft + targetWidth - 1;
614
615 const bool isInTarget = (targetWidth >= movingWidth && movingLeft >= targetLeft && movingRight <= targetRight)
616 || (targetWidth < movingWidth && movingLeft <= targetLeft && movingRight >= targetRight);
617
618 if (isInTarget) {
619 return targetIndex;
620 }
621
622 targetLeft += targetWidth;
623 ++targetIndex;
624 }
625
626 return m_movingRole.index;
627 }
628
629 qreal KItemListHeaderWidget::roleXPosition(const QByteArray &role) const
630 {
631 qreal x = -m_offset + m_leftPadding + unusedSpace();
632 for (const QByteArray &visibleRole : std::as_const(m_columns)) {
633 if (visibleRole == role) {
634 return x;
635 }
636
637 x += m_columnWidths.value(visibleRole);
638 }
639
640 return -1;
641 }
642
643 qreal KItemListHeaderWidget::unusedSpace() const
644 {
645 if (layoutDirection() == Qt::LeftToRight) {
646 return 0;
647 }
648 int unusedSpace = size().width() - m_leftPadding - m_rightPadding;
649 for (int i = 0; i < m_columns.count(); ++i) {
650 const QByteArray role = m_columns[i];
651 unusedSpace -= m_columnWidths.value(role);
652 }
653 return qMax(unusedSpace, 0);
654 }
655
656 #include "moc_kitemlistheaderwidget.cpp"