Audit main UI, menus, settings, and glossary against KDE HIG #4

Merged
vokhactri merged 4 commits from docs/kde-hig-ui-audit into main 2026-11-07 20:08:28 +07:00
8 changed files with 248 additions and 157 deletions
Showing only changes of commit 5edbb4a9e5 - Show all commits

No files matched your search

+42 -13
View File
@@ -13,6 +13,13 @@ import org.kde.kirigami as Kirigami
Item { Item {
id: root id: root
// Space activates the focused control before the window's playback action.
Keys.onShortcutOverride: event => {
if (event.key === Qt.Key_Space && event.modifiers === Qt.NoModifier) {
event.accepted = true
}
}
required property Actions actions required property Actions actions
required property MpvVideo mpv required property MpvVideo mpv
property bool fullscreen: false property bool fullscreen: false
@@ -22,9 +29,17 @@ Item {
readonly property bool isFloating: QtPotatoSettings.footerStyle === "floating" readonly property bool isFloating: QtPotatoSettings.footerStyle === "floating"
// Takes space below the video (instead of covering it). // Takes space below the video (instead of covering it).
readonly property bool isDocked: !isFloating && !fullscreen readonly property bool isDocked: !isFloating && !fullscreen
readonly property bool isSmallSize: width < 600 readonly property bool isSmallSize: width < Kirigami.Units.gridUnit * 30
readonly property int buttonSize: isSmallSize ? Kirigami.Units.iconSizes.small : Kirigami.Units.iconSizes.smallMedium readonly property int buttonSize: isSmallSize ? Kirigami.Units.iconSizes.small : Kirigami.Units.iconSizes.smallMedium
readonly property bool busy: footer.hovered || hamburgerMenu.isOpen || seekBar.chaptersPopupOpen || seekBar.pressed readonly property bool busy: footer.hovered || hamburgerMenu.isOpen || seekBar.chaptersPopupOpen || seekBar.pressed || controlHasFocus
readonly property bool controlHasFocus: {
let item = root.Window.window?.activeFocusItem
while (item) {
if (item === footer) return true
item = item.parent
}
return false
}
property bool shown: true property bool shown: true
implicitHeight: isFloating implicitHeight: isFloating
@@ -92,7 +107,7 @@ Item {
x: (root.width - width) / 2 x: (root.width - width) / 2
width: root.isFloating width: root.isFloating
? Math.min(root.width - Kirigami.Units.largeSpacing * (root.isSmallSize ? 2 : 4), 900) ? Math.min(root.width - Kirigami.Units.largeSpacing * (root.isSmallSize ? 2 : 4), Kirigami.Units.gridUnit * 50)
: root.width : root.width
state: root.shown ? "visible" : "hidden" state: root.shown ? "visible" : "hidden"
padding: Kirigami.Units.smallSpacing padding: Kirigami.Units.smallSpacing
@@ -112,16 +127,17 @@ Item {
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: Kirigami.Units.smallSpacing
RowLayout { RowLayout {
spacing: root.isSmallSize ? 1 : Kirigami.Units.smallSpacing spacing: Kirigami.Units.mediumSpacing
HamburgerMenu { HamburgerMenu {
id: hamburgerMenu id: hamburgerMenu
actions: root.actions actions: root.actions
mpv: root.mpv mpv: root.mpv
inFooter: true inFooter: true
focusPolicy: Qt.StrongFocus
visible: root.showMenuButton visible: root.showMenuButton
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
@@ -133,7 +149,7 @@ Item {
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
ToolTip.text: root.mpv.paused ? KI18n.i18nc("@info:tooltip", "Start playback") : KI18n.i18nc("@info:tooltip", "Pause playback") ToolTip.text: root.mpv.paused ? KI18n.i18nc("@info:tooltip", "Start playback") : KI18n.i18nc("@info:tooltip", "Pause playback")
ToolTip.visible: hovered ToolTip.visible: hovered
@@ -146,7 +162,7 @@ Item {
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
ToolTip.text: KI18n.i18nc("@info:tooltip", "Play previous file") ToolTip.text: KI18n.i18nc("@info:tooltip", "Play previous file")
ToolTip.visible: hovered ToolTip.visible: hovered
@@ -159,7 +175,7 @@ Item {
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
ToolTip.text: KI18n.i18nc("@info:tooltip", "Play next file") ToolTip.text: KI18n.i18nc("@info:tooltip", "Play next file")
ToolTip.visible: hovered ToolTip.visible: hovered
@@ -171,7 +187,6 @@ Item {
mpv: root.mpv mpv: root.mpv
actions: root.actions actions: root.actions
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 20
} }
ToolButton { ToolButton {
@@ -180,7 +195,7 @@ Item {
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
ToolTip.text: root.actions.mute.text ToolTip.text: root.actions.mute.text
ToolTip.visible: hovered ToolTip.visible: hovered
@@ -188,9 +203,21 @@ Item {
} }
VolumeSlider { VolumeSlider {
id: volumeSlider
mpv: root.mpv mpv: root.mpv
Layout.preferredWidth: root.isSmallSize ? 60 : 100 Layout.preferredWidth: Kirigami.Units.gridUnit * (root.isSmallSize ? 3 : 5)
Layout.preferredHeight: 20 }
Label {
text: root.mpv.mute ? KI18n.i18nc("@info volume muted", "Muted") : KI18n.i18nc("@info volume percentage", "%1%", Math.round(volumeSlider.value))
font: Kirigami.Theme.smallFont
Layout.minimumWidth: volumeTextMetrics.width
TextMetrics {
id: volumeTextMetrics
font: Kirigami.Theme.smallFont
text: KI18n.i18nc("@info volume percentage", "%1%", root.mpv.volumeMax)
}
} }
ToolButton { ToolButton {
@@ -198,7 +225,7 @@ Item {
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
visible: !QtPotatoSettings.playlistToggleWithMouse visible: !QtPotatoSettings.playlistToggleWithMouse
ToolTip.text: text ToolTip.text: text
@@ -208,6 +235,8 @@ Item {
} }
RowLayout { RowLayout {
spacing: Kirigami.Units.largeSpacing
LabelWithTooltip { LabelWithTooltip {
text: root.mpv.mediaTitle text: root.mpv.mediaTitle
font.pointSize: Kirigami.Theme.smallFont.pointSize font.pointSize: Kirigami.Theme.smallFont.pointSize
+38 -7
View File
@@ -12,6 +12,13 @@ import org.kde.kirigami as Kirigami
ToolBar { ToolBar {
id: root id: root
// Space activates the focused control before the window's playback action.
Keys.onShortcutOverride: event => {
if (event.key === Qt.Key_Space && event.modifiers === Qt.NoModifier) {
event.accepted = true
}
}
required property Actions actions required property Actions actions
required property MpvItem mpv required property MpvItem mpv
required property TranslationService translator required property TranslationService translator
@@ -19,7 +26,7 @@ ToolBar {
property bool shown: true property bool shown: true
property bool menuBarShown: true property bool menuBarShown: true
readonly property bool isSmallSize: width < 600 readonly property bool isSmallSize: width < Kirigami.Units.gridUnit * 34
readonly property int buttonSize: isSmallSize ? Kirigami.Units.iconSizes.small : Kirigami.Units.iconSizes.smallMedium readonly property int buttonSize: isSmallSize ? Kirigami.Units.iconSizes.small : Kirigami.Units.iconSizes.smallMedium
position: ToolBar.Header position: ToolBar.Header
@@ -27,29 +34,39 @@ ToolBar {
RowLayout { RowLayout {
width: parent.width width: parent.width
spacing: Kirigami.Units.mediumSpacing
HamburgerMenu { HamburgerMenu {
actions: root.actions actions: root.actions
mpv: root.mpv mpv: root.mpv
visible: !root.menuBarShown visible: !root.menuBarShown
focusPolicy: Qt.StrongFocus
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
} }
ToolButton { ToolButton {
action: root.actions.openFile action: root.actions.openFile
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
ToolTip.text: text
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
} }
ToolButton { ToolButton {
action: root.actions.openUrl action: root.actions.openUrl
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
ToolTip.text: text
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
} }
ToolSeparator {} ToolSeparator {}
@@ -60,10 +77,15 @@ ToolBar {
icon.name: "add-subtitle" icon.name: "add-subtitle"
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
enabled: root.mpv.fileLoaded enabled: root.mpv.fileLoaded
opacity: enabled ? 1.0 : 0.6 opacity: enabled ? 1.0 : 0.6
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
ToolTip.text: text
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
Accessible.role: Accessible.ButtonMenu
down: subtitleMenu.visible down: subtitleMenu.visible
onPressed: subtitleMenu.open() onPressed: subtitleMenu.open()
@@ -89,10 +111,15 @@ ToolBar {
icon.name: "audio-volume-high" icon.name: "audio-volume-high"
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
enabled: root.mpv.audioTracks.length > 0 enabled: root.mpv.audioTracks.length > 0
opacity: enabled ? 1.0 : 0.6 opacity: enabled ? 1.0 : 0.6
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
ToolTip.text: text
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
Accessible.role: Accessible.ButtonMenu
down: audioMenu.visible down: audioMenu.visible
onPressed: audioMenu.open() onPressed: audioMenu.open()
@@ -135,7 +162,7 @@ ToolBar {
ToolButton { ToolButton {
action: root.actions.cancelTranslation action: root.actions.cancelTranslation
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
ToolTip.text: text ToolTip.text: text
ToolTip.visible: hovered ToolTip.visible: hovered
@@ -149,10 +176,14 @@ ToolBar {
ToolButton { ToolButton {
action: root.actions.configure action: root.actions.configure
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
icon.width: root.buttonSize icon.width: root.buttonSize
icon.height: root.buttonSize icon.height: root.buttonSize
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
ToolTip.text: text
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
} }
} }
+53 -16
View File
@@ -5,46 +5,83 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import org.kde.ki18n
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
// Single-line text popup, used for Open URL. Kirigami.PromptDialog {
Popup {
id: root id: root
property string lastText: "" property string lastText: ""
property string placeholderText: "" property string placeholderText: ""
property string buttonText: "" property string buttonText: ""
property string errorText: ""
readonly property bool validUrl: isValidUrl(textField.text.trim())
signal submitted(text: string) signal submitted(text: string)
function isValidUrl(text: string): bool {
// Network URLs need both a scheme and a host. Reject whitespace, missing
// authorities, empty labels, and ports outside the TCP/UDP range.
const match = /^([a-z][a-z0-9+.-]*):\/\/([^/?#\s]+)(?:[/?#][^\s]*)?$/i.exec(text)
if (!match) return false
const authority = match[2].substring(match[2].lastIndexOf("@") + 1)
const hostPort = /^(\[[0-9a-f:.]+\]|[^:@\[\]]+)(?::([0-9]+))?$/i.exec(authority)
if (!hostPort || (hostPort[2] !== undefined && (+hostPort[2] < 1 || +hostPort[2] > 65535))) return false
const host = hostPort[1]
return host.startsWith("[") || host.split(".").every(label => /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(label))
}
function submit() {
if (!validUrl) {
errorText = KI18n.i18nc("@info:usagetip", "Enter a URL with a scheme and a host, such as https://example.org/video.mkv.")
textField.forceActiveFocus()
return
}
submitted(textField.text.trim())
close()
}
title: KI18n.i18nc("@title:window", "Open URL")
modal: true modal: true
standardButtons: Kirigami.Dialog.Cancel
customFooterActions: Kirigami.Action {
text: root.buttonText
icon.name: "document-open"
enabled: root.validUrl
onTriggered: root.submit()
}
onOpened: { onOpened: {
errorText = ""
textField.text = root.lastText textField.text = root.lastText
textField.forceActiveFocus(Qt.MouseFocusReason) textField.forceActiveFocus(Qt.OtherFocusReason)
textField.selectAll() textField.selectAll()
} }
contentItem: RowLayout { ColumnLayout {
spacing: Kirigami.Units.smallSpacing
Layout.fillWidth: true
Label {
text: KI18n.i18nc("@label:textbox", "URL:")
Accessible.ignored: true
}
TextField { TextField {
id: textField id: textField
placeholderText: root.placeholderText placeholderText: root.placeholderText
Accessible.name: KI18n.i18nc("@label:textbox", "URL")
inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhNoAutoUppercase inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhNoAutoUppercase
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.largeSpacing onAccepted: root.submit()
onAccepted: submitButton.clicked() onTextEdited: root.errorText = ""
Keys.onEscapePressed: root.close()
} }
Button { Kirigami.InlineMessage {
id: submitButton Layout.fillWidth: true
text: root.buttonText type: Kirigami.MessageType.Error
enabled: textField.text.trim().length > 0 text: root.errorText
Layout.rightMargin: Kirigami.Units.largeSpacing visible: text.length > 0
onClicked: {
root.submitted(textField.text.trim())
root.close()
}
} }
} }
} }
+8 -9
View File
@@ -50,10 +50,10 @@ ApplicationWindow {
visible: true visible: true
title: video.mediaTitle || KI18n.i18nc("@title:window", "QtPotato") title: video.mediaTitle || KI18n.i18nc("@title:window", "QtPotato")
width: 1000 width: Kirigami.Units.gridUnit * 55
height: 600 height: Kirigami.Units.gridUnit * 33
minimumWidth: 400 minimumWidth: Kirigami.Units.gridUnit * 23
minimumHeight: 200 minimumHeight: Kirigami.Units.gridUnit * 11
color: Kirigami.Theme.backgroundColor color: Kirigami.Theme.backgroundColor
onVisibilityChanged: visibility => { onVisibilityChanged: visibility => {
@@ -180,7 +180,7 @@ ApplicationWindow {
if (!QtPotatoSettings.playlistToggleWithMouse) { if (!QtPotatoSettings.playlistToggleWithMouse) {
return return
} }
const atEdge = playlistPanel.edge === Qt.RightEdge ? x > width - 50 : x < 50 const atEdge = playlistPanel.edge === Qt.RightEdge ? x > width - Kirigami.Units.gridUnit * 3 : x < Kirigami.Units.gridUnit * 3
playlistPanel.state = atEdge ? "visible" : "hidden" playlistPanel.state = atEdge ? "visible" : "hidden"
} }
onPointerLeft: footerItem.pointerLeft() onPointerLeft: footerItem.pointerLeft()
@@ -266,11 +266,10 @@ ApplicationWindow {
InputPopup { InputPopup {
id: openUrlPopup id: openUrlPopup
x: Kirigami.Units.largeSpacing title: KI18n.i18nc("@title:window", "Open URL")
y: Kirigami.Units.largeSpacing preferredWidth: Kirigami.Units.gridUnit * 30
width: Math.min(appWindow.width * 0.9, 600)
lastText: QtPotatoSettings.lastUrl lastText: QtPotatoSettings.lastUrl
placeholderText: "https://" placeholderText: "https://example.org/video.mkv"
buttonText: KI18n.i18nc("@action:button", "Open") buttonText: KI18n.i18nc("@action:button", "Open")
onSubmitted: text => { onSubmitted: text => {
QtPotatoSettings.lastUrl = text QtPotatoSettings.lastUrl = text
+5 -1
View File
@@ -30,6 +30,9 @@ MpvItem {
setPropertyAsync("sub-outline-size", QtPotatoSettings.subtitleOutlineSize) setPropertyAsync("sub-outline-size", QtPotatoSettings.subtitleOutlineSize)
} }
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
loadUserConfig: QtPotatoSettings.loadMpvConfig loadUserConfig: QtPotatoSettings.loadMpvConfig
// Set once the saved volume is applied; mpv reports its default volume before that. // Set once the saved volume is applied; mpv reports its default volume before that.
@@ -55,7 +58,7 @@ MpvItem {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: "black" color: Kirigami.Theme.backgroundColor
visible: !root.fileLoaded visible: !root.fileLoaded
z: -1 z: -1
} }
@@ -95,6 +98,7 @@ MpvItem {
onExited: root.pointerLeft() onExited: root.pointerLeft()
onPressed: mouse => { onPressed: mouse => {
root.forceActiveFocus(Qt.MouseFocusReason)
switch (mouse.button) { switch (mouse.button) {
case Qt.LeftButton: case Qt.LeftButton:
root.actions.playPause.trigger() root.actions.playPause.trigger()
+69 -79
View File
@@ -10,7 +10,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.ki18n import org.kde.ki18n
// Flat progress bar of Haruna's footer: click or drag to seek, hover for the time, // Playback position: click or drag to seek, hover for the time,
// chapter marks, right click for the chapter list, wheel to seek. // chapter marks, right click for the chapter list, wheel to seek.
Slider { Slider {
id: root id: root
@@ -19,7 +19,7 @@ Slider {
required property Actions actions required property Actions actions
readonly property alias chaptersPopupOpen: chaptersPopup.visible readonly property alias chaptersPopupOpen: chaptersPopup.visible
readonly property bool showChapterMarkers: root.mpv.chapters.length * 20 < width readonly property bool showChapterMarkers: root.mpv.chapters.length * Kirigami.Units.gridUnit < width
// Playing state before a drag started; playback pauses while dragging. // Playing state before a drag started; playback pauses while dragging.
property bool pausedBeforeDrag: false property bool pausedBeforeDrag: false
@@ -47,91 +47,81 @@ Slider {
return chapter ? `${formatTime(time)} - ${chapter}` : formatTime(time) return chapter ? `${formatTime(time)} - ${chapter}` : formatTime(time)
} }
Accessible.name: KI18n.i18nc("@label:slider", "Playback position")
Accessible.description: timeLabel(value)
stepSize: 1
from: 0 from: 0
to: Math.max(root.mpv.duration, 0) to: Math.max(root.mpv.duration, 0)
enabled: root.mpv.fileLoaded && root.mpv.duration > 0 enabled: root.mpv.fileLoaded && root.mpv.duration > 0
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
leftPadding: 0
rightPadding: 0
handle: Item { visible: false } // Preserve the Slider's own arrow-key behavior when playback shortcuts exist.
Keys.onShortcutOverride: event => {
background: Rectangle { if (event.modifiers === Qt.NoModifier && [Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down, Qt.Key_Home, Qt.Key_End].includes(event.key)) {
id: progressBarBackground event.accepted = true
color: Kirigami.Theme.alternateBackgroundColor
scale: root.mirrored ? -1 : 1
Rectangle {
width: root.visualPosition * parent.width
height: parent.height
color: Kirigami.Theme.highlightColor
} }
}
Keys.onPressed: event => {
if (event.modifiers === Qt.NoModifier && (event.key === Qt.Key_Home || event.key === Qt.Key_End)) {
value = event.key === Qt.Key_Home ? from : to
moved()
event.accepted = true
}
}
Repeater { Repeater {
model: root.showChapterMarkers ? root.mpv.chapters : [] model: root.showChapterMarkers ? root.mpv.chapters : []
delegate: Rectangle { delegate: Rectangle {
required property var modelData required property var modelData
x: modelData.time / root.mpv.duration * progressBarBackground.width x: root.leftPadding + (root.mirrored ? 1 - modelData.time / root.to : modelData.time / root.to) * root.availableWidth
width: 2 anchors.verticalCenter: parent.verticalCenter
height: progressBarBackground.height width: Kirigami.Units.smallSpacing / 2
color: Kirigami.Theme.textColor height: Kirigami.Units.smallSpacing
color: Kirigami.Theme.textColor
}
}
ToolTip {
id: timeToolTip
parent: root
property real pointerX: 0
x: {
const windowX = root.mapToItem(root.Window.window.contentItem, 0, 0).x
return Math.max(Kirigami.Units.largeSpacing - windowX,
Math.min(root.Window.window.width - width - Kirigami.Units.largeSpacing - windowX, pointerX - width / 2))
}
y: -height - Kirigami.Units.largeSpacing
width: Math.min(implicitWidth, root.Window.window.width - Kirigami.Units.largeSpacing * 2)
visible: (mouseArea.containsMouse || root.pressed) && root.mpv.duration > 0
text: root.timeLabel(root.pressed ? root.value : pointerX / Math.max(1, root.width) * root.to)
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
// Left button is the slider's own.
acceptedButtons: Qt.MiddleButton | Qt.RightButton
onPositionChanged: timeToolTip.pointerX = mouseX
onClicked: mouse => {
if (mouse.button === Qt.MiddleButton) {
const time = mouseX / width * root.to
const next = root.mpv.chapters.findIndex(chapter => chapter.time > time)
if (next >= 0) {
root.mpv.chapter = next
}
} else if (mouse.button === Qt.RightButton && root.mpv.chapters.length > 0) {
chaptersPopup.x = Math.max(0, Math.min(root.width - chaptersPopup.width, mouseX - chaptersPopup.width / 2))
chaptersPopup.open()
} }
} }
onWheel: wheel => {
Rectangle { if (wheel.angleDelta.y > 0) {
id: timeToolTip root.actions.seekForwardMedium.trigger()
} else if (wheel.angleDelta.y < 0) {
property real pointerX: 0 root.actions.seekBackwardMedium.trigger()
x: Math.max(0, Math.min(parent.width - width, pointerX - width / 2))
y: -height - Kirigami.Units.largeSpacing
z: 10
width: timeLabel.implicitWidth + Kirigami.Units.smallSpacing * 2
height: timeLabel.implicitHeight + Kirigami.Units.smallSpacing * 2
visible: (mouseArea.containsMouse || root.pressed) && root.mpv.duration > 0
color: Kirigami.Theme.backgroundColor
radius: Kirigami.Units.cornerRadius
border.width: 1
border.color: Qt.darker(Kirigami.Theme.backgroundColor, 1.2)
Label {
id: timeLabel
anchors.centerIn: parent
text: {
const time = root.pressed
? root.value
: timeToolTip.pointerX / Math.max(1, progressBarBackground.width) * root.to
return root.timeLabel(time)
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
// Left button is the slider's own.
acceptedButtons: Qt.MiddleButton | Qt.RightButton
onPositionChanged: timeToolTip.pointerX = mouseX
onClicked: mouse => {
if (mouse.button === Qt.MiddleButton) {
const time = mouseX / width * root.to
const next = root.mpv.chapters.findIndex(chapter => chapter.time > time)
if (next >= 0) {
root.mpv.chapter = next
}
} else if (mouse.button === Qt.RightButton && root.mpv.chapters.length > 0) {
chaptersPopup.x = Math.max(0, Math.min(root.width - chaptersPopup.width, mouseX - chaptersPopup.width / 2))
chaptersPopup.open()
}
}
onWheel: wheel => {
if (wheel.angleDelta.y > 0) {
root.actions.seekForwardMedium.trigger()
} else if (wheel.angleDelta.y < 0) {
root.actions.seekBackwardMedium.trigger()
}
} }
} }
} }
+16 -7
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick import QtQuick
import org.kde.ki18n
import QtQuick.Controls import QtQuick.Controls
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
@@ -9,10 +10,21 @@ import org.kde.kirigami as Kirigami
ToolButton { ToolButton {
id: root id: root
// Space activates the focused control before the window's playback action.
Keys.onShortcutOverride: event => {
if (event.key === Qt.Key_Space && event.modifiers === Qt.NoModifier) {
event.accepted = true
}
}
required property string toolTipText required property string toolTipText
icon.name: "documentinfo" icon.name: "documentinfo"
focusPolicy: Qt.NoFocus text: KI18n.i18nc("@action:button", "Help")
display: AbstractButton.IconOnly
Accessible.name: text
Accessible.description: toolTipText
focusPolicy: Qt.StrongFocus
checkable: true checkable: true
ToolTip { ToolTip {
@@ -21,11 +33,8 @@ ToolButton {
delay: root.checked ? 0 : Kirigami.Units.toolTipDelay delay: root.checked ? 0 : Kirigami.Units.toolTipDelay
timeout: -1 timeout: -1
closePolicy: Popup.NoAutoClose closePolicy: Popup.NoAutoClose
contentItem: Label { text: root.toolTipText
text: root.toolTipText x: (root.width - width) / 2
wrapMode: Text.WordWrap width: Math.min(implicitWidth, Kirigami.Units.gridUnit * 20, root.Window.window.width - Kirigami.Units.largeSpacing * 2)
width: Math.min(implicitWidth, Kirigami.Units.gridUnit * 20)
}
width: Math.min(contentItem.implicitWidth, Kirigami.Units.gridUnit * 20) + leftPadding + rightPadding
} }
} }
+17 -25
View File
@@ -7,49 +7,41 @@ import QtQuick.Controls
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.ki18n import org.kde.ki18n
// Flat volume bar with the value written on it, as in Haruna. // Use the desktop style's handle and hover/focus feedback.
Slider { Slider {
id: root id: root
required property MpvItem mpv required property MpvItem mpv
Accessible.name: KI18n.i18nc("@label:slider", "Volume")
Accessible.description: root.mpv.mute ? KI18n.i18nc("@info volume muted", "Muted") : KI18n.i18nc("@info volume percentage", "%1%", Math.round(value))
from: 0 from: 0
to: root.mpv.volumeMax to: root.mpv.volumeMax
implicitWidth: 100 implicitWidth: Kirigami.Units.gridUnit * 5
implicitHeight: 25
wheelEnabled: true wheelEnabled: true
stepSize: QtPotatoSettings.volumeStep stepSize: QtPotatoSettings.volumeStep
focusPolicy: Qt.NoFocus focusPolicy: Qt.StrongFocus
leftPadding: 0
rightPadding: 0
handle: Item { visible: false } // Let the Slider consume navigation keys before window playback shortcuts.
Keys.onShortcutOverride: event => {
background: Rectangle { if (event.modifiers === Qt.NoModifier && [Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down, Qt.Key_Home, Qt.Key_End].includes(event.key)) {
color: Kirigami.Theme.alternateBackgroundColor event.accepted = true
scale: root.mirrored ? -1 : 1 }
}
Rectangle { Keys.onPressed: event => {
width: root.visualPosition * parent.width if (event.modifiers === Qt.NoModifier && (event.key === Qt.Key_Home || event.key === Qt.Key_End)) {
height: parent.height value = event.key === Qt.Key_Home ? from : to
color: Kirigami.Theme.highlightColor moved()
event.accepted = true
} }
} }
onMoved: root.mpv.volume = Math.round(value) onMoved: root.mpv.volume = Math.round(value)
// Follow mpv, but not while the user drags (dragging replaces a plain binding). // Follow mpv, but not while the user drags.
Binding on value { Binding on value {
value: root.mpv.volume value: root.mpv.volume
when: !root.pressed when: !root.pressed
restoreMode: Binding.RestoreNone restoreMode: Binding.RestoreNone
} }
Label {
anchors.centerIn: root
text: root.mpv.mute ? KI18n.i18nc("@info volume muted", "Muted") : Math.round(root.value)
color: "#fff"
style: Text.Outline
styleColor: "#111"
}
} }