Repository navigation
Open folders in place in the list view - #113
rustytensor wants to merge 1 commit into
Conversation
The list can now show a tree: with it on, → opens the folder under the cursor right below it and ← closes it again (or steps out to the folder it's in, and at the top goes up as before). Each open folder's items are ordinary rows, one step further in and joined by thin lines like the tree command draws them, so selection, copy, delete and rename work on them as on any other row. Open folders are listed in the background and watched, so what changes in them shows without the rest of the list jumping, and closing one lets go of anything selected inside it. The tree is off until T is pressed in the list, or the tree button beside the view button is clicked, and the choice is remembered with the view. Off, the list is exactly what it was. Searches, Recents and plugins' views stay flat, and switching to the grid or the columns closes what was open.
[Medium impact] The change affects folder browsing in the list, while other views remain flat. Fix context-menu actions on nested selections before merging. Findings
SummaryAdds an optional, remembered tree to the list view:
The nested-row integration needs fixes for context-menu selection. Other findings cover rename cursor recovery, inactive arrows in flat views and repeated folder counting.
|
| int FolderModel::indexOf(const QString &name) const { | ||
| for (int row = 0; row < m_entries.size(); ++row) | ||
| if (m_entries[row].name == name) | ||
| if (m_entries[row].depth == 0 && m_entries[row].name == name) |
There was a problem hiding this comment.
indexOf() now skips nested rows, but FileMenu.forSelection still uses it to check whether the clicked item belongs to the shared selection. Select two nested files whose names do not appear at the top level, then right-click either one: Copy, Cut and Delete receive only that file. Keep the top-level lookup for pasted names, but change the menu's membership check to use indexOfPath(target.path).
Knowledge Base Used:
| for (let up = list.shiftingPath; row < 0 && up.startsWith(shown);) { | ||
| up = up.substring(0, up.lastIndexOf("/")) | ||
| row = folder.indexOfPath(up) |
There was a problem hiding this comment.
Rename loses the selected file
Renaming a nested file moves the cursor to its parent instead of keeping the renamed file selected. FileOps::rename() reports the actual parent folder, but FolderPane.onDone ignores that result because it differs from backend.path. When the child listing arrives, this fallback treats the old name as a removed file and selects its parent. Follow completed changes inside open folders by their full destination path before using the parent fallback.
Knowledge Base Used: Folder view layouts
| delegate: FileRow { | ||
| width: list.width | ||
| detailed: true | ||
| tree: list.tree |
There was a problem hiding this comment.
Folder arrows remain visible in search results and Recents when the tree is on, but clicking them does nothing. The delegate receives list.tree without checking whether the current model can nest, while expand() rejects both views. Hide the tree controls there, or give the arrow the same navigation fallback as the right-arrow key.
| // Its items show first and its folders are counted after, as for | ||
| // the folder's own: thousands of folders on a slow drive would | ||
| // otherwise keep it open and empty for as long as counting takes. | ||
| countFolders(m_generation); |
There was a problem hiding this comment.
Open folders repeat count work
Each child listing starts another count pass over every visible folder whose count is still unknown. Counts are written back only when a whole pass finishes, so opening several folders or refreshing a tree on a slow mount queues repeated scans of the same folders. These jobs share the same generation and do not cancel each other. Track counts already being read, or use one count worker that picks up newly arrived folders.
The list view can now show a tree: a folder opens right where it is, its items below it, one step further in. It's off by default, and until you turn it on the list is exactly what it is today.
Why
To see what's in a few folders side by side, the list makes you go into each one and back out, and the columns only show one path at a time. A tree keeps the folder you're in on screen while you look inside the folders under it.
It's off by default because Folio's list is simple on purpose, and plenty of people won't want arrows and indents in it. With the tree off, there's no arrow, no indent and no line in the rows, the name sits where it always has, and → and ← still go into a folder and back out. Nothing changes for anyone who doesn't press T.
What you'd notice
With the tree on (T in the list, or the new tree button beside the view button, which only shows in the list and lights while the tree is on):
treedraws them. Right to left, the arrows and the lines turn round with the row.Keys
Both are in the README and the
?overlay. The new strings are inen.json.What changed
FolderModelkeeps the folder's own items apart from the rows it shows.expand(),collapse(),collapseAll()andparentRow()open and close folders, and three new roles (depth,open,lines) tell the row how far in it is and which lines to draw. Open folders are listed in the background and watched on their own, so a change in one re-reads only that folder.indexOf(name)only looks at the folder's own items, so a pasted or renamed file is found in the folder shown, not under the same name in an open folder.FolderListgets atreeproperty, the new arrow-key behaviour, and puts the cursor back on its item when rows shift around it.FileRowdraws the arrow and the lines only whentreeis set.FolderPaneadds T and the tree button, and closes open folders when you leave the list.Main.qmlkeepstreeViewin its Settings, off by default.Windowstakes the folder Main.qml is read from (the app's resources by default), so a test can open Folio's own window from the source folder.Tested
On Fedora 45 in a clean build root, built with the project's
-Wall -Wextra: no warnings.folio_tests: 197 passed, 12 skipped, 2 failed. Main passes 182 on the same root. The 15 new tests are below. The two failures are the same ones main has there (kindsFromMimeTypes,previewerPicksAViewerForEachType, Fedora's MIME type for shell scripts).treeIsOffUntilTurnedOn: with the tree off, no arrows, lines or indent, the name at the same 46 px as on main, and → goes into the folder. Turned on, → opens in place; turned off again, everything closes and the cursor lands on the folder.treeOpensFoldersInPlace,treeKeepsOpenFoldersThroughChanges,treeLetsGoOfHiddenSelection,treeStaysFlatWhereItCannotNest,treeFindsNamesInTheFolderShown: the rows, depths and open folders for a small fixed folder, through edits on disk, a re-read, a search, Recents and moving to another folder.treeListKeys,treeArrowKeepsTheCursorOnTheFolder,treeKeepsTheSelectionAsRowsArrive,treeKeepsTheCursorThroughANewOrder: where the cursor and the selection end up after each key, click and change, mirrored too.treeDrawsItsLines,treeLinesTurnRoundRightToLeft: the exact lines for each row, drawn in the list only (never in the columns), and their position left to right and right to left.treeOpensALargeFolderWithoutFreezing: the 20,000-item case above.treeKeyAndButton,viewButtonCyclesViews: T and the button in the list, nothing in the grid or the columns, and the view button still just cycles grid, list, columns.This doesn't depend on the pane widths PR, and either can go in first. They edit neighbouring lines in the same files, and both add the same test helper for opening Folio's real window, so whichever lands second will need a rebase. I'll do that rebase.