Toolbar with back button, menu and play throbber
This commit is contained in:
parent
532a99edea
commit
7bdf6b783a
19 changed files with 298 additions and 129 deletions
|
@ -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',
|
||||
|
|
|
@ -26,6 +26,7 @@ MouseArea {
|
|||
id: mouseArea
|
||||
property bool transparent: false
|
||||
property bool canHighlight: true
|
||||
property alias color: background.color
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -34,27 +34,9 @@ SlidePage {
|
|||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Component.onCompleted: {
|
||||
episodeListModel.podcast_id = podcast_id;
|
||||
episodeListModel.setQuery(episodeListModel.queries.All);
|
||||
episodeListModel.reload();
|
||||
}
|
||||
|
||||
EpisodeQueryControl {
|
||||
id: queryControl
|
||||
model: episodeListModel
|
||||
title: 'Select filter'
|
||||
}
|
||||
|
||||
PListView {
|
||||
id: episodeList
|
||||
property int selectedIndex: -1
|
||||
title: episodesPage.title
|
||||
model: GPodderEpisodeListModel { id: episodeListModel }
|
||||
|
||||
headerIcon: Icons.cog
|
||||
headerIconText: 'Settings'
|
||||
onHeaderIconClicked: {
|
||||
hasMenuButton: true
|
||||
menuButtonLabel: 'Settings'
|
||||
onMenuButtonClicked: {
|
||||
pgst.showSelection([
|
||||
{
|
||||
label: 'Filter list (' + queryControl.currentFilter + ')',
|
||||
|
@ -81,6 +63,25 @@ SlidePage {
|
|||
]);
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
episodeListModel.podcast_id = podcast_id;
|
||||
episodeListModel.setQuery(episodeListModel.queries.All);
|
||||
episodeListModel.reload();
|
||||
}
|
||||
|
||||
EpisodeQueryControl {
|
||||
id: queryControl
|
||||
model: episodeListModel
|
||||
title: 'Select filter'
|
||||
}
|
||||
|
||||
PListView {
|
||||
id: episodeList
|
||||
property int selectedIndex: -1
|
||||
title: episodesPage.title
|
||||
model: GPodderEpisodeListModel { id: episodeListModel }
|
||||
|
||||
PPlaceholder {
|
||||
text: 'No episodes'
|
||||
visible: episodeList.count === 0
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,31 +132,69 @@ 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
|
||||
}
|
||||
|
||||
source: 'images/toolbarshadow.png'
|
||||
opacity: .1
|
||||
height: 10 * pgst.scalef
|
||||
visible: toolbar.showing
|
||||
}
|
||||
|
||||
PToolbar {
|
||||
id: toolbar
|
||||
z: 102
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
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'
|
||||
color: Constants.colors.playback
|
||||
icon: Icons.play
|
||||
|
||||
transparent: false
|
||||
enabled: opacity
|
||||
|
||||
opacity: player.episode != 0 && !pgst.dialogsVisible
|
||||
Behavior on opacity { PropertyAnimation { duration: 200 } }
|
||||
|
||||
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 {
|
||||
visible: py.ready
|
||||
}
|
||||
|
|
|
@ -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
48
touch/PToolbar.qml
Normal 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
59
touch/PToolbarButton.qml
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -29,16 +29,9 @@ SlidePage {
|
|||
id: podcastsPage
|
||||
canClose: false
|
||||
|
||||
PListView {
|
||||
id: podcastList
|
||||
title: 'Subscriptions'
|
||||
|
||||
section.property: 'section'
|
||||
section.delegate: SectionHeader { text: section }
|
||||
|
||||
headerIcon: Icons.cog
|
||||
headerIconText: 'Settings'
|
||||
onHeaderIconClicked: {
|
||||
hasMenuButton: true
|
||||
menuButtonLabel: 'Settings'
|
||||
onMenuButtonClicked: {
|
||||
pgst.showSelection([
|
||||
{
|
||||
label: 'Check for new episodes',
|
||||
|
@ -73,6 +66,13 @@ SlidePage {
|
|||
]);
|
||||
}
|
||||
|
||||
PListView {
|
||||
id: podcastList
|
||||
title: 'Subscriptions'
|
||||
|
||||
section.property: 'section'
|
||||
section.delegate: SectionHeader { text: section }
|
||||
|
||||
PPlaceholder {
|
||||
text: 'No podcasts'
|
||||
visible: podcastList.count === 0
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ Item {
|
|||
|
||||
function startFadeOut() {
|
||||
fadeOut.start();
|
||||
pgst.topOfStackChanged(-1);
|
||||
page.destroy(500);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,3 +19,4 @@ var folder = '\ue065';
|
|||
var magnifying_glass = '\ue074';
|
||||
var cog = '\u2699';
|
||||
var link = '\ue077';
|
||||
var vellipsis = '\u22ee';
|
||||
|
|
BIN
touch/images/toolbarshadow.png
Normal file
BIN
touch/images/toolbarshadow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 154 B |
Loading…
Add table
Add a link
Reference in a new issue