Replace pull menu with header icon; more dialogs
This commit is contained in:
parent
96cbdd4b64
commit
debc6519be
17 changed files with 121 additions and 201 deletions
|
@ -26,9 +26,13 @@ Rectangle {
|
|||
id: page
|
||||
color: Constants.colors.dialogBackground
|
||||
|
||||
Component.onCompleted: pgst.dialogsVisible = pgst.dialogsVisible + 1;
|
||||
Component.onDestruction: pgst.dialogsVisible = pgst.dialogsVisible - 1;
|
||||
|
||||
default property alias children: contents.children
|
||||
property bool isDialog: true
|
||||
property int contentHeight: -1
|
||||
property bool fullWidth: false
|
||||
|
||||
function closePage() {
|
||||
stacking.startFadeOut();
|
||||
|
@ -49,7 +53,7 @@ Rectangle {
|
|||
Rectangle {
|
||||
id: contents
|
||||
property int border: parent.width * 0.1
|
||||
width: parent.width - 2 * border
|
||||
width: parent.fullWidth ? parent.width : (parent.width - 2 * border)
|
||||
property int maxHeight: parent.height - 4 * border
|
||||
height: ((page.contentHeight > 0 && page.contentHeight < maxHeight) ? page.contentHeight : maxHeight) * parent.opacity
|
||||
anchors.centerIn: parent
|
||||
|
|
|
@ -24,16 +24,14 @@ MouseArea {
|
|||
id: dragging
|
||||
|
||||
property Item stacking
|
||||
property bool hasPull: false
|
||||
property bool canClose: true
|
||||
signal pulled
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
drag {
|
||||
target: parent
|
||||
axis: Drag.XAxis
|
||||
minimumX: dragging.hasPull ? (-parent.width/4) : 0
|
||||
minimumX: 0
|
||||
maximumX: canClose ? parent.width : 0
|
||||
filterChildren: true
|
||||
}
|
||||
|
@ -46,10 +44,7 @@ MouseArea {
|
|||
if (pressed) {
|
||||
dragging.stacking.stopAllAnimations();
|
||||
} else {
|
||||
if (hasPull && parent.x < -parent.width / 4 + 10) {
|
||||
pulled();
|
||||
parent.x = -parent.width / 4;
|
||||
} else if (parent.x > parent.width / 3) {
|
||||
if (parent.x > parent.width / 3) {
|
||||
dragging.stacking.startFadeOut();
|
||||
} else {
|
||||
dragging.stacking.fadeInAgain();
|
||||
|
|
|
@ -25,6 +25,7 @@ Item {
|
|||
|
||||
property var model
|
||||
property string title
|
||||
property string currentFilter: model.filters[model.currentFilterIndex].label
|
||||
|
||||
function showSelectionDialog() {
|
||||
pgst.loadPage('SelectionDialog.qml', {
|
||||
|
|
|
@ -48,7 +48,7 @@ SlidePage {
|
|||
id: episodeList
|
||||
property int selectedIndex: -1
|
||||
title: 'Episodes'
|
||||
headerHasIcon: true
|
||||
headerIcon: Icons.magnifying_glass
|
||||
headerIconText: 'Filter'
|
||||
onHeaderIconClicked: queryControl.showSelectionDialog();
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ import 'icons/icons.js' as Icons
|
|||
SlidePage {
|
||||
id: episodesPage
|
||||
|
||||
hasPull: true
|
||||
|
||||
property int podcast_id
|
||||
property string title
|
||||
|
||||
|
@ -48,52 +46,40 @@ SlidePage {
|
|||
title: 'Select filter'
|
||||
}
|
||||
|
||||
PullMenu {
|
||||
PullMenuItem {
|
||||
text: 'Now Playing'
|
||||
icon: Icons.play
|
||||
color: Constants.colors.playback
|
||||
onClicked: {
|
||||
pgst.loadPage('PlayerPage.qml');
|
||||
episodesPage.unPull();
|
||||
}
|
||||
}
|
||||
|
||||
PullMenuItem {
|
||||
text: 'Mark all as old'
|
||||
icon: Icons.eye
|
||||
color: Constants.colors.text
|
||||
onClicked: {
|
||||
py.call('main.mark_episodes_as_old', [episodesPage.podcast_id]);
|
||||
episodesPage.unPull();
|
||||
}
|
||||
}
|
||||
|
||||
PullMenuItem {
|
||||
text: 'Unsubscribe'
|
||||
icon: Icons.trash
|
||||
color: Constants.colors.destructive
|
||||
onClicked: {
|
||||
episodesPage.unPull();
|
||||
|
||||
var ctx = { py: py, id: episodesPage.podcast_id, page: episodesPage };
|
||||
pgst.showConfirmation('Unsubscribe', Icons.trash, function () {
|
||||
ctx.py.call('main.unsubscribe', [ctx.id]);
|
||||
ctx.page.closePage();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PListView {
|
||||
id: episodeList
|
||||
property int selectedIndex: -1
|
||||
title: episodesPage.title
|
||||
model: GPodderEpisodeListModel { id: episodeListModel }
|
||||
|
||||
headerHasIcon: true
|
||||
headerIconText: 'Filter'
|
||||
onHeaderIconClicked: queryControl.showSelectionDialog();
|
||||
headerIcon: Icons.cog
|
||||
headerIconText: 'Settings'
|
||||
onHeaderIconClicked: {
|
||||
pgst.showSelection([
|
||||
{
|
||||
label: 'Filter list (' + queryControl.currentFilter + ')',
|
||||
callback: function () {
|
||||
queryControl.showSelectionDialog();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Mark episodes as old',
|
||||
callback: function () {
|
||||
py.call('main.mark_episodes_as_old', [episodesPage.podcast_id]);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Unsubscribe',
|
||||
callback: function () {
|
||||
var ctx = { py: py, id: episodesPage.podcast_id, page: episodesPage };
|
||||
pgst.showConfirmation('Unsubscribe', Icons.trash, function () {
|
||||
ctx.py.call('main.unsubscribe', [ctx.id]);
|
||||
ctx.page.closePage();
|
||||
});
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
PPlaceholder {
|
||||
text: 'No episodes'
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
import QtQuick 2.0
|
||||
import 'common'
|
||||
|
||||
import 'common/constants.js' as Constants
|
||||
import 'icons/icons.js' as Icons
|
||||
|
||||
Item {
|
||||
id: pgst
|
||||
|
||||
|
@ -30,6 +33,7 @@ Item {
|
|||
GPodderPodcastListModel { id: podcastListModel }
|
||||
|
||||
property real scalef: width / 480
|
||||
property int dialogsVisible: 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
|
@ -60,6 +64,23 @@ Item {
|
|||
});
|
||||
}
|
||||
|
||||
function showSelection(items, title, selectedIndex) {
|
||||
loadPage('SelectionDialog.qml', {
|
||||
title: title,
|
||||
callback: function (index, item) {
|
||||
items[index].callback();
|
||||
},
|
||||
items: function() {
|
||||
var result = [];
|
||||
for (var i in items) {
|
||||
result.push(items[i].label);
|
||||
}
|
||||
return result;
|
||||
}(),
|
||||
selectedIndex: selectedIndex,
|
||||
});
|
||||
}
|
||||
|
||||
function loadPage(filename, properties) {
|
||||
if (pgst.loadPageInProgress) {
|
||||
console.log('ignoring loadPage request while load in progress');
|
||||
|
@ -85,6 +106,30 @@ Item {
|
|||
visible: !py.ready
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
id: throbber
|
||||
|
||||
z: 100
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 30 * pgst.scalef
|
||||
}
|
||||
|
||||
text: 'Now Playing'
|
||||
color: Constants.colors.playback
|
||||
icon: Icons.play
|
||||
|
||||
transparent: false
|
||||
enabled: opacity
|
||||
|
||||
opacity: player.episode != 0 && !pgst.dialogsVisible
|
||||
Behavior on opacity { PropertyAnimation { duration: 200 } }
|
||||
|
||||
onClicked: loadPage('PlayerPage.qml');
|
||||
}
|
||||
|
||||
StartPage {
|
||||
id: startPage
|
||||
visible: py.ready
|
||||
|
|
|
@ -27,7 +27,7 @@ ListView {
|
|||
|
||||
property string title
|
||||
property real pushPhase: 0
|
||||
property bool headerHasIcon: false
|
||||
property string headerIcon
|
||||
property string headerIconText
|
||||
|
||||
signal headerIconClicked()
|
||||
|
@ -35,8 +35,9 @@ ListView {
|
|||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
header: SlidePageHeader {
|
||||
id: header
|
||||
title: pListView.title
|
||||
hasIcon: pListView.headerHasIcon
|
||||
icon: pListView.headerIcon
|
||||
iconText: pListView.headerIconText
|
||||
onIconClicked: pListView.headerIconClicked()
|
||||
}
|
||||
|
|
|
@ -24,9 +24,12 @@ import 'common/util.js' as Util
|
|||
import 'common/constants.js' as Constants
|
||||
import 'icons/icons.js' as Icons
|
||||
|
||||
SlidePage {
|
||||
Dialog {
|
||||
id: playerPage
|
||||
|
||||
contentHeight: flickable.contentHeight
|
||||
fullWidth: true
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
|
@ -38,7 +41,7 @@ SlidePage {
|
|||
Column {
|
||||
id: column
|
||||
|
||||
width: playerPage.width
|
||||
width: flickable.width
|
||||
spacing: 10 * pgst.scalef
|
||||
|
||||
SlidePageHeader {
|
||||
|
@ -79,7 +82,7 @@ SlidePage {
|
|||
|
||||
PSlider {
|
||||
id: slider
|
||||
width: playerPage.width
|
||||
width: flickable.width
|
||||
value: player.position
|
||||
min: 0
|
||||
max: player.duration
|
||||
|
|
|
@ -27,38 +27,6 @@ import 'common/constants.js' as Constants
|
|||
|
||||
SlidePage {
|
||||
id: podcastsPage
|
||||
hasPull: true
|
||||
|
||||
PullMenu {
|
||||
PullMenuItem {
|
||||
text: 'Now Playing'
|
||||
color: Constants.colors.playback
|
||||
icon: Icons.play
|
||||
onClicked: {
|
||||
pgst.loadPage('PlayerPage.qml');
|
||||
podcastsPage.unPull();
|
||||
}
|
||||
}
|
||||
|
||||
PullMenuItem {
|
||||
text: 'Refresh feeds'
|
||||
icon: Icons.loop_alt2
|
||||
onClicked: {
|
||||
podcastsPage.unPull();
|
||||
py.call('main.check_for_episodes');
|
||||
}
|
||||
}
|
||||
|
||||
PullMenuItem {
|
||||
text: 'Subscribe'
|
||||
icon: Icons.plus
|
||||
color: Constants.colors.download
|
||||
onClicked: {
|
||||
pgst.loadPage('Subscribe.qml');
|
||||
podcastsPage.unPull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PListView {
|
||||
id: podcastList
|
||||
|
@ -67,6 +35,25 @@ SlidePage {
|
|||
section.property: 'section'
|
||||
section.delegate: SectionHeader { text: section }
|
||||
|
||||
headerIcon: Icons.cog
|
||||
headerIconText: 'Settings'
|
||||
onHeaderIconClicked: {
|
||||
pgst.showSelection([
|
||||
{
|
||||
label: 'Check for new episodes',
|
||||
callback: function () {
|
||||
py.call('main.check_for_episodes');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Add new podcast',
|
||||
callback: function () {
|
||||
pgst.loadPage('Subscribe.qml');
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
PPlaceholder {
|
||||
text: 'No podcasts'
|
||||
visible: podcastList.count === 0
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
/**
|
||||
*
|
||||
* gPodder QML UI Reference Implementation
|
||||
* Copyright (c) 2013, 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 'common/constants.js' as Constants
|
||||
|
||||
MouseArea {
|
||||
id: pullMenu
|
||||
default property alias items: pullMenuColumn.children
|
||||
|
||||
width: parent.width / 4
|
||||
height: parent.height
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.right
|
||||
|
||||
Column {
|
||||
id: pullMenuColumn
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
|
||||
/**
|
||||
*
|
||||
* gPodder QML UI Reference Implementation
|
||||
* Copyright (c) 2013, 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 'common/constants.js' as Constants
|
||||
|
||||
IconMenuItem {
|
||||
id: pullMenuItem
|
||||
size: 80
|
||||
|
||||
width: parent.width
|
||||
height: width
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
|
@ -26,7 +26,7 @@ import 'icons/icons.js' as Icons
|
|||
Dialog {
|
||||
id: selectionDialog
|
||||
|
||||
property string title: 'Dialog'
|
||||
property string title: ''
|
||||
property var callback: undefined
|
||||
property var items: ([])
|
||||
property var selectedIndex: -1
|
||||
|
@ -45,6 +45,7 @@ Dialog {
|
|||
|
||||
SlidePageHeader {
|
||||
id: header
|
||||
visible: title != ''
|
||||
color: Constants.colors.highlight
|
||||
title: selectionDialog.title
|
||||
}
|
||||
|
|
|
@ -27,15 +27,9 @@ Rectangle {
|
|||
color: Constants.colors.page
|
||||
|
||||
default property alias children: dragging.children
|
||||
property alias hasPull: dragging.hasPull
|
||||
property alias canClose: dragging.canClose
|
||||
property real pullPhase: (x >= 0) ? 0 : (-x / (width / 4))
|
||||
property bool isDialog: false
|
||||
|
||||
function unPull() {
|
||||
stacking.fadeInAgain();
|
||||
}
|
||||
|
||||
function closePage() {
|
||||
stacking.startFadeOut();
|
||||
}
|
||||
|
@ -52,19 +46,6 @@ Rectangle {
|
|||
stacking: stacking
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Constants.colors.page
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: page.pullPhase * 0.9
|
||||
|
||||
MouseArea {
|
||||
enabled: parent.opacity > 0
|
||||
anchors.fill: parent
|
||||
onClicked: page.unPull();
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors {
|
||||
right: parent.left
|
||||
|
|
|
@ -28,8 +28,8 @@ Item {
|
|||
property alias title: label.text
|
||||
property alias color: label.color
|
||||
|
||||
property alias hasIcon: icon.visible
|
||||
property alias iconText: icon.text
|
||||
property alias icon: icon.icon
|
||||
signal iconClicked()
|
||||
|
||||
width: parent.width
|
||||
|
@ -38,11 +38,11 @@ Item {
|
|||
IconMenuItem {
|
||||
id: icon
|
||||
|
||||
visible: false
|
||||
visible: icon != '' && icon != undefined
|
||||
enabled: visible
|
||||
|
||||
text: 'Search'
|
||||
icon: Icons.magnifying_glass
|
||||
icon: ''
|
||||
color: label.color
|
||||
|
||||
anchors {
|
||||
|
@ -56,7 +56,7 @@ Item {
|
|||
PLabel {
|
||||
id: label
|
||||
anchors {
|
||||
left: icon.right
|
||||
left: icon.visible ? icon.right : parent.left
|
||||
right: parent.right
|
||||
rightMargin: 20 * pgst.scalef
|
||||
leftMargin: 20 * pgst.scalef
|
||||
|
|
|
@ -27,19 +27,6 @@ import 'common/util.js' as Util
|
|||
SlidePage {
|
||||
id: startPage
|
||||
canClose: false
|
||||
hasPull: true
|
||||
|
||||
PullMenu {
|
||||
PullMenuItem {
|
||||
text: 'Now Playing'
|
||||
color: Constants.colors.playback
|
||||
icon: Icons.play
|
||||
onClicked: {
|
||||
pgst.loadPage('PlayerPage.qml');
|
||||
startPage.unPull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function update_stats() {
|
||||
py.call('main.get_stats', [], function (result) {
|
||||
|
|
|
@ -22,15 +22,14 @@ import QtQuick 2.0
|
|||
|
||||
import 'common/constants.js' as Constants
|
||||
|
||||
SlidePage {
|
||||
Dialog {
|
||||
id: subscribe
|
||||
|
||||
SlidePageHeader {
|
||||
title: 'Add subscription'
|
||||
color: Constants.colors.download
|
||||
}
|
||||
contentHeight: contentColumn.height
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 30 * pgst.scalef
|
||||
|
||||
|
|
|
@ -17,3 +17,4 @@ var eye = '\ue025';
|
|||
var loop_alt2 = '\ue033';
|
||||
var folder = '\ue065';
|
||||
var magnifying_glass = '\ue074';
|
||||
var cog = '\u2699';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue