From a6f5af838e58a9845a023493343a0b9761eb2566 Mon Sep 17 00:00:00 2001 From: Ian Walters Date: Thu, 7 May 2009 11:32:36 +1000 Subject: Move 'final' contacts application to demos. Will finish the applicaiton in this directory to fully functional. Missing features - removing contacts, clearing search, key only access Known bugs - Scroll crashes if data changes while scrolling, VerticalLayout doesn't align horizontally correctly when in ListView delegate. ListView only shows one element on startup, even though query consistently indicates 1000 items as count. --- demos/declarative/contacts/Button.qml | 57 +++++ demos/declarative/contacts/Contact.qml | 93 ++++++++ demos/declarative/contacts/ContactField.qml | 60 +++++ demos/declarative/contacts/FieldText.qml | 160 +++++++++++++ demos/declarative/contacts/RemoveButton.qml | 120 ++++++++++ demos/declarative/contacts/SearchBar.qml | 24 ++ demos/declarative/contacts/contacts.qml | 262 +++++++++++++++++++++ demos/declarative/contacts/contacts.sqlite | Bin 0 -> 86016 bytes demos/declarative/contacts/pics/cancel.png | Bin 0 -> 1038 bytes demos/declarative/contacts/pics/email.png | Bin 0 -> 977 bytes demos/declarative/contacts/pics/new.png | Bin 0 -> 688 bytes demos/declarative/contacts/pics/ok.png | Bin 0 -> 655 bytes demos/declarative/contacts/pics/phone.png | Bin 0 -> 624 bytes demos/declarative/contacts/pics/search.png | Bin 0 -> 635 bytes demos/declarative/contacts/pics/trash.png | Bin 0 -> 989 bytes .../tutorials/contacts/Final/Button.qml | 38 --- .../tutorials/contacts/Final/Contact.qml | 57 ----- .../tutorials/contacts/Final/ContactField.qml | 36 --- .../tutorials/contacts/Final/ContactView2.qml | 62 ----- .../tutorials/contacts/Final/FieldText.qml | 98 -------- .../tutorials/contacts/Final/RemoveButton.qml | 80 ------- .../tutorials/contacts/Final/SearchBar.qml | 18 -- .../tutorials/contacts/Final/contacts.qml | 148 ------------ 23 files changed, 776 insertions(+), 537 deletions(-) create mode 100644 demos/declarative/contacts/Button.qml create mode 100644 demos/declarative/contacts/Contact.qml create mode 100644 demos/declarative/contacts/ContactField.qml create mode 100644 demos/declarative/contacts/FieldText.qml create mode 100644 demos/declarative/contacts/RemoveButton.qml create mode 100644 demos/declarative/contacts/SearchBar.qml create mode 100644 demos/declarative/contacts/contacts.qml create mode 100644 demos/declarative/contacts/contacts.sqlite create mode 100644 demos/declarative/contacts/pics/cancel.png create mode 100644 demos/declarative/contacts/pics/email.png create mode 100644 demos/declarative/contacts/pics/new.png create mode 100644 demos/declarative/contacts/pics/ok.png create mode 100644 demos/declarative/contacts/pics/phone.png create mode 100644 demos/declarative/contacts/pics/search.png create mode 100644 demos/declarative/contacts/pics/trash.png delete mode 100644 examples/declarative/tutorials/contacts/Final/Button.qml delete mode 100644 examples/declarative/tutorials/contacts/Final/Contact.qml delete mode 100644 examples/declarative/tutorials/contacts/Final/ContactField.qml delete mode 100644 examples/declarative/tutorials/contacts/Final/ContactView2.qml delete mode 100644 examples/declarative/tutorials/contacts/Final/FieldText.qml delete mode 100644 examples/declarative/tutorials/contacts/Final/RemoveButton.qml delete mode 100644 examples/declarative/tutorials/contacts/Final/SearchBar.qml delete mode 100644 examples/declarative/tutorials/contacts/Final/contacts.qml diff --git a/demos/declarative/contacts/Button.qml b/demos/declarative/contacts/Button.qml new file mode 100644 index 0000000..d9f1236 --- /dev/null +++ b/demos/declarative/contacts/Button.qml @@ -0,0 +1,57 @@ +Item { + id: button + width: 30 + height: 30 + property var icon: "" + signal clicked + Rect { + id: buttonRect + anchors.fill: parent + color: "lightgreen" + radius: 5 + Image { + id: iconImage + source: button.icon + anchors.horizontalCenter: buttonRect.horizontalCenter + anchors.verticalCenter: buttonRect.verticalCenter + } + MouseRegion { + id: buttonMouseRegion + anchors.fill: buttonRect + onClicked: { button.clicked.emit() } + } + states: [ + State { + name: "pressed" + when: buttonMouseRegion.pressed == true + SetProperty { + target: buttonRect + property: "color" + value: "green" + } + } + ] + transitions: [ + Transition { + fromState: "*" + toState: "pressed" + ColorAnimation { + duration: 200 + } + }, + Transition { + fromState: "pressed" + toState: "*" + ColorAnimation { + duration: 1000 + } + } + ] + } + opacity: Behaviour { + NumericAnimation { + property: "opacity" + duration: 250 + } + } +} diff --git a/demos/declarative/contacts/Contact.qml b/demos/declarative/contacts/Contact.qml new file mode 100644 index 0000000..342c356 --- /dev/null +++ b/demos/declarative/contacts/Contact.qml @@ -0,0 +1,93 @@ +Item { + id: contactDetails + anchors.fill: parent + + property var contactId: "" + property var label: "" + property var phone: "" + property var email: "" + + onLabelChanged: { labelField.value = label } + onEmailChanged: { emailField.value = email } + onPhoneChanged: { phoneField.value = phone } + + resources: [ + SqlQuery { + id: updateContactQuery + connection: contactDatabase + query: "UPDATE contacts SET label = :l, email = :e, phone = :p WHERE recid = :r" + bindings: SqlBind { + name: ":r" + value: contactId + } + bindings: SqlBind { + name: ":l" + value: labelField.value + } + bindings: SqlBind { + name: ":e" + value: emailField.value + } + bindings: SqlBind { + name: ":p" + value: phoneField.value + } + }, + SqlQuery { + id: insertContactQuery + connection: contactDatabase + query: "INSERT INTO contacts (label, email, phone) VALUES(:l, :e, :p)" + bindings: SqlBind { + name: ":l" + value: labelField.value + } + bindings: SqlBind { + name: ":e" + value: emailField.value + } + bindings: SqlBind { + name: ":p" + value: phoneField.value + } + } + + ] + function update() { + updateContactQuery.exec(); + } + function insert() { + insertContactQuery.exec(); + } + VerticalLayout { + id: layout + anchors.fill: parent + spacing: 5 + margin: 5 + ContactField { + id: labelField + anchors.left: layout.left + anchors.leftMargin: 5 + anchors.right: layout.right + anchors.rightMargin: 5 + label: "Name" + } + ContactField { + id: phoneField + anchors.left: layout.left + anchors.leftMargin: 5 + anchors.right: layout.right + anchors.rightMargin: 5 + icon: "pics/phone.png" + label: "Phone" + } + ContactField { + id: emailField + anchors.left: layout.left + anchors.leftMargin: 5 + anchors.right: layout.right + anchors.rightMargin: 5 + icon: "pics/email.png" + label: "Email" + } + } +} diff --git a/demos/declarative/contacts/ContactField.qml b/demos/declarative/contacts/ContactField.qml new file mode 100644 index 0000000..3ce2c1a --- /dev/null +++ b/demos/declarative/contacts/ContactField.qml @@ -0,0 +1,60 @@ +Item { + id: contactField + clip: true + height: 30 + property var label: "Name" + property var icon: "pics/phone.png" + property var value: "" + onValueChanged: { fieldText.text=field.value } + RemoveButton { + id: removeButton + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + expandedWidth: contactField.width + onConfirmed: { print('Clear field text'); fieldText.text='' } + } + FieldText { + id: fieldText + width: contactField.width-70 + anchors.right: removeButton.left + anchors.rightMargin: 5 + anchors.verticalCenter: parent.verticalCenter + label: contactField.label + text: contactField.value + onConfirmed: { contactField.value=fieldText.text } + } + Image { + anchors.right: fieldText.left + anchors.rightMargin: 5 + anchors.verticalCenter: parent.verticalCenter + source: contactField.icon + } + states: [ + State { + name: "editingText" + when: fieldText.state == 'editing' + SetProperty { + target: removeButton.anchors + property: "rightMargin" + value: -35 + } + SetProperty { + target: fieldText + property: "width" + value: contactField.width + } + } + ] + transitions: [ + Transition { + fromState: "" + toState: "*" + reversible: true + NumericAnimation { + properties: "width,rightMargin" + duration: 200 + } + } + ] +} diff --git a/demos/declarative/contacts/FieldText.qml b/demos/declarative/contacts/FieldText.qml new file mode 100644 index 0000000..eb18ef2 --- /dev/null +++ b/demos/declarative/contacts/FieldText.qml @@ -0,0 +1,160 @@ +Rect { + id: fieldText + height: 30 + radius: 5 + color: "black" + property var text: "" + property var label: "" + onTextChanged: { reset() } + signal confirmed + resources: [ + Script { + + function edit() { + if (!contacts.mouseGrabbed) { + fieldText.state='editing'; + contacts.mouseGrabbed=true; + } + } + function confirm() { + fieldText.text = textEdit.text; + fieldText.state=''; + contacts.mouseGrabbed=false; + fieldText.confirmed.emit(); + } + function reset() { + textEdit.text = fieldText.text; + fieldText.state=''; + contacts.mouseGrabbed=false; + } + + } + ] + Image { + id: cancelIcon + width: 22 + height: 22 + anchors.right: parent.right + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + source: "pics/cancel.png" + opacity: 0 + } + Image { + id: confirmIcon + width: 22 + height: 22 + anchors.left: parent.left + anchors.leftMargin: 4 + anchors.verticalCenter: parent.verticalCenter + source: "pics/ok.png" + opacity: 0 + } + TextEdit { + id: textEdit + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + anchors.verticalCenter: parent.verticalCenter + color: "white" + font.bold: true + readOnly: true + wrap: false + } + Text { + id: textLabel + x: 5 + width: parent.width-10 + anchors.verticalCenter: parent.verticalCenter + hAlign: "AlignHCenter" + color: contactDetails.state == "editing" ? "#505050" : "#AAAAAA" + font.italic: true + font.bold: true + text: fieldText.label + opacity: textEdit.text == '' ? 1 : 0 + opacity: Behaviour { + NumericAnimation { + property: "opacity" + duration: 250 + } + } + } + MouseRegion { + anchors.fill: cancelIcon + onClicked: { reset() } + } + MouseRegion { + anchors.fill: confirmIcon + onClicked: { confirm() } + } + MouseRegion { + id: editRegion + anchors.fill: textEdit + onClicked: { edit() } + } + states: [ + State { + name: "editing" + SetProperty { + target: confirmIcon + property: "opacity" + value: 1 + } + SetProperty { + target: cancelIcon + property: "opacity" + value: 1 + } + SetProperty { + target: fieldText + property: "color" + value: "white" + } + SetProperty { + target: textEdit + property: "color" + value: "black" + } + SetProperty { + target: textEdit + property: "readOnly" + value: false + } + SetProperty { + target: textEdit + property: "focus" + value: true + } + SetProperty { + target: editRegion + property: "opacity" + value: 0 + } + SetProperty { + target: textEdit.anchors + property: "leftMargin" + value: 34 + } + SetProperty { + target: textEdit.anchors + property: "rightMargin" + value: 34 + } + } + ] + transitions: [ + Transition { + fromState: "" + toState: "*" + reversible: true + NumericAnimation { + properties: "opacity,leftMargin,rightMargin" + duration: 200 + } + ColorAnimation { + duration: 150 + } + } + ] +} diff --git a/demos/declarative/contacts/RemoveButton.qml b/demos/declarative/contacts/RemoveButton.qml new file mode 100644 index 0000000..114db2e --- /dev/null +++ b/demos/declarative/contacts/RemoveButton.qml @@ -0,0 +1,120 @@ +Rect { + id: removeButton + width: 30 + height: 30 + color: "red" + radius: 5 + property var expandedWidth: 230 + signal confirmed + resources: [ + Script { + function toggle() { + if (removeButton.state == 'opened') { + removeButton.state = ''; + contacts.mouseGrabbed=false; + } else { + if (!contacts.mouseGrabbed) { + removeButton.state = 'opened'; + contacts.mouseGrabbed=true; + } + } + } + } + ] + Image { + id: trashIcon + width: 22 + height: 22 + anchors.right: parent.right + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + source: "pics/trash.png" + opacity: 1 + MouseRegion { + anchors.fill: parent + onClicked: { toggle() } + } + } + Image { + id: cancelIcon + width: 22 + height: 22 + anchors.right: parent.right + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + source: "pics/cancel.png" + opacity: 0 + MouseRegion { + anchors.fill: parent + onClicked: { toggle() } + } + } + Image { + id: confirmIcon + width: 22 + height: 22 + anchors.left: parent.left + anchors.leftMargin: 4 + anchors.verticalCenter: parent.verticalCenter + source: "pics/ok.png" + opacity: 0 + MouseRegion { + anchors.fill: parent + onClicked: { toggle(); removeButton.confirmed.emit() } + } + } + Text { + id: text + anchors.verticalCenter: parent.verticalCenter + anchors.left: confirmIcon.right + anchors.leftMargin: 4 + anchors.right: cancelIcon.left + anchors.rightMargin: 4 + font.bold: true + color: "white" + hAlign: "AlignHCenter" + text: "Remove" + opacity: 0 + } + states: [ + State { + name: "opened" + SetProperty { + target: removeButton + property: "width" + value: removeButton.expandedWidth + } + SetProperty { + target: text + property: "opacity" + value: 1 + } + SetProperty { + target: confirmIcon + property: "opacity" + value: 1 + } + SetProperty { + target: cancelIcon + property: "opacity" + value: 1 + } + SetProperty { + target: trashIcon + property: "opacity" + value: 0 + } + } + ] + transitions: [ + Transition { + fromState: "*" + toState: "opened" + reversible: true + NumericAnimation { + properties: "opacity,x,width" + duration: 200 + } + } + ] +} diff --git a/demos/declarative/contacts/SearchBar.qml b/demos/declarative/contacts/SearchBar.qml new file mode 100644 index 0000000..3ee2665 --- /dev/null +++ b/demos/declarative/contacts/SearchBar.qml @@ -0,0 +1,24 @@ +Rect { + id: searchBar + color: "white" + property var text: searchEdit.text + Image { + id: searchIcon + anchors.left: parent.left + anchors.leftMargin: 5 + anchors.verticalCenter: parent.verticalCenter + source: "pics/search.png" + } + TextEdit { + id: searchEdit + anchors.left: searchIcon.right + anchors.right: parent.right + anchors.leftMargin: 5 + anchors.rightMargin: 5 + anchors.verticalCenter: parent.verticalCenter + readOnly: false + wrap: false + focus: true + text: "" + } +} diff --git a/demos/declarative/contacts/contacts.qml b/demos/declarative/contacts/contacts.qml new file mode 100644 index 0000000..879c616 --- /dev/null +++ b/demos/declarative/contacts/contacts.qml @@ -0,0 +1,262 @@ +Rect { + id: contacts + width: 240 + height: 320 + color: "black" + property var mode: "list" + property var mouseGrabbed: false + resources: [ + SqlConnection { + id: contactDatabase + name: "qmlConnection" + driver: "QSQLITE" + databaseName: "contacts.sqlite" + }, + SqlQuery { + id: contactList + connection: contactDatabase + query: "SELECT recid, label, email, phone FROM contacts WHERE lower(label) LIKE lower(:searchTerm) ORDER BY label, recid" + bindings: SqlBind { + name: ":searchTerm" + value: searchBar.text + '%' + } + }, + Component { + id: contactDelegate + Item { + id: wrapper + x: 0 + width: ListView.view.width + height: 34 + Text { + id: label + x: 40 + y: 12 + width: parent.width-30 + text: model.label + color: "white" + font.bold: true + children: [ + MouseRegion { + anchors.fill: parent + onClicked: { Details.qml = 'Contact.qml'; + wrapper.state='opened'; + contacts.mode = 'edit'; } + } + ] + } + Item { + id: Details + anchors.fill: wrapper + opacity: 0 + Bind { + target: Details.qmlItem + property: "contactId" + value: model.recid + } + Bind { + target: Details.qmlItem + property: "label" + value: model.label + } + Bind { + target: Details.qmlItem + property: "phone" + value: model.phone + } + Bind { + target: Details.qmlItem + property: "email" + value: model.email + } + } + states: [ + State { + name: "opened" + SetProperty { + target: wrapper + property: "height" + value: contactListView.height + } + SetProperty { + target: contactListView + property: "yPosition" + value: wrapper.y + } + SetProperty { + target: contactListView + property: "locked" + value: 1 + } + SetProperty { + target: label + property: "opacity" + value: 0 + } + SetProperty { + target: Details + property: "opacity" + value: 1 + } + } + ] + transitions: [ + Transition { + NumericAnimation { + duration: 500 + properties: "yPosition,height,opacity" + } + } + ] + Connection { + sender: confirmEditButton + signal: "clicked()" + script: { + if (wrapper.state == 'opened' && !contacts.mouseGrabbed) { + Details.qmlItem.update(); + wrapper.state = ''; + contacts.mode = 'list'; + contactList.exec(); + } + + } + } + Connection { + sender: cancelEditButton + signal: "clicked()" + script: { + if (wrapper.state == 'opened' && !contacts.mouseGrabbed) { + wrapper.state = ''; + contacts.mode = 'list'; + } + + } + } + } + } + ] + Button { + id: newContactButton + anchors.top: parent.top + anchors.topMargin: 5 + anchors.right: parent.right + anchors.rightMargin: 5 + icon: "pics/new.png" + onClicked: { print("open new contact edit"); newContactItem.label = ''; newContactItem.phone = ''; newContactItem.email = ''; contacts.mode = 'new' } + opacity: contacts.mode == 'list' ? 1 : 0 + } + Button { + id: confirmEditButton + anchors.top: parent.top + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 5 + icon: "pics/ok.png" + opacity: contacts.mode == 'list' || contacts.mouseGrabbed ? 0 : 1 + } + Button { + id: cancelEditButton + anchors.top: parent.top + anchors.topMargin: 5 + anchors.right: parent.right + anchors.rightMargin: 5 + icon: "pics/cancel.png" + opacity: contacts.mode == 'list' || contacts.mouseGrabbed ? 0 : 1 + } + ListView { + id: contactListView + anchors.left: parent.left + anchors.right: parent.right + anchors.top: cancelEditButton.bottom + anchors.bottom: searchBarWrapper.bottom + clip: true + model: contactList + delegate: contactDelegate + focus: contacts.mode != 'list' + } + Contact { + id: newContactItem + anchors.fill: contactListView + opacity: 0 + } + Connection { + sender: confirmEditButton + signal: "clicked()" + script: { + if (contacts.mode == 'new' && contacts.mouseGrabbed != 'true') { + newContactItem.insert(); + contacts.mode = 'list'; + contactList.exec(); + } + } + } + Connection { + sender: cancelEditButton + signal: "clicked()" + script: { + if (contacts.mode == 'new' && contacts.mouseGrabbed != 'true') { + contacts.mode = 'list'; + } + } + } + FocusRealm { + id: searchBarWrapper + height: 30 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottomMargin: 0 + focus: contacts.mode == 'list' + SearchBar { + id: searchBar + anchors.fill: parent + } + states: [ + State { + name: "searchHidden" + when: searchBar.text == '' || contacts.mode != 'list' + SetProperty { + target: searchBarWrapper.anchors + property: "bottomMargin" + value: -30 + } + } + ] + transitions: [ + Transition { + fromState: "*" + toState: "*" + NumericAnimation { + property: "bottomMargin" + duration: 250 + } + } + ] + } + states: [ + State { + name: "editNewState" + when: contacts.mode == 'new' + SetProperty { + target: contactListView + property: "opacity" + value: 0 + } + SetProperty { + target: newContactItem + property: "opacity" + value: 1 + } + } + ] + transitions: [ + Transition { + fromState: "*" + toState: "*" + NumericAnimation { + property: "opacity" + duration: 500 + } + } + ] +} diff --git a/demos/declarative/contacts/contacts.sqlite b/demos/declarative/contacts/contacts.sqlite new file mode 100644 index 0000000..559cdf3 Binary files /dev/null and b/demos/declarative/contacts/contacts.sqlite differ diff --git a/demos/declarative/contacts/pics/cancel.png b/demos/declarative/contacts/pics/cancel.png new file mode 100644 index 0000000..ecc9533 Binary files /dev/null and b/demos/declarative/contacts/pics/cancel.png differ diff --git a/demos/declarative/contacts/pics/email.png b/demos/declarative/contacts/pics/email.png new file mode 100644 index 0000000..04b3865 Binary files /dev/null and b/demos/declarative/contacts/pics/email.png differ diff --git a/demos/declarative/contacts/pics/new.png b/demos/declarative/contacts/pics/new.png new file mode 100644 index 0000000..c7ebac3 Binary files /dev/null and b/demos/declarative/contacts/pics/new.png differ diff --git a/demos/declarative/contacts/pics/ok.png b/demos/declarative/contacts/pics/ok.png new file mode 100644 index 0000000..5795f04 Binary files /dev/null and b/demos/declarative/contacts/pics/ok.png differ diff --git a/demos/declarative/contacts/pics/phone.png b/demos/declarative/contacts/pics/phone.png new file mode 100644 index 0000000..fc9c222 Binary files /dev/null and b/demos/declarative/contacts/pics/phone.png differ diff --git a/demos/declarative/contacts/pics/search.png b/demos/declarative/contacts/pics/search.png new file mode 100644 index 0000000..cc74e69 Binary files /dev/null and b/demos/declarative/contacts/pics/search.png differ diff --git a/demos/declarative/contacts/pics/trash.png b/demos/declarative/contacts/pics/trash.png new file mode 100644 index 0000000..2042595 Binary files /dev/null and b/demos/declarative/contacts/pics/trash.png differ diff --git a/examples/declarative/tutorials/contacts/Final/Button.qml b/examples/declarative/tutorials/contacts/Final/Button.qml deleted file mode 100644 index 14965b5..0000000 --- a/examples/declarative/tutorials/contacts/Final/Button.qml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/declarative/tutorials/contacts/Final/Contact.qml b/examples/declarative/tutorials/contacts/Final/Contact.qml deleted file mode 100644 index 679f4a8..0000000 --- a/examples/declarative/tutorials/contacts/Final/Contact.qml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - UPDATE contacts SET label = :l, email = :e, phone = :p WHERE recid = :r - - - - - - - - - INSERT INTO contacts (label, email, phone) VALUES(:l, :e, :p) - - - - - - - - - updateContactQuery.exec(); - - - insertContactQuery.exec(); - - - - - - - diff --git a/examples/declarative/tutorials/contacts/Final/ContactField.qml b/examples/declarative/tutorials/contacts/Final/ContactField.qml deleted file mode 100644 index 80ffd30..0000000 --- a/examples/declarative/tutorials/contacts/Final/ContactField.qml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/declarative/tutorials/contacts/Final/ContactView2.qml b/examples/declarative/tutorials/contacts/Final/ContactView2.qml deleted file mode 100644 index da1e5db..0000000 --- a/examples/declarative/tutorials/contacts/Final/ContactView2.qml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - SELECT recid AS contactid, label, email, phone FROM contacts ORDER BY label, recid - - - - - - - - - - - - - - - - - - - - - - - if (wrapper.state == 'opened') { - wrapper.state = ''; - } - - - - -