Using a slider under another mouse area QML / QT - qml

I am designing an UI interface in Qt where one of the pages can swipe between views. I have defined a SwipeArea region in the whole screen (which is a MouseArea I defined in another QML file), to be able to swipe between pages, with the property propagateComposedEvents set to true so that I can swipe between pages, and still being able to click on all buttons.
The problem comes when using a Slider. Apparently it can't get these mouse events, and I can't interact with it because when I click on the screen it starts the swiping handler. Any idea of how can I solve this?
This is where I define the SwipeArea.qml code:
import QtQuick 2.0
MouseArea {
property point origin
property bool ready: false
signal move(int x, int y)
signal swipe(string direction)
propagateComposedEvents: true
onPressed: {
drag.axis = Drag.XAndYAxis
origin = Qt.point(mouse.x, mouse.y)
}
onPositionChanged: {
switch (drag.axis) {
case Drag.XAndYAxis:
if (Math.abs(mouse.x - origin.x) > 16) {
drag.axis = Drag.XAxis
}
else if (Math.abs(mouse.y - origin.y) > 16) {
drag.axis = Drag.YAxis
}
break
case Drag.XAxis:
move(mouse.x - origin.x, 0)
break
case Drag.YAxis:
move(0, mouse.y - origin.y)
break
}
}
onReleased: {
switch (drag.axis) {
case Drag.XAndYAxis:
canceled(mouse)
break
case Drag.XAxis:
swipe(mouse.x - origin.x < 0 ? "left" : "right")
break
case Drag.YAxis:
swipe(mouse.y - origin.y < 0 ? "up" : "down")
break
}
}
}
And here is the main.qml where I use it:
Item {
id: content
width: root.width * itemDataLength
height:parent.height
property double k: (content.width - root.width) / (img.width - root.width)
Repeater {
id:repeater
model: itemDataLength
width:parent.width
height:parent.height
Loader{
id:loader
width:parent.width / 2
x: root.width * index
height:parent.height
source:loaderItems[index]
}
}
}
SwipeArea {
id: mouse
anchors.fill: parent
onMove: {
content.x = (-root.width * currentIndex) + x
}
onSwipe: {
switch (direction) {
case "left":
if (currentIndex === itemDataLength - 1) {
currentIndexChanged()
}
else {
currentIndex++
}
break
case "right":
if (currentIndex === 0) {
currentIndexChanged()
}
else {
currentIndex--
}
break
}
}
onCanceled: {
currentIndexChanged()
}
}
Maybe important to add, my slider code:
Slider {
id: slider
anchors.centerIn: parent
updateValueWhileDragging:true
activeFocusOnPress:true
style: SliderStyle {
id:sliderStyle
//Slider handler
handle: Rectangle {
id:handler
width: 22
height: 44
radius:2
antialiasing: true
Image{
source:"icons/slidercenter.png"
anchors.centerIn: parent
}
}
//Slider Groove
groove: Item {
id:slidergroove
implicitHeight: 50
implicitWidth: temperaturePage.width -50
}
}
}
I appreciate any help or opinion, and thank you in advance!

Related

(How) can i access relative position of qml element to main window

I have a qml element and want to show a (own) tooltip element as a new window right above this element. for this i need the absolute screen position to place the new window (AFAIK).
i got as far that the regular approach is to use "mapToItem" to get the relative position, but i cannot get to the "main window" - because the element in question is located within a "Loader" (which in this case is again located in another Loader).
So my question is: Is it possible to access the mainWindow from inside the dynamically loaded component, or is there maybe another easier way to anchor a new (tooltip) window right above an element ?
EDIT
mapToGlobal would probably work too, but i have to use qt 5.6.
i finally got it to work by setting the main window as a context property in c++:
this->qmlEngine->rootContext()->setContextProperty("mainWindow", this->root);
and in qml i can then access the main window position (on screen) and add the relative position the item has to the shown window like that:
tooltipWindow.setX(mainWindow.x +item1.mapToItem(item2,0,0).x )
The Window item has contentItem especially for that
[read-only] contentItem : Item
The invisible root item of the scene.
So you can refer to Window.contentItem as if it was Window:
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
id: mainWindow
visible: true
width: 600
height: 300
Component {
id: testElement
Rectangle {
id: rect
width: 100
height: 100
color: "orange"
border { width: 1; color: "#999" }
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: tooltip.show(true);
onExited: tooltip.show(false);
onPositionChanged: tooltip.setPosition(mapToItem(mainWindow.contentItem,mouse.x, mouse.y));
}
}
}
Item {
x: 40
y: 50
Item {
x: 80
y: 60
Loader {
sourceComponent: testElement
}
}
}
Rectangle {
id: tooltip
visible: false
width: 100
height: 20
color: "lightgreen"
border { width: 1; color: "#999" }
Text {
anchors.centerIn: parent
text: "I'm here"
}
function show(isShow) {
tooltip.visible = isShow;
}
function setPosition(point) {
tooltip.x = point.x - tooltip.width / 2;
tooltip.y = point.y - tooltip.height;
}
}
}
As for me I would reparent tooltip Item to hovered item itself at MouseArea.onEntered and so you can avoid position recalculation etc.:
onEntered: tooltip.show(true, rect);
onExited: tooltip.show(false);
onPositionChanged: tooltip.setPosition(mouse.x, mouse.y);
...
function show(isShow, obj) {
obj = (typeof obj !== 'undefined' ? obj : null);
if(obj !== null) {
tooltip.parent = obj;
}
tooltip.visible = isShow;
}
function setPosition(x, y) {
tooltip.x = x - tooltip.width / 2;
tooltip.y = y - tooltip.height;
}

QML Dialog is broken?

I have this code:
import QtQuick 2.3
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
Dialog {
standardButtons: StandardButton.Ok | StandardButton.Cancel
width: layout.implicitWidth
height: layout.implicitHeight
RowLayout {
id: layout
anchors.fill: parent
Item {
width: 10
height: 1
}
GridLayout {
columns: 2
rowSpacing: 10
Layout.fillHeight: true
Layout.fillWidth: true
Text {
text: "Hello world? "
}
Text {
text: "Hello world!"
}
Text {
text: "Goodbye world? "
}
Text {
text: "Goodbye world!"
}
}
Item {
width: 10
height: 1
}
}
}
When you run it it looks like this, and the dialog can be resized to any size. Also the RowLayout actually doesn't fill its parent as you can see.
How can I make it so that the dialog can't be resized below the minimum size of the layout, and so that the layout fills the dialog?
Unfortunately this is a bug in Qt. Currently the documentation is misleading and Dialog does not size itself correctly to the contents. Consider this working example, which I based on the DefaultFontDialog:
AbstractDialog {
title: "Hello"
id: root
// standardButtons: StandardButton.Ok | StandardButton.Cancel
modality: Qt.NonModal
Rectangle {
id: content
implicitWidth: mainLayout.implicitWidth + outerSpacing * 2
implicitHeight: mainLayout.implicitHeight + outerSpacing * 2
property real spacing: 6
property real outerSpacing: 12
color: "white"
GridLayout {
id: mainLayout
anchors { fill: parent; margins: content.outerSpacing }
rowSpacing: content.spacing
columnSpacing: content.spacing
columns: 5
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
}
}
}
This works exactly as expected, though of course you don't get the buttons.
If you just change it to a Dialog and uncomment the standardButtons, then it stops working - you can resize the dialog to clip its contents (width-wise at least), and the contents do not expand to the dialog size.
The reason for the minimum width not working becomes clear when we look at the source code for Dialog (in qtquickcontrols/src/dialogs/DefaultDialogWrapper.qml):
AbstractDialog {
id: root
default property alias data: defaultContentItem.data
onVisibilityChanged: if (visible && contentItem) contentItem.forceActiveFocus()
Rectangle {
id: content
property real spacing: 6
property real outerSpacing: 12
property real buttonsRowImplicitWidth: minimumWidth
property bool buttonsInSingleRow: defaultContentItem.width >= buttonsRowImplicitWidth
property real minimumHeight: implicitHeight
property real minimumWidth: Screen.pixelDensity * 50
implicitHeight: defaultContentItem.implicitHeight + spacing + outerSpacing * 2 + buttonsRight.implicitHeight
implicitWidth: Math.min(root.__maximumDimension, Math.max(
defaultContentItem.implicitWidth, buttonsRowImplicitWidth, Screen.pixelDensity * 50) + outerSpacing * 2);
minimumWidth is hardcoded to Screen.pixelDensity * 50!! There was never any hope that it would match the dialog contents. minimumHeight does work better (though not perfect, I believe because the spacing isn't considered).
I'm not sure why the defaultContentItem does not expand correctly, but anyway. It looks like the only solution at the moment is to use AbstractDialog and implement the buttons and accepted()/rejected()/etc. signals yourself. Bit of a pain.
Edit / Solution
I did some further investigation.
The reason the defaultContentItem doesn't expand is because it's bottom anchor isn't tied to the top of the button row:
Item {
id: defaultContentItem
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: content.outerSpacing
}
implicitHeight: childrenRect.height
}
Minimum sizes just don't work that well with anchor-based layouts. They do with GridLayout-based layouts.
Unfortunately childrenRect has no implicitWidth/Height so we have to actually have the child items go into a ColumnLayout rather than be the ColumnLayout.
...
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
// A Dialog that resizes properly. The defualt dialog doesn't work very well for this purpose.
AbstractDialog {
id: root
default property alias data: defaultContentItem.data
onVisibilityChanged: if (visible && contentItem) contentItem.forceActiveFocus()
Rectangle {
id: content
property real spacing: 6
property real outerSpacing: 12
property real buttonsRowImplicitWidth: minimumWidth
property bool buttonsInSingleRow: defaultContentItem.width >= buttonsRowImplicitWidth
property real minimumHeight: implicitHeight
property real minimumWidth: implicitWidth // Don't hard-code this.
implicitWidth: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 10, mainLayout.implicitWidth + outerSpacing * 2))
implicitHeight: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 10, mainLayout.implicitHeight + outerSpacing * 2))
color: palette.window
Keys.onPressed: {
event.accepted = true
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
reject()
break
case Qt.Key_Enter:
case Qt.Key_Return:
accept()
break
default:
event.accepted = false
}
}
SystemPalette { id: palette }
// We use layouts rather than anchors because there are no minimum widths/heights
// with the anchor system.
ColumnLayout {
id: mainLayout
anchors { fill: parent; margins: content.outerSpacing }
spacing: content.spacing
// We have to embed another item so that children don't go after the buttons.
ColumnLayout {
id: defaultContentItem
Layout.fillWidth: true
Layout.fillHeight: true
}
Flow {
Layout.fillWidth: true
id: buttonsLeft
spacing: content.spacing
Repeater {
id: buttonsLeftRepeater
Button {
text: (buttonsLeftRepeater.model && buttonsLeftRepeater.model[index] ? buttonsLeftRepeater.model[index].text : index)
onClicked: root.click(buttonsLeftRepeater.model[index].standardButton)
}
}
Button {
id: moreButton
text: qsTr("Show Details...")
visible: false
}
}
Flow {
Layout.fillWidth: true
id: buttonsRight
spacing: content.spacing
layoutDirection: Qt.RightToLeft
Repeater {
id: buttonsRightRepeater
// TODO maybe: insert gaps if the button requires it (destructive buttons only)
Button {
text: (buttonsRightRepeater.model && buttonsRightRepeater.model[index] ? buttonsRightRepeater.model[index].text : index)
onClicked: root.click(buttonsRightRepeater.model[index].standardButton)
}
}
}
}
}
function setupButtons() {
buttonsLeftRepeater.model = root.__standardButtonsLeftModel()
buttonsRightRepeater.model = root.__standardButtonsRightModel()
if (!buttonsRightRepeater.model || buttonsRightRepeater.model.length < 2)
return;
var calcWidth = 0;
function calculateForButton(i, b) {
var buttonWidth = b.implicitWidth;
if (buttonWidth > 0) {
if (i > 0)
buttonWidth += content.spacing
calcWidth += buttonWidth
}
}
for (var i = 0; i < buttonsRight.visibleChildren.length; ++i)
calculateForButton(i, buttonsRight.visibleChildren[i])
content.minimumWidth = calcWidth + content.outerSpacing * 2
for (i = 0; i < buttonsLeft.visibleChildren.length; ++i)
calculateForButton(i, buttonsLeft.visibleChildren[i])
content.buttonsRowImplicitWidth = calcWidth + content.spacing
}
onStandardButtonsChanged: setupButtons()
Component.onCompleted: setupButtons()
}
You have to use it a bit differently to a normal Dialog. Just imagine it is a ColumnLayout (this is a slightly different example to the original question):
ColumnLayoutDialog {
id: dialog1
standardButtons: StandardButton.Ok | StandardButton.Cancel
Text {
text: "Hello world? "
}
Text {
text: "Hello world!"
}
// Spacer.
Item {
Layout.fillHeight: true;
}
Text {
text: "Goodbye world? "
}
Text {
text: "Goodbye world!"
}
}
By the way you could change the ColumnLayout to a GridLayout and expose the columns property if you want. That might make more sense.
A small issue
It turns out a QWindow's minimum width and height only ensure that the dialog isn't actively resized to be less than its content. It doesn't ensure that the dialog is never smaller than its content, because the content can grow after the dialog is created (e.g. extra items added). To workaround this I added this function to my ColumnLayoutDialog:
// The minimumWidth/Height values of content are accessed by the C++ class, but they
// only ensure that the window isn't resized to be smaller than its content. They
// don't ensure that if the content grows the window grows with it.
function ensureMinimumSize()
{
if (root.width < content.minimumWidth)
root.width = content.minimumWidth;
if (root.height < content.minimumHeight)
root.height = content.minimumHeight;
}
It has to be called manually when you change the dialog contents. Or to do it automatically you can add this to the content rectangle:
onMinimumHeightChanged: {
if (root.height < content.minimumHeight)
root.height = content.minimumHeight;
}
onMinimumWidthChanged: {
if (root.width < content.minimumWidth)
root.width = content.minimumWidth;
}
This is a bug in QT up to version 5.6.0. Most likely the bug number 49058. The code from the question works as expected in QT 5.6.1 and 5.7.0.
A partial workaround for the old versions is to remove the lines
width: layout.implicitWidth
height: layout.implicitHeight
and replace
anchors.fill: parent
with
anchors.right: parent.right
anchors.left: parent.left
The dialog then respects the minimum height and the contents expand horizontally.
Here is also a complete workaround, but it relies on undocumented implementation details of Dialog, so it should be used with caution. It works in 5.5.1, 5.6.0, 5.6.1 and 5.7.0. Note also that the second Item is changed to a red Rectangle to make the behavior more apparent.
import QtQuick 2.3
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
Dialog {
visible: true
standardButtons: StandardButton.Ok | StandardButton.Cancel
RowLayout {
id: layout
// In the horizontal direction, expansion and shrinking can be achieved with anchors.
anchors.left: parent.left
anchors.right: parent.right
// Used only for guessing the height of the Dialog's standard buttons.
Button {
id: hiddenButton
visible: false
}
// Repeats until the relevant parts of the dialog (parent of the parent of the RowLayout)
// are complete, then overwrites the minimum width and implicit height and stops repeating.
Timer {
id: timer
interval: 50; running: true; repeat: true;
onTriggered: {
if(layout.parent.parent) {
var lp = layout.parent
var lpp = layout.parent.parent
lpp.minimumWidth = layout.implicitWidth + 2 * lpp.outerSpacing
layout.buttonHeight = 2 * lpp.outerSpacing + hiddenButton.implicitHeight + lpp.spacing
lp.implicitHeight = layout.implicitHeight + 2 * lpp.outerSpacing
running = false
}
}
}
// The guessed space needed for the Dialog's buttons.
property int buttonHeight: 80
// Expand and shrink vertically when the dialog is resized.
height: parent.parent ? Math.max(parent.parent.height-buttonHeight, implicitHeight) : implicitHeight
Item {
width: 10
height: 1
}
GridLayout {
columns: 2
rowSpacing: 10
Layout.fillHeight: true
Layout.fillWidth: true
Text {
text: "Hello world? "
}
Text {
text: "Hello world!"
}
Text {
text: "Goodbye world? "
}
Text {
text: "Goodbye world!"
}
}
Rectangle {
Layout.fillHeight: true
color: 'red'
width: 10
}
}
}

Drop down alignment in BB 10 cascades

Here i am facing one problem,I need to change the Drop down option to align right.
now i am pushing the value like...
foreach (int key, map.keys()){
if(position ==1 && key >1000) {
Option* option = Option::create();
option->setText(map[key]);
option->setValue(key);
dropDown1->add(option);
//dropDown1->setHorizontalAlignment(HorizontalAlignment::Right);
}else
if(position ==2 && key < 1000) {
Option* option = Option::create();
option->setText(map[key]);
option->setValue(key);
dropDown1->add(option);
}
}
and my qml is like
Container {
objectName: "DdContainer"
layout: StackLayout {
orientation: propertyMap.textSize.length == 6 ? LayoutOrientation.RightToLeft : LayoutOrientation.LeftToRight
}
topPadding: 20.0
horizontalAlignment: HorizontalAlignment.Fill
DropDown {
// title: "Select Symbol"
objectName: "myDropDown"
enabled: true
layoutProperties: StackLayoutProperties {
spaceQuota: 3.0
}
}
Button {
imageSource: "asset:///images/add_button.png"
maxHeight: 100
maxWidth: 100
layoutProperties: StackLayoutProperties {
spaceQuota: 1.0
}
onClicked: {
_addOrDelRef.showSymbolsListView();
// _addOrDelRef.dropSymbols(1);
}
}
}
if key is below 1000 align left otherwise align right.How to do that in bb 10 cascades...
Please help,
Thanks...
The logic of alignment is here:
orientation: propertyMap.textSize.length == 6 ? LayoutOrientation.RightToLeft : LayoutOrientation.LeftToRight
You need to change this value according to key

Bounds of Flickable.contentY in QML

What is the correct way of asking the bounds of Flickable.contentY? I need that for a scrollbar.
Experimentally I have discovered that
offsetY <= contentY <= offsetY + contentHeight - height
where offsetY can be calculated as
var offsetY = contentY-Math.round(visibleArea.yPosition*contentHeight)
offsetY is zero at the start of the application and seems to be constant unless Flickable is resized.
This formula works in general, but there probably should be a dedicated function for it.
I did a scrollbar easily without offset :
// ScrollBar.qml
import QtQuick 2.0
Rectangle {
id: scrollbar;
color: "#3C3C3C";
visible: (flicker.visibleArea.heightRatio < 1.0);
property Flickable flicker : null;
width: 20;
anchors {
top: flicker.top;
right: flicker.right;
bottom: flicker.bottom;
}
Rectangle {
id: handle;
height: (scrollbar.height * flicker.visibleArea.heightRatio);
color: "#5E5E5E";
border {
width: 1;
color: "white";
}
anchors {
left: parent.left;
right: parent.right;
}
Binding { // Calculate handle's x/y position based on the content position of the Flickable
target: handle;
property: "y";
value: (flicker.visibleArea.yPosition * scrollbar.height);
when: (!dragger.drag.active);
}
Binding { // Calculate Flickable content position based on the handle x/y position
target: flicker;
property: "contentY";
value: (handle.y / scrollbar.height * flicker.contentHeight);
when: (dragger.drag.active);
}
MouseArea {
id: dragger;
anchors.fill: parent;
drag {
target: handle;
minimumX: handle.x;
maximumX: handle.x;
minimumY: 0;
maximumY: (scrollbar.height - handle.height);
axis: Drag.YAxis;
}
}
}
}
Just use it like this :
Flickable {
id: myFlick;
}
ScrollBar {
flicker: myFlick;
}
It can be moved with mouse, and moves automatically when Flickable is scrolled.

Qt QML: Keyboard and mouse does not work together to change the source image

I am struggling with a problem, I guess its not so difficult but I am not able to solve it. My experience with QML is very small. I will appreciate your help.
I have three radio buttons as images. Focus moves among the radio buttons as I press the keys and so the buttons are highlighted. ( As focus of the radio button changes the source images also change so the radio button with the focus will be highlighted with an other image).
Problem: When I interact with the mouse (see source code) the source (image) does not change any more…..no idea… while the source was changing before mouse interaction. I checked the in the debugger the source line is never reached after mouse interaction.
I guess its not the right way to change to source image…Please help me to solve it or give me a suggestion for an alternative
Rectangle { //main container
id: rectangle1
x: 0
y: 0
width: 480
height: 620
color: "#ffffff"
Item { // focus scope container
id: focus_object
focus : true
Image { // radio button 1
id: rock
x: 5
y: 6
fillMode: Image.PreserveAspectFit
smooth: true
focus:true
source: focus ? "Radiobutton_unselected_highlighted.png" : "Radiobutton_unselected.png"
KeyNavigation.right: pop
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.source = "Radiobutton_unselected_highlighted.png"
}
onExited: {
parent.source = "Radiobutton_unselected.png"
}
onClicked:{
}
}
}
Image { // radio button 2
id: pop
x: 160
y: 6
width: 64
height: 64
fillMode: Image.PreserveAspectFit
smooth: true
source: focus ? "Radiobutton_unselected_highlighted.png" : "Radiobutton_unselected.png"
KeyNavigation.left: rock
KeyNavigation.right: classic
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.source = "Radiobutton_unselected_highlighted.png"
}
onExited: {
parent.source = "Radiobutton_unselected.png"
}
onClicked:{
}
}
Image { // radio button 3
id: classic
x: 306
y: 6
width: 64
height: 64
fillMode: Image.PreserveAspectFit
smooth: true
source : focus ? "Radiobutton_unselected_highlighted.png" : "Radiobutton_unselected.png"
KeyNavigation.left: pop
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
if (true == focus)
parent.source = "Radiobutton_unselected_highlighted.png"
}
onExited: {
parent.source = "Radiobutton_unselected.png"
}
onClicked:{
}
}
}
}
}
}
Please note that you are using : , and not an assignment operator = here -
source: focus ? "Radiobutton_unselected_highlighted.png" : "Radiobutton_unselected.png"
This means you are establishing a binding, and not just assigning a fixed value to the property.
So if you have something like
x : y
when you want to change property 'x', instead of changing the property directly, change the property 'y' on which this property 'x' depends, or is binded with.
For your case -
Image
{ // radio button 1
id: rock
x: 5
y: 6
fillMode: Image.PreserveAspectFit
smooth: true
focus:true
source: focus ? "Radiobutton_unselected_highlighted.png" : "Radiobutton_unselected.png"
KeyNavigation.right: pop
MouseArea
{
anchors.fill: parent
hoverEnabled: true
onEntered:
{
rock.focus = true
}
onExited:
{
rock.focus = false
}
onClicked:
{
}
}
}
Read about qml property binding in detail.