mirror of https://github.com/teverse/teverse
Compare commits
No commits in common. "28ed81c357bbb084cd60eca7f73349d14a0b3a20" and "afd690f59825579d30b47da7c8069e8ab5e32124" have entirely different histories.
28ed81c357
...
afd690f598
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020- teverse.com
|
||||
Copyright (c) 2018 teverse.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
This repo contains open source components produced for Teverse.
|
||||
|
||||
Contains the redesigned components of Workshop (``./workshop``).
|
||||
|
||||
|
||||
# Contributing to Teverse
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Fully Redesigned Workshop for TevX.
|
|
@ -0,0 +1,76 @@
|
|||
local keybinder = require("tevgit:workshop/controllers/core/keybinder.lua")
|
||||
local history = require("tevgit:workshop/controllers/core/history.lua")
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
|
||||
local clipboard = {}
|
||||
|
||||
keybinder:bind({
|
||||
name = "copy",
|
||||
key = enums.key.c,
|
||||
priorKey = enums.key.leftCtrl,
|
||||
action = function()
|
||||
clipboard = selection.selection
|
||||
end
|
||||
})
|
||||
|
||||
keybinder:bind({
|
||||
name = "paste",
|
||||
key = enums.key.v,
|
||||
priorKey = enums.key.leftCtrl,
|
||||
action = function()
|
||||
history.beginAction(workspace, "Paste")
|
||||
|
||||
local bounds = aabb()
|
||||
if #clipboard > 0 then
|
||||
bounds.min = clipboard[1].position
|
||||
bounds.max = clipboard[1].position
|
||||
end
|
||||
|
||||
-- translates the pasted objects by:
|
||||
local offset = vector3(0, 0, 0)
|
||||
-- pass the clipboard to calculate bounds
|
||||
for _,v in pairs(clipboard) do
|
||||
if type(v.position) == "vector3" and type(v.size) == "vector3" then
|
||||
bounds:expand(v.position + (v.size / 2))
|
||||
bounds:expand(v.position - (v.size / 2))
|
||||
end
|
||||
end
|
||||
offset = vector3(0, bounds.max.y - bounds.min.y, 0)
|
||||
|
||||
local centre = bounds:getCentre()
|
||||
|
||||
local clones = {}
|
||||
for _,v in pairs(clipboard) do
|
||||
if v and v.alive then
|
||||
local new = v:clone()
|
||||
new.parent = workspace
|
||||
if type(new.position) == "vector3" then
|
||||
new.position = new.position + offset
|
||||
end
|
||||
table.insert(clones, new)
|
||||
end
|
||||
end
|
||||
|
||||
history.endAction()
|
||||
selection.setSelection(clones)
|
||||
end
|
||||
})
|
||||
|
||||
keybinder:bind({
|
||||
name = "duplicate",
|
||||
key = enums.key.d,
|
||||
priorKey = enums.key.leftCtrl,
|
||||
action = function()
|
||||
history.beginAction(workspace, "Duplicate")
|
||||
local clones = {}
|
||||
for _,v in pairs(selection.selection) do
|
||||
if v and v.alive then
|
||||
local new = v:clone()
|
||||
new.parent = workspace
|
||||
table.insert(clones, new)
|
||||
end
|
||||
end
|
||||
history.endAction()
|
||||
selection.setSelection(clones)
|
||||
end
|
||||
})
|
|
@ -0,0 +1,307 @@
|
|||
--[[
|
||||
The history ui window is managed in another module
|
||||
|
||||
Example Usuage:
|
||||
local history = require(thismodule)
|
||||
|
||||
local selection = {obj1, obj2}
|
||||
history.beginAction(selection, "testAction")
|
||||
|
||||
obj1.colour = colour:random()
|
||||
something(obj2)
|
||||
|
||||
history.endAction()
|
||||
|
||||
]]
|
||||
|
||||
local limit = 100 -- how many actions can we store
|
||||
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local pointer = limit
|
||||
local actions = {}
|
||||
|
||||
-- used internally to prevent unfinished actions
|
||||
local actionInProgress = false
|
||||
|
||||
local callback = nil
|
||||
|
||||
-- Used to track changes during an action
|
||||
local changes = {}
|
||||
local destroyedObjects = {}
|
||||
local newObjects = {}
|
||||
|
||||
local eventListeners = {}
|
||||
local actionName = ""
|
||||
|
||||
local changedListener, ChildAddedListener, destroyingListener;
|
||||
|
||||
local function registerEvents(object)
|
||||
table.insert(eventListeners, object:onSync("changed", changedListener))
|
||||
table.insert(eventListeners, object:onSync("childAdded", ChildAddedListener))
|
||||
table.insert(eventListeners, object:onSync("destroying", destroyingListener))
|
||||
end
|
||||
|
||||
-- oldValue added to changed event from "POTATO 0.7.0"
|
||||
changedListener = function (property, value, oldValue)
|
||||
local changedObject = self.object
|
||||
if not changes[changedObject] then
|
||||
changes[changedObject] = {}
|
||||
end
|
||||
|
||||
if not changes[changedObject][property] then
|
||||
changes[changedObject][property] = {oldValue, value}
|
||||
else
|
||||
-- do not change the old value, we've already recorded it.
|
||||
changes[changedObject][property][2] = value
|
||||
end
|
||||
end
|
||||
|
||||
destroyingListener = function()
|
||||
local changedObject = self.object
|
||||
if not changes[changedObject] then
|
||||
changes[changedObject] = {}
|
||||
end
|
||||
-- Object is being destroyed, let's save a copy of all their writable properties so the user can undo this action
|
||||
local members = shared.workshop:getMembersOfObject( changedObject )
|
||||
local toStore = {}
|
||||
for _, prop in pairs(members) do
|
||||
local val = changedObject[prop.property]
|
||||
local pType = type(val)
|
||||
|
||||
if prop.writable and pType ~= "function" then
|
||||
-- We can save it and re-construct it
|
||||
toStore[prop.property] = val
|
||||
end
|
||||
end
|
||||
|
||||
toStore["parent"] = changedObject.parent
|
||||
toStore["className"] = changedObject.className
|
||||
toStore["_ref"] = changedObject
|
||||
|
||||
table.insert(destroyedObjects, toStore)
|
||||
end
|
||||
|
||||
ChildAddedListener = function(child)
|
||||
local changedObject = child
|
||||
if not changes[changedObject] then
|
||||
changes[changedObject] = {}
|
||||
end
|
||||
-- Object is being destroyed, let's save a copy of all their writable properties so the user can undo this action
|
||||
local members = shared.workshop:getMembersOfObject( changedObject )
|
||||
local toStore = {}
|
||||
for _, prop in pairs(members) do
|
||||
local val = changedObject[prop.property]
|
||||
local pType = type(val)
|
||||
|
||||
if prop.writable and pType ~= "function" then
|
||||
-- We can save it and re-construct it
|
||||
toStore[prop.property] = val
|
||||
end
|
||||
end
|
||||
|
||||
toStore["parent"] = changedObject.parent
|
||||
toStore["className"] = changedObject.className
|
||||
toStore["_ref"] = changedObject
|
||||
registerEvents(changedObject)
|
||||
|
||||
table.insert(newObjects, toStore)
|
||||
end
|
||||
|
||||
local function count(dictionary)
|
||||
local i = 0
|
||||
for _,v in pairs(dictionary) do i = i + 1 end
|
||||
return i
|
||||
end
|
||||
|
||||
-- Tell this module that we're about to change some things
|
||||
-- the module will register changed callbacks to record the before/after
|
||||
--
|
||||
-- object : table of teverse objects or teverse object
|
||||
-- name : name of the event/action
|
||||
--
|
||||
-- you need to call endAction after completing your changes to the objects
|
||||
local function beginAction( object, name )
|
||||
assert(not actionInProgress, "please use endAction before starting another")
|
||||
|
||||
actions[pointer+1] = nil
|
||||
|
||||
actionInProgress = true
|
||||
actionName = name or ""
|
||||
if type(object) == "table" then
|
||||
for _,v in pairs(object) do
|
||||
registerEvents(v)
|
||||
end
|
||||
else
|
||||
registerEvents(object)
|
||||
end
|
||||
end
|
||||
|
||||
local function endAction()
|
||||
assert(actionInProgress, "you must call beginAction first")
|
||||
|
||||
-- stop listening to the objects
|
||||
for _,v in pairs(eventListeners) do
|
||||
v:disconnect()
|
||||
end
|
||||
eventListeners = {}
|
||||
|
||||
-- if nothing changed dont create an action
|
||||
if count(changes) > 0 or count(destroyedObjects) > 0 or count(newObjects) > 0 then
|
||||
pointer = pointer + 1
|
||||
if pointer >= limit then
|
||||
actions[pointer - limit] = nil
|
||||
end
|
||||
|
||||
actions[pointer] = {os.time(), actionName, changes, destroyedObjects, newObjects}
|
||||
changes = {}
|
||||
destroyedObjects = {}
|
||||
newObjects = {}
|
||||
|
||||
if type(callback) == "function" then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
actionInProgress = false
|
||||
end
|
||||
|
||||
local function updateReferences(old, new)
|
||||
for p, a in pairs(actions) do
|
||||
local newTbl = {}
|
||||
for ref, props in pairs(a[3]) do
|
||||
if ref ~= old then
|
||||
newTbl[ref] = props
|
||||
else
|
||||
newTbl[new] = props
|
||||
end
|
||||
end
|
||||
a[3] = newTbl
|
||||
end
|
||||
end
|
||||
|
||||
local function undo()
|
||||
if actions[pointer] ~= nil then
|
||||
|
||||
-- destroyed objects (restore)
|
||||
for _, properties in pairs(actions[pointer][4]) do
|
||||
local obj = engine[properties["className"]]()
|
||||
for property, value in pairs(properties) do
|
||||
obj[property] = value
|
||||
end
|
||||
local oldRef = properties["_ref"]
|
||||
properties["_ref"] = obj
|
||||
updateReferences(oldRef, obj)
|
||||
end
|
||||
|
||||
-- created objects (destroy)
|
||||
for _,properties in pairs(actions[pointer][5]) do
|
||||
if properties["_ref"].alive then
|
||||
properties["_ref"]:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
for object, properties in pairs(actions[pointer][3]) do
|
||||
if object and object.alive then
|
||||
for property, values in pairs(properties) do
|
||||
--values[1] = original value
|
||||
--values[2] = changed value
|
||||
object[property] = values[1]
|
||||
end
|
||||
else
|
||||
for k,v in pairs(properties) do print(k,v) end
|
||||
warn("There was a change recorded, but we couldn't find the object.")
|
||||
end
|
||||
end
|
||||
|
||||
pointer = pointer - 1
|
||||
|
||||
if type(callback) == "function" then
|
||||
callback()
|
||||
end
|
||||
else
|
||||
print("nothing to undo")
|
||||
end
|
||||
end
|
||||
|
||||
local function redo()
|
||||
if actions[pointer + 1] ~= nil then
|
||||
pointer = pointer + 1
|
||||
|
||||
-- destroyed objects (destroy)
|
||||
for _, properties in pairs(actions[pointer][4]) do
|
||||
if properties["_ref"].alive then
|
||||
properties["_ref"]:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- created objects (create)
|
||||
for _,properties in pairs(actions[pointer][5]) do
|
||||
local obj = engine[properties["className"]]()
|
||||
for property, value in pairs(properties) do
|
||||
obj[property] = value
|
||||
end
|
||||
local oldRef = properties["_ref"]
|
||||
properties["_ref"] = obj
|
||||
updateReferences(oldRef, obj)
|
||||
end
|
||||
|
||||
for object, properties in pairs(actions[pointer][3]) do
|
||||
if object and object.alive then
|
||||
for property, values in pairs(properties) do
|
||||
--values[1] = original value
|
||||
--values[2] = changed value
|
||||
object[property] = values[2]
|
||||
end
|
||||
else
|
||||
warn("There was a change recorded, but we couldn't find the object.")
|
||||
end
|
||||
end
|
||||
|
||||
if type(callback) == "function" then
|
||||
callback()
|
||||
end
|
||||
else
|
||||
print("nothing to redo")
|
||||
end
|
||||
end
|
||||
|
||||
local keybinder = require("tevgit:workshop/controllers/core/keybinder.lua")
|
||||
|
||||
keybinder:bind({
|
||||
name = "undo",
|
||||
priorKey = enums.key.leftCtrl,
|
||||
key = enums.key.z,
|
||||
action = function ()
|
||||
if not engine.input:isKeyDown(enums.key.leftShift) then
|
||||
undo()
|
||||
else
|
||||
redo()
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
keybinder:bind({
|
||||
name = "redo",
|
||||
priorKey = enums.key.leftCtrl,
|
||||
key = enums.key.y,
|
||||
action = redo
|
||||
})
|
||||
|
||||
return {
|
||||
beginAction = beginAction,
|
||||
endAction = endAction,
|
||||
|
||||
getActions = function() return actions end,
|
||||
getPointer = function() return pointer end,
|
||||
limit = limit,
|
||||
|
||||
setCallback = function (cb)
|
||||
callback = cb
|
||||
end,
|
||||
|
||||
undo = undo,
|
||||
redo = redo,
|
||||
|
||||
count = count
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
-- Copyright (c) 2020 teverse.com
|
||||
|
||||
local hotkeysController = {
|
||||
bindings = {}
|
||||
}
|
||||
|
||||
function hotkeysController:bind(hotkeyData)
|
||||
if (hotkeyData.priorKey) then
|
||||
if (not self.bindings[hotkeyData.priorKey]) then
|
||||
self.bindings[hotkeyData.priorKey] = {}
|
||||
end
|
||||
if (self.bindings[hotkeyData.priorKey][hotkeyData.key]) then
|
||||
error("Hot key " .. hotkeyData.name .. " can not overwrite existing hotkey: " .. self.bindings[hotkeyData.priorKey][hotkeyData.key].name)
|
||||
end
|
||||
self.bindings[hotkeyData.priorKey][hotkeyData.key] = hotkeyData
|
||||
else
|
||||
if (self.bindings[hotkeyData.key]) then
|
||||
error("Hot key " .. hotkeyData.name .. " can not overwrite existing hotkey: " .. self.bindings[hotkeyData.key].name)
|
||||
end
|
||||
self.bindings[hotkeyData.key] = hotkeyData
|
||||
end
|
||||
|
||||
return hotkeyData.action
|
||||
end
|
||||
|
||||
function hotkeysController:handle(inputObject)
|
||||
for key, data in pairs(self.bindings) do
|
||||
if (not data.action) then
|
||||
if (engine.input:isKeyDown(key)) then
|
||||
for key, data in pairs(data) do
|
||||
if (inputObject.key == key) then
|
||||
data.action()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- bad: seperate pass because we prioritise prior key commands.
|
||||
for key, data in pairs(self.bindings) do
|
||||
if (data.action) then
|
||||
if (inputObject.key == key) then
|
||||
data.action()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
engine.input:keyPressed(function(inputObject)
|
||||
if (inputObject.systemHandled) then
|
||||
return
|
||||
else
|
||||
hotkeysController:handle(inputObject)
|
||||
end
|
||||
end)
|
||||
|
||||
return hotkeysController
|
|
@ -0,0 +1,39 @@
|
|||
-- This file manages lights!
|
||||
-- It creates a 3d mesh for each light in the scene
|
||||
-- this is so the developer can easily interact with their lights in the 3d scene...
|
||||
|
||||
local controller = {}
|
||||
controller.lights = {}
|
||||
|
||||
local function processChild(child)
|
||||
if type(child) == "light" then
|
||||
local block = engine.construct("block", workspace, {
|
||||
size = vector3(0.5, 0.5, 0.5),
|
||||
position = child.position,
|
||||
rotation = child.rotation:inverse(),
|
||||
workshopLocked = true,
|
||||
doNotSerialise = true,
|
||||
name = "_CreateMode_",
|
||||
mesh = "tevurl:3d/light.glb"
|
||||
})
|
||||
|
||||
child:on("changed", function(p, v)
|
||||
if p == "position" then
|
||||
block[p] = v
|
||||
elseif p == "rotation" then
|
||||
block[p] = v:inverse()
|
||||
end
|
||||
end)
|
||||
|
||||
controller.lights[block] = child
|
||||
|
||||
child:onSync("destroying", function()
|
||||
controller.lights[block] = nil
|
||||
block:destroy()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
workspace:on("childAdded", processChild)
|
||||
|
||||
return controller
|
|
@ -0,0 +1,206 @@
|
|||
-- Selection Controller is incharge of storing and managing the user’s selection
|
||||
|
||||
local controller = {}
|
||||
|
||||
local boundingBox = engine.construct("block", workspace, {
|
||||
name = "_bounding",
|
||||
wireframe = true,
|
||||
static = true,
|
||||
physics = false,
|
||||
workshopLocked = true,
|
||||
doNotSerialise = true,
|
||||
position = vector3(0, -100, 0)
|
||||
})
|
||||
|
||||
controller.selection = {}
|
||||
controller.destroyingListeners = {}
|
||||
|
||||
controller.callbacks = {}
|
||||
|
||||
controller.fireCallbacks = function ()
|
||||
for _,v in pairs(controller.callbacks) do
|
||||
v()
|
||||
end
|
||||
end
|
||||
|
||||
controller.registerCallback = function (cb)
|
||||
table.insert(controller.callbacks, cb)
|
||||
end
|
||||
|
||||
controller.setSelection = function(obj)
|
||||
controller.selection = {}
|
||||
|
||||
for _,v in pairs(controller.destroyingListeners) do
|
||||
v:disconnect()
|
||||
end
|
||||
controller.destroyingListeners = {}
|
||||
|
||||
controller.addSelection(obj)
|
||||
end
|
||||
|
||||
local function destroyListener()
|
||||
for i,v in pairs(controller.selection) do
|
||||
if v == self.object then
|
||||
table.remove(controller.selection, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
controller.destroyingListeners[self.object] = nil
|
||||
|
||||
self:disconnect()
|
||||
end
|
||||
|
||||
controller.addSelection = function(obj)
|
||||
if type(obj) == "table" then
|
||||
for _,v in pairs(obj) do
|
||||
if v.isA and v:isA("baseClass") and not v.workshopLocked then
|
||||
table.insert(controller.selection, v)
|
||||
controller.destroyingListeners[v] = v:once("destroying", destroyListener)
|
||||
else
|
||||
warn("selecting unknown object")
|
||||
end
|
||||
end
|
||||
else
|
||||
if obj.isA and obj:isA("baseClass") then
|
||||
table.insert(controller.selection, obj)
|
||||
controller.destroyingListeners[obj] = obj:once("destroying", destroyListener)
|
||||
else
|
||||
warn("selecting unknown object")
|
||||
end
|
||||
end
|
||||
controller.fireCallbacks()
|
||||
end
|
||||
|
||||
controller.isSelected = function(obj)
|
||||
for _,v in pairs(controller.selection) do
|
||||
if v == obj then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
controller.hasSelection = function()
|
||||
return #controller.selection > 0
|
||||
end
|
||||
|
||||
local boundingEvents = {}
|
||||
|
||||
local function boundUpdate()
|
||||
if not boundingBox or not boundingBox.alive then return end
|
||||
|
||||
--inefficient, is called for each change
|
||||
local bounds = aabb()
|
||||
|
||||
if #controller.selection > 0 and controller.selection[1].position then
|
||||
bounds.min = controller.selection[1].position
|
||||
bounds.max = controller.selection[1].position
|
||||
|
||||
for _,v in pairs(controller.selection) do
|
||||
if type(v) == "block" then
|
||||
bounds:expand(v)
|
||||
elseif type(v) == "light" then
|
||||
bounds:expand(v.position - vector3(0.25, 0.25, 0.25))
|
||||
bounds:expand(v.position + vector3(0.25, 0.25, 0.25))
|
||||
elseif type(v.position) == "vector3" then
|
||||
bounds:expand(v.position)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
boundingBox.position = bounds:getCentre()
|
||||
boundingBox.size = bounds.max - bounds.min
|
||||
end
|
||||
|
||||
-- on selection changed
|
||||
controller.registerCallback(function()
|
||||
for _,v in pairs(boundingEvents) do
|
||||
v:disconnect()
|
||||
end
|
||||
boundingEvents = {}
|
||||
|
||||
if not boundingBox or not boundingBox.alive then return end
|
||||
|
||||
local bounds = aabb()
|
||||
|
||||
if #controller.selection > 0 and type(controller.selection[1].position) == "vector3" then
|
||||
bounds.min = controller.selection[1].position
|
||||
bounds.max = controller.selection[1].position
|
||||
|
||||
for _,v in pairs(controller.selection) do
|
||||
if type(v.position) == "vector3" then
|
||||
if type(v) == "block" then
|
||||
bounds:expand(v)
|
||||
elseif type(v) == "light" then
|
||||
bounds:expand(v.position - vector3(0.25, 0.25, 0.25))
|
||||
bounds:expand(v.position + vector3(0.25, 0.25, 0.25))
|
||||
elseif type(v.position) == "vector3" then
|
||||
bounds:expand(v.position)
|
||||
end
|
||||
table.insert(boundingEvents, v:changed(boundUpdate))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
boundingBox.position = bounds:getCentre()
|
||||
boundingBox.size = bounds.max - bounds.min
|
||||
end)
|
||||
|
||||
|
||||
local keybinder = require("tevgit:workshop/controllers/core/keybinder.lua")
|
||||
local history = require("tevgit:workshop/controllers/core/history.lua")
|
||||
|
||||
keybinder:bind({
|
||||
name = "delete",
|
||||
key = enums.key.delete,
|
||||
action = function()
|
||||
history.beginAction(controller.selection, "Delete")
|
||||
for _,v in pairs(controller.selection) do
|
||||
v:destroy()
|
||||
end
|
||||
history.endAction()
|
||||
end
|
||||
})
|
||||
|
||||
keybinder:bind({
|
||||
name = "select all",
|
||||
key = enums.key.a,
|
||||
priorKey = enums.key.leftCtrl,
|
||||
action = function()
|
||||
local children = workspace.children
|
||||
for i,v in pairs(children) do
|
||||
if v:isA("camera") then
|
||||
table.remove(children, i)
|
||||
end
|
||||
end
|
||||
controller.setSelection(children)
|
||||
end
|
||||
})
|
||||
|
||||
keybinder:bind({
|
||||
name = "focus",
|
||||
key = enums.key.f,
|
||||
action = function()
|
||||
-- calculate the 'centre' of the selection
|
||||
local bounds = aabb()
|
||||
|
||||
if #controller.selection > 0 then
|
||||
bounds.min = controller.selection[1].position
|
||||
bounds.max = controller.selection[1].position
|
||||
end
|
||||
|
||||
for _,v in pairs(controller.selection) do
|
||||
local size = v.size and v.size or vector3(0.1, 0.1, 0.1)
|
||||
bounds:expand(v.position + (size/2))
|
||||
bounds:expand(v.position - (size/2))
|
||||
end
|
||||
|
||||
local centre = bounds:getCentre()
|
||||
|
||||
workspace.camera:lookAt(centre)
|
||||
end
|
||||
})
|
||||
|
||||
controller.box = boundingBox
|
||||
|
||||
return controller
|
|
@ -0,0 +1,74 @@
|
|||
-- Copyright 2020 teverse.com
|
||||
|
||||
local cameraController = {
|
||||
zoomStep = 3,
|
||||
rotateStep = 0.003,
|
||||
moveStep = 0.5,
|
||||
slow = 0.1
|
||||
}
|
||||
|
||||
cameraController.camera = workspace.camera
|
||||
|
||||
-- Setup the initial position of the camera
|
||||
cameraController.camera.position = vector3(11, 5, 10)
|
||||
cameraController.camera:lookAt(vector3(0,0,0))
|
||||
|
||||
-- Camera key input values
|
||||
cameraController.cameraKeyEventLooping = false
|
||||
cameraController.cameraKeyArray = {
|
||||
[enums.key.w] = vector3(0, 0, 1),
|
||||
[enums.key.s] = vector3(0, 0, -1),
|
||||
[enums.key.a] = vector3(-1, 0, 0),
|
||||
[enums.key.d] = vector3(1, 0, 0),
|
||||
[enums.key.q] = vector3(0, -1, 0),
|
||||
[enums.key.e] = vector3(0, 1, 0)
|
||||
}
|
||||
|
||||
engine.input:mouseScrolled(function( input )
|
||||
if input.systemHandled then return end
|
||||
|
||||
local cameraPos = cameraController.camera.position
|
||||
cameraPos = cameraPos + (cameraController.camera.rotation * (cameraController.cameraKeyArray[enums.key.w] * input.movement.z * cameraController.zoomStep))
|
||||
cameraController.camera.position = cameraPos
|
||||
|
||||
end)
|
||||
|
||||
engine.input:mouseMoved(function( input )
|
||||
if engine.input:isMouseButtonDown( enums.mouseButton.right ) then
|
||||
local pitch = quaternion():setEuler(input.movement.y * cameraController.rotateStep, 0, 0)
|
||||
local yaw = quaternion():setEuler(0, input.movement.x * cameraController.rotateStep, 0)
|
||||
|
||||
-- Applied seperately to avoid camera flipping on the wrong axis.
|
||||
cameraController.camera.rotation = yaw * cameraController.camera.rotation;
|
||||
cameraController.camera.rotation = cameraController.camera.rotation * pitch
|
||||
|
||||
--updatePosition()
|
||||
end
|
||||
end)
|
||||
|
||||
engine.input:keyPressed(function( inputObj )
|
||||
if inputObj.systemHandled or engine.input:isKeyDown(enums.key.leftCtrl) then return end
|
||||
|
||||
if cameraController.cameraKeyArray[inputObj.key] and not cameraController.cameraKeyEventLooping then
|
||||
cameraController.cameraKeyEventLooping = true
|
||||
repeat
|
||||
local cameraPos = cameraController.camera.position
|
||||
|
||||
for key, vector in pairs(cameraController.cameraKeyArray) do
|
||||
-- check this key is pressed (still)
|
||||
if engine.input:isKeyDown(key) then
|
||||
cameraPos = cameraPos + (cameraController.camera.rotation * vector * cameraController.moveStep) * (engine.input:isKeyDown(enums.key.leftShift) and cameraController.slow or 1)
|
||||
end
|
||||
end
|
||||
|
||||
cameraController.cameraKeyEventLooping = (cameraPos ~= cameraController.camera.position)
|
||||
cameraController.camera.position = cameraPos
|
||||
|
||||
wait(0.001)
|
||||
|
||||
until not cameraController.cameraKeyEventLooping
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
return cameraController
|
|
@ -0,0 +1,91 @@
|
|||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
return {
|
||||
camera = require("tevgit:workshop/controllers/environment/camera.lua"),
|
||||
|
||||
createDefaultMap = function ()
|
||||
local mainLight = engine.construct("light", workspace, {
|
||||
name = "mainLight",
|
||||
position = vector3(3, 2, 0),
|
||||
type = enums.lightType.directional,
|
||||
rotation = quaternion():setEuler(math.rad(80), 0, 0),
|
||||
})
|
||||
|
||||
local basePlate = engine.construct("block", workspace, {
|
||||
name = "basePlate",
|
||||
colour = colour(0.9, 0.9, 0.9),
|
||||
size = vector3(100, 1, 100),
|
||||
position = vector3(0, -1, 0),
|
||||
workshopLocked = true
|
||||
})
|
||||
|
||||
engine.construct("block", workspace, {
|
||||
name = "redBlock",
|
||||
colour = colour(1, 0, 0),
|
||||
size = vector3(1, 1, 1),
|
||||
position = vector3(0, 0, 0)
|
||||
})
|
||||
|
||||
engine.construct("block", workspace, {
|
||||
name = "greenBlock",
|
||||
colour = colour(0, 1, 0),
|
||||
size = vector3(1, 1, 1),
|
||||
position = vector3(1, 0, 0),
|
||||
mesh = "primitive:wedge",
|
||||
rotation = quaternion:setEuler(0, math.rad(90), 0)
|
||||
})
|
||||
end,
|
||||
|
||||
setupEnvironment = function ()
|
||||
engine.graphics.clearColour = colour:fromRGB(56,56,66)
|
||||
engine.graphics.ambientColour = colour(0.3, 0.3, 0.3)
|
||||
end,
|
||||
|
||||
createPBRDebugSpheres = function ()
|
||||
for r = 0, 1, 0.1 do
|
||||
for m = 0, 1, 0.1 do
|
||||
local b = engine.construct("block", workspace, {
|
||||
size = vector3(0.8, 0.8, 0.8),
|
||||
position = vector3((r*10)-5, 3, (m*10)-5),
|
||||
mesh = "primitive:sphere",
|
||||
roughness = r,
|
||||
metalness = m
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for r = 0, 1, 0.1 do
|
||||
for m = 0, 1, 0.1 do
|
||||
local b = engine.construct("block", workspace, {
|
||||
size = vector3(10, 1, 10),
|
||||
position = vector3((r*100)-5, 1, (m*100)-5),
|
||||
roughness = r,
|
||||
metalness = m
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local cubes = {}
|
||||
for r = 0, 1, 0.1 do
|
||||
for m = 0, 1, 0.1 do
|
||||
table.insert(cubes, engine.construct("block", workspace, {
|
||||
size = vector3(0.5, 0.5, 0.5),
|
||||
position = vector3((r*10)+10, 3, (m*10)-5),
|
||||
mesh = "tevurl:3d/duck.glb",
|
||||
roughness = r,
|
||||
metalness = m,
|
||||
colour = colour(1,1,0)
|
||||
}))
|
||||
end
|
||||
end
|
||||
|
||||
spawnThread(function()
|
||||
while wait() do
|
||||
for _,v in pairs(cubes) do
|
||||
v.rotation = v.rotation * quaternion:setEuler(0, math.rad(v.roughness*3), math.rad(v.metalness*3))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
-- Used to share variables between scripts
|
||||
|
||||
-- shamelessly stolen from http://lua-users.org/wiki/SimpleRound
|
||||
local function round(n, mult)
|
||||
mult = mult or 1
|
||||
return math.floor((n + mult/2)/mult) * mult
|
||||
end
|
||||
|
||||
local function calculateVertices (block)
|
||||
local vertices = {}
|
||||
for x = -1,1,2 do
|
||||
for y = -1,1,2 do
|
||||
for z = -1,1,2 do
|
||||
table.insert(vertices, block.position + block.rotation* (vector3(x,y,z) *block.size/2))
|
||||
end
|
||||
end
|
||||
end
|
||||
return vertices
|
||||
end
|
||||
|
||||
local faces = {
|
||||
{5, 6, 8, 7}, -- x forward,
|
||||
{1, 2, 4, 3}, -- x backward,
|
||||
{7, 8, 4, 3}, -- y upward,
|
||||
{5, 6, 2, 1}, -- y downward,
|
||||
{6, 2, 4, 8}, -- z forward
|
||||
{5, 1, 3, 7} -- z backward
|
||||
}
|
||||
|
||||
return {
|
||||
-- Storing workshop is important because sandbox access is restricted.
|
||||
workshop = nil,
|
||||
controllers = {},
|
||||
windows = {},
|
||||
|
||||
round = round,
|
||||
roundVector3 = function(v, mult)
|
||||
return vector3(round(v.x, mult), round(v.y, mult), round(v.z, mult))
|
||||
end,
|
||||
roundDp = function (num, numDecimalPlaces)
|
||||
local mult = 10^(numDecimalPlaces or 0)
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end,
|
||||
|
||||
calculateVertices = calculateVertices,
|
||||
|
||||
getCentreOfFace = function (block, face)
|
||||
local vertices = calculateVertices(block)
|
||||
local avg = vector3(0,0,0)
|
||||
for _,i in pairs(faces[face]) do
|
||||
avg = avg + vertices[i]
|
||||
end
|
||||
|
||||
return avg/4
|
||||
end,
|
||||
|
||||
--[[guiLine = function(a, b, parent)
|
||||
local avg = (a + b) / 2
|
||||
local size = a - b
|
||||
local length = size:length()
|
||||
return engine.construct("guiFrame", parent, {
|
||||
position = guiCoord(0, avg.x, 0, avg.y),
|
||||
size = guiCoord(0, 2, 0, length)
|
||||
})
|
||||
end,]]
|
||||
|
||||
-- this is set when workshop is set
|
||||
-- using the haslocaltevgit api
|
||||
developerMode = false
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
-- Tool Constants:
|
||||
local toolName = "Hand"
|
||||
local toolDesc = ""
|
||||
local toolIcon = "fa:s-hand-pointer"
|
||||
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
local history = require("tevgit:workshop/controllers/core/history.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local lightManager = require("tevgit:workshop/controllers/core/lightManager.lua")
|
||||
|
||||
local clickEvent = nil
|
||||
local keyEvent = nil
|
||||
local settingsGui = nil
|
||||
local isDragging = false
|
||||
|
||||
return {
|
||||
name = toolName,
|
||||
icon = toolIcon,
|
||||
desc = toolDesc,
|
||||
|
||||
activate = function()
|
||||
|
||||
settingsGui = ui.create("guiFrame", shared.workshop.interface["_toolBar"], {
|
||||
size = guiCoord(0, 120, 0, 28),
|
||||
position = guiCoord(0, 80, 0, 0),
|
||||
backgroundAlpha = 0.8,
|
||||
borderRadius = 4
|
||||
}, "primary")
|
||||
|
||||
ui.create("guiTextBox", settingsGui, {
|
||||
size = guiCoord(0.6, 0, 0, 18),
|
||||
position = guiCoord(0, 5, 0, 5),
|
||||
text = "Grid Step",
|
||||
fontSize = 18,
|
||||
textAlpha = 0.8
|
||||
}, "primaryText")
|
||||
|
||||
local gridStep = 0.25
|
||||
|
||||
local gridStepInput = ui.create("guiTextBox", settingsGui, {
|
||||
size = guiCoord(0.4, -6, 0, 18),
|
||||
position = guiCoord(0.6, 3, 0, 5),
|
||||
text = tostring(gridStep),
|
||||
fontSize = 18,
|
||||
textAlpha = 0.8,
|
||||
borderRadius = 4,
|
||||
align = enums.align.middle,
|
||||
readOnly = false
|
||||
}, "primaryVariant")
|
||||
|
||||
gridStepInput:on("changed", function()
|
||||
gridStep = tonumber(gridStepInput.text) or 0
|
||||
end)
|
||||
|
||||
local offsets = {}
|
||||
keyEvent = engine.input:keyPressed(function( inputObj )
|
||||
if inputObj.key == enums.key.r and isDragging then
|
||||
local targetRot = quaternion:setEuler(0,math.rad(-45),0)
|
||||
for _,v in pairs(selection.selection) do
|
||||
if offsets[v] then
|
||||
offsets[v][2] = offsets[v][2] * targetRot
|
||||
v.rotation = offsets[v][2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Set the event listener to a variable so we can disconnect this handler
|
||||
-- when the tool is deactivated
|
||||
clickEvent = engine.input:mouseLeftPressed(function ( inputObj )
|
||||
if not inputObj.systemHandled then
|
||||
-- This is not a gui event, let's continue.
|
||||
local hit = engine.physics:rayTestScreen(engine.input.mousePosition)
|
||||
if hit and (not hit.object.workshopLocked or lightManager.lights[hit.object]) then
|
||||
if lightManager.lights[hit.object] then
|
||||
hit.object = lightManager.lights[hit.object]
|
||||
end
|
||||
|
||||
-- user has clicked a object in 3d space.
|
||||
if selection.isSelected(hit.object) then
|
||||
-- user clicked a selected object,
|
||||
-- we're gonna turn into drag mode!
|
||||
|
||||
-- calculate the 'centre' of the selection
|
||||
local bounds = aabb()
|
||||
|
||||
if #selection.selection > 0 then
|
||||
bounds.min = selection.selection[1].position
|
||||
bounds.max = selection.selection[1].position
|
||||
end
|
||||
|
||||
for _,v in pairs(selection.selection) do
|
||||
local size = v.size or vector3(0,0,0)
|
||||
bounds:expand(v.position + (size/2))
|
||||
bounds:expand(v.position - (size/2))
|
||||
end
|
||||
|
||||
local centre = bounds:getCentre()
|
||||
|
||||
-- calculate the mouse's offset from the centre
|
||||
local mouseOffset = hit.hitPosition - centre
|
||||
|
||||
-- calculate every selected item's offset from the centre
|
||||
offsets = {}
|
||||
for _,v in pairs(selection.selection) do
|
||||
--offsets[v] = v.position - centre
|
||||
local relative = quaternion(0, 0, 0, 1) * v.rotation;
|
||||
local positionOffset = (relative*quaternion(0, 0, 0, 1)):inverse() * (v.position - centre)
|
||||
offsets[v] = {positionOffset, relative}
|
||||
end
|
||||
|
||||
-- tell history to monitor changes we make to selected items
|
||||
history.beginAction(selection.selection, "Hand tool drag")
|
||||
|
||||
local grid = engine.construct("grid", workspace, {
|
||||
step = gridStep,
|
||||
colour = colour(0.1, 0, 0.1),
|
||||
size = 12,
|
||||
rotation = quaternion:setEuler(math.rad(90), 0, 0)
|
||||
})
|
||||
|
||||
isDragging = true
|
||||
|
||||
while engine.input:isMouseButtonDown(enums.mouseButton.left) do
|
||||
-- fire a ray, exclude selected items.
|
||||
local hits, didExclude = engine.physics:rayTestScreenAllHits(engine.input.mousePosition, selection.selection)
|
||||
if (#hits > 0) then
|
||||
-- We dont want to raytest with create mode objects1
|
||||
local hit = hits[1]
|
||||
for _,v in pairs(hits) do
|
||||
if v.object.name ~= "_CreateMode_" then
|
||||
hit = v
|
||||
goto skip
|
||||
end
|
||||
end
|
||||
::skip::
|
||||
|
||||
local newCentre = hit.hitPosition - mouseOffset
|
||||
if gridStep ~= 0 then
|
||||
newCentre = shared.roundVector3(newCentre, gridStep)
|
||||
end
|
||||
|
||||
local avgPos = vector3(0,0,0)
|
||||
local minY = hit.hitPosition.y
|
||||
for _,v in pairs(selection.selection) do
|
||||
if offsets[v] then
|
||||
v.position = newCentre + (offsets[v][2] * offsets[v][1])
|
||||
|
||||
-- Calculate the lowest point in the selection:
|
||||
local size = v.size or vector3(0,0,0)
|
||||
minY = math.min(minY, v.position.y - (size.y/2))
|
||||
avgPos = avgPos + v.position
|
||||
end
|
||||
end
|
||||
|
||||
-- If the lowest object is less than the mouse's position
|
||||
if minY < hit.hitPosition.y then
|
||||
local offsetBy = vector3(0, hit.hitPosition.y - minY, 0)
|
||||
|
||||
-- increase height of each selected object so they're above the hovered position.
|
||||
for _,v in pairs(selection.selection) do
|
||||
if offsets[v] then
|
||||
v.position = v.position + offsetBy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
grid.position = (avgPos / #selection.selection) + vector3(0, 0.1, 0)
|
||||
end
|
||||
wait()
|
||||
end
|
||||
|
||||
isDragging = false
|
||||
|
||||
history.endAction()
|
||||
|
||||
grid:destroy()
|
||||
else
|
||||
-- user clicked an unselected object, let's select it
|
||||
if engine.input:isKeyDown(enums.key.leftShift) then
|
||||
-- holding shift, so we append the clicked object to selection
|
||||
selection.addSelection(hit.object)
|
||||
else
|
||||
-- we override the user's selection here
|
||||
selection.setSelection(hit.object)
|
||||
end
|
||||
end
|
||||
else
|
||||
selection.setSelection({})
|
||||
end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
||||
deactivate = function ()
|
||||
if settingsGui then
|
||||
settingsGui:destroy()
|
||||
settingsGui = nil
|
||||
end
|
||||
|
||||
if keyEvent then
|
||||
keyEvent:disconnect()
|
||||
keyEvent = nil
|
||||
end
|
||||
|
||||
if clickEvent then
|
||||
clickEvent:disconnect()
|
||||
clickEvent = nil
|
||||
end
|
||||
end
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
-- This script is responsible for loading in the other sidetools
|
||||
-- This is required by the UI system and the array returned is used to generate the sidebar
|
||||
|
||||
return {
|
||||
handTool = require("tevgit:workshop/controllers/sidetools/hand.lua"),
|
||||
moveTool = require("tevgit:workshop/controllers/sidetools/move.lua"),
|
||||
scaleTool = require("tevgit:workshop/controllers/sidetools/scale.lua"),
|
||||
rotateTool = require("tevgit:workshop/controllers/sidetools/rotate.lua"),
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
-- Tool Constants:
|
||||
local toolName = "Move"
|
||||
local toolDesc = ""
|
||||
local toolIcon = "fa:s-arrows-alt"
|
||||
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
local history = require("tevgit:workshop/controllers/core/history.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local boundingBoxChangedEvent;
|
||||
local gridGuideline;
|
||||
local handles;
|
||||
|
||||
local settingsGui = nil
|
||||
|
||||
local function updateHandles()
|
||||
if handles then
|
||||
if selection.box.size == vector3(0,0,0) then
|
||||
for _,v in pairs(handles) do
|
||||
v[1].size = vector3(0,0,0)
|
||||
v[1].opacity = 0
|
||||
v[1].line.positionA = vector3(0, 0, 0)
|
||||
v[1].line.positionB = vector3(0, 0, 0)
|
||||
end
|
||||
else
|
||||
for _,v in pairs(handles) do
|
||||
v[1].position = selection.box.position + selection.box.rotation* ((v[2] * selection.box.size/2) + (v[2]*1.5))
|
||||
v[1]:lookAt(selection.box.position)
|
||||
v[1].rotation = v[1].rotation * quaternion():setEuler(math.rad(90), 0, 0)
|
||||
v[1].size = vector3(0.2, 0.4, 0.2)
|
||||
v[1].opacity = 1
|
||||
v[1].line.positionA = v[1].position
|
||||
v[1].line.positionB = selection.box.position
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local axes = {"x", "y", "z"}
|
||||
|
||||
return {
|
||||
name = toolName,
|
||||
icon = toolIcon,
|
||||
desc = toolDesc,
|
||||
|
||||
activate = function()
|
||||
settingsGui = ui.create("guiFrame", shared.workshop.interface["_toolBar"], {
|
||||
size = guiCoord(0, 120, 0, 28),
|
||||
position = guiCoord(0, 80, 0, 0),
|
||||
backgroundAlpha = 0.8,
|
||||
borderRadius = 4
|
||||
}, "primary")
|
||||
|
||||
ui.create("guiTextBox", settingsGui, {
|
||||
size = guiCoord(0.6, 0, 0, 18),
|
||||
position = guiCoord(0, 5, 0, 5),
|
||||
text = "Grid Step",
|
||||
fontSize = 18,
|
||||
textAlpha = 0.8
|
||||
}, "primaryText")
|
||||
|
||||
local gridStep = 0.25
|
||||
|
||||
local gridStepInput = ui.create("guiTextBox", settingsGui, {
|
||||
size = guiCoord(0.4, -6, 0, 18),
|
||||
position = guiCoord(0.6, 3, 0, 5),
|
||||
text = tostring(gridStep),
|
||||
fontSize = 18,
|
||||
textAlpha = 0.8,
|
||||
borderRadius = 4,
|
||||
align = enums.align.middle,
|
||||
readOnly = false
|
||||
}, "primaryVariant")
|
||||
|
||||
gridStepInput:on("changed", function()
|
||||
gridStep = tonumber(gridStepInput.text) or 0
|
||||
end)
|
||||
|
||||
local offsets = {}
|
||||
keyEvent = engine.input:keyPressed(function( inputObj )
|
||||
if inputObj.key == enums.key.r and isDragging then
|
||||
local targetRot = quaternion:setEuler(0,math.rad(-45),0)
|
||||
for _,v in pairs(selection.selection) do
|
||||
if offsets[v] then
|
||||
offsets[v][2] = offsets[v][2] * targetRot
|
||||
v.rotation = offsets[v][2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- This is used to raycast the user's mouse position to an axis
|
||||
gridGuideline = engine.construct("block", workspace, {
|
||||
name = "_GridGuideline",
|
||||
size = vector3(0, 0, 0),
|
||||
colour = colour(1, 1, 1),
|
||||
opacity = 0,
|
||||
workshopLocked = true,
|
||||
castsShadows = false
|
||||
})
|
||||
|
||||
handles = {}
|
||||
|
||||
for axisNumber, axis in pairs(axes) do
|
||||
for i = -1, 1, 2 do
|
||||
local face = vector3(0, 0, 0)
|
||||
face[axis] = i
|
||||
|
||||
local handle = engine.construct("block", nil, {
|
||||
name = "_CreateMode_",
|
||||
castsShadows = false,
|
||||
opacity = 0,
|
||||
renderQueue = 1,
|
||||
doNotSerialise = true,
|
||||
size = vector3(0.1, 0.1, 0.1),
|
||||
colour = colour(axisNumber == 1 and 1 or 0, axisNumber == 2 and 1 or 0, axisNumber == 3 and 1 or 0),
|
||||
--emissiveColour = colour(axisNumber == 1 and 1 or 0, axisNumber == 2 and 1 or 0, axisNumber == 3 and 1 or 0),
|
||||
workshopLocked = true,
|
||||
mesh = "primitive:cone"
|
||||
})
|
||||
|
||||
engine.construct("line", handle, {name = "line", colour = handle.colour})
|
||||
|
||||
handle:mouseLeftPressed(function()
|
||||
gridGuideline.size = vector3(300, 0.1, 300)
|
||||
|
||||
local gridVisual = engine.construct("grid", workspace, {
|
||||
step = gridStep,
|
||||
colour = colour(0.1, 0, 0.1),
|
||||
size = 25,
|
||||
rotation = quaternion:setEuler(math.rad(90), 0, 0)
|
||||
})
|
||||
|
||||
local last = nil
|
||||
|
||||
history.beginAction(selection.selection, "Move tool drag")
|
||||
|
||||
while engine.input:isMouseButtonDown(enums.mouseButton.left) do
|
||||
-- Position and rotate the invisible guideline to face the camera.
|
||||
-- We use this guideline to raycast with
|
||||
local pos1 = gridGuideline.position
|
||||
pos1[axis] = 0
|
||||
|
||||
local pos2 = workspace.camera.position
|
||||
pos2[axis] = 0
|
||||
|
||||
local lookAt = gridGuideline.rotation:setLookRotation( pos1 - pos2 )
|
||||
gridGuideline.rotation = lookAt * quaternion():setEuler(math.rad(90),0,0)
|
||||
gridGuideline.position = selection.box.position
|
||||
|
||||
if axis == "y" then
|
||||
gridVisual.rotation = quaternion:setLookRotation( pos1 - pos2 ) * quaternion():setEuler(math.rad(180),0,0)
|
||||
end
|
||||
gridVisual.position = selection.box.position
|
||||
|
||||
local mouseHits = engine.physics:rayTestScreenAllHits( engine.input.mousePosition )
|
||||
local mouseHit = nil
|
||||
-- We only want the gridGuideline
|
||||
for _,hit in pairs(mouseHits) do
|
||||
if hit.object == gridGuideline then
|
||||
mouseHit = hit
|
||||
goto skip_loop
|
||||
end
|
||||
end
|
||||
::skip_loop::
|
||||
|
||||
if mouseHit and mouseHit.object == gridGuideline then
|
||||
local target = mouseHit.hitPosition
|
||||
local didMove = true
|
||||
if last ~= nil then
|
||||
local translateBy = vector3(0, 0, 0)
|
||||
translateBy[axis] = target[axis] - last
|
||||
|
||||
if gridStep ~= 0 then
|
||||
translateBy = shared.roundVector3(translateBy, gridStep)
|
||||
if translateBy[axis] == 0 then
|
||||
didMove = false
|
||||
end
|
||||
end
|
||||
|
||||
if didMove then
|
||||
for _,v in pairs(selection.selection) do
|
||||
if type(v.position) == "vector3" then
|
||||
v.position = v.position + translateBy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if didMove then
|
||||
last = target[axis]
|
||||
end
|
||||
end
|
||||
|
||||
wait()
|
||||
end
|
||||
|
||||
history.endAction()
|
||||
|
||||
gridVisual:destroy()
|
||||
gridGuideline.size = vector3(0, 0, 0)
|
||||
end)
|
||||
|
||||
table.insert(handles, {handle, face})
|
||||
end
|
||||
end
|
||||
|
||||
boundingBoxChangedEvent = selection.box:changed(updateHandles)
|
||||
updateHandles()
|
||||
end,
|
||||
|
||||
deactivate = function ()
|
||||
if settingsGui then
|
||||
settingsGui:destroy()
|
||||
settingsGui = nil
|
||||
end
|
||||
|
||||
if boundingBoxChangedEvent then
|
||||
boundingBoxChangedEvent:disconnect()
|
||||
boundingBoxChangedEvent = nil
|
||||
end
|
||||
|
||||
if gridGuideline then
|
||||
gridGuideline:destroy()
|
||||
gridGuideline = nil
|
||||
end
|
||||
|
||||
if handles then
|
||||
for _,v in pairs(handles) do
|
||||
v[1]:destroy()
|
||||
end
|
||||
handles = nil
|
||||
end
|
||||
end
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
-- Tool Constants:
|
||||
local toolName = "Rotate"
|
||||
local toolDesc = ""
|
||||
local toolIcon = "fa:s-redo"
|
||||
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
local history = require("tevgit:workshop/controllers/core/history.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local sensitivity = 50
|
||||
|
||||
local arrows = {
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
}
|
||||
|
||||
-- Each axis gets four arrows...
|
||||
-- This table maps each arrow index to an vertice index
|
||||
local arrowsVerticesMap = {
|
||||
{6, 4, 2, 1}, --x
|
||||
{2, 1, 7, 6}, --y
|
||||
{5, 7, 3, 1} --z
|
||||
}
|
||||
|
||||
local function positionArrows()
|
||||
if selection.box.size == vector3(0,0,0) then
|
||||
for _,v in pairs(arrows) do
|
||||
for i,arrow in pairs(v) do
|
||||
arrow.physics = false
|
||||
arrow.opacity = 0
|
||||
end
|
||||
end
|
||||
else
|
||||
local vertices = shared.calculateVertices(selection.box)
|
||||
|
||||
for a,v in pairs(arrows) do
|
||||
for i,arrow in pairs(v) do
|
||||
if i == 2 then i = 3 end
|
||||
arrow.physics = true
|
||||
arrow.opacity = 1
|
||||
arrow.position = vertices[arrowsVerticesMap[a][i]]
|
||||
if a == 1 then
|
||||
arrow.rotation = quaternion:setEuler(math.rad((i)*90), 0, math.rad(-90))
|
||||
elseif a == 2 then
|
||||
arrow.rotation = quaternion:setEuler(0, math.rad((i-1)*-90), 0)
|
||||
else
|
||||
arrow.rotation = quaternion:setEuler(math.rad((i-1)*-90), math.rad(90), math.rad(90))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local boundingEvent
|
||||
|
||||
-- calculates angle ABC
|
||||
-- returns in radians
|
||||
local function calculateAngleBetween3Points(a, b, c)
|
||||
local v1 = a - b
|
||||
local v2 = c - b
|
||||
return math.acos(v1:normal():dot(v2:normal()))
|
||||
end
|
||||
|
||||
local function calculateCircleAngle(hitbox, pos)
|
||||
local hitboxPosition = hitbox.position
|
||||
local hitboxRotation = hitbox.rotation
|
||||
|
||||
local hitboxUp = hitboxPosition + (hitboxRotation * vector3(0,0,-10))
|
||||
local hitboxRight = hitboxPosition + (hitboxRotation * vector3(10,0,0))
|
||||
|
||||
local angle = calculateAngleBetween3Points(hitboxUp, hitboxPosition, pos)
|
||||
if hitboxRight:dot(pos) < 0 then
|
||||
angle = (math.pi-angle)+math.pi
|
||||
end
|
||||
|
||||
return angle
|
||||
end
|
||||
|
||||
return {
|
||||
name = toolName,
|
||||
icon = toolIcon,
|
||||
desc = toolDesc,
|
||||
|
||||
activate = function()
|
||||
for axis = 1, 3 do
|
||||
local newArrow = engine.construct("block", engine.workspace, {
|
||||
name = "_CreateMode_",
|
||||
castsShadows = false,
|
||||
opacity = 0,
|
||||
renderQueue=1,
|
||||
doNotSerialise=true,
|
||||
size = vector3(.4, 0.1, .4),
|
||||
colour = colour(axis == 1 and 1 or 0, axis == 2 and 1 or 0, axis == 3 and 1 or 0),
|
||||
--emissiveColour = colour(axis == 1 and 0.5 or 0, axis == 2 and 0.5 or 0, axis == 3 and 0.5 or 0),
|
||||
workshopLocked = true,
|
||||
mesh = "tevurl:3d/arrowCurved.glb"
|
||||
})
|
||||
|
||||
newArrow:mouseLeftPressed(function ()
|
||||
local hitbox = engine.construct("block", workspace, {
|
||||
name = "_CreateMode_",
|
||||
castsShadows = false,
|
||||
opacity = 0,
|
||||
renderQueue = 1,
|
||||
doNotSerialise=true,
|
||||
size = vector3(60, 0.1, 60),
|
||||
workshopLocked = true,
|
||||
position = shared.getCentreOfFace(selection.box, (axis*2)-1),
|
||||
rotation = newArrow.rotation
|
||||
})
|
||||
hitbox.rotation = hitbox.rotation:setLookRotation( hitbox.position - workspace.camera.position ) * quaternion():setEuler(math.rad(90),0,0)
|
||||
--hitbox:lookAt(workspace.camera.position)
|
||||
|
||||
local mouseHits = engine.physics:rayTestScreenAllHits( engine.input.mousePosition )
|
||||
local mouseHit = nil
|
||||
for _,hit in pairs(mouseHits) do
|
||||
if hit.object == hitbox then
|
||||
mouseHit = hit
|
||||
goto skip_loop
|
||||
end
|
||||
end
|
||||
::skip_loop::
|
||||
|
||||
if not mouseHit then
|
||||
print("Did not collide")
|
||||
hitbox:destroy()
|
||||
return nil
|
||||
end
|
||||
|
||||
local start = mouseHit.hitPosition
|
||||
history.beginAction(selection.selection, "Rotate tool drag")
|
||||
|
||||
while engine.input:isMouseButtonDown(enums.mouseButton.left) and wait() do
|
||||
hitbox.rotation = hitbox.rotation:setLookRotation( hitbox.position - workspace.camera.position ) * quaternion():setEuler(math.rad(90),0,0)
|
||||
|
||||
local mouseHits = engine.physics:rayTestScreenAllHits( engine.input.mousePosition )
|
||||
local mouseHit = nil
|
||||
for _,hit in pairs(mouseHits) do
|
||||
if hit.object == hitbox then
|
||||
mouseHit = hit
|
||||
goto skip_loop
|
||||
end
|
||||
end
|
||||
::skip_loop::
|
||||
|
||||
if mouseHit then
|
||||
local current = mouseHit.hitPosition
|
||||
local diff = (start-current)
|
||||
local travelled = diff:length()
|
||||
|
||||
-- length of vectors is never less than 0. let's fix that
|
||||
if (newArrow.rotation * vector3(0,0,1)):dot(diff) < 0 then
|
||||
--user moved their mouse in an opposite direction to the arrow
|
||||
travelled = -travelled
|
||||
end
|
||||
|
||||
local n = shared.round(math.rad(travelled*sensitivity), math.rad(10))
|
||||
|
||||
for _,v in pairs(selection.selection) do
|
||||
local euler = vector3(
|
||||
axis == 1 and n or 0,
|
||||
axis == 2 and n or 0,
|
||||
axis == 3 and n or 0
|
||||
)
|
||||
|
||||
v.rotation = v.rotation * quaternion:setEuler(v.rotation:inverse() * euler)
|
||||
end
|
||||
|
||||
if n ~= 0 then
|
||||
start = current
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hitbox:destroy()
|
||||
|
||||
history.endAction()
|
||||
end)
|
||||
|
||||
|
||||
|
||||
table.insert(arrows[axis], newArrow)
|
||||
end
|
||||
|
||||
boundingEvent = selection.box:changed(positionArrows)
|
||||
|
||||
positionArrows()
|
||||
end,
|
||||
|
||||
deactivate = function ()
|
||||
boundingEvent:disconnect()
|
||||
boundingEvent = nil
|
||||
|
||||
for _,v in pairs(arrows) do
|
||||
for _,arrow in pairs(v) do
|
||||
print(arrow)
|
||||
arrow:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
arrows = {
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
}
|
||||
end
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
-- Tool Constants:
|
||||
local toolName = "Scale"
|
||||
local toolDesc = ""
|
||||
local toolIcon = "fa:s-expand"
|
||||
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
local history = require("tevgit:workshop/controllers/core/history.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local boundingBoxChangedEvent;
|
||||
local gridGuideline;
|
||||
local handles;
|
||||
|
||||
local settingsGui = nil
|
||||
|
||||
local function updateHandles()
|
||||
if handles then
|
||||
if selection.box.size == vector3(0,0,0) then
|
||||
for _,v in pairs(handles) do
|
||||
v[1].size = vector3(0,0,0)
|
||||
v[1].opacity = 0
|
||||
v[1].line.positionA = vector3(0, 0, 0)
|
||||
v[1].line.positionB = vector3(0, 0, 0)
|
||||
end
|
||||
else
|
||||
for _,v in pairs(handles) do
|
||||
v[1].position = selection.box.position + selection.box.rotation* ((v[2] * selection.box.size/2) + (v[2]*1.5))
|
||||
v[1]:lookAt(selection.box.position)
|
||||
v[1].rotation = v[1].rotation * quaternion():setEuler(math.rad(90), 0, 0)
|
||||
v[1].size = vector3(0.4, 0.4, 0.4)
|
||||
v[1].opacity = 1
|
||||
v[1].line.positionA = v[1].position
|
||||
v[1].line.positionB = selection.box.position
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local axes = {"x", "y", "z"}
|
||||
|
||||
local debugBlock = engine.construct("block", nil, {
|
||||
name = "_CreateMode_",
|
||||
castsShadows = false,
|
||||
doNotSerialise = true,
|
||||
size = vector3(0.1, 0.1, 0.1),
|
||||
workshopLocked = true
|
||||
})
|
||||
|
||||
return {
|
||||
name = toolName,
|
||||
icon = toolIcon,
|
||||
desc = toolDesc,
|
||||
|
||||
activate = function()
|
||||
settingsGui = ui.create("guiFrame", shared.workshop.interface["_toolBar"], {
|
||||
size = guiCoord(0, 120, 0, 28),
|
||||
position = guiCoord(0, 80, 0, 0),
|
||||
backgroundAlpha = 0.8,
|
||||
borderRadius = 4
|
||||
}, "primary")
|
||||
|
||||
ui.create("guiTextBox", settingsGui, {
|
||||
size = guiCoord(0.6, 0, 0, 18),
|
||||
position = guiCoord(0, 5, 0, 5),
|
||||
text = "Grid Step",
|
||||
fontSize = 18,
|
||||
textAlpha = 0.8
|
||||
}, "primaryText")
|
||||
|
||||
local gridStep = 1
|
||||
|
||||
local gridStepInput = ui.create("guiTextBox", settingsGui, {
|
||||
size = guiCoord(0.4, -6, 0, 18),
|
||||
position = guiCoord(0.6, 3, 0, 5),
|
||||
text = tostring(gridStep),
|
||||
fontSize = 18,
|
||||
textAlpha = 0.8,
|
||||
borderRadius = 4,
|
||||
align = enums.align.middle,
|
||||
readOnly = false
|
||||
}, "primaryVariant")
|
||||
|
||||
gridStepInput:on("changed", function()
|
||||
gridStep = tonumber(gridStepInput.text) or 0
|
||||
end)
|
||||
|
||||
local offsets = {}
|
||||
keyEvent = engine.input:keyPressed(function( inputObj )
|
||||
if inputObj.key == enums.key.r and isDragging then
|
||||
local targetRot = quaternion:setEuler(0,math.rad(-45),0)
|
||||
for _,v in pairs(selection.selection) do
|
||||
if offsets[v] then
|
||||
offsets[v][2] = offsets[v][2] * targetRot
|
||||
v.rotation = offsets[v][2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- This is used to raycast the user's mouse position to an axis
|
||||
gridGuideline = engine.construct("block", workspace, {
|
||||
name = "_GridGuideline",
|
||||
size = vector3(0, 0, 0),
|
||||
colour = colour(1, 1, 1),
|
||||
opacity = 0,
|
||||
workshopLocked = true,
|
||||
castsShadows = false
|
||||
})
|
||||
|
||||
handles = {}
|
||||
|
||||
for axisNumber, axis in pairs(axes) do
|
||||
for i = -1, 1, 2 do
|
||||
local face = vector3(0, 0, 0)
|
||||
face[axis] = i
|
||||
|
||||
local handle = engine.construct("block", nil, {
|
||||
name = "_CreateMode_",
|
||||
castsShadows = false,
|
||||
opacity = 0,
|
||||
renderQueue = 1,
|
||||
doNotSerialise = true,
|
||||
size = vector3(0.1, 0.1, 0.1),
|
||||
colour = colour(axisNumber == 1 and 1 or 0, axisNumber == 2 and 1 or 0, axisNumber == 3 and 1 or 0),
|
||||
--emissiveColour = colour(axisNumber == 1 and 1 or 0, axisNumber == 2 and 1 or 0, axisNumber == 3 and 1 or 0),
|
||||
workshopLocked = true
|
||||
})
|
||||
|
||||
engine.construct("line", handle, {name = "line", colour = handle.colour})
|
||||
|
||||
handle:mouseLeftPressed(function()
|
||||
gridGuideline.size = vector3(300, 0.1, 300)
|
||||
|
||||
local gridVisual = engine.construct("grid", workspace, {
|
||||
step = gridStep,
|
||||
colour = colour(0.1, 0, 0.1),
|
||||
size = 25,
|
||||
rotation = quaternion:setEuler(math.rad(90), 0, 0)
|
||||
})
|
||||
|
||||
local last = nil
|
||||
|
||||
history.beginAction(selection.selection, "Scale tool drag")
|
||||
|
||||
while engine.input:isMouseButtonDown(enums.mouseButton.left) do
|
||||
-- Position and rotate the invisible guideline to face the camera.
|
||||
-- We use this guideline to raycast with
|
||||
local pos1 = gridGuideline.position
|
||||
pos1[axis] = 0
|
||||
|
||||
local pos2 = workspace.camera.position
|
||||
pos2[axis] = 0
|
||||
|
||||
local lookAt = gridGuideline.rotation:setLookRotation( pos1 - pos2 )
|
||||
gridGuideline.rotation = lookAt * quaternion():setEuler(math.rad(90),0,0)
|
||||
gridGuideline.position = selection.box.position
|
||||
|
||||
if axis == "y" then
|
||||
gridVisual.rotation = quaternion:setLookRotation( pos1 - pos2 ) * quaternion():setEuler(math.rad(180),0,0)
|
||||
end
|
||||
|
||||
gridVisual.position = selection.box.position
|
||||
|
||||
local mouseHits = engine.physics:rayTestScreenAllHits( engine.input.mousePosition )
|
||||
local mouseHit = nil
|
||||
-- We only want the gridGuideline
|
||||
for _,hit in pairs(mouseHits) do
|
||||
if hit.object == gridGuideline then
|
||||
mouseHit = hit
|
||||
goto skip_loop
|
||||
end
|
||||
end
|
||||
::skip_loop::
|
||||
|
||||
if mouseHit and mouseHit.object == gridGuideline then
|
||||
local target = mouseHit.hitPosition
|
||||
local dist = selection.box.position[axis] - target[axis]
|
||||
local didMove = true
|
||||
if last ~= nil then
|
||||
local translateBy = vector3(0, 0, 0)
|
||||
translateBy[axis] = math.abs(dist) - math.abs(last)
|
||||
|
||||
local offsetBy = vector3(0, 0, 0)
|
||||
offsetBy[axis] = dist - last
|
||||
|
||||
if gridStep ~= 0 then
|
||||
translateBy = shared.roundVector3(translateBy, gridStep)
|
||||
offsetBy = shared.roundVector3(offsetBy, gridStep)
|
||||
if translateBy[axis] == 0 then
|
||||
didMove = false
|
||||
end
|
||||
end
|
||||
|
||||
print(translateBy, offsetBy)
|
||||
|
||||
if didMove then
|
||||
--print(dist, translateBy)
|
||||
for _,v in pairs(selection.selection) do
|
||||
if type(v.position) == "vector3" and type(v.size) == "vector3" then
|
||||
local scaleBy = (v.rotation * translateBy)
|
||||
scaleBy = vector3(math.abs(scaleBy.x), math.abs(scaleBy.y), math.abs(scaleBy.z))
|
||||
if scaleBy.x < 0.01 then
|
||||
scaleBy.x = 0
|
||||
end
|
||||
if scaleBy.y < 0.01 then
|
||||
scaleBy.y = 0
|
||||
end
|
||||
if scaleBy.z < 0.01 then
|
||||
scaleBy.z = 0
|
||||
end
|
||||
|
||||
if translateBy[axis] < 0 then
|
||||
scaleBy = -scaleBy
|
||||
end
|
||||
v.position = v.position - (offsetBy / 2)
|
||||
v.size = v.size + scaleBy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if didMove then
|
||||
last = dist
|
||||
end
|
||||
end
|
||||
|
||||
wait()
|
||||
end
|
||||
history.endAction()
|
||||
gridVisual:destroy()
|
||||
gridGuideline.size = vector3(0, 0, 0)
|
||||
end)
|
||||
|
||||
table.insert(handles, {handle, face})
|
||||
end
|
||||
end
|
||||
|
||||
boundingBoxChangedEvent = selection.box:changed(updateHandles)
|
||||
updateHandles()
|
||||
end,
|
||||
|
||||
deactivate = function ()
|
||||
if settingsGui then
|
||||
settingsGui:destroy()
|
||||
settingsGui = nil
|
||||
end
|
||||
|
||||
if boundingBoxChangedEvent then
|
||||
boundingBoxChangedEvent:disconnect()
|
||||
boundingBoxChangedEvent = nil
|
||||
end
|
||||
|
||||
if gridGuideline then
|
||||
gridGuideline:destroy()
|
||||
gridGuideline = nil
|
||||
end
|
||||
|
||||
if handles then
|
||||
for _,v in pairs(handles) do
|
||||
v[1]:destroy()
|
||||
end
|
||||
handles = nil
|
||||
end
|
||||
end
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
-- Auto Save
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local autoSave = {}
|
||||
|
||||
autoSave.Enabled = false
|
||||
autoSave.timeLimit = 5 -- Default (move to settings value soon)
|
||||
|
||||
autoSave.Sync = function()
|
||||
return spawnThread(function()
|
||||
while true do
|
||||
if autoSave.Enabled then
|
||||
shared.workshop:saveGame()
|
||||
end
|
||||
wait(autoSave.timeLimit)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return autoSave
|
|
@ -0,0 +1,299 @@
|
|||
--[[
|
||||
Requires refactoring
|
||||
]]
|
||||
|
||||
local controller = {}
|
||||
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local hues = {
|
||||
colour(1,0,0),
|
||||
colour(1,0,1),
|
||||
colour(0,0,1),
|
||||
colour(0,1,1),
|
||||
colour(0,1,0),
|
||||
colour(1,1,0),
|
||||
colour(1,0,0),
|
||||
}
|
||||
|
||||
local window = ui.window(shared.workshop.interface,
|
||||
"Colour Picker",
|
||||
guiCoord(0, 300, 0, 186),
|
||||
guiCoord(0.5, -150, 0.5, -93),
|
||||
false,
|
||||
true)
|
||||
window.zIndex = 10
|
||||
window.visible = false
|
||||
local callback = nil
|
||||
|
||||
local startColour = colour(1,0,0)
|
||||
local currentColour = colour(1,0,0)
|
||||
|
||||
local colourPickerGradient = engine.construct("guiFrameMultiColour", window.content, {
|
||||
name = "square",
|
||||
size = guiCoord(0, 150, 0, 150),
|
||||
position = guiCoord(0, 5, 0, 5),
|
||||
topLeftColour = colour(1,1,1),
|
||||
topRightColour = startColour,
|
||||
bottomLeftColour = colour(1,1,1),
|
||||
bottomRightColour = startColour
|
||||
})
|
||||
|
||||
-- To have black on the bottom we need to overlay this...
|
||||
engine.construct("guiFrameMultiColour", colourPickerGradient, {
|
||||
name = "overlay",
|
||||
size = guiCoord(1,0,1,0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
|
||||
topLeftColour = colour(0,0,0),
|
||||
topLeftAlpha = 0,
|
||||
|
||||
topRightColour = colour(0,0,0),
|
||||
topRightAlpha = 0,
|
||||
|
||||
bottomLeftColour = colour(0,0,0),
|
||||
bottomLeftAlpha = 1,
|
||||
|
||||
bottomRightColour = colour(0,0,0),
|
||||
bottomRightAlpha = 1,
|
||||
|
||||
handleEvents = false
|
||||
})
|
||||
|
||||
local marker = engine.construct("guiFrame", colourPickerGradient, {
|
||||
name = "marker",
|
||||
size = guiCoord(0, 6, 0, 6),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
handleEvents=false,
|
||||
backgroundColour = colour(1,1,1),
|
||||
borderColour = colour(0,0,0),
|
||||
zIndex = 10,
|
||||
borderWidth = 1,
|
||||
borderRadius = 6,
|
||||
borderAlpha = 1
|
||||
})
|
||||
|
||||
local hueBar = engine.construct("guiFrame", window.content, {
|
||||
name = "hueBar",
|
||||
size = guiCoord(0, 30, 1, -10),
|
||||
position = guiCoord(0, 160, 0, 5),
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
local hueBarMARKER = engine.construct("guiFrame", hueBar, {
|
||||
name = "hueBarMARKER",
|
||||
size = guiCoord(1, 0, 0, 1),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
handleEvents=false,
|
||||
backgroundAlpha = 0,
|
||||
borderColour = colour(0,0,0),
|
||||
zIndex = 10,
|
||||
borderWidth = 2,
|
||||
borderAlpha = 1
|
||||
})
|
||||
|
||||
for i = 1, 6 do
|
||||
local colourPickerGradient = engine.construct("guiFrameMultiColour", hueBar, {
|
||||
handleEvents = false,
|
||||
size = guiCoord(1, 0, 1/6, 1),
|
||||
position = guiCoord(0, 0, (i-1)*(1/6), 0),
|
||||
topLeftColour = hues[i],
|
||||
topRightColour = hues[i],
|
||||
bottomLeftColour = hues[i+1],
|
||||
bottomRightColour = hues[i+1],
|
||||
handleEvents = false
|
||||
})
|
||||
end
|
||||
|
||||
local rLabel = ui.create("guiTextBox", window.content, {
|
||||
name = "labelR",
|
||||
size = guiCoord(0, 20, 0, 16),
|
||||
position = guiCoord(0,200,0,5),
|
||||
fontSize = 16,
|
||||
textAlpha = 0.6,
|
||||
text = "R",
|
||||
align = enums.align.topLeft
|
||||
}, "primaryText")
|
||||
|
||||
local rInput = ui.create("guiTextBox", window.content, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
fontSize = 18,
|
||||
name = "r",
|
||||
size = guiCoord(1, -220, 0,16),
|
||||
position = guiCoord(0, 220, 0, 5),
|
||||
text = "1",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
local gLabel = rLabel:clone()
|
||||
gLabel.name = "gLabel"
|
||||
gLabel.text = "G"
|
||||
gLabel.parent = window.content
|
||||
gLabel.position = guiCoord(0, 200, 0, 22)
|
||||
--themeController.add(gLabel, "primaryText")
|
||||
|
||||
local g = rInput:clone()
|
||||
g.name = "g"
|
||||
g.parent = window.content
|
||||
g.position = guiCoord(0, 220, 0, 22)
|
||||
-- themeController.add(g, "primary")
|
||||
|
||||
local bLabel = rLabel:clone()
|
||||
bLabel.name = "bLabel"
|
||||
bLabel.text = "B"
|
||||
bLabel.parent = window.content
|
||||
bLabel.position = guiCoord(0, 200, 0, 39)
|
||||
--themeController.add(bLabel, "primaryText")
|
||||
|
||||
local b = rInput:clone()
|
||||
b.name = "b"
|
||||
b.parent = window.content
|
||||
b.position = guiCoord(0, 220, 0, 39)
|
||||
|
||||
|
||||
local hexLabel = rLabel:clone()
|
||||
hexLabel.name = "hexLabel"
|
||||
hexLabel.text = "#"
|
||||
hexLabel.parent = window.content
|
||||
hexLabel.position = guiCoord(0, 200, 0, 56)
|
||||
--themeController.add(bLabel, "primaryText")
|
||||
|
||||
local HEX = rInput:clone()
|
||||
HEX.name = "FFFFFF"
|
||||
HEX.parent = window.content
|
||||
HEX.position = guiCoord(0, 220, 0, 56)
|
||||
-- themeController.add(b, "primary")
|
||||
|
||||
local preview = engine.construct("guiFrame", window.content, {
|
||||
position = guiCoord(0, 220, 0, 73),
|
||||
size = guiCoord(1, -220, 0, 16)
|
||||
})
|
||||
|
||||
local function handler()
|
||||
local newR = tonumber(rInput.text)
|
||||
local newG = tonumber(g.text)
|
||||
local newB = tonumber(b.text)
|
||||
if not newR or not newG or not newB then return end
|
||||
|
||||
controller.setColour(colour:fromRGB(newR, newG, newB))
|
||||
end
|
||||
|
||||
rInput:textInput(handler)
|
||||
g:textInput(handler)
|
||||
b:textInput(handler)
|
||||
HEX:textInput(function()
|
||||
controller.setColour(colour:fromHex(HEX.text), true)
|
||||
end)
|
||||
|
||||
hueBar:mouseLeftPressed(function ()
|
||||
while engine.input:isMouseButtonDown(enums.mouseButton.left) do
|
||||
local pos = engine.input.mousePosition - hueBar.absolutePosition
|
||||
local size = hueBar.absoluteSize
|
||||
|
||||
local y = pos.y/hueBar.absoluteSize.y
|
||||
|
||||
local sector = math.ceil(pos.y/(size.y * (1/6)))
|
||||
local hue = hues[sector]
|
||||
if hue and hues[sector+1] then
|
||||
|
||||
hueBarMARKER.position = guiCoord(0,0,y,0)
|
||||
|
||||
local selected = hue:lerp(hues[sector+1], (y - ((size.y * ((sector-1)/6))/hueBar.absoluteSize.y)) / (1/6))
|
||||
startColour = selected
|
||||
colourPickerGradient.topRightColour = startColour
|
||||
colourPickerGradient.bottomRightColour = startColour
|
||||
|
||||
local x = (marker.position.offsetX)/colourPickerGradient.absoluteSize.x
|
||||
local y = (marker.position.offsetY)/colourPickerGradient.absoluteSize.y
|
||||
|
||||
local selectedColour = startColour:lerp(colour(1,1,1), 1-x)
|
||||
selectedColour = selectedColour:lerp(colour(0,0,0), y)
|
||||
preview.backgroundColour = selectedColour
|
||||
|
||||
rInput.text = tostring(math.floor(selectedColour.r*255))
|
||||
g.text = tostring(math.floor(selectedColour.g*255))
|
||||
b.text = tostring(math.floor(selectedColour.b*255))
|
||||
HEX.text = selectedColour:getHex()
|
||||
end
|
||||
|
||||
wait()
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback(preview.backgroundColour)
|
||||
end
|
||||
end)
|
||||
|
||||
colourPickerGradient:mouseLeftPressed(function ()
|
||||
while engine.input:isMouseButtonDown(enums.mouseButton.left) do
|
||||
local pos = engine.input.mousePosition - colourPickerGradient.absolutePosition
|
||||
marker.position = guiCoord(0, pos.x-2, 0, pos.y-2)
|
||||
|
||||
local x = pos.x/colourPickerGradient.absoluteSize.x
|
||||
local y = pos.y/colourPickerGradient.absoluteSize.y
|
||||
|
||||
local selectedColour = startColour:lerp(colour(1,1,1), 1-x)
|
||||
selectedColour = selectedColour:lerp(colour(0,0,0), y)
|
||||
preview.backgroundColour = selectedColour
|
||||
|
||||
rInput.text = tostring(math.floor(selectedColour.r*255))
|
||||
g.text = tostring(math.floor(selectedColour.g*255))
|
||||
b.text = tostring(math.floor(selectedColour.b*255))
|
||||
HEX.text = selectedColour:getHex()
|
||||
wait()
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback(preview.backgroundColour)
|
||||
end
|
||||
end)
|
||||
|
||||
controller.setColour = function(c, dontUpdateHex)
|
||||
local h,s,l = c:getHSV()
|
||||
h=(1-h)*360
|
||||
local markerh = math.ceil(h / 60)
|
||||
if markerh <= 0 then markerh = 1 end
|
||||
|
||||
local pos = hueBar.absolutePosition
|
||||
local size = hueBar.absoluteSize
|
||||
|
||||
local hue = hues[markerh]
|
||||
|
||||
local selected = hue:lerp(hues[markerh+1], ((h - (60*(markerh-1)))/60))
|
||||
|
||||
startColour = selected
|
||||
colourPickerGradient.topRightColour = startColour
|
||||
colourPickerGradient.bottomRightColour = startColour
|
||||
|
||||
preview.backgroundColour = c
|
||||
|
||||
rInput.text = tostring(math.floor(c.r*255))
|
||||
g.text = tostring(math.floor(c.g*255))
|
||||
b.text = tostring(math.floor(c.b*255))
|
||||
if not dontUpdateHex then
|
||||
HEX.text = c:getHex()
|
||||
end
|
||||
|
||||
marker.position = guiCoord(0, (s) * colourPickerGradient.absoluteSize.x, 0, (1-l) * colourPickerGradient.absoluteSize.y)
|
||||
marker.position = marker.position + guiCoord(0, -2, 0, -2)
|
||||
|
||||
hueBarMARKER.position = guiCoord(0,0,h/360,0)
|
||||
|
||||
if callback then
|
||||
callback(preview.backgroundColour)
|
||||
end
|
||||
end
|
||||
|
||||
controller.window = window
|
||||
|
||||
controller.prompt = function(startColour, cb)
|
||||
callback = nil
|
||||
controller.setColour(startColour)
|
||||
callback = cb
|
||||
controller.window.visible = true
|
||||
end
|
||||
|
||||
return controller
|
|
@ -0,0 +1,220 @@
|
|||
local controller = {}
|
||||
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
local context = require("tevgit:workshop/controllers/ui/core/contextMenu.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
|
||||
-- store icons for each class type
|
||||
-- use a table of two icons,
|
||||
-- [1] will be unexpanded and [2] will be used when the obj is expanded.
|
||||
local overridingIcons = {
|
||||
script = "fa:s-file-code",
|
||||
input = {"fa:s-keyboard", "fa:r-keyboard"},
|
||||
debug = "fa:s-bug",
|
||||
light = "fa:s-lightbulb",
|
||||
block = "fa:s-cube",
|
||||
camera = "fa:s-camera",
|
||||
}
|
||||
|
||||
-- dictionary of buttons to their corrosponding objects.
|
||||
local buttonToObject = {}
|
||||
|
||||
local function updatePositions(frame)
|
||||
local y = 10
|
||||
if not frame then
|
||||
frame = controller.scrollView
|
||||
else
|
||||
y = 20
|
||||
end
|
||||
|
||||
if frame.children then
|
||||
for _, v in pairs(frame.children) do
|
||||
if v.name ~= "icon" then
|
||||
v.position = guiCoord(0, 10, 0, y)
|
||||
y = y + updatePositions(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(frame) == "guiTextBox" then
|
||||
|
||||
local regularIconWithChildren = "fa:s-folder"
|
||||
local regularIconWithOutChildren = "fa:r-folder"
|
||||
local expandedIcon = "fa:s-folder-open"
|
||||
|
||||
local icons = overridingIcons[buttonToObject[frame].className]
|
||||
if icons then
|
||||
if type(icons) == "string" then
|
||||
regularIconWithChildren = icons
|
||||
regularIconWithOutChildren = icons
|
||||
else
|
||||
regularIconWithChildren = icons[1]
|
||||
regularIconWithOutChildren = icons[1]
|
||||
expandedIcon = icons[2]
|
||||
end
|
||||
end
|
||||
|
||||
if y == 20 then
|
||||
-- no children
|
||||
if buttonToObject[frame] and buttonToObject[frame].children and
|
||||
#buttonToObject[frame].children > 0 then
|
||||
-- object has children but is not expanded
|
||||
frame.icon.texture = regularIconWithChildren
|
||||
frame.icon.imageAlpha = 1
|
||||
frame.textAlpha = 1
|
||||
frame.fontFile = "local:OpenSans-SemiBold.ttf"
|
||||
else
|
||||
-- object has no children
|
||||
frame.icon.texture = regularIconWithOutChildren
|
||||
frame.icon.imageAlpha = 0.5
|
||||
frame.textAlpha = .6
|
||||
frame.fontFile = "local:OpenSans-Regular.ttf"
|
||||
end
|
||||
else
|
||||
-- object is expanded
|
||||
frame.textAlpha = 0.6
|
||||
frame.fontFile = "local:OpenSans-Regular.ttf"
|
||||
frame.icon.imageAlpha = 0.75
|
||||
frame.icon.texture = expandedIcon
|
||||
end
|
||||
|
||||
if buttonToObject[frame] and selection.isSelected(buttonToObject[frame]) then
|
||||
frame.backgroundAlpha = 0.3
|
||||
else
|
||||
frame.backgroundAlpha = 0
|
||||
end
|
||||
end
|
||||
|
||||
return y
|
||||
end
|
||||
|
||||
controller.updatePositions = updatePositions
|
||||
|
||||
local function createHierarchyButton(object, guiParent)
|
||||
local btn = ui.create("guiTextBox", guiParent, {
|
||||
text = " " .. object.name, -- ik...
|
||||
size = guiCoord(1, -6, 0, 18),
|
||||
fontSize = 16,
|
||||
cropChildren = false,
|
||||
backgroundAlpha = 0,
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, "backgroundText")
|
||||
|
||||
buttonToObject[btn] = object
|
||||
|
||||
local icon = ui.create("guiImage", btn, {
|
||||
name = "icon",
|
||||
texture = "fa:s-folder",
|
||||
position = guiCoord(0, 1, 0, 1),
|
||||
size = guiCoord(0, 16, 0, 16),
|
||||
handleEvents = false,
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
local expanded = false
|
||||
local lastClick = 0
|
||||
|
||||
btn:onSync("mouseRightPressed", function()
|
||||
if (object:isA("folder")) then
|
||||
selection.setSelection(object.children)
|
||||
propertyEditor.generateProperties(object)
|
||||
else
|
||||
selection.setSelection({object})
|
||||
end
|
||||
controller.scrollView.canvasSize = guiCoord(1, 0, 0, updatePositions())
|
||||
end)
|
||||
|
||||
btn:mouseLeftReleased(function()
|
||||
if os.time() - lastClick < 0.35 then
|
||||
lastClick = 0
|
||||
-- expand
|
||||
expanded = not expanded
|
||||
if expanded then
|
||||
for _, child in pairs(object.children) do
|
||||
if child.name ~= "_CreateMode_" then
|
||||
createHierarchyButton(child, btn)
|
||||
end
|
||||
end
|
||||
controller.scrollView.canvasSize =
|
||||
guiCoord(1, 0, 0, updatePositions())
|
||||
if object.className == "script" then
|
||||
object:editExternal()
|
||||
-- require("tevgit:create/controllers/scriptController.lua").editScript(object)
|
||||
end
|
||||
else
|
||||
for _, v in pairs(btn.children) do
|
||||
if v.name ~= "icon" then
|
||||
if buttonToObject[v] then
|
||||
buttonToObject[v] = nil
|
||||
end
|
||||
v:destroy()
|
||||
end
|
||||
end
|
||||
controller.scrollView.canvasSize =
|
||||
guiCoord(1, 0, 0, updatePositions())
|
||||
end
|
||||
elseif object.name ~= "_bounding" then
|
||||
-- single click
|
||||
local currentTime = os.time()
|
||||
lastClick = currentTime
|
||||
|
||||
if (object:isA("folder")) then
|
||||
selection.setSelection(object.children)
|
||||
propertyEditor.generateProperties(object)
|
||||
else
|
||||
selection.setSelection(object)
|
||||
end
|
||||
|
||||
controller.scrollView.canvasSize =
|
||||
guiCoord(1, 0, 0, updatePositions())
|
||||
end
|
||||
end)
|
||||
|
||||
local childAddedEvent = object:on("childAdded", function(child)
|
||||
if expanded then createHierarchyButton(child, btn) end
|
||||
controller.scrollView.canvasSize = guiCoord(1, 0, 0, updatePositions())
|
||||
end)
|
||||
|
||||
local childRemovedEvent = object:onSync("childRemoved", function(child)
|
||||
if expanded then
|
||||
for button, obj in pairs(buttonToObject) do
|
||||
if obj == child and button.alive then
|
||||
button:destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
controller.scrollView.canvasSize = guiCoord(1, 0, 0, updatePositions())
|
||||
end)
|
||||
|
||||
btn:once("destroying", function() childAddedEvent:disconnect() end)
|
||||
|
||||
if object:isA("luaSharedFolder")
|
||||
or object:isA("luaServerFolder")
|
||||
or object:isA("luaClientFolder") then
|
||||
context.bind(btn, {
|
||||
{name = "Add Script", callback = function() engine.construct("script", object) end}
|
||||
})
|
||||
else
|
||||
-- selectionController.applyContext(btn)
|
||||
end
|
||||
return btn
|
||||
end
|
||||
|
||||
controller.window = ui.window(shared.workshop.interface, "Hierarchy",
|
||||
guiCoord(0, 260, 0, 400), -- size
|
||||
guiCoord(1, -260, 0.75, -25), -- pos
|
||||
true, -- dockable
|
||||
true -- hidable
|
||||
)
|
||||
controller.window.visible = true
|
||||
|
||||
controller.scrollView = ui.create("guiScrollView", controller.window.content, {
|
||||
name = "scrollview",
|
||||
size = guiCoord(1, 0, 1, 0)
|
||||
}, "primaryText")
|
||||
|
||||
createHierarchyButton(engine, controller.scrollView)
|
||||
controller.scrollView.canvasSize = guiCoord(1, 0, 0, updatePositions())
|
||||
|
||||
return controller
|
|
@ -0,0 +1,64 @@
|
|||
-- the front-end for /workshop/controllers/core/history.lua
|
||||
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local history = shared.controllers.history
|
||||
|
||||
local window = ui.window(shared.workshop.interface,
|
||||
"History",
|
||||
guiCoord(0, 420, 0, 250), --size
|
||||
guiCoord(0.5, -210, 0.5, -25), --pos
|
||||
false, --dockable
|
||||
true -- hidable
|
||||
)
|
||||
|
||||
local function draw()
|
||||
window.content:destroyAllChildren()
|
||||
|
||||
local actions = history.getActions()
|
||||
local latestAction = history.getPointer()
|
||||
|
||||
local yPos = 0
|
||||
|
||||
-- render 'future' actions that are still on the stack
|
||||
-- these only exist if the user has undone something but hasn't started a new action
|
||||
if actions[latestAction+1] ~= nil then
|
||||
local i = latestAction + 1
|
||||
while actions[i] ~= nil do
|
||||
local action = actions[i]
|
||||
local formattedDate = os.date("%H:%M:%S", action[1])
|
||||
local actionName = action[2]:len() > 5 and action[2]:sub(0, 4) .. "..." or action[2]
|
||||
|
||||
ui.create("guiTextBox", window.content, {
|
||||
size = guiCoord(1, 0, 0, 18),
|
||||
position = guiCoord(0, 0, 0, yPos),
|
||||
fontFile = "tevurl:font/OpenSans-Italic.ttf",
|
||||
text = string.format("[ UNDONE ] %s (change: %i, add: %i, rem: %i)", actionName, history.count(action[3]), history.count(action[4]), history.count(action[5]))
|
||||
}, "backgroundText")
|
||||
|
||||
yPos = yPos + 20
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
for i = latestAction, 0, -1 do
|
||||
local action = actions[i]
|
||||
if action then
|
||||
local formattedDate = os.date("%H:%M:%S", action[1])
|
||||
local actionName = action[2]:len() > 5 and action[2]:sub(0, 4) .. "..." or action[2]
|
||||
|
||||
ui.create("guiTextBox", window.content, {
|
||||
size = guiCoord(1, 0, 0, 18),
|
||||
position = guiCoord(0, 0, 0, yPos),
|
||||
text = string.format("[ %s ] %s (change: %i, add: %i, rem: %i)", formattedDate, actionName, history.count(action[3]), #action[4], #action[5])
|
||||
}, "backgroundText")
|
||||
|
||||
yPos = yPos + 20
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
draw()
|
||||
history.setCallback(draw)
|
||||
|
||||
return window
|
|
@ -0,0 +1,127 @@
|
|||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local keybinder = require("tevgit:workshop/controllers/core/keybinder.lua")
|
||||
local history = shared.controllers.history
|
||||
|
||||
local window = ui.create("guiFrame", shared.workshop.interface["_toolBar"], {
|
||||
size = guiCoord(0, 32, 0, 100),
|
||||
position = guiCoord(0, 40, 0, 0),
|
||||
backgroundAlpha = 0.8,
|
||||
borderRadius = 4
|
||||
}, "primary")
|
||||
|
||||
local function insert(mesh)
|
||||
if not mesh then
|
||||
mesh = "primitive:cube"
|
||||
end
|
||||
|
||||
local insertPos = vector3(0, 0, 0)
|
||||
|
||||
local hit = engine.physics:rayTestScreen(engine.input.screenSize/2) -- what's in the centre of the screen?
|
||||
if hit then
|
||||
local hitDist = (shared.controllers.env.camera.camera.position - hit.hitPosition):length()
|
||||
if hitDist < 1 or hitDist > 40 then
|
||||
insertPos = shared.controllers.env.camera.camera.position + (shared.controllers.env.camera.camera.rotation * vector3(0, 0, 10))
|
||||
else
|
||||
insertPos = hit.hitPosition + vector3(0, 0.5, 0)
|
||||
end
|
||||
else
|
||||
insertPos = shared.controllers.env.camera.camera.position + (shared.controllers.env.camera.camera.rotation * vector3(0, 0, 10))
|
||||
end
|
||||
|
||||
history.beginAction(workspace, "Inserter")
|
||||
local block;
|
||||
|
||||
if mesh ~= "light" then
|
||||
block = engine.construct("block", workspace, {
|
||||
mesh = mesh,
|
||||
colour = colour(0.8, 0.8, 0.8),
|
||||
position = insertPos
|
||||
})
|
||||
else
|
||||
block = engine.construct("light", workspace, {
|
||||
position = insertPos
|
||||
})
|
||||
end
|
||||
|
||||
history.endAction()
|
||||
|
||||
return block
|
||||
end
|
||||
|
||||
local adders = {
|
||||
{
|
||||
name = "Cube",
|
||||
icon = "fa:s-cube",
|
||||
callback = function()
|
||||
insert()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "Sphere",
|
||||
icon = "fa:s-globe",
|
||||
callback = function()
|
||||
insert("primitive:sphere")
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "Light",
|
||||
icon = "fa:s-lightbulb",
|
||||
callback = function()
|
||||
insert("light")
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
local currentY = 5
|
||||
local toolIndex = 0
|
||||
for _, options in pairs(adders) do
|
||||
|
||||
-- used to assign a number keybind to each tool
|
||||
toolIndex = toolIndex + 1
|
||||
|
||||
-- options is the table returned by the tool's module.
|
||||
-- e.g. workshop/controllers/sidetools/hand.lua
|
||||
|
||||
local newTabBtn = ui.create("guiTextBox", window, {
|
||||
text = options.name,
|
||||
position = guiCoord(0, 4, 0, currentY),
|
||||
size = guiCoord(0, 24, 0, 24),
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, "primaryText")
|
||||
|
||||
if options.icon then
|
||||
newTabBtn.text = ""
|
||||
ui.create("guiImage", newTabBtn, {
|
||||
name = "icon",
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0., 0),
|
||||
texture = options.icon,
|
||||
handleEvents = false,
|
||||
imageAlpha = 0.75
|
||||
}, "primaryImage")
|
||||
end
|
||||
|
||||
local keybindText = ""
|
||||
if toolIndex < 10 then
|
||||
keybindText = " [ALT + " .. toolIndex .. "]"
|
||||
|
||||
keybinder:bind({
|
||||
name = "Insert " .. options.name .. " shape",
|
||||
priorKey = enums.key.alt,
|
||||
key = enums.key["number"..toolIndex],
|
||||
action = options.callback
|
||||
})
|
||||
end
|
||||
-- shows a tool tip if the user hovers over the button
|
||||
ui.tooltip(newTabBtn, options.name .. keybindText)
|
||||
|
||||
newTabBtn:mouseLeftPressed(options.callback)
|
||||
|
||||
currentY = currentY + 32
|
||||
end
|
||||
|
||||
window.size = guiCoord(0, 32, 0, currentY - 5)
|
||||
|
||||
return window
|
|
@ -0,0 +1,643 @@
|
|||
-- this module is responsible for creating the inputguis
|
||||
|
||||
local modulePrefix = "tevgit:workshop/controllers/ui/components/propertyEditor/"
|
||||
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local themer = require("tevgit:workshop/controllers/ui/core/themer.lua")
|
||||
local colourPicker = require("tevgit:workshop/controllers/ui/components/colourPicker.lua")
|
||||
|
||||
local parseInputs = require(modulePrefix .. "parseInputs.lua")
|
||||
local meshShortcuts = require(modulePrefix .. "meshShortcuts.lua")
|
||||
|
||||
local createInputs;
|
||||
|
||||
createInputs = {
|
||||
default = function(instance, property, value)
|
||||
return ui.create("guiFrame", nil, {
|
||||
backgroundAlpha = 0.25,
|
||||
name = "inputContainer",
|
||||
size = guiCoord(0.45, 0, 0, 20),
|
||||
position = guiCoord(0.55,0,0,0),
|
||||
cropChildren = false
|
||||
}, "secondary")
|
||||
end,
|
||||
|
||||
block = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = true,
|
||||
fontSize = 18,
|
||||
name = "input",
|
||||
size = guiCoord(1, -4, 1, -2),
|
||||
position = guiCoord(0, 2, 0, 1),
|
||||
text = "Instance Selector",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
boolean = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
container.backgroundAlpha = 0
|
||||
local x = ui.create("guiImage", container, {
|
||||
name = "input",
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
texture = "fa:s-toggle-on"
|
||||
}, "successImage")
|
||||
|
||||
x:mouseLeftReleased(function ()
|
||||
value = not value
|
||||
x.texture = value and "fa:s-toggle-on" or "fa:s-toggle-off"
|
||||
themer.registerGui(x, value and "successImage" or "errorImage")
|
||||
parseInputs[type(value)](property, container)
|
||||
end)
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
number = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
fontSize = 18,
|
||||
name = "input",
|
||||
size = guiCoord(1, -4, 0, 18),
|
||||
position = guiCoord(0, 2, 0, 1),
|
||||
text = "0",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
x:textInput(function ()
|
||||
parseInputs[type(value)](property, container)
|
||||
end)
|
||||
|
||||
if property == "type" and type(instance) == "light" then
|
||||
container.zIndex = 30 -- important because child elements need to be rendered above other properties!
|
||||
container.size = container.size + guiCoord(0,0,0,20)
|
||||
|
||||
|
||||
local presetSelect = ui.create("guiTextBox", container, {
|
||||
size = guiCoord(1, -4, 0, 16),
|
||||
position = guiCoord(0, 2, 0, 23),
|
||||
borderRadius = 3,
|
||||
text = "Light Options",
|
||||
fontSize = 16,
|
||||
align = enums.align.middle,
|
||||
backgroundAlpha = 0.75
|
||||
}, "primary")
|
||||
|
||||
local optionsModal = ui.create("guiFrame", container, {
|
||||
position = guiCoord(-0.8, 7, 0, 48),
|
||||
borderRadius = 6,
|
||||
visible = false,
|
||||
zIndex = 40,
|
||||
borderWidth = 1,
|
||||
cropChildren = false
|
||||
}, "main")
|
||||
|
||||
local isFocused = false
|
||||
local pendingHide = false
|
||||
local function queueCloseModal()
|
||||
if not pendingHide and optionsModal.visible then
|
||||
pendingHide = true
|
||||
wait(.4)
|
||||
if not isFocused then
|
||||
--still unfocused, lets hide.
|
||||
optionsModal.visible = false
|
||||
end
|
||||
pendingHide=false
|
||||
end
|
||||
end
|
||||
|
||||
presetSelect:mouseFocused(function ()
|
||||
optionsModal.visible = true
|
||||
isFocused = true
|
||||
end)
|
||||
|
||||
optionsModal:mouseFocused(function ()
|
||||
isFocused = true
|
||||
end)
|
||||
|
||||
presetSelect:mouseUnfocused(function ()
|
||||
isFocused = false
|
||||
queueCloseModal()
|
||||
end)
|
||||
|
||||
optionsModal:mouseUnfocused(function ()
|
||||
isFocused = false
|
||||
queueCloseModal()
|
||||
end)
|
||||
|
||||
ui.create("guiImage", optionsModal, {
|
||||
size = guiCoord(0, 24, 0, 24),
|
||||
position = guiCoord(0.75, -12, 0, -15),
|
||||
handleEvents=false,
|
||||
zIndex = 10,
|
||||
backgroundAlpha = 0,
|
||||
texture = "fa:s-caret-up",
|
||||
imageColour = optionsModal.backgroundColour
|
||||
})
|
||||
|
||||
local curY = 0
|
||||
local curX = 0
|
||||
for lightType, num in pairs(enums.lightType) do
|
||||
|
||||
local btn = ui.create("guiTextBox", optionsModal, {
|
||||
size = guiCoord(.5, -10, 0, 18),
|
||||
position = guiCoord(curX, 5, 0, curY + 4),
|
||||
borderRadius = 3,
|
||||
text = lightType,
|
||||
fontSize = 16,
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
btn:mouseFocused(function ()
|
||||
isFocused = true
|
||||
end)
|
||||
btn:mouseUnfocused(function ()
|
||||
isFocused = false
|
||||
queueCloseModal()
|
||||
end)
|
||||
btn:mouseLeftReleased(function ()
|
||||
x.text = tostring(num)
|
||||
parseInputs[type(value)](property, container)
|
||||
end)
|
||||
|
||||
if curX == 0.5 then
|
||||
curY = curY + 24
|
||||
curX = 0
|
||||
else
|
||||
curX = 0.5
|
||||
end
|
||||
end
|
||||
|
||||
if curX == 0.5 then
|
||||
curY = curY + 24
|
||||
end
|
||||
|
||||
optionsModal.size = guiCoord(1.8, -10, 0, curY+4)
|
||||
|
||||
|
||||
end
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
string = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
wrap = true,
|
||||
fontSize = 18,
|
||||
name = "input",
|
||||
size = guiCoord(1, -4, 0, 18),
|
||||
position = guiCoord(0, 2, 0, 1),
|
||||
text = "text input",
|
||||
align = enums.align.middleLeft,
|
||||
zIndex = 2,
|
||||
cropChildren = true
|
||||
}, "primary")
|
||||
|
||||
x:textInput(function ()
|
||||
parseInputs[type(value)](property, container)
|
||||
end)
|
||||
|
||||
-- TODO TODO TODO TODO
|
||||
-- We need some sort of helper function that'll make
|
||||
-- modals for situations like this:
|
||||
|
||||
if property == "mesh" then
|
||||
container.zIndex = 30 -- important because child elements need to be rendered above other properties!
|
||||
container.size = container.size + guiCoord(0,0,0,20)
|
||||
x.fontSize = 14
|
||||
|
||||
local presetSelect = ui.create("guiTextBox", container, {
|
||||
size = guiCoord(1, -4, 0, 16),
|
||||
position = guiCoord(0, 2, 0, 23),
|
||||
borderRadius = 3,
|
||||
text = "Mesh Presets",
|
||||
fontSize = 16,
|
||||
align = enums.align.middle,
|
||||
backgroundAlpha = 0.75
|
||||
}, "primary")
|
||||
|
||||
local meshModal = ui.create("guiFrame", container, {
|
||||
position = guiCoord(-0.8, 7, 0, 48),
|
||||
borderRadius = 6,
|
||||
visible = false,
|
||||
zIndex = 40,
|
||||
borderWidth = 1,
|
||||
cropChildren = false
|
||||
}, "main")
|
||||
|
||||
local isFocused = false
|
||||
local pendingHide = false
|
||||
local function queueCloseModal()
|
||||
if not pendingHide and meshModal.visible then
|
||||
pendingHide = true
|
||||
spawnThread(function ()
|
||||
-- Code here...
|
||||
wait(.4)
|
||||
if not isFocused then
|
||||
--still unfocused, lets hide.
|
||||
meshModal.visible = false
|
||||
end
|
||||
pendingHide=false
|
||||
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
presetSelect:onSync("mouseFocused", function ()
|
||||
meshModal.visible = true
|
||||
isFocused = true
|
||||
end)
|
||||
|
||||
meshModal:onSync("mouseFocused", function ()
|
||||
isFocused = true
|
||||
end)
|
||||
|
||||
presetSelect:onSync("mouseUnfocused", function ()
|
||||
isFocused = false
|
||||
queueCloseModal()
|
||||
end)
|
||||
|
||||
meshModal:onSync("mouseUnfocused", function ()
|
||||
isFocused = false
|
||||
queueCloseModal()
|
||||
end)
|
||||
|
||||
ui.create("guiImage", meshModal, {
|
||||
size = guiCoord(0, 24, 0, 24),
|
||||
position = guiCoord(0.75, -12, 0, -15),
|
||||
handleEvents=false,
|
||||
zIndex = 10,
|
||||
backgroundAlpha = 0,
|
||||
texture = "fa:s-caret-up",
|
||||
imageColour = meshModal.backgroundColour
|
||||
})
|
||||
|
||||
local curY = 0
|
||||
local curX = 0
|
||||
for meshName, actualMeshName in pairs(meshShortcuts) do
|
||||
|
||||
local btn = ui.create("guiTextBox", meshModal, {
|
||||
size = guiCoord(.5, -10, 0, 18),
|
||||
position = guiCoord(curX, 5, 0, curY + 4),
|
||||
borderRadius = 3,
|
||||
text = meshName,
|
||||
fontSize = 16,
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
btn:onSync("mouseFocused", function ()
|
||||
isFocused = true
|
||||
end)
|
||||
btn:onSync("mouseUnfocused", function ()
|
||||
isFocused = false
|
||||
queueCloseModal()
|
||||
end)
|
||||
btn:mouseLeftReleased(function ()
|
||||
x.text = actualMeshName
|
||||
parseInputs[type(value)](property, container)
|
||||
end)
|
||||
|
||||
if curX == 0.5 then
|
||||
curY = curY + 24
|
||||
curX = 0
|
||||
else
|
||||
curX = 0.5
|
||||
end
|
||||
end
|
||||
|
||||
if curX == 0.5 then
|
||||
curY = curY + 24
|
||||
end
|
||||
|
||||
meshModal.size = guiCoord(1.8, -10, 0, curY+4)
|
||||
|
||||
|
||||
end
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
vector3 = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
container.size = guiCoord(container.size.scaleX, 0, 0, 60)
|
||||
|
||||
local xLabel = ui.create("guiTextBox", container, {
|
||||
name = "labelX",
|
||||
size = guiCoord(0, 10, 1/3, -1),
|
||||
position = guiCoord(0,-10,0,1),
|
||||
fontSize = 16,
|
||||
textAlpha = 0.6,
|
||||
text = "X",
|
||||
align = enums.align.topLeft
|
||||
}, "backgroundText")
|
||||
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
fontSize = 18,
|
||||
name = "x",
|
||||
size = guiCoord(1, -4, 1/3, -1),
|
||||
position = guiCoord(0, 2, 0, 0),
|
||||
text = "0",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
local yLabel = xLabel:clone()
|
||||
yLabel.name = "yLabel"
|
||||
yLabel.text = "Y"
|
||||
yLabel.parent = container
|
||||
yLabel.position = guiCoord(0, -10, 1/3, 1)
|
||||
themer.registerGui(yLabel, "backgroundText")
|
||||
|
||||
local y = x:clone()
|
||||
y.name = "y"
|
||||
y.parent = container
|
||||
y.position = guiCoord(0, 2, 1/3, 0)
|
||||
themer.registerGui(y, "primary")
|
||||
|
||||
local zLabel = xLabel:clone()
|
||||
zLabel.name = "zLabel"
|
||||
zLabel.text = "Z"
|
||||
zLabel.parent = container
|
||||
zLabel.position = guiCoord(0, -10, 2/3, 1)
|
||||
themer.registerGui(yLabel, "backgroundText")
|
||||
|
||||
local z = x:clone()
|
||||
z.name = "z"
|
||||
z.parent = container
|
||||
z.position = guiCoord(0, 2, 2/3, 0)
|
||||
themer.registerGui(z, "primary")
|
||||
|
||||
local function handler()
|
||||
parseInputs[type(value)](property, container)
|
||||
end
|
||||
x:textInput(handler)
|
||||
y:textInput(handler)
|
||||
z:textInput(handler)
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
vector2 = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
container.size = guiCoord(container.size.scaleX, 0, 0, 40)
|
||||
|
||||
local xLabel = ui.create("guiTextBox", container, {
|
||||
name = "labelX",
|
||||
size = guiCoord(0, 10, 1/2, -1),
|
||||
position = guiCoord(0,-10,0,2),
|
||||
fontSize = 16,
|
||||
textAlpha = 0.6,
|
||||
text = "X",
|
||||
align = enums.align.topLeft
|
||||
}, "backgroundText")
|
||||
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
fontSize = 18,
|
||||
name = "x",
|
||||
size = guiCoord(0, -4, 1/2, -2),
|
||||
position = guiCoord(0, 2, 0, 1),
|
||||
text = "0",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
local yLabel = xLabel:clone()
|
||||
yLabel.name = "yLabel"
|
||||
yLabel.text = "Y"
|
||||
yLabel.parent = container
|
||||
yLabel.position = guiCoord(0, -10, 1/2, 2)
|
||||
themer.registerGui(yLabel, "backgroundText")
|
||||
|
||||
local y = x:clone()
|
||||
y.name = "y"
|
||||
y.parent = container
|
||||
y.position = guiCoord(0, 2, 1/2, 1)
|
||||
themer.registerGui(y, "primary")
|
||||
local function handler()
|
||||
parseInputs[type(value)](property, container)
|
||||
end
|
||||
x:textInput(handler)
|
||||
y:textInput(handler)
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
quaternion = function(instance, property, value)
|
||||
|
||||
-- maybe quaternions need an Euler editor?
|
||||
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
container.size = guiCoord(container.size.scaleX, 0, 0, 60)
|
||||
|
||||
local xLabel = ui.create("guiTextBox", container, {
|
||||
name = "labelX",
|
||||
size = guiCoord(0, 12, 1/3, -1),
|
||||
position = guiCoord(0,-10,0,1),
|
||||
fontSize = 16,
|
||||
textAlpha = 0.6,
|
||||
text = "X",
|
||||
align = enums.align.topLeft
|
||||
}, "backgroundText")
|
||||
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
fontSize = 18,
|
||||
name = "x",
|
||||
size = guiCoord(1, -4, 1/3, -1),
|
||||
position = guiCoord(0, 2, 0, 0),
|
||||
text = "0",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
local yLabel = xLabel:clone()
|
||||
yLabel.name = "yLabel"
|
||||
yLabel.text = "Y"
|
||||
yLabel.parent = container
|
||||
yLabel.position = guiCoord(0, -10, 1/3, 1)
|
||||
themer.registerGui(yLabel, "backgroundText")
|
||||
|
||||
local y = x:clone()
|
||||
y.name = "y"
|
||||
y.parent = container
|
||||
y.position = guiCoord(0, 2, 1/3, 0)
|
||||
themer.registerGui(y, "primary")
|
||||
|
||||
local zLabel = xLabel:clone()
|
||||
zLabel.name = "zLabel"
|
||||
zLabel.text = "Z"
|
||||
zLabel.parent = container
|
||||
zLabel.position = guiCoord(0, -10, 2/3, 1)
|
||||
themer.registerGui(zLabel, "backgroundText")
|
||||
|
||||
local z = x:clone()
|
||||
z.name = "z"
|
||||
z.parent = container
|
||||
z.position = guiCoord(0, 2, 2/3, 0)
|
||||
themer.registerGui(z, "primary")
|
||||
|
||||
--[[local wLabel = xLabel:clone()
|
||||
wLabel.name = "wLabel"
|
||||
wLabel.text = "W"
|
||||
wLabel.parent = container
|
||||
wLabel.position = guiCoord(0, -12, 3/4, 2)
|
||||
themer.registerGui(wLabel, "backgroundText")
|
||||
|
||||
local w = x:clone()
|
||||
w.name = "w"
|
||||
w.parent = container
|
||||
w.position = guiCoord(0, 2, 3/4, 1)
|
||||
themer.registerGui(w, "primary")]]
|
||||
|
||||
local function handler()
|
||||
parseInputs[type(value)](property, container)
|
||||
end
|
||||
x:textInput(handler)
|
||||
y:textInput(handler)
|
||||
z:textInput(handler)
|
||||
--w:textInput(handler)
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
guiCoord = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
fontSize = 18,
|
||||
name = "scaleX",
|
||||
size = guiCoord(1/4, -4, 1, -2),
|
||||
position = guiCoord(0, 2, 0, 1),
|
||||
text = "0",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
local y = x:clone()
|
||||
y.name = "offsetX"
|
||||
y.parent = container
|
||||
y.position = guiCoord(1/4, 2, 0, 1)
|
||||
themer.registerGui(y, "primary")
|
||||
|
||||
local z = x:clone()
|
||||
z.name = "scaleY"
|
||||
z.parent = container
|
||||
z.position = guiCoord(1/2, 2, 0, 1)
|
||||
themer.registerGui(z, "primary")
|
||||
|
||||
local w = x:clone()
|
||||
w.name = "offsetY"
|
||||
w.parent = container
|
||||
w.position = guiCoord(3/4, 2, 0, 1)
|
||||
themer.registerGui(w, "primary")
|
||||
|
||||
local function handler()
|
||||
parseInputs[type(value)](property, container)
|
||||
end
|
||||
x:textInput(handler)
|
||||
y:textInput(handler)
|
||||
z:textInput(handler)
|
||||
w:textInput(handler)
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
colour = function(instance, property, value)
|
||||
local container = createInputs.default(value, pType, readOnly)
|
||||
container.size = guiCoord(container.size.scaleX, 0, 0, 60)
|
||||
|
||||
local rLabel = ui.create("guiTextBox", container, {
|
||||
name = "labelR",
|
||||
size = guiCoord(0, 10, 1/3, -1),
|
||||
position = guiCoord(0,-10,0,2),
|
||||
fontSize = 16,
|
||||
textAlpha = 0.6,
|
||||
text = "R",
|
||||
align = enums.align.topLeft
|
||||
}, "backgroundText")
|
||||
|
||||
local x = ui.create("guiTextBox", container, {
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
fontSize = 18,
|
||||
name = "r",
|
||||
size = guiCoord(1, -24, 1/3, -2),
|
||||
position = guiCoord(0, 2, 0, 1),
|
||||
text = "1",
|
||||
align = enums.align.middle
|
||||
}, "primary")
|
||||
|
||||
local gLabel = rLabel:clone()
|
||||
gLabel.name = "gLabel"
|
||||
gLabel.text = "G"
|
||||
gLabel.parent = container
|
||||
gLabel.position = guiCoord(0, -10, 1/3, 1)
|
||||
themer.registerGui(gLabel, "backgroundText")
|
||||
|
||||
local g = x:clone()
|
||||
g.name = "g"
|
||||
g.parent = container
|
||||
g.position = guiCoord(0, 2, 1/3, 1)
|
||||
themer.registerGui(g, "primary")
|
||||
|
||||
local bLabel = rLabel:clone()
|
||||
bLabel.name = "bLabel"
|
||||
bLabel.text = "B"
|
||||
bLabel.parent = container
|
||||
bLabel.position = guiCoord(0, -10, 2/3, 1)
|
||||
themer.registerGui(bLabel, "backgroundText")
|
||||
|
||||
local b = x:clone()
|
||||
b.name = "b"
|
||||
b.parent = container
|
||||
b.position = guiCoord(0, 2, 2/3, 1)
|
||||
themer.registerGui(b, "primary")
|
||||
|
||||
local function handler()
|
||||
parseInputs[type(value)](property, container)
|
||||
end
|
||||
x:textInput(handler)
|
||||
g:textInput(handler)
|
||||
b:textInput(handler)
|
||||
|
||||
local col = engine.construct("guiFrame", container, {
|
||||
name = "col",
|
||||
size = guiCoord(0, 14, 1, -2),
|
||||
position = guiCoord(1, -18, 0, 1),
|
||||
backgroundColour = colour(1,1,1),
|
||||
borderRadius = 2,
|
||||
})
|
||||
|
||||
col:mouseLeftReleased(function ()
|
||||
colourPicker.prompt(value, function(c)
|
||||
instance[property] = c
|
||||
end)
|
||||
end)
|
||||
|
||||
return container
|
||||
end,
|
||||
}
|
||||
|
||||
return createInputs
|
|
@ -0,0 +1,16 @@
|
|||
-- List of mesh shortcuts for the mesh data member of objects
|
||||
|
||||
local meshShortcuts = {
|
||||
cube = "primitive:cube",
|
||||
sphere = "primitive:sphere",
|
||||
cylinder = "primitive:cylinder",
|
||||
torus = "primitive:torus",
|
||||
cone = "primitive:cone",
|
||||
wedge = "primitive:wedge",
|
||||
corner = "primitive:corner",
|
||||
worker = "tevurl:3d/worker.glb",
|
||||
duck = "tevurl:3d/Duck.glb",
|
||||
avocado = "tevurl:3d/Avocado.glb",
|
||||
}
|
||||
|
||||
return meshShortcuts
|
|
@ -0,0 +1,73 @@
|
|||
-- This module is responsible for converting the gui’s values to the appropriate datatype,
|
||||
-- AND updating the selection’s properties
|
||||
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
|
||||
local function callbackInput(property, value)
|
||||
local success, message = pcall(function ()
|
||||
for _,v in pairs(selection.selection) do
|
||||
if v[property] ~= nil then
|
||||
v[property] = value
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if not success then print(message) end
|
||||
end
|
||||
|
||||
return {
|
||||
block = function (property, gui)
|
||||
|
||||
end,
|
||||
|
||||
boolean = function (property, gui)
|
||||
callbackInput(property, gui.input.texture == "fa:s-toggle-on")
|
||||
end,
|
||||
|
||||
number = function (property, gui)
|
||||
local num = tonumber(gui.input.text)
|
||||
if num then
|
||||
callbackInput(property, num)
|
||||
end
|
||||
end,
|
||||
|
||||
string = function (property, gui)
|
||||
callbackInput(property, gui.input.text)
|
||||
end,
|
||||
|
||||
vector3 = function(property, gui)
|
||||
local x,y,z = tonumber(gui.x.text),tonumber(gui.y.text),tonumber(gui.z.text)
|
||||
if x and y and z then
|
||||
callbackInput(property, vector3(x,y,z))
|
||||
end
|
||||
end,
|
||||
|
||||
vector2 = function(property, gui)
|
||||
local x,y = tonumber(gui.x.text),tonumber(gui.y.text)
|
||||
if x and y then
|
||||
callbackInput(property, vector2(x,y))
|
||||
end
|
||||
end,
|
||||
|
||||
colour = function(property, gui)
|
||||
local r,g,b = tonumber(gui.r.text),tonumber(gui.g.text),tonumber(gui.b.text)
|
||||
if r and g and b then
|
||||
callbackInput(property, colour:fromRGB(r,g,b))
|
||||
end
|
||||
end,
|
||||
|
||||
quaternion = function(property, gui)
|
||||
--local x,y,z,w = tonumber(gui.x.text),tonumber(gui.y.text),tonumber(gui.z.text),tonumber(gui.w.text)
|
||||
local x,y,z = tonumber(gui.x.text),tonumber(gui.y.text),tonumber(gui.z.text)
|
||||
if x and y and z then
|
||||
callbackInput(property, quaternion():setEuler(math.rad(x),math.rad(y),math.rad(z)))
|
||||
end
|
||||
end,
|
||||
|
||||
guiCoord = function(property, gui)
|
||||
local sx,ox,sy,oy = tonumber(gui.scaleX.text),tonumber(gui.offsetX.text),tonumber(gui.scaleY.text),tonumber(gui.offsetY.text)
|
||||
if sx and ox and sy and oy then
|
||||
callbackInput(property, guiCoord(sx,ox,sy,oy))
|
||||
end
|
||||
end
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
-- these functions are responsible for updating the property editor gui
|
||||
-- when the selection’s members are updated by 3rd party source
|
||||
|
||||
local themer = require("tevgit:workshop/controllers/ui/core/themer.lua")
|
||||
|
||||
return {
|
||||
block = function (instance, gui, value)
|
||||
|
||||
end,
|
||||
boolean = function (instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.input then return end
|
||||
|
||||
gui.input.texture = value and "fa:s-toggle-on" or "fa:s-toggle-off"
|
||||
themer.registerGui(gui.input, value and "successImage" or "errorImage")
|
||||
end,
|
||||
number = function (instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.input then return end
|
||||
|
||||
gui.input.text = string.format("%.3f", value)
|
||||
end,
|
||||
string = function (instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.input then return end
|
||||
|
||||
gui.input.text = value
|
||||
end,
|
||||
vector3 = function(instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.x or engine.input.keyFocusedGui == gui.y or engine.input.keyFocusedGui == gui.z then return end
|
||||
|
||||
gui.x.text = string.format("%.3f", value.x)
|
||||
gui.y.text = string.format("%.3f", value.y)
|
||||
gui.z.text = string.format("%.3f", value.z)
|
||||
end,
|
||||
vector2 = function(instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.x or engine.input.keyFocusedGui == gui.y then return end
|
||||
|
||||
gui.x.text = string.format("%.3f", value.x)
|
||||
gui.y.text = string.format("%.3f", value.y)
|
||||
end,
|
||||
colour = function(instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.r or engine.input.keyFocusedGui == gui.g or engine.input.keyFocusedGui == gui.b then return end
|
||||
|
||||
gui.r.text = string.format("%.0f", value.r * 255)
|
||||
gui.g.text = string.format("%.0f", value.g * 255)
|
||||
gui.b.text = string.format("%.0f", value.b * 255)
|
||||
gui.col.backgroundColour = value
|
||||
end,
|
||||
quaternion = function(instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.x or engine.input.keyFocusedGui == gui.y or engine.input.keyFocusedGui == gui.z then return end
|
||||
|
||||
local euler = value:getEuler()
|
||||
gui.x.text = string.format("%.3f", math.deg(euler.x))
|
||||
gui.y.text = string.format("%.3f", math.deg(euler.y))
|
||||
gui.z.text = string.format("%.3f", math.deg(euler.z))
|
||||
--gui.w.text = tostring(value.w)
|
||||
end,
|
||||
guiCoord = function(instance, gui, value)
|
||||
if engine.input.keyFocusedGui == gui.scaleX or engine.input.keyFocusedGui == gui.offsetX or engine.input.keyFocusedGui == gui.scaleY or engine.input.keyFocusedGui == gui.offsetY then return end
|
||||
|
||||
gui.scaleX.text = tostring(value.scaleX)
|
||||
gui.offsetX.text = tostring(value.offsetX)
|
||||
gui.scaleY.text = tostring(value.scaleY)
|
||||
gui.offsetY.text = tostring(value.offsetY)
|
||||
end,
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local controller = {}
|
||||
|
||||
local modulePrefix = "tevgit:workshop/controllers/ui/components/propertyEditor/"
|
||||
|
||||
controller.window = ui.window(shared.workshop.interface, "Properties",
|
||||
guiCoord(0, 260, 0, 400), -- size
|
||||
guiCoord(1, -260, 0.5, -25), -- pos
|
||||
true, -- dockable
|
||||
true -- hidable
|
||||
)
|
||||
|
||||
info = ui.create("guiTextBox", controller.window.content, {
|
||||
size = guiCoord(1, 0, 0, 18),
|
||||
text = "Nothing selected",
|
||||
fontSize = 18
|
||||
}, "backgroundText")
|
||||
|
||||
controller.scrollView = ui.create("guiScrollView", controller.window.content, {
|
||||
size = guiCoord(1, 0, 1, -18),
|
||||
position = guiCoord(0, 0, 0, 18),
|
||||
backgroundAlpha = 0
|
||||
}, "primary")
|
||||
|
||||
controller.eventHandlers = {}
|
||||
|
||||
local parseUpdates = require(modulePrefix .. "parseUpdates.lua")
|
||||
local parseInputs = require(modulePrefix .. "parseInputs.lua")
|
||||
local createInputs = require(modulePrefix .. "createInputs.lua")
|
||||
|
||||
local selection = require("tevgit:workshop/controllers/core/selection.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local function alphabeticalSorter(a, b) return a.property < b.property end
|
||||
|
||||
local excludePropertyList = {
|
||||
physics = true, -- letting user changes this will break raycasts
|
||||
doNotSerialise = true,
|
||||
source = true
|
||||
}
|
||||
|
||||
function controller.generateProperties()
|
||||
for i, v in pairs(controller.eventHandlers) do v:disconnect() end
|
||||
|
||||
controller.eventHandlers = {}
|
||||
if #selection.selection > 0 then
|
||||
local firstObject = selection.selection[1]
|
||||
local members = shared.workshop:getMembersOfObject(firstObject)
|
||||
|
||||
table.sort(members, alphabeticalSorter)
|
||||
|
||||
controller.scrollView:destroyAllChildren()
|
||||
|
||||
local y = 10
|
||||
local propertiesCount = 0
|
||||
|
||||
for i, v in pairs(members) do
|
||||
local value = firstObject[v.property]
|
||||
local pType = type(value)
|
||||
local readOnly = not v.writable
|
||||
|
||||
if not readOnly and pType ~= "function" and
|
||||
not excludePropertyList[v.property] then
|
||||
propertiesCount = propertiesCount + 1
|
||||
|
||||
local container = engine.construct("guiFrame",
|
||||
controller.scrollView, {
|
||||
name = "_" .. v.property,
|
||||
backgroundAlpha = 0,
|
||||
size = guiCoord(1, -10, 0, 20),
|
||||
position = guiCoord(0, 0, 0, y),
|
||||
cropChildren = false
|
||||
})
|
||||
|
||||
local label = ui.create("guiTextBox", container, {
|
||||
name = "label",
|
||||
size = guiCoord(0.55, -15, 1, 0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
fontSize = 18,
|
||||
backgroundAlpha = 1,
|
||||
borderAlpha = 0,
|
||||
text = v.property,
|
||||
align = enums.align.topRight
|
||||
}, "backgroundText")
|
||||
|
||||
local inputGui = nil
|
||||
|
||||
if createInputs[pType] then
|
||||
inputGui = createInputs[pType](firstObject, v.property,
|
||||
value)
|
||||
else
|
||||
inputGui = createInputs.default(firstObject, v.property,
|
||||
value)
|
||||
end
|
||||
|
||||
container.size = guiCoord(1, -10, 0, inputGui.size.offsetY)
|
||||
container.zIndex = inputGui.zIndex
|
||||
inputGui.parent = container
|
||||
|
||||
if parseUpdates[pType] then
|
||||
parseUpdates[pType](firstObject, container.inputContainer,
|
||||
value)
|
||||
end
|
||||
|
||||
|
||||
|
||||
container.position = guiCoord(0, 5, 0, y)
|
||||
|
||||
y = y + container.size.offsetY + 3
|
||||
end
|
||||
end
|
||||
|
||||
info.text = type(firstObject) .. " has " .. tostring(propertiesCount) ..
|
||||
" visible members."
|
||||
|
||||
table.insert(controller.eventHandlers,
|
||||
firstObject:changed(
|
||||
function(prop, val)
|
||||
if parseUpdates[type(val)] then
|
||||
local container = controller.scrollView["_" .. prop]
|
||||
if container then
|
||||
parseUpdates[type(val)](firstObject, container.inputContainer, val)
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
|
||||
local newSize = guiCoord(0, 0, 0, y)
|
||||
|
||||
if newSize ~= controller.scrollView.canvasSize then
|
||||
controller.scrollView.viewOffset = vector2(0, 0)
|
||||
end
|
||||
|
||||
controller.scrollView.canvasSize = newSize
|
||||
else
|
||||
info.text = "Nothing selected."
|
||||
end
|
||||
end
|
||||
|
||||
selection.registerCallback(controller.generateProperties)
|
||||
|
||||
return controller
|
|
@ -0,0 +1,27 @@
|
|||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local window = ui.window(shared.workshop.interface,
|
||||
"Run Lua",
|
||||
guiCoord(0, 420, 0, 70), --size
|
||||
guiCoord(0.5, -210, 0.5, -25), --pos
|
||||
false, --dockable
|
||||
true -- hidable
|
||||
)
|
||||
|
||||
local runScriptBtn = ui.button(window.content, "Run", guiCoord(0, 50, 0, 30), guiCoord(0, 5, 0, 7), "primary")
|
||||
|
||||
local runScriptInput = ui.create("guiTextBox", window.content, {
|
||||
size = guiCoord(1, -65, 0, 30),
|
||||
position = guiCoord(0, 65, 0, 7),
|
||||
readOnly = false,
|
||||
wrap = true,
|
||||
fontSize = 16
|
||||
}, "secondary")
|
||||
|
||||
runScriptBtn:mouseLeftPressed(function ()
|
||||
shared.workshop:loadString(runScriptInput.text)
|
||||
runScriptInput.text = ""
|
||||
end)
|
||||
|
||||
return window
|
|
@ -0,0 +1,138 @@
|
|||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local autoSave = require("tevgit:workshop/controllers/ui/components/autoSave.lua")
|
||||
|
||||
local window = ui.window(shared.workshop.interface,
|
||||
"Settings",
|
||||
guiCoord(0, 620, 0, 500), --size
|
||||
guiCoord(0.5, -310, 0.5, -250), --pos
|
||||
false, --dockable
|
||||
true -- hidable
|
||||
)
|
||||
|
||||
local sideBar = ui.create("guiFrame", window.content, {
|
||||
size = guiCoord(0.35, 3, 1, 6),
|
||||
position = guiCoord(0, -3, 0, -3)
|
||||
}, "primaryVariant")
|
||||
|
||||
local tabs = {}
|
||||
|
||||
local function addTab(tabName, tabFrame)
|
||||
local tabBtn = ui.create("guiFrame", sideBar, {
|
||||
size = guiCoord(1, -30, 0, 30),
|
||||
position = guiCoord(0, 15, 0, 15 + (#tabs * 40)),
|
||||
borderRadius = 3,
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, "primary")
|
||||
|
||||
ui.create("guiTextBox", tabBtn, {
|
||||
size = guiCoord(1, -12, 1, -6),
|
||||
position = guiCoord(0, 6, 0, 3),
|
||||
text = tabName,
|
||||
handleEvents = false,
|
||||
align = enums.align.middleLeft
|
||||
}, "primaryText")
|
||||
|
||||
if #tabs > 0 then
|
||||
tabFrame.visible = false
|
||||
tabBtn.backgroundAlpha = 0
|
||||
end
|
||||
|
||||
tabBtn:mouseLeftPressed(function ()
|
||||
for _,v in pairs(tabs) do
|
||||
v[1].visible = false
|
||||
v[2].backgroundAlpha = 0
|
||||
end
|
||||
tabFrame.visible = true
|
||||
tabBtn.backgroundAlpha = 1
|
||||
end)
|
||||
|
||||
table.insert(tabs, {tabFrame, tabBtn})
|
||||
end
|
||||
|
||||
local generalPage = ui.create("guiFrame", window.content, {
|
||||
size = guiCoord(0.65, 0, 1, 0),
|
||||
position = guiCoord(0.35, 0, 0, 0)
|
||||
}, "background")
|
||||
|
||||
local syncThread = autoSave.Sync() -- Establish thread
|
||||
local autoSaveToggle = ui.button(generalPage, autoSave.Enabled and "Disable Auto-Save" or "Enabled Auto-Save", guiCoord(0, 200, 0, 30), guiCoord(0, 10, 0, 10), "secondary")
|
||||
autoSaveToggle:mouseLeftPressed(function()
|
||||
autoSave.Enabled = not autoSave.Enabled
|
||||
autoSaveToggle.label.text = autoSave.Enabled and "Disable Auto-Save" or "Enabled Auto-Save"
|
||||
end)
|
||||
|
||||
addTab("General", generalPage)
|
||||
|
||||
local themePage = ui.create("guiScrollView", window.content, {
|
||||
size = guiCoord(0.65, 0, 1, 0),
|
||||
position = guiCoord(0.35, 0, 0, 0),
|
||||
canvasSize = guiCoord(1,0,0,560)
|
||||
}, "background")
|
||||
|
||||
require("tevgit:workshop/controllers/ui/components/themePreviewer.lua").parent = themePage
|
||||
|
||||
addTab("Theme", themePage)
|
||||
|
||||
if shared.developerMode then
|
||||
local developmentPage = ui.create("guiScrollView", window.content, {
|
||||
size = guiCoord(0.65, 0, 1, 0),
|
||||
position = guiCoord(0.35, 0, 0, 0)
|
||||
}, "background")
|
||||
|
||||
ui.create("guiTextBox", developmentPage, {
|
||||
position = guiCoord(0, 15, 0, 15),
|
||||
size = guiCoord(1, -30, 0, 20),
|
||||
text = "This tab is mainly for developers of the workshop."
|
||||
}, "backgroundText")
|
||||
|
||||
|
||||
local createReload = ui.button(developmentPage, "Reload Workshop", guiCoord(0, 190, 0, 30), guiCoord(0, 15, 0, 50))
|
||||
createReload:mouseLeftPressed(function ()
|
||||
shared.workshop:reloadCreate()
|
||||
end)
|
||||
|
||||
local shaderReload = ui.button(developmentPage, "Reload Shaders", guiCoord(0, 190, 0, 30), guiCoord(0, 15, 0, 90), "secondary")
|
||||
shaderReload:mouseLeftPressed(function ()
|
||||
shared.workshop:reloadShaders()
|
||||
end)
|
||||
|
||||
local physicsDebugEnabled = false
|
||||
local physicsAABBs = ui.button(developmentPage, "Enable Physics AABBs", guiCoord(0, 190, 0, 30), guiCoord(0, 15, 0, 130), "secondary")
|
||||
physicsAABBs:mouseLeftPressed(function ()
|
||||
physicsDebugEnabled = not physicsDebugEnabled
|
||||
shared.workshop:setPhysicsDebug(physicsDebugEnabled)
|
||||
physicsAABBs.label.text = physicsDebugEnabled and "Disable Physics AABBs" or "Enable Physics AABBs"
|
||||
end)
|
||||
|
||||
local runScriptBtn = ui.button(developmentPage, "Run Lua", guiCoord(0, 190, 0, 30), guiCoord(0, 15, 0, 170), "secondary")
|
||||
|
||||
runScriptBtn:mouseLeftPressed(function ()
|
||||
shared.windows.runLua.visible = not shared.windows.runLua.visible
|
||||
end)
|
||||
|
||||
local printDump = ui.button(developmentPage, "Print Dump", guiCoord(0, 190, 0, 30), guiCoord(0, 15, 0, 210), "secondary")
|
||||
printDump:mouseLeftPressed(function()
|
||||
local dump = shared.workshop:apiDump()
|
||||
print(engine.json:encode(dump))
|
||||
end)
|
||||
|
||||
local physicsEnabled = engine.physics.running
|
||||
local physicsToggle= ui.button(developmentPage, physicsEnabled and "Stop Simulating Physics" or "Simulate Physics", guiCoord(0, 190, 0, 30), guiCoord(0, 15, 0, 250), "secondary")
|
||||
physicsToggle:mouseLeftPressed(function ()
|
||||
physicsEnabled = not physicsEnabled
|
||||
if physicsEnabled then
|
||||
engine.physics:resume()
|
||||
else
|
||||
engine.physics:pause()
|
||||
end
|
||||
physicsToggle.label.text = physicsEnabled and "Stop Simulating Physics" or "Simulate Physics"
|
||||
end)
|
||||
|
||||
addTab("Development", developmentPage)
|
||||
|
||||
--local dump = globalWorkshop:apiDump()
|
||||
--print(globalEngine.json:encode(dump))
|
||||
end
|
||||
|
||||
return window
|
|
@ -0,0 +1,281 @@
|
|||
-- Returns a frame with the different theme colours
|
||||
-- Useful for overviewing a theme?
|
||||
|
||||
ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
themer = require("tevgit:workshop/controllers/ui/core/themer.lua")
|
||||
colourPicker = require("tevgit:workshop/controllers/ui/components/colourPicker.lua")
|
||||
parseInputs = require("tevgit:workshop/controllers/ui/components/propertyEditor/parseInputs.lua")
|
||||
|
||||
-- Overrides
|
||||
local radiusNum = 0 -- defaults to flat
|
||||
|
||||
presets = {
|
||||
{"default", "Classic (default)"},
|
||||
{"black", "Tev Dark"},
|
||||
{"white", "Tev Light"},
|
||||
{"ow", "ow my eyes"},
|
||||
{"custom", "Custom"}
|
||||
}
|
||||
|
||||
container = ui.create("guiFrame", shared.workshop.interface, {
|
||||
size = guiCoord(1, -10, 0, 560),
|
||||
position = guiCoord(0, 10, 0, 10)
|
||||
}, "background")
|
||||
|
||||
presetMenu = ui.create("guiFrame", container, {
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
size = guiCoord(0.5, 0, 0, 140),
|
||||
borderRadius = 3
|
||||
}, "primary")
|
||||
|
||||
importWindow = ui.window(shared.workshop.interface,
|
||||
"Import Theme",
|
||||
guiCoord(0, 420, 0, 230), --size
|
||||
guiCoord(0.5, -210, 0.5, -25), --pos
|
||||
false, --dockable
|
||||
true -- hidable
|
||||
)
|
||||
importWindow.visible = false
|
||||
importWindow.xIndex = 1000
|
||||
|
||||
frame = ui.create("guiFrame", importWindow.content, {
|
||||
size = guiCoord(1, -20, 1, -60),
|
||||
position = guiCoord(0, 10, 0, 10),
|
||||
cropChildren = true,
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
importInput = ui.create("guiTextBox", frame, {
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
readOnly = false,
|
||||
wrap = true,
|
||||
fontSize = 16,
|
||||
zIndex = 100
|
||||
}, "secondary")
|
||||
|
||||
function attemptCustom()
|
||||
import = importInput.text
|
||||
import = string.gsub(import,"\\","")
|
||||
themer.setTheme(engine.json:decode(import))
|
||||
end
|
||||
|
||||
importConfirmButton = ui.button(importWindow.content, "Import", guiCoord(0.5, 0, 0, 30), guiCoord(0.25, 0, 1, -40), "primary")
|
||||
|
||||
importButton = ui.create("guiButton", container, {
|
||||
size = guiCoord(0.5, -20, 0, 30),
|
||||
position = guiCoord(0.5, 10, 0, 40),
|
||||
borderRadius = 3,
|
||||
text = "Import Theme",
|
||||
align = enums.align.middle
|
||||
},"primaryVariant"):mouseLeftReleased(function()
|
||||
importWindow.visible = true
|
||||
end)
|
||||
|
||||
exportWindow = ui.window(shared.workshop.interface,
|
||||
"Export Theme",
|
||||
guiCoord(0, 420, 0, 230), --size
|
||||
guiCoord(0.5, -210, 0.5, -25), --pos
|
||||
false, --dockable
|
||||
true -- hidable
|
||||
)
|
||||
exportWindow.visible = false
|
||||
|
||||
eframe = ui.create("guiFrame", exportWindow.content, {
|
||||
size = guiCoord(1, -20, 1, -20),
|
||||
position = guiCoord(0, 10, 0, 10),
|
||||
cropChildren = true,
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
exportInput = ui.create("guiTextBox", eframe, {
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
readOnly = false,
|
||||
wrap = true,
|
||||
fontSize = 16,
|
||||
zIndex = 100
|
||||
}, "secondary")
|
||||
|
||||
ui.create("guiTextBox", container, {
|
||||
position = guiCoord(0.5, 10, 0, 80),
|
||||
size = guiCoord(0.5, -20, 0, 30),
|
||||
text = "Border Radius"
|
||||
}, "backgroundText")
|
||||
|
||||
local borderRadiusInput = ui.create("guiTextBox", container, {
|
||||
size = guiCoord(0.153, 0, 0.038, 0),
|
||||
position = guiCoord(0.79, 10, 0, 80),
|
||||
backgroundAlpha = 0.25,
|
||||
readOnly = false,
|
||||
multiline = false,
|
||||
name = "input",
|
||||
text = tostring(radiusNum),
|
||||
align = enums.align.middle
|
||||
}, "secondary")
|
||||
|
||||
borderRadiusInput:textInput(function(numText)
|
||||
num = tonumber(numText)
|
||||
if #numText == 0 then radiusNum = 0 return end
|
||||
if string.find(numText,"%a") or string.find(numText,"%s") then borderRadiusInput:setText("0") radiusNum = 0 return end
|
||||
if string.len(numText) > 5 then borderRadiusInput:setText(string.sub(numText,1,5)) radiusNum = 0 return end
|
||||
if num < 0 then borderRadiusInput:setText("0") return end
|
||||
radiusNum = tonumber(borderRadiusInput.text)
|
||||
end)
|
||||
|
||||
THISISTHERESETBUTTON = ui.create("guiButton", container, {
|
||||
size = guiCoord(0.5, -20, 0, 30),
|
||||
position = guiCoord(0.5, 10, 0, 80),
|
||||
borderRadius = 3,
|
||||
text = "Export Theme",
|
||||
align = enums.align.middle,
|
||||
visible = false
|
||||
},"primaryVariant")
|
||||
|
||||
THISISTHERESETBUTTON:mouseLeftReleased(function()
|
||||
exportInput.text = shared.workshop:getSettings("customTheme")
|
||||
exportWindow.visible = true
|
||||
end)
|
||||
|
||||
customUI = ui.create("guiFrame", container, {
|
||||
size = guiCoord(1, -10, 2, 0),
|
||||
position = guiCoord(0, 0, 0, 150),
|
||||
visible = false
|
||||
}, "background")
|
||||
|
||||
theme = themer.getTheme()
|
||||
function themeReload()
|
||||
theme = null
|
||||
theme = themer.getTheme()
|
||||
end
|
||||
|
||||
function generateEditor()
|
||||
customUI:destroyAllChildren()
|
||||
local y = 0
|
||||
for _,prop in pairs(themer.types) do
|
||||
themeProperty = engine.construct("guiFrame", customUI, {
|
||||
size = guiCoord(1, 0, 0, 40),
|
||||
position = guiCoord(0, 0, 0, y),
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
ui.create("guiTextBox", themeProperty, {
|
||||
size = guiCoord(1, -10, 0, 16),
|
||||
position = guiCoord(0, 6, 0, 2),
|
||||
text = prop,
|
||||
fontSize = 16,
|
||||
align = enums.align.middleLeft,
|
||||
fontFile = "local:OpenSans-SemiBold.ttf"
|
||||
}, "backgroundText")
|
||||
|
||||
count = 0
|
||||
for _,v in pairs(theme[prop]) do if type(v) == "colour" then count = count + 1 end end
|
||||
|
||||
size = 1/count
|
||||
i = 0
|
||||
|
||||
for k,v in pairs(theme[prop]) do
|
||||
if type(v) == "colour" then
|
||||
local ch,cs,cv = v:getHSV()
|
||||
btn = ui.create("guiTextBox", themeProperty, {
|
||||
size = guiCoord(size, -10, 0, 20),
|
||||
position = guiCoord(size*i, 5, 0, 20),
|
||||
text = k,
|
||||
fontSize = 16,
|
||||
align = enums.align.middle,
|
||||
backgroundColour = v,
|
||||
textColour = cv > 0.5 and colour:black() or colour:white(),
|
||||
borderRadius = radiusNum, -- override this
|
||||
borderColour = colour:black(),
|
||||
borderAlpha = 0.3
|
||||
}, prop)
|
||||
|
||||
btn:mouseLeftReleased(function()
|
||||
colourPicker.prompt(theme[prop][k], function(c)
|
||||
theme[prop][k] = c
|
||||
themer.setTheme(theme)
|
||||
exportInput.text = engine.json:decode(shared.workshop:getSettings("customTheme"))
|
||||
themeReload()
|
||||
end)
|
||||
end)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
y = y + 44
|
||||
end
|
||||
end
|
||||
|
||||
function canvasSet(size)
|
||||
container.parent.canvasSize = size
|
||||
container.size = size
|
||||
end
|
||||
|
||||
function makePresetMenu()
|
||||
preset = shared.workshop:getSettings("themeType")
|
||||
THISISTHERESETBUTTON.visible = false
|
||||
customUI.visible = false
|
||||
pcall(canvasSet, guiCoord(1, 0, 1, 0))
|
||||
if shared.workshop:getSettings("themeType") == "custom" then
|
||||
THISISTHERESETBUTTON.visible = true
|
||||
customUI.visible = true
|
||||
generateEditor()
|
||||
pcall(canvasSet, guiCoord(1, 0, 0, 560))
|
||||
end
|
||||
|
||||
presetMenu:destroyAllChildren()
|
||||
|
||||
local y = 0
|
||||
for i = 1, #presets do
|
||||
if preset == presets[i][1] then background = 1 else background = 0 end
|
||||
preset = ui.create("guiButton", presetMenu, {
|
||||
size = guiCoord(1, 0, 0, 20),
|
||||
position = guiCoord(0, 0, 0, y),
|
||||
backgroundAlpha = background,
|
||||
text = " " .. presets[i][2],
|
||||
borderRadius = 3
|
||||
},"secondary"):mouseLeftReleased(function()
|
||||
if presets[i][1] == "custom" then
|
||||
shared.workshop:setSettings("themeType", presets[i][1])
|
||||
shared.workshop:setSettings("customTheme", engine.json:encodeWithTypes(theme))
|
||||
themer.setTheme(theme,radiusNum)
|
||||
themeReload()
|
||||
else
|
||||
shared.workshop:setSettings("themeType", presets[i][1])
|
||||
themer.setThemePreset(require("tevgit:workshop/controllers/ui/themes/" .. presets[i][1] .. ".lua"),radiusNum)
|
||||
themeReload()
|
||||
end
|
||||
makePresetMenu()
|
||||
end)
|
||||
y = y + 20
|
||||
end
|
||||
end
|
||||
|
||||
importConfirmButton:mouseLeftReleased(function()
|
||||
success, message = pcall(attemptCustom)
|
||||
if success then
|
||||
makePresetMenu()
|
||||
importWindow.visible = false
|
||||
importInput.text = ""
|
||||
else
|
||||
ui.prompt("The given theme is invalid, the theme has not been changed.")
|
||||
end
|
||||
end)
|
||||
|
||||
resetButton = ui.create("guiButton", container, {
|
||||
size = guiCoord(0.5, -20, 0, 30),
|
||||
position = guiCoord(0.5, 10, 0, 0),
|
||||
borderRadius = 3,
|
||||
text = "Reset Theme",
|
||||
align = enums.align.middle
|
||||
},"secondary"):mouseLeftReleased(function()
|
||||
shared.workshop:setSettings("themeType", "default")
|
||||
themer.setThemePreset(require("tevgit:workshop/controllers/ui/themes/default.lua"))
|
||||
themeReload()
|
||||
makePresetMenu()
|
||||
end)
|
||||
|
||||
makePresetMenu()
|
||||
|
||||
return container
|
|
@ -0,0 +1,96 @@
|
|||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local themer = require("tevgit:workshop/controllers/ui/core/themer.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local tools = require("tevgit:workshop/controllers/sidetools/main.lua")
|
||||
local keybinder = require("tevgit:workshop/controllers/core/keybinder.lua")
|
||||
|
||||
-- main gui dock that is on left of the screen
|
||||
-- it is moved by the dock controller IF windows are docked to the left side of the screen
|
||||
|
||||
local toolDock = engine.construct("guiFrame", shared.workshop.interface, {
|
||||
name = "_toolBar",
|
||||
cropChildren = false,
|
||||
backgroundAlpha = 0,
|
||||
position = guiCoord(0, 8, 0, 80),
|
||||
})
|
||||
|
||||
local toolBar = ui.create("guiFrame", toolDock, {
|
||||
name = "_toolBar",
|
||||
size = guiCoord(0, 32, 0, 100),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
backgroundAlpha = 0.8,
|
||||
borderRadius = 4
|
||||
}, "primary")
|
||||
|
||||
local activeTool = nil
|
||||
|
||||
local function toggleTool(toolName)
|
||||
--engine.sounds:play("tevurl:sound/click.ogg") -- too much?
|
||||
if activeTool ~= toolName then
|
||||
if activeTool then
|
||||
tools[activeTool].deactivate()
|
||||
tools[activeTool].gui.icon.imageAlpha = 0.75
|
||||
end
|
||||
tools[toolName].activate()
|
||||
tools[toolName].gui.icon.imageAlpha = 1
|
||||
activeTool = toolName
|
||||
else
|
||||
tools[activeTool].deactivate()
|
||||
tools[activeTool].gui.icon.imageAlpha = 0.75
|
||||
activeTool = nil
|
||||
end
|
||||
end
|
||||
|
||||
local currentY = 5
|
||||
local toolIndex = 0
|
||||
for toolName, options in pairs(tools) do
|
||||
|
||||
-- used to assign a number keybind to each tool
|
||||
toolIndex = toolIndex + 1
|
||||
|
||||
-- options is the table returned by the tool's module.
|
||||
-- e.g. workshop/controllers/sidetools/hand.lua
|
||||
|
||||
local newTabBtn = ui.create("guiTextBox", toolBar, {
|
||||
text = toolName,
|
||||
position = guiCoord(0, 4, 0, currentY),
|
||||
size = guiCoord(0, 24, 0, 24),
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, "primaryText")
|
||||
|
||||
tools[toolName].gui = newTabBtn
|
||||
|
||||
if options.icon then
|
||||
newTabBtn.text = ""
|
||||
ui.create("guiImage", newTabBtn, {
|
||||
name = "icon",
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0., 0),
|
||||
texture = options.icon,
|
||||
handleEvents = false,
|
||||
imageAlpha = 0.75
|
||||
}, "primaryImage")
|
||||
end
|
||||
|
||||
local keybindText = ""
|
||||
if toolIndex < 10 then
|
||||
keybindText = " [" .. toolIndex .. "]"
|
||||
|
||||
keybinder:bind({
|
||||
name = "Activate " .. options.name .. " tool",
|
||||
key = enums.key["number"..toolIndex],
|
||||
action = function() toggleTool(toolName) end
|
||||
})
|
||||
end
|
||||
-- shows a tool tip if the user hovers over the button
|
||||
ui.tooltip(newTabBtn, options.name .. keybindText)
|
||||
|
||||
newTabBtn:mouseLeftPressed(function ()
|
||||
toggleTool(toolName)
|
||||
end)
|
||||
|
||||
currentY = currentY + 32
|
||||
end
|
||||
|
||||
toolBar.size = guiCoord(0, 32, 0, currentY - 5)
|
|
@ -0,0 +1,157 @@
|
|||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local tabs = {
|
||||
["Main"] = {
|
||||
{"Open", "fa:s-folder-open", function()
|
||||
shared.workshop:openFileDialogue()
|
||||
end},
|
||||
{"Save", "fa:s-save", function()
|
||||
shared.workshop:saveGame()
|
||||
end},
|
||||
{"Save As", "fa:r-save", function()
|
||||
shared.workshop:saveGameAsDialogue()
|
||||
end},
|
||||
{"Seperator"},
|
||||
{"Properties", "fa:s-clipboard-list", function ()
|
||||
shared.windows.propertyEditor.visible = not shared.windows.propertyEditor.visible
|
||||
end},
|
||||
{"Hierarchy", "fa:s-align-left", function ()
|
||||
shared.windows.hierarchy.visible = not shared.windows.hierarchy.visible
|
||||
end},
|
||||
{"History", "fa:s-history", function ()
|
||||
shared.windows.history.visible = not shared.windows.history.visible
|
||||
end},
|
||||
{"Seperator"},
|
||||
{"Settings", "fa:s-cog", function ()
|
||||
shared.windows.settings.visible = not shared.windows.settings.visible
|
||||
end},
|
||||
{"Test", "fa:s-play-circle", function ()
|
||||
if not shared.workshop.gameFilePath or shared.workshop.gameFilePath == "" then
|
||||
ui.prompt("Please save this game before testing.")
|
||||
else
|
||||
|
||||
local content = engine.construct("guiTextBox", shared.workshop.interface, {
|
||||
name = "_loadingTest",
|
||||
backgroundAlpha = 0,
|
||||
textAlpha = 0,
|
||||
backgroundColour = colour:black(),
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
zIndex = 5000,
|
||||
fontSize = 24,
|
||||
align = enums.align.middle,
|
||||
text = "Uploading to Remote Testing Server\nServer: London"
|
||||
})
|
||||
|
||||
engine.tween:begin(content, 1, {
|
||||
backgroundAlpha = 0.95,
|
||||
textAlpha = 1
|
||||
}, "inOutQuad")
|
||||
|
||||
if not shared.workshop:remoteTestServer() then
|
||||
content:destroy()
|
||||
ui.prompt("Please save this game before testing.")
|
||||
end
|
||||
end
|
||||
end},
|
||||
}
|
||||
}
|
||||
|
||||
if shared.developerMode then
|
||||
tabs["Development"] = {
|
||||
{"Reload", "fa:s-sync-alt", function()
|
||||
shared.workshop:reloadCreate()
|
||||
end},
|
||||
{"Run Lua", "fa:s-chevron-right", function()
|
||||
shared.windows.runLua.visible = not shared.windows.runLua.visible
|
||||
end}
|
||||
}
|
||||
end
|
||||
|
||||
local topBar = ui.create("guiFrame", shared.workshop.interface, {
|
||||
name = "topBar",
|
||||
size = guiCoord(1, 0, 0, 22)
|
||||
}, "primary")
|
||||
|
||||
local topBarSubMenu = ui.create("guiFrame", shared.workshop.interface, {
|
||||
name = "topBarSubMenu",
|
||||
size = guiCoord(1, 0, 0, 50),
|
||||
position = guiCoord(0, 0, 0, 22)
|
||||
}, "primaryVariant")
|
||||
|
||||
local currentX = 20
|
||||
local guiTabs = {}
|
||||
|
||||
for tabName, options in pairs(tabs) do
|
||||
local newTabBtn = ui.create("guiTextBox", topBar, {
|
||||
text = tabName,
|
||||
position = guiCoord(0, currentX, 0, 2),
|
||||
fontSize = 20,
|
||||
align = enums.align.middle,
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, "primaryVariant")
|
||||
|
||||
local newSubMenu = engine.construct("guiFrame", topBarSubMenu, {
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
xpos = 12
|
||||
for i,v in pairs(options) do
|
||||
if v[1] == "Seperator" then
|
||||
local seperator = ui.create("guiFrame", newSubMenu, {
|
||||
size = guiCoord(0, 2, 0.6, 0),
|
||||
position = guiCoord(0, xpos, 0.2, 0)
|
||||
}, "primary")
|
||||
xpos = xpos + 12
|
||||
else
|
||||
local newOption = ui.create("guiFrame", newSubMenu, {
|
||||
size = guiCoord(0, 56, 0, 46),
|
||||
position = guiCoord(0, xpos, 0, 2),
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, "primaryVariant")
|
||||
|
||||
if type(v[3]) == "function" then
|
||||
newOption:mouseLeftPressed(v[3])
|
||||
end
|
||||
|
||||
ui.create("guiImage", newOption, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0, 18, 0, 6),
|
||||
texture = v[2],
|
||||
handleEvents = false
|
||||
}, "primaryImage")
|
||||
|
||||
ui.create("guiTextBox", newOption, {
|
||||
size = guiCoord(1, 0, 0, 16),
|
||||
position = guiCoord(0, 0, 0, 30),
|
||||
text = v[1],
|
||||
handleEvents = false,
|
||||
align = enums.align.middle,
|
||||
fontSize = 15
|
||||
}, "primaryText")
|
||||
|
||||
xpos = xpos + 62
|
||||
end
|
||||
end
|
||||
|
||||
newTabBtn:mouseLeftPressed(function ()
|
||||
for btn, submenu in pairs(guiTabs) do
|
||||
btn.backgroundAlpha = 0
|
||||
submenu.visible = false
|
||||
end
|
||||
newSubMenu.visible = true
|
||||
newTabBtn.backgroundAlpha = 1
|
||||
end)
|
||||
|
||||
if currentX > 20 then
|
||||
newSubMenu.visible = false
|
||||
newTabBtn.backgroundAlpha = 0
|
||||
end
|
||||
|
||||
local txtDim = newTabBtn.textDimensions
|
||||
newTabBtn.size = guiCoord(0, txtDim.x + 20, 0, 20)
|
||||
currentX = currentX + txtDim.x + 30
|
||||
guiTabs[newTabBtn] = newSubMenu
|
||||
end
|
|
@ -0,0 +1,101 @@
|
|||
-- Context Menu Helper
|
||||
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local controller = {}
|
||||
controller.currentContextMenu = nil
|
||||
|
||||
engine.input:mouseLeftReleased(function ()
|
||||
if controller.currentContextMenu then
|
||||
local focusedGui = engine.input.mouseFocusedGui
|
||||
-- The mouse was clicked...
|
||||
-- If there is no gui in focus,
|
||||
-- or if the focused gui is not the context menu
|
||||
-- ... we remove the context menu
|
||||
if not focusedGui or (focusedGui ~= controller.currentContextMenu and not focusedGui:isDescendantOf(controller.currentContextMenu)) then
|
||||
controller.currentContextMenu:destroy()
|
||||
controller.currentContextMenu = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Merely to demonstate the different options possible or to test the context menu helper.
|
||||
controller.exampleOptions = {
|
||||
{name = "Option 1", callback = function() print("callback") end},
|
||||
{name = "Option 2"},
|
||||
{name = "Option 3"}
|
||||
}
|
||||
|
||||
-- Generates the menu and removes any old menus.
|
||||
controller.generateMenu = function(options, position)
|
||||
|
||||
-- Destroy any preexisting context menu
|
||||
if controller.currentContextMenu then
|
||||
controller.currentContextMenu:destroy()
|
||||
controller.currentContextMenu = nil
|
||||
end
|
||||
|
||||
-- If the calling function did not provide a position for the context menu,
|
||||
-- we default to the user's cursor position.
|
||||
if not position or not type(position) == "guiCoord" then
|
||||
position = guiCoord(0, engine.input.mousePosition.x, 0, engine.input.mousePosition.y)
|
||||
end
|
||||
|
||||
-- create the context gui main frame, styled.
|
||||
local menu = ui.create("guiFrame", shared.workshop.interface, {
|
||||
name = "_contextMenu",
|
||||
zIndex = 10000,
|
||||
position = position
|
||||
}, "primary")
|
||||
|
||||
-- so we can delete it later
|
||||
controller.currentContextMenu = menu
|
||||
|
||||
local yPos = 6
|
||||
|
||||
-- create a gui for each option provided
|
||||
for _,option in pairs(options) do
|
||||
local btn = ui.create("guiTextBox", menu, {
|
||||
size = guiCoord(0.8, 0, 0, 20),
|
||||
position = guiCoord(0.1, 0, 0, yPos),
|
||||
align = enums.align.middleLeft,
|
||||
text = option.name,
|
||||
textAlpha = 0.6
|
||||
}, "primaryText")
|
||||
|
||||
if option.callback then
|
||||
btn:mouseLeftReleased(function ()
|
||||
option.callback()
|
||||
menu:destroy()
|
||||
controller.currentContextMenu = nil
|
||||
end)
|
||||
end
|
||||
|
||||
-- Improves accessibility
|
||||
btn:mouseFocused(function ()
|
||||
btn.textAlpha = 1
|
||||
end)
|
||||
|
||||
btn:mouseUnfocused(function ()
|
||||
btn.textAlpha = 0.6
|
||||
end)
|
||||
|
||||
yPos = yPos + 26
|
||||
end
|
||||
|
||||
-- make the main container the right size.
|
||||
menu.size = guiCoord(0, 160, 0, yPos)
|
||||
end
|
||||
|
||||
controller.bind = function(object, options)
|
||||
if not object.mouseRightReleased then
|
||||
return warn("Could not hook onto mouse event?!")
|
||||
end
|
||||
|
||||
object:on("mouseRightReleased", function()
|
||||
controller.generateMenu(options)
|
||||
end)
|
||||
end
|
||||
|
||||
return controller
|
|
@ -0,0 +1,323 @@
|
|||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local themer = require("tevgit:workshop/controllers/ui/core/themer.lua")
|
||||
local controller = {}
|
||||
|
||||
function roundToMultiple(number, multiple)
|
||||
if multiple == 0 then
|
||||
return number
|
||||
end
|
||||
|
||||
return ((number % multiple) > multiple/2) and number + multiple - number%multiple or number - number%multiple
|
||||
end
|
||||
|
||||
-- Used to sort objects by ZIndex.
|
||||
-- This script uses the object's ZIndex to control the order in the dock
|
||||
function numSorter(a,b)
|
||||
return a < b
|
||||
end
|
||||
|
||||
function zSorter(a,b)
|
||||
return a.zIndex < b.zIndex
|
||||
end
|
||||
|
||||
-- Currently only supposed to be called once,
|
||||
-- it creates 'docks's that can hold other windows.
|
||||
-- currently docks themselves are static and invisible,
|
||||
-- this can easily be changed.
|
||||
controller.setupDocks = function ()
|
||||
if controller.docks then
|
||||
-- delete old docks
|
||||
for _,dock in pairs(controller.docks) do
|
||||
for _,child in pairs(dock.children) do
|
||||
child.parent = dock.parent
|
||||
end
|
||||
|
||||
dock:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
controller.docks = {
|
||||
engine.construct("guiFrame", shared.workshop.interface, {
|
||||
name = "_dockTop",
|
||||
size = guiCoord(1, -500, 0, 250 - 76),
|
||||
position = guiCoord(250, 0, 0, 76),
|
||||
backgroundAlpha = 0,
|
||||
handleEvents = false,
|
||||
cropChildren = false,
|
||||
}),
|
||||
engine.construct("guiFrame", shared.workshop.interface, {
|
||||
name = "_dockLeft",
|
||||
size = guiCoord(0, 250, 1, -76),
|
||||
position = guiCoord(0, 0, 0, 76),
|
||||
backgroundAlpha = 0,
|
||||
handleEvents = false,
|
||||
cropChildren = false,
|
||||
}),
|
||||
engine.construct("guiFrame", shared.workshop.interface, {
|
||||
name = "_dockBottom",
|
||||
size = guiCoord(1, -500, 0, 250),
|
||||
position = guiCoord(0, 250, 1, -250),
|
||||
backgroundAlpha = 0,
|
||||
handleEvents = false,
|
||||
cropChildren = false,
|
||||
}),
|
||||
engine.construct("guiFrame", shared.workshop.interface, {
|
||||
name = "_dockRight",
|
||||
size = guiCoord(0, 265, 1, -76),
|
||||
position = guiCoord(1, -265, 0, 76),
|
||||
backgroundAlpha = 0,
|
||||
handleEvents = false,
|
||||
cropChildren = false,
|
||||
})
|
||||
}
|
||||
end
|
||||
|
||||
controller.setupDocks()
|
||||
|
||||
-- Store info about a window,
|
||||
-- such as their pre-docked size
|
||||
local windowDetails = {}
|
||||
|
||||
-- Ran whenever a dock's contents is changed
|
||||
local function dockCallback(dock, isPreviewing)
|
||||
if dock.name == "_dockLeft" then
|
||||
shared.workshop.interface["_toolBar"].position = (#dock.children > 0 or isPreviewing) and guiCoord(0, 258, 0, 80) or guiCoord(0, 8, 0, 80)
|
||||
end
|
||||
end
|
||||
|
||||
-- Adds a window to a dock
|
||||
local function pushToDock(window, dock, slot)
|
||||
local perWindow = 1 / (#dock.children + 1)
|
||||
local isVertical = dock.absoluteSize.y > dock.absoluteSize.x
|
||||
|
||||
-- sort zIndexes
|
||||
local children = dock.children
|
||||
table.sort(children, zSorter)
|
||||
|
||||
local ii = 0
|
||||
for i,v in pairs(children) do
|
||||
if ii == slot then
|
||||
ii = ii + 1
|
||||
end
|
||||
v.zIndex = ii
|
||||
v.size = guiCoord(isVertical and 1 or perWindow, 0, isVertical and perWindow or 1, 0)
|
||||
v.position = guiCoord(isVertical and 0 or (perWindow*(ii)), 0, isVertical and (perWindow*(ii)) or 0, 0)
|
||||
ii = ii + 1
|
||||
end
|
||||
|
||||
window.parent = dock
|
||||
window.size = guiCoord(isVertical and 1 or perWindow, 0, isVertical and perWindow or 1, 0)
|
||||
window.position = guiCoord(isVertical and 0 or (slot * perWindow), 0, isVertical and slot * perWindow or 0, 0)
|
||||
window.zIndex = slot
|
||||
dockCallback(dock)
|
||||
end
|
||||
|
||||
-- Properly orders windows within a dock
|
||||
-- useful when a window is removed
|
||||
local function orderDock(dock)
|
||||
local perWindow = 1 / #dock.children
|
||||
local isVertical = dock.absoluteSize.y > dock.absoluteSize.x
|
||||
|
||||
-- sort zIndexes
|
||||
local children = dock.children
|
||||
table.sort(children, zSorter)
|
||||
|
||||
local ii = 0
|
||||
for i,v in pairs(children) do
|
||||
v.zIndex = ii
|
||||
v.size = guiCoord(isVertical and 1 or perWindow, 0, isVertical and perWindow or 1, 0)
|
||||
v.position = guiCoord(isVertical and 0 or (perWindow*(ii)), 0, isVertical and (perWindow*(ii)) or 0, 0)
|
||||
ii = ii + 1
|
||||
end
|
||||
dockCallback(dock)
|
||||
end
|
||||
|
||||
-- returns the parent dock of a window if any
|
||||
local function isInDock(window)
|
||||
for _,v in pairs(controller.docks) do
|
||||
if window:isDescendantOf(v) then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
controller.saveDockSettings = function()
|
||||
-- Save dock to workshop settings
|
||||
local settings = {}
|
||||
for _,v in pairs(controller.docks) do
|
||||
settings[v.name] = {}
|
||||
|
||||
local children = v.children
|
||||
table.sort(children, zSorter)
|
||||
|
||||
for _,vv in pairs(children) do
|
||||
settings[v.name][vv.name] = vv.zIndex
|
||||
end
|
||||
|
||||
shared.workshop:setSettings("workshopDocks", settings)
|
||||
end
|
||||
end
|
||||
|
||||
-- used if docks haven't been saved before:
|
||||
local defaultSettings = {
|
||||
["_dockRight"] = {
|
||||
["Hierarchy"] = 0,
|
||||
["Properties"] = 1
|
||||
}
|
||||
}
|
||||
|
||||
controller.loadDockSettings = function()
|
||||
-- Load Dock from settings
|
||||
local settings = shared.workshop:getSettings("workshopDocks")
|
||||
if not settings then
|
||||
settings = defaultSettings
|
||||
end
|
||||
|
||||
for dockName, windows in pairs(settings) do
|
||||
local dock = shared.workshop.interface:hasChild(dockName)
|
||||
if dock then
|
||||
for windowName, zIndex in pairs(windows) do
|
||||
local win = shared.workshop.interface:hasChild(windowName)
|
||||
if win then
|
||||
if not windowDetails[win] then
|
||||
windowDetails[win] = {zIndex = win.zIndex, size = win.size}
|
||||
end
|
||||
|
||||
win.zIndex = zIndex
|
||||
win.parent = dock
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _,v in pairs(controller.docks) do
|
||||
orderDock(v)
|
||||
end
|
||||
end
|
||||
|
||||
controller.undock = function (window)
|
||||
local dock = isInDock(window)
|
||||
if dock then
|
||||
window.parent = dock.parent
|
||||
orderDock(dock)
|
||||
|
||||
if windowDetails[window] then
|
||||
window.size = windowDetails[window].size
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Invoked by a 3rd party script when user begins dragging window.
|
||||
controller.beginWindowDrag = function(window, dontDock)
|
||||
-- change window appeareance (mainly for debugging) to make it clear this window is on the move.
|
||||
if not windowDetails[window] then
|
||||
windowDetails[window] = {zIndex = window.zIndex, size = window.size}
|
||||
else
|
||||
window.size = windowDetails[window].size
|
||||
end
|
||||
|
||||
window.zIndex = 99
|
||||
|
||||
-- offset used for dragging
|
||||
local offset = window.absolutePosition - engine.input.mousePosition
|
||||
|
||||
-- undock window
|
||||
controller.undock(window)
|
||||
|
||||
local previewer = engine.construct("guiFrame", shared.workshop.interface, {
|
||||
handleEvents = false,
|
||||
visible = false,
|
||||
backgroundAlpha = 0.75
|
||||
})
|
||||
themer.registerGui(previewer, "primary")
|
||||
|
||||
local isOverDock, slot;
|
||||
local lastDock;
|
||||
|
||||
-- yield until user releases window drag
|
||||
while engine.input:isMouseButtonDown(enums.mouseButton.left) do
|
||||
local mPos = engine.input.mousePosition
|
||||
local newpos = mPos + offset
|
||||
window.position = guiCoord(0, newpos.x, 0, newpos.y)
|
||||
|
||||
isOverDock = false
|
||||
|
||||
if not dontDock then
|
||||
for _,dock in pairs(controller.docks) do
|
||||
-- the user's cursor is in this dock.
|
||||
if mPos.x > dock.absolutePosition.x and
|
||||
mPos.x < dock.absolutePosition.x + dock.absoluteSize.x and
|
||||
mPos.y > dock.absolutePosition.y and
|
||||
mPos.y < dock.absolutePosition.y + dock.absoluteSize.y then
|
||||
|
||||
local perWindow = 1 / (#dock.children + 1)
|
||||
local perWindowSize = perWindow * dock.absoluteSize
|
||||
local isVertical = dock.absoluteSize.y > dock.absoluteSize.x
|
||||
|
||||
local pws = (isVertical and perWindowSize.y or perWindowSize.x)
|
||||
local m = (isVertical and mPos.y or mPos.x)
|
||||
local dockPosition = (isVertical and dock.absolutePosition.y or dock.absolutePosition.x)
|
||||
local dockSize = (isVertical and dock.absoluteSize.y or dock.absoluteSize.x)
|
||||
|
||||
local mouseScaled = (m - dockPosition) / dockSize
|
||||
slot = math.clamp((roundToMultiple(mouseScaled, perWindow) / perWindow), 0, (#dock.children))
|
||||
|
||||
isOverDock = dock
|
||||
|
||||
previewer.visible = true
|
||||
previewer.size = guiCoord(0, isVertical and dock.absoluteSize.x or pws, 0, isVertical and pws or dock.absoluteSize.y)
|
||||
previewer.position = guiCoord(0, isVertical and dock.absolutePosition.x or dock.absolutePosition.x + (slot * pws), 0, isVertical and dock.absolutePosition.y + (slot * pws) or dock.absolutePosition.y)
|
||||
|
||||
local ii = 0
|
||||
local children = dock.children
|
||||
table.sort(children, zSorter)
|
||||
|
||||
for i,v in pairs(children) do
|
||||
if ii == slot then
|
||||
ii = ii + 1
|
||||
end
|
||||
|
||||
v.size = guiCoord(isVertical and 1 or perWindow, 0, isVertical and perWindow or 1, 0)
|
||||
v.position = guiCoord(isVertical and 0 or (perWindow*(ii)), 0, isVertical and (perWindow*(ii)) or 0, 0)
|
||||
ii = ii + 1
|
||||
end
|
||||
dockCallback(dock, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (lastDock ~= isOverDock) then
|
||||
if lastDock then
|
||||
orderDock(lastDock) -- reorder old dock we were hovering
|
||||
end
|
||||
lastDock = isOverDock
|
||||
end
|
||||
|
||||
if not isOverDock and previewer.visible then
|
||||
previewer.visible = false
|
||||
end
|
||||
|
||||
wait()
|
||||
end
|
||||
|
||||
if (lastDock and lastDock ~= isOverDock) then
|
||||
orderDock(lastDock) -- reorder old dock we were hovering
|
||||
end
|
||||
|
||||
previewer:destroy()
|
||||
|
||||
-- reset Window appearance
|
||||
window.zIndex = windowDetails[window].zIndex
|
||||
|
||||
if isOverDock then
|
||||
pushToDock(window, isOverDock, slot)
|
||||
else
|
||||
windowDetails[window] = nil
|
||||
end
|
||||
|
||||
-- save dock
|
||||
controller.saveDockSettings()
|
||||
end
|
||||
|
||||
return controller
|
|
@ -0,0 +1,130 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
-- Responsible for managing the aesthetics of different UI elements
|
||||
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local currentTheme = nil
|
||||
local registeredGuis = {}
|
||||
|
||||
local themeType = shared.workshop:getSettings("themeType")
|
||||
customTheme = shared.workshop:getSettings("customTheme")
|
||||
|
||||
if themeType == "custom" then
|
||||
currentTheme = engine.json:decode(customTheme)
|
||||
elseif themeType == "black" then
|
||||
currentTheme = require("tevgit:workshop/controllers/ui/themes/black.lua")
|
||||
elseif themeType == "white" then
|
||||
currentTheme = require("tevgit:workshop/controllers/ui/themes/white.lua")
|
||||
elseif themeType == "ow" then
|
||||
currentTheme = require("tevgit:workshop/controllers/ui/themes/ow.lua")
|
||||
else
|
||||
currentTheme = require("tevgit:workshop/controllers/ui/themes/default.lua")
|
||||
shared.workshop:setSettings("themeType", "default")
|
||||
end
|
||||
|
||||
local function themeriseGui(gui,...)
|
||||
-- Grab the gui's style name set in the "registerGui" func
|
||||
local styleName = registeredGuis[gui]
|
||||
|
||||
local args = nil
|
||||
if #{...} > 0 then
|
||||
args = {...}
|
||||
args = args[1]
|
||||
end
|
||||
|
||||
-- get the style's properties from the current theme
|
||||
local style = currentTheme[styleName]
|
||||
if not style then
|
||||
style = {}
|
||||
end
|
||||
|
||||
-- apply the theme's properties to the gui
|
||||
for property, value in pairs(style) do
|
||||
if gui[property] and gui[property] ~= value then
|
||||
gui[property] = value
|
||||
end
|
||||
if args then
|
||||
gui["borderRadius"] = args[1] or 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
types = {
|
||||
primary = "primary",
|
||||
primaryVariant = "primaryVariant",
|
||||
primaryText = "primaryText",
|
||||
primaryImage = "primaryImage",
|
||||
|
||||
secondary = "secondary",
|
||||
secondaryVariant = "secondaryVariant",
|
||||
secondaryText = "secondaryText",
|
||||
|
||||
error = "error",
|
||||
errorText = "errorText",
|
||||
|
||||
background = "background",
|
||||
backgroundText = "backgroundText",
|
||||
},
|
||||
|
||||
themeriseGui = themeriseGui,
|
||||
|
||||
registerGui = function(gui, style, ...)
|
||||
-- set the gui's style and themerise it.
|
||||
registeredGuis[gui] = style
|
||||
local args = {...} -- contains overrides
|
||||
if args then
|
||||
themeriseGui(gui,args)
|
||||
elseif not args then
|
||||
themeriseGui(gui)
|
||||
end
|
||||
end,
|
||||
|
||||
setTheme = function(theme,...)
|
||||
-- change the current theme AND re-themerise all guis
|
||||
currentTheme = theme
|
||||
args = {...} -- contains overrides
|
||||
for gui,v in pairs(registeredGuis) do
|
||||
if args then
|
||||
themeriseGui(gui,args)
|
||||
else
|
||||
themeriseGui(gui)
|
||||
end
|
||||
end
|
||||
|
||||
-- Save the theme
|
||||
shared.workshop:setSettings("themeType", "custom")
|
||||
shared.workshop:setSettings("customTheme", engine.json:encodeWithTypes(currentTheme))
|
||||
end,
|
||||
|
||||
resetTheme = function(theme)
|
||||
-- change the current theme AND re-themerise all guis
|
||||
currentTheme = require("tevgit:workshop/controllers/ui/themes/default.lua")
|
||||
for gui,v in pairs(registeredGuis) do
|
||||
themeriseGui(gui)
|
||||
end
|
||||
|
||||
-- Save the theme
|
||||
shared.workshop:setSettings("themeType", "default")
|
||||
shared.workshop:setSettings("customTheme", null)
|
||||
end,
|
||||
|
||||
setThemePreset = function(theme,...)
|
||||
-- change the current theme AND re-themerise all guis
|
||||
currentTheme = theme
|
||||
local args = {...} -- contains overrides
|
||||
for gui,v in pairs(registeredGuis) do
|
||||
if args then
|
||||
themeriseGui(gui,args)
|
||||
elseif not args then
|
||||
themeriseGui(gui)
|
||||
end
|
||||
end
|
||||
|
||||
shared.workshop:setSettings("customTheme", null)
|
||||
end,
|
||||
|
||||
getTheme = function()
|
||||
return currentTheme
|
||||
end
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
-- This script includes shorcuts for creating UIs.
|
||||
-- Any interface created here will be properly themed.
|
||||
|
||||
local themer = require("tevgit:workshop/controllers/ui/core/themer.lua")
|
||||
local dock = require("tevgit:workshop/controllers/ui/core/dock.lua")
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
|
||||
local create = function(className, parent, properties, style)
|
||||
if not parent then
|
||||
parent = shared.workshop.interface
|
||||
end
|
||||
|
||||
local gui = engine.construct(className, parent, properties)
|
||||
themer.registerGui(gui, style and style or "default")
|
||||
|
||||
return gui
|
||||
end
|
||||
|
||||
local activeTooltip = nil
|
||||
|
||||
return {
|
||||
tooltip = function ( gui, text, delay )
|
||||
if gui:isA("guiBase") then
|
||||
if not delay then delay = 0.4 end
|
||||
return gui:on("mouseFocused", function ()
|
||||
local tooltip = create("guiTextBox", shared.workshop.interface, {
|
||||
position = guiCoord(0, engine.input.mousePosition.x + 16, 0, engine.input.mousePosition.y),
|
||||
text = text,
|
||||
fontSize = 16,
|
||||
align = enums.align.middle,
|
||||
borderRadius = 0,
|
||||
borderAlpha = 1,
|
||||
zIndex = 5000,
|
||||
handleEvents = false,
|
||||
visible = false
|
||||
}, "primaryVariant")
|
||||
|
||||
local textDimensions = tooltip.textDimensions
|
||||
tooltip.size = guiCoord(0, textDimensions.x + 10, 0, textDimensions.y + 4)
|
||||
|
||||
gui:once("mouseUnfocused", function()
|
||||
tooltip:destroy()
|
||||
end)
|
||||
|
||||
wait(delay)
|
||||
if tooltip and tooltip.alive then
|
||||
tooltip.visible = true
|
||||
end
|
||||
end)
|
||||
end
|
||||
end,
|
||||
|
||||
create = create,
|
||||
|
||||
button = function(parent, text, size, position, theme)
|
||||
if not theme then theme = "primary" end
|
||||
local btn = create("guiFrame", parent, {
|
||||
size = size,
|
||||
position = position,
|
||||
borderRadius = 0,
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, theme)
|
||||
|
||||
create("guiTextBox", btn, {
|
||||
name = "label",
|
||||
size = guiCoord(1, -12, 1, -6),
|
||||
position = guiCoord(0, 6, 0, 3),
|
||||
text = text,
|
||||
handleEvents = false,
|
||||
align = enums.align.middle,
|
||||
}, theme .. "Text")
|
||||
|
||||
return btn
|
||||
end,
|
||||
|
||||
-- if closable is true OR a function, a close button will appear in the title bar.
|
||||
-- when clicked, if closable is a function, it is fired after hiding the window.
|
||||
window = function(parent, title, size, position, dockable, closable, dragable)
|
||||
local container = create("guiFrame", parent, {
|
||||
size = size,
|
||||
name = title,
|
||||
position = position,
|
||||
cropChildren = false,
|
||||
borderColour = colour:fromRGB(55, 59, 64),
|
||||
borderWidth = 2,
|
||||
borderAlpha = 1,
|
||||
}, themer.types.background)
|
||||
|
||||
container:on("changed", function (property, value)
|
||||
if property == "visible" and not value then
|
||||
dock.undock(container) -- just in case
|
||||
end
|
||||
end)
|
||||
|
||||
local titleBar = create("guiFrame", container, {
|
||||
name = "titleBar",
|
||||
position = guiCoord(0, -1, 0, -4),
|
||||
size = guiCoord(1, 2, 0, 25),
|
||||
borderRadius = 0,
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, themer.types.primary)
|
||||
|
||||
titleBar:mouseLeftPressed(function ()
|
||||
if dragable == false then return end -- Backwards compatibility
|
||||
dock.beginWindowDrag(container, not dockable)
|
||||
end)
|
||||
|
||||
-- create this to hide radius on bottom of titlebar
|
||||
create("guiFrame", titleBar, {
|
||||
size = guiCoord(1, 0, 0, 3),
|
||||
position = guiCoord(0, 0, 1, -3)
|
||||
}, themer.types.primary)
|
||||
|
||||
create("guiTextBox", titleBar, {
|
||||
name = "textBox",
|
||||
size = guiCoord(1, -12, 0, 20),
|
||||
position = guiCoord(0, 6, 0, 2),
|
||||
text = title,
|
||||
handleEvents = false
|
||||
}, themer.types.primaryText)
|
||||
|
||||
if closable then
|
||||
local closeBtn = create("guiImage", titleBar, {
|
||||
position = guiCoord(1, -22, 0, 3),
|
||||
size = guiCoord(0, 19, 0, 19),
|
||||
texture = "fa:s-window-close",
|
||||
imageAlpha = 0.5,
|
||||
hoverCursor = "fa:s-hand-pointer"
|
||||
}, "primaryImage")
|
||||
|
||||
closeBtn:mouseLeftReleased(function ()
|
||||
container.visible = false
|
||||
if type(closable) == "function" then
|
||||
closable()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local content = engine.construct("guiFrame", container, {
|
||||
name = "content",
|
||||
backgroundAlpha = 0,
|
||||
size = guiCoord(1, -12, 1, -27),
|
||||
position = guiCoord(0, 3, 0, 24),
|
||||
cropChildren = false,
|
||||
})
|
||||
|
||||
return container
|
||||
end,
|
||||
|
||||
-- Creates a full screen prompt
|
||||
-- Prevents user from doing anything until they acknowledge the prompt
|
||||
-- if callback is nil, the function yields until the user continues
|
||||
-- if callback is a function, it is ran when the user continues
|
||||
prompt = function ( message, callback )
|
||||
local content = create("guiFrame", shared.workshop.interface, {
|
||||
name = "_prompt",
|
||||
backgroundAlpha = 0.9,
|
||||
backgroundColour = colour:black(),
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
zIndex = 5000
|
||||
}, "background")
|
||||
|
||||
if shared.developerMode then
|
||||
content.handleEvents = false
|
||||
create("guiTextBox", content, {
|
||||
name = "disclaimer",
|
||||
size = guiCoord(1, 0, 0, 14),
|
||||
position = guiCoord(0, 0, 1, -14),
|
||||
text = "Developer mode is enabled, this prompt has not captured your mouse input, bg opacity is also decreased.",
|
||||
handleEvents = false,
|
||||
align = enums.align.middle,
|
||||
fontSize = 14
|
||||
}, "backgroundText")
|
||||
|
||||
content.backgroundAlpha = 0.75
|
||||
end
|
||||
|
||||
local container = create("guiFrame", content, {
|
||||
size = guiCoord(0.4, 0, 0.4, 0),
|
||||
position = guiCoord(0.3, 0, 0.3, 0),
|
||||
cropChildren = false,
|
||||
borderRadius = 0,
|
||||
handleEvents = false
|
||||
}, themer.types.primary)
|
||||
|
||||
local text = create("guiTextBox", container, {
|
||||
name = "disclaimer",
|
||||
size = guiCoord(1, -20, 1, -20),
|
||||
position = guiCoord(0, 10, 0, 10),
|
||||
text = message,
|
||||
handleEvents = false,
|
||||
align = enums.align.middle,
|
||||
fontSize = 21
|
||||
}, "primaryText")
|
||||
|
||||
local textDimensions = text.textDimensions
|
||||
container.size = guiCoord(0, textDimensions.x + 20, 0, textDimensions.y + 20)
|
||||
container.position = guiCoord(0.5, -(textDimensions.x + 20)/2, 0.5, -(textDimensions.y + 20)/2)
|
||||
|
||||
local continue = create("guiTextBox", content, {
|
||||
size = guiCoord(0, textDimensions.x + 20, 0, 40),
|
||||
position = guiCoord(0.5, -(textDimensions.x + 20)/2, 0.5, (textDimensions.y/2 + 5)),
|
||||
cropChildren = false,
|
||||
zIndex = 2,
|
||||
text = "Continue",
|
||||
align = enums.align.middle,
|
||||
fontFile = "local:OpenSans-Bold.ttf"
|
||||
}, themer.types.primaryVariant)
|
||||
|
||||
if not callback then
|
||||
local acknowledged = false
|
||||
continue:once("mouseLeftPressed", function() acknowledged = true end)
|
||||
repeat wait() until acknowledged
|
||||
content:destroy()
|
||||
return true
|
||||
else
|
||||
continue:once("mouseLeftPressed", function()
|
||||
if type(callback) == "function" then
|
||||
callback()
|
||||
end
|
||||
content:destroy()
|
||||
end)
|
||||
end
|
||||
end
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
-- Responsible for creating the workshop interface
|
||||
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
local ui = require("tevgit:workshop/controllers/ui/core/ui.lua")
|
||||
|
||||
require("tevgit:workshop/controllers/ui/components/topBar.lua")
|
||||
require("tevgit:workshop/controllers/ui/components/toolBar.lua")
|
||||
require("tevgit:workshop/controllers/ui/components/inserter.lua")
|
||||
|
||||
shared.windows.settings = require("tevgit:workshop/controllers/ui/components/settings.lua")
|
||||
shared.windows.settings.visible = false -- Dont show the window on start, thats annoying
|
||||
|
||||
shared.windows.runLua = require("tevgit:workshop/controllers/ui/components/runLua.lua")
|
||||
shared.windows.runLua.visible = false
|
||||
|
||||
shared.windows.propertyEditor = require("tevgit:workshop/controllers/ui/components/propertyEditor/window.lua").window
|
||||
|
||||
shared.windows.hierarchy = require("tevgit:workshop/controllers/ui/components/hierarchy.lua").window
|
||||
|
||||
shared.windows.history = require("tevgit:workshop/controllers/ui/components/historyUi.lua")
|
||||
shared.windows.history.visible = false
|
||||
|
||||
require("tevgit:workshop/controllers/ui/core/dock.lua").loadDockSettings()
|
||||
|
||||
if not shared.developerMode then
|
||||
ui.prompt("This is community driven software\nIt does not resemble the final product in anyway.", true)
|
||||
end
|
|
@ -0,0 +1,76 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
return {
|
||||
primary = {
|
||||
backgroundColour = colour:fromRGB(8, 8, 8),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
primaryVariant = {
|
||||
backgroundColour = colour:fromRGB(16, 16, 16),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
primaryText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
primaryImage = {
|
||||
imageColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
secondary = {
|
||||
backgroundColour = colour:fromRGB(32, 32, 32),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
secondaryVariant = {
|
||||
backgroundColour = colour:fromRGB(48, 48, 48),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
secondaryText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
secondaryImage = {
|
||||
imageColour = colour:fromRGB(255, 255, 25),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
error = {
|
||||
backgroundColour = colour:fromRGB(176, 0, 16),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
errorText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
errorImage = {
|
||||
imageColour = colour:fromRGB(176, 100, 116),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
success = {
|
||||
backgroundColour = colour:fromRGB(0, 176, 16),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
successText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
successImage = {
|
||||
imageColour = colour:fromRGB(100, 176, 116),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
background = {
|
||||
backgroundColour = colour:fromRGB(64, 64, 64),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
backgroundText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
backgroundImage = {
|
||||
imageColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
return {
|
||||
primary = {
|
||||
backgroundColour = colour:fromRGB(44, 47, 51),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
primaryVariant = {
|
||||
backgroundColour = colour:fromRGB(55, 59, 64),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
primaryText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
primaryImage = {
|
||||
imageColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
secondary = {
|
||||
backgroundColour = colour:fromRGB(67, 92, 125),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
secondaryVariant = {
|
||||
backgroundColour = colour:fromRGB(78, 107, 145),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
secondaryText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
secondaryImage = {
|
||||
imageColour = colour:fromRGB(215, 215, 215),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
error = {
|
||||
backgroundColour = colour:fromRGB(176, 0, 32),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
errorText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
errorImage = {
|
||||
imageColour = colour:fromRGB(176, 100, 132),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
success = {
|
||||
backgroundColour = colour:fromRGB(0, 176, 32),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
successText = {
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
successImage = {
|
||||
imageColour = colour:fromRGB(100, 176, 132),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
background = {
|
||||
backgroundColour = colour:fromRGB(169, 170, 171),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
backgroundText = {
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
backgroundImage = {
|
||||
imageColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
return {
|
||||
primary = {
|
||||
backgroundColour = colour:fromRGB(255,0,0),
|
||||
textColour = colour:fromRGB(255,255,0),
|
||||
},
|
||||
primaryVariant = {
|
||||
backgroundColour = colour:fromRGB(255,255,255),
|
||||
textColour = colour:fromRGB(0, 255, 0),
|
||||
},
|
||||
primaryText = {
|
||||
textColour = colour:fromRGB(0, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
primaryImage = {
|
||||
imageColour = colour:fromRGB(255, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
secondary = {
|
||||
backgroundColour = colour:fromRGB(255, 255, 0),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
},
|
||||
secondaryVariant = {
|
||||
backgroundColour = colour:fromRGB(0, 255, 0),
|
||||
textColour = colour:fromRGB(0, 255, 255),
|
||||
},
|
||||
secondaryText = {
|
||||
textColour = colour:fromRGB(255, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
secondaryImage = {
|
||||
imageColour = colour:fromRGB(255, 255, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
error = {
|
||||
backgroundColour = colour:fromRGB(255,255,255),
|
||||
textColour = colour:fromRGB(0, 255, 0),
|
||||
},
|
||||
errorText = {
|
||||
textColour = colour:fromRGB(0, 255, 255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
errorImage = {
|
||||
imageColour = colour:fromRGB(255, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
success = {
|
||||
backgroundColour = colour:fromRGB(255, 255, 0),
|
||||
textColour = colour:fromRGB(255, 0, 0),
|
||||
},
|
||||
successText = {
|
||||
textColour = colour:fromRGB(255, 255, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
successImage = {
|
||||
imageColour = colour:fromRGB(255,255,255),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
background = {
|
||||
backgroundColour = colour:fromRGB(0, 255, 0),
|
||||
textColour = colour:fromRGB(0, 255, 255),
|
||||
},
|
||||
backgroundText = {
|
||||
textColour = colour:fromRGB(255, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
backgroundImage = {
|
||||
imageColour = colour:fromRGB(255,255,255),
|
||||
backgroundAlpha = 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
-- Copyright 2020 Teverse.com
|
||||
|
||||
return {
|
||||
primary = {
|
||||
backgroundColour = colour:fromRGB(255, 255, 255),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
primaryVariant = {
|
||||
backgroundColour = colour:fromRGB(239, 239, 239),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
primaryText = {
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
primaryImage = {
|
||||
imageColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
secondary = {
|
||||
backgroundColour = colour:fromRGB(223, 223, 223),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
secondaryVariant = {
|
||||
backgroundColour = colour:fromRGB(207, 207, 207),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
secondaryText = {
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
secondaryImage = {
|
||||
imageColour = colour:fromRGB(0, 0, 25),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
error = {
|
||||
backgroundColour = colour:fromRGB(176, 0, 239),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
errorText = {
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
errorImage = {
|
||||
imageColour = colour:fromRGB(176, 100, 1239),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
success = {
|
||||
backgroundColour = colour:fromRGB(0, 176, 239),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
successText = {
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
successImage = {
|
||||
imageColour = colour:fromRGB(100, 176, 1239),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
|
||||
background = {
|
||||
backgroundColour = colour:fromRGB(191, 191, 191),
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
},
|
||||
backgroundText = {
|
||||
textColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
},
|
||||
backgroundImage = {
|
||||
imageColour = colour:fromRGB(0, 0, 0),
|
||||
backgroundAlpha = 0
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
-- Copyright 2020- Teverse.com
|
||||
-- Used to share variables between scripts
|
||||
|
||||
return {
|
||||
workshop = nil, -- Holds workshop instance and is set in main.lua
|
||||
user = nil, -- Holds user instance and is set in main.lua
|
||||
developerMode = false, -- Holds the developer_mode boolean and is set in main.lua
|
||||
sideBarPageDefault = nil, -- Holds the default sidebar page (view) as a string and is set in topbarInterface.lua
|
||||
sideBarPageActive = nil, -- Holds the current active sidebar page (view) as a string and is set in topbarInterface.lua
|
||||
defaultColours = { -- Default colors used for theming UI components (~\library\ui\components)
|
||||
primary = colour:fromRGB(112, 112, 112),
|
||||
secondary = colour:fromRGB(239, 239, 239),
|
||||
background = colour:fromRGB(33, 33, 33),
|
||||
red = colour:fromRGB(255, 82, 82),
|
||||
green = colour:fromRGB(105, 240, 174),
|
||||
yellow = colour:fromRGB(255, 215, 64),
|
||||
blue = colour:fromRGB(68, 138, 255),
|
||||
orange = colour:fromRGB(255, 171, 64),
|
||||
purple = colour:fromRGB(124, 77, 255),
|
||||
white = colour:fromRGB(255, 255, 255)
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
-- Copyright 2020- Teverse
|
||||
-- This script constructs (or builds) the tooltip component
|
||||
|
||||
local globals = require("tevgit:workshop/library/globals.lua") -- globals; variables or instances that can be shared between files
|
||||
|
||||
return {
|
||||
construct = function(orientation, element, text, ...)
|
||||
--[[
|
||||
@Description
|
||||
Constructor method that initializes the tooltip instance.
|
||||
|
||||
@Params
|
||||
String, orientation
|
||||
UiBase, element
|
||||
String, text
|
||||
{...}, overrides
|
||||
|
||||
@Returns
|
||||
Instance, tooltip
|
||||
]]--
|
||||
|
||||
local data = {}
|
||||
self = data
|
||||
|
||||
-- Only for horizontal orientation
|
||||
local args = {...} -- Hold overrides
|
||||
local positionOverride = args[1] or guiCoord(0, 0, 0, 0) -- If not specified, default to guiCoord(0, 0, 0, 0)
|
||||
|
||||
if orientation == "vertical" then -- If orientation is specified to "vertical"
|
||||
local container = engine.construct("guiFrame", globals.workshop.interface, {
|
||||
size = guiCoord(0.1, 0, 0.1, 0),
|
||||
position = element.position+guiCoord(-0.02, 0, -0.01, 0),
|
||||
backgroundColour = globals.defaultColours.red,
|
||||
visible = false,
|
||||
zIndex = 200,
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
engine.construct("guiImage", container, {
|
||||
size = guiCoord(0, 48, 0, 48),
|
||||
position = guiCoord(0.33, 0, -0.15, 0),
|
||||
texture = "fa:s-caret-up",
|
||||
imageColour = globals.defaultColours.secondary,
|
||||
backgroundColour = globals.defaultColours.red,
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
local bodyContainer = engine.construct("guiFrame", container, {
|
||||
size = guiCoord(0.95, 0, 0.4, 0),
|
||||
position = guiCoord(0.025, 0, 0.23, 0),
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
borderAlpha = 1,
|
||||
borderRadius = 5,
|
||||
borderWidth = 3,
|
||||
borderColour = globals.defaultColours.secondary
|
||||
})
|
||||
|
||||
engine.construct("guiImage", bodyContainer, {
|
||||
size = guiCoord(0, 16, 0, 16),
|
||||
position = guiCoord(0.04, 0, 0.25, 0),
|
||||
texture = "fa:s-info-circle",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
engine.construct("guiTextBox", bodyContainer, {
|
||||
size = guiCoord(0.82, 0, 1, 0),
|
||||
position = guiCoord(0.15, 0, 0, 0),
|
||||
text = text,
|
||||
fontSize = 16,
|
||||
textColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
align = enums.align.middle,
|
||||
wrap = true
|
||||
})
|
||||
|
||||
self.display = function() container.visible = true end -- Display tooltip method
|
||||
self.hide = function() container.visible = false end -- Hide tooltip method
|
||||
|
||||
elseif orientation == "horizontal" then -- If orientation is specified to "horizontal"
|
||||
local container = engine.construct("guiFrame", globals.workshop.interface, {
|
||||
size = guiCoord(0.13, 0, 0.05, 0),
|
||||
position = (element.position+guiCoord(-0.22, 0, 0.24, 0))+positionOverride, -- Shorthand positioning
|
||||
backgroundColour = globals.defaultColours.red,
|
||||
visible = false,
|
||||
zIndex = 200,
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
engine.construct("guiImage", container, {
|
||||
size = guiCoord(0, 48, 0, 48),
|
||||
position = guiCoord(-0.03, 0, -0.06, 0),
|
||||
texture = "fa:s-caret-left",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.red,
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
local bodyContainer = engine.construct("guiFrame", container, {
|
||||
size = guiCoord(0.8, 0, 0.9, 0),
|
||||
position = guiCoord(0.133, 0, 0.05, 0),
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
borderAlpha = 1,
|
||||
borderRadius = 5,
|
||||
borderWidth = 3,
|
||||
borderColour = globals.defaultColours.primary
|
||||
})
|
||||
|
||||
engine.construct("guiImage", bodyContainer, {
|
||||
size = guiCoord(0, 16, 0, 16),
|
||||
position = guiCoord(0.05, 0, 0.3, 0),
|
||||
texture = "fa:s-info-circle",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
engine.construct("guiTextBox", bodyContainer, {
|
||||
size = guiCoord(0.82, 0, 1, 0),
|
||||
position = guiCoord(0.15, 0, 0, 0),
|
||||
text = text,
|
||||
fontSize = 16,
|
||||
textColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
align = enums.align.middle,
|
||||
wrap = true
|
||||
})
|
||||
|
||||
self.display = function() container.visible = true end -- Display tooltip method
|
||||
self.hide = function() container.visible = false end -- Hide tooltip method
|
||||
end
|
||||
return data
|
||||
end
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
-- Copyright 2020- Teverse
|
||||
-- This script constructs (or builds) the sidebar controller
|
||||
|
||||
local globals = require("tevgit:workshop/library/globals.lua") -- globals; variables or instances that can be shared between files
|
||||
local toolTip = require("tevgit:workshop/library/ui/components/toolTip.lua") -- UI component
|
||||
|
||||
return {
|
||||
construct = function(idValue)
|
||||
--[[
|
||||
@Description
|
||||
Constructor method that initializes the sidebar instance.
|
||||
|
||||
@Params
|
||||
String, idValue
|
||||
|
||||
@Returns
|
||||
Instance, sidebar
|
||||
]]--
|
||||
|
||||
local data = {}
|
||||
self = data
|
||||
self.id = idValue -- Unique Indentifier
|
||||
self.pages = {} -- Where we store our pages for sidebar
|
||||
|
||||
engine.construct("guiFrame", globals.workshop.interface, {
|
||||
size = guiCoord(0.04, 0, 0.015, 0),
|
||||
position = guiCoord(0, 0, 0.05, 0),
|
||||
backgroundColour = globals.defaultColours.secondary,
|
||||
})
|
||||
|
||||
engine.construct("guiFrame", globals.workshop.interface, {
|
||||
size = guiCoord(0.04, 0, 0.015, 0),
|
||||
position = guiCoord(0, 0, 0.24, 0),
|
||||
backgroundColour = globals.defaultColours.secondary,
|
||||
})
|
||||
|
||||
local toolsContainer = engine.construct("guiFrame", globals.workshop.interface, {
|
||||
size = guiCoord(0.04, 0, 0.18, 0),
|
||||
position = guiCoord(0, 0, 0.065, 0),
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local selectTool = engine.construct("guiImage", toolsContainer, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0.25, 0, 0.1, 0),
|
||||
texture = "fa:s-location-arrow",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local moveTool = engine.construct("guiImage", toolsContainer, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0.25, 0, 0.32, 0),
|
||||
texture = "fa:s-arrows-alt-h",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local rotateTool = engine.construct("guiImage", toolsContainer, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0.25, 0, 0.54, 0),
|
||||
texture = "fa:s-sync",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local sizeTool = engine.construct("guiImage", toolsContainer, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0.25, 0, 0.76, 0),
|
||||
texture = "fa:s-expand",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local moreToolsContainer = engine.construct("guiFrame", globals.workshop.interface, {
|
||||
name = "moreToolsContainer",
|
||||
size = guiCoord(0.04, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0.255, 0),
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
self.registerPage = function(pageName)
|
||||
--[[
|
||||
@Description
|
||||
Registers page to sidebar instance.
|
||||
|
||||
@Params
|
||||
String, pageName
|
||||
|
||||
@Returns
|
||||
guiFrame, page
|
||||
]]--
|
||||
|
||||
local zIndexRange
|
||||
if pageName == "Default" then -- Default page zIndex is set lower than other pages
|
||||
zIndexRange = 100
|
||||
else
|
||||
zIndexRange = 101
|
||||
end
|
||||
|
||||
local iconContainer = engine.construct("guiFrame", moreToolsContainer, {
|
||||
name = pageName,
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
zIndex = zIndexRange,
|
||||
visible = false
|
||||
})
|
||||
return iconContainer
|
||||
end
|
||||
|
||||
self.registerIcon = function(page, name, icon, tooltip, callback, ...)
|
||||
--[[
|
||||
@Description
|
||||
Registers icon to page instance.
|
||||
|
||||
@Params
|
||||
Instance, page
|
||||
String, name
|
||||
String, icon
|
||||
String, tooltip
|
||||
Method, callback
|
||||
{...}, overrides
|
||||
|
||||
@Returns
|
||||
Void, null, nil
|
||||
]]--
|
||||
|
||||
local args = {...} -- Holds overrides
|
||||
local xPositionOverride = args[1] or 0 -- Override if specified, else 0
|
||||
local positionToolTipOverride = args[2] or guiCoord(0, 0, 0, 0) -- Override if specified, else guiCoord(0, 0, 0, 0)
|
||||
local iconImage = engine.construct("guiImage", page, {
|
||||
name = name,
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord((0.25+xPositionOverride), 0, 0.02+(#page.children*0.04), 0), -- Shorthand positioning w/o a for-loop
|
||||
texture = icon,
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local _tooltip = toolTip.construct("horizontal", iconImage, tooltip, positionToolTipOverride) -- Initialize tooltip instance
|
||||
|
||||
--button:mouseLeftPressed(callback) -- When button is clicked, perform callback action
|
||||
|
||||
-- When mouse hovers over button, display tooltip
|
||||
iconImage:on("mouseFocused", function()
|
||||
_tooltip.display()
|
||||
end)
|
||||
|
||||
-- When mouse leaves from button, hide tooltip
|
||||
iconImage:on("mouseUnfocused", function()
|
||||
_tooltip.hide()
|
||||
end)
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
-- Copyright 2020- Teverse
|
||||
-- This script constructs (or builds) the topbar controller
|
||||
|
||||
local globals = require("tevgit:workshop/library/globals.lua") -- globals; variables or instances that can be shared between files
|
||||
local toolTip = require("tevgit:workshop/library/ui/components/toolTip.lua") -- UI component
|
||||
|
||||
return {
|
||||
construct = function(idValue, titleIconValue, titleValue)
|
||||
--[[
|
||||
@Description
|
||||
Constructor method that initializes the topbar instance.
|
||||
|
||||
@Params
|
||||
String, idValue
|
||||
String, titleIconValue
|
||||
String, titleValue
|
||||
|
||||
@Returns
|
||||
Instance, topbar
|
||||
]]--
|
||||
|
||||
local data = {}
|
||||
self = data -- Ease of use
|
||||
self.id = idValue
|
||||
self.title = titleValue
|
||||
self.titleIcon = titleIconValue
|
||||
self.keys = {} -- Where item keys are stored
|
||||
|
||||
local container = engine.construct("guiFrame", globals.workshop.interface, {
|
||||
size = guiCoord(1, 0, 0.05, 0),
|
||||
position = guiCoord(0, 0, 0, 0),
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
engine.construct("guiImage", container, {
|
||||
size = guiCoord(0, 28, 0, 28),
|
||||
position = guiCoord(0.01, 0, 0.1, 0),
|
||||
texture = titleIconValue,
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
handleEvents = false,
|
||||
})
|
||||
|
||||
engine.construct("guiTextBox", container, {
|
||||
size = guiCoord(0.5, 0, 0.1, 0),
|
||||
position = guiCoord(0.04, 0, 0.05, 0),
|
||||
text = titleValue,
|
||||
textColour = globals.defaultColours.primary,
|
||||
fontFile = "local:OpenSans-Bold.ttf",
|
||||
fontSize = 30,
|
||||
readOnly = true
|
||||
})
|
||||
|
||||
engine.construct("guiTextBox", container, {
|
||||
size = guiCoord(0.48, 0, 0.1, 0),
|
||||
position = guiCoord(0.86, 0, 0.1, 0),
|
||||
text = globals.user[2],
|
||||
textColour = globals.defaultColours.primary,
|
||||
fontSize = 25,
|
||||
readOnly = true
|
||||
})
|
||||
|
||||
local userIcon = engine.construct("guiFrame", container, {
|
||||
size = guiCoord(0, 32, 0, 32),
|
||||
position = guiCoord(0.82, 0, 0, 0),
|
||||
backgroundColour = globals.defaultColours.primary,
|
||||
borderRadius = 100
|
||||
})
|
||||
|
||||
local statusIcon = engine.construct("guiFrame", container, {
|
||||
size = guiCoord(0, 16, 0, 16),
|
||||
position = guiCoord(0.836, 0, 0.5, 0),
|
||||
backgroundColour = globals.defaultColours.green,
|
||||
borderWidth = 2,
|
||||
borderColour = globals.defaultColours.white,
|
||||
borderAlpha = 1,
|
||||
borderRadius = 32,
|
||||
zIndex = 100
|
||||
})
|
||||
|
||||
local undoButton = engine.construct("guiImage", container, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0.92, 0, 0.2, 0),
|
||||
texture = "fa:s-arrow-left",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local redoButton = engine.construct("guiImage", container, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0.94, 0, 0.2, 0),
|
||||
texture = "fa:s-arrow-right",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
local settingsButton = engine.construct("guiImage", container, {
|
||||
size = guiCoord(0, 20, 0, 20),
|
||||
position = guiCoord(0.97, 0, 0.2, 0),
|
||||
texture = "fa:s-sliders-h",
|
||||
imageColour = globals.defaultColours.primary,
|
||||
backgroundColour = globals.defaultColours.white,
|
||||
})
|
||||
|
||||
self.register = function(name, tooltip, page)
|
||||
--[[
|
||||
@Description
|
||||
Register method that appends to the topbar instance.
|
||||
|
||||
@Params
|
||||
String, name
|
||||
String, tooltip
|
||||
Instance, page
|
||||
|
||||
@Returns
|
||||
Void, null, nil
|
||||
]]--
|
||||
|
||||
table.insert(self.keys, {name})
|
||||
local button = engine.construct("guiButton", container, {
|
||||
size = guiCoord(0.056, 0, 0.9, 0),
|
||||
position = guiCoord(0.2+(#self.keys*0.07), 0, 0.05, 0),
|
||||
text = name,
|
||||
textColour = globals.defaultColours.primary,
|
||||
fontSize = 30,
|
||||
align = enums.align.middle,
|
||||
zIndex = 100
|
||||
})
|
||||
|
||||
local _tooltip = toolTip.construct("vertical", button, tooltip) -- Initialize tooltip instance
|
||||
|
||||
button:mouseLeftPressed(function()
|
||||
globals.sideBarPageActive.visible = (not globals.sideBarPageActive.visible) -- Unlist active page from view
|
||||
if globals.sideBarPageActive == page then -- If the same page is clicked twice, unlist and replace with default page
|
||||
globals.sideBarPageActive = globals.sideBarPageDefault
|
||||
globals.sideBarPageDefault.visible = true
|
||||
return -- Acts as a break
|
||||
end
|
||||
globals.sideBarPageActive = page
|
||||
page.visible = (not page.visible)
|
||||
end)
|
||||
|
||||
-- When mouse hovers over button, display tooltip
|
||||
button:on("mouseFocused", function()
|
||||
_tooltip.display()
|
||||
end)
|
||||
|
||||
-- When mouse leaves from button, hide tooltip
|
||||
button:on("mouseUnfocused", function()
|
||||
_tooltip.hide()
|
||||
end)
|
||||
end
|
||||
return data
|
||||
end
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
-- Copyright 2020- Teverse
|
||||
-- This script is responsible for creating the workshop interface
|
||||
|
||||
local globals = require("tevgit:workshop/library/globals.lua") -- globals; variables or instances that can be shared between files
|
||||
local prompt = require("tevgit:workshop/library/ui/components/prompt.lua") -- UI Component
|
||||
local topbarInterface = require("tevgit:workshop/library/ui/controllers/topbarInterface.lua") -- UI Controller
|
||||
local sidebarInterface = require("tevgit:workshop/library/ui/controllers/sidebarInterface.lua") -- UI Controller
|
||||
|
||||
local topBar = topbarInterface.construct("horizontalNavbar", "fa:s-cloud", "Test Place 1") -- Create initial topbar instance
|
||||
local sideBar = sidebarInterface.construct("verticalNavbar") -- Create initial sidebar instance
|
||||
|
||||
local defaultPage = sideBar.registerPage("Default") -- Register default page to sidebar instance
|
||||
sideBar.registerIcon(defaultPage, "openFolderIcon", "fa:s-folder-open", "Open Folder", nil)
|
||||
sideBar.registerIcon(defaultPage, "newFileIcon", "fa:s-file", "Create new file", nil)
|
||||
sideBar.registerIcon(defaultPage, "uploadFileIcon", "fa:s-file-upload", "Upload current file", nil)
|
||||
sideBar.registerIcon(defaultPage, "downloadFileIcon", "fa:s-file-download", "Download current file", nil)
|
||||
sideBar.registerIcon(defaultPage, "importFileIcon", "fa:s-file-import", "Import a file", nil, -0.048, guiCoord(0.048, 0, 0, 0))
|
||||
sideBar.registerIcon(defaultPage, "exportFileIcon", "fa:s-file-export", "Export current file", nil, 0.048, guiCoord(-0.048, 0, 0, 0))
|
||||
defaultPage.visible = true -- Set default sidebar page to visible
|
||||
globals.sideBarPageDefault = defaultPage -- Set default sidebar page to default
|
||||
globals.sideBarPageActive = defaultPage -- Set default sidebar page as active
|
||||
|
||||
|
||||
local designPage = sideBar.registerPage("Design") -- Design default page to sidebar instance
|
||||
sideBar.registerIcon(designPage, "screenContainerIcon", "fa:s-tv", "Create a container instance", nil)
|
||||
sideBar.registerIcon(designPage, "guiFrameIcon", "fa:s-square-full", "Create a guiFrame instance", nil)
|
||||
sideBar.registerIcon(designPage, "guiTextBoxIcon", "fa:s-i-cursor", "Create a guiTextBox instance", nil)
|
||||
sideBar.registerIcon(designPage, "guiImageIcon", "fa:s-image", "Create a guiImage instance", nil)
|
||||
|
||||
|
||||
local modelPage = sideBar.registerPage("Model") -- Register model page to sidebar instance
|
||||
sideBar.registerIcon(modelPage, "modelIcon", "fa:s-shapes", "Group instance(s) together", nil)
|
||||
|
||||
|
||||
local insertPage = sideBar.registerPage("Insert") -- Register insert page to sidebar instance
|
||||
sideBar.registerIcon(insertPage, "blockIcon", "fa:s-cube", "Create a cube instance", nil)
|
||||
sideBar.registerIcon(insertPage, "circleIcon", "fa:s-circle", "Create a cylinder instance", nil)
|
||||
--sideBar.registerIcon(designPage, "triangleIcon", "fa:s-i-cursor", nil, nil) -- Triangle / Wedge Icon doesn't exist
|
||||
sideBar.registerIcon(insertPage, "scriptIcon", "fa:s-code", "Create a script instance", nil)
|
||||
sideBar.registerIcon(insertPage, "lightIcon", "fa:s-lightbulb", "Create a light instance", nil)
|
||||
|
||||
|
||||
local testPage = sideBar.registerPage("Test") -- Register test page to sidebar instance
|
||||
sideBar.registerIcon(testPage, "consoleIcon", "fa:s-terminal", " Open console window", nil)
|
||||
sideBar.registerIcon(testPage, "playIcon", "fa:s-play", "Play scene", nil)
|
||||
sideBar.registerIcon(testPage, "serverIcon", "fa:s-server", "Configure server", nil)
|
||||
sideBar.registerIcon(testPage, "fullScreenIcon", "fa:s-fullscreen", "Toggle full screen", nil)
|
||||
|
||||
|
||||
|
||||
-- Register topbar button (name labels) to topbar instance
|
||||
topBar.register("Design", "Design your guis", designPage)
|
||||
topBar.register("Model", "Model your scene", modelPage)
|
||||
topBar.register("Insert", "Insert an instance to your scene", insertPage)
|
||||
topBar.register("Test", "Test your scene", testPage)
|
|
@ -1,93 +1,143 @@
|
|||
-- Copyright 2020- Teverse
|
||||
-- This script is required when workshop is loaded.
|
||||
-- Copyright 2020 Teverse
|
||||
-- This script is required when workshop is loaded,
|
||||
-- and engine.workshop is passed to the function returned.
|
||||
-- e.g. require('tevgit:workshop/main.lua')(engine.workshop)
|
||||
|
||||
local globals = require("tevgit:workshop/library/globals.lua") -- globals; variables or instances that can be shared between files
|
||||
local function beginLoad(workshop)
|
||||
--[[
|
||||
Teverse currently downloads tevgit files from the github repo when requested by the client.
|
||||
This is quite slow, so there's a delay between EACH require when the user doesn't have a local tevgit.
|
||||
|
||||
local function init(workshop)
|
||||
--[[
|
||||
@Description
|
||||
The initializer method that comes first when a new scene is open.
|
||||
To compensate for this, we'll quickly draw up a UI here BEFORE requiring anymore remote files.
|
||||
We will load some files first in order to make a reload button for Development users.
|
||||
--]]
|
||||
|
||||
@Params
|
||||
Instance, workshop
|
||||
-- Load this now, we need it to make the reload button
|
||||
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||
shared.workshop = workshop
|
||||
shared.developerMode = not shared.workshop.hasLocalTevGit or shared.workshop:hasLocalTevGit()
|
||||
|
||||
@Returns
|
||||
void, null, nil
|
||||
]]--
|
||||
local loadingScreen;
|
||||
do
|
||||
loadingScreen = engine.construct("guiFrame", workshop.interface, {
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
backgroundColour = colour:fromRGB(66, 66, 76),
|
||||
zIndex = 1000
|
||||
})
|
||||
|
||||
globals.workshop = workshop -- Set workshop instance as a global
|
||||
globals.user = engine:isAuthenticated() -- Set & Streamline user instance as a global
|
||||
globals.developerMode = (not globals.workshop.hasLocalTevGit) or (globals.workshop:hasLocalTevGit()) -- Set developmode boolean as a global
|
||||
engine.construct("guiTextBox", loadingScreen, {
|
||||
size = guiCoord(0.5, 0, 0.5, 0),
|
||||
position = guiCoord(0.25, 0, 0.25, 0),
|
||||
align = enums.align.middle,
|
||||
backgroundAlpha = 0,
|
||||
text = "Downloading the latest workshop...\nThis takes longer than a moment during beta."
|
||||
})
|
||||
|
||||
local loadingScreen = engine.construct("guiFrame", workshop.interface, {
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
backgroundColour = globals.defaultColours.background,
|
||||
zIndex = 1000
|
||||
})
|
||||
if shared.developerMode then
|
||||
local emergencyReload = engine.construct("guiTextBox", loadingScreen, {
|
||||
text = "Emergency Reload",
|
||||
size = guiCoord(0, 200, 0, 30),
|
||||
position = guiCoord(0.5, -100, 1, -40),
|
||||
backgroundColour = colour:fromRGB(44, 47, 51),
|
||||
textColour = colour:fromRGB(255, 255, 255),
|
||||
borderRadius = 3,
|
||||
hoverCursor = "fa:s-hand-pointer",
|
||||
align = enums.align.middle,
|
||||
})
|
||||
|
||||
engine.construct("guiTextBox", loadingScreen, {
|
||||
size = guiCoord(0.5, 0, 0.5, 0),
|
||||
position = guiCoord(0.25, 0, 0.25, 0),
|
||||
align = enums.align.middle,
|
||||
backgroundAlpha = 0,
|
||||
text = "Downloading the latest workshop...\nThis takes longer than a moment during beta."
|
||||
})
|
||||
emergencyReload:mouseLeftPressed(function()
|
||||
shared.workshop:reloadCreate()
|
||||
end)
|
||||
end
|
||||
|
||||
-- Load stuff before initial load in
|
||||
require("tevgit:workshop/library/ui/controllers/workshopInterface.lua")
|
||||
local spinner = engine.construct("guiImage", loadingScreen, {
|
||||
size = guiCoord(0, 24, 0, 24),
|
||||
position = guiCoord(1, -44, 1, -44),
|
||||
texture = "fa:s-sync-alt",
|
||||
backgroundAlpha = 0
|
||||
})
|
||||
|
||||
local tween;
|
||||
tween = engine.tween:begin(spinner, 1, {
|
||||
rotation = math.rad(-360)
|
||||
}, "inOutQuad", function ()
|
||||
tween:reset()
|
||||
tween:resume()
|
||||
end)
|
||||
end
|
||||
|
||||
-- Okay now we can load remote files whilst the user is looking at a loading screen.
|
||||
shared.controllers.env = require("tevgit:workshop/controllers/environment/main.lua")
|
||||
|
||||
shared.controllers.history = require("tevgit:workshop/controllers/core/history.lua")
|
||||
|
||||
shared.controllers.clipboard = require("tevgit:workshop/controllers/core/clipboard.lua")
|
||||
|
||||
-- Create the Teverse interface
|
||||
require("tevgit:workshop/controllers/ui/createInterface.lua")
|
||||
|
||||
-- Setup the 3D world
|
||||
shared.controllers.env:setupEnvironment()
|
||||
shared.controllers.env:createDefaultMap()
|
||||
--shared.controllers.env:createPBRDebugSpheres()
|
||||
|
||||
-- Loading is no longer needed by this phase, remove if still valid
|
||||
if loadingScreen then
|
||||
loadingScreen:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
return function(workshop)
|
||||
--[[
|
||||
@Description
|
||||
The main method that comes when a new scene is opened.
|
||||
|
||||
@Params
|
||||
Instance, workshop
|
||||
|
||||
@Returns
|
||||
function, method
|
||||
]]--
|
||||
|
||||
local success, message = pcall(init, workshop)
|
||||
|
||||
-- If initialize phase fails, prompt to the error screen
|
||||
if (not success) then
|
||||
workshop.interface:destroyAllChildren()
|
||||
|
||||
local errorScreen = engine.construct("guiFrame", workshop.interface, {
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
backgroundColour = globals.defaultColours.background,
|
||||
zIndex = 10000
|
||||
})
|
||||
|
||||
engine.construct("guiTextBox", errorScreen, {
|
||||
size = guiCoord(0.8, 0, 0.8, 0),
|
||||
position = guiCoord(0.1, 0, 0.1, 0),
|
||||
backgroundColour = globals.defaultColours.background,
|
||||
textColour = globals.defaultColours.red,
|
||||
align = enums.align.topLeft,
|
||||
text = "Error loading Workshop\nIf this isn't your fault, please take a screenshot and report this as a bug. \n\n" .. message .." \n\nPlease press 'ENTER' on your keyboard to restart Teverse.",
|
||||
wrap = true,
|
||||
})
|
||||
|
||||
-- Bind the "return" key on the keyboard as temporary fast-reload keybind
|
||||
engine.input:on("keyPressed", function(keyboard)
|
||||
if keyboard.key == enums.key["return"] then
|
||||
workshop:reloadCreate()
|
||||
end
|
||||
end)
|
||||
loadingScreen = nil
|
||||
end
|
||||
|
||||
-- Bind the "f12" key on the keyboard as fast-reload keybind if initialize phase is successful
|
||||
engine.input:on("keyPressed", function(keyboard)
|
||||
if keyboard.key == enums.key["f12"] then
|
||||
workshop:reloadCreate()
|
||||
end
|
||||
end)
|
||||
local colourPicker = require("tevgit:workshop/controllers/ui/components/colourPicker.lua")
|
||||
|
||||
--print("Workshop Loaded. ", #engine.workspace.children) Lets not spam the console
|
||||
end
|
||||
|
||||
return function( workshop )
|
||||
local success, message = pcall(beginLoad, workshop)
|
||||
|
||||
if not success then
|
||||
workshop.interface:destroyAllChildren()
|
||||
|
||||
local errorScreen = engine.construct("guiFrame", workshop.interface, {
|
||||
size = guiCoord(1, 0, 1, 0),
|
||||
backgroundColour = colour:fromRGB(0, 0, 128),
|
||||
zIndex = 10000
|
||||
})
|
||||
|
||||
engine.construct("guiTextBox", errorScreen, {
|
||||
size = guiCoord(0.8, 0, 0.8, 0),
|
||||
position = guiCoord(0.1, 0, 0.1, 0),
|
||||
textColour = colour:fromRGB(255, 255, 0),
|
||||
align = enums.align.topLeft,
|
||||
backgroundAlpha = 0,
|
||||
text = "Error loading Workshop\nIf this isn't your fault, please take a screenshot and report this as a bug.\n\n" .. message,
|
||||
wrap = true,
|
||||
fontSize = 16,
|
||||
fontFile = "tevurl:fonts/PxPlus_AmstradPC1512-2y.ttf"
|
||||
})
|
||||
|
||||
-- delay showing this button for 1.5 seconds
|
||||
-- prevent spam reloads
|
||||
|
||||
wait(1.5)
|
||||
|
||||
engine.construct("guiTextBox", errorScreen, {
|
||||
text = "RELOAD",
|
||||
size = guiCoord(0, 80, 0, 30),
|
||||
position = guiCoord(.5, -40, 1, -60),
|
||||
backgroundColour = colour:fromRGB(255, 255, 0),
|
||||
textColour = colour:fromRGB(0, 0, 128),
|
||||
hoverCursor = "fa:s-hand-pointer",
|
||||
align = enums.align.middle,
|
||||
fontFile = "tevurl:fonts/PxPlus_AmstradPC1512-2y.ttf"
|
||||
}):mouseLeftPressed(function()
|
||||
workshop:reloadCreate()
|
||||
end)
|
||||
|
||||
engine.input:on("keyPressed", function(inputObj)
|
||||
if inputObj.key == enums.key["return"] then
|
||||
workshop:reloadCreate()
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue