fix(ui): address main window KDE HIG findings
This commit was merged in pull request #4.
This commit is contained in:
1 parent
0cd03335f7
commit
0bfaf3c9db
8 files changed
+248
-157
No files matched your search
+42
-13
@@ -13,6 +13,13 @@ import org.kde.kirigami as Kirigami
|
||||
Item {
|
||||
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 MpvVideo mpv
|
||||
property bool fullscreen: false
|
||||
@@ -22,9 +29,17 @@ Item {
|
||||
readonly property bool isFloating: QtPotatoSettings.footerStyle === "floating"
|
||||
// Takes space below the video (instead of covering it).
|
||||
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 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
|
||||
|
||||
implicitHeight: isFloating
|
||||
@@ -92,7 +107,7 @@ Item {
|
||||
|
||||
x: (root.width - width) / 2
|
||||
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
|
||||
state: root.shown ? "visible" : "hidden"
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
@@ -112,16 +127,17 @@ Item {
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
RowLayout {
|
||||
spacing: root.isSmallSize ? 1 : Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.mediumSpacing
|
||||
|
||||
HamburgerMenu {
|
||||
id: hamburgerMenu
|
||||
actions: root.actions
|
||||
mpv: root.mpv
|
||||
inFooter: true
|
||||
focusPolicy: Qt.StrongFocus
|
||||
visible: root.showMenuButton
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
@@ -133,7 +149,7 @@ Item {
|
||||
display: AbstractButton.IconOnly
|
||||
icon.width: 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.visible: hovered
|
||||
@@ -146,7 +162,7 @@ Item {
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
display: AbstractButton.IconOnly
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
|
||||
ToolTip.text: KI18n.i18nc("@info:tooltip", "Play previous file")
|
||||
ToolTip.visible: hovered
|
||||
@@ -159,7 +175,7 @@ Item {
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
display: AbstractButton.IconOnly
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
|
||||
ToolTip.text: KI18n.i18nc("@info:tooltip", "Play next file")
|
||||
ToolTip.visible: hovered
|
||||
@@ -171,7 +187,6 @@ Item {
|
||||
mpv: root.mpv
|
||||
actions: root.actions
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 20
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
@@ -180,7 +195,7 @@ Item {
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
display: AbstractButton.IconOnly
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
|
||||
ToolTip.text: root.actions.mute.text
|
||||
ToolTip.visible: hovered
|
||||
@@ -188,9 +203,21 @@ Item {
|
||||
}
|
||||
|
||||
VolumeSlider {
|
||||
id: volumeSlider
|
||||
mpv: root.mpv
|
||||
Layout.preferredWidth: root.isSmallSize ? 60 : 100
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * (root.isSmallSize ? 3 : 5)
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -198,7 +225,7 @@ Item {
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
display: AbstractButton.IconOnly
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
visible: !QtPotatoSettings.playlistToggleWithMouse
|
||||
|
||||
ToolTip.text: text
|
||||
@@ -208,6 +235,8 @@ Item {
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
LabelWithTooltip {
|
||||
text: root.mpv.mediaTitle
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
|
||||
+38
-7
@@ -12,6 +12,13 @@ import org.kde.kirigami as Kirigami
|
||||
ToolBar {
|
||||
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 MpvItem mpv
|
||||
required property TranslationService translator
|
||||
@@ -19,7 +26,7 @@ ToolBar {
|
||||
property bool shown: 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
|
||||
|
||||
position: ToolBar.Header
|
||||
@@ -27,29 +34,39 @@ ToolBar {
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: Kirigami.Units.mediumSpacing
|
||||
|
||||
HamburgerMenu {
|
||||
actions: root.actions
|
||||
mpv: root.mpv
|
||||
visible: !root.menuBarShown
|
||||
focusPolicy: Qt.StrongFocus
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
action: root.actions.openFile
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
action: root.actions.openUrl
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
ToolSeparator {}
|
||||
@@ -60,10 +77,15 @@ ToolBar {
|
||||
icon.name: "add-subtitle"
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
enabled: root.mpv.fileLoaded
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
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
|
||||
onPressed: subtitleMenu.open()
|
||||
|
||||
@@ -89,10 +111,15 @@ ToolBar {
|
||||
icon.name: "audio-volume-high"
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
enabled: root.mpv.audioTracks.length > 0
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
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
|
||||
onPressed: audioMenu.open()
|
||||
|
||||
@@ -135,7 +162,7 @@ ToolBar {
|
||||
ToolButton {
|
||||
action: root.actions.cancelTranslation
|
||||
display: AbstractButton.IconOnly
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.visible: hovered
|
||||
@@ -149,10 +176,14 @@ ToolBar {
|
||||
|
||||
ToolButton {
|
||||
action: root.actions.configure
|
||||
focusPolicy: Qt.NoFocus
|
||||
focusPolicy: Qt.StrongFocus
|
||||
icon.width: root.buttonSize
|
||||
icon.height: root.buttonSize
|
||||
display: root.isSmallSize ? AbstractButton.IconOnly : AbstractButton.TextBesideIcon
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+53
-16
@@ -5,46 +5,83 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import org.kde.ki18n
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
// Single-line text popup, used for Open URL.
|
||||
Popup {
|
||||
Kirigami.PromptDialog {
|
||||
id: root
|
||||
|
||||
property string lastText: ""
|
||||
property string placeholderText: ""
|
||||
property string buttonText: ""
|
||||
property string errorText: ""
|
||||
readonly property bool validUrl: isValidUrl(textField.text.trim())
|
||||
|
||||
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
|
||||
standardButtons: Kirigami.Dialog.Cancel
|
||||
customFooterActions: Kirigami.Action {
|
||||
text: root.buttonText
|
||||
icon.name: "document-open"
|
||||
enabled: root.validUrl
|
||||
onTriggered: root.submit()
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
errorText = ""
|
||||
textField.text = root.lastText
|
||||
textField.forceActiveFocus(Qt.MouseFocusReason)
|
||||
textField.forceActiveFocus(Qt.OtherFocusReason)
|
||||
textField.selectAll()
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
text: KI18n.i18nc("@label:textbox", "URL:")
|
||||
Accessible.ignored: true
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
placeholderText: root.placeholderText
|
||||
Accessible.name: KI18n.i18nc("@label:textbox", "URL")
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhNoAutoUppercase
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
onAccepted: submitButton.clicked()
|
||||
Keys.onEscapePressed: root.close()
|
||||
onAccepted: root.submit()
|
||||
onTextEdited: root.errorText = ""
|
||||
}
|
||||
|
||||
Button {
|
||||
id: submitButton
|
||||
text: root.buttonText
|
||||
enabled: textField.text.trim().length > 0
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
onClicked: {
|
||||
root.submitted(textField.text.trim())
|
||||
root.close()
|
||||
}
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
type: Kirigami.MessageType.Error
|
||||
text: root.errorText
|
||||
visible: text.length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-9
@@ -50,10 +50,10 @@ ApplicationWindow {
|
||||
|
||||
visible: true
|
||||
title: video.mediaTitle || KI18n.i18nc("@title:window", "QtPotato")
|
||||
width: 1000
|
||||
height: 600
|
||||
minimumWidth: 400
|
||||
minimumHeight: 200
|
||||
width: Kirigami.Units.gridUnit * 55
|
||||
height: Kirigami.Units.gridUnit * 33
|
||||
minimumWidth: Kirigami.Units.gridUnit * 23
|
||||
minimumHeight: Kirigami.Units.gridUnit * 11
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
onVisibilityChanged: visibility => {
|
||||
@@ -180,7 +180,7 @@ ApplicationWindow {
|
||||
if (!QtPotatoSettings.playlistToggleWithMouse) {
|
||||
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"
|
||||
}
|
||||
onPointerLeft: footerItem.pointerLeft()
|
||||
@@ -266,11 +266,10 @@ ApplicationWindow {
|
||||
|
||||
InputPopup {
|
||||
id: openUrlPopup
|
||||
x: Kirigami.Units.largeSpacing
|
||||
y: Kirigami.Units.largeSpacing
|
||||
width: Math.min(appWindow.width * 0.9, 600)
|
||||
title: KI18n.i18nc("@title:window", "Open URL")
|
||||
preferredWidth: Kirigami.Units.gridUnit * 30
|
||||
lastText: QtPotatoSettings.lastUrl
|
||||
placeholderText: "https://"
|
||||
placeholderText: "https://example.org/video.mkv"
|
||||
buttonText: KI18n.i18nc("@action:button", "Open")
|
||||
onSubmitted: text => {
|
||||
QtPotatoSettings.lastUrl = text
|
||||
|
||||
@@ -30,6 +30,9 @@ MpvItem {
|
||||
setPropertyAsync("sub-outline-size", QtPotatoSettings.subtitleOutlineSize)
|
||||
}
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
loadUserConfig: QtPotatoSettings.loadMpvConfig
|
||||
|
||||
// Set once the saved volume is applied; mpv reports its default volume before that.
|
||||
@@ -55,7 +58,7 @@ MpvItem {
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
visible: !root.fileLoaded
|
||||
z: -1
|
||||
}
|
||||
@@ -95,6 +98,7 @@ MpvItem {
|
||||
onExited: root.pointerLeft()
|
||||
|
||||
onPressed: mouse => {
|
||||
root.forceActiveFocus(Qt.MouseFocusReason)
|
||||
switch (mouse.button) {
|
||||
case Qt.LeftButton:
|
||||
root.actions.playPause.trigger()
|
||||
|
||||
+69
-79
@@ -10,7 +10,7 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
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.
|
||||
Slider {
|
||||
id: root
|
||||
@@ -19,7 +19,7 @@ Slider {
|
||||
required property Actions actions
|
||||
|
||||
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.
|
||||
property bool pausedBeforeDrag: false
|
||||
|
||||
@@ -47,91 +47,81 @@ Slider {
|
||||
return chapter ? `${formatTime(time)} - ${chapter}` : formatTime(time)
|
||||
}
|
||||
|
||||
Accessible.name: KI18n.i18nc("@label:slider", "Playback position")
|
||||
Accessible.description: timeLabel(value)
|
||||
stepSize: 1
|
||||
|
||||
from: 0
|
||||
to: Math.max(root.mpv.duration, 0)
|
||||
enabled: root.mpv.fileLoaded && root.mpv.duration > 0
|
||||
focusPolicy: Qt.NoFocus
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
focusPolicy: Qt.StrongFocus
|
||||
|
||||
handle: Item { visible: false }
|
||||
|
||||
background: Rectangle {
|
||||
id: progressBarBackground
|
||||
|
||||
color: Kirigami.Theme.alternateBackgroundColor
|
||||
scale: root.mirrored ? -1 : 1
|
||||
|
||||
Rectangle {
|
||||
width: root.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: Kirigami.Theme.highlightColor
|
||||
// Preserve the Slider's own arrow-key behavior when playback shortcuts exist.
|
||||
Keys.onShortcutOverride: event => {
|
||||
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)) {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
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 {
|
||||
model: root.showChapterMarkers ? root.mpv.chapters : []
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
x: modelData.time / root.mpv.duration * progressBarBackground.width
|
||||
width: 2
|
||||
height: progressBarBackground.height
|
||||
color: Kirigami.Theme.textColor
|
||||
Repeater {
|
||||
model: root.showChapterMarkers ? root.mpv.chapters : []
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
x: root.leftPadding + (root.mirrored ? 1 - modelData.time / root.to : modelData.time / root.to) * root.availableWidth
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Kirigami.Units.smallSpacing / 2
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: timeToolTip
|
||||
|
||||
property real pointerX: 0
|
||||
|
||||
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()
|
||||
}
|
||||
onWheel: wheel => {
|
||||
if (wheel.angleDelta.y > 0) {
|
||||
root.actions.seekForwardMedium.trigger()
|
||||
} else if (wheel.angleDelta.y < 0) {
|
||||
root.actions.seekBackwardMedium.trigger()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import org.kde.ki18n
|
||||
import QtQuick.Controls
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
@@ -9,10 +10,21 @@ import org.kde.kirigami as Kirigami
|
||||
ToolButton {
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
ToolTip {
|
||||
@@ -21,11 +33,8 @@ ToolButton {
|
||||
delay: root.checked ? 0 : Kirigami.Units.toolTipDelay
|
||||
timeout: -1
|
||||
closePolicy: Popup.NoAutoClose
|
||||
contentItem: Label {
|
||||
text: root.toolTipText
|
||||
wrapMode: Text.WordWrap
|
||||
width: Math.min(implicitWidth, Kirigami.Units.gridUnit * 20)
|
||||
}
|
||||
width: Math.min(contentItem.implicitWidth, Kirigami.Units.gridUnit * 20) + leftPadding + rightPadding
|
||||
text: root.toolTipText
|
||||
x: (root.width - width) / 2
|
||||
width: Math.min(implicitWidth, Kirigami.Units.gridUnit * 20, root.Window.window.width - Kirigami.Units.largeSpacing * 2)
|
||||
}
|
||||
}
|
||||
+17
-25
@@ -7,49 +7,41 @@ import QtQuick.Controls
|
||||
import org.kde.kirigami as Kirigami
|
||||
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 {
|
||||
id: root
|
||||
|
||||
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
|
||||
to: root.mpv.volumeMax
|
||||
implicitWidth: 100
|
||||
implicitHeight: 25
|
||||
implicitWidth: Kirigami.Units.gridUnit * 5
|
||||
wheelEnabled: true
|
||||
stepSize: QtPotatoSettings.volumeStep
|
||||
focusPolicy: Qt.NoFocus
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
focusPolicy: Qt.StrongFocus
|
||||
|
||||
handle: Item { visible: false }
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.alternateBackgroundColor
|
||||
scale: root.mirrored ? -1 : 1
|
||||
|
||||
Rectangle {
|
||||
width: root.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: Kirigami.Theme.highlightColor
|
||||
// Let the Slider consume navigation keys before window playback shortcuts.
|
||||
Keys.onShortcutOverride: event => {
|
||||
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)) {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
value: root.mpv.volume
|
||||
when: !root.pressed
|
||||
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"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user