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