Virtual Keyboard hides Text QML - qt

I have the following Virtual Keyboard which sets the y of a flickable to a value to not hide text. the settings variable is containts the IntSettingsRow below.
//Administration.qml
K_VirtualKeyBoard{
id: keyboard
z:100
onVisibleChanged: {
if(visible){
if(settings.posY - keyboard.height > 0){
flickable.contentY = settings.posY - keyboard.height + 20
}
else{
flickable.contentY = 0
}
}
if(!visible){
settings.settingsRow.textField.focus = false
flickable.contentY = 0
}
}
}
And i have a IntSettingsRow which is a Textfield to insert Some text.
//GeneralSettings.qml
IntSettingsRow{
id: touchDeactivationTimedTime
height: itemHeight
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 8
validator: IntValidator{bottom: 10; top: 300}
title: qsTr("deactivate time")+":"
value: currentSettings?currentSettings.deactivationTime:0
onValueChanged:{
var val = parseInt(value)
if (!isNaN(val)){
currentSettings?currentSettings.deactivationTime = val:{}
}
}
onEditingFinished: {
if (value === "") textField.text = currentSettings.deactivationTime
}
onFocusChanged: {
settingsRow = touchDeactivationTimedTime
if(!textField.focus){
editingFinished()
posY = 0
}else{
posY = mapToItem(parent, x, y).y
}
}
}
Somehow on the first click into the Textfield the virtual keyboard pops up as expected and the flickable shifts. i hide the keyboard and try it again it doesn't work. So the virtual keyboard pops up but the flickable does not change.

I found a solution with editing the flickable directly from the onfocus in my intsettingsrow. now it works
So i changed my K_VirtualKeyboard to:
K_VirtualKeyBoard{
id: keyboard
z:100
onVisibleChanged: {
if(!visible){
flickable.contentY = 0
}
}
}
And added this to the textfield that triggered the Keyboard
textField.onReleased: {
var keyBoardY
if(keyboard.visible)
keyBoardY = keyboard.y
else
keyBoardY = keyboard.y - keyboard.height
if(keyBoardY < (mapToItem(root.parent.parent,0,0).y + height*2))
flickable.contentY = (mapToItem(root.parent.parent,0,0).y + height*2) - keyBoardY + 8
}

Related

How to dynamically update a ListView in QML

I have a ListView that displays a list of all notifications for a user. Right now since I'm using Component.onCompleted, if the list updates, the new list is not displayed, but the one which existed during instantiation. How could we solve this? Would using a Loader with a separate component instead help?
property int numNotifications: backend_service.num_notifications
property var notifications: []
onNumNotificationsChanged: {
for(var x=0; x<numNotifications; x++) {
var notif = backend_service.notifications.get(x);
notifications.push(notif)
}
}
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
color: "black"
height: 500
width: 0.95 * parent.width
ListView {
anchors.fill: parent
model: notifModel
delegate: notifDelegate
}
}
ListModel {
id: notifModel
Component.onCompleted: {
for(var i in notifications) {
notifModel.append({"name": notifications[i]})
}
}
}
Component {
id: notifDelegate
Row {
spacing: 10
Text { text: name; color: "white" }
}
}
Component.onCompleted only runs when the object is built and never again. So using that method to add items to the model is useless, instead you should use the function that reports the new data:
onNumNotificationsChanged: {
for(var x=0; x<numNotifications; x++) {
var notif = backend_service.notifications.get(x);
notifModel.append({"name": notif})
}
}

Connect custom Scrollbar and Flickable

I have a virtual large table, and an area that is real drawn. Depending on the position of the content, a certain part of the virtual table is displayed.
And now I'm trying in different ways to synchronize the content and the scrollbar.
In this case, the scrollbar jumps with the content.
AreaScrollBar.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
ScrollBar {
id: root
/// Virtual content size
/// 50000
property real virtualContentSize: 0
/// Real content size
/// 2000
property real contentSize: 0
/// Visible content size
/// 500
property real visibleSize: orientation === Qt.Horizontal ? width : height
/// Position offset step.
/// 100
property real stepAreaPosition: 1
/// The position of the component area [0 .. virtualContentSize - contentSize]
property real areaPosition: 0
/// [0 .. contentSize - visibleSize]
property real positionInArea: 0
property QtObject priv: QtObject {
/// В запасе после перестроения
readonly property real buffer: 0.2 * contentSize - (0.2 * contentSize) % stepAreaPosition
}
size: (orientation === Qt.Horizontal ? width : height) / virtualContentSize
policy: ScrollBar.AlwaysOn
onPositionChanged: {
console.log('onPosChanged', position)
let arP = -1
let pia = -1
if (position < 0) {
position = 0
} else if (position + size > 1) {
position = 1 - size
}
if (position * virtualContentSize + visibleSize
> areaPosition + 0.9 * contentSize) {
pia = priv.buffer
} else if (position * virtualContentSize - areaPosition < 0.1 * contentSize) {
pia = contentSize - (priv.buffer + visibleSize)
} else {
positionInArea = position * virtualContentSize - areaPosition
return
}
arP = position * virtualContentSize - pia
let arp2 = 0
if (arP < 0) {
arp2 = 0
} else if (arP > virtualContentSize - contentSize) {
arp2 = virtualContentSize - contentSize
} else {
arp2 = arP - (arP % stepAreaPosition)
}
positionInArea = position * virtualContentSize - arp2
areaPosition = arp2
}
}
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Window {
width: 600
height: 600
visible: true
title: qsTr("Hello World")
Column {
focus: true
Keys.onDownPressed: vSb.position += 10 / vSb.virtualContentSize
Keys.onUpPressed: vSb.position -= 10 / vSb.virtualContentSize
Keys.onLeftPressed: hSb.position -= 10 / hSb.virtualContentSize
Keys.onRightPressed: hSb.position += 10 / hSb.virtualContentSize
Row {
Column {
Text { text: qsTr("vSb.position %1").arg(vSb.position) }
Text { text: qsTr("vSb.areaPosition %1").arg(vSb.areaPosition) }
Text { text: qsTr("vSb.positionInArea %1").arg(vSb.positionInArea) }
}
Item {
width: 100
height: 1
}
Column {
Text { text: qsTr("hSb.position %1").arg(hSb.position) }
Text { text: qsTr("hSb.areaPosition %1").arg(hSb.areaPosition) }
Text { text: qsTr("hSb.positionInArea %1").arg(hSb.positionInArea) }
}
}
Rectangle {
id: mainArea
width: 500
height: 500
clip: true
color: "gray"
border.color: "darkgray"
border.width: 3
property int _rows: 50
property int _columns: 10
property int _virtualRows: 500
property int _virtualColumns: 50
property int _rectWidth: 90
property int _rectHeight: 90
property int _spacing: 10
property int _virtualContentWidth: _virtualColumns * (_rectWidth + _spacing)
property int _virtualContentHeight: _virtualRows * (_rectHeight + _spacing)
Flickable {
id: _flick
anchors.fill: parent
contentWidth: mainArea._columns * (mainArea._rectWidth + mainArea._spacing)
contentHeight: mainArea._rows * (mainArea._rectHeight + mainArea._spacing)
Column {
spacing: mainArea._spacing
Repeater {
model: mainArea._rows
Row {
property int rowInd: index
spacing: mainArea._spacing
Repeater {
model: mainArea._columns
Rectangle {
id: rect
property int columnInd: index
width: mainArea._rectWidth
height: mainArea._rectHeight
color: "#3c324a"
Column {
Repeater {
model: rect.height / 10
Rectangle {
width: rect.width
height: 10
color: Qt.lighter(rect.color, 1 + index * 0.1)
}
}
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
text: '[%2, %3]'.arg(rowInd).arg(columnInd)
font.pointSize: 7
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: ('content\n[%1, %2]'
.arg(vSb.areaPosition / vSb.stepAreaPosition + rowInd)
.arg(hSb.areaPosition / hSb.stepAreaPosition + columnInd))
font.pixelSize: 15
}
}
}
}
}
}
interactive: true
Binding on contentX {
value: {
console.log('_flick.contentX', _flick.contentX, hSb.positionInArea)
return hSb.positionInArea
}
}
Binding on contentY {
value: {
console.log('_flick.contentY', _flick.contentY, vSb.positionInArea)
return vSb.positionInArea
}
}
}
AreaScrollBar {
id: vSb
orientation: Qt.Vertical
stepAreaPosition: mainArea._rectHeight + mainArea._spacing
virtualContentSize: mainArea._virtualRows * stepAreaPosition
contentSize: mainArea._rows * stepAreaPosition
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: hSb.top
Binding on position {
value: {
console.log('vSb.position', _flick.contentY,
vSb.areaPosition, vSb.virtualContentSize)
return (_flick.contentY + vSb.areaPosition) / vSb.virtualContentSize
}
}
}
AreaScrollBar {
id: hSb
orientation: Qt.Horizontal
stepAreaPosition: mainArea._rectWidth + mainArea._spacing
virtualContentSize: mainArea._virtualColumns * stepAreaPosition
visibleSize: mainArea.width
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: vSb.left
Binding on position {
value: {
console.log('hSb.position', _flick.contentX,
hSb.areaPosition, hSb.virtualContentSize)
return (_flick.contentX + hSb.areaPosition) / hSb.virtualContentSize
}
}
}
}
}
}
Error when I try to drag the canvas
This was always the problem when Qt project added QML. People try to skip learning C++ and do everything in JavaScript.
You need to read the TableView documentation. A TableView is already Flickable and provides a rectangle view into a TableModel. No need to try re-inventing the wheel.
Then look at this for adding scrollbars to TableView.
The correct solution is always create all data in C++, use QML for display only.
As soon as people start trying to implement logic in QML/JavaScript the wheels come off the cart.
Please test this code, I use some parts of your code but I didn't complicate it with the position I just use Flickable and Scrollbar, and the Repeaters that you use for creating your matrix.
I test it and in this way, scrollbars didn't jump.
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
id:mainArea
visible: true
width: 640
height: 480
title: qsTr("Flickable and Scrollbar ")
property int _rows: 50
property int _columns: 10
property int _virtualRows: 500
property int _virtualColumns: 50
property int _rectWidth: 90
property int _rectHeight: 90
property int _spacing: 10
property int _virtualContentWidth: _virtualColumns * (_rectWidth + _spacing)
property int _virtualContentHeight: _virtualRows * (_rectHeight + _spacing)
Flickable{
width: parent.width
height: parent.height
contentHeight: mColumnId.implicitHeight
contentWidth: mColumnId.implicitWidth
Column{
id : mColumnId
anchors.fill: parent
spacing: mainArea._spacing
Repeater {
model: mainArea._rows
Row {
property int rowInd: index
spacing: mainArea._spacing
Repeater {
model: mainArea._columns
Rectangle {
id: rect
property int columnInd: index
width: mainArea._rectWidth
height: mainArea._rectHeight
color: "#3c324a"
Column {
Repeater {
model: rect.height / 10
Rectangle {
width: rect.width
height: 10
color: Qt.lighter(rect.color, 1 + index * 0.1)
}
}
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
text: '[%2, %3]'.arg(rowInd).arg(columnInd)
font.pointSize: 7
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "Parisa"
font.pixelSize: 15
}
}
}
}
}
}
ScrollBar.vertical: ScrollBar{}
ScrollBar.horizontal: ScrollBar{}
}
}

QML update the property of Item inside the ListView

Item {
Component.onComplete: {
for (var i=0;i < 10;i++) {
myModel.append({"myTxt": "SomeThing"+i});
}
}
ListModel {
id: myModel
}
ListView {
id: listView
width: parent.width
height: parent.height
model: myModel
delegate: Rectangle {
property string propItemState: 0;
MyListItem {
id: test
itemText: myText
itemState: propItemState;
}
}
}
}
I want to update the propItemState once the list is displayed.
For that, I have tried the below method but am getting an undefined error.
I am calling this method once the list model is updated.
function updateListItems() {
for (var index=0;index < listView.count;index++) {
console.log("propItemState: "+listView.contentItem.children[index].propItemState);
listView.contentItem.children[index].propItemState = 2;
}
}
You can bind propItemState to something outside of your delegate. Or you can add signal handler (Connections), which listens to your model (for example; or to some another class) and changes state when your conditions met.
Example:
Item {
Component.onComplete: {
for (var i=0;i < 10;i++) {
myModel.append({"myTxt": "SomeThing"+i});
}
internal.state = "1";
}
ListModel {
id: myModel
}
QtObject {
id: internal
property string state: "0"
}
ListView {
id: listView
width: parent.width
height: parent.height
model: myModel
delegate: Rectangle {
property string propItemState: internal.state
MyListItem {
id: test
itemText: myText
itemState: propItemState;
}
}
}
}

How to translate a QQuickItem's position into its new parent

I have 2 QQuickItems like below which I can fetch on C++ side using the QMLEngine like this.
QQuickItem * quick_item_1 = m_qml_engine->rootObjects()[0]->findChild<QQuickItem*>("quickitem1");
QQuickItem * quick_item_2 = m_qml_engine->rootObjects()[0]->findChild<QQuickItem*>("quickitem2");
Note: quick_item_1's immediate parent is different & quick_item_2's immediate parent is also different. But they both are drawn on the same application window into different immediate parents.
I am drawing both of them offscreen on a different QQuickItem. Let's call it new_parent_surface. I draw both these items on new_parent_surface by changing their parent to new_parent_surface like this.
quick_item_1->setParentItem(new_parent_surface);
quick_item_2->setParentItem(new_parent_surface);
This works fine for the objective of drawing them on a new parent QQuickItem. I get the both quick_item_1 & quick_item_2 drawn on new_parent_surface. Even though new_parent_surface is not drawn on UI, but if I take a snapshot using grabToImage of new_parent_surface, I can see the 2 items drawn on them. Fine till here.
However the positioning of quick_item_1 & quick_item_2 is not correct. I want to position them similar to the way they were positioned their original parent item. I can do some percentage math & try positioning them the same way as they were drawn on their original parent but isn't there a QQQuickItem or Qt API to translate this positioning to a new parent?
I tried to look into QQuickItem's mapping APIs like mapToItem & trying them out like this.
quick_item_2->mapToItem(new_parent_surface, quick_item_2->position());
But the positioning is still not correct.
So, how can I map a QQuickItem's position into its new parent QQuickItem after doing a setParentItem?
Items position is always relative to its parent. And position of the parent is relative to its parent and and so on. But you always can get both relative or global position. QML has lots of coordination translation function. Here is small example that could explain the issue:
import QtQuick 2.7
import QtQuick.Window 2.0
import QtQuick.Controls 2.1
Window {
id:container
width: 800
height: 800
visible: true
Component {
id: rect
Rectangle {
property bool imParent: false
x: 50 + Math.round(Math.random() * 550)
y: 50 + Math.round(Math.random() * 550)
width: 100 + Math.round(Math.random() * 100)
height: 100 + Math.round(Math.random() * 100)
color: Qt.rgba(Math.random(),Math.random(),Math.random(),1);
Drag.active: dragArea.drag.active
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
}
Text {
anchors.centerIn: parent
text: imParent ? "I'm parent" : "Drag me"
color: "white"
}
}
}
Rectangle {
id: blk
x: 10
y: 10
z: 100
parent: null
height: 50
width: 50
radius: 5
border.color: "white"
color: "black"
}
Repeater {
id: reptr
model: 5
property int pos: 0
Loader {
id: loader
sourceComponent: rect
onLoaded: {
if(blk.parent == null) {
blk.parent = loader.item;
loader.item.imParent = true;
}
}
}
}
Row {
anchors.horizontalCenter: container.contentItem.horizontalCenter
spacing: 2
Button {
text: "Reparent relative to parent"
onClicked: {
reptr.pos ++;
if(reptr.pos >= reptr.model) {
reptr.pos = 0;
}
var item = reptr.itemAt(reptr.pos).item;
blk.parent.imParent = false;
blk.parent = item;
blk.parent.imParent = true;
}
}
Button {
text: "Reparent relative to scene"
onClicked: {
reptr.pos ++;
if(reptr.pos >= reptr.model) {
reptr.pos = 0;
}
var item = reptr.itemAt(reptr.pos).item;
var coord = blk.mapToGlobal(blk.x, blk.y);
blk.parent.imParent = false;
blk.parent = item;
blk.parent.imParent = true;
coord = blk.mapFromGlobal(coord.x, coord.y);
blk.x = coord.x;
blk.y = coord.y;
}
}
}
}

How to know "y" property of an Item when it is created in a ListView

I use a ListView and I need to fill my Window with the last Item that was inserted inside it.
In this case, I set the ListView property: contentY with the y value of myItem
My problem is that when you insert a new Item, I don't know how to know the y value of the Item because it is not yet set.
This is what I try:
ScrollView {
id: scroll
ListView {
model: DelegateModel {
id: visualModel
model: myModel //Model is set in the cpp
delegate: Rectangle {
id: rectangleDelegate
Component.onCompleted: {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
console.log(rectangleDelegate.y ) //Not set yet (y = 0)
}
Button {
onClicked {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
console.log(rectangleDelegate.y ) //Now it is ok (y = rightValue)
}
}
}
}
}
}
How could I proceed ?
Thanks to #derM, here is a simple example that works:
ScrollView {
id: scroll
ListView {
model: DelegateModel {
id: visualModel
model: myModel //Model is set in the cpp
delegate: Rectangle {
id: rectangleDelegate
onYchanged: {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
}
}
}
}
}

Resources