nano-ui-0.1.0.0: test/integration/Cases/Tooltip.hs
module Cases.Tooltip
( runTooltipHoverTest
, runTooltipIdStableTest
, runTooltipScrollPosTest
) where
import Control.Monad (void)
import Data.IORef (IORef)
import Data.Text qualified as T
import NanoUI
import NanoUI.Testing
import NanoUI.Testing.Assert (assert, evalUi, withInput)
import NanoUI.Testing.Harness
( centerOf
, hasText
, spanCenter
, warmup2
)
-- Hovering shows a text tooltip, and a widget tooltip only evaluates its body
-- while hovered.
runTooltipHoverTest :: Context -> IORef Int -> IO ()
runTooltipHoverTest ctx failed = do
let inp0 = withInput 640 480
ui = rowWith fillW $ do
help <- button' "Help Target"
tooltip help "Helpful advice here"
_ <- spacer (Grow 1) Fit
rich <- button' "Rich Info"
body <- tooltipWidget rich $ do
row $ do
void (label "[Icon]")
label "Rich tooltip body text"
pure (help, rich, body)
-- Unhovered: no tooltip overlay, and the widget body is not evaluated
(help, rich, body0) <- warmup2 ctx inp0 ui
spans0 <- collectOverlayTextSpans ctx inp0
assert failed (not (hasText "Helpful advice" spans0))
assert failed (case body0 of Nothing -> True; _ -> False)
-- Hovered: tooltip overlay present
let hoverHelp = inp0 {inputMousePos = centerOf help}
_ <- runFrame ctx hoverHelp ui
_ <- runFrame ctx hoverHelp ui
spans1 <- collectOverlayTextSpans ctx hoverHelp
assert failed (hasText "Helpful advice" spans1)
let hoverRich = inp0 {inputMousePos = centerOf rich}
_ <- runFrame ctx hoverRich ui
((_, _, body1), _, _, _) <- runFrame ctx hoverRich ui
assert failed (case body1 of Just _ -> True; Nothing -> False)
runTooltipIdStableTest :: Context -> IORef Int -> IO ()
runTooltipIdStableTest ctx failed = do
let inp0 = withInput 640 480
ui = column $ do
a <- button' "Help Target"
tooltip a "tip"
b <- button' "After"
pure (a, b)
(a0, b0) <- evalUi ctx inp0 ui
let hoverInp = inp0 {inputMousePos = centerOf a0}
_ <- runFrame ctx hoverInp ui
((_, b1), _, _, _) <- runFrame ctx hoverInp ui
assert failed (respId b0 == respId b1)
runTooltipScrollPosTest :: Context -> IORef Int -> IO ()
runTooltipScrollPosTest ctx failed = do
let inp0 = withInput 200 200
ui =
scrollArea (fillW . fixedH 80) $
column $ do
mapM_ (\_ -> void (label "pad line")) [(1 :: Int) .. 40]
btn <- button' "Tip Target"
tooltip btn "Scrolled tip text"
mapM_ (\_ -> void (label "tail line")) [(1 :: Int) .. 12]
pure btn
(sid, _) <- warmup2 ctx inp0 ui
mScroll <- getPrevRect ctx sid
case mScroll of
Nothing -> assert failed False
Just scrollRect@(Rect _ sy _ sh) -> do
let hover = inp0 {inputMousePos = spanCenter scrollRect}
wheel = hover {inputScroll = V2 0 1}
inView btn =
let y = rectY (respRect btn)
h = rectH (respRect btn)
in y >= sy + 4 && y + h + 16 <= sy + sh
pump = do
before <- getScrollOffset ctx sid
_ <- runFrame ctx wheel ui
after <- getScrollOffset ctx sid
((_, btn), _, _, _) <- runFrame ctx hover ui
if inView btn || after <= before then pure (after, btn) else pump
(off, btn1) <- pump
assert failed (off > 0)
let hoverInp = inp0 {inputMousePos = centerOf btn1}
visualBottom = rectY (respRect btn1) + rectH (respRect btn1)
layoutBottom = visualBottom + off
_ <- runFrame ctx hoverInp ui
_ <- runFrame ctx hoverInp ui
spans <- collectOverlayTextSpans ctx hoverInp
let ys =
[ rectY r
| (r, txt, _, _, _) <- spans
, "Scrolled tip" `T.isInfixOf` txt
]
case ys of
[] -> assert failed False
(tipY : _) -> do
assert failed (abs (tipY - visualBottom) <= 16)
assert failed (abs (tipY - visualBottom) < abs (tipY - layoutBottom))