Slideshow style item navigation with zooming in of current item (2024)

This topic has been deleted. Only users with topic management privileges can see it.

  • D

    dv__last edited by dv__


    This is a followup to an earlier (solved) question of mine. Since then, the requirements have changed somewhat.

    Now we need to create a user interface where items are arranged as a horizontal list at the top. It must be possible to scroll through them and select one as the current item. The current item should then move out of the list and be maximized in the middle, similar to what the slideshow.qml example in examples/quick/views/visualdatamodel/ does.

    The horizontal list I can do with a ListView. It is the current item zooming that is a problem. I could "steal" that item from the ListView and reparent it to the underlying item. But the problem is that later I might select a different item, so the current item should move back in at the same location in the list it was earlier, so it has to remember its previous place somehow. I am not sure if ListView can handle that.

    I was thinking of solving this by placing each item in a "container" item. So, the ListView delegate is then this container item, with the actual visible item inside. When maximizing the current item, I reparent the visible item, and the container stays in the ListView. To make it seem as if the item was moved out, I could then also set the width of the container item to 0.

    Does this sound reasonable, or is there a simpler way? slideshow.qml does not use a ListView, this is why I am asking. Also, would a container item width of 0 potentially cause problems?

    Also please note that the visible items are a custom item type that must not be created more often than necessary. This is because it does some custom OpenGL painting (similar to what the openglunderqml example does) to render objects that have a texture on them that is filled with video frames. (This is done with custom GStreamer code, not with QtMultimedia.) More importantly, this has to be able to run on embedded i.MX6 hardware, so it is crucial to not have more open video streams than necessary, so one data model item must not be represented by more than one delegate. This is why I prefer to shift around existing items instead of creating new ones.

    1 ReplyLast reply ReplyQuote0

    • SeeLooklast edited by SeeLook


      You might try a Tumbler but horizontal.

      Here is a working example:

      import QtQuick 2.9import QtQuick.Controls 2.2Tumbler { id: tumbler property real factor: 15 SystemPalette { id: activPal; colorGroup: SystemPalette.Active } width: 800 //parent.width height: factor * 10 visibleItemCount: Math.min(((width / (factor * 7)) / 2) * 2 - 1, 7) model: 15 delegate: Component { Column { spacing: factor / 4 opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2) scale: 1.7 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2) Text { font { pixelSize: factor * 3 } text: modelData + 1 anchors.horizontalCenter: parent.horizontalCenter color: tumbler.currentIndex === modelData ? activPal.highlightedText : activPal.text } Text { anchors.horizontalCenter: parent.horizontalCenter width: factor * 8 text: modelData * 1008 horizontalAlignment: Text.AlignHCenter color: activPal.text font { bold: tumbler.currentIndex === modelData; pixelSize: factor * 0.8 } } } } contentItem: PathView { id: pathView model: tumbler.model delegate: tumbler.delegate clip: true pathItemCount: tumbler.visibleItemCount + 1 preferredHighlightBegin: 0.5 preferredHighlightEnd: 0.5 dragMargin: width / 2 path: Path { startX: 0 startY: factor * 1.4 PathLine { x: pathView.width y: factor * 1.4 } } } Rectangle { z: -1; width: factor * 9; height: parent.height * 0.5 x: parent.width / 2 - width / 2; y: 2 color: activPal.highlight radius: width / 12 }}

      I plucked it from my code, so there can be more stuff than necessary and a bit messy, but You may see 'in action' is this approach suits You.

      D1 ReplyLast reply ReplyQuote0

      • D

        dv__ @SeeLooklast edited by dv__


        @SeeLook Wow, this is perfect! I did not know the Tumbler exists. I'm still a beginner at QML. I just have to arrange the items a bit to be more vertically centered, but otherwise this is even better than my idea, because the selected element does not have to be zoomed in! (I guess I could still do that if I want a 100% maximized mode, but I can live without it for now, I think the tumbler will suffice.) Thanks!

        EDIT: To make it even better, it would be good if I could click on an item and the tumbler would scroll to it, as an addition to the dragging motion. I figure that I'd have to add a custom onClick handler to the contentItem that sets accepted to false, and then, in a MouseArea in the delegate, I'd have to set the Tumbler's current index to the index of the clicked element?

        1 ReplyLast reply ReplyQuote0

        • SeeLook @dv__last edited by


          @dv__
          I tried to add 'custom' click but the tumbler stopped working then.
          But I didn't put much efforts to search why. Scrolling only is sufficient for me for now.
          ... but if You will find how to make it either scrollable and clickable, please write.

          D1 ReplyLast reply ReplyQuote0

          • D

            dv__ @SeeLooklast edited by dv__


            @SeeLook I got it to work like this. However, I'm now not so sure if the Tumbler is really the way to go. What we are doing here is essentially bending the Tumbler . The actual items are still "vertical", just in the same row. The child items from these items are repositioned, and they are what we see. However, to really zoom in the current video, the adjacent items would have to be scaled down pretty hard so they don't consume too much width. Probably doable by adjusting the visibleItemCount formula, but so far I ended up with cases where the items overlap, and I can't afford that, because the 3D objects in my custom quickitems are being drawn directly with GL..

            Window {id: windowvisible: truewidth: 800height: 600property var itemWidth: 200property var itemHeight: 200ListModel {id: nameModelListElement { name: "Alice" }ListElement { name: "Bob" }ListElement { name: "Jane" }ListElement { name: "Peter" }ListElement { name: "James" }ListElement { name: "A" }ListElement { name: "B" }ListElement { name: "C" }ListElement { name: "D" }}Component {id: itemDelegateItem {opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)scale: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2) * 0.7Rectangle {anchors.horizontalCenter: parent.horizontalCenteranchors.verticalCenter: parent.verticalCenterwidth: window.itemWidthheight: window.itemHeightborder.color: "red"border.width: 5color: "green"Text {anchors.horizontalCenter: parent.horizontalCenteranchors.verticalCenter: parent.verticalCentertext: name}MouseArea {anchors.fill: parentonClicked: {console.log("New current index: " + index);tumbler.currentIndex = index}}}}}Tumbler {id: tumbleranchors.fill: parentmodel: nameModelvisibleItemCount: Math.floor(width / window.itemWidth - 1) | 1 // bitwise OR to make sure the item count is always odd and at least 1delegate: itemDelegatecontentItem: PathView {model: tumbler.modeldelegate: tumbler.delegateclip: truepathItemCount: tumbler.visibleItemCountpreferredHighlightBegin: 0.5preferredHighlightEnd: 0.5dragMargin: width / 2path: Path {startX: 0startY: tumbler.height / 2PathLine {x: tumbler.widthy: tumbler.height / 2}}}}}

            1 ReplyLast reply ReplyQuote0

            • D

              dv__last edited by


              And here is a third version. This one allows for maximizing the current item. To make sure it isn't maximized while the tumbler's pathview is moving, I temporarily create a connection to onMovementEnded and tear it down once I maximized.

              Unclear yet:

              1. disconnect() a non-connected function - is this ok?
              2. On touchscreens, does onMovementEnded() trigger when the items really don't move anymore? The documentation makes it sound as if it is triggered when the user no longer flicks the view, that is, releases the finger - but the items are still moving then.
              3. What if I delete the current item? What if it is currently maximized?
              import QtQuick 2.0import QtQuick.Controls 2.0import QtQuick.Layouts 1.3import QtQuick.Window 2.0Window {id: windowvisible: truewidth: 800height: 600property var itemWidth: 200property var itemHeight: 200ListModel {id: nameModelListElement { name: "Alice" }ListElement { name: "Bob" }ListElement { name: "Jane" }ListElement { name: "Peter" }ListElement { name: "James" }ListElement { name: "A" }ListElement { name: "B" }ListElement { name: "C" }ListElement { name: "D" }}Component {id: itemDelegateItem {opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)scale: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2) * 0.7z: (tumbler.model.count - Math.abs(index - tumbler.currentIndex)) / tumbler.model.countproperty alias maximized: itemDelegateRect.maximizedproperty alias innerWidth: itemDelegateRect.widthproperty alias innerHeight: itemDelegateRect.heightRectangle {id: itemDelegateRectanchors.horizontalCenter: parent.horizontalCenteranchors.verticalCenter: parent.verticalCenterproperty bool maximized: falsewidth: window.itemWidthheight: window.itemHeightborder.color: "red"border.width: 5color: "green"Text {anchors.horizontalCenter: parent.horizontalCenteranchors.verticalCenter: parent.verticalCentertext: name}MouseArea {anchors.fill: parentonClicked: {console.log("New current index: " + index);tumbler.currentIndex = index}}Behavior on width { NumberAnimation { duration: 300; easing.type: Easing.InOutCubic } }Behavior on height { NumberAnimation { duration: 300; easing.type: Easing.InOutCubic } }}}}function toggleMaximize() {var item = tumbler.currentItem;if (item.maximized) {item.innerWidth = Qt.binding(function() { return window.itemWidth; });item.innerHeight = Qt.binding(function() { return window.itemHeight; });tumbler.contentItem.interactive = true;} else {item.innerWidth = Qt.binding(function() { return tumbler.width; });item.innerHeight = Qt.binding(function() { return tumbler.height; });tumbler.contentItem.interactive = false;}item.maximized = !(item.maximized);tumbler.contentItem.onMovementEnded.disconnect(toggleMaximize);}ColumnLayout {anchors.fill: parentTumbler {id: tumblermodel: nameModelvisibleItemCount: Math.floor(width / window.itemWidth - 1) | 1delegate: itemDelegatecontentItem: PathView {model: tumbler.modeldelegate: tumbler.delegateclip: truepathItemCount: tumbler.visibleItemCountpreferredHighlightBegin: 0.5preferredHighlightEnd: 0.5dragMargin: width / 2interactive: truesnapMode: PathView.SnapToItempath: Path {startX: 0startY: tumbler.height / 2PathLine {x: tumbler.widthy: tumbler.height / 2}}}Layout.fillWidth: trueLayout.fillHeight: true}Button {id: maximizeButtontext: "Toggle maximize"Layout.fillWidth: trueLayout.fillHeight: falseonClicked: {if (tumbler.contentItem.moving)tumbler.contentItem.onMovementEnded.connect(toggleMaximize);elsetoggleMaximize();}}}}

              1 ReplyLast reply ReplyQuote1

              • SeeLook @dv__last edited by


                @dv__ Thanks for sharing your experience. My 'bended' Tumbler also reacts for clicks now.
                BTW, I think all QML controls are 'meant to be bend' , so as far as one doesn't break - it is allowed.

                In 'our' examples the crucial part is the PathView which does all tricks. It just uses model and delegate from the tumbler.

                I noticed that also in Your example hitting 'toggle maximize' button without touching tumbler before, causes maximizing wrong item (not this one in the middle). My workaround is to use a timer as follow:

                Timer { // workaround to properly select 0 item, call it with delay running: true interval: 50 onTriggered: tumbler.currentIndex = 0 }

                1 ReplyLast reply ReplyQuote0

                • D

                  dv__last edited by


                  Hmm good catch!

                  Although it turns out that I can't flick the list, because the items require some custom mouse move handling ... which reduces it to a non-interactive PathView. So, solved I guess! Thanks for pointing out the Tumbler though, and I hope my examples prove to be useful to others.

                  1 ReplyLast reply ReplyQuote0

                  • Slideshow style item navigation with zooming in of current item (2024)

                    References

                    Top Articles
                    Latest Posts
                    Article information

                    Author: Virgilio Hermann JD

                    Last Updated:

                    Views: 5387

                    Rating: 4 / 5 (41 voted)

                    Reviews: 80% of readers found this page helpful

                    Author information

                    Name: Virgilio Hermann JD

                    Birthday: 1997-12-21

                    Address: 6946 Schoen Cove, Sipesshire, MO 55944

                    Phone: +3763365785260

                    Job: Accounting Engineer

                    Hobby: Web surfing, Rafting, Dowsing, Stand-up comedy, Ghost hunting, Swimming, Amateur radio

                    Introduction: My name is Virgilio Hermann JD, I am a fine, gifted, beautiful, encouraging, kind, talented, zealous person who loves writing and wants to share my knowledge and understanding with you.