How do I go directly from FullScreen to Maximized in a QML Window? - qml

Below is a small QML application. What I intended was for the application to start full screen, and on the Escape key, change it to maximized:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: topLevelWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
visibility: Window.FullScreen
Rectangle {
id: rect
anchors.fill: parent
color: "lightBlue"
focus: true
Keys.onPressed: {
if (event.key === Qt.Key_Escape) {
rect.color = "lightGreen"
topLevelWindow.visibility = Window.Maximized
}
}
}
}
What actually happens, though, is that it starts full screen as intended, but pressing Escape makes it windowed but not maximized. Pressing Escape a second time actually maximizes it.
Is there a way to do this without making the user hit Escape twice?

It looks like this is indeed a bug. Seems like it's been lingering for a while, but (some) newer versions may have fixed it?
In the meantime, it's relatively easy to add a hacky workaround:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: topLevelWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
visibility: Window.FullScreen
Rectangle {
id: rect
anchors.fill: parent
color: "lightBlue"
focus: true
Keys.onPressed: {
if (event.key === Qt.Key_Escape) {
rect.color = "lightGreen"
topLevelWindow.visibility = Window.Windowed
windowHackTimer.start()
}
}
}
Timer {
id: windowHackTimer
interval: 0
repeat: false
onTriggered: {
topLevelWindow.visibility = Window.Maximized
}
}
}
On the button press it becomes windowed, and then after it returns to the event loop (with a 0ms timer), it sets it to maximized. Just setting topLevelWindow.visibility multiple times in Keys.onPressed doesn't get the job done.

I think you should (1) make sure that the Escape key is accepted by your event handler (so that it is not passed down) and (2) schedule the outcome with Qt.callLater.
Keys.onPressed: {
if (event.key === Qt.Key_Escape) {
event.accepted = true;
Qt.callLater( greenRectAndMaximize );
}
}
function greenRectAndMaximize() {
rect.color = "lightGreen";
topLevelWindow.visibility = Window.Maximized;
}
The event.accept() is important to ensure that no other UI will handle the same event. That could be the cause of your problem. The Qt.callLater is important because it ensures that your action is added to the end of the windows messaging queue so that the UI/UX for the Escape key (and any other UI/UX events can complete) before we do our action.
Generally, whenever processing events, key presses, button clicks, and so forth, think about what needs to happen now and what needs to be queued for later.
In your case, you have a simple action, but, imagine something more complex such as a button click that triggers a page transition and a sort of 10000 records. The UI/UX is the user clicks the button and we want to see the button animate down and up fully before the action kicks off. If we did it during the event handler, the button may appear to be stuck in the down portion of the animation and the app will appear momentarily hang/crash. So, it is important to understand when we need to queue up actions.
Qt.callLater is one of the easiest ways to separate the UI/UX event and the user action and can improve the perceptual responsiveness of your application.

Related

Qt Quick Controls 2 TextArea `tabChangesFocus`, how to use Tab key to change focus, not type Tab character

QML TextArea from Qt Quick Controls 1.x (http://doc.qt.io/qt-5/qml-qtquick-controls-textarea.html) had a property called tabChangesFocus, which could be set to toggle the behaviour of the Tab key between two possible actions:
true: enter Tab character in the TextArea
false: move the focus to next item in the tab Chain
This property doesn't seem to exist for TextArea in Quick Controls 2.x (https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html).
The default is the true behaviour, but I would like the false behaviour (focus change).
Does anyone know a simple way to achieve the same effect for Quick Controls 2?
Another way is to use Item::nextItemInFocusChain(). This way, you don't need to know the next item in focus chain:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
id: window
width: 300
height: 300
visible: true
Column {
spacing: 20
TextArea {
id: textArea1
focus: true
text: "TextArea1"
Keys.onTabPressed: nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
}
TextArea {
id: textArea2
text: "TextArea2"
objectName: text
Keys.onTabPressed: nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
}
}
}
This should probably be made more convenient in the future, but you can setup tab navigation with QML KeyNavigation:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
id: window
width: 300
height: 300
visible: true
Column {
spacing: 20
TextArea {
id: textArea1
focus: true
text: "TextArea1"
KeyNavigation.tab: textArea2
KeyNavigation.backtab: textArea2
KeyNavigation.priority: KeyNavigation.BeforeItem
}
TextArea {
id: textArea2
text: "TextArea2"
KeyNavigation.tab: textArea1
KeyNavigation.backtab: textArea1
KeyNavigation.priority: KeyNavigation.BeforeItem
}
}
}

Why does my custom TabBar lose it's content children in this example?

I am trying to create a TabBar has preview images of the connected layout's children. However after adding several tabs (the exact number depends on the number of elements within the tabs) QML throws an error and the PreviewTabBar loses all its content children.
The following is a minimal working example:
My main.qml:
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
StackLayout {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
}
Timer {
interval: 50; running: true; repeat: true
onTriggered: addTab()
}
function addTab() {
console.log("add Tab")
var component = Qt.createComponent("qrc:/TabContent.qml")
if(component.status !== Component.Ready)
console.log("component not ready")
var item = component.createObject(swipeView)
tabBar.addTab(item)
tabBar.currentIndex = tabBar.contentChildren.length - 1
console.log("current index " + tabBar.currentIndex)
}
header: PreviewTabBar {
id: tabBar
currentIndex: swipeView.currentIndex
}
}
The PreviewTabBar.qml containing previews of the content:
import QtQuick 2.8
import QtQuick.Controls 2.1
TabBar {
signal closeCurrentTab
clip: true
background: Rectangle {
color: "white"
}
function addTab(imageSource) {
var component = Qt.createComponent("PreviewTabButton.qml")
if(component.status !== Component.Ready)
console.log("component not ready")
else {
var item = component.createObject()
item.setSource(imageSource)
addItem(item)
}
}
function closeTab() {
console.log("closeTab")
closeCurrentTab()
}
}
and last but not least the PreviewButton.qml using a ShaderEffectSource to render the preview:
import QtQuick 2.8
import QtQuick.Controls 2.1
TabButton {
height: 80
width: 140
function setSource(source) {
preview.sourceItem = source
}
contentItem: ShaderEffectSource {
id: preview
}
}
This example gets to about 80 tabs on my machine, after that the PreviewTabBar loses all its children (not so the StackLayout). However in the real life example with more complicated tab contents I only get up to around 8 tabs. What could I be doing wrong?
Here is the relevant part of the application output:
qml: current index 99
qml: add Tab
file:///usr/lib/qt/qml/QtQuick/Controls.2/TabButton.qml:65: TypeError: Cannot read property of null
qml: current index 100
qml: add Tab
qml: current index 1
I tried finishing the dynamic component creation in a callback as described here:
http://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html#creating-a-component-dynamically
however that brough no improvement.
Here is a link to the example project:
https://www.file-upload.net/download-12341284/tabtestshader.zip.html
The most probable cause is line 17 in PreviewTabBar.qml which reads:
var item = component.createObject()
As you have no parent set in the createObject()-function the GarbageCollector tends to run wild, and delete your object, even if it is still referenced.
Though not documented that way, you should always pass a parent object, to make sure it survives the GC.
A more stable way would be to generate the Tabs from a model, and add the according model entries in the addTab-functions.
As a little remark on the side: You create a new component everytime you call one of your addTab-functions. Why don't you declare it once like
Component {
id: myComp1
...
}
and create the objects from that?

Quit app after Toast message dissapears

I've found fantastic topic on creation of Qt/QML implementation of Android Toast. I am trying to upgrade it after Toast disappears some signal is emmited - I want to quit simple Qt/QML app. Here is ToastManager:
import QtQuick 2.7
Column
{
id: root
z: Infinity
spacing: 5
anchors.centerIn: parent
signal signalQuitApp;
property var toastComponent
function show(text,
duration)
{
var toast=toastComponent.createObject(root);
toast.selfDestroying=true;
toast.show(text,
duration);
signalQuitApp();
} // show
Component.onCompleted:
{
toastComponent=Qt.createComponent("Toast.qml");
} // Component.onCompleted
} // Column
The result is, if I click some button inside QML application, it quits before Toast animation is over. How do I emit signal AFTER Toast dissapears?
Assuming you are referring to the answer by #ayberk-Özgür you will need to modify Toast.qml to emit the signal when it is done.
id: root
signal toastFinished()
// ...
onRunningChanged:{
if (!running) {
root.toastFinished();
if (selftDestroying)
root.destroy();
}
}
Before you show the toast you connect to that signal
toast.toastFinished.connect(Qt.quit);
toast.show(text, duration);

How Can I Scroll the Images In QML Automatically?

I have a ListView Containing only Images. I assigned the orientation of ListView as horizontal Direction. How can I change, i.e. scroll, the images automatically with some time gap?
Use a Timer. When it is triggered, update the currentIndex of the ListView. This will scroll automatically with default animations. Finally, according to the documentation, positionViewAtIndex is
The correct way to bring an item into view is with positionViewAtIndex
Indeed the method provides a more fine-grained control over the appearance of Items via the PositionMode parameter. See the documentation for further details.
Minimal example:
import QtQuick 2.5
import QtQuick.Window 2.0
Window {
visible: true
width: 200
height: 15
ListView {
id: list
anchors.fill: parent
orientation: ListView.Horizontal
model: 10
delegate: Text {
width: 40
id: name
text: index
}
}
Timer {
interval: 500
repeat: true
running: true
onTriggered: {
//list.currentIndex += 1 // this...
//list.incrementCurrentIndex() // ...or this!
//list.positionViewAtIndex(list.currentIndex, ListView.Center)
}
}
}

dynamical reparentig in qml

I want to change dynamicaly type of qml Item without re-creation. In this example window transforms into popup window and question is how to transform it to qml Item.
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
Window {
id: myWindow
height: 300
width: 300
visible: true
MouseArea {
anchors.fill: parent
onDoubleClicked: myWindow.flags = Qt.Popup
}
}}
What are you trying to achieve?
You must understand that when you "transform" your Window into a popup Window, the actual type of your object does not change. You only set a flag, which happen to give your window a popup behavior. As to dynamically change the type of a QML object, I don't think it is even possible, and I don't see the point of it.
When I want to make a 'pupup', I use something like that
Rectangle{
id:picker
visible:false
function find_superparent( child_object) {
var fparent;
fparent=child_object.parent;
while(fparent.parent) fparent= fparent.parent;
return fparent;
}
Component.onCompleted: picker.parent=find_superparent(picker)
...
}
and, when I want to show the popup
picker.visible=true
I use this function (find_superparent) in dynamic component creation/destruction too...
this method works... I don't know if exists a better way ...