Commit 8ebd0c58 authored by Bas Lijnse's avatar Bas Lijnse

More UI diff improvements

git-svn-id: https://svn.cs.ru.nl/repos/iTask-system/trunk@2370 63da3aa8-80fd-4f01-9db8-e6ea747a3da2
parent 55382fe3
...@@ -26,10 +26,8 @@ Ext.define('itwc.container.Panel',{ ...@@ -26,10 +26,8 @@ Ext.define('itwc.container.Panel',{
this.callParent(arguments); this.callParent(arguments);
}, },
afterRender: function() { afterRender: function() {
var me = this; this.callParent(arguments);
this.initHotkeys();
me.callParent();
me.initHotkeys();
}, },
onDestroy: function () { onDestroy: function () {
this.destroyHotkeys(); this.destroyHotkeys();
......
...@@ -2,7 +2,8 @@ Ext.define('itwc.container.Window',{ ...@@ -2,7 +2,8 @@ Ext.define('itwc.container.Window',{
extend: 'Ext.window.Window', extend: 'Ext.window.Window',
alias: 'widget.itwc_window', alias: 'widget.itwc_window',
requires: ['itwc.layout.container.Box'], requires: ['itwc.layout.container.Box'],
mixins: ['itwc.component.edit.Editable'], mixins: ['itwc.component.edit.Editable','itwc.container.HotkeyArea'],
autoShow: true, autoShow: true,
autoScroll: true, autoScroll: true,
...@@ -45,5 +46,13 @@ Ext.define('itwc.container.Window',{ ...@@ -45,5 +46,13 @@ Ext.define('itwc.container.Window',{
me.viewport = me.findViewport(); me.viewport = me.findViewport();
me.viewport.fireEvent('action',me.closeTaskId,'Close'); me.viewport.fireEvent('action',me.closeTaskId,'Close');
return false; return false;
},
afterRender: function() {
this.callParent(arguments);
this.initHotkeys();
},
onDestroy: function () {
this.destroyHotkeys();
this.callParent(arguments);
} }
}); });
...@@ -223,28 +223,34 @@ Ext.define('itwc.controller.Controller', { ...@@ -223,28 +223,34 @@ Ext.define('itwc.controller.Controller', {
partialUpdate: function(updates) { partialUpdate: function(updates) {
var me = this, var me = this,
numUpdates = updates.length, numUpdates = updates.length,
update, cmp, i; update,
cmp, operations, numOperations, operation, i, j;
for(i = 0; i < numUpdates; i++) { for(i = 0; i < numUpdates; i++) {
update = updates[i]; update = updates[i];
try { try {
if(cmp = me.viewport.getComponentByPath(update.path)) { if(cmp = me.viewport.getComponentByPath(update.path)) {
//Try to call the update method operations = update.operations;
if(cmp && typeof cmp[update.method] == 'function') { numOperations = operations.length;
cmp[update.method].apply(cmp,update.arguments);
} else { //If multiple operations need to be done on the same component, don't layout in between
//If replace is not defined as function, try remove followed by add if(numOperations > 1 && cmp.doLayout) {
if(update.method == 'replace' && typeof cmp['remove'] == 'function' && typeof cmp['insert'] == 'function') { cmp.suspendLayout = true;
me.warn("Doing inefficient replace by using remove followed by add in " + update.arguments[1].xtype); }
cmp.suspendLayout = true; //Don't layout in between remove and add for(j = 0; j < numOperations; j++) {
cmp.remove(update.arguments[0]); operation = operations[j];
cmp.insert(update.arguments[0],update.arguments[1]);
cmp.suspendLayout = false; //Try to call the update method
cmp.doLayout(); //Now do the layout if(cmp && typeof cmp[operation.method] == 'function') {
cmp[operation.method].apply(cmp,operation.arguments);
} else { } else {
me.error("Can't apply " + update.method + " to " + cmp.getId() + " (" + cmp.getXType() + ")"); me.error("Can't apply " + operation.method + " to " + cmp.getId() + " (" + cmp.getXType() + ")");
} }
} }
if(cmp.suspendLayout) {
cmp.suspendLayout = false;
cmp.doLayout();
}
} else { } else {
me.error("Could not find user interface component at location " + update.path); me.error("Could not find user interface component at location " + update.path);
} }
......
...@@ -301,22 +301,22 @@ sequenceMerge :: ParallelLayout ...@@ -301,22 +301,22 @@ sequenceMerge :: ParallelLayout
sequenceMerge = merge sequenceMerge = merge
where where
merge prompt=:{UIControlSequence|attributes,controls,direction} defs merge prompt=:{UIControlSequence|attributes,controls,direction} defs
# (actions,parts) = unzip (map processDef defs) # parts = (map processDef defs)
# controls = decoratePrompt controls ++ [c \\ Just (Left c) <- parts] # controls = decoratePrompt controls ++ [c \\ (_,_,Just c) <- parts]
# windows = [w \\ Just (Right w) <- parts] # actions = flatten [a \\ (a,_,_) <- parts]
# actions = foldr (++) [] actions # windows = flatten [w \\ (_,w,_) <- parts]
= UIAbstractContainer {UIAbstractContainer|attributes=attributes,controls=controls,direction=direction,actions=actions,windows=windows,hotkeys=[]} = UIAbstractContainer {UIAbstractContainer|attributes=attributes,controls=controls,direction=direction,actions=actions,windows=windows,hotkeys=[]}
//Action sets do not get a panel in the sequence. Their actions are passed upwards //Action sets do not get a panel in the sequence. Their actions are passed upwards
processDef (UIActionSet {UIActionSet|actions}) processDef (UIActionSet {UIActionSet|actions})
= (actions,Nothing) = (actions,[],Nothing)
processDef def processDef def
| hasWindowAttr (uiDefAttributes def) //TODO: Pass hotkeys along | hasWindowAttr (uiDefAttributes def) //TODO: Pass hotkeys along
# (actions,_, window) = placeWindowActions (uiDefActions def) (defToWindow (layoutControls def)) # (actions,_, window) = placeWindowActions (uiDefActions def) (defToWindow (layoutControls def))
= ([],Just (Right window)) = ([],[window:uiDefWindows def],Nothing)
| otherwise | otherwise
# (actions,_, panel) = placePanelActions (uiDefActions def) False (defToPanel (layoutControls def)) # (actions,_, panel) = placePanelActions (uiDefActions def) False (defToPanel (layoutControls def))
= (actions,Just (Left panel)) = (actions,uiDefWindows def,Just panel)
sideMerge :: UISide Int ParallelLayout -> ParallelLayout sideMerge :: UISide Int ParallelLayout -> ParallelLayout
sideMerge side size restMerge = merge sideMerge side size restMerge = merge
...@@ -395,7 +395,7 @@ where ...@@ -395,7 +395,7 @@ where
# (tabsAndWindows,actions) = unzip [mkTabOrWindow (i == active) d \\ d <- defs & i <- [0..]] # (tabsAndWindows,actions) = unzip [mkTabOrWindow (i == active) d \\ d <- defs & i <- [0..]]
= ((setDirection Horizontal o setHeight WrapSize o setBaseCls "x-tab-bar") (defaultContainer [tab \\Left tab <- tabsAndWindows]) = ((setDirection Horizontal o setHeight WrapSize o setBaseCls "x-tab-bar") (defaultContainer [tab \\Left tab <- tabsAndWindows])
,[window \\ Right window <- tabsAndWindows] ,[window \\ Right window <- tabsAndWindows]
,foldr (++) [] actions ,flatten actions
) )
mkTabOrWindow active def mkTabOrWindow active def
...@@ -412,7 +412,6 @@ where ...@@ -412,7 +412,6 @@ where
# tabOpts = {text = text ,focusTaskId = taskId, active = active, closeTaskId = close,iconCls=iconCls} # tabOpts = {text = text ,focusTaskId = taskId, active = active, closeTaskId = close,iconCls=iconCls}
= (Left (UITab defaultSizeOpts tabOpts), if active actions []) = (Left (UITab defaultSizeOpts tabOpts), if active actions [])
hideLayout :: Layout hideLayout :: Layout
hideLayout = hideLayout =
{ editor = \prompt -> [] { editor = \prompt -> []
......
...@@ -3,34 +3,9 @@ definition module UIDiff ...@@ -3,34 +3,9 @@ definition module UIDiff
import UIDefinition import UIDefinition
from Task import :: Event from Task import :: Event
:: UIUpdate = UIUpdate !UIPath !UIUpdateOperation :: UIUpdate = UIUpdate !UIPath ![UIUpdateOperation]
:: UIUpdateOperation :: UIUpdateOperation :== (String,[JSONNode])
//Component updates
= UISetValue !JSONNode // Set the value of a component
| UISetOptions !JSONNode // Change the options in a choice component
| UISetTaskId !String // Set taskId a component belongs to
| UISetCloseTaskId !(Maybe String)
| UISetFocusTaskId !(Maybe String)
| UISetEditorId !String // Set editorId a component belongs to
| UISetActionId !String // Set actionId a component belongs to
| UISetName !String // Set name of a component
| UISetEnabled !Bool // Enable/disable form elements
| UISetActive !Bool // Make a tab active/inactive
| UISetTitle !(Maybe String) // Set/reset title of a container
| UISetText !(Maybe String) // Set/reset text of a button
| UISetIconCls !(Maybe String) // Set/reset icon of component
| UISetTooltip !(Maybe String) // Set/reset tooltip of a component
| UISetHotkeys ![UIKeyAction] // Set hotkeys for a container
| UISelfUpdate !UIControl // Let a component update itself with a new UI definition (for custom components)
//Structure edits
| UIAdd !Int !UIControl //Add child element at index
| UIRemove !Int //Remove child element at index
| UIReplace !Int !UIControl //Replace child element at index
| UIAddWindow !Int !UIWindow //Add a window
| UIRemoveWindow !Int //Remove a window
//Changing size
| UIResize !UISizeOpts
:: UIPath :== [UIStep] :: UIPath :== [UIStep]
:: UIStep :: UIStep
= ItemStep !Int //Select item i = ItemStep !Int //Select item i
......
...@@ -21,7 +21,7 @@ diffUIDefinitions :: !UIDef !UIDef !Event -> [UIUpdate] ...@@ -21,7 +21,7 @@ diffUIDefinitions :: !UIDef !UIDef !Event -> [UIUpdate]
diffUIDefinitions (UIFinal (UIViewport iOpts1 opts1)) (UIFinal (UIViewport iOpts2 opts2)) event diffUIDefinitions (UIFinal (UIViewport iOpts1 opts1)) (UIFinal (UIViewport iOpts2 opts2)) event
= diffItems [] event iOpts1.UIItemsOpts.items iOpts2.UIItemsOpts.items = diffItems [] event iOpts1.UIItemsOpts.items iOpts2.UIItemsOpts.items
++ diffAllWindows event opts1.UIViewportOpts.windows opts2.UIViewportOpts.windows ++ diffAllWindows event opts1.UIViewportOpts.windows opts2.UIViewportOpts.windows
++ diffHotkeys [] (fromMaybe [] opts1.UIViewportOpts.hotkeys) (fromMaybe [] opts2.UIViewportOpts.hotkeys) ++ (case (diffHotkeys (fromMaybe [] opts1.UIViewportOpts.hotkeys) (fromMaybe [] opts2.UIViewportOpts.hotkeys)) of [] = []; ops = [UIUpdate [] ops])
diffUIDefinitions d1 d2 event diffUIDefinitions d1 d2 event
= diffItems [] event (uiDefControls d1) (uiDefControls d2) = diffItems [] event (uiDefControls d1) (uiDefControls d2)
...@@ -88,23 +88,22 @@ diffControls path event c1 c2 ...@@ -88,23 +88,22 @@ diffControls path event c1 c2
// check their instance id. Different: replace, Equals: update (mostly taskId) // check their instance id. Different: replace, Equals: update (mostly taskId)
(UITasklet sOpts1 opts1, UITasklet sOpts2 opts2) (UITasklet sOpts1 opts1, UITasklet sOpts2 opts2)
| opts1.UITaskletOpts.iid == opts2.UITaskletOpts.iid | opts1.UITaskletOpts.iid == opts2.UITaskletOpts.iid
= [DiffPossible [UIUpdate path (UISelfUpdate = [DiffPossible [UIUpdate path [("selfUpdate",[encodeUIControl
(UITaskletPH sOpts2 {UITaskletPHOpts|iid = opts2.UITaskletOpts.iid, taskId = opts2.UITaskletOpts.taskId}))]] (UITaskletPH sOpts2 {UITaskletPHOpts|iid = opts2.UITaskletOpts.iid, taskId = opts2.UITaskletOpts.taskId})])]]]
= [DiffImpossible] = [DiffImpossible]
(UITaskletPH sOpts1 opts1, UITasklet sOpts2 opts2) (UITaskletPH sOpts1 opts1, UITasklet sOpts2 opts2)
| opts1.UITaskletPHOpts.iid == opts2.UITaskletOpts.iid | opts1.UITaskletPHOpts.iid == opts2.UITaskletOpts.iid
= [DiffPossible [UIUpdate path (UISelfUpdate = [DiffPossible [UIUpdate path [("selfUpdate",[encodeUIControl
(UITaskletPH sOpts2 {UITaskletPHOpts|iid = opts2.UITaskletOpts.iid, taskId = opts2.UITaskletOpts.taskId}))]] (UITaskletPH sOpts2 {UITaskletPHOpts|iid = opts2.UITaskletOpts.iid, taskId = opts2.UITaskletOpts.taskId})])]]]
= [DiffImpossible] = [DiffImpossible]
// Placeholder on the right hand side: update // Placeholder on the right hand side: update
(UITaskletPH sOpts1 opts1, UITaskletPH sOpts2 opts2) (UITaskletPH sOpts1 opts1, UITaskletPH sOpts2 opts2)
| opts1.UITaskletPHOpts.iid == opts2.UITaskletPHOpts.iid | opts1.UITaskletPHOpts.iid == opts2.UITaskletPHOpts.iid
= [DiffPossible [UIUpdate path (UISelfUpdate (UITaskletPH sOpts2 opts2))]] = [DiffPossible [UIUpdate path [("selfUpdate",[encodeUIControl (UITaskletPH sOpts2 opts2)])]]]
= [DiffImpossible] = [DiffImpossible]
(UITasklet sOpts1 opts1, UITaskletPH sOpts2 opts2) (UITasklet sOpts1 opts1, UITaskletPH sOpts2 opts2)
| opts1.UITaskletOpts.iid == opts2.UITaskletPHOpts.iid | opts1.UITaskletOpts.iid == opts2.UITaskletPHOpts.iid
= [DiffPossible [UIUpdate path (UISelfUpdate (UITaskletPH sOpts2 opts2))]] = [DiffPossible [UIUpdate path [("selfUpdate",[encodeUIControl (UITaskletPH sOpts2 opts2)])]]]
= [DiffImpossible] = [DiffImpossible]
(UIContainer sOpts1 iOpts1 opts1, UIContainer sOpts2 iOpts2 opts2) (UIContainer sOpts1 iOpts1 opts1, UIContainer sOpts2 iOpts2 opts2)
...@@ -117,34 +116,34 @@ diffControls path event c1 c2 ...@@ -117,34 +116,34 @@ diffControls path event c1 c2
// = [diffSizeOpts path sOpts1 sOpts2,diffItemsOpts path event iOpts1 iOpts2, diffOpts opts1 opts2] // = [diffSizeOpts path sOpts1 sOpts2,diffItemsOpts path event iOpts1 iOpts2, diffOpts opts1 opts2]
(_,_) (_,_)
= [DiffImpossible] = [DiffImpossible]
= DiffPossible (replaceIfImpossible path c2 parts) = DiffPossible (replaceControlIfImpossible path c2 parts)
//As a first step, only do diffs for value changes, all other diffs trigger replacements... //As a first step, only do diffs for value changes, all other diffs trigger replacements...
diffSizeOpts :: UIPath UISizeOpts UISizeOpts -> DiffResult diffSizeOpts :: UIPath UISizeOpts UISizeOpts -> DiffResult
diffSizeOpts path opts1 opts2 diffSizeOpts path opts1 opts2
| opts1 === opts2 = DiffPossible [] | opts1 === opts2 = DiffPossible []
= DiffImpossible // DiffPossible [UIResize (toString path) opts2] = DiffImpossible
diffViewOpts :: UIPath (UIViewOpts a) (UIViewOpts a) -> DiffResult | gEq{|*|} a & encodeUIValue a diffViewOpts :: UIPath (UIViewOpts a) (UIViewOpts a) -> DiffResult | gEq{|*|} a & encodeUIValue a
diffViewOpts path opts1 opts2 diffViewOpts path opts1 opts2
| opts1 === opts2 = DiffPossible [] | opts1 === opts2 = DiffPossible []
= DiffPossible [UIUpdate path (UISetValue (encodeUIValue opts2.UIViewOpts.value))] = DiffPossible [UIUpdate path [("setValue",[encodeUIValue opts2.UIViewOpts.value])]]
diffEditOpts :: UIPath Event (UIEditOpts a) (UIEditOpts a) -> DiffResult | gEq{|*|} a & encodeUIValue a diffEditOpts :: UIPath Event (UIEditOpts a) (UIEditOpts a) -> DiffResult | gEq{|*|} a & encodeUIValue a
diffEditOpts path event opts1 opts2 diffEditOpts path event opts1 opts2
| isEmpty taskIdUpd && isEmpty editorIdUpd | isEmpty taskIdUpd && isEmpty editorIdUpd
= DiffPossible (foldr (++) [] [taskIdUpd,editorIdUpd,valueUpd]) = DiffPossible (flatten [taskIdUpd,editorIdUpd,valueUpd])
| otherwise | otherwise
= DiffImpossible = DiffImpossible
where where
taskIdUpd = if (opts1.UIEditOpts.taskId == opts2.UIEditOpts.taskId) [] [UIUpdate path (UISetTaskId opts2.UIEditOpts.taskId)] taskIdUpd = if (opts1.UIEditOpts.taskId == opts2.UIEditOpts.taskId) [] [UIUpdate path [("setTaskId",[toJSON opts2.UIEditOpts.taskId])]]
editorIdUpd = if (opts1.UIEditOpts.editorId == opts2.UIEditOpts.editorId) [] [UIUpdate path (UISetEditorId opts2.UIEditOpts.editorId)] editorIdUpd = if (opts1.UIEditOpts.editorId == opts2.UIEditOpts.editorId) [] [UIUpdate path [("setEditorId",[toJSON opts2.UIEditOpts.editorId])]]
valueUpd valueUpd
| eventMatch opts2 event | eventMatch opts2 event
# value2 = encodeUIValue opts2.UIEditOpts.value # value2 = encodeUIValue opts2.UIEditOpts.value
= if (eventValue event === value2) [] [UIUpdate path (UISetValue value2)] = if (eventValue event === value2) [] [UIUpdate path [("setValue",[value2])]]
| otherwise | otherwise
= if (opts1.UIEditOpts.value === opts2.UIEditOpts.value) [] [UIUpdate path (UISetValue (encodeUIValue opts2.UIEditOpts.value))] = if (opts1.UIEditOpts.value === opts2.UIEditOpts.value) [] [UIUpdate path [("setValue",[encodeUIValue opts2.UIEditOpts.value])]]
eventMatch {UIEditOpts|taskId,editorId} (EditEvent matchTask matchEditor _) = (taskId == toString matchTask) && (editorId == matchEditor) eventMatch {UIEditOpts|taskId,editorId} (EditEvent matchTask matchEditor _) = (taskId == toString matchTask) && (editorId == matchEditor)
eventMatch _ _ = False eventMatch _ _ = False
...@@ -158,14 +157,14 @@ diffChoiceOpts path opts1 opts2 ...@@ -158,14 +157,14 @@ diffChoiceOpts path opts1 opts2
| opts1.UIChoiceOpts.options =!= opts2.UIChoiceOpts.options = DiffImpossible | opts1.UIChoiceOpts.options =!= opts2.UIChoiceOpts.options = DiffImpossible
= DiffPossible valueDiff //(valueDiff ++ optionDiff) = DiffPossible valueDiff //(valueDiff ++ optionDiff)
where where
valueDiff = if (opts1.UIChoiceOpts.value === opts2.UIChoiceOpts.value) [] [UIUpdate path (UISetValue (toJSON opts2.UIChoiceOpts.value))] valueDiff = if (opts1.UIChoiceOpts.value === opts2.UIChoiceOpts.value) [] [UIUpdate path [("setValue",[toJSON opts2.UIChoiceOpts.value])]]
optionDiff = if (opts1.UIChoiceOpts.options === opts2.UIChoiceOpts.options) [] [UIUpdate path (UISetOptions (toJSON opts2.UIChoiceOpts.options))] optionDiff = if (opts1.UIChoiceOpts.options === opts2.UIChoiceOpts.options) [] [UIUpdate path [("setOptions",[toJSON opts2.UIChoiceOpts.options])]]
diffActionOpts :: UIPath UIActionOpts UIActionOpts -> DiffResult diffActionOpts :: UIPath UIActionOpts UIActionOpts -> DiffResult
diffActionOpts path opts1 opts2 = DiffPossible (flatten [taskIdUpd,actionIdUpd]) diffActionOpts path opts1 opts2 = DiffPossible (flatten [taskIdUpd,actionIdUpd])
where where
taskIdUpd = if (opts1.UIActionOpts.taskId == opts2.UIActionOpts.taskId) [] [UIUpdate path (UISetTaskId opts2.UIActionOpts.taskId)] taskIdUpd = if (opts1.UIActionOpts.taskId == opts2.UIActionOpts.taskId) [] [UIUpdate path [("setTaskId",[toJSON opts2.UIActionOpts.taskId])]]
actionIdUpd = if (opts1.UIActionOpts.actionId == opts2.UIActionOpts.actionId) [] [UIUpdate path (UISetActionId opts2.UIActionOpts.actionId)] actionIdUpd = if (opts1.UIActionOpts.actionId == opts2.UIActionOpts.actionId) [] [UIUpdate path [("setActionId",[toJSON opts2.UIActionOpts.actionId])]]
diffItemsOpts :: UIPath !Event UIItemsOpts UIItemsOpts -> DiffResult diffItemsOpts :: UIPath !Event UIItemsOpts UIItemsOpts -> DiffResult
diffItemsOpts path event opts1 opts2 diffItemsOpts path event opts1 opts2
...@@ -185,13 +184,21 @@ diffOpts opts1 opts2 ...@@ -185,13 +184,21 @@ diffOpts opts1 opts2
selfUpdate :: UIPath UIControl UIControl -> DiffResult selfUpdate :: UIPath UIControl UIControl -> DiffResult
selfUpdate path c1 c2 selfUpdate path c1 c2
| c1 === c2 = DiffPossible [] | c1 === c2 = DiffPossible []
= DiffPossible [UIUpdate path (UISelfUpdate c2)] = DiffPossible [UIUpdate path [("selfUpdate",[encodeUIControl c2])]]
//Group multiple operations in a single component update
diffMultiProperties :: UIPath [[UIUpdateOperation]] -> DiffResult
diffMultiProperties path ops = case flatten ops of
[] = DiffPossible []
ops = DiffPossible [UIUpdate path ops]
//Specialized diffs for the control specific options //Specialized diffs for the control specific options
diffPanelOpts :: UIPath Event UIPanelOpts UIPanelOpts -> DiffResult diffPanelOpts :: UIPath Event UIPanelOpts UIPanelOpts -> DiffResult
diffPanelOpts path event opts1 opts2 diffPanelOpts path event opts1 opts2
| impossible = DiffImpossible | impossible = DiffImpossible
= DiffPossible (flatten [titleUpd,menusUpd,hotkeyUpd]) = case flatten [titleUpd,hotkeyUpd] of
[] = DiffPossible menusUpd
ops = DiffPossible [UIUpdate path ops:menusUpd]
where where
impossible = opts1.UIPanelOpts.frame <> opts2.UIPanelOpts.frame impossible = opts1.UIPanelOpts.frame <> opts2.UIPanelOpts.frame
|| opts1.UIPanelOpts.iconCls <> opts2.UIPanelOpts.iconCls || opts1.UIPanelOpts.iconCls <> opts2.UIPanelOpts.iconCls
...@@ -200,31 +207,49 @@ where ...@@ -200,31 +207,49 @@ where
|| (isJust opts1.UIPanelOpts.tbar && isNothing opts2.UIPanelOpts.tbar) //Can only update menu items, not create a menubar suddenly || (isJust opts1.UIPanelOpts.tbar && isNothing opts2.UIPanelOpts.tbar) //Can only update menu items, not create a menubar suddenly
|| (isNothing opts1.UIPanelOpts.tbar && isJust opts2.UIPanelOpts.tbar) || (isNothing opts1.UIPanelOpts.tbar && isJust opts2.UIPanelOpts.tbar)
titleUpd = if (opts1.UIPanelOpts.title == opts2.UIPanelOpts.title) [] [UIUpdate path (UISetTitle opts2.UIPanelOpts.title)] titleUpd = if (opts1.UIPanelOpts.title == opts2.UIPanelOpts.title) [] [("setTitle",[toJSON opts2.UIPanelOpts.title])]
hotkeyUpd = diffHotkeys (fromMaybe [] opts1.UIPanelOpts.hotkeys) (fromMaybe [] opts2.UIPanelOpts.hotkeys)
menusUpd = diffItems [MenuStep:path] event (fromMaybe [] opts1.UIPanelOpts.tbar) (fromMaybe [] opts2.UIPanelOpts.tbar) menusUpd = diffItems [MenuStep:path] event (fromMaybe [] opts1.UIPanelOpts.tbar) (fromMaybe [] opts2.UIPanelOpts.tbar)
hotkeyUpd = diffHotkeys path (fromMaybe [] opts1.UIPanelOpts.hotkeys) (fromMaybe [] opts2.UIPanelOpts.hotkeys)
diffWindowOpts :: UIPath Event UIWindowOpts UIWindowOpts -> DiffResult
diffWindowOpts path event opts1 opts2
| impossible = DiffImpossible
= case flatten [titleUpd,hotkeyUpd] of
[] = DiffPossible menusUpd
ops = DiffPossible [UIUpdate path ops:menusUpd]
where
impossible = opts1.UIWindowOpts.focusTaskId =!= opts2.UIWindowOpts.focusTaskId //TODO Make more possible on client
|| opts1.UIWindowOpts.closeTaskId =!= opts2.UIWindowOpts.closeTaskId
|| opts1.UIWindowOpts.iconCls =!= opts2.UIWindowOpts.iconCls
|| opts1.UIWindowOpts.baseCls =!= opts2.UIWindowOpts.baseCls
|| opts1.UIWindowOpts.bodyCls =!= opts2.UIWindowOpts.bodyCls
titleUpd = if (opts1.UIWindowOpts.title == opts2.UIWindowOpts.title) [] [("setTitle",[toJSON opts2.UIWindowOpts.title])]
hotkeyUpd = diffHotkeys (fromMaybe [] opts1.UIWindowOpts.hotkeys) (fromMaybe [] opts2.UIWindowOpts.hotkeys)
menusUpd = diffItems [MenuStep:path] event (fromMaybe [] opts1.UIWindowOpts.tbar) (fromMaybe [] opts2.UIWindowOpts.tbar)
diffButtonOpts :: UIPath UIButtonOpts UIButtonOpts -> DiffResult diffButtonOpts :: UIPath UIButtonOpts UIButtonOpts -> DiffResult
diffButtonOpts path b1 b2 = DiffPossible (flatten [textUpd,iconUpd,enabledUpd]) diffButtonOpts path b1 b2 = diffMultiProperties path [textUpd,iconUpd,enabledUpd]
where where
textUpd = if (b1.UIButtonOpts.text === b2.UIButtonOpts.text) [] [UIUpdate path (UISetText b2.UIButtonOpts.text)] textUpd = if (b1.UIButtonOpts.text === b2.UIButtonOpts.text) [] [("setText",[toJSON b2.UIButtonOpts.text])]
iconUpd = if (b1.UIButtonOpts.iconCls === b2.UIButtonOpts.iconCls) [] [UIUpdate path (UISetIconCls b2.UIButtonOpts.iconCls)] iconUpd = if (b1.UIButtonOpts.iconCls === b2.UIButtonOpts.iconCls) [] [("setIconCls",[toJSON b2.UIButtonOpts.iconCls])]
enabledUpd = if (b1.UIButtonOpts.disabled === b2.UIButtonOpts.disabled) [] [UIUpdate path (UISetEnabled (not b2.UIButtonOpts.disabled))] enabledUpd = if (b1.UIButtonOpts.disabled === b2.UIButtonOpts.disabled) [] [("setDisabled",[toJSON b2.UIButtonOpts.disabled])]
diffIconOpts :: UIPath UIIconOpts UIIconOpts -> DiffResult diffIconOpts :: UIPath UIIconOpts UIIconOpts -> DiffResult
diffIconOpts path i1 i2 = DiffPossible (flatten [iconUpd,tooltipUpd]) diffIconOpts path i1 i2 = diffMultiProperties path [iconUpd,tooltipUpd]
where where
iconUpd = if (i1.UIIconOpts.iconCls === i2.UIIconOpts.iconCls) [] [UIUpdate path (UISetIconCls (Just i2.UIIconOpts.iconCls))] iconUpd = if (i1.UIIconOpts.iconCls === i2.UIIconOpts.iconCls) [] [("setIconCls",[toJSON i2.UIIconOpts.iconCls])]
tooltipUpd = if (i1.UIIconOpts.tooltip === i2.UIIconOpts.tooltip) [] [UIUpdate path (UISetTooltip i2.UIIconOpts.tooltip)] tooltipUpd = if (i1.UIIconOpts.tooltip === i2.UIIconOpts.tooltip) [] [("setTooltip",[toJSON i2.UIIconOpts.tooltip])]
diffTabOpts :: UIPath UITabOpts UITabOpts -> DiffResult diffTabOpts :: UIPath UITabOpts UITabOpts -> DiffResult
diffTabOpts path t1 t2 = DiffPossible (flatten [textUpd,activeUpd,focusUpd,closeUpd,iconUpd]) diffTabOpts path t1 t2 = diffMultiProperties path [textUpd,activeUpd,focusUpd,closeUpd,iconUpd]
where where
textUpd = if (t1.UITabOpts.text === t2.UITabOpts.text) [] [UIUpdate path (UISetText (Just t2.UITabOpts.text))] textUpd = if (t1.UITabOpts.text === t2.UITabOpts.text) [] [("setText",[toJSON t2.UITabOpts.text])]
activeUpd = if (t1.UITabOpts.active == t2.UITabOpts.active) [] [UIUpdate path (UISetActive t2.UITabOpts.active)] activeUpd = if (t1.UITabOpts.active == t2.UITabOpts.active) [] [("setActive",[toJSON t2.UITabOpts.active])]
focusUpd = if (t1.UITabOpts.focusTaskId === t2.UITabOpts.focusTaskId) [] [UIUpdate path (UISetFocusTaskId t2.UITabOpts.focusTaskId)] focusUpd = if (t1.UITabOpts.focusTaskId === t2.UITabOpts.focusTaskId) [] [("setFocusTaskId",[toJSON t2.UITabOpts.focusTaskId])]
closeUpd = if (t1.UITabOpts.closeTaskId === t2.UITabOpts.closeTaskId) [] [UIUpdate path (UISetCloseTaskId t2.UITabOpts.closeTaskId)] closeUpd = if (t1.UITabOpts.closeTaskId === t2.UITabOpts.closeTaskId) [] [("setCloseTaskId",[toJSON t2.UITabOpts.closeTaskId])]
iconUpd = if (t1.UITabOpts.iconCls === t2.UITabOpts.iconCls) [] [UIUpdate path (UISetIconCls t2.UITabOpts.iconCls)] iconUpd = if (t1.UITabOpts.iconCls === t2.UITabOpts.iconCls) [] [("setIconCls",[toJSON t2.UITabOpts.iconCls])]
diffItems :: UIPath Event [UIControl] [UIControl] -> [UIUpdate] diffItems :: UIPath Event [UIControl] [UIControl] -> [UIUpdate]
diffItems path event items1 items2 = diff path event 0 items1 items2 diffItems path event items1 items2 = diff path event 0 items1 items2
...@@ -232,11 +257,11 @@ where ...@@ -232,11 +257,11 @@ where
diff path event i [] [] diff path event i [] []
= [] = []
diff path event i items1 [] //Less items in new than old (remove starting with the last item) diff path event i items1 [] //Less items in new than old (remove starting with the last item)
= [UIUpdate path (UIRemove n) \\ n <- reverse [i.. i + length items1 - 1 ]] = [UIUpdate path [("remove",[toJSON n])] \\ n <- reverse [i.. i + length items1 - 1 ]]
diff path event i [] items2 //More items in new than old diff path event i [] items2 //More items in new than old
= [UIUpdate path (UIAdd n def) \\ n <- [i..] & def <- items2] = [UIUpdate path [("add",[toJSON n,encodeUIControl def])] \\ n <- [i..] & def <- items2]
diff path event i [c1:c1s] [c2:c2s] //Compare side by side diff path event i [c1:c1s] [c2:c2s] //Compare side by side
= replaceIfImpossible [ItemStep i:path] c2 [diffControls [ItemStep i:path] event c1 c2] = replaceControlIfImpossible [ItemStep i:path] c2 [diffControls [ItemStep i:path] event c1 c2]
++ diff path event (i + 1) c1s c2s ++ diff path event (i + 1) c1s c2s
diffAllWindows :: Event [UIWindow] [UIWindow] -> [UIUpdate] diffAllWindows :: Event [UIWindow] [UIWindow] -> [UIUpdate]
...@@ -245,38 +270,52 @@ where ...@@ -245,38 +270,52 @@ where
diff event i [] [] diff event i [] []
= [] = []
diff event i windows1 [] //Less windows diff event i windows1 [] //Less windows
= [UIUpdate [] (UIRemoveWindow n) \\ n <- reverse [i.. i + length windows1 - 1 ]] = [UIUpdate [] [("removeWindow",[toJSON n])] \\ n <- reverse [i.. i + length windows1 - 1 ]]
diff event i [] items2 //More windows diff event i [] items2 //More windows
= [UIUpdate [] (UIAddWindow n def) \\ n <- [i..] & def <- windows2] = [UIUpdate [] [("addWindow",[toJSON n,encodeUIWindow def])] \\ n <- [i..] & def <- windows2]
diff event i [w1:w1s] [w2:w2s] //Compare side by side (TODO: Make more granular) diff event i [w1:w1s] [w2:w2s] //Compare side by side (TODO: Make more granular)
= if (w1 === w2) [] [UIUpdate [] (UIRemoveWindow i),UIUpdate [] (UIAddWindow i w2)] = diffWindows [WindowStep i] event w1 w2 ++ diff event (i + 1) w1s w2s
++ diff event (i + 1) w1s w2s ++ diff event (i + 1) w1s w2s
diffWindows :: UIPath Event UIWindow UIWindow -> DiffResult diffWindows :: UIPath Event UIWindow UIWindow -> [UIUpdate]
diffWindows path event w1 w2 = DiffImpossible diffWindows path event w1=:(UIWindow sOpts1 iOpts1 opts1) w2=:(UIWindow sOpts2 iOpts2 opts2)
= replaceWindowIfImpossible path w2 [diffSizeOpts path sOpts1 sOpts2
,diffItemsOpts path event iOpts1 iOpts2
,diffWindowOpts path event opts1 opts2]
diffHotkeys :: UIPath [UIKeyAction] [UIKeyAction] -> [UIUpdate] diffHotkeys :: [UIKeyAction] [UIKeyAction] -> [UIUpdateOperation]
diffHotkeys path keys1 keys2 = if (keys1 === keys2) [] [UIUpdate path (UISetHotkeys keys2)] diffHotkeys keys1 keys2 = if (keys1 === keys2) [] [("setHotkeys",[toJSON keys2])]
//Try to diff a control in parts. If one of the parts is impossible, then return a full replace instruction //Try to diff a control in parts. If one of the parts is impossible, then return a full replace instruction
replaceIfImpossible :: UIPath UIControl [DiffResult] -> [UIUpdate] replaceControlIfImpossible :: UIPath UIControl [DiffResult] -> [UIUpdate]
replaceIfImpossible path fallback parts replaceControlIfImpossible path fallback parts
| allPossible parts = foldr (++) [] [d \\DiffPossible d <- parts] | allDiffsPossible parts = flatten [d \\DiffPossible d <- parts]
= [UIUpdate parentPath (UIReplace parentIndex fallback)] = [UIUpdate parentPath [("remove",[toJSON parentIndex])
,("add",[toJSON parentIndex,encodeUIControl fallback])]]
where where
[ItemStep parentIndex:parentPath] = path [ItemStep parentIndex:parentPath] = path
allPossible [] = True replaceWindowIfImpossible ::