diff --git a/common/GPodderCore.qml b/common/GPodderCore.qml index a5983f3..7920d32 100644 --- a/common/GPodderCore.qml +++ b/common/GPodderCore.qml @@ -77,6 +77,16 @@ Python { }); } + function setConfig(key, value) { + py.call('main.set_config_value', [key, value]); + } + + function getConfig(key, callback) { + py.call('main.get_config_value', [key], function (result) { + callback(result); + }); + } + onReceived: { console.log('unhandled message: ' + data); } diff --git a/common/GPodderEpisodeListModel.qml b/common/GPodderEpisodeListModel.qml index f8e632f..a51b540 100644 --- a/common/GPodderEpisodeListModel.qml +++ b/common/GPodderEpisodeListModel.qml @@ -62,14 +62,6 @@ ListModel { }); } - onPodcast_idChanged: { - reload(); - } - - property var worker: ModelWorkerScript { - id: modelWorker - } - function forEachEpisode(callback) { // Go from bottom up (= chronological order) for (var i=count-1; i>=0; i--) { @@ -130,12 +122,11 @@ ListModel { ready = false; py.call('main.load_episodes', [podcast_id, query], function (episodes) { - modelWorker.updateModelFrom(episodeListModel, episodes, function () { - episodeListModel.ready = true; - if (callback !== undefined) { - callback(); - } - }); + Util.updateModelFrom(episodeListModel, episodes); + episodeListModel.ready = true; + if (callback !== undefined) { + callback(); + } }); } } diff --git a/common/GPodderEpisodeListModelConnections.qml b/common/GPodderEpisodeListModelConnections.qml index 22806ad..9744dab 100644 --- a/common/GPodderEpisodeListModelConnections.qml +++ b/common/GPodderEpisodeListModelConnections.qml @@ -26,11 +26,11 @@ Connections { target: py onDownloadProgress: { - episodeListModel.worker.updateModelWith(episodeListModel, 'id', episode_id, + Util.updateModelWith(episodeListModel, 'id', episode_id, {'progress': progress}); } onPlaybackProgress: { - episodeListModel.worker.updateModelWith(episodeListModel, 'id', episode_id, + Util.updateModelWith(episodeListModel, 'id', episode_id, {'playbackProgress': progress}); } onUpdatedEpisode: { diff --git a/common/GPodderPlatform.qml b/common/GPodderPlatform.qml index 3fe70b1..57fb97d 100644 --- a/common/GPodderPlatform.qml +++ b/common/GPodderPlatform.qml @@ -26,9 +26,11 @@ Item { property bool android: (typeof(gpodderAndroid) !== 'undefined') || emulatingAndroid property bool needsBackButton: !android - property bool toolbarOnTop: android + + property bool toolbarOnTop: true property bool invertedToolbar: toolbarOnTop property bool titleInToolbar: toolbarOnTop - property bool floatingPlayButton: android - property bool hideDisabledMenu: android + + property bool floatingPlayButton: true + property bool hideDisabledMenu: true } diff --git a/common/GPodderPodcastListModel.qml b/common/GPodderPodcastListModel.qml index aa17c2f..285bf16 100644 --- a/common/GPodderPodcastListModel.qml +++ b/common/GPodderPodcastListModel.qml @@ -25,13 +25,9 @@ import 'util.js' as Util ListModel { id: podcastListModel - property var worker: ModelWorkerScript { - id: modelWorker - } - function reload() { py.call('main.load_podcasts', [], function (podcasts) { - modelWorker.updateModelFrom(podcastListModel, podcasts); + Util.updateModelFrom(podcastListModel, podcasts); }); } } diff --git a/common/ModelWorkerScript.qml b/common/ModelWorkerScript.qml deleted file mode 100644 index 19c0057..0000000 --- a/common/ModelWorkerScript.qml +++ /dev/null @@ -1,67 +0,0 @@ - -/** - * - * gPodder QML UI Reference Implementation - * Copyright (c) 2015, Thomas Perl - * - * 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 - - -WorkerScript { - source: 'modelworker.js' - - property int callbacks_seq: 1 - property var callbacks: ({}) - - function refCallback(callback) { - var id = callbacks_seq++; - callbacks[id] = callback; - return id; - } - - function unrefCallback(callback) { - var result = callbacks[callback]; - delete callbacks[callback]; - return result; - } - - function updateModelFrom(model, data, callback) { - sendMessage({ - action: 'updateModelFrom', - model: model, - data: data, - callback: refCallback(callback), - }); - } - - function updateModelWith(model, key, value, update, callback) { - sendMessage({ - action: 'updateModelWith', - model: model, - key: key, - value: value, - update: update, - callback: refCallback(callback), - }); - } - - onMessage: { - if (messageObject.callback !== undefined) { - unrefCallback(messageObject.callback)(); - } - } -} diff --git a/common/modelworker.js b/common/modelworker.js deleted file mode 100644 index 44561bd..0000000 --- a/common/modelworker.js +++ /dev/null @@ -1,42 +0,0 @@ -function updateModelFrom(model, data) { - // TODO: This is very naive at the moment, we should do proper remove(), - // move(), set() and insert() calls, so that the UI can animate changes. - for (var i=0; i data.length) { - model.remove(model.count-1); - } - - model.sync(); -} - -function updateModelWith(model, key, value, update) { - for (var row=0; row data.length) { + model.remove(model.count-1); + } +} + +function updateModelWith(model, key, value, update) { + for (var row=0; row 0: - yield '%02d:%02d:%02d' % (episode.total_time / (60 * 60), (episode.total_time / 60) % 60, episode.total_time % 60) + yield '%02d:%02d:%02d' % (episode.total_time / (60 * 60), + (episode.total_time / 60) % 60, + episode.total_time % 60) def show_podcast(self, podcast_id): podcast = self._get_podcast_by_id(podcast_id) @@ -404,8 +423,7 @@ class gPotherSide: return [{ 'label': provider.name, 'can_search': provider.kind == provider.PROVIDER_SEARCH - } for provider in sorted(registry.directory.select(select_provider), - key=provider_sort_key, reverse=True)] + } for provider in sorted(registry.directory.select(select_provider), key=provider_sort_key, reverse=True)] def get_directory_entries(self, provider, query): def match_provider(p): @@ -422,6 +440,116 @@ class gPotherSide: return [] + +PILL_TEMPLATE = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {left_text} + {left_text} + + + + + + + {right_text} + {right_text} + + + +""" + + +class PillExpression(object): + def __init__(self, **kwargs): + self.kwargs = kwargs + + def __call__(self, matchobj): + return str(eval(matchobj.group(1), self.kwargs)) + + +def pill_image_provider(image_id, requested_size): + left_text, right_text = (int(x) for x in image_id.split('/')) + + width = 44 + height = 24 + radius = 6 + font_size = 13 + + text_lx = width / 4 + text_rx = width * 3 / 4 + + charheight = font_size + charwidth = font_size / 1.3 + + if left_text: + text_lx -= charwidth * len(str(left_text)) / 2 + if right_text: + text_rx -= charwidth * len(str(right_text)) / 2 + + outer_style = 'stroke: #333333; stroke-width: 1; fill-opacity: 0; stroke-opacity: 0.6;' + inner_style = 'stroke: #ffffff; stroke-width: 1; fill-opacity: 0; stroke-opacity: 0.3;' + + expression = PillExpression(height=height, width=width, left_text=left_text, + right_text=right_text, radius=radius, + lx=text_lx, rx=text_rx, font_size=font_size, + outer_style=outer_style, inner_style=inner_style) + svg = re.sub(r'[{]([^}]+)[}]', expression, PILL_TEMPLATE) + return bytearray(svg.encode('utf-8')), (width, height), pyotherside.format_data + + +@pyotherside.set_image_provider +def gpotherside_image_provider(image_id, requested_size): + provider, args = image_id.split('/', 1) + if provider == 'pill': + return pill_image_provider(args, requested_size) + + raise ValueError('Unknown provider: %s' % (provider,)) + + gpotherside = gPotherSide() pyotherside.atexit(gpotherside.atexit) @@ -433,6 +561,7 @@ load_podcasts = gpotherside.load_podcasts load_episodes = gpotherside.load_episodes show_episode = gpotherside.show_episode play_episode = gpotherside.play_episode +import_opml = gpotherside.import_opml subscribe = gpotherside.subscribe unsubscribe = gpotherside.unsubscribe check_for_episodes = gpotherside.check_for_episodes diff --git a/makefile b/makefile index 27a4fd0..0d05e9a 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ PROJECT := gpodder-ui-qml -VERSION := 4.4.0 +VERSION := 4.6.0 all: @echo "" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..68859ad --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[pep8] +max-line-length = 120 diff --git a/touch/AboutPage.qml b/touch/AboutPage.qml index fa62f11..36944a4 100644 --- a/touch/AboutPage.qml +++ b/touch/AboutPage.qml @@ -73,7 +73,7 @@ SlidePage { anchors.horizontalCenter: parent.horizontalCenter wrapMode: Text.WordWrap text: [ - '© 2005-2014 Thomas Perl and the gPodder Team', + '© 2005-2015 Thomas Perl and the gPodder Team', 'License: ISC / GPLv3 or later', 'Website: http://gpodder.org/', '', diff --git a/touch/Dragging.qml b/touch/Dragging.qml index 4225681..ed9f72d 100644 --- a/touch/Dragging.qml +++ b/touch/Dragging.qml @@ -31,8 +31,8 @@ MouseArea { drag { target: parent axis: Drag.XAxis - minimumX: parent.leftDragLimit - maximumX: canClose ? pgst.width : 0 + minimumX: 0 + maximumX: canClose ? parent.width : 0 filterChildren: true } @@ -44,7 +44,7 @@ MouseArea { if (pressed) { dragging.stacking.stopAllAnimations(); } else { - if (parent.x > pgst.width * 3 / 4) { + if (parent.x > parent.width / 3) { dragging.stacking.startFadeOut(); } else { dragging.stacking.fadeInAgain(); diff --git a/touch/EpisodeDetail.qml b/touch/EpisodeDetail.qml index 2b5a0f9..536ed23 100644 --- a/touch/EpisodeDetail.qml +++ b/touch/EpisodeDetail.qml @@ -27,8 +27,6 @@ import 'icons/icons.js' as Icons SlidePage { id: detailPage - width: pgst.width / 3 - property int episode_id property string title property string link @@ -45,7 +43,7 @@ SlidePage { visible: !detailPage.ready } - onEpisode_idChanged: { + Component.onCompleted: { py.call('main.show_episode', [episode_id], function (episode) { detailPage.title = episode.title; descriptionLabel.text = episode.description; diff --git a/touch/EpisodeItem.qml b/touch/EpisodeItem.qml index 8e28d15..8a8a55e 100644 --- a/touch/EpisodeItem.qml +++ b/touch/EpisodeItem.qml @@ -135,22 +135,22 @@ Item { Rectangle { anchors { top: parent.top - left: downloadIndicator.right + left: parent.left } height: Constants.layout.padding * pgst.scalef - width: (parent.width - downloadIndicator.width) * progress + width: parent.width * progress color: Constants.colors.download } Rectangle { anchors { bottom: parent.bottom - left: downloadIndicator.right + left: parent.left } height: Constants.layout.padding * pgst.scalef - width: (parent.width - downloadIndicator.width) * playbackProgress + width: parent.width * playbackProgress color: titleLabel.color opacity: episodeItem.isPlaying ? 1 : .2 } @@ -163,27 +163,17 @@ Item { right: parent.right } - Rectangle { + RectangleIndicator { id: downloadIndicator - - width: Constants.layout.padding * pgst.scalef * (downloadState == Constants.state.downloaded) - - Behavior on width { PropertyAnimation { } } - - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - } - + enabled: downloadState == Constants.state.downloaded color: titleLabel.color } Column { anchors { left: parent.left - leftMargin: 2 * Constants.layout.padding * pgst.scalef - right: parent.right + leftMargin: Constants.layout.padding * pgst.scalef + right: downloadIndicator.left rightMargin: Constants.layout.padding * pgst.scalef verticalCenter: parent.verticalCenter } diff --git a/touch/EpisodeListView.qml b/touch/EpisodeListView.qml index 3430ac9..4fd0088 100644 --- a/touch/EpisodeListView.qml +++ b/touch/EpisodeListView.qml @@ -27,14 +27,9 @@ import 'common/util.js' as Util PListView { id: episodeList - property alias podcast_id: episodeListModel.podcast_id property int selectedIndex: -1 - onPodcast_idChanged: { - selectedIndex = -1; - } - PScrollIntoView { id: scrollIntoView } onSelectedIndexChanged: { diff --git a/touch/EpisodeQueryPage.qml b/touch/EpisodeQueryPage.qml index ebd8d01..bf8f423 100644 --- a/touch/EpisodeQueryPage.qml +++ b/touch/EpisodeQueryPage.qml @@ -44,6 +44,10 @@ SlidePage { title: 'Episodes' section.property: 'section' - section.delegate: SectionHeader { text: section } + section.delegate: SectionHeader { + text: section + color: episodeList.selectedIndex === -1 ? Constants.colors.secondaryHighlight : Constants.colors.text + opacity: episodeList.selectedIndex === -1 ? 1 : 0.2 + } } } diff --git a/touch/EpisodesPage.qml b/touch/EpisodesPage.qml index a4ca5b9..dc046c2 100644 --- a/touch/EpisodesPage.qml +++ b/touch/EpisodesPage.qml @@ -28,8 +28,6 @@ import 'icons/icons.js' as Icons SlidePage { id: page - width: pgst.width / 3 - property int podcast_id property string title @@ -82,6 +80,12 @@ SlidePage { ], undefined, undefined, true); } + + Component.onCompleted: { + episodeList.model.podcast_id = podcast_id; + // List model will be loaded automatically on load + } + EpisodeQueryControl { id: queryControl model: episodeList.model @@ -91,6 +95,5 @@ SlidePage { EpisodeListView { id: episodeList title: page.title - podcast_id: page.podcast_id } } diff --git a/touch/IconContextMenu.qml b/touch/IconContextMenu.qml index 68711ea..7703956 100644 --- a/touch/IconContextMenu.qml +++ b/touch/IconContextMenu.qml @@ -20,6 +20,8 @@ import QtQuick 2.0 +import 'common/constants.js' as Constants + Item { id: contextMenu default property alias children: contextMenuRow.children @@ -27,6 +29,10 @@ Item { Row { id: contextMenuRow - anchors.centerIn: parent + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + margins: Constants.layout.padding * pgst.scalef + } } } diff --git a/touch/Main.qml b/touch/Main.qml index c55ebea..9d4f850 100644 --- a/touch/Main.qml +++ b/touch/Main.qml @@ -68,7 +68,7 @@ Item { // Initial focus focus: true - property real scalef: 1.0 //(width < height) ? (width / 480) : (height / 480) + property real scalef: (width < height) ? (width / 480) : (height / 480) property int shorterSide: (width < height) ? width : height property int dialogsVisible: 0 @@ -84,9 +84,9 @@ Item { } if (page.isDialog) { - //children[index-1].opacity = 1; + children[index-1].opacity = 1; } else { - //children[index-1].opacity = x / width; + children[index-1].opacity = x / width; } //children[index-1].pushPhase = x / width; @@ -115,26 +115,6 @@ Item { } } - onChildrenChanged: resizePages() - - function resizePages() { - var pages = []; - for (var i=0; i width) { + x = width; + } else if (x < 0) { + x = 0; + } + var v = (max - min) * (x / width); + if (slider.step > 0.0) { + v = slider.step * parseInt(0.5 + v / slider.step); + } + slider.temporaryValue = min + v; + return slider.temporaryValue; } + onClicked: slider.valueChangeRequested(updateValue(mouse.x)); + onPressed: updateValue(mouse.x); + onPositionChanged: updateValue(mouse.x); preventStealing: true } @@ -68,4 +78,14 @@ Item { verticalCenter: parent.verticalCenter } } + + Repeater { + model: slider.step ? ((slider.max - slider.min) / slider.step - 1) : 0 + delegate: Rectangle { + width: Math.max(1, 1 * pgst.scalef) + height: parent.height + color: Constants.colors.area + x: parent.width * ((slider.step * (1 + index)) / (slider.max - slider.min)); + } + } } diff --git a/touch/PTextField.qml b/touch/PTextField.qml index 660d630..6c33d2e 100644 --- a/touch/PTextField.qml +++ b/touch/PTextField.qml @@ -50,6 +50,7 @@ Item { right: clipboardIcon.left margins: 5 * pgst.scalef } + clip: true inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText diff --git a/touch/PodcastDetail.qml b/touch/PodcastDetail.qml index b710e39..3f3a594 100644 --- a/touch/PodcastDetail.qml +++ b/touch/PodcastDetail.qml @@ -27,8 +27,6 @@ import 'icons/icons.js' as Icons SlidePage { id: page - width: pgst.width / 3 - property int podcast_id property string title property string description diff --git a/touch/PodcastItem.qml b/touch/PodcastItem.qml index 91c15d5..3eb80fd 100644 --- a/touch/PodcastItem.qml +++ b/touch/PodcastItem.qml @@ -33,19 +33,6 @@ ButtonArea { right: parent.right } - Rectangle { - width: Constants.layout.padding * pgst.scalef * (newEpisodes > 0) - Behavior on width { PropertyAnimation { } } - - anchors { - top: cover.top - bottom: cover.bottom - left: parent.left - } - - color: Constants.colors.fresh - } - CoverArt { id: cover visible: !updating @@ -85,7 +72,7 @@ ButtonArea { PLabel { id: downloadsLabel anchors { - right: parent.right + right: newEpisodesIndicator.enabled ? newEpisodesIndicator.left : parent.right rightMargin: Constants.layout.padding * pgst.scalef verticalCenter: parent.verticalCenter } @@ -93,4 +80,10 @@ ButtonArea { text: downloaded ? downloaded : '' color: Constants.colors.text } + + RectangleIndicator { + id: newEpisodesIndicator + enabled: newEpisodes > 0 + color: Constants.colors.fresh + } } diff --git a/touch/PodcastsPage.qml b/touch/PodcastsPage.qml index 2d977a3..6cdab93 100644 --- a/touch/PodcastsPage.qml +++ b/touch/PodcastsPage.qml @@ -28,8 +28,6 @@ import 'common/constants.js' as Constants SlidePage { id: page - width: pgst.width / 3 - canClose: false hasMenuButton: true @@ -49,9 +47,9 @@ SlidePage { } }, { - label: 'About', + label: 'Settings', callback: function () { - pgst.loadPage('AboutPage.qml'); + pgst.loadPage('SettingsPage.qml'); }, }, { @@ -68,6 +66,20 @@ SlidePage { }); }, }, + { + label: 'Add from OPML', + callback: function () { + var ctx = { py: py }; + pgst.loadPage('TextInputDialog.qml', { + buttonText: 'Subscribe', + placeholderText: 'OPML URL', + pasteOnLoad: true, + callback: function (url) { + ctx.py.call('main.import_opml', [url]); + } + }); + }, + }, { label: 'Discover new podcasts', callback: function () { diff --git a/touch/RectangleIndicator.qml b/touch/RectangleIndicator.qml new file mode 100644 index 0000000..08adccd --- /dev/null +++ b/touch/RectangleIndicator.qml @@ -0,0 +1,36 @@ + +/** + * + * gPodder QML UI Reference Implementation + * Copyright (c) 2015, Thomas Perl + * + * 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 'common/constants.js' as Constants + +Rectangle { + width: Constants.layout.padding * 2 * pgst.scalef * enabled + height: width + + Behavior on width { PropertyAnimation { } } + + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + margins: Constants.layout.padding * 2 * pgst.scalef - width / 2 + } +} diff --git a/touch/SectionHeader.qml b/touch/SectionHeader.qml index 55234f6..7ce75e0 100644 --- a/touch/SectionHeader.qml +++ b/touch/SectionHeader.qml @@ -24,15 +24,29 @@ import 'common/constants.js' as Constants Item { property alias text: pLabel.text + property alias color: pLabel.color height: 70 * pgst.scalef + width: parent.width + + Rectangle { + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: pLabel.left + margins: Constants.layout.padding * pgst.scalef + } + + color: pLabel.color + height: 1 * pgst.scalef + } PLabel { id: pLabel anchors { - left: parent.left - bottom: parent.bottom - margins: 10 * pgst.scalef + right: parent.right + verticalCenter: parent.verticalCenter + margins: Constants.layout.padding * pgst.scalef } color: Constants.colors.secondaryHighlight diff --git a/touch/SettingsLabel.qml b/touch/SettingsLabel.qml new file mode 100644 index 0000000..643189e --- /dev/null +++ b/touch/SettingsLabel.qml @@ -0,0 +1,32 @@ + +/** + * + * gPodder QML UI Reference Implementation + * Copyright (c) 2015, Thomas Perl + * + * 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 'common/constants.js' as Constants + +PLabel { + anchors { + left: parent.left + right: parent.right + margins: Constants.layout.padding * pgst.scalef + } + color: Constants.colors.secondaryHighlight +} diff --git a/touch/SettingsPage.qml b/touch/SettingsPage.qml new file mode 100644 index 0000000..8d47886 --- /dev/null +++ b/touch/SettingsPage.qml @@ -0,0 +1,113 @@ + +/** + * + * gPodder QML UI Reference Implementation + * Copyright (c) 2015, Thomas Perl + * + * 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 'common/constants.js' as Constants + +SlidePage { + id: page + + Component.onCompleted: { + py.getConfig('plugins.youtube.api_key_v3', function (value) { + youtube_api_key_v3.text = value; + }); + py.getConfig('limit.episodes', function (value) { + limit_episodes.value = value; + }); + } + + Component.onDestruction: { + py.setConfig('plugins.youtube.api_key_v3', youtube_api_key_v3.text); + py.setConfig('limit.episodes', parseInt(limit_episodes.value)); + } + + Flickable { + id: flickable + anchors.fill: parent + boundsBehavior: Flickable.StopAtBounds + + contentWidth: detailColumn.width + contentHeight: detailColumn.height + detailColumn.spacing + + Column { + id: detailColumn + + width: page.width + spacing: 15 * pgst.scalef + + SlidePageHeader { title: 'Settings' } + + SectionHeader { text: 'YouTube' } + + SettingsLabel { text: 'API Key (v3)' } + + PTextField { + id: youtube_api_key_v3 + anchors { + left: parent.left + right: parent.right + margins: Constants.layout.padding * pgst.scalef + } + } + + SectionHeader { text: 'Limits' } + + SettingsLabel { text: 'Maximum episodes per feed' } + + PSlider { + id: limit_episodes + min: 100 + step: 100 + max: 1000 + anchors { + left: parent.left + right: parent.right + margins: Constants.layout.padding * pgst.scalef + } + onValueChangeRequested: { value = newValue; } + } + + PLabel { + text: parseInt(limit_episodes.displayedValue) + anchors { + left: parent.left + right: parent.right + margins: Constants.layout.padding * pgst.scalef + } + } + + SectionHeader { text: 'About' } + + ButtonArea { + width: parent.width + height: Constants.layout.item.height * pgst.scalef + PLabel { + anchors.centerIn: parent + text: 'About gPodder ' + py.uiversion + } + onClicked: pgst.loadPage('AboutPage.qml') + } + } + } + + PScrollDecorator { flickable: flickable } +} + diff --git a/touch/SlidePage.qml b/touch/SlidePage.qml index b7dd7ef..cdad75f 100644 --- a/touch/SlidePage.qml +++ b/touch/SlidePage.qml @@ -33,14 +33,10 @@ Rectangle { property alias canClose: dragging.canClose property bool isDialog: false - property string filename - property int leftDragLimit - property int nextDragLimit: page.x + page.width - property string title: '' property bool hasMenuButton: false property string menuButtonLabel: 'Menu' - property string menuButtonIcon: Icons.vellipsis + property string menuButtonIcon: Icons.stack signal menuButtonClicked() function closePage() { @@ -58,10 +54,6 @@ Rectangle { Stacking { id: stacking } - function slideToLeft() { - stacking.fadeInAgain(); - } - Dragging { id: dragging stacking: stacking diff --git a/touch/Stacking.qml b/touch/Stacking.qml index e85f083..5523552 100644 --- a/touch/Stacking.qml +++ b/touch/Stacking.qml @@ -28,7 +28,7 @@ Item { id: fadeIn target: stacking.page property: 'x' - to: page.leftDragLimit + to: 0 duration: 500 easing.type: Easing.OutCubic @@ -41,7 +41,7 @@ Item { id: fadeOut target: stacking.page property: 'x' - to: pgst.width + to: stacking.page.width duration: 500 easing.type: Easing.OutCubic } @@ -62,7 +62,7 @@ Item { Component.onCompleted: { if (pgst.loadPageInProgress) { - page.x = pgst.width; + page.x = page.width; fadeIn.start(); } } diff --git a/touch/gpodder.qml b/touch/gpodder.qml index 5df7a5d..122ff47 100644 --- a/touch/gpodder.qml +++ b/touch/gpodder.qml @@ -24,7 +24,7 @@ import 'common/constants.js' as Constants Rectangle { color: Constants.colors.page - width: 1280 + width: 480 height: 800 Main {} diff --git a/touch/icons/icons.js b/touch/icons/icons.js index 4372966..344a50b 100644 --- a/touch/icons/icons.js +++ b/touch/icons/icons.js @@ -19,8 +19,8 @@ var folder = '\ue065'; var magnifying_glass = '\ue074'; var cog = '\u2699'; var link = '\ue077'; -var vellipsis = '\u22ee'; var paperclip = '\ue08a'; var tag_fill = '\ue02b'; var headphones = '\ue061'; var sleep = '\u263e'; +var stack = '\ue020';