plasmoids: añade commandoutput y codexbar al tracking

Ambos ya estaban puestos en un panel real pero no viajaban a la otra
máquina -- detectado en la misma auditoría que quitó claudeusageswitcher
y tt.launchpadPlasma (sin usar). Sin esto, una reinstalación desde
arch-setup dejaría el panel sin estos dos widgets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Josevi
2026-09-20 23:15:48 +02:00
co-authored by Claude Sonnet 5
parent d434a3c635
commit b98999cde9
40 changed files with 4029 additions and 0 deletions
@@ -0,0 +1,23 @@
import QtQuick
import org.kde.plasma.configuration
ConfigModel {
ConfigCategory {
name: i18n("Command")
icon: "configure"
source: "config/ConfigCommand.qml"
}
ConfigCategory {
name: i18n("Appearance")
icon: "preferences-desktop-color"
source: "config/ConfigAppearance.qml"
}
ConfigCategory {
name: i18n("Actions")
icon: "preferences-desktop-mouse"
source: "config/ConfigActions.qml"
}
}
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<kcfgfile name=""/>
<group name="General">
<entry name="interval" type="int">
<default>1000</default>
<!-- <default>60000</default> -->
</entry>
<entry name="command" type="string">
<default>echo "Test $(date +%S)"</default>
<!-- <default>echo "&lt;a href=\"https://google.com\"&gt;Test $(date +%S)&lt;/a&gt;"</default> -->
<!-- <default>SYSTEMD_COLORS=1 journalctl -q -b0 -n20</default> -->
</entry>
<entry name="tooltipCommand" type="string">
<default></default>
<!-- <default>echo "Tooltip test $(date +%S)"</default> -->
</entry>
<entry name="waitForCompletion" type="bool">
<default>false</default>
</entry>
<entry name="fontSize" type="int">
<default>13</default>
</entry>
<entry name="fontFamily" type="string">
<default></default>
</entry>
<entry name="bold" type="bool">
<default>false</default>
</entry>
<entry name="italic" type="bool">
<default>false</default>
</entry>
<entry name="underline" type="bool">
<default>false</default>
</entry>
<entry name="textAlign" type="int">
<!-- Text.AlignLeft: 1 -->
<!-- Text.AlignHCenter: 4 -->
<!-- Text.AlignRight: 2 -->
<!-- Text.AlignJustify: 8 -->
<default>1</default>
</entry>
<entry name="vertAlign" type="int">
<!-- Text.AlignTop: 32 -->
<!-- Text.AlignVCenter: 128 -->
<!-- Text.AlignBottom: 64 -->
<default>128</default>
</entry>
<entry name="textColor" type="string">
<default></default>
</entry>
<entry name="outlineColor" type="string">
<default></default>
</entry>
<entry name="showOutline" type="bool">
<default>false</default>
</entry>
<entry name="showBackground" type="bool">
<default>true</default>
</entry>
<entry name="clickCommand" type="string">
<default></default>
<!-- <default>kdialog &#45;&#45;yesno "test"</default> -->
</entry>
<entry name="mousewheelUpCommand" type="string">
<default></default>
</entry>
<entry name="mousewheelDownCommand" type="string">
<default></default>
</entry>
<entry name="useFixedWidth" type="bool">
<default>false</default>
</entry>
<entry name="fixedWidth" type="int">
<default>100</default>
</entry>
<entry name="useFixedHeight" type="bool">
<default>false</default>
</entry>
<entry name="fixedHeight" type="int">
<default>100</default>
</entry>
<entry name="replaceAllNewlines" type="bool">
<default>false</default>
</entry>
</group>
</kcfg>
@@ -0,0 +1,83 @@
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import "../libconfig" as LibConfig
LibConfig.FormKCM {
//-------------------------------------------------------
LibConfig.Heading {
text: i18n("Tooltip")
label.horizontalAlignment: Text.AlignHCenter
}
ColumnLayout {
Kirigami.FormData.isSection: true
Layout.preferredWidth: 360 * Screen.devicePixelRatio
spacing: 0
QQC2.Label {
text: i18n("Hover Command:")
}
LibConfig.TextField {
Layout.fillWidth: true
configKey: 'tooltipCommand'
wrapMode: LibConfig.TextField.Wrap
}
}
//-------------------------------------------------------
LibConfig.Heading {
text: i18n("Click")
label.horizontalAlignment: Text.AlignHCenter
}
ColumnLayout {
Kirigami.FormData.isSection: true
Layout.preferredWidth: 360 * Screen.devicePixelRatio
spacing: 0
QQC2.Label {
text: i18n("Run Command:")
}
LibConfig.TextField {
Layout.fillWidth: true
configKey: 'clickCommand'
wrapMode: LibConfig.TextField.Wrap
}
}
//-------------------------------------------------------
LibConfig.Heading {
text: i18n("Mouse Wheel")
label.horizontalAlignment: Text.AlignHCenter
}
ColumnLayout {
Kirigami.FormData.isSection: true
Layout.preferredWidth: 360 * Screen.devicePixelRatio
spacing: 0
QQC2.Label {
text: i18n("Scroll Up:")
}
LibConfig.TextField {
Layout.fillWidth: true
configKey: 'mousewheelUpCommand'
wrapMode: LibConfig.TextField.Wrap
}
}
ColumnLayout {
Kirigami.FormData.isSection: true
Layout.preferredWidth: 360 * Screen.devicePixelRatio
spacing: 0
QQC2.Label {
text: i18n("Scroll Down:")
}
LibConfig.TextField {
Layout.fillWidth: true
configKey: 'mousewheelDownCommand'
wrapMode: LibConfig.TextField.Wrap
}
}
}
@@ -0,0 +1,94 @@
import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import "../libconfig" as LibConfig
LibConfig.FormKCM {
//-------------------------------------------------------
LibConfig.Heading {
text: i18n("Font")
}
LibConfig.FontFamily {
Kirigami.FormData.label: i18n("Font Family:")
configKey: 'fontFamily'
}
LibConfig.SpinBox {
Kirigami.FormData.label: i18n("Font Size:")
configKey: 'fontSize'
suffix: i18n("px")
}
LibConfig.TextFormat {
boldConfigKey: 'bold'
italicConfigKey: 'italic'
underlineConfigKey: 'underline'
alignConfigKey: 'textAlign'
vertAlignConfigKey: 'vertAlign'
}
//-------------------------------------------------------
LibConfig.Heading {
text: i18n("Colors")
}
LibConfig.ColorField {
Kirigami.FormData.label: i18n("Text:")
configKey: 'textColor'
defaultColor: PlasmaCore.Theme.textColor
}
RowLayout {
Kirigami.FormData.label: i18n("Outline:")
spacing: 0
LibConfig.CheckBox {
configKey: 'showOutline'
}
LibConfig.ColorField {
configKey: 'outlineColor'
defaultColor: PlasmaCore.Theme.backgroundColor
}
}
//-------------------------------------------------------
LibConfig.Heading {
text: i18n("Misc")
}
LibConfig.CheckBox {
Kirigami.FormData.label: i18n("Desktop Widget:")
configKey: 'showBackground'
text: i18n("Show background")
visible: plasmoid.location == PlasmaCore.Types.Floating
}
RowLayout {
Kirigami.FormData.label: i18n("Fixed Width:")
spacing: 0
visible: plasmoid.formFactor == PlasmaCore.Types.Horizontal
LibConfig.CheckBox {
configKey: 'useFixedWidth'
}
LibConfig.SpinBox {
configKey: 'fixedWidth'
suffix: i18n("px")
}
}
RowLayout {
Kirigami.FormData.label: i18n("Fixed Height:")
spacing: 0
visible: plasmoid.formFactor == PlasmaCore.Types.Vertical
LibConfig.CheckBox {
configKey: 'useFixedHeight'
}
LibConfig.SpinBox {
configKey: 'fixedHeight'
suffix: i18n("px")
}
}
LibConfig.CheckBox {
Kirigami.FormData.label: i18n("Replace all newlines with spaces")
configKey: 'replaceAllNewlines'
}
}
@@ -0,0 +1,41 @@
import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import "../libconfig" as LibConfig
LibConfig.FormKCM {
property alias cfg_command: command.text
property alias cfg_interval: interval.value
property alias cfg_waitForCompletion: waitForCompletion.checked
//-------------------------------------------------------
LibConfig.Heading {
text: i18n("Command")
}
LibConfig.TextField {
id: command
// configKey: 'command'
Kirigami.FormData.isSection: true
Layout.fillWidth: true
Layout.preferredWidth: 240 * Screen.devicePixelRatio // + ~180px from label column
wrapMode: LibConfig.TextField.Wrap
}
LibConfig.SpinBox {
id: interval
Kirigami.FormData.label: i18n("Run every ")
configKey: 'interval'
suffix: i18n("ms")
stepSize: 500
}
LibConfig.CheckBox {
id: waitForCompletion
Kirigami.FormData.label: i18n("Wait for completion")
// configKey: 'waitForCompletion'
text: i18n("Enabled")
}
}
@@ -0,0 +1,12 @@
// Version 5
import QtQuick
import QtQuick.Controls as QQC2
QQC2.CheckBox {
id: configCheckBox
property string configKey: ''
checked: plasmoid.configuration[configKey]
onClicked: plasmoid.configuration[configKey] = !plasmoid.configuration[configKey]
}
@@ -0,0 +1,159 @@
// Version 8
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Dialogs as QtDialogs
import QtQuick.Window
import org.kde.kirigami as Kirigami
// https://doc.qt.io/qt-6/qtgraphicaleffects5-index.html
import Qt5Compat.GraphicalEffects as QtGraphicalEffects // TODO Deprecated in Qt6
QQC2.TextField {
id: colorField
font.family: "monospace"
readonly property string defaultText: "#AARRGGBB"
placeholderText: defaultColor ? defaultColor : defaultText
onTextChanged: {
// Make sure the text is:
// Empty (use default)
// or #123 or #112233 or #11223344 before applying the color.
if (text.length === 0
|| (text.indexOf('#') === 0 && (text.length == 4 || text.length == 7 || text.length == 9))
) {
colorField.value = text
}
}
property bool showAlphaChannel: true
property bool showPreviewBg: true
property string configKey: ''
property string defaultColor: ''
property string value: {
if (configKey) {
return plasmoid.configuration[configKey]
} else {
return "#000"
}
}
readonly property color defaultColorValue: defaultColor
readonly property color valueColor: {
if (value == '' && defaultColor) {
return defaultColor
} else {
return value
}
}
onValueChanged: {
if (!activeFocus) {
text = colorField.value
}
if (configKey) {
if (value == defaultColorValue) {
plasmoid.configuration[configKey] = ""
} else {
plasmoid.configuration[configKey] = value
}
}
}
leftPadding: rightPadding + mouseArea.height + rightPadding
FontMetrics {
id: fontMetrics
font.family: colorField.font.family
font.italic: colorField.font.italic
font.pointSize: colorField.font.pointSize
font.pixelSize: colorField.font.pixelSize
font.weight: colorField.font.weight
}
readonly property int defaultWidth: Math.ceil(fontMetrics.advanceWidth(defaultText))
implicitWidth: rightPadding + Math.max(defaultWidth, contentWidth) + leftPadding
MouseArea {
id: mouseArea
anchors.leftMargin: parent.rightPadding
anchors.topMargin: parent.topPadding
anchors.bottomMargin: parent.bottomPadding
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: height
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: dialogLoader.active = true
// Color Preview Circle
Rectangle {
id: previewBgMask
visible: false
anchors.fill: parent
border.width: 1 * Screen.devicePixelRatio
border.color: "transparent"
radius: width / 2
}
QtGraphicalEffects.ConicalGradient {
id: previewBgGradient
visible: colorField.showPreviewBg
anchors.fill: parent
angle: 0.0
gradient: Gradient {
GradientStop { position: 0.00; color: "white" }
GradientStop { position: 0.24; color: "white" }
GradientStop { position: 0.25; color: "#cccccc" }
GradientStop { position: 0.49; color: "#cccccc" }
GradientStop { position: 0.50; color: "white" }
GradientStop { position: 0.74; color: "white" }
GradientStop { position: 0.75; color: "#cccccc" }
GradientStop { position: 1.00; color: "#cccccc" }
}
source: previewBgMask
}
Rectangle {
id: previewFill
anchors.fill: parent
color: colorField.valueColor
border.width: 1 * Screen.devicePixelRatio
border.color: Kirigami.ColorUtils.linearInterpolation(color, Kirigami.Theme.textColor, 0.5)
radius: width / 2
}
}
Loader {
id: dialogLoader
active: false
sourceComponent: QtDialogs.ColorDialog {
id: dialog
visible: true
modality: Qt.WindowModal
options: colorField.showAlphaChannel ? QtDialogs.ColorDialog.ShowAlphaChannel : 0
selectedColor: colorField.valueColor
onSelectedColorChanged: {
if (visible) {
colorField.text = selectedColor
}
}
onAccepted: {
colorField.text = selectedColor
dialogLoader.active = false
}
onRejected: {
// This event is also triggered when the user clicks outside the popup modal.
// TODO Find a way to only trigger when Cancel is clicked.
colorField.text = initColor
dialogLoader.active = false
}
property color initColor
Component.onCompleted: {
initColor = colorField.valueColor
}
}
}
}
@@ -0,0 +1,102 @@
// Version 8
import QtQuick
import QtQuick.Controls as QQC2
/*
** Example:
**
import './libconfig' as LibConfig
LibConfig.ComboBox {
configKey: "appDescription"
model: [
{ value: "a", text: i18n("A") },
{ value: "b", text: i18n("B") },
{ value: "c", text: i18n("C") },
]
}
LibConfig.ComboBox {
configKey: "appDescription"
populated: false
onPopulate: {
model = [
{ value: "a", text: i18n("A") },
{ value: "b", text: i18n("B") },
{ value: "c", text: i18n("C") },
]
populated = true
}
}
*/
QQC2.ComboBox {
id: configComboBox
property string configKey: ''
readonly property string configValue: configKey ? plasmoid.configuration[configKey] : ""
onConfigValueChanged: {
if (!focus && value != configValue) {
selectValue(configValue)
}
}
readonly property var currentItem: currentIndex >= 0 ? model[currentIndex] : null
textRole: "text" // Doesn't autodeduce from model if we manually populate it
// Note that ComboBox.valueRole and ComboBox.currentValue was introduced in Qt 5.14.
// Ubuntu 20.04 only has Qt 5.12. We cannot define a currentValue property or it will
// break when users upgrade to Qt 5.14.
property string _valueRole: "value"
readonly property var _currentValue: _valueRole && currentIndex >= 0 ? model[currentIndex][_valueRole] : null
readonly property alias value: configComboBox._currentValue
model: []
signal populate()
property bool populated: true
Component.onCompleted: {
populate()
selectValue(configValue)
}
onCurrentIndexChanged: {
if (typeof model !== 'number' && 0 <= currentIndex && currentIndex < count) {
var item = model[currentIndex]
if (typeof item !== "undefined") {
var val = item[_valueRole]
if (configKey && (typeof val !== "undefined") && populated) {
plasmoid.configuration[configKey] = val
}
}
}
}
function size() {
if (typeof model === "number") {
return model
} else if (typeof model.count === "number") {
return model.count
} else if (typeof model.length === "number") {
return model.length
} else {
return 0
}
}
function findValue(val) {
for (var i = 0; i < size(); i++) {
if (model[i][_valueRole] == val) {
return i
}
}
return -1
}
function selectValue(val) {
var index = findValue(val)
if (index >= 0) {
currentIndex = index
}
}
}
@@ -0,0 +1,24 @@
// Version 4
import QtQuick
import "." as LibConfig
LibConfig.ComboBox {
id: configFontFamily
populated: false
// Based on: org.kde.plasma.digitalclock
onPopulate: {
var arr = [] // Use temp array to avoid constant binding stuff
arr.push({ text: i18nc("Use default font", "Default"), value: "" })
var fonts = Qt.fontFamilies()
for (var i = 0; i < fonts.length; i++) {
arr.push({ text: fonts[i], value: fonts[i] })
}
model = arr
populated = true
}
}
@@ -0,0 +1,33 @@
// Version 3
import QtQuick
import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kcmutils as KCM
KCM.SimpleKCM {
id: simpleKCM
default property alias _formChildren: formLayout.data
Kirigami.FormLayout {
id: formLayout
}
// https://invent.kde.org/plasma/plasma-desktop/-/blame/master/desktoppackage/contents/configuration/AppletConfiguration.qml
// AppletConfiguration.implicitWidth: Kirigami.Units.gridUnit * 40 = 720
// AppletConfiguration.Layout.minimumWidth: Kirigami.Units.gridUnit * 30 = 540
// In practice, Window.width = 744px is a typical FormLayout.wideMode switchWidth
// A rough guess is 128+24+180+10+360+24+20 = 746px
// TabSidebar=128x, Padding=24px, Labels=180px, Spacing=10px, Controls=360px, Padding=24px, Scrollbar=20px
// However the default is only 720px. So we'll set it to a 800px minimum to avoid wideMode=false
property int wideModeMinWidth: 800 * Screen.devicePixelRatio
Window.onWindowChanged: {
if (Window.window) {
Window.window.visibleChanged.connect(function(){
if (Window.window && Window.window.visible && Window.window.width < wideModeMinWidth) {
Window.window.width = wideModeMinWidth
}
})
}
}
}
@@ -0,0 +1,83 @@
// Version 6
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
/*
** Example:
**
import './libconfig' as LibConfig
LibConfig.Heading {
text: i18n("SpinBox (Double)")
}
*/
// While the following Kirigami is very simple:
// Kirigami.Separator {
// Kirigami.FormData.label: "Heading"
// Kirigami.FormData.isSection: true
// }
//
// I want to be able to adjust the label size and make it bold.
// Kirigami's buddy Heading is level=3, which does not stand out
// very well. I also want to center the heading.
// Since we can't access the Heading in the buddy component, we
// need to make sure the Heading has no text, and draw our own.
ColumnLayout {
id: heading
spacing: 0
property string text: ""
property alias separator: separator
property alias label: label
property bool useThickTopMargin: true
property Item __formLayout: {
if (parent && typeof parent.wideMode === 'boolean') {
return parent
} else if (typeof formLayout !== 'undefined' && typeof formLayout.wideMode === 'boolean') {
return formLayout
} else if (typeof page !== 'undefined' && typeof page.wideMode === 'boolean') {
return page
} else {
return null
}
}
Layout.fillWidth: true
// Kirigami.FormData.isSection: true
Kirigami.MnemonicData.controlType: Kirigami.MnemonicData.FormLabel
Kirigami.Separator {
id: separator
visible: false
Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.bottomMargin: Kirigami.Units.largeSpacing
}
Kirigami.Heading {
id: label
Layout.topMargin: useThickTopMargin ? Kirigami.Units.largeSpacing * 3 : Kirigami.Units.largeSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.fillWidth: true
text: heading.text
level: 1
font.weight: Font.Bold
// horizontalAlignment: (!__formLayout || __formLayout.wideMode) ? Text.AlignHCenter : Text.AlignLeft
verticalAlignment: (!__formLayout || __formLayout.wideMode) ? Text.AlignVCenter : Text.AlignBottom
}
}
//--- Test Default Kirigami Heading
// Kirigami.Separator {
// property string text: ""
// Kirigami.FormData.label: text
// Kirigami.FormData.isSection: true
// property alias separator: separator
// Item {
// id: separator
// }
// }
@@ -0,0 +1,225 @@
// Version 7
import QtQuick
import QtQuick.Controls as QQC2
/*
** Example:
**
import './libconfig' as LibConfig
// Integer
LibConfig.SpinBox {
configKey: "leftPadding"
suffix: "px"
from: 0
to: 1000
stepSize: 5
}
// Double
LibConfig.SpinBox {
configKey: "distance"
decimals: 3
suffix: "m"
minimumValue: 0.0
maximumValue: 1000.0
stepSize: Math.round(0.5 * factor)
}
*/
// QQC1.SpinBox: https://github.com/qt/qtquickcontrols/blob/dev/src/controls/SpinBox.qml
// QQC2.SpinBox: https://github.com/qt/qtquickcontrols2/blob/5.15/src/imports/controls/SpinBox.qml
// KDE Config Theme: https://invent.kde.org/frameworks/qqc2-desktop-style/-/blob/master/org.kde.desktop/SpinBox.qml
// Qt6 QQC2.SpinBox: https://github.com/qt/qtquickcontrols2/blob/dev/src/imports/controls/basic/SpinBox.qml
QQC2.SpinBox {
id: spinBox
property string configKey: ''
readonly property var configValue: configKey ? plasmoid.configuration[configKey] : 0
readonly property real factor: Math.pow(10, decimals)
readonly property real valueReal: value / factor
value: Math.round(configValue * factor)
onValueRealChanged: serializeTimer.start()
readonly property int spinBox_MININT: Math.ceil(-2147483648 / factor)
readonly property int spinBox_MAXINT: Math.floor(2147483647 / factor)
from: Math.round(minimumValue * factor)
to: Math.round(maximumValue * factor)
// Reimplement QQC1 properties
// https://github.com/qt/qtquickcontrols/blob/dev/src/controls/SpinBox.qml
property int decimals: 0
property alias prefix: prefixLabel.text
property alias suffix: suffixLabel.text
property real minimumValue: 0
property real maximumValue: spinBox_MAXINT
// Avoid selecting prefix/suffix by drawing them overlayed on top.
// As a bonus, we can draw with in a different color (textColor at 60% opacity).
QQC2.Label {
id: prefixLabel
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.leftMargin: spinBox.leftPadding
anchors.topMargin: spinBox.topPadding
anchors.bottomMargin: spinBox.bottomPadding
font: spinBox.font
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
color: spinBox.palette.text
opacity: 0.6
}
QQC2.Label {
id: suffixLabel
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.rightMargin: spinBox.rightPadding
anchors.topMargin: spinBox.topPadding
anchors.bottomMargin: spinBox.bottomPadding
font: spinBox.font
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
color: spinBox.palette.text
opacity: 0.6
}
Timer { // throttle
id: serializeTimer
interval: 300
onTriggered: {
if (configKey) {
if (decimals == 0) {
plasmoid.configuration[configKey] = spinBox.value
} else {
plasmoid.configuration[configKey] = spinBox.valueReal
}
}
}
}
// Note: Qt5 used RegExpValidator { regExp: /[\-\.\d]+/ }
// validator: RegularExpressionValidator {
// regularExpression: /[\-\.\d]+/
// }
validator: DoubleValidator {
bottom: Math.min(spinBox.from, spinBox.to)
top: Math.max(spinBox.from, spinBox.to)
decimals: spinBox.decimals
notation: DoubleValidator.StandardNotation
}
textFromValue: function(value, locale) {
return Number(value / factor).toFixed(decimals)
}
valueFromText: function(text, locale) {
var text2 = text
.replace(/[^\-\.\d]/g, '') // Remove non digit characters
.replace(/\.+/g, '.') // Allow user to type '.' instead of RightArrow to enter to decimals
var val = Number(text2)
if (isNaN(val)) {
val = -0
}
// console.log('valueFromText', text, val)
return Math.round(val * factor)
}
// Select value on foucs
onActiveFocusChanged: {
if (activeFocus) {
selectValue()
}
}
function selectValue() {
// Check if SpinBox.contentItem == TextInput
// https://invent.kde.org/frameworks/qqc2-desktop-style/-/blob/master/org.kde.desktop/SpinBox.qml
// https://doc.qt.io/qt-5/qml-qtquick-textinput.html#select-method
if (contentItem && contentItem instanceof TextInput) {
contentItem.selectAll()
}
}
function fixMinus(str) {
var minusIndex = str.indexOf('-')
if (minusIndex >= 0) {
var a = str.substr(0, minusIndex)
var b = str.substr(minusIndex+1)
console.log('a', a, 'b', b)
return '-' + a + b
} else {
return str
}
}
function fixDecimals(str) {
var periodIndex = str.indexOf('.')
var a = str.substr(0, periodIndex+1)
var b = str.substr(periodIndex+1)
return a + b.replace(/\.+/g, '') // Remove extra periods
}
function fixText(str) {
return fixMinus(fixDecimals(str))
}
function onTextEdited() {
var oldText = spinBox.contentItem.text
// console.log('onTextEdited', 'oldText1', oldText)
oldText = fixText(oldText)
// console.log('onTextEdited', 'oldText2', oldText)
var oldPeriodIndex = oldText.indexOf('.')
if (oldPeriodIndex == -1) {
oldPeriodIndex = oldText.length
}
var oldCursorPosition = spinBox.contentItem.cursorPosition
var oldCursorDelta = oldPeriodIndex - oldCursorPosition
spinBox.value = spinBox.valueFromText(oldText, spinBox.locale)
spinBox.valueModified()
var newText = spinBox.contentItem.text
// console.log('onTextEdited', 'newText1', newText)
newText = fixText(newText)
// console.log('onTextEdited', 'newText2', newText)
var newPeriodIndex = newText.indexOf('.')
if (newPeriodIndex == -1) {
newPeriodIndex = newText.length
}
if (newText != spinBox.contentItem.text) {
spinBox.contentItem.text = Qt.binding(function(){
return spinBox.textFromValue(spinBox.value, spinBox.locale)
})
}
spinBox.contentItem.cursorPosition = newPeriodIndex - oldCursorDelta
}
function bindContentItem() {
if (contentItem && contentItem instanceof TextInput) {
// We bind the left/right padding in the TextInput so that
// clicking the prefix/suffix will focus the TextInput. If we set
// the SpinBox left/right padding, then they do not focus the TextInput.
contentItem.leftPadding = Qt.binding(function(){ return prefixLabel.implicitWidth })
contentItem.rightPadding = Qt.binding(function(){ return suffixLabel.implicitWidth })
// Bind value update on keypress, while retaining cursor position
spinBox.contentItem.textEdited.connect(spinBox.onTextEdited)
}
}
onContentItemChanged: {
bindContentItem()
}
Component.onCompleted: {
for (var i = 0; i < data.length; i++) {
if (data[i] instanceof Connections) {
// Remove the Connections where it changes the text/cursor when typing.
// onTextEdited { value = valueFromText() }
data[i].destroy()
break
}
}
bindContentItem()
}
}
@@ -0,0 +1,57 @@
// Version 4
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
RowLayout {
id: configTextAlign
property string configKey: ''
readonly property string configValue: configKey ? plasmoid.configuration[configKey] : ""
function setValue(val) {
if (configKey) {
plasmoid.configuration[configKey] = val
}
updateChecked()
}
function updateChecked() {
// button.checked bindings are unbound when clicked
justifyLeftButton.checked = Qt.binding(function(){ return configValue == Text.AlignLeft })
justifyCenterButton.checked = Qt.binding(function(){ return configValue == Text.AlignHCenter })
justifyRightButton.checked = Qt.binding(function(){ return configValue == Text.AlignRight })
justifyFillButton.checked = Qt.binding(function(){ return configValue == Text.AlignJustify })
}
Component.onCompleted: updateChecked()
QQC2.Button {
id: justifyLeftButton
icon.name: "format-justify-left-symbolic"
checkable: true
onClicked: setValue(Text.AlignLeft)
}
QQC2.Button {
id: justifyCenterButton
icon.name: "format-justify-center-symbolic"
checkable: true
onClicked: setValue(Text.AlignHCenter)
}
QQC2.Button {
id: justifyRightButton
icon.name: "format-justify-right-symbolic"
checkable: true
onClicked: setValue(Text.AlignRight)
}
QQC2.Button {
id: justifyFillButton
icon.name: "format-justify-fill-symbolic"
checkable: true
onClicked: setValue(Text.AlignJustify)
}
}
@@ -0,0 +1,48 @@
// Version 6
import QtQuick
import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami
QQC2.TextField {
id: textField
property string configKey: ''
readonly property string configValue: configKey ? plasmoid.configuration[configKey] : ""
onConfigValueChanged: {
if (!focus && text != configValue) {
text = configValue
}
}
text: configValue
onTextChanged: serializeTimer.start()
property string defaultValue: ""
rightPadding: clearButton.width + Kirigami.Units.smallSpacing
property alias clearButton: clearButton
QQC2.ToolButton {
id: clearButton
visible: textField.text != textField.defaultValue
icon.name: "edit-clear"
onClicked: textField.text = defaultValue
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
width: implicitBackgroundHeight
}
Timer { // throttle
id: serializeTimer
interval: 300
onTriggered: {
if (configKey) {
plasmoid.configuration[configKey] = textField.text
}
}
}
}
@@ -0,0 +1,63 @@
// Version 6
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import "." as LibConfig
Flow {
id: configTextFormat
spacing: Kirigami.Units.smallSpacing * 2
Layout.fillWidth: true
property alias boldConfigKey: configBold.configKey
property alias italicConfigKey: configItalic.configKey
property alias underlineConfigKey: configUnderline.configKey
property alias alignConfigKey: configTextAlign.configKey
property alias vertAlignConfigKey: configVertAlign.configKey
RowLayout {
QQC2.Button {
id: configBold
property string configKey: ''
visible: configKey
icon.name: 'format-text-bold-symbolic'
checkable: true
checked: configKey ? plasmoid.configuration[configKey] : false
onClicked: plasmoid.configuration[configKey] = checked
}
QQC2.Button {
id: configItalic
property string configKey: ''
visible: configKey
icon.name: 'format-text-italic-symbolic'
checkable: true
checked: configKey ? plasmoid.configuration[configKey] : false
onClicked: plasmoid.configuration[configKey] = checked
}
QQC2.Button {
id: configUnderline
property string configKey: ''
visible: configKey
icon.name: 'format-text-underline-symbolic'
checkable: true
checked: configKey ? plasmoid.configuration[configKey] : false
onClicked: plasmoid.configuration[configKey] = checked
}
}
LibConfig.TextAlign {
id: configTextAlign
visible: configKey
}
LibConfig.VertAlign {
id: configVertAlign
visible: configKey
}
}
@@ -0,0 +1,49 @@
// Version 5
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
RowLayout {
id: configVertAlign
property string configKey: ''
readonly property string configValue: configKey ? plasmoid.configuration[configKey] : ""
function setValue(val) {
if (configKey) {
plasmoid.configuration[configKey] = val
}
updateChecked()
}
function updateChecked() {
// button.checked bindings are unbound when clicked
vertTopButton.checked = Qt.binding(function(){ return configValue == Text.AlignTop })
vertCenterButton.checked = Qt.binding(function(){ return configValue == Text.AlignVCenter })
vertBottomButton.checked = Qt.binding(function(){ return configValue == Text.AlignBottom })
}
Component.onCompleted: updateChecked()
QQC2.Button {
id: vertTopButton
icon.name: "align-vertical-top"
checkable: true
onClicked: setValue(Text.AlignTop)
}
QQC2.Button {
id: vertCenterButton
icon.name: "align-vertical-center"
checkable: true
onClicked: setValue(Text.AlignVCenter)
}
QQC2.Button {
id: vertBottomButton
icon.name: "align-vertical-bottom"
checkable: true
onClicked: setValue(Text.AlignBottom)
}
}
@@ -0,0 +1,393 @@
import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.plasma.plasmoid
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components as PlasmaComponent
import org.kde.plasma.plasma5support as Plasma5Support
PlasmoidItem {
id: widget
// https://github.com/KDE/plasma-workspace/blob/master/dataengines/executable/executable.h
// https://github.com/KDE/plasma-workspace/blob/master/dataengines/executable/executable.cpp
// https://github.com/KDE/plasma-framework/blob/master/src/declarativeimports/core/datasource.h
// https://github.com/KDE/plasma-framework/blob/master/src/declarativeimports/core/datasource.cpp
// https://github.com/KDE/plasma-framework/blob/master/src/plasma/scripting/dataenginescript.cpp
Plasma5Support.DataSource {
id: executable
engine: "executable"
connectedSources: []
onNewData: (sourceName, data) => {
var exitCode = data["exit code"]
var exitStatus = data["exit status"]
var stdout = data["stdout"]
var stderr = data["stderr"]
exited(sourceName, exitCode, exitStatus, stdout, stderr)
disconnectSource(sourceName) // cmd finished
}
function exec(cmd) {
if (cmd) {
connectSource(cmd)
}
}
signal exited(string cmd, int exitCode, int exitStatus, string stdout, string stderr)
}
function performClick() {
executable.exec(plasmoid.configuration.clickCommand)
}
function performMouseWheelUp() {
executable.exec(plasmoid.configuration.mousewheelUpCommand)
}
function performMouseWheelDown() {
executable.exec(plasmoid.configuration.mousewheelDownCommand)
}
Item {
id: config
readonly property bool active: !!command
readonly property bool waitForCompletion: plasmoid.configuration.waitForCompletion
readonly property int interval: Math.max(0, plasmoid.configuration.interval)
readonly property string command: plasmoid.configuration.command || ''
readonly property string tooltipCommand: plasmoid.configuration.tooltipCommand || ''
readonly property bool clickEnabled: !!plasmoid.configuration.clickCommand
readonly property bool mousewheelEnabled: (plasmoid.configuration.mousewheelUpCommand || plasmoid.configuration.mousewheelDownCommand)
readonly property color textColor: plasmoid.configuration.textColor || Kirigami.Theme.textColor
readonly property color outlineColor: plasmoid.configuration.outlineColor || Kirigami.Theme.backgroundColor
readonly property bool showOutline: plasmoid.configuration.showOutline
onCommandChanged: widget.runCommand()
onTooltipCommandChanged: widget.runCommand()
onIntervalChanged: {
// interval=0 stops the timer even with Timer.repeat=true, so we may
// need to restart the timer. Might as well restart the interval too.
timer.restart()
}
onWaitForCompletionChanged: {
if (!waitForCompletion) {
// The timer needs to be restarted in case the timer was already
// triggered and the command is running. If we don't restart the
// timer, it'll stop forever.
timer.restart()
}
}
}
// https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences
property var ansiColors: ({
30: '#000000', // Black
31: '#aa0000', // Red
32: '#00aa00', // Green
33: '#aa6500', // Yellow
34: '#0000aa', // Blue
35: '#aa00aa', // Magenta
36: '#00aaaa', // Cyan
37: '#aaaaaa', // White
90: '#656565', // Bright Black
91: '#ff6565', // Bright Red
92: '#65ff65', // Bright Green
93: '#ffff65', // Bright Yellow
94: '#6565ff', // Bright Blue
95: '#ff65ff', // Bright Magenta
96: '#65ffff', // Bright Cyan
97: '#ffffff', // Bright White
})
function resetState(state) {
var out = state.closeTags.join(' ')
state.bold = false
state.closeTags = []
return out
}
function parseAnsiCode(n, i, tokens, state) {
if (n == 0) { // Reset
return resetState(state)
} else if (n == 1) {
state.closeTags.push('</b>')
state.bold = true
return '<b>'
} else if (30 <= n && n <= 37 || 90 <= n && n <= 97) {
if (state.bold && 30 <= n && n <= 37) {
// Bold also intensifies the colors to "Bright".
// 30 => 90
n += 60
}
var hexColor = ansiColors[n]
state.closeTags.push('</font>')
return '<font color="' + hexColor + '">'
} else {
return ''
}
}
// https://stackoverflow.com/questions/4745317/converting-integers-to-hex-string-in-javascript
function formatHexInt(n) {
var num = Number(n)
if (isNaN(num)) {
return "00"
}
num = Math.max(0, Math.min(num, 255))
var str = num.toString(16)
return str.length == 1 ? '0' + str : str
}
function rgbToHex(r, g, b) {
return '#' + formatHexInt(r) + formatHexInt(g) + formatHexInt(b)
}
function parseColorMode(i, tokens) {
var colorMode = parseInt(tokens[++i], 10)
if (colorMode == 2) { // RGB
var r = parseInt(tokens[++i], 10)
var g = parseInt(tokens[++i], 10)
var b = parseInt(tokens[++i], 10)
return rgbToHex(r, g, b)
} else if (colorMode == 5) { // Preset of 256 colors
// Logic taken from Konsole
// https://invent.kde.org/utilities/konsole/-/blob/master/src/autotests/CharacterColorTest.cpp#L159
var n = parseInt(tokens[++i], 10)
if (0 <= n && n <= 7) { // Normal
var u = n + 30
return ansiColors[u]
} else if (8 <= n && n <= 15) { // Bright
var u = n - 8 + 90
return ansiColors[u]
} else if (16 <= n && n <= 231) { // 212
var u = n - 16
var r = Math.floor(((u / 36) % 6) != 0 ? (40 * ((u / 36) % 6) + 55) : 0)
var g = Math.floor(((u / 6) % 6) != 0 ? (40 * ((u / 6) % 6) + 55) : 0)
var b = Math.floor(((u / 1) % 6) != 0 ? (40 * ((u / 1) % 6) + 55) : 0)
return rgbToHex(r, g, b)
} else if (232 <= n && n <= 255) {
var gray = Math.floor((n - 232) * 10 + 8)
return rgbToHex(gray, gray, gray)
}
}
return null
}
function parseAnsiEscape(codes, state) {
var tokens = codes.split(';')
var out = ''
for (var i = 0; i < tokens.length; i++) {
tokens[i] = parseInt(tokens[i], 10)
}
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i]
if (token == 38) { // Set FG
var hexColor = parseColorMode(i, tokens)
if (hexColor) {
state.closeTags.push('</font>')
out += '<font color="' + hexColor + '">'
}
} else if (token == 48) { // Set BG
var hexColor = parseColorMode(i, tokens)
// Ignore
} else {
out += parseAnsiCode(token, i, tokens, state)
}
}
return out
}
property string outputText: ''
property string tooltipText: ''
function formatOutputText(stdout) {
var formattedText = stdout
// Newlines
if (plasmoid.configuration.replaceAllNewlines) {
formattedText = formattedText.replace(/\n/g, ' ').trim()
} else if (formattedText.length >= 1 && formattedText[formattedText.length-1] == '\n') {
formattedText = formattedText.substr(0, formattedText.length-1)
}
// Terminal Colors (Issue #7)
var state = {
html: false,
bold: false,
closeTags: [],
}
formattedText = formattedText.replace(/\033\[(\d+(;\d+)*)?m/g, function(match, p1, p2){
state.html = true
if (typeof p1 === 'string') {
return parseAnsiEscape(p1, state)
} else { // \033[m is Reset
return parseAnsiEscape('0', state)
}
})
formattedText += resetState(state)
// Format Newlines when in HTML mode
if (state.html) {
formattedText = formattedText.replace(/\n/g, '<br>')
}
return formattedText
}
Connections {
target: executable
function onExited(cmd, exitCode, exitStatus, stdout, stderr) {
if ((cmd == config.command) || (cmd == config.tooltipCommand)) {
var formattedText = formatOutputText(stdout)
// console.log('[commandoutput]', 'stdout', JSON.stringify(stdout))
// console.log('[commandoutput]', 'format', JSON.stringify(formattedText))
if (cmd == config.command) {
widget.outputText = formattedText
} else if (cmd == config.tooltipCommand) {
widget.tooltipText = formattedText
}
if (config.waitForCompletion) {
timer.restart()
}
}
}
}
function runCommand() {
// console.log('[commandoutput]', Date.now(), 'runCommand', config.command)
executable.exec(config.command)
executable.exec(config.tooltipCommand)
}
Timer {
id: timer
interval: config.interval
running: true
repeat: !config.waitForCompletion
onTriggered: widget.runCommand()
// onIntervalChanged: console.log('interval', interval)
// onRunningChanged: console.log('running', running)
// onRepeatChanged: console.log('repeat', repeat)
Component.onCompleted: {
// Run right away in case the interval is very long.
triggered()
}
}
Plasmoid.onActivated: widget.performClick()
Plasmoid.backgroundHints: plasmoid.configuration.showBackground ? PlasmaCore.Types.DefaultBackground : PlasmaCore.Types.NoBackground
fullRepresentation: Item {
id: panelItem
readonly property bool isHorizontal: plasmoid.formFactor == PlasmaCore.Types.Horizontal
readonly property bool isVertical: plasmoid.formFactor == PlasmaCore.Types.Vertical
readonly property bool isInPanel: isHorizontal || isVertical
readonly property bool isOnDesktop: !isInPanel
readonly property int itemWidth: {
if (isOnDesktop) {
return Math.ceil(output.contentWidth)
} else if (isHorizontal && plasmoid.configuration.useFixedWidth) {
return plasmoid.configuration.fixedWidth * Kirigami.Units.devicePixelRatio
} else { // isHorizontal || isVertical
return Math.ceil(output.implicitWidth)
}
}
Layout.minimumWidth: isHorizontal ? itemWidth : -1
Layout.fillWidth: isVertical
Layout.preferredWidth: itemWidth // Panel widget default
// width: itemWidth // Desktop widget default
// onItemWidthChanged: console.log('itemWidth', itemWidth, 'implicitWidth', output.implicitWidth, 'contentWidth', output.contentWidth)
readonly property int itemHeight: {
if (isOnDesktop) {
return Math.ceil(output.contentHeight)
} else if (isVertical && plasmoid.configuration.useFixedHeight) {
return plasmoid.configuration.fixedHeight * Kirigami.Units.devicePixelRatio
} else { // isHorizontal || isVertical
return Math.ceil(output.implicitHeight)
}
}
Layout.minimumHeight: isVertical ? itemHeight : -1
Layout.fillHeight: isHorizontal
Layout.preferredHeight: itemHeight // Panel widget default
// height: itemHeight // Desktop widget default
// onItemHeightChanged: console.log('itemHeight', itemHeight, 'implicitHeight', output.implicitHeight, 'contentHeight', output.contentHeight)
// Note MouseArea is below the Text so
// that we don't eat the link clicks.
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: config.clickEnabled
cursorShape: output.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
widget.performClick()
}
property int wheelDelta: 0
onWheel: (wheel) => {
var delta = wheel.angleDelta.y || wheel.angleDelta.x
wheelDelta += delta
// Magic number 120 for common "one click"
// See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop
while (wheelDelta >= 120) {
wheelDelta -= 120
widget.performMouseWheelUp()
}
while (wheelDelta <= -120) {
wheelDelta += 120
widget.performMouseWheelDown()
}
wheel.accepted = true
}
}
Text {
id: output
width: parent.width
height: parent.height
PlasmaCore.ToolTipArea {
anchors.fill: parent
mainText: widget.tooltipText
enabled: widget.tooltipText
}
text: widget.outputText
color: config.textColor
style: config.showOutline ? Text.Outline : Text.Normal
styleColor: config.outlineColor
linkColor: Kirigami.Theme.linkColor
onLinkActivated: Qt.openUrlExternally(link)
font.pointSize: plasmoid.configuration.fontSize
font.family: plasmoid.configuration.fontFamily || Kirigami.Theme.defaultFont.family
font.weight: plasmoid.configuration.bold ? Font.Bold : Font.Normal
font.italic: plasmoid.configuration.italic
font.underline: plasmoid.configuration.underline
fontSizeMode: Text.FixedSize
horizontalAlignment: plasmoid.configuration.textAlign
verticalAlignment: plasmoid.configuration.vertAlign
property bool isFixedWidth: {
if (plasmoid.formFactor == PlasmaCore.Types.Planar) { // Desktop Widget
return true
} else if (plasmoid.formFactor == PlasmaCore.Types.Horizontal) {
return plasmoid.configuration.useFixedWidth
} else if (plasmoid.formFactor == PlasmaCore.Types.Vertical) {
return true
} else {
return false
}
}
elide: Text.ElideRight
wrapMode: isFixedWidth ? Text.Wrap : Text.NoWrap
}
}
preferredRepresentation: fullRepresentation
}
@@ -0,0 +1,36 @@
{
"KPackageStructure": "Plasma/Applet",
"KPlugin": {
"Authors": [
{
"Email": "zrenfire@gmail.com",
"Name": "Chris Holland"
},
{
"Email": "aricaldeira@gmail.com",
"Name": "Ari Caldeira"
},
{
"Email": "oded@geek.co.il",
"Name": "Oded Arbel"
}
],
"Category": "System Information",
"Description": "Run a command periodically and render its output.",
"Description[bz]": "Ezekuta un komandu a kada X milisegundus, i mòstra a saida/rezuwtadu",
"Description[fr]": "Exécute une commande périodiquement et affiche son retour.",
"Description[nl]": "Voer een opdracht periodiek uit en toon de uitvoer ervan.",
"Description[pt_br]": "Executa um comando a cada X milissegundos, e mostra a saída/resultado",
"Icon": "utilities-terminal",
"Id": "com.github.zren.commandoutput",
"License": "GPL-2.0+",
"Name": "Command Output",
"Name[bz]": "Saida di Komandus",
"Name[fr]": "Command Output",
"Name[nl]": "Opdrachtuitvoer",
"Name[pt_br]": "Saída de Comandos",
"Version": "14",
"Website": "https://github.com/Zren/plasma-applet-commandoutput"
},
"X-Plasma-API-Minimum-Version": "6.0"
}
@@ -0,0 +1,53 @@
# Translate
## Status
| Locale | Lines | % Done|
|----------|---------|-------|
| Template | 30 | |
| bz | 26/30 | 86% |
| fr | 30/30 | 100% |
| nl | 30/30 | 100% |
| pt_br | 27/30 | 90% |
## New Translations
* Fill out [`template.pot`](template.pot) with your translations then open a [new issue](https://github.com/Zren/plasma-applet-commandoutput/issues/new), name the file `spanish.txt`, attach the txt file to the issue (drag and drop).
Or if you know how to make a pull request
* Copy the `template.pot` file and name it your locale's code (Eg: `en`/`de`/`fr`) with the extension `.po`. Then fill out all the `msgstr ""`.
* Your region's locale code can be found at: https://stackoverflow.com/questions/3191664/list-of-all-locales-and-their-short-codes/28357857#28357857
## Scripts
Zren's `kpac` script can easily run the `gettext` commands for you, parsing the `metadata.json` and filling out any placeholders for you. `kpac` can be [downloaded here](https://github.com/Zren/plasma-applet-lib/blob/master/kpac) and should be placed at `~/Code/plasmoid-widgetname/kpac` to edit translations at `~/Code/plasmoid-widgetname/package/translate/`.
* `python3 ./kpac i18n` will parse the `i18n()` calls in the `*.qml` files and write it to the `template.pot` file. Then it will merge any changes into the `*.po` language files. Then it converts the `*.po` files to it's binary `*.mo` version and move it to `contents/locale/...` which will bundle the translations in the `*.plasmoid` without needing the user to manually install them.
* `python3 ./kpac localetest` will convert the `.po` to the `*.mo` files then run `plasmoidviewer` (part of `plasma-sdk`).
## How it works
Since KDE Frameworks v5.37, translations can be bundled with the zipped `*.plasmoid` file downloaded from the store.
* `xgettext` extracts the messages from the source code into a `template.pot`.
* Translators copy the `template.pot` to `fr.po` to translate the French language.
* When the source code is updated, we use `msgmerge` to update the `fr.po` based on the updated `template.pot`.
* When testing or releasing the widget, we convert the `.po` files to their binary `.mo` form with `msgfmt`.
The binary `.mo` translation files are placed in `package/contents/locale/` so you may want to add `*.mo` to your `.gitignore`.
```
package/contents/locale/fr/LC_MESSAGES/plasma_applet_com.github.zren.commandoutput.mo
```
## Links
* https://develop.kde.org/docs/plasma/widget/translations-i18n/
* https://l10n.kde.org/stats/gui/trunk-kf5/team/fr/plasma-desktop/
* https://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems
* https://api.kde.org/frameworks/ki18n/html/prg_guide.html
> Version 8 of [Zren's i18n scripts](https://github.com/Zren/plasma-applet-lib).
@@ -0,0 +1,166 @@
# Translation of commandoutput in fr
# Copyright (C) 2020
# This file is distributed under the same license as the commandoutput package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: commandoutput\n"
"Report-Msgid-Bugs-To: https://github.com/Zren/plasma-applet-commandoutput\n"
"POT-Creation-Date: 2024-03-12 16:30-0400\n"
"PO-Revision-Date: 2020-10-05 21:55+020\n"
"Last-Translator: Arí KAWDERA <aricaldeira@gmail.com>\n"
"Language-Team: \n"
"Language: bz_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../metadata.json
msgid "Command Output"
msgstr "Saida di Komandus"
#: ../metadata.json
msgid "Run a command periodically and render its output."
msgstr "Ezekuta un komandu a kada X milisegundus, i mòstra a saida/rezuwtadu"
#: ../contents/config/config.qml ../contents/ui/config/ConfigCommand.qml
msgid "Command"
msgstr "Komandu"
#: ../contents/config/config.qml
msgid "Appearance"
msgstr "Aparensya"
#: ../contents/config/config.qml
msgid "Actions"
msgstr "Asoyns"
#: ../contents/ui/config/ConfigActions.qml
msgid "Tooltip"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Hover Command:"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Click"
msgstr "Kliki"
#: ../contents/ui/config/ConfigActions.qml
msgid "Run Command:"
msgstr "Rodar u komandu:"
#: ../contents/ui/config/ConfigActions.qml
msgid "Mouse Wheel"
msgstr "Rodiỹa du mawzi"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Up:"
msgstr "Rolar pra sima:"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Down:"
msgstr "Rolar pra baxu:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font"
msgstr "Fonti"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Family:"
msgstr "Familya:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Size:"
msgstr "Tamaỹu:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "px"
msgstr "px"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Colors"
msgstr "Koris"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Text:"
msgstr "Testu:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Outline:"
msgstr "Kontornu:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Misc"
msgstr "Divèrsus"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Desktop Widget:"
msgstr "Widigèti pru dèskitòpi:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Show background"
msgstr "nuwn dexar transparenti"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Width:"
msgstr "Largura fiksa:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Height:"
msgstr "Awtura fiksa:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Replace all newlines with spaces"
msgstr "Junta as liỹas numa liỹa sò, separandu kun espasus"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Run every "
msgstr "Rodar a kada "
#: ../contents/ui/config/ConfigCommand.qml
msgid "ms"
msgstr ""
#: ../contents/ui/config/ConfigCommand.qml
msgid "Wait for completion"
msgstr "Esperar a ezekusawn terminar"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Enabled"
msgstr ""
#: ../contents/ui/libconfig/FontFamily.qml
msgctxt "Use default font"
msgid "Default"
msgstr "Padrawn"
#~ msgid "<b>Version:</b> %1"
#~ msgstr "<b>Versawn:</b> %1"
#~ msgid "Mouse Click"
#~ msgstr "Kliki du mawzi"
#~ msgid "Command:"
#~ msgstr "Komandu:"
#~ msgid "pt"
#~ msgstr "pt"
#~ msgid "Desktop Widget: Show background"
#~ msgstr "Widigèti pru dèskitòpi: nuwn dexar transparenti"
#~ msgid "Tooltip Command"
#~ msgstr "Komandu pra pasajen du mawzi"
#~ msgid "Run"
#~ msgstr "Ezekusawn"
#~ msgid "Tooltip command"
#~ msgstr "Komandu pra pasajen du mawzi"
#~ msgid "General"
#~ msgstr "Jeraw"
@@ -0,0 +1,148 @@
# Translation of commandoutput in fr
# Copyright (C) 2020
# This file is distributed under the same license as the commandoutput package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: commandoutput\n"
"Report-Msgid-Bugs-To: https://github.com/Zren/plasma-applet-commandoutput\n"
"POT-Creation-Date: 2024-03-12 16:30-0400\n"
"PO-Revision-Date: 2024-03-14 09:56+020\n"
"Last-Translator: Sasha MOREL <s.morel@almtesh.net>\n"
"Language-Team: \n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../metadata.json
msgid "Command Output"
msgstr "Command Output"
#: ../metadata.json
msgid "Run a command periodically and render its output."
msgstr "Exécute une commande périodiquement et affiche son retour."
#: ../contents/config/config.qml ../contents/ui/config/ConfigCommand.qml
msgid "Command"
msgstr "Commande"
#: ../contents/config/config.qml
msgid "Appearance"
msgstr "Apparence"
#: ../contents/config/config.qml
msgid "Actions"
msgstr "Actions"
#: ../contents/ui/config/ConfigActions.qml
msgid "Tooltip"
msgstr "Infobulle"
#: ../contents/ui/config/ConfigActions.qml
msgid "Hover Command:"
msgstr "Commande au survol :"
#: ../contents/ui/config/ConfigActions.qml
msgid "Click"
msgstr "Clic"
#: ../contents/ui/config/ConfigActions.qml
msgid "Run Command:"
msgstr "Lancer la commande :"
#: ../contents/ui/config/ConfigActions.qml
msgid "Mouse Wheel"
msgstr "Molette de souris"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Up:"
msgstr "Défilement vers le haut :"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Down:"
msgstr "Défilement vers le bas :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font"
msgstr "Police"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Family:"
msgstr "Famille :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Size:"
msgstr "Taille :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "px"
msgstr "px"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Colors"
msgstr "Couleurs"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Text:"
msgstr "Texte :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Outline:"
msgstr "Contours :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Misc"
msgstr "Divers"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Desktop Widget:"
msgstr "Pour le widget de bureau :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Show background"
msgstr "afficher un fond"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Width:"
msgstr "Largeur fixe :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Height:"
msgstr "Hauteur fixe :"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Replace all newlines with spaces"
msgstr "Remplacer les sauts de lignes par des espaces"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Run every "
msgstr "Exécuter toutes les "
#: ../contents/ui/config/ConfigCommand.qml
msgid "ms"
msgstr " ms"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Wait for completion"
msgstr "Attendre la fin de l'éxécution"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Enabled"
msgstr "Activé"
#: ../contents/ui/libconfig/FontFamily.qml
msgctxt "Use default font"
msgid "Default"
msgstr "Par défaut"
#~ msgid "<b>Version:</b> %1"
#~ msgstr "<b>Version :</b> %1"
#~ msgid "Desktop Widget: Show background"
#~ msgstr "Pour le widget de bureau : afficher un fond"
#~ msgid "General"
#~ msgstr "Général"
@@ -0,0 +1,149 @@
# Translation of commandoutput in nl_NL
# Copyright (C) 2019
# This file is distributed under the same license as the commandoutput package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: commandoutput\n"
"Report-Msgid-Bugs-To: https://github.com/Zren/plasma-applet-commandoutput\n"
"POT-Creation-Date: 2024-03-12 16:30-0400\n"
"PO-Revision-Date: 2024-03-14 10:27+0100\n"
"Last-Translator: Heimen Stoffels <vistausss@fastmail.com>\n"
"Language-Team: Dutch <vistausss@outlook.com>\n"
"Language: nl_NL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.2\n"
#: ../metadata.json
msgid "Command Output"
msgstr "Opdrachtuitvoer"
#: ../metadata.json
msgid "Run a command periodically and render its output."
msgstr "Voer een opdracht periodiek uit en toon de uitvoer ervan."
#: ../contents/config/config.qml ../contents/ui/config/ConfigCommand.qml
msgid "Command"
msgstr "Opdracht"
#: ../contents/config/config.qml
msgid "Appearance"
msgstr "Vormgeving"
#: ../contents/config/config.qml
msgid "Actions"
msgstr "Acties"
#: ../contents/ui/config/ConfigActions.qml
msgid "Tooltip"
msgstr "Hulpballon"
#: ../contents/ui/config/ConfigActions.qml
msgid "Hover Command:"
msgstr "Opdracht na aanwijzen:"
#: ../contents/ui/config/ConfigActions.qml
msgid "Click"
msgstr "Klikken"
#: ../contents/ui/config/ConfigActions.qml
msgid "Run Command:"
msgstr "Opdracht uitvoeren:"
#: ../contents/ui/config/ConfigActions.qml
msgid "Mouse Wheel"
msgstr "Muiswiel"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Up:"
msgstr "Omhoog scrollen:"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Down:"
msgstr "Omlaag scrollen:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font"
msgstr "Lettertype"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Family:"
msgstr "Lettertype:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Size:"
msgstr "Tekstgrootte:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "px"
msgstr "px"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Colors"
msgstr "Kleuren"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Text:"
msgstr "Tekst:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Outline:"
msgstr "Contour:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Misc"
msgstr "Overig"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Desktop Widget:"
msgstr "Bureaubladwidget:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Show background"
msgstr "Achtergrond tonen"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Width:"
msgstr "Vaste breedte:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Height:"
msgstr "Vaste hoogte:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Replace all newlines with spaces"
msgstr "Alle witregels vervangen door spaties"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Run every "
msgstr "Uitvoeren, elke "
#: ../contents/ui/config/ConfigCommand.qml
msgid "ms"
msgstr "ms"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Wait for completion"
msgstr "Wachten tot opdracht is afgerond"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Enabled"
msgstr "Ingeschakeld"
#: ../contents/ui/libconfig/FontFamily.qml
msgctxt "Use default font"
msgid "Default"
msgstr "Standaard"
#~ msgid "<b>Version:</b> %1"
#~ msgstr "<b>Versie:</b> %1"
#~ msgid "Desktop Widget: Show background"
#~ msgstr "Bureaubladwidget: achtergrond tonen"
#~ msgid "General"
#~ msgstr "Algemeen"
@@ -0,0 +1,166 @@
# Translation of commandoutput in fr
# Copyright (C) 2020
# This file is distributed under the same license as the commandoutput package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: commandoutput\n"
"Report-Msgid-Bugs-To: https://github.com/Zren/plasma-applet-commandoutput\n"
"POT-Creation-Date: 2024-03-12 16:30-0400\n"
"PO-Revision-Date: 2020-10-05 21:55+020\n"
"Last-Translator: Ari CALDEIRA <aricaldeira@gmail.com>\n"
"Language-Team: \n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../metadata.json
msgid "Command Output"
msgstr "Saída de Comandos"
#: ../metadata.json
msgid "Run a command periodically and render its output."
msgstr "Executa um comando a cada X milissegundos, e mostra a saída/resultado"
#: ../contents/config/config.qml ../contents/ui/config/ConfigCommand.qml
msgid "Command"
msgstr "Comando"
#: ../contents/config/config.qml
msgid "Appearance"
msgstr "Aparência"
#: ../contents/config/config.qml
msgid "Actions"
msgstr "Ações"
#: ../contents/ui/config/ConfigActions.qml
msgid "Tooltip"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Hover Command:"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Click"
msgstr "Clique"
#: ../contents/ui/config/ConfigActions.qml
msgid "Run Command:"
msgstr "Rodar o comando:"
#: ../contents/ui/config/ConfigActions.qml
msgid "Mouse Wheel"
msgstr "Rodinha do mouse"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Up:"
msgstr "Rolar para cima:"
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Down:"
msgstr "Rolar para baixo:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font"
msgstr "Fonte"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Family:"
msgstr "Família:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Size:"
msgstr "Tamanho:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "px"
msgstr "px"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Colors"
msgstr "Cores"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Text:"
msgstr "Texto:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Outline:"
msgstr "Contorno:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Misc"
msgstr "Diversos"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Desktop Widget:"
msgstr "Widget para Desktop:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Show background"
msgstr "não deixa transparente"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Width:"
msgstr "Largura fixa:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Height:"
msgstr "Altura fixa:"
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Replace all newlines with spaces"
msgstr "Junta as linhas numa linha só, separando com espaços"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Run every "
msgstr "Executar a cada "
#: ../contents/ui/config/ConfigCommand.qml
msgid "ms"
msgstr "ms"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Wait for completion"
msgstr "Esperar a execução terminar"
#: ../contents/ui/config/ConfigCommand.qml
msgid "Enabled"
msgstr ""
#: ../contents/ui/libconfig/FontFamily.qml
msgctxt "Use default font"
msgid "Default"
msgstr "Padrão"
#~ msgid "<b>Version:</b> %1"
#~ msgstr "<b>Versão:</b> %1"
#~ msgid "Mouse Click"
#~ msgstr "Clique do mouse"
#~ msgid "Command:"
#~ msgstr "Comando:"
#~ msgid "pt"
#~ msgstr "pt"
#~ msgid "Desktop Widget: Show background"
#~ msgstr "Widget para Desktop: não deixa transparente"
#~ msgid "Tooltip Command"
#~ msgstr "Comando para a passagem do mouse"
#~ msgid "Run"
#~ msgstr "Execução"
#~ msgid "Tooltip command"
#~ msgstr "Comando para a passagem do mouse"
#~ msgid "General"
#~ msgstr "Geral"
@@ -0,0 +1,139 @@
# Translation of commandoutput in LANGUAGE
# Copyright (C) 2024
# This file is distributed under the same license as the commandoutput package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: commandoutput\n"
"Report-Msgid-Bugs-To: https://github.com/Zren/plasma-applet-commandoutput\n"
"POT-Creation-Date: 2024-03-12 16:30-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../metadata.json
msgid "Command Output"
msgstr ""
#: ../metadata.json
msgid "Run a command periodically and render its output."
msgstr ""
#: ../contents/config/config.qml ../contents/ui/config/ConfigCommand.qml
msgid "Command"
msgstr ""
#: ../contents/config/config.qml
msgid "Appearance"
msgstr ""
#: ../contents/config/config.qml
msgid "Actions"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Tooltip"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Hover Command:"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Click"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Run Command:"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Mouse Wheel"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Up:"
msgstr ""
#: ../contents/ui/config/ConfigActions.qml
msgid "Scroll Down:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Family:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Font Size:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "px"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Colors"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Text:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Outline:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Misc"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Desktop Widget:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Show background"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Width:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Fixed Height:"
msgstr ""
#: ../contents/ui/config/ConfigAppearance.qml
msgid "Replace all newlines with spaces"
msgstr ""
#: ../contents/ui/config/ConfigCommand.qml
msgid "Run every "
msgstr ""
#: ../contents/ui/config/ConfigCommand.qml
msgid "ms"
msgstr ""
#: ../contents/ui/config/ConfigCommand.qml
msgid "Wait for completion"
msgstr ""
#: ../contents/ui/config/ConfigCommand.qml
msgid "Enabled"
msgstr ""
#: ../contents/ui/libconfig/FontFamily.qml
msgctxt "Use default font"
msgid "Default"
msgstr ""