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:
co-authored by
Claude Sonnet 5
parent
d434a3c635
commit
b98999cde9
+83
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -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'
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
+12
@@ -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]
|
||||
}
|
||||
+159
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+102
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -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
|
||||
}
|
||||
}
|
||||
+33
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
@@ -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
|
||||
// }
|
||||
// }
|
||||
+225
@@ -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()
|
||||
}
|
||||
}
|
||||
+57
@@ -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)
|
||||
}
|
||||
}
|
||||
+48
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+63
@@ -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
|
||||
}
|
||||
}
|
||||
+49
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user