7 -- Displayer are objects attached to instance in the scenario
8 -- They react to modification events (creations of new objects such as nps, groups ...)
9 -- and update their display accordingly
10 -- There is zero or one displayer attached per category of display for each instance in the map
11 -- For now this include :
12 -- UI displayers : - They update the scenario window to display new things added to the map
13 -- Property displayers : - They update the property sheet for an instance when one is displayed
14 -- Visual displayers : - For now they are only implemented in C++. Their fonction is to update the display of a instance
17 -- Displayer at attached at creation time by the C++ code
18 -- The displayers to add to a specific object are given its the class definition
19 -- (see r2_base_class.lua for details)
21 -- helper : update the context toolbar for the given instance if it is the current selected instance
22 local function updateContextToolbar(instance
)
23 if r2
:getSelectedInstance() == instance
then
24 r2
.ContextualCommands
:update()
31 -----------------------------------------------------------------------------------------------------
32 -----------------------------------------------------------------------------------------------------
33 -----------------------------------------------------------------------------------------------------
35 -- The following code describes how to create a basic displayer that just
36 -- output the events it handles in the log
37 -- Mots of the time, when creating a new displayer, one will
38 -- just construct an existing displayer, and redefine methods of interest,
39 -- possibly calling the parent one
41 function r2
:exampleUIDisplayer()
43 local ct
= colorTag(0, 0, 255)
44 ------------------------------------------------
45 -- Called by C++ at creation
46 function handler
:onCreate(instance
)
47 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." was created")
49 ------------------------------------------------
50 -- Called by C++ just before object is removed (so properties are still readable)
51 function handler
:onErase(instance
)
52 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." was erased")
54 ------------------------------------------------
55 -- Called by C++ just before object is moved in the object hierarchy
56 function handler
:onPreHrcMove(instance
)
57 updateContextToolbar(instance
)
58 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." is about to move")
60 ------------------------------------------------
61 -- Called by C++ just after object is move in the object hierarchy
62 function handler
:onPostHrcMove(instance
)
63 updateContextToolbar(instance
)
64 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." has moved")
66 ------------------------------------------------
67 -- Called by C++ just after object is highlighted by mouse
68 function handler
:onFocus(instance
, hasFocus
)
69 if (instance
.User
.HasFocus
~= hasFocus
) then
70 if hasFocus
== true then
71 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." has gained focus")
73 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." has lost focus")
75 instance
.User
.HasFocus
= hasFocus
78 ------------------------------------------------
79 -- Called by C++ just after object has been selected
80 function handler
:onSelect(instance
, isSelected
)
81 if (isSelected
== true) then
82 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." is selected")
84 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." is unselected")
87 ------------------------------------------------
88 -- Called by C++ when an attribute of this object has been modified
89 -- An attribute inside this object has been modified
90 -- attributeName :Name of the attribute inside this object, as given by its class definition. If the attribute
91 -- is an array, then an additionnal parameter gives the index of the element being modified in the array (or -1 if the whole array is set)
92 function handler
:onAttrModified(instance
, attributeName
, indexInArray
)
93 updateContextToolbar(instance
)
94 debugInfo(ct
.. "Instance " .. instance
.InstanceId
.." has an attribute modified : " .. attributeName
)
99 function r2
:onInstanceSelectedInTree(id
)
100 -- is there's an active pick tool then
101 local currTool
= r2
:getCurrentTool()
102 if currTool
and currTool
:isPickTool() then
103 local tree
= getUICaller()
104 tree
:cancelNextSelectLine() -- don't want real selection, actually ...
105 if currTool
:canPick() then
111 --debugInfo("Seleting instance with id = " .. tostring(id) )
112 r2
:setSelectedInstanceId(id
)
115 function r2
:onInstanceRightClickInTree(id
)
116 r2
:setSelectedInstanceId(id
)
117 r2
:displayContextMenu()
121 r2
.VerboseEvents
= false;
123 -- before to go to "test mode", store opened/closed nodes in scenario window tree
124 -- to correctly initialize tree when go back in edition mode
125 r2
.storedClosedTreeNodes
= {}
127 -----------------------------------------------------------------------------------------------------
128 -----------------------------------------------------------------------------------------------------
129 -----------------------------------------------------------------------------------------------------
131 -- displayer that update the tree control (scenario window)
132 function r2
:defaultUIDisplayer()
133 local function eventDebugInfo(msg
)
134 if r2
.VerboseEvents
== true then
140 local ct
= colorTag(255, 0, 255)
141 ------------------------------------------------
142 -- helper function : notify current act ui displayer that its quota has been modified
143 function handler
:updateCurrentActQuota()
144 -- defer update to the next frame (many element can be added at once)
145 r2
.UIMainLoop
.LeftQuotaModified
= true
147 ------------------------------------------------
148 function handler
:onCut(instance
, cutted
)
150 -- debugInfo("On cut " .. tostring(cutted))
151 -- local tree = getUI(r2.InstanceTreePath)
152 -- debugInfo(tostring(select(cutted, 127, 255)))
153 -- instance.User.TreeNode.Color.G = select(cutted, 0, 255)
154 -- tree:forceRebuild()
156 ------------------------------------------------
157 function handler
:onCreate(instance
)
158 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." was created")
159 self
:addTreeNode(instance
)
160 -- if my quota is not 0, then we should update
161 -- the current act quota ..
162 --if instance:getUsedQuota() ~= 0 then
163 -- self:updateCurrentActQuota()
165 if instance
:hasScenarioCost() ~= false then
166 self
:updateCurrentActQuota()
170 ------------------------------------------------
171 function handler
:onPostCreate(instance
)
173 -- Special : if the cookie 'AskName' is set (by C++ or lua), then show property and ask name
174 -- to user for that object
175 if instance
.User
.AskName
then
176 if instance
.User
.ShowProps
then
177 r2
:showProperties(instance
)
178 instance
.User
.ShowProps
= nil
180 if instance
.User
.Select
then
181 r2
:setSelectedInstanceId(instance
.InstanceId
)
183 local propWindow
= r2
.CurrentPropertyWindow
185 -- tmp : quick & dirty access to the widget ...
186 if propWindow
and propWindow
.active
then
187 local editBox
= propWindow
:find("Name"):find("eb")
189 setCaptureKeyboard(editBox
)
190 editBox
:setSelectionAll()
193 instance
.User
.AskName
= nil -- get rid of cookie
195 -- Special : if the cookie 'Select' is set (by C++ or lua), then the object should be selected after creation
196 if instance
.User
.Select
then
197 r2
:setSelectedInstanceId(instance
.InstanceId
)
199 if type(instance
.User
.CreateFunc
) == "function" then
200 instance
.User
.CreateFunc(instance
)
203 ------------------------------------------------
204 function handler
:onErase(instance
)
205 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." was erased")
206 self
:removeTreeNode(instance
)
207 -- if my quota is not 0, then we should update
208 -- the current act quota ..
209 if instance
:hasScenarioCost() ~= false then
210 self
:updateCurrentActQuota()
213 ------------------------------------------------
214 function handler
:onPreHrcMove(instance
)
215 updateContextToolbar(instance
)
216 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." is about to move")
217 self
:removeTreeNode(instance
)
219 ------------------------------------------------
220 function handler
:onPostHrcMove(instance
)
222 -- if parent is a group, for its creation you don't know category of children : people or creature
223 -- you check it for first child
224 local parent
= instance
.ParentInstance
225 if instance
:isGrouped() and parent
.Components
.Size
==1 then
227 self
:onCreate(parent
)
228 self
:onPostCreate(parent
)
231 updateContextToolbar(instance
)
232 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." has moved")
233 --eventDebugInfo(ct .. "New parent is " .. instance.ParentInstance.InstanceId)
234 local nodes
= self
:addTreeNode(instance
)
235 if (r2
:getSelectedInstance() == instance
) and nodes
then
236 for k
, node
in pairs(nodes
) do
238 assert(node
:getParentTree())
239 assert(node
:getParentTree().selectNodeById
)
240 node
:getParentTree():selectNodeById(node
.Id
, false)
243 -- if my quota is not 0, then we should update
244 -- the current act quota ..
245 if instance
:hasScenarioCost() ~= false then
246 self
:updateCurrentActQuota()
249 -- if instance has Components, its children's nodes have been deleted at onPreHrcMove call
250 if instance
.Components
then
251 for i
=0, instance
.Components
.Size
-1 do
252 local child
= instance
.Components
[i
]
256 self
:onPostCreate(instance
)
259 ------------------------------------------------
260 function handler
:onFocus(instance
, hasFocus
)
261 if (instance
.User
.HasFocus
~= hasFocus
) then
262 if hasFocus
== true then
263 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." has gained focus")
265 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." has lost focus")
267 instance
.User
.HasFocus
= hasFocus
270 ------------------------------------------------
271 function handler
:onSelect(instance
, isSelected
)
272 if not instance
.User
.TreeNodes
then
275 for k
, treeNode
in pairs(instance
.User
.TreeNodes
) do
277 if not (treeNode
== nil or treeNode
.isNil
== true) then
279 local tree
= treeNode
:getParentTree()
280 if (isSelected
== true) then
281 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." is selected")
282 tree
:selectNodeById(instance
.InstanceId
, false)
284 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." is unselected")
290 ------------------------------------------------
291 function handler
:onAttrModified(instance
, attributeName
, indexInArray
)
292 if attributeName
== "Position" or attributeName
== "Angle" then
295 if attributeName
== "Selectable" then
296 self
:removeTreeNode(instance
)
297 self
:addTreeNode(instance
)
300 updateContextToolbar(instance
)
301 if not instance
.User
.TreeNodes
then
304 local nodes
= instance
.User
.TreeNodes
305 for k
, node
in pairs(nodes
) do
306 local tree
= node
:getParentTree()
307 if attributeName
== 'Name' then
308 setupNodeName(instance
)
309 if node
:getFather() then
310 node
:getFather():sortByBitmap()
313 tree
:selectNodeById(node
.Id
, false) -- reforce the selection
317 if attr
== "Ghost" then
318 if instance
.Ghost
then
319 self
:removeTreeNode(instance
)
322 --eventDebugInfo(ct .. "Instance " .. instance.InstanceId .." has an attribute modified : " .. attributeName)
325 function setupNodeName(instance
)
326 local treeNodes
= instance
.User
.TreeNodes
327 if not treeNodes
then return end
329 for k
, treeNode
in pairs(treeNodes
) do
330 if not (treeNode
== nil or treeNode
.isNil
== true) then
331 local tree
= treeNode
:getParentTree()
333 treeNode
.Text
= instance
:getDisplayName()
334 if tree
then -- nb : tree may be nil if node is setupped before being attached to its parent tree
341 function handler
:storeClosedTreeNodes()
343 function downInTree(node
, nodeTable
)
345 for i
=0, node
:getNumChildren()-1 do
346 local child
= node
:getChild(i
)
349 nodeTable
[child
.Id
] = child
.Opened
351 if child
:getNumChildren()>0 then
352 downInTree(child
, nodeTable
)
357 r2
.storedClosedTreeNodes
= {}
360 r2
.storedClosedTreeNodes
[r2
.Scenario
:getBaseAct().InstanceId
] = {}
361 local objectNodes
= r2
.storedClosedTreeNodes
[r2
.Scenario
:getBaseAct().InstanceId
]
363 local container
= getUI("ui:interface:r2ed_scenario")
364 --local objectsRoot = container:find("content_tree_list"):getRootNode():getNodeFromId("scenery_objects")
365 local objectsRoot
= container
:find("content_tree_list"):getRootNode()
367 downInTree(objectsRoot
, objectNodes
)
369 -- entities and components
370 if r2
.Scenario
.Acts
.Size
>1 then
371 for i
=1, r2
.Scenario
.Acts
.Size
-1 do
372 local act
= r2
.Scenario
.Acts
[i
]
373 local peopleRoot
= act
:getContentTree():getRootNode():getNodeFromId("people")
375 local creatureRoot
= act
:getContentTree():getRootNode():getNodeFromId("creatures")
377 --local componentRoot = act:getMacroContentTree():getRootNode():getNodeFromId("macro_components")
378 local componentRoot
= act
:getMacroContentTree():getRootNode()
379 assert(componentRoot
)
381 r2
.storedClosedTreeNodes
[act
.InstanceId
] = {}
382 local actNodes
= r2
.storedClosedTreeNodes
[act
.InstanceId
]
384 downInTree(peopleRoot
, actNodes
)
385 downInTree(creatureRoot
, actNodes
)
386 downInTree(componentRoot
, actNodes
)
391 function handler
:addPermanentNodes()
393 if r2
.ScenarioInstanceId
then
394 local scenario
= r2
:getInstanceFromId(r2
.ScenarioInstanceId
)
395 if scenario
and scenario
.Acts
.Size
>0 then
396 local addToTreesTable
= {}
397 scenario
:getBaseAct():appendInstancesByType(addToTreesTable
, "LogicEntity")
398 for k
, instance
in pairs(addToTreesTable
) do
399 self
:addTreeNode(instance
)
406 function handler
:addTreeNode(instance
)
408 if instance
.Ghost
then return nil end
410 local parentNodes
= instance
:getParentTreeNode()
412 if parentNodes
==nil then return nil end
414 if instance
.User
.TreeNodes
==nil then instance
.User
.TreeNodes
= {} end
416 for actId
,parentNode
in pairs(parentNodes
) do
418 local alreadyAdded
= false
420 for k2
, treeNode
in pairs(instance
.User
.TreeNodes
) do
421 if not (treeNode
==nil or treeNode
.isNil
==true) then
423 local father
= treeNode
:getFather()
424 if father
==parentNode
then
431 if not alreadyAdded
then
433 if parentNode
== nil then
434 return nil -- one of the ancestors may be unselectable
436 if not instance
.SelectableFromRoot
then
439 local tree
= parentNode
:getParentTree()
440 local treeNode
= SNode()
442 -- store reference in object
443 table.insert(instance
.User
.TreeNodes
, treeNode
)
445 treeNode
.Bitmap
= instance
:getPermanentStatutIcon()
446 local openTree
= true
447 if r2
.storedClosedTreeNodes
[actId
] then
448 openTree
= (r2
.storedClosedTreeNodes
[actId
][instance
.InstanceId
]==true)
450 treeNode
.Opened
= openTree
452 treeNode
.Id
= instance
.InstanceId
453 treeNode
.AHName
= "lua"
454 local ahParams
= "r2:onInstanceSelectedInTree('" .. instance
.InstanceId
.. "')"
455 --eventDebugInfo(ahParams)
456 treeNode
.AHParams
= ahParams
457 treeNode
.AHNameRight
= "lua"
458 treeNode
.AHParamsRight
= "r2:onInstanceRightClickInTree('" .. instance
.InstanceId
.. "')"
459 treeNode
.AHNameClose
= "lua"
460 treeNode
.AHParamsClose
= "r2.storedClosedTreeNodes = {}"
462 setupNodeName(instance
)
465 parentNode
:addChildSortedByBitmap(treeNode
)
466 parentNode
.Show
= (parentNode
:getNumChildren() ~= 0)
472 return instance
.User
.TreeNodes
474 function handler
:removeTreeNode(instance
)
476 local nodes
= instance
.User
.TreeNodes
477 if nodes
== nil or nodes
.isNil
then
480 for k
, node
in pairs(nodes
) do
481 if not (node
== nil or node
.isNil
== true) then
482 local tree
= node
:getParentTree()
483 if node
:getFather().isNil
then
484 if (node
== node
:getParentTree():getRootNode()) then
485 --debugInfo("ROOT NODE")
486 node
:getParentTree():setRootNode(nil)
488 --debugInfo("ISOLATED NODE")
489 deleteReflectable(node
) -- isolated node (the tree was never built ?)
492 -- update parent node visibility only if a direct son of the root node
493 if node
:getFather() then
494 if (node
:getFather():getFather() == tree
:getRootNode()) then
495 node
:getFather().Show
= (node
:getFather():getNumChildren() > 1)
497 node
:getFather():deleteChild(node
)
504 instance
.User
.TreeNodes
= nil
511 -----------------------------------------------------------------------------------------------------
512 -----------------------------------------------------------------------------------------------------
513 -----------------------------------------------------------------------------------------------------
515 -- special display for groups in scenario window
516 function r2
:groupUIDisplayer()
517 local handler
= self
:defaultUIDisplayer()
518 function handler
:updateLeaderColor(instance
)
519 if not instance
.User
.TreeNodes
then
522 for k
, node
in pairs(instance
.User
.TreeNodes
) do
523 local tree
= node
:getParentTree()
524 for i
= 0, instance
.Components
.Size
- 1 do
525 --debugInfo("I = " .. tostring(i))
526 local treeNodes
= instance
.Components
[i
].User
.TreeNodes
528 for k2
, treeNode
in pairs(treeNodes
) do
530 treeNode
.Color
= CRGBA(255, 0, 0) -- mark leader in red
532 treeNode
.Color
= CRGBA(255, 255, 255)
541 local oldOnAttrModified
= handler
.onAttrModified
542 function handler
:onAttrModified(instance
, attrName
, indexInArray
)
543 if attrName
== "Components" then
544 self
:updateLeaderColor(instance
)
546 oldOnAttrModified(self
, instance
, attrName
, indexInArray
)
549 -- local oldOnCreate = handler.onCreate
550 -- function handler:onCreate(instance)
551 -- debugInfo("On create group")
552 -- oldOnCreate(self, instance)
555 local oldOnPostCreate
= handler
.onPostCreate
556 function handler
:onPostCreate(instance
)
557 oldOnPostCreate(self
, instance
)
558 self
:updateLeaderColor(instance
)
566 -----------------------------------------------------------------------------------------------------
567 -----------------------------------------------------------------------------------------------------
568 -----------------------------------------------------------------------------------------------------
570 -- Displayer for ACTS. In the ui, acts are added into the act combo box --
571 -- in the environment of the container we store a table that gives the act Instance id,
572 -- and the index of the tree control for each line in the combo box
573 -- Table has the following look
574 -- ActTable = { { Act = ..., TreeIndex = ... }, -- combo box line 1
575 -- { Act = ..., TreeIndex = ... }, -- combo box line 2
576 -- { Act = ..., TreeIndex = ... }, -- combo box line 3 etc.
580 r2
.ActUIDisplayer
= {}
581 r2
.ActUIDisplayer
.ActTable
= {} -- table that map each line of the combo box to an act
582 r2
.ActUIDisplayer
.LastSelfCreatedActInstanceId
= nil -- id of the last act created by the pionner (not by another pionner)
583 -- When created, an act change will automatically occur
585 ------------------------------------------------
586 -- helper function : notify current act ui displayer that its quota has been modified
587 function r2
.ActUIDisplayer
:updateCurrentActQuota()
588 -- defer update to the next frame (many element can be added at once)
589 r2
.UIMainLoop
.LeftQuotaModified
= true
592 ------------------------------------------------
593 function r2
.ActUIDisplayer
:updateActName(act
)
595 if act
and not act
:isBaseAct() then
597 local actTable
= self
:getActTable()
598 for index
, entry
in pairs(actTable
) do
599 if entry
.Act
== act
then
600 local comboBox
= self
:getActComboBox()
602 local actTitle
= act
:getName()
603 if act
==r2
.Scenario
:getCurrentAct() then
604 actTitle
= actTitle
.. " [" .. i18n
.get("uiR2EDCurrentActComboBox"):toUtf8() .."]"
606 local text
= ucstring()
607 text
:fromUtf8(actTitle
)
608 comboBox
:setText(index
- 1, text
)
615 ------------------------------------------------
616 function r2
.ActUIDisplayer
:onAttrModified(instance
, attributeName
, indexInArray
)
618 -- if title is modified, then must update names of all entities in the scene
619 if attributeName
== "Name" then
621 r2
:getCurrentAct():appendInstancesByType(npcs
, "Npc")
622 for k
, npc
in pairs(npcs
) do
623 npc
.DisplayerVisual
:updateName()
626 self
:updateActName(instance
)
632 ------------------------------------------------
633 function r2
.ActUIDisplayer
:onCreate(act
)
635 local container
= self
:getContainer()
636 local comboBox
= self
:getActComboBox()
638 local tree
, macroTree
639 if not act
:isBaseAct() then
640 local text
= ucstring()
641 local index
= r2
.logicComponents
:searchElementIndex(act
)-2
642 local actTitle
= act
:getName()
643 if type(actTitle
) ~= "string" then
644 text
:fromUtf8("bad type for title : " .. type(actTitle
))
645 comboBox
:insertText(index
, text
)
647 text
:fromUtf8(actTitle
)
648 comboBox
:insertText(index
, text
)
651 tree
= self
:findFreeTreeCtrl()
652 macroTree
= self
:findFreeTreeCtrl(true)
653 local actTable
= self
:getActTable()
654 table.insert(actTable
, index
+1, { Act
= act
, Tree
= tree
, MacroTree
= macroTree
})
657 -- store tree in the act for future insertion of items
658 act
.User
.ContentTree
= tree
659 act
.User
.MacroContentTree
= macroTree
660 self
:updateCurrentActQuota()
662 -- add permanent nodes to act node
663 r2
:defaultUIDisplayer():addPermanentNodes()
666 ------------------------------------------------
667 function r2
.ActUIDisplayer
:onPostCreate(act
)
668 -- when a new act is created, select this act as the default
669 if act
.InstanceId
== self
.LastSelfCreatedActInstanceId
then
670 -- the act was just created by pionner on that computer, so change right now
671 r2
.ScenarioWindow
:setAct(act
)
672 self
.LastSelfCreatedActInstanceId
= nil
674 r2
.ScenarioWindow
:updateUIFromCurrentAct()
675 self
:updateCurrentActQuota()
678 ------------------------------------------------
679 function r2
.ActUIDisplayer
:onErase(erasedAct
)
680 -- clean tree content
681 local tree
= erasedAct
.User
.ContentTree
682 local macroTree
= erasedAct
.User
.MacroContentTree
684 r2
:cleanTreeNode(tree
, "people")
685 r2
:cleanTreeNode(tree
, "creatures")
688 --r2:cleanTreeNode(macroTree, "macro_components")
689 r2
:cleanTreeRootNode(macroTree
)
691 local actTable
= self
:getActTable()
692 for index
, entry
in pairs(actTable
) do
693 if entry
.Act
== erasedAct
then
694 self
:getActComboBox():removeTextByIndex(index
- 1)
695 table.remove(actTable
, index
)
700 self
:updateCurrentActQuota()
702 ------------------------------------------------
703 function r2
.ActUIDisplayer
:getActTable()
707 ------------------------------------------------
708 function r2
.ActUIDisplayer
:getContainer()
709 return getUI("ui:interface:r2ed_scenario")
712 ------------------------------------------------
713 function r2
.ActUIDisplayer
:getActComboBox()
714 return self
:getContainer():find("act_combo_box")
717 -----------------------------------------------
718 function r2
.ActUIDisplayer
:findFreeTreeCtrl(macroTree
)
720 local treeName
= "act_tree_"
721 if macroTree
==true then treeName
="macro_act_tree_" end
722 for i
= 0, r2
:getMaxNumberOfAdditionnalActs() - 1 do
723 local tree
= self
:getContainer():find(treeName
.. tostring(i
))
725 for index
, entry
in pairs(self
:getActTable()) do
726 local entryTree
= entry
.Tree
727 if macroTree
==true then entryTree
= entry
.MacroTree
end
728 if entryTree
== tree
then
741 function r2
:createActUIDisplayer()
742 return r2
.ActUIDisplayer