InvertedMouseArea.qml 2.91 KB
Newer Older
Ronan Abhamon's avatar
Ronan Abhamon committed
1
import QtQuick 2.7
2

3
import Common 1.0
4
import Utils 1.0
5

6
// ===================================================================
7
// Helper to handle button click outside an item.
8 9 10
// ===================================================================

Item {
11
  id: item
12

13
  property bool _mouseAlwaysOutside
14
  property var _mouseArea: null
15

16 17 18
  // When emitted, returns a function to test if the click
  // is on a specific item. It takes only a item parameter.
  signal pressed (var pointIsInItem)
19

20 21
  // -----------------------------------------------------------------

22
  function _createMouseArea () {
23 24
    var parent = Utils.getTopParent(item, true)

25
    if (_mouseArea == null) {
26
      _mouseArea = builder.createObject()
27
    }
28

29
    _mouseArea.parent = parent
30 31 32

    // Must be true if a fake parent is used and if `mouseArea`
    // is not in the same window that `item`.
33 34
    _mouseAlwaysOutside =
      _mouseArea.parent !== Utils.getTopParent(item)
35
  }
36

37 38 39 40
  function _deleteMouseArea () {
    if (_mouseArea != null) {
      _mouseArea.destroy()
      _mouseArea = null
41
    }
42 43
  }

44 45
  // -----------------------------------------------------------------

46 47 48 49
  // It's necessary to use a `enabled` variable.
  // See: http://doc.qt.io/qt-5/qml-qtqml-component.html#completed-signal
  //
  // The creation order of components in a view is undefined,
50 51
  // so the mouse area must be created only when the target component
  // was completed.
52 53 54 55 56 57 58 59
  Component.onCompleted: enabled && _createMouseArea()
  Component.onDestruction: _deleteMouseArea()

  onEnabledChanged: {
    _deleteMouseArea()

    if (enabled) {
      _createMouseArea()
60
    }
61
  }
62

63 64 65 66
  Component {
    id: builder

    MouseArea {
67 68
      property var _timeout

69
      function _checkPosition (positionEvent) {
70
        // Propagate event.
71
        positionEvent.accepted = false
Ronan Abhamon's avatar
Ronan Abhamon committed
72

73
        // Click is outside or not.
74 75 76 77
        if (
          _mouseAlwaysOutside ||
          !Utils.pointIsInItem(this, item, positionEvent)
        ) {
78
          if (_timeout != null) {
79 80
            // Remove existing timeout to avoid the creation of
            // many children.
81 82 83 84 85 86 87
            Utils.clearTimeout(_timeout)
          }

          // Use a asynchronous call that is executed
          // after the propagated event.
          //
          // It's useful to ensure the window's context is not
88
          // modified with the positionEvent before the `onPressed`
89
          // call.
90 91
          //
          // The timeout is destroyed with the `MouseArea` component.
92 93 94 95
          _timeout = Utils.setTimeout(this, 0, item.pressed.bind(
            this,
            (function (point, item) {
              return Utils.pointIsInItem(this, item, point)
96
            }).bind(this, { x: positionEvent.x, y: positionEvent.y })
97
          ))
98
        }
99
      }
100 101 102 103 104 105 106

      anchors.fill: parent
      propagateComposedEvents: true
      z: Constants.zMax

      onPressed: _checkPosition.call(this, mouse)
      onWheel: _checkPosition.call(this, wheel)
107
    }
108
  }
109
}