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
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
This repo contains open source components produced for Teverse.
|
This repo contains open source components produced for Teverse.
|
||||||
|
|
||||||
Contains the redesigned components of Workshop (``./workshop``).
|
|
||||||
|
|
||||||
|
|
||||||
# Contributing to Teverse
|
# 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
|
-- Copyright 2020 Teverse
|
||||||
-- This script is required when workshop is loaded.
|
-- 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)
|
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.
|
||||||
@Description
|
--]]
|
||||||
The initializer method that comes first when a new scene is open.
|
|
||||||
|
|
||||||
@Params
|
-- Load this now, we need it to make the reload button
|
||||||
Instance, workshop
|
local shared = require("tevgit:workshop/controllers/shared.lua")
|
||||||
|
shared.workshop = workshop
|
||||||
|
shared.developerMode = not shared.workshop.hasLocalTevGit or shared.workshop:hasLocalTevGit()
|
||||||
|
|
||||||
@Returns
|
local loadingScreen;
|
||||||
void, null, nil
|
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
|
engine.construct("guiTextBox", loadingScreen, {
|
||||||
globals.user = engine:isAuthenticated() -- Set & Streamline user instance as a global
|
size = guiCoord(0.5, 0, 0.5, 0),
|
||||||
globals.developerMode = (not globals.workshop.hasLocalTevGit) or (globals.workshop:hasLocalTevGit()) -- Set developmode boolean as a global
|
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, {
|
if shared.developerMode then
|
||||||
size = guiCoord(1, 0, 1, 0),
|
local emergencyReload = engine.construct("guiTextBox", loadingScreen, {
|
||||||
backgroundColour = globals.defaultColours.background,
|
text = "Emergency Reload",
|
||||||
zIndex = 1000
|
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, {
|
emergencyReload:mouseLeftPressed(function()
|
||||||
size = guiCoord(0.5, 0, 0.5, 0),
|
shared.workshop:reloadCreate()
|
||||||
position = guiCoord(0.25, 0, 0.25, 0),
|
end)
|
||||||
align = enums.align.middle,
|
end
|
||||||
backgroundAlpha = 0,
|
|
||||||
text = "Downloading the latest workshop...\nThis takes longer than a moment during beta."
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Load stuff before initial load in
|
local spinner = engine.construct("guiImage", loadingScreen, {
|
||||||
require("tevgit:workshop/library/ui/controllers/workshopInterface.lua")
|
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
|
if loadingScreen then
|
||||||
loadingScreen:destroy()
|
loadingScreen:destroy()
|
||||||
end
|
loadingScreen = nil
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind the "f12" key on the keyboard as fast-reload keybind if initialize phase is successful
|
local colourPicker = require("tevgit:workshop/controllers/ui/components/colourPicker.lua")
|
||||||
engine.input:on("keyPressed", function(keyboard)
|
|
||||||
if keyboard.key == enums.key["f12"] then
|
--print("Workshop Loaded. ", #engine.workspace.children) Lets not spam the console
|
||||||
workshop:reloadCreate()
|
end
|
||||||
end
|
|
||||||
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
|
end
|
Loading…
Reference in New Issue