Toolbar with back button, menu and play throbber

This commit is contained in:
Thomas Perl 2014-03-17 17:40:29 +01:00
parent 532a99edea
commit 7bdf6b783a
19 changed files with 298 additions and 129 deletions

View file

@ -32,11 +32,17 @@ var colors = {
destructive: '#cf424f', /* destructive actions */
page: '#dddddd',
toolbar: '#d0d0d0',
dialog: '#dddddd',
dialogBackground: '#aa000000',
text: '#333333', /* text color */
dialogText: '#333333',
highlight: '#433b67',
dialogHighlight: '#433b67',
secondaryHighlight: '#605885',
area: '#cccccc',
dialogArea: '#d0d0d0',
toolbarArea: '#bbbbbb',
placeholder: '#666666',
//page: '#000000',

View file

@ -26,6 +26,7 @@ MouseArea {
id: mouseArea
property bool transparent: false
property bool canHighlight: true
property alias color: background.color
Rectangle {
id: background

View file

@ -24,6 +24,8 @@ import 'common/constants.js' as Constants
Rectangle {
id: page
z: 200
color: Constants.colors.dialogBackground
Component.onCompleted: pgst.dialogsVisible = pgst.dialogsVisible + 1;
@ -57,7 +59,7 @@ Rectangle {
property int maxHeight: parent.height - 4 * border
height: ((page.contentHeight > 0 && page.contentHeight < maxHeight) ? page.contentHeight : maxHeight) * parent.opacity
anchors.centerIn: parent
color: Constants.colors.page
color: Constants.colors.dialog
clip: true
}
}

View file

@ -31,6 +31,11 @@ SlidePage {
property string link
property bool ready: false
hasMenuButton: detailPage.link != ''
menuButtonIcon: Icons.link
menuButtonLabel: 'Website'
onMenuButtonClicked: Qt.openUrlExternally(detailPage.link)
PBusyIndicator {
anchors.centerIn: parent
visible: !detailPage.ready
@ -59,12 +64,7 @@ SlidePage {
width: detailPage.width
spacing: 10 * pgst.scalef
SlidePageHeader {
title: 'Shownotes'
icon: (detailPage.link != '') ? Icons.link : ''
iconText: 'Website'
onIconClicked: Qt.openUrlExternally(detailPage.link);
}
Item { height: 20 * pgst.scalef; width: parent.width }
Column {
width: parent.width - 2 * 30 * pgst.scalef

View file

@ -28,6 +28,11 @@ import 'icons/icons.js' as Icons
SlidePage {
id: allEpisodesPage
hasMenuButton: true
menuButtonIcon: Icons.magnifying_glass
menuButtonLabel: 'Filter'
onMenuButtonClicked: queryControl.showSelectionDialog()
EpisodeQueryControl {
id: queryControl
model: episodesListModel
@ -48,9 +53,6 @@ SlidePage {
id: episodeList
property int selectedIndex: -1
title: 'Episodes'
headerIcon: Icons.magnifying_glass
headerIconText: 'Filter'
onHeaderIconClicked: queryControl.showSelectionDialog();
PPlaceholder {
text: 'No episodes found'

View file

@ -34,6 +34,36 @@ SlidePage {
width: parent.width
height: parent.height
hasMenuButton: true
menuButtonLabel: 'Settings'
onMenuButtonClicked: {
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();
});
},
},
]);
}
Component.onCompleted: {
episodeListModel.podcast_id = podcast_id;
episodeListModel.setQuery(episodeListModel.queries.All);
@ -52,35 +82,6 @@ SlidePage {
title: episodesPage.title
model: GPodderEpisodeListModel { id: episodeListModel }
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'
visible: episodeList.count === 0

View file

@ -32,6 +32,8 @@ ButtonArea {
property alias size: icon.size
property bool alwaysShowText: false
Behavior on _real_color { ColorAnimation { duration: 100 } }
transparent: true
canHighlight: false

View file

@ -51,10 +51,36 @@ Item {
} else {
children[index-1].opacity = x / width;
}
//children[index-1].pushPhase = x / width;
}
property bool loadPageInProgress: false
property bool hasBackButton: false
property int bottomSpacing: toolbar.showing ? toolbar.height+toolbar.anchors.bottomMargin : 0
property bool hasMenuButton: false
property string menuButtonLabel: ''
property string menuButtonIcon: ''
function topOfStackChanged(offset) {
if (offset === undefined) {
offset = 0;
}
var page = children[children.length+offset-1];
// TODO: Maybe make these property bindings instead
pgst.hasBackButton = (!page.isDialog && page.canClose);
pgst.hasMenuButton = page.hasMenuButton;
if (pgst.hasMenuButton) {
pgst.menuButtonLabel = page.menuButtonLabel;
pgst.menuButtonIcon = page.menuButtonIcon;
} else {
pgst.menuButtonLabel = 'Menu';
pgst.menuButtonIcon = Icons.vellipsis;
}
}
function showConfirmation(title, icon, callback) {
loadPage('Confirmation.qml', {
@ -106,29 +132,67 @@ Item {
visible: !py.ready
}
IconMenuItem {
id: throbber
z: 100
Image {
z: 101
anchors {
left: parent.left
right: parent.right
rightMargin: (opacity-1) * width
top: parent.top
topMargin: (Constants.layout.header.height * pgst.scalef - height) / 2
bottom: toolbar.top
}
text: 'Now Playing'
color: Constants.colors.playback
icon: Icons.play
source: 'images/toolbarshadow.png'
opacity: .1
height: 10 * pgst.scalef
visible: toolbar.showing
}
transparent: false
enabled: opacity
PToolbar {
id: toolbar
z: 102
opacity: player.episode != 0 && !pgst.dialogsVisible
Behavior on opacity { PropertyAnimation { duration: 200 } }
Row {
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: loadPage('PlayerPage.qml');
PToolbarButton {
id: backButton
text: 'Back'
icon: Icons.arrow_left
enabled: pgst.hasBackButton
onClicked: pgst.children[pgst.children.length-1].closePage();
}
}
Row {
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
}
PToolbarButton {
id: throbber
text: 'Now Playing'
icon: Icons.play
enabled: player.episode != 0
onClicked: loadPage('PlayerPage.qml');
}
PToolbarButton {
id: menuButton
text: pgst.menuButtonLabel
icon: pgst.menuButtonIcon
enabled: pgst.hasMenuButton
onClicked: pgst.children[pgst.children.length-1].menuButtonClicked()
}
}
}
PodcastsPage {

View file

@ -27,19 +27,12 @@ ListView {
property string title
property real pushPhase: 0
property string headerIcon
property string headerIconText
signal headerIconClicked()
boundsBehavior: Flickable.StopAtBounds
header: SlidePageHeader {
id: header
title: pListView.title
icon: pListView.headerIcon
iconText: pListView.headerIconText
onIconClicked: pListView.headerIconClicked()
}
PScrollDecorator { flickable: pListView }

48
touch/PToolbar.qml Normal file
View file

@ -0,0 +1,48 @@
/**
*
* gPodder QML UI Reference Implementation
* Copyright (c) 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 'common'
import 'common/constants.js' as Constants
import 'icons/icons.js' as Icons
Rectangle {
id: toolbar
property bool showing: true
color: Constants.colors.toolbar
height: 80 * pgst.scalef
MouseArea {
// Capture all touch events
anchors.fill: parent
}
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
bottomMargin: toolbar.showing ? 0 : -height
}
Behavior on anchors.bottomMargin { PropertyAnimation { duration: 100 } }
}

59
touch/PToolbarButton.qml Normal file
View file

@ -0,0 +1,59 @@
/**
*
* gPodder QML UI Reference Implementation
* Copyright (c) 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 'common'
import 'common/constants.js' as Constants
import 'icons/icons.js' as Icons
Rectangle {
id: toolbarButton
//property alias color: iconMenuItem.color
//property alias text: iconMenuItem.text
property string text: ''
property alias icon: iconMenuItem.icon
signal clicked()
width: iconMenuItem.width
height: iconMenuItem.height
color: iconMenuItem.pressed ? Constants.colors.toolbarArea : 'transparent'
Rectangle {
height: 5 * pgst.scalef
color: iconMenuItem.pressed ? Constants.colors.secondaryHighlight : 'transparent'
anchors {
left: parent.left
right: parent.right
top: parent.top
}
}
IconMenuItem {
id: iconMenuItem
color: Constants.colors.text
transparent: true
enabled: parent.enabled
onClicked: toolbarButton.clicked()
}
}

View file

@ -63,6 +63,7 @@ Dialog {
}
text: player.episode_title
elide: Text.ElideRight
color: Constants.colors.dialogText
}
PLabel {
@ -72,12 +73,14 @@ Dialog {
}
text: player.podcast_title
elide: Text.ElideRight
color: Constants.colors.dialogText
}
}
PLabel {
anchors.horizontalCenter: parent.horizontalCenter
text: Util.formatPosition(slider.displayedValue/1000, player.duration/1000)
color: Constants.colors.dialogText
}
PSlider {

View file

@ -29,6 +29,43 @@ SlidePage {
id: podcastsPage
canClose: false
hasMenuButton: true
menuButtonLabel: 'Settings'
onMenuButtonClicked: {
pgst.showSelection([
{
label: 'Check for new episodes',
callback: function () {
py.call('main.check_for_episodes');
}
},
{
label: 'Filter episodes',
callback: function () {
pgst.loadPage('EpisodeQueryPage.qml');
}
},
{
label: 'About',
callback: function () {
pgst.loadPage('AboutPage.qml');
},
},
{
label: 'Add new podcast',
callback: function () {
pgst.loadPage('Subscribe.qml');
},
},
{
label: 'Search gpodder.net',
callback: function () {
pgst.loadPage('Directory.qml');
},
},
]);
}
PListView {
id: podcastList
title: 'Subscriptions'
@ -36,43 +73,6 @@ 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: 'Filter episodes',
callback: function () {
pgst.loadPage('EpisodeQueryPage.qml');
}
},
{
label: 'About',
callback: function () {
pgst.loadPage('AboutPage.qml');
},
},
{
label: 'Add new podcast',
callback: function () {
pgst.loadPage('Subscribe.qml');
},
},
{
label: 'Search gpodder.net',
callback: function () {
pgst.loadPage('Directory.qml');
},
},
]);
}
PPlaceholder {
text: 'No podcasts'
visible: podcastList.count === 0

View file

@ -46,7 +46,7 @@ Dialog {
SlidePageHeader {
id: header
visible: title != ''
color: Constants.colors.highlight
color: Constants.colors.dialogHighlight
title: selectionDialog.title
}
@ -56,8 +56,9 @@ Dialog {
delegate: ButtonArea {
id: buttonArea
color: Constants.colors.dialogArea
width: parent.width
height: 60 * pgst.scalef
height: 70 * pgst.scalef
transparent: (index != selectionDialog.selectedIndex)
@ -69,7 +70,8 @@ Dialog {
}
text: modelData
color: (index == selectionDialog.selectedIndex || buttonArea.pressed) ? Constants.colors.highlight : Constants.colors.text
color: (index == selectionDialog.selectedIndex || buttonArea.pressed) ? Constants.colors.dialogHighlight : Constants.colors.dialogText
font.pixelSize: 30 * pgst.scalef
}
onClicked: {

View file

@ -21,15 +21,23 @@
import QtQuick 2.0
import 'common/constants.js' as Constants
import 'icons/icons.js' as Icons
Rectangle {
id: page
color: Constants.colors.page
Component.onCompleted: pgst.topOfStackChanged();
default property alias children: dragging.children
property alias canClose: dragging.canClose
property bool isDialog: false
property bool hasMenuButton: false
property string menuButtonLabel: 'Menu'
property string menuButtonIcon: Icons.vellipsis
signal menuButtonClicked()
function closePage() {
stacking.startFadeOut();
}
@ -37,7 +45,7 @@ Rectangle {
onXChanged: pgst.update(page, x)
width: parent.width
height: parent.height
height: parent.height - parent.bottomSpacing
Stacking { id: stacking }

View file

@ -28,45 +28,21 @@ Item {
property alias title: label.text
property alias color: label.color
property alias iconText: icon.text
property alias icon: icon.icon
signal iconClicked()
width: parent.width
height: Constants.layout.header.height * pgst.scalef
IconMenuItem {
id: icon
visible: icon != '' && icon != undefined
enabled: visible
text: 'Search'
icon: ''
color: label.color
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
onClicked: slidePageHeader.iconClicked()
}
PLabel {
id: label
anchors {
left: icon.visible ? icon.right : parent.left
left: parent.left
right: parent.right
rightMargin: 20 * pgst.scalef + (throbber.width * throbber.opacity)
rightMargin: 20 * pgst.scalef
leftMargin: 20 * pgst.scalef
verticalCenter: parent.verticalCenter
}
color: Constants.colors.highlight
horizontalAlignment: Text.AlignRight
font.pixelSize: parent.height * .4
elide: Text.ElideRight
}
}

View file

@ -48,6 +48,7 @@ Item {
function startFadeOut() {
fadeOut.start();
pgst.topOfStackChanged(-1);
page.destroy(500);
}

View file

@ -19,3 +19,4 @@ var folder = '\ue065';
var magnifying_glass = '\ue074';
var cog = '\u2699';
var link = '\ue077';
var vellipsis = '\u22ee';

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B