diff options
Diffstat (limited to 'demos')
167 files changed, 6140 insertions, 7 deletions
diff --git a/demos/browser/browsermainwindow.cpp b/demos/browser/browsermainwindow.cpp index ed36c25..8c2ed89 100644 --- a/demos/browser/browsermainwindow.cpp +++ b/demos/browser/browsermainwindow.cpp @@ -433,10 +433,8 @@ void BrowserMainWindow::setupMenu() QMenu *toolsMenu = menuBar()->addMenu(tr("&Tools")); toolsMenu->addAction(tr("Web &Search"), this, SLOT(slotWebSearch()), QKeySequence(tr("Ctrl+K", "Web Search"))); -#ifndef Q_CC_MINGW a = toolsMenu->addAction(tr("Enable Web &Inspector"), this, SLOT(slotToggleInspector(bool))); a->setCheckable(true); -#endif QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt())); diff --git a/demos/declarative/calculator/CalcButton.qml b/demos/declarative/calculator/CalcButton.qml new file mode 100644 index 0000000..6210e46 --- /dev/null +++ b/demos/declarative/calculator/CalcButton.qml @@ -0,0 +1,41 @@ +import Qt 4.6 + +Rectangle { + property alias operation: label.text + property bool toggable: false + property bool toggled: false + signal clicked + + id: button; width: 50; height: 30 + border.color: palette.mid; radius: 6 + gradient: Gradient { + GradientStop { id: gradientStop1; position: 0.0; color: Qt.lighter(palette.button) } + GradientStop { id: gradientStop2; position: 1.0; color: palette.button } + } + + Text { id: label; anchors.centerIn: parent; color: palette.buttonText } + + MouseArea { + id: clickRegion + anchors.fill: parent + onClicked: { + doOp(operation); + button.clicked(); + if (!button.toggable) return; + button.toggled ? button.toggled = false : button.toggled = true + } + } + + states: [ + State { + name: "Pressed"; when: clickRegion.pressed == true + PropertyChanges { target: gradientStop1; color: palette.dark } + PropertyChanges { target: gradientStop2; color: palette.button } + }, + State { + name: "Toggled"; when: button.toggled == true + PropertyChanges { target: gradientStop1; color: palette.dark } + PropertyChanges { target: gradientStop2; color: palette.button } + } + ] +} diff --git a/demos/declarative/calculator/calculator.js b/demos/declarative/calculator/calculator.js new file mode 100644 index 0000000..cd6490a --- /dev/null +++ b/demos/declarative/calculator/calculator.js @@ -0,0 +1,87 @@ + +var curVal = 0; +var memory = 0; +var lastOp = ""; +var timer = 0; + +function disabled(op) { + if (op == "." && curNum.text.toString().search(/\./) != -1) { + return true; + } else if (op == "Sqrt" && curNum.text.toString().search(/-/) != -1) { + return true; + } else { + return false; + } +} + +function doOp(op) { + if (disabled(op)) { + return; + } + + if (op.toString().length==1 && ((op >= "0" && op <= "9") || op==".") ) { + if (curNum.text.toString().length >= 14) + return; // No arbitrary length numbers + if (lastOp.toString().length == 1 && ((lastOp >= "0" && lastOp <= "9") || lastOp==".") ) { + curNum.text = curNum.text + op.toString(); + } else { + curNum.text = op; + } + lastOp = op; + return; + } + lastOp = op; + + // Pending operations + if (currentOperation.text == "+") { + curNum.text = Number(curNum.text.valueOf()) + Number(curVal.valueOf()); + } else if (currentOperation.text == "-") { + curNum.text = Number(curVal) - Number(curNum.text.valueOf()); + } else if (currentOperation.text == "x") { + curNum.text = Number(curVal) * Number(curNum.text.valueOf()); + } else if (currentOperation.text == "/") { + curNum.text = Number(Number(curVal) / Number(curNum.text.valueOf())).toString(); + } else if (currentOperation.text == "=") { + } + + if (op == "+" || op == "-" || op == "x" || op == "/") { + currentOperation.text = op; + curVal = curNum.text.valueOf(); + return; + } + curVal = 0; + currentOperation.text = ""; + + // Immediate operations + if (op == "1/x") { // reciprocal + curNum.text = (1 / curNum.text.valueOf()).toString(); + } else if (op == "^2") { // squared + curNum.text = (curNum.text.valueOf() * curNum.text.valueOf()).toString(); + } else if (op == "Abs") { + curNum.text = (Math.abs(curNum.text.valueOf())).toString(); + } else if (op == "Int") { + curNum.text = (Math.floor(curNum.text.valueOf())).toString(); + } else if (op == "+/-") { // plus/minus + curNum.text = (curNum.text.valueOf() * -1).toString(); + } else if (op == "Sqrt") { // square root + curNum.text = (Math.sqrt(curNum.text.valueOf())).toString(); + } else if (op == "MC") { // memory clear + memory = 0; + } else if (op == "M+") { // memory increment + memory += curNum.text.valueOf(); + } else if (op == "MR") { // memory recall + curNum.text = memory.toString(); + } else if (op == "MS") { // memory set + memory = curNum.text.valueOf(); + } else if (op == "Bksp") { + curNum.text = curNum.text.toString().slice(0, -1); + } else if (op == "C") { + curNum.text = "0"; + } else if (op == "AC") { + curVal = 0; + memory = 0; + lastOp = ""; + curNum.text ="0"; + } +} + diff --git a/demos/declarative/calculator/calculator.qml b/demos/declarative/calculator/calculator.qml new file mode 100644 index 0000000..66705e2 --- /dev/null +++ b/demos/declarative/calculator/calculator.qml @@ -0,0 +1,124 @@ +import Qt 4.6 + +Rectangle { + width: 320; height: 270; color: palette.window + + SystemPalette { id: palette } + Script { source: "calculator.js" } + + Column { + x: 2; spacing: 10; + + Rectangle { + id: container + width: 316; height: 50 + border.color: palette.dark; color: palette.base + + Text { + id: curNum + font.bold: true; font.pointSize: 16 + color: palette.text + anchors.right: container.right + anchors.rightMargin: 5 + anchors.verticalCenter: container.verticalCenter + } + + Text { + id: currentOperation + color: palette.text + font.bold: true; font.pointSize: 16 + anchors.left: container.left + anchors.leftMargin: 5 + anchors.verticalCenter: container.verticalCenter + } + } + + Item { + width: 320; height: 30 + + CalcButton { + id: advancedCheckBox + x: 55; width: 206 + operation: "Advanced Mode" + toggable: true + } + } + + Item { + width: 320; height: 160 + + Item { + id: basicButtons + x: 55; width: 160; height: 160 + + CalcButton { operation: "Bksp"; id: bksp; width: 67; opacity: 0 } + CalcButton { operation: "C"; id: c; width: 76 } + CalcButton { operation: "AC"; id: ac; x: 78; width: 76 } + + Grid { + id: numKeypad; y: 32; spacing: 2; columns: 3 + + CalcButton { operation: "7" } + CalcButton { operation: "8" } + CalcButton { operation: "9" } + CalcButton { operation: "4" } + CalcButton { operation: "5" } + CalcButton { operation: "6" } + CalcButton { operation: "1" } + CalcButton { operation: "2" } + CalcButton { operation: "3" } + } + + Row { + y: 128; spacing: 2 + + CalcButton { operation: "0"; width: 50 } + CalcButton { operation: "."; x: 77; width: 50 } + CalcButton { operation: "="; id: equals; x: 77; width: 102 } + } + + Column { + id: simpleOperations + x: 156; y: 0; spacing: 2 + + CalcButton { operation: "x" } + CalcButton { operation: "/" } + CalcButton { operation: "-" } + CalcButton { operation: "+" } + } + } + + Grid { + id: advancedButtons + x: 350; spacing: 2; columns: 2; opacity: 0 + + CalcButton { operation: "Abs" } + CalcButton { operation: "Int" } + CalcButton { operation: "MC" } + CalcButton { operation: "Sqrt" } + CalcButton { operation: "MR" } + CalcButton { operation: "^2" } + CalcButton { operation: "MS" } + CalcButton { operation: "1/x" } + CalcButton { operation: "M+" } + CalcButton { operation: "+/-" } + } + } + } + + states: State { + name: "Advanced"; when: advancedCheckBox.toggled == true + PropertyChanges { target: basicButtons; x: 0 } + PropertyChanges { target: simpleOperations; y: 32 } + PropertyChanges { target: bksp; opacity: 1 } + PropertyChanges { target: c; x: 69; width: 67 } + PropertyChanges { target: ac; x: 138; width: 67 } + PropertyChanges { target: equals; width: 50 } + PropertyChanges { target: advancedButtons; x: 210; opacity: 1 } + } + + transitions: Transition { + NumberAnimation { properties: "x,y,width"; easing.type: "OutBounce"; duration: 500 } + NumberAnimation { properties: "opacity"; easing.type: "InOutQuad"; duration: 500 } + } +} diff --git a/demos/declarative/declarative.pro b/demos/declarative/declarative.pro new file mode 100644 index 0000000..4d169e3 --- /dev/null +++ b/demos/declarative/declarative.pro @@ -0,0 +1,17 @@ +TEMPLATE = subdirs + +# These demos contain C++ and need to be compiled +SUBDIRS = \ + minehunt + +# These examples contain no C++ and can simply be copied +sources.files = \ + calculator \ + flickr \ + samegame \ + snake \ + twitter \ + webbrowser +sources.path = $$[QT_INSTALL_DEMOS]/declarative +INSTALLS += sources + diff --git a/demos/declarative/flickr/common/ImageDetails.qml b/demos/declarative/flickr/common/ImageDetails.qml new file mode 100644 index 0000000..862eeb1 --- /dev/null +++ b/demos/declarative/flickr/common/ImageDetails.qml @@ -0,0 +1,160 @@ +import Qt 4.6 +import org.webkit 1.0 + +Flipable { + id: container + + property var frontContainer: containerFront + property string photoTitle: "" + property string photoDescription: "" + property string photoTags: "" + property int photoWidth + property int photoHeight + property string photoType + property string photoAuthor + property string photoDate + property string photoUrl + property int rating: 2 + property var prevScale: 1.0 + + signal closed + + transform: Rotation { + id: detailsRotation + origin.y: container.height / 2; + origin.x: container.width / 2; + axis.y: 1; axis.z: 0 + } + + front: Item { + id: containerFront; anchors.fill: container + + Rectangle { + anchors.fill: parent + color: "black"; opacity: 0.4 + border.color: "white"; border.width: 2 + } + + MediaButton { + id: backButton; x: 630; y: 370; text: "Back" + onClicked: { container.closed() } + } + + MediaButton { + id: moreButton; x: 530; y: 370; text: "View..." + onClicked: { container.state='Back' } + } + + Text { id: titleText; style: Text.Raised; styleColor: "black"; color: "white"; elide: Text.ElideRight + x: 220; y: 30; width: parent.width - 240; text: container.photoTitle; font.pointSize: 22 } + + LikeOMeter { x: 40; y: 250; rating: container.rating } + + Flickable { id: flickable; x: 220; width: 480; height: 210; y: 130; clip: true + contentWidth: 480; contentHeight: descriptionText.height + + WebView { id: descriptionText; width: parent.width + html: "<style TYPE=\"text/css\">body {color: white;} a:link {color: cyan; text-decoration: underline; }</style>" + container.photoDescription } + } + + Text { id: size; color: "white"; width: 300; x: 40; y: 300 + text: "<b>Size:</b> " + container.photoWidth + 'x' + container.photoHeight } + Text { id: type; color: "white"; width: 300; x: 40; anchors.top: size.bottom + text: "<b>Type:</b> " + container.photoType } + + Text { id: author; color: "white"; width: 300; x: 220; y: 80 + text: "<b>Author:</b> " + container.photoAuthor } + Text { id: date; color: "white"; width: 300; x: 220; anchors.top: author.bottom + text: "<b>Published:</b> " + container.photoDate } + Text { id: tagsLabel; color: "white"; x: 220; anchors.top: date.bottom; + text: container.photoTags == "" ? "" : "<b>Tags:</b> " } + Text { id: tags; color: "white"; width: parent.width-x-20; + anchors.left: tagsLabel.right; anchors.top: date.bottom; + elide: Text.ElideRight; text: container.photoTags } + + ScrollBar { id: scrollBar; x: 720; y: flickable.y; width: 7; height: flickable.height; opacity: 0; + flickableArea: flickable; clip: true } + } + + back: Item { + anchors.fill: container + + Rectangle { anchors.fill: parent; color: "black"; opacity: 0.4; border.color: "white"; border.width: 2 } + + Progress { anchors.centerIn: parent; width: 200; height: 18; progress: bigImage.progress; visible: bigImage.status!=1 } + Flickable { + id: flick; width: container.width - 10; height: container.height - 10 + x: 5; y: 5; clip: true; + contentWidth: imageContainer.width; contentHeight: imageContainer.height + + Item { + id: imageContainer + width: Math.max(bigImage.width * bigImage.scale, flick.width); + height: Math.max(bigImage.height * bigImage.scale, flick.height); + + Image { + id: bigImage; source: container.photoUrl; scale: slider.value + anchors.centerIn: parent; + smooth: !flick.moving + onStatusChanged : { + // Default scale shows the entire image. + if (status == 1 && width != 0) { + slider.minimum = Math.min(flick.width / width, flick.height / height); + prevScale = Math.min(slider.minimum, 1); + slider.value = prevScale; + } + } + } + } + } + + MediaButton { + id: backButton2; x: 630; y: 370; text: "Back"; onClicked: { container.state = '' } + } + Text { + text: "Image Unavailable" + visible: bigImage.status == 'Error' + anchors.centerIn: parent; color: "white"; font.bold: true + } + + Slider { + id: slider; x: 25; y: 374; visible: { bigImage.status == 1 && maximum > minimum } + onValueChanged: { + if (bigImage.width * value > flick.width) { + var xoff = (flick.width/2 + flick.contentX) * value / prevScale; + flick.contentX = xoff - flick.width/2; + } + if (bigImage.height * value > flick.height) { + var yoff = (flick.height/2 + flick.contentY) * value / prevScale; + flick.contentY = yoff - flick.height/2; + } + prevScale = value; + } + } + } + + states: [ + State { + name: "Back" + PropertyChanges { target: detailsRotation; angle: 180 } + } + ] + + transitions: [ + Transition { + SequentialAnimation { + PropertyAction { + target: bigImage + property: "smooth" + value: false + } + NumberAnimation { easing.type: "InOutQuad"; properties: "angle"; duration: 500 } + PropertyAction { + target: bigImage + property: "smooth" + value: !flick.moving + } + } + } + ] +} diff --git a/demos/declarative/flickr/common/LikeOMeter.qml b/demos/declarative/flickr/common/LikeOMeter.qml new file mode 100644 index 0000000..5ee048b --- /dev/null +++ b/demos/declarative/flickr/common/LikeOMeter.qml @@ -0,0 +1,35 @@ +import Qt 4.6 + +Item { + id: container + + property int rating: 2 + + Row { + Star { + rating: 0 + onClicked: { container.rating = rating } + on: container.rating >= 0 + } + Star { + rating: 1 + onClicked: { container.rating = rating } + on: container.rating >= 1 + } + Star { + rating: 2 + onClicked: { container.rating = rating } + on: container.rating >= 2 + } + Star { + rating: 3 + onClicked: { container.rating = rating } + on: container.rating >= 3 + } + Star { + rating: 4 + onClicked: { container.rating = rating } + on: container.rating >= 4 + } + } +} diff --git a/demos/declarative/flickr/common/Loading.qml b/demos/declarative/flickr/common/Loading.qml new file mode 100644 index 0000000..938a080 --- /dev/null +++ b/demos/declarative/flickr/common/Loading.qml @@ -0,0 +1,8 @@ +import Qt 4.6 + +Image { + id: loading; source: "pics/loading.png"; transformOrigin: "Center" + NumberAnimation on rotation { + from: 0; to: 360; running: loading.visible == true; repeat: true; duration: 900 + } +} diff --git a/demos/declarative/flickr/common/MediaButton.qml b/demos/declarative/flickr/common/MediaButton.qml new file mode 100644 index 0000000..86ac948 --- /dev/null +++ b/demos/declarative/flickr/common/MediaButton.qml @@ -0,0 +1,41 @@ +import Qt 4.6 + +Item { + id: container + + signal clicked + + property string text + + Image { + id: buttonImage + source: "pics/button.png" + } + Image { + id: pressed + source: "pics/button-pressed.png" + opacity: 0 + } + MouseArea { + id: mouseRegion + anchors.fill: buttonImage + onClicked: { container.clicked(); } + } + Text { + font.bold: true + color: "white" + anchors.centerIn: buttonImage + text: container.text + } + width: buttonImage.width + states: [ + State { + name: "Pressed" + when: mouseRegion.pressed == true + PropertyChanges { + target: pressed + opacity: 1 + } + } + ] +} diff --git a/demos/declarative/flickr/common/MediaLineEdit.qml b/demos/declarative/flickr/common/MediaLineEdit.qml new file mode 100644 index 0000000..9559f6a --- /dev/null +++ b/demos/declarative/flickr/common/MediaLineEdit.qml @@ -0,0 +1,104 @@ +import Qt 4.6 + +Item { + id: container + + property string label + property string text + + width: Math.max(94,labeltext.width + editor.width + 20) + height: buttonImage.height + + states: [ + State { + name: "Edit" + PropertyChanges { + target: labeltext + text: container.label + ": " + } + PropertyChanges { + target: labeltext + x: 10 + } + PropertyChanges { + target: editor + cursorVisible: true + width: 100 + } + PropertyChanges { + target: container + focus: true + } + StateChangeScript { + script:editor.selectAll() + } + }, + State { + // When returning to default state, typed text is propagated + StateChangeScript { + script: container.text = editor.text + } + } + ] + transitions: [ + Transition { + NumberAnimation { properties: "x,width"; duration: 500; easing.type: "InOutQuad" } + } + ] + + + BorderImage { + id: buttonImage + source: "pics/button.sci" + anchors.left: container.left + anchors.right: container.right + } + + BorderImage { + id: pressed + source: "pics/button-pressed.sci" + opacity: 0 + anchors.left: container.left + anchors.right: container.right + } + + MouseArea { + id: mouseRegion + anchors.fill: buttonImage + onClicked: { container.state = container.state=="Edit" ? "" : "Edit" } + states: [ + State { + when: mouseRegion.pressed == true + PropertyChanges { + target: pressed + opacity: 1 + } + } + ] + } + + Text { + id: labeltext + font.bold: true + color: "white" + anchors.verticalCenter: container.verticalCenter + x: (container.width - width)/2 + text: container.label + "..." + } + + TextInput { + id: editor + font.bold: true + color: "white" + selectionColor: "green" + width: 0 + clip: true + anchors.left: labeltext.right + anchors.verticalCenter: container.verticalCenter + } + Keys.forwardTo: [(returnKey), (editor)] + Item { + id: returnKey + Keys.onReturnPressed: "container.state = ''" + } +} diff --git a/demos/declarative/flickr/common/Progress.qml b/demos/declarative/flickr/common/Progress.qml new file mode 100644 index 0000000..fd9be10 --- /dev/null +++ b/demos/declarative/flickr/common/Progress.qml @@ -0,0 +1,32 @@ +import Qt 4.6 + +Item { + property var progress: 0 + + Rectangle { + anchors.fill: parent; smooth: true + border.color: "white"; border.width: 0; radius: height/2 - 2 + gradient: Gradient { + GradientStop { position: 0; color: "#66343434" } + GradientStop { position: 1.0; color: "#66000000" } + } + } + + Rectangle { + y: 2; height: parent.height-4; + x: 2; width: Math.max(parent.width * progress - 4, 0); + opacity: width < 1 ? 0 : 1; smooth: true + gradient: Gradient { + GradientStop { position: 0; color: "lightsteelblue" } + GradientStop { position: 1.0; color: "steelblue" } + } + radius: height/2 - 2 + } + + Text { + text: Math.round(progress * 100) + "%" + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: "white"; font.bold: true + } +} diff --git a/demos/declarative/flickr/common/RssModel.qml b/demos/declarative/flickr/common/RssModel.qml new file mode 100644 index 0000000..ed9fd5c --- /dev/null +++ b/demos/declarative/flickr/common/RssModel.qml @@ -0,0 +1,20 @@ +import Qt 4.6 + +XmlListModel { + property string tags : "" + + source: "http://api.flickr.com/services/feeds/photos_public.gne?"+(tags ? "tags="+tags+"&" : "")+"format=rss2" + query: "/rss/channel/item" + namespaceDeclarations: "declare namespace media=\"http://search.yahoo.com/mrss/\";" + + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "imagePath"; query: "media:thumbnail/@url/string()" } + XmlRole { name: "url"; query: "media:content/@url/string()" } + XmlRole { name: "description"; query: "description/string()" } + XmlRole { name: "tags"; query: "media:category/string()" } + XmlRole { name: "photoWidth"; query: "media:content/@width/string()" } + XmlRole { name: "photoHeight"; query: "media:content/@height/string()" } + XmlRole { name: "photoType"; query: "media:content/@type/string()" } + XmlRole { name: "photoAuthor"; query: "author/string()" } + XmlRole { name: "photoDate"; query: "pubDate/string()" } +} diff --git a/demos/declarative/flickr/common/ScrollBar.qml b/demos/declarative/flickr/common/ScrollBar.qml new file mode 100644 index 0000000..feebcb0 --- /dev/null +++ b/demos/declarative/flickr/common/ScrollBar.qml @@ -0,0 +1,40 @@ +import Qt 4.6 + +Item { + id: container + + property var flickableArea + + Rectangle { + radius: 5 + color: "black" + opacity: 0.3 + border.color: "white" + border.width: 2 + x: 0 + y: flickableArea.visibleArea.yPosition * container.height + width: parent.width + height: flickableArea.visibleArea.heightRatio * container.height + } + states: [ + State { + name: "show" + when: flickableArea.moving + PropertyChanges { + target: container + opacity: 1 + } + } + ] + transitions: [ + Transition { + from: "*" + to: "*" + NumberAnimation { + target: container + properties: "opacity" + duration: 400 + } + } + ] +} diff --git a/demos/declarative/flickr/common/Slider.qml b/demos/declarative/flickr/common/Slider.qml new file mode 100644 index 0000000..05a87e7 --- /dev/null +++ b/demos/declarative/flickr/common/Slider.qml @@ -0,0 +1,36 @@ +import Qt 4.6 + +Item { + id: slider; width: 400; height: 16 + + // value is read/write. + property real value + onValueChanged: { handle.x = 2 + (value - minimum) * slider.xMax / (maximum - minimum); } + property real maximum: 1 + property real minimum: 1 + property int xMax: slider.width - handle.width - 4 + + Rectangle { + anchors.fill: parent + border.color: "white"; border.width: 0; radius: 8 + gradient: Gradient { + GradientStop { position: 0.0; color: "#66343434" } + GradientStop { position: 1.0; color: "#66000000" } + } + } + + Rectangle { + id: handle; smooth: true + x: slider.width / 2 - handle.width / 2; y: 2; width: 30; height: slider.height-4; radius: 6 + gradient: Gradient { + GradientStop { position: 0.0; color: "lightgray" } + GradientStop { position: 1.0; color: "gray" } + } + + MouseArea { + anchors.fill: parent; drag.target: parent + drag.axis: "XAxis"; drag.minimumX: 2; drag.maximumX: slider.xMax+2 + onPositionChanged: { value = (maximum - minimum) * (handle.x-2) / slider.xMax + minimum; } + } + } +} diff --git a/demos/declarative/flickr/common/Star.qml b/demos/declarative/flickr/common/Star.qml new file mode 100644 index 0000000..748a5ec --- /dev/null +++ b/demos/declarative/flickr/common/Star.qml @@ -0,0 +1,45 @@ +import Qt 4.6 + +Item { + id: container + width: 24 + height: 24 + + property int rating + property bool on + signal clicked + + Image { + id: starImage + source: "pics/ghns_star.png" + x: 6 + y: 7 + opacity: 0.4 + scale: 0.5 + } + MouseArea { + anchors.fill: container + onClicked: { container.clicked() } + } + states: [ + State { + name: "on" + when: container.on == true + PropertyChanges { + target: starImage + opacity: 1 + scale: 1 + x: 1 + y: 0 + } + } + ] + transitions: [ + Transition { + NumberAnimation { + properties: "opacity,scale,x,y" + easing.type: "OutBounce" + } + } + ] +} diff --git a/demos/declarative/flickr/common/pics/background.png b/demos/declarative/flickr/common/pics/background.png Binary files differnew file mode 100644 index 0000000..5b37072 --- /dev/null +++ b/demos/declarative/flickr/common/pics/background.png diff --git a/demos/declarative/flickr/common/pics/button-pressed.png b/demos/declarative/flickr/common/pics/button-pressed.png Binary files differnew file mode 100644 index 0000000..e434d32 --- /dev/null +++ b/demos/declarative/flickr/common/pics/button-pressed.png diff --git a/demos/declarative/flickr/common/pics/button-pressed.sci b/demos/declarative/flickr/common/pics/button-pressed.sci new file mode 100644 index 0000000..b8db272 --- /dev/null +++ b/demos/declarative/flickr/common/pics/button-pressed.sci @@ -0,0 +1,5 @@ +border.left: 8 +border.top: 4 +border.bottom: 4 +border.right: 8 +source: button.png diff --git a/demos/declarative/flickr/common/pics/button.png b/demos/declarative/flickr/common/pics/button.png Binary files differnew file mode 100644 index 0000000..56a63ce --- /dev/null +++ b/demos/declarative/flickr/common/pics/button.png diff --git a/demos/declarative/flickr/common/pics/button.sci b/demos/declarative/flickr/common/pics/button.sci new file mode 100644 index 0000000..b8db272 --- /dev/null +++ b/demos/declarative/flickr/common/pics/button.sci @@ -0,0 +1,5 @@ +border.left: 8 +border.top: 4 +border.bottom: 4 +border.right: 8 +source: button.png diff --git a/demos/declarative/flickr/common/pics/ghns_star.png b/demos/declarative/flickr/common/pics/ghns_star.png Binary files differnew file mode 100644 index 0000000..4ad43cc --- /dev/null +++ b/demos/declarative/flickr/common/pics/ghns_star.png diff --git a/demos/declarative/flickr/common/pics/loading.png b/demos/declarative/flickr/common/pics/loading.png Binary files differnew file mode 100644 index 0000000..47a1589 --- /dev/null +++ b/demos/declarative/flickr/common/pics/loading.png diff --git a/demos/declarative/flickr/common/pics/reflection.png b/demos/declarative/flickr/common/pics/reflection.png Binary files differnew file mode 100644 index 0000000..c143a48 --- /dev/null +++ b/demos/declarative/flickr/common/pics/reflection.png diff --git a/demos/declarative/flickr/common/pics/shadow-bottom.png b/demos/declarative/flickr/common/pics/shadow-bottom.png Binary files differnew file mode 100644 index 0000000..523f6e7 --- /dev/null +++ b/demos/declarative/flickr/common/pics/shadow-bottom.png diff --git a/demos/declarative/flickr/common/pics/shadow-corner.png b/demos/declarative/flickr/common/pics/shadow-corner.png Binary files differnew file mode 100644 index 0000000..ef8c856 --- /dev/null +++ b/demos/declarative/flickr/common/pics/shadow-corner.png diff --git a/demos/declarative/flickr/common/pics/shadow-right-screen.png b/demos/declarative/flickr/common/pics/shadow-right-screen.png Binary files differnew file mode 100644 index 0000000..9856c4f --- /dev/null +++ b/demos/declarative/flickr/common/pics/shadow-right-screen.png diff --git a/demos/declarative/flickr/common/pics/shadow-right.png b/demos/declarative/flickr/common/pics/shadow-right.png Binary files differnew file mode 100644 index 0000000..f534a35 --- /dev/null +++ b/demos/declarative/flickr/common/pics/shadow-right.png diff --git a/demos/declarative/flickr/common/qmldir b/demos/declarative/flickr/common/qmldir new file mode 100644 index 0000000..0c94f60 --- /dev/null +++ b/demos/declarative/flickr/common/qmldir @@ -0,0 +1,10 @@ +ImageDetails 0.0 ImageDetails.qml +LikeOMeter 0.0 LikeOMeter.qml +Loading 0.0 Loading.qml +MediaButton 0.0 MediaButton.qml +MediaLineEdit 0.0 MediaLineEdit.qml +Progress 0.0 Progress.qml +RssModel 0.0 RssModel.qml +ScrollBar 0.0 ScrollBar.qml +Slider 0.0 Slider.qml +Star 0.0 Star.qml diff --git a/demos/declarative/flickr/flickr-desktop.qml b/demos/declarative/flickr/flickr-desktop.qml new file mode 100644 index 0000000..99216cb --- /dev/null +++ b/demos/declarative/flickr/flickr-desktop.qml @@ -0,0 +1,194 @@ +import Qt 4.6 + +import "common" + +Item { + id: mainWindow; width: 800; height: 450 + + property bool showPathView : false + + resources: [ + Component { + id: photoDelegate + Item { + id: wrapper; width: 85; height: 85 + scale: wrapper.PathView.scale ? wrapper.PathView.scale : 1 + z: wrapper.PathView.z ? wrapper.PathView.z : 0 + + transform: Rotation { + id: itemRotation; origin.x: wrapper.width/2; origin.y: wrapper.height/2 + axis.y: 1; axis.z: 0 + angle: wrapper.PathView.angle ? wrapper.PathView.angle : 0 + } + + Connections { + target: imageDetails + onClosed: { + if (wrapper.state == 'Details') { + wrapper.state = ''; + imageDetails.photoUrl = ""; + } + } + } + + Script { + function photoClicked() { + imageDetails.photoTitle = title; + imageDetails.photoDescription = description; + imageDetails.photoTags = tags; + imageDetails.photoWidth = photoWidth; + imageDetails.photoHeight = photoHeight; + imageDetails.photoType = photoType; + imageDetails.photoAuthor = photoAuthor; + imageDetails.photoDate = photoDate; + imageDetails.photoUrl = url; + imageDetails.rating = 0; + wrapper.state = "Details"; + } + } + + Rectangle { + id: whiteRect; anchors.fill: parent; color: "white"; radius: 5 + + Loading { x: 26; y: 26; visible: thumb.status!=1 } + Image { id: thumb; source: imagePath; x: 5; y: 5 } + + Item { + id: shadows + Image { source: "common/pics/shadow-right.png"; x: whiteRect.width; height: whiteRect.height } + Image { source: "common/pics/shadow-bottom.png"; y: whiteRect.height; width: whiteRect.width } + Image { id: corner; source: "common/pics/shadow-corner.png"; x: whiteRect.width; y: whiteRect.height } + } + } + + MouseArea { anchors.fill: wrapper; onClicked: { photoClicked() } } + + states: [ + State { + name: "Details" + PropertyChanges { target: imageDetails; z: 2 } + ParentChange { target: wrapper; parent: imageDetails.frontContainer } + PropertyChanges { target: wrapper; x: 45; y: 35; scale: 1; z: 1000 } + PropertyChanges { target: itemRotation; angle: 0 } + PropertyChanges { target: shadows; opacity: 0 } + PropertyChanges { target: imageDetails; y: 20 } + PropertyChanges { target: photoGridView; y: -480 } + PropertyChanges { target: photoPathView; y: -480 } + PropertyChanges { target: viewModeButton; opacity: 0 } + PropertyChanges { target: tagsEdit; opacity: 0 } + PropertyChanges { target: fetchButton; opacity: 0 } + PropertyChanges { target: categoryText; y: "-50" } + } + ] + + transitions: [ + Transition { + from: "*"; to: "Details" + SequentialAnimation { + ParentAction { } + NumberAnimation { properties: "x,y,scale,opacity,angle"; duration: 500; easing.type: "InOutQuad" } + } + }, + Transition { + from: "Details"; to: "*" + SequentialAnimation { + ParentAction { } + NumberAnimation { properties: "x,y,scale,opacity,angle"; duration: 500; easing.type: "InOutQuad" } + PropertyAction { targets: wrapper; properties: "z" } + } + } + ] + + } + } + ] + + Item { + id: background + + anchors.fill: parent + + Image { source: "common/pics/background.png"; anchors.fill: parent } + RssModel { id: rssModel; tags : tagsEdit.text } + Loading { anchors.centerIn: parent; visible: rssModel.status == 2 } + + GridView { + id: photoGridView; model: rssModel; delegate: photoDelegate; cacheBuffer: 100 + cellWidth: 105; cellHeight: 105; x:32; y: 80; width: 800; height: 330; z: 1 + } + + PathView { + id: photoPathView; model: rssModel; delegate: photoDelegate + y: -380; width: 800; height: 330; pathItemCount: 10; z: 1 + path: Path { + startX: -50; startY: 40; + + PathAttribute { name: "scale"; value: 1 } + PathAttribute { name: "angle"; value: -45 } + + PathCubic { + x: 400; y: 220 + control1X: 140; control1Y: 40 + control2X: 210; control2Y: 220 + } + + PathAttribute { name: "scale"; value: 1.2 } + PathAttribute { name: "z"; value: 1 } + PathAttribute { name: "angle"; value: 0 } + + PathCubic { + x: 850; y: 40 + control2X: 660; control2Y: 40 + control1X: 590; control1Y: 220 + } + + PathAttribute { name: "scale"; value: 1 } + PathAttribute { name: "angle"; value: 45 } + } + + } + + ImageDetails { id: imageDetails; width: 750; x: 25; y: 500; height: 410 } + + MediaButton { + id: viewModeButton; x: 680; y: 410; text: "View Mode" + onClicked: { if (mainWindow.showPathView == true) mainWindow.showPathView = false; else mainWindow.showPathView = true } + } + + MediaButton { + id: fetchButton + text: "Update" + anchors.right: viewModeButton.left; anchors.rightMargin: 5 + anchors.top: viewModeButton.top + onClicked: { rssModel.reload(); } + } + + MediaLineEdit { + id: tagsEdit; + label: "Tags" + anchors.right: fetchButton.left; anchors.rightMargin: 5 + anchors.top: viewModeButton.top + } + + states: State { + name: "PathView" + when: mainWindow.showPathView == true + PropertyChanges { target: photoPathView; y: 80 } + PropertyChanges { target: photoGridView; y: -380 } + } + + transitions: [ + Transition { + from: "*"; to: "*" + NumberAnimation { properties: "y"; duration: 1000; easing.type: "OutBounce"; easing.amplitude: 0.5 } + } + ] + } + + Text { + id: categoryText; anchors.horizontalCenter: parent.horizontalCenter; y: 15; + text: "Flickr - " + + (rssModel.tags=="" ? "Uploads from everyone" : "Recent Uploads tagged " + rssModel.tags) + font.pointSize: 20; font.bold: true; color: "white"; style: Text.Raised; styleColor: "black" + } +} diff --git a/demos/declarative/flickr/flickr-mobile-90.qml b/demos/declarative/flickr/flickr-mobile-90.qml new file mode 100644 index 0000000..9fec242 --- /dev/null +++ b/demos/declarative/flickr/flickr-mobile-90.qml @@ -0,0 +1,11 @@ +import Qt 4.6 + +Item { + width: 480; height: 320 + + Loader { + y: 320; rotation: -90 + transformOrigin: Item.TopLeft + source: "flickr-mobile.qml" + } +} diff --git a/demos/declarative/flickr/flickr-mobile.qml b/demos/declarative/flickr/flickr-mobile.qml new file mode 100644 index 0000000..21e4c49 --- /dev/null +++ b/demos/declarative/flickr/flickr-mobile.qml @@ -0,0 +1,82 @@ +import Qt 4.6 +import "common" as Common +import "mobile" as Mobile + +Item { + id: screen; width: 320; height: 480 + property bool inListView : false + + Rectangle { + id: background + anchors.fill: parent; color: "#343434"; + + Image { source: "mobile/images/stripes.png"; fillMode: Image.Tile; anchors.fill: parent; opacity: 0.3 } + + Common.RssModel { id: rssModel } + Common.Loading { anchors.centerIn: parent; visible: rssModel.status == 2 } + + Item { + id: views + x: 2; width: parent.width - 4 + anchors.top: titleBar.bottom; anchors.bottom: toolBar.top + + Mobile.GridDelegate { id: gridDelegate } + GridView { + id: photoGridView; model: rssModel; delegate: gridDelegate; cacheBuffer: 100 + cellWidth: 79; cellHeight: 79; width: parent.width; height: parent.height - 1; z: 6 + } + + Mobile.ListDelegate { id: listDelegate } + ListView { + id: photoListView; model: rssModel; delegate: listDelegate; z: 6 + width: parent.width; height: parent.height; x: -(parent.width * 1.5); cacheBuffer: 100; + } + states: State { + name: "ListView"; when: screen.inListView == true + PropertyChanges { target: photoListView; x: 0 } + PropertyChanges { target: photoGridView; x: -(parent.width * 1.5) } + } + + transitions: Transition { + NumberAnimation { properties: "x"; duration: 500; easing.type: "InOutQuad" } + } + } + + Mobile.ImageDetails { id: imageDetails; width: parent.width; anchors.left: views.right; height: parent.height; z:1 } + Mobile.TitleBar { id: titleBar; z: 5; width: parent.width; height: 40; opacity: 0.9 } + + Mobile.ToolBar { + id: toolBar; z: 5 + height: 40; anchors.bottom: parent.bottom; width: parent.width; opacity: 0.9 + button1Label: "Update"; button2Label: "View mode" + onButton1Clicked: rssModel.reload() + onButton2Clicked: if (screen.inListView == true) screen.inListView = false; else screen.inListView = true + } + + Connections { + target: imageDetails + onClosed: { + if (background.state == "DetailedView") { + background.state = ''; + imageDetails.photoUrl = ""; + } + } + } + + states: State { + name: "DetailedView" + PropertyChanges { target: views; x: -parent.width } + PropertyChanges { target: toolBar; button1Label: "More..." } + PropertyChanges { + target: toolBar + onButton1Clicked: if (imageDetails.state=='') imageDetails.state='Back'; else imageDetails.state='' + } + PropertyChanges { target: toolBar; button2Label: "Back" } + PropertyChanges { target: toolBar; onButton2Clicked: imageDetails.closed() } + } + + transitions: Transition { + NumberAnimation { properties: "x"; duration: 500; easing.type: "InOutQuad" } + } + } +} diff --git a/demos/declarative/flickr/mobile/Button.qml b/demos/declarative/flickr/mobile/Button.qml new file mode 100644 index 0000000..4ba6b19 --- /dev/null +++ b/demos/declarative/flickr/mobile/Button.qml @@ -0,0 +1,38 @@ +import Qt 4.6 + +Item { + id: container + + signal clicked + + property string text + + BorderImage { + id: buttonImage + source: "images/toolbutton.sci" + width: container.width; height: container.height + } + BorderImage { + id: pressed + opacity: 0 + source: "images/toolbutton.sci" + width: container.width; height: container.height + } + MouseArea { + id: mouseRegion + anchors.fill: buttonImage + onClicked: { container.clicked(); } + } + Text { + color: "white" + anchors.centerIn: buttonImage; font.bold: true + text: container.text; style: Text.Raised; styleColor: "black" + } + states: [ + State { + name: "Pressed" + when: mouseRegion.pressed == true + PropertyChanges { target: pressed; opacity: 1 } + } + ] +} diff --git a/demos/declarative/flickr/mobile/GridDelegate.qml b/demos/declarative/flickr/mobile/GridDelegate.qml new file mode 100644 index 0000000..291d874 --- /dev/null +++ b/demos/declarative/flickr/mobile/GridDelegate.qml @@ -0,0 +1,72 @@ + import Qt 4.6 + + Component { + id: photoDelegate + Item { + id: wrapper; width: 79; height: 79 + + Script { + function photoClicked() { + imageDetails.photoTitle = title; + imageDetails.photoTags = tags; + imageDetails.photoWidth = photoWidth; + imageDetails.photoHeight = photoHeight; + imageDetails.photoType = photoType; + imageDetails.photoAuthor = photoAuthor; + imageDetails.photoDate = photoDate; + imageDetails.photoUrl = url; + imageDetails.rating = 0; + scaleMe.state = "Details"; + } + } + + Item { + anchors.centerIn: parent + scale: 0.0 + Behavior on scale { NumberAnimation { easing.type: "InOutQuad"} } + id: scaleMe + + Rectangle { height: 79; width: 79; id: blackRect; anchors.centerIn: parent; color: "black"; smooth: true } + Rectangle { + id: whiteRect; width: 77; height: 77; anchors.centerIn: parent; color: "#dddddd"; smooth: true + Image { id: thumb; source: imagePath; x: 1; y: 1; smooth: true} + Image { source: "images/gloss.png" } + } + + Connections { + target: toolBar + onButton2Clicked: if (scaleMe.state == 'Details' ) scaleMe.state = 'Show' + } + + states: [ + State { + name: "Show"; when: thumb.status == 1 + PropertyChanges { target: scaleMe; scale: 1 } + }, + State { + name: "Details" + PropertyChanges { target: scaleMe; scale: 1 } + ParentChange { target: wrapper; parent: imageDetails.frontContainer } + PropertyChanges { target: wrapper; x: 20; y: 60; z: 1000 } + PropertyChanges { target: background; state: "DetailedView" } + } + ] + transitions: [ + Transition { + from: "Show"; to: "Details" + ParentAction { } + NumberAnimation { properties: "x,y"; duration: 500; easing.type: "InOutQuad" } + }, + Transition { + from: "Details"; to: "Show" + SequentialAnimation { + ParentAction { } + NumberAnimation { properties: "x,y"; duration: 500; easing.type: "InOutQuad" } + PropertyAction { targets: wrapper; properties: "z" } + } + } + ] + } + MouseArea { anchors.fill: wrapper; onClicked: { photoClicked() } } + } + } diff --git a/demos/declarative/flickr/mobile/ImageDetails.qml b/demos/declarative/flickr/mobile/ImageDetails.qml new file mode 100644 index 0000000..2f4df8a --- /dev/null +++ b/demos/declarative/flickr/mobile/ImageDetails.qml @@ -0,0 +1,121 @@ +import Qt 4.6 +import "../common" as Common + +Flipable { + id: container + + property var frontContainer: containerFront + property string photoTitle: "" + property string photoTags: "" + property int photoWidth + property int photoHeight + property string photoType + property string photoAuthor + property string photoDate + property string photoUrl + property int rating: 2 + property var prevScale: 1.0 + + signal closed + + transform: Rotation { + id: itemRotation + origin.x: container.width / 2; + axis.y: 1; axis.z: 0 + } + + front: Item { + id: containerFront; anchors.fill: container + + Rectangle { + anchors.fill: parent + color: "black"; opacity: 0.4 + } + + Column { + spacing: 10 + anchors { + left: parent.left; leftMargin: 20 + right: parent.right; rightMargin: 20 + top: parent.top; topMargin: 180 + } + Text { font.bold: true; color: "white"; elide: Text.ElideRight; text: container.photoTitle } + Text { color: "white"; elide: Text.ElideRight; text: "<b>Size:</b> " + container.photoWidth + 'x' + container.photoHeight } + Text { color: "white"; elide: Text.ElideRight; text: "<b>Type:</b> " + container.photoType } + Text { color: "white"; elide: Text.ElideRight; text: "<b>Author:</b> " + container.photoAuthor } + Text { color: "white"; elide: Text.ElideRight; text: "<b>Published:</b> " + container.photoDate } + Text { color: "white"; elide: Text.ElideRight; text: container.photoTags == "" ? "" : "<b>Tags:</b> " } + Text { color: "white"; elide: Text.ElideRight; text: container.photoTags } + } + } + + back: Item { + anchors.fill: container + + Rectangle { anchors.fill: parent; color: "black"; opacity: 0.4 } + + Common.Progress { anchors.centerIn: parent; width: 200; height: 18; progress: bigImage.progress; visible: bigImage.status!=1 } + Flickable { + id: flickable; anchors.fill: parent; clip: true + contentWidth: imageContainer.width; contentHeight: imageContainer.height + + Item { + id: imageContainer + width: Math.max(bigImage.width * bigImage.scale, flickable.width); + height: Math.max(bigImage.height * bigImage.scale, flickable.height); + + Image { + id: bigImage; source: container.photoUrl; scale: slider.value + anchors.centerIn: parent; smooth: !flickable.moving + onStatusChanged : { + // Default scale shows the entire image. + if (status == 1 && width != 0) { + slider.minimum = Math.min(flickable.width / width, flickable.height / height); + prevScale = Math.min(slider.minimum, 1); + slider.value = prevScale; + } + } + } + } + } + + Text { + text: "Image Unavailable" + visible: bigImage.status == 'Error' + anchors.centerIn: parent; color: "white"; font.bold: true + } + + Common.Slider { + id: slider; visible: { bigImage.status == 1 && maximum > minimum } + anchors { + bottom: parent.bottom; bottomMargin: 65 + left: parent.left; leftMargin: 25 + right: parent.right; rightMargin: 25 + } + onValueChanged: { + if (bigImage.width * value > flickable.width) { + var xoff = (flickable.width/2 + flickable.contentX) * value / prevScale; + flickable.contentX = xoff - flickable.width/2; + } + if (bigImage.height * value > flickable.height) { + var yoff = (flickable.height/2 + flickable.contentY) * value / prevScale; + flickable.contentY = yoff - flickable.height/2; + } + prevScale = value; + } + } + } + + states: State { + name: "Back" + PropertyChanges { target: itemRotation; angle: 180 } + } + + transitions: Transition { + SequentialAnimation { + PropertyAction { target: bigImage; property: "smooth"; value: false } + NumberAnimation { easing.type: "InOutQuad"; properties: "angle"; duration: 500 } + PropertyAction { target: bigImage; property: "smooth"; value: !flickable.moving } + } + } +} diff --git a/demos/declarative/flickr/mobile/ListDelegate.qml b/demos/declarative/flickr/mobile/ListDelegate.qml new file mode 100644 index 0000000..381664b --- /dev/null +++ b/demos/declarative/flickr/mobile/ListDelegate.qml @@ -0,0 +1,23 @@ +import Qt 4.6 + +Component { + Item { + id: wrapper; width: wrapper.ListView.view.width; height: 86 + Item { + id: moveMe + Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 } + Rectangle { + x: 6; y: 4; width: 77; height: 77; color: "white"; smooth: true + + Image { source: imagePath; x: 1; y: 1 } + Image { source: "images/gloss.png" } + } + Column { + x: 92; width: wrapper.ListView.view.width - 95; y: 15; spacing: 2 + Text { text: title; color: "white"; width: parent.width; font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: "black" } + Text { text: photoAuthor; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" } + Text { text: photoDate; width: parent.width; elide: Text.ElideRight; color: "#cccccc"; style: Text.Raised; styleColor: "black" } + } + } + } +} diff --git a/demos/declarative/flickr/mobile/TitleBar.qml b/demos/declarative/flickr/mobile/TitleBar.qml new file mode 100644 index 0000000..0a06771 --- /dev/null +++ b/demos/declarative/flickr/mobile/TitleBar.qml @@ -0,0 +1,76 @@ +import Qt 4.6 + +Item { + id: titleBar + property string untaggedString: "Uploads from everyone" + property string taggedString: "Recent uploads tagged " + + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + + Item { + id: container + width: (parent.width * 2) - 55 ; height: parent.height + + Script { + function accept() { + titleBar.state = "" + background.state = "" + rssModel.tags = editor.text + } + } + + Text { + id: categoryText + anchors { + left: parent.left; right: tagButton.left; leftMargin: 10; rightMargin: 10 + verticalCenter: parent.verticalCenter + } + elide: Text.ElideLeft + text: (rssModel.tags=="" ? untaggedString : taggedString + rssModel.tags) + font.bold: true; color: "White"; style: Text.Raised; styleColor: "Black" + } + + Button { + id: tagButton; x: titleBar.width - 50; width: 45; height: 32; text: "..." + onClicked: if (titleBar.state == "Tags") accept(); else titleBar.state = "Tags" + anchors.verticalCenter: parent.verticalCenter + } + + Item { + id: lineEdit + y: 4; height: parent.height - 9 + anchors { left: tagButton.right; leftMargin: 5; right: parent.right; rightMargin: 5 } + + BorderImage { source: "images/lineedit.sci"; anchors.fill: parent } + + TextInput { + id: editor + anchors { + left: parent.left; right: parent.right; leftMargin: 10; rightMargin: 10 + verticalCenter: parent.verticalCenter + } + cursorVisible: true; font.bold: true + color: "#151515"; selectionColor: "Green" + } + + Keys.forwardTo: [ (returnKey), (editor)] + + Item { + id: returnKey + Keys.onReturnPressed: accept() + Keys.onEscapePressed: titleBar.state = "" + } + } + } + + states: State { + name: "Tags" + PropertyChanges { target: container; x: -tagButton.x + 5 } + PropertyChanges { target: tagButton; text: "OK" } + PropertyChanges { target: lineEdit; focus: true } + } + + transitions: Transition { + NumberAnimation { properties: "x"; easing.type: "InOutQuad" } + } +} diff --git a/demos/declarative/flickr/mobile/ToolBar.qml b/demos/declarative/flickr/mobile/ToolBar.qml new file mode 100644 index 0000000..f96c767 --- /dev/null +++ b/demos/declarative/flickr/mobile/ToolBar.qml @@ -0,0 +1,24 @@ +import Qt 4.6 + +Item { + id: toolbar + + property alias button1Label: button1.text + property alias button2Label: button2.text + signal button1Clicked + signal button2Clicked + + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + + Button { + id: button1 + anchors.left: parent.left; anchors.leftMargin: 5; y: 3; width: 140; height: 32 + onClicked: toolbar.button1Clicked() + } + + Button { + id: button2 + anchors.right: parent.right; anchors.rightMargin: 5; y: 3; width: 140; height: 32 + onClicked: toolbar.button2Clicked() + } +} diff --git a/demos/declarative/flickr/mobile/images/gloss.png b/demos/declarative/flickr/mobile/images/gloss.png Binary files differnew file mode 100644 index 0000000..5d370cd --- /dev/null +++ b/demos/declarative/flickr/mobile/images/gloss.png diff --git a/demos/declarative/flickr/mobile/images/lineedit.png b/demos/declarative/flickr/mobile/images/lineedit.png Binary files differnew file mode 100644 index 0000000..2cc38dc --- /dev/null +++ b/demos/declarative/flickr/mobile/images/lineedit.png diff --git a/demos/declarative/flickr/mobile/images/lineedit.sci b/demos/declarative/flickr/mobile/images/lineedit.sci new file mode 100644 index 0000000..054bff7 --- /dev/null +++ b/demos/declarative/flickr/mobile/images/lineedit.sci @@ -0,0 +1,5 @@ +border.left: 10 +border.top: 10 +border.bottom: 10 +border.right: 10 +source: lineedit.png diff --git a/demos/declarative/flickr/mobile/images/stripes.png b/demos/declarative/flickr/mobile/images/stripes.png Binary files differnew file mode 100644 index 0000000..9f36727 --- /dev/null +++ b/demos/declarative/flickr/mobile/images/stripes.png diff --git a/demos/declarative/flickr/mobile/images/titlebar.png b/demos/declarative/flickr/mobile/images/titlebar.png Binary files differnew file mode 100644 index 0000000..51c9008 --- /dev/null +++ b/demos/declarative/flickr/mobile/images/titlebar.png diff --git a/demos/declarative/flickr/mobile/images/titlebar.sci b/demos/declarative/flickr/mobile/images/titlebar.sci new file mode 100644 index 0000000..0418d94 --- /dev/null +++ b/demos/declarative/flickr/mobile/images/titlebar.sci @@ -0,0 +1,5 @@ +border.left: 10 +border.top: 12 +border.bottom: 12 +border.right: 10 +source: titlebar.png diff --git a/demos/declarative/flickr/mobile/images/toolbutton.png b/demos/declarative/flickr/mobile/images/toolbutton.png Binary files differnew file mode 100644 index 0000000..1131001 --- /dev/null +++ b/demos/declarative/flickr/mobile/images/toolbutton.png diff --git a/demos/declarative/flickr/mobile/images/toolbutton.sci b/demos/declarative/flickr/mobile/images/toolbutton.sci new file mode 100644 index 0000000..9e4f965 --- /dev/null +++ b/demos/declarative/flickr/mobile/images/toolbutton.sci @@ -0,0 +1,5 @@ +border.left: 15 +border.top: 4 +border.bottom: 4 +border.right: 15 +source: toolbutton.png diff --git a/demos/declarative/minehunt/MinehuntCore/Explosion.qml b/demos/declarative/minehunt/MinehuntCore/Explosion.qml new file mode 100644 index 0000000..e337c46 --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/Explosion.qml @@ -0,0 +1,26 @@ +import Qt 4.6 + +Item { + property bool explode : false + + Particles { + id: particles + width: 40 + height: 40 + lifeSpan: 1000 + lifeSpanDeviation: 0 + source: "pics/star.png" + count: 0 + angle: 270 + angleDeviation: 360 + velocity: 100 + velocityDeviation: 20 + z: 100 + opacity: 1 + } + states: [ State { name: "exploding"; when: explode == true + StateChangeScript {script: particles.burst(200); } + } + ] + +} diff --git a/demos/declarative/minehunt/MinehuntCore/pics/No-Ones-Laughing-3.jpg b/demos/declarative/minehunt/MinehuntCore/pics/No-Ones-Laughing-3.jpg Binary files differnew file mode 100644 index 0000000..445567f --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/No-Ones-Laughing-3.jpg diff --git a/demos/declarative/minehunt/MinehuntCore/pics/back.png b/demos/declarative/minehunt/MinehuntCore/pics/back.png Binary files differnew file mode 100644 index 0000000..f6b3f0b --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/back.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/bomb-color.png b/demos/declarative/minehunt/MinehuntCore/pics/bomb-color.png Binary files differnew file mode 100644 index 0000000..61ad0a9 --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/bomb-color.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/bomb.png b/demos/declarative/minehunt/MinehuntCore/pics/bomb.png Binary files differnew file mode 100644 index 0000000..a992575 --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/bomb.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/face-sad.png b/demos/declarative/minehunt/MinehuntCore/pics/face-sad.png Binary files differnew file mode 100644 index 0000000..cf00aaf --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/face-sad.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/face-smile-big.png b/demos/declarative/minehunt/MinehuntCore/pics/face-smile-big.png Binary files differnew file mode 100644 index 0000000..f9c2335 --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/face-smile-big.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/face-smile.png b/demos/declarative/minehunt/MinehuntCore/pics/face-smile.png Binary files differnew file mode 100644 index 0000000..3d66d72 --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/face-smile.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/flag-color.png b/demos/declarative/minehunt/MinehuntCore/pics/flag-color.png Binary files differnew file mode 100644 index 0000000..aadad0f --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/flag-color.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/flag.png b/demos/declarative/minehunt/MinehuntCore/pics/flag.png Binary files differnew file mode 100644 index 0000000..39cde4d --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/flag.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/front.png b/demos/declarative/minehunt/MinehuntCore/pics/front.png Binary files differnew file mode 100644 index 0000000..834331b --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/front.png diff --git a/demos/declarative/minehunt/MinehuntCore/pics/star.png b/demos/declarative/minehunt/MinehuntCore/pics/star.png Binary files differnew file mode 100644 index 0000000..3772359 --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/pics/star.png diff --git a/demos/declarative/minehunt/MinehuntCore/qmldir b/demos/declarative/minehunt/MinehuntCore/qmldir new file mode 100644 index 0000000..862c396 --- /dev/null +++ b/demos/declarative/minehunt/MinehuntCore/qmldir @@ -0,0 +1,2 @@ +plugin minehunt +Explosion 1.0 Explosion.qml diff --git a/demos/declarative/minehunt/README b/demos/declarative/minehunt/README new file mode 100644 index 0000000..7379dcf --- /dev/null +++ b/demos/declarative/minehunt/README @@ -0,0 +1,3 @@ +To run, simply load the minehunt.qml file with the qml runtime. + +Note that on X11, this demo has problems with the native graphicssystem. If you are using the X11 window system, please pass -graphicssystem raster to the qml binary. diff --git a/demos/declarative/minehunt/minehunt.cpp b/demos/declarative/minehunt/minehunt.cpp new file mode 100644 index 0000000..89845ef --- /dev/null +++ b/demos/declarative/minehunt/minehunt.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <stdlib.h> +#include <qdeclarativeextensionplugin.h> +#include <qdeclarativecontext.h> +#include <qdeclarativeengine.h> +#include <qdeclarative.h> + +#include <QTime> +#include <QTimer> + +class Tile : public QObject +{ + Q_OBJECT +public: + Tile() : _hasFlag(false), _hasMine(false), _hint(-1), _flipped(false) {} + + Q_PROPERTY(bool hasFlag READ hasFlag WRITE setHasFlag NOTIFY hasFlagChanged); + bool hasFlag() const { return _hasFlag; } + + Q_PROPERTY(bool hasMine READ hasMine NOTIFY hasMineChanged); + bool hasMine() const { return _hasMine; } + + Q_PROPERTY(int hint READ hint NOTIFY hintChanged); + int hint() const { return _hint; } + + Q_PROPERTY(bool flipped READ flipped NOTIFY flippedChanged()); + bool flipped() const { return _flipped; } + + void setHasFlag(bool flag) {if(flag==_hasFlag) return; _hasFlag = flag; emit hasFlagChanged();} + void setHasMine(bool mine) {if(mine==_hasMine) return; _hasMine = mine; emit hasMineChanged();} + void setHint(int hint) { if(hint == _hint) return; _hint = hint; emit hintChanged(); } + void flip() { if (_flipped) return; _flipped = true; emit flippedChanged(); } + void unflip() { if(!_flipped) return; _flipped = false; emit flippedChanged(); } + +signals: + void flippedChanged(); + void hasFlagChanged(); + void hintChanged(); + void hasMineChanged(); + +private: + bool _hasFlag; + bool _hasMine; + int _hint; + bool _flipped; +}; + +class MinehuntGame : public QObject +{ + Q_OBJECT +public: + MinehuntGame(); + + Q_PROPERTY(QDeclarativeListProperty<Tile> tiles READ tiles CONSTANT); + QDeclarativeListProperty<Tile> tiles() { return QDeclarativeListProperty<Tile>(this, _tiles); } + + Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY isPlayingChanged); + bool isPlaying() {return playing;} + + Q_PROPERTY(bool hasWon READ hasWon NOTIFY hasWonChanged); + bool hasWon() {return won;} + + Q_PROPERTY(int numMines READ numMines NOTIFY numMinesChanged); + int numMines() const{return nMines;} + + Q_PROPERTY(int numFlags READ numFlags NOTIFY numFlagsChanged); + int numFlags() const{return nFlags;} + +public slots: + Q_INVOKABLE bool flip(int row, int col); + Q_INVOKABLE bool flag(int row, int col); + void setBoard(); + void reset(); + +signals: + void isPlayingChanged(); + void hasWonChanged(); + void numMinesChanged(); + void numFlagsChanged(); + +private: + bool onBoard( int r, int c ) const { return r >= 0 && r < numRows && c >= 0 && c < numCols; } + Tile *tile( int row, int col ) { return onBoard(row, col) ? _tiles[col+numRows*row] : 0; } + int getHint(int row, int col); + void setPlaying(bool b){if(b==playing) return; playing=b; emit isPlayingChanged();} + + QList<Tile *> _tiles; + int numCols; + int numRows; + bool playing; + bool won; + int remaining; + int nMines; + int nFlags; +}; + +MinehuntGame::MinehuntGame() +: numCols(9), numRows(9), playing(true), won(false) +{ + setObjectName("mainObject"); + srand(QTime(0,0,0).secsTo(QTime::currentTime())); + + //initialize array + for(int ii = 0; ii < numRows * numCols; ++ii) { + _tiles << new Tile; + } + reset(); + +} + +void MinehuntGame::setBoard() +{ + foreach(Tile* t, _tiles){ + t->setHasMine(false); + t->setHint(-1); + } + //place mines + int mines = nMines; + remaining = numRows*numCols-mines; + while ( mines ) { + int col = int((double(rand()) / double(RAND_MAX)) * numCols); + int row = int((double(rand()) / double(RAND_MAX)) * numRows); + + Tile* t = tile( row, col ); + + if (t && !t->hasMine()) { + t->setHasMine( true ); + mines--; + } + } + + //set hints + for (int r = 0; r < numRows; r++) + for (int c = 0; c < numCols; c++) { + Tile* t = tile(r, c); + if (t && !t->hasMine()) { + int hint = getHint(r,c); + t->setHint(hint); + } + } + + setPlaying(true); +} + +void MinehuntGame::reset() +{ + foreach(Tile* t, _tiles){ + t->unflip(); + t->setHasFlag(false); + } + nMines = 12; + nFlags = 0; + setPlaying(false); + QTimer::singleShot(600,this, SLOT(setBoard())); +} + +int MinehuntGame::getHint(int row, int col) +{ + int hint = 0; + for (int c = col-1; c <= col+1; c++) + for (int r = row-1; r <= row+1; r++) { + Tile* t = tile(r, c); + if (t && t->hasMine()) + hint++; + } + return hint; +} + +bool MinehuntGame::flip(int row, int col) +{ + if(!playing) + return false; + + Tile *t = tile(row, col); + if (!t || t->hasFlag()) + return false; + + if(t->flipped()){ + int flags = 0; + for (int c = col-1; c <= col+1; c++) + for (int r = row-1; r <= row+1; r++) { + Tile *nearT = tile(r, c); + if(!nearT || nearT == t) + continue; + if(nearT->hasFlag()) + flags++; + } + if(!t->hint() || t->hint() != flags) + return false; + for (int c = col-1; c <= col+1; c++) + for (int r = row-1; r <= row+1; r++) { + Tile *nearT = tile(r, c); + if (nearT && !nearT->flipped() && !nearT->hasFlag()) { + flip( r, c ); + } + } + return true; + } + + t->flip(); + + if (t->hint() == 0) { + for (int c = col-1; c <= col+1; c++) + for (int r = row-1; r <= row+1; r++) { + Tile* t = tile(r, c); + if (t && !t->flipped()) { + flip( r, c ); + } + } + } + + if(t->hasMine()){ + for (int r = 0; r < numRows; r++)//Flip all other mines + for (int c = 0; c < numCols; c++) { + Tile* t = tile(r, c); + if (t && t->hasMine()) { + flip(r, c); + } + } + won = false; + hasWonChanged(); + setPlaying(false); + } + + remaining--; + if(!remaining){ + won = true; + hasWonChanged(); + setPlaying(false); + } + return true; +} + +bool MinehuntGame::flag(int row, int col) +{ + Tile *t = tile(row, col); + if(!t) + return false; + + t->setHasFlag(!t->hasFlag()); + nFlags += (t->hasFlag()?1:-1); + emit numFlagsChanged(); + return true; +} + +QML_DECLARE_TYPE(Tile); +QML_DECLARE_TYPE(MinehuntGame); + +class MinehuntExtensionPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT + + public: + void registerTypes(const char *uri) { + Q_UNUSED(uri); + QML_REGISTER_TYPE(SameGameCore, 0, 1, Tile, Tile); + QML_REGISTER_TYPE(SameGameCore, 0, 1, Game, MinehuntGame); + } + + void initializeEngine(QDeclarativeEngine *engine, const char *uri) { + Q_UNUSED(uri); + + srand(QTime(0,0,0).secsTo(QTime::currentTime())); + + MinehuntGame* game = new MinehuntGame(); + + engine->rootContext()->addDefaultObject(game); + } +}; + +#include "minehunt.moc" + +Q_EXPORT_PLUGIN(MinehuntExtensionPlugin); + diff --git a/demos/declarative/minehunt/minehunt.pro b/demos/declarative/minehunt/minehunt.pro new file mode 100644 index 0000000..2df33e6 --- /dev/null +++ b/demos/declarative/minehunt/minehunt.pro @@ -0,0 +1,25 @@ +TEMPLATE = lib +TARGET = minehunt +QT += declarative +CONFIG += qt plugin + +TARGET = $$qtLibraryTarget($$TARGET) +DESTDIR = MinehuntCore + +# Input +SOURCES += minehunt.cpp + + +sources.files = minehunt.qml minehunt.pro +sources.path = $$[QT_INSTALL_DEMOS]/declarative/minehunt + +target.path = $$[QT_INSTALL_DEMOS]/declarative/minehunt/MinehuntCore + +MinehuntCore_sources.files = \ + MinehuntCore/Explosion.qml \ + MinehuntCore/pics \ + MinehuntCore/qmldir +MinehuntCore_sources.path = $$[QT_INSTALL_DEMOS]/declarative/minehunt/MinehuntCore + +INSTALLS = sources MinehuntCore_sources target + diff --git a/demos/declarative/minehunt/minehunt.qml b/demos/declarative/minehunt/minehunt.qml new file mode 100644 index 0000000..9e99706 --- /dev/null +++ b/demos/declarative/minehunt/minehunt.qml @@ -0,0 +1,190 @@ +import Qt 4.6 +import MinehuntCore 1.0 + +Item { + id: field + width: 370 + height: 480 + + property int clickx : 0 + property int clicky : 0 + + resources: [ + Component { + id: tile + Flipable { + id: flipable + width: 40 + height: 40 + property int angle: 0; + transform: Rotation { + origin.x: 20 + origin.y: 20 + axis.x: 1 + axis.z: 0 + angle: flipable.angle; + } + front: Image { + source: "MinehuntCore/pics/front.png" + width: 40 + height: 40 + Image { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + source: "MinehuntCore/pics/flag.png" + opacity: modelData.hasFlag + Behavior on opacity { + NumberAnimation { + property: "opacity" + duration: 250 + } + } + } + } + back: Image { + source: "MinehuntCore/pics/back.png" + width: 40 + height: 40 + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + text: modelData.hint + color: "white" + font.bold: true + opacity: !modelData.hasMine && modelData.hint > 0 + } + Image { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + source: "MinehuntCore/pics/bomb.png" + opacity: modelData.hasMine + } + Explosion { + id: expl + } + } + states: [ + State { + name: "back" + when: modelData.flipped + PropertyChanges { target: flipable; angle: 180 } + } + ] + transitions: [ + Transition { + SequentialAnimation { + PauseAnimation { + duration: { + var ret; + if(flipable.parent != null) + ret = Math.abs(flipable.parent.x-field.clickx) + + Math.abs(flipable.parent.y-field.clicky); + else + ret = 0; + if (ret > 0) { + if (modelData.hasMine && modelData.flipped) { + ret*3; + } else { + ret; + } + } else { + 0; + } + } + } + NumberAnimation { + easing.type: "InOutQuad" + properties: "angle" + } + ScriptAction{ + script: if(modelData.hasMine && modelData.flipped){expl.explode = true;} + } + } + } + ] + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onPressed: { + field.clickx = flipable.parent.x; + field.clicky = flipable.parent.y; + var row = Math.floor(index/9); + var col = index - (Math.floor(index/9) * 9); + if (mouse.button==undefined || mouse.button==Qt.RightButton) { + flag(row,col); + } else { + flip(row,col); + } + } + } + } + } + ] + Image { + source: "MinehuntCore/pics/No-Ones-Laughing-3.jpg" + fillMode: Image.Tile + } + Repeater { + id: repeater + model: tiles + x: 1 + y: 1 + Component { + Loader { + sourceComponent: tile + x: (index - (Math.floor(index/9) * 9)) * 41 + y: Math.floor(index/9) * 41 + } + } + } + Row { + id: gamedata + // width: 370 + // height: 100 + y: 400 + x: 20 + spacing: 20 + Column { + spacing: 2 + width: childrenRect.width + Image { + // x: 100 + // y: 20 + source: "MinehuntCore/pics/bomb-color.png" + } + Text { + // x: 100 + // y: 60 + anchors.horizontalCenter: parent.horizontalCenter + color: "white" + text: numMines + } + } + Column { + spacing: 2 + width: childrenRect.width + Image { + // x: 140 + // y: 20 + source: "MinehuntCore/pics/flag-color.png" + } + Text { + // x: 140 + // y: 60 + anchors.horizontalCenter: parent.horizontalCenter + color: "white" + text: numFlags + } + } + } + Image { + y: 390 + anchors.right: field.right + anchors.rightMargin: 20 + source: isPlaying ? 'MinehuntCore/pics/face-smile.png' : hasWon ? 'MinehuntCore/pics/face-smile-big.png': 'MinehuntCore/pics/face-sad.png' + MouseArea { + anchors.fill: parent + onPressed: { reset() } + } + } +} diff --git a/demos/declarative/samegame/SamegameCore/BoomBlock.qml b/demos/declarative/samegame/SamegameCore/BoomBlock.qml new file mode 100644 index 0000000..e48194a --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/BoomBlock.qml @@ -0,0 +1,55 @@ +import Qt 4.6 + +Item { id:block + property bool dying: false + property bool spawned: false + property int type: 0 + property int targetX: 0 + property int targetY: 0 + + SpringFollow on x { enabled: spawned; source: targetX; spring: 2; damping: 0.2 } + SpringFollow on y { source: targetY; spring: 2; damping: 0.2 } + + Image { id: img + source: { + if(type == 0){ + "pics/redStone.png"; + } else if(type == 1) { + "pics/blueStone.png"; + } else { + "pics/greenStone.png"; + } + } + opacity: 0 + Behavior on opacity { NumberAnimation { duration: 200 } } + anchors.fill: parent + } + + Particles { id: particles + width:1; height:1; anchors.centerIn: parent; + emissionRate: 0; + lifeSpan: 700; lifeSpanDeviation: 600; + angle: 0; angleDeviation: 360; + velocity: 100; velocityDeviation:30; + source: { + if(type == 0){ + "pics/redStar.png"; + } else if (type == 1) { + "pics/blueStar.png"; + } else { + "pics/greenStar.png"; + } + } + } + + states: [ + State{ name: "AliveState"; when: spawned == true && dying == false + PropertyChanges { target: img; opacity: 1 } + }, + State{ name: "DeathState"; when: dying == true + StateChangeScript { script: particles.burst(50); } + PropertyChanges { target: img; opacity: 0 } + StateChangeScript { script: block.destroy(1000); } + } + ] +} diff --git a/demos/declarative/samegame/SamegameCore/Button.qml b/demos/declarative/samegame/SamegameCore/Button.qml new file mode 100644 index 0000000..6629302 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/Button.qml @@ -0,0 +1,25 @@ +import Qt 4.6 + +Rectangle { + id: container + + signal clicked + property string text: "Button" + + color: activePalette.button; smooth: true + width: txtItem.width + 20; height: txtItem.height + 6 + border.width: 1; border.color: Qt.darker(activePalette.button); radius: 8; + + gradient: Gradient { + GradientStop { + id: topGrad; position: 0.0 + color: if (mr.pressed) { activePalette.dark } else { activePalette.light } } + GradientStop { position: 1.0; color: activePalette.button } + } + + MouseArea { id: mr; anchors.fill: parent; onClicked: container.clicked() } + + Text { + id: txtItem; text: container.text; anchors.centerIn: container; color: activePalette.buttonText + } +} diff --git a/demos/declarative/samegame/SamegameCore/Dialog.qml b/demos/declarative/samegame/SamegameCore/Dialog.qml new file mode 100644 index 0000000..6d5d6b5 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/Dialog.qml @@ -0,0 +1,22 @@ +import Qt 4.6 + +Rectangle { + id: page + function forceClose() { + page.closed(); + page.opacity = 0; + } + function show(txt) { + myText.text = txt; + page.opacity = 1; + } + signal closed(); + property Item text: myText + color: "white"; border.width: 1; width: myText.width + 20; height: myText.height + 40; + opacity: 0 + Behavior on opacity { + NumberAnimation { duration: 1000 } + } + Text { id: myText; anchors.centerIn: parent; text: "Hello World!" } + MouseArea { id: mr; anchors.fill: parent; onClicked: forceClose(); } +} diff --git a/demos/declarative/samegame/SamegameCore/pics/background.png b/demos/declarative/samegame/SamegameCore/pics/background.png Binary files differnew file mode 100644 index 0000000..3734a27 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/background.png diff --git a/demos/declarative/samegame/SamegameCore/pics/blueStar.png b/demos/declarative/samegame/SamegameCore/pics/blueStar.png Binary files differnew file mode 100644 index 0000000..ff9588f --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/blueStar.png diff --git a/demos/declarative/samegame/SamegameCore/pics/blueStone.png b/demos/declarative/samegame/SamegameCore/pics/blueStone.png Binary files differnew file mode 100644 index 0000000..20e43c7 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/blueStone.png diff --git a/demos/declarative/samegame/SamegameCore/pics/greenStar.png b/demos/declarative/samegame/SamegameCore/pics/greenStar.png Binary files differnew file mode 100644 index 0000000..cd06854 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/greenStar.png diff --git a/demos/declarative/samegame/SamegameCore/pics/greenStone.png b/demos/declarative/samegame/SamegameCore/pics/greenStone.png Binary files differnew file mode 100644 index 0000000..b568a19 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/greenStone.png diff --git a/demos/declarative/samegame/SamegameCore/pics/redStar.png b/demos/declarative/samegame/SamegameCore/pics/redStar.png Binary files differnew file mode 100644 index 0000000..0a4dffe --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/redStar.png diff --git a/demos/declarative/samegame/SamegameCore/pics/redStone.png b/demos/declarative/samegame/SamegameCore/pics/redStone.png Binary files differnew file mode 100644 index 0000000..36b09a2 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/redStone.png diff --git a/demos/declarative/samegame/SamegameCore/pics/star.png b/demos/declarative/samegame/SamegameCore/pics/star.png Binary files differnew file mode 100644 index 0000000..defbde5 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/star.png diff --git a/demos/declarative/samegame/SamegameCore/pics/yellowStone.png b/demos/declarative/samegame/SamegameCore/pics/yellowStone.png Binary files differnew file mode 100644 index 0000000..b1ce762 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/pics/yellowStone.png diff --git a/demos/declarative/samegame/SamegameCore/qmldir b/demos/declarative/samegame/SamegameCore/qmldir new file mode 100644 index 0000000..a8f8a98 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/qmldir @@ -0,0 +1,3 @@ +BoomBlock 0.0 BoomBlock.qml +Button 0.0 Button.qml +Dialog 0.0 Dialog.qml diff --git a/demos/declarative/samegame/SamegameCore/samegame.js b/demos/declarative/samegame/SamegameCore/samegame.js new file mode 100755 index 0000000..1214b79 --- /dev/null +++ b/demos/declarative/samegame/SamegameCore/samegame.js @@ -0,0 +1,250 @@ +/* This script file handles the game logic */ +//Note that X/Y referred to here are in game coordinates +var maxX = 10;//Nums are for gameCanvas.tileSize 40 +var maxY = 15; +var maxIndex = maxX*maxY; +var board = new Array(maxIndex); +var tileSrc = "SamegameCore/BoomBlock.qml"; +var scoresURL = "http://qtfx-nokia.trolltech.com.au/samegame/scores.php"; +var scoresURL = ""; +var timer; +var component = createComponent(tileSrc); + +//Index function used instead of a 2D array +function index(xIdx,yIdx) { + return xIdx + (yIdx * maxX); +} + +function timeStr(msecs) { + var secs = Math.floor(msecs/1000); + var m = Math.floor(secs/60); + var ret = "" + m + "m " + (secs%60) + "s"; + return ret; +} + +function getTileSize() +{ + return tileSize; +} + +function initBoard() +{ + for(var i = 0; i<maxIndex; i++){ + //Delete old blocks + if(board[i] != null) + board[i].destroy(); + } + + //Calculate board size + maxX = Math.floor(gameCanvas.width/gameCanvas.tileSize); + maxY = Math.floor(gameCanvas.height/gameCanvas.tileSize); + maxIndex = maxY*maxX; + + //Close dialogs + scoreName.forceClose(); + dialog.forceClose(); + + var a = new Date(); + //Initialize Board + board = new Array(maxIndex); + gameCanvas.score = 0; + for(var xIdx=0; xIdx<maxX; xIdx++){ + for(var yIdx=0; yIdx<maxY; yIdx++){ + board[index(xIdx,yIdx)] = null; + createBlock(xIdx,yIdx); + } + } + timer = new Date(); + + //print(timer.valueOf() - a.valueOf()); +} + +var fillFound;//Set after a floodFill call to the number of tiles found +var floodBoard;//Set to 1 if the floodFill reaches off that node +//NOTE: Be careful with vars named x,y, as the calling object's x,y are still in scope +function handleClick(x,y) +{ + var xIdx = Math.floor(x/gameCanvas.tileSize); + var yIdx = Math.floor(y/gameCanvas.tileSize); + if(xIdx >= maxX || xIdx < 0 || yIdx >= maxY || yIdx < 0) + return; + if(board[index(xIdx, yIdx)] == null) + return; + //If it's a valid tile, remove it and all connected (does nothing if it's not connected) + floodFill(xIdx,yIdx, -1); + if(fillFound <= 0) + return; + gameCanvas.score += (fillFound - 1) * (fillFound - 1); + shuffleDown(); + victoryCheck(); +} + +function floodFill(xIdx,yIdx,type) +{ + if(board[index(xIdx, yIdx)] == null) + return; + var first = false; + if(type == -1){ + first = true; + type = board[index(xIdx,yIdx)].type; + + //Flood fill initialization + fillFound = 0; + floodBoard = new Array(maxIndex); + } + if(xIdx >= maxX || xIdx < 0 || yIdx >= maxY || yIdx < 0) + return; + if(floodBoard[index(xIdx, yIdx)] == 1 || (!first && type != board[index(xIdx,yIdx)].type)) + return; + floodBoard[index(xIdx, yIdx)] = 1; + floodFill(xIdx+1,yIdx,type); + floodFill(xIdx-1,yIdx,type); + floodFill(xIdx,yIdx+1,type); + floodFill(xIdx,yIdx-1,type); + if(first==true && fillFound == 0) + return;//Can't remove single tiles + board[index(xIdx,yIdx)].dying = true; + board[index(xIdx,yIdx)] = null; + fillFound += 1; +} + +function shuffleDown() +{ + //Fall down + for(var xIdx=0; xIdx<maxX; xIdx++){ + var fallDist = 0; + for(var yIdx=maxY-1; yIdx>=0; yIdx--){ + if(board[index(xIdx,yIdx)] == null){ + fallDist += 1; + }else{ + if(fallDist > 0){ + var obj = board[index(xIdx,yIdx)]; + obj.targetY += fallDist * gameCanvas.tileSize; + board[index(xIdx,yIdx+fallDist)] = obj; + board[index(xIdx,yIdx)] = null; + } + } + } + } + //Fall to the left + fallDist = 0; + for(xIdx=0; xIdx<maxX; xIdx++){ + if(board[index(xIdx, maxY - 1)] == null){ + fallDist += 1; + }else{ + if(fallDist > 0){ + for(yIdx=0; yIdx<maxY; yIdx++){ + obj = board[index(xIdx,yIdx)]; + if(obj == null) + continue; + obj.targetX -= fallDist * gameCanvas.tileSize; + board[index(xIdx-fallDist,yIdx)] = obj; + board[index(xIdx,yIdx)] = null; + } + } + } + } +} + +function victoryCheck() +{ + //awards bonuses for no tiles left + var deservesBonus = true; + for(var xIdx=maxX-1; xIdx>=0; xIdx--) + if(board[index(xIdx, maxY - 1)] != null) + deservesBonus = false; + if(deservesBonus) + gameCanvas.score += 500; + //Checks for game over + if(deservesBonus || !(floodMoveCheck(0,maxY-1, -1))){ + timer = new Date() - timer; + //scoreName.show("You won! Please enter your name: "); + scoreName.show("You won! Please enter your name: "); + scoreName.initialWidth = scoreName.text.width + 20; + scoreName.width = scoreName.initialWidth; + scoreName.text.opacity = 0;//Just a spacer + //dialog.show("Game Over. Your score is " + gameCanvas.score); + } +} + +//only floods up and right, to see if it can find adjacent same-typed tiles +function floodMoveCheck(xIdx, yIdx, type) +{ + if(xIdx >= maxX || xIdx < 0 || yIdx >= maxY || yIdx < 0) + return false; + if(board[index(xIdx, yIdx)] == null) + return false; + var myType = board[index(xIdx, yIdx)].type; + if(type == myType) + return true; + return floodMoveCheck(xIdx + 1, yIdx, myType) || + floodMoveCheck(xIdx, yIdx - 1, board[index(xIdx,yIdx)].type); +} + +function createBlock(xIdx,yIdx){ + // Note that we don't wait for the component to become ready. This will + // only work if the block QML is a local file. Otherwise the component will + // not be ready immediately. There is a statusChanged signal on the + // component you could use if you want to wait to load remote files. + if(component.isReady){ + var dynamicObject = component.createObject(); + if(dynamicObject == null){ + print("error creating block"); + print(component.errorsString()); + return false; + } + dynamicObject.type = Math.floor(Math.random() * 3); + dynamicObject.parent = gameCanvas; + dynamicObject.x = xIdx*gameCanvas.tileSize; + dynamicObject.targetX = xIdx*gameCanvas.tileSize; + dynamicObject.targetY = yIdx*gameCanvas.tileSize; + dynamicObject.width = gameCanvas.tileSize; + dynamicObject.height = gameCanvas.tileSize; + dynamicObject.spawned = true; + board[index(xIdx,yIdx)] = dynamicObject; + }else{//isError or isLoading + print("error loading block component"); + print(component.errorsString()); + return false; + } + return true; +} + +function saveHighScore(name) { + if(scoresURL!="") + sendHighScore(name); + //OfflineStorage + var db = openDatabaseSync("SameGameScores", "1.0", "Local SameGame High Scores",100); + var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)"; + var data = [name, gameCanvas.score, maxX+"x"+maxY ,Math.floor(timer/1000)]; + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); + tx.executeSql(dataStr, data); + + //Only show results for the current grid size + var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "'+maxX+"x"+maxY+'" ORDER BY score desc LIMIT 10'); + var r = "\nHIGH SCORES for this grid size\n\n" + for(var i = 0; i < rs.rows.length; i++){ + r += (i+1)+". " + rs.rows.item(i).name +' got ' + + rs.rows.item(i).score + ' points in ' + + rs.rows.item(i).time + ' seconds.\n'; + } + dialog.show(r); + } + ); +} + +function sendHighScore(name) { + var postman = new XMLHttpRequest() + var postData = "name="+name+"&score="+gameCanvas.score + +"&gridSize="+maxX+"x"+maxY +"&time="+Math.floor(timer/1000); + postman.open("POST", scoresURL, true); + postman.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + postman.onreadystatechange = function() { + if (postman.readyState == postman.DONE) { + dialog.show("Your score has been uploaded."); + } + } + postman.send(postData); +} diff --git a/demos/declarative/samegame/highscores/README b/demos/declarative/samegame/highscores/README new file mode 100644 index 0000000..eaa00fa --- /dev/null +++ b/demos/declarative/samegame/highscores/README @@ -0,0 +1 @@ +The SameGame example can interface with a simple PHP script to store XML high score data on a remote server. We do not have a publically accessible server available for this use, but if you have access to a PHP capable webserver you can copy the files (score_data.xml, score.php, score_style.xsl) to it and alter the highscore_server variable at the top of the samegame.js file to point to it. diff --git a/demos/declarative/samegame/highscores/score_data.xml b/demos/declarative/samegame/highscores/score_data.xml new file mode 100755 index 0000000..c3fd90d --- /dev/null +++ b/demos/declarative/samegame/highscores/score_data.xml @@ -0,0 +1,2 @@ +<record><score>1000000</score><name>Alan the Tester</name><gridSize>0x0</gridSize><seconds>0</seconds></record> +<record><score>6213</score><name>Alan</name><gridSize>12x17</gridSize><seconds>51</seconds></record> diff --git a/demos/declarative/samegame/highscores/score_style.xsl b/demos/declarative/samegame/highscores/score_style.xsl new file mode 100755 index 0000000..670354c --- /dev/null +++ b/demos/declarative/samegame/highscores/score_style.xsl @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +<xsl:template match="/"> + <html> + <head><title>SameGame High Scores</title></head> + <body> + <h2>SameGame High Scores</h2> + <table border="1"> + <tr bgcolor="lightsteelblue"> + <th>Name</th> + <th>Score</th> + <th>Grid Size</th> + <th>Time, s</th> + </tr> + <xsl:for-each select="records/record"> + <xsl:sort select="score" data-type="number" order="descending"/> + <tr> + <td><xsl:value-of select="name"/></td> + <td><xsl:value-of select="score"/></td> + <td><xsl:value-of select="gridSize"/></td> + <td><xsl:value-of select="seconds"/></td> + </tr> + </xsl:for-each> + </table> + </body> + </html> +</xsl:template> +</xsl:stylesheet> diff --git a/demos/declarative/samegame/highscores/scores.php b/demos/declarative/samegame/highscores/scores.php new file mode 100755 index 0000000..3cceb2d --- /dev/null +++ b/demos/declarative/samegame/highscores/scores.php @@ -0,0 +1,34 @@ +<?php + $score = $_POST["score"]; + echo "<html>"; + echo "<head><title>SameGame High Scores</title></head><body>"; + if($score > 0){#Sending in a new high score + $name = $_POST["name"]; + $grid = $_POST["gridSize"]; + $time = $_POST["time"]; + if($name == "") + $name = "Anonymous"; + //if($grid != "10x10"){ + //Need a standard, so as to reject others? + //} + $file = fopen("score_data.xml", "a"); #It's XML. Happy? + $ret = fwrite($file, "<record><score>". $score . "</score><name>" + . $name . "</name><gridSize>" . $grid . "</gridSize><seconds>" + . $time . "</seconds></record>\n"); + echo "Your score has been recorded. Thanks for playing!"; + if($ret == False) + echo "<br/> There was an error though, so don't expect to see that score again."; + }else{#Read high score list + #Now uses XSLT to display. So just print the file. With XML cruft added. + #Note that firefox at least won't apply the XSLT on a php file. So redirecting + $file = fopen("scores.xml", "w"); + $ret = fwrite($file, '<?xml version="1.0" encoding="ISO-8859-1"?>' . "\n" + . '<?xml-stylesheet type="text/xsl" href="score_style.xsl"?>' . "\n" + . "<records>\n" . file_get_contents("score_data.xml") . "</records>\n"); + if($ret == False) + echo "There was an internal error. Sorry."; + else + echo '<script type="text/javascript">window.location.replace("scores.xml")</script>'; + } + echo "</body></html>"; +?> diff --git a/demos/declarative/samegame/samegame.qml b/demos/declarative/samegame/samegame.qml new file mode 100644 index 0000000..3b19cbe --- /dev/null +++ b/demos/declarative/samegame/samegame.qml @@ -0,0 +1,95 @@ +import Qt 4.6 +import SamegameCore 1.0 + +Rectangle { + id: screen + width: 490; height: 720 + + SystemPalette { id: activePalette } + + Item { + width: parent.width; anchors.top: parent.top; anchors.bottom: toolBar.top + + Image { + id: background + anchors.fill: parent; source: "SamegameCore/pics/background.png" + fillMode: Image.PreserveAspectCrop + smooth: true + } + + Item { + id: gameCanvas + property int score: 0 + property int tileSize: 40 + + Script { source: "SamegameCore/samegame.js" } + + z: 20; anchors.centerIn: parent + width: parent.width - (parent.width % getTileSize()); + height: parent.height - (parent.height % getTileSize()); + + MouseArea { + id: gameMR + anchors.fill: parent; onClicked: handleClick(mouse.x,mouse.y); + } + } + } + + Dialog { id: dialog; anchors.centerIn: parent; z: 21 } + Dialog { + id: scoreName; anchors.centerIn: parent; z: 22; + property int initialWidth: 0 + Behavior on width {NumberAnimation{} enabled: initialWidth!=0} + Text { + id: spacer + anchors.left: scoreName.left + anchors.leftMargin: 20 + anchors.verticalCenter: parent.verticalCenter + text: "You won! Please enter your name: " + } + TextInput { + id: editor + onTextChanged: { + var newWidth = editor.width + spacer.width + 40; + if((newWidth > scoreName.width && newWidth < screen.width) + || (scoreName.width > scoreName.initialWidth)) + scoreName.width = newWidth; + } + onAccepted: { + if(scoreName.opacity==1&&editor.text!="") + saveHighScore(editor.text); + scoreName.forceClose(); + } + anchors.verticalCenter: parent.verticalCenter + focus: true + anchors.left: spacer.right + } + } + + Rectangle { + id: toolBar + color: activePalette.window + height: 32; width: parent.width + anchors.bottom: screen.bottom + + Button { + id: btnA; text: "New Game"; onClicked: {initBoard();} + anchors.left: parent.left; anchors.leftMargin: 3 + anchors.verticalCenter: parent.verticalCenter + } + + Button { + id: btnB; text: "Quit"; onClicked: {Qt.quit();} + anchors.left: btnA.right; anchors.leftMargin: 3 + anchors.verticalCenter: parent.verticalCenter + } + + Text { + id: score + text: "Score: " + gameCanvas.score; font.bold: true + anchors.right: parent.right; anchors.rightMargin: 3 + anchors.verticalCenter: parent.verticalCenter + color: activePalette.windowText + } + } +} diff --git a/demos/declarative/snake/content/Button.qml b/demos/declarative/snake/content/Button.qml new file mode 100644 index 0000000..6629302 --- /dev/null +++ b/demos/declarative/snake/content/Button.qml @@ -0,0 +1,25 @@ +import Qt 4.6 + +Rectangle { + id: container + + signal clicked + property string text: "Button" + + color: activePalette.button; smooth: true + width: txtItem.width + 20; height: txtItem.height + 6 + border.width: 1; border.color: Qt.darker(activePalette.button); radius: 8; + + gradient: Gradient { + GradientStop { + id: topGrad; position: 0.0 + color: if (mr.pressed) { activePalette.dark } else { activePalette.light } } + GradientStop { position: 1.0; color: activePalette.button } + } + + MouseArea { id: mr; anchors.fill: parent; onClicked: container.clicked() } + + Text { + id: txtItem; text: container.text; anchors.centerIn: container; color: activePalette.buttonText + } +} diff --git a/demos/declarative/snake/content/Cookie.qml b/demos/declarative/snake/content/Cookie.qml new file mode 100644 index 0000000..0ea95cb --- /dev/null +++ b/demos/declarative/snake/content/Cookie.qml @@ -0,0 +1,48 @@ +import Qt 4.6 + +Item { + id: root + property bool dying: false + property int row; + property int column; + x: margin + column * gridSize + y: margin + row * gridSize + + width: gridSize + height: gridSize + property int value : 1; + + Image { + id: img + anchors.fill: parent + source: "pics/cookie.png" + opacity: 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + Text { + font.bold: true + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + text: value + } + } + + + Particles { id: particles + width:1; height:1; anchors.centerIn: parent; + emissionRate: 0; + lifeSpan: 700; lifeSpanDeviation: 600; + angle: 0; angleDeviation: 360; + velocity: 100; velocityDeviation:30; + source: "pics/yellowStar.png"; + } + + states: [ + State{ name: "AliveState"; when: dying == false + PropertyChanges { target: img; opacity: 1 } + }, + State{ name: "DeathState"; when: dying == true + StateChangeScript { script: particles.burst(50); } + PropertyChanges { target: img; opacity: 0 } + } + ] +} diff --git a/demos/declarative/snake/content/HighScoreModel.qml b/demos/declarative/snake/content/HighScoreModel.qml new file mode 100644 index 0000000..f585ce8 --- /dev/null +++ b/demos/declarative/snake/content/HighScoreModel.qml @@ -0,0 +1,100 @@ +import Qt 4.6 + +// Models a high score table. +// +// Use this component like this: +// +// HighScoreModel { +// id: highScores +// game: "MyCoolGame" +// } +// +// Then use either use the top-score properties: +// +// Text { text: "HI: " + highScores.topScore } +// +// or, use the model in a view: +// +// ListView { +// model: highScore +// delegate: Component { +// ... player ... score ... +// } +// } +// +// Add new scores via: +// +// saveScore(newScore) +// +// or: +// +// savePlayerScore(playerName,newScore) +// +// The best maxScore scores added by this method will be retained in an SQL database, +// and presented in the model and in the topScore/topPlayer properties. +// + +ListModel { + id: model + property string game: "" + property int topScore: 0 + property string topPlayer: "" + property int maxScores: 10 + + Script { + function db() + { + return openDatabaseSync("HighScoreModel", "1.0", "Generic High Score Functionality for QML", 1000000); + } + function ensureTables(tx) + { + tx.executeSql('CREATE TABLE IF NOT EXISTS HighScores(game TEXT, score INT, player TEXT)', []); + } + } + + function fillModel() { + db().transaction( + function(tx) { + ensureTables(tx); + var rs = tx.executeSql("SELECT score,player FROM HighScores WHERE game=? ORDER BY score DESC", [game]); + model.clear(); + if (rs.rows.length > 0) { + topScore = rs.rows.item(0).score + topPlayer = rs.rows.item(0).player + for (var i=0; i<rs.rows.length; ++i) { + if (i < maxScores) + model.append(rs.rows.item(i)) + } + if (rs.rows.length > maxScores) + tx.executeSql("DELETE FROM HighScores WHERE game=? AND score <= ?", + [rs.rows.item(maxScores).score]); + } + } + ) + } + + function savePlayerScore(player,score) { + db().transaction( + function(tx) { + ensureTables(tx); + tx.executeSql("INSERT INTO HighScores VALUES(?,?,?)", [game,score,player]); + fillModel(); + } + ) + } + + function saveScore(score) { + savePlayerScore("player",score); + } + + function clearScores() { + db().transaction( + function(tx) { + tx.executeSql("DELETE FROM HighScores WHERE game=?", [game]); + fillModel(); + } + ) + } + + Component.onCompleted: { fillModel() } +} diff --git a/demos/declarative/snake/content/Link.qml b/demos/declarative/snake/content/Link.qml new file mode 100644 index 0000000..31ac4b9 --- /dev/null +++ b/demos/declarative/snake/content/Link.qml @@ -0,0 +1,75 @@ +import Qt 4.6 + +Item { id:link + property bool dying: false + property bool spawned: false + property int type: 0 + property int row: 0 + property int column: 0 + property int rotation; + + width: 40; + height: 40 + + x: margin - 3 + gridSize * column + y: margin - 3 + gridSize * row + Behavior on x { NumberAnimation { duration: spawned ? heartbeatInterval : 0} } + Behavior on y { NumberAnimation { duration: spawned ? heartbeatInterval : 0 } } + + + Item { + id: img + anchors.fill: parent + Image { + source: { + if(type == 1) { + "pics/blueStone.png"; + } else if (type == 2) { + "pics/head.png"; + } else { + "pics/redStone.png"; + } + } + + transform: Rotation { + id: actualImageRotation + origin.x: width/2; origin.y: height/2; + angle: rotation * 90 + Behavior on angle { NumberAnimation { duration: spawned ? 200 : 0} } + } + } + + Image { + source: "pics/stoneShadow.png" + } + + opacity: 0 + Behavior on opacity { NumberAnimation { duration: 200 } } + } + + + Particles { id: particles + width:1; height:1; anchors.centerIn: parent; + emissionRate: 0; + lifeSpan: 700; lifeSpanDeviation: 600; + angle: 0; angleDeviation: 360; + velocity: 100; velocityDeviation:30; + source: { + if(type == 1){ + "pics/blueStar.png"; + } else { + "pics/redStar.png"; + } + } + } + + states: [ + State{ name: "AliveState"; when: spawned == true && dying == false + PropertyChanges { target: img; opacity: 1 } + }, + State{ name: "DeathState"; when: dying == true + StateChangeScript { script: particles.burst(50); } + PropertyChanges { target: img; opacity: 0 } + } + ] +} diff --git a/demos/declarative/snake/content/Skull.qml b/demos/declarative/snake/content/Skull.qml new file mode 100644 index 0000000..821996a --- /dev/null +++ b/demos/declarative/snake/content/Skull.qml @@ -0,0 +1,21 @@ +import Qt 4.6 + +Image { + property bool spawned: false + property int row; + property int column; + property int verticalMovement; + property int horizontalMovement; + + x: margin + column * gridSize + 2 + y: margin + row * gridSize - 3 + Behavior on x { NumberAnimation { duration: spawned ? halfbeatInterval : 0} } + Behavior on y { NumberAnimation { duration: spawned ? halfbeatInterval : 0 } } + + opacity: spawned ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 200 } } + + source: "pics/skull.png" + width: 24 + height: 40 +} diff --git a/demos/declarative/snake/content/pics/README b/demos/declarative/snake/content/pics/README new file mode 100644 index 0000000..0215132 --- /dev/null +++ b/demos/declarative/snake/content/pics/README @@ -0,0 +1 @@ +snake.jpg: This image is based on the picture "Eastern Green Mamba.jpg" from the free media databse Wikimedia Commons and is published under the terms of the GNU Free Documentation License. The original picture was taken by Danleo. diff --git a/demos/declarative/snake/content/pics/background.png b/demos/declarative/snake/content/pics/background.png Binary files differnew file mode 100644 index 0000000..72dffaa --- /dev/null +++ b/demos/declarative/snake/content/pics/background.png diff --git a/demos/declarative/snake/content/pics/blueStar.png b/demos/declarative/snake/content/pics/blueStar.png Binary files differnew file mode 100644 index 0000000..ba7acab --- /dev/null +++ b/demos/declarative/snake/content/pics/blueStar.png diff --git a/demos/declarative/snake/content/pics/blueStone.png b/demos/declarative/snake/content/pics/blueStone.png Binary files differnew file mode 100644 index 0000000..356affd --- /dev/null +++ b/demos/declarative/snake/content/pics/blueStone.png diff --git a/demos/declarative/snake/content/pics/cookie.png b/demos/declarative/snake/content/pics/cookie.png Binary files differnew file mode 100644 index 0000000..aec2957 --- /dev/null +++ b/demos/declarative/snake/content/pics/cookie.png diff --git a/demos/declarative/snake/content/pics/eyes.svg b/demos/declarative/snake/content/pics/eyes.svg new file mode 100644 index 0000000..1078692 --- /dev/null +++ b/demos/declarative/snake/content/pics/eyes.svg @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + width="40" + height="40" + version="1.0" + sodipodi:docname="eyes.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="/home/ettrich/dev/research/qml-validate/snake/pics/eyes.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs5"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective9" /> + </defs> + <sodipodi:namedview + inkscape:window-height="838" + inkscape:window-width="907" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="12.35" + inkscape:cx="20" + inkscape:cy="20" + inkscape:window-x="117" + inkscape:window-y="45" + inkscape:current-layer="svg2" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path2384" + sodipodi:cx="18.056681" + sodipodi:cy="9.5141697" + sodipodi:rx="7.1255059" + sodipodi:ry="11.295547" + d="M 25.182187,9.5141697 A 7.1255059,11.295547 0 1 1 10.931175,9.5141697 A 7.1255059,11.295547 0 1 1 25.182187,9.5141697 z" + transform="matrix(1.0089865,0,0,0.5462656,-4.9233835,3.3301401)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3158" + sodipodi:cx="18.056681" + sodipodi:cy="9.5141697" + sodipodi:rx="7.1255059" + sodipodi:ry="11.295547" + d="M 25.182187,9.5141697 A 7.1255059,11.295547 0 1 1 10.931175,9.5141697 A 7.1255059,11.295547 0 1 1 25.182187,9.5141697 z" + transform="matrix(1.0089865,0,0,0.5462656,9.6190931,3.3522563)" /> + <path + sodipodi:type="arc" + style="fill:#000000;fill-opacity:1" + id="path3182" + sodipodi:cx="16.275303" + sodipodi:cy="12.307693" + sodipodi:rx="2.2672064" + sodipodi:ry="3.4008098" + d="M 18.542509,12.307693 A 2.2672064,3.4008098 0 0 1 14.008446,12.367372" + sodipodi:start="0" + sodipodi:end="3.1240432" + transform="translate(11.65992,-9.740891)" + sodipodi:open="true" /> + <rect + style="fill:#000000;fill-opacity:0" + id="rect2382" + width="40" + height="40" + x="0" + y="-7.1054274e-15" + inkscape:export-filename="/home/ettrich/dev/research/qml-validate/snake/pics/eyes.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + <path + sodipodi:type="arc" + style="fill:#000000;fill-opacity:1" + id="path2383" + sodipodi:cx="16.275303" + sodipodi:cy="12.307693" + sodipodi:rx="2.2672064" + sodipodi:ry="3.4008098" + d="M 18.542509,12.307693 A 2.2672064,3.4008098 0 0 1 14.008446,12.367372" + sodipodi:start="0" + sodipodi:end="3.1240432" + transform="translate(-3.3200119,-9.821862)" + sodipodi:open="true" /> +</svg> diff --git a/demos/declarative/snake/content/pics/head.png b/demos/declarative/snake/content/pics/head.png Binary files differnew file mode 100644 index 0000000..550e002 --- /dev/null +++ b/demos/declarative/snake/content/pics/head.png diff --git a/demos/declarative/snake/content/pics/head.svg b/demos/declarative/snake/content/pics/head.svg new file mode 100644 index 0000000..3bf0bd2 --- /dev/null +++ b/demos/declarative/snake/content/pics/head.svg @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + width="40" + height="40" + version="1.0" + sodipodi:docname="head.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="/home/ettrich/dev/research/qml-validate/snake/pics/head.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs5"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective9" /> + <inkscape:perspective + id="perspective2444" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 526.18109 : 1" + sodipodi:type="inkscape:persp3d" /> + </defs> + <sodipodi:namedview + inkscape:window-height="838" + inkscape:window-width="907" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="12.35" + inkscape:cx="20" + inkscape:cy="20" + inkscape:window-x="117" + inkscape:window-y="45" + inkscape:current-layer="svg2" /> + <image + y="0.21862352" + x="-0.048582077" + id="image2446" + height="40" + width="40" + sodipodi:absref="/home/ettrich/dev/research/qml-validate/snake/pics/redStone.png" + xlink:href="redStone.png" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path2384" + sodipodi:cx="18.056681" + sodipodi:cy="9.5141697" + sodipodi:rx="7.1255059" + sodipodi:ry="11.295547" + d="M 25.182187,9.5141697 A 7.1255059,11.295547 0 1 1 10.931175,9.5141697 A 7.1255059,11.295547 0 1 1 25.182187,9.5141697 z" + transform="matrix(1.0089865,0,0,0.5462656,-4.9233835,3.3301401)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3158" + sodipodi:cx="18.056681" + sodipodi:cy="9.5141697" + sodipodi:rx="7.1255059" + sodipodi:ry="11.295547" + d="M 25.182187,9.5141697 A 7.1255059,11.295547 0 1 1 10.931175,9.5141697 A 7.1255059,11.295547 0 1 1 25.182187,9.5141697 z" + transform="matrix(1.0089865,0,0,0.5462656,9.6190931,3.3522563)" /> + <path + sodipodi:type="arc" + style="fill:#000000;fill-opacity:1" + id="path3182" + sodipodi:cx="16.275303" + sodipodi:cy="12.307693" + sodipodi:rx="2.2672064" + sodipodi:ry="3.4008098" + d="M 18.542509,12.307693 A 2.2672064,3.4008098 0 0 1 14.008446,12.367372" + sodipodi:start="0" + sodipodi:end="3.1240432" + transform="translate(11.65992,-9.740891)" + sodipodi:open="true" /> + <rect + style="fill:#000000;fill-opacity:0" + id="rect2382" + width="40" + height="40" + x="0" + y="-7.1054274e-15" + inkscape:export-filename="/home/ettrich/dev/research/qml-validate/snake/pics/eyes.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + <path + sodipodi:type="arc" + style="fill:#000000;fill-opacity:1" + id="path2383" + sodipodi:cx="16.275303" + sodipodi:cy="12.307693" + sodipodi:rx="2.2672064" + sodipodi:ry="3.4008098" + d="M 18.542509,12.307693 A 2.2672064,3.4008098 0 0 1 14.008446,12.367372" + sodipodi:start="0" + sodipodi:end="3.1240432" + transform="translate(-3.3200119,-9.821862)" + sodipodi:open="true" /> +</svg> diff --git a/demos/declarative/snake/content/pics/redStar.png b/demos/declarative/snake/content/pics/redStar.png Binary files differnew file mode 100644 index 0000000..cd06854 --- /dev/null +++ b/demos/declarative/snake/content/pics/redStar.png diff --git a/demos/declarative/snake/content/pics/redStone.png b/demos/declarative/snake/content/pics/redStone.png Binary files differnew file mode 100644 index 0000000..9bb7fe4 --- /dev/null +++ b/demos/declarative/snake/content/pics/redStone.png diff --git a/demos/declarative/snake/content/pics/skull.png b/demos/declarative/snake/content/pics/skull.png Binary files differnew file mode 100644 index 0000000..6318616 --- /dev/null +++ b/demos/declarative/snake/content/pics/skull.png diff --git a/demos/declarative/snake/content/pics/snake.jpg b/demos/declarative/snake/content/pics/snake.jpg Binary files differnew file mode 100644 index 0000000..e91a784 --- /dev/null +++ b/demos/declarative/snake/content/pics/snake.jpg diff --git a/demos/declarative/snake/content/pics/star.png b/demos/declarative/snake/content/pics/star.png Binary files differnew file mode 100644 index 0000000..defbde5 --- /dev/null +++ b/demos/declarative/snake/content/pics/star.png diff --git a/demos/declarative/snake/content/pics/stoneShadow.png b/demos/declarative/snake/content/pics/stoneShadow.png Binary files differnew file mode 100644 index 0000000..1bd56af --- /dev/null +++ b/demos/declarative/snake/content/pics/stoneShadow.png diff --git a/demos/declarative/snake/content/pics/yellowStar.png b/demos/declarative/snake/content/pics/yellowStar.png Binary files differnew file mode 100644 index 0000000..52fb9c4 --- /dev/null +++ b/demos/declarative/snake/content/pics/yellowStar.png diff --git a/demos/declarative/snake/content/pics/yellowStone.png b/demos/declarative/snake/content/pics/yellowStone.png Binary files differnew file mode 100644 index 0000000..c56124a --- /dev/null +++ b/demos/declarative/snake/content/pics/yellowStone.png diff --git a/demos/declarative/snake/content/snake.js b/demos/declarative/snake/content/snake.js new file mode 100644 index 0000000..12176c7 --- /dev/null +++ b/demos/declarative/snake/content/snake.js @@ -0,0 +1,308 @@ + +var snake = new Array; +var board = new Array; +var links = new Array; +var scheduledDirections = new Array; +var numRows = 1; +var numColumns = 1; +var linkComponent = createComponent("content/Link.qml"); // XXX should resolve relative to script, not component +var cookieComponent = createComponent("content/Cookie.qml"); +var cookie; +var linksToGrow = 0; +var linksToDie = 0; +var waitForCookie = 0; +var growType = 0; +var skullMovementsBeforeDirectionChange = 0; + + +function rand(n) +{ + return (Math.floor(Math.random() * n)); +} + +function scheduleDirection(dir) +{ + direction = dir; + if(scheduledDirections[scheduledDirections.length-1]!=direction) + scheduledDirections.push(direction); +} + +function startNewGame() +{ + if (state == "starting") + return; + + if (heartbeat.running) { + endGame(); + startNewGameTimer.running = true; + return; + } + numRows = numRowsAvailable; + numColumns = numColumnsAvailable; + board = new Array(numRows * numColumns); + snake = new Array; + scheduledDirections = new Array; + growType = 0; + + skull.z = numRows * numColumns + 1; + + for (var i = 0; i < numRows * numColumns; ++i) { + if (i < links.length) { + var link = links[i]; + link.spawned = false; + link.dying = false; + } else { + if(linkComponent.isReady == false){ + if(linkComponent.isError == true) + print(linkComponent.errorsString()); + else + print("Still loading linkComponent"); + continue;//TODO: Better error handling? + } + var link = linkComponent.createObject(); + link.parent = playfield; + link.z = numRows * numColumns + 1 - i; + link.type = i == 0 ? 2 : 0; + link.spawned = false; + link.dying = false; + links.push(link); + } + } + + head = links[0]; + snake.push(head); + head.row = numRows/2 -1; + head.column = numColumns/2 -1; + head.spawned = true; + + linksToGrow = 5; + linksToDie = 0; + waitForCookie = 5; + score = 0; + startHeartbeatTimer.running = true; + heartbeat.running = true; +} + +function endGame() +{ + heartbeat.running = false; + for(var i in snake) + snake[i].dying = true; + if (cookie) { + cookie.dying = true; + cookie = 0; + } + lastScore = score; + highScores.saveScore(lastScore); +} + +function move() { + + if (!head) + return; + + var dir = direction; + + if (scheduledDirections.length) { + dir = scheduledDirections.shift(); + } + + if (state == "starting") { + var turn = (dir - headDirection); + head.rotation += turn == -3 ? 1 : (turn == 3 ? -1 : turn ); + headDirection = dir; + return; + } + + var row = head.row; + var column = head.column; + + if (dir == 0) { + row = row - 1; + } else if (dir == 1) { + column = column + 1 + } else if (dir == 2) { + row = row + 1; + } else if (dir == 3) { + column = column - 1; + } + + //validate the new position + if (row < 0 || row >= numRows + || column < 0 || column >= numColumns + || (row == skull.row && column == skull.column) + || !isFree(row, column)) { + var turn = (dir - headDirection); + head.rotation += turn == -3 ? 1 : (turn == 3 ? -1 : turn ); + headDirection = dir; + endGame(); + return; + } + + var newLink; + if (linksToGrow > 0) { + --linksToGrow; + newLink = links[snake.length]; + newLink.spawned = false; + newLink.rotation = snake[snake.length-1].rotation; + newLink.type = growType; + newLink.dying = false; + snake.push(newLink); + } else { + var lastLink = snake[snake.length-1]; + board[lastLink.row * numColumns + lastLink.column] = Undefined; + } + + if (waitForCookie > 0) { + if (--waitForCookie == 0) + createCookie(cookie? (cookie.value+1) : 1); + } + + for (var i = snake.length-1; i > 0; --i) { + snake[i].row = snake[i-1].row; + snake[i].column = snake[i-1].column; + snake[i].rotation = snake[i-1].rotation; + } + + if (newLink) { + newLink.spawned = true; + } + + // move the head + head.row = row; + head.column = column; + board[row * numColumns + column] = head; + + var turn = (dir - headDirection); + head.rotation += turn == -3 ? 1 : (turn == 3 ? -1 : turn ); + headDirection = dir; + + var value = testCookie(row, column); + if (value > 0) { + linksToGrow += value; + score += value; + } +} + +function isFree(row, column) +{ + return board[row * numColumns + column] == Undefined; +} + +function isHead(row, column) +{ + return head.column == column && head.row == row; +} + +function testCookie(row, column) +{ + if (cookie && !cookie.dying && cookie.row == row && cookie.column == column) { + var value = cookie.value; + waitForCookie = value; + growType = snake[snake.length-1].type == 1 ? 0 : 1; + cookie.dying = true; + cookie.z = numRows * numColumns + 2; + return value; + } + return 0; +} + +function moveSkull() +{ + + if (linksToDie > 0) { + --linksToDie; + var link = snake.pop(); + link.dying = true; + board[link.row * numColumns + link.column] = Undefined; + if (score > 0) + --score; + if (snake.length == 0) { + endGame(); + return; + } + } + + var row = skull.row; + var column = skull.column; + if (isHead(row, column)) { + endGame(); + return; + } + row += skull.verticalMovement; + column += skull.horizontalMovement; + + var attempts = 4; + + while (skullMovementsBeforeDirectionChange == 0 || row < 0 || row >= numRows + || column < 0 || column >= numColumns + || (!isFree(row, column) && !isHead(row, column))) { + var d = rand(8); + skull.verticalMovement = 0; + skull.horizontalMovement = 0; + skullMovementsBeforeDirectionChange = rand(20)+1; + if (d == 0) { + skull.verticalMovement = -1 + } else if (d == 1) { + skull.horizontalMovement = -1; + } else if (d == 2) { + skull.verticalMovement = 1 + } else if (d == 3){ + skull.horizontalMovement = 1; + } else if (cookie) { + var rd = cookie.row - skull.row; + var rc = cookie.column - skull.column; + if (Math.abs(rd) > Math.abs(rc)) { + skull.verticalMovement = rd > 0 ? 1 : -1; + skullMovementsBeforeDirectionChange = Math.abs(rd); + } else { + skull.horizontalMovement= rc > 0 ? 1 : -1; + skullMovementsBeforeDirectionChange = Math.abs(rc); + } + } + row = skull.row + skull.verticalMovement; + column = skull.column + skull.horizontalMovement; + if (--attempts == 0) + return; + } + + skull.row = row; + skull.column = column; + --skullMovementsBeforeDirectionChange; + var value = testCookie(row, column); + if (value > 0) + linksToDie += value/2; + + if (isHead(row, column)) + endGame(); +} + +function createCookie(value) { + if (numRows * numColumns - snake.length < 10) + return; + + var column = rand(numColumns); + var row = rand(numRows); + while (!isFree(row, column)) { + column++; + if (column == numColumns) { + column = 0; + row++; + if (row == numRows) + row = 0; + } + } + + if(cookieComponent.isReady == false){ + if(cookieComponent.isError == true) + print(cookieComponent.errorsString()); + else + print("Still loading cookieComponent"); + return;//TODO: Better error handling? + } + cookie = cookieComponent.createObject(); + cookie.parent = head.parent; + cookie.value = value; + cookie.row = row; + cookie.column = column; +} diff --git a/demos/declarative/snake/snake.qml b/demos/declarative/snake/snake.qml new file mode 100644 index 0000000..f9d02c7 --- /dev/null +++ b/demos/declarative/snake/snake.qml @@ -0,0 +1,190 @@ +import Qt 4.6 +import "content" + +Rectangle { + id: screen; + SystemPalette { id: activePalette } + color: activePalette.window + + Script { source: "content/snake.js" } + + property int gridSize : 34 + property int margin: 4 + property int numRowsAvailable: Math.floor((height-32-2*margin)/gridSize) + property int numColumnsAvailable: Math.floor((width-2*margin)/gridSize) + + property int lastScore : 0 + + property int score: 0; + property int heartbeatInterval: 200 + property int halfbeatInterval: 160 + + width: 480 + height: 750 + + property int direction + property int headDirection + + property var head; + + HighScoreModel { + id: highScores + game: "Snake" + } + + Timer { + id: heartbeat; + interval: heartbeatInterval; + repeat: true + onTriggered: { move() } + } + Timer { + id: halfbeat; + interval: halfbeatInterval; + repeat: true + running: heartbeat.running + onTriggered: { moveSkull() } + } + Timer { + id: startNewGameTimer; + interval: 700; + onTriggered: {startNewGame(); } + } + + Timer { + id: startHeartbeatTimer; + interval: 1000 ; + } + + + Image { + Image { + id: title + source: "content/pics/snake.jpg" + fillMode: "PreserveAspectCrop" + anchors.fill: parent + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + Behavior on opacity { NumberAnimation { duration: 500 } } + + Text { + color: "white" + font.pointSize: 24 + horizontalAlignment: "AlignHCenter" + text: "Last Score:\t" + lastScore + "\nHighscore:\t" + highScores.topScore; + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: gridSize + } + } + + source: "content/pics/background.png" + fillMode: "PreserveAspectCrop" + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: toolbar.top + + Rectangle { + id: playfield + border.width: 1 + border.color: "white" + color: "transparent" + anchors.horizontalCenter: parent.horizontalCenter + y: (screen.height - 32 - height)/2; + width: numColumnsAvailable * gridSize + 2*margin + height: numRowsAvailable * gridSize + 2*margin + + + Skull { + id: skull + } + + MouseArea { + anchors.fill: parent + onPressed: { + if (!head || !heartbeat.running) { + startNewGame(); + return; + } + if (direction == 0 || direction == 2) + scheduleDirection((mouseX > (head.x + head.width/2)) ? 1 : 3); + else + scheduleDirection((mouseY > (head.y + head.height/2)) ? 2 : 0); + } + } + } + + } + + Rectangle { + id: progressBar + opacity: 0 + Behavior on opacity { NumberAnimation { duration: 200 } } + color: "transparent" + border.width: 2 + border.color: "#221edd" + x: 50 + y: 50 + width: 200 + height: 30 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 40 + + Rectangle { + id: progressIndicator + color: "#221edd"; + width: 0; + Behavior on width { NumberAnimation { duration: startHeartbeatTimer.running ? 1000 : 0}} + height: 30; + } + } + + Rectangle { + id: toolbar + color: activePalette.window + height: 32; width: parent.width + anchors.bottom: screen.bottom + + Button { + id: btnA; text: "New Game"; onClicked: {startNewGame();} + anchors.left: parent.left; anchors.leftMargin: 3 + anchors.verticalCenter: parent.verticalCenter + } + + Text { + color: activePalette.text + text: "Score: " + score; font.bold: true + anchors.right: parent.right; anchors.rightMargin: 3 + anchors.verticalCenter: parent.verticalCenter + } + } + + focus: true + Keys.onSpacePressed: startNewGame(); + Keys.onLeftPressed: if (state == "starting" || direction != 1) scheduleDirection(3); + Keys.onRightPressed: if (state == "starting" || direction != 3) scheduleDirection(1); + Keys.onUpPressed: if (state == "starting" || direction != 2) scheduleDirection(0); + Keys.onDownPressed: if (state == "starting" || direction != 0) scheduleDirection(2); + + states: [ + State { + name: "starting" + when: startHeartbeatTimer.running + PropertyChanges {target: progressIndicator; width: 200} + PropertyChanges {target: title; opacity: 0} + PropertyChanges {target: progressBar; opacity: 1} + }, + State { + name: "running" + when: (heartbeat.running && !startHeartbeatTimer.running) + PropertyChanges {target: progressIndicator; width: 200} + PropertyChanges {target: title; opacity: 0} + PropertyChanges {target: skull; row: 0; column: 0; } + PropertyChanges {target: skull; spawned: 1} + } + ] + +} diff --git a/demos/declarative/twitter/TwitterCore/AuthView.qml b/demos/declarative/twitter/TwitterCore/AuthView.qml new file mode 100644 index 0000000..bcf4646 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/AuthView.qml @@ -0,0 +1,99 @@ +import Qt 4.6 + +Item { + id: wrapper + Column { + anchors.centerIn: parent + spacing: 20 + Column{ + spacing: 4 + Text { + text: "Screen name:" + font.pixelSize: 16; font.bold: true; color: "white"; style: Text.Raised; styleColor: "black" + horizontalAlignment: Qt.AlignRight + } + Item { + width: 220 + height: 28 + BorderImage { source: "images/lineedit.sci"; anchors.fill: parent } + TextInput{ + id: nameIn + width: parent.width - 8 + anchors.centerIn: parent + maximumLength:21 + font.pixelSize: 16; + font.bold: true + color: "#151515"; selectionColor: "green" + KeyNavigation.down: passIn + focus: true + } + } + } + Column{ + spacing: 4 + Text { + text: "Password:" + font.pixelSize: 16; font.bold: true; color: "white"; style: Text.Raised; styleColor: "black" + horizontalAlignment: Qt.AlignRight + } + Item { + width: 220 + height: 28 + BorderImage { source: "images/lineedit.sci"; anchors.fill: parent } + TextInput{ + id: passIn + width: parent.width - 8 + anchors.centerIn: parent + maximumLength:21 + echoMode: TextInput.Password + font.pixelSize: 16; + font.bold: true + color: "#151515"; selectionColor: "green" + KeyNavigation.down: login + KeyNavigation.up: nameIn + } + } + } + Row{ + spacing: 10 + Button { + width: 100 + height: 32 + id: login + keyUsing: true; + function doLogin(){ + rssModel.authName=nameIn.text; + rssModel.authPass=passIn.text; + rssModel.tags='my timeline'; + screen.focus = true; + } + text: "Log in" + KeyNavigation.right: guest + KeyNavigation.up: passIn + Keys.onReturnPressed: login.doLogin(); + Keys.onSelectPressed: login.doLogin(); + Keys.onSpacePressed: login.doLogin(); + onClicked: login.doLogin(); + } + Button { + width: 100 + height: 32 + id: guest + keyUsing: true; + function doGuest() + { + rssModel.authName='-'; + screen.focus = true; + screen.setMode(true); + } + text: "Guest" + KeyNavigation.left: login + KeyNavigation.up: passIn + Keys.onReturnPressed: guest.doGuest(); + Keys.onSelectPressed: guest.doGuest(); + Keys.onSpacePressed: guest.doGuest(); + onClicked: guest.doGuest(); + } + } + } +} diff --git a/demos/declarative/twitter/TwitterCore/Button.qml b/demos/declarative/twitter/TwitterCore/Button.qml new file mode 100644 index 0000000..4cba8c3 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/Button.qml @@ -0,0 +1,49 @@ +import Qt 4.6 + +Item { + id: container + + signal clicked + + property string text + property bool keyUsing: false + + BorderImage { + id: buttonImage + source: "images/toolbutton.sci" + width: container.width; height: container.height + } + BorderImage { + id: pressed + opacity: 0 + source: "images/toolbutton.sci" + width: container.width; height: container.height + } + MouseArea { + id: mouseRegion + anchors.fill: buttonImage + onClicked: { container.clicked(); } + } + Text { + id: btnText + color: if(container.keyUsing){"#DDDDDD";} else {"#FFFFFF";} + anchors.centerIn: buttonImage; font.bold: true + text: container.text; style: Text.Raised; styleColor: "black" + font.pixelSize: 12 + } + states: [ + State { + name: "Pressed" + when: mouseRegion.pressed == true + PropertyChanges { target: pressed; opacity: 1 } + }, + State { + name: "Focused" + when: container.focus == true + PropertyChanges { target: btnText; color: "#FFFFFF" } + } + ] + transitions: Transition { + ColorAnimation { target: btnText; } + } +} diff --git a/demos/declarative/twitter/TwitterCore/FatDelegate.qml b/demos/declarative/twitter/TwitterCore/FatDelegate.qml new file mode 100644 index 0000000..0f013e6 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/FatDelegate.qml @@ -0,0 +1,46 @@ +import Qt 4.6 + +Component { + id: listDelegate + Item { + id: wrapper; width: wrapper.ListView.view.width; height: if(txt.height > 58){txt.height+8}else{58}//50+4+4 + Script { + function handleLink(link){ + if(link.slice(0,3) == 'app'){ + setUser(link.slice(7)); + screen.setMode(true); + }else if(link.slice(0,4) == 'http'){ + Qt.openUrlExternally(link); + } + } + function addTags(str){ + var ret = str.replace(/@[a-zA-Z0-9_]+/g, '<a href="app://$&">$&</a>');//click to jump to user? + var ret2 = ret.replace(/http:\/\/[^ \n\t]+/g, '<a href="$&">$&</a>');//surrounds http links with html link tags + return ret2; + } + } + Item { + id: moveMe; height: parent.height + Rectangle { + id: blackRect + color: "black"; opacity: wrapper.ListView.index % 2 ? 0.2 : 0.3; height: wrapper.height-2; width: wrapper.width; y: 1 + } + Rectangle { + id: whiteRect; x: 6; width: 50; height: 50; color: "white"; smooth: true + anchors.verticalCenter: parent.verticalCenter + + Loading { x: 1; y: 1; width: 48; height: 48; visible: realImage.status != 1 } + Image { id: realImage; source: userImage; x: 1; y: 1; width:48; height:48 } + } + Text { id:txt; y:4; x: 56 + text: '<html><style type="text/css">a:link {color:"#aaccaa"}; a:visited {color:"#336633"}</style>' + + '<a href="app://@'+userScreenName+'"><b>'+userScreenName + "</b></a> from " +source + + "<br /><b>" + addTags(statusText) + "</b></html>"; + textFormat: Qt.RichText + color: "#cccccc"; style: Text.Raised; styleColor: "black"; wrap: true + anchors.left: whiteRect.right; anchors.right: blackRect.right; anchors.leftMargin: 6; anchors.rightMargin: 6 + onLinkActivated: handleLink(link) + } + } + } +} diff --git a/demos/declarative/twitter/TwitterCore/HomeTitleBar.qml b/demos/declarative/twitter/TwitterCore/HomeTitleBar.qml new file mode 100644 index 0000000..a206c87 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/HomeTitleBar.qml @@ -0,0 +1,121 @@ +import Qt 4.6 + +Item { + id: titleBar + + signal update() + onYChanged: state="" //When switching titlebars + + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + Item { + id: container + width: (parent.width * 2) - 55 ; height: parent.height + + Script { + function accept() { + if(rssModel.authName == '' || rssModel.authPass == '') + return false;//Can't login like that + + var postData = "status=" + editor.text; + var postman = new XMLHttpRequest(); + postman.open("POST", "http://twitter.com/statuses/update.xml", true, rssModel.authName, rssModel.authPass); + postman.onreadystatechange = function() { + if (postman.readyState == postman.DONE) { + titleBar.update(); + } + } + postman.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + postman.send(postData); + + editor.text = "" + titleBar.state = "" + } + } + + Rectangle { + x: 6; width: 50; height: 50; color: "white"; smooth: true + anchors.verticalCenter: parent.verticalCenter + + UserModel { user: rssModel.authName; id: userModel } + Component { id: imgDelegate; + Item { + Loading { width:48; height:48; visible: realImage.status != 1 } + Image { source: image; width:48; height:48; id: realImage } + } + } + ListView { model: userModel.model; x:1; y:1; delegate: imgDelegate } + } + + Text { + id: categoryText + anchors.left: parent.left; anchors.right: tagButton.left + anchors.leftMargin: 58; anchors.rightMargin: 10 + anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideLeft + text: "Timeline for " + rssModel.authName + font.pixelSize: 12; font.bold: true; color: "white"; style: Text.Raised; styleColor: "black" + } + + Button { + id: tagButton; x: titleBar.width - 90; width: 85; height: 32; text: "New Post..." + anchors.verticalCenter: parent.verticalCenter; + onClicked: if (titleBar.state == "Posting") accept(); else titleBar.state = "Posting" + } + + Text { + id: charsLeftText; anchors.horizontalCenter: tagButton.horizontalCenter; + anchors.top: tagButton.bottom; anchors.topMargin: 2 + text: {140 - editor.text.length;} visible: titleBar.state == "Posting" + font.pointSize: 10; font.bold: true; color: "white"; style: Text.Raised; styleColor: "black" + } + Item { + id: txtEdit; + anchors.left: tagButton.right; anchors.leftMargin: 5; y: 4 + anchors.right: parent.right; anchors.rightMargin: 40; height: parent.height - 9 + BorderImage { source: "images/lineedit.sci"; anchors.fill: parent } + + Binding {//TODO: Can this be a function, which also resets the cursor? And flashes? + when: editor.text.length > 140 + target: editor + property: "text" + value: editor.text.slice(0,140) + } + TextEdit { + id: editor + anchors.left: parent.left; + anchors.leftMargin: 8; + anchors.bottom: parent.bottom + anchors.bottomMargin: 4; + cursorVisible: true; font.bold: true + width: parent.width - 12 + height: parent.height - 8 + font.pointSize: 10 + wrap: true + color: "#151515"; selectionColor: "green" + } + Keys.forwardTo: [(returnKey), (editor)] + Item { + id: returnKey + Keys.onReturnPressed: accept() + Keys.onEscapePressed: titleBar.state = "" + } + } + } + states: [ + State { + name: "Posting" + PropertyChanges { target: container; x: -tagButton.x + 5 } + PropertyChanges { target: titleBar; height: 80 } + PropertyChanges { target: tagButton; text: "OK" } + PropertyChanges { target: tagButton; width: 28 } + PropertyChanges { target: tagButton; height: 24 } + PropertyChanges { target: txtEdit; focus: true } + } + ] + transitions: [ + Transition { + from: "*"; to: "*" + NumberAnimation { properties: "x,y,width,height"; easing.type: "InOutQuad" } + } + ] +} diff --git a/demos/declarative/twitter/TwitterCore/Loading.qml b/demos/declarative/twitter/TwitterCore/Loading.qml new file mode 100644 index 0000000..76bf64b --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/Loading.qml @@ -0,0 +1,8 @@ +import Qt 4.6 + +Image { + id: loading; source: "images/loading.png"; transformOrigin: "Center" + NumberAnimation on rotation { + from: 0; to: 360; running: loading.visible == true; repeat: true; duration: 900 + } +} diff --git a/demos/declarative/twitter/TwitterCore/MultiTitleBar.qml b/demos/declarative/twitter/TwitterCore/MultiTitleBar.qml new file mode 100644 index 0000000..e0205b8 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/MultiTitleBar.qml @@ -0,0 +1,24 @@ +import Qt 4.6 + +Item { + height: homeBar.height + HomeTitleBar { id: homeBar; width: parent.width; height: 60; + onUpdate: rssModel.reload() + } + TitleBar { id: titleBar; width: parent.width; height: 60; + y: -80 + untaggedString: "Latest tweets from everyone" + taggedString: "Latest tweets from " + } + states: [ + State { + name: "search"; when: screen.userView + PropertyChanges { target: titleBar; y: 0 } + PropertyChanges { target: homeBar; y: -80 } + } + ] + transitions: [ + Transition { NumberAnimation { properties: "x,y"; duration: 500; easing.type: "InOutQuad" } } + ] +} + diff --git a/demos/declarative/twitter/TwitterCore/RssModel.qml b/demos/declarative/twitter/TwitterCore/RssModel.qml new file mode 100644 index 0000000..9d88bb7 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/RssModel.qml @@ -0,0 +1,44 @@ +import Qt 4.6 + +Item { id: wrapper + property var model: xmlModel + property string tags : "" + property string authName : "" + property string authPass : "" + property string mode : "everyone" + property int status: xmlModel.status + function reload() { xmlModel.reload(); } +XmlListModel { + id: xmlModel + + source:{ + if (wrapper.authName == ""){ + ""; //Avoid worthless calls to twitter servers + }else if(wrapper.mode == 'user'){ + "https://"+ ((wrapper.authName!="" && wrapper.authPass!="")? (wrapper.authName+":"+wrapper.authPass+"@") : "" )+"twitter.com/statuses/user_timeline.xml?screen_name="+wrapper.tags; + }else if(wrapper.mode == 'self'){ + "https://"+ ((wrapper.authName!="" && wrapper.authPass!="")? (wrapper.authName+":"+wrapper.authPass+"@") : "" )+"twitter.com/statuses/friends_timeline.xml"; + }else{//everyone/public + "http://twitter.com/statuses/public_timeline.xml"; + } + } + query: "/statuses/status" + + XmlRole { name: "statusText"; query: "text/string()" } + XmlRole { name: "timestamp"; query: "created_at/string()" } + XmlRole { name: "source"; query: "source/string()" } + XmlRole { name: "userName"; query: "user/name/string()" } + XmlRole { name: "userScreenName"; query: "user/screen_name/string()" } + XmlRole { name: "userImage"; query: "user/profile_image_url/string()" } + XmlRole { name: "userLocation"; query: "user/location/string()" } + XmlRole { name: "userDescription"; query: "user/description/string()" } + XmlRole { name: "userFollowers"; query: "user/followers_count/string()" } + XmlRole { name: "userStatuses"; query: "user/statuses_count/string()" } + //TODO: Could also get the user's color scheme, timezone and a few other things +} +Binding { + property: "mode" + target: wrapper + value: {if(wrapper.tags==''){"everyone";}else if(wrapper.tags=='my timeline'){"self";}else{"user";}} +} +} diff --git a/demos/declarative/twitter/TwitterCore/TitleBar.qml b/demos/declarative/twitter/TwitterCore/TitleBar.qml new file mode 100644 index 0000000..149aa82 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/TitleBar.qml @@ -0,0 +1,77 @@ +import Qt 4.6 + +Item { + id: titleBar + property string untaggedString: "Uploads from everyone" + property string taggedString: "Recent uploads tagged " + + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + + Item { + id: container + width: (parent.width * 2) - 55 ; height: parent.height + + Script { + function accept() { + titleBar.state = "" + background.state = "" + rssModel.tags = editor.text + } + } + + Text { + id: categoryText + anchors { + left: parent.left; right: tagButton.left; leftMargin: 10; rightMargin: 10 + verticalCenter: parent.verticalCenter + } + elide: Text.ElideLeft + text: (rssModel.tags=="" ? untaggedString : taggedString + rssModel.tags) + font.bold: true; color: "White"; style: Text.Raised; styleColor: "Black" + font.pixelSize: 12 + } + + Button { + id: tagButton; x: titleBar.width - 50; width: 45; height: 32; text: "..." + onClicked: if (titleBar.state == "Tags") accept(); else titleBar.state = "Tags" + anchors.verticalCenter: parent.verticalCenter + } + + Item { + id: lineEdit + y: 4; height: parent.height - 9 + anchors { left: tagButton.right; leftMargin: 5; right: parent.right; rightMargin: 5 } + + BorderImage { source: "images/lineedit.sci"; anchors.fill: parent } + + TextInput { + id: editor + anchors { + left: parent.left; right: parent.right; leftMargin: 10; rightMargin: 10 + verticalCenter: parent.verticalCenter + } + cursorVisible: true; font.bold: true + color: "#151515"; selectionColor: "Green" + } + + Keys.forwardTo: [ (returnKey), (editor)] + + Item { + id: returnKey + Keys.onReturnPressed: accept() + Keys.onEscapePressed: titleBar.state = "" + } + } + } + + states: State { + name: "Tags" + PropertyChanges { target: container; x: -tagButton.x + 5 } + PropertyChanges { target: tagButton; text: "OK" } + PropertyChanges { target: lineEdit; focus: true } + } + + transitions: Transition { + NumberAnimation { properties: "x"; easing.type: "InOutQuad" } + } +} diff --git a/demos/declarative/twitter/TwitterCore/ToolBar.qml b/demos/declarative/twitter/TwitterCore/ToolBar.qml new file mode 100644 index 0000000..f96c767 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/ToolBar.qml @@ -0,0 +1,24 @@ +import Qt 4.6 + +Item { + id: toolbar + + property alias button1Label: button1.text + property alias button2Label: button2.text + signal button1Clicked + signal button2Clicked + + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + + Button { + id: button1 + anchors.left: parent.left; anchors.leftMargin: 5; y: 3; width: 140; height: 32 + onClicked: toolbar.button1Clicked() + } + + Button { + id: button2 + anchors.right: parent.right; anchors.rightMargin: 5; y: 3; width: 140; height: 32 + onClicked: toolbar.button2Clicked() + } +} diff --git a/demos/declarative/twitter/TwitterCore/UserModel.qml b/demos/declarative/twitter/TwitterCore/UserModel.qml new file mode 100644 index 0000000..c146b84 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/UserModel.qml @@ -0,0 +1,26 @@ +import Qt 4.6 + +//This "model" gets the user information about the searched user. Mainly for the icon. +//Copied from RssModel + +Item { id: wrapper + property var model: xmlModel + property string user : "" + property int status: xmlModel.status + function reload() { xmlModel.reload(); } +XmlListModel { + id: xmlModel + + source: {if(user!="") {"http://twitter.com/users/show.xml?screen_name="+user;}else{"";}} + query: "/user" + + XmlRole { name: "name"; query: "name/string()" } + XmlRole { name: "screenName"; query: "screen_name/string()" } + XmlRole { name: "image"; query: "profile_image_url/string()" } + XmlRole { name: "location"; query: "location/string()" } + XmlRole { name: "description"; query: "description/string()" } + XmlRole { name: "followers"; query: "followers_count/string()" } + //XmlRole { name: "protected"; query: "protected/bool()" } + //TODO: Could also get the user's color scheme, timezone and a few other things +} +} diff --git a/demos/declarative/twitter/TwitterCore/images/gloss.png b/demos/declarative/twitter/TwitterCore/images/gloss.png Binary files differnew file mode 100644 index 0000000..5d370cd --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/gloss.png diff --git a/demos/declarative/twitter/TwitterCore/images/lineedit.png b/demos/declarative/twitter/TwitterCore/images/lineedit.png Binary files differnew file mode 100644 index 0000000..2cc38dc --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/lineedit.png diff --git a/demos/declarative/twitter/TwitterCore/images/lineedit.sci b/demos/declarative/twitter/TwitterCore/images/lineedit.sci new file mode 100644 index 0000000..054bff7 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/lineedit.sci @@ -0,0 +1,5 @@ +border.left: 10 +border.top: 10 +border.bottom: 10 +border.right: 10 +source: lineedit.png diff --git a/demos/declarative/twitter/TwitterCore/images/loading.png b/demos/declarative/twitter/TwitterCore/images/loading.png Binary files differnew file mode 100644 index 0000000..47a1589 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/loading.png diff --git a/demos/declarative/twitter/TwitterCore/images/stripes.png b/demos/declarative/twitter/TwitterCore/images/stripes.png Binary files differnew file mode 100644 index 0000000..9f36727 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/stripes.png diff --git a/demos/declarative/twitter/TwitterCore/images/titlebar.png b/demos/declarative/twitter/TwitterCore/images/titlebar.png Binary files differnew file mode 100644 index 0000000..51c9008 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/titlebar.png diff --git a/demos/declarative/twitter/TwitterCore/images/titlebar.sci b/demos/declarative/twitter/TwitterCore/images/titlebar.sci new file mode 100644 index 0000000..0418d94 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/titlebar.sci @@ -0,0 +1,5 @@ +border.left: 10 +border.top: 12 +border.bottom: 12 +border.right: 10 +source: titlebar.png diff --git a/demos/declarative/twitter/TwitterCore/images/toolbutton.png b/demos/declarative/twitter/TwitterCore/images/toolbutton.png Binary files differnew file mode 100644 index 0000000..1131001 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/toolbutton.png diff --git a/demos/declarative/twitter/TwitterCore/images/toolbutton.sci b/demos/declarative/twitter/TwitterCore/images/toolbutton.sci new file mode 100644 index 0000000..9e4f965 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/images/toolbutton.sci @@ -0,0 +1,5 @@ +border.left: 15 +border.top: 4 +border.bottom: 4 +border.right: 15 +source: toolbutton.png diff --git a/demos/declarative/twitter/TwitterCore/qmldir b/demos/declarative/twitter/TwitterCore/qmldir new file mode 100644 index 0000000..8b56c56 --- /dev/null +++ b/demos/declarative/twitter/TwitterCore/qmldir @@ -0,0 +1,10 @@ +AuthView 1.0 AuthView.qml +Button 1.0 Button.qml +FatDelegate 1.0 FatDelegate.qml +HomeTitleBar 1.0 HomeTitleBar.qml +Loading 1.0 Loading.qml +MultiTitleBar 1.0 MultiTitleBar.qml +TitleBar 1.0 TitleBar.qml +RssModel 1.0 RssModel.qml +UserModel 1.0 UserModel.qml +ToolBar 1.0 ToolBar.qml diff --git a/demos/declarative/twitter/twitter.qml b/demos/declarative/twitter/twitter.qml new file mode 100644 index 0000000..259f79a --- /dev/null +++ b/demos/declarative/twitter/twitter.qml @@ -0,0 +1,95 @@ +import Qt 4.6 +import TwitterCore 1.0 as Twitter + +Item { + id: screen; width: 320; height: 480 + property bool userView : false + property var tmpStr + function setMode(m){ + screen.userView = m; + if(m == false){ + rssModel.tags='my timeline'; + rssModel.reload(); + toolBar.button2Label = "View others"; + } else { + toolBar.button2Label = "Return home"; + } + } + //Workaround for bug 260266 + Timer{ interval: 1; running: false; repeat: false; onTriggered: reallySetUser(); id:hack } + Script { + function setUser(str){hack.running = true; tmpStr = str} + function reallySetUser(){rssModel.tags = tmpStr;} + } + + //TODO: better way to return to the auth screen + Keys.onEscapePressed: rssModel.authName='' + Rectangle { + id: background + anchors.fill: parent; color: "#343434"; + + Image { source: "TwitterCore/images/stripes.png"; fillMode: Image.Tile; anchors.fill: parent; opacity: 0.3 } + + Twitter.RssModel { id: rssModel } + Twitter.Loading { anchors.centerIn: parent; visible: rssModel.status==XmlListModel.Loading && state!='unauthed'} + Text { + width: 180 + text: "Could not access twitter using this screen name and password pair."; + color: "#cccccc"; style: Text.Raised; styleColor: "black"; wrap: true + visible: rssModel.status==XmlListModel.Error; anchors.centerIn: parent + } + + Item { + id: views + x: 2; width: parent.width - 4 + y:60 //Below the title bars + height: 380 + + Twitter.AuthView{ + id: authView + anchors.verticalCenter: parent.verticalCenter + width: parent.width; height: parent.height-60; + x: -(screen.width * 1.5) + } + + Twitter.FatDelegate { id: fatDelegate } + ListView { + id: mainView; model: rssModel.model; delegate: fatDelegate; + width: parent.width; height: parent.height; x: 0; cacheBuffer: 100; + } + } + + Twitter.MultiTitleBar { id: titleBar; width: parent.width } + Twitter.ToolBar { id: toolBar; height: 40; + //anchors.bottom: parent.bottom; + //TODO: Use anchor changes instead of hard coding + y: screen.height - 40 + width: parent.width; opacity: 0.9 + button1Label: "Update" + button2Label: "View others" + onButton1Clicked: rssModel.reload(); + onButton2Clicked: + { + if(screen.userView == true){ + screen.setMode(false); + }else{ + rssModel.tags=''; + screen.setMode(true); + } + } + } + + states: [ + State { + name: "unauthed"; when: rssModel.authName=="" + PropertyChanges { target: authView; x: 0 } + PropertyChanges { target: mainView; x: -(parent.width * 1.5) } + PropertyChanges { target: titleBar; y: -80 } + PropertyChanges { target: toolBar; y: screen.height } + } + ] + transitions: [ + Transition { NumberAnimation { properties: "x,y"; duration: 500; easing.type: "InOutQuad" } } + ] + } +} diff --git a/demos/declarative/webbrowser/content/FlickableWebView.qml b/demos/declarative/webbrowser/content/FlickableWebView.qml new file mode 100644 index 0000000..30a5d78 --- /dev/null +++ b/demos/declarative/webbrowser/content/FlickableWebView.qml @@ -0,0 +1,165 @@ +import Qt 4.6 +import org.webkit 1.0 + +Flickable { + property alias title: webView.title + property alias icon: webView.icon + property alias progress: webView.progress + property alias url: webView.url + property alias back: webView.back + property alias reload: webView.reload + property alias forward: webView.forward + + id: flickable + width: parent.width + contentWidth: Math.max(parent.width,webView.width*webView.scale) + contentHeight: Math.max(parent.height,webView.height*webView.scale) + anchors.top: headerSpace.bottom + anchors.bottom: footer.top + anchors.left: parent.left + anchors.right: parent.right + pressDelay: 200 + + WebView { + id: webView + pixelCacheSize: 4000000 + transformOrigin: Item.TopLeft + + Script { + function fixUrl(url) + { + if (url == "") return url + if (url[0] == "/") return "file://"+url + if (url.indexOf(":")<0) { + if (url.indexOf(".")<0 || url.indexOf(" ")>=0) { + // Fall back to a search engine; hard-code Wikipedia + return "http://en.wikipedia.org/w/index.php?search="+url + } else { + return "http://"+url + } + } + return url + } + } + + url: fixUrl(webBrowser.urlString) + smooth: false // We don't want smooth scaling, since we only scale during (fast) transitions + smoothCache: true // We do want smooth rendering + fillColor: "white" + focus: true + zoomFactor: 4 + + onAlert: console.log(message) + + function doZoom(zoom,centerX,centerY) + { + if (centerX) { + var sc = zoom/contentsScale; + scaleAnim.to = sc; + flickVX.from = flickable.contentX + flickVX.to = Math.max(0,Math.min(centerX-flickable.width/2,webView.width*sc-flickable.width)) + finalX.value = flickVX.to + flickVY.from = flickable.contentY + flickVY.to = Math.max(0,Math.min(centerY-flickable.height/2,webView.height*sc-flickable.height)) + finalY.value = flickVY.to + finalZoom.value = zoom + quickZoom.start() + } + } + + Keys.onLeftPressed: webView.contentsScale -= 0.1 + Keys.onRightPressed: webView.contentsScale += 0.1 + + preferredWidth: flickable.width*zoomFactor + preferredHeight: flickable.height*zoomFactor + contentsScale: 1/zoomFactor + onContentsSizeChanged: { + // zoom out + contentsScale = Math.min(0.25,flickable.width / contentsSize.width) + } + onUrlChanged: { + // got to topleft + flickable.contentX = 0 + flickable.contentY = 0 + if (url != null) { header.editUrl = url.toString(); } + } + onDoubleClick: { + if (!heuristicZoom(clickX,clickY,2.5)) { + var zf = flickable.width / contentsSize.width + if (zf >= contentsScale) + zf = 2.0/zoomFactor // zoom in (else zooming out) + doZoom(zf,clickX*zf,clickY*zf) + } + } + + SequentialAnimation { + id: quickZoom + + PropertyAction { + target: webView + property: "renderingEnabled" + value: false + } + ParallelAnimation { + NumberAnimation { + id: scaleAnim + target: webView + property: "scale" + from: 1 + to: 0 // set before calling + easing.type: "Linear" + duration: 200 + } + NumberAnimation { + id: flickVX + target: flickable + property: "contentX" + easing.type: "Linear" + duration: 200 + from: 0 // set before calling + to: 0 // set before calling + } + NumberAnimation { + id: flickVY + target: flickable + property: "contentY" + easing.type: "Linear" + duration: 200 + from: 0 // set before calling + to: 0 // set before calling + } + } + PropertyAction { + id: finalZoom + target: webView + property: "contentsScale" + } + PropertyAction { + target: webView + property: "scale" + value: 1.0 + } + // Have to set the contentXY, since the above 2 + // size changes may have started a correction if + // contentsScale < 1.0. + PropertyAction { + id: finalX + target: flickable + property: "contentX" + value: 0 // set before calling + } + PropertyAction { + id: finalY + target: flickable + property: "contentY" + value: 0 // set before calling + } + PropertyAction { + target: webView + property: "renderingEnabled" + value: true + } + } + onZoomTo: doZoom(zoom,centerX,centerY) + } +} diff --git a/demos/declarative/webbrowser/content/RectSoftShadow.qml b/demos/declarative/webbrowser/content/RectSoftShadow.qml new file mode 100644 index 0000000..5b6d4ec --- /dev/null +++ b/demos/declarative/webbrowser/content/RectSoftShadow.qml @@ -0,0 +1,32 @@ +import Qt 4.6 + +Item { + BorderImage { + source: "pics/softshadow-left.sci" + x: -16 + y: -16 + width: 16 + height: parent.height+32 + } + BorderImage { + source: "pics/softshadow-right.sci" + x: parent.width + y: -16 + width: 16 + height: parent.height+32 + } + Image { + source: "pics/softshadow-top.png" + x: 0 + y: -16 + width: parent.width + height: 16 + } + Image { + source: "pics/softshadow-bottom.png" + x: 0 + y: parent.height + width: parent.width + height: 16 + } +} diff --git a/demos/declarative/webbrowser/content/RetractingWebBrowserHeader.qml b/demos/declarative/webbrowser/content/RetractingWebBrowserHeader.qml new file mode 100644 index 0000000..94c94f2 --- /dev/null +++ b/demos/declarative/webbrowser/content/RetractingWebBrowserHeader.qml @@ -0,0 +1,115 @@ +import Qt 4.6 + +import "fieldtext" + +Image { + property alias editUrl: editUrl.text + + id: header + source: "pics/header.png" + width: parent.width + height: 60 + x: webView.contentX < 0 ? -webView.contentX : webView.contentX > webView.contentWidth-webView.width + ? -webView.contentX+webView.contentWidth-webView.width : 0 + y: webView.contentY < 0 ? -webView.contentY : progressOff* + (webView.contentY>height?-height:-webView.contentY) + Row { + id: headerTitle + + anchors.top: header.top + anchors.topMargin: 4 + anchors.horizontalCenter: parent.horizontalCenter + spacing: 6 + + Image { + id: headerIcon + pixmap: webView.icon + } + + Text { + id: headerText + + text: webView.title!='' || webView.progress == 1.0 ? webView.title : 'Loading...' + elide: Text.ElideRight + //width: parent.width - headerIcon.width-4 + + color: "white" + styleColor: "black" + style: Text.Raised + + font.family: "Helvetica" + font.pointSize: 10 + font.bold: true + + horizontalAlignment: Text.AlignHCenter + } + } + Item { + width: parent.width + anchors.top: headerTitle.bottom + anchors.topMargin: 2 + anchors.bottom: parent.bottom + + Item { + id: urlBox + height: 31 + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.top: parent.top + clip: true + property bool mouseGrabbed: false + + BorderImage { + source: "pics/addressbar.sci" + anchors.fill: urlBox + } + + BorderImage { + id: urlBoxhl + source: "pics/addressbar-filled.sci" + width: parent.width*webView.progress + height: parent.height + opacity: 1-header.progressOff + clip: true + } + + FieldText { + id: editUrl + mouseGrabbed: parent.mouseGrabbed + + text: webBrowser.urlString + label: "url:" + onConfirmed: { webBrowser.urlString = editUrl.text; webView.focus=true } + onCancelled: { webView.focus=true } + onStartEdit: { webView.focus=false } + + anchors.left: urlBox.left + anchors.right: urlBox.right + anchors.leftMargin: 6 + anchors.verticalCenter: urlBox.verticalCenter + anchors.verticalCenterOffset: 1 + } + } + } + + property real progressOff : 1 + states: [ + State { + name: "ProgressShown" + when: webView.progress < 1.0 + PropertyChanges { target: header; progressOff: 0; } + } + ] + transitions: [ + Transition { + NumberAnimation { + targets: header + properties: "progressOff" + easing.type: "InOutQuad" + duration: 300 + } + } + ] +} diff --git a/demos/declarative/webbrowser/content/fieldtext/FieldText.qml b/demos/declarative/webbrowser/content/fieldtext/FieldText.qml new file mode 100644 index 0000000..d282209 --- /dev/null +++ b/demos/declarative/webbrowser/content/fieldtext/FieldText.qml @@ -0,0 +1,161 @@ +import Qt 4.6 + +Item { + id: fieldText + height: 30 + property string text: "" + property string label: "" + property bool mouseGrabbed: false + signal confirmed + signal cancelled + signal startEdit + + Script { + + function edit() { + if (!mouseGrabbed) { + fieldText.startEdit(); + fieldText.state='editing'; + mouseGrabbed=true; + } + } + + function confirm() { + fieldText.state=''; + fieldText.text = textEdit.text; + mouseGrabbed=false; + fieldText.confirmed(); + } + + function reset() { + textEdit.text = fieldText.text; + fieldText.state=''; + mouseGrabbed=false; + fieldText.cancelled(); + } + + } + + 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 + } + + TextInput { + id: textEdit + text: fieldText.text + focus: false + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + anchors.verticalCenter: parent.verticalCenter + color: "black" + font.bold: true + readOnly: true + onAccepted: confirm() + Keys.onEscapePressed: reset() + } + + Text { + id: textLabel + x: 5 + width: parent.width-10 + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignHCenter + color: fieldText.state == "editing" ? "#505050" : "#AAAAAA" + font.italic: true + font.bold: true + text: label + opacity: textEdit.text == '' ? 1 : 0 + Behavior on opacity { + NumberAnimation { + property: "opacity" + duration: 250 + } + } + } + + MouseArea { + anchors.fill: cancelIcon + onClicked: { reset() } + } + + MouseArea { + anchors.fill: confirmIcon + onClicked: { confirm() } + } + + MouseArea { + id: editRegion + anchors.fill: textEdit + onClicked: { edit() } + } + + states: [ + State { + name: "editing" + PropertyChanges { + target: confirmIcon + opacity: 1 + } + PropertyChanges { + target: cancelIcon + opacity: 1 + } + PropertyChanges { + target: textEdit + color: "black" + readOnly: false + focus: true + selectionStart: 0 + selectionEnd: -1 + } + PropertyChanges { + target: editRegion + opacity: 0 + } + PropertyChanges { + target: textEdit.anchors + leftMargin: 34 + } + PropertyChanges { + target: textEdit.anchors + rightMargin: 34 + } + } + ] + + transitions: [ + Transition { + from: "" + to: "*" + reversible: true + NumberAnimation { + properties: "opacity,leftMargin,rightMargin" + duration: 200 + } + ColorAnimation { + property: "color" + duration: 150 + } + } + ] +} diff --git a/demos/declarative/webbrowser/content/fieldtext/pics/cancel.png b/demos/declarative/webbrowser/content/fieldtext/pics/cancel.png Binary files differnew file mode 100644 index 0000000..ecc9533 --- /dev/null +++ b/demos/declarative/webbrowser/content/fieldtext/pics/cancel.png diff --git a/demos/declarative/webbrowser/content/fieldtext/pics/ok.png b/demos/declarative/webbrowser/content/fieldtext/pics/ok.png Binary files differnew file mode 100644 index 0000000..5795f04 --- /dev/null +++ b/demos/declarative/webbrowser/content/fieldtext/pics/ok.png diff --git a/demos/declarative/webbrowser/content/pics/addressbar-filled.png b/demos/declarative/webbrowser/content/pics/addressbar-filled.png Binary files differnew file mode 100644 index 0000000..d8452ec --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/addressbar-filled.png diff --git a/demos/declarative/webbrowser/content/pics/addressbar-filled.sci b/demos/declarative/webbrowser/content/pics/addressbar-filled.sci new file mode 100644 index 0000000..96c5efb --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/addressbar-filled.sci @@ -0,0 +1,6 @@ +border.left: 7 +border.top: 7 +border.bottom: 7 +border.right: 7 +source: addressbar-filled.png + diff --git a/demos/declarative/webbrowser/content/pics/addressbar.png b/demos/declarative/webbrowser/content/pics/addressbar.png Binary files differnew file mode 100644 index 0000000..3278f58 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/addressbar.png diff --git a/demos/declarative/webbrowser/content/pics/addressbar.sci b/demos/declarative/webbrowser/content/pics/addressbar.sci new file mode 100644 index 0000000..8f1cd18 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/addressbar.sci @@ -0,0 +1,6 @@ +border.left: 7 +border.top: 7 +border.bottom: 7 +border.right: 7 +source: addressbar.png + diff --git a/demos/declarative/webbrowser/content/pics/back-disabled.png b/demos/declarative/webbrowser/content/pics/back-disabled.png Binary files differnew file mode 100644 index 0000000..91b9e76 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/back-disabled.png diff --git a/demos/declarative/webbrowser/content/pics/back.png b/demos/declarative/webbrowser/content/pics/back.png Binary files differnew file mode 100644 index 0000000..9988dd3 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/back.png diff --git a/demos/declarative/webbrowser/content/pics/footer.png b/demos/declarative/webbrowser/content/pics/footer.png Binary files differnew file mode 100644 index 0000000..8391a93 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/footer.png diff --git a/demos/declarative/webbrowser/content/pics/footer.sci b/demos/declarative/webbrowser/content/pics/footer.sci new file mode 100644 index 0000000..7be58f1 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/footer.sci @@ -0,0 +1,6 @@ +border.left: 5 +border.top: 0 +border.bottom: 0 +border.right: 5 +source: footer.png + diff --git a/demos/declarative/webbrowser/content/pics/forward-disabled.png b/demos/declarative/webbrowser/content/pics/forward-disabled.png Binary files differnew file mode 100644 index 0000000..cb87f4f --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/forward-disabled.png diff --git a/demos/declarative/webbrowser/content/pics/forward.png b/demos/declarative/webbrowser/content/pics/forward.png Binary files differnew file mode 100644 index 0000000..83870ee --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/forward.png diff --git a/demos/declarative/webbrowser/content/pics/header.png b/demos/declarative/webbrowser/content/pics/header.png Binary files differnew file mode 100644 index 0000000..26588c3 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/header.png diff --git a/demos/declarative/webbrowser/content/pics/reload.png b/demos/declarative/webbrowser/content/pics/reload.png Binary files differnew file mode 100644 index 0000000..45b5535 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/reload.png diff --git a/demos/declarative/webbrowser/content/pics/softshadow-bottom.png b/demos/declarative/webbrowser/content/pics/softshadow-bottom.png Binary files differnew file mode 100644 index 0000000..85b0b44 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/softshadow-bottom.png diff --git a/demos/declarative/webbrowser/content/pics/softshadow-left.png b/demos/declarative/webbrowser/content/pics/softshadow-left.png Binary files differnew file mode 100644 index 0000000..02926d1 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/softshadow-left.png diff --git a/demos/declarative/webbrowser/content/pics/softshadow-left.sci b/demos/declarative/webbrowser/content/pics/softshadow-left.sci new file mode 100644 index 0000000..45c88d5 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/softshadow-left.sci @@ -0,0 +1,5 @@ +border.left: 0 +border.top: 16 +border.bottom: 16 +border.right: 0 +source: softshadow-left.png diff --git a/demos/declarative/webbrowser/content/pics/softshadow-right.png b/demos/declarative/webbrowser/content/pics/softshadow-right.png Binary files differnew file mode 100644 index 0000000..e459f4f --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/softshadow-right.png diff --git a/demos/declarative/webbrowser/content/pics/softshadow-right.sci b/demos/declarative/webbrowser/content/pics/softshadow-right.sci new file mode 100644 index 0000000..4d459c0 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/softshadow-right.sci @@ -0,0 +1,5 @@ +border.left: 0 +border.top: 16 +border.bottom: 16 +border.right: 0 +source: softshadow-right.png diff --git a/demos/declarative/webbrowser/content/pics/softshadow-top.png b/demos/declarative/webbrowser/content/pics/softshadow-top.png Binary files differnew file mode 100644 index 0000000..9a9e232 --- /dev/null +++ b/demos/declarative/webbrowser/content/pics/softshadow-top.png diff --git a/demos/declarative/webbrowser/webbrowser.qml b/demos/declarative/webbrowser/webbrowser.qml new file mode 100644 index 0000000..b6cccb0 --- /dev/null +++ b/demos/declarative/webbrowser/webbrowser.qml @@ -0,0 +1,170 @@ +import Qt 4.6 +import org.webkit 1.0 + +import "content" + +Item { + id: webBrowser + + property string urlString : "http://qt.nokia.com/" + + width: 640 + height: 480 + + Item { + id: webPanel + anchors.fill: parent + clip: true + Rectangle { + color: "#555555" + anchors.fill: parent + } + Image { + source: "content/pics/softshadow-bottom.png" + width: webPanel.width + height: 16 + } + Image { + source: "content/pics/softshadow-top.png" + width: webPanel.width + height: 16 + anchors.bottom: footer.top + } + RectSoftShadow { + x: -webView.contentX + y: -webView.contentY + width: webView.contentWidth + height: webView.contentHeight+headerSpace.height + } + Item { + id: headerSpace + width: parent.width + height: 60 + z: 1 + + RetractingWebBrowserHeader { id: header } + } + FlickableWebView { + id: webView + width: parent.width + anchors.top: headerSpace.bottom + anchors.bottom: footer.top + anchors.left: parent.left + anchors.right: parent.right + } + BorderImage { + id: footer + source: "content/pics/footer.sci" + width: parent.width + height: 43 + anchors.bottom: parent.bottom + Rectangle { + y: -1 + width: parent.width + height: 1 + color: "#555555" + } + Item { + id: backbutton + width: back_e.width + height: back_e.height + anchors.right: reload.left + anchors.rightMargin: 10 + anchors.verticalCenter: parent.verticalCenter + Image { + id: back_e + source: "content/pics/back.png" + anchors.fill: parent + } + Image { + id: back_d + source: "content/pics/back-disabled.png" + anchors.fill: parent + } + states: [ + State { + name: "Enabled" + when: webView.back.enabled==true + PropertyChanges { target: back_e; opacity: 1 } + PropertyChanges { target: back_d; opacity: 0 } + }, + State { + name: "Disabled" + when: webView.back.enabled==false + PropertyChanges { target: back_e; opacity: 0 } + PropertyChanges { target: back_d; opacity: 1 } + } + ] + transitions: [ + Transition { + NumberAnimation { + properties: "opacity" + easing.type: "InOutQuad" + duration: 300 + } + } + ] + MouseArea { + anchors.fill: back_e + onClicked: { if (webView.back.enabled) webView.back.trigger() } + } + } + Image { + id: reload + source: "content/pics/reload.png" + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + MouseArea { + anchors.fill: reload + onClicked: { webView.reload.trigger() } + } + Item { + id: forwardbutton + width: forward_e.width + height: forward_e.height + anchors.left: reload.right + anchors.leftMargin: 10 + anchors.verticalCenter: parent.verticalCenter + Image { + id: forward_e + source: "content/pics/forward.png" + anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + } + Image { + id: forward_d + source: "content/pics/forward-disabled.png" + anchors.fill: parent + } + states: [ + State { + name: "Enabled" + when: webView.forward.enabled==true + PropertyChanges { target: forward_e; opacity: 1 } + PropertyChanges { target: forward_d; opacity: 0 } + }, + State { + name: "Disabled" + when: webView.forward.enabled==false + PropertyChanges { target: forward_e; opacity: 0 } + PropertyChanges { target: forward_d; opacity: 1 } + } + ] + transitions: [ + Transition { + NumberAnimation { + properties: "opacity" + easing.type: "InOutQuad" + duration: 320 + } + } + ] + MouseArea { + anchors.fill: parent + onClicked: { if (webView.forward.enabled) webView.forward.trigger() } + } + } + } + } +} diff --git a/demos/demos.pro b/demos/demos.pro index 5a9b6db..83e9355 100644 --- a/demos/demos.pro +++ b/demos/demos.pro @@ -38,7 +38,7 @@ wince*: SUBDIRS = \ demos_undo \ demos_sub-attaq -contains(QT_CONFIG, opengl):!contains(QT_CONFIG, opengles1):!contains(QT_CONFIG, opengles1cl):!contains(QT_CONFIG, opengles2):{ +contains(QT_CONFIG, opengl):!contains(QT_CONFIG, opengles1):!contains(QT_CONFIG, opengles2):{ SUBDIRS += demos_boxes } @@ -55,6 +55,8 @@ wince*:SUBDIRS += demos_sqlbrowser } contains(QT_CONFIG, phonon):!static:SUBDIRS += demos_mediaplayer contains(QT_CONFIG, webkit):contains(QT_CONFIG, svg):!symbian:SUBDIRS += demos_browser +contains(QT_CONFIG, multimedia):SUBDIRS += demos_multimedia +contains(QT_CONFIG, declarative):SUBDIRS += demos_declarative # install sources.files = README *.pro @@ -82,6 +84,8 @@ demos_sqlbrowser.subdir = sqlbrowser demos_undo.subdir = undo demos_qtdemo.subdir = qtdemo demos_mediaplayer.subdir = qmediaplayer +demos_multimedia.subdir = multimedia +demos_declarative.subdir = declarative demos_browser.subdir = browser diff --git a/demos/interview/model.cpp b/demos/interview/model.cpp index 3f9548a..840bc60 100644 --- a/demos/interview/model.cpp +++ b/demos/interview/model.cpp @@ -45,6 +45,7 @@ Model::Model(int rows, int columns, QObject *parent) : QAbstractItemModel(parent), + services(QPixmap(":/images/services.png")), rc(rows), cc(columns), tree(new QVector<Node>(rows, Node(0))) { @@ -105,7 +106,6 @@ QVariant Model::data(const QModelIndex &index, int role) const QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const { - static QIcon services(QPixmap(":/images/services.png")); if (role == Qt::DisplayRole) return QString::number(section); if (role == Qt::DecorationRole) diff --git a/demos/interview/model.h b/demos/interview/model.h index bad83a8..c7c15f7 100644 --- a/demos/interview/model.h +++ b/demos/interview/model.h @@ -44,6 +44,7 @@ #include <QAbstractItemModel> #include <QFileIconProvider> +#include <QIcon> #include <QVector> class Model : public QAbstractItemModel @@ -80,6 +81,7 @@ private: Node *parent(Node *child) const; int row(Node *node) const; + QIcon services; int rc, cc; QVector<Node> *tree; QFileIconProvider iconProvider; diff --git a/demos/mainwindow/mainwindow.cpp b/demos/mainwindow/mainwindow.cpp index 32066d7..3ddb74b 100644 --- a/demos/mainwindow/mainwindow.cpp +++ b/demos/mainwindow/mainwindow.cpp @@ -329,7 +329,7 @@ void MainWindow::setupDockWidgets(const QMap<QString, QSize> &customSizeHints) BlueTitleBar *titlebar = new BlueTitleBar(swatch); swatch->setTitleBarWidget(titlebar); connect(swatch, SIGNAL(topLevelChanged(bool)), titlebar, SLOT(updateMask())); - connect(swatch, SIGNAL(featuresChanged(QDockWidget::DockWidgetFeatures)), titlebar, SLOT(updateMask())); + connect(swatch, SIGNAL(featuresChanged(QDockWidget::DockWidgetFeatures)), titlebar, SLOT(updateMask()), Qt::QueuedConnection); #ifdef Q_WS_QWS QPalette pal = palette(); diff --git a/demos/multimedia/multimedia.pro b/demos/multimedia/multimedia.pro new file mode 100644 index 0000000..042650f --- /dev/null +++ b/demos/multimedia/multimedia.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = player + + diff --git a/demos/multimedia/player/main.cpp b/demos/multimedia/player/main.cpp new file mode 100644 index 0000000..87c5b87 --- /dev/null +++ b/demos/multimedia/player/main.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "player.h" + +#include <QtGui> + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + Player player; + player.show(); + + return app.exec(); +}; diff --git a/demos/multimedia/player/player.cpp b/demos/multimedia/player/player.cpp new file mode 100644 index 0000000..af30a97 --- /dev/null +++ b/demos/multimedia/player/player.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "player.h" + +#include "playercontrols.h" +#include "playlistmodel.h" +#include "videowidget.h" + +#include <QtMultimedia/qmediaservice.h> +#include <QtMultimedia/qmediaplaylist.h> + +#include <QtGui> + +Player::Player(QWidget *parent) + : QWidget(parent) + , videoWidget(0) + , coverLabel(0) + , slider(0) + , colorDialog(0) +{ + player = new QMediaPlayer(this); + playlist = new QMediaPlaylist(this); + playlist->setMediaObject(player); + + connect(player, SIGNAL(durationChanged(qint64)), SLOT(durationChanged(qint64))); + connect(player, SIGNAL(positionChanged(qint64)), SLOT(positionChanged(qint64))); + connect(player, SIGNAL(metaDataChanged()), SLOT(metaDataChanged())); + connect(playlist, SIGNAL(currentIndexChanged(int)), SLOT(playlistPositionChanged(int))); + connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + this, SLOT(statusChanged(QMediaPlayer::MediaStatus))); + connect(player, SIGNAL(bufferStatusChanged(int)), this, SLOT(bufferingProgress(int))); + + videoWidget = new VideoWidget; + videoWidget->setMediaObject(player); + + playlistModel = new PlaylistModel(this); + playlistModel->setPlaylist(playlist); + + playlistView = new QListView; + playlistView->setModel(playlistModel); + playlistView->setCurrentIndex(playlistModel->index(playlist->currentIndex(), 0)); + + connect(playlistView, SIGNAL(activated(QModelIndex)), this, SLOT(jump(QModelIndex))); + + playbackModeBox = new QComboBox; + playbackModeBox->addItem(tr("Linear"), + QVariant::fromValue<QMediaPlaylist::PlaybackMode>(QMediaPlaylist::Linear)); + playbackModeBox->addItem(tr("Loop"), + QVariant::fromValue<QMediaPlaylist::PlaybackMode>(QMediaPlaylist::Loop)); + playbackModeBox->addItem(tr("Random"), + QVariant::fromValue<QMediaPlaylist::PlaybackMode>(QMediaPlaylist::Random)); + playbackModeBox->addItem(tr("Current Item Once"), + QVariant::fromValue<QMediaPlaylist::PlaybackMode>(QMediaPlaylist::CurrentItemOnce)); + playbackModeBox->addItem(tr("Current Item In Loop"), + QVariant::fromValue<QMediaPlaylist::PlaybackMode>(QMediaPlaylist::CurrentItemInLoop)); + playbackModeBox->setCurrentIndex(0); + + connect(playbackModeBox, SIGNAL(activated(int)), SLOT(updatePlaybackMode())); + updatePlaybackMode(); + + slider = new QSlider(Qt::Horizontal); + slider->setRange(0, player->duration() / 1000); + + connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(seek(int))); + + QPushButton *openButton = new QPushButton(tr("Open")); + + connect(openButton, SIGNAL(clicked()), this, SLOT(open())); + + PlayerControls *controls = new PlayerControls; + controls->setState(player->state()); + controls->setVolume(player->volume()); + controls->setMuted(controls->isMuted()); + + connect(controls, SIGNAL(play()), player, SLOT(play())); + connect(controls, SIGNAL(pause()), player, SLOT(pause())); + connect(controls, SIGNAL(stop()), player, SLOT(stop())); + connect(controls, SIGNAL(next()), playlist, SLOT(next())); + connect(controls, SIGNAL(previous()), this, SLOT(previousClicked())); + connect(controls, SIGNAL(changeVolume(int)), player, SLOT(setVolume(int))); + connect(controls, SIGNAL(changeMuting(bool)), player, SLOT(setMuted(bool))); + connect(controls, SIGNAL(changeRate(qreal)), player, SLOT(setPlaybackRate(qreal))); + + connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), + controls, SLOT(setState(QMediaPlayer::State))); + connect(player, SIGNAL(volumeChanged(int)), controls, SLOT(setVolume(int))); + connect(player, SIGNAL(mutedChanged(bool)), controls, SLOT(setMuted(bool))); + + QPushButton *fullScreenButton = new QPushButton(tr("FullScreen")); + fullScreenButton->setCheckable(true); + + if (videoWidget != 0) { + connect(fullScreenButton, SIGNAL(clicked(bool)), videoWidget, SLOT(setFullScreen(bool))); + connect(videoWidget, SIGNAL(fullScreenChanged(bool)), + fullScreenButton, SLOT(setChecked(bool))); + } else { + fullScreenButton->setEnabled(false); + } + + QPushButton *colorButton = new QPushButton(tr("Color Options...")); + if (videoWidget) + connect(colorButton, SIGNAL(clicked()), this, SLOT(showColorDialog())); + else + colorButton->setEnabled(false); + + QBoxLayout *playlistLayout = new QVBoxLayout; + playlistLayout->addWidget(playlistView); + playlistLayout->addWidget(playbackModeBox); + + QBoxLayout *displayLayout = new QHBoxLayout; + if (videoWidget) + displayLayout->addWidget(videoWidget, 2); + else + displayLayout->addWidget(coverLabel, 2); + displayLayout->addLayout(playlistLayout); + + QBoxLayout *controlLayout = new QHBoxLayout; + controlLayout->setMargin(0); + controlLayout->addWidget(openButton); + controlLayout->addStretch(1); + controlLayout->addWidget(controls); + controlLayout->addStretch(1); + controlLayout->addWidget(fullScreenButton); + controlLayout->addWidget(colorButton); + + QBoxLayout *layout = new QVBoxLayout; + layout->addLayout(displayLayout); + layout->addWidget(slider); + layout->addLayout(controlLayout); + + setLayout(layout); + + metaDataChanged(); + + QStringList fileNames = qApp->arguments(); + fileNames.removeAt(0); + foreach (QString const &fileName, fileNames) { + if (QFileInfo(fileName).exists()) + playlist->addMedia(QUrl::fromLocalFile(fileName)); + } +} + +Player::~Player() +{ + delete playlist; + delete player; +} + +void Player::open() +{ + QStringList fileNames = QFileDialog::getOpenFileNames(); + foreach (QString const &fileName, fileNames) + playlist->addMedia(QUrl::fromLocalFile(fileName)); +} + +void Player::durationChanged(qint64 duration) +{ + slider->setMaximum(duration / 1000); +} + +void Player::positionChanged(qint64 progress) +{ + slider->setValue(progress / 1000); +} + +void Player::metaDataChanged() +{ + //qDebug() << "update metadata" << player->metaData(QtMultimedia::Title).toString(); + if (player->isMetaDataAvailable()) { + setTrackInfo(QString("%1 - %2") + .arg(player->metaData(QtMultimedia::AlbumArtist).toString()) + .arg(player->metaData(QtMultimedia::Title).toString())); + + if (coverLabel) { + QUrl url = player->metaData(QtMultimedia::CoverArtUrlLarge).value<QUrl>(); + + coverLabel->setPixmap(!url.isEmpty() + ? QPixmap(url.toString()) + : QPixmap()); + } + } +} + +void Player::previousClicked() +{ + // Go to previous track if we are within the first 5 seconds of playback + // Otherwise, seek to the beginning. + if(player->position() <= 5000) + playlist->previous(); + else + player->setPosition(0); +} + +void Player::jump(const QModelIndex &index) +{ + if (index.isValid()) { + playlist->setCurrentIndex(index.row()); + player->play(); + } +} + +void Player::playlistPositionChanged(int currentItem) +{ + playlistView->setCurrentIndex(playlistModel->index(currentItem, 0)); +} + +void Player::seek(int seconds) +{ + player->setPosition(seconds * 1000); +} + +void Player::statusChanged(QMediaPlayer::MediaStatus status) +{ + switch (status) { + case QMediaPlayer::UnknownMediaStatus: + case QMediaPlayer::NoMedia: + case QMediaPlayer::LoadedMedia: + case QMediaPlayer::BufferingMedia: + case QMediaPlayer::BufferedMedia: +#ifndef QT_NO_CURSOR + unsetCursor(); +#endif + setStatusInfo(QString()); + break; + case QMediaPlayer::LoadingMedia: +#ifndef QT_NO_CURSOR + setCursor(QCursor(Qt::BusyCursor)); +#endif + setStatusInfo(tr("Loading...")); + break; + case QMediaPlayer::StalledMedia: +#ifndef QT_NO_CURSOR + setCursor(QCursor(Qt::BusyCursor)); +#endif + break; + case QMediaPlayer::EndOfMedia: +#ifndef QT_NO_CURSOR + unsetCursor(); +#endif + setStatusInfo(QString()); + QApplication::alert(this); + break; + case QMediaPlayer::InvalidMedia: +#ifndef QT_NO_CURSOR + unsetCursor(); +#endif + setStatusInfo(player->errorString()); + break; + } +} + +void Player::bufferingProgress(int progress) +{ + setStatusInfo(tr("Buffering %4%%").arg(progress)); +} + +void Player::setTrackInfo(const QString &info) +{ + trackInfo = info; + + if (!statusInfo.isEmpty()) + setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo)); + else + setWindowTitle(trackInfo); + +} + +void Player::setStatusInfo(const QString &info) +{ + statusInfo = info; + + if (!statusInfo.isEmpty()) + setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo)); + else + setWindowTitle(trackInfo); +} + +void Player::showColorDialog() +{ + if (!colorDialog) { + QSlider *brightnessSlider = new QSlider(Qt::Horizontal); + brightnessSlider->setRange(-100, 100); + brightnessSlider->setValue(videoWidget->brightness()); + connect(brightnessSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setBrightness(int))); + connect(videoWidget, SIGNAL(brightnessChanged(int)), brightnessSlider, SLOT(setValue(int))); + + QSlider *contrastSlider = new QSlider(Qt::Horizontal); + contrastSlider->setRange(-100, 100); + contrastSlider->setValue(videoWidget->contrast()); + connect(contrastSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setContrast(int))); + connect(videoWidget, SIGNAL(contrastChanged(int)), contrastSlider, SLOT(setValue(int))); + + QSlider *hueSlider = new QSlider(Qt::Horizontal); + hueSlider->setRange(-100, 100); + hueSlider->setValue(videoWidget->hue()); + connect(hueSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setHue(int))); + connect(videoWidget, SIGNAL(hueChanged(int)), hueSlider, SLOT(setValue(int))); + + QSlider *saturationSlider = new QSlider(Qt::Horizontal); + saturationSlider->setRange(-100, 100); + saturationSlider->setValue(videoWidget->saturation()); + connect(saturationSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setSaturation(int))); + connect(videoWidget, SIGNAL(saturationChanged(int)), saturationSlider, SLOT(setValue(int))); + + QFormLayout *layout = new QFormLayout; + layout->addRow(tr("Brightness"), brightnessSlider); + layout->addRow(tr("Contrast"), contrastSlider); + layout->addRow(tr("Hue"), hueSlider); + layout->addRow(tr("Saturation"), saturationSlider); + + colorDialog = new QDialog(this); + colorDialog->setWindowTitle(tr("Color Options")); + colorDialog->setLayout(layout); + } + colorDialog->show(); +} + +void Player::updatePlaybackMode() +{ + playlist->setPlaybackMode( + playbackModeBox->itemData(playbackModeBox->currentIndex()).value<QMediaPlaylist::PlaybackMode>()); +} diff --git a/demos/multimedia/player/player.h b/demos/multimedia/player/player.h new file mode 100644 index 0000000..cda3eb9 --- /dev/null +++ b/demos/multimedia/player/player.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLAYER_H +#define PLAYER_H + +#include <QtGui/QWidget> + +#include <qmediaplayer.h> +#include <qmediaplaylist.h> +#include <qvideowidget.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAbstractItemView; +class QLabel; +class QModelIndex; +class QSlider; +class QComboBox; +class QMediaPlayer; +class QVideoWidget; +class PlaylistModel; + +class Player : public QWidget +{ + Q_OBJECT +public: + Player(QWidget *parent = 0); + ~Player(); + +Q_SIGNALS: + void fullScreenChanged(bool fullScreen); + +private slots: + void open(); + void durationChanged(qint64 duration); + void positionChanged(qint64 progress); + void metaDataChanged(); + + void previousClicked(); + + void seek(int seconds); + void jump(const QModelIndex &index); + void playlistPositionChanged(int); + + void statusChanged(QMediaPlayer::MediaStatus status); + void bufferingProgress(int progress); + + void showColorDialog(); + void updatePlaybackMode(); + +private: + void setTrackInfo(const QString &info); + void setStatusInfo(const QString &info); + + QMediaPlayer *player; + QMediaPlaylist *playlist; + QVideoWidget *videoWidget; + QLabel *coverLabel; + QSlider *slider; + QComboBox *playbackModeBox; + PlaylistModel *playlistModel; + QAbstractItemView *playlistView; + QDialog *colorDialog; + QString trackInfo; + QString statusInfo; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/demos/multimedia/player/player.pro b/demos/multimedia/player/player.pro new file mode 100644 index 0000000..dc731e4 --- /dev/null +++ b/demos/multimedia/player/player.pro @@ -0,0 +1,22 @@ +TEMPLATE = app +TARGET = player + +QT += gui multimedia + + +HEADERS = \ + player.h \ + playercontrols.h \ + playlistmodel.h \ + videowidget.h + +SOURCES = \ + main.cpp \ + player.cpp \ + playercontrols.cpp \ + playlistmodel.cpp \ + videowidget.cpp + +target.path = $$[QT_INSTALL_DEMOS]/multimedia/player +INSTALLS += target + diff --git a/demos/multimedia/player/playercontrols.cpp b/demos/multimedia/player/playercontrols.cpp new file mode 100644 index 0000000..3798a71 --- /dev/null +++ b/demos/multimedia/player/playercontrols.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "playercontrols.h" + +#include <QtGui/qboxlayout.h> +#include <QtGui/qslider.h> +#include <QtGui/qstyle.h> +#include <QtGui/qtoolbutton.h> +#include <QtGui/qcombobox.h> + +PlayerControls::PlayerControls(QWidget *parent) + : QWidget(parent) + , playerState(QMediaPlayer::StoppedState) + , playerMuted(false) + , playButton(0) + , stopButton(0) + , nextButton(0) + , previousButton(0) + , muteButton(0) + , volumeSlider(0) + , rateBox(0) +{ + playButton = new QToolButton; + playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay)); + + connect(playButton, SIGNAL(clicked()), this, SLOT(playClicked())); + + stopButton = new QToolButton; + stopButton->setIcon(style()->standardIcon(QStyle::SP_MediaStop)); + stopButton->setEnabled(false); + + connect(stopButton, SIGNAL(clicked()), this, SIGNAL(stop())); + + nextButton = new QToolButton; + nextButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipForward)); + + connect(nextButton, SIGNAL(clicked()), this, SIGNAL(next())); + + previousButton = new QToolButton; + previousButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipBackward)); + + connect(previousButton, SIGNAL(clicked()), this, SIGNAL(previous())); + + muteButton = new QToolButton; + muteButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolume)); + + connect(muteButton, SIGNAL(clicked()), this, SLOT(muteClicked())); + + volumeSlider = new QSlider(Qt::Horizontal); + volumeSlider->setRange(0, 100); + + connect(volumeSlider, SIGNAL(sliderMoved(int)), this, SIGNAL(changeVolume(int))); + + rateBox = new QComboBox; + rateBox->addItem("0.5x", QVariant(0.5)); + rateBox->addItem("1.0x", QVariant(1.0)); + rateBox->addItem("2.0x", QVariant(2.0)); + rateBox->setCurrentIndex(1); + + connect(rateBox, SIGNAL(activated(int)), SLOT(updateRate())); + + QBoxLayout *layout = new QHBoxLayout; + layout->setMargin(0); + layout->addWidget(stopButton); + layout->addWidget(previousButton); + layout->addWidget(playButton); + layout->addWidget(nextButton); + layout->addWidget(muteButton); + layout->addWidget(volumeSlider); + layout->addWidget(rateBox); + setLayout(layout); +} + +QMediaPlayer::State PlayerControls::state() const +{ + return playerState; +} + +void PlayerControls::setState(QMediaPlayer::State state) +{ + if (state != playerState) { + playerState = state; + + switch (state) { + case QMediaPlayer::StoppedState: + stopButton->setEnabled(false); + playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay)); + break; + case QMediaPlayer::PlayingState: + stopButton->setEnabled(true); + playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause)); + break; + case QMediaPlayer::PausedState: + stopButton->setEnabled(true); + playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay)); + break; + } + } +} + +int PlayerControls::volume() const +{ + return volumeSlider->value(); +} + +void PlayerControls::setVolume(int volume) +{ + volumeSlider->setValue(volume); +} + +bool PlayerControls::isMuted() const +{ + return playerMuted; +} + +void PlayerControls::setMuted(bool muted) +{ + if (muted != playerMuted) { + playerMuted = muted; + + muteButton->setIcon(style()->standardIcon(muted + ? QStyle::SP_MediaVolumeMuted + : QStyle::SP_MediaVolume)); + } +} + +void PlayerControls::playClicked() +{ + switch (playerState) { + case QMediaPlayer::StoppedState: + case QMediaPlayer::PausedState: + emit play(); + break; + case QMediaPlayer::PlayingState: + emit pause(); + break; + } +} + +void PlayerControls::muteClicked() +{ + emit changeMuting(!playerMuted); +} + +qreal PlayerControls::playbackRate() const +{ + return rateBox->itemData(rateBox->currentIndex()).toDouble(); +} + +void PlayerControls::setPlaybackRate(float rate) +{ + for (int i=0; i<rateBox->count(); i++) { + if (qFuzzyCompare(rate, float(rateBox->itemData(i).toDouble()))) { + rateBox->setCurrentIndex(i); + return; + } + } + + rateBox->addItem( QString("%1x").arg(rate), QVariant(rate)); + rateBox->setCurrentIndex(rateBox->count()-1); +} + +void PlayerControls::updateRate() +{ + emit changeRate(playbackRate()); +} diff --git a/demos/multimedia/player/playercontrols.h b/demos/multimedia/player/playercontrols.h new file mode 100644 index 0000000..99894ff --- /dev/null +++ b/demos/multimedia/player/playercontrols.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLAYERCONTROLS_H +#define PLAYERCONTROLS_H + +#include <QtMultimedia/qmediaplayer.h> + +#include <QtGui/qwidget.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAbstractButton; +class QAbstractSlider; +class QComboBox; + +class PlayerControls : public QWidget +{ + Q_OBJECT +public: + PlayerControls(QWidget *parent = 0); + + QMediaPlayer::State state() const; + + int volume() const; + bool isMuted() const; + qreal playbackRate() const; + +public slots: + void setState(QMediaPlayer::State state); + void setVolume(int volume); + void setMuted(bool muted); + void setPlaybackRate(float rate); + +signals: + void play(); + void pause(); + void stop(); + void next(); + void previous(); + void changeVolume(int volume); + void changeMuting(bool muting); + void changeRate(qreal rate); + +private slots: + void playClicked(); + void muteClicked(); + void updateRate(); + +private: + QMediaPlayer::State playerState; + bool playerMuted; + QAbstractButton *playButton; + QAbstractButton *stopButton; + QAbstractButton *nextButton; + QAbstractButton *previousButton; + QAbstractButton *muteButton; + QAbstractSlider *volumeSlider; + QComboBox *rateBox; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/demos/multimedia/player/playlistmodel.cpp b/demos/multimedia/player/playlistmodel.cpp new file mode 100644 index 0000000..b60f914 --- /dev/null +++ b/demos/multimedia/player/playlistmodel.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "playlistmodel.h" + +#include <QtCore/qfileinfo.h> +#include <QtCore/qurl.h> + +#include <qmediaplaylist.h> + +PlaylistModel::PlaylistModel(QObject *parent) + : QAbstractItemModel(parent) + , m_playlist(0) +{ +} + +int PlaylistModel::rowCount(const QModelIndex &parent) const +{ + return m_playlist && !parent.isValid() ? m_playlist->mediaCount() : 0; +} + +int PlaylistModel::columnCount(const QModelIndex &parent) const +{ + return !parent.isValid() ? ColumnCount : 0; +} + +QModelIndex PlaylistModel::index(int row, int column, const QModelIndex &parent) const +{ + return m_playlist && !parent.isValid() + && row >= 0 && row < m_playlist->mediaCount() + && column >= 0 && column < ColumnCount + ? createIndex(row, column) + : QModelIndex(); +} + +QModelIndex PlaylistModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + + return QModelIndex(); +} + +QVariant PlaylistModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && role == Qt::DisplayRole) { + QVariant value = m_data[index]; + if (!value.isValid() && index.column() == Title) { + QUrl location = m_playlist->media(index.row()).canonicalUrl(); + return QFileInfo(location.path()).fileName(); + } + + return value; + } + return QVariant(); +} + +QMediaPlaylist *PlaylistModel::playlist() const +{ + return m_playlist; +} + +void PlaylistModel::setPlaylist(QMediaPlaylist *playlist) +{ + if (m_playlist) { + disconnect(m_playlist, SIGNAL(mediaAboutToBeInserted(int,int)), this, SLOT(beginInsertItems(int,int))); + disconnect(m_playlist, SIGNAL(mediaInserted(int,int)), this, SLOT(endInsertItems())); + disconnect(m_playlist, SIGNAL(mediaAboutToBeRemoved(int,int)), this, SLOT(beginRemoveItems(int,int))); + disconnect(m_playlist, SIGNAL(mediaRemoved(int,int)), this, SLOT(endRemoveItems())); + disconnect(m_playlist, SIGNAL(mediaChanged(int,int)), this, SLOT(changeItems(int,int))); + } + + m_playlist = playlist; + + if (m_playlist) { + connect(m_playlist, SIGNAL(mediaAboutToBeInserted(int,int)), this, SLOT(beginInsertItems(int,int))); + connect(m_playlist, SIGNAL(mediaInserted(int,int)), this, SLOT(endInsertItems())); + connect(m_playlist, SIGNAL(mediaAboutToBeRemoved(int,int)), this, SLOT(beginRemoveItems(int,int))); + connect(m_playlist, SIGNAL(mediaRemoved(int,int)), this, SLOT(endRemoveItems())); + connect(m_playlist, SIGNAL(mediaChanged(int,int)), this, SLOT(changeItems(int,int))); + } + + + reset(); +} + +bool PlaylistModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + Q_UNUSED(role); + m_data[index] = value; + emit dataChanged(index, index); + return true; +} + +void PlaylistModel::beginInsertItems(int start, int end) +{ + m_data.clear(); + beginInsertRows(QModelIndex(), start, end); +} + +void PlaylistModel::endInsertItems() +{ + endInsertRows(); +} + +void PlaylistModel::beginRemoveItems(int start, int end) +{ + m_data.clear(); + beginRemoveRows(QModelIndex(), start, end); +} + +void PlaylistModel::endRemoveItems() +{ + endInsertRows(); +} + +void PlaylistModel::changeItems(int start, int end) +{ + m_data.clear(); + emit dataChanged(index(start,0), index(end,ColumnCount)); +} + + diff --git a/demos/multimedia/player/playlistmodel.h b/demos/multimedia/player/playlistmodel.h new file mode 100644 index 0000000..0180282 --- /dev/null +++ b/demos/multimedia/player/playlistmodel.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLAYLISTMODEL_H +#define PLAYLISTMODEL_H + +#include <QtCore/qabstractitemmodel.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMediaPlaylist; + +class PlaylistModel : public QAbstractItemModel +{ + Q_OBJECT +public: + enum Column + { + Title = 0, + ColumnCount + }; + + PlaylistModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + QMediaPlaylist *playlist() const; + void setPlaylist(QMediaPlaylist *playlist); + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole); + +private slots: + void beginInsertItems(int start, int end); + void endInsertItems(); + void beginRemoveItems(int start, int end); + void endRemoveItems(); + void changeItems(int start, int end); + +private: + QMediaPlaylist *m_playlist; + QMap<QModelIndex, QVariant> m_data; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/demos/multimedia/player/videowidget.cpp b/demos/multimedia/player/videowidget.cpp new file mode 100644 index 0000000..3bf36c3 --- /dev/null +++ b/demos/multimedia/player/videowidget.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "videowidget.h" + +#include <QtGui> + +VideoWidget::VideoWidget(QWidget *parent) + : QVideoWidget(parent) +{ + setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); +} + +void VideoWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape && isFullScreen()) { + showNormal(); + + event->accept(); + } else if (event->key() == Qt::Key_Enter && event->modifiers() & Qt::Key_Alt) { + setFullScreen(!isFullScreen()); + + event->accept(); + } else { + QVideoWidget::keyPressEvent(event); + } +} + +void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event) +{ + setFullScreen(!isFullScreen()); + + event->accept(); +} diff --git a/demos/multimedia/player/videowidget.h b/demos/multimedia/player/videowidget.h new file mode 100644 index 0000000..543e1e0 --- /dev/null +++ b/demos/multimedia/player/videowidget.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef VIDEOWIDGET_H +#define VIDEOWIDGET_H + +#include <QtMultimedia/qvideowidget.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class VideoWidget : public QVideoWidget +{ + Q_OBJECT +public: + VideoWidget(QWidget *parent = 0); + +protected: + void keyPressEvent(QKeyEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/demos/qtdemo/qtdemo.pro b/demos/qtdemo/qtdemo.pro index 011ea0c..2a776ac 100644 --- a/demos/qtdemo/qtdemo.pro +++ b/demos/qtdemo/qtdemo.pro @@ -3,8 +3,6 @@ TARGET = qtdemo DEMO_DESTDIR = $$QT_BUILD_TREE isEmpty(DEMO_DESTDIR):DEMO_DESTDIR=../.. DESTDIR = $$DEMO_DESTDIR/bin -OBJECTS_DIR = .obj -MOC_DIR = .moc INSTALLS += target sources diff --git a/demos/spreadsheet/spreadsheet.cpp b/demos/spreadsheet/spreadsheet.cpp index 9693f3c..f2a1738 100644 --- a/demos/spreadsheet/spreadsheet.cpp +++ b/demos/spreadsheet/spreadsheet.cpp @@ -70,6 +70,7 @@ SpreadSheet::SpreadSheet(int rows, int cols, QWidget *parent) updateColor(0); setupMenuBar(); setupContents(); + setupContextMenu(); setCentralWidget(table); statusBar(); |