Row list

A column of list rows that is one stop for Tab and owns the selection and the keyboard, so the rows only have to draw themselves.

Part of a window with a list headed Today: four rows, the second and third selected as one wash, the keyboard ring on the third.

Use it for any list people scan by name and pick from: files, songs, downloads. Fill it with list rows. For pictures, use a picture grid. For a few choices that change a setting, use a menu.

Anatomy

A row list draws almost nothing itself. It keeps the selection, the place of the keyboard and the scrolling, and tells each row what to show.

A list with a heading, five rows, the third and fourth selected as one wash, the fifth with the keyboard ring. Numbered callouts name the heading, the selection, a plain row and the ring.
A list under a section heading.
  1. HeadingA section heading, from the list's section property when rows fall into groups (a day, a folder).
  2. SelectionRows that are selected next to each other are one wash, round only at its two ends.
  3. RowEach row is the app's delegate, a list row, bound to the list.
  4. Keyboard placeThe ring shows which row the keys act on.

States

The list has a selection and a place for the keyboard. They are separate: Ctrl with the arrow keys moves the place without changing the selection.

The same three-row list four times, numbered: nothing selected, one row selected, all three rows selected as one wash, one row selected with the ring on the row below it.
  1. Nothing selectedNo wash anywhere. The first arrow key selects a row.
  2. One selectedThe row's selected wash.
  3. A run selectedOne wash across the run (Shift with the arrows, Shift-click, Ctrl+A).
  4. Keyboard elsewhereThe selection stays where it was, and the ring stands on the row the keys will act on next.

Sizes

A list of three rows with its row height of 44 pixels and its width of 500 measured.
The list at its own size.
PartValue
Row height44
WidthThe width the page gives it; names with one number should not run past 720
Room for the ring4 pixels around the rows (a 2-pixel ring, 2 pixels out), kept inside the list's clip
Default size480 by 400
Page Up, Page DownThe rows that fit, less one
TypingStarts again after 0.8 seconds of no key

Keys and pointer

The list is one stop for Tab. Inside it the keyboard moves between rows.

Do thisTo
↑ ↓Move to the row above or below and select it
Shift with ↑ ↓Select from where you started to here
Ctrl with ↑ ↓Move the ring without selecting
Ctrl SpaceAdd or remove the row under the ring
Home End Page Up Page DownGo to the first, the last, or a screen up or down
Ctrl ASelect every row
F2Rename the row with the ring (editRequested); the app sets that row's editing
EnterOpen (activated)
SpaceTake a look without opening (peeked), for example in a docked panel
MenuThe row's menu (contextRequested)
Type a nameGo to the first row whose name starts with what you typed
Click, Ctrl-click, Shift-clickSelect one, add or remove one, select a run
Double-clickOpen
Right-clickThe row's menu

The scroll wheel and the touchpad move the list as everywhere in Tacit: the touchpad follows the fingers and glides on after a flick.

Accessibility

Writing

The list shows what the app gives it, so the work is in the names. Use the file's or song's own name. The section headings say what the run is: "Today", "This week", the folder or artist. Type-to-jump matches the start of the name, so keep names starting with the word people know them by.

Do and don't

Two selected rows next to each other drawn as one wash, round only at the ends.
DoJoin neighbouring selected rows into one wash. The list gives each row joinAbove and joinBelow.
Two selected rows each drawn with its own round wash, notched where they meet.
Don'tLeave out joinAbove and joinBelow. Stacked round washes touch and notch each other.

Code

A row list needs a model and a delegate. The delegate binds to the list, and the list does the rest.

import Tacit.Kit

SectionHeading { text: "Today" }
RowList {
    id: list
    width: 640; height: 400
    model: files                           // items have a `name`, for type-to-jump
    delegate: ListRow {
        required property var modelData
        required property int index
        width: list.rowWidth
        focusable: false                   // the list is the one tab stop
        glyph: modelData.kind
        name: modelData.name
        facts: [modelData.size, modelData.when]
        selected: list.isSelected(index)
        joinAbove: list.isSelected(index - 1)
        joinBelow: list.isSelected(index + 1)
        ring: list.ringAt(index)
        onClicked: m => list.clickAt(index, m)
        onActivated: list.activated(index)
    }
    onActivated: i => open(files[i])       // Enter, double-click
    onPeeked: i => panel.show(files[i])    // Space
}
Property or signalWhat it does
model, delegate, sectionWhat the list shows, how each row looks, and how rows are grouped under headings
cursor, selection, selectionCountThe row with the ring, the selected rows (an object by index), and how many
selected(), setSelection(indexes)The selected indexes as an array; set them
isSelected(i), ringAt(i), clickAt(i, modifiers)What a delegate binds to
moveTo(i, how)Move the ring to row i. how is "select" (the default), "extend" or "cursor" (move without selecting)
labelWhat a screen reader calls the list ("List" until the app says)
rowWidthThe width a delegate takes
nameAt(i)The name type-to-jump reads (the model's name by default)
activated(index), peeked(index), contextRequested(index, at)Open, look, and ask for the row's menu
editRequested(index)F2 on the row with the ring. The list does not rename: set that row's editing (see list row)

Experimental, in Tacit.Kit 0.1. Changed 25 October 2026: first version.