{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE DataKinds #-} -- | A generic ADT editor defined on top of the main LGtk interface, "LGtk". module LGtk.ADTEditor ( List (..), Elems(..), ADTLens(..) , adtEditor ) where import LGtk import Prelude hiding ((.), id) -- | Type-level lists data List a = Nil | Cons a (List a) -- | Heterogeneous lists data Elems (xs :: List *) where ElemsNil :: Elems Nil ElemsCons :: ADTLens a => a -> Elems as -> Elems (Cons a as) {- | Lens for editable ADTs with support of shared record fields between constructors. Suppose we have the data type @ data X = X1 { a :: Int, b :: Bool } | X2 { a :: Int, c :: Char } @ We can build an editor which can switch between two editor for the constructors. If the field @a@ is edited in one editor, it will be updated in the other. -} class ADTLens a where {- | @ADTEls a@ is the list of types of the parts of the ADT. For example, @ADTEls X = Cons Int (Cons Bool (Cons Char Nil))@ -} type ADTEls a :: List * {- | The lens which defines an abstract editor. The first parameter defines the displayed constructor name and the parts of the constructor for each constructor. @Int@ is an index in the @ADTEls@ list. For example, in case of @X@, @fst3 adtLens = [(\"X1\", [0, 1]), (\"X2\", [0, 2])]@ The second parameter is the list of default values for each part. The third parameter is a lens from the selected constructor index plus the values of the ADT parts to the ADT values. -} adtLens :: ([(String, [Int])], Elems (ADTEls a), Lens (Int, Elems (ADTEls a)) a) -- | A generic ADT editor adtEditor :: (EffRef m, ADTLens a) => Ref m a -> m (Widget m) adtEditor = liftM action . memoRead . editor where editor r = do q <- extRef r k (0, ls) es <- mkEditors ls $ sndLens `lensMap` q return $ hcat [ combobox (map fst ss) $ fstLens `lensMap` q , cell (liftM fst $ readRef q) $ \i -> vcat [es !! j | j <- snd $ ss !! i] ] where (ss, ls, k) = adtLens mkEditors :: EffRef m => Elems xs -> Ref m (Elems xs) -> m [Widget m] mkEditors ElemsNil _ = return [] mkEditors (ElemsCons _ xs) r = do i <- adtEditor $ lHead `lensMap` r is <- mkEditors xs $ lTail `lensMap` r return $ i : is where lHead = lens get set where get :: Elems (Cons x xs) -> x get (ElemsCons a _) = a set :: x -> Elems (Cons x xs) -> Elems (Cons x xs) set a (ElemsCons _ as) = ElemsCons a as lTail = lens get set where get :: Elems (Cons x xs) -> Elems xs get (ElemsCons _ as) = as set :: Elems xs -> Elems (Cons x xs) -> Elems (Cons x xs) set as (ElemsCons a _) = ElemsCons a as