-- | Text-field editing facade: single-line fields ("NanoUI.Frame.TextInput"),
-- text areas ("NanoUI.Frame.TextArea") and their context menu
-- ("NanoUI.Frame.TextEdit.Menu"), plus the dispatchers that pick between the
-- two field kinds.
module NanoUI.Frame.TextEdit
  ( -- * Dispatch between field kinds
    finalizeTextFieldMouse
  , collapseTextFieldSelection
    -- * Context menu
  , applyTextFieldMenuAction
  , textEditMenuRectAt
  , textEditMenuWidth
    -- * Shared field helpers
  , normalizeTextFieldClicks
  , textWordBounds
    -- * Text areas
  , TextAreaHit (..)
  , TextAreaScrollBarLayouts (..)
  , resolveTextAreaFont
  , textAreaContentMetrics
  , textAreaBarLane
  , textAreaLineHeight
  , textAreaHitForWidget
  , textAreaScrollBarLayout
  , textAreaHScrollBarLayout
  , textAreaScrollBarLayouts
  ) where

import Control.Monad (unless, when)
import Data.IORef (readIORef)
import NanoUI.Context (Context (..), setTextInputDrag)
import NanoUI.Frame.Hit (findNodeByWidgetId)
import NanoUI.Frame.TextArea
import NanoUI.Frame.TextArea.Content (resolveTextAreaFont, textAreaContentMetrics)
import NanoUI.Frame.TextArea.Geometry
import NanoUI.Frame.TextEdit.Menu (applyTextFieldMenuAction, textEditMenuRectAt, textEditMenuWidth)
import NanoUI.Frame.TextInput
import NanoUI.Id (WidgetId, hashWidgetId)
import NanoUI.Input (Input, inputMouseReleased)
import NanoUI.Layout.Arena (NodeType (NodeTextArea, NodeTextInput), getNodeType)
import NanoUI.Widgets.TextCommon (textWordBounds)

-- | Mouse selection in the focused field, whichever kind it is. A release
-- ends any drag.
finalizeTextFieldMouse :: Context -> Input -> IO ()
finalizeTextFieldMouse :: Context -> Input -> IO ()
finalizeTextFieldMouse Context
ctx Input
inp = do
  focus <- IORef WidgetId -> IO WidgetId
forall a. IORef a -> IO a
readIORef (Context -> IORef WidgetId
ctxFocusId Context
ctx)
  when (hashWidgetId focus /= 0) $ do
    handled <- finalizeTextInputMouse ctx inp focus
    unless handled $ finalizeTextAreaMouse ctx inp focus
  when (inputMouseReleased inp) $
    setTextInputDrag ctx Nothing

collapseTextFieldSelection :: Context -> WidgetId -> IO ()
collapseTextFieldSelection :: Context -> WidgetId -> IO ()
collapseTextFieldSelection Context
ctx WidgetId
wid =
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (WidgetId -> Word64
hashWidgetId WidgetId
wid Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word64
0) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
    mIdx <- Context -> WidgetId -> IO (Maybe NodeIdx)
findNodeByWidgetId Context
ctx WidgetId
wid
    case mIdx of
      Maybe NodeIdx
Nothing -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
      Just NodeIdx
idx ->
        NodeArena -> NodeIdx -> IO NodeType
getNodeType (Context -> NodeArena
ctxNodeArena Context
ctx) NodeIdx
idx IO NodeType -> (NodeType -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
          NodeType
NodeTextInput -> Context -> WidgetId -> IO ()
collapseTextInputSelection Context
ctx WidgetId
wid
          NodeType
NodeTextArea -> Context -> WidgetId -> IO ()
collapseTextAreaSelection Context
ctx WidgetId
wid
          NodeType
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()