packages feed

moffy-samples-0.1.0.0: src/Control/Moffy/Samples/Boxes.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments, LambdaCase #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PatternSynonyms #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Control.Moffy.Samples.Boxes where

import Prelude hiding (until, repeat, cycle)

import Control.Arrow qualified as A
import Control.Monad
import Control.Monad.Trans.Except (pattern ExceptT, runExceptT)
import Control.Moffy
import Control.Moffy.Event.Time
import Control.Moffy.Samples.Event.Mouse qualified as Mouse
import Control.Moffy.Samples.Boxes.Viewable
import Data.Type.Set
import Data.Type.Flip
import Data.Bool
import Data.Or
import Data.List.NonEmpty (fromList)
import Data.List.Infinite (Infinite(..), cycle)

sameClick :: React s (Singleton Mouse.Down) Bool
sameClick = do
	pressed <- Mouse.down
	pressed2 <- Mouse.down
	pure $ pressed == pressed2

clickOn :: Mouse.Button -> React s (Singleton Mouse.Down) ()
clickOn b0 = do
	b <- Mouse.down
	bool (clickOn b0) (pure ()) (b == b0)

leftClick, middleClick, rightClick :: React s (Singleton Mouse.Down) ()
leftClick = clickOn Mouse.ButtonPrimary
middleClick = clickOn Mouse.ButtonMiddle
rightClick = clickOn Mouse.ButtonSecondary

releaseOn :: Mouse.Button -> React s (Singleton Mouse.Up) ()
releaseOn b0 = do
	b <- Mouse.up
	bool (releaseOn b0) (pure ()) (b == b0)

leftUp :: React s (Singleton Mouse.Up) ()
leftUp = releaseOn Mouse.ButtonPrimary

before :: Firstable es es' a b =>
	React s es a -> React s es' b -> React s (es :+: es') Bool
l `before` r = (\case L _ -> True; _ -> False) <$> l `first` r

doubler :: React s (TryWait :- Singleton Mouse.Down) ()
doubler = adjust rightClick >>
	rightClick `before` sleep 0.2 >>= bool doubler (pure ())

curRect :: Point -> Sig s (Singleton Mouse.Move) Rect ()
curRect p1 = Rect p1 <$%> Mouse.position

wiggleRect :: Rect -> Sig s (Singleton DeltaTime) Rect r
wiggleRect (Rect lu rd) = rectAtTime <$%> elapsed
	where rectAtTime t = let dx = sin (realToFrac t * 5) * 15 in
		Rect ((+ dx) `A.first` lu) ((+ dx) `A.first` rd)

posInside :: Rect -> Sig s es Point r -> React s es (Either Point r)
posInside rct = find (`inside` rct)
	where (x, y) `inside` Rect (l, u) (r, d) =
		(l <= x && x <= r || r <= x && x <= l) &&
		(u <= y && y <= d || d <= y && y <= u)

firstPoint :: React s (Mouse.Down :- Mouse.Move :- 'Nil) (Either String Point)
firstPoint = atResult (const "firstPoint 1") (const "firstPoint 2")
	<$> Mouse.position `at` leftClick

completeRect :: Point ->
	Sig s (Mouse.Up :- Mouse.Move :- 'Nil) Rect (Either String Rect)
completeRect p1 = const (Left "never occur") `either` (Right . fst)
	<$> curRect p1 `until` leftUp

defineRect :: Sig s Mouse.Events Rect Rect
defineRect = either error pure =<< runExceptT do
	p1 <- ExceptT . waitFor $ adjust firstPoint
	ExceptT . adjustSig $ completeRect p1

cycleColor :: Sig s (Mouse.Down :- 'Nil) BColor ()
cycleColor = go . cycle $ fromList [Red .. Magenta] where
	go (h :~ t) = do
		emit h
		(bool (pure ()) (go t)
			=<< waitFor (middleClick `before` rightClick))

chooseBoxColor :: Rect -> Sig s (Mouse.Down :- DeltaTime :- 'Nil) Box ()
chooseBoxColor r = Box <$%> adjustSig (wiggleRect r) <*%> adjustSig cycleColor

drClickOn :: Rect -> React s (Mouse.Down :- Mouse.Move :- TryWait :- 'Nil) ()
drClickOn rct = void . posInside rct
	. (fst <$%>) $ Mouse.position `indexBy` repeat doubler

box :: Sig s (Mouse.Events :+: DeltaTime :- TryWait :- 'Nil) Box ()
box = (`Box` Red) <$%> adjustSig defineRect >>= \b -> do
	adjustSig $ chooseBoxColor b
	waitFor . adjust $ drClickOn b

boxes :: Sig s (Mouse.Events :+: DeltaTime :- TryWait :- 'Nil) [Box] ()
boxes = () <$ parList (spawn box)