packages feed

keid-frp-banana-0.1.0.0: src/Engine/ReactiveBanana/Window.hs

module Engine.ReactiveBanana.Window where

import RIO

import Engine.Types (StageRIO)
import Engine.Types qualified as Engine
import Engine.UI.Layout qualified as Layout
import Engine.Window.MouseButton qualified as MouseButton
import Engine.Worker qualified as Worker
import Geomancy (Vec2, vec2, (^/))
import GHC.Float (double2Float)
import Reactive.Banana ((<@>), (<@>))
import Reactive.Banana qualified as RB
import Reactive.Banana.Frameworks qualified as RBF
import Vulkan.Core10 qualified as Vk

setupScreenBox
  :: (forall a. StageRIO env a -> RBF.MomentIO a)
  -> RBF.MomentIO (RB.Behavior Layout.Box)
setupScreenBox unlift = do
  screenExtent <- unlift Engine.askScreenVar >>=
    RBF.fromPoll . Worker.getOutputData

  let
    screenSize =
      screenExtent <&>
        \Vk.Extent2D{width, height} ->
          vec2
            (fromIntegral width)
            (fromIntegral height)

    screenBox =
      screenSize <&> \size ->
        Layout.Box
          { boxPosition = 0 -- XXX: since Camera.spawnOrthoPixelsCentered
          , boxSize     = size
          }

  pure screenBox

setupCursorPos
  :: RB.MonadMoment m
  => m (RB.Event (Double, Double))
  -> RB.Behavior Layout.Box
  -> m (RB.Event Vec2, RB.Behavior Vec2)
setupCursorPos fromCursorPos screenBox = do
  cursorPosRawE <- fromCursorPos
  let cursorPosE = convertPos <$> screenBox <@> cursorPosRawE

  cursorPos <- RB.stepper
    (1/0) -- XXX: prevent accidental flash of hover at (0, 0)
    cursorPosE
  pure (cursorPosE, cursorPos)
  where
    convertPos Layout.Box{boxSize} (cx, cy) =
      -- XXX: since Camera.spawnOrthoPixelsCentered
      vec2 (double2Float cx) (double2Float cy) -
      boxSize ^/ 2

setupMouseClicks
  :: RBF.MomentIO (RB.Event (MouseButton.ModifierKeys, MouseButton.MouseButtonState, MouseButton.MouseButton))
  -> RB.Behavior cursor
  -> RBF.MomentIO (MouseButton.Collection (RB.Event (MouseButton.ModifierKeys, cursor)))
setupMouseClicks fromMouseButton cursorPos = do
  -- imguiCaptureMouse <- RBF.fromPoll ImGui.wantCaptureMouse

  -- mouseButtonE <- RB.whenE (fmap not imguiCaptureMouse) <$> fromMouseButton
  mouseButtonE <- fromMouseButton

  -- XXX: Set up cursor event fusion, driven by mouseButtonE
  mouseButtons' <- sequenceA @MouseButton.Collection $ pure RBF.newEvent

  let
    dispatchButtons pos (mods, state, mb) =
      MouseButton.whenPressed state $
        -- XXX: Use one event handler to drive multiple derived events
        snd (MouseButton.atGlfw mouseButtons' mb) (mods, pos)

  RBF.reactimate $
    dispatchButtons <$> cursorPos <@> mouseButtonE

  pure $ fmap fst mouseButtons'