-- réed selection bar r2.SelectBar = { Content={}, -- array of instance for the current select bar that is displayed ObserverHandles = {}, -- for each observed instance, gives its observer handle Touched = false } -- select bar observer : allows to know when a targeted object has been modified, or -- if its name has changed local selectBarObserver = {} function selectBarObserver:onInstanceCreated(instance) -- no-op end function selectBarObserver:onInstanceErased(instance) debugInfo(tostring(instance)) -- nb : we don't use the 'instance' as a key because it is a pointer -- to the C++ "weak pointer object", thus 2 references to the same instance may have a different -- value when tested with rawequal ( == operator gives correct results however ...) r2:removeInstanceObserver(r2.SelectBar.ObserverHandles[instance.InstanceId]) r2.SelectBar.ObserverHandles[instance.InstanceId] = nil -- object is not erased yet, so need to clean the table before the update local maxElem = r2.SelectBar:getMaxNumEntries() for k, v in pairs(r2.SelectBar.Content) do if v == instance then for index = k, maxElem do r2.SelectBar.Content[index] = nil end break end end if instance ~= r2:getSelectedInstance() then -- nb : if instance is the selection, no need to update the bar right now, because setSelecteInstance(nil) is -- called just after 'erase' notification messages have been sent r2.SelectBar:touch() end end function selectBarObserver:onAttrModified(instance, attrName, attrIndex) if attrName == "Name" or attrName == "Title" then r2.SelectBar:touch() end end -------------------------------------------------------- -- retrieve a reference to the select bar function r2.SelectBar:getBar() return getUI("ui:interface:r2ed_select_bar:buttons") end -------------------------------------------------------- -- get a button in the bar function r2.SelectBar:getButton(index) return self:getBar():find(string.format("b%d", index)) end -------------------------------------------------------- -- return the max number of button in the select bar function r2.SelectBar:getMaxNumEntries() return tonumber(getDefine("r2ed_max_num_select_bar_button")) end -------------------------------------------------------- -- init the select bar function r2.SelectBar:init() assert(next(self.ObserverHandles) == nil) -- observer table should be empty local bar = self:getBar() bar:clear() for k = 1, self:getMaxNumEntries() do debugInfo(tostring(k)) local butt = createGroupInstance("r2ed_select_bar_button", bar.id, { id = string.format("b%d", k), onclick_l="lua", params_l=string.format("r2.SelectBar:onButtonPushed(%d)", k), onclick_r="lua", params_r=string.format("r2.SelectBar:onRightButtonPushed(%d)", k) }) butt.active = false bar:addChild(butt) end end -------------------------------------------------------- -- mark the select bar as modified for update function r2.SelectBar:touch() self.Touched = true end -------------------------------------------------------- -- update select bar content if necessary function r2.SelectBar:update() if not self.Touched then return end self.Touched = false -- clear the observer table for k, v in pairs(self.ObserverHandles) do r2:removeInstanceObserver(v) end table.clear(self.ObserverHandles) local selection = r2:getSelectedInstance() self:getBar().active = true -- count number of elligible elements for display in the select bar local buttCount = 0 local currElem = selection -- setup a button from an instance local function setupButton(butt, instance) butt.active = true butt.b.pushed = (instance == selection) butt.b.uc_hardtext = instance:getDisplayName() local icon = instance:getSelectBarIcon() if icon ~= "" and icon ~= nil then butt.icon.texture = icon butt.icon.active = true -- butt.icon.color_rgba = CRGBA(255, 255, 255, 255) butt.b.text_x = 28 butt.b.wmargin = 10 else -- butt.icon.color_rgba = CRGBA(127, 127, 127, 255) butt.icon.active = false butt.b.text_x = 12 butt.b.wmargin = 12 end butt:invalidateCoords() end if selection then while currElem do if currElem:displayInSelectBar() then buttCount = buttCount + 1 end currElem = currElem.ParentInstance end end buttCount =math.min(buttCount, self:getMaxNumEntries()) local target = selection if buttCount < 2 then target = r2:getCurrentAct() -- ensure that at least 'scenario' and 'act' are visible buttCount = 2 end -- add the buttons for real -- parents local buttIndex = buttCount local currElem = target while currElem do if currElem:displayInSelectBar() then local butt = setupButton(self:getButton(buttIndex), currElem) self.Content[buttIndex] = currElem self.ObserverHandles[currElem.InstanceId] = r2:addInstanceObserver(currElem.InstanceId, selectBarObserver) buttIndex = buttIndex - 1 end currElem = currElem.ParentInstance end -- sons -- preserve previous sons if they where in previous selection, -- possibly renaming them -- (nb : update may be triggered by modification of sons from a third party, so -- the simpler approach 'if curr selection was in previous then leave unchanged' -- doesn't work here ...) buttIndex = buttCount + 1 -- get next son that can be displayed in the select bar local currParent = target while self.Content[buttIndex] ~= nil and not self.Content[buttIndex].isNil do currElem = self.Content[buttIndex] if currElem:getFirstSelectBarParent() == currParent then -- there's a match so keep this entry setupButton(self:getButton(buttIndex), currElem) currParent = self.Content[buttIndex] buttIndex = buttIndex + 1 self.ObserverHandles[currElem.InstanceId] = r2:addInstanceObserver(currElem.InstanceId, selectBarObserver) else -- no match -> exit break end end -- hide remaining buttons for k = buttIndex, self:getMaxNumEntries() do self:getButton(k).active = false self.Content[k] = nil end -- show/hide sequence browser if necessary local sequenceMenuButton = self:getSequenceButton() if not selection then sequenceMenuButton.active = false else sequenceMenuButton.active = selection:isSequencable() and (selection.Ghost ~= true) end end -------------------------------------------------------- -- called by the ui when one of the select bar button has been pushed function r2.SelectBar:onButtonPushed(index) local instanceId = self.Content[index].InstanceId local selectedInstance = r2:getSelectedInstance() if selectedInstance and instanceId == selectedInstance.InstanceId then -- on second click the contextual menu is displayed self:popMenu(index) end r2:setSelectedInstanceId(instanceId) self:getButton(index).b.pushed = true end -------------------------------------------------------- -- called by the ui when one of the select bar button has been pushed with the right button function r2.SelectBar:onRightButtonPushed(index) r2:setSelectedInstanceId(self.Content[index].InstanceId) self:update() self:onButtonPushed(index) end -------------------------------------------------------- -- get root menu for the select bar function r2.SelectBar:getRootMenu() local menu = getUI("ui:interface:r2ed_select_bar_menu") return menu:getRootMenu() end -------------------------------------------------------- -- get menu for the select bar function r2.SelectBar:getMenu() return getUI("ui:interface:r2ed_select_bar_menu") end -------------------------------------------------------- -- get the 'sequences' button function r2.SelectBar:getSequenceButton() return getUI("ui:interface:r2ed_select_bar:sequences") end -------------------------------------------------------- -- display sub-instance menu for the given button function r2.SelectBar:popMenu(index) local target = r2:getInstanceFromId(self.Content[index].InstanceId) --r2:setSelectedInstanceId(target.InstanceId) local menu = getUI("ui:interface:r2ed_select_bar_menu") local rm = self:getRootMenu() r2:clearMenu(rm) local st = os.clock() -- retrieve all sons (to optimize if needed ...) local allSons = {} if target:isKindOf("Scenario") then -- special case for scenario : 'appendInstancesByType' would only add the active act, and we want them all for k, v in specPairs(target.Acts) do table.insert(allSons, v) end else target:appendInstancesByType(allSons, "BaseClass") end local et = os.clock() debugInfo("#1 " .. tostring(et - st)) st = os.clock() local sons = {} -- retrieve direct selectable sons for k,v in pairs(allSons) do if v:displayInSelectBar() and v:getFirstSelectBarParent() == target then table.insert(sons, v) end end et = os.clock() debugInfo("#2 " .. tostring(et - st)) st = os.clock() -- sort by category, then by icon local function sorter(lhs, rhs) if lhs:getClassName() ~= rhs:getClassName() then return lhs:getClassName() < rhs:getClassName() end if lhs:getSelectBarIcon() ~= rhs:getSelectBarIcon() then return lhs:getSelectBarIcon() < rhs:getSelectBarIcon() end return lhs:getDisplayName() < rhs:getDisplayName() end table.sort(sons, sorter) et = os.clock() debugInfo("#3 " .. tostring(et - st)) st = os.clock() -- fill menu local currentLine = 0 for k, v in pairs(sons) do r2:addMenuLine(rm, v:getDisplayName(), "lua", "r2:setSelectedInstanceId('" .. v.InstanceId .."')", tostring(k), v:getSelectBarIcon(), 14) --rm:addLine(v:getDisplayName(), "lua", "r2:setSelectedInstanceId('" .. v.InstanceId .."')", tostring(k)) --if v:getSelectBarIcon() ~= "" then -- local menuButton = createGroupInstance("r2_menu_button", "", { bitmap = v:getSelectBarIcon(), size="14" }) -- if menuButton then -- rm:setUserGroupLeft(currentLine, menuButton) -- end -- end currentLine = currentLine + 1 end target:completeSelectBarMenu(rm) if rm:getNumLine() == 0 then rm:addLine(i18n.get("uiR2EDEmpty"), "", "", "empty") end et = os.clock() debugInfo("#4 " .. tostring(et - st)) et = os.clock() launchContextMenuInGame(menu.id) local butt = self:getButton(index) menu.x = butt.x_real menu.y = butt.y_real + butt.h_real menu:updateCoords() et = os.clock() debugInfo("#5 " .. tostring(et - st)) end -------------------------------------------------------- -- TMP placeholder: called when the "sequence" menu is clicked function r2.SelectBar:browseSequences() --r2:updateActivitiesAndChatsUI(r2:getSelectedInstance()) r2.activities:initEditorAfterFirstCall() local logicEntity = r2:getSelectedInstance() local activitySequences = logicEntity:getBehavior().Activities local menu = self:getMenu() local rm = menu:getRootMenu() r2:clearMenu(rm) for s = 0, activitySequences.Size - 1 do local sequence = activitySequences[s] rm:addLine(ucstring(r2:getSequenceName(sequence)), "lua", "r2:selectActivitiesSequence(".. tostring(s) .. ")", "s") end rm:addSeparator() r2:addMenuLine(rm, i18n.get("uiR2EDNewSequence"), "lua", "r2:newMiniActivitySequence()", "new_sequence", "r2_icon_create.tga", 14) local sequenceMenuButton = self:getSequenceButton() sequenceMenuButton:updateCoords() launchContextMenuInGame(menu.id) menu.x = sequenceMenuButton.x_real menu.y = sequenceMenuButton.y_real + sequenceMenuButton.h_real menu:updateCoords() end