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 33535b6..a51b540 100644 --- a/common/GPodderEpisodeListModel.qml +++ b/common/GPodderEpisodeListModel.qml @@ -62,10 +62,6 @@ ListModel { }); } - property var worker: ModelWorkerScript { - id: modelWorker - } - function forEachEpisode(callback) { // Go from bottom up (= chronological order) for (var i=count-1; i>=0; i--) { @@ -126,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/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/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/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 28c89f7..9d4f850 100644 --- a/touch/Main.qml +++ b/touch/Main.qml @@ -125,7 +125,7 @@ Item { pgst.hasBackButton = Qt.binding(function () { return page.isDialog || page.canClose; }); pgst.hasMenuButton = Qt.binding(function () { return !page.isDialog && page.hasMenuButton; }); pgst.menuButtonLabel = Qt.binding(function () { return (!page.isDialog && pgst.hasMenuButton) ? page.menuButtonLabel : 'Menu'; }); - pgst.menuButtonIcon = Qt.binding(function () { return (!page.isDialog && pgst.hasMenuButton) ? page.menuButtonIcon : Icons.vellipsis; }); + pgst.menuButtonIcon = Qt.binding(function () { return (!page.isDialog && pgst.hasMenuButton) ? page.menuButtonIcon : Icons.stack; }); if (!page.isDialog) { pgst.windowTitle = page.title || 'gPodder'; diff --git a/touch/PListView.qml b/touch/PListView.qml index 7f077d6..e607207 100644 --- a/touch/PListView.qml +++ b/touch/PListView.qml @@ -30,6 +30,19 @@ ListView { boundsBehavior: Flickable.StopAtBounds + function relayout() { + var _contentY = contentY; + var _model = model; + model = null; + model = _model; + contentY = _contentY; + } + + Connections { + target: pgst + onScalefChanged: relayout(); + } + header: SlidePageHeader { id: header title: pListView.title diff --git a/touch/PSlider.qml b/touch/PSlider.qml index eb32ad3..2f6855f 100644 --- a/touch/PSlider.qml +++ b/touch/PSlider.qml @@ -28,6 +28,7 @@ Item { property real value property real min: 0.0 property real max: 1.0 + property real step: 0.0 property color color: Constants.colors.highlight property real displayedValue: mouseArea.pressed ? temporaryValue : value @@ -42,13 +43,22 @@ Item { MouseArea { id: mouseArea anchors.fill: parent - onClicked: slider.valueChangeRequested(min + (max - min) * (mouse.x / width)) - onPressed: { - slider.temporaryValue = (min + (max - min) * (mouse.x / width)); - } - onPositionChanged: { - slider.temporaryValue = (min + (max - min) * (mouse.x / width)); + function updateValue(x) { + if (x > 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/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 b07129f..6cdab93 100644 --- a/touch/PodcastsPage.qml +++ b/touch/PodcastsPage.qml @@ -47,9 +47,9 @@ SlidePage { } }, { - label: 'About', + label: 'Settings', callback: function () { - pgst.loadPage('AboutPage.qml'); + pgst.loadPage('SettingsPage.qml'); }, }, { @@ -66,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 182fdf6..cdad75f 100644 --- a/touch/SlidePage.qml +++ b/touch/SlidePage.qml @@ -36,7 +36,7 @@ Rectangle { 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() { 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';