Ask for the collection's name before there is a collection to name

"New collection from selection" created the collection under a placeholder
name and then opened the sidebar tree's rename field to correct it.

On a tablet that field is not on screen. The collections panel is always
instantiated — app.slint collapses it to zero width and `visible: false`
rather than using an `if`, because an `if` there is a layout loop Slint
panics on — so the rename TextInput was created all the same, its `init`
called `self.focus()`, and Android raised the on-screen keyboard for a box
nobody could see. Nothing else on the screen is focusable, so the keyboard
had nowhere to go back to: it stayed up, the name could not be typed, and
the collection was already written under the name the user did not want.

Asked in a sheet instead, on the same card, scrim and dismissal the filing
and keywording sheets use. The field takes the keyboard as the sheet
appears — over a field that is actually drawn, which is the whole
difference — and the card sits a third of the way down rather than centred,
because on a tablet the keyboard is the bottom half of the window.

Nothing reaches the catalog until Create. That also ends a second bug the
old order could not avoid: an abandoned rename used to leave a collection
called "New collection" behind, because creating came first.

`Field` grows a `take-focus()` for this. A function rather than a property:
focus is an event, and bound to a property it would fight whatever took
focus next and re-take it on every unrelated re-evaluation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 20:31:02 +02:00
co-authored by Claude Opus 5
parent 5133e53bc8
commit ab49280614
5 changed files with 188 additions and 29 deletions
+28 -9
View File
@@ -1586,13 +1586,20 @@ pub fn wire<S, R, P, C>(
// the tree's "+". A selection can be gathered from anywhere, including
// across collections, so filing it under whichever one happens to be open
// would put it somewhere its contents did not come from.
//
// The name arrives already chosen. It used to be a placeholder, with the
// sidebar's rename field opened straight afterwards to correct it — which
// on a tablet meant opening a field inside a panel that is instantiated but
// never drawn, so it took the on-screen keyboard for a box nobody could see
// and had nowhere to give it back to. `library.slint`'s naming sheet asks
// first, and nothing reaches the catalog until it is answered.
{
let weak = window.as_weak();
let ctl = ctl.clone();
let catalog = catalog.clone();
let visible = visible_ids.clone();
let reload = on_scope_changed.clone();
window.on_library_collection_from_selection(move || {
window.on_library_collection_from_selection(move |name| {
let Some(w) = weak.upgrade() else { return };
let images = ctl.selected();
if images.is_empty() {
@@ -1605,12 +1612,25 @@ pub fn wire<S, R, P, C>(
return;
};
let name = unique_name(cat.connection(), None);
// The sheet refuses an empty name in both of its two ways in, so
// this is belt-and-braces rather than a path the UI can reach. But
// a collection with no name at all is unfindable in the tree, and
// falling back to the placeholder is recoverable where an unnamed
// row is not.
let name = match name.trim() {
"" => unique_name(cat.connection(), None),
typed => typed.to_string(),
};
// The id is no longer carried out of here. It used to be, to open
// the rename field on the row it names; the name is now right
// before the row exists, and `refresh_tree` redraws the tree from
// the catalog either way.
let made = coll::create(cat.connection(), &name, None, CollectionKind::Manual)
.and_then(|id| coll::add_images(cat.connection(), id, &images).map(|n| (id, n)));
.and_then(|id| coll::add_images(cat.connection(), id, &images));
match made {
Ok((id, added)) => {
Ok(added) => {
w.set_collection_error(slint::SharedString::new());
w.set_library_status(
format!(
@@ -1628,11 +1648,10 @@ pub fn wire<S, R, P, C>(
ctl.clear_selection();
sync_selection(&w, &ctl, &visible());
// Into the name field, for the same reason `collection_new`
// does it: "New collection" is a placeholder nobody wants
// to keep, and making them find the rename afterwards is
// asking them to finish a job we started.
w.set_collection_renaming(id.0 as i32);
// No rename opened here. The name was chosen before the
// collection existed, so there is nothing left to correct
// — and opening the sidebar's field is precisely what
// stranded the keyboard on a tablet.
reload();
}
Err(e) => {