This commit is contained in:
Vyn 2024-04-09 18:46:06 +02:00
commit 1bb1ade305
36 changed files with 2837 additions and 0 deletions

View file

@ -0,0 +1,64 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
Rectangle {
property var anime
property var title: anime.title
property var imageUrl: anime.imageUrl
property var cachedImagePath: anime.cachedImage
property bool cachedImageFound: true
property int imageWidth: (grid.width / grid.columns) - ((grid.columnSpacing * grid.columns) / grid.columns)
Layout.preferredWidth: imageWidth
Layout.preferredHeight: imageWidth * 1.5 + 64
radius: 16
color: "transparent"
ColumnLayout {
Image {
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: parent.parent.width
Layout.preferredHeight: parent.parent.height - 64
fillMode: Image.PreserveAspectCrop
asynchronous: true
source: cachedImagePath
signal failedToLoad
onStatusChanged: {
if (status === Image.Error && cachedImageFound === true) {
source = imageUrl
cachedImageFound = false
}
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: mask
}
}
Text {
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: parent.parent.width
text: title || "[Missing title]"
wrapMode: Text.Wrap
color: "white"
font.pixelSize: 16
font.bold: true
}
Rectangle {
id: mask
width: 500
height: 500
radius: 16
visible: false
}
}
}

View file

@ -0,0 +1,92 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Lali.Types
ColumnLayout {
property Anime anime
anchors.fill: parent
Layout.alignment: Qt.AlignTop
onAnimeChanged: {
image.source = anime.cachedImage
}
RowLayout {
Layout.alignment: Qt.AlignTop
Image {
id: image
property bool cachedImageFound: true
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.preferredHeight: 128 * 1.5
Layout.preferredWidth: 128
fillMode: Image.PreserveAspectCrop
asynchronous: true
source: anime.cachedImage
signal failedToLoad
onStatusChanged: {
if (status === Image.Error && cachedImageFound === true) {
source = anime?.imageUrl
cachedImageFound = false
}
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: mask
}
Rectangle {
id: mask
width: 128
height: 128
radius: 8
visible: false
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.leftMargin: 16
spacing: 32
// Missing status and genres
Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
text: anime?.title || "[Missing title]"
wrapMode: Text.Wrap
color: "white"
font.pixelSize: 24
font.bold: true
}
Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
text: `${anime?.startDay}/${anime?.startMonth}/${anime?.startYear} - ${anime?.endDay}/${anime?.endMonth}/${anime?.endYear}\n${anime?.episodeCount} episode${anime?.episodeCount > 1 ? "s" : ""}`
wrapMode: Text.Wrap
color: "white"
}
}
}
Item { implicitHeight: 16 }
Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
text: anime?.description || "[No description given]"
wrapMode: Text.Wrap
color: "white"
}
}

View file

@ -0,0 +1,132 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Lali.Types
import "../components" as Components
ScrollView {
id: scroller
property AnimeList list
property Anime selectedAnime
property string filterByTitle
anchors.fill: parent
clip : true
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
contentHeight: grid.height
Rectangle {
anchors.fill: parent
anchors.margins: 16
color: "transparent"
GridLayout {
property int animeCardWidth: 256 // Yes, this is an arbitrary number, "because it looks good on my computer"
id: grid
columns: grid.width / animeCardWidth
rows: 300
width: parent.width
columnSpacing: 32
rowSpacing: 32
Repeater {
model: {
if (!list)
return ;
if (filterByTitle) {
return list.animes.filter(anime => anime.title.toLowerCase().includes(filterByTitle.toLowerCase()))
}
return list.animes
}
AnimeCard {
anime: modelData
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
selectedAnime = modelData
if (mouse.button === Qt.LeftButton) {
animeInfo.open()
} else if (mouse.button === Qt.RightButton) {
contextMenu.popup()
}
}
}
}
}
// Autofill blanks
Repeater {
model: {
if (!list) {
return []
}
let blanks = []
for (let i = list.animes.length; i < grid.columns; i++) {
blanks.push(null)
}
return blanks
}
Rectangle {
width: grid.animeCardWidth
}
}
}
}
Components.Modal {
id: animeInfo
width: root.width * 0.75
x: (root.width - width) / 2
y: 128
AnimeInfo {
anime: selectedAnime
}
}
Menu {
id: contextMenu
Menu {
title: "Add to list"
Repeater {
model: _app.animeLists.map(x => x.getName())
MenuItem {
text: modelData
onClicked: {
_app.addAnimeToList(selectedAnime, modelData)
}
}
}
}
Menu {
title: "Move to list"
Repeater {
model: _app.animeLists.map(x => x.getName())
MenuItem {
text: modelData
onClicked: {
_app.moveAnimeToList(selectedAnime, list.name, modelData)
}
}
}
}
MenuItem {
text: "Remove from list"
onClicked: {
_app.removeAnimeFromList(selectedAnime, list.name)
}
}
}
}

View file

@ -0,0 +1,29 @@
import QtQuick
import QtQuick.Controls as QtQuick
QtQuick.Button {
property string textColor: "white"
property string bgColor: "#666666"
text: "Delete"
implicitHeight: 32
contentItem: Text {
text: parent.text
font: parent.font
color: textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
color: bgColor
radius: 4
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
}
}

View file

@ -0,0 +1,6 @@
import QtQuick
Rectangle {
height: 16
color: "transparent"
}

View file

@ -0,0 +1,16 @@
import QtQuick
import QtQuick.Controls
Dialog {
parent: root.contentItem
modal: true
width: root.width * 0.75
x: (root.width - width) / 2
y: 128
padding: 16
background: Rectangle {
color: "#222222"
radius: 16
}
}

View file

@ -0,0 +1,54 @@
import QtQuick
import QtQuick.Controls
Rectangle {
id: button
property string text
property string image
property bool selected
signal clicked
color: ma.hovered || selected ? "#444444" : "transparent"
radius: 8
Image {
// anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 8
sourceSize.width: 48
sourceSize.height: 48
fillMode: Image.PreserveAspectFit
source: image
}
MouseArea {
id: ma
property bool hovered: false
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
button.clicked();
}
hoverEnabled: true
onEntered: {
hovered = true
}
onExited: {
hovered = false
}
}
Text {
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
anchors.horizontalCenter: parent.horizontalCenter
text: button.text
color: "#AAAAAA"
font.pixelSize: 14
}
}

View file

@ -0,0 +1,12 @@
import QtQuick
import QtQuick.Controls as QtQuick
QtQuick.TextField {
property string textColor: "white"
property string bgColor: "#494949"
color : textColor
background: Rectangle {
color: bgColor
radius: 4
}
}