Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
edd94f9
Draw the selected name in the bright text color
thisisgm Nov 11, 2026
2364c92
Scroll the cursor into view when keys move it
thisisgm Nov 11, 2026
cc41cb1
Frame the cursor's item when nothing is selected
thisisgm Nov 11, 2026
c95f185
Draw the other pane's selection without the accent
thisisgm Nov 11, 2026
b3c8818
Wrap the details pane between words
thisisgm Nov 11, 2026
7084409
Keep the folder in view in the notice line
thisisgm Nov 11, 2026
a824473
Say how many are selected in the details pane
thisisgm Nov 11, 2026
2472b0a
Name a search result by its file, with its folder beside it
thisisgm Nov 11, 2026
760853f
Dim the whole window behind a dialog
thisisgm Nov 11, 2026
47d053a
Show the shortcut sheet's scroll bar at rest
thisisgm Nov 11, 2026
aca122f
Copy instead of move when Ctrl is held on a drop
thisisgm Nov 11, 2026
54e0bba
Draw the shortcut sheet's resting scroll bar so it reads
thisisgm Nov 11, 2026
5c3a7a6
Keep menus off the window edge and their open parent grey
thisisgm Nov 11, 2026
c53e1ca
Put a plain space before AM and PM in dates
thisisgm Nov 11, 2026
b845199
Show no one file's picture when several are selected
thisisgm Nov 11, 2026
4af40ec
Leave the date off a search result tile led by its folder
thisisgm Nov 11, 2026
3761edf
Say whether a drop will move or copy while it hovers
thisisgm Nov 11, 2026
0ce1d6c
Make the pane a right click lands in the active one
thisisgm Nov 11, 2026
4089e5b
Line up "Nothing selected" with the empty folder's caption
thisisgm Nov 11, 2026
8ad8fc7
Drop the selection's accent while the keys are elsewhere
thisisgm Nov 11, 2026
f71fd55
Fold whole crumbs into a leading ellipsis
thisisgm Nov 11, 2026
841100d
Make Home a row of its own at the top of the sidebar
thisisgm Nov 11, 2026
505b837
Draw the open place grey while the files hold the keyboard
thisisgm Nov 11, 2026
1c973ab
Say a toolbar toggle is on by brightness, not a plate
thisisgm Nov 11, 2026
d54a008
Edit the path with a click on the empty part of the bar
thisisgm Nov 11, 2026
031fb04
Say how many a search found once it settles
thisisgm Nov 11, 2026
2b7d22d
Say how to leave a search that found nothing
thisisgm Nov 11, 2026
697c444
Keep the empty caption level while a band shows
thisisgm Nov 11, 2026
7c3f37d
Name what goes in the permanent delete title
thisisgm Nov 11, 2026
08644c7
List dragging files on the shortcut sheet
thisisgm Nov 11, 2026
0995642
Draw the other pane's selection ring so it reads
thisisgm Nov 11, 2026
0572e0c
Say move or copy next to the pointer during a drop
thisisgm Nov 11, 2026
50dc803
Drop the heading over the sidebar's Home row
thisisgm Nov 11, 2026
cef8544
Draw a disabled toolbar glyph clearly dimmer than off
thisisgm Nov 11, 2026
c838994
Add a top bar button that splits the window
thisisgm Nov 11, 2026
54d61ba
Draw drive, list and archive glyphs as what they are
thisisgm Nov 11, 2026
78d7d51
Keep secondary text at 4.5:1 on every ground
thisisgm Nov 11, 2026
4f5d6d8
Draw accent frames at 3:1 on every ground
thisisgm Nov 11, 2026
6463540
Draw the other pane's ring and disabled glyphs at 3:1
thisisgm Nov 11, 2026
3aaed29
Keep red words readable on every theme
thisisgm Nov 11, 2026
f603960
Draw accent words at 4.5:1 on their ground
thisisgm Nov 11, 2026
11a6f08
Draw a plugin mark's warning status at 4.5:1
thisisgm Nov 11, 2026
e28b1aa
Write a grey theme red in the foreground
thisisgm Nov 11, 2026
4bcd81f
Write tile badges in an ink that reads on their color
thisisgm Nov 11, 2026
2529573
Draw the details and split glyphs as two things
thisisgm Nov 11, 2026
ff6c0e9
Draw an archive tile as the box its row draws
thisisgm Nov 11, 2026
cfecb01
Say where it searched when nothing was found
thisisgm Nov 11, 2026
a2fe31f
Draw the drop verb in the foreground ink
thisisgm Nov 11, 2026
8c70a6b
Light the part of a result's name that matched
thisisgm Nov 11, 2026
21cbaf4
Draw file names as plain text
thisisgm Nov 11, 2026
b8efc81
Draw each app's own icon in Open With
thisisgm Nov 11, 2026
e248b49
Draw the matched part of a result's name in bold
thisisgm Nov 11, 2026
b505b7a
Leave the key gap off a menu row without a key
thisisgm Nov 11, 2026
07d5330
Render Markdown in the details pane, without pictures
thisisgm Nov 11, 2026
4993e53
Draw the audio stand-in as sound, with a chip
thisisgm Nov 11, 2026
7e04504
Keep typed search text inside a narrow field
thisisgm Nov 11, 2026
41e9896
Keep the parent in view before folding it
thisisgm Nov 11, 2026
53c52f2
Draw all text gray, without LCD color fringes
thisisgm Nov 11, 2026
873024c
Keep the folder card's fill visible on every theme
thisisgm Nov 11, 2026
36d0614
Draw app names as plain text
thisisgm Nov 11, 2026
d220bcb
Give a rendered Markdown page room between its blocks
thisisgm Nov 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plans/folio.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Done, tested, and verified offscreen:
- `/` searches this folder and every folder below it by name as you type: a live breadth-first walk, nearest first, that stays on the folder's drive and doesn't follow symlinked folders.
- Three views, remembered between launches: grid (Ctrl+1), list with name, size and date (Ctrl+2; no headings; the date then the size step aside when narrow), and Finder-style columns (Ctrl+3).
- Preview pane: large preview, the system's type name, size, modified, dimensions, opening lines of text files, Open.
- Sidebar: a Home section (its label goes home) listing every folder in home (XDG folders and Dropbox with their own glyphs, `omarchy` shown as Omarchy); Pinned folders (Ctrl+D or drag onto the sidebar) stored as GTK bookmarks; Dropbox (with `dropbox-cli` sync status), Trash, physical drives (removable and extra internal disks, not the system's own), online tailnet machines from `tailscale status --json`.
- Sidebar: a Home section holding Home, every folder in home (XDG folders and Dropbox with their own glyphs, `omarchy` shown as Omarchy), Recents and Trash; Pinned folders (Ctrl+D or drag onto the sidebar) stored as GTK bookmarks; Dropbox (with `dropbox-cli` sync status), physical drives (removable and extra internal disks, not the system's own), online tailnet machines from `tailscale status --json`.
- Tailnet machines open over SFTP through GIO, landing in the matching home folder; their host keys are accepted without asking (Tailscale already checked the peer), and a password, when keys aren't set up, is asked in the window.
- Mouse back/forward buttons move through history (in the preview: previous/next file).
- Space opens a large preview in its own process (`folio preview`), like Sushi: pictures at full size with zoom, animated GIF/WebP, video and audio playing (Qt Multimedia's FFmpeg backend), PDFs scrolling (Qt PDF), text and rendered Markdown, folders with their pictures, and the largest cached thumbnail for anything else. Arrows step through the folder and the grid follows; a crash in the preview is reported in the Folio window. Ctrl+I toggles the details pane.
Expand Down
2 changes: 1 addition & 1 deletion src/Band.qml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,6 @@ MouseArea {
height: Math.abs(band.end.y - band.start.y)
color: Qt.alpha(theme.accent, 0.12)
border.width: 1
border.color: theme.accent
border.color: theme.accentMark
}
}
2 changes: 1 addition & 1 deletion src/ClashDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ FolioDialog {
radius: Math.min(3, theme.radius)
color: forAll.checked ? theme.accent : "transparent"
border.width: forAll.checked ? 0 : 1
border.color: forAll.visualFocus ? theme.accent : Qt.alpha(theme.foreground, 0.4)
border.color: forAll.visualFocus ? theme.accentMark : Qt.alpha(theme.foreground, 0.4)
Glyph {
anchors.centerIn: parent
visible: forAll.checked
Expand Down
19 changes: 19 additions & 0 deletions src/FileDrop.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import QtQuick

// A folder files can be dropped on. While they hover it knows whether letting go moves or copies them.
DropArea {
id: area
property string folder
property bool copies: false
// Whether the hovering files all come from other drives. It asks the drives, so it is read once a drag, never on the timer.
property bool otherDrive: false
// The drag loop takes the key events, so Ctrl pressed or let go with the pointer still is seen only by asking again.
readonly property int ctrlCheckMs: 100

keys: ["text/uri-list"]
function recheck() { copies = otherDrive || fileOps.ctrlHeld() }
onEntered: (drag) => { otherDrive = fileOps.dropCopies(drag.urls, folder, false); recheck() }
onPositionChanged: recheck()

Timer { interval: area.ctrlCheckMs; repeat: true; running: area.containsDrag; onTriggered: area.recheck() }
}
2 changes: 2 additions & 0 deletions src/FileMenu.qml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ FolioMenu {
FolioMenuItem {
required property var modelData
text: modelData.isDefault ? qsTr("%1 (default)").arg(modelData.name) : modelData.name
iconColumn: true
icon.source: modelData.icon ? "image://appicon/" + encodeURIComponent(modelData.icon) : ""
onTriggered: fileOps.openWith(fileMenu.target.path, modelData.id)
}
}
Expand Down
54 changes: 41 additions & 13 deletions src/FileRow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ Item {
required property int itemCount
required property string icon
required property bool selected
required property string fileName
required property string folder

property bool current: false
// In a pane the keys don't go to: the selection is drawn without the accent.
property bool inactive: false
// In a column to the left: the folder the next column is showing.
property bool trail: false
property bool detailed: false
Expand All @@ -37,34 +41,37 @@ Item {
// for it.)
readonly property bool showDate: detailed && width >= 480
readonly property bool showSize: detailed && width >= 380
// With nothing selected, keys act on the cursor's item; only the folder being shown has one.
readonly property bool cursor: ListView.isCurrentItem && ListView.view.model === backend.folder && backend.folder.selectedCount === 0

readonly property string glyph: row.isDir ? "folder"
: row.kind === "item" ? row.icon
: row.kind === "image" ? "pictures"
: row.kind === "video" ? "videos"
: row.kind === "audio" ? "music"
: row.kind === "pdf" || row.kind === "text" ? "documents"
: row.kind === "archive" ? "archive"
: "file"

// A folder row takes files dropped on it, lighting up while they hover.
DropArea {
FileDrop {
id: folderDrop
anchors.fill: parent
enabled: row.isDir
keys: ["text/uri-list"]
folder: row.path
onEntered: (drag) => { drag.accepted = fileOps.canDrop(drag.urls, row.path) }
onDropped: (drop) => { fileOps.dropInto(drop.urls, row.path); drop.accept(Qt.MoveAction) }
onDropped: (drop) => drop.accept(fileOps.dropInto(drop.urls, row.path, fileOps.ctrlHeld()))
}

Rectangle {
anchors.fill: parent
anchors.leftMargin: 4
anchors.rightMargin: 4
radius: Math.min(4, theme.radius)
border.width: folderDrop.containsDrag ? 2 : 0
border.color: theme.accent
color: row.current || folderDrop.containsDrag ? Qt.alpha(theme.accent, 0.18)
: row.trail ? Qt.alpha(theme.foreground, 0.08)
border.width: folderDrop.containsDrag ? 2 : row.cursor ? 1 : 0
border.color: row.inactive && !folderDrop.containsDrag ? theme.quietMark : theme.accentMark
color: row.current && !row.inactive || folderDrop.containsDrag ? Qt.alpha(theme.accent, 0.18)
: row.trail || row.current ? Qt.alpha(theme.foreground, 0.08)
: hover.hovered ? Qt.alpha(theme.foreground, 0.05)
: "transparent"
}
Expand Down Expand Up @@ -113,34 +120,55 @@ Item {
// mirrored: left as they are, they'd follow their own script, and a
// Latin name would sit at the far end of a right-to-left row.
Text {
// A search result lights the run of its name that matched; only the folder being shown is searched.
readonly property string lit: row.ListView.view.model === backend.folder
? Format.lit(row.fileName, backend.folder.matchStart(row.fileName), backend.folder.query.length, theme.brightForeground) : ""
Layout.fillWidth: true
Layout.minimumWidth: 120
horizontalAlignment: Text.AlignLeft
text: row.name
color: row.current ? theme.accent : theme.foreground
text: lit || row.fileName
textFormat: lit ? Text.StyledText : Text.PlainText
color: row.current ? theme.brightForeground : theme.foreground
elide: Text.ElideMiddle
}
Text {
visible: row.showSize
visible: row.folder.length > 0
Layout.maximumWidth: Math.round(row.width / 3)
horizontalAlignment: Text.AlignLeft
text: row.folder
textFormat: Text.PlainText
color: theme.secondaryForeground
font.pixelSize: 12
elide: Text.ElideMiddle
}
Text {
visible: row.showSize && !folderDrop.containsDrag
Layout.preferredWidth: 96
horizontalAlignment: Text.AlignRight
text: row.kind === "item" ? row.extension : row.isDir ? Format.count(row.itemCount) : Format.size(row.size)
color: theme.secondaryForeground
color: row.current ? theme.foreground : theme.secondaryForeground
font.pixelSize: 12
}
Text {
visible: row.showDate
visible: row.showDate && !folderDrop.containsDrag
Layout.preferredWidth: 96
Layout.leftMargin: 12
horizontalAlignment: Text.AlignLeft
text: {
translator.language // Re-read when the language (and so the default locale) changes.
return Format.when(row.modified)
}
color: theme.secondaryForeground
color: row.current ? theme.foreground : theme.secondaryForeground
font.pixelSize: 12
elide: Text.ElideRight
}
// While files hover a folder, what the drop will do takes the size and date's place.
Text {
visible: folderDrop.containsDrag
text: folderDrop.copies ? qsTr("Copy here") : qsTr("Move here")
color: theme.foreground
font.pixelSize: 12
}
// Points to where the next column opens.
Glyph {
visible: row.chevron && row.isDir
Expand Down
Loading