QML naming confussion - reference

I have a basic question about naming components in QML files. I have read that the top element should always get the id root and parent is always the reference to the next element above it.
I have a two qml files, one with a ListView and one with the listview delegate
Userlogon.qml
Item {
id: root
height: parent.height
width: parent.width
property var password: ['0', '1', '2', '3']
property int selectedField : mill.selectedIndex
property int selectedUser : 0
property string p_background: configuration.getColor(Colors.ContentBackground)
ColumnLayout { anchors.fill: parent
Rectangle { id: userlist; Layout.fillWidth: true; Layout.fillHeight: true; Layout.preferredHeight: 300; color: p_background
ColumnLayout { anchors.fill: parent
ListView { Layout.fillWidth: true; Layout.fillHeight: true
model: user.model
currentIndex: 1
onCurrentIndexChanged: { console.log("currentIndex changed") }
header: UserItemDelegate { p_index: -1; p_name: "Benutzeranmeldung"; p_icon: "password"; p_isHeader: true }
delegate: UserItemDelegate { p_index: index; p_name: name; p_icon: icon; p_isHeader: false }
spacing: 20
}
}
}
UserItemDelegate.qml
Item {
id: root
height: configuration.getSize(Sizes.ListItemHeight)
width: parent.width
property int p_index
property string p_name
property string p_icon
property bool p_isHeader
property bool p_isSelected: root.ListView.view.currentIndex == p_index
property string p_color: configuration.getColor(p_isHeader ? Colors.ListItemDisabled : (p_isSelected ? Colors.ListItemSelected : Colors.ListItemDefault))
Rectangle { anchors.fill: parent; Layout.fillWidth: true; Layout.fillHeight: true; color: p_color
RowLayout { anchors.verticalCenter: parent.verticalCenter;
Image { Layout.leftMargin: 10; sourceSize.height: root.height * 0.6; source: "image://iconprovider/" + p_icon }
Label { Layout.leftMargin: 10; text: p_name }
}
MouseArea{ enabled: !p_isHeader; anchors.fill: parent; onClicked: { root.ListView.view.currentIndex = p_index; } }
}
}
With root.Listview.view.currentIndex I can access the listview in the parent UserLogon.qml although root is the id of the current item?
And is it possible to access e.g. a timer defined in UserLogon.qml from the delegate. If so how would the referencing be?

First of all, you don't need to name every root object "root". I don't know where you read that, but you can use whatever name makes sense to you.
When you call root.ListView.view.currentIndex, the root in that case is the id of the delegate, not the list. QML's scoping rules guarantee that. The ListView.view attached property then allows you to reference back to the list without needing its id.
Your timer question is unclear. In general, yes you can have access to the ListView's properties from the delegate, but you should usually do it the other way around. The ListView should reference the delegate's properties. The reason is that you may have a need to use that delegate somewhere else and you don't necessarily want it tightly coupled with the ListView in that case. Here's an example:
MyList.qml
ListView {
id: contactList
Timer {
id: backgroundTimer
interval: 3000
running: true
}
model: 5
delegate: MyDelegate {
width: contactList.width
// ListView binds the delegate's 'timedOut' property to something
timedOut: backgroundTimer.running
}
}
MyDelegate.qml
Rectangle {
id: contact
property bool timedOut: false
height: 30
radius: height / 2
color: timedOut ? "red" : "blue"
}

Related

SwiftUI layout grows outside the bounds of the device when using .edgesIgnoringSafeArea()

Having an issue in SwiftUI where some Views are growing bigger vertically than the size of the device when using .edgesIgnoringSafeArea(.bottom). On an iPhone 11 Pro which is 812 pixels high I am seeing a view of size 846. I am using the Debug View Hierarchy to verify it. This has been tested on Xcode 11.4.1 and 11.1 and exists in both versions and probably all in between.
I have included sample code below.
I am pretty sure this is a SwiftUI bug, but was wondering if anyone has a workaround for it. I need the edgesIgnoringSafeArea(.bottom) code to draw the TabBar, and for the ProfileView() to extend to the bottom of the screen when I hide my custom tab bar.
struct ContentView: View {
var body: some View {
MainTabView()
}
}
struct MainTabView : View {
enum Item : CaseIterable {
case home
case resources
case profile
}
#State private var selected : Item = .home
var body: some View {
VStack(spacing: 0.0) {
ZStack {
HomeView()
.zIndex(selected == .home ? 1 : 0)
ResourcesView()
.zIndex(selected == .resources ? 1 : 0)
ProfileView()
.zIndex(selected == .profile ? 1 : 0)
}
// Code here for building and showing/hiding a Toolbar
// Basically just a HStack with a few buttons in it
}
.edgesIgnoringSafeArea(.bottom) // <- This causes the screen to jump to 846
}
}
struct ProfileView : View {
#State private var showQuestionnaireView = false
var body: some View {
NavigationView {
ZStack {
NavigationLink(destination: QuestionnaireView( showQuestionnaireView:$showQuestionnaireView),
isActive: $showQuestionnaireView) {
Text("Show Questionnaire View")
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
}
struct QuestionnaireView : View {
#Binding var showQuestionnaireView : Bool
var body: some View {
GeometryReader { screenGeometry in
ZStack {
Color.orange
VStack {
Text("Top")
Spacer()
Text("Bottom")
}
}
}
}
}
HomeView() and ResourcesView() are just copies of ProfileView() that do their own thing.
When you run it you will see a button, push the button and a hidden Navigation Stack View pushes on the QuestionnaireView, this view contains a VStack with two text fields, neither of which you will be able to see due to this issue. Understandably the top one is behind the notch, but the bottom one is off the bottom of the screen. In my real project this issue is rarely seen at runtime, but switching between dark mode and light mode shows it. In the above code there is no need to switch appearances.
EDIT: FB7677794 for anyone interested, have not received any updates from Apple since lodging it 3 weeks ago.
EDIT2: Added some more code to MainTabBar
Update: This is fixed in Xcode 12 Beta 2
After reading the updated question I have made some changes and tried to make a small demo. In this, I am using the same approach as before, put NavigationView in your main tab view and with this you don't have to hide and show every time you come or leave your main tab view.
import SwiftUI
struct ContentView: View {
var body: some View {
MainTabView()
}
}
struct MainTabView : View {
enum Item : CaseIterable {
case home
case resources
case profile
}
#State private var selected : Item = .home
var body: some View {
NavigationView {
VStack(spacing: 0.0) {
ZStack {
Group {
HomeView()
.zIndex(selected == .home ? 1 : 0)
ResourcesView()
.zIndex(selected == .resources ? 1 : 0)
ProfileView()
.zIndex(selected == .profile ? 1 : 0)
}
.frame(minWidth: .zero, maxWidth: .infinity, minHeight: .zero, maxHeight: .infinity)
.background(Color.white)
}
HStack {
Group {
Image(systemName: "house.fill")
.onTapGesture {
self.selected = .home
}
Spacer()
Image(systemName: "plus.app.fill")
.onTapGesture {
self.selected = .resources
}
Spacer()
Image(systemName: "questionmark.square.fill")
.onTapGesture {
self.selected = .profile
}
}
.padding(.horizontal, 30)
}
.frame(height: 40)
.foregroundColor(Color.white)
.background(Color.gray)
// Code here for building and showing/hiding a Toolbar
// Basically just a HStack with a few buttons in it
}
.edgesIgnoringSafeArea(.bottom)
} // <- This causes the screen to jump to 846
}
}
struct ProfileView : View {
#State private var showQuestionnaireView = false
var body: some View {
// NavigationView {
ZStack {
NavigationLink(destination: QuestionnaireView( showQuestionnaireView:$showQuestionnaireView),
isActive: $showQuestionnaireView) {
Text("Show Questionnaire View")
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
// }
}
}
struct QuestionnaireView : View {
#Binding var showQuestionnaireView : Bool
var body: some View {
GeometryReader { screenGeometry in
ZStack {
Color.orange
VStack {
Text("Top")
Spacer()
Text("Bottom")
}
}
.edgesIgnoringSafeArea(.bottom)
}
}
}
struct HomeView: View {
var body: some View {
NavigationLink(destination: SecondView()) {
Text("Home View")
}
}
}
struct ResourcesView: View {
var body: some View {
NavigationLink(destination: SecondView()) {
Text("Resources View")
}
}
}
struct SecondView: View {
var body: some View {
Text("Second view in navigation")
.background(Color.black)
.foregroundColor(.white)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewDevice(PreviewDevice(rawValue: "iPhone 11"))
}
}
It is due to undefined size for NavigationView. When you add your custom tab bar component, as in example below, that limits bottom area, the NavigationView will layout correctly.
Tested with Xcode 11.4 / iOS 13.4
struct MainTabView : View {
var body: some View {
VStack(spacing: 0.0) {
ZStack {
Color(.cyan)
ProfileView() // << this injects NavigationView
}
HStack { // custom tab bar
Button(action: {}) { Image(systemName: "1.circle").padding() }
Button(action: {}) { Image(systemName: "2.circle").padding() }
Button(action: {}) { Image(systemName: "3.circle").padding() }
}.padding(.bottom)
}
.edgesIgnoringSafeArea(.bottom) // works !!
}
}

force button enable change before function finish

I have a qml image button. If button is pressed then an long operation is start at C++ side. I want button is disable when click on it , them enable it after function is finish.
Image {
id:searchimgbtn
source: "/Images/Search.png"
height: parent.height
width: dp(32)
fillMode: Image.PreserveAspectFit
enabled: isvalF
opacity: isvalF ? 1 :0.1
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
disablesearchbutton();
getcbsvalue();
}
onPressed:{
searchimgbtn.scale=1.08
// searchimgbtn.opacity=0.3
// opacity: enabled ? 1 :0.1
}
onReleased: {
searchimgbtn.scale=1
// searchimgbtn.opacity=1
// opacity: enabled ? 1 :0.1
}
}
}
You need to branch your long running job into another process/thread, so that the UI-Process is not blocked and can render.
Look into QProcess or QThread, I'm using it to ping stuff.
const QString PING_PATH = "C:/Windows/System32/ping.exe";
QStringList pingArguments << "192.168.1.1";
QProcess *pingProcess = new QProcess(this);
pingProcess->startDetached(PING_PATH, pingArguments);

QtQuick 2 - Make custom palette object and throw it as property to another custom widget (for assigning color properties)

I trying to make custom button and few other elements styled as KDE 5 'Breeze' theme. I considered to make separated palette object (called BreezePalette.qml that contains a lot of readonly color properties) for all of this widgets (because I do not want them to be styled in any other way, that's thy they called Breeze). The main concept is to make palette as property of widgets and create one palette in main.qml where I can change property theme to light or dark. It looks to me rational, because I planning only include all subset of .qml files into project, without any other additional files to Qt itself (that making it portable and easy to deploy). Here is that I have, can someone let me know how can I forward palete as a property?
main.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
menuBar: MenuBar{
Menu{
title: "File"
MenuItem{
text: "Exit"
onTriggered: Qt.quit()
}
}
}
BreezeButton{
x: 106
y: 82
palette: brPalette
onClicked: {
Qt.quit()
}
caption: "Button"
}
BreezePalette{
id: brPalette
theme: "light"
}
}
BreezePalette.qml
import QtQuick 2.2
QtObject {
id: palette
property string theme: "light"
readonly property color base: if (theme == "light"){
"#eff0f1"
} else if (theme == "dark"){
"#31363b"
}
readonly property color focus: "#3daee9"
readonly property color buttonText: if (theme == "light"){
"#31363b"
} else if (theme == "dark"){
"#eff0f1"
}
}
BreezeButton.qml
import QtQuick 2.2
import QtQuick.Window 2.0
import QtQuick.Layouts 1.1
Item {
id: root
implicitHeight: bodyText.font.pixelSize + 32
implicitWidth: bodyText.width + 32
property string caption: "Button"
property string iconSource
property int fontSize: 18
//I've tried to throw BreezePalette as a property to BreezeButton, but looks like my skills ended there (I have no any experience with js or qml before. I started learn it only few weeks)
property BreezePalette palette
signal clicked
Rectangle {
id: body
border {
width: 1
color: "#808e8e"
}
anchors{
fill: parent
}
gradient: Gradient {
id: bodyGradient
GradientStop { position: 0.4; color: "#4c4c4c" }
GradientStop { position: 0.9; color: "#31363b" }
}
MouseArea{
id: bodyMouseArea
z: bodyText.z + 1
anchors {
fill: parent
}
hoverEnabled: true
onEntered: {
body.border.color = "#3daee9"
}
onExited: {
body.border.color = "#7f8c8d"
}
onPressed: {
body.color = "#3daee9" // this one works, but I need to switching theme as you can see n `BreezePalette.qml`
//This one not working as expected, but seeing my properties as I need
//body.color = palette.focus
body.gradient = null
}
onReleased: {
body.color = "#4d4d4d"
body.gradient = bodyGradient
}
onClicked: {
root.clicked()
}
}
Text {
id: bodyText
anchors {
verticalCenter: body.verticalCenter
horizontalCenter: body.horizontalCenter
}
font.pointSize: fontSize
color: "#fcfcfc"
text: caption
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
}
Since stackexchange designed for sharing knowledge (or maybe even for ask for something you don't know well) I see it's rational to post it there because I need knowledge of experts. If you have any other point of view regarding this question I'll be glad to hear that. Appreciated any help.
Thanks
Svyatoslav
UPDATE:
Just found an answer, this code snippet working as well
property BreezePalette palette: BreezePalette
So, my second answer is - is that good to user this method? It's provide me thing I need, exactly as was expected.
Quite a late answer, but there is a module to have breeze theme.
qml-module-qtquick-controls-styles-breeze

QML toggle PropertyChanges onclick

I try to toggle my navigation with a toggle function. I want to change "x" position.
So here is what i got so far. But don't work. I try to use a toggle function to chnage state on click. I set two different state one that the navigation is visible and one that the navigation is hidden.
I get this error "ReferenceError: toggle is not defined"
Item {
id: toggleswitch
width: 200
height: 200
property bool on: false
function toggle() {
if (toggleswitch.state == "on")
toggleswitch.state = "off";
else
toggleswitch.state = "on";
}
Rectangle {
id: open
width: parent.width
height: 35
color: "#33000000"
Text {
anchors.centerIn: parent
text: "open"
color: "white"
font.family: "Helvetica"
font.pixelSize: 25
}
MouseArea { anchors.fill: parent; onClicked: toggle() }
}
states: [
State {
name: "on"
PropertyChanges { target: navigation; x: 0 }
PropertyChanges { target: toggleswitch; on: true }
},
State {
name: "off"
PropertyChanges { target: navigation; x: -300 }
PropertyChanges { target: toggleswitch; on: false }
}
]
}
Some small slider example:
import QtQuick 2.2
Rectangle {
width: 360
height: 360
Rectangle {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
id: slider
state: "close"
states: [
State {
name: "close"
PropertyChanges {
target: slider
width: 50
}
},
State {
name: "open"
PropertyChanges {
target: slider
width: 360
}
}
]
transitions: [
Transition {
NumberAnimation {
target: slider
property: "width"
duration: 500
easing.type: Easing.InOutBack
}
}
]
color: "green"
}
MouseArea {
anchors.fill: parent
onClicked: {
if (slider.state == "close")
slider.state = "open";
else
slider.state = "close";
}
}
}
transitions is optional here
You can say to QML which object is your function.
Item {
id: toggleswitch
width: 200
height: 200
state: "off" //INIT YOUR STATE !!
property bool on: false
function toggle() {
if (toggleswitch.state == "on")
toggleswitch.state = "off";
else
toggleswitch.state = "on";
}
Rectangle {
id: open
width: parent.width
height: 35
color: "#33000000"
Text {
anchors.centerIn: parent
text: "open"
color: "white"
font.family: "Helvetica"
font.pixelSize: 25
}
MouseArea { anchors.fill: parent; onClicked: toggleswitch.toggle() } //here
}
states: [
State {
name: "on"
PropertyChanges { target: navigation; x: 0 }
PropertyChanges { target: toggleswitch; on: true }
},
State {
name: "off"
PropertyChanges { target: navigation; x: -300 }
PropertyChanges { target: toggleswitch; on: false }
}
]
What I would do here is not manipulate the state directly but toggle on the property directly, and bind the states to that property.
To me it feels more readable, semantical and reduce the coupling between the object and its visual states.
This also has the advantage of having states always coherent with the on property and provides a better abstraction. When using this component you can freely change the on property programmatically and the component display will update accordingly.
That's what I would probably end up with :
Item {
id: toggleswitch
width: 200
height: 200
property bool on: false
function toggle() {
on = !on //simpler toggle function
}
Rectangle {
id: open
width: parent.width
height: 35
color: "#33000000"
Text {
anchors.centerIn: parent
text: "open"
color: "white"
font.family: "Helvetica"
font.pixelSize: 25
}
MouseArea { anchors.fill: parent; onClicked: toggleswitch.toggle() }
}
states: [
State {
name: "on"
when: toggleswith.on
PropertyChanges { target: navigation; x: 0 }
},
State {
name: "off"
when: !toggleswith.on
PropertyChanges { target: navigation; x: -300 }
}
]

Control z-index in Fabric.js

In fabricjs, I want to create a scene in which the object under the mouse rises to the top of the scene in z-index, then once the mouse leaves that object, it goes back to the z-index where it came from. One cannot set object.zindex (which would be nice). Instead, I'm using a placeholder object which is put into the object list at the old position, and then the old object is put back in the position where it was in the list using canvas.insertAt. However this is not working.
See http://jsfiddle.net/rFSEV/ for the status of this.
var canvasS = new fabric.Canvas('canvasS', { renderOnAddition: false, hoverCursor: 'pointer', selection: false });
var bars = {}; //storage for bars (bar number indexed by group object)
var selectedBar = null; //selected bar (group object)
var placeholder = new fabric.Text("XXXXX", { fontSize: 12 });
//pass null or a bar
function selectBar(bar) {
if (selectedBar) {
//remove the old topmost bar and put it back in the right zindex
//PROBLEM: It doesn't go back; it stays at the same zindex
selectedBar.remove();
canvasS.insertAt(selectedBar, selectedBar.XZIndex, true);
selectedBar = null;
}
if (bar) {
//put a placeholder object ("XXX" for now) in the position
//where the bar was, and put the bar in the top position
//so it shows topmost
selectedBar = bar;
canvasS.insertAt(placeholder, selectedBar.XZIndex, true);
canvasS.add(bar);
canvasS.renderAll();
}
}
canvasS.on({
'mouse:move': function(e) {
//hook up dynamic zorder
if (!e.target) return;
if (bars[e.target])
selectBar(e.target);
else
selectBar(null);
},
});
var objcount = canvasS.getObjects().length;
//create bars
for (var i = 0; i < 20; ++i) {
var rect = new fabric.Rect({
left: 0,
top: 0,
rx: 3,
ry: 3,
stroke: 'red',
width: 200,
height: 25
});
rect.setGradientFill({
x1: 0,
y1: 0,
x2: 0,
y2: rect.height,
colorStops: {
0: '#080',
1: '#fff'
}
});
var text = new fabric.Text("Bar number " + (i+1), {
fontSize: 12
});
var group = new fabric.Group([ rect, text ], {
left: i + 101,
top: i * 4 + 26
});
group.hasControls = group.hasBorders = false;
//our properties (not part of fabric)
group.XBar = rect;
group.XZIndex = objcount++;
canvasS.add(group);
bars[group] = i;
}
canvasS.renderAll();
Since fabric.js version 1.1.4 a new method for zIndex manipulation is available:
canvas.moveTo(object, index);
object.moveTo(index);
I think this is helpful for your use case. I've updated your jsfiddle - i hope this is what you want:
jsfiddle
Also make sure you change z-index AFTER adding object to canvas.
So code will looks like:
canvas.add(object);
canvas.moveTo(object, index);
Otherwise fabricjs don`t care about z-indexes you setup.
After I added a line object, I was make the line appear under the object using:
canvas.add(line);
canvas.sendToBack(line);
Other options are
canvas.sendBackwards
canvas.sendToBack
canvas.bringForward
canvas.bringToFront
see: https://github.com/fabricjs/fabric.js/issues/135
You can modify your _chooseObjectsToRender method to have the following change at the end of it, and you'll be able to achieve css-style zIndexing.
objsToRender = objsToRender.sort(function(a, b) {
var sortValue = 0, az = a.zIndex || 0, bz = b.zIndex || 0;
if (az < bz) {
sortValue = -1;
}
else if (az > bz) {
sortValue = 1;
}
return sortValue;
});
https://github.com/fabricjs/fabric.js/pull/5088/files
You can use these two functions to get z-index of a fabric object and modify an object's z-index, since there is not specific method to modify z-index by object index :
fabric.Object.prototype.getZIndex = function() {
return this.canvas.getObjects().indexOf(this);
}
fabric.Canvas.prototype.moveToLayer = function(object,position) {
while(object.getZIndex() > position) {
this.sendBackwards(object);
}
}

Resources