373 lines
11 KiB
QML
373 lines
11 KiB
QML
import QtQuick
|
||
import QtQuick.Controls
|
||
import QtQuick.Layouts
|
||
|
||
Item {
|
||
id: control
|
||
|
||
// 属性定义
|
||
property var value: Qt.size(0, 0) // QSizeF
|
||
property int decimals: 1 // 默认小数位为1
|
||
property int precision: 2 // 精度,允许的小数位数
|
||
|
||
// 尺寸相关属性
|
||
property real minWidth: 0
|
||
property real maxWidth: 999999
|
||
property real minHeight: 0
|
||
property real maxHeight: 999999
|
||
|
||
// 步进增量
|
||
property real widthStep: 1.0
|
||
property real heightStep: 1.0
|
||
|
||
// 是否显示步进按钮
|
||
property bool showSpinButtons: true
|
||
|
||
implicitHeight: 25
|
||
implicitWidth: showSpinButtons ? 140 : 120
|
||
|
||
// 信号 - 重命名避免与内置信号冲突
|
||
signal asValueChanged(value: var)
|
||
signal sizeWidthChanged(width: real) // 改为 sizeWidthChanged
|
||
signal sizeHeightChanged(height: real) // 改为 sizeHeightChanged
|
||
|
||
// 获取宽高
|
||
function getWidth() {
|
||
return value ? value.width : 0
|
||
}
|
||
|
||
function getHeight() {
|
||
return value ? value.height : 0
|
||
}
|
||
|
||
// 设置值
|
||
function setValue(newValue: var) {
|
||
if (!control.value ||
|
||
Math.abs(control.value.width - newValue.width) > 0.000001 ||
|
||
Math.abs(control.value.height - newValue.height) > 0.000001) {
|
||
|
||
// 应用边界限制
|
||
var limitedWidth = Math.max(minWidth, Math.min(maxWidth, newValue.width))
|
||
var limitedHeight = Math.max(minHeight, Math.min(maxHeight, newValue.height))
|
||
|
||
// 保留精度
|
||
limitedWidth = parseFloat(limitedWidth.toFixed(precision))
|
||
limitedHeight = parseFloat(limitedHeight.toFixed(precision))
|
||
|
||
value = Qt.size(limitedWidth, limitedHeight)
|
||
asValueChanged(value)
|
||
}
|
||
}
|
||
|
||
// 设置宽度
|
||
function setWidth(width: real) {
|
||
if (control.value && Math.abs(control.value.width - width) > 0.000001) {
|
||
var limitedWidth = Math.max(minWidth, Math.min(maxWidth, width))
|
||
limitedWidth = parseFloat(limitedWidth.toFixed(precision))
|
||
control.setValue(Qt.size(limitedWidth, control.value.height))
|
||
}
|
||
}
|
||
|
||
// 设置高度
|
||
function setHeight(height: real) {
|
||
if (control.value && Math.abs(control.value.height - height) > 0.000001) {
|
||
var limitedHeight = Math.max(minHeight, Math.min(maxHeight, height))
|
||
limitedHeight = parseFloat(limitedHeight.toFixed(precision))
|
||
control.setValue(Qt.size(control.value.width, limitedHeight))
|
||
}
|
||
}
|
||
|
||
// 步进增加/减少
|
||
function stepWidth(positive: bool) {
|
||
if (!control.value) return
|
||
var step = positive ? widthStep : -widthStep
|
||
setWidth(getWidth() + step)
|
||
}
|
||
|
||
function stepHeight(positive: bool) {
|
||
if (!control.value) return
|
||
var step = positive ? heightStep : -heightStep
|
||
setHeight(getHeight() + step)
|
||
}
|
||
|
||
// UI布局
|
||
RowLayout {
|
||
anchors.fill: parent
|
||
spacing: 4
|
||
|
||
// 宽度部分
|
||
Label {
|
||
text: "W:"
|
||
Layout.alignment: Qt.AlignVCenter
|
||
font.pixelSize: 10
|
||
}
|
||
|
||
TextField {
|
||
id: widthBox
|
||
Layout.preferredWidth: 60
|
||
Layout.preferredHeight: 22
|
||
Layout.alignment: Qt.AlignVCenter
|
||
selectByMouse: true // 允许鼠标选择文本
|
||
|
||
text: control.value ? control.value.width.toFixed(control.decimals) : "0.0"
|
||
font.pixelSize: 10
|
||
leftPadding: 4
|
||
rightPadding: 4
|
||
|
||
// 双击全选的处理
|
||
MouseArea {
|
||
id: widthMouseArea
|
||
anchors.fill: parent
|
||
propagateComposedEvents: true
|
||
acceptedButtons: Qt.LeftButton
|
||
cursorShape: Qt.IBeamCursor
|
||
|
||
onDoubleClicked: function(mouse) {
|
||
parent.selectAll()
|
||
parent.forceActiveFocus()
|
||
mouse.accepted = true
|
||
}
|
||
|
||
onClicked: function(mouse) {
|
||
// 单击时设置焦点但不全选
|
||
parent.forceActiveFocus()
|
||
mouse.accepted = false
|
||
}
|
||
}
|
||
|
||
onActiveFocusChanged: {
|
||
if (activeFocus) {
|
||
// 如果不是双击触发的焦点变化,就全选文本
|
||
if (!widthMouseArea.containsPress) {
|
||
selectAll()
|
||
}
|
||
}
|
||
}
|
||
|
||
// 验证器
|
||
validator: DoubleValidator {
|
||
bottom: control.minWidth
|
||
top: control.maxWidth
|
||
decimals: control.precision
|
||
notation: DoubleValidator.StandardNotation
|
||
}
|
||
|
||
background: Rectangle {
|
||
color: widthBox.enabled ? "white" : "#f0f0f0"
|
||
border.color: widthBox.activeFocus ? "#2196F3" : "#cccccc"
|
||
border.width: 1
|
||
radius: 2
|
||
}
|
||
|
||
onEditingFinished: {
|
||
var num = parseFloat(text)
|
||
if (!isNaN(num) && control.value) {
|
||
control.setWidth(num)
|
||
} else {
|
||
// 恢复原值
|
||
widthBox.text = control.value ?
|
||
control.value.width.toFixed(control.decimals) :
|
||
"0.0"
|
||
}
|
||
}
|
||
|
||
// 键盘上下键调整
|
||
Keys.onUpPressed: function(event) {
|
||
control.stepWidth(true)
|
||
event.accepted = true
|
||
}
|
||
|
||
Keys.onDownPressed: function(event) {
|
||
control.stepWidth(false)
|
||
event.accepted = true
|
||
}
|
||
}
|
||
|
||
// 宽度步进按钮
|
||
Row {
|
||
visible: control.showSpinButtons
|
||
spacing: 0
|
||
Layout.alignment: Qt.AlignVCenter
|
||
|
||
Button {
|
||
width: 12
|
||
height: 11
|
||
text: "▲"
|
||
font.pixelSize: 6
|
||
padding: 0
|
||
onClicked: control.stepWidth(true)
|
||
|
||
background: Rectangle {
|
||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||
border.color: "#cccccc"
|
||
border.width: 1
|
||
radius: 2
|
||
}
|
||
}
|
||
|
||
Button {
|
||
width: 12
|
||
height: 11
|
||
text: "▼"
|
||
font.pixelSize: 6
|
||
padding: 0
|
||
onClicked: control.stepWidth(false)
|
||
|
||
background: Rectangle {
|
||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||
border.color: "#cccccc"
|
||
border.width: 1
|
||
radius: 2
|
||
}
|
||
}
|
||
}
|
||
|
||
// 高度部分
|
||
Label {
|
||
text: "H:"
|
||
Layout.alignment: Qt.AlignVCenter
|
||
font.pixelSize: 10
|
||
}
|
||
|
||
TextField {
|
||
id: heightBox
|
||
Layout.preferredWidth: 60
|
||
Layout.preferredHeight: 22
|
||
Layout.alignment: Qt.AlignVCenter
|
||
selectByMouse: true // 允许鼠标选择文本
|
||
|
||
text: control.value ? control.value.height.toFixed(control.decimals) : "0.0"
|
||
font.pixelSize: 10
|
||
leftPadding: 4
|
||
rightPadding: 4
|
||
|
||
// 双击全选的处理
|
||
MouseArea {
|
||
id: heightMouseArea
|
||
anchors.fill: parent
|
||
propagateComposedEvents: true
|
||
acceptedButtons: Qt.LeftButton
|
||
cursorShape: Qt.IBeamCursor
|
||
|
||
onDoubleClicked: function(mouse) {
|
||
parent.selectAll()
|
||
parent.forceActiveFocus()
|
||
mouse.accepted = true
|
||
}
|
||
|
||
onClicked: function(mouse) {
|
||
// 单击时设置焦点但不全选
|
||
parent.forceActiveFocus()
|
||
mouse.accepted = false
|
||
}
|
||
}
|
||
|
||
onActiveFocusChanged: {
|
||
if (activeFocus) {
|
||
// 如果不是双击触发的焦点变化,就全选文本
|
||
if (!heightMouseArea.containsPress) {
|
||
selectAll()
|
||
}
|
||
}
|
||
}
|
||
|
||
// 验证器
|
||
validator: DoubleValidator {
|
||
bottom: control.minHeight
|
||
top: control.maxHeight
|
||
decimals: control.precision
|
||
notation: DoubleValidator.StandardNotation
|
||
}
|
||
|
||
background: Rectangle {
|
||
color: heightBox.enabled ? "white" : "#f0f0f0"
|
||
border.color: heightBox.activeFocus ? "#2196F3" : "#cccccc"
|
||
border.width: 1
|
||
radius: 2
|
||
}
|
||
|
||
onEditingFinished: {
|
||
var num = parseFloat(text)
|
||
if (!isNaN(num) && control.value) {
|
||
control.setHeight(num)
|
||
} else {
|
||
// 恢复原值
|
||
heightBox.text = control.value ?
|
||
control.value.height.toFixed(control.decimals) :
|
||
"0.0"
|
||
}
|
||
}
|
||
|
||
// 键盘上下键调整
|
||
Keys.onUpPressed: function(event) {
|
||
control.stepHeight(true)
|
||
event.accepted = true
|
||
}
|
||
|
||
Keys.onDownPressed: function(event) {
|
||
control.stepHeight(false)
|
||
event.accepted = true
|
||
}
|
||
}
|
||
|
||
// 高度步进按钮
|
||
Row {
|
||
visible: control.showSpinButtons
|
||
spacing: 0
|
||
Layout.alignment: Qt.AlignVCenter
|
||
|
||
Button {
|
||
width: 12
|
||
height: 11
|
||
text: "▲"
|
||
font.pixelSize: 6
|
||
padding: 0
|
||
onClicked: control.stepHeight(true)
|
||
|
||
background: Rectangle {
|
||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||
border.color: "#cccccc"
|
||
border.width: 1
|
||
radius: 2
|
||
}
|
||
}
|
||
|
||
Button {
|
||
width: 12
|
||
height: 11
|
||
text: "▼"
|
||
font.pixelSize: 6
|
||
padding: 0
|
||
onClicked: control.stepHeight(false)
|
||
|
||
background: Rectangle {
|
||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||
border.color: "#cccccc"
|
||
border.width: 1
|
||
radius: 2
|
||
}
|
||
}
|
||
}
|
||
|
||
Item {
|
||
Layout.fillWidth: true
|
||
}
|
||
}
|
||
|
||
// 当value从外部改变时,更新输入框显示
|
||
onValueChanged: {
|
||
if (value) {
|
||
if (!widthBox.activeFocus) {
|
||
widthBox.text = value.width.toFixed(decimals)
|
||
}
|
||
if (!heightBox.activeFocus) {
|
||
heightBox.text = value.height.toFixed(decimals)
|
||
}
|
||
|
||
// 发出单独的宽高变化信号
|
||
sizeWidthChanged(value.width)
|
||
sizeHeightChanged(value.height)
|
||
}
|
||
}
|
||
}
|