local module = {}; -- local exportItemMeta = { -- __index = function (self,key) -- local methods = self.__methods; -- local events = self.__events; -- return (methods and methods[key]) or (events and events[key]) or (self.getters[key](self.__this)); -- end; -- __newindex = function (self,key,value) -- self.__setters[key](self.__this,value); -- end; -- }; function module.init(shared) local new = {}; local event = shared.event; ---@module src.event local bind = event.bind; local store = shared.store; ---@module src.store local addObject = store.addObject; local storeNew = store.new; local mount = shared.mount; ---@module src.mount local getHolder = mount.getHolder; local advancedTween = shared.advancedTween; ---@module src.libs.AdvancedTween local round = shared.round; ---@module src.libs.round -- make object that from instance, class and more function new.make(ClassName,...) -- render object -- make thing local item; local classOfClassName = type(ClassName); if classOfClassName == "string" then -- if classname is a string value, cdall instance.new for making new roblox instance item = Instance.new(ClassName); elseif classOfClassName == "function" then -- if classname is a function value, call it for making new object (OOP object) item = ClassName(); elseif classOfClassName == "table" then -- if classname is a calss what is included new function, call it for making new object (object) local func = ClassName.new or ClassName.New or ClassName.__new; if ClassName.__noSetup then -- if is support initing props local childs,props = {},{...}; local parsed; for iprop = #props,1,-1 do local prop = props[iprop]; for i,v in ipairs(prop) do childs[i] = ((iprop == 1) and v or v:Clone()); prop[i] = nil; end if next(prop) then -- there are (key/value)s if parsed then for i,v in pairs(prop) do parsed[i] = v; end else parsed = prop; end end end item = func(parsed); local holder = getHolder(item); for _,v in ipairs(childs) do mount(item,v,holder); end return item; end item = func(); end if not item then -- if cannot make new object, ignore this call local str = tostring(ClassName); print(("fail to make item '%s', did you forget to checking the class '%s' is exist?"):format(str,str)); end -- set property and adding child and binding functions local holder = getHolder(item); -- to __holder or holder or it self (for tabled) -- for iprop,prop in ipairs({...}) do for iprop = select("#",...),1,-1 do local prop = select(iprop,...); for index,value in pairs(prop) do local valueType = typeof(value); local indexType = typeof(index); -- child if indexType == "string" and valueType == "table" and value.t == "reg" then -- register (bind to store event) -- store binding local with = value.wfunc; local set = value.store[value.key]; if set then if with then set = with(set); end item[index] = set; else local dset = value.dvalue; if dset then item[index] = dset; end end local tween = value.tvalue; local from = value.fvalue; -- adding event function local function regFn(newValue,store) if from then newValue = from[newValue]; end if with then newValue = with(newValue,item,store); end if tween then if not advancedTween then return warn "module 'AdvancedTween' needs to be loaded for tween properties but it is not found on 'src.libs'. you should adding that to src.libs directory"; end advancedTween.RunTween(item,tween,{[index] = newValue}); else item[index] = newValue; end end value:register(regFn); -- this is using hacky of roblox instance -- this is will keep reference from week table until -- inscance got GCed item:GetPropertyChangedSignal("ClassName"):Connect(regFn); elseif (valueType == "function" or valueType == "table") and indexType == "string" and bind(item,index,value,valueType) then -- connect event -- event binding elseif indexType == "string" then -- prop set local isImage = (ClassName == "ImageLabel" or ClassName == "ImageButton"); if index == "roundSize" and isImage then if not round then warn "module 'round' needs to be loaded for set images round size but it is not found on 'src.libs'. you should adding that to src.libs directory" end round.setRound(item,value); else item[index] = value; -- set property end elseif indexType == "number" then -- object -- child object mount(item,((iprop == 1) and value or value:Clone()),holder); end end end return item; end local make = new.make; -- import quad object function new.import(ClassName,defaultProperties) -- make new quad class object local this = defaultProperties or {}; setmetatable(this,{ __call = function (self,prop) if type(prop) == "string" then local lastName = prop; return function (nprop) nprop.Name = lastName; local item = make(ClassName,nprop); addObject(lastName,item); return item; end; end return make(ClassName,prop,this); end; }); return this; end -- function new.export(newFn,setters,getters,methods,events) -- make quad importable class -- return function () -- return setmetatable({ -- __this = newFn(); -- __setters = setters; -- __getters = getters; -- __methods = methods; -- __events = events; -- },exportItemMeta); -- end; -- end -- set module calling function setmetatable(new,{ __call = function (self,...) return new.import(...); end; }); -- make class function new.extend() local this = {}; --- make new object function this.new(prop) -- make metatable prop = storeNew(prop); local parent = prop and (prop.Parent or prop.parent); local self = {__prop = prop; __parent = parent}; local init = this.init; if init then init(self,prop); end setmetatable(self,this); -- render that local object = self:render(prop); rawset(self,"__object",object); rawset(self,"__holder",object); if parent then rawset(self,"__parent",parent); mount(self,parent) end -- prevent gc ((type(object) == "table" and object.__object) or object):GetPropertyChangedSignal "ClassName":Connect(function() return self; end); return self; end --- re-render object (if some props chaged, call this to render again) function this:update() -- swap object local object = rawget(self,"__object"); -- get last instance local parent = object and object.Parent; -- date parent if not object then parent = rawget(self,"__parent"); end local lastObject = object; if lastObject then lastObject.Parent = nil; end object = self:render(self.__prop); -- new instance rawset(self,"__holder",object); rawset(self,"__object",object); object.Parent = getHolder(parent); -- update parent -- restore childs local child = rawget(self,"__child"); if child then for _,v in pairs(child) do if v then v.Parent = object; end end end -- prevnet gc ((type(object) == "table" and object.__object) or object):GetPropertyChangedSignal "ClassName":Connect(function() return self; end); if lastObject then -- remove old instance self:Destroy(lastObject); end end -- define destroy method function this:Destroy(object) local unload = rawget(self,"unload"); object = object or rawget(self,"__object"); if object then if unload then unload(self,object); else local destroy = (object.Destroy or object.destroy); if destroy then pcall(destroy,object); end end end self = nil; end --- link to new function this.__call(self,...) return (self.new or self.New or self.__new)(...); end; -- indexer (getter) function this:__index(k) if k == "destroy" then return self.Destroy; end local getter = this.getter[k]; if getter then return getter(self,rawget(self,"__object")); end -- from this (have more priority) local fromThis = this[k]; if fromThis then return fromThis; end -- from prop, not start with '_' (private value) if k:sub(1,1) ~= "_" then return self.__prop[k]; end end --- new indexer (setter) function this:__newindex(k,v) -- from setter local setter = this.setter[k]; if setter then return setter(self,v,rawget(self,"__object")); end -- is private (self value) if k == "holder" or k:sub(1,1) == "_" then return rawset(self,k,v); end -- prop update self.__prop[k] = v; if this.updateTriggers[k] then self:update(); end end this.getter = {}; this.setter = { Parent = function(self,parent,object) rawset(self,"__parent",parent); if object then object.Parent = getHolder(parent); end end; }; this.updateTriggers = {}; this.__noSetup = true; return this; end return new; end return module;