From 240d33ce17fc531e8bc6638af8f71454fe7c05e6 Mon Sep 17 00:00:00 2001 From: Eugene Popov Date: Wed, 27 Mar 2024 10:28:15 +0000 Subject: [PATCH] Better support for RTL This MR fixes some issues related to RTL scripts: - wrong layout in Compact View mode - broken horizontal scrolling in Icon View and Details View modes - broken navigation with left and right arrow keys in Details View mode BUG: 484012 BUG: 449493 --- src/kitemviews/kitemlistcontainer.cpp | 5 ++ src/kitemviews/kitemlistcontroller.cpp | 62 ++++++++++++------- src/kitemviews/kitemlistview.cpp | 4 ++ src/kitemviews/kstandarditemlistwidget.cpp | 13 +++- .../private/kitemlistviewlayouter.cpp | 30 ++++++--- src/tests/kitemlistcontrollertest.cpp | 2 +- 6 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/kitemviews/kitemlistcontainer.cpp b/src/kitemviews/kitemlistcontainer.cpp index 1cac8f7a60..2f9f5d401b 100644 --- a/src/kitemviews/kitemlistcontainer.cpp +++ b/src/kitemviews/kitemlistcontainer.cpp @@ -399,6 +399,11 @@ void KItemListContainer::updateSmoothScrollers(Qt::Orientation orientation) m_horizontalSmoothScroller->setPropertyName("scrollOffset"); m_verticalSmoothScroller->setPropertyName("itemOffset"); } + + const bool isRightToLeft = m_controller->view()->layoutDirection() == Qt::RightToLeft; + QScrollBar *hScrollBar = horizontalScrollBar(); + hScrollBar->setInvertedAppearance(isRightToLeft && orientation == Qt::Vertical); + hScrollBar->setInvertedControls(!isRightToLeft || orientation == Qt::Vertical); } void KItemListContainer::updateScrollOffsetScrollBarPolicy() diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index 3c1b088bec..71c37a7aff 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -241,6 +241,8 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) int key = event->key(); const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; + const bool horizontalScrolling = m_view->scrollOrientation() == Qt::Horizontal; + // Handle the expanding/collapsing of items // expand / collapse all selected directories if (m_view->supportsItemExpanding() && m_model->isExpandable(index) && (key == Qt::Key_Right || key == Qt::Key_Left)) { @@ -276,7 +278,7 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) // For horizontal scroll orientation, transform // the arrow keys to simplify the event handling. - if (m_view->scrollOrientation() == Qt::Horizontal) { + if (horizontalScrolling) { switch (key) { case Qt::Key_Up: key = Qt::Key_Left; @@ -295,17 +297,31 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) } } - // For right to left languages, exchange right and left arrow keys. if (m_view->layoutDirection() == Qt::RightToLeft) { - switch (key) { - case Qt::Key_Left: - key = Qt::Key_Right; - break; - case Qt::Key_Right: - key = Qt::Key_Left; - break; - default: - break; + if (horizontalScrolling) { + // swap up and down arrow keys + switch (key) { + case Qt::Key_Up: + key = Qt::Key_Down; + break; + case Qt::Key_Down: + key = Qt::Key_Up; + break; + default: + break; + } + } else if (!m_view->supportsItemExpanding()) { + // swap left and right arrow keys + switch (key) { + case Qt::Key_Left: + key = Qt::Key_Right; + break; + case Qt::Key_Right: + key = Qt::Key_Left; + break; + default: + break; + } } } @@ -371,7 +387,7 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) break; case Qt::Key_PageUp: - if (m_view->scrollOrientation() == Qt::Horizontal) { + if (horizontalScrolling) { // The new current index should correspond to the first item in the current column. int newIndex = qMax(index - 1, 0); while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() < m_view->itemRect(index).topLeft().y()) { @@ -399,7 +415,7 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) break; case Qt::Key_PageDown: - if (m_view->scrollOrientation() == Qt::Horizontal) { + if (horizontalScrolling) { // The new current index should correspond to the last item in the current column. int newIndex = qMin(index + 1, m_model->count() - 1); while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() > m_view->itemRect(index).topLeft().y()) { @@ -1486,12 +1502,12 @@ int KItemListController::nextRowIndex(int index) const return index; } - const bool leftToRight = m_view->layoutDirection() != Qt::RightToLeft; + const bool reversed = m_view->layoutDirection() == Qt::RightToLeft && m_view->scrollOrientation() == Qt::Vertical; // Calculate the index of the last column inside the row of the current index int lastColumnIndex = index; - while ((leftToRight && keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) - || (!leftToRight && keyboardAnchorPos(lastColumnIndex + 1) < keyboardAnchorPos(lastColumnIndex))) { + while ((!reversed && keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) + || (reversed && keyboardAnchorPos(lastColumnIndex + 1) < keyboardAnchorPos(lastColumnIndex))) { ++lastColumnIndex; if (lastColumnIndex >= maxIndex) { return index; @@ -1504,8 +1520,8 @@ int KItemListController::nextRowIndex(int index) const int searchIndex = nextRowIndex; qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex)); while (searchIndex < maxIndex - && ((leftToRight && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) - || (!leftToRight && keyboardAnchorPos(searchIndex + 1) < keyboardAnchorPos(searchIndex)))) { + && ((!reversed && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) + || (reversed && keyboardAnchorPos(searchIndex + 1) < keyboardAnchorPos(searchIndex)))) { ++searchIndex; const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex)); if (searchDiff < minDiff) { @@ -1523,12 +1539,12 @@ int KItemListController::previousRowIndex(int index) const return index; } - const bool leftToRight = m_view->layoutDirection() != Qt::RightToLeft; + const bool reversed = m_view->layoutDirection() == Qt::RightToLeft && m_view->scrollOrientation() == Qt::Vertical; // Calculate the index of the first column inside the row of the current index int firstColumnIndex = index; - while ((leftToRight && keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) - || (!leftToRight && keyboardAnchorPos(firstColumnIndex - 1) > keyboardAnchorPos(firstColumnIndex))) { + while ((!reversed && keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) + || (reversed && keyboardAnchorPos(firstColumnIndex - 1) > keyboardAnchorPos(firstColumnIndex))) { --firstColumnIndex; if (firstColumnIndex <= 0) { return index; @@ -1541,8 +1557,8 @@ int KItemListController::previousRowIndex(int index) const int searchIndex = previousRowIndex; qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex)); while (searchIndex > 0 - && ((leftToRight && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) - || (!leftToRight && keyboardAnchorPos(searchIndex - 1) > keyboardAnchorPos(searchIndex)))) { + && ((!reversed && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) + || (reversed && keyboardAnchorPos(searchIndex - 1) > keyboardAnchorPos(searchIndex)))) { --searchIndex; const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex)); if (searchDiff < minDiff) { diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index f906b7a13d..e291ee5ecd 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -550,6 +550,10 @@ void KItemListView::scrollToItem(int index, ViewItemPosition viewItemPosition) } QRectF currentRect = itemRect(index); + if (layoutDirection() == Qt::RightToLeft && scrollOrientation() == Qt::Horizontal) { + currentRect.moveTo(m_layouter->size().width() - currentRect.right(), 0); + } + // Fix for Bug 311099 - View the underscore when using Ctrl + PageDown currentRect.adjust(-m_styleOption.horizontalMargin, -m_styleOption.verticalMargin, m_styleOption.horizontalMargin, m_styleOption.verticalMargin); diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 2ac8f4f693..0d3cfed1a5 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -1132,7 +1132,11 @@ void KStandardItemListWidget::updatePixmapCache() } else { // Center horizontally and vertically within the icon-area const TextInfo *textInfo = m_textInfo.value("text"); - m_pixmapPos.setX(textInfo->pos.x() - 2.0 * padding - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0); + if (QApplication::isRightToLeft() && m_layout == CompactLayout) { + m_pixmapPos.setX(size().width() - padding - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0); + } else { + m_pixmapPos.setX(textInfo->pos.x() - 2.0 * padding - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0); + } // Derive icon's vertical center from the center of the text frame, including // any necessary adjustment if the font's midline is offset from the frame center @@ -1175,6 +1179,9 @@ void KStandardItemListWidget::updateTextsCache() textOption.setAlignment(Qt::AlignHCenter); break; case CompactLayout: + textOption.setAlignment(QApplication::isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft); + textOption.setWrapMode(QTextOption::NoWrap); + break; case DetailsLayout: textOption.setAlignment(Qt::AlignLeft); textOption.setWrapMode(QTextOption::NoWrap); @@ -1404,9 +1411,9 @@ void KStandardItemListWidget::updateCompactLayoutTextCache() const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing; qreal maximumRequiredTextWidth = 0; - const qreal x = option.padding * 3 + iconSize(); + const qreal x = QApplication::isRightToLeft() ? option.padding : option.padding * 3 + iconSize(); qreal y = qRound((widgetHeight - textLinesHeight) / 2); - const qreal maxWidth = size().width() - x - option.padding; + const qreal maxWidth = size().width() - iconSize() - 4 * option.padding; for (const QByteArray &role : std::as_const(m_sortedVisibleRoles)) { const QString text = escapeString(roleText(role, values)); TextInfo *textInfo = m_textInfo.value(role); diff --git a/src/kitemviews/private/kitemlistviewlayouter.cpp b/src/kitemviews/private/kitemlistviewlayouter.cpp index 5b0df0bd02..a2d92f0deb 100644 --- a/src/kitemviews/private/kitemlistviewlayouter.cpp +++ b/src/kitemviews/private/kitemlistviewlayouter.cpp @@ -226,8 +226,12 @@ QRectF KItemListViewLayouter::itemRect(int index) const // Rotate the logical direction which is always vertical by 90° // to get the physical horizontal direction QPointF pos(y, x); - pos.rx() -= m_scrollOffset; sizeHint.transpose(); + if (QGuiApplication::isRightToLeft()) { + pos.rx() = m_size.width() + m_scrollOffset - pos.x() - sizeHint.width(); + } else { + pos.rx() -= m_scrollOffset; + } return QRectF(pos, sizeHint); } @@ -361,7 +365,7 @@ void KItemListViewLayouter::doLayout() const bool grouped = createGroupHeaders(); - const bool horizontalScrolling = (m_scrollOrientation == Qt::Horizontal); + const bool horizontalScrolling = m_scrollOrientation == Qt::Horizontal; if (horizontalScrolling) { // Flip everything so that the layout logically can work like having // a vertical scrolling @@ -377,8 +381,9 @@ void KItemListViewLayouter::doLayout() } } + const bool isRightToLeft = QGuiApplication::isRightToLeft(); m_columnWidth = itemSize.width() + itemMargin.width(); - const qreal widthForColumns = size.width() - itemMargin.width(); + const qreal widthForColumns = std::max(size.width() - itemMargin.width(), m_columnWidth); m_columnCount = qMax(1, int(widthForColumns / m_columnWidth)); m_xPosInc = itemMargin.width(); @@ -397,7 +402,7 @@ void KItemListViewLayouter::doLayout() // Calculate the offset of each column, i.e., the x-coordinate where the column starts. m_columnOffsets.resize(m_columnCount); - qreal currentOffset = QGuiApplication::isRightToLeft() ? widthForColumns : m_xPosInc; + qreal currentOffset = isRightToLeft ? widthForColumns : m_xPosInc; if (grouped && horizontalScrolling) { // All group headers will always be aligned on the top and not @@ -405,16 +410,21 @@ void KItemListViewLayouter::doLayout() currentOffset += m_groupHeaderHeight; } - if (QGuiApplication::isLeftToRight()) + if (isRightToLeft) { + for (int column = 0; column < m_columnCount; ++column) { + if (horizontalScrolling) { + m_columnOffsets[column] = column * m_columnWidth; + } else { + currentOffset -= m_columnWidth; + m_columnOffsets[column] = currentOffset; + } + } + } else { for (int column = 0; column < m_columnCount; ++column) { m_columnOffsets[column] = currentOffset; currentOffset += m_columnWidth; } - else - for (int column = 0; column < m_columnCount; ++column) { - m_columnOffsets[column] = currentOffset - m_columnWidth; - currentOffset -= m_columnWidth; - } + } // Prepare the QVector which stores the y-coordinate for each new row. int numberOfRows = (itemCount + m_columnCount - 1) / m_columnCount; diff --git a/src/tests/kitemlistcontrollertest.cpp b/src/tests/kitemlistcontrollertest.cpp index 56d3839589..40b2cecaad 100644 --- a/src/tests/kitemlistcontrollertest.cpp +++ b/src/tests/kitemlistcontrollertest.cpp @@ -309,7 +309,7 @@ void KItemListControllerTest::testKeyboardNavigation_data() std::swap(nextItemKey, previousItemKey); break; case KFileItemListView::CompactLayout: - std::swap(nextItemKey, previousItemKey); + std::swap(nextRowKey, previousRowKey); break; default: break;