From 0cd03335f7dc82172b5bc05f94aea9904e63ddf1 Mon Sep 17 00:00:00 2001 From: Tri Vo Khac Date: Sat, 7 Nov 2026 18:48:04 +0700 Subject: [PATCH] fix(menus): align menus and actions with KDE HIG --- src/mpv/mpvitem.cpp | 18 +++++++++++++ src/mpv/mpvitem.h | 8 ++++++ src/qml/Actions.qml | 49 +++++++++++++++++++++++++++++----- src/qml/AppMenu.qml | 24 ++++++++++++++++- src/qml/AppMenuItem.qml | 8 ++++-- src/qml/AudioMenu.qml | 1 + src/qml/AudioTracksMenu.qml | 10 ++++++- src/qml/FileMenu.qml | 17 ++++++++++-- src/qml/HamburgerMenu.qml | 23 +++++----------- src/qml/HelpMenu.qml | 1 + src/qml/MpvContextMenu.qml | 1 + src/qml/PlaybackMenu.qml | 2 ++ src/qml/RecentFilesMenu.qml | 5 +++- src/qml/SettingsMenu.qml | 20 +++++++------- src/qml/SubtitleTracksMenu.qml | 13 +++++++-- src/qml/SubtitlesMenu.qml | 11 ++++---- src/qml/VideoMenu.qml | 5 ++++ src/qml/ViewMenu.qml | 1 + 18 files changed, 170 insertions(+), 47 deletions(-) diff --git a/src/mpv/mpvitem.cpp b/src/mpv/mpvitem.cpp index d110a41..f21ad90 100644 --- a/src/mpv/mpvitem.cpp +++ b/src/mpv/mpvitem.cpp @@ -45,6 +45,8 @@ MpvItem::MpvItem(QQuickItem *parent) observeProperty(QStringLiteral("volume"), MPV_FORMAT_INT64); observeProperty(QStringLiteral("volume-max"), MPV_FORMAT_INT64); observeProperty(QStringLiteral("mute"), MPV_FORMAT_FLAG); + observeProperty(QStringLiteral("sub-visibility"), MPV_FORMAT_FLAG); + observeProperty(QStringLiteral("deinterlace"), MPV_FORMAT_FLAG); observeProperty(QStringLiteral("speed"), MPV_FORMAT_DOUBLE); observeProperty(QStringLiteral("chapter-list"), MPV_FORMAT_NODE); observeProperty(QStringLiteral("chapter"), MPV_FORMAT_INT64); @@ -258,6 +260,16 @@ bool MpvItem::mute() const return m_mute; } +bool MpvItem::subtitleVisible() const +{ + return m_subtitleVisible; +} + +bool MpvItem::deinterlace() const +{ + return m_deinterlace; +} + void MpvItem::setMute(bool mute) { setPropertyAsync(QStringLiteral("mute"), mute); @@ -414,6 +426,12 @@ void MpvItem::onPropertyChanged(const QString &property, const QVariant &value) } else if (property == u"mute") { m_mute = value.toBool(); Q_EMIT muteChanged(); + } else if (property == u"sub-visibility") { + m_subtitleVisible = value.toBool(); + Q_EMIT subtitleVisibleChanged(); + } else if (property == u"deinterlace") { + m_deinterlace = value.toBool(); + Q_EMIT deinterlaceChanged(); } else if (property == u"speed") { m_speed = value.toDouble(); Q_EMIT speedChanged(); diff --git a/src/mpv/mpvitem.h b/src/mpv/mpvitem.h index 810ec9b..f37a590 100644 --- a/src/mpv/mpvitem.h +++ b/src/mpv/mpvitem.h @@ -36,6 +36,8 @@ class MpvItem : public MpvAbstractItem Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(int volumeMax READ volumeMax NOTIFY volumeMaxChanged) Q_PROPERTY(bool mute READ mute WRITE setMute NOTIFY muteChanged) + Q_PROPERTY(bool subtitleVisible READ subtitleVisible NOTIFY subtitleVisibleChanged) + Q_PROPERTY(bool deinterlace READ deinterlace NOTIFY deinterlaceChanged) Q_PROPERTY(double speed READ speed WRITE setSpeed NOTIFY speedChanged) // Chapters of the current file, each a map with title and time (in seconds). Q_PROPERTY(QVariantList chapters READ chapters NOTIFY chaptersChanged) @@ -76,6 +78,8 @@ public: int volumeMax() const; bool mute() const; void setMute(bool mute); + bool subtitleVisible() const; + bool deinterlace() const; double speed() const; void setSpeed(double speed); QVariantList chapters() const; @@ -116,6 +120,8 @@ Q_SIGNALS: void volumeChanged(); void volumeMaxChanged(); void muteChanged(); + void subtitleVisibleChanged(); + void deinterlaceChanged(); void speedChanged(); void chaptersChanged(); void chapterChanged(); @@ -149,6 +155,8 @@ private: int m_volume = 100; int m_volumeMax = 130; bool m_mute = false; + bool m_subtitleVisible = true; + bool m_deinterlace = false; double m_speed = 1.0; QVariantList m_chapters; int m_chapter = -1; diff --git a/src/qml/Actions.qml b/src/qml/Actions.qml index 0760410..0b0e524 100644 --- a/src/qml/Actions.qml +++ b/src/qml/Actions.qml @@ -22,6 +22,9 @@ Item { readonly property bool hasFile: mpv.fileLoaded + readonly property bool subtitlesVisible: mpv.subtitleVisible + readonly property bool deinterlacingEnabled: mpv.deinterlace + signal openFileRequested() // Open a known file, such as one from Recent Files. signal fileOpenRequested(url: url) @@ -115,7 +118,7 @@ Item { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Restart Playback") icon.name: "edit-reset" - shortcut: "F5" + shortcut: "Ctrl+Shift+R" onTriggered: root.mpv.seek(0) } @@ -138,6 +141,7 @@ Item { property Action seekForwardSmall: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek Small Step Forward") + icon.name: "go-next" shortcut: "Right" onTriggered: root.seekBy(QtPotatoSettings.seekSmallStep) } @@ -145,6 +149,7 @@ Item { property Action seekBackwardSmall: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek Small Step Backward") + icon.name: "go-previous" shortcut: "Left" onTriggered: root.seekBy(-QtPotatoSettings.seekSmallStep) } @@ -152,6 +157,7 @@ Item { property Action seekForwardMedium: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek Medium Step Forward") + icon.name: "media-seek-forward" shortcut: "Shift+Right" onTriggered: root.seekBy(QtPotatoSettings.seekMediumStep) } @@ -159,6 +165,7 @@ Item { property Action seekBackwardMedium: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek Medium Step Backward") + icon.name: "media-seek-backward" shortcut: "Shift+Left" onTriggered: root.seekBy(-QtPotatoSettings.seekMediumStep) } @@ -166,6 +173,7 @@ Item { property Action seekForwardBig: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek Big Step Forward") + icon.name: "arrow-right-double" shortcut: "Up" onTriggered: root.seekBy(QtPotatoSettings.seekBigStep) } @@ -173,6 +181,7 @@ Item { property Action seekBackwardBig: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek Big Step Backward") + icon.name: "arrow-left-double" shortcut: "Down" onTriggered: root.seekBy(-QtPotatoSettings.seekBigStep) } @@ -180,6 +189,7 @@ Item { property Action seekNextSubtitle: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek To Next Subtitle") + icon.name: "go-down" shortcut: "Ctrl+Right" onTriggered: root.mpv.commandAsync(["sub-seek", "1"]) } @@ -187,6 +197,7 @@ Item { property Action seekPreviousSubtitle: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Seek To Previous Subtitle") + icon.name: "go-up" shortcut: "Ctrl+Left" onTriggered: root.mpv.commandAsync(["sub-seek", "-1"]) } @@ -194,6 +205,7 @@ Item { property Action seekNextChapter: Action { enabled: root.shortcutsEnabled && root.mpv.chapters.length > 0 text: KI18n.i18nc("@action", "Seek To Next Chapter") + icon.name: "go-last" shortcut: "PgUp" onTriggered: root.mpv.commandAsync(["add", "chapter", "1"]) } @@ -201,6 +213,7 @@ Item { property Action seekPreviousChapter: Action { enabled: root.shortcutsEnabled && root.mpv.chapters.length > 0 text: KI18n.i18nc("@action", "Seek To Previous Chapter") + icon.name: "go-first" shortcut: "PgDown" onTriggered: root.mpv.commandAsync(["add", "chapter", "-1"]) } @@ -208,6 +221,7 @@ Item { property Action frameStepForward: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Step Frame Forward") + icon.name: "keyframe-next" shortcut: "." onTriggered: root.mpv.frameStep(true) } @@ -215,6 +229,7 @@ Item { property Action frameStepBackward: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Step Frame Backward") + icon.name: "keyframe-previous" shortcut: "," onTriggered: root.mpv.frameStep(false) } @@ -222,6 +237,7 @@ Item { property Action speedUp: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Increase Playback Speed") + icon.name: "media-seek-forward" shortcut: "]" onTriggered: root.changeSpeed(0.1) } @@ -229,6 +245,7 @@ Item { property Action speedDown: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Decrease Playback Speed") + icon.name: "media-seek-backward" shortcut: "[" onTriggered: root.changeSpeed(-0.1) } @@ -236,6 +253,7 @@ Item { property Action speedReset: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Reset Playback Speed") + icon.name: "speedometer" shortcut: "Backspace" onTriggered: root.changeSpeed(0) } @@ -270,6 +288,7 @@ Item { property Action audioCycleUp: Action { enabled: root.shortcutsEnabled && root.mpv.audioTracks.length > 1 text: KI18n.i18nc("@action", "Cycle Up Audio Track") + icon.name: "go-up" shortcut: "Shift+3" onTriggered: root.mpv.audioId = root.cycleTrack(root.mpv.audioTracks, root.mpv.audioId, 1) } @@ -277,6 +296,7 @@ Item { property Action audioCycleDown: Action { enabled: root.shortcutsEnabled && root.mpv.audioTracks.length > 1 text: KI18n.i18nc("@action", "Cycle Down Audio Track") + icon.name: "go-down" shortcut: "Shift+2" onTriggered: root.mpv.audioId = root.cycleTrack(root.mpv.audioTracks, root.mpv.audioId, -1) } @@ -291,12 +311,16 @@ Item { property Action subtitleToggle: Action { enabled: root.shortcutsEnabled && root.mpv.subtitleTracks.length > 0 - text: KI18n.i18nc("@action", "Toggle Subtitles") + text: KI18n.i18nc("@action", "Show Subtitles") + icon.name: "media-view-subtitles-symbolic" + checkable: true + checked: root.subtitlesVisible shortcut: "Ctrl+S" onTriggered: { const visible = root.mpv.getProperty("sub-visibility") root.mpv.setPropertyAsync("sub-visibility", !visible) root.mpv.setPropertyAsync("secondary-sub-visibility", !visible) + checked = Qt.binding(() => root.subtitlesVisible) root.osd.message(visible ? KI18n.i18nc("@info:osd", "Subtitles hidden") : KI18n.i18nc("@info:osd", "Subtitles shown")) } } @@ -304,6 +328,7 @@ Item { property Action subtitleCycleUp: Action { enabled: root.shortcutsEnabled && root.mpv.subtitleTracks.length > 0 text: KI18n.i18nc("@action", "Cycle Up Subtitle") + icon.name: "go-up" shortcut: "J" onTriggered: root.mpv.subtitleId = root.cycleTrack(root.mpv.subtitleTracks, root.mpv.subtitleId, 1) } @@ -311,6 +336,7 @@ Item { property Action subtitleCycleDown: Action { enabled: root.shortcutsEnabled && root.mpv.subtitleTracks.length > 0 text: KI18n.i18nc("@action", "Cycle Down Subtitle") + icon.name: "go-down" shortcut: "Shift+J" onTriggered: root.mpv.subtitleId = root.cycleTrack(root.mpv.subtitleTracks, root.mpv.subtitleId, -1) } @@ -318,6 +344,7 @@ Item { property Action subtitleEarlier: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Show Subtitles Earlier") + icon.name: "go-previous" shortcut: "Z" onTriggered: root.changeSubtitleDelay(-0.1) } @@ -325,6 +352,7 @@ Item { property Action subtitleLater: Action { enabled: root.shortcutsEnabled && root.hasFile text: KI18n.i18nc("@action", "Show Subtitles Later") + icon.name: "go-next" shortcut: "Shift+Z" onTriggered: root.changeSubtitleDelay(0.1) } @@ -332,12 +360,14 @@ Item { property Action subtitleDelayReset: Action { enabled: root.shortcutsEnabled && root.hasFile && root.mpv.subtitleDelay !== 0 text: KI18n.i18nc("@action", "Reset Subtitle Delay") + icon.name: "edit-reset" onTriggered: root.changeSubtitleDelay(-root.mpv.subtitleDelay) } property Action subtitleBigger: Action { enabled: root.shortcutsEnabled text: KI18n.i18nc("@action", "Increase Subtitle Font Size") + icon.name: "format-font-size-more" shortcut: "Ctrl+Z" onTriggered: { QtPotatoSettings.subtitleFontSize = Math.min(150, QtPotatoSettings.subtitleFontSize + 2) @@ -349,6 +379,7 @@ Item { property Action subtitleSmaller: Action { enabled: root.shortcutsEnabled text: KI18n.i18nc("@action", "Decrease Subtitle Font Size") + icon.name: "format-font-size-less" shortcut: "Ctrl+X" onTriggered: { QtPotatoSettings.subtitleFontSize = Math.max(20, QtPotatoSettings.subtitleFontSize - 2) @@ -392,11 +423,15 @@ Item { property Action toggleDeinterlacing: Action { enabled: root.shortcutsEnabled && root.hasFile - text: KI18n.i18nc("@action", "Toggle Deinterlacing") + text: KI18n.i18nc("@action", "Deinterlace") + icon.name: "view-filter" + checkable: true + checked: root.deinterlacingEnabled shortcut: "D" onTriggered: { - const enabled = root.mpv.getProperty("deinterlace") === "yes" - root.mpv.setPropertyAsync("deinterlace", enabled ? "no" : "yes") + const enabled = root.mpv.getProperty("deinterlace") === true + root.mpv.setPropertyAsync("deinterlace", !enabled) + checked = Qt.binding(() => root.deinterlacingEnabled) root.osd.message(enabled ? KI18n.i18nc("@info:osd", "Deinterlacing off") : KI18n.i18nc("@info:osd", "Deinterlacing on")) } } @@ -476,6 +511,7 @@ Item { property Action toggleHeader: Action { enabled: root.shortcutsEnabled text: QtPotatoSettings.showHeader ? KI18n.i18nc("@action", "Hide Toolbar") : KI18n.i18nc("@action", "Show Toolbar") + icon.name: "configure-toolbars" shortcut: "Ctrl+H" onTriggered: root.headerToggleRequested() } @@ -489,7 +525,7 @@ Item { } property Action configure: Action { - text: KI18n.i18nc("@action", "Settings") + text: KI18n.i18nc("@action", "Configure QtPotato…") icon.name: "configure" shortcut: "Ctrl+Shift+," onTriggered: root.settingsRequested("general") @@ -498,7 +534,6 @@ Item { property Action about: Action { text: KI18n.i18nc("@action", "About QtPotato") icon.name: "help-about" - shortcut: "F1" onTriggered: root.settingsRequested("about") } diff --git a/src/qml/AppMenu.qml b/src/qml/AppMenu.qml index 97f657b..0978f67 100644 --- a/src/qml/AppMenu.qml +++ b/src/qml/AppMenu.qml @@ -1,8 +1,30 @@ // SPDX-FileCopyrightText: 2026 Tri Vo Khac // SPDX-License-Identifier: GPL-3.0-or-later +import QtQuick import QtQuick.Controls +import org.kde.kirigami as Kirigami Menu { - delegate: AppMenuItem {} + id: root + + readonly property bool hasCheckables: { + for (let i = 0; i < count; ++i) { + const item = itemAt(i) + if (item && item.checkable) { + return true + } + } + return false + } + + // Keep long filenames and translated labels from obscuring parent menus. + readonly property real maximumWidth: Math.min(Kirigami.Units.gridUnit * 30, + (root.parent && root.parent.Window.window ? root.parent.Window.window.width : Kirigami.Units.gridUnit * 30)) + width: Math.min(implicitWidth, maximumWidth) + + delegate: AppMenuItem { + icon.name: subMenu ? subMenu.icon.name : (action ? action.icon.name : "") + icon.source: subMenu ? subMenu.icon.source : (action ? action.icon.source : "") + } } diff --git a/src/qml/AppMenuItem.qml b/src/qml/AppMenuItem.qml index 9c95e61..582b5b1 100644 --- a/src/qml/AppMenuItem.qml +++ b/src/qml/AppMenuItem.qml @@ -9,6 +9,8 @@ import org.kde.kirigami as Kirigami MenuItem { id: root + property int textElide: Text.ElideRight + // org.kde.desktop sets these on its MenuItem, but other Plasma styles such // as org.kde.union do not, which would leave the label below empty. Kirigami.MnemonicData.enabled: enabled && visible @@ -33,7 +35,7 @@ MenuItem { layoutDirection: root.mirrored ? Qt.RightToLeft : Qt.LeftToRight Item { - Layout.preferredWidth: (root.ListView.view && root.ListView.view.hasCheckables) || root.checkable + Layout.preferredWidth: (root.menu && root.menu.hasCheckables) || root.checkable ? root.indicator.width : Kirigami.Units.smallSpacing } @@ -62,12 +64,14 @@ MenuItem { textFormat: Text.StyledText font: root.font color: root.pressed ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor - elide: Text.ElideRight + elide: root.textElide horizontalAlignment: root.mirrored ? Text.AlignRight : Text.AlignLeft } Label { id: shortcut + Layout.leftMargin: root.mirrored ? 0 : Kirigami.Units.largeSpacing - row.spacing + Layout.rightMargin: root.mirrored ? Kirigami.Units.largeSpacing - row.spacing : 0 Layout.alignment: Qt.AlignBaseline visible: !root.subMenu && root.action && root.action.shortcut !== undefined text: visible ? itemShortcut.nativeText : "" diff --git a/src/qml/AudioMenu.qml b/src/qml/AudioMenu.qml index 6b27383..57e2b66 100644 --- a/src/qml/AudioMenu.qml +++ b/src/qml/AudioMenu.qml @@ -8,6 +8,7 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "audio-volume-high" required property Actions actions required property MpvItem mpv diff --git a/src/qml/AudioTracksMenu.qml b/src/qml/AudioTracksMenu.qml index 458c4c9..396aea9 100644 --- a/src/qml/AudioTracksMenu.qml +++ b/src/qml/AudioTracksMenu.qml @@ -11,6 +11,7 @@ import org.kde.ki18n // Checkable list of audio tracks. AppMenu { id: root + icon.name: "audio-x-generic" required property MpvItem mpv @@ -23,14 +24,21 @@ AppMenu { enabled: mpv.audioTracks.length > 0 + ButtonGroup { id: tracksGroup; exclusive: true } + Instantiator { model: root.mpv.audioTracks delegate: AppMenuItem { + id: trackItem required property var modelData text: root.trackLabel(modelData) checkable: true + ButtonGroup.group: tracksGroup checked: modelData.id === root.mpv.audioId - onTriggered: root.mpv.audioId = modelData.id + onTriggered: { + root.mpv.audioId = modelData.id + checked = Qt.binding(() => trackItem.modelData.id === root.mpv.audioId) + } } onObjectAdded: (index, object) => root.insertItem(index, object) onObjectRemoved: (index, object) => root.removeItem(object) diff --git a/src/qml/FileMenu.qml b/src/qml/FileMenu.qml index 95d9335..524a0a4 100644 --- a/src/qml/FileMenu.qml +++ b/src/qml/FileMenu.qml @@ -8,14 +8,27 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "folder" required property Actions actions + // The hamburger must not instantiate application management commands. + property bool includeQuit: true title: KI18n.i18nc("@title:menu", "&File") AppMenuItem { action: root.actions.openFile } AppMenuItem { action: root.actions.openUrl } RecentFilesMenu { actions: root.actions } - MenuSeparator {} - AppMenuItem { action: root.actions.quit } + Instantiator { + model: root.includeQuit ? 1 : 0 + delegate: MenuSeparator {} + onObjectAdded: (index, object) => root.insertItem(3, object) + onObjectRemoved: (index, object) => root.removeItem(object) + } + Instantiator { + model: root.includeQuit ? 1 : 0 + delegate: AppMenuItem { action: root.actions.quit } + onObjectAdded: (index, object) => root.insertItem(4, object) + onObjectRemoved: (index, object) => root.removeItem(object) + } } diff --git a/src/qml/HamburgerMenu.qml b/src/qml/HamburgerMenu.qml index 695884d..85704ca 100644 --- a/src/qml/HamburgerMenu.qml +++ b/src/qml/HamburgerMenu.qml @@ -13,7 +13,7 @@ ToolButton { required property Actions actions required property MpvItem mpv - // In the footer the menu opens upwards and also offers what the header would. + // In the footer the same menu opens upwards. property bool inFooter: false readonly property alias isOpen: menu.visible @@ -36,16 +36,8 @@ ToolButton { modal: true dim: false - AppMenuItem { - action: root.actions.openFile - visible: root.inFooter - height: visible ? implicitHeight : 0 - } - AppMenuItem { - action: root.actions.openUrl - visible: root.inFooter - height: visible ? implicitHeight : 0 - } + AppMenuItem { action: root.actions.openFile } + AppMenuItem { action: root.actions.openUrl } RecentFilesMenu { actions: root.actions } SubtitleTracksMenu { mpv: root.mpv @@ -67,17 +59,14 @@ ToolButton { AppMenuItem { action: root.actions.togglePlaylist } AppMenuItem { action: root.actions.screenshot } MenuSeparator {} - AppMenuItem { - action: root.actions.configure - visible: root.inFooter - height: visible ? implicitHeight : 0 - } + AppMenuItem { action: root.actions.configure } AppMenuItem { action: root.actions.toggleMenuBar } AppMenuItem { action: root.actions.about } MenuSeparator {} AppMenu { title: KI18n.i18nc("@title:menu", "&More") - FileMenu { actions: root.actions } + icon.name: "overflow-menu" + FileMenu { actions: root.actions; includeQuit: false } ViewMenu { actions: root.actions } PlaybackMenu { actions: root.actions } VideoMenu { actions: root.actions } diff --git a/src/qml/HelpMenu.qml b/src/qml/HelpMenu.qml index 65e09fc..400f86c 100644 --- a/src/qml/HelpMenu.qml +++ b/src/qml/HelpMenu.qml @@ -8,6 +8,7 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "help-contents" required property Actions actions diff --git a/src/qml/MpvContextMenu.qml b/src/qml/MpvContextMenu.qml index 784c458..689b0aa 100644 --- a/src/qml/MpvContextMenu.qml +++ b/src/qml/MpvContextMenu.qml @@ -18,6 +18,7 @@ AppMenu { AppMenuItem { action: root.actions.playPreviousFile } AppMenu { title: KI18n.i18nc("@title:menu", "Playback Speed") + icon.name: "speedometer" AppMenuItem { action: root.actions.speedUp } AppMenuItem { action: root.actions.speedDown } AppMenuItem { action: root.actions.speedReset } diff --git a/src/qml/PlaybackMenu.qml b/src/qml/PlaybackMenu.qml index 801cfab..acb1faa 100644 --- a/src/qml/PlaybackMenu.qml +++ b/src/qml/PlaybackMenu.qml @@ -8,6 +8,7 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "media-playback-start" required property Actions actions @@ -25,6 +26,7 @@ AppMenu { MenuSeparator {} AppMenu { title: KI18n.i18nc("@title:menu", "Seek") + icon.name: "go-jump" AppMenuItem { action: root.actions.seekForwardSmall } AppMenuItem { action: root.actions.seekBackwardSmall } MenuSeparator {} diff --git a/src/qml/RecentFilesMenu.qml b/src/qml/RecentFilesMenu.qml index 64e884d..eb9584b 100644 --- a/src/qml/RecentFilesMenu.qml +++ b/src/qml/RecentFilesMenu.qml @@ -6,10 +6,12 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Controls +import org.kde.kirigami as Kirigami import org.kde.ki18n AppMenu { id: root + icon.name: "document-open-recent" required property Actions actions @@ -23,11 +25,12 @@ AppMenu { required property string name required property string path text: name + textElide: Text.ElideMiddle onTriggered: root.actions.fileOpenRequested(url) ToolTip.text: path ToolTip.visible: hovered - ToolTip.delay: 1000 + ToolTip.delay: Kirigami.Units.toolTipDelay } onObjectAdded: (index, object) => root.insertItem(index, object) onObjectRemoved: (index, object) => root.removeItem(object) diff --git a/src/qml/SettingsMenu.qml b/src/qml/SettingsMenu.qml index a522156..1237ece 100644 --- a/src/qml/SettingsMenu.qml +++ b/src/qml/SettingsMenu.qml @@ -8,33 +8,35 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "settings-configure" required property Actions actions title: KI18n.i18nc("@title:menu", "&Settings") AppMenuItem { - text: KI18n.i18nc("@action:inmenu", "General") + text: KI18n.i18nc("@action:inmenu", "General…") + icon.name: "configure" onTriggered: root.actions.settingsRequested("general") } AppMenuItem { - text: KI18n.i18nc("@action:inmenu", "Playback") + text: KI18n.i18nc("@action:inmenu", "Playback…") + icon.name: "preferences-media-playback-amarok" onTriggered: root.actions.settingsRequested("playback") } AppMenuItem { - text: KI18n.i18nc("@action:inmenu", "Subtitles") + text: KI18n.i18nc("@action:inmenu", "Subtitles…") + icon.name: "preferences-desktop-font" onTriggered: root.actions.settingsRequested("subtitles") } AppMenuItem { - text: KI18n.i18nc("@action:inmenu", "Translation") + text: KI18n.i18nc("@action:inmenu", "Translation…") + icon.name: "translate" onTriggered: root.actions.settingsRequested("translation") } AppMenuItem { - text: KI18n.i18nc("@action:inmenu", "Playlist") + text: KI18n.i18nc("@action:inmenu", "Playlist…") + icon.name: "view-media-playlist" onTriggered: root.actions.settingsRequested("playlist") } - AppMenuItem { - text: KI18n.i18nc("@action:inmenu", "About") - onTriggered: root.actions.settingsRequested("about") - } } diff --git a/src/qml/SubtitleTracksMenu.qml b/src/qml/SubtitleTracksMenu.qml index b6b4c41..bf59e38 100644 --- a/src/qml/SubtitleTracksMenu.qml +++ b/src/qml/SubtitleTracksMenu.qml @@ -11,6 +11,7 @@ import org.kde.ki18n // Checkable list of subtitle tracks, for the primary or the secondary subtitle. AppMenu { id: root + icon.name: isPrimary ? "text-x-generic" : "text-x-script" required property MpvItem mpv property bool isPrimary: true @@ -36,13 +37,16 @@ AppMenu { enabled: mpv.subtitleTracks.length > 0 + ButtonGroup { id: tracksGroup; exclusive: true } + AppMenuItem { text: KI18n.i18nc("@item:inmenu no subtitle", "None") checkable: true + ButtonGroup.group: tracksGroup checked: root.selectedId === 0 onTriggered: { root.select(0) - // Clicking a checked item unchecks it, which drops the binding. + // Keep mpv authoritative after MenuItem updates its checked state. checked = Qt.binding(() => root.selectedId === 0) } } @@ -50,13 +54,18 @@ AppMenu { Instantiator { model: root.mpv.subtitleTracks delegate: AppMenuItem { + id: trackItem required property var modelData text: root.trackLabel(modelData) checkable: true + ButtonGroup.group: tracksGroup checked: modelData.id === root.selectedId // mpv cannot show one track in both places. enabled: modelData.id !== root.otherId - onTriggered: root.select(modelData.id) + onTriggered: { + root.select(modelData.id) + checked = Qt.binding(() => trackItem.modelData.id === root.selectedId) + } } onObjectAdded: (index, object) => root.insertItem(index + 1, object) onObjectRemoved: (index, object) => root.removeItem(object) diff --git a/src/qml/SubtitlesMenu.qml b/src/qml/SubtitlesMenu.qml index 96626a7..6bb2b80 100644 --- a/src/qml/SubtitlesMenu.qml +++ b/src/qml/SubtitlesMenu.qml @@ -8,12 +8,14 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "media-view-subtitles-symbolic" required property Actions actions required property MpvItem mpv title: KI18n.i18nc("@title:menu", "&Subtitles") + SubtitleTracksMenu { mpv: root.mpv isPrimary: true @@ -27,18 +29,17 @@ AppMenu { MenuSeparator {} AppMenuItem { action: root.actions.translateSubtitle } AppMenuItem { action: root.actions.editGlossary } - AppMenuItem { - action: root.actions.cancelTranslation - visible: root.actions.cancelTranslation.enabled - height: visible ? implicitHeight : 0 - } + AppMenuItem { action: root.actions.cancelTranslation } MenuSeparator {} AppMenuItem { action: root.actions.addSubtitle } + MenuSeparator {} AppMenuItem { action: root.actions.subtitleBigger } AppMenuItem { action: root.actions.subtitleSmaller } + MenuSeparator {} AppMenuItem { action: root.actions.subtitleEarlier } AppMenuItem { action: root.actions.subtitleLater } AppMenuItem { action: root.actions.subtitleDelayReset } + MenuSeparator {} AppMenuItem { action: root.actions.subtitleToggle } AppMenuItem { action: root.actions.subtitleCycleUp } AppMenuItem { action: root.actions.subtitleCycleDown } diff --git a/src/qml/VideoMenu.qml b/src/qml/VideoMenu.qml index b3fc970..f352c2e 100644 --- a/src/qml/VideoMenu.qml +++ b/src/qml/VideoMenu.qml @@ -8,16 +8,21 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "video-display" required property Actions actions title: KI18n.i18nc("@title:menu", "&Video") + AppMenuItem { action: root.actions.toggleDeinterlacing } + MenuSeparator {} AppMenuItem { action: root.actions.zoomIn } AppMenuItem { action: root.actions.zoomOut } AppMenuItem { action: root.actions.zoomReset } + MenuSeparator {} AppMenuItem { action: root.actions.screenshot } + MenuSeparator {} AppMenuItem { action: root.actions.rotateClockwise } AppMenuItem { action: root.actions.rotateCounterClockwise } } diff --git a/src/qml/ViewMenu.qml b/src/qml/ViewMenu.qml index a17cf52..307f0ea 100644 --- a/src/qml/ViewMenu.qml +++ b/src/qml/ViewMenu.qml @@ -8,6 +8,7 @@ import org.kde.ki18n AppMenu { id: root + icon.name: "view-list-details" required property Actions actions