From 3e38345dfb7850c3b3d41173a02bf0cf84604e37 Mon Sep 17 00:00:00 2001 From: Qwreey Date: Sun, 19 Feb 2023 06:48:07 +0900 Subject: [PATCH] Fix signal, mount, types, store bugs. Adding tutorials --- docs/kr/tutorial/0_gettingStarted.md | 2 +- .../tutorial/{1_install.md => 10_extend.md} | 0 docs/kr/tutorial/11_signal.md | 0 docs/kr/tutorial/1_importRobloxClass.md | 46 ++++++ docs/kr/tutorial/2_mount.md | 84 +++++++++++ docs/kr/tutorial/3_storeGetObject.md | 138 ++++++++++++++++++ docs/kr/tutorial/4_style.md | 0 docs/kr/tutorial/5_events.md | 0 docs/kr/tutorial/6_tween.md | 0 docs/kr/tutorial/7_register.md | 0 docs/kr/tutorial/8_lang.md | 4 + docs/kr/tutorial/9_quadProperty.md | 0 src/class.lua | 4 +- src/libs/AdvancedTween/init.lua | 9 ++ src/mount.lua | 2 +- src/signal.lua | 4 +- src/store.lua | 88 ++++++++--- src/types.lua | 4 +- testing/init.lua | 8 +- testing/signal | 36 +++++ 20 files changed, 400 insertions(+), 29 deletions(-) rename docs/kr/tutorial/{1_install.md => 10_extend.md} (100%) create mode 100644 docs/kr/tutorial/11_signal.md create mode 100644 docs/kr/tutorial/1_importRobloxClass.md create mode 100644 docs/kr/tutorial/2_mount.md create mode 100644 docs/kr/tutorial/3_storeGetObject.md create mode 100644 docs/kr/tutorial/4_style.md create mode 100644 docs/kr/tutorial/5_events.md create mode 100644 docs/kr/tutorial/6_tween.md create mode 100644 docs/kr/tutorial/7_register.md create mode 100644 docs/kr/tutorial/8_lang.md create mode 100644 docs/kr/tutorial/9_quadProperty.md create mode 100644 testing/signal diff --git a/docs/kr/tutorial/0_gettingStarted.md b/docs/kr/tutorial/0_gettingStarted.md index 4e07b02..5d0852c 100644 --- a/docs/kr/tutorial/0_gettingStarted.md +++ b/docs/kr/tutorial/0_gettingStarted.md @@ -13,6 +13,6 @@ ## 목차 -[1. 설치하기](kr/tutorial/1_install) +[1. 설치하기](kr/tutorial/1_importRobloxClass) [2. 시작하기](kr/tutorial/2_starting) diff --git a/docs/kr/tutorial/1_install.md b/docs/kr/tutorial/10_extend.md similarity index 100% rename from docs/kr/tutorial/1_install.md rename to docs/kr/tutorial/10_extend.md diff --git a/docs/kr/tutorial/11_signal.md b/docs/kr/tutorial/11_signal.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/kr/tutorial/1_importRobloxClass.md b/docs/kr/tutorial/1_importRobloxClass.md new file mode 100644 index 0000000..89e31b4 --- /dev/null +++ b/docs/kr/tutorial/1_importRobloxClass.md @@ -0,0 +1,46 @@ + +# 로블록스 오브젝트 불러오기 + +Frame 과 같은 UI 오브젝트를 만들고 싶은 경우 먼저 import 를 이용해주어야 합니다. +다음과 같이 입력해보세요! + +```lua +local Quad = require(path.to.module).Init() +local Class = Quad.Class + +local Frame = Class "Frame" + +print( + Frame { + Name = "Wow!"; + -- 다음과 같이 자식을 추가할 수 있습니다 + Frame { + Name = "Child"; + }; + } +) +``` + +나중에 자신만의 UI 오브젝트를 생성할 수 있지만, 일반적인 로블록스가 지원하는 오브젝트의 경우 `Class(ClassName:string)` 으로 불러올 수 있습니다 + +불러온 Class 에 기본값을 설정할 수도 있습니다 + +```lua +local Quad = require(path.to.module).Init() +local Class = Quad.Class + +local FrameWithoutBorder = Class "Frame" +FrameWithoutBorder.BorderSizePixel = 0 + +local FrameWithBorder = Class "Frame" +FrameWithBorder.BorderSizePixel = 1 + +local frame1 = FrameWithoutBorder {} +local frame2 = FrameWithBorder {} +print(frame1.FrameWithBorder) +print(frame2.FrameWithBorder) +``` + +이제 그려낸 프레임을 화면에 그려보고 싶나요? 다음 튜토리얼 Mount 를 확인해보세요! + +[다음 튜토리얼](./2_mount) diff --git a/docs/kr/tutorial/2_mount.md b/docs/kr/tutorial/2_mount.md new file mode 100644 index 0000000..4d34a94 --- /dev/null +++ b/docs/kr/tutorial/2_mount.md @@ -0,0 +1,84 @@ + +# 트리를 연결하고 화면에 UI 를 표시하기 + +일반적으로 *Quad* 모듈에서 모든 Parent 처리는 `Mount` 를 이용합니다. `Mount(to:any,...item:any)->mounts` 함수를 이용하면 다루는 오브젝트가 로블록스 Instance 인지, Quad 확장 클래스인지 상관없이 알아서 Parent 를 처리해 줍니다. + +```lua +--- StarterGui 안에 ScreenGui 를 넣고 로컬스크립트로 입력해보세요 +local ScreenGUI = script.Parent +local Quad = require(path.to.module).Init() +local Class,Mount = Quad.Class,Quad.Mount + +local Frame = Class "Frame" + +Mount(ScreenGui, + Frame { + Name = "Wow!"; + } + --[[ + , Frame {} , Frame {} + 더많은 오브젝트를 다같이 Mount 할 수 있습니다 + ]] +) +``` + +편의를 위해서 Mount 함수는 mounts 라는 객체를 반환합니다. 이것을 이용하면 필요에 따라서 Mount 상태를 해제할 수 있습니다. + +```lua +local ScreenGUI = script.Parent +local Quad = require(path.to.module).Init() +local Class,Mount = Quad.Class,Quad.Mount + +local Frame = Class "Frame" + +local mainFrame = Frame {} +Mount(ScreenGui,mainFrame) + +local childFrame = Frame {} +local childFrameMount = Mount(mainFrame,childFrame) +childFrameMount:Unmount() +-- childFrame 은 단순히 .Parent 되는 것을 넘어 메모리에서 사라집니다 +-- 즉, Destroy() 가 호출됩니다 +``` + +이 동작은 리스트를 만들 때 유용할 수 있습니다. **다시로드 동작시 직접 제거가 필요하지 않습니다** + +```lua +local ScreenGUI = script.Parent +local Quad = require(path.to.module).Init() +local Class,Mount = Quad.Class,Quad.Mount + +local Frame = Class "Frame" + +local mainFrame = Frame {} +Mount(ScreenGui,mainFrame) + +local lastListMount +local function refreshList() + if lastListMount then -- 이전 children 을 알아서 제거합니다 + lastListMount:Unmount() + end + + local children = {} + for i = 1,10 do + table.insert(children,Frame { Name = tostring(i) }) + end + + lastListMount = Mount(mainFrame,unpack(children)) +end + +task.spawn(function() + while true do + refreshList() + task.wait(2) + end +end) +``` + +다음과 같이 이전 children 을 간접적으로 제거합니다. 단 ScrollFrame 의 경우 CanvasPosition 이 초기화 되어버리므로 자식들을 `:Unmount()` 하기 전에 위치를 저장하고 `Mount()` 후 불러와야 합니다 +
+ +아직 생성한 오브젝트를 변수에 넣어야하는것이 귀찮아 보이는가요? 이제 `Store.GetObject(id:string)` 에 대해 알아봅시다. +
+ +[다음 튜토리얼](./3_storeGetObject) diff --git a/docs/kr/tutorial/3_storeGetObject.md b/docs/kr/tutorial/3_storeGetObject.md new file mode 100644 index 0000000..f082537 --- /dev/null +++ b/docs/kr/tutorial/3_storeGetObject.md @@ -0,0 +1,138 @@ + +# 오브젝트에 아이디 부여하기, 그리고 사용하기 + +**변수에 만들어진 오브젝트를 하나하나 저장해???** 그게 편할리가 없죠. 오브젝트에 아이디를 넣고 사용해봅시다. + +```lua +local ScreenGUI = script.Parent +local Quad = require(path.to.module).Init() +local Class,Mount,Store = Quad.Class,Quad.Mount,Quad.Store + +local Frame = Class "Frame" + +Frame "mainFrame" { + Size = UDim2.fromScale(1,1); +} + +Mount(ScreenGUI,Store.GetObject("mainFrame")) +``` + +아이디에 허용되는 글자는 변수명과 같습니다. 일반적으로 대소문자, 숫자, 언더바(_) 를 허용합니다. +오브젝트 생성시 `Frame "mainFrame" {}` 와 같이 id 값을 지정해 준다면 생성된 오브젝트는 그 아이디를 가지게 됩니다. +`Store.GetObject(id:string)->Object` 는 생성된 객체중 가장 첫번째의 id 가 일치하는 오브젝트를 반환해 줍니다. 만약 원한다면 + +```lua +local index = 1 +Frame ("mainFrame" .. index) {} +print(Store.GetObject("mainFrame" .. index)) +``` + +처럼 생성이나 가져오기에서 식을 만들어 넣을 수도 있습니다. + +## 다량의 오브젝트를 다루기 + +또한, Quad 는 다량의 오브젝트를 다루기 편하도록 `Store.GetObjects(id:string)->objectList` 라는 함수를 가지고 있습니다. + +> 주의 : *GetObjects 의 반환값인 objectList 는 캐시해서는 안됩니다. objectList 를 local 에 넣고 계속해서 유지시키지 마십시오, 반환값은 자동으로 업데이트 되지 않습니다. 반환값을 바로바로 사용하는 것이 이상적입니다.* + +```lua +local ScreenGUI = script.Parent +local Quad = require(path.to.module).Init() +local Class,Mount,Store = Quad.Class,Quad.Mount,Quad.Store + +local Frame = Class "Frame" + +Frame "mainFrame" { + Size = UDim2.fromScale(1,1); + Frame "Child" { + Size = UDim2.fromOffset(20,20); + }; + Frame "Child" { + Size = UDim2.fromOffset(20,20); + Position = UDim2.fromOffset(30,0); + }; + Frame "Child" { + Size = UDim2.fromOffset(20,20); + Position = UDim2.fromOffset(60,0); + }; + Frame "Child" { + Size = UDim2.fromOffset(20,20); + Position = UDim2.fromOffset(90,0); + }; +} + +Mount(ScreenGUI,Store.GetObject("mainFrame")) + +-- 값을 설정할 수 있습니다. 그러니 값을 읽거나 :Connect 할 수는 없습니다 +Store.GetObjects("Child").Size = UDim2.fromOffset(30,30) + +-- 필요에 따라 Store.GetObjects("Child") 를 pairs 에 넣어 for 문을 사용하거나 +-- :Each , :EachAsync , :IsEmpty 함수를 이용할 수 있습니다 +task.spawn(function() + while true do + Store.GetObjects("Child"):Each(function (item,index) + item.BackgroundColor3 = Color3.fromRGB( + math.random(0,255), + math.random(0,255), + math.random(0,255) + ) + end) + task.wait(1) + end) +end +``` + +`Store.GetObjects()` 의 반환 `objectList` 의 메소드들은 다음과 같습니다. + +:Each((item:any,index:number)->()) +> 일반적인 반복입니다. for 를 사용하는것과 효과가 같으나 함수를 이용합니다 + +:EachAsync((item:any,index:number)->()) +> 일반적으로 반환을 기다리지 않고 바로바로 다음 아이템으로 넘어갑니다. +> 코루틴이 이용됩니다 `wrap()` + +:IsEmpty() +> 이 ObjectList 가 비어있는지 여부를 반환합니다. + +:Remove(indexOrItem:number|any) +> objectList 에서 해당하는 오브젝트를 제거합니다. +> , & 를 사용한 고급 검색에서는 작동하지 않습니다 + +## 고급 ID 사용하기 + +id 기능을 조금더 고급적인 방법으로 사용할 수도 있습니다. + +```lua +--다음과 같이 a 와 b 모두 가진 프레임을 생성할 수도 있습니다. +Frame "a,b" { + Name = "main"; + Frame "a" { + Name = "Child1"; + }; + Frame "b" { + Name = "Child2"; + }; +} + +-- 다음과 같이 a 와 b 둘다 선택할 수 있습니다 +Store.GetObjects("a,b"):Each(function(item,index) + print(item.Name) +end) +print("----") + +-- 다음과 같이 a 와 b 를 동시에 가진 것을 선택할 수 있습니다 +Store.GetObjects("a&b"):Each(function(item,index) + print(item.Name) +end) +print("----") + +-- 다음과 같이 a와b 를 동시에 가진것과, b 를 가진것을 선택 +-- 할 수 있습니다 +Store.GetObjects("a&b,b"):Each(function(item,index) + print(item.Name) +end) +``` + +또는(or) 연산 `,` 와 그리고(and) 연산 `&` 연산을 GetObjects 에 사용할 수 있고, 생성시에는 `,` 로 여러 아이디를 부여해줄 수 있습니다. *띄어쓰기는 무시됩니다* + +> 주의 : *고급 ID 사용시 :Remove 연산자는 사용할 수 없습니다* diff --git a/docs/kr/tutorial/4_style.md b/docs/kr/tutorial/4_style.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/kr/tutorial/5_events.md b/docs/kr/tutorial/5_events.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/kr/tutorial/6_tween.md b/docs/kr/tutorial/6_tween.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/kr/tutorial/7_register.md b/docs/kr/tutorial/7_register.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/kr/tutorial/8_lang.md b/docs/kr/tutorial/8_lang.md new file mode 100644 index 0000000..4ae5251 --- /dev/null +++ b/docs/kr/tutorial/8_lang.md @@ -0,0 +1,4 @@ + +# 언어관리 + +Quad 에는 귀찮은 언어관리를 자동으로 해주는 모듈이 내장되어 있습니다. diff --git a/docs/kr/tutorial/9_quadProperty.md b/docs/kr/tutorial/9_quadProperty.md new file mode 100644 index 0000000..e69de29 diff --git a/src/class.lua b/src/class.lua index 7296e73..fd7f269 100644 --- a/src/class.lua +++ b/src/class.lua @@ -274,7 +274,9 @@ function module.init(shared) local lastName = prop return function (nprop,...) nprop = nprop or {} - nprop.Name = gsub(gsub(match(lastName,"[^,]+")," +$",""),"^ +","") + if not nprop.Name then + nprop.Name = gsub(gsub(match(lastName,"[^,]+")," +$",""),"^ +","") + end for styleName,styleObj in pairs(styleList) do if match(prop,styleName) then insert(nprop,styleObj) diff --git a/src/libs/AdvancedTween/init.lua b/src/libs/AdvancedTween/init.lua index 66e68ef..443c4ed 100644 --- a/src/libs/AdvancedTween/init.lua +++ b/src/libs/AdvancedTween/init.lua @@ -12,6 +12,15 @@ local insert = table.insert local find = table.find local floor = math.floor +if not find then + find = function (table,item) + for i,v in pairs(table) do + if v == item then return i end + end + return nil + end +end + local script = script local EasingFunctions = require(script and script.EasingFunctions or "EasingFunctions") local Stepped = require(script and script.Stepped or "Stepped") diff --git a/src/mount.lua b/src/mount.lua index 83605cf..6de6eb9 100644 --- a/src/mount.lua +++ b/src/mount.lua @@ -76,7 +76,7 @@ function module.init(shared) mountsClass.__index = mountsClass function mountsClass:Unmount() for _,v in ipairs(self) do - v:unmount() + v:Unmount() end end diff --git a/src/signal.lua b/src/signal.lua index 378c2b7..551e421 100644 --- a/src/signal.lua +++ b/src/signal.lua @@ -54,7 +54,7 @@ function module.init(shared) local connection = {} connection.__index = connection function connection.New(signal,func) - return {signal = signal,func = func} + return setmetatable({signal = signal,func = func},connection) end function connection:Disconnect(slient) local signal = self.signal @@ -69,7 +69,7 @@ function module.init(shared) end for i,v in pairs(onceConnection) do if v.connection == self then - remove(connection,i) + remove(onceConnection,i) return end end diff --git a/src/store.lua b/src/store.lua index 27ad11e..49cb3c4 100644 --- a/src/store.lua +++ b/src/store.lua @@ -11,6 +11,16 @@ local remove = table.remove local gmatch = string.gmatch local gsub = string.gsub local match = string.match +local find = table.find + +if not find then + find = function (table,item) + for i,v in pairs(table) do + if v == item then return i end + end + return nil + end +end local function catch(...) local passed,err = pcall(...) @@ -32,17 +42,17 @@ function module.init(shared) -- id space (array of object) local objectListClass = {__type = "quad_objectlist"} - function objectListClass:Each(func) + function objectListClass:EachAsync(func) local index = 1 for i,v in pairs(self) do - wrap(catch)(func,index,v) + wrap(catch)(func,v,index) index = index + 1 end end - function objectListClass:EachSync(func) + function objectListClass:Each(func) local index = 1 for _,v in pairs(self) do - local ret = func(index,v) + local ret = func(v,index) index = index + 1 if ret then break @@ -50,6 +60,9 @@ function module.init(shared) end end function objectListClass:Remove(indexOrItem) + if self.__locked then + error("This objectList is locked, maybe used Store.GetObjects('a,b')? AdvancedObjectQuery does not support :Remove") + end local thisType = type(indexOrItem) if thisType == "number" then return remove(self,indexOrItem),indexOrItem @@ -78,24 +91,65 @@ function module.init(shared) -- get object array with id (objSpace) function new.GetObjects(ids) - if match(ids,",") then + if match(ids,"[,&]") then + local list = objectListClass.__new() + objectListClass.__locked = true + local checked = {} + for query in gmatch(ids,"[^,]+") do -- split by , + if match(query,"&") then + -- multi ids (and) + + -- make list of combined ids + local queryIds = {} + for id in gmatch(query,"[^&]+") do + id = gsub(gsub(id,"^ +","")," +$","") + insert(queryIds,id) + end + + -- loop as first list + local targetList = queryIds[1] and items[remove(queryIds,1)] + if targetList then + for _,item in pairs(targetList) do + if not checked[item] then + -- check item has all of ids from queryIds + local ok = true + for _,id in ipairs(queryIds) do + local checkList = items[id] + if (not checkList) or (not find(checkList,item)) then + ok = false + break + end + end + + -- insert in return list + if ok then + insert(list,item) + -- prevent multi insert + checked[item] = true + end + end + end + end + else + query = gsub(gsub(query,"^ +","")," +$","") + local idItem = items[query] + if idItem then + for _,item in pairs(idItem) do + if not checked[item] then + insert(list,item) + checked[item] = true -- prevent multi insert + end + end + end + end + end + return list + else local list = items[ids] if list then return list end list = objectListClass.__new() items[ids] = list return list - else - local list = objectListClass.__new() - for id in gmatch(ids,"[^,]+") do -- split by , - id = gsub(gsub(id,"^ +","")," +$","") - local idItem = items[id] - if idItem then - for _,item in pairs(idItem) do - insert(list,item) - end - end - end - return list end end -- get first object with id (not array) diff --git a/src/types.lua b/src/types.lua index 4d3abcb..6363c01 100644 --- a/src/types.lua +++ b/src/types.lua @@ -91,8 +91,8 @@ export type register = { } export type store = {} export type objectList = { - Each: (self:objectList,(item:DOM,index:number)->())->(); - EachSync: (self:objectList,(item:DOM,index:number)->())->(); + Each: (self:objectList,(item:DOM|any,index:number)->())->(); + EachSync: (self:objectList,(item:DOM|any,index:number)->())->(); Remove: (self:objectList,index:number|DOM)->(DOM?,number?); IsEmpty: (self:objectList)->boolean; } diff --git a/testing/init.lua b/testing/init.lua index 73b4071..774ab6e 100644 --- a/testing/init.lua +++ b/testing/init.lua @@ -54,9 +54,7 @@ function module.init() local Global = Store.GetStore "global" local TweenTest = Class(script.tween) - local LangTest = Class(script.lang) - - -- Signal. + -- local LangTest = Class(script.lang) SetTheme(Global) @@ -65,8 +63,8 @@ function module.init() Size = UDim2.fromOffset(400,640); Position = UDim2.fromScale(0.5,0.5); AnchorPoint = Vector2.new(0.5,0.5); - -- TweenTest{}; - LangTest{}; + TweenTest{}; + -- LangTest{}; } } Mount(game.StarterGui,Store.GetObject "MainGui") diff --git a/testing/signal b/testing/signal new file mode 100644 index 0000000..f3ed743 --- /dev/null +++ b/testing/signal @@ -0,0 +1,36 @@ + +local mySignal = Signal.Bindable.New() + +local connection = mySignal:Connect(function(v) + print(v) +end) +mySignal:Fire("우왕") +connection:Disconnect() +mySignal:Fire("이거뜨면안된다") + +task.spawn(function() + task.wait(5) + mySignal:Fire("우와왕") +end) +print(mySignal:Wait()) + +mySignal:Once(function(v) + print(v) +end) +mySignal:Fire("우와와왕") +mySignal:Fire("이것도 뜨면안된다") + +mySignal:Once(function(v) + print(v) +end):Disconnect() +mySignal:Fire("이것도 뜨면안된다*2") + +local myClass = Class.Extend() +function myClass:Render(props) + return Class "TextBox" { + [Event.Prop "Text"] = function(this,value) + self:EmitPropertyChangedSignal("Text",value) + end; + Size = UDim2.new() + } +end