226 lines
6.5 KiB
QML
226 lines
6.5 KiB
QML
// 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()
|
|
}
|
|
}
|