{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} import Control.Monad import Data.Coerce import Data.Default import Data.Maybe import Data.Profunctor import qualified Generics.SOP as SOP import GHC.Generics import Graphics.UI.Threepenny.Core import Graphics.UI.Threepenny.Editors import Graphics.UI.Threepenny.Elements main :: IO () main = startGUI defaultConfig setup data LegalStatus = Single | Married | Divorced | Widowed deriving (Bounded, Enum, Eq, Ord, Show, Generic) instance Editable LegalStatus instance SOP.HasDatatypeInfo LegalStatus instance SOP.Generic LegalStatus instance Default LegalStatus where def = Single data Education = Basic | Intermediate | Other String deriving (Eq, Ord, Read, Show, Generic) instance Default Education where def = Basic getOther :: Education -> Maybe String getOther (Other s) = Just s getOther _ = Nothing editorEducation :: EditorFactory Education Education editorEducation = do let selector x = case x of Other _ -> "Other" _ -> show x editorSum [ ("Basic", const Basic <$> editorUnit) , ("Intermediate", const Intermediate <$> editorUnit) , ("Other", dimap (fromMaybe "" . getOther) Other editor) ] selector instance Editable Education instance SOP.HasDatatypeInfo Education instance SOP.Generic Education newtype Brexiteer = Brexiteer Bool deriving (Eq, Show, Ord, Generic) instance Default Brexiteer where def = Brexiteer False instance Editable Brexiteer where editor = editorGenericSimple instance SOP.HasDatatypeInfo Brexiteer instance SOP.Generic Brexiteer data Person = Person { education :: Education , firstName, lastName :: String , age :: Maybe Int , brexiteer :: Brexiteer , status :: LegalStatus } deriving (Generic, Show) instance Editable Person instance SOP.HasDatatypeInfo Person instance SOP.Generic Person instance Default Person where def = Person Basic "First" "Last" (Just 18) def def editorPersonRows :: EditorFactory Person Person editorPersonRows = vertically $ do (firstName, lastName) <- coerce $ horizontally $ do firstName <- coerce $ field "First:" firstName editor lastName <- coerce $ field "Last:" lastName editor return (firstName, lastName) (age, education) <- coerce $ horizontally $ do age <- coerce $ field "Age:" age editor education <- coerce $ field "Education:" education editorEducation return (age, education) (status, brexiteer) <- coerce $ horizontally $ do status <- coerce $ field "Status" status (editorJust $ editorSelection (pure [minBound..]) (pure (string.show))) brexiteer <- coerce $ field "Brexiter" brexiteer editor return (status, brexiteer) return Person{..} editorPersonColumns :: EditorFactory Person Person editorPersonColumns = horizontally $ do (firstName, lastName, age) <- coerce $ vertically $ do firstName <- coerce $ field "First:" firstName editor lastName <- coerce $ field "Last:" lastName editor age <- coerce $ field "Age:" age editor return (firstName, lastName, age) (education, status, brexiteer) <- coerce $ vertically $ do education <- coerce $ field "Education:" education editorEducation status <- coerce $ field "Status" status (editorJust $ editorSelection (pure [minBound..]) (pure (string.show))) brexiteer <- coerce $ field "Brexiter" brexiteer editor return (education, status, brexiteer) return Person{..} setup :: Window -> UI () setup w = void $ mdo _ <- return w # set title "Threepenny editors example" person1r <- createEditor editorPersonRows person1B person1c <- createEditor editorPersonColumns person1B person2 <- createEditor editorGeneric person1B person1B <- stepper def (head <$> unions [edited person1r, edited person1c, edited person2]) getBody w #+ [grid [ [return $ editorElement person1r] , [hr] , [return $ editorElement person1c] , [hr] , [return $ editorElement person2] , [hr] ]]