Commit e3da426a authored by Bas Lijnse's avatar Bas Lijnse

Created basic support for move commands in list editor. Had to add MoveChild...

Created basic support for move commands in list editor. Had to add MoveChild command in the UIChange type. The layout primitives still need to be updated for this instruction.
parent 13ff01a0
......@@ -269,6 +269,18 @@ itasks.Component = {
}
me.children.splice(idx,1);
},
moveChild: function(sidx,didx) {
var me = this, child;
if(me.initialized) {
if(didx == (me.containerEl.children.length - 1)) {
me.containerEl.appendChild(me.containerEl.children[sidx]);
} else {
me.containerEl.insertBefore(me.containerEl.children[sidx],me.containerEl.children[(didx > sidx) ? (didx + 1) : didx]);
}
}
child = me.children.splice(sidx,1)[0]; //Remove followed by insert...
me.children.splice((didx > sidx) ? (didx - 1) : didx, 0, child);
},
beforeChildRemove: function(idx) {},
setAttribute: function(name,value) {
var me = this;
......@@ -327,6 +339,8 @@ itasks.Component = {
case 'remove':
me.removeChild(idx);
break;
case 'move':
me.moveChild(idx,change[2]);
}
});
},
......
......@@ -39,6 +39,7 @@ derive class iTask UITreeNode
:: UIChildChange = ChangeChild !UIChange //Select a sub-component and apply the change definition there
| RemoveChild //Remove the child at the given index (next children 'move down')
| InsertChild !UI //Insert a new child at the given index (next children 'move up')
| MoveChild !Int //Move an existing child a given index to a new index
derive class iTask UIChange, UIAttributeChange, UIChildChange
......
......@@ -408,4 +408,5 @@ where
encodeChildChange (i,ChangeChild child) = JSONArray [JSONInt i,JSONString "change",encodeUIChange child]
encodeChildChange (i,RemoveChild) = JSONArray [JSONInt i,JSONString "remove"]
encodeChildChange (i,InsertChild child) = JSONArray [JSONInt i,JSONString "insert",encodeUI child]
encodeChildChange (i,MoveChild ni) = JSONArray [JSONInt i,JSONString "move",JSONInt ni]
......@@ -35,12 +35,16 @@ from GenEq import generic gEq
*/
:: EditMask
= FieldMask !FieldMask
| CompoundMask ![EditMask]
| CompoundMask !CompoundMask
:: FieldMask =
{ touched :: !Bool
//, version :: !Int
, valid :: !Bool
, state :: !JSONNode //Usually contains the (serialized) value
}
:: CompoundMask =
{ fields :: ![EditMask]
, state :: !JSONNode
}
......
......@@ -8,15 +8,15 @@ import qualified Data.Map as DM
import Text, Text.JSON
import GenEq
derive JSONEncode EditMask, FieldMask
derive JSONDecode EditMask, FieldMask
derive gEq EditMask, FieldMask
derive JSONEncode EditMask, FieldMask, CompoundMask
derive JSONDecode EditMask, FieldMask, CompoundMask
derive gEq EditMask, FieldMask, CompoundMask
newFieldMask :: EditMask
newFieldMask = FieldMask {FieldMask|touched=False,valid=True,state=JSONNull}
newCompoundMask :: EditMask
newCompoundMask = CompoundMask []
newCompoundMask = CompoundMask {CompoundMask|fields=[],state=JSONNull}
editorId :: !DataPath -> String
editorId dp = "v" + join "-" (map toString dp)
......@@ -27,16 +27,16 @@ s2dp str
= map toInt (split "-" (subString 1 (textSize str) str))
subMasks :: !Int EditMask -> [EditMask]
subMasks n (CompoundMask ms) = ms
subMasks n (CompoundMask {CompoundMask|fields}) = fields
subMasks n m = repeatn n m
isTouched :: !EditMask -> Bool
isTouched (FieldMask {FieldMask|touched}) = touched
isTouched (CompoundMask ms) = or (map isTouched ms)
isTouched (CompoundMask {CompoundMask|fields}) = or (map isTouched fields)
containsInvalidFields :: !EditMask -> Bool
containsInvalidFields (FieldMask {FieldMask|valid}) = not valid
containsInvalidFields (CompoundMask ms) = or (map containsInvalidFields ms)
containsInvalidFields (CompoundMask {CompoundMask|fields}) = or (map containsInvalidFields fields)
checkMask :: !EditMask a -> Maybe a
checkMask mask val
......
implementation module iTasks.UI.Editor.Common
import StdBool
import StdBool, StdEnum, StdOrdList
import iTasks.UI.Definition, iTasks.UI.Editor
import Data.Tuple, Data.Error, Text, Text.JSON
import qualified Data.Map as DM
......@@ -18,10 +19,10 @@ where
genUI dp val vst=:{VSt|taskId,mode} = case genChildUIs dp 0 val [] vst of
(Ok (items,masks),vst)
//Add list structure editing buttons
# items = if (not (mode =: View) && (remove || reorder)) [listItemUI taskId dp (length val) idx dx \\ dx <- items & idx <- [0..]] items
# items = if (not (mode =: View) && (remove || reorder)) [listItemUI taskId dp (length val) idx idx dx \\ dx <- items & idx <- [0..]] items
//Add the add button
# items = if (not (mode =: View) && add =: Just _) (items ++ [addItemControl val]) items
= (Ok (uic UIContainer items,CompoundMask masks), vst)
= (Ok (uic UIContainer items,CompoundMask {fields=masks,state=toJSON (indexList val)}), vst)
(Error e,vst) = (Error e,vst)
where
genChildUIs dp _ [] us vst = (Ok (unzip (reverse us)), vst)
......@@ -35,41 +36,49 @@ where
# attr = 'DM'.unions [halignAttr AlignRight,heightAttr WrapSize,directionAttr Horizontal]
= uiac UIContainer attr (counter ++ button)
listItemUI taskId dp numItems idx item
listItemUI taskId dp numItems idx id item
# buttons = (if reorder
[uia UIButton ('DM'.unions [iconClsAttr "icon-up", enabledAttr (idx <> 0), editAttrs taskId (editorId dp) (Just (JSONString ("mup_" +++ toString idx)))])
,uia UIButton ('DM'.unions [iconClsAttr "icon-down", enabledAttr (idx <> numItems - 1), editAttrs taskId (editorId dp) (Just (JSONString ("mdn_" +++ toString idx)))])
[uia UIButton ('DM'.unions [iconClsAttr "icon-up", enabledAttr (idx <> 0), editAttrs taskId (editorId dp) (Just (JSONString ("mup_" +++ toString id)))])
,uia UIButton ('DM'.unions [iconClsAttr "icon-down", enabledAttr (idx <> numItems - 1), editAttrs taskId (editorId dp) (Just (JSONString ("mdn_" +++ toString id)))])
] []) ++
(if remove
[uia UIButton ('DM'.unions [iconClsAttr "icon-remove",editAttrs taskId (editorId dp) (Just (JSONString ("rem_" +++ toString idx)))])
[uia UIButton ('DM'.unions [iconClsAttr "icon-remove",editAttrs taskId (editorId dp) (Just (JSONString ("rem_" +++ toString id)))])
] [])
# attr = 'DM'.unions [halignAttr AlignRight,heightAttr WrapSize,directionAttr Horizontal]
= uiac UIContainer attr (if (reorder || remove) ([item] ++ buttons) [item])
//Structural edits on the list
onEdit dp ([],JSONString e) items (CompoundMask masks) vst=:{VSt|taskId}
# [op,index:_] = split "_" e
# index = toInt index
onEdit dp ([],JSONString e) items (CompoundMask {fields=masks,state}) vst=:{VSt|taskId}
# ids = fromMaybe [] (fromJSON state) //All item UI's have a unique id that is used in the data-paths of that UI
# [op,id:_] = split "_" e
# id = toInt id
# index = itemIndex id ids
| op == "mup" && reorder
= (Ok (NoChange,CompoundMask (swap masks index)), (swap items index), vst) //TODO: ChangeUI instruction
| index < 1 || index >= (length items) = (Error "List move-up out of bounds",items,vst)
= (Ok (ChangeUI [] [(index,MoveChild (index - 1))],CompoundMask {fields=(swap masks index),state=toJSON (swap ids index)}), (swap items index), vst)
| op == "mdn" && reorder
= (Ok (NoChange,CompoundMask (swap masks (index + 1))), (swap items (index + 1)), vst) //TODO: ChangeUI instruction
| index < 0 || index > (length items - 2) = (Error "List move-down out of bounds",items,vst)
= (Ok (ChangeUI [] [(index,MoveChild (index + 1))],CompoundMask {fields=(swap masks (index + 1)),state=toJSON (swap ids (index + 1))}), (swap items (index + 1)), vst)
| op == "rem" && remove
= (Ok (NoChange,CompoundMask (removeAt index masks)), (removeAt index items), vst) //TODO: ChangeUI instruction
| index < 0 || index >= (length items) = (Error "List remove out of bounds",items,vst)
= (Ok (ChangeUI [] [(index,RemoveChild)],CompoundMask {fields=removeAt index masks,state=toJSON (removeAt index ids)}), (removeAt index items), vst)
| op == "add" && add =: (Just _)
# f = fromJust add
# nx = f items
# ni = length items
= case itemEditor.Editor.genUI (dp++[ni]) nx vst of
# nid = nextId ids
= case itemEditor.Editor.genUI (dp++[nid]) nx vst of
(Error e,vst) = (Error e,items,vst)
(Ok (ui,nm),vst)
# nitems = items ++ [nx]
# nmasks = masks ++ [nm]
# insert = [(ni,InsertChild (listItemUI taskId dp (ni + 1) ni ui))]
# nids = ids ++ [nid]
# insert = [(ni,InsertChild (listItemUI taskId dp (ni + 1) ni nid ui))]
# counter = maybe [] (\f -> [(ni + 1, ChangeChild (ChangeUI [] [(0,ChangeChild (ChangeUI [SetAttribute "value" (JSONString (f nitems))] []))]))]) count
# change = ChangeUI [] (insert ++ counter)
= (Ok (change,CompoundMask nmasks),nitems,vst)
= (Ok (NoChange,CompoundMask masks),items,vst)
# prevdown = if (ni > 0) [(ni - 1,ChangeChild (ChangeUI [] [(2,ChangeChild (ChangeUI [SetAttribute "enabled" (JSONBool True)] []))]))] []
# change = ChangeUI [] (insert ++ counter ++ prevdown)
= (Ok (change,CompoundMask {fields=nmasks,state=toJSON nids}),nitems,vst)
= (Ok (NoChange,CompoundMask {fields=masks,state=toJSON ids}),items,vst)
where
swap [] _ = []
swap list index
......@@ -81,14 +90,16 @@ where
= updateAt (index-1) l (updateAt index f list)
//Edits inside the list
onEdit dp ([i:tp],e) items (CompoundMask masks) vst
| i < 0 || i >= length items = (Error "List edit out of bounds",items,vst)
onEdit dp ([id:tp],e) items (CompoundMask {fields=masks,state}) vst
# ids = fromMaybe [] (fromJSON state)
# index = itemIndex id ids
| index < 0 || index >= length items = (Error "List edit out of bounds",items,vst)
| otherwise
= case itemEditor.Editor.onEdit (dp ++ [i]) (tp,e) (items !! i) (masks !! i) vst of
= case itemEditor.Editor.onEdit (dp ++ [id]) (tp,e) (items !! index) (masks !! index) vst of
(Error e,nx,vst)
= (Error e, items,vst)
(Ok (change,nm),nx,vst)
= (Ok (childChange i change,CompoundMask (updateAt i nm masks)), (updateAt i nx items),vst)
= (Ok (childChange index change,CompoundMask {fields=updateAt index nm masks,state=state}), (updateAt index nx items),vst)
where
childChange i NoChange = NoChange
childChange i change = ChangeUI [] [(i,ChangeChild (ChangeUI [] [(0,ChangeChild change)]))]
......@@ -100,3 +111,11 @@ where
(Ok (ui,mask),vst) = (Ok (ReplaceUI ui,mask),new,vst)
(Error e,vst) = (Error e,old,vst)
*/
nextId [] = 0
nextId ids = maxList ids + 1
itemIndex id ids = itemIndex` 0 id ids
where
itemIndex` _ _ [] = -1
itemIndex` i id [x:xs] = if (id == x) i (itemIndex` (i + 1) id xs)
This diff is collapsed.
......@@ -59,8 +59,6 @@ where
= (ChangeUI attrChanges itemChanges,s)
layout (change,s) = (change,s)
copyAttributes :: [String] NodePath NodePath -> Layout
copyAttributes selection src dst = copyAttributes` (Just selection) src dst
......@@ -230,7 +228,7 @@ where
(before,equal,after) = split idx ms
//Adjust an individual child change
//Known precondition: moves only holds moves with an index >= the index of the change
//Known precondition: only holds for moves with an index >= the index of the change
//Replacements
adjustChildChange targetIdx numRem (idx,ChangeChild change=:(ReplaceUI ui)) mbMove movesAfter = case mbMove of
......@@ -291,6 +289,9 @@ where
| otherwise //One ore more sub nodes matched, we need to record the moves for this branch
= (targetIdx, change, Just (idx,Right subMoves), movesAfter, subInserts)
adjustChildChange targetIdx numRem (idx, MoveChild nidx) mbMove movesAfter //TODO
= (targetIdx,Just (idx,MoveChild nidx),Nothing,movesAfter, [])
insertAndAdjust_ :: NodePath Int Int [(Int,UIChildChange)] UIChange -> UIChange
insertAndAdjust_ path=:[] startIdx numInserts insertChanges change = case change of //Add the inserts here
NoChange
......@@ -410,9 +411,11 @@ where
= ((i,ChangeChild change),states)
layoutChildChange_ path pred layout (i,InsertChild ui) states
# (ui,eitherState) = layoutUI_ (path ++ [i]) pred layout ui
= ((i,InsertChild ui),[(i,eitherState):[(i + 1,s) \\ (i,s) <- states]]) //Aslo adjust the indices of the other states
= ((i,InsertChild ui),[(i,eitherState):[(i + 1,s) \\ (i,s) <- states]]) //Also adjust the indices of the other states
layoutChildChange_ path pred layout (idx,RemoveChild) states
= ((idx,RemoveChild),[(i - 1, s) \\ (i,s) <- states | i <> idx]) //Remove the current state from the states and adjust the indices accordingly
layoutChildChange_ path pred layout (idx,MoveChild nidx) states //Adjust the indices TODO
= ((idx,MoveChild nidx),states)
selectState idx states = case splitWith (((==) idx) o fst) states of
([(_,s):_],states) = (Just s,states)
......
......@@ -7,7 +7,7 @@ import iTasks._Framework.IWorld
import qualified Data.Map as DM
import StdMisc
derive gText EditMask, FieldMask
derive gText EditMask, FieldMask, CompoundMask
//COMPLEX TYPES FOR TESTING
......@@ -80,7 +80,7 @@ testRealUpdate = testGenUI "Update Real"
testConsFieldsUpdate = testGenUI "Update constructor fields"
(uic UICons [fieldExp "v0" 1, fieldExp "v1" 2, fieldExp "v2" 3, fieldExp "v3" 4,fieldExp "v4" 5,fieldExp "v5" 6]
,CompoundMask [maskExp n \\ n <- [1..6]])
,CompoundMask {fields=[maskExp n \\ n <- [1..6]],state=JSONNull})
(TestConsFields 1 2 3 4 5 6) Update
where
fieldExp editorId val =
......@@ -155,6 +155,9 @@ testGenericEditorEdits = testsuite "Generic edits" "Tests for processing edits b
[testEditConsChange
,testEditListElement
,testAddListElement
,testMoveListElementUp
,testMoveListElementDown
,testRemoveListElement
]
testGenEdit :: String (a,EditMask,UIChange) (a,EditMask) (DataPath,JSONNode) -> Test | iTask a
......@@ -176,18 +179,18 @@ testEditConsChange = skip "Change constructor"
*/
testEditListElement = testGenEdit "List element edit"
([42],CompoundMask [FieldMask {touched=True,valid=True,state=JSONInt 42}]
([42],CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 42}],state=JSONNull}
,ChangeUI [] [(0, ChangeChild (ChangeUI [SetAttribute "value" (JSONInt 42), SetAttribute "hint" (JSONString "You have correctly entered a whole number"), SetAttribute "hint-type" (JSONString "valid")] []))])
([0],CompoundMask [FieldMask {touched=True,valid=True,state=JSONNull}])
([0],CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONNull}],state=JSONNull})
([0],JSONInt 42)
testAddListElement = testGenEdit "List element add"
([42,0],CompoundMask [FieldMask {touched=True,valid=True,state=JSONInt 42},FieldMask {touched=False,valid=False,state=JSONNull}]
([42,0],CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 42},FieldMask {touched=False,valid=False,state=JSONNull}],state=JSONNull}
,ChangeUI [] [(1,InsertChild elui)
,(2,ChangeChild (ChangeUI [] [(0, ChangeChild (ChangeUI [SetAttribute "value" (JSONString "2 items")] []))]))
])
([42],CompoundMask [FieldMask {touched=True,valid=True,state=JSONInt 42}])
([42],CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 42}],state=JSONNull})
([],JSONString "add")
where
elui = uiac UIContainer ('DM'.fromList [("direction",JSONString "horizontal"),("halign",JSONString "right"),("height",JSONString "wrap")]) [inui:buis]
......@@ -202,8 +205,23 @@ where
,uia UIButton ('DM'.fromList [("iconCls",JSONString "icon-remove"),("value",JSONString "rem_1"),("taskId",JSONString "STUB"),("editorId",JSONString "v")])
]
testMoveListElementUp = testGenEdit "Move list element up"
([2,1,3],CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 2},FieldMask {touched=True,valid=True,state=JSONInt 1},FieldMask {touched=True,valid=True,state=JSONInt 3}],state=JSONNull},ChangeUI [] [(1,MoveChild 0)])
([1,2,3], CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 1},FieldMask {touched=True,valid=True,state=JSONInt 2},FieldMask {touched=True,valid=True,state=JSONInt 3}],state=JSONNull})
([],JSONString "mup_1")
testMoveListElementDown = testGenEdit "Move list element down"
([1,3,2],CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 1},FieldMask {touched=True,valid=True,state=JSONInt 3},FieldMask {touched=True,valid=True,state=JSONInt 2}],state=JSONNull},ChangeUI [] [(1,MoveChild 2)])
([1,2,3], CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 1},FieldMask {touched=True,valid=True,state=JSONInt 2},FieldMask {touched=True,valid=True,state=JSONInt 3}],state=JSONNull})
([],JSONString "mdn_1")
testRemoveListElement = testGenEdit "Remove list element"
([1,2],CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 1},FieldMask {touched=True,valid=True,state=JSONInt 2}],state=JSONNull},ChangeUI [] [(2,RemoveChild)])
([1,2,3], CompoundMask {fields=[FieldMask {touched=True,valid=True,state=JSONInt 1},FieldMask {touched=True,valid=True,state=JSONInt 2},FieldMask {touched=True,valid=True,state=JSONInt 3}],state=JSONNull})
([],JSONString "rem_2")
testGenericEditorRefreshes :: TestSuite
testGenericEditorRefreshes = testsuite "Generic diffs" "Tests for the generic diffs"
testGenericEditorRefreshes = testsuite "Generic refresh" "Tests for the generic refresh functions"
[testSameInt
,testDifferentInt1
,testDifferentInt2
......@@ -262,7 +280,7 @@ testDiffConsFields2
] []))])
(TestConsFields 1 2 3 44 5 6)
(TestConsFields 1 2 3 4 5 6)
(CompoundMask [newFieldMask,newFieldMask,newFieldMask,newFieldMask,newFieldMask,newFieldMask])
(CompoundMask {fields=[newFieldMask,newFieldMask,newFieldMask,newFieldMask,newFieldMask,newFieldMask],state=JSONNull})
testDiffRecordFields :: Test
testDiffRecordFields
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment