/src/Graphics/Vty/Widgets/Builder/GenLib.hs

https://github.com/jtdaugherty/vty-ui-builder · Haskell · 231 lines · 171 code · 39 blank · 21 comment · 8 complexity · 7c8d542288bc4a5a2374a22a1bbc0585 MD5 · raw file

  1. module Graphics.Vty.Widgets.Builder.GenLib
  2. ( gen
  3. , append
  4. , newEntry
  5. , registerInterface
  6. , lookupFocusMethod
  7. , declareWidget
  8. , withField
  9. , mergeFocus
  10. , getWidgetStateType
  11. , registerWidgetName
  12. , lookupFocusValue
  13. , getFieldValueName
  14. , getElementStringContent
  15. , registerFieldValueName
  16. , widgetLikeName
  17. , getAttribute
  18. , registerReferenceTarget
  19. , setFocusValue
  20. )
  21. where
  22. import Control.Applicative hiding (optional)
  23. import Control.Monad.State
  24. import qualified Data.Map as Map
  25. import Data.Foldable (for_)
  26. import Graphics.Vty.Widgets.Builder.Types
  27. import Graphics.Vty.Widgets.Builder.Util
  28. import qualified Graphics.Vty.Widgets.Builder.AST as A
  29. import qualified Graphics.Vty.Widgets.Builder.SrcHelpers as S
  30. import qualified Language.Haskell.Exts as Hs
  31. generateWidgetSource :: WidgetElementHandler
  32. -> A.WidgetElement
  33. -> ValidationState
  34. -> Hs.Name
  35. -> GenM WidgetHandlerResult
  36. generateWidgetSource (WidgetElementHandler genSrc validator specTyp) spec st nam = do
  37. case runValidation (validator $ A.getElement spec) st of
  38. ValidationError e -> error $ "Error while generating widget source for type " ++ show specTyp ++
  39. " (up-front validation should have prevented this): " ++ show e
  40. Valid val -> genSrc nam val
  41. gen :: A.WidgetLike -> Hs.Name -> GenM ()
  42. gen (A.Widget spec) nam = do
  43. hs <- gets elemHandlers
  44. handler <- case lookup (A.widgetElementName spec) hs of
  45. Nothing -> error $ show (A.sourceLocation spec) ++
  46. ": no handler for widget type " ++ (show $ A.widgetElementName spec)
  47. Just h -> return h
  48. doc <- gets document
  49. iface <- gets currentInterface
  50. genResult <- generateWidgetSource handler spec (ValidationState iface doc) nam
  51. -- Register the widget value name.
  52. registerWidgetName $ resultWidgetName genResult
  53. -- If the element has an ID, use that to set up field information so
  54. -- we know how to assign the widget to the field.
  55. for_ (getAttribute spec "id") $ \newName ->
  56. do
  57. let fieldValName = case fieldValueName genResult of
  58. Just fValName -> VName fValName
  59. Nothing -> WName $ resultWidgetName genResult
  60. -- When determining which value constitutes the type of the
  61. -- interface elements field for this widget, use the custom
  62. -- field value if specified by the handler (which may not have
  63. -- type Widget a), or fall back to the widget value if no
  64. -- custom field value was specified.
  65. registerFieldValueName (S.mkName newName) fieldValName
  66. -- Register the 'id' as a valid reference target so that 'ref'
  67. -- tags can use it
  68. registerReferenceTarget (S.mkName newName) A.InterfaceWidgetRef
  69. (widgetName $ resultWidgetName genResult)
  70. (widgetType $ resultWidgetName genResult)
  71. -- When setting up the widget value to be added to the focus
  72. -- group for this widget, always use the resultWidgetName name
  73. -- since it will have the right type (Widget a) in the
  74. -- generated source.
  75. setFocusValue (S.mkName newName) $ resultWidgetName genResult
  76. -- Use common attributes on the element to annotate it with
  77. -- widget-agnostic properties.
  78. annotateWidget spec nam
  79. gen (A.WidgetRef (A.WidgetReference tgt loc refType)) nam = do
  80. let target = S.mkName tgt
  81. val <- getReferenceTarget target refType
  82. result <- case val of
  83. Nothing -> error $ show loc ++
  84. ": ref: target '" ++ tgt ++ "' invalid"
  85. Just (wName, typ) -> do
  86. append $ S.mkLet [(nam, S.expr wName)]
  87. return $ declareWidget nam typ
  88. registerWidgetName $ resultWidgetName result
  89. getReferenceTarget :: Hs.Name -> A.ReferenceType -> GenM (Maybe (Hs.Name, Hs.Type))
  90. getReferenceTarget nam typ =
  91. lookup (nam, typ) <$> gets referenceTargets
  92. registerReferenceTarget :: Hs.Name -> A.ReferenceType -> Hs.Name -> Hs.Type -> GenM ()
  93. registerReferenceTarget target refType valName typ =
  94. modify $ \st ->
  95. st { referenceTargets = referenceTargets st
  96. ++ [((target, refType), (valName, typ))]
  97. }
  98. registerWidgetName :: WidgetName -> GenM ()
  99. registerWidgetName wn =
  100. modify $ \st ->
  101. st { allWidgetNames = allWidgetNames st ++ [(widgetName wn, wn)] }
  102. registerFieldValueName :: Hs.Name -> AnyName -> GenM ()
  103. registerFieldValueName fName valName = do
  104. modify $ \st ->
  105. st { registeredFieldNames = registeredFieldNames st ++ [(fName, valName)]
  106. }
  107. getFieldValueName :: Hs.Name -> GenM (Maybe AnyName)
  108. getFieldValueName fName = lookup fName <$> registeredFieldNames <$> get
  109. lookupFocusValue :: Hs.Name -> GenM (Maybe WidgetName)
  110. lookupFocusValue s = lookup s <$> focusValues <$> get
  111. mergeFocus :: Hs.Name -> Hs.Name -> GenM ()
  112. mergeFocus wName fgName = do
  113. st <- get
  114. put $ st { focusMethods = (wName, Merge fgName) : focusMethods st }
  115. lookupFocusMethod :: Hs.Name -> GenM (Maybe FocusMethod)
  116. lookupFocusMethod valName = do
  117. st <- get
  118. return $ lookup valName (focusMethods st)
  119. setFocusValue :: Hs.Name -> WidgetName -> GenM ()
  120. setFocusValue s wName = do
  121. modify $ \st -> st { focusValues = (s, wName) : focusValues st }
  122. getWidgetStateType :: Hs.Name -> GenM Hs.Type
  123. getWidgetStateType nam = do
  124. vts <- gets allWidgetNames
  125. case lookup nam vts of
  126. Nothing -> error $ "BUG: request for state type for value "
  127. ++ show nam
  128. ++ " impossible; did the element handler forget"
  129. ++ " to register the type?"
  130. Just wName -> return $ widgetType wName
  131. getAttribute :: (A.IsElement a) => a -> String -> Maybe String
  132. getAttribute val attrName = lookup attrName (A.getAttributes val)
  133. registerInterface :: String -> InterfaceValues -> GenM ()
  134. registerInterface ifName vals = do
  135. st <- get
  136. -- It's important to append the interface information so that the
  137. -- order of the interfaces in the AST is preserved in the generated
  138. -- code.
  139. put $ st { interfaceNames = interfaceNames st ++ [(ifName, vals)]
  140. }
  141. annotateWidget :: A.WidgetElement -> Hs.Name -> GenM ()
  142. annotateWidget spec nam = do
  143. -- Normal attribute override
  144. let normalResult = ( getAttribute spec "normalFg"
  145. , getAttribute spec "normalBg"
  146. )
  147. case S.attrsToExpr normalResult of
  148. Nothing -> return ()
  149. Just e ->
  150. append $ S.act $ S.call "setNormalAttribute" [S.expr nam, e]
  151. -- Focus attribute override
  152. let focusResult = ( getAttribute spec "focusFg"
  153. , getAttribute spec "focusBg"
  154. )
  155. case S.attrsToExpr focusResult of
  156. Nothing -> return ()
  157. Just e ->
  158. append $ S.act $ S.call "setFocusAttribute" [S.expr nam, e]
  159. widgetLikeName :: A.WidgetLike -> String
  160. widgetLikeName (A.WidgetRef _) = "ref"
  161. widgetLikeName (A.Widget w) = A.widgetElementName w
  162. getElementStringContent :: A.Element -> String
  163. getElementStringContent =
  164. concat . map elemText . A.elementContents
  165. where
  166. elemText (A.Text s _) = s
  167. elemText _ = []
  168. append :: Hs.Stmt -> GenM ()
  169. append stmt =
  170. modify $ \st -> st { hsStatements = hsStatements st ++ [stmt] }
  171. newEntry :: String -> GenM Hs.Name
  172. newEntry n = do
  173. st <- get
  174. let val = case Map.lookup n (nameCounters st) of
  175. Just nextVal -> nextVal
  176. Nothing -> 1
  177. let newMap = Map.insert n (val + 1) (nameCounters st)
  178. put $ st { nameCounters = newMap }
  179. return $ S.mkName $ (replace '-' '_' n) ++ show val
  180. declareWidget :: Hs.Name -> Hs.Type -> WidgetHandlerResult
  181. declareWidget nam tyCon =
  182. WidgetHandlerResult { resultWidgetName = WidgetName { widgetName = nam
  183. , widgetType = tyCon
  184. }
  185. , fieldValueName = Nothing
  186. }
  187. withField :: WidgetHandlerResult -> (Hs.Name, Hs.Type) -> WidgetHandlerResult
  188. withField mh (val, typ) =
  189. mh { fieldValueName = Just $ ValueName { valueName = val
  190. , valueType = typ
  191. }
  192. }