-/***************************************************************************
- * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
- ***************************************************************************/
+/*
+ * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
#include "kitemlistheaderwidget.h"
#include "kitemviews/kitemmodelbase.h"
m_automaticColumnResizing(true),
m_model(nullptr),
m_offset(0),
+ m_leadingPadding(0),
m_columns(),
m_columnWidths(),
m_preferredColumnWidths(),
void KItemListHeaderWidget::setColumns(const QList<QByteArray>& roles)
{
- foreach (const QByteArray& role, roles) {
+ for (const QByteArray& role : roles) {
if (!m_columnWidths.contains(role)) {
- m_columnWidths.remove(role);
m_preferredColumnWidths.remove(role);
}
}
return m_offset;
}
+void KItemListHeaderWidget::setLeadingPadding(qreal width)
+{
+ if (m_leadingPadding != width) {
+ m_leadingPadding = width;
+ leadingPaddingChanged(width);
+ update();
+ }
+}
+
+qreal KItemListHeaderWidget::leadingPadding() const
+{
+ return m_leadingPadding;
+}
+
qreal KItemListHeaderWidget::minimumColumnWidth() const
{
QFontMetricsF fontMetrics(font());
void KItemListHeaderWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
- Q_UNUSED(option);
- Q_UNUSED(widget);
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
if (!m_model) {
return;
painter->setFont(font());
painter->setPen(palette().text().color());
- qreal x = -m_offset;
+ qreal x = -m_offset + m_leadingPadding;
int orderIndex = 0;
- foreach (const QByteArray& role, m_columns) {
+ for (const QByteArray& role : qAsConst(m_columns)) {
const qreal roleWidth = m_columnWidths.value(role);
const QRectF rect(x, 0, roleWidth, size().height());
paintRole(painter, role, rect, orderIndex, widget);
void KItemListHeaderWidget::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
if (event->button() & Qt::LeftButton) {
- updatePressedRoleIndex(event->pos());
m_pressedMousePos = event->pos();
- m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
- ResizeRoleOperation : NoRoleOperation;
+ if (isAbovePaddingGrip(m_pressedMousePos, PaddingGrip::Leading)) {
+ m_roleOperation = ResizeLeadingColumnOperation;
+ } else {
+ updatePressedRoleIndex(event->pos());
+ m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
+ ResizeRoleOperation : NoRoleOperation;
+ }
event->accept();
} else {
event->ignore();
const Qt::SortOrder current = (m_model->sortOrder() == Qt::AscendingOrder) ?
Qt::DescendingOrder : Qt::AscendingOrder;
m_model->setSortOrder(current);
- emit sortOrderChanged(current, previous);
+ Q_EMIT sortOrderChanged(current, previous);
} else {
// Change the sort role and reset to the ascending order
const QByteArray previous = m_model->sortRole();
const QByteArray current = m_columns[m_pressedRoleIndex];
const bool resetSortOrder = m_model->sortOrder() == Qt::DescendingOrder;
m_model->setSortRole(current, !resetSortOrder);
- emit sortRoleChanged(current, previous);
+ Q_EMIT sortRoleChanged(current, previous);
if (resetSortOrder) {
m_model->setSortOrder(Qt::AscendingOrder);
- emit sortOrderChanged(Qt::AscendingOrder, Qt::DescendingOrder);
+ Q_EMIT sortOrderChanged(Qt::AscendingOrder, Qt::DescendingOrder);
}
}
break;
case ResizeRoleOperation: {
const QByteArray pressedRole = m_columns[m_pressedRoleIndex];
const qreal currentWidth = m_columnWidths.value(pressedRole);
- emit columnWidthChangeFinished(pressedRole, currentWidth);
+ Q_EMIT columnWidthChangeFinished(pressedRole, currentWidth);
break;
}
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 furter mouse-move-event with the mouse-position.
+ // synchronized on each further mouse-move-event with the mouse-position.
m_roleOperation = MoveRoleOperation;
const int roleIndex = roleIndexAt(m_pressedMousePos);
m_movingRole.index = roleIndex;
} else {
m_movingRole.pixmap = createRolePixmap(roleIndex);
- qreal roleX = -m_offset;
+ qreal roleX = -m_offset + m_leadingPadding;
for (int i = 0; i < roleIndex; ++i) {
const QByteArray role = m_columns[i];
roleX += m_columnWidths.value(role);
m_columnWidths.insert(pressedRole, currentWidth);
update();
- emit columnWidthChanged(pressedRole, currentWidth, previousWidth);
+ Q_EMIT columnWidthChanged(pressedRole, currentWidth, previousWidth);
+ break;
+ }
+
+ case ResizeLeadingColumnOperation: {
+ qreal currentWidth = m_leadingPadding;
+ currentWidth += event->pos().x() - event->lastPos().x();
+ currentWidth = qMax(0.0, currentWidth);
+
+ m_leadingPadding = currentWidth;
+
+ update();
+
+ Q_EMIT leadingPaddingChanged(currentWidth);
+
break;
}
const QByteArray role = m_columns[m_movingRole.index];
const int previousIndex = m_movingRole.index;
m_movingRole.index = targetIndex;
- emit columnMoved(role, targetIndex, previousIndex);
+ Q_EMIT columnMoved(role, targetIndex, previousIndex);
m_movingRole.xDec = event->pos().x() - roleXPosition(role);
}
setColumnWidth(role, preferredColumnWidth(role));
qreal currentWidth = columnWidth(role);
- emit columnWidthChanged(role, currentWidth, previousWidth);
- emit columnWidthChangeFinished(role, currentWidth);
+ Q_EMIT columnWidthChanged(role, currentWidth, previousWidth);
+ Q_EMIT columnWidthChangeFinished(role, currentWidth);
}
}
const QPointF& pos = event->pos();
updateHoveredRoleIndex(pos);
- if (m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) {
+ if ((m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) ||
+ isAbovePaddingGrip(pos, PaddingGrip::Leading) ||
+ isAbovePaddingGrip(pos, PaddingGrip::Trailing)) {
setCursor(Qt::SplitHCursor);
} else {
unsetCursor();
void KItemListHeaderWidget::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
{
- Q_UNUSED(current);
- Q_UNUSED(previous);
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
update();
}
void KItemListHeaderWidget::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
{
- Q_UNUSED(current);
- Q_UNUSED(previous);
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
update();
}
QWidget* widget) const
{
// The following code is based on the code from QHeaderView::paintSection().
- // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ // SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies).
QStyleOptionHeader option;
option.section = orderIndex;
option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
}
option.rect = rect.toRect();
+ option.orientation = Qt::Horizontal;
+ option.selectedPosition = QStyleOptionHeader::NotAdjacent;
+ option.text = m_model->roleDescription(role);
- bool paintBackgroundForEmptyArea = false;
+ // First we paint any potential empty (padding) space on left and/or right of this role's column.
+ const auto paintPadding = [&](int section, const QRectF &rect, const QStyleOptionHeader::SectionPosition &pos){
+ QStyleOptionHeader padding;
+ padding.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
+ padding.section = section;
+ padding.sortIndicator = QStyleOptionHeader::None;
+ padding.rect = rect.toRect();
+ padding.position = pos;
+ padding.text = QString();
+ style()->drawControl(QStyle::CE_Header, &padding, painter, widget);
+ };
if (m_columns.count() == 1) {
- option.position = QStyleOptionHeader::OnlyOneSection;
+ 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);
} else if (orderIndex == 0) {
- option.position = QStyleOptionHeader::Beginning;
+ // 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) {
+ option.position = QStyleOptionHeader::Middle;
+ paintPadding(0,QRectF(0.0, 0.0, rect.left(), rect.height()), QStyleOptionHeader::Beginning);
+ } else {
+ option.position = QStyleOptionHeader::Beginning;
+ }
} else if (orderIndex == m_columns.count() - 1) {
- // We are just painting the header for the last column. Check if there
- // is some empty space to the right which needs to be filled.
+ // 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;
- paintBackgroundForEmptyArea = true;
+ paintPadding(m_columns.count(), QRectF(rect.left(), 0.0, size().width() - rect.left(), rect.height()), QStyleOptionHeader::End);
} else {
option.position = QStyleOptionHeader::End;
}
option.position = QStyleOptionHeader::Middle;
}
- option.orientation = Qt::Horizontal;
- option.selectedPosition = QStyleOptionHeader::NotAdjacent;
- option.text = m_model->roleDescription(role);
-
style()->drawControl(QStyle::CE_Header, &option, painter, widget);
-
- if (paintBackgroundForEmptyArea) {
- option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
- option.section = m_columns.count();
- option.sortIndicator = QStyleOptionHeader::None;
-
- qreal backgroundRectX = rect.x() + rect.width();
- QRectF backgroundRect(backgroundRectX, 0.0, size().width() - backgroundRectX, rect.height());
- option.rect = backgroundRect.toRect();
- option.position = QStyleOptionHeader::End;
- option.text = QString();
-
- style()->drawControl(QStyle::CE_Header, &option, painter, widget);
- }
}
void KItemListHeaderWidget::updatePressedRoleIndex(const QPointF& pos)
{
int index = -1;
- qreal x = -m_offset;
- foreach (const QByteArray& role, m_columns) {
+ qreal x = -m_offset + m_leadingPadding;
+ for (const QByteArray& role : qAsConst(m_columns)) {
++index;
x += m_columnWidths.value(role);
if (pos.x() <= x) {
bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
{
- qreal x = -m_offset;
+ qreal x = -m_offset + m_leadingPadding;
for (int i = 0; i <= roleIndex; ++i) {
const QByteArray role = m_columns[i];
x += m_columnWidths.value(role);
return pos.x() >= (x - grip) && pos.x() <= x;
}
+bool KItemListHeaderWidget::isAbovePaddingGrip(const QPointF& pos, PaddingGrip paddingGrip) const
+{
+ const qreal lx = -m_offset + m_leadingPadding;
+ const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
+
+ switch (paddingGrip) {
+ case Leading:
+ return pos.x() >= (lx - grip) && pos.x() <= lx;
+ case Trailing:
+ {
+ qreal rx = lx;
+ for (const QByteArray& role : qAsConst(m_columns)) {
+ rx += m_columnWidths.value(role);
+ }
+ return pos.x() >= (rx - grip) && pos.x() <= rx;
+ }
+ default:
+ return false;
+ }
+}
+
QPixmap KItemListHeaderWidget::createRolePixmap(int roleIndex) const
{
const QByteArray role = m_columns[roleIndex];
const int movingRight = movingLeft + movingWidth - 1;
int targetIndex = 0;
- qreal targetLeft = -m_offset;
+ qreal targetLeft = -m_offset + m_leadingPadding;
while (targetIndex < m_columns.count()) {
const QByteArray role = m_columns[targetIndex];
const qreal targetWidth = m_columnWidths.value(role);
qreal KItemListHeaderWidget::roleXPosition(const QByteArray& role) const
{
- qreal x = -m_offset;
- foreach (const QByteArray& visibleRole, m_columns) {
+ qreal x = -m_offset + m_leadingPadding;
+ for (const QByteArray& visibleRole : qAsConst(m_columns)) {
if (visibleRole == role) {
return x;
}