@@ -195,6 +195,11 @@ Here, *color* can be specified in the following ways:
|
|||||||
| data:set_data(data: table<int>) | replaces pixel data (width * height * 4 numbers) |
|
| data:set_data(data: table<int>) | replaces pixel data (width * height * 4 numbers) |
|
||||||
| data:create_texture(name: str) | creates and shares texture to renderer |
|
| data:create_texture(name: str) | creates and shares texture to renderer |
|
||||||
|
|
||||||
|
## Inline frame (iframe)
|
||||||
|
|
||||||
|
| Name | Type | Read | Write | Description |
|
||||||
|
|----------|--------|------|-------|-----------------------------|
|
||||||
|
| src | string | yes | yes | id of the embedded document |
|
||||||
|
|
||||||
## Inventory
|
## Inventory
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,12 @@ The key code for comparison can be obtained via `input.keycode("key_name")`
|
|||||||
- `supplier` - Lua function - value supplier
|
- `supplier` - Lua function - value supplier
|
||||||
- `change-on-release` - Call consumer on trackbar release only. Type: boolean. Default: false
|
- `change-on-release` - Call consumer on trackbar release only. Type: boolean. Default: false
|
||||||
|
|
||||||
|
## Inline frame - *iframe*
|
||||||
|
|
||||||
|
Container for embedding an external document. Content is scaling to the iframe size.
|
||||||
|
|
||||||
|
- `src` - document id in the format `pack:name` (`pack/layouts/name.xml`)
|
||||||
|
|
||||||
# Inventory elements
|
# Inventory elements
|
||||||
|
|
||||||
## *inventory*
|
## *inventory*
|
||||||
|
|||||||
@@ -196,11 +196,17 @@ document["worlds-panel"]:clear()
|
|||||||
| data:set_data(data: table<int>) | заменяет данные пикселей (ширина * высота * 4 чисел) |
|
| data:set_data(data: table<int>) | заменяет данные пикселей (ширина * высота * 4 чисел) |
|
||||||
| data:create_texture(name: str) | создаёт и делится текстурой с рендерером |
|
| data:create_texture(name: str) | создаёт и делится текстурой с рендерером |
|
||||||
|
|
||||||
## Inventory (inventory)
|
## Рамка встраивания (iframe)
|
||||||
|
|
||||||
|
| Название | Тип | Чтение | Запись | Описание |
|
||||||
|
|----------|--------|--------|--------|----------------------------|
|
||||||
|
| src | string | да | да | id встраиваемого документа |
|
||||||
|
|
||||||
|
## Инвентарь (inventory)
|
||||||
|
|
||||||
Свойства:
|
Свойства:
|
||||||
|
|
||||||
| Название | Тип | Чтение | Запись | Описание |
|
| Название | Тип | Чтение | Запись | Описание |
|
||||||
| --------- | --- | ------ | ------ | ----------------------------------------- |
|
|-----------|-----|--------|--------|-------------------------------------------|
|
||||||
| inventory | int | да | да | id инвентаря, к которому привязан элемент |
|
| inventory | int | да | да | id инвентаря, к которому привязан элемент |
|
||||||
|
|
||||||
|
|||||||
@@ -155,6 +155,12 @@
|
|||||||
- `supplier` - lua функция-поставщик значения
|
- `supplier` - lua функция-поставщик значения
|
||||||
- `change-on-release` - Вызов функции-приемника (consumer) происходит только тогда, когда пользователь отпускает указатель. Тип: логический. По-умолчанию: false
|
- `change-on-release` - Вызов функции-приемника (consumer) происходит только тогда, когда пользователь отпускает указатель. Тип: логический. По-умолчанию: false
|
||||||
|
|
||||||
|
## Рамка встраивания - *iframe*
|
||||||
|
|
||||||
|
Контейнер для встраивания внешнего документа. Масштабирует содержимое под свой размер.
|
||||||
|
|
||||||
|
- `src` - id документа в формате `пак:имя` (`пак/layouts/имя.xml`)
|
||||||
|
|
||||||
# Элементы инвентаря
|
# Элементы инвентаря
|
||||||
|
|
||||||
## Инвентарь - *inventory*
|
## Инвентарь - *inventory*
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<splitbox id="editorRoot" orientation="horizontal" split-pos="0.3">
|
||||||
|
<splitbox split-pos="0.75">
|
||||||
|
<panel interval="2" color="0" padding="2">
|
||||||
|
<textbox pos="2" sub-consumer="filter_files"></textbox>
|
||||||
|
<panel id="filesList" color="#00000010" interval="6" padding="4"
|
||||||
|
size-func="-1,-45" pos="2,38">
|
||||||
|
<!-- content is generated in script -->
|
||||||
|
</panel>
|
||||||
|
</panel>
|
||||||
|
<panel id="problemsLog"
|
||||||
|
color="#00000010"
|
||||||
|
padding="5,15,5,15">
|
||||||
|
<label margin="0,0,0,5">@Problems</label>
|
||||||
|
</panel>
|
||||||
|
</splitbox>
|
||||||
|
<splitbox id="editorContainer" split-pos="0.8">
|
||||||
|
<container color="#00000080"
|
||||||
|
onclick="document.editor.focused = true document.editor.caret = -1">
|
||||||
|
<container size-func="-1,30" color="#00000020">
|
||||||
|
<image id="lockIcon" src="gui/lock" tooltip="@Read only"
|
||||||
|
interactive="true" onclick="unlock_access()"
|
||||||
|
color="#FFFFFF80" size="16" pos="4,6"
|
||||||
|
hover-color="#1080FF"></image>
|
||||||
|
<panel orientation="horizontal" gravity="top-right"
|
||||||
|
size="60,16" padding="8" interval="8" color="0">
|
||||||
|
<image id="saveIcon" src="gui/save" tooltip="@Save"
|
||||||
|
enabled="false" interactive="true"
|
||||||
|
hover-color="#1080FF"
|
||||||
|
onclick="save_current_file()"
|
||||||
|
color="#FFFFFF80" size="16"></image>
|
||||||
|
<image id="infoIcon" src="gui/info" tooltip="@editor.info.tooltip"
|
||||||
|
enabled="true" interactive="true"
|
||||||
|
color="#FFFFFF80" size="16"></image>
|
||||||
|
<image id="syncIcon" src="gui/play" tooltip="@Run"
|
||||||
|
enabled="true" interactive="true"
|
||||||
|
hover-color="#1080FF"
|
||||||
|
onclick="run_current_file()"
|
||||||
|
color="#FFFFFF80" size="16"></image>
|
||||||
|
</panel>
|
||||||
|
<label id="title" pos="26,8"></label>
|
||||||
|
</container>
|
||||||
|
<textbox
|
||||||
|
id='editor'
|
||||||
|
pos='0,30'
|
||||||
|
color='0'
|
||||||
|
autoresize='true'
|
||||||
|
margin='0'
|
||||||
|
padding='5'
|
||||||
|
multiline='true'
|
||||||
|
line-numbers='true'
|
||||||
|
oncontrolkey='on_control_combination'
|
||||||
|
size-func="-1,40"
|
||||||
|
text-wrap='false'
|
||||||
|
scroll-step='50'
|
||||||
|
></textbox>
|
||||||
|
</container>
|
||||||
|
<splitbox orientation="horizontal" split-pos="0.4">
|
||||||
|
<panel id="traceback" padding="4" color="#000000A0">
|
||||||
|
</panel>
|
||||||
|
<textbox id="output"
|
||||||
|
padding="4"
|
||||||
|
editable="false"
|
||||||
|
markup="md"
|
||||||
|
multiline="true"
|
||||||
|
color="#000000A0">
|
||||||
|
</textbox>
|
||||||
|
</splitbox>
|
||||||
|
</splitbox>
|
||||||
|
</splitbox>
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
local writeables = {}
|
||||||
|
local registry = require "core:internal/scripts_registry"
|
||||||
|
local filenames
|
||||||
|
|
||||||
|
local current_file = {
|
||||||
|
filename = "",
|
||||||
|
mutable = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
local warnings_all = {}
|
||||||
|
local errors_all = {}
|
||||||
|
|
||||||
|
local warning_id = 0
|
||||||
|
local error_id = 0
|
||||||
|
|
||||||
|
events.on("core:warning", function (wtype, text, traceback)
|
||||||
|
local full = wtype..": "..text
|
||||||
|
if table.has(warnings_all, full) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local encoded = base64.encode(bjson.tobytes({frames=traceback}))
|
||||||
|
document.problemsLog:add(gui.template("problem", {
|
||||||
|
type="warning",
|
||||||
|
text=full,
|
||||||
|
traceback=encoded,
|
||||||
|
id=tostring(warning_id)
|
||||||
|
}))
|
||||||
|
warning_id = warning_id + 1
|
||||||
|
table.insert(warnings_all, full)
|
||||||
|
end)
|
||||||
|
|
||||||
|
events.on("core:error", function (msg, traceback)
|
||||||
|
local _, endindex = string.find(msg, ": ")
|
||||||
|
local full = ""
|
||||||
|
for i,frame in ipairs(traceback) do
|
||||||
|
full = full..frame.source..tostring(frame.currentline)
|
||||||
|
end
|
||||||
|
if table.has(errors_all, full) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local encoded = base64.encode(bjson.tobytes({frames=traceback}))
|
||||||
|
document.problemsLog:add(gui.template("problem", {
|
||||||
|
type="error",
|
||||||
|
text=msg:sub(endindex),
|
||||||
|
traceback=encoded,
|
||||||
|
id=tostring(error_id)
|
||||||
|
}))
|
||||||
|
error_id = error_id + 1
|
||||||
|
table.insert(errors_all, full)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function find_mutable(filename)
|
||||||
|
local packid = file.prefix(filename)
|
||||||
|
if packid == "core" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local saved = writeables[packid]
|
||||||
|
if saved then
|
||||||
|
return saved..":"..file.path(filename)
|
||||||
|
end
|
||||||
|
local packinfo = pack.get_info(packid)
|
||||||
|
if not packinfo then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local path = packinfo.path
|
||||||
|
if file.is_writeable(path) then
|
||||||
|
return file.join(path, file.path(filename))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function refresh_file_title()
|
||||||
|
if current_file.filename == "" then
|
||||||
|
document.title.text = ""
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local edited = document.editor.edited
|
||||||
|
current_file.modified = edited
|
||||||
|
document.saveIcon.enabled = edited
|
||||||
|
document.title.text = gui.str('File')..' - '..current_file.filename
|
||||||
|
..(edited and ' *' or '')
|
||||||
|
end
|
||||||
|
|
||||||
|
function filter_files(text)
|
||||||
|
local filtered = {}
|
||||||
|
for _, filename in ipairs(filenames) do
|
||||||
|
if filename:find(text) then
|
||||||
|
table.insert(filtered, filename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
build_files_list(filtered, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_control_combination(keycode)
|
||||||
|
if keycode == input.keycode("s") then
|
||||||
|
save_current_file()
|
||||||
|
elseif keycode == input.keycode("r") then
|
||||||
|
run_current_file()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function unlock_access()
|
||||||
|
if current_file.filename == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
pack.request_writeable(file.prefix(current_file.filename),
|
||||||
|
function(token)
|
||||||
|
writeables[file.prefix(current_file.filename)] = token
|
||||||
|
current_file.mutable = token..":"..file.path(current_file.filename)
|
||||||
|
open_file_in_editor(current_file.filename, 0, current_file.mutable)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function run_current_file()
|
||||||
|
if not current_file.filename then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local chunk, err = loadstring(document.editor.text, current_file.filename)
|
||||||
|
clear_output()
|
||||||
|
if not chunk then
|
||||||
|
local line, message = err:match(".*:(%d*): (.*)")
|
||||||
|
document.output:paste(
|
||||||
|
string.format(
|
||||||
|
"\n[#FF3030]%s: %s[#FFFFFF]",
|
||||||
|
gui.str("Error at line %{0}"):gsub("%%{0}", line), message)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local info = registry.get_info(current_file.filename)
|
||||||
|
local script_type = info and info.type or "file"
|
||||||
|
local unit = info and info.unit
|
||||||
|
save_current_file()
|
||||||
|
|
||||||
|
local func = function()
|
||||||
|
local stack_size = debug.count_frames()
|
||||||
|
xpcall(chunk, function(msg) __vc__error(msg, 1, 1, stack_size) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local funcs = {
|
||||||
|
block = block.reload_script,
|
||||||
|
item = item.reload_script,
|
||||||
|
world = world.reload_script,
|
||||||
|
hud = hud.reload_script,
|
||||||
|
component = entities.reload_component,
|
||||||
|
module = reload_module,
|
||||||
|
}
|
||||||
|
func = funcs[script_type] or func
|
||||||
|
local output = core.capture_output(function() func(unit) end)
|
||||||
|
document.output:paste(string.format("\n%s", output))
|
||||||
|
end
|
||||||
|
|
||||||
|
function clear_traceback()
|
||||||
|
local tb_list = document.traceback
|
||||||
|
tb_list:clear()
|
||||||
|
tb_list:add("<label enabled='false' margin='2'>@devtools.traceback</label>")
|
||||||
|
end
|
||||||
|
|
||||||
|
function clear_output()
|
||||||
|
local output = document.output
|
||||||
|
output.text = ""
|
||||||
|
output:paste("[#FFFFFF80]"..gui.str("devtools.output").."[#FFFFFF]")
|
||||||
|
end
|
||||||
|
|
||||||
|
events.on("core:open_traceback", function(traceback_b64)
|
||||||
|
local traceback = bjson.frombytes(base64.decode(traceback_b64))
|
||||||
|
modes:set('debug')
|
||||||
|
|
||||||
|
clear_traceback()
|
||||||
|
|
||||||
|
local tb_list = document.traceback
|
||||||
|
local srcsize = tb_list.size
|
||||||
|
for _, frame in ipairs(traceback.frames) do
|
||||||
|
local callback = ""
|
||||||
|
local framestr = ""
|
||||||
|
if frame.what == "C" then
|
||||||
|
framestr = "C/C++ "
|
||||||
|
else
|
||||||
|
framestr = frame.source..":"..tostring(frame.currentline).." "
|
||||||
|
if file.exists(frame.source) then
|
||||||
|
callback = string.format(
|
||||||
|
"open_file_in_editor('%s', %s)",
|
||||||
|
frame.source, frame.currentline-1
|
||||||
|
)
|
||||||
|
else
|
||||||
|
callback = "document.editor.text = 'Could not open source file'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if frame.name then
|
||||||
|
framestr = framestr.."("..tostring(frame.name)..")"
|
||||||
|
end
|
||||||
|
local color = "#FFFFFF"
|
||||||
|
tb_list:add(gui.template("stack_frame", {
|
||||||
|
location=framestr,
|
||||||
|
color=color,
|
||||||
|
callback=callback,
|
||||||
|
enabled=file.exists(frame.source)
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
tb_list.size = srcsize
|
||||||
|
end)
|
||||||
|
|
||||||
|
function save_current_file()
|
||||||
|
if not current_file.mutable then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
file.write(current_file.mutable, document.editor.text)
|
||||||
|
current_file.modified = false
|
||||||
|
document.saveIcon.enabled = false
|
||||||
|
document.title.text = gui.str('File')..' - '..current_file.filename
|
||||||
|
document.editor.edited = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function open_file_in_editor(filename, line, mutable)
|
||||||
|
local editor = document.editor
|
||||||
|
local source = file.read(filename):gsub('\t', ' ')
|
||||||
|
editor.scroll = 0
|
||||||
|
editor.text = source
|
||||||
|
editor.focused = true
|
||||||
|
editor.syntax = file.ext(filename)
|
||||||
|
if line then
|
||||||
|
time.post_runnable(function()
|
||||||
|
editor.caret = editor:linePos(line)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
document.title.text = gui.str('File') .. ' - ' .. filename
|
||||||
|
current_file.filename = filename
|
||||||
|
current_file.mutable = mutable or find_mutable(filename)
|
||||||
|
document.lockIcon.visible = current_file.mutable == nil
|
||||||
|
document.editor.editable = current_file.mutable ~= nil
|
||||||
|
document.saveIcon.enabled = current_file.modified
|
||||||
|
end
|
||||||
|
|
||||||
|
function build_files_list(filenames, selected)
|
||||||
|
local files_list = document.filesList
|
||||||
|
files_list.scroll = 0
|
||||||
|
files_list:clear()
|
||||||
|
|
||||||
|
for _, actual_filename in ipairs(filenames) do
|
||||||
|
local filename = actual_filename
|
||||||
|
if selected then
|
||||||
|
filename = filename:gsub(selected, "**"..selected.."**")
|
||||||
|
end
|
||||||
|
local parent = file.parent(filename)
|
||||||
|
local info = registry.get_info(actual_filename)
|
||||||
|
local icon = "file"
|
||||||
|
if info then
|
||||||
|
icon = info.type == "component" and "entity" or info.type
|
||||||
|
end
|
||||||
|
files_list:add(gui.template("script_file", {
|
||||||
|
path = parent .. (parent[#parent] == ':' and '' or '/'),
|
||||||
|
name = file.name(filename),
|
||||||
|
icon = icon,
|
||||||
|
unit = info and info.unit or '',
|
||||||
|
filename = actual_filename
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_open(mode)
|
||||||
|
local files_list = document.filesList
|
||||||
|
|
||||||
|
filenames = registry.filenames
|
||||||
|
table.sort(filenames)
|
||||||
|
build_files_list(filenames)
|
||||||
|
|
||||||
|
document.editorContainer:setInterval(200, refresh_file_title)
|
||||||
|
|
||||||
|
clear_traceback()
|
||||||
|
clear_output()
|
||||||
|
end
|
||||||
+3
-70
@@ -22,76 +22,9 @@
|
|||||||
markup="md"
|
markup="md"
|
||||||
></textbox>
|
></textbox>
|
||||||
</container>
|
</container>
|
||||||
<splitbox id="editorRoot" pos="0,30" size-func="-1,gui.get_viewport()[2]-30"
|
<iframe id="editorRoot" pos="0,30" size-func="-1,gui.get_viewport()[2]-30"
|
||||||
orientation="horizontal" split-pos="0.3">
|
src="core:code_editor">
|
||||||
<splitbox split-pos="0.75">
|
</iframe>
|
||||||
<panel interval="2" color="0" padding="2">
|
|
||||||
<textbox pos="2" sub-consumer="filter_files"></textbox>
|
|
||||||
<panel id="filesList" color="#00000010" interval="6" padding="4"
|
|
||||||
size-func="-1,-45" pos="2,38">
|
|
||||||
<!-- content is generated in script -->
|
|
||||||
</panel>
|
|
||||||
</panel>
|
|
||||||
<panel id="problemsLog"
|
|
||||||
color="#00000010"
|
|
||||||
padding="5,15,5,15">
|
|
||||||
<label margin="0,0,0,5">@Problems</label>
|
|
||||||
</panel>
|
|
||||||
</splitbox>
|
|
||||||
<splitbox id="editorContainer" split-pos="0.8">
|
|
||||||
<container color="#00000080"
|
|
||||||
onclick="document.editor.focused = true document.editor.caret = -1">
|
|
||||||
<container size-func="-1,30" color="#00000020">
|
|
||||||
<image id="lockIcon" src="gui/lock" tooltip="@Read only"
|
|
||||||
interactive="true" onclick="unlock_access()"
|
|
||||||
color="#FFFFFF80" size="16" pos="4,6"
|
|
||||||
hover-color="#1080FF"></image>
|
|
||||||
<panel orientation="horizontal" gravity="top-right"
|
|
||||||
size="60,16" padding="8" interval="8" color="0">
|
|
||||||
<image id="saveIcon" src="gui/save" tooltip="@Save"
|
|
||||||
enabled="false" interactive="true"
|
|
||||||
hover-color="#1080FF"
|
|
||||||
onclick="save_current_file()"
|
|
||||||
color="#FFFFFF80" size="16"></image>
|
|
||||||
<image id="infoIcon" src="gui/info" tooltip="@editor.info.tooltip"
|
|
||||||
enabled="true" interactive="true"
|
|
||||||
color="#FFFFFF80" size="16"></image>
|
|
||||||
<image id="syncIcon" src="gui/play" tooltip="@Run"
|
|
||||||
enabled="true" interactive="true"
|
|
||||||
hover-color="#1080FF"
|
|
||||||
onclick="run_current_file()"
|
|
||||||
color="#FFFFFF80" size="16"></image>
|
|
||||||
</panel>
|
|
||||||
<label id="title" pos="26,8"></label>
|
|
||||||
</container>
|
|
||||||
<textbox
|
|
||||||
id='editor'
|
|
||||||
pos='0,30'
|
|
||||||
color='0'
|
|
||||||
autoresize='true'
|
|
||||||
margin='0'
|
|
||||||
padding='5'
|
|
||||||
multiline='true'
|
|
||||||
line-numbers='true'
|
|
||||||
oncontrolkey='on_control_combination'
|
|
||||||
size-func="-1,40"
|
|
||||||
text-wrap='false'
|
|
||||||
scroll-step='50'
|
|
||||||
></textbox>
|
|
||||||
</container>
|
|
||||||
<splitbox orientation="horizontal" split-pos="0.4">
|
|
||||||
<panel id="traceback" padding="4" color="#000000A0">
|
|
||||||
</panel>
|
|
||||||
<textbox id="output"
|
|
||||||
padding="4"
|
|
||||||
editable="false"
|
|
||||||
markup="md"
|
|
||||||
multiline="true"
|
|
||||||
color="#000000A0">
|
|
||||||
</textbox>
|
|
||||||
</splitbox>
|
|
||||||
</splitbox>
|
|
||||||
</splitbox>
|
|
||||||
<textbox id='prompt'
|
<textbox id='prompt'
|
||||||
consumer='submit'
|
consumer='submit'
|
||||||
margin='0'
|
margin='0'
|
||||||
|
|||||||
@@ -3,264 +3,6 @@ console_mode = "console"
|
|||||||
history = session.get_entry("commands_history")
|
history = session.get_entry("commands_history")
|
||||||
history_pointer = #history
|
history_pointer = #history
|
||||||
|
|
||||||
local warnings_all = {}
|
|
||||||
local errors_all = {}
|
|
||||||
|
|
||||||
local warning_id = 0
|
|
||||||
local error_id = 0
|
|
||||||
|
|
||||||
local writeables = {}
|
|
||||||
local registry = require "core:internal/scripts_registry"
|
|
||||||
local filenames
|
|
||||||
|
|
||||||
local current_file = {
|
|
||||||
filename = "",
|
|
||||||
mutable = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
events.on("core:warning", function (wtype, text, traceback)
|
|
||||||
local full = wtype..": "..text
|
|
||||||
if table.has(warnings_all, full) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local encoded = base64.encode(bjson.tobytes({frames=traceback}))
|
|
||||||
document.problemsLog:add(gui.template("problem", {
|
|
||||||
type="warning",
|
|
||||||
text=full,
|
|
||||||
traceback=encoded,
|
|
||||||
id=tostring(warning_id)
|
|
||||||
}))
|
|
||||||
warning_id = warning_id + 1
|
|
||||||
table.insert(warnings_all, full)
|
|
||||||
end)
|
|
||||||
|
|
||||||
events.on("core:error", function (msg, traceback)
|
|
||||||
local _, endindex = string.find(msg, ": ")
|
|
||||||
local full = ""
|
|
||||||
for i,frame in ipairs(traceback) do
|
|
||||||
full = full..frame.source..tostring(frame.currentline)
|
|
||||||
end
|
|
||||||
if table.has(errors_all, full) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local encoded = base64.encode(bjson.tobytes({frames=traceback}))
|
|
||||||
document.problemsLog:add(gui.template("problem", {
|
|
||||||
type="error",
|
|
||||||
text=msg:sub(endindex),
|
|
||||||
traceback=encoded,
|
|
||||||
id=tostring(error_id)
|
|
||||||
}))
|
|
||||||
error_id = error_id + 1
|
|
||||||
table.insert(errors_all, full)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local function find_mutable(filename)
|
|
||||||
local packid = file.prefix(filename)
|
|
||||||
if packid == "core" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local saved = writeables[packid]
|
|
||||||
if saved then
|
|
||||||
return saved..":"..file.path(filename)
|
|
||||||
end
|
|
||||||
local packinfo = pack.get_info(packid)
|
|
||||||
if not packinfo then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local path = packinfo.path
|
|
||||||
if file.is_writeable(path) then
|
|
||||||
return file.join(path, file.path(filename))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function refresh_file_title()
|
|
||||||
if current_file.filename == "" then
|
|
||||||
document.title.text = ""
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local edited = document.editor.edited
|
|
||||||
current_file.modified = edited
|
|
||||||
document.saveIcon.enabled = edited
|
|
||||||
document.title.text = gui.str('File')..' - '..current_file.filename
|
|
||||||
..(edited and ' *' or '')
|
|
||||||
end
|
|
||||||
|
|
||||||
function build_files_list(filenames, selected)
|
|
||||||
local files_list = document.filesList
|
|
||||||
files_list.scroll = 0
|
|
||||||
files_list:clear()
|
|
||||||
|
|
||||||
for _, actual_filename in ipairs(filenames) do
|
|
||||||
local filename = actual_filename
|
|
||||||
if selected then
|
|
||||||
filename = filename:gsub(selected, "**"..selected.."**")
|
|
||||||
end
|
|
||||||
local parent = file.parent(filename)
|
|
||||||
local info = registry.get_info(actual_filename)
|
|
||||||
local icon = "file"
|
|
||||||
if info then
|
|
||||||
icon = info.type == "component" and "entity" or info.type
|
|
||||||
end
|
|
||||||
files_list:add(gui.template("script_file", {
|
|
||||||
path = parent .. (parent[#parent] == ':' and '' or '/'),
|
|
||||||
name = file.name(filename),
|
|
||||||
icon = icon,
|
|
||||||
unit = info and info.unit or '',
|
|
||||||
filename = actual_filename
|
|
||||||
}))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function filter_files(text)
|
|
||||||
local filtered = {}
|
|
||||||
for _, filename in ipairs(filenames) do
|
|
||||||
if filename:find(text) then
|
|
||||||
table.insert(filtered, filename)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
build_files_list(filtered, text)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_control_combination(keycode)
|
|
||||||
if keycode == input.keycode("s") then
|
|
||||||
save_current_file()
|
|
||||||
elseif keycode == input.keycode("r") then
|
|
||||||
run_current_file()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function unlock_access()
|
|
||||||
if current_file.filename == "" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
pack.request_writeable(file.prefix(current_file.filename),
|
|
||||||
function(token)
|
|
||||||
writeables[file.prefix(current_file.filename)] = token
|
|
||||||
current_file.mutable = token..":"..file.path(current_file.filename)
|
|
||||||
open_file_in_editor(current_file.filename, 0, current_file.mutable)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function run_current_file()
|
|
||||||
if not current_file.filename then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local chunk, err = loadstring(document.editor.text, current_file.filename)
|
|
||||||
clear_output()
|
|
||||||
if not chunk then
|
|
||||||
local line, message = err:match(".*:(%d*): (.*)")
|
|
||||||
document.output:paste(
|
|
||||||
string.format(
|
|
||||||
"\n[#FF3030]%s: %s[#FFFFFF]",
|
|
||||||
gui.str("Error at line %{0}"):gsub("%%{0}", line), message)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local info = registry.get_info(current_file.filename)
|
|
||||||
local script_type = info and info.type or "file"
|
|
||||||
local unit = info and info.unit
|
|
||||||
save_current_file()
|
|
||||||
|
|
||||||
local func = function()
|
|
||||||
local stack_size = debug.count_frames()
|
|
||||||
xpcall(chunk, function(msg) __vc__error(msg, 1, 1, stack_size) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local funcs = {
|
|
||||||
block = block.reload_script,
|
|
||||||
item = item.reload_script,
|
|
||||||
world = world.reload_script,
|
|
||||||
hud = hud.reload_script,
|
|
||||||
component = entities.reload_component,
|
|
||||||
module = reload_module,
|
|
||||||
}
|
|
||||||
func = funcs[script_type] or func
|
|
||||||
local output = core.capture_output(function() func(unit) end)
|
|
||||||
document.output:paste(string.format("\n%s", output))
|
|
||||||
end
|
|
||||||
|
|
||||||
function save_current_file()
|
|
||||||
if not current_file.mutable then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
file.write(current_file.mutable, document.editor.text)
|
|
||||||
current_file.modified = false
|
|
||||||
document.saveIcon.enabled = false
|
|
||||||
document.title.text = gui.str('File')..' - '..current_file.filename
|
|
||||||
document.editor.edited = false
|
|
||||||
end
|
|
||||||
|
|
||||||
function open_file_in_editor(filename, line, mutable)
|
|
||||||
local editor = document.editor
|
|
||||||
local source = file.read(filename):gsub('\t', ' ')
|
|
||||||
editor.scroll = 0
|
|
||||||
editor.text = source
|
|
||||||
editor.focused = true
|
|
||||||
editor.syntax = file.ext(filename)
|
|
||||||
if line then
|
|
||||||
time.post_runnable(function()
|
|
||||||
editor.caret = editor:linePos(line)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
document.title.text = gui.str('File') .. ' - ' .. filename
|
|
||||||
current_file.filename = filename
|
|
||||||
current_file.mutable = mutable or find_mutable(filename)
|
|
||||||
document.lockIcon.visible = current_file.mutable == nil
|
|
||||||
document.editor.editable = current_file.mutable ~= nil
|
|
||||||
document.saveIcon.enabled = current_file.modified
|
|
||||||
end
|
|
||||||
|
|
||||||
function clear_traceback()
|
|
||||||
local tb_list = document.traceback
|
|
||||||
tb_list:clear()
|
|
||||||
tb_list:add("<label enabled='false' margin='2'>@devtools.traceback</label>")
|
|
||||||
end
|
|
||||||
|
|
||||||
function clear_output()
|
|
||||||
local output = document.output
|
|
||||||
output.text = ""
|
|
||||||
output:paste("[#FFFFFF80]"..gui.str("devtools.output").."[#FFFFFF]")
|
|
||||||
end
|
|
||||||
|
|
||||||
events.on("core:open_traceback", function(traceback_b64)
|
|
||||||
local traceback = bjson.frombytes(base64.decode(traceback_b64))
|
|
||||||
modes:set('debug')
|
|
||||||
|
|
||||||
clear_traceback()
|
|
||||||
|
|
||||||
local tb_list = document.traceback
|
|
||||||
local srcsize = tb_list.size
|
|
||||||
for _, frame in ipairs(traceback.frames) do
|
|
||||||
local callback = ""
|
|
||||||
local framestr = ""
|
|
||||||
if frame.what == "C" then
|
|
||||||
framestr = "C/C++ "
|
|
||||||
else
|
|
||||||
framestr = frame.source..":"..tostring(frame.currentline).." "
|
|
||||||
if file.exists(frame.source) then
|
|
||||||
callback = string.format(
|
|
||||||
"open_file_in_editor('%s', %s)",
|
|
||||||
frame.source, frame.currentline-1
|
|
||||||
)
|
|
||||||
else
|
|
||||||
callback = "document.editor.text = 'Could not open source file'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if frame.name then
|
|
||||||
framestr = framestr.."("..tostring(frame.name)..")"
|
|
||||||
end
|
|
||||||
local color = "#FFFFFF"
|
|
||||||
tb_list:add(gui.template("stack_frame", {
|
|
||||||
location=framestr,
|
|
||||||
color=color,
|
|
||||||
callback=callback,
|
|
||||||
enabled=file.exists(frame.source)
|
|
||||||
}))
|
|
||||||
end
|
|
||||||
tb_list.size = srcsize
|
|
||||||
end)
|
|
||||||
|
|
||||||
function setup_variables()
|
function setup_variables()
|
||||||
local pid = hud.get_player()
|
local pid = hud.get_player()
|
||||||
local x,y,z = player.get_pos(pid)
|
local x,y,z = player.get_pos(pid)
|
||||||
@@ -351,9 +93,7 @@ end
|
|||||||
function set_mode(mode)
|
function set_mode(mode)
|
||||||
local show_prompt = mode == 'chat' or mode == 'console'
|
local show_prompt = mode == 'chat' or mode == 'console'
|
||||||
|
|
||||||
document.lockIcon.visible = false
|
|
||||||
document.editorRoot.visible = mode == 'debug'
|
document.editorRoot.visible = mode == 'debug'
|
||||||
document.editorContainer.visible = mode == 'debug'
|
|
||||||
document.logContainer.visible = mode ~= 'debug'
|
document.logContainer.visible = mode ~= 'debug'
|
||||||
|
|
||||||
if mode == 'debug' then
|
if mode == 'debug' then
|
||||||
@@ -378,17 +118,6 @@ function on_open(mode)
|
|||||||
}, function (mode)
|
}, function (mode)
|
||||||
set_mode(mode)
|
set_mode(mode)
|
||||||
end, mode or "console")
|
end, mode or "console")
|
||||||
|
|
||||||
local files_list = document.filesList
|
|
||||||
|
|
||||||
filenames = registry.filenames
|
|
||||||
table.sort(filenames)
|
|
||||||
build_files_list(filenames)
|
|
||||||
|
|
||||||
document.editorContainer:setInterval(200, refresh_file_title)
|
|
||||||
|
|
||||||
clear_traceback()
|
|
||||||
clear_output()
|
|
||||||
elseif mode then
|
elseif mode then
|
||||||
modes:set(mode)
|
modes:set(mode)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
|||||||
auto size = node->getSize();
|
auto size = node->getSize();
|
||||||
|
|
||||||
batch2D->setColor(0, 255, 255);
|
batch2D->setColor(0, 255, 255);
|
||||||
batch2D->lineRect(parentPos.x, parentPos.y, size.x-1, size.y-1);
|
batch2D->lineRect(parentPos.x+1, parentPos.y, size.x-2, size.y-1);
|
||||||
|
|
||||||
node = node->getParent();
|
node = node->getParent();
|
||||||
}
|
}
|
||||||
@@ -368,3 +368,7 @@ Window& GUI::getWindow() {
|
|||||||
devtools::Editor& GUI::getEditor() {
|
devtools::Editor& GUI::getEditor() {
|
||||||
return engine.getEditor();
|
return engine.getEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Engine& GUI::getEngine() {
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|||||||
@@ -164,5 +164,6 @@ namespace gui {
|
|||||||
Input& getInput();
|
Input& getInput();
|
||||||
Window& getWindow();
|
Window& getWindow();
|
||||||
devtools::Editor& getEditor();
|
devtools::Editor& getEditor();
|
||||||
|
Engine& getEngine();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include "InlineFrame.hpp"
|
||||||
|
#include "frontend/UiDocument.hpp"
|
||||||
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
|
#include "engine/Engine.hpp"
|
||||||
|
#include "../GUI.hpp"
|
||||||
|
|
||||||
|
using namespace gui;
|
||||||
|
|
||||||
|
InlineFrame::InlineFrame(GUI& gui) : Container(gui, glm::vec2(1)) {}
|
||||||
|
InlineFrame::~InlineFrame() = default;
|
||||||
|
|
||||||
|
void InlineFrame::setSrc(const std::string& src) {
|
||||||
|
this->src = src;
|
||||||
|
if (document) {
|
||||||
|
scripting::on_ui_close(document.get(), nullptr);
|
||||||
|
document = nullptr;
|
||||||
|
root = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineFrame::setDocument(const std::shared_ptr<UiDocument>& document) {
|
||||||
|
clear();
|
||||||
|
if (document == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->document = document;
|
||||||
|
this->root = document->getRoot();
|
||||||
|
add(root);
|
||||||
|
|
||||||
|
root->setSize(size);
|
||||||
|
|
||||||
|
gui.postRunnable([this]() {
|
||||||
|
scripting::on_ui_open(this->document.get(), {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineFrame::act(float delta) {
|
||||||
|
if (document || src.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& assets = *gui.getEngine().getAssets();
|
||||||
|
setDocument(assets.getShared<UiDocument>(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineFrame::setSize(glm::vec2 size) {
|
||||||
|
Container::setSize(size);
|
||||||
|
if (root) {
|
||||||
|
root->setSize(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& InlineFrame::getSrc() const {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Container.hpp"
|
||||||
|
|
||||||
|
class UiDocument;
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
class InlineFrame : public Container {
|
||||||
|
public:
|
||||||
|
explicit InlineFrame(GUI& gui);
|
||||||
|
virtual ~InlineFrame();
|
||||||
|
|
||||||
|
void setSrc(const std::string& src);
|
||||||
|
void setDocument(const std::shared_ptr<UiDocument>& document);
|
||||||
|
|
||||||
|
void act(float delta) override;
|
||||||
|
void setSize(glm::vec2 size) override;
|
||||||
|
|
||||||
|
const std::string& getSrc() const;
|
||||||
|
private:
|
||||||
|
std::string src;
|
||||||
|
std::shared_ptr<UiDocument> document;
|
||||||
|
std::shared_ptr<UINode> root;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "elements/SplitBox.hpp"
|
#include "elements/SplitBox.hpp"
|
||||||
#include "elements/TrackBar.hpp"
|
#include "elements/TrackBar.hpp"
|
||||||
#include "elements/Image.hpp"
|
#include "elements/Image.hpp"
|
||||||
|
#include "elements/InlineFrame.hpp"
|
||||||
#include "elements/InputBindBox.hpp"
|
#include "elements/InputBindBox.hpp"
|
||||||
#include "elements/InventoryView.hpp"
|
#include "elements/InventoryView.hpp"
|
||||||
#include "elements/Menu.hpp"
|
#include "elements/Menu.hpp"
|
||||||
@@ -292,7 +293,7 @@ static std::wstring parse_inner_text(
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::shared_ptr<UINode> readLabel(
|
static std::shared_ptr<UINode> read_label(
|
||||||
const UiXmlReader& reader, const xml::xmlelement& element
|
const UiXmlReader& reader, const xml::xmlelement& element
|
||||||
) {
|
) {
|
||||||
std::wstring text = parse_inner_text(element, reader.getContext());
|
std::wstring text = parse_inner_text(element, reader.getContext());
|
||||||
@@ -739,11 +740,24 @@ static std::shared_ptr<UINode> read_page_box(
|
|||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<UINode> read_iframe(
|
||||||
|
UiXmlReader& reader, const xml::xmlelement& element
|
||||||
|
) {
|
||||||
|
auto& gui = reader.getGUI();
|
||||||
|
auto iframe = std::make_shared<InlineFrame>(gui);
|
||||||
|
read_container_impl(reader, element, *iframe);
|
||||||
|
|
||||||
|
std::string src = element.attr("src", "").getText();
|
||||||
|
iframe->setSrc(src);
|
||||||
|
return iframe;
|
||||||
|
}
|
||||||
|
|
||||||
UiXmlReader::UiXmlReader(gui::GUI& gui, const scriptenv& env) : gui(gui), env(env) {
|
UiXmlReader::UiXmlReader(gui::GUI& gui, const scriptenv& env) : gui(gui), env(env) {
|
||||||
contextStack.emplace("");
|
contextStack.emplace("");
|
||||||
add("image", read_image);
|
add("image", read_image);
|
||||||
add("canvas", read_canvas);
|
add("canvas", read_canvas);
|
||||||
add("label", readLabel);
|
add("iframe", read_iframe);
|
||||||
|
add("label", read_label);
|
||||||
add("panel", read_panel);
|
add("panel", read_panel);
|
||||||
add("button", read_button);
|
add("button", read_button);
|
||||||
add("textbox", read_text_box);
|
add("textbox", read_text_box);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "graphics/ui/elements/Panel.hpp"
|
#include "graphics/ui/elements/Panel.hpp"
|
||||||
#include "graphics/ui/elements/TextBox.hpp"
|
#include "graphics/ui/elements/TextBox.hpp"
|
||||||
#include "graphics/ui/elements/TrackBar.hpp"
|
#include "graphics/ui/elements/TrackBar.hpp"
|
||||||
|
#include "graphics/ui/elements/InlineFrame.hpp"
|
||||||
#include "graphics/ui/gui_util.hpp"
|
#include "graphics/ui/gui_util.hpp"
|
||||||
#include "graphics/ui/markdown.hpp"
|
#include "graphics/ui/markdown.hpp"
|
||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
@@ -330,6 +331,8 @@ static int p_get_markup(UINode* node, lua::State* L) {
|
|||||||
static int p_get_src(UINode* node, lua::State* L) {
|
static int p_get_src(UINode* node, lua::State* L) {
|
||||||
if (auto image = dynamic_cast<Image*>(node)) {
|
if (auto image = dynamic_cast<Image*>(node)) {
|
||||||
return lua::pushstring(L, image->getTexture());
|
return lua::pushstring(L, image->getTexture());
|
||||||
|
} else if (auto iframe = dynamic_cast<InlineFrame*>(node)) {
|
||||||
|
return lua::pushstring(L, iframe->getSrc());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -644,6 +647,8 @@ static void p_set_markup(UINode* node, lua::State* L, int idx) {
|
|||||||
static void p_set_src(UINode* node, lua::State* L, int idx) {
|
static void p_set_src(UINode* node, lua::State* L, int idx) {
|
||||||
if (auto image = dynamic_cast<Image*>(node)) {
|
if (auto image = dynamic_cast<Image*>(node)) {
|
||||||
image->setTexture(lua::require_string(L, idx));
|
image->setTexture(lua::require_string(L, idx));
|
||||||
|
} else if (auto iframe = dynamic_cast<InlineFrame*>(node)) {
|
||||||
|
iframe->setSrc(lua::require_string(L, idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void p_set_value(UINode* node, lua::State* L, int idx) {
|
static void p_set_value(UINode* node, lua::State* L, int idx) {
|
||||||
@@ -704,10 +709,10 @@ static void p_set_inventory(UINode* node, lua::State* L, int idx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void p_set_focused(
|
static void p_set_focused(
|
||||||
const std::shared_ptr<UINode>& node, lua::State* L, int idx
|
UINode* node, lua::State* L, int idx
|
||||||
) {
|
) {
|
||||||
if (lua::toboolean(L, idx) && !node->isFocused()) {
|
if (lua::toboolean(L, idx) && !node->isFocused()) {
|
||||||
engine->getGUI().setFocus(node);
|
engine->getGUI().setFocus(node->shared_from_this());
|
||||||
} else if (node->isFocused()) {
|
} else if (node->isFocused()) {
|
||||||
node->defocus();
|
node->defocus();
|
||||||
}
|
}
|
||||||
@@ -771,21 +776,12 @@ static int l_gui_setattr(lua::State* L) {
|
|||||||
{"page", p_set_page},
|
{"page", p_set_page},
|
||||||
{"inventory", p_set_inventory},
|
{"inventory", p_set_inventory},
|
||||||
{"cursor", p_set_cursor},
|
{"cursor", p_set_cursor},
|
||||||
|
{"focused", p_set_focused},
|
||||||
};
|
};
|
||||||
auto func = setters.find(attr);
|
auto func = setters.find(attr);
|
||||||
if (func != setters.end()) {
|
if (func != setters.end()) {
|
||||||
func->second(node.get(), L, 4);
|
func->second(node.get(), L, 4);
|
||||||
}
|
}
|
||||||
static const std::unordered_map<
|
|
||||||
std::string_view,
|
|
||||||
std::function<void(std::shared_ptr<UINode>, lua::State*, int)>>
|
|
||||||
setters2 {
|
|
||||||
{"focused", p_set_focused},
|
|
||||||
};
|
|
||||||
auto func2 = setters2.find(attr);
|
|
||||||
if (func2 != setters2.end()) {
|
|
||||||
func2->second(node, L, 4);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user