forked from qt-creator/qt-creator
QmlDesigner: Add invalid input notification
* Add notifications that show the user when invalid was made and automatically reverted by the backend * Rename Design System to Design Tokens * Show warning for cyclic dependencies * Fix invalid binding not being able to reset Change-Id: I6effc9bf47e086dd3007f96c15b083a972ee79b5 Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
This commit is contained in:
committed by
Thomas Hartmann
parent
7ccf5bf720
commit
2314319102
@@ -81,10 +81,193 @@ Rectangle {
|
||||
}
|
||||
|
||||
function setValue(value: var, row: int, column: int, isBinding: bool): bool {
|
||||
console.log("setValue(", value, row, column, isBinding, ")")
|
||||
return tableView.model.setData(tableView.index(row, column),
|
||||
//console.log("setValue(", value, row, column, isBinding, ")")
|
||||
let result = tableView.model.setData(tableView.index(row, column),
|
||||
DesignSystemBackend.dsInterface.createThemeProperty("", value, isBinding),
|
||||
Qt.EditRole)
|
||||
|
||||
if (!result)
|
||||
overlayInvalid.showData(row, column)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function dismissInvalidOverlay() {
|
||||
overlayInvalid.hide()
|
||||
notification.visible = false
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: overlayInvalid
|
||||
|
||||
property Item cellItem
|
||||
|
||||
color: "transparent"
|
||||
border {
|
||||
width: StudioTheme.Values.border
|
||||
color: StudioTheme.Values.themeAmberLight
|
||||
}
|
||||
|
||||
visible: false
|
||||
z: 112
|
||||
|
||||
function show() {
|
||||
//tableView.closeEditor() // Close all currently visible edit delegates
|
||||
|
||||
overlayInvalid.visible = true
|
||||
overlayInvalid.layout()
|
||||
notification.visible = true
|
||||
|
||||
notification.forceActiveFocus()
|
||||
}
|
||||
|
||||
function showData(row: int, column: int) {
|
||||
overlayInvalid.parent = tableView.contentItem
|
||||
overlayInvalid.cellItem = tableView.itemAtCell(Qt.point(column, row))
|
||||
|
||||
notification.message = qsTr("Invalid binding. Please use a valid non-cyclic binding.")
|
||||
|
||||
overlayInvalid.show()
|
||||
}
|
||||
|
||||
function showHeaderData(section: int, orientation: var) {
|
||||
if (orientation === Qt.Horizontal) {
|
||||
overlayInvalid.parent = horizontalHeaderView.contentItem
|
||||
overlayInvalid.cellItem = horizontalHeaderView.itemAtCell(Qt.point(overlay.section, 0))
|
||||
} else {
|
||||
overlayInvalid.parent = verticalHeaderView.contentItem
|
||||
overlayInvalid.cellItem = verticalHeaderView.itemAtCell(Qt.point(0, overlay.section))
|
||||
}
|
||||
|
||||
notification.message = qsTr("This name is already in use, please use a different name.")
|
||||
|
||||
overlayInvalid.show()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
overlayInvalid.visible = false
|
||||
}
|
||||
|
||||
function layout() {
|
||||
if (!overlayInvalid.visible)
|
||||
return
|
||||
|
||||
if (overlayInvalid.cellItem !== null) {
|
||||
overlayInvalid.x = overlayInvalid.cellItem.x + 1
|
||||
overlayInvalid.y = overlayInvalid.cellItem.y + 1
|
||||
overlayInvalid.width = overlayInvalid.cellItem.width - 2
|
||||
overlayInvalid.height = overlayInvalid.cellItem.height - 2
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: tableView
|
||||
|
||||
function onLayoutChanged() { overlayInvalid.layout() }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: notification
|
||||
|
||||
property alias message: contentItemText.text
|
||||
|
||||
width: 260
|
||||
height: 78
|
||||
z: 666
|
||||
|
||||
visible: false
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 20
|
||||
|
||||
color: StudioTheme.Values.themePopoutBackground
|
||||
border.color: "#636363"
|
||||
border.width: StudioTheme.Values.border
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (!notification.activeFocus)
|
||||
root.dismissInvalidOverlay()
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
anchors.margins: StudioTheme.Values.border
|
||||
|
||||
Item {
|
||||
id: titleBarItem
|
||||
width: parent.width
|
||||
height: StudioTheme.Values.height
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 8
|
||||
anchors.rightMargin: 4
|
||||
spacing: 4
|
||||
|
||||
Item {
|
||||
id: titleBarContent
|
||||
width: row.width - row.spacing - closeIndicator.width
|
||||
height: row.height
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
T.Label {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: root.customStyle.mediumIconFontSize
|
||||
text: StudioTheme.Constants.warning2_medium
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
color: StudioTheme.Values.themeAmberLight
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("Warning")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.IconIndicator {
|
||||
id: closeIndicator
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: StudioTheme.Constants.colorPopupClose
|
||||
pixelSize: StudioTheme.Values.myIconFontSize
|
||||
onClicked: root.dismissInvalidOverlay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentItem
|
||||
width: parent.width
|
||||
height: parent.height - titleBarItem.height
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
anchors.topMargin: 4
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: contentItemText
|
||||
anchors.fill: parent
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -154,7 +337,7 @@ Rectangle {
|
||||
|
||||
StudioControls.Dialog {
|
||||
id: createCollectionDialog
|
||||
property alias newCollectionName: createCollectionTextField.text;
|
||||
property alias newCollectionName: createCollectionTextField.text
|
||||
title: qsTr("Create collection")
|
||||
width: Math.min(300, root.width)
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
@@ -318,7 +501,7 @@ Rectangle {
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Create collection")
|
||||
onTriggered: {
|
||||
createCollectionDialog.newCollectionName = DesignSystemBackend.dsInterface.generateCollectionName(qsTr("NewCollection"));
|
||||
createCollectionDialog.newCollectionName = DesignSystemBackend.dsInterface.generateCollectionName(qsTr("NewCollection"))
|
||||
createCollectionDialog.open()
|
||||
}
|
||||
}
|
||||
@@ -338,10 +521,7 @@ Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
buttonIcon: StudioTheme.Constants.updateContent_medium
|
||||
tooltip: qsTr("Refresh")
|
||||
onClicked: {
|
||||
DesignSystemBackend.dsInterface.loadDesignSystem()
|
||||
//root.loadModel(DesignSystemBackend.dsInterface.collections[0])
|
||||
}
|
||||
onClicked: DesignSystemBackend.dsInterface.loadDesignSystem()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,6 +541,7 @@ Rectangle {
|
||||
required property var propertyValue
|
||||
|
||||
readonly property bool bindingEditor: cell.isBinding || tableView.model.editableOverride
|
||||
readonly property bool isValid: cell.resolvedValue !== undefined
|
||||
|
||||
color: root.backgroundColor
|
||||
implicitWidth: root.cellWidth
|
||||
@@ -377,10 +558,13 @@ Rectangle {
|
||||
HoverHandler { id: cellHoverHandler }
|
||||
|
||||
DSC.BindingIndicator {
|
||||
icon.text: dataCell.isBinding ? StudioTheme.Constants.actionIconBinding
|
||||
: StudioTheme.Constants.actionIcon
|
||||
icon.color: dataCell.isBinding ? StudioTheme.Values.themeInteraction
|
||||
: StudioTheme.Values.themeTextColor
|
||||
id: bindingIndicator
|
||||
icon.text: !dataCell.isValid ? StudioTheme.Constants.warning2_medium
|
||||
: dataCell.isBinding ? StudioTheme.Constants.actionIconBinding
|
||||
: StudioTheme.Constants.actionIcon
|
||||
icon.color: !dataCell.isValid ? StudioTheme.Values.themeAmberLight
|
||||
: dataCell.isBinding ? StudioTheme.Values.themeInteraction
|
||||
: StudioTheme.Values.themeTextColor
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
@@ -391,9 +575,24 @@ Rectangle {
|
||||
tableView.closeEditor()
|
||||
menu.show(dataCell.row, dataCell.column)
|
||||
}
|
||||
|
||||
onHoverChanged: {
|
||||
if (dataCell.isValid)
|
||||
return
|
||||
|
||||
if (bindingIndicator.hover)
|
||||
toolTipInvalid.showText(dataCell,
|
||||
Qt.point(bindingIndicator.x + bindingIndicator.width,
|
||||
bindingIndicator.y),
|
||||
qsTr("Invalid binding. Cyclic binding is not allowed."))
|
||||
else
|
||||
toolTipInvalid.hideText()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.ToolTipExt { id: toolTipInvalid }
|
||||
|
||||
DelegateChooser {
|
||||
id: chooser
|
||||
role: "group"
|
||||
@@ -783,7 +982,7 @@ Rectangle {
|
||||
}
|
||||
text: qsTr("Reset")
|
||||
onTriggered: {
|
||||
let data = tableView.model.data(menu.modelIndex, CollectionModel.ResolvedValueRole)
|
||||
let data = tableView.model.data(menu.modelIndex, CollectionModel.ResolvedValueRole) ?? ""
|
||||
var prop = DesignSystemBackend.dsInterface.createThemeProperty("", data, false)
|
||||
let result = tableView.model.setData(menu.modelIndex, prop, Qt.EditRole)
|
||||
}
|
||||
@@ -895,6 +1094,10 @@ Rectangle {
|
||||
|
||||
// Revoke active focus from text field by forcing active focus on another item
|
||||
tableView.forceActiveFocus()
|
||||
|
||||
|
||||
if (!result)
|
||||
overlayInvalid.showHeaderData(overlay.section, overlay.orientation)
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -929,8 +1132,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
function show(section, orientation) {
|
||||
// Close all currently visible edit delegates
|
||||
tableView.closeEditor()
|
||||
tableView.closeEditor() // Close all currently visible edit delegates
|
||||
|
||||
if (orientation === Qt.Horizontal)
|
||||
overlay.parent = horizontalHeaderView.contentItem
|
||||
|
@@ -11,7 +11,7 @@ Item {
|
||||
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||
property alias icon: icon
|
||||
|
||||
property bool hover: mouseArea.containsMouse//false
|
||||
property bool hover: mouseArea.containsMouse
|
||||
property bool pressed: false
|
||||
property bool forceVisible: false
|
||||
|
||||
@@ -19,6 +19,7 @@ Item {
|
||||
implicitHeight: control.style.actionIndicatorSize.height
|
||||
|
||||
signal clicked
|
||||
|
||||
z: 10
|
||||
|
||||
T.Label {
|
||||
@@ -56,7 +57,6 @@ Item {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
//onContainsMouseChanged: control.hover = mouseArea.containsMouse
|
||||
onClicked: control.clicked()
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Templates as T
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
T.ToolTip {
|
||||
id: control
|
||||
@@ -38,4 +38,3 @@ T.ToolTip {
|
||||
border.color: control.style.toolTip.border
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -53,8 +53,8 @@ WidgetInfo DesignSystemView::widgetInfo()
|
||||
return createWidgetInfo(m_designSystemWidget,
|
||||
"DesignSystemView",
|
||||
WidgetInfo::RightPane,
|
||||
Tr::tr("Design System"),
|
||||
Tr::tr("Design System view"),
|
||||
Tr::tr("Design Tokens"),
|
||||
Tr::tr("Design Tokens view"),
|
||||
DesignerWidgetFlags::IgnoreErrors);
|
||||
}
|
||||
|
||||
|
@@ -62,7 +62,7 @@ DesignSystemWidget::DesignSystemWidget(DesignSystemView *view, DesignSystemInter
|
||||
|
||||
Theme::setupTheme(engine());
|
||||
|
||||
setWindowTitle(tr("Design System", "Title of Editor widget"));
|
||||
setWindowTitle(tr("Design Tokens", "Title of Editor widget"));
|
||||
setMinimumSize(QSize(195, 195));
|
||||
|
||||
// init the first load of the QML UI elements
|
||||
|
Reference in New Issue
Block a user