ContextMenu: Added Control element.
authorTomasz Olszak <olszak.tomasz@gmail.com>
Mon, 10 Jun 2013 21:33:34 +0000 (21:33 +0000)
committerTomasz Olszak <olszak.tomasz@gmail.com>
Mon, 10 Jun 2013 22:12:14 +0000 (00:12 +0200)
Some of content need to be moved to ContextMenuStyle in the future.
Additionally it should inherit from Control - not Item.

Change-Id: I3549abd7eec2d5028a8d621328a8d232a283e720
Reviewed-by: Tomasz Olszak <olszak.tomasz@gmail.com>
13 files changed:
examples/touch/content/ContextMenuPage.qml [new file with mode: 0644]
examples/touch/content/TabBarPage.qml
examples/touch/images/NOTICE.txt
examples/touch/images/internet-mail.png [new file with mode: 0644]
examples/touch/images/internet-web-browser.png [new file with mode: 0644]
examples/touch/main.qml
examples/touch/touch.pro
src/controls/Menu.qml [new file with mode: 0644]
src/controls/controls.pro
src/controls/qmldir
src/styles/DefaultSettings.js
src/styles/Floater.qml
src/styles/styles.pro

diff --git a/examples/touch/content/ContextMenuPage.qml b/examples/touch/content/ContextMenuPage.qml
new file mode 100644 (file)
index 0000000..06de2b7
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013 Tomasz Olszak <olszak.tomasz@gmail.com>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+import QtQuick 2.1
+import QtQuick.Controls 1.0
+import QtQuick.Controls.Tizen 1.0
+
+Item {
+    id:root
+    width: parent.width
+    height: parent.height
+
+
+    Menu {
+        id: menu
+        anchorItem: center
+        anchorPoint: Qt.point(center.width/2, center.height/2)
+        MenuItem {
+            text: "Test"
+            onTriggered: {
+                console.log("Test triggered")
+            }
+        }
+        MenuItem {
+            text: "Mail"
+            iconSource: "../images/internet-mail.png"
+        }
+        MenuItem {
+            text: "Web Browser"
+            iconSource: "../images/internet-web-browser.png"
+        }
+
+        MenuItem {
+            text: "Test4"
+        }
+    }
+
+
+    Text {
+        id: center
+        x: parent.width/2 - width/2
+        y: parent.height/2 - height/2
+        width: 100
+        height: 100
+        text: "Drag Anchor Point"
+        font.pixelSize: 26
+        verticalAlignment: Text.AlignVCenter
+        horizontalAlignment: Text.AlignHCenter
+        wrapMode: Text.WordWrap
+        Rectangle {
+            anchors.fill: parent
+            border.width: 1
+            border.color: "darkgrey"
+            color:"transparent"
+        }
+        MouseArea {
+            anchors.fill: parent
+            drag.target: center
+            onPressAndHold: menu.popup()
+        }
+    }
+
+    Button {
+        id:btnShow
+        width: 150
+        anchors.margins: 50
+        anchors.top: parent.top
+        anchors.horizontalCenter: parent.horizontalCenter
+        text: "Show"
+        onClicked: {
+            menu.popup()
+        }
+    }
+    Button {
+        width: 150
+        anchors.topMargin: 50
+        anchors.top: parent.top
+        anchors.left: btnShow.right
+        text: "Animate"
+        onClicked: animation.running = !animation.running
+    }
+    NumberAnimation {
+        id:animation
+        target: center
+        property: "x"
+        from: 0
+        to: parent.width
+        loops: Animation.Infinite
+        running: false
+        duration: 5000
+    }
+
+}
index 30e6ca2..5c7372c 100644 (file)
@@ -70,7 +70,7 @@ Item {
         }
         Tab {
             title: "5"
-            TextInputPage{ visible: true }
+            ContextMenuPage{ visible: true }
         }
     }   
 }
index 93a9afc..88ef2dc 100644 (file)
@@ -1,2 +1,10 @@
 Notice some of these images are derived from Google applications resources. They were provided under the following license:
 You may use the materials in this directory without restriction to develop your apps and to use in your apps.
+
+
+================
+
+
+icons are from the Tango Desktop project(http://tango.freedesktop.org/Tango_Desktop_Project):
+internet-mail.png
+internet-web-browser.png
diff --git a/examples/touch/images/internet-mail.png b/examples/touch/images/internet-mail.png
new file mode 100644 (file)
index 0000000..7e6b93b
Binary files /dev/null and b/examples/touch/images/internet-mail.png differ
diff --git a/examples/touch/images/internet-web-browser.png b/examples/touch/images/internet-web-browser.png
new file mode 100644 (file)
index 0000000..a979a92
Binary files /dev/null and b/examples/touch/images/internet-web-browser.png differ
index 9d6678c..136d961 100644 (file)
@@ -124,6 +124,10 @@ ApplicationWindow {
             page: "content/CheckBoxPage.qml"
         }
         ListElement {
+            title: "ContextMenu"
+            page: "content/ContextMenuPage.qml"
+        }
+        ListElement {
             title: "TextInput"
             page: "content/TextInputPage.qml"
         }
index 3cec48a..747c8dc 100644 (file)
@@ -8,6 +8,7 @@ OTHER_FILES += \
     content/SliderPage.qml \
     content/TabBarPage.qml \
     content/CheckBoxPage.qml \
-    content/TextInputPage.qml
+    content/TextInputPage.qml \
+    content/ContextMenuPage.qml
 
 
diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml
new file mode 100644 (file)
index 0000000..a1167ff
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2013 Tomasz Olszak <olszak.tomasz@gmail.com>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+import QtQuick 2.0
+import QtQuick.Controls 1.0
+import QtQuick.Controls.Styles.Tizen 1.0
+import QtQuick.Window 2.0
+
+
+Floater {
+    id: menu
+    visible:false
+
+    /*! In anchorPoint of this item menu arrow will be placed */
+    property Item anchorItem
+    /*! It is the point where top of the arrow will be placed.
+
+        If anchorItem is set then point is interpreter in anchorItem's coordinates
+    */
+    property point anchorPoint
+
+
+    /* for getting all menu items to model*/
+    default property alias menuItems: workaround.data
+
+    /* shows ContextMenu */
+    onVisibleChanged: {
+        if (visible) {
+            popup()
+        } else {
+            hide()
+        }
+    }
+    property string __state: "hidden"
+    function popup(){
+        __state = "shown"
+    }
+    function hide() {
+        __state = "hidden"
+    }
+
+    QtObject {
+        id:workaround
+        property list<QtObject> data
+
+    }
+
+    property Item control: menu
+
+    content: Item {
+        id: contentItem
+
+        property point __menuAnchorPoint: control.anchorItem ?
+                                          __globalAnchorPoint(control.width,control.height,control.x,control.y,control.anchorItem.x,control.anchorItem.y,control.anchorPoint) :
+                                          __globalAnchorPoint(control.width,control.height,control.x,control.y,control.anchorPoint)
+
+        /* internal position of ContextMenu relative to anchorPoint */
+        property int __position: updatePosition(__menuAnchorPoint)
+
+        function __globalAnchorPoint() {
+            var tmp = menu.parent.mapFromItem(anchorItem, anchorPoint.x, anchorPoint.y)
+            return Qt.point(tmp.x,tmp.y)
+        }
+        state: control.__state === "hidden" ? "contentHidden" : "contentShown"
+        states: [
+            State {
+                name:"contentHidden"
+                PropertyChanges {
+                    target: contentItem
+                    opacity:0
+                }
+            },
+            State {
+                name:"contentShown"
+                PropertyChanges {
+                    target: contentItem
+                    opacity:0
+                }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                to: "contentHidden"
+                SequentialAnimation {
+                    NumberAnimation {
+                        target: contentItem
+                        property: "opacity"
+                        duration: 250
+                    }
+                    PropertyAction {
+                        target: menu
+                        property: "visible"
+                        value:false
+                    }
+                }
+            },
+            Transition {
+                to: "contentShown"
+                SequentialAnimation {
+                    PropertyAction {
+                        target: menu
+                        property: "visible"
+                        value:true
+                    }
+                    NumberAnimation {
+                        target: contentItem
+                        property: "opacity"
+                        to: 1
+                        duration: 250
+                    }
+                }
+            }
+        ]
+
+        function updatePosition() {
+            var pos = Qt.BottomEdge
+            if (arrow.height/2 + bg.height - Theme.contextMenu.arrow.anchors.bottomMargin < (menu.height - __menuAnchorPoint.y) ) {
+                pos = Qt.BottomEdge
+            } else if ( arrow.height/2 + bg.height - Theme.contextMenu.arrow.anchors.topMargin < __menuAnchorPoint.y ) {
+                pos = Qt.TopEdge
+            } else if (arrow.width/2 + bg.width - Theme.contextMenu.arrow.anchors.rightMargin < (menu.width - __menuAnchorPoint.x))  {
+                pos = Qt.RightEdge
+            } else if (arrow.width/2 + bg.width - Theme.contextMenu.arrow.anchors.leftMargin < __menuAnchorPoint.x)  {
+                pos = Qt.LeftEdge
+            }
+            return pos
+        }
+        anchors.fill: parent
+        Binding {
+            target: control
+            property: "anchors.fill"
+            value: control.parent
+        }
+
+        Behavior on opacity {
+            NumberAnimation{duration:250}
+        }
+        MouseArea {
+            anchors.fill: parent
+            onClicked: menu.hide()
+        }
+        implicitWidth: bg.width
+        implicitHeight: bg.height
+
+        TizenBorderImage {
+            id:arrow
+
+            source: "../Styles/Tizen/"+Theme.contextMenu.arrow.source.top
+            effectSource: "../Styles/Tizen/"+Theme.contextMenu.arrow.effectSource.top
+            backgroundColor: Theme.contextMenu.color
+
+            states: [
+                State {
+                    name:"bottom"
+                    when: __position === Qt.BottomEdge
+                    PropertyChanges {
+                        target: arrow
+                        rotation: 0
+                        x: Math.max(Math.min(__menuAnchorPoint.x - width/2, menu.width - arrow.width),0)
+                        y: __menuAnchorPoint.y - arrow.height/2
+                    }
+                    AnchorChanges {
+                        target: bg
+                        anchors.top: arrow.bottom
+                    }
+                    PropertyChanges {
+                        target: bg
+                        anchors.topMargin: -Theme.contextMenu.arrow.anchors.bottomMargin
+                        x: Math.min(Math.max(arrow.x+arrow.width/2-bg.width/2,0),menu.width - bg.width)
+                    }
+                },
+                State {
+                    name:"top"
+                    when: __position === Qt.TopEdge
+                    PropertyChanges {
+                        target: arrow
+                        rotation: 180
+                        x: Math.max(Math.min(__menuAnchorPoint.x - width/2, menu.width - arrow.width),0)
+                        y: __menuAnchorPoint.y - arrow.height/2
+                    }
+                    AnchorChanges {
+                        target: bg
+                        anchors.bottom: arrow.top
+                    }
+                    PropertyChanges {
+                        target: bg
+                        anchors.bottomMargin: -Theme.contextMenu.arrow.anchors.topMargin
+                        x: Math.min(Math.max(arrow.x+arrow.width/2-bg.width/2,0),menu.width - bg.width)
+                    }
+                },
+                State {
+                    name: "right"
+                    when: __position === Qt.RightEdge
+                    PropertyChanges {
+                        target: arrow
+                        rotation: -90
+                        x: __menuAnchorPoint.x + -arrow.width/2
+                        y: __menuAnchorPoint.y - arrow.height/2
+                    }
+                    AnchorChanges {
+                        target: bg
+                        anchors.left: arrow.right
+                    }
+                    PropertyChanges {
+                        target: bg
+                        anchors.leftMargin: -Theme.contextMenu.arrow.anchors.rightMargin
+                        y: Math.min(Math.max(arrow.y+arrow.height/2-bg.height/2,0),menu.height-bg.height)
+                    }
+
+                },
+                State {
+                    name:"left"
+                    when: __position === Qt.LeftEdge
+                    PropertyChanges {
+                        target: arrow
+                        rotation: 90
+                        x: __menuAnchorPoint.x - arrow.width/2
+                        y: __menuAnchorPoint.y - arrow.height/2
+                    }
+                    AnchorChanges {
+                        target: bg
+                        anchors.right: arrow.left
+                    }
+                    PropertyChanges {
+                        target: bg
+                        anchors.rightMargin: -Theme.contextMenu.arrow.anchors.leftMargin
+                        y: Math.min(Math.max(arrow.y+arrow.height/2-bg.height/2,0),menu.height-bg.height)
+                    }
+                }
+            ]
+        }
+        TizenBorderImage {
+            id:bg
+            width: Math.min(layout.contentWidth + layout.anchors.leftMargin + layout.anchors.rightMargin,Theme.contextMenu.maximumWidth)
+            height: layout.implicitHeight + layout.anchors.topMargin + layout.anchors.bottomMargin
+            source: "../Styles/Tizen/"+Theme.contextMenu.source
+            effectSource: "../Styles/Tizen/"+Theme.contextMenu.effectSource
+            backgroundColor: Theme.contextMenu.color
+
+            MouseArea {
+                anchors.fill: bg
+            }
+        }
+
+        Column {
+            id:layout
+            anchors.fill: bg
+
+            anchors.leftMargin: Theme.contextMenu.listLeftMargin + Theme.contextMenu.screen.leftMargin
+            anchors.rightMargin: Theme.contextMenu.listRightMargin + Theme.contextMenu.screen.rightMargin
+            anchors.topMargin: Theme.contextMenu.listTopMargin + Theme.contextMenu.screen.topMargin
+            anchors.bottomMargin: Theme.contextMenu.listBottomMargin + Theme.contextMenu.screen.bottomMargin
+
+            property real contentWidth: Theme.contextMenu.minimumWidth
+            Repeater {
+                model: menuItems
+
+                Item {
+                    id: delegateRoot
+                    implicitWidth: Math.max(itemBg.implicitWidth,label.contentWidth)
+                    implicitHeight: Theme.contextMenu.item.height// Math.max(itemBg.implicitHeight+separator.height,label.contentHeight+separator.height)
+                    width: layout.contentWidth
+                    MouseArea {
+                        id: mouseArea
+                        anchors.fill: parent
+                        TizenBorderImage {
+                            id:itemBg
+                            anchors.fill: parent
+                            source: "../Styles/Tizen/"+Theme.contextMenu.item.source.pressed
+                            backgroundColor: mouseArea.pressed && enabled ? Theme.contextMenu.item.color.pressed: Theme.contextMenu.item.color.normal
+                        }
+                        Image {
+                            id:icon
+                            anchors.left:parent.left
+                            anchors.verticalCenter: parent.verticalCenter
+                            source: modelData.iconSource
+                            width: Theme.contextMenu.item.iconWidth
+                            height: Theme.contextMenu.item.iconHeight
+                        }
+
+                        Text {
+                            id:label
+                            anchors.left: icon.right
+                            anchors.leftMargin: Theme.contextMenu.item.spacing
+                            anchors.top: parent.top
+                            anchors.bottom: parent.bottom
+                            anchors.right: parent.right
+                            anchors.bottomMargin: 3
+                            text: modelData.text
+                            color: Theme.contextMenu.item.text.color.normal
+                            font.pixelSize: Theme.contextMenu.item.text.font.pixelSize
+                            verticalAlignment: Text.AlignVCenter
+                        }
+                        Rectangle {
+                            id:separator
+                            anchors.bottom: parent.bottom
+                            anchors.left: parent.left
+                            anchors.right: parent.right
+                            height: 2
+                            color: Theme.contextMenu.listItemDividerColor2
+                            visible: index < menuItems.length -1
+                        }
+                        onClicked: menu.visible = false
+                    }
+                }
+
+                onItemAdded: {
+                    layout.contentWidth = Math.max(item.implicitWidth, layout.contentWidth)
+                }
+            }
+        }
+    }
+}
index 011d27b..ed6df76 100644 (file)
@@ -3,7 +3,8 @@ TARGETPATH = QtQuick/Controls/Tizen
 
 QML_FILES += \
     Switch.qml \
-    DetailButton.qml
+    DetailButton.qml\
+    Menu.qml
 
 CONFIG += force_independent
 
index 61e242f..6275a9c 100644 (file)
@@ -2,3 +2,4 @@ module QtQuick.Controls.Tizen
 
 Switch 1.0 Switch.qml
 DetailButton 1.0 DetailButton.qml
+Menu 1.0 Menu.qml
index 8936db7..b0563ca 100644 (file)
@@ -959,3 +959,74 @@ var detailButton = {
         //highlighted: colors.F032L1P,
     }
 }
+
+var contextMenu = {
+
+    color: colors.B061L4,
+    listItemDividerColor1: colors.B0622L1,
+    listItemDividerColor2: colors.B0622L2,
+    item: {
+        color: {
+            normal: colors.B061L4,
+            pressed: colors.B044,
+            highlighted: colors.B044,
+        },
+        text: {
+            color: {
+                normal: colors.B063L5,
+                pressed: colors.B063L5,
+                highlighted: colors.B063L5,
+            },
+            font: {
+                pixelSize:40
+            }
+        },
+        source: {
+            pressed: images.img_00_more_popup_press
+        },
+        height: 112,
+        iconWidth: 45,
+        iconHeight: 45,
+        spacing: 16
+    },
+    gridItemDividerColor1: colors.B0623L1,
+    gridItemDividerColor2: colors.B0623L2,
+
+    source: images.img_00_popup_bubble_bg,
+    effectSource: images.img_00_popup_bubble_bg_ef,
+
+    arrow: {
+        source: {
+            left: images.img_00_popup_bubble_tail_left,
+            right: images.img_00_popup_bubble_tail_right,
+            top: images.img_00_popup_bubble_tail_top,
+            bottom: images.img_00_popup_bubble_tail_bottom
+        },
+        effectSource: {
+            left: images.img_00_popup_bubble_tail_left_ef,
+            right: images.img_00_popup_bubble_tail_right_ef,
+            top: images.img_00_popup_bubble_tail_top_ef,
+            bottom: images.img_00_popup_bubble_tail_bottom_ef
+        },
+        width: 36,
+        height: 36,
+        anchors: {
+            topMargin: 18,
+            leftMargin: 12,
+            rightMargin:12,
+            bottomMargin:10
+        }
+    },
+    minimumWidth: 386,
+    maximumWidth: 620,
+    listTopMargin: 8,
+    listBottomMargin: 16,
+    listLeftMargin: 10,
+    listRightMargin: 10,
+    screen: {
+        topMargin: 24,
+        bottomMargin: 24,
+        leftMargin: 24,
+        rightMargin: 24
+    }
+}
index 00a2d86..326bdd2 100644 (file)
@@ -4,8 +4,8 @@ Item {
     id: floaterBehavior
     visible: false
     property Component content
-    width: contentLoader ? contentLoader.item.width:0
-    height: contentLoader ? contentLoader.item.height:0
+    width: contentLoader ? contentLoader.item.implicitWidth:0
+    height: contentLoader ? contentLoader.item.implicitHeight:0
     property Item root: findRoot()
 
     function findRoot() {
@@ -19,6 +19,7 @@ Item {
     Loader {
         id: contentLoader
         sourceComponent: content
+        anchors.fill: parent
     }
 
     states: State {
@@ -26,5 +27,4 @@ Item {
         when: Qt.isQtObject(contentLoader.item) && contentLoader.item.opacity > 0
         ParentChange { target: floaterBehavior; parent: root }
     }
-
 }
index 9a05b35..84925a2 100644 (file)
@@ -10,7 +10,8 @@ QML_FILES = \
     TabViewStyle.qml \
     Floater.qml \
     SwitchStyle.qml\
-    DetailButtonStyle.qml
+    DetailButtonStyle.qml\
+    DefaultSettings.js
 
 # Images
 QML_FILES += \
@@ -19,9 +20,6 @@ QML_FILES += \
     images/720x1280/white/*.png \
     images/720x1280/white/*.sci
 
-QML_FILES += \
-    DefaultSettings.js
-
 CONFIG += force_independent
 
 OTHER_FILES += qmldir