diff --git a/src/qml/Main.qml b/src/qml/Main.qml
index 40c3dfb..19bb0a5 100644
--- a/src/qml/Main.qml
+++ b/src/qml/Main.qml
@@ -18,6 +18,7 @@ Kirigami.ApplicationWindow {
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
diff --git a/src/qml/PlayerPage.qml b/src/qml/PlayerPage.qml
index d2c2bce..00ccb5b 100644
--- a/src/qml/PlayerPage.qml
+++ b/src/qml/PlayerPage.qml
@@ -9,6 +9,9 @@ Kirigami.Page {
id: page
property url initialUrl
+ // Set by the window. In full screen the bars float over the video and hide when the
+ // pointer rests.
+ property bool fullScreen: false
readonly property alias mediaTitle: mpv.mediaTitle
readonly property alias translator: translator
@@ -18,6 +21,27 @@ Kirigami.Page {
title: KI18n.i18nc("@title", "Player")
padding: 0
+ globalToolBarStyle: fullScreen ? Kirigami.ApplicationHeaderStyle.None : Kirigami.ApplicationHeaderStyle.ToolBar
+
+ // Kept true for a moment after the pointer moves.
+ property bool pointerActive: false
+ readonly property bool controlsVisible: !fullScreen || pointerActive || topBarHover.hovered || footerHover.hovered
+ || subtitlePopup.visible || seekBar.pressed || !mpv.fileLoaded || menuOpen
+ // Set while the full screen "More" menu is open, so the bars stay.
+ property bool menuOpen: false
+
+ onFullScreenChanged: page.revealControls()
+
+ function revealControls() {
+ pointerActive = true
+ idleTimer.restart()
+ }
+
+ Timer {
+ id: idleTimer
+ interval: 2500
+ onTriggered: page.pointerActive = false
+ }
actions: [
Kirigami.Action {
@@ -27,10 +51,10 @@ Kirigami.Page {
onTriggered: fileDialog.open()
},
Kirigami.Action {
- icon.name: "view-fullscreen"
- text: KI18n.i18nc("@action", "Full Screen")
+ // F and Esc are handled by the Shortcuts below, which work while the bars are hidden.
+ icon.name: page.fullScreen ? "view-restore" : "view-fullscreen"
+ text: page.fullScreen ? KI18n.i18nc("@action", "Exit Full Screen") : KI18n.i18nc("@action", "Full Screen")
displayHint: Kirigami.DisplayHint.IconOnly
- shortcut: "F"
onTriggered: page.fullScreenToggled()
},
Kirigami.Action {
@@ -49,11 +73,23 @@ Kirigami.Page {
]
Component.onCompleted: {
+ page.applySubtitleStyle()
if (page.initialUrl.toString().length > 0) {
mpv.loadFile(page.initialUrl)
}
}
+ // Plain-text and translated subtitles; styled ASS subtitles keep their own fonts.
+ function applySubtitleStyle() {
+ mpv.setPropertyAsync("sub-font-size", QtPotatoSettings.subtitleFontSize)
+ mpv.setPropertyAsync("sub-outline-size", QtPotatoSettings.subtitleOutlineSize)
+ }
+
+ readonly property int subtitleFontSize: QtPotatoSettings.subtitleFontSize
+ onSubtitleFontSizeChanged: applySubtitleStyle()
+ readonly property int subtitleOutlineSize: QtPotatoSettings.subtitleOutlineSize
+ onSubtitleOutlineSizeChanged: applySubtitleStyle()
+
function formatTime(totalSeconds: real): string {
const s = Math.max(0, Math.floor(totalSeconds))
const h = Math.floor(s / 3600)
@@ -66,6 +102,15 @@ Kirigami.Page {
sequence: "Space"
onActivated: mpv.togglePause()
}
+ Shortcut {
+ sequence: "F"
+ onActivated: page.fullScreenToggled()
+ }
+ Shortcut {
+ sequence: "Escape"
+ enabled: page.fullScreen
+ onActivated: page.fullScreenToggled()
+ }
Shortcut {
sequence: "Left"
onActivated: mpv.seek(mpv.position - 5)
@@ -125,14 +170,17 @@ Kirigami.Page {
onAccepted: mpv.loadFile(selectedFile)
}
- ColumnLayout {
+ Item {
anchors.fill: parent
- spacing: 0
MpvItem {
id: mpv
- Layout.fillWidth: true
- Layout.fillHeight: true
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ bottom: page.fullScreen ? parent.bottom : footer.top
+ }
Rectangle {
anchors.fill: parent
@@ -153,6 +201,9 @@ Kirigami.Page {
MouseArea {
anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: page.controlsVisible ? Qt.ArrowCursor : Qt.BlankCursor
+ onPositionChanged: page.revealControls()
onClicked: mpv.togglePause()
onDoubleClicked: page.fullScreenToggled()
}
@@ -173,9 +224,85 @@ Kirigami.Page {
}
}
+ // Stands in for the page header in full screen.
QQC2.ToolBar {
- Layout.fillWidth: true
+ id: topBar
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ position: QQC2.ToolBar.Header
+ // No fade: over a paused video Qt Quick renders frames too rarely for a smooth one.
+ visible: page.fullScreen && page.controlsVisible
+
+ HoverHandler {
+ id: topBarHover
+ }
+
+ contentItem: RowLayout {
+ spacing: Kirigami.Units.smallSpacing
+
+ Kirigami.Heading {
+ Layout.fillWidth: true
+ Layout.leftMargin: Kirigami.Units.smallSpacing
+ level: 1
+ elide: Text.ElideRight
+ text: mpv.mediaTitle
+ }
+
+ // Plain buttons: Kirigami.ActionToolBar dropped the full screen action here.
+ QQC2.ToolButton {
+ action: page.actions[0]
+ focusPolicy: Qt.NoFocus
+ }
+
+ QQC2.ToolButton {
+ action: page.actions[1]
+ focusPolicy: Qt.NoFocus
+ display: QQC2.AbstractButton.IconOnly
+
+ QQC2.ToolTip.text: text
+ QQC2.ToolTip.visible: hovered
+ QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
+ }
+
+ QQC2.ToolButton {
+ id: moreButton
+ focusPolicy: Qt.NoFocus
+ icon.name: "overflow-menu"
+ text: KI18n.i18nc("@action:button", "More")
+ display: QQC2.AbstractButton.IconOnly
+ down: moreMenu.visible
+ onPressed: moreMenu.popup(moreButton, 0, moreButton.height)
+
+ QQC2.Menu {
+ id: moreMenu
+ onVisibleChanged: page.menuOpen = visible
+ QQC2.MenuItem {
+ action: page.actions[2]
+ }
+ QQC2.MenuItem {
+ action: page.actions[3]
+ }
+ }
+ }
+ }
+ }
+
+ QQC2.ToolBar {
+ id: footer
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
position: QQC2.ToolBar.Footer
+ visible: page.controlsVisible
+
+ HoverHandler {
+ id: footerHover
+ }
RowLayout {
anchors.fill: parent
diff --git a/src/qml/SettingsPage.qml b/src/qml/SettingsPage.qml
index 81d58e8..c8593f1 100644
--- a/src/qml/SettingsPage.qml
+++ b/src/qml/SettingsPage.qml
@@ -27,6 +27,32 @@ FormCard.FormCardPage {
onVisibleChanged: if (!visible) page.translator.clearError()
}
+ FormCard.FormHeader {
+ title: KI18n.i18nc("@title:group", "Subtitles")
+ }
+
+ FormCard.FormCard {
+ FormCard.FormSpinBoxDelegate {
+ label: KI18n.i18nc("@label:spinbox", "Text size")
+ description: KI18n.i18nc("@info", "Text size and outline apply to translated and plain-text subtitles. Styled subtitles (ASS) keep the look they were made with.")
+ from: 20
+ to: 150
+ stepSize: 2
+ value: QtPotatoSettings.subtitleFontSize
+ onValueModified: QtPotatoSettings.subtitleFontSize = value
+ }
+
+ FormCard.FormDelegateSeparator {}
+
+ FormCard.FormSpinBoxDelegate {
+ label: KI18n.i18nc("@label:spinbox", "Outline size")
+ from: 0
+ to: 10
+ value: QtPotatoSettings.subtitleOutlineSize
+ onValueModified: QtPotatoSettings.subtitleOutlineSize = value
+ }
+ }
+
FormCard.FormHeader {
title: KI18n.i18nc("@title:group", "Subtitle Translation")
}
diff --git a/src/settings/qtpotatosettings.kcfg b/src/settings/qtpotatosettings.kcfg
index b53ef97..0695c94 100644
--- a/src/settings/qtpotatosettings.kcfg
+++ b/src/settings/qtpotatosettings.kcfg
@@ -3,6 +3,20 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
+
+
+
+ 52
+ 20
+ 150
+
+
+
+ 3
+ 0
+ 10
+
+