QML split: Touch, Desktop, Common
2
README
|
@ -14,3 +14,5 @@ Qt Multimedia 5.0.2 http://qt-project.org/
|
||||||
Python 3.3.0 http://python.org/
|
Python 3.3.0 http://python.org/
|
||||||
PyOtherSide 1.0.0 http://thp.io/2011/pyotherside/
|
PyOtherSide 1.0.0 http://thp.io/2011/pyotherside/
|
||||||
gPodder 4.0.0 http://gpodder.org/
|
gPodder 4.0.0 http://gpodder.org/
|
||||||
|
|
||||||
|
On the Desktop: Qt Quick Controls 5.1
|
||||||
|
|
59
common/GPodderCore.qml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gPodder QML UI Reference Implementation
|
||||||
|
* Copyright (c) 2013, 2014, Thomas Perl <m@thp.io>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
import io.thp.pyotherside 1.0
|
||||||
|
|
||||||
|
|
||||||
|
Python {
|
||||||
|
id: py
|
||||||
|
|
||||||
|
property bool ready: false
|
||||||
|
signal downloading(int episode_id)
|
||||||
|
signal downloadProgress(int episode_id, real progress)
|
||||||
|
signal downloaded(int episode_id)
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
setHandler('hello', function (version, copyright) {
|
||||||
|
console.log('gPodder version ' + version + ' starting up');
|
||||||
|
console.log('Copyright: ' + copyright);
|
||||||
|
});
|
||||||
|
|
||||||
|
setHandler('downloading', py.downloading);
|
||||||
|
setHandler('download-progress', py.downloadProgress);
|
||||||
|
setHandler('downloaded', py.downloaded);
|
||||||
|
|
||||||
|
var path = Qt.resolvedUrl('../..').substr('file://'.length);
|
||||||
|
addImportPath(path);
|
||||||
|
|
||||||
|
// Load the Python side of things
|
||||||
|
importModule('main', function() {
|
||||||
|
py.ready = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onReceived: {
|
||||||
|
console.log('unhandled message: ' + data);
|
||||||
|
}
|
||||||
|
|
||||||
|
onError: {
|
||||||
|
console.log('Python failure: ' + traceback);
|
||||||
|
}
|
||||||
|
}
|
1
desktop/common
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../common
|
88
desktop/gpodder.qml
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 1.0
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
|
||||||
|
import 'common'
|
||||||
|
import 'common/util.js' as Util
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
width: 500
|
||||||
|
height: 400
|
||||||
|
|
||||||
|
title: 'gPodder'
|
||||||
|
|
||||||
|
GPodderCore {
|
||||||
|
id: py
|
||||||
|
|
||||||
|
onReadyChanged: {
|
||||||
|
if (ready) {
|
||||||
|
py.call('main.load_podcasts', [], function (podcasts) {
|
||||||
|
Util.updateModelFrom(podcastListModel, podcasts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuBar: MenuBar {
|
||||||
|
Menu { title: 'File'; MenuItem { text: 'Quit' } }
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
width: 200
|
||||||
|
model: ListModel { id: podcastListModel }
|
||||||
|
headerVisible: false
|
||||||
|
alternatingRowColors: false
|
||||||
|
|
||||||
|
rowDelegate: Rectangle {
|
||||||
|
height: 60
|
||||||
|
color: styleData.selected ? '#eee' : '#fff'
|
||||||
|
}
|
||||||
|
|
||||||
|
TableViewColumn {
|
||||||
|
role: 'coverart'
|
||||||
|
title: 'Image'
|
||||||
|
delegate: Item {
|
||||||
|
height: 60
|
||||||
|
width: 60
|
||||||
|
Image {
|
||||||
|
source: styleData.value
|
||||||
|
width: 50
|
||||||
|
height: 50
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 60
|
||||||
|
}
|
||||||
|
|
||||||
|
TableViewColumn {
|
||||||
|
role: 'title'
|
||||||
|
title: 'Podcast'
|
||||||
|
delegate: Item {
|
||||||
|
height: 60
|
||||||
|
Text {
|
||||||
|
text: styleData.value
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentRowChanged: {
|
||||||
|
var id = podcastListModel.get(currentRow).id;
|
||||||
|
py.call('main.load_episodes', [id], function (episodes) {
|
||||||
|
Util.updateModelFrom(episodeListModel, episodes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: ListModel { id: episodeListModel }
|
||||||
|
|
||||||
|
TableViewColumn { role: 'title'; title: 'Title' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
main.py
|
@ -19,7 +19,7 @@
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'gpodder', 'src'))
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'gpodder-core', 'src'))
|
||||||
|
|
||||||
import pyotherside
|
import pyotherside
|
||||||
import gpodder
|
import gpodder
|
||||||
|
|
|
@ -19,9 +19,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import io.thp.pyotherside 1.0
|
|
||||||
|
|
||||||
import 'util.js' as Util
|
import 'common/util.js' as Util
|
||||||
|
|
||||||
SlidePage {
|
SlidePage {
|
||||||
id: episodesPage
|
id: episodesPage
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
|
|
||||||
import 'util.js' as Util
|
import 'common/util.js' as Util
|
||||||
|
|
||||||
SlidePage {
|
SlidePage {
|
||||||
id: freshEpisodes
|
id: freshEpisodes
|
||||||
|
@ -51,7 +51,7 @@ SlidePage {
|
||||||
onClicked: py.call('main.download_episode', [id]);
|
onClicked: py.call('main.download_episode', [id]);
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: pgst
|
target: py
|
||||||
onDownloadProgress: {
|
onDownloadProgress: {
|
||||||
if (episode_id == id) {
|
if (episode_id == id) {
|
||||||
freshEpisodesListModel.setProperty(index, 'progress', progress);
|
freshEpisodesListModel.setProperty(index, 'progress', progress);
|
|
@ -19,11 +19,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import io.thp.pyotherside 1.0
|
import 'common'
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: pgst
|
id: pgst
|
||||||
property bool ready: false
|
|
||||||
|
GPodderCore { id: py }
|
||||||
|
|
||||||
property real scalef: width / 480
|
property real scalef: width / 480
|
||||||
|
|
||||||
|
@ -41,10 +42,6 @@ Item {
|
||||||
children[index-1].opacity = x / width;
|
children[index-1].opacity = x / width;
|
||||||
}
|
}
|
||||||
|
|
||||||
signal downloading(int episode_id)
|
|
||||||
signal downloadProgress(int episode_id, real progress)
|
|
||||||
signal downloaded(int episode_id)
|
|
||||||
|
|
||||||
function loadPage(filename, properties) {
|
function loadPage(filename, properties) {
|
||||||
var component = Qt.createComponent(filename);
|
var component = Qt.createComponent(filename);
|
||||||
if (component.status != Component.Ready) {
|
if (component.status != Component.Ready) {
|
||||||
|
@ -58,50 +55,17 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Python {
|
|
||||||
id: py
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
addImportPath('.');
|
|
||||||
|
|
||||||
setHandler('hello', function (version, copyright) {
|
|
||||||
console.log('gPodder version ' + version + ' starting up');
|
|
||||||
console.log('Copyright: ' + copyright);
|
|
||||||
});
|
|
||||||
|
|
||||||
setHandler('downloading', pgst.downloading);
|
|
||||||
setHandler('download-progress', pgst.downloadProgress);
|
|
||||||
setHandler('downloaded', pgst.downloaded);
|
|
||||||
|
|
||||||
var path = Qt.resolvedUrl('..').substr('file://'.length);
|
|
||||||
addImportPath(path);
|
|
||||||
|
|
||||||
// Load the Python side of things
|
|
||||||
importModule('main', function() {
|
|
||||||
pgst.ready = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onReceived: {
|
|
||||||
console.log('unhandled message: ' + data);
|
|
||||||
}
|
|
||||||
|
|
||||||
onError: {
|
|
||||||
console.log('Python failure: ' + traceback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Player {
|
Player {
|
||||||
id: player
|
id: player
|
||||||
}
|
}
|
||||||
|
|
||||||
PBusyIndicator {
|
PBusyIndicator {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: !pgst.ready
|
visible: !py.ready
|
||||||
}
|
}
|
||||||
|
|
||||||
StartPage {
|
StartPage {
|
||||||
id: startPage
|
id: startPage
|
||||||
visible: pgst.ready
|
visible: py.ready
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
|
|
||||||
import 'util.js' as Util
|
import 'common/util.js' as Util
|
||||||
|
|
||||||
SlidePage {
|
SlidePage {
|
||||||
id: podcastsPage
|
id: podcastsPage
|
|
@ -44,9 +44,9 @@ SlidePage {
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
Connections {
|
Connections {
|
||||||
target: pgst
|
target: py
|
||||||
onReadyChanged: {
|
onReadyChanged: {
|
||||||
if (pgst.ready) {
|
if (py.ready) {
|
||||||
startPage.update_stats();
|
startPage.update_stats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,9 +229,9 @@ SlidePage {
|
||||||
spacing: 20 * pgst.scalef
|
spacing: 20 * pgst.scalef
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: pgst
|
target: py
|
||||||
onReadyChanged: {
|
onReadyChanged: {
|
||||||
if (pgst.ready) {
|
if (py.ready) {
|
||||||
py.call('main.load_podcasts', [], function (podcasts) {
|
py.call('main.load_podcasts', [], function (podcasts) {
|
||||||
recommendationsRepeater.model = podcasts.splice(0, 4);
|
recommendationsRepeater.model = podcasts.splice(0, 4);
|
||||||
});
|
});
|
1
touch/common
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../common
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import 'qml'
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: '#336688'
|
color: '#336688'
|
||||||
|
@ -28,12 +27,12 @@ Rectangle {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: 'qml/images/mask.png'
|
source: 'images/mask.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: 'qml/images/noise.png'
|
source: 'images/noise.png'
|
||||||
fillMode: Image.Tile
|
fillMode: Image.Tile
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 394 B |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 162 B After Width: | Height: | Size: 162 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |