forked from qt-creator/qt-creator
QmlDesigner: Improve design system view
* Add custom SpinBox with input and indicator
* Set value on SpinBox destruction when value was changed
* Add binding indicator
* Implement binding menu and connect it to the backend
* Add readonly state to custom Switch
* Update StudioControls.DialogButton style
* Add dialogs for creating, deleting and renaming collections
* Fix crash in CollectionModel::setData
* Add themes/mode ComboBox
* Fix color editor connection
* Add overlay for header view editing
Task-number: QDS-11856
Change-Id: Ibc5fdd915a298162ed4970fb2845d7484a8f042a
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
(cherry picked from commit 30c62e3a32
)
This commit is contained in:
committed by
Henning Gründl
parent
04f1512194
commit
2bfdd6856a
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Templates as T
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
|
property alias icon: icon
|
||||||
|
|
||||||
|
property bool hover: mouseArea.containsMouse//false
|
||||||
|
property bool pressed: false
|
||||||
|
property bool forceVisible: false
|
||||||
|
|
||||||
|
implicitWidth: control.style.actionIndicatorSize.width
|
||||||
|
implicitHeight: control.style.actionIndicatorSize.height
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
T.Label {
|
||||||
|
id: icon
|
||||||
|
anchors.fill: parent
|
||||||
|
text: StudioTheme.Constants.actionIcon
|
||||||
|
color: control.style.icon.idle
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
font.pixelSize: control.style.baseIconFontSize
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: control.hover && !control.pressed && control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: icon
|
||||||
|
scale: 1.2
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "disable"
|
||||||
|
when: !control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: icon
|
||||||
|
color: control.style.icon.disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
//onContainsMouseChanged: control.hover = mouseArea.containsMouse
|
||||||
|
onClicked: control.clicked()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,365 @@
|
|||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Templates as T
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
import StudioQuickUtils
|
||||||
|
|
||||||
|
T.SpinBox {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
|
|
||||||
|
property real realFrom: 0.0
|
||||||
|
property real realTo: 99.0
|
||||||
|
property real realValue: 1.0
|
||||||
|
property real realStepSize: 1.0
|
||||||
|
|
||||||
|
property alias labelColor: spinBoxInput.color
|
||||||
|
|
||||||
|
property int decimals: 0
|
||||||
|
|
||||||
|
property real minStepSize: {
|
||||||
|
var tmpMinStepSize = Number((control.realStepSize * 0.1).toFixed(control.decimals))
|
||||||
|
return (tmpMinStepSize) ? tmpMinStepSize : control.realStepSize
|
||||||
|
}
|
||||||
|
property real maxStepSize: {
|
||||||
|
var tmpMaxStepSize = Number((control.realStepSize * 10.0).toFixed(control.decimals))
|
||||||
|
return (tmpMaxStepSize < control.realTo) ? tmpMaxStepSize : control.realStepSize
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool edit: spinBoxInput.activeFocus
|
||||||
|
// This property is used to indicate the global hover state
|
||||||
|
property bool hover: (spinBoxInput.hover || spinBoxIndicatorUp.hover || spinBoxIndicatorDown.hover)
|
||||||
|
&& control.enabled
|
||||||
|
|
||||||
|
property bool dirty: false // user modification flag
|
||||||
|
|
||||||
|
property bool spinBoxIndicatorVisible: true
|
||||||
|
property real __spinBoxIndicatorWidth: control.style.spinBoxIndicatorSize.width
|
||||||
|
property real __spinBoxIndicatorHeight: control.height / 2 - control.style.borderWidth
|
||||||
|
|
||||||
|
property alias compressedValueTimer: myTimer
|
||||||
|
|
||||||
|
property string preFocusText: ""
|
||||||
|
|
||||||
|
signal realValueModified
|
||||||
|
signal compressedRealValueModified
|
||||||
|
signal indicatorPressed
|
||||||
|
|
||||||
|
locale: Utils.locale
|
||||||
|
|
||||||
|
// Use custom wheel handling due to bugs
|
||||||
|
property bool __wheelEnabled: false
|
||||||
|
wheelEnabled: false
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
width: control.style.controlSize.width
|
||||||
|
height: control.style.controlSize.height
|
||||||
|
|
||||||
|
leftPadding: spinBoxIndicatorDown.x + spinBoxIndicatorDown.width
|
||||||
|
rightPadding: control.style.borderWidth
|
||||||
|
|
||||||
|
font.pixelSize: control.style.baseFontSize
|
||||||
|
editable: true
|
||||||
|
|
||||||
|
// Leave this in for now
|
||||||
|
from: -99
|
||||||
|
value: 0
|
||||||
|
to: 99
|
||||||
|
|
||||||
|
function checkAndClearFocus() {
|
||||||
|
if (!spinBoxIndicatorUp.activeFocus && !spinBoxIndicatorDown.activeFocus && !spinBoxInput.activeFocus)
|
||||||
|
control.focus = false
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleValidator {
|
||||||
|
id: doubleValidator
|
||||||
|
locale: control.locale
|
||||||
|
notation: DoubleValidator.StandardNotation
|
||||||
|
decimals: control.decimals
|
||||||
|
bottom: Math.min(control.realFrom, control.realTo)
|
||||||
|
top: Math.max(control.realFrom, control.realTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
IntValidator {
|
||||||
|
id: intValidator
|
||||||
|
locale: control.locale
|
||||||
|
bottom: Math.round(Math.min(control.realFrom, control.realTo))
|
||||||
|
top: Math.round(Math.max(control.realFrom, control.realTo))
|
||||||
|
}
|
||||||
|
|
||||||
|
validator: control.decimals === 0 ? intValidator : doubleValidator
|
||||||
|
|
||||||
|
up.indicator: SpinBoxIndicator {
|
||||||
|
id: spinBoxIndicatorUp
|
||||||
|
style: control.style
|
||||||
|
parentHover: control.hover
|
||||||
|
parentEdit: control.edit
|
||||||
|
iconFlip: -1
|
||||||
|
visible: control.spinBoxIndicatorVisible
|
||||||
|
onRealPressed: control.indicatorPressed()
|
||||||
|
onRealReleased: control.realIncrease()
|
||||||
|
onRealPressAndHold: control.realIncrease()
|
||||||
|
x: control.style.borderWidth
|
||||||
|
y: control.style.borderWidth
|
||||||
|
width: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorWidth : 0
|
||||||
|
height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0
|
||||||
|
|
||||||
|
realEnabled: (control.realFrom < control.realTo) ? (control.realValue < control.realTo)
|
||||||
|
: (control.realValue > control.realTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
down.indicator: SpinBoxIndicator {
|
||||||
|
id: spinBoxIndicatorDown
|
||||||
|
style: control.style
|
||||||
|
parentHover: control.hover
|
||||||
|
parentEdit: control.edit
|
||||||
|
visible: control.spinBoxIndicatorVisible
|
||||||
|
onRealPressed: control.indicatorPressed()
|
||||||
|
onRealReleased: control.realDecrease()
|
||||||
|
onRealPressAndHold: control.realDecrease()
|
||||||
|
x: control.style.borderWidth
|
||||||
|
y: spinBoxIndicatorUp.y + spinBoxIndicatorUp.height
|
||||||
|
width: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorWidth : 0
|
||||||
|
height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0
|
||||||
|
|
||||||
|
realEnabled: (control.realFrom < control.realTo) ? (control.realValue > control.realFrom)
|
||||||
|
: (control.realValue < control.realFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: SpinBoxInput {
|
||||||
|
id: spinBoxInput
|
||||||
|
style: control.style
|
||||||
|
validator: control.validator
|
||||||
|
font: control.font
|
||||||
|
readOnly: !control.editable
|
||||||
|
inputMethodHints: control.inputMethodHints
|
||||||
|
|
||||||
|
function handleEditingFinished() {
|
||||||
|
control.checkAndClearFocus()
|
||||||
|
|
||||||
|
// Keep the dirty state before calling setValueFromInput(),
|
||||||
|
// it will be set to false (cleared) internally
|
||||||
|
var valueModified = control.dirty
|
||||||
|
|
||||||
|
control.setValueFromInput()
|
||||||
|
myTimer.stop()
|
||||||
|
|
||||||
|
// Only trigger the signal, if the value was modified
|
||||||
|
if (valueModified)
|
||||||
|
control.compressedRealValueModified()
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
spinBoxInput.focus = false
|
||||||
|
spinBoxInput.handleEditingFinished()
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: control.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: spinBoxBackground
|
||||||
|
color: control.style.background.idle
|
||||||
|
border.color: control.style.border.idle
|
||||||
|
border.width: control.style.borderWidth
|
||||||
|
x: 0
|
||||||
|
width: control.width
|
||||||
|
height: control.height
|
||||||
|
}
|
||||||
|
|
||||||
|
textFromValue: function (value, locale) {
|
||||||
|
// -128 is the value of QLocale::FloatingPointShortest
|
||||||
|
return Number(control.realValue).toLocaleString(locale, 'f', -128)
|
||||||
|
}
|
||||||
|
|
||||||
|
valueFromText: function (text, locale) {
|
||||||
|
control.setRealValue(Number.fromLocaleString(locale, spinBoxInput.text))
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "default"
|
||||||
|
when: control.enabled && !control.hover && !control.hovered
|
||||||
|
&& !control.edit
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
__wheelEnabled: false
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxInput
|
||||||
|
selectByMouse: false
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxBackground
|
||||||
|
color: control.style.background.idle
|
||||||
|
border.color: control.style.border.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: control.enabled && control.hover && control.hovered
|
||||||
|
&& !control.edit
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxBackground
|
||||||
|
border.color: control.style.border.hover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "edit"
|
||||||
|
when: control.edit
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
__wheelEnabled: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxInput
|
||||||
|
selectByMouse: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxBackground
|
||||||
|
border.color: control.style.border.interaction
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "disable"
|
||||||
|
when: !control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxBackground
|
||||||
|
border.color: control.style.border.disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: myTimer
|
||||||
|
repeat: false
|
||||||
|
running: false
|
||||||
|
interval: 400
|
||||||
|
onTriggered: control.compressedRealValueModified()
|
||||||
|
}
|
||||||
|
|
||||||
|
onRealValueChanged: {
|
||||||
|
control.setRealValue(control.realValue) // sanitize and clamp realValue
|
||||||
|
spinBoxInput.text = control.textFromValue(control.realValue, control.locale)
|
||||||
|
control.value = 0 // Without setting value back to 0, it can happen that one of
|
||||||
|
// the indicator will be disabled due to range logic.
|
||||||
|
}
|
||||||
|
onRealValueModified: myTimer.restart()
|
||||||
|
onFocusChanged: {
|
||||||
|
if (control.focus)
|
||||||
|
control.dirty = false
|
||||||
|
}
|
||||||
|
onDisplayTextChanged: spinBoxInput.text = control.displayText
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (control.activeFocus) { // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason)
|
||||||
|
control.preFocusText = spinBoxInput.text
|
||||||
|
spinBoxInput.selectAll()
|
||||||
|
} else {
|
||||||
|
// Make sure displayed value is correct after focus loss, as onEditingFinished
|
||||||
|
// doesn't trigger when value is something validator doesn't accept.
|
||||||
|
if (spinBoxInput.text === "")
|
||||||
|
spinBoxInput.text = control.textFromValue(control.realValue, control.locale)
|
||||||
|
|
||||||
|
if (control.dirty)
|
||||||
|
spinBoxInput.handleEditingFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDecimalsChanged: spinBoxInput.text = control.textFromValue(control.realValue, control.locale)
|
||||||
|
|
||||||
|
Keys.onPressed: function(event) {
|
||||||
|
if (event.key === Qt.Key_Up || event.key === Qt.Key_Down) {
|
||||||
|
event.accepted = true
|
||||||
|
|
||||||
|
// Store current step size
|
||||||
|
var currStepSize = control.realStepSize
|
||||||
|
|
||||||
|
// Set realStepSize according to used modifier key
|
||||||
|
if (event.modifiers & Qt.ControlModifier)
|
||||||
|
control.realStepSize = control.minStepSize
|
||||||
|
|
||||||
|
if (event.modifiers & Qt.ShiftModifier)
|
||||||
|
control.realStepSize = control.maxStepSize
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Up)
|
||||||
|
control.realIncrease()
|
||||||
|
else
|
||||||
|
control.realDecrease()
|
||||||
|
|
||||||
|
// Reset realStepSize
|
||||||
|
control.realStepSize = currStepSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
spinBoxInput.text = control.preFocusText
|
||||||
|
control.dirty = true
|
||||||
|
spinBoxInput.handleEditingFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(v, lo, hi) {
|
||||||
|
return (v < lo || v > hi) ? Math.min(Math.max(lo, v), hi) : v
|
||||||
|
}
|
||||||
|
|
||||||
|
function setValueFromInput() {
|
||||||
|
if (!control.dirty)
|
||||||
|
return
|
||||||
|
|
||||||
|
// FIX: This is a temporary fix for QTBUG-74239
|
||||||
|
var currValue = control.realValue
|
||||||
|
|
||||||
|
// Call the function but don't use return value. The realValue property
|
||||||
|
// will be implicitly set inside the function/procedure.
|
||||||
|
control.valueFromText(spinBoxInput.text, control.locale)
|
||||||
|
|
||||||
|
if (control.realValue !== currValue) {
|
||||||
|
control.realValueModified()
|
||||||
|
} else {
|
||||||
|
// Check if input text differs in format from the current value
|
||||||
|
var tmpInputValue = control.textFromValue(control.realValue, control.locale)
|
||||||
|
|
||||||
|
if (tmpInputValue !== spinBoxInput.text)
|
||||||
|
spinBoxInput.text = tmpInputValue
|
||||||
|
}
|
||||||
|
|
||||||
|
control.dirty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRealValue(value) {
|
||||||
|
if (Number.isNaN(value))
|
||||||
|
value = 0
|
||||||
|
|
||||||
|
if (control.decimals === 0)
|
||||||
|
value = Math.round(value)
|
||||||
|
|
||||||
|
control.realValue = control.clamp(value,
|
||||||
|
control.validator.bottom,
|
||||||
|
control.validator.top)
|
||||||
|
}
|
||||||
|
|
||||||
|
function realDecrease() {
|
||||||
|
// Store the current value for comparison
|
||||||
|
var currValue = control.realValue
|
||||||
|
control.valueFromText(spinBoxInput.text, control.locale)
|
||||||
|
|
||||||
|
control.setRealValue(control.realValue - control.realStepSize)
|
||||||
|
|
||||||
|
if (control.realValue !== currValue)
|
||||||
|
control.realValueModified()
|
||||||
|
}
|
||||||
|
|
||||||
|
function realIncrease() {
|
||||||
|
// Store the current value for comparison
|
||||||
|
var currValue = control.realValue
|
||||||
|
control.valueFromText(spinBoxInput.text, control.locale)
|
||||||
|
|
||||||
|
control.setRealValue(control.realValue + control.realStepSize)
|
||||||
|
|
||||||
|
if (control.realValue !== currValue)
|
||||||
|
control.realValueModified()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,247 @@
|
|||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Templates as T
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
|
|
||||||
|
property bool hover: spinBoxIndicatorMouseArea.containsMouse
|
||||||
|
property bool pressed: spinBoxIndicatorMouseArea.containsPress
|
||||||
|
property bool released: false
|
||||||
|
property bool realEnabled: true
|
||||||
|
|
||||||
|
property bool parentHover: false
|
||||||
|
property bool parentEdit: false
|
||||||
|
|
||||||
|
signal realPressed
|
||||||
|
signal realPressAndHold
|
||||||
|
signal realReleased
|
||||||
|
|
||||||
|
property alias iconFlip: spinBoxIndicatorIconScale.yScale
|
||||||
|
|
||||||
|
|
||||||
|
color: control.style.background.idle
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
onEnabledChanged: control.syncEnabled()
|
||||||
|
onRealEnabledChanged: {
|
||||||
|
control.syncEnabled()
|
||||||
|
if (control.realEnabled === false) {
|
||||||
|
pressAndHoldTimer.stop()
|
||||||
|
spinBoxIndicatorMouseArea.pressedAndHeld = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is meant to synchronize enabled with realEnabled to avoid
|
||||||
|
// the internal logic messing with the actual state.
|
||||||
|
function syncEnabled() {
|
||||||
|
control.enabled = control.realEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: pressAndHoldTimer
|
||||||
|
repeat: true
|
||||||
|
running: false
|
||||||
|
interval: 100
|
||||||
|
onTriggered: control.realPressAndHold()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This MouseArea is a workaround to avoid some hover state related bugs
|
||||||
|
// when using the actual signal 'up.hovered'. QTBUG-74688
|
||||||
|
MouseArea {
|
||||||
|
id: spinBoxIndicatorMouseArea
|
||||||
|
|
||||||
|
property bool pressedAndHeld: false
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
pressAndHoldInterval: 500
|
||||||
|
onPressed: function(mouse) {
|
||||||
|
//if (control.__parentControl.activeFocus)
|
||||||
|
// control.forceActiveFocus()
|
||||||
|
|
||||||
|
control.realPressed()
|
||||||
|
mouse.accepted = true
|
||||||
|
}
|
||||||
|
onPressAndHold: {
|
||||||
|
pressAndHoldTimer.restart()
|
||||||
|
spinBoxIndicatorMouseArea.pressedAndHeld = true
|
||||||
|
}
|
||||||
|
onReleased: function(mouse) {
|
||||||
|
// Only trigger real released when pressAndHold isn't active
|
||||||
|
if (!pressAndHoldTimer.running && containsMouse)
|
||||||
|
control.realReleased()
|
||||||
|
pressAndHoldTimer.stop()
|
||||||
|
mouse.accepted = true
|
||||||
|
spinBoxIndicatorMouseArea.pressedAndHeld = false
|
||||||
|
}
|
||||||
|
onEntered: {
|
||||||
|
if (spinBoxIndicatorMouseArea.pressedAndHeld)
|
||||||
|
pressAndHoldTimer.restart()
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
if (pressAndHoldTimer.running)
|
||||||
|
pressAndHoldTimer.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T.Label {
|
||||||
|
id: spinBoxIndicatorIcon
|
||||||
|
text: StudioTheme.Constants.upDownSquare2
|
||||||
|
color: control.style.icon.idle
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.pixelSize: control.style.smallIconFontSize
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
anchors.fill: parent
|
||||||
|
transform: Scale {
|
||||||
|
id: spinBoxIndicatorIconScale
|
||||||
|
origin.x: 0
|
||||||
|
origin.y: spinBoxIndicatorIcon.height / 2
|
||||||
|
yScale: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "default"
|
||||||
|
when: control.enabled && !control.hover && !control.parentEdit && !control.parentHover
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
color: control.style.icon.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "globalHover"
|
||||||
|
when: control.enabled && !control.hover && !control.parentEdit && control.parentHover
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
color: control.style.icon.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: control.enabled && control.hover && !control.pressed && control.parentHover
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
color: control.style.icon.hover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "press"
|
||||||
|
when: control.enabled && control.pressed
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
color: control.style.icon.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "parentEdit"
|
||||||
|
when: control.parentEdit && control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
color: control.style.icon.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "disable"
|
||||||
|
when: !control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
color: control.style.icon.disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "default"
|
||||||
|
when: !control.parentEdit && !control.hover && !control.parentHover
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.background.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "globalHover"
|
||||||
|
when: control.enabled && !control.hover && !control.parentEdit && control.parentHover
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.background.globalHover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: control.enabled && control.hover && !control.pressed && control.parentHover
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.background.hover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "press"
|
||||||
|
when: control.enabled && control.pressed
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.interaction
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "parentEdit"
|
||||||
|
when: control.parentEdit && control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.background.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "disable"
|
||||||
|
when: !control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.background.disabled
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "limit"
|
||||||
|
when: !control.enabled && !control.realEnabled && control.parentHover
|
||||||
|
PropertyChanges {
|
||||||
|
target: spinBoxIndicatorIcon
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.background.idle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Templates as T
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
|
|
||||||
|
property bool edit: control.activeFocus
|
||||||
|
property bool drag: false
|
||||||
|
property bool hover: hoverHandler.hovered && control.enabled
|
||||||
|
|
||||||
|
z: 2
|
||||||
|
color: control.style.text.idle
|
||||||
|
selectionColor: control.style.text.selection
|
||||||
|
selectedTextColor: control.style.text.selectedText
|
||||||
|
|
||||||
|
horizontalAlignment: Qt.AlignLeft
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
leftPadding: control.style.inputHorizontalPadding
|
||||||
|
rightPadding: control.style.inputHorizontalPadding
|
||||||
|
|
||||||
|
selectByMouse: false
|
||||||
|
activeFocusOnPress: false
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
// TextInput focus needs to be set to activeFocus whenever it changes,
|
||||||
|
// otherwise TextInput will get activeFocus whenever the parent SpinBox gets
|
||||||
|
// activeFocus. This will lead to weird side effects.
|
||||||
|
onActiveFocusChanged: control.focus = control.activeFocus
|
||||||
|
|
||||||
|
HoverHandler { id: hoverHandler }
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: textInputBackground
|
||||||
|
x: 0
|
||||||
|
y: control.style.borderWidth
|
||||||
|
z: -1
|
||||||
|
width: control.width
|
||||||
|
height: control.height - (control.style.borderWidth * 2)
|
||||||
|
color: control.style.background.idle
|
||||||
|
border.width: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that we get Up and Down key press events first
|
||||||
|
Keys.onShortcutOverride: function(event) {
|
||||||
|
event.accepted = (event.key === Qt.Key_Up || event.key === Qt.Key_Down)
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "default"
|
||||||
|
when: control.enabled && !control.edit && !control.hover
|
||||||
|
PropertyChanges {
|
||||||
|
target: textInputBackground
|
||||||
|
color: control.style.background.idle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "globalHover"
|
||||||
|
when: !control.hover && !control.edit
|
||||||
|
PropertyChanges {
|
||||||
|
target: textInputBackground
|
||||||
|
color: control.style.background.globalHover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: control.hover && !control.edit
|
||||||
|
PropertyChanges {
|
||||||
|
target: textInputBackground
|
||||||
|
color: control.style.background.hover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "edit"
|
||||||
|
when: control.edit
|
||||||
|
PropertyChanges {
|
||||||
|
target: textInputBackground
|
||||||
|
color: control.style.background.interaction
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "disable"
|
||||||
|
when: !control.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: textInputBackground
|
||||||
|
color: control.style.background.disabled
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
color: control.style.text.disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -4,7 +4,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
|
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
T.Switch {
|
T.Switch {
|
||||||
id: control
|
id: control
|
||||||
@@ -14,6 +14,7 @@ T.Switch {
|
|||||||
// This property is used to indicate the global hover state
|
// This property is used to indicate the global hover state
|
||||||
property bool hover: control.hovered && control.enabled
|
property bool hover: control.hovered && control.enabled
|
||||||
property bool edit: false
|
property bool edit: false
|
||||||
|
property bool readonly: false
|
||||||
|
|
||||||
property alias labelVisible: label.visible
|
property alias labelVisible: label.visible
|
||||||
property alias labelColor: label.color
|
property alias labelColor: label.color
|
||||||
@@ -33,6 +34,8 @@ T.Switch {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
activeFocusOnTab: false
|
activeFocusOnTab: false
|
||||||
|
|
||||||
|
enabled: !control.readonly
|
||||||
|
|
||||||
indicator: Rectangle {
|
indicator: Rectangle {
|
||||||
id: switchBackground
|
id: switchBackground
|
||||||
x: 0
|
x: 0
|
||||||
@@ -60,6 +63,7 @@ T.Switch {
|
|||||||
color: control.style.icon.idle
|
color: control.style.icon.idle
|
||||||
border.width: 0
|
border.width: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: T.Label {
|
contentItem: T.Label {
|
||||||
@@ -133,7 +137,7 @@ T.Switch {
|
|||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "disable"
|
name: "disable"
|
||||||
when: !control.enabled && !control.checked
|
when: !control.enabled && !control.checked && !control.readonly
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: switchBackground
|
target: switchBackground
|
||||||
color: control.style.background.disabled
|
color: control.style.background.disabled
|
||||||
@@ -186,8 +190,19 @@ T.Switch {
|
|||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "disableChecked"
|
name: "disableChecked"
|
||||||
when: !control.enabled && control.checked
|
when: !control.enabled && control.checked && !control.readonly
|
||||||
extend: "disable"
|
extend: "disable"
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "readonly"
|
||||||
|
when: !control.enabled && !control.checked && control.readonly
|
||||||
|
extend: "default"
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "readonlyChecked"
|
||||||
|
when: !control.enabled && control.checked && control.readonly
|
||||||
|
extend: "defaultChecked"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,7 @@
|
|||||||
|
BindingIndicator 1.0 BindingIndicator.qml
|
||||||
MenuItem 1.0 MenuItem.qml
|
MenuItem 1.0 MenuItem.qml
|
||||||
|
SpinBox 1.0 SpinBox.qml
|
||||||
|
SpinBoxIndicator 1.0 SpinBoxIndicator.qml
|
||||||
|
SpinBoxInput 1.0 SpinBoxInput.qml
|
||||||
Switch 1.0 Switch.qml
|
Switch 1.0 Switch.qml
|
||||||
TextField 1.0 TextField.qml
|
TextField 1.0 TextField.qml
|
||||||
|
@@ -3,27 +3,29 @@
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
T.Button {
|
T.Button {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
|
|
||||||
implicitWidth: Math.max(buttonBackground ? buttonBackground.implicitWidth : 0,
|
implicitWidth: Math.max(control.style.squareControlSize.width,
|
||||||
textItem.implicitWidth + leftPadding + rightPadding)
|
implicitBackgroundWidth + leftInset + rightInset,
|
||||||
implicitHeight: Math.max(buttonBackground ? buttonBackground.implicitHeight : 0,
|
implicitContentWidth + leftPadding + rightPadding)
|
||||||
textItem.implicitHeight + topPadding + bottomPadding)
|
implicitHeight: Math.max(control.style.squareControlSize.height,
|
||||||
|
implicitBackgroundHeight + topInset + bottomInset,
|
||||||
|
implicitContentHeight + topPadding + bottomPadding)
|
||||||
|
|
||||||
leftPadding: control.style.dialogPadding
|
leftPadding: control.style.dialogPadding
|
||||||
rightPadding: control.style.dialogPadding
|
rightPadding: control.style.dialogPadding
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: controlBackground
|
||||||
implicitWidth: 70
|
|
||||||
implicitHeight: 20
|
|
||||||
color: control.style.background.idle
|
color: control.style.background.idle
|
||||||
border.color: control.style.border.idle
|
border.color: control.style.border.idle
|
||||||
anchors.fill: parent
|
border.width: control.style.borderWidth
|
||||||
|
radius: control.style.radius
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
@@ -41,7 +43,7 @@ T.Button {
|
|||||||
when: control.enabled && !control.down && !control.hovered && !control.checked
|
when: control.enabled && !control.down && !control.hovered && !control.checked
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: controlBackground
|
||||||
color: control.highlighted ? control.style.interaction
|
color: control.highlighted ? control.style.interaction
|
||||||
: control.style.background.idle
|
: control.style.background.idle
|
||||||
border.color: control.style.border.idle
|
border.color: control.style.border.idle
|
||||||
@@ -56,7 +58,7 @@ T.Button {
|
|||||||
when: control.enabled && control.hovered && !control.checked && !control.down
|
when: control.enabled && control.hovered && !control.checked && !control.down
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: controlBackground
|
||||||
color: control.style.background.hover
|
color: control.style.background.hover
|
||||||
border.color: control.style.border.hover
|
border.color: control.style.border.hover
|
||||||
}
|
}
|
||||||
@@ -70,9 +72,9 @@ T.Button {
|
|||||||
when: control.enabled && (control.checked || control.down)
|
when: control.enabled && (control.checked || control.down)
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: controlBackground
|
||||||
color: control.style.background.interaction
|
color: control.style.interaction
|
||||||
border.color: control.style.border.interaction
|
border.color: control.style.interaction
|
||||||
}
|
}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: textItem
|
target: textItem
|
||||||
@@ -83,7 +85,7 @@ T.Button {
|
|||||||
name: "disable"
|
name: "disable"
|
||||||
when: !control.enabled
|
when: !control.enabled
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: controlBackground
|
||||||
color: control.style.background.disabled
|
color: control.style.background.disabled
|
||||||
border.color: control.style.border.disabled
|
border.color: control.style.border.disabled
|
||||||
}
|
}
|
||||||
|
@@ -116,6 +116,11 @@ QVariant CollectionModel::headerData(int section, Qt::Orientation orientation, i
|
|||||||
|
|
||||||
Qt::ItemFlags CollectionModel::flags(const QModelIndex &index) const
|
Qt::ItemFlags CollectionModel::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
|
// If group type is FLAGS and not binding block editable
|
||||||
|
if (data(index, Roles::GroupRole).value<GroupType>() == GroupType::Flags
|
||||||
|
&& !data(index, Roles::BindingRole).toBool())
|
||||||
|
return QAbstractItemModel::flags(index);
|
||||||
|
|
||||||
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
|
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,9 +219,9 @@ bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, i
|
|||||||
p.name = propName;
|
p.name = propName;
|
||||||
const ThemeId id = m_themeIdList[index.column()];
|
const ThemeId id = m_themeIdList[index.column()];
|
||||||
if (m_collection->updateProperty(id, groupType, p)) {
|
if (m_collection->updateProperty(id, groupType, p)) {
|
||||||
beginResetModel();
|
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
|
||||||
|
emit dataChanged(index, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@@ -25,6 +25,7 @@ public:
|
|||||||
ResolvedValueRole,
|
ResolvedValueRole,
|
||||||
PropertyValueRole
|
PropertyValueRole
|
||||||
};
|
};
|
||||||
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
Q_PROPERTY(QStringList themeNames READ themeNameList NOTIFY themeNameChanged FINAL)
|
Q_PROPERTY(QStringList themeNames READ themeNameList NOTIFY themeNameChanged FINAL)
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ DesignSystemInterface::DesignSystemInterface(DSStore *store)
|
|||||||
{
|
{
|
||||||
qmlRegisterUncreatableMetaObject(
|
qmlRegisterUncreatableMetaObject(
|
||||||
QmlDesigner::staticMetaObject, "QmlDesigner.DesignSystem", 1, 0, "GroupType", "");
|
QmlDesigner::staticMetaObject, "QmlDesigner.DesignSystem", 1, 0, "GroupType", "");
|
||||||
|
qmlRegisterUncreatableType<CollectionModel>("QmlDesigner.DesignSystem", 1, 0, "CollectionModel", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
DesignSystemInterface::~DesignSystemInterface() {}
|
DesignSystemInterface::~DesignSystemInterface() {}
|
||||||
|
Reference in New Issue
Block a user