[Solved] iOS Style Rearrange of Icons



  • Lets say I have several rectangles on a page arranged in a grid. I want to implement the iPhone/iPad feature where a user can click and hold on a rectangle, signal to the user that he can move them (iOS does squiggly movement), and then allow him to rearrange the icons while the other icons fill in around his movement. He also needs a button on the interface to stop the rearrange mode. Does this make sense?

    How could I implement something like this in QML.



  • What you are proposing is quite complicated to to begin with. I would start writing on paper a state machine of the basic states that are needed. The default state is easy, the "squiggly" state is entered when someone long-presses the MouseArea, and is the same but adds animations to the rectangles, and allows dragging of them, etc.

    The next one is probably the harder. I have no idea on how to do it, since I haven't played with dragging stuff.



  • Appreciate your help, really need to get my mind around the element drag rearrange code... mind blown



  • Isn't this exactly what a "GridView":http://doc.qt.nokia.com/latest/qml-gridview.html does?
    You can use an "onPressAndHold":http://doc.qt.nokia.com/latest/qml-mousearea.html#onPressAndHold-signal inside the delegate to wait for the users action and then reparent that item to move it.
    The other items will slide in to position (you can use "onRemove":http://doc.qt.nokia.com/latest/qml-gridview.html#onRemove-signal signal).



  • Is onLongPress the same as onPressAndHold?



  • Oh yes, it would be :) Thanks.



  • I guess that would just leave animating the squiggly state, which I assume could be done in the delegate.

    xsacha, thanks for the update. And the links to the docs! :)



  • Not quite sure what you mean by a 'squiggle'. I assume you just mean some sort of animation related to 'loading' though.

    I will show how I manage loading events. I use a rotating circle with different shades of black along the perimeter:

    loading.png
    http://goo.gl/SDG7T

    Loading.qml
    @
    import QtQuick 1.0
    Image {
    id: loading; source: "images/loading.png"
    NumberAnimation on rotation {
    from: 0; to: 360; running: loading.visible == true; loops: Animation.Infinite; duration: 900
    }
    }@

    You could just have the Loading item inside your delegate then:
    @
    Loading { id: squiggle; width:36; height:36; visible: false }
    @

    There are a few ways to enable/disable the Loading element. One way is to use the onPressed signal to make the Loading element visible (visible = true) and onReleased to make it invisible (visible = false).

    @
    onPressed: squiggle.visible = true
    onReleased: squiggle.visible = false
    @

    You would also make it invisible when the onPressAndHold enters though, as it will be reparented as that stage and not part of the GridView. Makes sense I think :).



  • Squiggly refers to the slight rotation oscillation on the icons when in a moveable state. Here is a "video":http://www.youtube.com/watch?v=qnXoGnUU6uI



  • Oh, I see.
    Not sure if the OP wants to replicate this effect exactly as it seems a bit silly and obvious copying if you used it.

    However, to do this, you would have to make use of States.

    @
    GridView {
    id: view
    ...
    states: [
    State {
    name: "normal";
    ...
    },
    State {
    name: "squiggle";
    ...
    },
    ]
    }
    @

    Inside the delegate you would use:
    @
    onPressAndHold: {
    // code mentioned above for reparenting item
    ...
    view.state="squiggle"
    }
    @

    Then you would have code within the delegate for the animation, as follows:
    @
    Image {
    id: icon;
    source: modelImageUrl // derived from model
    NumberAnimation on rotation {
    from: -40; to: 40; running: view.state=="squiggle"; loops: Animation.Infinite; duration: 400
    }
    }
    @

    I haven't tested that rotation out but I assume it loops -40, 40, -40. 400ms per side. Which is what you would want.



  • So has anyone confirmed that rearranging an item in the grid view will cause the items to rearrange.



  • I can confirm this.

    Where your model is:
    @
    model: EventModel {id: listmodel}
    @
    You can remove the object from the grid using:
    @
    listmodel.remove(index)
    @
    Or simply move it to the end of the grid while you re-parent it and assign it to a new object:
    @
    listmodel.move(index, listmodel.count - 1, 1)
    @
    And the grid will automatically refit. Note: Use the onRemove signal to create a fancy animation to go with this. Or use Behaviour modifiers.

    You can also use "Flow":http://doc.qt.nokia.com/latest/qml-flow.html QML element as an alternative to GridView. It doesn't support model/delegate but rather Loader's and Qt.createObject's. As there is no predefined order, the order is created only from what appears. So you can remove objects from the view and have them realign depending on visibility/opacity and position.



  • I can't wait to try this on Monday morning.

    Here is another complexity: what if I have two different sizes of rectangles that need rearranging?



  • [quote author="kyleplattner" date="1292095856"]Here is another complexity: what if I have two different sizes of rectangles that need rearranging?[/quote]

    The GridView will still be a grid of identically sized areas. You'd have space around the smaller ones.



  • They will certainly appear as different sizes. However, as Bradley says, the center of the rectangle will not change. There will just be more space around them.
    You can change the spacing of the GridView by using "cellHeight":http://doc.qt.nokia.com/4.7-snapshot/qml-gridview.html#cellHeight-prop and "cellWidth":http://doc.qt.nokia.com/4.7-snapshot/qml-gridview.html#cellWidth-prop properties.

    If you need variable spacing, perhaps you would prefer a Flow element.



  • I will have to apologize but I started poking around and I cannot figure out the reparenting code. Here is how I have things setup:

    @import Qt 4.7

    Rectangle {
    width: 640
    height: 480
    color: "#111111"

        Component {
               id: widgetmodel
               Item {
                   width: grid.cellWidth; height: grid.cellHeight
                   Column {
                       anchors.fill: parent
                       Image { source: portrait; anchors.horizontalCenter: parent.horizontalCenter }
                       
                   }
               }
           }
    
           GridView {
               id: grid
               x: 0
               y: 0
               width: 640
               height: 480
               anchors.fill: parent
               cellWidth: 80; cellHeight: 80
    
               model: WidgetModel {}
               delegate: widgetmodel
               highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
               focus: true
    
           }
    
           MouseArea {
               id: mouse_area1
               x: 0
               y: 0
               width: grid.width
               height: grid.height
               onPressAndHold: {
    
                   widgetmodel.move(index, listmodel.count - 1, 1)
               }
           }
    

    }
    @



  • Also, if I use flow with a mousearea over it, how would I reparent each of the items in QML?



  • So far this is my flow implementation and it runs and the icons squiggle but I cannot move the widgets around:

    @import Qt 4.7

    Rectangle {
    width: 640
    height: 480
    color: "#000000"

    Flow {
        id:flowtest
        x: 0
        y: 0
        anchors.rightMargin: 0
        anchors.bottomMargin: 4
        anchors.leftMargin: 8
        anchors.topMargin: 4
             anchors.fill: parent
             anchors.margins: 4
             spacing: 10
    
    
            Image {
                source: "Images/widget.png"
                id: number1
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number1.x; to: number1.x + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number1.y; to: number1.y + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
            Image {
                source: "Images/widget.png"
                id: number2
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number2.x; to: number2.x - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number2.y; to: number2.y - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
            Image {
                source: "Images/widget.png"
                id: number3
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number3.x; to: number3.x + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number3.y; to: number3.y + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
            Image {
                source: "Images/widget.png"
                id: number4
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number4.x; to: number4.x - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number4.y; to: number4.y - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
            Image {
                source: "Images/widget.png"
                id: number5
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number5.x; to: number5.x + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number5.y; to: number5.y + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
            Image {
                source: "Images/widget.png"
                id: number6
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number6.x; to: number6.x - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number6.y; to: number6.y - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
            Image {
                source: "Images/widget.png"
                id: number7
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number7.x; to: number7.x - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number7.y; to: number7.y - 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
            Image {
                source: "Images/widget.png"
                id: number8
                PropertyAnimation on x { running: number1.state=="squiggle"; from: number8.x; to: number8.x + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
                PropertyAnimation on y { running: number1.state=="squiggle"; from: number8.y; to: number8.y + 1; duration: 100; loops: Animation.Infinite; easing.type: Easing.OutInBounce }
            }
    
    
    
                    Transition {
                             NumberAnimation { properties: "x,y"; easing.type: Easing.InOutQuad }
    
                         }
    
    
    }
    
    MouseArea {
        id: mouse_area1
        x: 0
        y: 0
        width: 640
        height: 480
        drag.target: number1
        onPressAndHold:{
    
            if(number1.state != "squiggle")
                {
    
            number1.state="squiggle"
            number2.state="squiggle"
            number3.state="squiggle"
            number4.state="squiggle"
            number5.state="squiggle"
            number6.state="squiggle"
            number7.state="squiggle"
            number8.state="squiggle"
    
    
            }
    
            else
                {
                number1.state="Base State"
                number2.state="Base State"
                number3.state="Base State"
                number4.state="Base State"
                number5.state="Base State"
                number6.state="Base State"
                number7.state="Base State"
                number8.state="Base State"
    
            }
    
        }
    }
    

    }
    @



  • I don't now that reparenting was the right word to use. I just want to move them and have them snap to a new location while the other icons fill in around it.





  • I've updated the above wiki entry with an alternate implementation.



  • I used the code in the following path, but when i added more list modle element in the list , i can't scroll the list if i want to move the last element in the end, what should i do ?

    This has been made in to a Wiki Entry: http://developer.qt.nokia.com/wiki/Drag_and_Drop_within_a_GridView


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.