aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/handlers/qquicktaphandler.cpp
Commit message (Collapse)AuthorAgeFilesLines
...
| * Add handlers declared as Flickable children to its contentItemShawn Rutledge2019-01-221-1/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | QQuickItemPrivate::data_append() was not invoked when any kind of Pointer Handler was directly declared in a Flickable (or subclass) because QQuickFlickable redefines the default property to be its own flickableData property. So we need to repeat the special handling in QQuickFlickablePrivate::data_append() too. The handler must be added to the private->extra->pointerHandlers vector, so that QQuickItemPrivate::handlePointerEvent() will attempt to deliver events to those handlers. TapHandler seems OK (especially with its default gesturePolicy so that it does not do an exclusive grab). PointHandler seems OK. DragHandler competes with Flickable for the exclusive grab. pressDelay can help; or set acceptedDevices: PointerDevice.Mouse to allow the mouse to drag but not flick, and the touchscreen to flick but not drag. Fixes: QTBUG-71918 Fixes: QTBUG-73035 Change-Id: Icb97ed5230abe0cb6ec0230b5b5759a0528df7e8 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
| * Merge 5.12 into 5.12.1Kari Oikarinen2019-01-081-0/+4
| |\ | |/ |/| | | Change-Id: Ic746fbce93430867e2eda4bc7155d34e20a4aa2b
| * TapHandler: clean up when wantsEventPoint returns falseShawn Rutledge2018-12-131-1/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We already emitted grabCanceled() to inform QML callbacks, but we didn't call reset(). It seems more proper to do everything that would normally be done when grab is canceled. TapHandler should not give up its passive grab yet though, because that prevents delivery to any parent Flickable that might be filtering events. A parent Flickable should be able to start flicking after the drag threshold is exceeded (it happens to be exactly when TapHandler gives up). Fixes: QTBUG-71466 Fixes: QTBUG-71970 Change-Id: Ibba1b0de92cfd88547eeb44edb095d019de76a94 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* | TapHandler: ignore scroll events and native gesturesShawn Rutledge2018-12-101-0/+4
|/ | | | | | | | | | | | | | During a 2-finger press (to emulate right click on a trackpad), the OS may also generate a QWheelEvent with ScrollBegin phase just in case scrolling starts. This must not prematurely deactivate the TapHandler. Also if a gesture or wheel event begins as the very first event after an application starts, ensure that subsequent mouse events are not mis-delivered as wheel or gesture events. Fixes: QTBUG-71955 Change-Id: Ic12e116483ab9ad37c4ac3b1d10ccb62e1349e0a Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Rename QQEventPoint::GrabState to GrabTransitionShawn Rutledge2018-08-311-3/+3
| | | | | | | | | | | | | | | | | | | This enum represents a transient state transition, and only sometimes corresponds to the current grab state of an event point. For example after exclusive grab has been canceled, the current state is that there is no exclusive grab: it doesn't make sense to remember that the way it got there was by cancellation. There was an idea to add a grabState property, but not all values would be eligible. An EventPoint can be exclusively grabbed by one item or handler at a time, and by multiple passive grabbers at the same time, so even a Q_FLAG would not fully express all possible states. Besides, there is already an exclusiveGrabber property, and we could add a passiveGrabbers list property if we had a real need. So adding a grabState property seems unlikely, and therefore is not a good enough reason to keep this enum named as GrabState. Change-Id: Ie37742b4bd431a7e51910d79a7223fba9a6bd848 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: emit eventPoint from the tapped signalsShawn Rutledge2018-07-271-3/+3
| | | | | | | | | | | | | | | | | | | | | | | | Any JS callback using one of these signals probably needs to know which button was tapped. We do not want to require TapHandler.point.pressedButtons to tell a lie (temporarily hold the previous state even though a button was actually released). We could add a releasedButtons property, but it would be a bit weird to have it holding state indefinitely between events. We could add just a button parameter to these signals, which would not be so bad, but emitting the whole eventPoint opens up other possibilities, like doing filtering in JS based on the device. To be able to get the device property of the event in QML, it must not be a const pointer. Q_PROPERTY(const type* ...) is so far unprecedented in Qt Quick; and the device has only const properties anyway. This reverts 8dc02aab72a714b5195ccc641fbfb534c3ae9e98 Task-number: QTBUG-61749 Task-number: QTBUG-64847 Change-Id: I09067498b22cc86e9f68c5ff13d6aa5447faba3d Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Fix PointerHandler constructors and destructorsShawn Rutledge2018-07-271-10/+1
| | | | | | | | | | | | | - Constructors should take QQuickItem* not QObject* to be symmetric with the parentItem() accessor (and other code) which assumes its type - Use header initialization everywhere possible - Reorder variables to minimize padding (somewhat) - Remove empty destructor bodies (the compiler can write them) - Remove override and virtual from destructors in accordance with https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rh-override Change-Id: I682a53a803d65e29136bfaec3a5b534e975ecf30 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Rearrange docs: Pointer Handlers -> Input HandlersShawn Rutledge2018-07-261-2/+2
| | | | | | | | | | | | | | | At QtCS 2018 we decided to rename Pointer Handlers to Input Handlers and include the Keys attached property as part of this set (since we plan to have attached-property pointer handlers too, eventually). It's no longer a module, it's included in Qt Quick 2.12. We need to start promoting Input Handlers and reducing the visibility of legacy stuff like MouseArea and MultiPointTouchArea (in the hope of being able to deprecate them eventually). Task-number: QTBUG-66651 Change-Id: I801351ac2531191cbb1faac9318441c67a109af6 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io> Reviewed-by: Venugopal Shivashankar <Venugopal.Shivashankar@qt.io>
* TapHandler docs: correct parent Item linksShawn Rutledge2018-07-261-6/+6
| | | | | | | | | There is a parentItem() accessor, but the property name is parent. Correction to 8048eed4142e1566bff8e4ca4bfd48c7a8adb8c8 Task-number: QTBUG-65089 Change-Id: I9af330498412e76904fcf46e779d5d4b9233e9f0 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Doc: revise TapHandler signal docs; parent not targetShawn Rutledge2018-07-231-25/+34
| | | | | | | | | | The target can be changed, but bounds-checking always means checking the parent Item's bounds (which is not always the target). Task-number: QTBUG-65089 Change-Id: Id09b69340a4ed1a5024023f50ae3636f53d6f3bb Reviewed-by: Venugopal Shivashankar <Venugopal.Shivashankar@qt.io> Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: enforce drag threshold for timeHeld and longPressedShawn Rutledge2018-07-191-1/+6
| | | | | | | | | | | | As the docs say, timeHeld is the amount of time that a pressed point has been held, without moving beyond the drag threshold. Likewise the longPressed signal should not be omitted if the point has moved beyond the drag threshold. In typical interactions these are relevant for long-pressing in one place, not while moving around. Task-number: QTBUG-65012 Change-Id: I3236def5de4d70f8d174362972a76e680452f018 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Rename Pointer Handlers to Input Handlers (as a concept)Shawn Rutledge2018-07-121-2/+2
| | | | | | | | | | | | | | | | This is a documentation change to alleviate some confusion that we've seen during the Tech Preview period. It doesn't make sense to actually rename the base class though, because it is intended to handle QQuickPointerEvents, not QEvents. The reason for that is that refactoring the QEvent hierarchy has to wait until Qt 6. So maybe in Qt 6 we can remove QQuickPointerEvent and have a QQuickInputHandler base class which handles QInputEvents; but for now, this conceptual renaming seems about as far as we can go. Task-number: QTBUG-66651 Change-Id: I84a41dc282c480d08f4d4a0d9a857e37e074aa7a Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io>
* Move wantsEventPoint() up from SinglePointHandler to QQPointerHandlerShawn Rutledge2018-06-201-1/+1
| | | | | | | | We want to be able to call it from the upcoming QQuickItemPrivate::anyPointerHandlerWants function. Change-Id: I15ef60303fa56f43e66b16c8dd0f4102070536d0 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* TapHandler: add singleTapped and doubleTapped signalsShawn Rutledge2018-03-021-3/+27
| | | | | | | | | | | | | This is for convenience. It's nicer not to need to test tapCount in JavaScript if you know you want to react on double-click: onTapped: if (tapCount === 2) doSomething() becomes onDoubleTapped: doSomething() Neither of these are guaranteed to be exclusive though: singleTapped occurs first, then doubleTapped if you tap again quickly, then tapCount keeps increasing as long as you keep tapping. Change-Id: I6fff10880831d5be0848b9957141311db8c2c0f0 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: document that DragThreshold is the default gesturePolicyShawn Rutledge2018-02-281-18/+19
| | | | | | | Followup to 6eaa95662c2d4ba287ac5d1de5ec49bd3a9f59e6 Change-Id: I082d0a52588a90f6c066f31e4595a01302e19241 Reviewed-by: Martin Smith <martin.smith@qt.io>
* doc: Fix several Can't link to errorsMartin Smith2018-02-281-6/+19
| | | | | | | | This update corrects several "Can't link to..." errors and splits a \qmlpropertygroup into two separate \qmlpropertygroups. Change-Id: Ic9b89a11eef64069154a932dd9dedf18279506a2 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* Change default TapHandler.gesturePolicy to DragThresholdShawn Rutledge2018-01-311-6/+12
| | | | | | | | | | | | | This is based on the idea that TapHandler may be more often used to modify existing behavior rather than building Button controls from scratch. DragThreshold is reasonable newbie-friendly default behavior for both use cases. The drag-off-drag-back-release-and-click behavior is more advanced, and the designers of the best-behaving Button controls can be expected to discover the need to change gesturePolicy to get it. Change-Id: If220acf080e04f664d020d5e832f8d16a16b857a Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* TapHandler: emit canceled when a gesturePolicy constrait is violatedShawn Rutledge2017-12-111-0/+2
| | | | | | | | | | | | | If gesturePolicy is DragThreshold and the threshold is exceeded or if gesturePolicy is withinBounds and you drag outside the bounds, that means any pending tap gesture is canceled, so the canceled signal should be emitted. It should not depend on a grab or an event to be canceled. QQuickTapHandler::setPressed(press, cancel, point) should take care of it when cancel is true. Task-number: QTBUG-65003 Change-Id: I2018695f5fc4438e68df3b34cf258251dbd5f198 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* improve documentation of the PointerHandler base classes and indexShawn Rutledge2017-09-291-0/+3
| | | | | | | also QQuickPointerEvent and QQuickPointerDevice Change-Id: I8bdb7c26cf6a5775a77dbf748c47c170270c5fff Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* rename QQuickEventPoint pos properties to positionShawn Rutledge2017-09-051-3/+3
| | | | | | | | For consistency we always spell it out, although it does make some of these properties inconveniently verbose. Change-Id: I64a08c3aa261c0ab89e09472dd47510abafbf7ca Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Rename single and multi pointer handler classnamesJan Arve Sæther2017-09-031-2/+2
| | | | | | | | | Renames: QQuickPointerSingleHandler -> QQuickSinglePointHandler QQuickMultiPointerHandler -> QQuickMultiPointHandler Change-Id: I10c54944f85ca7cac374ebc6241d61793e2d38bf Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* Merge dev into 5.10Frederik Gladhorn2017-09-021-1/+1
|\ | | | | | | Change-Id: I4376b711fbf02ea978f5d347d34a4a6a0c95dab2
| * Remove QQuickEventPoint parameter from tapped propertyJan Arve Sæther2017-08-281-1/+1
| | | | | | | | | | | | | | | | The information is already available from the 'point' property of QQuickPointerSingleHandler Change-Id: I96bdf2f1d8fbd1de901710a0d12dee5604565756 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* | TapHandler docs: correct the import statementShawn Rutledge2017-08-291-1/+1
|/ | | | | Change-Id: I27fb2cf491b59319d95bd7e34732fc18d1d8c532 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* Doc: Fix PointerHandler QML docsTopi Reinio2017-08-111-32/+39
| | | | | | | | | | | | | | | | | | | | | | qquickpinchhandler.cpp:138: warning: Missing property type for QQuickPinchHandler::minimumX qquickpinchhandler.cpp:151: warning: Missing property type for QQuickPinchHandler::maximumX qquickpinchhandler.cpp:164: warning: Missing property type for QQuickPinchHandler::minimumY qquickpinchhandler.cpp:177: warning: Missing property type for QQuickPinchHandler::maximumY qquickpinchhandler.cpp:194: warning: Missing property type for QQuickPinchHandler::minimumTouchPoints qquickpinchhandler.cpp:198: warning: Missing property type for QQuickPinchHandler::active qquickpointerdevicehandler.cpp:107: warning: Missing property type for QQuickPointerDeviceHandler::acceptedModifiers qquickpointerhandler.cpp:47: warning: C++ class QQuickPointerHandler not found: \instantiates QQuickPointerHandler qquickpointerhandler.cpp:176: warning: Missing property type for QQuickPointerHandler::enabled qquickpointerhandler.cpp:255: warning: Missing property type for QQuickPointerHandler::parent qquicktaphandler.cpp:175: warning: Missing property type for longPressThreshold qquicktaphandler.cpp:235: warning: Missing property type for gesturePolicy qquicktaphandler.cpp:252: warning: Missing property type for pressed qquicktaphandler.cpp:329: warning: Missing '\endqml' qquicktaphandler.cpp:340: warning: Missing property type for tapCount qquicktaphandler.cpp:353: warning: Missing property type for timeHeld Change-Id: I8a5bd0ec7c5603573f39f5b5f1f24d5735ba98dd Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* Move properties into grouped "point" propertyJan Arve Saether2017-05-231-2/+2
| | | | | Change-Id: I80000110a2e0ca69210322a0fcc587d86158358e Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* Start over with event delivery when touchpoint releases occurShawn Rutledge2017-05-221-3/+4
| | | | | | | | | | | | | | | | | | | | The new rule is that when the number of touchpoints changes, we start over with event delivery as if the touch had just begun, to give more opportunities to hand off processing from one item or handler to another. And MultiPointTouchArea can now handle the handoff: for example in tests/manual/pointer/pinchDragFlingMPTA.qml when the user is pressing three fingers, the PinchHandler is active; when the user then lifts one finger, the MPTA can resume handling the two remaining touchpoints as if they were just pressed. The change in QQuickMultiPointerHandler::wantsPointerEvent is both a behavior change and an optimization: released points aren't eligible; but if some points are released, then pressed, updated and stationary points are all eligible. And, figure this out without looping over the points twice. Change-Id: I26b7593de8e72b471adfec4a4482dd87a8288442 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: don't give up passive grab on setPressed(false)Shawn Rutledge2017-05-151-5/+2
| | | | | | | | | | | | | | | | | | | | If a button with a TapHandler with the DragThreshold policy is inside a Flickable, and you press on the button and start dragging, when you exceed the drag threshold, TapHandler will not "want" the EventPoint anymore. That triggers it to call setPressed(false). Flickable does not have a grab because it was relying on filtering the children's events; and QQuickWindow does not deliver touch updates to non-grabber items. So when TapHandler gives up its passive grab, Flickable does not get a chance to filter TapHandler's touch update events anymore. Thus, we cannot do that. When the touchpoint is actually released though, passive grabs are ungrabbed anyway. This is required to get the new tst_FlickableInterop::touchDragFlickableBehindButton() autotest working. Change-Id: I55a175ae358f75b9d7ab64f0b8124d91b6a9e1d6 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* QQPSingleHandler: accept only left mouse button by defaultShawn Rutledge2017-04-281-1/+0
| | | | | | | | | | It makes as much sense for DragHandler as it does for TapHandler. If you want to allow right-button dragging, you need to enable it in QML. In general, UIs have always relied on the left button for normal interaction, while the other buttons are for special cases. Change-Id: I708d5d080832c32ef581ca333c9be06e987ef007 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: CancelGrabPassive => setPressed(false)Shawn Rutledge2017-04-281-1/+1
| | | | | | | | | | | When a TapHandler's Item is inside a Flickable, the user has pressed the mouse button over the TapHandler, and then the Flickable takes the mouse grab during dragging from there, if the TapHandler has only a passive grab, it is cancelled. In this case the TapHandler should no longer be pressed, because it's being dragged instead. Change-Id: I129f44cc9b8d8e99b00e23cd5943dd57d4ae5d16 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* TapHandler: do not react to stationary touchpointsShawn Rutledge2017-04-211-2/+8
| | | | | | | | | | In autotests, stationary points normally have invalid position. A TapHandler does not need to react to them anyway. But we must also avoid having a grab cancelation due to a stationary point, and that applies to all PointerHandlers in general. Change-Id: I99493ad7d859e0c4ef155afc699aa34f28ffdbc7 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: don't "want" every updated/stationary pointShawn Rutledge2017-04-211-1/+1
| | | | | | | | | | | | | | | | | | If the policy is ReleaseWithinBounds, we assumed wrongly that wantsEventPoint() would not be asked about a point which QQuickPointerSingleHandler::wantsEventPoint() would already rule out, or about a point which was not grabbed either passively or exclusively. But in fact it is asked about every point. A tap is not going to occur unless the press occurs within bounds; if that happened, then QQuickPointerSingleHandler::m_pointId will have been set. So a TapHandler with this policy should remain interested in an updated or stationary point only if it is the same one which it was already interested in when it was pressed. This gets the multipoint button example and autotest working. Change-Id: I399c06071fd804cd6994d5f0153c307cec9d2f90 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler:wants: don't setPressed(false) unless pointId matchesShawn Rutledge2017-04-211-1/+1
| | | | | | | | | | | This fixes the multibuttons manual test: the first button would see that a second point was pressed, and that it didn't want it, because it's outside the bounds. This is not a good reason to discontinue responding to the first touchpoint, which is the one it was first interested in. Change-Id: I7004a667873f235d3dda84b4261113d37d911763 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: add qt.quick.handler.tap logging categoryShawn Rutledge2017-04-201-14/+23
| | | | | | | PinchHandler has its own. Maybe every pointerhandler should. Change-Id: Ic87494c8e333dcd502d4e259789b68337c3de349 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* TapHandler: grab before emitting on press, after emitting on releaseShawn Rutledge2017-03-071-4/+14
| | | | | | | | | The issue was that in an onTapped handler, the position property would be wrong and active would be false, because setActive(false) occurred too early. Change-Id: I71b43da703aa2f007a367c239d2ded64e6e7e850 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* API: Move acceptedButtons to QQuickPointerSingleHandlerJan Arve Saether2017-02-231-1/+1
| | | | | Change-Id: I8cb393986e587e69d550ec03f691258c79d9237a Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* unify handler grab state handling into onGrabChangedShawn Rutledge2017-02-211-7/+11
| | | | | | | | | | | | | | | | | | | | | | onGrabChanged and handleGrab looked redundant. It was also not clear how important it is for handlers to react to passive ungrabs, overrides or cancellations. Rather than debating about when to call one of these and when not to, let's centralize the responsibility in QQuickEventPoint (because the grabber pointers are stored there, so it's the ultimate destination of any grab change), and let's notify all the relevant handlers about all changes, with enough information that each handler can decide for itself what's important and what isn't. But so far most handlers don't need to override this virtual. The base class QQuickPointerHandler takes care of setting the active property to false, rejecting the eventpoint, and unsetting keepMouseGrab and keepTouchGrab whenever grab is lost; and emitting grabChanged or canceled as appropriate to notify any QML code which needs to know. Subclasses mainly care about the change of active state: they must initiate active state themselves, and may react when it reverts to false. Change-Id: I6c7f29472d12564d74ae091b0c81fa08fe131ce7 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Notify timeHeld property changed whenever press state changesJan Arve Saether2017-02-161-0/+1
| | | | | | | | Otherwise, we might read the previous timeHeld value (when we released) in for instance the onPressed handler. Change-Id: I2678ad95fad9bd5062573b7ca6d2bff08ce29b87 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
* start making explicit exclusive or passive grabsShawn Rutledge2017-02-101-2/+1
| | | | | Change-Id: I4a6e3c72d69e893fec2e39f4faab24af6d00c7e0 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: add timeHeld propertyShawn Rutledge2017-02-091-2/+32
| | | | | | | It enables long-press gestures to have continuous feedback. Change-Id: Idd0838aff6213ebfc2fce66639bbc932e77208b4 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: add gesturePolicyShawn Rutledge2017-02-091-19/+97
| | | | | | | | | | | | | | Until now it behaved as if this was set to DragThreshold: give up on the tap as soon as you are clearly dragging rather than tapping. But that's not what is normally wanted when building a Button control, for example. So provide 3 options: give up past the drag threshold, when the pointer goes outside the bounds, or when it's released outside the bounds. The longPressThreshold also constrains all three cases: holding (or dragging) for too long will not result in an immediate cancellation, but it also will not be a tap gesture. Change-Id: I95aec978e783892b55371391a27642751d91d9ff Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* TapHandler: add long-press featureShawn Rutledge2017-02-091-1/+43
| | | | | | | | Add a longPressed signal, emitted when the point is held long enough. Add the longPressThreshold to control how long that is. Change-Id: I95a65f1e4c62eb41fb9ea02b14bdc3f16aa72ec2 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
* Introduce TapHandlerShawn Rutledge2017-02-081-0/+177
Device-agnostic tap/click detection. Also detect whether the taps or clicks occur close enough together in both time and space to be considered part of a multi-tap gesture. Change-Id: I41a378feea3340b9f0409118273746a289641d6c Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>