ScrollView automatically scroll to focus child component QML - qt

This is a follow up of another question i had, that can be find on this post
When tab to another component position the correspondent scroll in QML
What i want to do is, as i tab on different components i want the scroll to automatically scroll to that same component so that the following happens:
lets suppose im here
now i do 2 tabs and i go to
here it should adjust the scroll to show at least that TextField complete.
Like this
The problem here is that i'm using QtQuick.Controls 1.4 for load of component ScrollView
so with that example i get the error:
Error: Cannot assign to non-existent property "contentY"
with import QtQuick.Controls 2.2it works well.
I provide now a minimalistic code to show the same as the images:
import QtQuick 2.9
import QtQuick.Controls 1.4
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
ApplicationWindow {
id: window
title: "Stack"
visible: true
height: 200
width: 400
ListModel {
id: libraryModel
ListElement {
text: "A Masterpiece"
}
ListElement {
text: "Brilliance"
}
ListElement {
text: "Outstanding"
}
}
Item {
id: page
anchors.fill: parent
width:parent.width
height: parent.height
ScrollView {
id:scrollView
anchors.fill:parent
style: ScrollViewStyle{
handle: Rectangle {
implicitWidth: 30
color: "black"
}
scrollToClickedPosition: true
transientScrollBars:true
}
function scrollToY(y) {
scrollView.contentItem.contentY = y;
}
Column{
width:parent.width
spacing:10
TextField {
id:textField
implicitHeight: 30
font.bold: true
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
ComboBox {
id:comboBox
anchors.topMargin: 10
textRole: "text"
model: libraryModel
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
TextField {
id:textField2
anchors.topMargin: 10
implicitHeight: 30
font.bold: true
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
ComboBox {
id:comboBox2
anchors.topMargin: 10
textRole: "text"
model: libraryModel
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
TextField {
id:textField3
anchors.topMargin: 10
implicitHeight: 30
font.bold: true
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
ComboBox {
id:comboBox3
anchors.topMargin: 10
textRole: "text"
model: libraryModel
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
TextField {
id:textField4
anchors.topMargin: 10
implicitHeight: 30
font.bold: true
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
ComboBox {
id:comboBox4
anchors.topMargin: 10
textRole: "text"
model: libraryModel
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
}
}
}
}

I edited my answer https://stackoverflow.com/a/53650089/6165833
For QtQuick.Controls 1.4 the ScrollView behaves in a different way and you access the Flickable through ScrollView.flickableItem (which is now read-only for QtQuick.Controls 2.2).
So you can still do the trick by using flickableItem instead of contentItem.
You add this function into your ScrollView (with id: scrollView)
// For QtQuick.Controls 1.4
function scrollToY(y) {
scrollView.flickableItem.contentY = y;
}
And then you need to call that in every item when they get the focus :
TextField {
id:textField3
anchors.topMargin: 10
implicitHeight: 30
font.bold: true
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}

Related

QML ListView contentHeight property value is incorrect

I have a QML page with a GridLayout that contains the page title, ListView and close button:
GridLayout {
columns: 1
rows: 5
anchors.fill: parent
<page title item>....
ListView
{
id: list
spacing: 15
model: logModel
Layout.fillWidth: true
Layout.fillHeight: true
delegate: Item {
implicitWidth: parent.width
implicitHeight: grid.height
RowLayout
{
id: grid
spacing: 0
width: parent.width
height: commentLabel.implicitHeight
<icon>....
Label {
id: commentLabel
Layout.fillWidth: true
text: comment
wrapMode: Label.Wrap
}
}
}
ScrollIndicator.vertical: ScrollIndicator { }
}
<close button>...
}
Component.onCompleted:
{
console.log("list.contentHeight = ", list.contentHeight, "list.height = ", list.height)
}
when I bind ListView to a model (logModel) that contains items with comment property that contains relatively long multiline text and check contentHeight property of ListView from Component.onCompleted I get the value 161 that is obviously too small (actually it should be greater than 500, because ListView height property value is 461 and it is not enough for all the items).
So I cannot figure out what this 161 is. The only guess is that it is something close to the content height with single-line items (not multiline), but it is not clear what is the logic behind that.
My QT version is 5.13.2.
EDIT1:
The full source code:
ChangeLogPage.qml:
import QtQuick 2.7
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
Page
{
id: root
property alias model: list.model
background: Rectangle {
color: "transparent"
}
function close()
{
stack.pop()
}
SystemPalette {
id: palette
}
GroupBox
{
id: box
background: Rectangle {
color: palette.base
}
anchors.margins: 15
anchors.fill: parent
GridLayout {
columns: 1
rows: 5
anchors.fill: parent
ListView
{
id: list
Layout.fillWidth: true
Layout.fillHeight: true
//It is not clear why, but there is an enough left margin.
Layout.leftMargin: 0
Layout.rightMargin: 0
Layout.topMargin: 15
Layout.bottomMargin: 20
//Looks like it is only vertical spacing.
spacing: 15
clip: true
delegate: Item {
width: parent.width
height: grid.height
RowLayout
{
id: grid
spacing: 0
width: parent.width
//height: commentLabel.height
Label {
//id: commentLabel
Layout.fillWidth: true
text: comment //qsTr(comment)
wrapMode: Label.Wrap
}
}
}
ScrollIndicator.vertical: ScrollIndicator { }
}
Button {
Layout.alignment: Qt.AlignRight
text: qsTr("Close")
onClicked: close()
}
}
}
Component.onCompleted:
{
console.log("list.contentHeight = ", list.contentHeight, "list.height = ", list.height)
}
}
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import LinesGame 1.0
ApplicationWindow {
id: window
visible: true
width: 360
height: 640
title: "Test"
Component {
id: listModelComponent
ListModel {
id: model
}
}
StackView
{
anchors.fill: parent
id: stack
}
function showChangeLogPage(beginIndex, endIndex)
{
if (beginIndex < endIndex)
{
//var component = Qt.createComponent("ChangeLogModel.qml")
//var logModel = component.createObject(this)
var logModel = listModelComponent.createObject(this);
for (var i = beginIndex; i < endIndex; i++)
{
//i.toString()
logModel.append({comment: qsTr("The advertising was added, but it is shown only when you start a new game or load a saved game. Support the developers, tap on the advertising!")})
}
stack.push(Qt.resolvedUrl("ChangeLogPage.qml"), {model: logModel})
}
}
Component.onCompleted:
{
showChangeLogPage(0, 5)
}
}

How to make SpinBox loose focus when clicking on buttons of another SpinBox in QML

I'm having a specific problem where i click on the text on the middle of a SpinBox and it gets focus but after that i go to another SpinBoxand i click on the - or +, when i do that i want to get the focus out of the last SpinBox to the new one, for that value to get assumed.
As an example, lets suppose i click on the top SpinBox:
Now i click on the +button on the bottom SpinBox, doing that i want to get the focus on this bottom SpinBox
I provide the code of main.qmlbelow:
import QtQuick 2.9
import QtQuick.Controls 1.4
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
ApplicationWindow {
id: window
title: "Stack"
visible: true
height: 200
width: 400
ListModel {
id: libraryModel
ListElement {
text: "A Masterpiece"
}
ListElement {
text: "Brilliance"
}
ListElement {
text: "Outstanding"
}
}
Item {
id: page
anchors.fill: parent
width:parent.width
height: parent.height
ScrollView {
id:scrollView
anchors.fill:parent
style: ScrollViewStyle{
handle: Rectangle {
implicitWidth: 30
color: "black"
}
scrollToClickedPosition: true
transientScrollBars:true
}
function scrollToY(y) {
scrollView.contentItem.contentY = y;
}
Column{
width:parent.width
spacing:10
TextField {
id:textField
implicitHeight: 30
font.bold: true
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
ComboBox {
id:comboBox
anchors.topMargin: 10
textRole: "text"
model: libraryModel
onFocusChanged: if(focus) { scrollView.scrollToY(y); }
}
SpinBox {
width: 100
height: 30
editable: true
}
SpinBox {
width: 100
height: 30
editable: true
}
}
}
}
}
How can this be done?
You can use focusPolicy:
import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
height: 200
width: 400
visible: true
Column{
spacing: 10
SpinBox {
width: 100
height: 30
editable: true
}
SpinBox {
width: 100
height: 30
editable: true
focusPolicy: Qt.ClickFocus // or Qt.StrongFocus
}
}
}
Qt.ClickFocus will give focus to the control when it's clicked, Qt.TabFocus for tab, Qt.WheelFocus for wheel, Qt.StrongFocus for all three.

How to customize ScrollView from qtquick controls 2.3

I'm using using ScrollView control from import QtQuick.Controls 2.3version because it looks better that prior ones.
The problem is that if i try to customize it with ScrollBar.vertical it looses some functionality. I can't press it and drag it up and down like it does by default.
I've searched and i've find a way to do the drag functionality.
The code i've used is:
import QtQuick 2.9
import QtQuick.Controls 1.4
import QtQuick.Controls 2.3
ApplicationWindow {
id: window
title: "Stack"
visible: true
height: 200
width: 400
ListModel {
id: libraryModel
ListElement {
text: "A Masterpiece"
}
ListElement {
text: "Brilliance"
}
ListElement {
text: "Outstanding"
}
}
Item {
id: page
anchors.fill: parent
width:parent.width
height: parent.height
ScrollView {
id:scrollView
anchors.fill:parent
ScrollBar.vertical: ScrollBar {
parent: scrollView
x: scrollView.mirrored ? 0 : scrollView.width - width
y: scrollView.topPadding
height: scrollView.availableHeight
active: scrollView.ScrollBar.horizontal.active
contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 100
radius: width/2
color: scrollView.pressed ? "orange" : "green"
}
}
Column{
width:parent.width
spacing:10
TextField {
id:textField
implicitHeight: 30
font.bold: true
}
ComboBox {
id:comboBox
anchors.topMargin: 10
textRole: "text"
model: libraryModel
}
TextField {
id:textField2
anchors.topMargin: 10
implicitHeight: 30
font.bold: true
}
ComboBox {
id:comboBox2
anchors.topMargin: 10
textRole: "text"
model: libraryModel
}
TextField {
id:textField3
anchors.topMargin: 10
implicitHeight: 30
font.bold: true
}
ComboBox {
id:comboBox3
anchors.topMargin: 10
textRole: "text"
model: libraryModel
}
TextField {
id:textField4
anchors.topMargin: 10
implicitHeight: 30
font.bold: true
}
ComboBox {
id:comboBox4
anchors.topMargin: 10
textRole: "text"
model: libraryModel
}
}
}
}
}
What did i missed on the code? Maybe on ScrollBar?
I've fixed it by setting a different parent for ScrollBar (link). Also, to change the color you need to check ScrollBar property instead of ScrollView:
ScrollBar.vertical: ScrollBar {
id: scrollBar
parent: scrollView.parent
policy: ScrollBar.AlwaysOn
x: scrollView.mirrored ? 0 : scrollView.width - width
y: scrollView.topPadding
height: scrollView.availableHeight
active: scrollView.ScrollBar.horizontal.active
contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 100
radius: width/2
color: scrollBar.pressed ? "orange" : "green"
}
}
If you set z: 1 (or any value > 0) to your ScrollBar the problem should be fixed.
I am not sure about the reason but it looks like it is related to the scrollbar attached by default to the ScrollView, that prevents you to access the custom scrollbar in this 10px zone.
cf Why is there a dead zone on the right side of my flipable?

How can a Tab or Enter Key jump a Control in QML

What i want to do here is to prevent the tab and enter to jump the focus to a specific control but go to the next.
So for example lets say i have 3 sequencial TextField
i'm focused on the first TextField now i press tab and instead of jumping to the second TextField the focus goes to the third TextField.
A full code example:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
ApplicationWindow {
id: window
title: "Stack"
visible: true
width: 1400
Page {
id: page
anchors.fill: parent
property int responsiveWidth: 1000
property int maximumWidth: 900
ScrollView {
id:configScroll
anchors.fill: parent
function scrollToY(y) {
configScroll.flickableItem.contentY = y-30
}
GridLayout {
columns: 2
Keys.onPressed: {
if(event.key==Qt.Key_Return)
for (var i = 0; i < children.length; ++i)
if (children[i].focus) {
children[i].nextItemInFocusChain().forceActiveFocus()
break
}
}
width: page.width > page.responsiveWidth ? page.maximumWidth : page.width
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: page.width > page.responsiveWidth ? (page.width - childrenRect.width)/2 : 10
anchors.rightMargin: page.width > page.responsiveWidth ? 0 : 10
Label {
text: "Company Name"
color: "red"
Layout.fillWidth: true
}
TextField {
objectName: "company_name"
font.bold: true
Layout.fillWidth: true
Layout.rightMargin: 10
onFocusChanged: if(focus) { configScroll.scrollToY(y); }
}
Label {
text: "tab or enter passes this TextField"
color: "red"
Layout.fillWidth: true
}
TextField {
objectName: "company_name2"
font.bold: true
Layout.fillWidth: true
Layout.rightMargin: 10
onFocusChanged: if(focus) { configScroll.scrollToY(y); }
}
Label {
text: "label"
color: "red"
Layout.fillWidth: true
}
TextField {
objectName: "company_name"
font.bold: true
Layout.fillWidth: true
Layout.rightMargin: 10
onFocusChanged: if(focus) { configScroll.scrollToY(y); }
}
}
}
}
}
You can use the QML Type KeyNavigation to setup custom navigation, take a look at the documentation: KeyNavigation QML Type
You can set the KeyNavigation.tab property to navigate to a specific id like this:
TextField {
id: field1
objectName: "company_name"
font.bold: true
Layout.fillWidth: true
Layout.rightMargin: 10
onFocusChanged: if(focus) { configScroll.scrollToY(y); }
KeyNavigation.tab: field3
}
For navigation with enter-key here is a solution for this specific situation:
children[i].nextItemInFocusChain().nextItemInFocusChain().forceActiveFocus()

Even width of Text and Textfield

I have a simple login form written in QML with a custom component, MediumText, and two TextFields. Unfortunately, I'm not able to properly align the elements, as shown by the following picture:
I want the labels to the left (MediumText type), as well as the TextField instances on the right, to take up the same amount of space so that they are correctly aligned. Can you suggest me an approach? Here is my current code.
MediumText.qml:
import QtQuick 2.3
Text {
clip: true
font.pixelSize: 20
font.family: "Liberation Mono"
smooth: true
font.bold: true
wrapMode: Text.WordWrap
opacity: 1
}
Login.qml:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
Rectangle {
id:rootRect
anchors.centerIn: parent
Layout.preferredWidth: 480
Layout.preferredHeight: 640
ColumnLayout {
anchors.centerIn: parent
Layout.fillWidth: true
spacing: 16
Row{
Image {
id: logoimage
height: 135
fillMode: Image.PreserveAspectFit
source: "images/logo.png"
}
}
RowLayout {
anchors.left: parent.left; anchors.right: parent.right
spacing: 4
MediumText { text: "Username: ";Layout.fillWidth:true; }
TextField { id:usernameText; placeholderText: "username"; Layout.fillWidth: true;}
}
RowLayout {
anchors.left: parent.left; anchors.right: parent.right
spacing: 4
MediumText { text: "Password:";Layout.fillWidth:true }
TextField { id:passwordText; placeholderText: "password"; echoMode: TextInput.Password; Layout.fillWidth: true;}
}
RowLayout {
spacing: 16
anchors.horizontalCenter: parent.horizontalCenter
Button { text: "Login"; onClicked: {
console.log(mojoLoginLoader.visible);
mojoLoginLoader.visible=true;
passwordText.enabled=false;
usernameText.enabled=false;
//auth_controller.sayHello();
mojoRootViewHolder.source="Welcome.qml"
}
}
Button { text: "Exit"; onClicked: auth_controller.sayNay() }
}
}
CenteredLoader{visible:false; id:mojoLoginLoader}
}
One fix that works is setting the preferredWidth property of the TextField:
MediumText { text: "Username: ";Layout.fillWidth:true;Layout.preferredWidth: parent.width/2}
You can use a GridLayout instead of building a grid by means of ColumnLayout and RowLayout.
By using the GridLayout, what you want is already guaranteed by the component.
Here is a full example from which you can start:
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Window {
visible: true
width: 500
height: 500
title: "Grid example"
ColumnLayout {
anchors.centerIn: parent
Layout.fillWidth: true
spacing: 16
Row{
Image {
id: logoimage
height: 135
fillMode: Image.PreserveAspectFit
source: "images/logo.png"
}
}
GridLayout {
anchors.centerIn: parent
Layout.fillWidth: true
columnSpacing: 16
rowSpacing: 4
columns: 2
MediumText { text: "Username: "; Layout.fillWidth:true; }
TextField { id:usernameText; placeholderText: "username"; Layout.fillWidth: true;}
MediumText { text: "Password:";Layout.fillWidth:true }
TextField { id:passwordText; placeholderText: "password"; echoMode: TextInput.Password; Layout.fillWidth: true;}
}
RowLayout {
spacing: 16
anchors.horizontalCenter: parent.horizontalCenter
Button { text: "Login"; onClicked: {
console.log(mojoLoginLoader.visible);
mojoLoginLoader.visible=true;
passwordText.enabled=false;
usernameText.enabled=false;
//auth_controller.sayHello();
mojoRootViewHolder.source="Welcome.qml"
}
}
Button { text: "Exit"; onClicked: auth_controller.sayNay() }
}
}
}

Resources