Repository navigation
Description
In all three views, moving the cursor with a plain arrow key doesn't scroll the view. Hold Down in a folder with more files than fit, and the cursor goes past the bottom edge and keeps going (the details pane follows it), but the view stays at the top. The same thing happens with Up past the top. Shift+arrows and clicks do scroll.
Affected: 0.1.1 (Omarchy's package), v0.2.0 and main at ac9d14f. Seen with Qt 6.12.0 on a Dell XPS 13 and a ThinkPad T460s. The cause is in the views' QML, so the compositor and the GPU make no difference.
Reproduce
mkdir /tmp/5000; cd /tmp/5000; touch file-{0001..5000}.txt
folio /tmp/5000 # Ctrl+2 for the list (the grid and the columns do the same)
# hold Down for a few secondsExpected: the cursor's row stays on screen, and the list scrolls under it.
Actual: the list still shows the first files, while the details pane names a file far below.
Cause
FolderList.qml, FolderGrid.qml and the here view in FolderColumns.qml set keyNavigationEnabled: true and highlightFollowsCurrentItem: false, and have no highlight. With that combination, Qt's key navigation changes currentIndex but nothing scrolls the view.
Their Keys.onPressed handlers pass only Shift+arrows to Selecting.extendBy() → extendTo(), which calls positionViewAtIndex. Selecting.select() also scrolls. A plain arrow goes to Qt's key navigation, and from there to currentIndexChanged → Selecting.cursorMoved(), which only updates the selection. The grid's own first-Down case (currentIndex < 0 → 0) doesn't scroll either.
Fix
Scroll in cursorMoved() (src/Selecting.qml). Every cursor move goes through it, except moveTo()'s own quiet moves:
--- a/src/Selecting.qml
+++ b/src/Selecting.qml
@@ -24,6 +24,8 @@ QtObject {
return
backend.folder.selectOnly(view.currentIndex)
anchor = view.currentIndex
+ if (view.currentIndex >= 0)
+ view.positionViewAtIndex(view.currentIndex, ListView.Contain)
}Contain scrolls only when the item isn't fully in view, so the wheel and the scroll bar are left alone (neither changes currentIndex). ListView.Contain and GridView.Contain have the same value, and Selecting already uses ListView.Contain for the grid.
I built main (a2bc0ca, which has the same Selecting.qml as ac9d14f) with only this change and pressed Down 100 times in a 300-file folder. The cursor stayed on screen in all three views: the list ended on file-101, the grid scrolled row by row with the cursor, and the column view's file column scrolled like the list. With #18 (which has the same change), holding Down and then Up in /tmp/5000 keeps the cursor in view on the way down and back to the top.
Things to check with the fix: moves the view makes on its own (an item removed under the cursor, or a re-read that keeps the cursor on its row) will now scroll to the cursor too. In the columns, check that revealNext() and the new scroll don't fight when you step into a folder with →.
The same two lines are part of #18, which covers a lot more. I'm filing this on its own so the fix can land without waiting for #18. I'm happy to open a small PR for it.
A headless test that shows the bug without folio
This builds a ListView and a GridView with the properties folio's views set, then presses Down 100 times. The "folio" views fail, and the "fixed" views pass. The two differ only by onCurrentIndexChanged: if (currentIndex >= 0) positionViewAtIndex(currentIndex, ListView.Contain).
QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tst_follow_cursor.qml
FAIL! : ...test_downKeepsCursorInView(folio list) 'cursor outside the viewport: folio list: current index 99 at y 3168, contentY 0, viewport height 600'
PASS : ...test_downKeepsCursorInView(fixed list)
FAIL! : ...test_downKeepsCursorInView(folio grid) 'cursor outside the viewport: folio grid: current index 499 at y 15840, contentY 0, viewport height 600'
PASS : ...test_downKeepsCursorInView(fixed grid)
// Standalone reproduction of the Files (folio) keyboard scrolling bug, without folio itself.
//
// QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tst_follow_cursor.qml
//
// Each view has the properties folio's views set (src/FolderList.qml, src/FolderGrid.qml, the ListView in
// src/FolderColumns.qml): keyNavigationEnabled, highlightFollowsCurrentItem: false, no highlight, currentIndex -1,
// acceptedButtons: Qt.NoButton. The test presses Down 100 times and checks that the current item is inside the
// viewport. Expected today: the "folio" rows FAIL (the cursor is at row 100, the view still at the top), the
// "fixed" rows PASS. The fixed views add one line: onCurrentIndexChanged: positionViewAtIndex(currentIndex, Contain).
import QtQuick
import QtTest
Item {
id: root
width: 800
height: 600
component FolioList: ListView {
width: root.width
height: root.height
model: 5000
keyNavigationEnabled: true
currentIndex: -1
acceptedButtons: Qt.NoButton
boundsBehavior: Flickable.StopAtBounds
highlightFollowsCurrentItem: false
clip: true
delegate: Rectangle { width: ListView.view.width; height: 32; color: ListView.isCurrentItem ? "steelblue" : "white" }
}
component FolioGrid: GridView {
width: root.width
height: root.height
model: 5000
cellWidth: 160
cellHeight: 160
keyNavigationEnabled: true
currentIndex: -1
acceptedButtons: Qt.NoButton
boundsBehavior: Flickable.StopAtBounds
highlightFollowsCurrentItem: false
clip: true
delegate: Rectangle { width: 150; height: 150; color: GridView.isCurrentItem ? "steelblue" : "white" }
}
FolioList { id: folioList; visible: false }
FolioList {
id: fixedList
visible: false
onCurrentIndexChanged: if (currentIndex >= 0) positionViewAtIndex(currentIndex, ListView.Contain)
}
FolioGrid { id: folioGrid; visible: false }
FolioGrid {
id: fixedGrid
visible: false
onCurrentIndexChanged: if (currentIndex >= 0) positionViewAtIndex(currentIndex, GridView.Contain)
}
TestCase {
name: "FollowCursor"
when: windowShown
function test_downKeepsCursorInView_data() {
return [
{ tag: "folio list", view: folioList },
{ tag: "fixed list", view: fixedList },
{ tag: "folio grid", view: folioGrid },
{ tag: "fixed grid", view: fixedGrid },
]
}
function test_downKeepsCursorInView(data) {
const view = data.view
view.visible = true
view.forceActiveFocus()
for (let i = 0; i < 100; ++i)
keyClick(Qt.Key_Down)
const item = view.currentItem
const top = item.y - view.contentY
const bottom = top + item.height
const msg = data.tag + ": current index " + view.currentIndex + " at y " + item.y + ", contentY " +
view.contentY + ", viewport height " + view.height
view.visible = false
verify(view.currentIndex > 0, msg)
verify(top >= 0 && bottom <= view.height, "cursor outside the viewport: " + msg)
}
}
}

Activity
sebishogun commentedon Nov 12, 2026
Confirmed on the current Omarchy package, Folio 0.4.0-2, with Qt 6.12.0 and Hyprland 0.57.2-2 (Omarchy 4.0.0.r7840.gac6cb9c-1, Linux 7.2.15-2-omarchy, AMD Ryzen AI MAX+ 395 / Radeon 8060S).
The symptom here is specifically keyboard auto-scrolling: moving through a folder with the arrow keys updates the selected file and details pane, but the file list does not scroll to keep that selection visible. This confirmation is not reporting a wheel/touchpad problem.
I checked the source and independently tested the existing fix in #79 against main at 61b7e20. The affected Selecting.qml is identical in v0.4.0 and that main revision.
Using PR #79's real-view regression tests:
This matches the reported cause: cursorMoved() updates the selected row but never calls positionViewAtIndex(). PR #79 adds that call with ListView.Contain, keeping the current item visible while leaving wheel scrolling alone.
The build and complete test suite also passed with the fix: 199 passed, 0 failed, 0 skipped, using LC_ALL=en_US.UTF-8 and dbus-run-session -- bin/test -V. The view tests run with QT_QPA_PLATFORM=offscreen.
e2jk commentedon Nov 13, 2026
Also affected on Folio 0.4.0-3 on Omarchy, using grid view. Down Arrow moves the selection beyond the visible area without scrolling, while the details pane continues to follow it.
— Codex, on behalf of e2jk