How to scroll with arrow keys in a Kirigami ScrollablePage? - qt

The Qt QML based mobile/desktop convergent UI framework Kirigami provides a QML type ScrollablePage to support scrolling through content. Placing any visual QML item into it automatically makes it scrollable if it's larger than the ScrollablePage itself:
ScrollablePage is a Page that holds scrollable content, such as ListViews.
Scrolling and scrolling indicators will be automatically managed.
Kirigami.ScrollablePage {
id: root
//The rectangle will automatically be scrollable
Rectangle {
width: root.width
height: 99999
}
}
(source)
This provides scrollbars and allows scrolling with the mouse wheel, two-finger-scrolling with the touchpad and flicking ("click and throw") scrolling as we're used to from touchscreen devices.
However, it does not allow scrolling with any keyboard keys (Arrow Up / Down, Page Up / Down). How can I make that possible? The usual approach of doing Keys.onUpPressed: scrollBar.decrease() does not work because the ScrollablePage's scrollbar is not accessible as part of its public API.

Instructions
Use a Flickable to wrap the content items you put into your ScrollablePage. Then evaluate key press events in the Flickable and in response execute flick() to scroll the view. Example (combining examples from the Kirigami manual and from the Qt manual):
Kirigami.ScrollablePage {
id: root
Flickable {
focus: true
topMargin: 20; leftMargin: 20; bottomMargin: 20; rightMargin: 20
Keys.onUpPressed: flick(0, 800)
Keys.onDownPressed: flick(0, -800)
Rectangle {
width: root.width
height: 5000
}
}
}
Details and Explanation
While you can't access the scrollbar, you can access what the scrollbar uses to move the view: a Flickable instance. You just have to wrap it around the page's content. If you don't, ScrollablePage internally uses ScrollView to wrap your page's content in a Flickable anyway, but then you don't have a reference on it to execute flick().
Executing flick() does the same as when the user flicks the element, so the scrollbar position etc. will be updated alright.
If it still does not work, then (1) maybe you give too small pixel/second values to Flickable::flick() for scrolling to be visible or (2) maybe the initial Flickable::flickDeceleration values on your platform are messed up. These values are platform specific, so it can require some experimentation. On some platforms, setting them to zero during a flick() will help, while under Linux this is exactly the value preventing any scroll movement.
It is not necessary to enable ScrollablePage::keyboardNavigationEnabled for the above solution to work, since that is only for moving the currentItem of suitable content with the arrow keys (see below), and not for scrolling in general. It will even prevent ordinary scrolling in case your page content is an item view (ListView, GridView etc.).
Alternative solution for item views
If the content of your ScrollablePage is an item view (any QML object that has a currentItem property, such as ListView or GridView), then instead of wrapping that content in a Flickable just enable ScrollablePage::keyboardNavigationEnabled. It will allow you to move the currentItem with the Arrow Up and Arrow Down keys. That's what one usually wants for these views, even though it's not scrolling but rather keyboard navigation.

Related

How to disable clicking through item in qml?

The application I currently work on has a map as a background, and above it various other dialogs(views) with more than one view inside can be opened. When some of the dialogs is active, when dragging over it's background map is moving like there's nothing above it. Does someone know how to disable this? I don't want map to react on clicks or anything inside a dialog.
The project is organised so that each dialog is implemented in separate qml file:
I have each qml file for each dialog, and each component of application (map), so
when you click, for example on settings tab in scrollable horizontal list, settings tab is opened from qml that holds all dialogs, including bottom and top of the app
each dialog is above map and has a 50% transparent background, with related images and buttons in it
I want to disable dragging map while dragging over dialog's background. I tried with setting this to each dialog:
MouseArea {
anchors.fill: parent
onClicked: mouse.accepted = true
}
(parent is Item that holds all elements of a dialog), but this doesn't work.
If I'm understanding your question correctly, it should suffice to set the MouseArea's propagateComposedEvents to false.

Keep cursor shape while pressed as it moves outside its MouseArea

I am implementing narrow resize handles which give me annoying behavior. The cursor shape is as expected while the mouse is directly over the handle, but once dragging the handle is initiated, the cursor shape becomes inconsistent. There are two causes of this:
when the cursor moves fast and goes ahead of the handle until the handle "catches up" (or when "fluid qml" is too fluid) - this is particularly nasty as the cursor shape rapidly changes and blinks
when the cursor moves outside of the allowed degree of freedom for the handle
I looked up the doc but it doesn't seem to contain anything about locking the cursor until the press is released.
I did manage to find a hack to fix it - using a dummy overlay MouseArea with acceptedButtons: Qt.NoButton - this actually helps to fake cursor consistency, but comes with an issue of its own. Having that overlay mouse area doesn't allow the cursor to change to a resize shape when it is over the handle, since the handle is under the overlay mouse area it doesn't get to modify the cursor shape at all. So the resize shape kicks in only after the handle is clicked, which is far from ideal. Setting the overlay mouse area to enabled: false doesn't change that - it still keeps blocking cursor shape changes from underlying mouse areas. There is a workaround for that as well, for example setting the overlay mouse area size to 0x0, but it is kind of ugly.
Ideally, the cursor shape should persist until the mouse area is pressed, regardless of whether it is in or outside of its area - after all, the press is not released if you go outside of it, thus the mouse area is still in control and should persist its cursor shape. For example - the window resize handles remain resize shape even if it is moved to resize the window smaller than its minimal size, until the press is released.
To me it seems that there are flaws in the implementation of MouseArea - the cursor shape is not kept while pressed, and the cursor shape is changed even if the mouse area is disabled.
I didn't find a way to do this out-of-the-box, it is pretty easy to create a helper for this though. On the qml side you can e.g. have:
CursorChanger {
cursor: Qt.SizeHorCursor
active: dragArea.containsMouse || dragArea.drag.active
}
On the C++ side you'll need a helper class like this:
class CursorChanger : public QObject
{
Q_OBJECT
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(int cursor READ cursor WRITE setCursor NOTIFY cursorChanged)
// ...
}
In the implementation you can use QGuiApplication::setOverrideCursor and QGuiApplication::restoreOverrideCursor to actually set/reset the cursor. Don't forget to also reset in the CursorChanger destructor if active at that point. If you then register the type:
qmlRegisterType<CursorChanger>(uri, 1, 0, "CursorChanger");`
You can use this type from qml.
I think there are some use cases for the current behavior. For instance, the cursor could convey some relationship between the currently hovered object and the one where the mouse was pressed. Another example, a drag could have its speed intentionally limited, and when the user goes too fast there is a consequence that depends on the item behind.
dtech's demand is more common for sure, and I also would like to see that as an optional feature, but not as a change. The way it is now provides more versatile pieces for apps. I don't like polished components that can be used only exactly as the library writer imagined.
Another QML only solution for a persistent drag cursor is to have a MouseArea behind all itens to hold the persistent shape when needed:
Item
{
id: scene; width: 800; height: 600
MouseArea
{
id: mouse
anchors.fill: scene
}
Rectangle
{
id: draggable; width: 40; height: 30; color: "red"
MouseArea
{
anchors.fill: draggable
drag.target : draggable
//set and unset a persistent cursor
onPressed : mouse.cursorShape = Qt.DragMoveCursor;
onReleased: mouse.cursorShape = Qt.ArrowCursor; //QT default cursor
//let non default scene cursors prevail over the item's
cursorShape: mouse.cursorShape === Qt.ArrowCursor ?
Qt.OpenHandCursor : mouse.cursorShape;
}
}
}

QML tab occasionally stretches off-screen

I have a QML-based GUI with a fixed width that uses the TabView type in several places.
On one page, I have something like this (leaving out most properties except for lateral anchors):
ColumnLayout {
MyTabsViewSubmenu { // defined below
Layout.fillWidth: true
Tab {
id: someId
title: someString
anchors.fill: parent
SomeCustomClass {
id: someId2
anchors.fill: parent
}
}
// three more tabs defined the same way, with the same anchors...
}
// another item below the tabs...
}
MyTabsViewSubmenu is something like this:
TabView {
Rectanble {
anchors.fill: parent
}
style: TabViewStyle {
// miscellaneous style stuff
}
}
One of my four tabs in the ColumnLayout above sometimes stretches off the screen when selected. As far as I can tell, there is nothing special about it compared to the other items used as tabs throughout the GUI. The layout of this tab is something like this:
Item {
MyTabsViewSubmenu {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: sibling.top
Tab {
// no anchors (see below)
SomeItem {
anchors.fill: parent
}
// ... other tabs....
}
Rectangle {
id: sibling
anchors.left: parent.left
anchors.right: parent.right
// ... stuff....
}
}
The entire page stretches off the screen: both the sub-tab content and the content in the Rectangle I've shown here as sibling.
I would suspect that possibly the missing anchors.fill: parent in the innermost Tabs might be the problem, except that sibling is not (as far as I can tell* missing any anchors, and I've never seen the tabs stretched offscreen without the sibling being stretched offscreen as well.
It seems entirely unpredictable whether the stretching occurs or the layout is done correctly. Once the layout has stretched off the screen, I can sometimes get it to correct itself by navigating away from that page and back.
I'm using Qt 5.6.1-1 on Debian 7.
EDIT: When I navigate to the "stretched" tab and the bug occurs causing the stretching, the tabs themselves at the top of the page also get "stretched" somewhat. Returning to a different tab un-stretches the tabs.
The fix
Setting Layout.maximumWidth (as per Velkan's comment) appears to resolve the issue. Additionally, it appears to make the page load faster.
Observations and testing
It's now about a week and a half since I introduced this change to the code, and the product has been heavily tested since then.
We have discovered a second component that needs Layout.maximumWidth set in order to keep from stretching off the screen, and indeed applying this fix to both the original problematic components has prevented the screen-stretching bug. So this is definitely a valid fix.
Possible root cause (i.e. groundless speculation)
I suspect that the QML engine attempts to size "Layout" objects by starting with the maximum width, then shrinking them to fit (or something like this). If the maximum width is unset, it's set to something like "infinity". Without a maximumWidth, it appears that the auto-shrinking operation sometimes fails, leaving the component stretched offscreen. I suspect that the automatic-resizing code may be impacted by some kind of nondeterminism in the order in which the sizes of different QML components are computed.

How to disable scrolling in a QML ScrollView (or TextArea)

I want to use a QML TextArea on a mobile device, where the user cannot scroll by swiping through the contents. Instead I want the widget to grow with the content (which I can do by utilizing contentHeight).
But I am unable to disable the scrolling behavior. TextArea has this scrolling behavior because it inherits from ScrollView.
You can use ScrollView's property flickableItem to change or disable scrolling behavior. In your case I'd use:
TextArea {
flickableItem.interactive: false
}
If you want to do something else, check out QML's Flickable, you should be able to use all its properties like with any other Flickable.

Fullscreen mode for a video

I have a video output embedded in a QML view. It is working fine, but I want to make that video output go fullscreen when I click on it.
Every time, some images that are in the view (some sibiling, and some not) are visible on top of my video. In fact, it should fill the root element, and be at the front screen.
Changing the z property doesn't do anything.
What is the best trick to make a video go fullscreen? When I switch from normal to fullscreen, the video should continue its flow with no interuption.
A solution only in QML (and no C++) would be preferable, as I build my QMLs by parsing XML files.
You can create new fullscreen window from QtQuick.Window module and pass tpo that window video path,time and play.
Component {
Window{
id: videoWindow
flags: Qt.FramelessWindowHint
HereYourPlayer{
}
}
}
than you should create that Component and call videoWindow.showFullScreen()
I finaly found the solution I needed. In fact it was simplier that it seemed. I created an Item just under the root, and I changed the parent of my video element when I wanted to go fullscreen. I put my new Item as the parent of my video element.
I didnt know that we could change the parent of an element.

Resources