packages feed

monomer-1.4.0.0: test/unit/Monomer/Common/CursorIconSpec.hs

{-|
Module      : Monomer.Common.CursorIconSpec
Copyright   : (c) 2018 Francisco Vallarino
License     : BSD-3-Clause (see the LICENSE file)
Maintainer  : fjvallarino@gmail.com
Stability   : experimental
Portability : non-portable

Unit tests for Cursor handling.
-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}

module Monomer.Common.CursorIconSpec (spec) where

import Control.Lens
import Control.Lens.TH (abbreviatedFields, makeLensesWith)
import Data.Default
import Data.Maybe
import Data.Sequence (Seq(..))
import Data.Text (Text)
import Test.Hspec

import qualified Data.Map.Strict as M
import qualified Data.Sequence as Seq

import Monomer.Core
import Monomer.Core.Combinators
import Monomer.Core.Themes.SampleThemes
import Monomer.Event
import Monomer.Helper (headMay)
import Monomer.Main
import Monomer.TestUtil
import Monomer.TestEventUtil
import Monomer.Widgets.Containers.Box
import Monomer.Widgets.Containers.Grid
import Monomer.Widgets.Containers.Stack
import Monomer.Widgets.Singles.Label
import Monomer.Widgets.Singles.Spacer
import Monomer.Widgets.Singles.TextDropdown

import qualified Monomer.Lens as L

newtype TestModel = TestModel {
  _tmSelectedItem :: Int
} deriving (Eq, Show)

makeLensesWith abbreviatedFields ''TestModel

spec :: Spec
spec = describe "Cursor Icon" $ do
  handleEventSimple
  handleEventNested
  handleEventOverlay

handleEventSimple :: Spec
handleEventSimple = describe "handleEventSimple" $ do
  it "should not change the cursor if not event happened" $ do
    icons [] `shouldBe` [CursorArrow]

  it "should not change the cursor if the widget does not have a cursor" $ do
    icons [[evtMove p1]] `shouldBe` [CursorArrow, CursorArrow]

  it "should change the cursor if the widget has a custom cursor" $ do
    icons [[evtMove p2]] `shouldBe` [CursorArrow, CursorHand]
    icons [[evtMove p3]] `shouldBe` [CursorArrow, CursorIBeam]
    icons [[evtMove p4]] `shouldBe` [CursorArrow, CursorInvalid]

  it "should generate the correct sequence of cursors from the events" $ do
    let evtsGroups = [[evtMove p2], [evtMove p3], [evtMove p4]]
    icons evtsGroups `shouldBe` [CursorArrow, CursorHand, CursorIBeam, CursorInvalid]

  where
    wenv = mockWenvEvtUnit ()
    node = vstack [
        label "Test",
        label "Test" `styleBasic` [cursorIcon CursorHand],
        label "Test" `styleBasic` [cursorIcon CursorIBeam],
        label "Test" `styleBasic` [cursorIcon CursorInvalid]
      ]
    icons egs = getIcons wenv node egs
    p1 = Point 100 10
    p2 = Point 100 30
    p3 = Point 100 50
    p4 = Point 100 70

handleEventNested :: Spec
handleEventNested = describe "handleEventNested" $ do
  it "should change the cursor if the widget has a custom cursor" $ do
    icons [[evtMove p11]] `shouldBe` [CursorArrow, CursorArrow]
    icons [[evtMove p21]] `shouldBe` [CursorArrow, CursorSizeH]
    icons [[evtMove p22]] `shouldBe` [CursorArrow, CursorHand]
    icons [[evtMove p31]] `shouldBe` [CursorArrow, CursorSizeV]
    icons [[evtMove p32]] `shouldBe` [CursorArrow, CursorHand]

  it "should generate the correct sequence of cursors from the events" $ do
    let evtsGroups = [[evtMove p11], [evtMove p21], [evtMove p22], [evtMove p31], [evtMove p32]]
    icons evtsGroups `shouldBe` [CursorArrow, CursorArrow, CursorSizeH, CursorHand, CursorSizeV, CursorHand]

  where
    wenv = mockWenvEvtUnit ()
    node = vstack [
        label "Test",
        hgrid [
          hgrid [
            label "Test" `styleBasic` [cursorIcon CursorSizeH],
            filler
          ]
        ] `styleBasic` [cursorIcon CursorHand],
        hgrid [
          hgrid [
            label "Test" `styleBasic` [cursorIcon CursorSizeV],
            filler
          ] `styleBasic` [cursorIcon CursorInvalid],
          spacer
        ] `styleBasic` [cursorIcon CursorHand]
      ]
    icons egs = getIcons wenv node egs
    p11 = Point 100 10
    p21 = Point 100 30
    p22 = Point 400 30
    p31 = Point 100 50
    p32 = Point 400 50

handleEventOverlay :: Spec
handleEventOverlay = describe "handleEventOverlay" $ do
  it "should not change the cursor if not event happened" $ do
    icons [] `shouldBe` [CursorArrow]

  it "should not show arrow in overlay area if dropdown is not open" $ do
    let evtsGroups = [[evtMove p1], [evtMove p2], [evtMove p3]]
    icons evtsGroups `shouldBe` [CursorArrow, CursorHand, CursorInvalid, CursorInvalid]

  it "should show arrow in overlay area if dropdown is open" $ do
    let evtsGroups = [[evtMove p1], [evtClick p1], [evtMove p2], [evtMove p3]]
    icons evtsGroups `shouldBe` [CursorArrow, CursorHand, CursorArrow, CursorHand, CursorArrow]

  it "should show arrow in overlay area when dropdown is open, invalid after it's closed" $ do
    let evtsGroups = [[evtMove p1], [evtClick p1], [evtMove p3], [evtClick p3]]
    icons evtsGroups `shouldBe` [CursorArrow, CursorHand, CursorArrow, CursorArrow, CursorInvalid]

  where
    wenv = mockWenvEvtUnit (TestModel 0)
      & L.theme .~ darkTheme
    node = vstack [
        textDropdown selectedItem [0..10::Int],
        filler
      ] `styleBasic` [cursorIcon CursorInvalid]
    icons egs = getIcons wenv node egs
    p1 = Point 100 10   -- Header
    p2 = Point 100 50   -- List overlay
    p3 = Point 100 460  -- Outside

getIcons
  :: Eq s
  => WidgetEnv s e
  -> WidgetNode s e
  -> [[SystemEvent]]
  -> [CursorIcon]
getIcons wenv root evtsGroups = iconsRes where
  firstIcon stack = fromMaybe CursorArrow (headMay stack)
  ctxs = snd <$> tail (nodeHandleEvents_ wenv WInit evtsGroups root)
  cursors = (^.. L.cursorStack . folded . _2) <$> ctxs
  iconsRes = firstIcon <$> cursors