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)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|