forked from qt-creator/qt-creator
QmlDesigner: Fix ExpressionTextField popup
Fix the ListView/Popup which is shown on the ExpressionTextField when requesting auto completion. * Fix key behavior of the auto completion list * Adapt to the look and feel of the property editor * Fix size and position of the TextField and the overlayed Label Task-number: QDS-2561 Change-Id: Ie8df6a2960b1c273600543532f0a136eb0c542b5 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
committed by
Henning Gründl
parent
d1aabbe262
commit
9f2bb4abaf
@@ -66,15 +66,12 @@ Rectangle {
|
|||||||
|
|
||||||
RoundedPanel {
|
RoundedPanel {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 24
|
height: StudioTheme.Values.height
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
x: 6
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: StudioTheme.Values.inputHorizontalPadding
|
||||||
|
|
||||||
text: backendValues.className.value
|
text: backendValues.className.value
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
}
|
||||||
ToolTipArea {
|
ToolTipArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -88,10 +85,10 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExpressionTextField {
|
ExpressionTextField {
|
||||||
z: 2
|
|
||||||
id: typeLineEdit
|
id: typeLineEdit
|
||||||
|
z: 2
|
||||||
completeOnlyTypes: true
|
completeOnlyTypes: true
|
||||||
|
replaceCurrentTextByCompletion: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
@@ -107,11 +104,18 @@ Rectangle {
|
|||||||
|
|
||||||
typeLineEdit.blockEditingFinished = true
|
typeLineEdit.blockEditingFinished = true
|
||||||
|
|
||||||
if (visible)
|
if (typeLineEdit.visible)
|
||||||
changeTypeName(typeLineEdit.text.trim())
|
changeTypeName(typeLineEdit.text.trim())
|
||||||
visible = false
|
typeLineEdit.visible = false
|
||||||
|
|
||||||
typeLineEdit.blockEditingFinished = false
|
typeLineEdit.blockEditingFinished = false
|
||||||
|
|
||||||
|
typeLineEdit.completionList.model = null
|
||||||
|
}
|
||||||
|
|
||||||
|
onRejected: {
|
||||||
|
typeLineEdit.visible = false
|
||||||
|
typeLineEdit.completionList.model = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -63,15 +63,12 @@ Rectangle {
|
|||||||
|
|
||||||
RoundedPanel {
|
RoundedPanel {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 24
|
height: StudioTheme.Values.height
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
x: 6
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: StudioTheme.Values.inputHorizontalPadding
|
||||||
|
|
||||||
text: backendValues.className.value
|
text: backendValues.className.value
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
}
|
||||||
ToolTipArea {
|
ToolTipArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -88,7 +85,7 @@ Rectangle {
|
|||||||
z: 2
|
z: 2
|
||||||
id: typeLineEdit
|
id: typeLineEdit
|
||||||
completeOnlyTypes: true
|
completeOnlyTypes: true
|
||||||
|
replaceCurrentTextByCompletion: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
@@ -96,10 +93,26 @@ Rectangle {
|
|||||||
showButtons: false
|
showButtons: false
|
||||||
fixedSize: true
|
fixedSize: true
|
||||||
|
|
||||||
|
property bool blockEditingFinished: false
|
||||||
|
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
if (visible)
|
if (typeLineEdit.blockEditingFinished)
|
||||||
|
return
|
||||||
|
|
||||||
|
typeLineEdit.blockEditingFinished = true
|
||||||
|
|
||||||
|
if (typeLineEdit.visible)
|
||||||
changeTypeName(typeLineEdit.text.trim())
|
changeTypeName(typeLineEdit.text.trim())
|
||||||
visible = false
|
typeLineEdit.visible = false
|
||||||
|
|
||||||
|
typeLineEdit.blockEditingFinished = false
|
||||||
|
|
||||||
|
typeLineEdit.completionList.model = null
|
||||||
|
}
|
||||||
|
|
||||||
|
onRejected: {
|
||||||
|
typeLineEdit.visible = false
|
||||||
|
typeLineEdit.completionList.model = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,13 +23,14 @@
|
|||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
import QtQuick 2.1
|
import QtQuick 2.15
|
||||||
import "Constants.js" as Constants
|
import QtQuick.Window 2.15
|
||||||
import StudioControls 1.0 as StudioControls
|
import QtQuick.Controls 2.15
|
||||||
import QtQuickDesignerTheme 1.0
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import StudioControls 1.0 as StudioControls
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
StudioControls.TextField {
|
StudioControls.TextField {
|
||||||
|
|
||||||
id: textField
|
id: textField
|
||||||
|
|
||||||
signal rejected
|
signal rejected
|
||||||
@@ -38,103 +39,199 @@ StudioControls.TextField {
|
|||||||
actionIndicator.visible: false
|
actionIndicator.visible: false
|
||||||
|
|
||||||
property bool completeOnlyTypes: false
|
property bool completeOnlyTypes: false
|
||||||
|
property bool completionActive: listView.model !== null
|
||||||
property bool completionActive: listView.count > 0
|
|
||||||
property bool dotCompletion: false
|
property bool dotCompletion: false
|
||||||
property int dotCursorPos: 0
|
property int dotCursorPos: 0
|
||||||
property string prefix
|
property string prefix
|
||||||
|
|
||||||
property alias showButtons: buttonrow.visible
|
property alias showButtons: buttonrow.visible
|
||||||
|
|
||||||
property bool fixedSize: false
|
property bool fixedSize: false
|
||||||
|
property bool replaceCurrentTextByCompletion: false
|
||||||
|
|
||||||
|
property alias completionList: listView
|
||||||
|
|
||||||
function commitCompletion() {
|
function commitCompletion() {
|
||||||
var cursorPos = textField.cursorPosition
|
if (replaceCurrentTextByCompletion) {
|
||||||
|
textField.text = listView.currentItem.text
|
||||||
var string = textField.text
|
} else {
|
||||||
var before = string.slice(0, cursorPos - textField.prefix.length)
|
var cursorPos = textField.cursorPosition
|
||||||
var after = string.slice(cursorPos)
|
var string = textField.text
|
||||||
|
var before = string.slice(0, cursorPos - textField.prefix.length)
|
||||||
textField.text = before + listView.currentItem.text + after
|
var after = string.slice(cursorPos)
|
||||||
|
|
||||||
textField.cursorPosition = cursorPos + listView.currentItem.text.length - prefix.length
|
|
||||||
|
|
||||||
|
textField.text = before + listView.currentItem.text + after
|
||||||
|
textField.cursorPosition = cursorPos + listView.currentItem.text.length - prefix.length
|
||||||
|
}
|
||||||
listView.model = null
|
listView.model = null
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Popup {
|
||||||
id: listView
|
id: textFieldPopup
|
||||||
|
x: textField.x
|
||||||
|
y: textField.height - StudioTheme.Values.border
|
||||||
|
width: textField.width
|
||||||
|
// TODO Setting the height on the popup solved the problem with the popup of height 0,
|
||||||
|
// but it has the problem that it sometimes extend over the border of the actual window
|
||||||
|
// and is then cut off.
|
||||||
|
height: Math.min(contentItem.implicitHeight + textFieldPopup.topPadding + textFieldPopup.bottomPadding,
|
||||||
|
textField.Window.height - topMargin - bottomMargin,
|
||||||
|
StudioTheme.Values.maxComboBoxPopupHeight)
|
||||||
|
padding: StudioTheme.Values.border
|
||||||
|
margins: 0 // If not defined margin will be -1
|
||||||
|
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnPressOutsideParent
|
||||||
|
| Popup.CloseOnEscape | Popup.CloseOnReleaseOutside
|
||||||
|
| Popup.CloseOnReleaseOutsideParent
|
||||||
|
|
||||||
clip: true
|
|
||||||
cacheBuffer: 0
|
|
||||||
snapMode: ListView.SnapToItem
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
visible: textField.completionActive
|
visible: textField.completionActive
|
||||||
delegate: Text {
|
|
||||||
text: modelData
|
onClosed: listView.model = null
|
||||||
color: Theme.color(Theme.PanelTextColorLight)
|
|
||||||
Rectangle {
|
contentItem: ListView {
|
||||||
visible: index === listView.currentIndex
|
id: listView
|
||||||
z: -1
|
clip: true
|
||||||
anchors.fill: parent
|
implicitHeight: contentHeight
|
||||||
color: Theme.qmlDesignerBackgroundColorDarkAlternate()
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
ScrollBar.vertical: StudioControls.ScrollBar {
|
||||||
|
id: popupScrollBar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model: null
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
id: myItemDelegate
|
||||||
|
|
||||||
|
width: textFieldPopup.width - textFieldPopup.leftPadding - textFieldPopup.rightPadding
|
||||||
|
- (popupScrollBar.visible ? popupScrollBar.contentItem.implicitWidth
|
||||||
|
+ 2 : 0) // TODO Magic number
|
||||||
|
height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
|
||||||
|
padding: 0
|
||||||
|
text: itemDelegateText.text
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
id: itemDelegateText
|
||||||
|
leftPadding: 8
|
||||||
|
text: modelData
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font: textField.font
|
||||||
|
elide: Text.ElideRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered)
|
||||||
|
listView.currentIndex = index
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
listView.currentIndex = index
|
||||||
|
if (textField.completionActive)
|
||||||
|
textField.commitCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
id: listViewHighlight
|
||||||
|
width: textFieldPopup.width - textFieldPopup.leftPadding - textFieldPopup.rightPadding
|
||||||
|
- (popupScrollBar.visible ? popupScrollBar.contentItem.implicitWidth
|
||||||
|
+ 2 : 0)
|
||||||
|
height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
|
||||||
|
color: StudioTheme.Values.themeInteraction
|
||||||
|
y: listView.currentItem.y
|
||||||
|
}
|
||||||
|
highlightFollowsCurrentItem: false
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.top: parent.top
|
background: Rectangle {
|
||||||
anchors.topMargin: 26
|
color: StudioTheme.Values.themeControlBackground
|
||||||
anchors.bottomMargin: textField.fixedSize ? -180 : 12
|
border.color: StudioTheme.Values.themeInteraction
|
||||||
anchors.bottom: parent.bottom
|
border.width: StudioTheme.Values.border
|
||||||
anchors.left: parent.left
|
}
|
||||||
width: 200
|
|
||||||
spacing: 2
|
|
||||||
children: [
|
|
||||||
Rectangle {
|
|
||||||
visible: textField.fixedSize
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Theme.qmlDesignerBackgroundColorDarker()
|
|
||||||
border.color: Theme.qmlDesignerBorderColor()
|
|
||||||
anchors.rightMargin: 12
|
|
||||||
z: -1
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
enter: Transition {
|
||||||
|
}
|
||||||
|
exit: Transition {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verticalAlignment: Text.AlignTop
|
verticalAlignment: Text.AlignTop
|
||||||
|
|
||||||
|
onPressed: listView.model = null
|
||||||
|
|
||||||
Keys.priority: Keys.BeforeItem
|
Keys.priority: Keys.BeforeItem
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
|
var text = textField.text
|
||||||
|
var pos = textField.cursorPosition
|
||||||
|
var explicitComplete = true
|
||||||
|
|
||||||
if (event.key === Qt.Key_Period) {
|
switch (event.key) {
|
||||||
|
|
||||||
|
case Qt.Key_Period:
|
||||||
textField.dotCursorPos = textField.cursorPosition + 1
|
textField.dotCursorPos = textField.cursorPosition + 1
|
||||||
var list = autoComplete(textField.text+".", textField.dotCursorPos, false, textField.completeOnlyTypes)
|
text = textField.text + "."
|
||||||
textField.prefix = list.pop()
|
pos = textField.dotCursorPos
|
||||||
listView.model = list;
|
explicitComplete = false
|
||||||
textField.dotCompletion = true
|
textField.dotCompletion = true
|
||||||
} else {
|
break
|
||||||
if (textField.completionActive) {
|
|
||||||
var list2 = autoComplete(textField.text + event.text,
|
case Qt.Key_Right:
|
||||||
textField.cursorPosition + event.text.length,
|
if (!textField.completionActive)
|
||||||
true, textField.completeOnlyTypes)
|
return
|
||||||
textField.prefix = list2.pop()
|
|
||||||
listView.model = list2;
|
pos = Math.min(textField.cursorPosition + 1, textField.text.length)
|
||||||
}
|
break
|
||||||
|
|
||||||
|
case Qt.Key_Left:
|
||||||
|
if (!textField.completionActive)
|
||||||
|
return
|
||||||
|
|
||||||
|
pos = Math.max(0, textField.cursorPosition - 1)
|
||||||
|
break
|
||||||
|
|
||||||
|
case Qt.Key_Backspace:
|
||||||
|
if (!textField.completionActive)
|
||||||
|
return
|
||||||
|
|
||||||
|
pos = textField.cursorPosition - 1
|
||||||
|
if (pos < 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
text = textField.text.substring(0, pos) + textField.text.substring(textField.cursorPosition)
|
||||||
|
break
|
||||||
|
|
||||||
|
case Qt.Key_Delete:
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!textField.completionActive)
|
||||||
|
return
|
||||||
|
|
||||||
|
var tmp = textField.text
|
||||||
|
text = tmp.substring(0, textField.cursorPosition) + event.text + tmp.substring(textField.cursorPosition)
|
||||||
|
pos = textField.cursorPosition + event.text.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var list = autoComplete(text.trim(), pos, explicitComplete, textField.completeOnlyTypes)
|
||||||
|
textField.prefix = text.substring(0, pos)
|
||||||
|
|
||||||
|
if (list.length && list[list.length - 1] === textField.prefix)
|
||||||
|
list.pop()
|
||||||
|
|
||||||
|
listView.model = list
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onSpacePressed: {
|
Keys.onSpacePressed: {
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
var list = autoComplete(textField.text, textField.cursorPosition, true, textField.completeOnlyTypes)
|
var list = autoComplete(textField.text, textField.cursorPosition, true, textField.completeOnlyTypes)
|
||||||
textField.prefix = list.pop()
|
textField.prefix = textField.text.substring(0, textField.cursorPosition)
|
||||||
listView.model = list;
|
if (list.length && list[list.length - 1] === textField.prefix)
|
||||||
|
list.pop()
|
||||||
|
|
||||||
|
listView.model = list
|
||||||
textField.dotCompletion = false
|
textField.dotCompletion = false
|
||||||
|
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
|
||||||
if (list.length === 1)
|
|
||||||
textField.commitCompletion()
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
event.accepted = false
|
event.accepted = false
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user