QML: monitoring mouse moving event in the "whole" window - qt

Hmm... maybe this task should be implemented in C++? I don't know...
I'm using QML to making a media-player like (e.g. QuickTime in OSX) interface, which can detect mouse event:
When mouse idle for a period, hide all control panels and show only VideoOutput. Or as soon as mouse out of the window, do the same things immediately.
So my requirements are:
monitor mouse move event within the whole window
don't block original mouse event on any other component.
Or more precisely, see pseudo code:
MouseArea {
anchors.fill: applicationWindow // <- This is wrong and will throw error
hoverEnabled: true
/* After reading the Qt doc, this property seems should be
placed in a MouseArea, which is under another MouseArea.
When child has mouse event, it propagate up to parent MouseArea.
So this should be also wrong. */
propagateComposedEvents: true
onClicked: mouse.accepted = false;
onPressed: mouse.accepted = false;
onReleased: mouse.accepted = false;
onDoubleClicked: mouse.accepted = false;
onPressAndHold: mouse.accepted = false;
onPositionChanged: {
console.log("moved!", mouse.x, mouse.y)
mouse.accepted = false
}
}

The Problem with QML is, that there are certain Event-Chains that you can either handle or leave it.
So if you want to handle the clicked-event in one MouseArea you can't propagate the pressed-event.
This however might be a sufficient solution:
Rectangle {
width: 50
height: 50
color: 'orchid'
MouseArea {
anchors.fill: parent
drag.target: parent
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onMouseXChanged: console.log(mouseX)
onPressed: {
mouse.accepted = false
}
}
You handle the mouse-movement in the MouseArea that lies uppon everything else, while not handling anything as long as a button is pressed.
If you need information about the MouseMovement even then, you can create a singleton signal you... ring... in every other MouseArea you use... or propagate the signals through every qml-file of yours.
So instead of writing: mouse.accepted = false in every MouseArea you write: mySingleton.mouseEventSignal(). You might even add the Item and the coordinates, so you can then map the coordinates to what ever you want...
This might be sufficient for a fast and dirty prototype. For the real thing, maybe you need to do it in C++ as Kevin Krammer proposed

Related

Combobox doesn't open popup if MouseArea is above it

I have a listview with a delegate that has MouseArea covering the whole of a delegate. In that MouseArea's onClick slot I specifically set
mouse.accepted = false
but the Combobox from QtQuick.Controls 1.4 that lives in that delegate still refuses to open its popup on clicks. I've tested that combobox should receive the click with:
ComboBox {
id: cbChapters
model: chapters
MouseArea {
anchors.fill: parent
onClicked: {
mouse.accepted = false
console.log("arrived")
}
}
}
And the click events do arrive into this inner mouse area, just not into the combobox itself it seems... what might be the problem?
Answering my own question:
Problem seems to be that MouseArea automatically accepts Pressed events and in the Combobox code itself there's this:
onPressed: {
if (!Settings.hasTouchScreen)
popup.toggleShow()
}
onClicked: {
if (Settings.hasTouchScreen)
popup.toggleShow()
}
So it seems like Clicked requires touchscreen to open the popup(which is not present on the desktop, obviously)
This leaves only Pressed to open the popup, but it's being suppressed at the uppermost MouseArea as it is not a composed event and propagateComposedEvents does nothing for it.
The solution could be :
1) to go through your mouse area chain and in each one of them set:
onPressed: {
mouse.accepted = false
}
2) call popup directly in "clicked" handler
ComboBox {
id: cbChapters
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: {
cbChapters.__popup.toggleShow()
}
}
}

Unable to get highlighting working on TextEdit

TL;DR: TextEdit paints highlighted text only when I click on it. Nothing helps
I have a ListView with a QAbstractListModel model with string properties.
Those string properties are being spellchecked and QSyntaxHighlighter is used to show spell errors. I create QSyntaxHighlighter descendant in Component.onCompleted of TextEdit. I double-checked highlighting get's executed with correct spell errors and setFormat() of Highlighter is executed with correct positions. The problem is that it draws text in red (invalidates) only when I click on the TextEdit itself.
TextEdit lives in a Flickable (to track cursor) and Flickable lives in a Rectangle (to have nice background and border). Binding to some signals and calling update() of TextEdit does not help.
After spellcheck finishes, I emit rehighlight() signal of created SyntaxHighlighter.
Rectangle {
id: descriptionRect
height: 30
border.width: descriptionTextInput.activeFocus ? 1 : 0
clip: true
Flickable {
id: descriptionFlick
contentWidth: descriptionTextInput.paintedWidth
contentHeight: descriptionTextInput.paintedHeight
anchors.fill: parent
interactive: false
flickableDirection: Flickable.HorizontalFlick
height: 30
clip: true
focus: false
function ensureVisible(r) {
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
}
TextEdit {
id: descriptionTextInput
width: descriptionFlick.width
height: descriptionFlick.height
text: description
onTextChanged: model.editdescription = text
Component.onCompleted: {
globalModel.initDescriptionHighlighting(index, descriptionTextInput.textDocument)
}
onCursorRectangleChanged: descriptionFlick.ensureVisible(cursorRectangle)
}
}
}
Here is a small sample of project with demonstration of how it does not work until you click on a text https://bitbucket.org/ribtoks/qt-highlighting-issue
Any ideas how I can solve this?
Just encountered this issue on 5.11.2 and found the following fix which allows updating of individual blocks without having to highlight/deselect the whole text area
rehighlightBlock(newBlock);
Q_EMIT document()->documentLayout()->updateBlock(newBlock);
The issue was probably caused by QTBUG-44765, fixed in Qt 5.5.
Given the low level of the bug, I don't think it is practically to work around it.
You can work around that by appending an empty string to the TextEdit when you're done with the syntax highlighting
TextEdit {
id: captionTextEdit
width: wrapperFlick.width
height: wrapperFlick.height
text: display
readOnly: true
Component.onCompleted: {
itemsModel.initHighlighter(index, captionTextEdit.textDocument)
}
Connections {
target: itemsModel
onUpdateTextEdit: {
console.log("Update element at index: " + indexToUpdate)
if (indexToUpdate == index)
{
console.log("Update me!")
captionTextEdit.append("")
}
}
}
onCursorRectangleChanged: wrapperFlick.ensureVisible(cursorRectangle)
}
where updateTextEdit(indexToUpdate) is a new signal your itemsModel has to emit.
itemsmodel.h
signals:
void updateTextEdit(int indexToUpdate);
itemsmodel.cpp
void ItemsModel::initHighlighter(int index, QQuickTextDocument *document) {
// Signal mapper could be avoided if lamda slot are available (Qt5 and C++11)
QSignalMapper* signalMapper = new QSignalMapper(this);
if (0 <= index && index < m_ItemsList.length()) {
SingleItem *item = m_ItemsList.at(index);
SpellCheckHighlighter *highlighter = new SpellCheckHighlighter(document->textDocument(), item);
QObject::connect(item, SIGNAL(spellCheckResultsReady()),
highlighter, SLOT(rehighlight()));
// TODO: Don't connect this slot for Qt 5.5+ to avoid performance overhead
QObject::connect(item, SIGNAL(spellCheckResultsReady()),
signalMapper, SLOT(map()));
signalMapper->setMapping(item, index);
}
connect(signalMapper, SIGNAL(mapped(int)),
this, SIGNAL(updateTextEdit(int)));
}
Full code is available here: https://bitbucket.org/swarta/rehighlighdemo/branch/workaround#diff

How to show a context menu on right click in Qt5.5 qml TreeView

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.

QtQuick 2 Pass function to elements

I have a QML element "Button" which has a MouseArea element inside.
What I want to do is to send a function to the item and then pass it to MouseArea:
Example:
Button{
id: smth
...
...
onClicked: console.log("Someone Clicked Me!")
}
And my element could be something like this:
Rectangle{
property var onClicked
...
MouseArea{
onClicked : parent.onClicked
}
}
I found out that I can pass the function like in native js without parentheses, but I think there is a way to implement it like MouseArea element does.
The usual way to handle this situation in QML is to let the Button emit a signal, which is then handled by the user of Button.
Button.qml:
Rectangle {
id: root
signal clicked
MouseArea {
anchors.fill: parent
onClicked: root.clicked();
}
}
Usage:
Button {
onClicked: console.log("Button was clicked!");
}

Limit QML PathView to 1 index swipe

I have created a sort of page switcher using pathview element which works fine but if you flick hard enough it flicks over a few pages. I want to know if there is a way I can limit my pathview element to only be able to change 1 index at a time?
Edit:
I don't want to know how I can create my own switcher thing, I already have the pathview one working smooth with the pages following my finger etc. all I want to know is how I can limit pathview to changing only 1 index at a time.
You can try to use MouseArea and incrementCurrentIndex/decrementCurrentIndex
Rectangle {
// ...
PathView {
id: path_view
anchors.fill: parent
interactive: false
// model, delegate, etc...
MouseArea {
anchors.fill: parent
property int x_pos
onPressed: {
x_pos = mouse.x
}
onReleased: {
if (x_pos > mouse.x) {
path_view.incrementCurrentIndex()
}
else {
path_view.decrementCurrentIndex()
}
}
}
}
}
It seems Qt 5 has this functionality so I will just leave it like this until then.

Resources