Warning, /multimedia/haruna/src/qml/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 import QtQuick
0008 import QtQuick.Window
0009 import QtQuick.Layouts
0010 import Qt.labs.platform as Platform
0011 import QtQml
0012 
0013 import org.kde.kirigami as Kirigami
0014 import org.kde.haruna
0015 import org.kde.haruna.settings
0016 
0017 Kirigami.ApplicationWindow {
0018     id: window
0019 
0020     property bool containsMouse: false
0021 
0022     property int previousVisibility: Window.Windowed
0023     property var acceptedSubtitleTypes: ["application/x-subrip", "text/x-ssa"]
0024 
0025     visible: true
0026     title: mpv.mediaTitle || i18nc("@title:window", "Haruna")
0027     width: Kirigami.Units.gridUnit * 66
0028     minimumWidth: Kirigami.Units.gridUnit * 36
0029     height: Kirigami.Units.gridUnit * 40
0030     minimumHeight: Kirigami.Units.gridUnit * 22
0031     color: Kirigami.Theme.backgroundColor
0032 
0033     onClosing: app.saveWindowGeometry(window)
0034     onWidthChanged: saveWindowGeometryTimer.restart()
0035     onHeightChanged: saveWindowGeometryTimer.restart()
0036     onXChanged: saveWindowGeometryTimer.restart()
0037     onYChanged: saveWindowGeometryTimer.restart()
0038 
0039     onVisibilityChanged: function(visibility) {
0040         if (PlaybackSettings.pauseWhileMinimized) {
0041             if (visibility === Window.Minimized) {
0042                 if (mpv.pause) {
0043                     mpv.preMinimizePlaybackState = MpvVideo.PlaybackState.Paused
0044                 } else {
0045                     mpv.preMinimizePlaybackState = MpvVideo.PlaybackState.Playing
0046                 }
0047                 mpv.pause = true
0048             }
0049             if (previousVisibility === Window.Minimized
0050                     && visibility === Window.Windowed | Window.Maximized | Window.FullScreen) {
0051                 if (mpv.preMinimizePlaybackState === MpvVideo.PlaybackState.Playing) {
0052                     mpv.pause = false
0053                 }
0054             }
0055         }
0056 
0057         // used to restore window state, when exiting fullscreen,
0058         // to the one it had before going fullscreen
0059         if (visibility !== Window.FullScreen) {
0060             previousVisibility = visibility
0061         }
0062     }
0063 
0064     header: Header { id: header }
0065 
0066     menuBar: MenuBarLoader {
0067         id: menuBarLoader
0068     }
0069 
0070     MpvVideo {
0071         id: mpv
0072 
0073         width: window.contentItem.width
0074         height: window.isFullScreen() ? window.contentItem.height : window.contentItem.height - footer.height
0075         anchors.left: PlaylistSettings.overlayVideo
0076                       ? window.contentItem.left
0077                       : (PlaylistSettings.position === "left" ? playlist.right : window.contentItem.left)
0078         anchors.right: PlaylistSettings.overlayVideo
0079                        ? window.contentItem.right
0080                        : (PlaylistSettings.position === "right" ? playlist.left : window.contentItem.right)
0081         anchors.top: parent.top
0082 
0083         onVideoReconfig: {
0084             resizeWindow()
0085         }
0086 
0087         onAddToRecentFiles: function(url) {
0088             recentFilesModel.addUrl(url)
0089         }
0090 
0091         Osd {
0092             id: osd
0093 
0094             maxWidth: mpv.width
0095         }
0096 
0097         SelectActionPopup {
0098             id: triggerActionPopup
0099 
0100             property int minHeight: mpv.height * 0.5
0101             property int maxHeight: mpv.height * 0.9
0102 
0103             x: mpv.width * 0.5 - width * 0.5
0104             y: Kirigami.Units.largeSpacing
0105             width: Kirigami.Units.gridUnit * 20
0106             height: minHeight < Kirigami.Units.gridUnit * 16 ? maxHeight : minHeight
0107             title: ""
0108             subtitle: ""
0109 
0110             onActionSelected: function (actionName) {
0111                 appActions[actionName].trigger()
0112             }
0113         }
0114     }
0115 
0116     PlayList {
0117         id: playlist
0118 
0119         anchors.top: mpv.top
0120         anchors.bottom: footer.top
0121     }
0122 
0123     Footer {
0124         id: footer
0125 
0126         m_mpv: mpv
0127 
0128         anchors.left: window.contentItem.left
0129         anchors.right: window.contentItem.right
0130         anchors.bottom: window.isFullScreen() ? mpv.bottom : window.contentItem.bottom
0131         state: !window.isFullScreen() || (mpv.mouseY > window.height - footer.implicitHeight && window.containsMouse)
0132                ? "visible" : "hidden"
0133     }
0134 
0135     Actions {}
0136 
0137     ActionsModel {
0138         id: actionsModel
0139     }
0140 
0141     ProxyActionsModel {
0142         id: proxyActionsModel
0143 
0144         sourceModel: actionsModel
0145     }
0146 
0147     CustomCommandsModel {
0148         id: customCommandsModel
0149 
0150         appActionsModel: actionsModel
0151         Component.onCompleted: init()
0152     }
0153 
0154     RecentFilesModel {
0155         id: recentFilesModel
0156     }
0157 
0158     SubtitlesFoldersModel {
0159         id: subtitlesFoldersModel
0160     }
0161 
0162     RowLayout {
0163         width: window.width * 0.8 > Kirigami.Units.gridUnit * 50
0164                ? Kirigami.Units.gridUnit * 50
0165                : window.width * 0.8
0166         anchors.centerIn: parent
0167 
0168         Kirigami.InlineMessage {
0169             id: messageBox
0170 
0171             Layout.fillWidth: true
0172             Layout.fillHeight: true
0173             type: Kirigami.MessageType.Error
0174             showCloseButton: true
0175         }
0176     }
0177 
0178     Loader {
0179         id: mpvContextMenuLoader
0180 
0181         active: false
0182         sourceComponent: ContextMenu {
0183             onClosed: mpvContextMenuLoader.active = false
0184         }
0185     }
0186 
0187     Loader {
0188         id: settingsLoader
0189 
0190         active: false
0191         sourceComponent: SettingsWindow {}
0192     }
0193 
0194     Connections {
0195         target: app
0196         function onQmlApplicationMouseLeave() {
0197             if (PlaylistSettings.canToggleWithMouse && window.isFullScreen()) {
0198                 playlist.state = "hidden"
0199             }
0200             window.containsMouse = false
0201         }
0202         function onQmlApplicationMouseEnter() {
0203             window.containsMouse = true
0204         }
0205         function onError(message) {
0206             messageBox.visible = true
0207             messageBox.text = message
0208         }
0209         function onOpenUrl(url) {
0210             if (GeneralSettings.appendVideoToSingleInstance) {
0211                 mpv.playlistModel.appendItem(url.toString())
0212             } else {
0213                 openFile(url)
0214             }
0215         }
0216     }
0217 
0218     Platform.FileDialog {
0219         id: fileDialog
0220 
0221         property url location: GeneralSettings.fileDialogLocation
0222                                ? app.pathToUrl(GeneralSettings.fileDialogLocation)
0223                                : app.pathToUrl(GeneralSettings.fileDialogLastLocation)
0224 
0225         folder: location
0226         title: i18nc("@title:window", "Select file")
0227         fileMode: Platform.FileDialog.OpenFile
0228 
0229         onAccepted: {
0230             openFile(fileDialog.file.toString(), true)
0231             mpv.focus = true
0232 
0233             GeneralSettings.fileDialogLastLocation = app.parentUrl(fileDialog.file)
0234             GeneralSettings.save()
0235         }
0236         onRejected: mpv.focus = true
0237     }
0238 
0239     Platform.FileDialog {
0240         id: subtitlesFileDialog
0241 
0242         property url location: {
0243             if (mpv.currentUrl) {
0244                 return app.parentUrl(mpv.currentUrl)
0245             } else {
0246                 return (GeneralSettings.fileDialogLocation
0247                 ? app.pathToUrl(GeneralSettings.fileDialogLocation)
0248                 : app.pathToUrl(GeneralSettings.fileDialogLastLocation))
0249             }
0250         }
0251 
0252         folder: location
0253         title: i18nc("@title:window", "Select subtitles file")
0254         fileMode: Platform.FileDialog.OpenFile
0255         nameFilters: ["Subtitles (*.srt *.ssa *.ass)"]
0256 
0257         onAccepted: {
0258             if (acceptedSubtitleTypes.includes(app.mimeType(subtitlesFileDialog.file))) {
0259                 mpv.command(["sub-add", subtitlesFileDialog.file.toString(), "select"])
0260             }
0261         }
0262         onRejected: mpv.focus = true
0263     }
0264 
0265     InputPopup {
0266         id: openUrlPopup
0267 
0268         x: 10
0269         y: 10
0270         lastUrl: GeneralSettings.lastUrl
0271         buttonText: i18nc("@action:button", "Open")
0272 
0273         onUrlOpened: function(url) {
0274             window.openFile(url, true)
0275             GeneralSettings.lastUrl = url
0276             GeneralSettings.save()
0277         }
0278     }
0279 
0280     // This timer allows to batch update the window size change to reduce
0281     // the io load and also work around the fact that x/y/width/height are
0282     // changed when loading the page and overwrite the saved geometry from
0283     // the previous session.
0284     Timer {
0285         id: saveWindowGeometryTimer
0286 
0287         interval: 1000
0288         onTriggered: app.saveWindowGeometry(window)
0289     }
0290 
0291     Component.onCompleted: {
0292         app.restoreWindowGeometry(window)
0293         app.activateColorScheme(GeneralSettings.colorScheme)
0294     }
0295 
0296     function openFile(path, addToRecentFiles = false) {
0297         if (addToRecentFiles) {
0298             recentFilesModel.addUrl(path)
0299         }
0300 
0301         mpv.playlistModel.addItem(path, PlaylistModel.Clear)
0302     }
0303 
0304     function isFullScreen() {
0305         return window.visibility === Window.FullScreen
0306     }
0307 
0308     function toggleFullScreen() {
0309         if (!isFullScreen()) {
0310             window.showFullScreen()
0311         } else {
0312             exitFullscreen()
0313         }
0314     }
0315 
0316     function exitFullscreen() {
0317         if (window.previousVisibility === Window.Maximized) {
0318             window.show()
0319             window.showMaximized()
0320         } else {
0321             window.showNormal()
0322         }
0323     }
0324 
0325     function resizeWindow() {
0326         if (!GeneralSettings.resizeWindowToVideo || isFullScreen()) {
0327             return
0328         }
0329 
0330         window.width = mpv.videoWidth
0331         window.height = mpv.videoHeight
0332                 + (footer.visible ? footer.height : 0)
0333                 + (header.visible ? header.height : 0)
0334                 + (menuBar.visible ? menuBar.height : 0)
0335     }
0336 }