implementation module EditorExt from Data.Func import $ import Data.Tuple import qualified Data.Map as DM import StdTuple import Data.Maybe import iTasks import iTasks.UI.Editor.Common row :: String (Editor a) -> Editor a row l e = bijectEditorValue (tuple l) snd $ container2 label e <<@ directionAttr Horizontal import StdDebug, StdMisc // Choose combinators (Used for selecting constructors in ADTs) choose :: [(String, Editor a)] -> Editor (Int, a) choose [] = emptyEditor choose [(k,v)] = bijectEditorValue snd (tuple 0) $ row k v choose cs = {Editor|genUI=genUI,onEdit=onEdit,onRefresh=onRefresh} where numConses = length cs conses = map fst cs genUI dp (consindex, value) vst=:{VSt|taskId,mode,optional,selectedConsIndex} # cured = snd (cs !! consindex) = case mode of Enter # (consChooseUI,consChooseMask) = genConsChooseUI taskId dp optional conses Nothing = (Ok (UI UIVarCons 'DM'.newMap [consChooseUI],CompoundMask {fields=[consChooseMask],state=JSONNull}),{vst & selectedConsIndex = selectedConsIndex}) Update = case cured.Editor.genUI dp value vst of (Ok (consUI=:(UI _ attr items), consMask=:(CompoundMask {fields})),vst) # (consChooseUI,consChooseMask) = genConsChooseUI taskId dp optional conses (Just vst.selectedConsIndex) = (Ok (UI UIVarCons attr [consChooseUI:items],CompoundMask {fields=[consChooseMask:fields],state=JSONNull}) ,{vst & selectedConsIndex = selectedConsIndex}) (Error e,vst) = (Error e,vst) View = case cured.Editor.genUI dp value vst of (Ok (consUI=:(UI _ attr items), consMask=:(CompoundMask {fields})),vst) # (consViewUI,consViewMask) = genConsViewUI conses vst.selectedConsIndex = (Ok (UI UIVarCons attr [consViewUI:items],CompoundMask {fields=[consViewMask:fields],state=JSONNull}) ,{vst & selectedConsIndex = selectedConsIndex}) (Error e,vst) = (Error e,vst) genConsChooseUI taskId dp optional gtd_conses mbSelectedCons = (consChooseUI,consChooseMask) where consOptions = [JSONObject [("id",JSONInt i),("text",JSONString gdc)] \\ gdc <- gtd_conses & i <- [0..]] consChooseUI = uia UIDropdown (choiceAttrs taskId (editorId dp) (maybeToList mbSelectedCons) consOptions) consChooseMask = FieldMask {touched=False,valid=optional || isJust mbSelectedCons,state=maybe JSONNull JSONInt mbSelectedCons} genConsViewUI gtd_conses selectedCons = (uia UITextView (valueAttr (JSONString (gtd_conses !! selectedCons))), newFieldMask) //Update is a constructor switch onEdit dp ([],JSONArray [JSONInt consIdx]) (i, val) (CompoundMask {fields=[FieldMask {FieldMask|touched,valid,state}:masks]}) vst//=:{VSt|mode} | not (trace_tn ("cons switch from " +++ toString i +++ " to " +++ toString consIdx)) = undef # cured = snd (cs !! consIdx) | consIdx < 0 || consIdx >= numConses = (Error "Constructor selection out of bounds", (i, val),vst) //Create a default value for the selected constructor //This is a rather ugly trick: We create a special target path that consists only of negative values that is //decoded by the the onEdit instance of EITHER to create a value that consists of the correct nesting of LEFT's and RIGHT's // //TODO hier zit de bug # (_,val,vst) = cured.Editor.onEdit dp (consCreatePath consIdx numConses,JSONNull) val newCompoundMask vst //Create a UI for the new constructor = case cured.Editor.genUI dp val {vst & mode = Enter} of (Ok (UI _ attr items, CompoundMask {fields=masks}),vst) //Construct a UI change that does the following: //1: If necessary remove the fields of the previously selected constructor # removals = case state of // (JSONInt prevConsIdx) = repeatn (length (snd (cs !! prevConsIdx))) (1,RemoveChild) // (JSONInt prevConsIdx) = repeatn (length items) (1,RemoveChild) _ = [] //2: Inserts the fields of the newly created ui # inserts = [(i,InsertChild ui) \\ ui <- items & i <- [1..]] # change = ChangeUI [] (removals ++ inserts) //Create a new mask for the constructor selection # consChooseMask = FieldMask {touched=True,valid=True,state=JSONInt consIdx} = (Ok (change,CompoundMask {fields=[consChooseMask:masks],state=JSONNull}), (consIdx, val), vst) (Error e,vst) = (Error e, (i, val), vst) //Other events targeted directly at the ADT onEdit dp ([],e) (i, val) (CompoundMask {fields=[consChooseMask=:(FieldMask {FieldMask|touched,valid,state}):masks]}) vst=:{VSt|optional} | e =: JSONNull || e =: (JSONArray []) // A null or an empty array are accepted as a reset events //If necessary remove the fields of the previously selected constructor # change = case state of // (JSONInt prevConsIdx) = ChangeUI [] (repeatn 32 /*(gtd_conses !! prevConsIdx).gcd_arity */(1,RemoveChild)) _ = NoChange # consChooseMask = FieldMask {touched=True,valid=optional,state=JSONNull} = (Ok (change,CompoundMask {fields=[consChooseMask:masks],state=JSONNull}),(i, val), vst) = (Error "Unknown constructor select event ",(i, val),vst) //Update is targeted somewhere inside this value onEdit dp (tp,e) (i, val) mask=:(CompoundMask {fields,state}) vst # cured = snd (cs !! i) //Adjust for the added constructor switch UI # consChooseMask = hd fields = case cured.Editor.onEdit dp (tp,e) val (CompoundMask {fields=tl fields,state=JSONNull}) vst of (Ok (change,CompoundMask {fields}),val,vst) # change = case change of (ChangeUI attrChanges itemChanges) = ChangeUI attrChanges [(i + 1,c) \\ (i,c) <- itemChanges] _ = NoChange = (Ok (change,CompoundMask {fields=[consChooseMask:fields],state=JSONNull}),(i, val),vst) (Error e,val,vst) = (Error e, (i, val), vst) consCreatePath i n | i >= n = [] | n == 1 = [] | i < (n /2) = [ -1: consCreatePath i (n/2) ] | otherwise = [ -2: consCreatePath (i - (n/2)) (n - (n/2)) ] onRefresh dp (i, new) (j, old) mask=:(CompoundMask {fields}) vst=:{VSt|mode,taskId,optional,selectedConsIndex=curSelectedConsIndex} # cured = snd (cs !! i) | numConses == 1 # (change,val,vst) = cured.Editor.onRefresh dp new old mask vst = (change,(j, val),vst) | otherwise //Adjust for the added constructor view/choose UI # consChooseMask = hd fields //Don't recursively refresh if no constructor has been chosen | (not mode =: View) && consChooseMask =: (FieldMask {FieldMask|state=JSONNull}) = (Ok (NoChange,mask),(i, old),vst) = case cured.Editor.onRefresh dp new old (CompoundMask {fields=tl fields,state=JSONNull}) {vst & selectedConsIndex = 0} of (Ok (change,CompoundMask {fields}),val,vst=:{VSt|selectedConsIndex}) //If the cons was changed we need to update the selector # consIndex = ~selectedConsIndex - 1 # consChange = if (selectedConsIndex < 0) [(0, ChangeChild (ChangeUI [SetAttribute "value" (JSONArray [JSONInt consIndex,JSONBool True])] []))] [] //Adjust the changes # change = case change of NoChange = if (consChange =: []) NoChange (ChangeUI [] consChange) (ChangeUI attrChanges itemChanges) = ChangeUI attrChanges (consChange ++ [(i + 1,c) \\ (i,c) <- itemChanges]) (ReplaceUI ui=:(UI type attr items)) //Add the constructor selection/view ui # (consUI,_) = if (mode =: View) (genConsViewUI conses consIndex) (genConsChooseUI taskId dp optional conses (Just consIndex)) = ReplaceUI (UI type attr [consUI:items]) | otherwise = (Ok (change, CompoundMask {fields=[consChooseMask:fields],state=JSONNull}), (j, val),{vst & selectedConsIndex = curSelectedConsIndex}) (Ok (change,mask),val,vst=:{VSt|selectedConsIndex}) = (Error "Corrupt mask in generic choose editor",(i, old), vst) (Error e,val,vst) = (Error e,(i, val),vst) onRefresh dp (i, new) (j, old) mask vst = (Error "Corrupt mask in generic choose editor",(j, old), vst)