feat(StatusBaseInput): introduce component
property
This property enables users to load any component into the input field. This is useful for rendering a "clearable" icon button, simple icons or even more complex buttons. Usage: ```qml StatusBaseInput { ... component: StatusIcon { name: "cancel" color: Theme.palette.dangerColor1 width: 16 } } ``` The `clearable` property of `StatusBaseInput` also renders and icon button on the right hand side. With this new feature, `clearable` is just a short-hand for: ```qml StatusBaseInput { ... component: StatusFlatRoundButton { visible: edit.text.length != 0 && statusBaseInput.clearable && !statusBaseInput.multiline && edit.activeFocus type: StatusFlatRoundButton.Type.Secondary width: 24 height: 24 icon.name: "clear" icon.width: 16 icon.height: 16 icon.color: Theme.palette.baseColor1 onClicked: { edit.clear() } } } ``` Closes #380
This commit is contained in:
parent
88fb57dd3b
commit
019471c804
@ -102,6 +102,17 @@ Column {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusInput {
|
||||||
|
label: "Label"
|
||||||
|
input.placeholderText: "Input width component (right side)"
|
||||||
|
input.component: StatusIcon {
|
||||||
|
icon: "cancel"
|
||||||
|
height: 16
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
StatusInput {
|
StatusInput {
|
||||||
input.multiline: true
|
input.multiline: true
|
||||||
input.placeholderText: "Multiline"
|
input.placeholderText: "Multiline"
|
||||||
|
@ -56,6 +56,23 @@ Item {
|
|||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property Item component
|
||||||
|
|
||||||
|
onClearableChanged: {
|
||||||
|
if (clearable && !component) {
|
||||||
|
clearButtonLoader.active = true
|
||||||
|
clearButtonLoader.parent = statusBaseInputComponentSlot
|
||||||
|
} else {
|
||||||
|
clearButtonLoader.active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onComponentChanged: {
|
||||||
|
if (!!component) {
|
||||||
|
component.parent = statusBaseInputComponentSlot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
implicitWidth: 448
|
implicitWidth: 448
|
||||||
implicitHeight: multiline ? Math.max((edit.implicitHeight + topPadding + bottomPadding), 44) : 44
|
implicitHeight: multiline ? Math.max((edit.implicitHeight + topPadding + bottomPadding), 44) : 44
|
||||||
|
|
||||||
@ -80,149 +97,168 @@ Item {
|
|||||||
return sensor.containsMouse ? Theme.palette.primaryColor2 : "transparent"
|
return sensor.containsMouse ? Theme.palette.primaryColor2 : "transparent"
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusIcon {
|
|
||||||
id: statusIcon
|
|
||||||
anchors.topMargin: 10
|
|
||||||
anchors.left: statusBaseInput.leftIcon ? parent.left : undefined
|
|
||||||
anchors.right: !statusBaseInput.leftIcon ? parent.right : undefined
|
|
||||||
anchors.leftMargin: 10
|
|
||||||
anchors.rightMargin: 10
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
icon: statusBaseInput.icon.name
|
|
||||||
width: statusBaseInput.icon.width
|
|
||||||
height: statusBaseInput.icon.height
|
|
||||||
color: statusBaseInput.icon.color
|
|
||||||
visible: !!statusBaseInput.icon.name
|
|
||||||
}
|
|
||||||
|
|
||||||
Flickable {
|
|
||||||
id: flick
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: (statusIcon.visible && statusBaseInput.leftIcon) ?
|
|
||||||
statusIcon.right : parent.left
|
|
||||||
anchors.right: (statusIcon.visible && !statusBaseInput.leftIcon) ?
|
|
||||||
statusIcon.left : parent.right
|
|
||||||
anchors.leftMargin: statusIcon.visible && statusBaseInput.leftIcon ? 8
|
|
||||||
: statusBaseInput.leftPadding
|
|
||||||
anchors.rightMargin: statusBaseInput.rightPadding + clearable ? clearButton.width
|
|
||||||
: statusIcon.visible && !leftIcon ? 8: 0
|
|
||||||
anchors.topMargin: statusBaseInput.topPadding
|
|
||||||
anchors.bottomMargin: statusBaseInput.bottomPadding
|
|
||||||
contentWidth: edit.paintedWidth
|
|
||||||
contentHeight: edit.paintedHeight
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
QC.ScrollBar.vertical: QC.ScrollBar { interactive: multiline; enabled: multiline }
|
|
||||||
function ensureVisible(r) {
|
|
||||||
if (contentX >= r.x)
|
|
||||||
contentX = r.x;
|
|
||||||
else if (contentX+width <= r.x+r.width)
|
|
||||||
contentX = r.x+r.width-width;
|
|
||||||
if (contentY >= r.y)
|
|
||||||
contentY = r.y;
|
|
||||||
else if (contentY+height <= r.y+r.height)
|
|
||||||
contentY = r.y+r.height-height;
|
|
||||||
}
|
|
||||||
TextEdit {
|
|
||||||
id: edit
|
|
||||||
property string previousText: text
|
|
||||||
width: flick.width
|
|
||||||
height: flick.height
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
selectByMouse: true
|
|
||||||
selectionColor: Theme.palette.primaryColor2
|
|
||||||
selectedTextColor: color
|
|
||||||
focus: true
|
|
||||||
font.pixelSize: 15
|
|
||||||
font.family: Theme.palette.baseFont.name
|
|
||||||
color: Theme.palette.directColor1
|
|
||||||
onCursorRectangleChanged: { flick.ensureVisible(cursorRectangle); }
|
|
||||||
wrapMode: statusBaseInput.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (statusBaseInput.pristine) {
|
|
||||||
statusBaseInput.pristine = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (multiline) {
|
|
||||||
event.accepted = false
|
|
||||||
} else {
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (multiline) {
|
|
||||||
event.accepted = false
|
|
||||||
} else {
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.forwardTo: [statusBaseInput]
|
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
statusBaseInput.dirty = true
|
|
||||||
if (statusBaseInput.maximumLength > 0) {
|
|
||||||
if (text.length > statusBaseInput.maximumLength) {
|
|
||||||
var cursor = cursorPosition;
|
|
||||||
text = previousText;
|
|
||||||
if (cursor > text.length) {
|
|
||||||
cursorPosition = text.length;
|
|
||||||
} else {
|
|
||||||
cursorPosition = cursor-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previousText = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StatusBaseText {
|
|
||||||
id: placeholder
|
|
||||||
visible: (edit.text.length === 0)
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: statusBaseInput.rightPadding
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
font.pixelSize: 15
|
|
||||||
elide: StatusBaseText.ElideRight
|
|
||||||
font.family: Theme.palette.baseFont.name
|
|
||||||
color: statusBaseInput.enabled ? Theme.palette.baseColor1 :
|
|
||||||
Theme.palette.directColor6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Flickable
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: sensor
|
id: sensor
|
||||||
enabled: !edit.activeFocus
|
enabled: !edit.activeFocus
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
onClicked: edit.forceActiveFocus()
|
onClicked: {
|
||||||
}
|
edit.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusIcon {
|
||||||
|
id: statusIcon
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.left: statusBaseInput.leftIcon ? parent.left : undefined
|
||||||
|
anchors.right: !statusBaseInput.leftIcon ? parent.right : undefined
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
icon: statusBaseInput.icon.name
|
||||||
|
width: statusBaseInput.icon.width
|
||||||
|
height: statusBaseInput.icon.height
|
||||||
|
color: statusBaseInput.icon.color
|
||||||
|
visible: !!statusBaseInput.icon.name
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: flick
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: (statusIcon.visible && statusBaseInput.leftIcon) ?
|
||||||
|
statusIcon.right : parent.left
|
||||||
|
anchors.right: {
|
||||||
|
if (!!statusBaseInput.component) {
|
||||||
|
return statusBaseInputComponentSlot.left
|
||||||
|
}
|
||||||
|
return statusIcon.visible && !statusBaseInput.leftIcon ? statusIcon.left : parent.right
|
||||||
|
}
|
||||||
|
anchors.leftMargin: statusIcon.visible && statusBaseInput.leftIcon ? 8
|
||||||
|
: statusBaseInput.leftPadding
|
||||||
|
anchors.rightMargin: {
|
||||||
|
return clearable ? clearButtonLoader.width + 12 :
|
||||||
|
(statusIcon.visible && !leftIcon) || !!statusBaseInput.component ? 8 : 0
|
||||||
|
}
|
||||||
|
anchors.topMargin: statusBaseInput.topPadding
|
||||||
|
anchors.bottomMargin: statusBaseInput.bottomPadding
|
||||||
|
contentWidth: edit.paintedWidth
|
||||||
|
contentHeight: edit.paintedHeight
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
QC.ScrollBar.vertical: QC.ScrollBar { interactive: multiline; enabled: multiline }
|
||||||
|
function ensureVisible(r) {
|
||||||
|
if (contentX >= r.x)
|
||||||
|
contentX = r.x;
|
||||||
|
else if (contentX+width <= r.x+r.width)
|
||||||
|
contentX = r.x+r.width-width;
|
||||||
|
if (contentY >= r.y)
|
||||||
|
contentY = r.y;
|
||||||
|
else if (contentY+height <= r.y+r.height)
|
||||||
|
contentY = r.y+r.height-height;
|
||||||
|
}
|
||||||
|
TextEdit {
|
||||||
|
id: edit
|
||||||
|
property string previousText: text
|
||||||
|
width: flick.width
|
||||||
|
height: flick.height
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
selectByMouse: true
|
||||||
|
selectionColor: Theme.palette.primaryColor2
|
||||||
|
selectedTextColor: color
|
||||||
|
focus: true
|
||||||
|
font.pixelSize: 15
|
||||||
|
font.family: Theme.palette.baseFont.name
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
onCursorRectangleChanged: { flick.ensureVisible(cursorRectangle); }
|
||||||
|
wrapMode: statusBaseInput.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (statusBaseInput.pristine) {
|
||||||
|
statusBaseInput.pristine = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (multiline) {
|
||||||
|
event.accepted = false
|
||||||
|
} else {
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: {
|
||||||
|
if (multiline) {
|
||||||
|
event.accepted = false
|
||||||
|
} else {
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.forwardTo: [statusBaseInput]
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
statusBaseInput.dirty = true
|
||||||
|
if (statusBaseInput.maximumLength > 0) {
|
||||||
|
if (text.length > statusBaseInput.maximumLength) {
|
||||||
|
var cursor = cursorPosition;
|
||||||
|
text = previousText;
|
||||||
|
if (cursor > text.length) {
|
||||||
|
cursorPosition = text.length;
|
||||||
|
} else {
|
||||||
|
cursorPosition = cursor-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousText = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
id: placeholder
|
||||||
|
visible: (edit.text.length === 0)
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: statusBaseInput.rightPadding
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: 15
|
||||||
|
elide: StatusBaseText.ElideRight
|
||||||
|
font.family: Theme.palette.baseFont.name
|
||||||
|
color: statusBaseInput.enabled ? Theme.palette.baseColor1 :
|
||||||
|
Theme.palette.directColor6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Flickable
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusBaseInputComponentSlot
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 12
|
||||||
|
width: childrenRect.width
|
||||||
|
height: childrenRect.height
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
} // Rectangle
|
} // Rectangle
|
||||||
|
|
||||||
StatusFlatRoundButton {
|
Loader {
|
||||||
id: clearButton
|
id: clearButtonLoader
|
||||||
visible: edit.text.length != 0 &&
|
sourceComponent: StatusFlatRoundButton {
|
||||||
statusBaseInput.clearable &&
|
id: clearButton
|
||||||
!statusBaseInput.multiline &&
|
visible: edit.text.length != 0 &&
|
||||||
edit.activeFocus
|
statusBaseInput.clearable &&
|
||||||
anchors.right: parent.right
|
!statusBaseInput.multiline &&
|
||||||
anchors.rightMargin: 8
|
edit.activeFocus
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
type: StatusFlatRoundButton.Type.Secondary
|
||||||
type: StatusFlatRoundButton.Type.Secondary
|
width: 24
|
||||||
width: 24
|
height: 24
|
||||||
height: 24
|
icon.name: "clear"
|
||||||
icon.name: "clear"
|
icon.width: 16
|
||||||
icon.width: 16
|
icon.height: 16
|
||||||
icon.height: 16
|
icon.color: Theme.palette.baseColor1
|
||||||
icon.color: Theme.palette.baseColor1
|
onClicked: {
|
||||||
onClicked: {
|
edit.clear()
|
||||||
edit.clear()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user