import QtQuick 2.11 import QtQuick.Controls 2.4 import QtQuick.Dialogs 1.3 import QtQuick.Layouts 1.11 import QtQuick.Window 2.11 import Qt.labs.settings 1.0 import Qt.labs.platform 1.0 as LabsPlatform import player 1.0 import "codes.js" as LanguageCodes ApplicationWindow { id: mainWindow title: titleLabel.text visible: true width: 720 height: 480 Translator { id: translate } property int lastScreenVisibility function toggleFullscreen() { if (mainWindow.visibility != Window.FullScreen) { lastScreenVisibility = mainWindow.visibility mainWindow.visibility = Window.FullScreen } else { mainWindow.visibility = lastScreenVisibility } } function tracksMenuUpdate() { subModel.clear() audioModel.clear() vidModel.clear() var newTracks = player.getTracks() for (var i = 0, len = newTracks.length; i < len; i++) { var track = newTracks[i] var trackID = track["id"] var trackType = track["type"] var trackLang = LanguageCodes.localeCodeToEnglish( String(track["lang"])) var trackTitle = track["title"] if (trackType == "sub") { subModel.append({ key: trackLang, value: trackID }) if (track["selected"]) { subList.currentIndex = subList.count - 1 } } else if (trackType == "audio") { audioModel.append({ key: (trackTitle === undefined ? "" : trackTitle + " ") + trackLang, value: trackID }) if (track["selected"]) { audioList.currentIndex = audioList.count - 1 } } else if (trackType == "video") { vidModel.append({ key: "Video " + trackID, value: trackID }) if (track["selected"]) { vidList.currentIndex = vidList.count - 1 } } } } PlayerBackend { id: player anchors.fill: parent width: parent.width height: parent.height Settings { id: appearance category: "Appearance" property bool titleOnlyOnFullscreen: true property bool clickToPause: true property bool useMpvSubs: false property string fontName: "Roboto" } Settings { id: i18n category: "I18N" property string language: "english" } Settings { id: fun category: "Fun" property bool nyanCat: false } function startPlayer() { var args = Qt.application.arguments var len = Qt.application.arguments.length var argNo = 0 if (!appearance.useMpvSubs) { player.setOption("sub-font", "Noto Sans") player.setOption("sub-font-size", "24") player.setOption("sub-ass-override", "force") player.setOption("sub-ass", "off") player.setOption("sub-border-size", "0") player.setOption("sub-bold", "off") player.setOption("sub-scale-by-window", "on") player.setOption("sub-scale-with-window", "on") player.setOption("sub-color", "0.0/0.0/0.0/0.0") player.setOption("sub-border-color", "0.0/0.0/0.0/0.0") player.setOption("sub-back-color", "0.0/0.0/0.0/0.0") } player.setOption("ytdl-format", "bestvideo[width<=" + Screen.width + "][height<=" + Screen.height + "]+bestaudio") if (len > 1) { for (argNo = 1; argNo < len; argNo++) { var argument = args[argNo] if (argument.indexOf("KittehPlayer") !== -1) { continue } if (argument.startsWith("--")) { argument = argument.substr(2) if (argument.length > 0) { var splitArg = argument.split(/=(.+)/) if (splitArg[0] == "fullscreen") { toggleFullscreen() } else { if (splitArg[1].length == 0) { splitArg[1] = "true" } player.setOption(splitArg[0], splitArg[1]) } } } else { player.loadFile(argument) } } } } function setProgressBarEnd(val) { progressBar.to = val } function setProgressBarValue(val) { progressBar.value = val timeLabel.text = player.createTimestamp(val) + " / " + player.createTimestamp( progressBar.to) + " (" + parseFloat( player.getProperty("speed").toFixed(2)) + "x)" } function skipToNinth(val) { var skipto = 0 if (val != 0) { skipto = Math.floor(progressBar.to / 9 * val) } player.command(["seek", skipto, "absolute"]) } function updatePrev(val) { if (val != 0) { playlistPrevButton.visible = true playlistPrevButton.width = playPauseButton.width } else { playlistPrevButton.visible = false playlistPrevButton.width = 0 } } function updateVolume(volume) { var muted = player.getProperty("mute") if (muted || volume === 0) { volumeButton.icon.source = "qrc:/player/icons/volume-mute.svg" } else { if (volume < 25) { volumeButton.icon.source = "qrc:/player/icons/volume-down.svg" } else { volumeButton.icon.source = "qrc:/player/icons/volume-up.svg" } } } function updateMuted(muted) { if (muted) { volumeButton.icon.source = "qrc:/player/icons/volume-mute.svg" } } function updatePlayPause() { var paused = player.getProperty("pause") if (paused) { playPauseButton.icon.source = "qrc:/player/icons/play.svg" } else { playPauseButton.icon.source = "qrc:/player/icons/pause.svg" } } function setTitle(title) { titleLabel.text = title } function setSubtitles(subs) { nativeSubs.text = subs } function isAnyMenuOpen() { return subtitlesMenu.visible || settingsMenu.visible || fileMenuBarItem.opened || playbackMenuBarItem.opened || viewMenuBarItem.opened || audioMenuBarItem.opened || videoMenuBarItem.opened || subsMenuBarItem.opened || aboutMenuBarItem.opened } function hideControls(force) { if (!isAnyMenuOpen() || force) { controlsBar.visible = false controlsBackground.visible = false titleBar.visible = false titleBackground.visible = false menuBar.visible = false } } function showControls() { if (!controlsBar.visible) { controlsBar.visible = true controlsBackground.visible = true if (appearance.titleOnlyOnFullscreen) { if (mainWindow.visibility == Window.FullScreen) { titleBar.visible = true } } else { titleBar.visible = true } titleBackground.visible = true menuBar.visible = true } } LabsPlatform.FileDialog { id: screenshotSaveDialog title: translate.getTranslation("SAVE_SCREENSHOT", i18n.language) fileMode: LabsPlatform.FileDialog.SaveFile defaultSuffix: "png" nameFilters: ["Images (*.png)", "All files (*)"] onAccepted: { player.grabToImage(function (result) { var filepath = String(screenshotSaveDialog.file).replace( "file://", '') result.saveToFile(filepath) subtitlesBar.visible = appearance.useMpvSubs ? false : true }) } } LabsPlatform.FileDialog { id: fileDialog title: translate.getTranslation("OPEN_FILE", i18n.language) nameFilters: ["All files (*)"] onAccepted: { player.loadFile(String(fileDialog.file)) fileDialog.close() } onRejected: { fileDialog.close() } } Dialog { id: loadDialog title: translate.getTranslation("URL_FILE_PATH", i18n.language) standardButtons: StandardButton.Cancel | StandardButton.Open onAccepted: { player.loadFile(pathText.text) pathText.text = "" } TextField { id: pathText placeholderText: translate.getTranslation("URL_FILE_PATH", i18n.language) } } MouseArea { id: mouseAreaBar x: 0 y: parent.height width: parent.width height: (controlsBar.height * 2) + progressBar.height anchors.bottom: parent.bottom anchors.bottomMargin: 0 hoverEnabled: true onEntered: { mouseAreaPlayerTimer.stop() } } MouseArea { id: mouseAreaPlayer z: 1000 focus: true width: parent.width anchors.bottom: mouseAreaBar.top anchors.bottomMargin: 10 anchors.right: parent.right anchors.rightMargin: 0 anchors.left: parent.left anchors.leftMargin: 0 anchors.top: titleBar.bottom anchors.topMargin: 0 hoverEnabled: true cursorShape: controlsBar.visible ? Qt.ArrowCursor : Qt.BlankCursor onClicked: { if (appearance.clickToPause) { player.togglePlayPause() } } Timer { id: mouseAreaPlayerTimer interval: 1000 running: true repeat: false onTriggered: { player.hideControls() } } onPositionChanged: { player.showControls() mouseAreaPlayerTimer.restart() } } Settings { id: keybinds category: "Keybinds" property string playPause: "K" property string forward10: "L" property string rewind10: "J" property string forward5: "Right" property string rewind5: "Left" property string openFile: "Ctrl+O" property string openURI: "Ctrl+Shift+O" property string quit: "Ctrl+Q" property string fullscreen: "F" property string tracks: "Ctrl+T" property string statsForNerds: "I" property string forwardFrame: "." property string backwardFrame: "," property string cycleSub: "Alt+S" property string cycleSubBackwards: "Alt+Shift+S" property string cycleAudio: "A" property string cycleVideo: "V" property string cycleVideoAspect: "Shift+A" property string screenshot: "S" property string screenshotWithoutSubtitles: "Shift+S" property string fullScreenshot: "Ctrl+S" property string nyanCat: "Ctrl+N" property string decreaseSpeedBy10Percent: "[" property string increaseSpeedBy10Percent: "]" property string halveSpeed: "{" property string doubleSpeed: "}" property string increaseVolume: "*" property string decreaseVolume: "/" property string mute: "m" property string customKeybind0: "" property string customKeybind0Command: "" property string customKeybind1: "" property string customKeybind1Command: "" property string customKeybind2: "" property string customKeybind2Command: "" property string customKeybind3: "" property string customKeybind3Command: "" property string customKeybind4: "" property string customKeybind4Command: "" property string customKeybind5: "" property string customKeybind5Command: "" property string customKeybind6: "" property string customKeybind6Command: "" property string customKeybind7: "" property string customKeybind7Command: "" property string customKeybind8: "" property string customKeybind8Command: "" property string customKeybind9: "" property string customKeybind9Command: "" } MenuBar { id: menuBar //width: parent.width height: Math.max(24, Screen.height / 32) delegate: MenuBarItem { id: menuBarItem padding: 4 topPadding: padding leftPadding: padding rightPadding: padding bottomPadding: padding contentItem: Text { id: menuBarItemText text: menuBarItem.text font.family: appearance.fontName font.pixelSize: 14 font.bold: menuBarItem.highlighted opacity: 1 color: menuBarItem.highlighted ? "#5a50da" : "white" horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight renderType: Text.NativeRendering } background: Rectangle { implicitWidth: 10 implicitHeight: 10 opacity: 1 color: menuBarItem.highlighted ? "#c0c0f0" : "transparent" } } background: Rectangle { width: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } Menu { id: fileMenuBarItem title: translate.getTranslation("FILE_MENU", i18n.language) font.family: appearance.fontName width: 300 background: Rectangle { implicitWidth: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } delegate: CustomMenuItem { } Action { text: translate.getTranslation("OPEN_FILE", i18n.language) onTriggered: fileDialog.open() shortcut: keybinds.openFile } Action { text: translate.getTranslation("OPEN_URL", i18n.language) onTriggered: loadDialog.open() shortcut: keybinds.openURI } Action { text: translate.getTranslation("SCREENSHOT", i18n.language) onTriggered: { player.hideControls(true) screenshotSaveDialog.open() } shortcut: keybinds.screenshot } Action { text: translate.getTranslation( "SCREENSHOT_WITHOUT_SUBTITLES", i18n.language) onTriggered: { player.hideControls(true) subtitlesBar.visible = false screenshotSaveDialog.open() } shortcut: keybinds.screenshotWithoutSubtitles } Action { text: translate.getTranslation("FULL_SCREENSHOT", i18n.language) onTriggered: { screenshotSaveDialog.open() } shortcut: keybinds.fullScreenshot } Action { text: translate.getTranslation("EXIT", i18n.language) onTriggered: Qt.quit() shortcut: keybinds.quit } } Menu { id: playbackMenuBarItem title: translate.getTranslation("PLAYBACK", i18n.language) width: 300 background: Rectangle { implicitWidth: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } delegate: CustomMenuItem { width: parent.width } Action { text: translate.getTranslation("PLAY_PAUSE", i18n.language) onTriggered: { player.togglePlayPause() } shortcut: String(keybinds.playPause) } Action { text: translate.getTranslation("REWIND_10S", i18n.language) onTriggered: { player.seek("-10") } shortcut: keybinds.rewind10 } Action { text: translate.getTranslation("FORWARD_10S", i18n.language) onTriggered: { player.seek("10") } shortcut: keybinds.forward10 } Action { text: translate.getTranslation("FORWARD_5S", i18n.language) onTriggered: { player.seek("-5") } shortcut: keybinds.rewind5 } Action { text: translate.getTranslation("FORWARD_5S", i18n.language) onTriggered: { player.seek("5") } shortcut: keybinds.forward5 } Action { text: translate.getTranslation("SPEED_DECREASE_10PERCENT", i18n.language) onTriggered: { player.command(["multiply", "speed", "1/1.1"]) } shortcut: keybinds.decreaseSpeedBy10Percent } Action { text: translate.getTranslation("SPEED_INCREASE_10PERCENT", i18n.language) onTriggered: { player.command(["multiply", "speed", "1.1"]) } shortcut: keybinds.increaseSpeedBy10Percent } Action { text: translate.getTranslation("HALVE_SPEED", i18n.language) onTriggered: { player.command(["multiply", "speed", "0.5"]) } shortcut: keybinds.halveSpeed } Action { text: translate.getTranslation("DOUBLE_SPEED", i18n.language) onTriggered: { player.command(["multiply", "speed", "2.0"]) } shortcut: keybinds.doubleSpeed } Action { text: translate.getTranslation("FORWARD_FRAME", i18n.language) onTriggered: { player.command(["frame-step"]) } shortcut: keybinds.forwardFrame } Action { text: translate.getTranslation("BACKWARD_FRAME", i18n.language) onTriggered: { player.command(["frame-back-step"]) } shortcut: keybinds.backwardFrame } } Menu { id: audioMenuBarItem title: translate.getTranslation("AUDIO", i18n.language) width: 300 background: Rectangle { implicitWidth: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } delegate: CustomMenuItem { width: parent.width } Action { text: translate.getTranslation("CYCLE_AUDIO_TRACK", i18n.language) onTriggered: { player.nextAudioTrack() } shortcut: keybinds.cycleAudio } Action { text: translate.getTranslation("INCREASE_VOLUME", i18n.language) onTriggered: { player.addVolume("2") } shortcut: keybinds.increaseVolume } Action { text: translate.getTranslation("DECREASE_VOLUME", i18n.language) onTriggered: { player.addVolume("-2") } shortcut: keybinds.decreaseVolume } Action { text: translate.getTranslation("MUTE_VOLUME", i18n.language) onTriggered: { player.toggleMute() } shortcut: keybinds.mute } } Menu { id: videoMenuBarItem title: translate.getTranslation("VIDEO", i18n.language) width: 300 background: Rectangle { implicitWidth: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } delegate: CustomMenuItem { width: parent.width } Action { text: translate.getTranslation("CYCLE_VIDEO", i18n.language) onTriggered: { player.nextVideoTrack() } shortcut: keybinds.cycleVideo } } Menu { id: subsMenuBarItem title: translate.getTranslation("SUBTITLES", i18n.language) width: 300 background: Rectangle { implicitWidth: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } delegate: CustomMenuItem { width: parent.width } Action { text: translate.getTranslation("CYCLE_SUB_TRACK", i18n.language) onTriggered: { player.nextSubtitleTrack() } shortcut: keybinds.cycleSub } Action { text: translate.getTranslation("TOGGLE_MPV_SUBS", i18n.language) onTriggered: { appearance.useMpvSubs = !appearance.useMpvSubs } shortcut: keybinds.cycleSubBackwards } } Menu { id: viewMenuBarItem title: translate.getTranslation("VIEW", i18n.language) width: 300 background: Rectangle { implicitWidth: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } delegate: CustomMenuItem { width: parent.width } Action { text: translate.getTranslation("FULLSCREEN", i18n.language) onTriggered: { toggleFullscreen() } shortcut: keybinds.fullscreen } Action { text: translate.getTranslation("TRACK_MENU", i18n.language) onTriggered: { tracksMenuUpdate() subtitlesMenu.visible = !subtitlesMenu.visible subtitlesMenuBackground.visible = !subtitlesMenuBackground.visible } shortcut: keybinds.tracks } Action { text: translate.getTranslation("STATS", i18n.language) onTriggered: { player.command( ["script-binding", "stats/display-stats-toggle"]) } shortcut: keybinds.statsForNerds } Action { text: translate.getTranslation("TOGGLE_NYAN_CAT", i18n.language) onTriggered: { fun.nyanCat = !fun.nyanCat } shortcut: keybinds.nyanCat } } Menu { id: aboutMenuBarItem title: translate.getTranslation("ABOUT", i18n.language) width: 300 background: Rectangle { implicitWidth: parent.width implicitHeight: 10 color: "black" opacity: 0.6 } delegate: CustomMenuItem { width: parent.width } Action { text: translate.getTranslation("ABOUT_QT", i18n.language) onTriggered: { player.launchAboutQt() } } } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "1" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "2" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "3" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "4" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "5" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "6" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "7" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "8" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "9" } Action { onTriggered: player.skipToNinth(parseInt(shortcut)) shortcut: "0" } Action { onTriggered: player.command(keybinds.customKeybind0Command) shortcut: keybinds.customKeybind0 } Action { onTriggered: player.command(keybinds.customKeybind1Command) shortcut: keybinds.customKeybind1 } Action { onTriggered: player.command(keybinds.customKeybind2Command) shortcut: keybinds.customKeybind2 } Action { onTriggered: player.command(keybinds.customKeybind3Command) shortcut: keybinds.customKeybind3 } Action { onTriggered: player.command(keybinds.customKeybind4Command) shortcut: keybinds.customKeybind4 } Action { onTriggered: player.command(keybinds.customKeybind5Command) shortcut: keybinds.customKeybind5 } Action { onTriggered: player.command(keybinds.customKeybind6Command) shortcut: keybinds.customKeybind6 } Action { onTriggered: player.command(keybinds.customKeybind7Command) shortcut: keybinds.customKeybind7 } Action { onTriggered: player.command(keybinds.customKeybind8Command) shortcut: keybinds.customKeybind8 } Action { onTriggered: player.command(keybinds.customKeybind9Command) shortcut: keybinds.customKeybind9 } } Rectangle { id: subtitlesMenuBackground anchors.fill: subtitlesMenu Layout.fillWidth: true Layout.fillHeight: true visible: false color: "black" opacity: 0.6 } Rectangle { id: subtitlesMenu color: "transparent" width: controlsBar.width / 2 height: childrenRect.height visible: false z: 90000 anchors.centerIn: player border.color: "black" border.width: 2 Text { id: audioLabel anchors.left: parent.left anchors.right: parent.right text: translate.getTranslation("AUDIO", i18n.language) color: "white" font.family: appearance.fontName font.pixelSize: 14 horizontalAlignment: Text.AlignHCenter opacity: 1 } ComboBox { id: audioList textRole: "key" width: parent.width anchors.top: audioLabel.bottom model: ListModel { id: audioModel } onActivated: { player.command(["set", "aid", String(audioModel.get( index).value)]) } opacity: 1 } Text { id: subLabel anchors.left: parent.left anchors.right: parent.right text: translate.getTranslation("SUBTITLES", i18n.language) color: "white" font.family: appearance.fontName font.pixelSize: 14 anchors.top: audioList.bottom horizontalAlignment: Text.AlignHCenter opacity: 1 } ComboBox { id: subList textRole: "key" width: parent.width anchors.top: subLabel.bottom model: ListModel { id: subModel } onActivated: { player.command(["set", "sid", String(subModel.get( index).value)]) } opacity: 1 } Text { id: vidLabel anchors.left: parent.left anchors.right: parent.right text: translate.getTranslation("VIDEO", i18n.language) color: "white" font.family: appearance.fontName font.pixelSize: 14 anchors.top: subList.bottom horizontalAlignment: Text.AlignHCenter opacity: 1 } ComboBox { id: vidList textRole: "key" width: parent.width anchors.top: vidLabel.bottom model: ListModel { id: vidModel } onActivated: { player.command(["set", "vid", String(vidModel.get( index).value)]) } opacity: 1 } } Rectangle { id: titleBackground height: titleBar.height anchors.top: titleBar.top anchors.left: titleBar.left anchors.right: titleBar.right Layout.fillWidth: true Layout.fillHeight: true color: "black" opacity: 0.6 } Rectangle { id: titleBar height: menuBar.height anchors.right: parent.right anchors.left: menuBar.right anchors.top: parent.top visible: !appearance.titleOnlyOnFullscreen color: "transparent" Text { id: titleLabel text: translate.getTranslation("TITLE", i18n.language) color: "white" width: parent.width height: parent.height anchors.left: parent.left anchors.leftMargin: 10 anchors.bottom: parent.bottom topPadding: 4 bottomPadding: 4 anchors.top: parent.top font.family: appearance.fontName font.pixelSize: 14 font.bold: true opacity: 1 } } Rectangle { id: controlsBackground height: controlsBar.visible ? controlsBar.height + progressBackground.height + (progressBar.topPadding * 2) - (progressBackground.height * 2) : 0 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right Layout.fillWidth: true Layout.fillHeight: true color: "black" opacity: 0.6 } Rectangle { id: subtitlesBar visible: !appearance.useMpvSubs color: "transparent" height: player.height / 8 anchors.bottom: controlsBackground.top anchors.bottomMargin: 5 anchors.right: parent.right anchors.left: parent.left RowLayout { id: nativeSubtitles visible: true anchors.left: subtitlesBar.left anchors.right: subtitlesBar.right height: childrenRect.height anchors.bottom: parent.bottom anchors.bottomMargin: 10 Rectangle { id: subsContainer Layout.fillWidth: true Layout.fillHeight: true Layout.rightMargin: 0 Layout.leftMargin: 0 Layout.maximumWidth: nativeSubtitles.width color: "transparent" height: childrenRect.height Label { id: nativeSubs onWidthChanged: { if (width > parent.width - 10) width = parent.width - 10 } onTextChanged: if (width <= parent.width - 10) width = undefined color: "white" anchors.horizontalCenter: parent.horizontalCenter wrapMode: Text.WrapAtWordBoundaryOrAnywhere font.pixelSize: Screen.height / 24 font.family: appearance.fontName horizontalAlignment: Text.AlignHCenter opacity: 1 background: Rectangle { id: subsBackground color: Qt.rgba(0, 0, 0, 0.6) width: subsContainer.childrenRect.width height: subsContainer.childrenRect.height } } } } } function setCachedDuration(val) { cachedLength.width = ((progressBar.width / progressBar.to) * val) - progressLength.width } Rectangle { id: controlsBar height: controlsBar.visible ? Screen.height / 24 : 0 anchors.right: parent.right anchors.rightMargin: parent.width / 128 anchors.left: parent.left anchors.leftMargin: parent.width / 128 anchors.bottom: parent.bottom anchors.bottomMargin: 1 visible: true color: "transparent" Rectangle { id: settingsMenuBackground anchors.fill: settingsMenu Layout.fillWidth: true Layout.fillHeight: true visible: false color: "black" opacity: 0.6 radius: 5 } Rectangle { id: settingsMenu color: "transparent" width: childrenRect.width height: childrenRect.height visible: false anchors.right: settingsButton.right anchors.bottom: progressBar.top radius: 5 } Slider { id: progressBar to: 1 value: 0.0 anchors.bottom: parent.top anchors.left: parent.left anchors.right: parent.right anchors.bottomMargin: 0 anchors.topMargin: progressBackground.height bottomPadding: 0 onMoved: { player.command(["seek", progressBar.value, "absolute"]) } background: Rectangle { id: progressBackground x: progressBar.leftPadding y: progressBar.topPadding + progressBar.availableHeight / 2 - height / 2 implicitHeight: Math.max(Screen.height / 256, fun.nyanCat ? 12 : 2) width: progressBar.availableWidth height: implicitHeight color: Qt.rgba(255, 255, 255, 0.6) Rectangle { id: progressLength width: progressBar.visualPosition * parent.width height: parent.height color: "red" opacity: 1 Image { visible: fun.nyanCat id: rainbow anchors.fill: parent height: parent.height width: parent.width source: "qrc:/player/icons/rainbow.png" fillMode: Image.TileHorizontally } } Rectangle { id: cachedLength z: 10 anchors.left: progressLength.right anchors.leftMargin: progressBar.handle.width - 2 //anchors.left: progressBar.handle.horizontalCenter anchors.bottom: progressBar.background.bottom anchors.top: progressBar.background.top height: progressBar.background.height color: "white" opacity: 0.8 } } handle: Rectangle { id: handleRect x: progressBar.leftPadding + progressBar.visualPosition * (progressBar.availableWidth - width) y: progressBar.topPadding + progressBar.availableHeight / 2 - height / 2 implicitHeight: 12 implicitWidth: 12 radius: 12 color: fun.nyanCat ? "transparent" : "red" //border.color: "red" AnimatedImage { visible: fun.nyanCat paused: progressBar.pressed height: 30 id: nyanimation anchors.centerIn: parent source: "qrc:/player/icons/nyancat.gif" fillMode: Image.PreserveAspectFit } } } Button { id: playlistPrevButton //icon.name: "prev" icon.source: "icons/prev.svg" icon.color: "white" display: AbstractButton.IconOnly anchors.top: parent.top anchors.bottom: parent.bottom visible: false width: 0 onClicked: { player.prevPlaylistItem() } background: Rectangle { color: "transparent" } } Button { id: playPauseButton //icon.name: "pause" icon.source: "icons/pause.svg" icon.color: "white" display: AbstractButton.IconOnly anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: playlistPrevButton.right onClicked: { player.togglePlayPause() } background: Rectangle { color: "transparent" } } Button { id: playlistNextButton //icon.name: "next" icon.source: "icons/next.svg" icon.color: "white" display: AbstractButton.IconOnly anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: playPauseButton.right onClicked: { player.nextPlaylistItem() } background: Rectangle { color: "transparent" } } Button { id: volumeButton //icon.name: "volume-up" icon.source: "icons/volume-up.svg" icon.color: "white" display: AbstractButton.IconOnly anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: playlistNextButton.right onClicked: { player.toggleMute() } background: Rectangle { color: "transparent" } } Slider { id: volumeBar to: 100 value: 100 palette.dark: "#f00" implicitWidth: Math.max( background ? background.implicitWidth : 0, (handle ? handle.implicitWidth : 0) + leftPadding + rightPadding) implicitHeight: Math.max( background ? background.implicitHeight : 0, (handle ? handle.implicitHeight : 0) + topPadding + bottomPadding) anchors.left: volumeButton.right anchors.top: parent.top anchors.bottom: parent.bottom onMoved: { player.setVolume(Math.round(volumeBar.value).toString()) } handle: Rectangle { x: volumeBar.leftPadding + volumeBar.visualPosition * (volumeBar.availableWidth - width) y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2 implicitWidth: 12 implicitHeight: 12 radius: 12 color: "#f6f6f6" border.color: "#f6f6f6" } background: Rectangle { x: volumeBar.leftPadding y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2 implicitWidth: 60 implicitHeight: 3 width: volumeBar.availableWidth height: implicitHeight color: "#33333311" Rectangle { width: volumeBar.visualPosition * parent.width height: parent.height color: "white" } } } Text { id: timeLabel text: "0:00 / 0:00" color: "white" anchors.left: volumeBar.right anchors.top: parent.top anchors.bottom: parent.bottom padding: 2 font.family: appearance.fontName font.pixelSize: 14 verticalAlignment: Text.AlignVCenter renderType: Text.NativeRendering } Button { id: subtitlesButton //icon.name: "subtitles" icon.source: "icons/subtitles.svg" icon.color: "white" anchors.right: settingsButton.left anchors.top: parent.top anchors.bottom: parent.bottom display: AbstractButton.IconOnly onClicked: { tracksMenuUpdate() subtitlesMenu.visible = !subtitlesMenu.visible subtitlesMenuBackground.visible = !subtitlesMenuBackground.visible } background: Rectangle { color: "transparent" } } Button { id: settingsButton //icon.name: "settings" icon.source: "icons/settings.svg" icon.color: "white" Layout.alignment: Qt.AlignVCenter | Qt.AlignRight anchors.right: fullscreenButton.left anchors.top: parent.top anchors.bottom: parent.bottom display: AbstractButton.IconOnly onClicked: { settingsMenu.visible = !settingsMenu.visible settingsMenuBackground.visible = !settingsMenuBackground.visible } background: Rectangle { color: "transparent" } } Button { id: fullscreenButton //icon.name: "fullscreen" icon.source: "icons/fullscreen.svg" icon.color: "white" Layout.alignment: Qt.AlignVCenter | Qt.AlignRight anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom display: AbstractButton.IconOnly onClicked: { toggleFullscreen() } background: Rectangle { color: "transparent" } } } } }