Fix signal, mount, types, store bugs. Adding tutorials

This commit is contained in:
Qwreey 2023-02-19 06:48:07 +09:00
parent 52bc94cef2
commit 3e38345dfb
20 changed files with 400 additions and 29 deletions

View file

@ -13,6 +13,6 @@
## 목차
[1. 설치하기](kr/tutorial/1_install)
[1. 설치하기](kr/tutorial/1_importRobloxClass)
[2. 시작하기](kr/tutorial/2_starting)

View file

View file

@ -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)

View file

@ -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()` 후 불러와야 합니다
<br>
아직 생성한 오브젝트를 변수에 넣어야하는것이 귀찮아 보이는가요? 이제 `Store.GetObject(id:string)` 에 대해 알아봅시다.
<br>
[다음 튜토리얼](./3_storeGetObject)

View file

@ -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 연산자는 사용할 수 없습니다*

View file

View file

View file

View file

View file

@ -0,0 +1,4 @@
# 언어관리
Quad 에는 귀찮은 언어관리를 자동으로 해주는 모듈이 내장되어 있습니다.

View file

View file

@ -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)

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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;
}

View file

@ -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")

36
testing/signal Normal file
View file

@ -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