1
0
mirror of https://invent.kde.org/system/dolphin synced 2024-07-04 17:30:55 +00:00

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
This commit is contained in:
Eugene Popov 2024-03-27 10:28:15 +00:00 committed by Méven Car
parent 7a7cab61b6
commit 240d33ce17
6 changed files with 79 additions and 37 deletions

View File

@ -399,6 +399,11 @@ void KItemListContainer::updateSmoothScrollers(Qt::Orientation orientation)
m_horizontalSmoothScroller->setPropertyName("scrollOffset"); m_horizontalSmoothScroller->setPropertyName("scrollOffset");
m_verticalSmoothScroller->setPropertyName("itemOffset"); 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() void KItemListContainer::updateScrollOffsetScrollBarPolicy()

View File

@ -241,6 +241,8 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
int key = event->key(); int key = event->key();
const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
const bool horizontalScrolling = m_view->scrollOrientation() == Qt::Horizontal;
// Handle the expanding/collapsing of items // Handle the expanding/collapsing of items
// expand / collapse all selected directories // expand / collapse all selected directories
if (m_view->supportsItemExpanding() && m_model->isExpandable(index) && (key == Qt::Key_Right || key == Qt::Key_Left)) { 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 // For horizontal scroll orientation, transform
// the arrow keys to simplify the event handling. // the arrow keys to simplify the event handling.
if (m_view->scrollOrientation() == Qt::Horizontal) { if (horizontalScrolling) {
switch (key) { switch (key) {
case Qt::Key_Up: case Qt::Key_Up:
key = Qt::Key_Left; 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) { if (m_view->layoutDirection() == Qt::RightToLeft) {
switch (key) { if (horizontalScrolling) {
case Qt::Key_Left: // swap up and down arrow keys
key = Qt::Key_Right; switch (key) {
break; case Qt::Key_Up:
case Qt::Key_Right: key = Qt::Key_Down;
key = Qt::Key_Left; break;
break; case Qt::Key_Down:
default: key = Qt::Key_Up;
break; 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; break;
case Qt::Key_PageUp: 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. // The new current index should correspond to the first item in the current column.
int newIndex = qMax(index - 1, 0); int newIndex = qMax(index - 1, 0);
while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() < m_view->itemRect(index).topLeft().y()) { 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; break;
case Qt::Key_PageDown: 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. // The new current index should correspond to the last item in the current column.
int newIndex = qMin(index + 1, m_model->count() - 1); int newIndex = qMin(index + 1, m_model->count() - 1);
while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() > m_view->itemRect(index).topLeft().y()) { 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; 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 // Calculate the index of the last column inside the row of the current index
int lastColumnIndex = index; int lastColumnIndex = index;
while ((leftToRight && keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) while ((!reversed && keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex))
|| (!leftToRight && keyboardAnchorPos(lastColumnIndex + 1) < keyboardAnchorPos(lastColumnIndex))) { || (reversed && keyboardAnchorPos(lastColumnIndex + 1) < keyboardAnchorPos(lastColumnIndex))) {
++lastColumnIndex; ++lastColumnIndex;
if (lastColumnIndex >= maxIndex) { if (lastColumnIndex >= maxIndex) {
return index; return index;
@ -1504,8 +1520,8 @@ int KItemListController::nextRowIndex(int index) const
int searchIndex = nextRowIndex; int searchIndex = nextRowIndex;
qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex)); qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex));
while (searchIndex < maxIndex while (searchIndex < maxIndex
&& ((leftToRight && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) && ((!reversed && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex))
|| (!leftToRight && keyboardAnchorPos(searchIndex + 1) < keyboardAnchorPos(searchIndex)))) { || (reversed && keyboardAnchorPos(searchIndex + 1) < keyboardAnchorPos(searchIndex)))) {
++searchIndex; ++searchIndex;
const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex)); const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
if (searchDiff < minDiff) { if (searchDiff < minDiff) {
@ -1523,12 +1539,12 @@ int KItemListController::previousRowIndex(int index) const
return index; 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 // Calculate the index of the first column inside the row of the current index
int firstColumnIndex = index; int firstColumnIndex = index;
while ((leftToRight && keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) while ((!reversed && keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex))
|| (!leftToRight && keyboardAnchorPos(firstColumnIndex - 1) > keyboardAnchorPos(firstColumnIndex))) { || (reversed && keyboardAnchorPos(firstColumnIndex - 1) > keyboardAnchorPos(firstColumnIndex))) {
--firstColumnIndex; --firstColumnIndex;
if (firstColumnIndex <= 0) { if (firstColumnIndex <= 0) {
return index; return index;
@ -1541,8 +1557,8 @@ int KItemListController::previousRowIndex(int index) const
int searchIndex = previousRowIndex; int searchIndex = previousRowIndex;
qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex)); qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex));
while (searchIndex > 0 while (searchIndex > 0
&& ((leftToRight && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) && ((!reversed && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex))
|| (!leftToRight && keyboardAnchorPos(searchIndex - 1) > keyboardAnchorPos(searchIndex)))) { || (reversed && keyboardAnchorPos(searchIndex - 1) > keyboardAnchorPos(searchIndex)))) {
--searchIndex; --searchIndex;
const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex)); const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
if (searchDiff < minDiff) { if (searchDiff < minDiff) {

View File

@ -550,6 +550,10 @@ void KItemListView::scrollToItem(int index, ViewItemPosition viewItemPosition)
} }
QRectF currentRect = itemRect(index); 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 // 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); currentRect.adjust(-m_styleOption.horizontalMargin, -m_styleOption.verticalMargin, m_styleOption.horizontalMargin, m_styleOption.verticalMargin);

View File

@ -1132,7 +1132,11 @@ void KStandardItemListWidget::updatePixmapCache()
} else { } else {
// Center horizontally and vertically within the icon-area // Center horizontally and vertically within the icon-area
const TextInfo *textInfo = m_textInfo.value("text"); 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 // 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 // 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); textOption.setAlignment(Qt::AlignHCenter);
break; break;
case CompactLayout: case CompactLayout:
textOption.setAlignment(QApplication::isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft);
textOption.setWrapMode(QTextOption::NoWrap);
break;
case DetailsLayout: case DetailsLayout:
textOption.setAlignment(Qt::AlignLeft); textOption.setAlignment(Qt::AlignLeft);
textOption.setWrapMode(QTextOption::NoWrap); textOption.setWrapMode(QTextOption::NoWrap);
@ -1404,9 +1411,9 @@ void KStandardItemListWidget::updateCompactLayoutTextCache()
const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing; const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing;
qreal maximumRequiredTextWidth = 0; 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); 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)) { for (const QByteArray &role : std::as_const(m_sortedVisibleRoles)) {
const QString text = escapeString(roleText(role, values)); const QString text = escapeString(roleText(role, values));
TextInfo *textInfo = m_textInfo.value(role); TextInfo *textInfo = m_textInfo.value(role);

View File

@ -226,8 +226,12 @@ QRectF KItemListViewLayouter::itemRect(int index) const
// Rotate the logical direction which is always vertical by 90° // Rotate the logical direction which is always vertical by 90°
// to get the physical horizontal direction // to get the physical horizontal direction
QPointF pos(y, x); QPointF pos(y, x);
pos.rx() -= m_scrollOffset;
sizeHint.transpose(); 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); return QRectF(pos, sizeHint);
} }
@ -361,7 +365,7 @@ void KItemListViewLayouter::doLayout()
const bool grouped = createGroupHeaders(); const bool grouped = createGroupHeaders();
const bool horizontalScrolling = (m_scrollOrientation == Qt::Horizontal); const bool horizontalScrolling = m_scrollOrientation == Qt::Horizontal;
if (horizontalScrolling) { if (horizontalScrolling) {
// Flip everything so that the layout logically can work like having // Flip everything so that the layout logically can work like having
// a vertical scrolling // a vertical scrolling
@ -377,8 +381,9 @@ void KItemListViewLayouter::doLayout()
} }
} }
const bool isRightToLeft = QGuiApplication::isRightToLeft();
m_columnWidth = itemSize.width() + itemMargin.width(); 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_columnCount = qMax(1, int(widthForColumns / m_columnWidth));
m_xPosInc = itemMargin.width(); 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. // Calculate the offset of each column, i.e., the x-coordinate where the column starts.
m_columnOffsets.resize(m_columnCount); m_columnOffsets.resize(m_columnCount);
qreal currentOffset = QGuiApplication::isRightToLeft() ? widthForColumns : m_xPosInc; qreal currentOffset = isRightToLeft ? widthForColumns : m_xPosInc;
if (grouped && horizontalScrolling) { if (grouped && horizontalScrolling) {
// All group headers will always be aligned on the top and not // All group headers will always be aligned on the top and not
@ -405,16 +410,21 @@ void KItemListViewLayouter::doLayout()
currentOffset += m_groupHeaderHeight; 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) { for (int column = 0; column < m_columnCount; ++column) {
m_columnOffsets[column] = currentOffset; m_columnOffsets[column] = currentOffset;
currentOffset += m_columnWidth; 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. // Prepare the QVector which stores the y-coordinate for each new row.
int numberOfRows = (itemCount + m_columnCount - 1) / m_columnCount; int numberOfRows = (itemCount + m_columnCount - 1) / m_columnCount;

View File

@ -309,7 +309,7 @@ void KItemListControllerTest::testKeyboardNavigation_data()
std::swap(nextItemKey, previousItemKey); std::swap(nextItemKey, previousItemKey);
break; break;
case KFileItemListView::CompactLayout: case KFileItemListView::CompactLayout:
std::swap(nextItemKey, previousItemKey); std::swap(nextRowKey, previousRowKey);
break; break;
default: default:
break; break;