import QtQuick 2.7

import Common 1.0
import Utils 1.0

// ===================================================================
// Helper to handle button click outside an item.
// ===================================================================

Item {
  id: item

  property bool _mouseAlwaysOutside
  property var _mouseArea: null

  // 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)

  // -----------------------------------------------------------------

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

    if (_mouseArea == null) {
      _mouseArea = builder.createObject()
    }

    _mouseArea.parent = parent

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

  function _deleteMouseArea () {
    if (_mouseArea != null) {
      _mouseArea.destroy()
      _mouseArea = null
    }
  }

  // -----------------------------------------------------------------

  // 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,
  // so the mouse area must be created only when the target component
  // was completed.
  Component.onCompleted: enabled && _createMouseArea()
  Component.onDestruction: _deleteMouseArea()

  onEnabledChanged: {
    _deleteMouseArea()

    if (enabled) {
      _createMouseArea()
    }
  }

  Component {
    id: builder

    MouseArea {
      property var _timeout

      function _checkPosition (positionEvent) {
        // Propagate event.
        positionEvent.accepted = false

        // Click is outside or not.
        if (
          _mouseAlwaysOutside ||
          !Utils.pointIsInItem(this, item, positionEvent)
        ) {
          if (_timeout != null) {
            // Remove existing timeout to avoid the creation of
            // many children.
            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
          // modified with the positionEvent before the `onPressed`
          // call.
          //
          // The timeout is destroyed with the `MouseArea` component.
          _timeout = Utils.setTimeout(this, 0, item.pressed.bind(
            this,
            (function (point, item) {
              return Utils.pointIsInItem(this, item, point)
            }).bind(this, { x: positionEvent.x, y: positionEvent.y })
          ))
        }
      }

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

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