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 {
|
function setValue(value: var, row: int, column: int, isBinding: bool): bool {
|
||||||
console.log("setValue(", value, row, column, isBinding, ")")
|
//console.log("setValue(", value, row, column, isBinding, ")")
|
||||||
return tableView.model.setData(tableView.index(row, column),
|
let result = tableView.model.setData(tableView.index(row, column),
|
||||||
DesignSystemBackend.dsInterface.createThemeProperty("", value, isBinding),
|
DesignSystemBackend.dsInterface.createThemeProperty("", value, isBinding),
|
||||||
Qt.EditRole)
|
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 {
|
Connections {
|
||||||
@@ -154,7 +337,7 @@ Rectangle {
|
|||||||
|
|
||||||
StudioControls.Dialog {
|
StudioControls.Dialog {
|
||||||
id: createCollectionDialog
|
id: createCollectionDialog
|
||||||
property alias newCollectionName: createCollectionTextField.text;
|
property alias newCollectionName: createCollectionTextField.text
|
||||||
title: qsTr("Create collection")
|
title: qsTr("Create collection")
|
||||||
width: Math.min(300, root.width)
|
width: Math.min(300, root.width)
|
||||||
closePolicy: Popup.CloseOnEscape
|
closePolicy: Popup.CloseOnEscape
|
||||||
@@ -318,7 +501,7 @@ Rectangle {
|
|||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Create collection")
|
text: qsTr("Create collection")
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
createCollectionDialog.newCollectionName = DesignSystemBackend.dsInterface.generateCollectionName(qsTr("NewCollection"));
|
createCollectionDialog.newCollectionName = DesignSystemBackend.dsInterface.generateCollectionName(qsTr("NewCollection"))
|
||||||
createCollectionDialog.open()
|
createCollectionDialog.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,10 +521,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
buttonIcon: StudioTheme.Constants.updateContent_medium
|
buttonIcon: StudioTheme.Constants.updateContent_medium
|
||||||
tooltip: qsTr("Refresh")
|
tooltip: qsTr("Refresh")
|
||||||
onClicked: {
|
onClicked: DesignSystemBackend.dsInterface.loadDesignSystem()
|
||||||
DesignSystemBackend.dsInterface.loadDesignSystem()
|
|
||||||
//root.loadModel(DesignSystemBackend.dsInterface.collections[0])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,6 +541,7 @@ Rectangle {
|
|||||||
required property var propertyValue
|
required property var propertyValue
|
||||||
|
|
||||||
readonly property bool bindingEditor: cell.isBinding || tableView.model.editableOverride
|
readonly property bool bindingEditor: cell.isBinding || tableView.model.editableOverride
|
||||||
|
readonly property bool isValid: cell.resolvedValue !== undefined
|
||||||
|
|
||||||
color: root.backgroundColor
|
color: root.backgroundColor
|
||||||
implicitWidth: root.cellWidth
|
implicitWidth: root.cellWidth
|
||||||
@@ -377,10 +558,13 @@ Rectangle {
|
|||||||
HoverHandler { id: cellHoverHandler }
|
HoverHandler { id: cellHoverHandler }
|
||||||
|
|
||||||
DSC.BindingIndicator {
|
DSC.BindingIndicator {
|
||||||
icon.text: dataCell.isBinding ? StudioTheme.Constants.actionIconBinding
|
id: bindingIndicator
|
||||||
: StudioTheme.Constants.actionIcon
|
icon.text: !dataCell.isValid ? StudioTheme.Constants.warning2_medium
|
||||||
icon.color: dataCell.isBinding ? StudioTheme.Values.themeInteraction
|
: dataCell.isBinding ? StudioTheme.Constants.actionIconBinding
|
||||||
: StudioTheme.Values.themeTextColor
|
: StudioTheme.Constants.actionIcon
|
||||||
|
icon.color: !dataCell.isValid ? StudioTheme.Values.themeAmberLight
|
||||||
|
: dataCell.isBinding ? StudioTheme.Values.themeInteraction
|
||||||
|
: StudioTheme.Values.themeTextColor
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -391,9 +575,24 @@ Rectangle {
|
|||||||
tableView.closeEditor()
|
tableView.closeEditor()
|
||||||
menu.show(dataCell.row, dataCell.column)
|
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 {
|
DelegateChooser {
|
||||||
id: chooser
|
id: chooser
|
||||||
role: "group"
|
role: "group"
|
||||||
@@ -783,7 +982,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
text: qsTr("Reset")
|
text: qsTr("Reset")
|
||||||
onTriggered: {
|
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)
|
var prop = DesignSystemBackend.dsInterface.createThemeProperty("", data, false)
|
||||||
let result = tableView.model.setData(menu.modelIndex, prop, Qt.EditRole)
|
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
|
// Revoke active focus from text field by forcing active focus on another item
|
||||||
tableView.forceActiveFocus()
|
tableView.forceActiveFocus()
|
||||||
|
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
overlayInvalid.showHeaderData(overlay.section, overlay.orientation)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -929,8 +1132,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function show(section, orientation) {
|
function show(section, orientation) {
|
||||||
// Close all currently visible edit delegates
|
tableView.closeEditor() // Close all currently visible edit delegates
|
||||||
tableView.closeEditor()
|
|
||||||
|
|
||||||
if (orientation === Qt.Horizontal)
|
if (orientation === Qt.Horizontal)
|
||||||
overlay.parent = horizontalHeaderView.contentItem
|
overlay.parent = horizontalHeaderView.contentItem
|
||||||
|
@@ -11,7 +11,7 @@ Item {
|
|||||||
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
property alias icon: icon
|
property alias icon: icon
|
||||||
|
|
||||||
property bool hover: mouseArea.containsMouse//false
|
property bool hover: mouseArea.containsMouse
|
||||||
property bool pressed: false
|
property bool pressed: false
|
||||||
property bool forceVisible: false
|
property bool forceVisible: false
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ Item {
|
|||||||
implicitHeight: control.style.actionIndicatorSize.height
|
implicitHeight: control.style.actionIndicatorSize.height
|
||||||
|
|
||||||
signal clicked
|
signal clicked
|
||||||
|
|
||||||
z: 10
|
z: 10
|
||||||
|
|
||||||
T.Label {
|
T.Label {
|
||||||
@@ -56,7 +57,6 @@ Item {
|
|||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
//onContainsMouseChanged: control.hover = mouseArea.containsMouse
|
|
||||||
onClicked: control.clicked()
|
onClicked: control.clicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,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.ToolTip {
|
T.ToolTip {
|
||||||
id: control
|
id: control
|
||||||
@@ -38,4 +38,3 @@ T.ToolTip {
|
|||||||
border.color: control.style.toolTip.border
|
border.color: control.style.toolTip.border
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -53,8 +53,8 @@ WidgetInfo DesignSystemView::widgetInfo()
|
|||||||
return createWidgetInfo(m_designSystemWidget,
|
return createWidgetInfo(m_designSystemWidget,
|
||||||
"DesignSystemView",
|
"DesignSystemView",
|
||||||
WidgetInfo::RightPane,
|
WidgetInfo::RightPane,
|
||||||
Tr::tr("Design System"),
|
Tr::tr("Design Tokens"),
|
||||||
Tr::tr("Design System view"),
|
Tr::tr("Design Tokens view"),
|
||||||
DesignerWidgetFlags::IgnoreErrors);
|
DesignerWidgetFlags::IgnoreErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -62,7 +62,7 @@ DesignSystemWidget::DesignSystemWidget(DesignSystemView *view, DesignSystemInter
|
|||||||
|
|
||||||
Theme::setupTheme(engine());
|
Theme::setupTheme(engine());
|
||||||
|
|
||||||
setWindowTitle(tr("Design System", "Title of Editor widget"));
|
setWindowTitle(tr("Design Tokens", "Title of Editor widget"));
|
||||||
setMinimumSize(QSize(195, 195));
|
setMinimumSize(QSize(195, 195));
|
||||||
|
|
||||||
// init the first load of the QML UI elements
|
// init the first load of the QML UI elements
|
||||||
|
Reference in New Issue
Block a user