local Prop = require("gfx.prop") local Element = require("gfx.element") local Event = require("gfx.event") local Children = Prop:new{ defaultState = {}, uid = "CHILDREN" } function Children:with(elementType) local propSelf = self local defaultReload = elementType._reload function elementType:_iterateChildren() return ipairs(self:_children()) end function elementType:_iterateChildrenReversed() local children = self:_children() return function(tbl, idx) if idx < 1 then return nil, nil end return idx - 1, tbl[idx] end, children, #children end function elementType:_children() return propSelf:getState(self) end function elementType:childCount() return #self:_children() end function elementType:_childAt(index) return self:_children()[elementType:_validChildIndex(index)] end function elementType:_validChildIndex(from) return math.max(1, math.min((from or self:childCount()) + 1, self:childCount() + 1)) end function elementType:_triggerChildChanged(index) if type(self.onChildrenChanged) == "function" then self:onChildrenChanged(index) end self:_reload() end function elementType:_addChild(child, index) index = self:_validChildIndex(index) table.insert(propSelf:getState(self), index, child) self:_triggerChildChanged(index) end function elementType:_removeChild(locator) local index = nil local locatorType = type(locator) if locatorType == "table" then for i,v in self:_iterateChildren() do if v == locator then index = i break end end elseif locatorType == "string" then for i,v in self:_iterateChildren() do if v:getId() == locator then index = i break end end else--if locatorType == "number" then index = self:_validChildIndex(locator) end -- Child not found if index == nil then return false, nil end local result = table.remove(propSelf:getState(self), index) self:_triggerChildChanged(index) return true, result end function elementType:setDirty(fullInvalidate) Element.setDirty(self, fullInvalidate) for _,child in self:_iterateChildren() do child:setDirty(fullInvalidate) end end function elementType:_isDirty() if Element._isDirty(self) then return true end for _,child in self:_iterateChildren() do if child:_isDirty() then return true end end return false end function elementType:draw() local wasDirty = Element._isDirty(self); local dirty = Element.draw(self) if wasDirty then self:_getWindow().clear() end if dirty then for _,child in self:_iterateChildren() do dirty = child:draw() or dirty end end return dirty end function elementType:findById(id) local find = Element.findById(self, id) if find then return find end return self:findChildById(id) end function elementType:findChildById(id) for _,child in self:_iterateChildrenReversed() do local result = child:findById(id) if result then return result end end return nil end function elementType:handleEvent(evt) local evtLocalCoords = Event.toElementLocalPosition(evt, self) if Event.isClickEvent(evt) then -- If click is not inside list bounds, we can safely ignore it if not Event.containsClick(self, evt) then return false end for _,child in self:_iterateChildrenReversed() do if child:handleEvent(evtLocalCoords) then return true end end else for _,child in self:_iterateChildrenReversed() do if child:handleEvent(evtLocalCoords) then return true end end end return Element.handleEvent(self, evt) end function elementType:_reload() if defaultReload ~= nil then defaultReload(self) end -- Reload child windows local win = self:_getWindow() for _,child in self:_iterateChildren() do child:setParent(win) end end return elementType end return Children