1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2024-12-22 22:15:17 +00:00

Album page landscape improvements

This commit is contained in:
Chris Josten 2020-10-27 02:35:50 +01:00
parent 040aeb1a40
commit a172b6d914
9 changed files with 433 additions and 199 deletions

View file

@ -10,6 +10,11 @@ set(harbour-sailfin_SOURCES
set(sailfin_QML_SOURCES
qml/Constants.qml
qml/Utils.js
qml/components/music/NarrowAlbumCover.qml
qml/components/music/WideAlbumCover.qml
qml/components/music/SongDelegate.qml
qml/components/videoplayer/VideoError.qml
qml/components/videoplayer/VideoHud.qml
qml/components/GlassyBackground.qml
qml/components/IconListItem.qml
qml/components/LibraryItemDelegate.qml
@ -21,8 +26,6 @@ set(sailfin_QML_SOURCES
qml/components/UserGridDelegate.qml
qml/components/VideoPlayer.qml
qml/components/VideoTrackSelector.qml
qml/components/videoplayer/VideoError.qml
qml/components/videoplayer/VideoHud.qml
qml/cover/CoverPage.qml
qml/cover/PosterCover.qml
qml/cover/VideoCover.qml

View file

@ -23,10 +23,30 @@ import QtQuick 2.6
import Sailfish.Silica 1.0
QtObject {
readonly property real libraryDelegateWidth: Screen.width / 3
readonly property real libraryDelegateHeight: Screen.width / 3
readonly property real libraryDelegateWidth: {
switch(Screen.sizeCategory) {
case Screen.Small:
case Screen.Medium:
return Screen.width / 3
case Screen.Large:
return Screen.width / 5
case Screen.ExtraLarge:
return Screen.width / 7
}
}
readonly property real libraryDelegateHeight: {
switch(Screen.sizeCategory) {
case Screen.Small:
case Screen.Medium:
return Screen.width / 3
case Screen.Large:
return Screen.width / 5
case Screen.ExtraLarge:
return Screen.width / 7
}
}
readonly property real libraryDelegatePosterHeight: Screen.width / 2
readonly property real libraryDelegatePosterHeight: libraryDelegateHeight * 1.6667
readonly property real libraryProgressHeight: Theme.paddingMedium

View file

@ -53,11 +53,15 @@ function itemImageUrl(baseUrl, item, type, options) {
}
function itemModelImageUrl(baseUrl, itemId, tag, type, options) {
if (tag == undefined) return ""
if (tag === undefined) return ""
var extraQuery = "";
for (var prop in options) {
if (options.hasOwnProperty(prop)) {
extraQuery += "&" + prop + "=" + options[prop];
var value = options[prop];
if (prop === "maxWidth" || prop === "maxHeight") {
value = Math.floor(options[prop]);
}
extraQuery += "&" + prop + "=" + value;
}
}
return baseUrl + "/Items/" + itemId + "/Images/" + type + "?tag=" + tag + extraQuery

View file

@ -0,0 +1,166 @@
/*
Sailfin: a Jellyfin client written using Qt
Copyright (C) 2020 Chris Josten
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import ".."
import "../.."
/**
* Album details for "narrow" devices such as phones in portrait mode.
*/
Item {
property ListView listview
property real releaseYear
property alias albumArt: albumArt.source
property string albumArtist
property real duration
property int songCount
property string name
property string stateIfArt: "largeArt"
property alias _albumArt: albumArt
id: listHeader
width: parent.width
//spacing: Theme.paddingLarge
state: _albumArt.source != "" ? stateIfArt : "noArt"
MouseArea {
anchors.fill: parent
onClicked: {
if (listHeader.stateIfArt == "largeArt") {
listHeader.stateIfArt = "details"
} else {
listHeader.stateIfArt = "largeArt"
}
}
}
RemoteImage {
id: albumArt
anchors {
top: parent.top
right: parent.right
}
sourceSize.width: listHeader.width
sourceSize.height: listHeader.width
fillMode: Image.PreserveAspectFit
opacity: 1
clip: true
}
PageHeader {
id: albumHeader
width: parent.width - Theme.horizontalPageMargin - height
title: name
//: Short description of the album: %1 -> album artist, %2 -> amount of songs, %3 -> duration, %4 -> release year
description: qsTr("%1\n%2 songs | %3 | %4")
.arg(albumArtist)
.arg(songCount)
.arg(Utils.ticksToText(duration))
//: Unknown album release year
.arg(releaseYear >= 0 ? releaseYear : qsTr("Unknown year"))
}
states: [
State {
name: "largeArt"
PropertyChanges {
target: _albumArt
width: parent.width
height: width
}
PropertyChanges {
target: listHeader
height: width
}
PropertyChanges {
target: albumHeader
opacity: 0
}
PropertyChanges {
target: listview
contentY: -listview.width
}
AnchorChanges {
target: albumHeader
anchors.left: undefined
anchors.right: _albumArt.left
}
},
State {
name: "details"
PropertyChanges {
target: _albumArt
width: height
height: albumHeader.height
}
PropertyChanges {
target: listHeader
height: albumHeader.height
}
PropertyChanges {
target: albumHeader
opacity: 1
}
PropertyChanges {
target: listview
contentY: -albumHeader.height
}
AnchorChanges {
target: albumHeader
anchors.left: undefined
anchors.right: _albumArt.left
}
},
State {
name: "noArt"
extend: "details"
PropertyChanges {
target: _albumArt
opacity: 0
}
PropertyChanges {
target: albumHeader
width: parent.width - Theme.horizontalPageMargin * 2
}
AnchorChanges {
target: albumHeader
anchors.left: parent.left
anchors.right: parent.right
}
}
]
transitions: [
Transition {
from: "noArt"
// No transitions from "noArt", otherwise the layout animates every load
},
Transition {
OpacityAnimator { target: albumHeader}
OpacityAnimator { target: _albumArt}
NumberAnimation {
properties: "width,height,contentY"
//velocity: 1600
duration: 300
easing.type: Easing.OutQuad
}
AnchorAnimation {}
}
]
}

View file

@ -0,0 +1,95 @@
/*
Sailfin: a Jellyfin client written using Qt
Copyright (C) 2020 Chris Josten
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import "../.."
ListItem {
id: songDelegateRoot
property var artists: []
property real duration
property string name
property int indexNumber
contentHeight: songName.height + songArtists.height + 2 * Theme.paddingMedium
width: parent.width
TextMetrics {
id: indexMetrics
text: "99"
font.pixelSize: Theme.fontSizeLarge
}
Label {
id: songIndex
anchors {
top: parent.top
topMargin: Theme.paddingMedium
left: parent.left
leftMargin: Theme.horizontalPageMargin
}
text: indexNumber
horizontalAlignment: Text.AlignRight
font.pixelSize: Theme.fontSizeExtraLarge
width: indexMetrics.width
}
Label {
id: songName
anchors {
left: songIndex.right
leftMargin: Theme.paddingLarge
top: parent.top
topMargin: Theme.paddingMedium
right: duration.left
rightMargin: Theme.paddingLarge
}
text: name
font.pixelSize: Theme.fontSizeMedium
truncationMode: TruncationMode.Fade
}
Label {
id: songArtists
anchors {
top: songName.bottom
left: songIndex.right
leftMargin: Theme.paddingLarge
right: parent.right
rightMargin: Theme.horizontalPageMargin
}
text: artists.join(", ")
font.pixelSize: Theme.fontSizeSmall
truncationMode: TruncationMode.Fade
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
}
Label {
id: duration
anchors {
right: parent.right
rightMargin: Theme.horizontalPageMargin
baseline: songName.baseline
}
width: contentWidth
text: Utils.ticksToText(songDelegateRoot.duration)
font.pixelSize: Theme.fontSizeSmall
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
}
}

View file

@ -0,0 +1,60 @@
/*
Sailfin: a Jellyfin client written using Qt
Copyright (C) 2020 Chris Josten
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import ".."
import "../.."
/**
* Album details for "wide" devices such as tablets and phones in landscape mode.
*/
Column {
property ListView listview
property real releaseYear
property alias albumArt: albumArt.source
property string albumArtist
property real duration
property int songCount
property string name
Item { width:1; height: Theme.paddingLarge }
RemoteImage {
id: albumArt
width: parent.width
height: width
fillMode: Image.PreserveAspectFit
}
PageHeader {
id: albumHeader
leftMargin: 0
rightMargin: 0
title: name
//: Short description of the album: %1 -> album artist, %2 -> amount of songs, %3 -> duration, %4 -> release year
description: qsTr("%1\n%2 songs | %3 | %4")
.arg(albumArtist)
.arg(songCount)
.arg(Utils.ticksToText(duration))
//: Unknown album release year
.arg(releaseYear >= 0 ? releaseYear : qsTr("Unknown year"))
}
}

View file

@ -192,6 +192,12 @@ Page {
}
}
Label {
text: Screen.sizeCategory
x: 100
y: 100
}
Component {
id: carrouselView
SilicaListView {

View file

@ -40,7 +40,7 @@ BaseDetailPage {
anchors.fill: parent
model: collectionModel
cellWidth: Constants.libraryDelegateWidth
cellHeight: Utils.usePortraitCover(itemData.CollectionType) ? Constants.libraryDelegatePosterHeight
cellHeight: Utils.usePortraitCover(itemData.collectionType) ? Constants.libraryDelegatePosterHeight
: Constants.libraryDelegateHeight
visible: itemData.status !== JellyfinItem.Error

View file

@ -1,14 +1,36 @@
/*
Sailfin: a Jellyfin client written using Qt
Copyright (C) 2020 Chris Josten
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import QtQuick.Layouts 1.1
import nl.netsoj.chris.Jellyfin 1.0
import "../../components"
import "../../components/music"
import "../.."
BaseDetailPage {
id: albumPageRoot
readonly property int _songIndexWidth: 100
property string _albumArtistText: itemData.albumArtist
width: 800 * Theme.pixelRatio
UserItemModel {
id: collectionModel
@ -18,203 +40,51 @@ BaseDetailPage {
parentId: itemData.jellyfinId
onParentIdChanged: reload()
}
SilicaListView {
id: list
RowLayout {
anchors.fill: parent
model: collectionModel
header: Item {
property string stateIfArt: "largeArt"
property alias albumArt: albumArt
id: listHeader
width: parent.width
//spacing: Theme.paddingLarge
state: albumArt.source != "" ? stateIfArt : "noArt"
MouseArea {
anchors.fill: parent
onClicked: {
if (listHeader.stateIfArt == "largeArt") {
listHeader.stateIfArt = "details"
} else {
listHeader.stateIfArt = "largeArt"
}
}
}
RemoteImage {
id: albumArt
anchors {
top: parent.top
right: parent.right
}
source: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})
sourceSize.width: listHeader.width
sourceSize.height: listHeader.width
fillMode: Image.PreserveAspectFit
opacity: 1
clip: true
}
PageHeader {
id: albumHeader
width: parent.width - Theme.horizontalPageMargin - height
title: itemData.name
description: qsTr("%1\n%2 songs | %3 | %4")
.arg(_albumArtistText)
.arg(itemData.childCount)
.arg(Utils.ticksToText(itemData.runTimeTicks))
.arg(itemData.productionYear > 0 ? itemData.productionYear : qsTr("Unknown year"))
}
states: [
State {
name: "largeArt"
PropertyChanges {
target: albumArt
width: parent.width
height: width
}
PropertyChanges {
target: listHeader
height: width
}
PropertyChanges {
target: albumHeader
opacity: 0
}
PropertyChanges {
target: list
contentY: -list.width
}
AnchorChanges {
target: albumHeader
anchors.left: undefined
anchors.right: albumArt.left
}
},
State {
name: "details"
PropertyChanges {
target: albumArt
width: height
height: albumHeader.height
}
PropertyChanges {
target: listHeader
height: albumHeader.height
}
PropertyChanges {
target: albumHeader
opacity: 1
}
PropertyChanges {
target: list
contentY: -albumHeader.height
}
AnchorChanges {
target: albumHeader
anchors.left: undefined
anchors.right: albumArt.left
}
},
State {
name: "noArt"
extend: "details"
PropertyChanges {
target: albumArt
opacity: 0
}
PropertyChanges {
target: albumHeader
width: parent.width - Theme.horizontalPageMargin * 2
}
AnchorChanges {
target: albumHeader
anchors.left: parent.left
anchors.right: parent.right
}
}
]
transitions: Transition {
OpacityAnimator { target: albumHeader}
OpacityAnimator { target: albumArt}
NumberAnimation {
properties: "width,height,contentY"
//velocity: 1600
duration: 300
easing.type: Easing.OutQuad
}
AnchorAnimation {}
}
Item {height: 1; width: Theme.horizontalPageMargin; visible: wideAlbumCover.visible; }
Loader {
id: wideAlbumCover
visible: albumPageRoot.width / Theme.pixelRatio >= 800
Layout.minimumWidth: 1000 / Theme.pixelRatio
Layout.fillHeight: true
source: visible
? "../../components/music/WideAlbumCover.qml" : ""
onLoaded: bindAlbum(item)
}
section {
property: "parentIndexNumber"
delegate: SectionHeader {
text: qsTr("Disc %1").arg(section)
Item {height: 1; width: Theme.horizontalPageMargin; visible: wideAlbumCover.visible; }
SilicaListView {
id: list
Layout.fillHeight: true
Layout.fillWidth: true
model: collectionModel
header: Loader {
width: parent.width
height: item ? item.height : 0
source: albumPageRoot.width / Theme.pixelRatio < 800
? "../../components/music/NarrowAlbumCover.qml" : ""
onLoaded: bindAlbum(item)
}
section {
property: "parentIndexNumber"
delegate: SectionHeader {
text: qsTr("Disc %1").arg(section)
}
}
delegate: SongDelegate {
name: model.name
artists: model.artists
duration: model.runTimeTicks
indexNumber: model.indexNumber
}
VerticalScrollDecorator {}
}
delegate: ListItem {
contentHeight: songName.height + songArtists.height + 2 * Theme.paddingMedium
width: parent.width
}
Label {
id: songIndex
anchors {
top: parent.top
topMargin: Theme.paddingMedium
left: parent.left
leftMargin: Theme.horizontalPageMargin
}
text: model.indexNumber
horizontalAlignment: Text.AlignRight
font.pixelSize: Theme.fontSizeExtraLarge
width: _songIndexWidth
}
Label {
id: songName
anchors {
left: songIndex.right
leftMargin: Theme.paddingLarge
top: parent.top
topMargin: Theme.paddingMedium
right: duration.left
rightMargin: Theme.paddingLarge
}
text: model.name
font.pixelSize: Theme.fontSizeMedium
truncationMode: TruncationMode.Fade
}
Label {
id: songArtists
anchors {
top: songName.bottom
left: songIndex.right
leftMargin: Theme.paddingLarge
right: parent.right
rightMargin: Theme.horizontalPageMargin
}
text: model.artists.join(", ")
font.pixelSize: Theme.fontSizeSmall
truncationMode: TruncationMode.Fade
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
}
Label {
id: duration
anchors {
right: parent.right
rightMargin: Theme.horizontalPageMargin
baseline: songName.baseline
}
width: contentWidth
text: Utils.ticksToText(model.runTimeTicks)
font.pixelSize: Theme.fontSizeSmall
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
}
}
VerticalScrollDecorator {}
Label {
text: "%1; %2".arg(Theme.pixelRatio).arg(stateIfArt)
}
Connections {
@ -227,4 +97,14 @@ BaseDetailPage {
}
}
}
function bindAlbum(item) {
item.albumArt = Qt.binding(function(){ return Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})})
item.name = Qt.binding(function(){ return itemData.name})
item.releaseYear = Qt.binding(function() { return itemData.productionYear})
item.albumArtist = Qt.binding(function() { return _albumArtistText})
item.duration = Qt.binding(function() { return itemData.runTimeTicks})
item.songCount = Qt.binding(function() { return itemData.childCount})
item.listview = Qt.binding(function() { return list})
}
}