slint-ui / slint

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

Bug in SwipeGestureHandler #6272

Open yanshay opened 5 days ago

yanshay commented 5 days ago

I think I found a critical bug in the SwipeGestureHandler in the nightly.

It happened in an app I'm developing and after quite some work I downsized a reproduction. Maybe can be even simpler but I think this is small enough.

In the app below there are two pages vertically. Top one is black, below it is half grey and half white. From the black swipe up to reveal the bottom half grey/half white page. Then from that page swiping down should bring back the black.

I couldn't find how to share on SlintPad, so here is the code:

import {  VerticalBox, HorizontalBox    } from "std-widgets.slint";

component Component1 inherits Rectangle {
    background: grey;
    VerticalLayout {
        Text {
            horizontal-alignment: center;
            font-size: 20px;
        }
    }

    area := TouchArea {                 // Removing this touch area bring back gesture in this area
        width: parent.width;
        height: parent.height;
    }
}

component Component2 inherits Window {
    comps := HorizontalLayout {
        VerticalLayout {
            Component1 {
                width: 200px;
            }
        }
    }
}

export component Component3 {
    VerticalLayout {
        HorizontalLayout {
            Component2 {
            }
        }
    }
}

export component AppWindow inherits Window {

    width: 480px;
    height: 320px;

    property <int> current-page: 0;

    sgr := SwipeGestureHandler {
      width: parent.width;
      height: parent.height;
      handle-swipe-up: current-page < 1;
      handle-swipe-down: current-page > 0;
      swiped => {
          if self.current-position.y > self.pressed-position.y + (self.height / 8) {
              current-page -= 1;
          } else if self.current-position.y < self.pressed-position.y - (self.height / 8) {
              current-page += 1;
          }
      }
    }

    VerticalLayout {
      y: - current-page*320px;
      animate y {
          duration: 1000ms;
          easing: ease-in-out;
      }
        // Page 0
      Rectangle {
            height: parent.height;
            width: parent.width;
            background: black;
        }

        // Page 1
      VerticalLayout {
            height: parent.height;
            width: parent.width;
            HorizontalLayout {
                Component3 {
                }
            }

        }
    }
}
ogoffart commented 5 days ago

Thanks for filling an issue.

What happens here is that the layout with all the pages is a sibling of the gesture handler. If instead, you make it a parent, it works:

export component AppWindow inherits Window {

    width: 480px;
    height: 320px;

    property <int> current-page: 0;

    sgr := SwipeGestureHandler {
      width: parent.width;
      height: parent.height;
      handle-swipe-up: current-page < 1;
      handle-swipe-down: current-page > 0;
      swiped => {
          if self.current-position.y > self.pressed-position.y + (self.height / 8) {
              current-page -= 1;
          } else if self.current-position.y < self.pressed-position.y - (self.height / 8) {
              current-page += 1;
          }
      }

      // CHANGED!!!  now the VerticalLayout is INSIDE the SwipeGestureHandler
      VerticalLayout {
        y: - current-page*320px;
        animate y {
            duration: 1000ms;
            easing: ease-in-out;
        }
            // Page 0
        Rectangle {
                height: parent.height;
                width: parent.width;
                background: black;
            }

            // Page 1
        VerticalLayout {
                height: parent.height;
                width: parent.width;
                HorizontalLayout {
                    Component3 {
                    }
                }

            }
        }
    }
}

SlintPad Link

yanshay commented 5 days ago

Thanks, now it works in the Live Preview. I think its worth emphasizing in the doc. It is sort of there in the explanation now that I read it again, but hard to notice. And since it works, just not on all the screen it's prone to user error.

However on the device it still doesn't work well. What I notice is that it works well on a screen that's almost empty (just rectangle with text area). However, on the other page I have quite a few widgets with relatively complex hierarchy, so I manage to swipe only after several tries. I even removed the TouchArea to verify it is not the issue.

Maybe it has to do with rendering taking more time and therefore too much time pass between touch events so the handler ignores it? Even though the screen is not changing at all.