0

I wrote a simple carousel in QML. I run Qt 6.2.0 under Ubuntu 20.04.

import QtQuick

PathView {
    id: view

    property int item_width: 864
    property int item_gap: 250

    anchors.fill: parent
    anchors.bottomMargin: 150
    anchors.topMargin: 50
    pathItemCount: 3
    preferredHighlightBegin: 0.5
    preferredHighlightEnd: 0.5
    highlightRangeMode: PathView.StrictlyEnforceRange
    highlightMoveDuration: 1000
    snapMode: PathView.SnapToItem
    rotation: -90

    model: modelContent
    delegate: DelegateContent { }

    path: Path {
        startX: -item_gap; startY: view.height / 2
        PathAttribute { name: "iconScale"; value: 0.7 }
        PathAttribute { name: "iconOpacity"; value: 0.1 }
        PathAttribute { name: "iconOrder"; value: 0 }
        PathLine {x: view.width / 2; y: view.height / 2; }
        PathAttribute { name: "iconScale"; value: 1 }
        PathAttribute { name: "iconOpacity"; value: 1 }
        PathAttribute { name: "iconOrder"; value: 9 }
        PathLine {x: view.width + item_gap; y: view.height / 2; }
    }

    Component.onCompleted: {
        gesture.gestureFired.connect(gestureFired)
    }

    onCurrentIndexChanged: {
        itemAtIndex(0).visible = currentIndex < (count - 1)
    }

    function gestureFired(type) {
        switch (type) {
        case 1:
            if (view.currentIndex > 0) view.decrementCurrentIndex();
            break;

        case 2:
            if (view.currentIndex < view.count - 1) view.incrementCurrentIndex();
            break;
        }
    }
}

It works very well. The change of the current index is done programmatically only (gestureFired signal from C++). As you can see there is no circular behavior.

Hence, I want to hide the first item (when the last one is selected) or the last item (when the first is selected).

I tried with the code above:

onCurrentIndexChanged: {
    itemAtIndex(0).visible = currentIndex < (count - 1)
}

The idea is: if the currentIndex is the last element ( = count - 1) the visible property of the first item should be false.

But it does not work:

TypeError: Value is null and could not be converted to an object

Which "Value" is it talking about? I debugged the currentIndex and count and they are both valid (4 and 5).

Which is the correct way to achieve such a behavior?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Mark
  • 4,338
  • 7
  • 58
  • 120
  • I don't agree to the change of the tag from `qt5` to `qt6`. `qt5` has 1.6k of watchers, `qt6` only 31! And the differences are not so big... – Mark Nov 03 '21 at 17:34
  • Qt6 is making big changes underneath so it is not Qt 5.16 but Qt 6. So it is better to use that tag. If you want to refer to the watchers then look at the Qt tag that is the relevant one that has 21.7k. The other tags typically specify the environment. On the other hand, if you check the activity of the Qt community in SO you will see that we are few (less than 50 probably). – eyllanesc Nov 03 '21 at 17:45

1 Answers1

1

A simple solution is to do the binding in the delegate:

import QtQuick

Column {
    id: root

    required property int index

    // mapping model roles
    required property string name

    visible: _internals.calculateVisibility()

    QtObject {
        id: _internals

        function calculateVisibility() {
            console.log(index, root.PathView.isCurrentItem, root.PathView.view.currentIndex)
            if ((root.index === (root.PathView.view.count - 1)) && (root.PathView.view.currentIndex === 0))
                return false;
            else if ((root.index === 0) && (root.PathView.view.currentIndex === (root.PathView.view.count - 1)))
                return false;
            return true;
        }

    }

    Text {
        id: nameText

        text: root.name
        font.pointSize: 16
        color: root.PathView.isCurrentItem ? "red" : "blue"
    }

}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Out of curiosity, what is the purpose of embed a `function` inside a `QtObject`? I can declare it even without and it seems to work as well. – Mark Nov 04 '21 at 06:46
  • Your solution almost work! The only caveat is when the current index changes (the item moves) the previously hidden delegate is now visible! And it's not nice to see. – Mark Nov 04 '21 at 06:47
  • 1
    @Mark The idea is to hide functions that should not be public. That is the same reason because in C ++ there is public, protected and private. – eyllanesc Nov 04 '21 at 06:48
  • As ugly workaround I added a `Timer` that delays the switching of `root.visibility` from `false` to `true` of the same amount of `view.highlightMoveDuration` – Mark Nov 04 '21 at 07:52