Suppose I have a QML Window that contains a bunch of different controls:
Window {
...
TextEdit { ... }
CheckBox { ... }
Button { ... }
etc
}
Now I want my window to change "mode" on a certain event, and display a completely different set of controls.
Imagine its part of a multi-screen form. The current state is the first page of the form. When the user clicks "Next" button it goes to page 2 of the form. I want to add a new set of controls that represents page 2.
What is the correct way to organize this in QtQuick / QML ?
A common way to do that is with a StackView. The organization would be something like this:
Window {
StackView {
id: stackView
initialItem: page1
}
Item {
id: footerItem
// Maybe add other buttons here too
Button {
id: nextBtn
text: "Next"
onClicked: {
stackView.push(page2);
}
}
}
Component {
id: page1
Page1 {
// Define this in separate Page1.qml file
// This is where your page 1 controls go.
}
}
Component {
id: page2
Page2 {
// Define this in separate Page2.qml file
// This is where your page 2 controls go.
}
}
}
I would probably implement a mode changing view or form with "Back" and "Next" buttons using StackLayout. The StackLayout class provides a stack of items where only one item is visible at a time. You go to the next or previous mode by updating currentIndex.
StackLayout {
id: layout
anchors.fill: parent
currentIndex: 1
Rectangle {
color: 'teal'
implicitWidth: 200
implicitHeight: 200
}
Rectangle {
color: 'plum'
implicitWidth: 300
implicitHeight: 200
}
}
Related
I have StackView in main.qml.I pushed menu.qml file from main.qml using stackview. I'm trying to access stackview in menu.qml file to open new item. Is there a way with which we can push component/items with properties using stackview? My components are basically.qml files for different views
ApplicationWindow {
id: settingsWindow
StackView {
id: stack
initialItem: view
Component {
id: view
MouseArea {
onClicked: stack.push(view)
}
}
}
Button{
id: button1
onClicked: {
stack.pop(StackView.Immediate)
stack.push (Qt.resolvedUrl("menu.qml"))
}
}
}
menu.qml
Item {
Button{
id: button1
onclicked : { stack.push (Qt.resolvedUrl("new.qml")) }
}
}
Assuming you mean you want to access the StackView object from withing pages you pushed on it.
StackView has an attached property, which lets you obtain a reference to the view that owns the page.
Long story short, in Menu.qml you can do:
Item {
id: root
Button {
id: button1
onClicked: { root.StackView.view.push(Qt.resolvedUrl("new.qml")) }
}
}
https://doc.qt.io/qt-5/qml-qtquick-controls2-stackview.html#view-attached-prop
Finally ended up creating each page in StackView as a component property and then pushing each of them using a signal. Added the signal to every page and connected it to main page where stackview existed . This answer helped
https://stackoverflow.com/a/45354861/11288640
I have my simplified application window as the following. It opens a menu when clicked on it and while the menu is open, it is immediately closed when I click anywhere outside of it. I want to learn which specific event or signal causes context menus to close when clicked on their parent? I need to mimic this behavior manually.
ApplicationWindow
{
MouseArea
{
anchors.fill: parent
onClicked:
{
contextMenu.popup()
}
}
Menu
{
id: contextMenu
MenuItem
{
text: "File"
}
}
}
I am using Qt 5.9.3. I have a QML Item like so:
Item {
id: root
width : 400
height: 700
}
How do I track when the Item was first time visible on screen of my application window? onVisibilityChanged?
How do I do track the same for a MyQuickItem which is something like this:
// C++ definition
class MyQuickItem : public QQuickitem {
}
// QML side
MyQuickItem {
id: some_id
}
I guess the complete code suppose like:
ApplicationWindow{
visible: true
onVisibilityChanged: {
console.log("ApplicationWindow is ", visibility)
}
Component.onCompleted: {
console.log("ApplicationWindow is onCompleted")
}
Item {
id: root
width : 400
height: 700
onVisibleChanged: {
console.log("Item is ", visible)
}
Component.onCompleted: {
console.log("Item is onCompleted")
}
}
}
Obviously the onVisibilityChanged of ApplicationWindow will trigger when the visibility property is changed, so it triggers when you minimize and reshow the window. As you mention the first time visible, so I think you are looking for Component.onCompleted.
I'm following this tutorial (without the flickable content in each entry) for Qt 4.8 while using Qt 5.7 with QtQuick 2.0. The way the ListView there works is as follows:
User clicks on item in list
Alternative (detailed) view of item is displayed
User has to click on Close button in detailed view to reset the state of entry to its default compact view.
This leads to a clutter where at some point if the user clicks on all items in which case all will be shown in their full view. Having the user click on the Close button every time he/she opens a detailed view also is (omho) not that handy.
I've altered the entry to close when the user clicks on the view. I'm also trying to prevent this clutter and achieve a more (omho) flowing behaviour:
User clicks on item in list
Alternative view of item is displayed
User clicks on detailed view to reset state of entry to its default compact view OR
User clicks on another entry and all currently in detailed view entries are reset to their compact view
Currently I'm looping through my ListView's contentItem.children[loop_index] and setting the state to "" ("Details" = show detailed view | "" = show compact view). Due to the way ListView works (loading/unloading delegates on demand) this is quite unreliable and I often get an undefined reference when I try to access the state of other delegates. The following MouseArea, which I'm using to do all that, is part of every delegate:
// state is a QML `State` that is bound to the delegate (see below for the details on it)
MouseArea {
anchors.fill: background
onClicked: {
// Iterate through all other entries and close them
for (var entry = 0; entry < listView.count; ++entry) {
if(listView.contentItem.children[entry] !== gestureEntry) {
console.log("Hide other element");
listView.contentItem.children[entry].state = ""; // IT FAILS HERE (SOMETIMES)
}
}
// Change view of current entry
if(gestureEntry.state === "Details") {
gestureEntry.state = "";
console.log("Hiding details")
}
else {
gestureEntry.state = "Details";
console.log("Showing details");
}
}
}
with state being a delegate's state:
states: State {
name: "Details"
PropertyChanges { target: background; color: "white" }
PropertyChanges { target: gestureImage; width: 130; height: 130 } // Make picture bigger
PropertyChanges { target: gestureEntry; detailsOpacity: 1; x: 0; y: 0 } // Make details visible
PropertyChanges { target: gestureEntry; height: listView.height } // Fill the entire list area with the detailed view
}
I'm thinking that the state information can be stored inside the ListModel itself making it possible to iterate through the model's contents (which are always there unlike the contents of the delegates) however I don't know how to automatically update my list (and the currently visible/invisible delegates) when an entry changes in the model. From what I've found so far it seems not possible to do that since the ListView doesn't actively monitor its ListModel.
Is this indeed the case? If yes, then is it possible to go around this problem in a different way?
Why don't you use the currentIndex property of your ListView?
Just modify your delegate like this:
Item {
id: gestureEntry
...
state: ListView.isCurrentItem?"Details":""
...
MouseArea {
anchors.fill: background
onClicked: {
if(listView.currentIndex == index)
listView.currentIndex = -1
else
listView.currentIndex = index
}
}
}
EDIT:
The only issue with the solution above is that - upon loading - an entry in the ListView is preselected which automatically triggers the detailed view of that entry. In order to avoid that the following needs to be added to listView:
Component.onCompleted: {
listView.currentIndex = -1;
}
This ensures that no entry will be preselected.
guess it is an issue because you stored a state in your delegate. You should not do this as described in the delegate-property (Link), because the delegates get reused when they get out of view.
At least you should use a when: ListView.isCurrentItem in the State and depend on a value of the ListView. So only your current delegate is maximized. Then in the MouseArea only set `ListView.view.currentIndex = index'. Don't change the state manually in the function!
I ran in the same trouble, removed the states completely and just used the attached property ListView.isCurrentItem. But binding the state to a Value from the ListView should also work, because it's not stored in the delegate.
Minimal example:
import QtQuick 2.0
Item {
width: 800
height: 600
ListView {
id: view
anchors.fill: parent
model: 3
spacing: 5
currentIndex: -1
delegate: Rectangle {
id: delegate
color: ListView.isCurrentItem ? "lightblue" : "green" // directly change properties depending on isCurrentItem
height: 100
width: 100
states: State {
name: "maximized"
when: delegate.ListView.isCurrentItem // bind to isCurrentItem to set the state
PropertyChanges {
target: delegate
height: 200
}
}
MouseArea {
anchors.fill: parent
//onClicked: delegate.ListView.view.currentIndex = model.index // if only selection is wanted
onClicked: {
//console.debug("click");
if (delegate.ListView.isCurrentItem)
{
delegate.ListView.view.currentIndex = -1;
}
else
{
delegate.ListView.view.currentIndex = model.index;
}
}
}
Text {
anchors.centerIn: parent
text: index
}
}
Text {
text: "CurrentIndex: " + parent.currentIndex
}
}
}
I want to show a context menu when right-clicking on Qt5.5 qml TreeView item, but it has clicked signal. How to show a context menu on right click?
TreeView {
id: tree_view
anchors.fill: parent
model: tree_model
headerVisible: false
backgroundVisible: false
TableViewColumn {
role: "display"
}
onClicked: {
console.log("clicked", index)
}
onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index)
}
It's actually quite easy, you just need a MouseArea configured to accept only right click events, and it won't interfere with the mouse handling performed by the TreeView itself:
TreeView {
id: tree_view
anchors.fill: parent
model: tree_model
TableViewColumn {
role: "display"
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
var index = parent.indexAt(mouse.x, mouse.y)
if (index.valid) {
console.log("show context menu for row: " + index.row)
}
}
}
}
Can you simply define your menu somewhere and use the popup method to show it? That method open the menu near to the mouse cursor, so to the right position.
Of course, you have to define your itemDelegate as well and let the event flows out of your item if needed (do not consume it).
The documentation for the clicked signal of a TreeView explicitly refers to the item delegate to consume those events, so I guess this is the intended approach.