feat: rebuild the interface after Haruna
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54ad4eecf3
commit
614a31db1b
45 files changed
+3818
-798
No files matched your search
+302
-31
@@ -1,44 +1,315 @@
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
// SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import org.kde.ki18n
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.config as KConfig
|
||||
|
||||
// Main window, laid out like Haruna: menu bar, toolbar, video with the playlist at
|
||||
// its edge, and the playback controls below.
|
||||
ApplicationWindow {
|
||||
id: appWindow
|
||||
|
||||
// Set from C++ (main.cpp) when a file is passed on the command line.
|
||||
required property url initialUrl
|
||||
|
||||
width: Kirigami.Units.gridUnit * 50
|
||||
height: Kirigami.Units.gridUnit * 32
|
||||
minimumWidth: Kirigami.Units.gridUnit * 20
|
||||
minimumHeight: Kirigami.Units.gridUnit * 14
|
||||
property int previousVisibility: Window.Windowed
|
||||
readonly property bool isFullScreen: visibility === Window.FullScreen
|
||||
|
||||
title: player.mediaTitle.length > 0 ? player.mediaTitle : "QtPotato"
|
||||
|
||||
pageStack.initialPage: PlayerPage {
|
||||
id: player
|
||||
initialUrl: root.initialUrl
|
||||
fullScreen: root.visibility === Window.FullScreen
|
||||
onAboutRequested: root.pageStack.layers.push(aboutPage)
|
||||
onSettingsRequested: root.pageStack.layers.push(settingsPage)
|
||||
onFullScreenToggled: root.visibility = root.visibility === Window.FullScreen ? Window.Windowed : Window.FullScreen
|
||||
function openFile(url: url) {
|
||||
playlistModel.openFile(url, QtPotatoSettings.playlistLoadSiblings)
|
||||
}
|
||||
|
||||
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar
|
||||
pageStack.columnView.columnResizeMode: Kirigami.ColumnView.SingleColumn
|
||||
|
||||
onClosing: QtPotatoSettings.save()
|
||||
|
||||
Component {
|
||||
id: settingsPage
|
||||
SettingsPage {
|
||||
translator: player.translator
|
||||
mpvItem: player.mpvItem
|
||||
function toggleFullScreen() {
|
||||
if (isFullScreen) {
|
||||
exitFullScreen()
|
||||
} else {
|
||||
showFullScreen()
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: aboutPage
|
||||
FormCard.AboutPage {}
|
||||
function exitFullScreen() {
|
||||
if (previousVisibility === Window.Maximized) {
|
||||
showMaximized()
|
||||
} else {
|
||||
showNormal()
|
||||
}
|
||||
}
|
||||
|
||||
function translateCurrentSubtitle() {
|
||||
const track = video.subtitleTracks.find(track => track.id === video.subtitleId)
|
||||
if (track) {
|
||||
translationService.translate(video.currentUrl, track, video.mediaTitle)
|
||||
}
|
||||
}
|
||||
|
||||
visible: true
|
||||
title: video.mediaTitle || KI18n.i18nc("@title:window", "QtPotato")
|
||||
width: 1000
|
||||
height: 600
|
||||
minimumWidth: 400
|
||||
minimumHeight: 200
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
onVisibilityChanged: visibility => {
|
||||
// Restored when leaving full screen.
|
||||
if (visibility !== Window.FullScreen) {
|
||||
previousVisibility = visibility
|
||||
}
|
||||
}
|
||||
|
||||
onClosing: QtPotatoSettings.save()
|
||||
|
||||
Component.onCompleted: {
|
||||
if (initialUrl.toString().length > 0) {
|
||||
openFile(initialUrl)
|
||||
}
|
||||
}
|
||||
|
||||
KConfig.WindowStateSaver {
|
||||
configGroupName: "MainWindow"
|
||||
}
|
||||
|
||||
menuBar: MainMenuBar {
|
||||
actions: appActions
|
||||
mpv: video
|
||||
visible: !appWindow.isFullScreen && QtPotatoSettings.showMenuBar
|
||||
}
|
||||
|
||||
header: Header {
|
||||
id: headerItem
|
||||
actions: appActions
|
||||
mpv: video
|
||||
translator: translationService
|
||||
shown: !appWindow.isFullScreen && QtPotatoSettings.showHeader
|
||||
menuBarShown: appWindow.menuBar.visible
|
||||
}
|
||||
|
||||
Actions {
|
||||
id: appActions
|
||||
mpv: video
|
||||
playlist: playlistModel
|
||||
translator: translationService
|
||||
osd: osdItem
|
||||
fullscreen: appWindow.isFullScreen
|
||||
shortcutsEnabled: !fileDialog.visible && !subtitleDialog.visible && !addToPlaylistDialog.visible && !openUrlPopup.visible
|
||||
|
||||
onOpenFileRequested: fileDialog.open()
|
||||
onFileOpenRequested: url => appWindow.openFile(url)
|
||||
onOpenUrlRequested: openUrlPopup.open()
|
||||
onAddSubtitleRequested: subtitleDialog.open()
|
||||
onSettingsRequested: page => settingsWindow.openPage(page)
|
||||
onFullscreenToggleRequested: appWindow.toggleFullScreen()
|
||||
onFullscreenExitRequested: appWindow.exitFullScreen()
|
||||
onPlaylistToggleRequested: playlistPanel.toggle()
|
||||
onContextMenuRequested: contextMenu.popup()
|
||||
onTranslateRequested: appWindow.translateCurrentSubtitle()
|
||||
onMenuBarToggleRequested: {
|
||||
QtPotatoSettings.showMenuBar = !QtPotatoSettings.showMenuBar
|
||||
QtPotatoSettings.save()
|
||||
}
|
||||
onHeaderToggleRequested: {
|
||||
QtPotatoSettings.showHeader = !QtPotatoSettings.showHeader
|
||||
QtPotatoSettings.save()
|
||||
}
|
||||
}
|
||||
|
||||
PlaylistModel {
|
||||
id: playlistModel
|
||||
onPlayRequested: url => {
|
||||
video.loadFile(url)
|
||||
RecentFilesModel.add(url)
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: RecentFilesModel
|
||||
property: "maxCount"
|
||||
value: QtPotatoSettings.maxRecentFiles
|
||||
}
|
||||
|
||||
MprisItem {
|
||||
mpvItem: video
|
||||
playlist: playlistModel
|
||||
window: appWindow
|
||||
onRaiseRequested: {
|
||||
appWindow.show()
|
||||
appWindow.raise()
|
||||
appWindow.requestActivate()
|
||||
}
|
||||
onOpenUriRequested: uri => appWindow.openFile(uri)
|
||||
onNextRequested: appActions.playNext()
|
||||
onPreviousRequested: appActions.playPrevious()
|
||||
}
|
||||
|
||||
TranslationService {
|
||||
id: translationService
|
||||
onTranslated: (subtitleUrl, fromCache) => {
|
||||
video.showTranslatedSubtitleFile(subtitleUrl,
|
||||
KI18n.i18nc("@item:inlistbox subtitle track, %1 is a language", "%1 (translated)", QtPotatoSettings.targetLanguage))
|
||||
osdItem.message(fromCache
|
||||
? KI18n.i18nc("@info:osd", "Loaded the saved translation")
|
||||
: KI18n.i18nc("@info:osd", "Translation ready"))
|
||||
}
|
||||
}
|
||||
|
||||
MpvVideo {
|
||||
id: video
|
||||
|
||||
actions: appActions
|
||||
fullscreen: appWindow.isFullScreen
|
||||
|
||||
readonly property bool playlistBeside: playlistPanel.visible && !QtPotatoSettings.playlistOverlayVideo
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: playlistBeside && playlistPanel.edge === Qt.LeftEdge ? playlistPanel.right : parent.left
|
||||
anchors.right: playlistBeside && playlistPanel.edge === Qt.RightEdge ? playlistPanel.left : parent.right
|
||||
height: parent.height - (footerItem.isDocked ? footerItem.height : 0)
|
||||
|
||||
onPointerMoved: (x, y) => {
|
||||
footerItem.pointerMoved(y)
|
||||
if (!QtPotatoSettings.playlistToggleWithMouse) {
|
||||
return
|
||||
}
|
||||
const atEdge = playlistPanel.edge === Qt.RightEdge ? x > width - 50 : x < 50
|
||||
playlistPanel.state = atEdge ? "visible" : "hidden"
|
||||
}
|
||||
onPointerLeft: footerItem.pointerLeft()
|
||||
onFilesDropped: (urls, append) => {
|
||||
if (append) {
|
||||
playlistModel.append(urls)
|
||||
} else if (urls.length === 1) {
|
||||
appWindow.openFile(urls[0])
|
||||
} else {
|
||||
playlistModel.replace(urls)
|
||||
}
|
||||
}
|
||||
onSubtitleDropped: url => video.addSubtitleFile(url)
|
||||
|
||||
onEndOfFile: {
|
||||
switch (QtPotatoSettings.playbackBehavior) {
|
||||
case "RepeatItem":
|
||||
video.seek(0)
|
||||
video.paused = false
|
||||
break
|
||||
case "StopAfterItem":
|
||||
break
|
||||
default:
|
||||
appActions.playNext()
|
||||
}
|
||||
}
|
||||
|
||||
// A translation belongs to the file it was started for.
|
||||
onCurrentUrlChanged: translationService.cancel()
|
||||
|
||||
Osd {
|
||||
id: osdItem
|
||||
maxWidth: video.width
|
||||
}
|
||||
}
|
||||
|
||||
Playlist {
|
||||
id: playlistPanel
|
||||
playlist: playlistModel
|
||||
mpv: video
|
||||
mainWindowWidth: appWindow.width
|
||||
fullscreen: appWindow.isFullScreen
|
||||
height: video.height
|
||||
onAddFilesRequested: addToPlaylistDialog.open()
|
||||
}
|
||||
|
||||
Footer {
|
||||
id: footerItem
|
||||
actions: appActions
|
||||
mpv: video
|
||||
fullscreen: appWindow.isFullScreen
|
||||
showMenuButton: !headerItem.visible && !appWindow.menuBar.visible
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
width: Math.min(Kirigami.Units.gridUnit * 40, appWindow.width * 0.8)
|
||||
anchors.centerIn: parent
|
||||
type: Kirigami.MessageType.Error
|
||||
text: translationService.errorText
|
||||
visible: text.length > 0
|
||||
showCloseButton: true
|
||||
onVisibleChanged: if (!visible) translationService.clearError()
|
||||
actions: Kirigami.Action {
|
||||
icon.name: "configure"
|
||||
text: KI18n.i18nc("@action:button", "Translation Settings")
|
||||
onTriggered: settingsWindow.openPage("translation")
|
||||
}
|
||||
}
|
||||
|
||||
MpvContextMenu {
|
||||
id: contextMenu
|
||||
actions: appActions
|
||||
mpv: video
|
||||
}
|
||||
|
||||
SettingsWindow {
|
||||
id: settingsWindow
|
||||
mpv: video
|
||||
translator: translationService
|
||||
}
|
||||
|
||||
InputPopup {
|
||||
id: openUrlPopup
|
||||
x: Kirigami.Units.largeSpacing
|
||||
y: Kirigami.Units.largeSpacing
|
||||
width: Math.min(appWindow.width * 0.9, 600)
|
||||
lastText: QtPotatoSettings.lastUrl
|
||||
placeholderText: "https://"
|
||||
buttonText: KI18n.i18nc("@action:button", "Open")
|
||||
onSubmitted: text => {
|
||||
QtPotatoSettings.lastUrl = text
|
||||
QtPotatoSettings.save()
|
||||
appWindow.openFile(text)
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
title: KI18n.i18nc("@title:window", "Select File")
|
||||
currentFolder: QtPotatoSettings.fileDialogLastLocation
|
||||
fileMode: FileDialog.OpenFile
|
||||
nameFilters: [
|
||||
KI18n.i18nc("@item:inlistbox file filter", "Video files (*.mkv *.mp4 *.webm *.avi *.mov *.m4v *.ts *.flv *.wmv)"),
|
||||
KI18n.i18nc("@item:inlistbox file filter", "All files (*)")
|
||||
]
|
||||
onAccepted: {
|
||||
appWindow.openFile(selectedFile)
|
||||
QtPotatoSettings.fileDialogLastLocation = currentFolder
|
||||
QtPotatoSettings.save()
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: addToPlaylistDialog
|
||||
title: KI18n.i18nc("@title:window", "Add to Playlist")
|
||||
currentFolder: QtPotatoSettings.fileDialogLastLocation
|
||||
fileMode: FileDialog.OpenFiles
|
||||
onAccepted: playlistModel.append(selectedFiles)
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: subtitleDialog
|
||||
title: KI18n.i18nc("@title:window", "Select Subtitles File")
|
||||
currentFolder: {
|
||||
const url = video.currentUrl.toString()
|
||||
return url.substring(0, url.lastIndexOf("/"))
|
||||
}
|
||||
fileMode: FileDialog.OpenFile
|
||||
nameFilters: [
|
||||
KI18n.i18nc("@item:inlistbox file filter", "Subtitle files (*.srt *.ass *.ssa *.vtt *.sub *.sup)"),
|
||||
KI18n.i18nc("@item:inlistbox file filter", "All files (*)")
|
||||
]
|
||||
onAccepted: video.addSubtitleFile(selectedFile)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user