Pin "Export N" to the right of the library's selection bar

It was the last of a dozen buttons in a row that scrolls sideways once
it outgrows the window, which at a desktop width it does. The button
sat past the right edge and nothing says the row scrolls to a mouse,
so batch export of a selection looked like a feature the library did
not have. The rest of the row still scrolls; Export, which is also the
cancel for a running batch, now sits beside it and is always visible.
This commit is contained in:
2026-09-23 19:05:42 -04:00
parent 2be80d4203
commit 949fe40d5b
3 changed files with 58 additions and 46 deletions
+46 -34
View File
@@ -4372,9 +4372,19 @@ export component LibraryGrid inherits Rectangle {
// the count beside the buttons elides, so the row genuinely has a
// smaller footprint than its natural one and a window with room for
// the shrunk row should get it shrunk rather than scrolled.
//
// The row scrolls sideways once it outgrows the window, which at a
// desktop width it does. Export is pinned beside it rather than inside
// it: last in a scrolling row put it past the right edge, and with a
// mouse nothing says the row scrolls — so batch export looked like a
// feature the library did not have. A layout, not the Rectangle, is
// the parent of the two, or both sit at the origin and overlap.
HorizontalLayout {
padding-right: Theme.gap;
spacing: Theme.gap-sm;
Flickable {
width: 100%;
height: 100%;
horizontal-stretch: 1;
viewport-height: self.height;
viewport-width: max(self.width, selection-row.min-width);
@@ -4605,38 +4615,40 @@ export component LibraryGrid inherits Rectangle {
clicked => { root.open-membership(); }
}
// TRACES: FR-EXP-7 | NFR-ARCH-3
// Export the selection, and stop the batch that is running.
//
// One button doing both, because they are the same thought a
// moment apart and a separate cancel would have to appear from
// somewhere — shifting the row under the pointer at the exact
// moment the user is reaching for it.
//
// Last on the bar because it is the last thing done to a
// selection, and because it is the one control here that outlives
// the selection: the bar itself stays up for a running batch (see
// the note on the bar) so this is always reachable while there is
// something to cancel.
//
// The count is in the label rather than behind a confirmation,
// exactly as the paste above puts it there: "Export 40" read
// before the click is worth more than a dialogue asking the same
// question after it.
if !root.ranging: Button {
text: root.exporting
? "Cancel export"
: (root.export-to-server
? "Export " + root.selected-count + " to the library"
: "Export " + root.selected-count);
active: root.exporting;
y: (parent.height - self.height) / 2;
clicked => {
if (root.exporting) {
root.cancel-export();
} else {
root.export-selection();
}
}
}
// TRACES: FR-EXP-7 | NFR-ARCH-3
// Export the selection, and stop the batch that is running.
//
// One button doing both, because they are the same thought a
// moment apart and a separate cancel would have to appear from
// somewhere — shifting the row under the pointer at the exact
// moment the user is reaching for it.
//
// Pinned at the right end of the bar, outside the scrolling row
// (see above), because it is the last thing done to a selection
// and the one control here that outlives the selection: the bar
// stays up for a running batch, so this is always reachable while
// there is something to cancel.
//
// The count is in the label rather than behind a confirmation,
// exactly as the paste above puts it there: "Export 40" read
// before the click is worth more than a dialogue asking the same
// question after it.
if !root.ranging: Button {
text: root.exporting
? "Cancel export"
: (root.export-to-server
? "Export " + root.selected-count + " to the library"
: "Export " + root.selected-count);
active: root.exporting;
y: (parent.height - self.height) / 2;
clicked => {
if (root.exporting) {
root.cancel-export();
} else {
root.export-selection();
}
}
}