Settings dialog infrastructure
authorTofe <chris.chapuis+gitorious@gmail.com>
Wed, 29 Feb 2012 21:24:03 +0000 (22:24 +0100)
committerTofe <chris.chapuis+gitorious@gmail.com>
Wed, 29 Feb 2012 21:24:03 +0000 (22:24 +0100)
There is now a basic infrastructure for building settings dialogs.

20 files changed:
qml/QuickNewsReader/content/images/apply.png [new file with mode: 0644]
qml/QuickNewsReader/content/images/cancel.png [new file with mode: 0644]
qml/QuickNewsReader/content/images/rss.png [new file with mode: 0755]
qml/QuickNewsReader/content/images/settings.png [new file with mode: 0644]
qml/QuickNewsReader/content/js/SettingsStorage.js [new file with mode: 0644]
qml/QuickNewsReader/content/modelimpl/FavoriteFeedsSourceModel.qml
qml/QuickNewsReader/content/modelimpl/LeMondeSequenceNews.qml
qml/QuickNewsReader/content/modelimpl/LeMondeSourceModel.qml
qml/QuickNewsReader/content/modelitf/SourceModel.qml
qml/QuickNewsReader/content/view/Categories.qml
qml/QuickNewsReader/content/view/FancyButton.qml [new file with mode: 0644]
qml/QuickNewsReader/content/view/FavoriteRssConfig.qml [new file with mode: 0644]
qml/QuickNewsReader/content/view/News.qml
qml/QuickNewsReader/content/view/NewsComments.qml [new file with mode: 0644]
qml/QuickNewsReader/content/view/NewsDelegate.qml
qml/QuickNewsReader/content/view/NewsDetail.qml
qml/QuickNewsReader/content/view/SourceConfigDialog.qml [new file with mode: 0644]
qml/QuickNewsReader/content/view/SourceDelegate.qml
qml/QuickNewsReader/main.qml
qtc_packaging/debian_fremantle/control

diff --git a/qml/QuickNewsReader/content/images/apply.png b/qml/QuickNewsReader/content/images/apply.png
new file mode 100644 (file)
index 0000000..01334d0
Binary files /dev/null and b/qml/QuickNewsReader/content/images/apply.png differ
diff --git a/qml/QuickNewsReader/content/images/cancel.png b/qml/QuickNewsReader/content/images/cancel.png
new file mode 100644 (file)
index 0000000..fd285bc
Binary files /dev/null and b/qml/QuickNewsReader/content/images/cancel.png differ
diff --git a/qml/QuickNewsReader/content/images/rss.png b/qml/QuickNewsReader/content/images/rss.png
new file mode 100755 (executable)
index 0000000..d64c669
Binary files /dev/null and b/qml/QuickNewsReader/content/images/rss.png differ
diff --git a/qml/QuickNewsReader/content/images/settings.png b/qml/QuickNewsReader/content/images/settings.png
new file mode 100644 (file)
index 0000000..4a37651
Binary files /dev/null and b/qml/QuickNewsReader/content/images/settings.png differ
diff --git a/qml/QuickNewsReader/content/js/SettingsStorage.js b/qml/QuickNewsReader/content/js/SettingsStorage.js
new file mode 100644 (file)
index 0000000..76a9a15
--- /dev/null
@@ -0,0 +1,56 @@
+//SettingsStorage.js
+.pragma library
+
+// First, let's create a short helper function to get the database connection
+function getDatabase() {
+     return openDatabaseSync("QuickNewsReader", "1.0", "SettingsStorageDatabase", 100000);
+}
+
+// At the start of the application, we can initialize the tables we need if they haven't been created yet
+function initialize() {
+    var db = getDatabase();
+    db.transaction(
+        function(tx) {
+            // Create the settings table if it doesn't already exist
+            // If the table exists, this is skipped
+            tx.executeSql('CREATE TABLE IF NOT EXISTS settings(setting TEXT UNIQUE, value TEXT)');
+      });
+}
+
+// This function is used to write a setting into the database
+function setSetting(setting, value) {
+   // setting: string representing the setting name (eg: “username”)
+   // value: string representing the value of the setting (eg: “myUsername”)
+   var db = getDatabase();
+   var res = "";
+   db.transaction(function(tx) {
+        var rs = tx.executeSql('INSERT OR REPLACE INTO settings VALUES (?,?);', [setting,value]);
+              //console.log(rs.rowsAffected)
+              if (rs.rowsAffected > 0) {
+                res = "OK";
+              } else {
+                res = "Error";
+              }
+        }
+  );
+  // The function returns “OK” if it was successful, or “Error” if it wasn't
+  return res;
+}
+
+// This function is used to retrieve a setting from the database
+function getSetting(setting) {
+   var db = getDatabase();
+   var res="";
+   db.transaction(function(tx) {
+     var rs = tx.executeSql('SELECT value FROM settings WHERE setting=?;', [setting]);
+     if (rs.rows.length > 0) {
+          res = rs.rows.item(0).value;
+     } else {
+         res = "Unknown";
+     }
+  })
+  // The function returns “Unknown” if the setting was not found in the database
+  // For more advanced projects, this should probably be handled through error codes
+  return res
+}
+
index fbbf451..cd6b9a0 100644 (file)
@@ -19,4 +19,6 @@ SourceModel {
     }
 
     loading: newsModel.loading
+    hasSettings: true
+    settingsComponent: "FavoriteRssConfig.qml"
 }
index 48c0a0c..c59f227 100644 (file)
@@ -15,4 +15,5 @@ XmlListModel {
     XmlRole { name: "link"; query: "lien_web/string()" }
     XmlRole { name: "description"; query: "description_appel/string()" }
     XmlRole { name: "detailedContent"; query: "contenu/string()" }
+    XmlRole { name: "commentURL"; query: "commentaires/url_commentaires/string()" }
 }
index 7fd5e3f..7a2b54d 100644 (file)
@@ -7,12 +7,14 @@ SourceModel {
     listViews: [
         { viewComponent: 'content/view/Categories.qml', viewId: 'categoriesRect' } ,
         { viewComponent: 'content/view/News.qml', viewId: 'newsRect' },
-        { viewComponent: 'content/view/NewsDetail.qml', viewId: 'newsDetailRect' }
+        { viewComponent: 'content/view/NewsDetail.qml', viewId: 'newsDetailRect' },
+        { viewComponent: 'content/view/NewsComments.qml', viewId: 'newsCommentsRect' }
     ]
     listModels: [
         categoriesModel,
         newsModel,
-        newsModel
+        newsModelDetail,
+        newsModelDetailComments
     ]
 
     function getSeqId(categoryIndex)
@@ -24,10 +26,38 @@ SourceModel {
         return ""
     }
 
+    function getNewsModelItem(prop)
+    {
+        var newsIndex = currentPath[2]
+
+        if( typeof newsModel.get(newsIndex) != "undefined" )
+            if( typeof newsModel.get(newsIndex)[prop] != "undefined" )
+                return newsModel.get(newsIndex)[prop]
+
+        return ""
+    }
+
     property variant categoriesModel: LeMondeSequencesList { }
     property variant newsModel: LeMondeSequenceNews {
         seqid: getSeqId(currentPath[1])
     }
+    property variant newsModelDetail: QtObject {
+        property variant parentModel: newsModel
+        property int sourceDepth: 3
+
+        property string urllink: getNewsModelItem('link')
+        property string htmlcontent: getNewsModelItem('detailedContent')
+        property string title: getNewsModelItem('title')
+        property string image: getNewsModelItem('image')
+    }
+    property variant newsModelDetailComments: QtObject {
+        property variant parentModel: newsModel
+        property int sourceDepth: 4
+
+        property string commentURL: "http://www.lemonde.fr" + getNewsModelItem('commentURL')
+        property string title: getNewsModelItem('title')
+        property string image: getNewsModelItem('image')
+    }
 
     loading: categoriesModel.loading || newsModel.loading
 }
index 5e6bb0a..1b5484f 100644 (file)
@@ -5,6 +5,8 @@ QtObject {
     property string name: "Source"
 
     property bool loading
+    property bool hasSettings: false
+    property string settingsComponent
 
     property variant listViews;
     property variant listModels;
index bca890e..695b752 100644 (file)
@@ -12,12 +12,40 @@ Rectangle {
         x: 0; y: 0
         width: 220; height: window.height
         model: currentSource.listModels[componentDepth-1]
-        footer: quitButtonDelegate
+        footer: getFooter()
         delegate: CategoryDelegate { }
         highlight: Rectangle { color: "steelblue" }
         highlightMoveSpeed: 9999999
+
+        function getFooter()
+        {
+            return componentDepth === 1 && currentSource.hasSettings ? settingsButtonDelegate : null
+        }
     }
     ScrollBar { scrollArea: categories; height: categories.height; width: 8; anchors.right: categories.right }
 
     Component.onCompleted: categories.currentIndex = -1
+
+    Component {
+        id: settingsButtonDelegate
+        Item {
+            width: categories.width; height: 70
+
+            FancyButton {
+                icon: "../images/settings.png"
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.bottom: parent.bottom
+                anchors.bottomMargin: -2
+
+                /*
+                onClicked: {
+                    if (editMenu.visible) {
+                        editMenu.opacity = 0.0
+                    } else {
+                        editMenu.opacity = 0.8
+                    }
+                }*/
+            }
+        }
+    }
 }
diff --git a/qml/QuickNewsReader/content/view/FancyButton.qml b/qml/QuickNewsReader/content/view/FancyButton.qml
new file mode 100644 (file)
index 0000000..8d73db1
--- /dev/null
@@ -0,0 +1,61 @@
+/* This file is part of MeeGoFrames Component Library
+ * Copyright (C) 2011 Martin Grimme  <martin.grimme _AT_ gmail.com>
+ *
+ * This program is free software; you can redistribute it, even commercially, 
+ * as long as this copyright notice remains in place.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+import Qt 4.7
+
+Rectangle {
+
+    property alias icon: imageLabel.source
+
+    signal clicked()
+
+    id: button
+    width: 70
+    height: 70
+
+    radius: 3
+    color: "#a0000000"
+
+
+    states: [
+        State {
+            name: "pressed"
+            when: mouseArea.pressed
+            PropertyChanges {
+                target: button
+                color: "#606060"
+                scale: 0.9
+            }
+        }
+    ]
+
+    Behavior on color {
+        ColorAnimation { duration: 100 }
+    }
+
+    Behavior on scale {
+        NumberAnimation { duration: 100 }
+    }
+
+    Image {
+        anchors.centerIn: parent
+        id: imageLabel
+        visible: source != ""
+        anchors.horizontalCenter: parent.horizontalCenter
+    }
+
+    MouseArea {
+        id: mouseArea
+        anchors.fill: parent
+        onClicked: parent.clicked()
+    }
+}
diff --git a/qml/QuickNewsReader/content/view/FavoriteRssConfig.qml b/qml/QuickNewsReader/content/view/FavoriteRssConfig.qml
new file mode 100644 (file)
index 0000000..ebf283c
--- /dev/null
@@ -0,0 +1,8 @@
+import QtQuick 1.0
+
+Rectangle {
+    width: 100
+    height: 62
+
+    color: "blue"
+}
index 1e4f022..cc71822 100644 (file)
@@ -10,6 +10,9 @@ Item {
         width: newsRect.width; height: window.height
         model: currentSource.listModels[componentDepth-1]
         delegate: NewsDelegate { }
+        highlight: Rectangle { color: "steelblue" }
     }
     ScrollBar { scrollArea: list; height: list.height; width: 8; anchors.right: list.right }
+
+    Component.onCompleted: list.currentIndex = -1
 }
diff --git a/qml/QuickNewsReader/content/view/NewsComments.qml b/qml/QuickNewsReader/content/view/NewsComments.qml
new file mode 100644 (file)
index 0000000..3da859c
--- /dev/null
@@ -0,0 +1,61 @@
+import QtQuick 1.0
+import QtWebKit 1.0
+
+Item {
+    id: newsCommentsRect
+    width: window.width; height: window.height
+
+    function getNewsModelItem(prop)
+    {
+        if( typeof currentSource.listModels[componentDepth-1] != "undefined" )
+            if( typeof currentSource.listModels[componentDepth-1][prop] != "undefined" )
+                return currentSource.listModels[componentDepth-1][prop]
+
+        return ""
+    }
+
+    property string commentURL: getNewsModelItem('commentURL')
+    property string title: getNewsModelItem('title')
+    property string image: getNewsModelItem('image')
+
+    VisualItemModel {
+        id: itemModel
+
+        Column {
+            id: column
+            x: 0; y: 0
+            width: newsCommentsRect.width
+//            height: newsCommentsRect.height
+
+            Row {
+                id: titleRow
+                spacing: 5
+
+                Image {
+                    id: detailImage
+                    source: image
+                }
+
+                Text {
+                    anchors.verticalCenter: detailImage.verticalCenter
+                    text: title; width: column.width - detailImage.width - 10; wrapMode: Text.WordWrap
+                    font { bold: true; family: "Helvetica"; pointSize: 16 }
+                }
+            }
+
+            WebView {
+                id: newsCommentsWebView
+                width: newsCommentsRect.width
+                url: commentURL
+                preferredWidth: window.width
+//                preferredHeight: parent.height - titleRow.height
+            }
+        }
+    }
+    ListView {
+        id: itemListView
+        anchors.fill: newsCommentsRect
+        model: itemModel
+    }
+    ScrollBar { scrollArea: itemListView; height: itemListView.height; width: 8; anchors.right: itemListView.right }
+}
index 82cfd63..d51f3ee 100644 (file)
@@ -67,6 +67,7 @@ Item {
             }
 
             Text {
+                anchors.verticalCenter: detailImage.verticalCenter
                 text: description; width: column.width - detailImage.width - 10; wrapMode: Text.WordWrap
                 font.family: "Helvetica"
             }
@@ -76,7 +77,7 @@ Item {
     MouseArea {
         anchors.fill: delegate
 
-        onDoubleClicked: {
+        onClicked: {
             if (typeof detailedContent != "undefined") {
                 var currentSourceDepth = delegate.ListView.view.model.sourceDepth
 
index 22f60a1..da55341 100644 (file)
@@ -7,47 +7,73 @@ Item {
 
     function getNewsModelItem(prop)
     {
-        var newsIndex = listSourceModel[window.currentSourceIndex].currentPath[componentDepth-1]
-
         if( typeof currentSource.listModels[componentDepth-1] != "undefined" )
-            if( typeof currentSource.listModels[componentDepth-1].get(newsIndex) != "undefined" )
-                if( typeof currentSource.listModels[componentDepth-1].get(newsIndex)[prop] != "undefined" )
-                    return currentSource.listModels[componentDepth-1].get(newsIndex)[prop]
+            if( typeof currentSource.listModels[componentDepth-1][prop] != "undefined" )
+                return currentSource.listModels[componentDepth-1][prop]
 
         return ""
     }
 
-    property string urllink: getNewsModelItem('link')
-    property string htmlcontent: getNewsModelItem('detailedContent')
+    property string urllink: getNewsModelItem('urllink')
+    property string htmlcontent: getNewsModelItem('htmlcontent')
     property string title: getNewsModelItem('title')
     property string image: getNewsModelItem('image')
 
     VisualItemModel {
         id: itemModel
 
-        Column {
-            id: column
-            x: 10; y: 10
-            width: newsDetailRect.width - 20
+        Item {
+            x: 0; y: 0
+            width: newsDetailRect.width
+            height: column.height
+
+            Column {
+                id: column
+                x: 0; y: 0
+                width: parent.width - 20
+
+                Row {
+                    spacing: 5
 
-            Row {
-                spacing: 5
+                    Image {
+                        id: detailImage
+                        source: image
+                    }
 
-                Image {
-                    id: detailImage
-                    source: image
+                    Text {
+                        anchors.verticalCenter: detailImage.verticalCenter
+                        text: title; width: column.width - detailImage.width - 10; wrapMode: Text.WordWrap
+                        font { bold: true; family: "Helvetica"; pointSize: 16 }
+                    }
                 }
 
                 Text {
-                    text: title; width: column.width - detailImage.width - 10; wrapMode: Text.WordWrap
-                    font { bold: true; family: "Helvetica"; pointSize: 16 }
+                    id: detailText
+                    text: htmlcontent; width: newsDetailRect.width - 20; wrapMode: Text.WordWrap
+                    font.family: "Helvetica"
                 }
             }
 
-            Text {
-                id: detailText
-                text: htmlcontent; width: newsDetailRect.width - 20; wrapMode: Text.WordWrap
-                font.family: "Helvetica"
+            MouseArea {
+                anchors.fill: column
+
+                onClicked: {
+                    var currentSourceDepth = currentSource.listModels[componentDepth-1].sourceDepth
+
+                    // here we remove everything in viewsModel after index "currentSourceDepth"
+                    while(window.windowViewsModel.count>currentSourceDepth+1)
+                        window.windowViewsModel.remove(window.windowViewsModel.count-1)
+
+                    var path = listSourceModel[window.currentSourceIndex].currentPath
+                    path[currentSourceDepth] = index
+                    listSourceModel[window.currentSourceIndex].currentPath = path
+
+                    window.windowViewsModel.append({ component: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewComponent,
+                                                     componentId: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewId,
+                                                     componentDepth: currentSourceDepth+1 })
+
+                    window.windowViewsList.currentIndex = currentSourceDepth+1;
+                }
             }
         }
     }
@@ -57,14 +83,4 @@ Item {
         model: itemModel
     }
     ScrollBar { scrollArea: itemListView; height: itemListView.height; width: 8; anchors.right: itemListView.right }
-/*
-    WebView {
-        id: newsDetailWebView
-        anchors.fill: parent
-        html: htmlcontent
-        url: urllink
-        preferredWidth: window.width
-        preferredHeight: window.height
-    }
-*/
 }
diff --git a/qml/QuickNewsReader/content/view/SourceConfigDialog.qml b/qml/QuickNewsReader/content/view/SourceConfigDialog.qml
new file mode 100644 (file)
index 0000000..72e53b3
--- /dev/null
@@ -0,0 +1,98 @@
+import QtQuick 1.0
+import "../modelitf"
+
+Rectangle {
+    id: configDialog
+    anchors.fill: parent
+    color: "#00000000"
+    visible: false
+    state: "hidden"
+
+    property string configViewComponent
+
+    //property SourceModel model;
+    //property SourceConfigComponentView viewComponent;
+
+    states: [
+        State {
+            name: "showSourceConfig"
+
+            // In this state, we bring the configuration UI of the source
+            PropertyChanges { target: configDialog; color: "#d0000000" }
+            PropertyChanges { target: sourceConfigLoader; opacity: 1 }
+            PropertyChanges { target: sourceConfigLoader; source: configViewComponent }
+
+            AnchorChanges { target: quitApplyConfigButton; anchors.left: undefined; anchors.right: configDialog.right }
+            AnchorChanges { target: quitCancelConfigButton; anchors.right: undefined; anchors.left: configDialog.left }
+        }
+    ]
+
+    transitions: [
+        Transition {
+            from: "hidden"
+            to: "showSourceConfig"
+
+            SequentialAnimation {
+                // Show the dialog
+                PropertyAction { target: configDialog; property: "visible"; value: true }
+                // Bring the UI elements
+                ParallelAnimation {
+                    AnchorAnimation { duration: 500 }
+                    ColorAnimation { duration: 400 }
+                }
+            }
+        },
+        Transition {
+            from: "showSourceConfig"
+            to: "hidden"
+
+            SequentialAnimation {
+                // Move out the UI elements
+                ParallelAnimation {
+                    AnchorAnimation { duration: 500 }
+                    ColorAnimation { duration: 400 }
+                }
+                // Hide the dialog
+                PropertyAction { target: configDialog; property: "visible"; value: false }
+            }
+        }
+    ]
+
+    Loader {
+        id: sourceConfigLoader
+        opacity: 0
+        anchors.top: parent.top
+        anchors.left: parent.left
+        anchors.right: parent.right
+        anchors.bottom: quitApplyConfigButton.top
+
+        Behavior on opacity {
+            NumberAnimation { duration: 1000; easing.type: Easing.InOutQuad }
+        }
+    }
+
+    FancyButton {
+        id: quitApplyConfigButton
+        icon: "../images/apply.png"
+        anchors.bottom: parent.bottom
+        anchors.left: parent.right
+
+        onClicked: {
+            // Store the configuration of this source, and disappear
+            configDialog.state = "hidden"
+        }
+    }
+
+    FancyButton {
+        id: quitCancelConfigButton
+        icon: "../images/cancel.png"
+        anchors.bottom: parent.bottom
+        anchors.right: parent.left
+
+        onClicked: {
+            // Store the configuration of this source, and disappear
+            configDialog.state = "hidden"
+        }
+    }
+
+}
index e3e72e0..8824282 100644 (file)
@@ -73,6 +73,15 @@ Item {
 
     MouseArea {
         anchors.fill: delegate
+
+        onPressAndHold: {
+            // show the configuration for this journal
+            if( listSourceModel[index].hasSettings )
+            {
+                window.showConfigDialog(listSourceModel[index].settingsComponent)
+            }
+        }
+
         onClicked: {
             if( delegate.ListView.view.currentIndex !== index )
             {
@@ -86,7 +95,7 @@ Item {
                                                  componentId: listSourceModel[index].listViews[0].viewId,
                                                  componentDepth: 1 })
 
-                listSourceModel[window.currentSourceIndex].currentPath = [index]
+                listSourceModel[index].currentPath = [index]
 
                 window.currentSourceIndex = index
 
index 2a72511..0c41d95 100644 (file)
@@ -41,6 +41,18 @@ Rectangle {
         }
     }
 
+    function showConfigDialog(settingsComponent) {
+        configDialog.configViewComponent = settingsComponent
+        configDialog.state = "showSourceConfig"
+    }
+
+    SourceConfigDialog {
+        id:configDialog
+    }
+
+/*
+*/
+
     Component {
         id: quitButtonDelegate
         Item {
index 7d98fd8..dda4359 100644 (file)
@@ -1,5 +1,5 @@
 Source: quicknewsreader
-Section: web
+Section: user/network
 Version: 0.2-0
 Priority: optional
 Maintainer: Christophe CHAPUIS <chris@unknown>