All Downloads are FREE. Search and download functionalities are using the official Maven repository.

indigo.shared.input.Mouse.scala Maven / Gradle / Ivy

The newest version!
package indigo.shared.input

import indigo.shared.collections.Batch
import indigo.shared.datatypes.Point
import indigo.shared.datatypes.Rectangle
import indigo.shared.events.MouseButton
import indigo.shared.events.MouseEvent
import indigo.shared.events.MouseWheel

import scala.annotation.tailrec

final class Mouse(
    mouseEvents: Batch[MouseEvent],
    val position: Point,
    @deprecated("use `isButtonDown` function instead of this value", "0.12.0") val leftMouseIsDown: Boolean,
    val buttonsDown: Set[MouseButton]
) {

  def this(mouseEvents: Batch[MouseEvent], position: Point, leftMouseIsDown: Boolean) =
    this(
      mouseEvents,
      position,
      leftMouseIsDown,
      buttonsDown = if (leftMouseIsDown) Set(MouseButton.LeftMouseButton) else Set.empty
    )

  def isButtonDown(button: MouseButton): Boolean = buttonsDown.contains(button)

  lazy val isLeftDown: Boolean  = isButtonDown(MouseButton.LeftMouseButton)
  lazy val isRightDown: Boolean = isButtonDown(MouseButton.RightMouseButton)

  lazy val mouseClicked: Boolean = mouseEvents.exists {
    case _: MouseEvent.Click => true
    case _                   => false
  }
  lazy val mousePressed: Boolean  = pressed(MouseButton.LeftMouseButton)
  lazy val mouseReleased: Boolean = released(MouseButton.LeftMouseButton)

  def pressed(button: MouseButton): Boolean =
    mouseEvents.exists {
      case md: MouseEvent.MouseDown if md.button == button => true
      case _                                               => false
    }

  def released(button: MouseButton): Boolean =
    mouseEvents.exists {
      case mu: MouseEvent.MouseUp if mu.button == button => true
      case _                                             => false
    }

  lazy val scrolled: Option[MouseWheel] =
    val amount = mouseEvents.foldLeft(0d) {
      case (acc, MouseEvent.Wheel(_, deltaY)) => acc + deltaY
      case (acc, _)                           => acc
    }

    if amount == 0 then Option.empty[MouseWheel]
    else if amount < 0 then Some(MouseWheel.ScrollUp)
    else Some(MouseWheel.ScrollDown)

  lazy val mouseClickAt: Option[Point] = mouseEvents.collectFirst { case m: MouseEvent.Click =>
    m.position
  }
  lazy val mouseUpAt: Option[Point]   = maybeUpAtPositionWith(MouseButton.LeftMouseButton)
  lazy val mouseDownAt: Option[Point] = maybeDownAtPositionWith(MouseButton.LeftMouseButton)

  def maybeUpAtPositionWith(button: MouseButton): Option[Point] = mouseEvents.collectFirst {
    case m: MouseEvent.MouseUp if m.button == button => m.position
  }
  def maybeDownAtPositionWith(button: MouseButton): Option[Point] = mouseEvents.collectFirst {
    case m: MouseEvent.MouseDown if m.button == button => m.position
  }

  private def wasMouseAt(position: Point, maybePosition: Option[Point]): Boolean =
    maybePosition match
      case Some(pt) => position == pt
      case None     => false

  def wasMouseClickedAt(position: Point): Boolean = wasMouseAt(position, mouseClickAt)
  def wasMouseClickedAt(x: Int, y: Int): Boolean  = wasMouseClickedAt(Point(x, y))

  def wasMouseUpAt(position: Point): Boolean = wasMouseAt(position, mouseUpAt)
  def wasMouseUpAt(x: Int, y: Int): Boolean  = wasMouseUpAt(Point(x, y))
  def wasUpAt(position: Point, button: MouseButton): Boolean =
    wasMouseAt(position, maybeUpAtPositionWith(button))
  def wasUpAt(x: Int, y: Int, button: MouseButton): Boolean =
    wasUpAt(Point(x, y), button)

  def wasMouseDownAt(position: Point): Boolean = wasMouseAt(position, mouseDownAt)
  def wasMouseDownAt(x: Int, y: Int): Boolean  = wasMouseDownAt(Point(x, y))
  def wasDownAt(position: Point, button: MouseButton): Boolean =
    wasMouseAt(position, maybeDownAtPositionWith(button))
  def wasDownAt(x: Int, y: Int, button: MouseButton): Boolean =
    wasDownAt(Point(x, y), button)

  def wasMousePositionAt(target: Point): Boolean  = target == position
  def wasMousePositionAt(x: Int, y: Int): Boolean = wasMousePositionAt(Point(x, y))

  // Within
  private def wasMouseWithin(bounds: Rectangle, maybePosition: Option[Point]): Boolean =
    maybePosition match {
      case Some(pt) => bounds.isPointWithin(pt)
      case None     => false
    }

  def wasMouseClickedWithin(bounds: Rectangle): Boolean = wasMouseWithin(bounds, mouseClickAt)
  def wasMouseClickedWithin(x: Int, y: Int, width: Int, height: Int): Boolean =
    wasMouseClickedWithin(Rectangle(x, y, width, height))

  def wasMouseUpWithin(bounds: Rectangle): Boolean = wasMouseWithin(bounds, mouseUpAt)
  def wasMouseUpWithin(x: Int, y: Int, width: Int, height: Int): Boolean = wasMouseUpWithin(
    Rectangle(x, y, width, height)
  )
  def wasUpWithin(bounds: Rectangle, button: MouseButton): Boolean =
    wasMouseWithin(bounds, maybeUpAtPositionWith(button))
  def wasUpWithin(x: Int, y: Int, width: Int, height: Int, button: MouseButton): Boolean =
    wasUpWithin(Rectangle(x, y, width, height), button)

  def wasMouseDownWithin(bounds: Rectangle): Boolean = wasMouseWithin(bounds, mouseDownAt)
  def wasMouseDownWithin(x: Int, y: Int, width: Int, height: Int): Boolean = wasMouseDownWithin(
    Rectangle(x, y, width, height)
  )
  def wasDownWithin(bounds: Rectangle, button: MouseButton): Boolean =
    wasMouseWithin(bounds, maybeDownAtPositionWith(button))
  def wasDownWithin(x: Int, y: Int, width: Int, height: Int, button: MouseButton): Boolean =
    wasDownWithin(Rectangle(x, y, width, height), button)

  def wasMousePositionWithin(bounds: Rectangle): Boolean = bounds.isPointWithin(position)
  def wasMousePositionWithin(x: Int, y: Int, width: Int, height: Int): Boolean =
    wasMousePositionWithin(Rectangle(x, y, width, height))

}
object Mouse:
  val default: Mouse =
    Mouse(Batch.empty, Point.zero, false)

  def calculateNext(previous: Mouse, events: Batch[MouseEvent]): Mouse =
    val newButtonsDown = calculateButtonsDown(events, previous.buttonsDown)
    Mouse(
      events,
      lastMousePosition(previous.position, events),
      newButtonsDown.contains(MouseButton.LeftMouseButton),
      newButtonsDown
    )

  private def lastMousePosition(previous: Point, events: Batch[MouseEvent]): Point =
    events.collect { case mp: MouseEvent.Move => mp.position }.lastOption.fold(previous)(identity)

  private given CanEqual[Batch[MouseEvent], Batch[MouseEvent]] = CanEqual.derived

  // Similar strategy as followed for `Keyboard`, this one uses Set (no button order preserved)
  private def calculateButtonsDown(
      mouseEvents: Batch[MouseEvent],
      previousButtonsDown: Set[MouseButton]
  ): Set[MouseButton] =
    @tailrec
    def rec(remaining: List[MouseEvent], buttonsDownAcc: Set[MouseButton]): Set[MouseButton] =
      remaining match
        case Nil =>
          buttonsDownAcc
        case (e: MouseEvent.MouseDown) :: moreEvents =>
          rec(moreEvents, buttonsDownAcc + e.button)
        case (e: MouseEvent.MouseUp) :: moreEvents =>
          rec(moreEvents, buttonsDownAcc - e.button)
        case _ :: moreEvents =>
          rec(moreEvents, buttonsDownAcc)

    rec(mouseEvents.toList, previousButtonsDown)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy