Fix keyboard issues when groups are enabled

When groups are enabled in Dolphin the key-up and key-down keys did not behave
consistent in comparison to traditional views or like done in editors.

CCBUG: 261995
CCBUG: 262038
This commit is contained in:
Peter Penz 2011-12-26 22:16:32 +01:00
parent 4dc5714016
commit 118fe89449
6 changed files with 152 additions and 48 deletions

View file

@ -50,7 +50,9 @@ KItemListController::KItemListController(QObject* parent) :
m_pressedIndex(-1), m_pressedIndex(-1),
m_pressedMousePos(), m_pressedMousePos(),
m_autoActivationTimer(0), m_autoActivationTimer(0),
m_oldSelection() m_oldSelection(),
m_keyboardAnchorIndex(-1),
m_keyboardAnchorXPos(0)
{ {
connect(m_keyboardManager, SIGNAL(changeCurrentItem(QString,bool)), connect(m_keyboardManager, SIGNAL(changeCurrentItem(QString,bool)),
this, SLOT(slotChangeCurrentItem(QString,bool))); this, SLOT(slotChangeCurrentItem(QString,bool)));
@ -175,7 +177,6 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
const bool shiftOrControlPressed = shiftPressed || controlPressed; const bool shiftOrControlPressed = shiftPressed || controlPressed;
const int itemCount = m_model->count(); const int itemCount = m_model->count();
const int itemsPerRow = m_view->itemsPerOffset();
// 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.
@ -210,37 +211,28 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
case Qt::Key_Left: case Qt::Key_Left:
if (index > 0) { if (index > 0) {
index--; --index;
m_keyboardAnchorIndex = index;
m_keyboardAnchorXPos = keyboardAnchorPos(index);
} }
break; break;
case Qt::Key_Right: case Qt::Key_Right:
if (index < itemCount - 1) { if (index < itemCount - 1) {
index++; ++index;
m_keyboardAnchorIndex = index;
m_keyboardAnchorXPos = keyboardAnchorPos(index);
} }
break; break;
case Qt::Key_Up: case Qt::Key_Up:
if (index >= itemsPerRow) { updateKeyboardAnchor();
index -= itemsPerRow; index = previousRowIndex();
}
break; break;
case Qt::Key_Down: case Qt::Key_Down:
if (index + itemsPerRow < itemCount) { updateKeyboardAnchor();
// We are not in the last row yet. index = nextRowIndex();
index += itemsPerRow;
} else {
// We are either in the last row already, or we are in the second-last row,
// and there is no item below the current item.
// In the latter case, we jump to the very last item.
const int currentColumn = index % itemsPerRow;
const int lastItemColumn = (itemCount - 1) % itemsPerRow;
const bool inLastRow = currentColumn < lastItemColumn;
if (!inLastRow) {
index = itemCount - 1;
}
}
break; break;
case Qt::Key_Enter: case Qt::Key_Enter:
@ -955,4 +947,97 @@ KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
return 0; return 0;
} }
void KItemListController::updateKeyboardAnchor()
{
const bool validAnchor = m_keyboardAnchorIndex >= 0 &&
m_keyboardAnchorIndex < m_model->count() &&
keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorXPos;
if (!validAnchor) {
const int index = m_selectionManager->currentItem();
m_keyboardAnchorIndex = index;
m_keyboardAnchorXPos = keyboardAnchorPos(index);
}
}
int KItemListController::nextRowIndex() const
{
const int currentIndex = m_selectionManager->currentItem();
if (m_keyboardAnchorIndex < 0) {
return currentIndex;
}
const int maxIndex = m_model->count() - 1;
if (currentIndex == maxIndex) {
return currentIndex;
}
// Calculate the index of the last column inside the row of the current index
int lastColumnIndex = currentIndex;
while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) {
++lastColumnIndex;
if (lastColumnIndex >= maxIndex) {
return currentIndex;
}
}
// Based on the last column index go to the next row and calculate the nearest index
// that is below the current index
int nextRowIndex = lastColumnIndex + 1;
int searchIndex = nextRowIndex;
qreal minDiff = qAbs(m_keyboardAnchorXPos - keyboardAnchorPos(nextRowIndex));
while (searchIndex < maxIndex && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) {
++searchIndex;
const qreal searchDiff = qAbs(m_keyboardAnchorXPos - keyboardAnchorPos(searchIndex));
if (searchDiff < minDiff) {
minDiff = searchDiff;
nextRowIndex = searchIndex;
}
}
return nextRowIndex;
}
int KItemListController::previousRowIndex() const
{
const int currentIndex = m_selectionManager->currentItem();
if (m_keyboardAnchorIndex < 0 || currentIndex == 0) {
return currentIndex;
}
// Calculate the index of the first column inside the row of the current index
int firstColumnIndex = currentIndex;
while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) {
--firstColumnIndex;
if (firstColumnIndex <= 0) {
return currentIndex;
}
}
// Based on the first column index go to the previous row and calculate the nearest index
// that is above the current index
int previousRowIndex = firstColumnIndex - 1;
int searchIndex = previousRowIndex;
qreal minDiff = qAbs(m_keyboardAnchorXPos - keyboardAnchorPos(previousRowIndex));
while (searchIndex > 0 && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) {
--searchIndex;
const qreal searchDiff = qAbs(m_keyboardAnchorXPos - keyboardAnchorPos(searchIndex));
if (searchDiff < minDiff) {
minDiff = searchDiff;
previousRowIndex = searchIndex;
}
}
return previousRowIndex;
}
qreal KItemListController::keyboardAnchorPos(int index) const
{
const QRectF itemRect = m_view->itemRect(index);
if (!itemRect.isEmpty()) {
return (m_view->scrollOrientation() == Qt::Vertical) ? itemRect.x() : itemRect.y();
}
return 0;
}
#include "kitemlistcontroller.moc" #include "kitemlistcontroller.moc"

View file

@ -220,6 +220,34 @@ private:
*/ */
KItemListWidget* widgetForPos(const QPointF& pos) const; KItemListWidget* widgetForPos(const QPointF& pos) const;
/**
* Updates m_keyboardAnchorIndex and m_keyboardAnchorPos. If no anchor is
* set, it will be adjusted to the current item. If it is set it will be
* checked whether it is still valid, otherwise it will be reset to the
* current item.
*/
void updateKeyboardAnchor();
/**
* @return Index for the next row based on the current index.
* If there is no next row the current index will be returned.
*/
int nextRowIndex() const;
/**
* @return Index for the previous row based on the current index.
* If there is no previous row the current index will be returned.
*/
int previousRowIndex() const;
/**
* Helper method for updateKeyboardAnchor(), previousRowIndex() and nextRowIndex().
* @return The position of the keyboard anchor for the item with the index \a index.
* If a horizontal scrolling is used the y-position of the item will be returned,
* for the vertical scrolling the x-position will be returned.
*/
qreal keyboardAnchorPos(int index) const;
private: private:
bool m_selectionTogglePressed; bool m_selectionTogglePressed;
SelectionBehavior m_selectionBehavior; SelectionBehavior m_selectionBehavior;
@ -235,10 +263,27 @@ private:
/** /**
* When starting a rubberband selection during a Shift- or Control-key has been * When starting a rubberband selection during a Shift- or Control-key has been
* pressed the current selection should never be deleted. To be able to restore * pressed the current selection should never be deleted. To be able to restore
* the current selection it is remembered in m_oldSelection before * the current selection it is remembered in m_oldSelection before the
* rubberband gets activated. * rubberband gets activated.
*/ */
QSet<int> m_oldSelection; QSet<int> m_oldSelection;
/**
* Assuming a view is given with a vertical scroll-orientation, grouped items and
* a maximum of 4 columns:
*
* 1 2 3 4
* 5 6 7
* 8 9 10 11
* 12 13 14
*
* If the current index is on 4 and key-down is pressed, then item 7 gets the current
* item. Now when pressing key-down again item 11 should get the current item and not
* item 10. This makes it necessary to keep track of the requested column to have a
* similar behavior like in a text editor:
*/
int m_keyboardAnchorIndex;
qreal m_keyboardAnchorXPos;
}; };
#endif #endif

View file

@ -485,11 +485,6 @@ void KItemListView::scrollToItem(int index)
} }
} }
int KItemListView::itemsPerOffset() const
{
return m_layouter->itemsPerOffset();
}
void KItemListView::beginTransaction() void KItemListView::beginTransaction()
{ {
++m_activeTransactions; ++m_activeTransactions;

View file

@ -202,15 +202,6 @@ public:
*/ */
void scrollToItem(int index); void scrollToItem(int index);
/**
* @return The number of items that can be shown in parallel for one offset.
* Assuming the scrolldirection is vertical then a value of 4 means
* that 4 columns for items are available. Assuming the scrolldirection
* is horizontal then a value of 4 means that 4 lines for items are
* available.
*/
int itemsPerOffset() const;
void beginTransaction(); void beginTransaction();
void endTransaction(); void endTransaction();
bool isTransactionActive() const; bool isTransactionActive() const;

View file

@ -253,11 +253,6 @@ int KItemListViewLayouter::maximumVisibleItems() const
return rows * m_columnCount; return rows * m_columnCount;
} }
int KItemListViewLayouter::itemsPerOffset() const
{
return m_columnCount;
}
bool KItemListViewLayouter::isFirstGroupItem(int itemIndex) const bool KItemListViewLayouter::isFirstGroupItem(int itemIndex) const
{ {
const_cast<KItemListViewLayouter*>(this)->doLayout(); const_cast<KItemListViewLayouter*>(this)->doLayout();

View file

@ -107,13 +107,6 @@ public:
*/ */
int maximumVisibleItems() const; int maximumVisibleItems() const;
/**
* @return Maximum number of items that can be shown in the same row
* (= vertical scrolldirection) or same column
* (= horizontal scrolldirection).
*/
int itemsPerOffset() const;
/** /**
* @return True if the item with the index \p itemIndex * @return True if the item with the index \p itemIndex
* is the first item within a group. * is the first item within a group.