add testing module: signal

This commit is contained in:
Qwreey 2023-02-17 02:22:16 +09:00
parent 46da90f5f8
commit 9e1216c761
9 changed files with 281 additions and 203 deletions

View file

@ -131,7 +131,7 @@ function module.init(shared)
-- store reading
do
local setValue = value:calcWithDefault(item)
local setValue = value:CalcWithDefault(item)
if setValue == nil then
warn "[Quad] register return value must not be nil, but got nil. did you inited values before?"
end
@ -140,7 +140,7 @@ function module.init(shared)
-- adding handle function (bindding)
local function regFn(_,newValue,key)
local setValue,tween = value:calcWithNewValue(item,newValue,key)
local setValue,tween = value:CalcWithNewValue(item,newValue,key)
if tween then
if not advancedTween then
return warn "[QUAD] 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"
@ -161,7 +161,7 @@ function module.init(shared)
SetProperty(item,index,setValue,className)
end
end
value:register(regFn)
value:Register(regFn)
-- this is using hacky of roblox instance
-- this is will keep reference from week table until
@ -335,7 +335,7 @@ function module.init(shared)
--after render
local afterRender = this.AfterRender
if afterRender then
afterRender(self,object)
afterRender(self,object,prop)
end
return self
end

View file

@ -13,6 +13,7 @@ local event = require "event"
local store = require "store"
local class = require "class"
local mount = require "mount"
local signal = require "signal"
-- local lang = require "lang"
-- submodule
@ -43,6 +44,7 @@ function module.Init(id)
end
this.Round = type(round) == "table" and round ---@type quad_module_round
this.Tween = type(advancedTween) == "table" and advancedTween ---@type quad_module_tween
this.Signal = signal.init(this) ---@type quad_module_signal
this.Style = style.init(this) ---@type quad_module_style
this.Event = event.init(this) ---@type quad_module_event
this.Store = store.init(this) ---@type quad_module_store

View file

@ -5,60 +5,18 @@ local running = coroutine.running
local resume = coroutine.resume
local yield = coroutine.yield
local insert = table.insert
local remove = table.remove
local customConnection = {}
customConnection.__index = customConnection
function customConnection.new(signal,func)
func
end
local customSignal = {}
customSignal.__index = customSignal
function customSignal:Fire(...)
local waitting = self.waitting
self.waitting = {}
for _,v in pairs(waitting) do
task.spawn(resume,v,...)
end
for _,v in pairs(self.connection) do
task.spawn(v,...)
end
end
function customSignal:Wait()
insert(self.waitting,running())
return yield()
end
function customSignal:Connect(func)
local connection = self.connection
for _,v in pairs(connection) do
if v == func then
error("Function %s Connected already")
end
end
insert(self.connection,func)
return customConnection.new(self,func)
end
local function onceWaitter(targetFunction)
end
function customSignal:Once()
end
function customSignal.new()
local self = {waitting={},onceConnection={},connection={}}
setmetatable(self,customSignal)
end
function this.new(...)
customSignal.new(...)
end
this.customSignal = customSignal
function module.init(shared)
---@class quad_module_signal
local new = {}
local warn = shared.warn
-- roblox connections disconnecter
local insert = table.insert
local idSpace = {}
local disconnecters = {}
new.disconnecters = disconnecters
local disconnecterClass = {__type = "quad_disconnecter"}
disconnecterClass.__index = disconnecterClass
function disconnecterClass:Add(connection)
insert(self,connection)
end
@ -71,7 +29,7 @@ end
function disconnecterClass:Destroy()
local id = self.id
if id then
idSpace[id] = nil
disconnecters[id] = nil
end
for i,v in pairs(self) do
pcall(v.Disconnect,v)
@ -80,15 +38,114 @@ function disconnecterClass:Destroy()
end
function disconnecterClass.New(id)
if id then
local old = idSpace[id];
local old = disconnecters[id];
if old then
return old;
end
end
local this = setmetatable({id = id},disconnecterClass);
if id then
idSpace[id] = this;
disconnecters[id] = this;
end
return this;
end
disconnecterClass.__index = disconnecterClass
new.Disconnecter = disconnecterClass
local customConnection = {}
customConnection.__index = customConnection
function customConnection.New(signal,func)
return {signal = signal,func = func}
end
function customConnection:Disconnect(slient)
local signal = self.signal
local onceConnection = signal.onceConnection
local connection = signal.connection
for i,v in pairs(connection) do
if v.connection == self then
remove(connection,i)
return
end
end
for i,v in pairs(onceConnection) do
if v.connection == self then
remove(connection,i)
return
end
end
if not slient then
warn(("Connection %s is not found from signal %s, but tried :Disconnect(). Maybe disconnected aleady?"):format(tostring(self),tostring(signal)))
end
end
function customConnection:Destroy()
return self:Disconnect(true)
end
new.Connection = customConnection
local signal = {}
signal.__index = signal
function signal:Fire(...)
local waitting = self.waitting
local onceConnection = self.onceConnection
self.waitting = {}
self.onceConnection = {}
for _,v in pairs(waitting) do
task.spawn(resume,v,...)
end
for _,v in pairs(self.connection) do
task.spawn(v.func,...)
end
for _,v in pairs(onceConnection) do
task.spawn(v.func,...)
end
end
function signal:Wait()
insert(self.waitting,running())
return yield()
end
function signal:CheckConnected(func)
local onceConnection = self.onceConnection
local connection = self.connection
for _,v in pairs(connection) do
if v.func == func then
return true
end
end
for _,v in pairs(onceConnection) do
if v.func == func then
return true
end
end
return false
end
function signal:Connect(func)
if self:CheckConnected(func) then
warn(("[Quad] Function %s Connected already on signal %s"):format(tostring(func),tostring(self)))
end
local thisConnection = customConnection.New(self,func)
insert(self.connection,{func=func,connection=thisConnection})
return thisConnection
end
function signal:Once(func)
if self:CheckConnected(func) then
warn(("[Quad] Function %s Connected already on signal %s"):format(tostring(func),tostring(self)))
end
local thisConnection = customConnection.New(self,func)
insert(self.onceConnection,{func=func,connection=thisConnection})
return thisConnection
end
function signal.New()
local self = {waitting={},onceConnection={},connection={}}
setmetatable(self,signal)
end
new.Signal = signal
return new
end
return module

View file

@ -28,18 +28,18 @@ function module.init(shared)
---@class quad_module_store
local new = {__type = "quad_module_store"}
local items = {}
new.items = items
new.Items = items
-- id space (array of object)
local objectListClass = {__type = "quad_objectlist"}
function objectListClass:each(func)
function objectListClass:Each(func)
local index = 1
for i,v in pairs(self) do
wrap(catch)(func,index,v)
index = index + 1
end
end
function objectListClass:eachSync(func)
function objectListClass:EachSync(func)
local index = 1
for _,v in pairs(self) do
local ret = func(index,v)
@ -49,7 +49,7 @@ function module.init(shared)
end
end
end
function objectListClass:remove(indexOrItem)
function objectListClass:Remove(indexOrItem)
local thisType = type(indexOrItem)
if thisType == "number" then
return remove(self,indexOrItem),indexOrItem
@ -61,7 +61,7 @@ function module.init(shared)
end
end
end
function objectListClass:isEmpty()
function objectListClass:IsEmpty()
if next(self) then return true end
return false
end
@ -69,7 +69,7 @@ function module.init(shared)
return setmetatable({},objectListClass)
end
function objectListClass:__newIndex(key,value) -- props setter
self:each(function (this)
self:Each(function (this)
this[key] = value
end)
end
@ -77,7 +77,7 @@ function module.init(shared)
objectListClass.__index = objectListClass
-- get object array with id (objSpace)
function new.getObjects(ids)
function new.GetObjects(ids)
if match(ids,",") then
local list = items[ids]
if list then return list end
@ -99,13 +99,13 @@ function module.init(shared)
end
end
-- get first object with id (not array)
function new.getObject(id)
function new.GetObject(id)
local item = items[id]
return item and item[next(item)]
end
--TODO: if item is exist already, ignore this call
-- adding object with id
function new.addObject(ids,object)
function new.AddObject(ids,object)
for id in gmatch(ids,"[^,]+") do -- split by ,
-- remove trailing, heading spaces
id = gsub(gsub(id,"^ +","")," +$","")
@ -120,7 +120,7 @@ function module.init(shared)
local registerClass = {
__type = "quad_register";
register = function (s,efunc)
Register = function (s,efunc)
local self = s.store
local events = self.__evt
for key in s.key:gmatch("[^,]+") do
@ -133,35 +133,35 @@ function module.init(shared)
insert(event,efunc)
end
end;
with = function (s,wfunc)
With = function (s,wfunc)
-- s.wfunc = wfunc
-- return s
return setmetatable({wfunc = wfunc},{__index = s})
end;
default = function (s,dvalue)
Default = function (s,dvalue)
-- s.dvalue = dvalue
-- return s
return setmetatable({dvalue = dvalue},{__index = s})
end;
tween = function (s,tvalue)
Tween = function (s,tvalue)
-- s.tvalue = tvalue
-- return s
return setmetatable({tvalue = tvalue},{__index = s})
end;
---@deprecated
from = function (s,fvalue)
From = function (s,fvalue)
warn "[QUAD] register:from() is deprecated. Use register:add(t:table|function) instead"
-- s.fvalue = fvalue
-- return s
return setmetatable({fvalue = fvalue},{__index = s})
end;
add = function (s,avalue)
Add = function (s,avalue)
-- s.avalue = avalue
-- return s
return setmetatable({avalue = avalue},{__index = s})
end;
-- return init data
calcWithDefault = function (s,withItem)
CalcWithDefault = function (s,withItem)
local with = s.wfunc
local tstore = s.store
local rawKey = s.key
@ -188,7 +188,7 @@ function module.init(shared)
warn "[Quad] no default value found."
end
end;
calcWithNewValue = function(s,withItem,newValue,key)
CalcWithNewValue = function(s,withItem,newValue,key)
local with = s.wfunc
local tstore = s.store
local rawKey = s.key
@ -280,7 +280,7 @@ function module.init(shared)
-- calc value
do
local setValue,tween = item:calcWithDefault(withItem)
local setValue,tween = item:CalcWithDefault(withItem)
selfValues[key] = setValue
selfTweens[key] = tween
end
@ -288,10 +288,10 @@ function module.init(shared)
-- make event connection
local function regFn(_,newValue,eventKey)
-- !HOLD IT SELF TO PREVENT THIS REGISTER BEGIN REMOVED FROM MEMORY
local setValue = item:calcWithNewValue(withItem,newValue,eventKey)
local setValue = item:CalcWithNewValue(withItem,newValue,eventKey)
self[key] = setValue
end
item:register(regFn)
item:Register(regFn)
insert(selfKeep,item)
insert(selfKeep,regFn)
end
@ -313,17 +313,17 @@ function module.init(shared)
end
if func then
register.register(func)
register.Register(func)
end
return register
end
function store:default(key,value)
function store:Default(key,value)
if self[key] == nil then
self[key] = value
return
end
end
function new.new(self,id)
function new.New(self,id)
local this = setmetatable(
{
__self = self or {}, -- real value storage
@ -338,8 +338,8 @@ function module.init(shared)
end
return this
end
local storeNew = new.new
function new.getStore(id)
local storeNew = new.New
function new.GetStore(id)
return storeIdSpace[id] or storeNew({},id)
end

View file

@ -9,16 +9,16 @@ function module.init(shared)
local new = {__type = "quad_module_style"};
local styles = {};
new.styles = styles;
new.Styles = styles;
---@class quad_style
local styleClass = {__type = "quad_style"};
styleClass.__index = styleClass;
function new.new(props)
function new.New(props)
return setmetatable(props,styleClass);
end
local newStyle = new.new;
local newStyle = new.New;
setmetatable(new,{
---@return quad_style
@ -50,7 +50,7 @@ function module.init(shared)
end
local parsed = {};
function new.parseStyles(style)
function new.ParseStyles(style)
do -- check cache
local cached = parsed[style];
if cached then return cached; end

View file

@ -2,10 +2,17 @@
-- module_class
-------------------
export type extend = {
Getter: {[string]: ()->any|any?};
Setter: {[string]: ()->()?};
New: (prop:DOM_constructor)->DOM;
Init: (self:DOM)->();
Getter: {[string]: ()->(any?)?};
Setter: {[string]: (value:any)->()?};
New: (prop:DOM_constructor)->DOM|any;
Init: (self:extend,props:store)->();
Render: (self:extend,props:store)->(DOM|any);
AfterRender: (self:extend,item:DOM|any,props:store)->();
GetPropertyChangedSignal: (self:extend,propertyName:string)->signal;
EmitPropertyChangedSignal: (self:extend,propertyName:string,...any)->();
Update: (self:extend)->();
Destroy: (self:extend)->();
UpdateTriggers: {[string]:boolean?};
}
export type DOM = {
[string]: any;
@ -37,24 +44,26 @@ export type module_class = {
-- module_event
-------------------
export type event = string
export type disconnecter = {
Add: (self:disconnecter,connection:RBXScriptConnection)->();
Destroy: (self:disconnecter)->();
Disconnect: (self:disconnecter)->();
}
export type module_event = {
Prop: (propName:string)->event;
CreatedAsync: event;
Created: event;
Bind: (this:DOM|any,key:event|string,func:(...any)->(),typefunc:string?)->();
Disconnecter: (id:string?)->disconnecter;
} & (eventName:string)->event;
-------------------
-- module_signal
-------------------
export type signal = {}
export type module_signal = {}
export type disconnecter = {
Add: (self:disconnecter,connection:RBXScriptConnection)->();
Destroy: (self:disconnecter)->();
Disconnect: (self:disconnecter)->();
New: (id:string?)->disconnecter;
}
export type module_signal = {
Disconnecter: disconnecter;
}
-------------------
-- module_register
@ -81,15 +90,24 @@ export type module_store = {
}
-------------------
-- module_signal
-- module_style
-------------------
export type style = {}
export type module_style = {}
export type module_style = {
New: (props:{[string]:any})->style;
} & (props:{[string]:any})->style
-------------------
-- module_signal
-- module_mount
-------------------
export type module_mount = {}
export type mount = {
Unmount: (self:mount)->();
}
export type mounts = mount
export type module_mount = {
Mount: (to:DOM|any,object:DOM|any,holder:DOM|any?)->mount;
GetHolder: (item:DOM|any)->any?;
} & (to:DOM|any,object:DOM|any,holder:DOM|any?)->mounts
-------------------
-- module_tween
@ -177,10 +195,12 @@ export type module_exported = {
Store: module_store;
Mount: module_mount;
Class: module_class;
Signal: module_signal;
Lang: module_lang;
}
export type module = {
Uninit: (id:string)->();
Init: (id:string)->module_exported;
Init: (id:string?)->module_exported;
}
return {}

View file

@ -1,11 +1,9 @@
local module = {}
local Quad = require(script.Quad)
local IsTestMode = script.Name:match("testing_")
local isTestMode = script.Name:match("testing_")
function module.setTheme(global)
local function SetTheme(global)
local isDark
local hasPermissionToGetSettings = pcall(function()
isDark = tostring(settings().Studio.Theme) == "Dark"
@ -45,41 +43,43 @@ function module.setTheme(global)
end
function module.init()
local quad = Quad.init("ui")
local round,class,mount,store,event,tween,style
= quad.round,quad.class,quad.mount,quad.store,quad.event,quad.tween,quad.style
---@module Quad.src.types
local types = require(script.Parent.Quad.types)
local quad = (require(script.Parent.Quad) :: types.module).Init("ui")
local Round,Class,Mount,Store,Event,Tween,Style,Signal
= quad.Round,quad.Class,quad.Mount,quad.Store,quad.Event,quad.Tween,quad.Style,quad.Signal
local frame = class "Frame"
local gui = class "ScreenGui"
local Frame = Class "Frame"
local Gui = Class "ScreenGui"
local global = store.getStore "global"
local tweenTest = class(script.tween)
local Global = Store.GetStore "global"
local TweenTest = Class(script.tween)
module.setTheme(global)
SetTheme(Global)
gui "MainGui" {
frame "Main" {
Gui "MainGui" {
Frame "Main" {
Size = UDim2.fromOffset(400,640);
Position = UDim2.fromScale(0.5,0.5);
AnchorPoint = Vector2.new(0.5,0.5);
tweenTest{};
TweenTest{};
}
}
mount(game.StarterGui,store.getObject "MainGui")
_G.uiinstance = store.getObject "MainGui"
Mount(game.StarterGui,Store.GetObject "MainGui")
_G.uiinstance = Store.GetObject "MainGui"
end
function module.deinit()
if isTestMode then
if IsTestMode then
local lastInstance = _G.uiinstance
Quad.uninit("ui")
require(script.Parent.Quad).Uninit("ui")
if lastInstance then
lastInstance:Destroy()
end
_G.uiinstance = nil
return
end
Quad.uninit("ui")
require(script.Parent.Quad).Uninit("ui")
end
return module

View file

@ -11,7 +11,7 @@ local function reload()
if lastModule then
local passed,module = pcall(require,lastModule)
if passed then
module.deinit()
pcall(module.deinit)
_G.uiloaded = false
end
lastModule:Destroy()
@ -29,7 +29,7 @@ local function reload()
local module = require(lastModule)
if _G.uiloaded then
module.deinit()
pcall(module.deinit)
_G.uiloaded = false
end
@ -43,7 +43,6 @@ return {
reload()
if not _G.uichangetracker then
---@module "src.test.tracker"
local tracker = require(script.tracker)
local changeTracker = tracker.new(script.Parent)

View file

@ -3,97 +3,97 @@ local module = {}
---@module Quad.src.types
local types = require(script.Parent.Quad.types)
local quad = (require(script.Parent.Quad) :: types.module).init("ui")
local round,class,mount,store,event,tween,style
= quad.round,quad.class,quad.mount,quad.store,quad.event,quad.tween,quad.style
local quad = (require(script.Parent.Quad) :: types.module).Init("ui")
local Round,Class,Mount,Store,Event,Tween,Style
= quad.Round,quad.Class,quad.Mount,quad.Store,quad.Event,quad.Tween,quad.Style
local global = store.getStore "global"
local Global = Store.GetStore "global"
-- Import classes
local frame = class "Frame"
frame.BorderSizePixel = 0
frame.BackgroundColor3 = global "backgroundColor"
local textLabel = class "TextLabel"
textLabel.BackgroundTransparency = 1
textLabel.BorderSizePixel = 0
textLabel.Font = Enum.Font.Gotham;
textLabel.TextColor3 = global "textColor"
textLabel.TextSize = 16;
local Frame = Class "Frame"
Frame.BorderSizePixel = 0
Frame.BackgroundColor3 = Global "backgroundColor"
local TextLabel = Class "TextLabel"
TextLabel.BackgroundTransparency = 1
TextLabel.BorderSizePixel = 0
TextLabel.Font = Enum.Font.Gotham;
TextLabel.TextColor3 = Global "textColor"
TextLabel.TextSize = 16;
local tweenTestFrameStyle = style "tweenTestFrame" {
Style "tweenTestFrame" {
BackgroundTransparency = 0;
Size = UDim2.fromOffset(60,30);
BackgroundColor3 = Color3.fromRGB(0, 0, 0);
};
}
-- Create GUI
local tweenTest = class.extend()
local tweenTest = Class.Extend()
function tweenTest:init(prop)
prop:default("tweenTest",UDim2.fromOffset(50,0))
function tweenTest:Init(Prop)
Prop:Default("Position",UDim2.fromOffset(50,0))
local on = false
task.spawn(function()
while wait(2) do
while task.wait(2) do
on = not on
prop.tweenTest = on and UDim2.new(1,-50-60,0,0) or UDim2.fromOffset(50,0)
Prop.Position = on and UDim2.new(1,-50-60,0,0) or UDim2.fromOffset(50,0)
end
end)
end
function tweenTest:render(prop)
return frame {
function tweenTest:Render(Prop)
return Frame {
Size = UDim2.fromScale(1,1);
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Linear";
Position = prop "tweenTest":add(UDim2.fromOffset(0,80)):tween{Direction = "InOut",Time = 2,Easing = "Linear"};
Position = Prop "Position":Add(UDim2.fromOffset(0,80)):Tween{Direction = "InOut",Time = 2,Easing = "Linear"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Expo";
Position = prop "tweenTest":add(UDim2.fromOffset(0,120)):tween{Direction = "InOut",Time = 2,Easing = "Expo"};
Position = Prop "Position":Add(UDim2.fromOffset(0,120)):Tween{Direction = "InOut",Time = 2,Easing = "Expo"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Quint";
Position = prop "tweenTest":add(UDim2.fromOffset(0,160)):tween{Direction = "InOut",Time = 2,Easing = "Quint"};
Position = Prop "Position":Add(UDim2.fromOffset(0,160)):Tween{Direction = "InOut",Time = 2,Easing = "Quint"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Quart";
Position = prop "tweenTest":add(UDim2.fromOffset(0,200)):tween{Direction = "InOut",Time = 2,Easing = "Quart"};
Position = Prop "Position":Add(UDim2.fromOffset(0,200)):Tween{Direction = "InOut",Time = 2,Easing = "Quart"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Cubic";
Position = prop "tweenTest":add(UDim2.fromOffset(0,240)):tween{Direction = "InOut",Time = 2,Easing = "Cubic"};
Position = Prop "Position":Add(UDim2.fromOffset(0,240)):Tween{Direction = "InOut",Time = 2,Easing = "Cubic"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Quad";
Position = prop "tweenTest":add(UDim2.fromOffset(0,280)):tween{Direction = "InOut",Time = 2,Easing = "Quad"};
Position = Prop "Position":Add(UDim2.fromOffset(0,280)):Tween{Direction = "InOut",Time = 2,Easing = "Quad"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Sin";
Position = prop "tweenTest":add(UDim2.fromOffset(0,320)):tween{Direction = "InOut",Time = 2,Easing = "Sin"};
Position = Prop "Position":Add(UDim2.fromOffset(0,320)):Tween{Direction = "InOut",Time = 2,Easing = "Sin"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Circle";
Position = prop "tweenTest":add(UDim2.fromOffset(0,360)):tween{Direction = "InOut",Time = 2,Easing = "Circle"};
Position = Prop "Position":Add(UDim2.fromOffset(0,360)):Tween{Direction = "InOut",Time = 2,Easing = "Circle"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Exp2";
Position = prop "tweenTest":add(UDim2.fromOffset(0,400)):tween{Direction = "InOut",Time = 2,Easing = "Exp2"};
Position = Prop "Position":Add(UDim2.fromOffset(0,400)):Tween{Direction = "InOut",Time = 2,Easing = "Exp2"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Exp4";
Position = prop "tweenTest":add(UDim2.fromOffset(0,440)):tween{Direction = "InOut",Time = 2,Easing = "Exp4"};
Position = Prop "Position":Add(UDim2.fromOffset(0,440)):Tween{Direction = "InOut",Time = 2,Easing = "Exp4"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Elastic";
Position = prop "tweenTest":add(UDim2.fromOffset(0,480)):tween{Direction = "InOut",Time = 2,Easing = "Elastic"};
Position = Prop "Position":Add(UDim2.fromOffset(0,480)):Tween{Direction = "InOut",Time = 2,Easing = "Elastic"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Bounce";
Position = prop "tweenTest":add(UDim2.fromOffset(0,520)):tween{Direction = "InOut",Time = 2,Easing = "Bounce"};
Position = Prop "Position":Add(UDim2.fromOffset(0,520)):Tween{Direction = "InOut",Time = 2,Easing = "Bounce"};
};
textLabel "tweenTestFrame" {
TextLabel "tweenTestFrame" {
Text = "Back";
Position = prop "tweenTest":add(UDim2.fromOffset(0,560)):tween{Direction = "InOut",Time = 2,Easing = "Back"};
Position = Prop "Position":Add(UDim2.fromOffset(0,560)):Tween{Direction = "InOut",Time = 2,Easing = "Back"};
};
-- frame {
-- Size = UDim2.new(100,100);