QmlDesigner: Add focus handling to close popup

Add proper focus handling to the expression builder in order to close
the popup when e.g. clicking outside.

Change-Id: I5ea8422c727b5fc6e47dc77866b709e4ec04e6fd
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Henning Gruendl
2023-09-13 16:40:11 +02:00
committed by Henning Gründl
parent 1ae4b05599
commit 9f391acdd9
4 changed files with 126 additions and 113 deletions

View File

@@ -20,6 +20,10 @@ Column {
width: parent.width
spacing: root.verticalSpacing
TapHandler {
onTapped: root.forceActiveFocus()
}
Row {
spacing: root.horizontalSpacing

View File

@@ -34,7 +34,8 @@ Rectangle {
width: 400
height: root.expressionHeight + 2 * StudioTheme.Values.flowMargin
color: root.style.background.idle
color: (focusScope.activeFocus || popup.searchActive) ? root.style.background.interaction
: root.style.background.idle
border {
color: root.conditionListModel.valid ? root.style.border.idle
: StudioTheme.Values.themeError
@@ -215,129 +216,133 @@ Rectangle {
}
}
Flow {
id: flow
property int focusIndex: -1
FocusScope {
id: focusScope
anchors.fill: parent
anchors.margins: StudioTheme.Values.flowMargin
spacing: StudioTheme.Values.flowSpacing
onPositioningComplete: {
if (root.textInputActive())
root.placeCursor(newTextInput.index)
if (!root.shadowPillVisible)
root.heightBeforeShadowPill = flow.childrenRect.height
onActiveFocusChanged: {
if (!focusScope.activeFocus && !popup.searchActive)
popup.close()
}
Repeater {
id: repeater
Flow {
id: flow
onItemRemoved: function(index, item) {
if (!root.textInputActive())
return
property int focusIndex: -1
// Udpate the cursor position
if (index < newTextInput.index)
newTextInput.index = newTextInput.index - 1
anchors.fill: parent
anchors.margins: StudioTheme.Values.flowMargin
spacing: StudioTheme.Values.flowSpacing
onPositioningComplete: {
if (root.textInputActive())
root.placeCursor(newTextInput.index)
if (!root.shadowPillVisible)
root.heightBeforeShadowPill = flow.childrenRect.height
}
onItemAdded: function(index, item) {
if (!root.textInputActive())
return
Repeater {
id: repeater
if (index >= newTextInput.index)
newTextInput.index = newTextInput.index + 1
}
Pill {
id: pill
operatorModel: __operatorModel
onRemove: function() {
// If pill has focus due to selection or keyboard navigation
if (pill.focus)
root.placeCursor(pill.index)
Qt.callLater(root.remove, pill.index)
}
onUpdate: function(value) {
if (value === "")
Qt.callLater(root.remove, pill.index) // Otherwise crash
else
Qt.callLater(root.update, pill.index, value)
}
onFocusChanged: function() {
if (pill.focus)
flow.focusIndex = pill.index
}
onSubmit: function (cursorPosition) {
let index = pill.index
// If cursor position is 0 the user moved the cursor out to left side, so place
// the cursor before the pill
if (cursorPosition !== 0)
index++
root.placeCursor(index)
}
}
}
}
TextInput {
id: newTextInput
property int index
height: 20
topPadding: 1
font.pixelSize: root.style.baseFontSize
color: root.style.text.idle
visible: false
validator: RegularExpressionValidator { regularExpression: /^\S.+/ }
//onActiveFocusChanged: {
// if (!newTextInput.activeFocus && !root.shadowPillVisible) {
// console.log("CLOSE POPUP")
// popup.close()
// }
//}
onTextEdited: {
if (newTextInput.text === "")
return
newTextInput.visible = false
root.insert(newTextInput.index, newTextInput.text, ConditionListModel.Intermediate)
newTextInput.clear()
// Set focus on the newly created item
let newItem = repeater.itemAt(newTextInput.index)
newItem.forceActiveFocus()
}
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Backspace) {
if (root.textInputActive()) {
let previousIndex = newTextInput.index - 1
if (previousIndex < 0)
onItemRemoved: function(index, item) {
if (!root.textInputActive())
return
let item = repeater.itemAt(previousIndex)
item.setCursorEnd()
item.forceActiveFocus()
popup.close()
// Udpate the cursor position
if (index < newTextInput.index)
newTextInput.index = newTextInput.index - 1
}
onItemAdded: function(index, item) {
if (!root.textInputActive())
return
if (index >= newTextInput.index)
newTextInput.index = newTextInput.index + 1
}
Pill {
id: pill
operatorModel: __operatorModel
onRemove: function() {
// If pill has focus due to selection or keyboard navigation
if (pill.focus)
root.placeCursor(pill.index)
Qt.callLater(root.remove, pill.index)
}
onUpdate: function(value) {
if (value === "")
Qt.callLater(root.remove, pill.index) // Otherwise crash
else
Qt.callLater(root.update, pill.index, value)
}
onFocusChanged: function() {
if (pill.focus)
flow.focusIndex = pill.index
}
onSubmit: function (cursorPosition) {
let index = pill.index
// If cursor position is 0 the user moved the cursor out to left side,
// so place the cursor before the pill
if (cursorPosition !== 0)
index++
root.placeCursor(index)
}
}
}
}
TextInput {
id: newTextInput
property int index
height: 20
topPadding: 1
font.pixelSize: root.style.baseFontSize
color: root.style.text.idle
visible: false
validator: RegularExpressionValidator { regularExpression: /^\S.+/ }
onTextEdited: {
if (newTextInput.text === "")
return
newTextInput.visible = false
root.insert(newTextInput.index, newTextInput.text, ConditionListModel.Intermediate)
newTextInput.clear()
// Set focus on the newly created item
let newItem = repeater.itemAt(newTextInput.index)
newItem.forceActiveFocus()
}
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Backspace) {
if (root.textInputActive()) {
let previousIndex = newTextInput.index - 1
if (previousIndex < 0)
return
let item = repeater.itemAt(previousIndex)
item.setCursorEnd()
item.forceActiveFocus()
popup.close()
}
}
}
}
}
SuggestionPopup {
@@ -348,12 +353,15 @@ Rectangle {
x: 0
y: root.height
width: root.width
operatorModel: __operatorModel
//onOpened: console.log("POPUP opened")
//onClosed: console.log("POPUP closed")
onAboutToHide: {
newTextInput.visible = false
}
onSelect: function(value) {
newTextInput.visible = true
newTextInput.forceActiveFocus()

View File

@@ -30,8 +30,9 @@ Controls.Popup {
root.listModel.reset()
}
closePolicy: Controls.Popup.NoAutoClose
closePolicy: Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutsideParent
padding: 0
focus: search.activeFocus
background: Rectangle {
implicitWidth: root.width

View File

@@ -15,7 +15,7 @@ ControlStyle {
idle: Values.themePopoutControlBackground_idle
hover: Values.themePopoutControlBackground_hover
globalHover: Values.themePopoutControlBackground_globalHover
interaction: Values.themeInteraction
interaction: Values.themeControlBackgroundInteraction
disabled: Values.themePopoutControlBackground_disabled
}