feat(ui): style app menus and extract glossary dialog

- Add AppMenu and AppMenuItem so menu labels, mnemonics and shortcuts render consistently under org.kde.union
- Move the translation glossary into GlossaryDialog with add, remove and clear-all actions
- Regroup the translation preset and instructions settings layout
This commit was merged in pull request #3.
This commit is contained in:
tri committed 2026-11-07 15:03:37 +07:00
1 parent 9e55dba687
commit de9e6484ee
24 files changed
+519 -152

No files matched your search

+3
View File
@@ -62,11 +62,14 @@ ecm_add_qml_module(qtpotato
ecm_target_qml_sources(qtpotato SOURCES
qml/Actions.qml
qml/AppMenu.qml
qml/AppMenuItem.qml
qml/AudioMenu.qml
qml/AudioTracksMenu.qml
qml/FileMenu.qml
qml/Footer.qml
qml/GeneralSettings.qml
qml/GlossaryDialog.qml
qml/HamburgerMenu.qml
qml/Header.qml
qml/HelpMenu.qml
+8
View File
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick.Controls
Menu {
delegate: AppMenuItem {}
}
+93
View File
@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
MenuItem {
id: root
// 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
Kirigami.MnemonicData.controlType: Kirigami.MnemonicData.MenuItem
Kirigami.MnemonicData.label: text
// The content includes the submenu arrow slot, so do not add it again as
// the desktop style does. Keep the style's background and indicator sizing.
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
contentItem: Item {
implicitWidth: row.implicitWidth
implicitHeight: row.implicitHeight
baselineOffset: row.y + label.y + label.baselineOffset
RowLayout {
id: row
width: parent.width
anchors.verticalCenter: parent.verticalCenter
spacing: Kirigami.Units.smallSpacing
layoutDirection: root.mirrored ? Qt.RightToLeft : Qt.LeftToRight
Item {
Layout.preferredWidth: (root.ListView.view && root.ListView.view.hasCheckables) || root.checkable
? root.indicator.width : Kirigami.Units.smallSpacing
}
// Reserve the icon column even for rows without an icon.
Item {
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Layout.preferredWidth
Layout.alignment: Qt.AlignVCenter
Kirigami.Icon {
anchors.fill: parent
visible: root.icon.name !== "" || root.icon.source.toString() !== ""
source: root.icon.name !== "" ? root.icon.name : root.icon.source
color: root.icon.color
selected: root.pressed
}
}
Label {
id: label
Layout.fillWidth: true
Layout.alignment: Qt.AlignBaseline
text: root.Kirigami.MnemonicData.richTextLabel
// StyledText keeps mnemonic underlines without RichText's
// different line metrics relative to the shortcut label.
textFormat: Text.StyledText
font: root.font
color: root.pressed ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor
elide: Text.ElideRight
horizontalAlignment: root.mirrored ? Text.AlignRight : Text.AlignLeft
}
Label {
id: shortcut
Layout.alignment: Qt.AlignBaseline
visible: !root.subMenu && root.action && root.action.shortcut !== undefined
text: visible ? itemShortcut.nativeText : ""
textFormat: Text.PlainText
font: root.font
color: label.color
horizontalAlignment: Text.AlignRight
Shortcut {
id: itemShortcut
sequence: shortcut.visible && root.action ? root.action.shortcut : ""
}
}
// The inherited arrow sits over this slot. Without a submenu,
// shortcut text reaches the very same trailing edge.
Item {
visible: root.subMenu !== null
Layout.preferredWidth: root.arrow ? root.arrow.width : 0
}
}
}
}
+6 -6
View File
@@ -6,7 +6,7 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
@@ -19,9 +19,9 @@ Menu {
title: KI18n.i18nc("@title:menu", "&Track")
}
MenuSeparator {}
MenuItem { action: root.actions.mute }
MenuItem { action: root.actions.volumeUp }
MenuItem { action: root.actions.volumeDown }
MenuItem { action: root.actions.audioCycleUp }
MenuItem { action: root.actions.audioCycleDown }
AppMenuItem { action: root.actions.mute }
AppMenuItem { action: root.actions.volumeUp }
AppMenuItem { action: root.actions.volumeDown }
AppMenuItem { action: root.actions.audioCycleUp }
AppMenuItem { action: root.actions.audioCycleDown }
}
+2 -2
View File
@@ -9,7 +9,7 @@ import QtQuick.Controls
import org.kde.ki18n
// Checkable list of audio tracks.
Menu {
AppMenu {
id: root
required property MpvItem mpv
@@ -25,7 +25,7 @@ Menu {
Instantiator {
model: root.mpv.audioTracks
delegate: MenuItem {
delegate: AppMenuItem {
required property var modelData
text: root.trackLabel(modelData)
checkable: true
+4 -4
View File
@@ -6,16 +6,16 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
title: KI18n.i18nc("@title:menu", "&File")
MenuItem { action: root.actions.openFile }
MenuItem { action: root.actions.openUrl }
AppMenuItem { action: root.actions.openFile }
AppMenuItem { action: root.actions.openUrl }
RecentFilesMenu { actions: root.actions }
MenuSeparator {}
MenuItem { action: root.actions.quit }
AppMenuItem { action: root.actions.quit }
}
+287
View File
@@ -0,0 +1,287 @@
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.ki18n
import org.kde.kirigami as Kirigami
import org.kde.kirigami.dialogs as KDialogs
Kirigami.Dialog {
id: root
required property var translator
readonly property real removeButtonWidth: Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.smallSpacing * 2
// One inset for the header, content and footer so every edge lines up.
readonly property real inset: Kirigami.Units.largeSpacing
title: KI18n.i18nc("@title:window", "Translation Glossary")
standardButtons: Kirigami.Dialog.NoButton
width: Math.min(parent.width * 0.9, Kirigami.Units.gridUnit * 38)
height: Math.min(parent.height - Kirigami.Units.largeSpacing * 2, Kirigami.Units.gridUnit * 30)
leftPadding: inset
rightPadding: inset
topPadding: 0
bottomPadding: 0
header: KDialogs.DialogHeader {
dialog: root
leftPadding: root.inset
rightPadding: root.inset
topPadding: root.inset
contentItem: KDialogs.DialogHeaderTopContent {
dialog: root
}
}
function addTerm() {
glossaryModel.append({
source: "",
target: ""
})
glossaryList.currentIndex = glossaryModel.count - 1
Qt.callLater(() => {
glossaryList.forceLayout()
glossaryList.positionViewAtIndex(glossaryList.currentIndex, ListView.Contain)
glossaryList.forceLayout()
const row = glossaryList.itemAtIndex(glossaryList.currentIndex)
if (row) {
row.focusOriginal()
}
})
}
onOpened: {
glossaryModel.clear()
for (const entry of translator.glossaryEntries) {
glossaryModel.append(entry)
}
glossaryList.positionViewAtBeginning()
}
onAccepted: {
const entries = []
for (let i = 0; i < glossaryModel.count; ++i) {
const entry = glossaryModel.get(i)
entries.push({
source: entry.source,
target: entry.target
})
}
translator.saveGlossary(entries)
}
ListModel {
id: glossaryModel
}
// Union's DialogButtonBox adds its own padding on top of the dialog's, so
// lay the footer out ourselves with the same inset as the header and content.
footer: Control {
leftPadding: root.inset
rightPadding: root.inset
topPadding: Kirigami.Units.largeSpacing
bottomPadding: root.inset
contentItem: RowLayout {
spacing: Kirigami.Units.smallSpacing
Item {
Layout.fillWidth: true
}
Button {
text: KI18n.i18nc("@action:button", "Save")
icon.name: "document-save"
onClicked: root.accept()
}
Button {
text: KI18n.i18nc("@action:button", "Cancel")
icon.name: "dialog-cancel"
onClicked: root.reject()
}
}
}
Action {
id: addTermAction
text: KI18n.i18nc("@action:button", "Add Term")
icon.name: "list-add"
onTriggered: root.addTerm()
}
contentItem: ColumnLayout {
spacing: Kirigami.Units.largeSpacing
RowLayout {
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing
Label {
Layout.fillWidth: true
text: KI18n.i18ncp("@label glossary term count", "%1 term", "%1 terms", glossaryModel.count)
color: Kirigami.Theme.disabledTextColor
}
Button {
action: addTermAction
}
Button {
text: KI18n.i18nc("@action:button", "Clear All")
icon.name: "edit-clear-all"
enabled: glossaryModel.count > 0
onClicked: glossaryModel.clear()
}
}
RowLayout {
visible: glossaryModel.count > 0
Layout.fillWidth: true
Layout.rightMargin: listScroll.rightPadding
spacing: Kirigami.Units.smallSpacing
Label {
Layout.fillWidth: true
Layout.preferredWidth: 1
Layout.minimumWidth: 0
text: KI18n.i18nc("@title:column glossary source term", "Original")
font: Kirigami.Theme.smallFont
color: Kirigami.Theme.disabledTextColor
}
Label {
Layout.fillWidth: true
Layout.preferredWidth: 1
Layout.minimumWidth: 0
text: KI18n.i18nc("@title:column glossary translated term", "Translation")
font: Kirigami.Theme.smallFont
color: Kirigami.Theme.disabledTextColor
}
Item {
Layout.preferredWidth: root.removeButtonWidth
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
ScrollView {
id: listScroll
anchors.fill: parent
clip: true
contentWidth: availableWidth
leftPadding: 0
topPadding: 0
bottomPadding: 0
// Room for the scrollbar plus a gap, so its right edge lines up
// with the buttons above and below.
rightPadding: ScrollBar.vertical.visible ? ScrollBar.vertical.width + Kirigami.Units.smallSpacing : 0
// The style's frame and focus ring would stretch into the margin.
background: null
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
// The style's scrollbar is wide and draws a separator line; a slim
// one keeps the right side tidy.
ScrollBar.vertical: ScrollBar {
parent: listScroll
policy: ScrollBar.AsNeeded
active: true
// Union sets each side explicitly, so `padding: 0` alone is not enough.
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
leftInset: 0
rightInset: 0
topInset: 0
bottomInset: 0
background: null
contentItem: Rectangle {
implicitWidth: Kirigami.Units.smallSpacing * 1.5
radius: width / 2
color: Kirigami.Theme.textColor
opacity: parent.pressed ? 0.6 : parent.hovered ? 0.45 : 0.25
}
width: implicitWidth
x: listScroll.width - width
y: 0
height: listScroll.height
}
contentItem: ListView {
id: glossaryList
clip: true
spacing: Kirigami.Units.smallSpacing
boundsBehavior: Flickable.StopAtBounds
model: glossaryModel
delegate: RowLayout {
id: termRow
required property int index
required property string source
required property string target
width: glossaryList.width
height: implicitHeight
spacing: Kirigami.Units.smallSpacing
function focusOriginal() {
originalField.forceActiveFocus(Qt.TabFocusReason)
}
TextField {
id: originalField
Layout.fillWidth: true
Layout.preferredWidth: 1
Layout.minimumWidth: 0
text: termRow.source
placeholderText: KI18n.i18nc("@info:placeholder glossary source term", "Original term")
Accessible.name: KI18n.i18nc("@label:textbox glossary source term", "Original term")
onTextEdited: glossaryModel.setProperty(termRow.index, "source", text)
}
TextField {
Layout.fillWidth: true
Layout.preferredWidth: 1
Layout.minimumWidth: 0
text: termRow.target
placeholderText: KI18n.i18nc("@info:placeholder glossary translated term", "Translation")
Accessible.name: KI18n.i18nc("@label:textbox glossary translated term", "Translation")
onTextEdited: glossaryModel.setProperty(termRow.index, "target", text)
}
ToolButton {
Layout.preferredWidth: root.removeButtonWidth
Layout.minimumWidth: root.removeButtonWidth
Layout.maximumWidth: root.removeButtonWidth
Layout.alignment: Qt.AlignVCenter
flat: true
icon.name: "edit-delete"
icon.color: Kirigami.Theme.textColor
display: AbstractButton.IconOnly
text: KI18n.i18nc("@action:button remove glossary term", "Remove")
ToolTip.text: text
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
onClicked: glossaryModel.remove(termRow.index)
}
}
}
}
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
width: parent.width - Kirigami.Units.largeSpacing * 2
visible: glossaryModel.count === 0
text: KI18n.i18nc("@info:placeholder", "No glossary terms yet")
explanation: KI18n.i18nc("@info:placeholder", "Terms are extracted automatically before translation, or you can add them manually.")
helpfulAction: addTermAction
}
}
}
}
+11 -11
View File
@@ -29,19 +29,19 @@ ToolButton {
ToolTip.visible: hovered && !menu.visible
ToolTip.delay: Kirigami.Units.toolTipDelay
Menu {
AppMenu {
id: menu
y: root.inFooter ? -height - Kirigami.Units.largeSpacing : root.height + Kirigami.Units.largeSpacing
modal: true
dim: false
MenuItem {
AppMenuItem {
action: root.actions.openFile
visible: root.inFooter
height: visible ? implicitHeight : 0
}
MenuItem {
AppMenuItem {
action: root.actions.openUrl
visible: root.inFooter
height: visible ? implicitHeight : 0
@@ -62,20 +62,20 @@ ToolButton {
title: KI18n.i18nc("@title:menu", "Audio Track")
}
MenuSeparator {}
MenuItem { action: root.actions.translateSubtitle }
MenuItem { action: root.actions.toggleFullscreen }
MenuItem { action: root.actions.togglePlaylist }
MenuItem { action: root.actions.screenshot }
AppMenuItem { action: root.actions.translateSubtitle }
AppMenuItem { action: root.actions.toggleFullscreen }
AppMenuItem { action: root.actions.togglePlaylist }
AppMenuItem { action: root.actions.screenshot }
MenuSeparator {}
MenuItem {
AppMenuItem {
action: root.actions.configure
visible: root.inFooter
height: visible ? implicitHeight : 0
}
MenuItem { action: root.actions.toggleMenuBar }
MenuItem { action: root.actions.about }
AppMenuItem { action: root.actions.toggleMenuBar }
AppMenuItem { action: root.actions.about }
MenuSeparator {}
Menu {
AppMenu {
title: KI18n.i18nc("@title:menu", "&More")
FileMenu { actions: root.actions }
ViewMenu { actions: root.actions }
+2 -2
View File
@@ -79,8 +79,8 @@ ToolBar {
enabled: true
MenuSeparator {}
MenuItem { action: root.actions.translateSubtitle }
MenuItem { action: root.actions.addSubtitle }
AppMenuItem { action: root.actions.translateSubtitle }
AppMenuItem { action: root.actions.addSubtitle }
}
}
+3 -3
View File
@@ -6,13 +6,13 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
title: KI18n.i18nc("@title:menu", "&Help")
MenuItem { action: root.actions.about }
MenuItem { action: root.actions.reportBug }
AppMenuItem { action: root.actions.about }
AppMenuItem { action: root.actions.reportBug }
}
+2 -33
View File
@@ -318,39 +318,8 @@ ApplicationWindow {
onAccepted: video.addSubtitleFile(selectedFile)
}
Kirigami.Dialog {
GlossaryDialog {
id: glossaryDialog
title: KI18n.i18nc("@title:window", "Translation Glossary")
standardButtons: Kirigami.Dialog.Save | Kirigami.Dialog.Cancel
width: Math.min(appWindow.width * 0.9, 650)
contentItem: ColumnLayout {
ListView {
id: glossaryList
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 15
clip: true
model: ListModel { id: glossaryModel }
delegate: RowLayout {
width: glossaryList.width
TextField { text: source; Layout.fillWidth: true; onTextChanged: model.source = text }
TextField { text: target; Layout.fillWidth: true; onTextChanged: model.target = text }
ToolButton { icon.name: "edit-delete-remove"; onClicked: glossaryModel.remove(index) }
}
}
RowLayout {
Button { text: KI18n.i18nc("@action:button", "Add"); onClicked: glossaryModel.append({source: "", target: ""}) }
Button { text: KI18n.i18nc("@action:button", "Clear"); onClicked: glossaryModel.clear() }
}
}
onOpened: {
glossaryModel.clear()
for (const entry of translationService.glossaryEntries) glossaryModel.append(entry)
}
onAccepted: {
const entries = []
for (let i = 0; i < glossaryModel.count; ++i) entries.push(glossaryModel.get(i))
translationService.saveGlossary(entries)
}
translator: translationService
}
}
+16 -16
View File
@@ -7,24 +7,24 @@ import QtQuick.Controls
import org.kde.ki18n
// Right click menu of the video.
Menu {
AppMenu {
id: root
required property Actions actions
required property MpvItem mpv
MenuItem { action: root.actions.playPause }
MenuItem { action: root.actions.playNextFile }
MenuItem { action: root.actions.playPreviousFile }
Menu {
AppMenuItem { action: root.actions.playPause }
AppMenuItem { action: root.actions.playNextFile }
AppMenuItem { action: root.actions.playPreviousFile }
AppMenu {
title: KI18n.i18nc("@title:menu", "Playback Speed")
MenuItem { action: root.actions.speedUp }
MenuItem { action: root.actions.speedDown }
MenuItem { action: root.actions.speedReset }
AppMenuItem { action: root.actions.speedUp }
AppMenuItem { action: root.actions.speedDown }
AppMenuItem { action: root.actions.speedReset }
}
MenuSeparator {}
MenuItem { action: root.actions.openFile }
MenuItem { action: root.actions.openUrl }
AppMenuItem { action: root.actions.openFile }
AppMenuItem { action: root.actions.openUrl }
MenuSeparator {}
SubtitleTracksMenu {
mpv: root.mpv
@@ -40,7 +40,7 @@ Menu {
mpv: root.mpv
title: KI18n.i18nc("@title:menu", "Audio Track")
}
MenuItem { action: root.actions.translateSubtitle }
AppMenuItem { action: root.actions.translateSubtitle }
MenuSeparator {}
VideoMenu { actions: root.actions }
SubtitlesMenu {
@@ -52,10 +52,10 @@ Menu {
mpv: root.mpv
}
MenuSeparator {}
MenuItem { action: root.actions.toggleFullscreen }
MenuItem { action: root.actions.togglePlaylist }
MenuItem { action: root.actions.toggleMenuBar }
MenuItem { action: root.actions.toggleHeader }
AppMenuItem { action: root.actions.toggleFullscreen }
AppMenuItem { action: root.actions.togglePlaylist }
AppMenuItem { action: root.actions.toggleMenuBar }
AppMenuItem { action: root.actions.toggleHeader }
MenuSeparator {}
MenuItem { action: root.actions.configure }
AppMenuItem { action: root.actions.configure }
}
+22 -22
View File
@@ -6,41 +6,41 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
title: KI18n.i18nc("@title:menu", "&Playback")
MenuItem { action: root.actions.playPause }
MenuItem { action: root.actions.stop }
MenuItem { action: root.actions.playNextFile }
MenuItem { action: root.actions.playPreviousFile }
MenuItem { action: root.actions.restart }
AppMenuItem { action: root.actions.playPause }
AppMenuItem { action: root.actions.stop }
AppMenuItem { action: root.actions.playNextFile }
AppMenuItem { action: root.actions.playPreviousFile }
AppMenuItem { action: root.actions.restart }
MenuSeparator {}
MenuItem { action: root.actions.speedUp }
MenuItem { action: root.actions.speedDown }
MenuItem { action: root.actions.speedReset }
AppMenuItem { action: root.actions.speedUp }
AppMenuItem { action: root.actions.speedDown }
AppMenuItem { action: root.actions.speedReset }
MenuSeparator {}
Menu {
AppMenu {
title: KI18n.i18nc("@title:menu", "Seek")
MenuItem { action: root.actions.seekForwardSmall }
MenuItem { action: root.actions.seekBackwardSmall }
AppMenuItem { action: root.actions.seekForwardSmall }
AppMenuItem { action: root.actions.seekBackwardSmall }
MenuSeparator {}
MenuItem { action: root.actions.seekForwardMedium }
MenuItem { action: root.actions.seekBackwardMedium }
AppMenuItem { action: root.actions.seekForwardMedium }
AppMenuItem { action: root.actions.seekBackwardMedium }
MenuSeparator {}
MenuItem { action: root.actions.seekForwardBig }
MenuItem { action: root.actions.seekBackwardBig }
AppMenuItem { action: root.actions.seekForwardBig }
AppMenuItem { action: root.actions.seekBackwardBig }
MenuSeparator {}
MenuItem { action: root.actions.seekNextSubtitle }
MenuItem { action: root.actions.seekPreviousSubtitle }
AppMenuItem { action: root.actions.seekNextSubtitle }
AppMenuItem { action: root.actions.seekPreviousSubtitle }
MenuSeparator {}
MenuItem { action: root.actions.seekNextChapter }
MenuItem { action: root.actions.seekPreviousChapter }
AppMenuItem { action: root.actions.seekNextChapter }
AppMenuItem { action: root.actions.seekPreviousChapter }
MenuSeparator {}
MenuItem { action: root.actions.frameStepForward }
MenuItem { action: root.actions.frameStepBackward }
AppMenuItem { action: root.actions.frameStepForward }
AppMenuItem { action: root.actions.frameStepBackward }
}
}
+6 -6
View File
@@ -113,15 +113,15 @@ Item {
}
}
Menu {
AppMenu {
id: contextMenu
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Play")
icon.name: "media-playback-start"
onTriggered: root.playlist.play(root.index)
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Open Containing Folder")
icon.name: "folder-open"
enabled: root.url.toString().startsWith("file://")
@@ -131,19 +131,19 @@ Item {
}
}
MenuSeparator {}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Move Up")
icon.name: "go-up"
enabled: root.index > 0
onTriggered: root.playlist.move(root.index, root.index - 1)
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Move Down")
icon.name: "go-down"
enabled: root.index < root.playlist.count - 1
onTriggered: root.playlist.move(root.index, root.index + 1)
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Remove from Playlist")
icon.name: "list-remove"
onTriggered: root.playlist.remove(root.index)
+3 -3
View File
@@ -8,7 +8,7 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
@@ -18,7 +18,7 @@ Menu {
Instantiator {
model: RecentFilesModel
delegate: MenuItem {
delegate: AppMenuItem {
required property url url
required property string name
required property string path
@@ -35,7 +35,7 @@ Menu {
MenuSeparator {}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Clear List")
icon.name: "edit-clear-history"
onTriggered: RecentFilesModel.clear()
+7 -7
View File
@@ -6,34 +6,34 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
title: KI18n.i18nc("@title:menu", "&Settings")
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "General")
onTriggered: root.actions.settingsRequested("general")
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Playback")
onTriggered: root.actions.settingsRequested("playback")
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Subtitles")
onTriggered: root.actions.settingsRequested("subtitles")
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Translation")
onTriggered: root.actions.settingsRequested("translation")
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "Playlist")
onTriggered: root.actions.settingsRequested("playlist")
}
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@action:inmenu", "About")
onTriggered: root.actions.settingsRequested("about")
}
+3 -3
View File
@@ -9,7 +9,7 @@ import QtQuick.Controls
import org.kde.ki18n
// Checkable list of subtitle tracks, for the primary or the secondary subtitle.
Menu {
AppMenu {
id: root
required property MpvItem mpv
@@ -36,7 +36,7 @@ Menu {
enabled: mpv.subtitleTracks.length > 0
MenuItem {
AppMenuItem {
text: KI18n.i18nc("@item:inmenu no subtitle", "None")
checkable: true
checked: root.selectedId === 0
@@ -49,7 +49,7 @@ Menu {
Instantiator {
model: root.mpv.subtitleTracks
delegate: MenuItem {
delegate: AppMenuItem {
required property var modelData
text: root.trackLabel(modelData)
checkable: true
+13 -13
View File
@@ -6,7 +6,7 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
@@ -25,21 +25,21 @@ Menu {
title: KI18n.i18nc("@title:menu", "Secondary Subtitle")
}
MenuSeparator {}
MenuItem { action: root.actions.translateSubtitle }
MenuItem { action: root.actions.editGlossary }
MenuItem {
AppMenuItem { action: root.actions.translateSubtitle }
AppMenuItem { action: root.actions.editGlossary }
AppMenuItem {
action: root.actions.cancelTranslation
visible: root.actions.cancelTranslation.enabled
height: visible ? implicitHeight : 0
}
MenuSeparator {}
MenuItem { action: root.actions.addSubtitle }
MenuItem { action: root.actions.subtitleBigger }
MenuItem { action: root.actions.subtitleSmaller }
MenuItem { action: root.actions.subtitleEarlier }
MenuItem { action: root.actions.subtitleLater }
MenuItem { action: root.actions.subtitleDelayReset }
MenuItem { action: root.actions.subtitleToggle }
MenuItem { action: root.actions.subtitleCycleUp }
MenuItem { action: root.actions.subtitleCycleDown }
AppMenuItem { action: root.actions.addSubtitle }
AppMenuItem { action: root.actions.subtitleBigger }
AppMenuItem { action: root.actions.subtitleSmaller }
AppMenuItem { action: root.actions.subtitleEarlier }
AppMenuItem { action: root.actions.subtitleLater }
AppMenuItem { action: root.actions.subtitleDelayReset }
AppMenuItem { action: root.actions.subtitleToggle }
AppMenuItem { action: root.actions.subtitleCycleUp }
AppMenuItem { action: root.actions.subtitleCycleDown }
}
+7 -7
View File
@@ -147,12 +147,9 @@ SettingsBasePage {
Label {
text: KI18n.i18nc("@label:listbox", "Preset")
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
}
ColumnLayout {
Layout.fillWidth: true
ComboBox {
id: presetCombo
Layout.fillWidth: true
@@ -166,17 +163,19 @@ SettingsBasePage {
}
}
Item { Layout.preferredWidth: 1 }
Label {
text: presetCombo.currentIndex >= 0 ? presetCombo.model[presetCombo.currentIndex].guidance : ""
text: presetCombo.currentIndex >= 0 ? presetCombo.model[presetCombo.currentIndex].description : ""
wrapMode: Text.WordWrap
opacity: 0.7
font: Kirigami.Theme.smallFont
Layout.fillWidth: true
}
}
Label {
text: KI18n.i18nc("@label:textbox", "Additional instructions")
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.alignment: Qt.AlignRight | Qt.AlignBaseline
}
TextArea {
@@ -184,6 +183,7 @@ SettingsBasePage {
placeholderText: KI18n.i18nc("@info:placeholder", "e.g. Translate 'Young Master' as 'Thiếu gia'. Use 'ta/ngươi' between enemies.")
wrapMode: TextArea.Wrap
Layout.fillWidth: true
Layout.alignment: Qt.AlignBaseline
Layout.preferredHeight: Kirigami.Units.gridUnit * 4
// TextArea has no textEdited before Qt 6.9.
onTextChanged: QtPotatoSettings.customInstructions = text
+8 -8
View File
@@ -6,18 +6,18 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
title: KI18n.i18nc("@title:menu", "&Video")
MenuItem { action: root.actions.toggleDeinterlacing }
MenuItem { action: root.actions.zoomIn }
MenuItem { action: root.actions.zoomOut }
MenuItem { action: root.actions.zoomReset }
MenuItem { action: root.actions.screenshot }
MenuItem { action: root.actions.rotateClockwise }
MenuItem { action: root.actions.rotateCounterClockwise }
AppMenuItem { action: root.actions.toggleDeinterlacing }
AppMenuItem { action: root.actions.zoomIn }
AppMenuItem { action: root.actions.zoomOut }
AppMenuItem { action: root.actions.zoomReset }
AppMenuItem { action: root.actions.screenshot }
AppMenuItem { action: root.actions.rotateClockwise }
AppMenuItem { action: root.actions.rotateCounterClockwise }
}
+5 -5
View File
@@ -6,16 +6,16 @@ import QtQuick
import QtQuick.Controls
import org.kde.ki18n
Menu {
AppMenu {
id: root
required property Actions actions
title: KI18n.i18nc("@title:menu", "&View")
MenuItem { action: root.actions.toggleFullscreen }
MenuItem { action: root.actions.togglePlaylist }
AppMenuItem { action: root.actions.toggleFullscreen }
AppMenuItem { action: root.actions.togglePlaylist }
MenuSeparator {}
MenuItem { action: root.actions.toggleMenuBar }
MenuItem { action: root.actions.toggleHeader }
AppMenuItem { action: root.actions.toggleMenuBar }
AppMenuItem { action: root.actions.toggleHeader }
}
+6
View File
@@ -15,31 +15,37 @@ const QList<Preset> &all()
{
u"general"_s,
i18nc("@item:inlistbox translation preset", "General"),
i18nc("@info translation preset description", "Natural dialogue with conventional names and a tone suited to the scene."),
u"Keep names as they are conventionally rendered and use a natural, conversational tone appropriate to the scene."_s,
},
{
u"chinese-donghua"_s,
i18nc("@item:inlistbox translation preset", "Chinese animation / wuxia / xianxia"),
i18nc("@info translation preset description", "Traditional names, cultivation terms, and period-appropriate forms of address."),
u"For Chinese animation, wuxia, xianxia, and historical settings, the source is often an English subtitle with pinyin names. When translating into Vietnamese, convert Chinese personal names, place names, sect and clan names, techniques, and cultivation terms into their Sino-Vietnamese (Hán-Việt) forms (for example, Xiao Yan → Tiêu Viêm, Dou Qi → Đấu Khí, Sect Master → Tông chủ). For other target languages, use that language's conventional rendering. Be consistent and prefer well-known established renderings from famous works. When translating into Vietnamese, use period-appropriate forms of address such as ta/ngươi, huynh/đệ, sư phụ, and tiền bối."_s,
},
{
u"japanese-anime"_s,
i18nc("@item:inlistbox translation preset", "Japanese anime"),
i18nc("@info translation preset description", "Casual dialogue with Japanese names and honorifics preserved."),
u"Keep Japanese names in romaji. Keep honorifics such as -san, -kun, and -senpai as-is. Use a natural, casual tone."_s,
},
{
u"korean-drama"_s,
i18nc("@item:inlistbox translation preset", "Korean drama"),
i18nc("@info translation preset description", "Modern dialogue with Korean names and relationship-aware forms of address."),
u"Keep Korean names. Use modern forms of address that fit the relationships; when translating into Vietnamese, use forms such as anh/em, and oppa may be kept when appropriate."_s,
},
{
u"western"_s,
i18nc("@item:inlistbox translation preset", "Western"),
i18nc("@info translation preset description", "A neutral modern tone with naturally adapted idioms."),
u"Keep names. Use a neutral modern tone and adapt idioms naturally for the target language."_s,
},
{
u"documentary"_s,
i18nc("@item:inlistbox translation preset", "Documentary"),
i18nc("@info translation preset description", "Formal, precise language with accurate terminology."),
u"Keep names. Use a formal, precise tone and translate terminology accurately."_s,
},
};
+1
View File
@@ -11,6 +11,7 @@ namespace TranslationPresets
struct Preset {
QString id;
QString name;
QString description;
QString guidance;
};
+1 -1
View File
@@ -117,7 +117,7 @@ QVariantList TranslationService::translationPresets() const
{
QVariantList result;
for (const auto &preset : TranslationPresets::all()) {
result.append(QVariantMap{{u"id"_s, preset.id}, {u"name"_s, preset.name}, {u"guidance"_s, preset.guidance}});
result.append(QVariantMap{{u"id"_s, preset.id}, {u"name"_s, preset.name}, {u"description"_s, preset.description}, {u"guidance"_s, preset.guidance}});
}
return result;
}