17
19
return controller.ntranslate(x, y, z)
20
property alias podcastModel: podcastList.model
21
property variant episodeModel
22
22
property alias multiEpisodesSheetOpened: multiEpisodesSheet.opened
23
onEpisodeModelChanged: episodeList.resetFilterDialog()
24
property alias currentEpisode: mediaPlayer.episode
25
property alias showNotesEpisode: showNotes.episode
26
23
property variant currentPodcast: undefined
27
24
property bool hasPodcasts: podcastList.hasItems
28
25
property alias currentFilterText: episodeList.currentFilterText
26
property variant podcastListView: podcastList.listview
30
28
property bool playing: mediaPlayer.playing
31
property bool canGoBack: (main.state != 'podcasts' || contextMenu.state != 'closed' || mediaPlayer.visible) && !progressIndicator.opacity
32
property bool hasPlayButton: nowPlayingThrobber.shouldAppear && !progressIndicator.opacity
33
property bool hasSearchButton: (contextMenu.state == 'closed' && main.state == 'podcasts') && !mediaPlayer.visible && !progressIndicator.opacity
34
property bool hasFilterButton: state == 'episodes' && !mediaPlayer.visible
29
property bool hasPlayButton: ((mediaPlayer.episode !== undefined)) && !progressIndicator.opacity
30
property bool hasSearchButton: !progressIndicator.opacity
36
32
property bool loadingEpisodes: false
38
34
function clearEpisodeListModel() {
39
loadingEpisodes = true
40
startProgress(_('Loading episodes'))
43
function setEpisodeListModel(model) {
35
/* Abort loading when clearing list model */
36
episodeListModelLoader.running = false;
37
loadingEpisodes = true;
44
38
episodeListModel.clear();
45
for (var i=0; i<model.length; i++) {
46
episodeListModel.append(model[i]);
42
id: episodeListModelLoader
45
* These values determined by non-scientific experimentation,
46
* feel free to tweak depending on the power of your device.
48
* Loads <stepSize> items every <interval> ms, and to populate
49
* the first screen, loads <initialStepSize> items on start.
51
property int initialStepSize: 13
52
property int stepSize: 4
56
property int position: 0
59
triggeredOnStart: true
62
var step = (position === 0) ? initialStepSize : stepSize;
63
var end = Math.min(count, position+step);
65
for (var i=position; i<end; i++) {
66
episodeListModel.append(episodeModel.get_object_by_index(i));
70
if (position === count) {
72
main.loadingEpisodes = false;
73
} else if (pageStack.depth === 1) {
74
/* Abort loading when switching to main view */
76
main.loadingEpisodes = false;
48
loadingEpisodes = false
81
function setEpisodeListModel() {
82
episodeListModelLoader.count = episodeModel.getCount();
83
episodeListModelLoader.position = 0;
84
episodeListModelLoader.restart();
87
Component.onCompleted: {
88
/* Signal connections for upcalls from the backend */
89
controller.episodeUpdated.connect(episodeUpdated);
91
controller.showMessage.connect(showMessage);
92
controller.showInputDialog.connect(showInputDialog);
93
controller.openContextMenu.connect(openContextMenu);
95
controller.startProgress.connect(startProgress);
96
controller.endProgress.connect(endProgress);
98
controller.clearEpisodeListModel.connect(clearEpisodeListModel);
99
controller.setEpisodeListModel.connect(setEpisodeListModel);
101
controller.enqueueEpisode.connect(enqueueEpisode);
102
controller.removeQueuedEpisode.connect(removeQueuedEpisode);
103
controller.removeQueuedEpisodesForPodcast.connect(removeQueuedEpisodesForPodcast);
105
controller.shutdown.connect(shutdown);
52
108
function episodeUpdated(id) {
69
125
function clickSearchButton() {
70
contextMenu.showSubscribe()
126
pageStack.push(subscribePage);
74
if (nowPlayingThrobber.opened) {
76
} else if (contextMenu.state == 'opened') {
77
contextMenu.state = 'closed'
78
} else if (main.state == 'podcasts') {
81
} else if (main.state == 'episodes') {
82
main.state = 'podcasts'
83
main.currentPodcast = undefined
84
} else if (main.state == 'shownotes') {
85
main.state = 'episodes'
129
function shutdown() {
89
133
function showFilterDialog() {
154
206
progressIndicator.opacity = 0
166
anchors.leftMargin: 100
174
script: episodeList.resetSelection()
181
opacity: !main.loadingEpisodes
186
anchors.leftMargin: -100
191
anchors.leftMargin: main.width
197
target: listContainer
203
anchors.leftMargin: 0
210
213
anchors.fill: parent
219
controller.podcastSelected(podcast)
220
main.currentPodcast = podcast
222
onPodcastContextMenu: controller.podcastContextMenu(podcast)
223
onSubscribe: contextMenu.showSubscribe()
225
Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
226
Behavior on anchors.leftMargin { NumberAnimation { duration: Config.slowTransition } }
216
controller.podcastSelected(podcast);
217
main.currentPodcast = podcast;
218
pageStack.push(episodesPage);
220
onPodcastContextMenu: controller.podcastContextMenu(podcast)
221
onSubscribe: pageStack.push(subscribePage);
226
lockToPortrait: mainPage.lockToPortrait
227
listview: episodeList.listview
230
episodeList.resetSelection();
231
main.currentPodcast = undefined;
231
mainState: main.state
233
239
model: ListModel { id: episodeListModel }
239
240
onEpisodeContextMenu: controller.episodeContextMenu(episode)
241
Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
242
Behavior on anchors.leftMargin { NumberAnimation { duration: Config.slowTransition } }
245
Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
246
Behavior on scale { NumberAnimation { duration: Config.fadeTransition } }
255
bottom: parent.bottom
259
Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
260
Behavior on anchors.leftMargin { NumberAnimation { duration: Config.slowTransition } }
245
text: _('Now playing')
247
main.clickPlayButton();
251
text: _('Filter:') + ' ' + mainObject.currentFilterText
253
mainObject.showFilterDialog();
257
text: _('Download episodes')
259
main.showMultiEpisodesSheet(text, _('Download'), 'download');
263
text: _('Playback episodes')
265
main.showMultiEpisodesSheet(text, _('Play'), 'play');
269
text: _('Delete episodes')
271
main.showMultiEpisodesSheet(text, _('Delete'), 'delete');
264
279
id: overlayInteractionBlockWall
265
280
anchors.fill: parent
266
z: (contextMenu.state != 'opened')?2:0
268
opacity: (nowPlayingThrobber.opened || contextMenu.state == 'opened' || messageDialog.opacity || inputDialog.opacity || progressIndicator.opacity)?1:0
283
opacity: (inputDialog.opacity || progressIndicator.opacity)?1:0
269
284
Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
272
287
anchors.fill: parent
274
if (contextMenu.state == 'opened') {
276
} else if (progressIndicator.opacity) {
289
if (progressIndicator.opacity) {
278
291
} else if (inputDialog.opacity) {
279
292
inputDialog.close()
280
} else if (messageDialog.opacity) {
281
messageDialog.opacity = 0
283
nowPlayingThrobber.opened = false
302
id: nowPlayingThrobber
303
property bool shouldAppear: ((contextMenu.state != 'opened') && (mediaPlayer.episode !== undefined))
304
property bool opened: false
309
visible: nowPlayingThrobber.opened
313
anchors.top: parent.bottom
314
anchors.left: parent.left
315
anchors.right: parent.right
316
anchors.topMargin: nowPlayingThrobber.opened?-(height+(parent.height-height)/2):0
318
Behavior on anchors.topMargin { PropertyAnimation { duration: Config.quickTransition; easing.type: Easing.OutCirc } }
311
lockToPortrait: mainPage.lockToPortrait
319
verticalCenter: parent.verticalCenter
326
onClicked: main.openShowNotes(mediaPlayer.episode)
330
text: _('Play queue')
332
if (mediaPlayer.hasQueue) {
333
mediaPlayer.showQueue();
335
main.showMessage(_('Playlist empty'));
348
bottom: parent.bottom
351
onClose: contextMenu.state = 'closed'
352
onResponse: controller.contextMenuResponse(index)
356
Behavior on opacity { NumberAnimation { duration: Config.fadeTransition } }
367
anchors.right: main.right
378
anchors.right: main.left
381
script: controller.contextMenuClosed()
386
transitions: Transition {
387
AnchorAnimation { duration: Config.slowTransition }
391
361
function showMessage(message) {
392
messageDialogText.text = message
393
messageDialog.opacity = 1
402
Behavior on opacity { PropertyAnimation { } }
405
id: messageDialogText
409
verticalCenter: parent.verticalCenter
410
leftMargin: Config.largeSpacing
411
rightMargin: Config.largeSpacing
417
horizontalAlignment: Text.AlignHCenter
418
wrapMode: Text.WordWrap
362
infoBanner.text = message;
422
366
function showInputDialog(message, value, accept, reject, textInput) {