slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
16.85k stars 556 forks source link

Forwarding focus to FocusScope inside of Flickable does not always work #2201

Open medhefgo opened 1 year ago

medhefgo commented 1 year ago

The following snipped - when opened as a window (the online editor does not work here) - shows an empty window and pressing a button will not make the key appear as expected (unless an area inside the window has been clicked with the mouse before pressing a key).

By either changing the flickable to a rectangle or explicitly sizing the window, the snippet works as expected and a key press is correctly forwarded/shown without having to click inside the window first.

export component BootMenu inherits Window {
    // height: 500px;
    // width: 500px;
    forward-focus: fs;
    txt := Text {
    }
    Flickable {
        fs := FocusScope {
            key-pressed(event) => {
                txt.text = event.text;
                accept
            }
        }
    }
}
ogoffart commented 1 year ago

I can reproduce the problem. What happens is that we try to set the focus from the initialization code, before the window is assigned a width/height.

So we reach the move_focus function, and we check that the item is visible, because we don't want to focus an item which would be in a hidden tab or something. https://github.com/slint-ui/slint/blob/a454ffe8f9e6cf2ada282db971815cb337d83d2d/internal/core/window.rs#L527 But that visibility check works by checking if the item is within clippable of size 0. And a Flickable is clippable. So we don't focus that item :-/

We could solve this by having a better visibility check. We could already remove the Flickable from that test for the purpose of focussing. But that wouldn't help if the item was in a Rectangle{ clip: true .... } So we need to somehow have a better information about what items are able to accept focus without relying on that visibility check.

As a workaround, you can try to add a public function set_focus() { root.focus() } and call that from the native code after showing the window. Then the size should be assigned.