commit
6736613dc2
@ -27,4 +27,7 @@ utf8.upper(text: str) -> str
|
||||
|
||||
-- Converts a string to lowercase
|
||||
utf8.lower(text: str) -> str
|
||||
|
||||
-- Escapes a string
|
||||
utf8.escape(text: str) -> str
|
||||
```
|
||||
|
||||
@ -78,14 +78,18 @@ Properties:
|
||||
| caret | int | yes | yes | carriage position. `textbox.caret = -1` will set the position to the end of the text |
|
||||
| editable | bool | yes | yes | text mutability |
|
||||
| multiline | bool | yes | yes | multiline support |
|
||||
| lineNumbers | bool | yes | yes | display line numbers |
|
||||
| textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") |
|
||||
| valid | bool | yes | no | is the entered text correct |
|
||||
| textColor | vec4 | yes | yes | text color |
|
||||
|
||||
Methods:
|
||||
|
||||
| Method | Description |
|
||||
| ----------- | ------------------------------------------------ |
|
||||
| paste(text) | inserts the specified text at the caret position |
|
||||
| Method | Description |
|
||||
| ------------------------- | ---------------------------------------------------------------- |
|
||||
| paste(text: str) | inserts the specified text at the caret position |
|
||||
| lineAt(pos: int) -> int | determines the line number by position in the text |
|
||||
| linePos(line: int) -> int | determines the position of the beginning of the line in the text |
|
||||
|
||||
## Slider (trackbar)
|
||||
|
||||
|
||||
@ -56,7 +56,8 @@ Buttons and panels are also containers.
|
||||
|
||||
- `padding` - element padding. Type: 4D vector.
|
||||
*left, top, right, bottom*
|
||||
`scrollable` - element scrollability. Works on panels only. Type: boolean
|
||||
- `scrollable` - element scrollability. Type: boolean.
|
||||
- `scroll-step` - scrolling step. Type: integer.
|
||||
|
||||
# Common *panel* attributes
|
||||
|
||||
@ -104,7 +105,9 @@ Inner text - initially entered text
|
||||
- `multiline` - allows display of multiline text.
|
||||
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true")
|
||||
- `editable` - determines whether the text can be edited.
|
||||
- `line-numbers` - enables line numbers display.
|
||||
- `error-color` - color when entering incorrect data (the text does not pass the validator check). Type: RGBA color.
|
||||
- `text-color` - text color. Type: RGBA color.
|
||||
- `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct.
|
||||
- `onup` - lua function called when the up arrow is pressed.
|
||||
- `ondown` - lua function called when the down arrow is pressed.
|
||||
|
||||
@ -27,4 +27,7 @@ utf8.upper(text: str) -> str
|
||||
|
||||
-- Переводит строку в нижний регистр
|
||||
utf8.lower(text: str) -> str
|
||||
|
||||
-- Экранирует строку
|
||||
utf8.escape(text: str) -> str
|
||||
```
|
||||
|
||||
@ -4,92 +4,109 @@
|
||||
|
||||
## Расширения для table
|
||||
|
||||
Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной
|
||||
```lua
|
||||
function table.copy(t: table) -> table
|
||||
table.copy(t: table) -> table
|
||||
```
|
||||
|
||||
Возвращает количество пар в переданной таблице
|
||||
Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной.
|
||||
|
||||
```lua
|
||||
function table.count_pairs(t: table) -> integer
|
||||
table.count_pairs(t: table) -> integer
|
||||
```
|
||||
|
||||
Возвращает один элемент из переданной таблицы на случайной позиции
|
||||
Возвращает количество пар в переданной таблице.
|
||||
|
||||
```lua
|
||||
function table.random(t: table) -> object
|
||||
table.random(t: table) -> object
|
||||
```
|
||||
|
||||
Возвращает **true**, если **x** содержится в **t**
|
||||
Возвращает один элемент из переданной таблицы на случайной позиции.
|
||||
|
||||
```lua
|
||||
function table.has(t: table, x: object) -> bool
|
||||
table.has(t: table, x: object) -> bool
|
||||
```
|
||||
|
||||
Возвращает индекс обьекта **x** в **t**. Если переданный обьект не содержится в таблице, то функция вернёт значение **-1**
|
||||
Возвращает **true**, если **x** содержится в **t**.
|
||||
|
||||
```lua
|
||||
function table.index(t: table, x: object) -> integer
|
||||
table.index(t: table, x: object) -> integer
|
||||
```
|
||||
|
||||
Удаляет элемент **x** из **t**
|
||||
Возвращает индекс обьекта **x** в **t**. Если переданный обьект не содержится в таблице, то функция вернёт значение **-1**.
|
||||
|
||||
```lua
|
||||
function table.remove_value(t: table, x: object)
|
||||
table.remove_value(t: table, x: object)
|
||||
```
|
||||
|
||||
Конвертирует переданную таблицу в строку
|
||||
Удаляет элемент **x** из **t**.
|
||||
|
||||
```lua
|
||||
function table.tostring(t: table) -> string
|
||||
table.tostring(t: table) -> string
|
||||
```
|
||||
|
||||
Конвертирует переданную таблицу в строку.
|
||||
|
||||
## Расширения для string
|
||||
|
||||
Разбивает строку **str** на части по указанному разделителю/выражению **separator** и возвращает результат ввиде таблицы из строк. Если **withpattern** равен **true**, то параметр **separator** будет определяться как регулярное выражение
|
||||
```lua
|
||||
function string.explode(separator: string, str: string, withpattern: bool) -> table[string]
|
||||
string.explode(separator: string, str: string, withpattern: bool) -> table[string]
|
||||
```
|
||||
|
||||
Разбивает строку **str** на части по указанному разделителю **delimiter** и возвращает результат ввиде таблицы из строк
|
||||
Разбивает строку **str** на части по указанному разделителю/выражению **separator** и возвращает результат ввиде таблицы из строк. Если **withpattern** равен **true**, то параметр **separator** будет определяться как регулярное выражение.
|
||||
|
||||
```lua
|
||||
function string.split(str: string, delimiter: string) -> table[string]
|
||||
string.split(str: string, delimiter: string) -> table[string]
|
||||
```
|
||||
|
||||
Экранирует специальные символы в строке, такие как `()[]+-.$%^?*` в формате `%символ`. Символ `NUL` (`\0`) будет преобразован в `%z`
|
||||
Разбивает строку **str** на части по указанному разделителю **delimiter** и возвращает результат ввиде таблицы из строк.
|
||||
|
||||
```lua
|
||||
function string.pattern_safe(str: string)
|
||||
string.pattern_safe(str: string)
|
||||
```
|
||||
|
||||
Разбивает секунды на часы, минуты и миллисекунды и форматирует в **format** с следующим порядком параметров: `минуты, секунды, миллисекунды` и после возвращает результат. Если **format** не указан, то возвращает таблицу, где: **h** - hours, **m** - minutes, **s** - seconds, **ms** - milliseconds
|
||||
Экранирует специальные символы в строке, такие как `()[]+-.$%^?*` в формате `%символ`. Символ `NUL` (`\0`) будет преобразован в `%z`.
|
||||
|
||||
```lua
|
||||
function string.formatted_time(seconds: number, format: string) -> string | table
|
||||
string.formatted_time(seconds: number, format: string) -> string | table
|
||||
```
|
||||
|
||||
Заменяет все подстроки в **str**, равные **tofind** на **toreplace** и возвращает строку со всеми измененными подстроками
|
||||
Разбивает секунды на часы, минуты и миллисекунды и форматирует в **format** с следующим порядком параметров: `минуты, секунды, миллисекунды` и после возвращает результат. Если **format** не указан, то возвращает таблицу, где: **h** - hours, **m** - minutes, **s** - seconds, **ms** - milliseconds.
|
||||
|
||||
```lua
|
||||
function string.replace(str: string, tofind: string, toreplace: string) -> string
|
||||
string.replace(str: string, tofind: string, toreplace: string) -> string
|
||||
```
|
||||
|
||||
Заменяет все подстроки в **str**, равные **tofind** на **toreplace** и возвращает строку со всеми измененными подстроками.
|
||||
|
||||
```lua
|
||||
string.trim(str: string, char: string) -> string
|
||||
```
|
||||
|
||||
Удаляет все символы, равные **char** из строки **str** с левого и правого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
||||
|
||||
```lua
|
||||
function string.trim(str: string, char: string) -> string
|
||||
string.trim_left(str: string, char: string) -> string
|
||||
```
|
||||
|
||||
Удаляет все символы, равные **char** из строки **str** с левого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
||||
|
||||
```lua
|
||||
function string.trim_left(str: string, char: string) -> string
|
||||
string.trim_right(str: string, char: string) -> string
|
||||
```
|
||||
|
||||
Удаляет все символы, равные **char** из строки **str** с правого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
||||
|
||||
```lua
|
||||
function string.trim_right(str: string, char: string) -> string
|
||||
string.starts_with(str: string, start: string) -> bool
|
||||
```
|
||||
|
||||
Возвращает **true**, если строка **str** начинается на подстроку **start**
|
||||
|
||||
```lua
|
||||
function string.starts_with(str: string, start: string) -> bool
|
||||
string.ends_with(str: string, endStr: string) -> bool
|
||||
```
|
||||
|
||||
Возвращает **true**, если строка **str** заканчивается на подстроку **endStr**
|
||||
```lua
|
||||
function string.ends_with(str: string, endStr: string) -> bool
|
||||
```
|
||||
|
||||
Также важно подметить, что все выше перечисленные функции, расширяющие **string** можно использовать как мета-методы на экземплярах строк, т.е.:
|
||||
|
||||
@ -103,39 +120,51 @@ end
|
||||
|
||||
Также функции `string.lower` и `string.upper` переопределены на `utf8.lower` и `utf8.upper`
|
||||
|
||||
```lua
|
||||
string.escape(str: string) -> string
|
||||
```
|
||||
|
||||
Экранирует строку. Является псевдонимом `utf8.escape`.
|
||||
|
||||
## Расширения для math
|
||||
|
||||
Ограничивает число **_in** по лимитам **low** и **high**. Т.е.: Если **_in** больше чем **high** - вернётся **high**, если **_in** меньше чем **low** - вернётся **low**. В противном случае вернётся само число
|
||||
```lua
|
||||
function math.clamp(_in, low, high)
|
||||
math.clamp(_in, low, high)
|
||||
```
|
||||
|
||||
Возвращает случайное дробное число в диапазоне от **low** до **high**
|
||||
Ограничивает число **_in** по лимитам **low** и **high**. Т.е.: Если **_in** больше чем **high** - вернётся **high**, если **_in** меньше чем **low** - вернётся **low**. В противном случае вернётся само число.
|
||||
|
||||
```lua
|
||||
function math.rand(low, high)
|
||||
math.rand(low, high)
|
||||
```
|
||||
|
||||
Возвращает случайное дробное число в диапазоне от **low** до **high**.
|
||||
|
||||
## Дополнительные глобальные функции
|
||||
|
||||
В этом же скрипте также определены и другие глобальные функции которые доступны для использования. Ниже их список
|
||||
|
||||
|
||||
Возвращает **true**, если переданная таблица является массивом, тоесть если каждый ключ это целое число больше или равное единице и если каждый ключ следует за прошлым
|
||||
```lua
|
||||
function is_array(x: table) -> bool
|
||||
is_array(x: table) -> bool
|
||||
```
|
||||
|
||||
Разбивает путь на две части и возвращает их: входную точку и путь к файлу
|
||||
Возвращает **true**, если переданная таблица является массивом, тоесть если каждый ключ это целое число больше или равное единице и если каждый ключ следует за прошлым.
|
||||
|
||||
```lua
|
||||
function parse_path(path: string) -> string, string
|
||||
```
|
||||
|
||||
Вызывает функцию **func** **iters** раз, передавая ей аргументы `...`, а после выводит в консоль время в микросекундах, которое прошло с момента вызова **timeit**
|
||||
Разбивает путь на две части и возвращает их: входную точку и путь к файлу.
|
||||
|
||||
```lua
|
||||
function timeit(iters: integer, func: func, ...)
|
||||
```
|
||||
|
||||
Вызывает остановку корутины до тех пор, пока не пройдёт количество секунд, указанное в **timesec**. Функция может быть использована только внутри корутины
|
||||
Вызывает функцию **func** **iters** раз, передавая ей аргументы `...`, а после выводит в консоль время в микросекундах, которое прошло с момента вызова **timeit**.
|
||||
|
||||
```lua
|
||||
function sleep(timesec: number)
|
||||
```
|
||||
```
|
||||
|
||||
Вызывает остановку корутины до тех пор, пока не пройдёт количество секунд, указанное в **timesec**. Функция может быть использована только внутри корутины.
|
||||
|
||||
@ -78,14 +78,18 @@ document["worlds-panel"]:clear()
|
||||
| caret | int | да | да | позиция каретки. `textbox.caret = -1` установит позицию в конец текста |
|
||||
| editable | bool | да | да | изменяемость текста |
|
||||
| multiline | bool | да | да | поддержка многострочности |
|
||||
| lineNumbers | bool | да | да | отображение номеров строк |
|
||||
| textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") |
|
||||
| valid | bool | да | нет | является ли введенный текст корректным |
|
||||
| textColor | vec4 | да | да | цвет текста |
|
||||
|
||||
Методы:
|
||||
|
||||
| Метод | Описание |
|
||||
| ----------- | -------------------------------------------- |
|
||||
| paste(text) | вставляет указанный текст на позицию каретки |
|
||||
| Метод | Описание |
|
||||
| ------------------------- | -------------------------------------------- |
|
||||
| paste(text: str) | вставляет указанный текст на позицию каретки |
|
||||
| lineAt(pos: int) -> int | определяет номер строки по позиции в тексте |
|
||||
| linePos(line: int) -> int | определяет позицию начала строки в тексте |
|
||||
|
||||
## Ползунок (trackbar)
|
||||
|
||||
|
||||
@ -59,7 +59,8 @@
|
||||
В число контейнеров также входят панели и кнопки.
|
||||
- `padding` - внутренний отступ элемента. Тип: 4D вектор.
|
||||
Порядок: `"left,top,right,bottom"`
|
||||
- `scrollable` - возможность скроллинга. Работает только у Panel. Тип: логический.
|
||||
- `scrollable` - возможность скроллинга. Тип: логический.
|
||||
- `scroll-step` - шаг скроллинга. Тип: целочисленный.
|
||||
|
||||
# Общие атрибуты панелей
|
||||
|
||||
@ -105,7 +106,9 @@
|
||||
- `multiline` - разрешает отображение многострочного текста.
|
||||
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
|
||||
- `editable`- определяет возможность редактирования текста.
|
||||
- `line-numbers` - включает отображение номеров строк.
|
||||
- `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет.
|
||||
- `text-color` - цвет текста. Тип: RGBA цвет.
|
||||
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
|
||||
- `onup` - lua функция вызываемая при нажатии стрелки вверх.
|
||||
- `ondown` - lua функция вызываемая при нажатии стрелки вниз.
|
||||
|
||||
@ -1,5 +1,18 @@
|
||||
<container color='#00000080' size='400' size-func="unpack(gui.get_viewport())">
|
||||
<container size-func="gui.get_viewport()[1],gui.get_viewport()[2]-40">
|
||||
<panel interval="0"
|
||||
orientation="horizontal"
|
||||
color="#00000010"
|
||||
size-func="gui.get_viewport()[1]-350,30">
|
||||
<button id="s_chat" size="110,30" onclick="modes:set('chat')">@Chat</button>
|
||||
<button id="s_console" size="110,30" onclick="modes:set('console')">@Console</button>
|
||||
<button id="s_debug" size="110,30" onclick="modes:set('debug')">@Debug</button>
|
||||
</panel>
|
||||
<container pos="0,30" size-func="gui.get_viewport()[1]-350,30" color="#00000020">
|
||||
<label id="title" pos="8,8"></label>
|
||||
</container>
|
||||
|
||||
<container id="logContainer" pos="0,60"
|
||||
size-func="unpack(vec2.add(gui.get_viewport(), {0,-100}))">
|
||||
<textbox
|
||||
id='log'
|
||||
color='0'
|
||||
@ -11,12 +24,33 @@
|
||||
gravity="bottom-left"
|
||||
></textbox>
|
||||
</container>
|
||||
<container id="editorContainer" pos="0,60" color="#00000080"
|
||||
size-func="unpack(vec2.add(gui.get_viewport(), {-350,-230}))">
|
||||
<textbox
|
||||
id='editor'
|
||||
color='0'
|
||||
autoresize='true'
|
||||
margin='0'
|
||||
padding='5'
|
||||
editable='false'
|
||||
multiline='true'
|
||||
line-numbers='true'
|
||||
text-color="#FFFFFFA0"
|
||||
size-func="gui.get_viewport()[1]-350,40"
|
||||
gravity="top-left"
|
||||
text-wrap='false'
|
||||
scroll-step='50'
|
||||
></textbox>
|
||||
</container>
|
||||
<panel id="traceback" gravity="bottom-left" padding="4" color="#000000A0"
|
||||
max-length="170" size-func="gui.get_viewport()[1]-350,170">
|
||||
</panel>
|
||||
<panel id="problemsLog"
|
||||
color="#00000010"
|
||||
position-func="gui.get_viewport()[1]-350,0"
|
||||
size-func="351,gui.get_viewport()[2]-40"
|
||||
size-func="350,gui.get_viewport()[2]-40"
|
||||
padding="5,15,5,15">
|
||||
<label>@Problems</label>
|
||||
<label margin="0,0,0,5">@Problems</label>
|
||||
</panel>
|
||||
<textbox id='prompt'
|
||||
consumer='submit'
|
||||
|
||||
@ -1,21 +1,101 @@
|
||||
console_mode = "console"
|
||||
|
||||
history = session.get_entry("commands_history")
|
||||
history_pointer = #history
|
||||
|
||||
local warnings_all = {}
|
||||
local errors_all = {}
|
||||
|
||||
local warning_id = 0
|
||||
events.on("core:warning", function (wtype, text)
|
||||
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, id=tostring(warning_id)
|
||||
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)
|
||||
|
||||
events.on("core:open_traceback", function(traceback_b64)
|
||||
local traceback = bjson.frombytes(base64.decode(traceback_b64))
|
||||
modes:set('debug')
|
||||
|
||||
local tb_list = document.traceback
|
||||
local srcsize = tb_list.size
|
||||
tb_list:clear()
|
||||
tb_list:add("<label enabled='false' margin='2'>@devtools.traceback</label>")
|
||||
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(
|
||||
"local editor = document.editor "..
|
||||
"local source = file.read('%s') "..
|
||||
"editor.text = source "..
|
||||
"editor.focused = true "..
|
||||
"time.post_runnable(function()"..
|
||||
"editor.caret = editor:linePos(%s) "..
|
||||
"end)",
|
||||
frame.source, frame.currentline-1
|
||||
)
|
||||
else
|
||||
callback = "document.editor.text = 'Could not open source file'"
|
||||
end
|
||||
callback = string.format(
|
||||
"%s document.title.text = gui.str('File')..' - %s'",
|
||||
callback,
|
||||
frame.source
|
||||
)
|
||||
end
|
||||
if frame.name then
|
||||
framestr = framestr.."("..tostring(frame.name)..")"
|
||||
end
|
||||
local color = "#FFFFFF"
|
||||
if frame.source:starts_with("core:") then
|
||||
color = "#C0D0C5"
|
||||
end
|
||||
tb_list:add(gui.template("stack_frame", {
|
||||
location=framestr,
|
||||
color=color,
|
||||
callback=callback
|
||||
}))
|
||||
end
|
||||
tb_list.size = srcsize
|
||||
end)
|
||||
|
||||
function setup_variables()
|
||||
local pid = hud.get_player()
|
||||
local x,y,z = player.get_pos(pid)
|
||||
@ -56,10 +136,19 @@ function add_to_history(text)
|
||||
end
|
||||
|
||||
function submit(text)
|
||||
text = text:trim()
|
||||
add_to_history(text)
|
||||
|
||||
if console_mode == "chat" then
|
||||
if not text:starts_with("/") then
|
||||
text = "chat "..string.escape(text)
|
||||
else
|
||||
text = text:sub(2)
|
||||
end
|
||||
end
|
||||
|
||||
setup_variables()
|
||||
|
||||
text = text:trim()
|
||||
local name
|
||||
for s in text:gmatch("%S+") do
|
||||
name = s
|
||||
@ -84,6 +173,38 @@ function submit(text)
|
||||
document.prompt.focused = true
|
||||
end
|
||||
|
||||
function on_open()
|
||||
document.prompt.focused = true
|
||||
function set_mode(mode)
|
||||
local show_prompt = mode == 'chat' or mode == 'console'
|
||||
|
||||
document.title.text = ""
|
||||
document.editorContainer.visible = mode == 'debug'
|
||||
document.logContainer.visible = mode ~= 'debug'
|
||||
|
||||
if mode == 'debug' then
|
||||
document.root.color = {16, 18, 20, 220}
|
||||
else
|
||||
document.root.color = {0, 0, 0, 128}
|
||||
end
|
||||
|
||||
document.traceback.visible = mode == 'debug'
|
||||
document.prompt.visible = show_prompt
|
||||
if show_prompt then
|
||||
document.prompt.focused = true
|
||||
end
|
||||
console_mode = mode
|
||||
end
|
||||
|
||||
function on_open(mode)
|
||||
if modes == nil then
|
||||
modes = RadioGroup({
|
||||
chat=document.s_chat,
|
||||
console=document.s_console,
|
||||
debug=document.s_debug
|
||||
}, function (mode)
|
||||
set_mode(mode)
|
||||
end, "console")
|
||||
end
|
||||
if mode then
|
||||
modes:set(mode)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<container size='668,418' color='#0F1E2DB2' context='menu'>
|
||||
<panel pos='6' size='250' color='0' interval='1'>
|
||||
<button id='s_aud' onclick='set_page("s_aud", "settings_audio")'>@Audio</button>
|
||||
<button id='s_dsp' onclick='set_page("s_dsp", "settings_display")'>@Display</button>
|
||||
<button id='s_gfx' onclick='set_page("s_gfx", "settings_graphics")'>@Graphics</button>
|
||||
<button id='s_ctl' onclick='set_page("s_ctl", "settings_controls")'>@Controls</button>
|
||||
<button id='s_aud' onclick='sections:set("audio")'>@Audio</button>
|
||||
<button id='s_dsp' onclick='sections:set("display")'>@Display</button>
|
||||
<button id='s_gfx' onclick='sections:set("graphics")'>@Graphics</button>
|
||||
<button id='s_ctl' onclick='sections:set("controls")'>@Controls</button>
|
||||
</panel>
|
||||
<pagebox id='menu' pos='260,6' size='400'>
|
||||
</pagebox>
|
||||
@ -11,7 +11,7 @@
|
||||
<panel margin='6' gravity='bottom-left' size='250' color='0' interval='1'>
|
||||
<button onclick='menu.page="languages"' id='langs_btn'>-</button>
|
||||
<button onclick='core.open_folder("user:")'>@Open data folder</button>
|
||||
<button id='s_rst' onclick='set_page("s_rst", "settings_reset")'>@Reset settings</button>
|
||||
<button id='s_rst' onclick='sections:set("reset")'>@Reset settings</button>
|
||||
<button onclick='menu:back()'>@Back</button>
|
||||
</panel>
|
||||
</container>
|
||||
|
||||
@ -3,15 +3,13 @@ function on_open()
|
||||
"%s: %s", gui.str("Language", "settings"),
|
||||
gui.get_locales_info()[core.get_setting("ui.language")].name
|
||||
)
|
||||
set_page("s_gfx", "settings_graphics")
|
||||
end
|
||||
|
||||
function set_page(btn, page)
|
||||
document.s_aud.enabled = true
|
||||
document.s_dsp.enabled = true
|
||||
document.s_gfx.enabled = true
|
||||
document.s_ctl.enabled = true
|
||||
document.s_rst.enabled = true
|
||||
document[btn].enabled = false
|
||||
document.menu.page = page
|
||||
sections = RadioGroup({
|
||||
audio=document.s_aud,
|
||||
display=document.s_dsp,
|
||||
graphics=document.s_gfx,
|
||||
controls=document.s_ctl,
|
||||
reset=document.s_rst
|
||||
}, function (page)
|
||||
document.menu.page = "settings_"..page
|
||||
end, "graphics")
|
||||
end
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<container id="%{id}" size="32" tooltip="%{text}">
|
||||
<container id="%{id}" size="32" tooltip="%{text}"
|
||||
onclick="events.emit('core:open_traceback', '%{traceback}')">
|
||||
<image src="gui/%{type}" size="32"/>
|
||||
<label pos="36,2">%{text}</label>
|
||||
<image src="gui/cross" interactive="true" size="16" gravity="top-right"
|
||||
|
||||
3
res/layouts/templates/stack_frame.xml
Normal file
3
res/layouts/templates/stack_frame.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<label hover-color="#A0A0FF" interactive="true" onclick="%{callback}" color="%{color}">
|
||||
%{location}
|
||||
</label>
|
||||
@ -256,6 +256,14 @@ console.add_command(
|
||||
end
|
||||
)
|
||||
|
||||
console.add_command(
|
||||
"chat text:str",
|
||||
"Send chat message",
|
||||
function (args, kwargs)
|
||||
console.log("[you] "..args[1])
|
||||
end
|
||||
)
|
||||
|
||||
console.cheats = {
|
||||
"blocks.fill",
|
||||
"tp",
|
||||
|
||||
@ -84,6 +84,32 @@ function Document.new(docname)
|
||||
})
|
||||
end
|
||||
|
||||
local _RadioGroup = {}
|
||||
function _RadioGroup.set(self, key)
|
||||
if type(self) ~= 'table' then
|
||||
error("called as non-OOP via '.', use radiogroup:set")
|
||||
end
|
||||
if self.current then
|
||||
self.elements[self.current].enabled = true
|
||||
end
|
||||
self.elements[key].enabled = false
|
||||
self.current = key
|
||||
if self.callback then
|
||||
self.callback(key)
|
||||
end
|
||||
end
|
||||
function _RadioGroup.__call(self, elements, onset, default)
|
||||
local group = setmetatable({
|
||||
elements=elements,
|
||||
callback=onset,
|
||||
current=nil
|
||||
}, {__index=_RadioGroup})
|
||||
group:set(default)
|
||||
return group
|
||||
end
|
||||
setmetatable(_RadioGroup, _RadioGroup)
|
||||
RadioGroup = _RadioGroup
|
||||
|
||||
_GUI_ROOT = Document.new("core:root")
|
||||
_MENU = _GUI_ROOT.menu
|
||||
menu = _MENU
|
||||
|
||||
@ -162,6 +162,7 @@ end
|
||||
|
||||
string.lower = utf8.lower
|
||||
string.upper = utf8.upper
|
||||
string.escape = utf8.escape
|
||||
|
||||
local meta = getmetatable("")
|
||||
|
||||
@ -227,8 +228,22 @@ function file.readlines(path)
|
||||
return lines
|
||||
end
|
||||
|
||||
function debug.get_traceback(start)
|
||||
local frames = {}
|
||||
local n = 2 + (start or 0)
|
||||
while true do
|
||||
local info = debug.getinfo(n)
|
||||
if info then
|
||||
table.insert(frames, info)
|
||||
else
|
||||
return frames
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
|
||||
package = {
|
||||
loaded={}
|
||||
loaded = {}
|
||||
}
|
||||
local __cached_scripts = {}
|
||||
local __warnings_hidden = {}
|
||||
@ -238,7 +253,7 @@ function on_deprecated_call(name, alternatives)
|
||||
return
|
||||
end
|
||||
__warnings_hidden[name] = true
|
||||
events.emit("core:warning", "deprecated call", name)
|
||||
events.emit("core:warning", "deprecated call", name, debug.get_traceback(2))
|
||||
if alternatives then
|
||||
debug.warning("deprecated function called ("..name.."), use "..
|
||||
alternatives.." instead\n"..debug.traceback())
|
||||
@ -292,3 +307,10 @@ function __scripts_cleanup()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function __vc__error(msg, frame)
|
||||
if events then
|
||||
events.emit("core:error", msg, debug.get_traceback(1))
|
||||
end
|
||||
return debug.traceback(msg, frame)
|
||||
end
|
||||
|
||||
@ -11,6 +11,8 @@ world.delete-confirm=Do you want to delete world forever?
|
||||
world.generators.default=Default
|
||||
world.generators.flat=Flat
|
||||
|
||||
devtools.traceback=Traceback (most recent call first)
|
||||
|
||||
# Tooltips
|
||||
graphics.gamma.tooltip=Lighting brightness curve
|
||||
graphics.backlight.tooltip=Backlight to prevent total darkness
|
||||
|
||||
@ -12,7 +12,15 @@ Dependencies=Зависимости
|
||||
Description=Описание
|
||||
Converting world...=Выполняется конвертация мира...
|
||||
Unlimited=Неограниченно
|
||||
Chat=Чат
|
||||
Console=Консоль
|
||||
Log=Лог
|
||||
Problems=Проблемы
|
||||
Monitor=Мониторинг
|
||||
Debug=Отладка
|
||||
File=Файл
|
||||
|
||||
devtools.traceback=Стек вызовов (от последнего)
|
||||
error.pack-not-found=Не удалось найти пакет
|
||||
error.dependency-not-found=Используемая зависимость не найдена
|
||||
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
|
||||
|
||||
@ -172,7 +172,9 @@ assetload::postfunc assetload::layout(
|
||||
return [=](auto assets) {
|
||||
try {
|
||||
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
|
||||
assets->store(UiDocument::read(cfg->env, name, file), name);
|
||||
assets->store(
|
||||
UiDocument::read(cfg->env, name, file, "abs:" + file), name
|
||||
);
|
||||
} catch (const parsing_error& err) {
|
||||
throw std::runtime_error(
|
||||
"failed to parse layout XML '" + file + "':\n" + err.errorLog()
|
||||
|
||||
@ -373,7 +373,7 @@ std::string BasicParser::parseString(char quote, bool closeRequired) {
|
||||
case 'b': ss << '\b'; break;
|
||||
case 't': ss << '\t'; break;
|
||||
case 'f': ss << '\f'; break;
|
||||
case '\'': ss << '\\'; break;
|
||||
case '\'': ss << '\''; break;
|
||||
case '"': ss << '"'; break;
|
||||
case '\\': ss << '\\'; break;
|
||||
case '/': ss << '/'; break;
|
||||
|
||||
@ -250,7 +250,8 @@ std::string Parser::parseText() {
|
||||
}
|
||||
nextChar();
|
||||
}
|
||||
return std::string(source.substr(start, pos - start));
|
||||
return Parser("<string>", std::string(source.substr(start, pos - start)))
|
||||
.parseString('\0', false);
|
||||
}
|
||||
|
||||
inline bool is_xml_identifier_start(char c) {
|
||||
@ -336,7 +337,7 @@ xmldocument Parser::parse() {
|
||||
return document;
|
||||
}
|
||||
|
||||
xmldocument xml::parse(const std::string& filename, const std::string& source) {
|
||||
xmldocument xml::parse(std::string_view filename, std::string_view source) {
|
||||
Parser parser(filename, source);
|
||||
return parser.parse();
|
||||
}
|
||||
|
||||
@ -140,6 +140,6 @@ namespace xml {
|
||||
/// @param source xml source code string
|
||||
/// @return xml document
|
||||
extern xmldocument parse(
|
||||
const std::string& filename, const std::string& source
|
||||
std::string_view filename, std::string_view source
|
||||
);
|
||||
}
|
||||
|
||||
@ -485,7 +485,13 @@ void ContentLoader::loadBlock(
|
||||
|
||||
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_block_script(env, full, scriptfile, def.rt.funcsset);
|
||||
scripting::load_block_script(
|
||||
env,
|
||||
full,
|
||||
scriptfile,
|
||||
pack->id + ":scripts/" + def.scriptName + ".lua",
|
||||
def.rt.funcsset
|
||||
);
|
||||
}
|
||||
if (!def.hidden) {
|
||||
auto& item = builder.items.create(full + BLOCK_ITEM_SUFFIX);
|
||||
@ -511,7 +517,13 @@ void ContentLoader::loadItem(
|
||||
|
||||
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_item_script(env, full, scriptfile, def.rt.funcsset);
|
||||
scripting::load_item_script(
|
||||
env,
|
||||
full,
|
||||
scriptfile,
|
||||
pack->id + ":scripts/" + def.scriptName + ".lua",
|
||||
def.rt.funcsset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -720,7 +732,11 @@ void ContentLoader::load() {
|
||||
fs::path scriptFile = folder / fs::path("scripts/world.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_world_script(
|
||||
env, pack->id, scriptFile, runtime->worldfuncsset
|
||||
env,
|
||||
pack->id,
|
||||
scriptFile,
|
||||
pack->id + ":scripts/world.lua",
|
||||
runtime->worldfuncsset
|
||||
);
|
||||
}
|
||||
|
||||
@ -795,7 +811,11 @@ void ContentLoader::load() {
|
||||
fs::path componentsDir = folder / fs::u8path("scripts/components");
|
||||
foreach_file(componentsDir, [this](const fs::path& file) {
|
||||
auto name = pack->id + ":" + file.stem().u8string();
|
||||
scripting::load_entity_component(name, file);
|
||||
scripting::load_entity_component(
|
||||
name,
|
||||
file,
|
||||
pack->id + ":scripts/components/" + file.filename().u8string()
|
||||
);
|
||||
});
|
||||
|
||||
// Process content.json and load defined content units
|
||||
|
||||
@ -53,7 +53,12 @@ scriptenv UiDocument::getEnvironment() const {
|
||||
return env;
|
||||
}
|
||||
|
||||
std::unique_ptr<UiDocument> UiDocument::read(const scriptenv& penv, const std::string& name, const fs::path& file) {
|
||||
std::unique_ptr<UiDocument> UiDocument::read(
|
||||
const scriptenv& penv,
|
||||
const std::string& name,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
) {
|
||||
const std::string text = files::read_string(file);
|
||||
auto xmldoc = xml::parse(file.u8string(), text);
|
||||
|
||||
@ -69,12 +74,16 @@ std::unique_ptr<UiDocument> UiDocument::read(const scriptenv& penv, const std::s
|
||||
uidocscript script {};
|
||||
auto scriptFile = fs::path(file.u8string()+".lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_layout_script(env, name, scriptFile, script);
|
||||
scripting::load_layout_script(
|
||||
env, name, scriptFile, fileName + ".lua", script
|
||||
);
|
||||
}
|
||||
return std::make_unique<UiDocument>(name, script, view, env);
|
||||
}
|
||||
|
||||
std::shared_ptr<gui::UINode> UiDocument::readElement(const fs::path& file) {
|
||||
auto document = read(nullptr, file.filename().u8string(), file);
|
||||
std::shared_ptr<gui::UINode> UiDocument::readElement(
|
||||
const fs::path& file, const std::string& fileName
|
||||
) {
|
||||
auto document = read(nullptr, file.filename().u8string(), file, fileName);
|
||||
return document->getRoot();
|
||||
}
|
||||
|
||||
@ -45,6 +45,13 @@ public:
|
||||
const uidocscript& getScript() const;
|
||||
scriptenv getEnvironment() const;
|
||||
|
||||
static std::unique_ptr<UiDocument> read(const scriptenv& parent_env, const std::string& name, const fs::path& file);
|
||||
static std::shared_ptr<gui::UINode> readElement(const fs::path& file);
|
||||
static std::unique_ptr<UiDocument> read(
|
||||
const scriptenv& parent_env,
|
||||
const std::string& name,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
static std::shared_ptr<gui::UINode> readElement(
|
||||
const fs::path& file, const std::string& fileName
|
||||
);
|
||||
};
|
||||
|
||||
@ -230,7 +230,11 @@ void Hud::processInput(bool visible) {
|
||||
}
|
||||
}
|
||||
if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) {
|
||||
showOverlay(assets->get<UiDocument>("core:console"), false);
|
||||
showOverlay(
|
||||
assets->get<UiDocument>("core:console"),
|
||||
false,
|
||||
std::string("console")
|
||||
);
|
||||
}
|
||||
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
|
||||
setPause(true);
|
||||
@ -465,7 +469,9 @@ void Hud::showExchangeSlot() {
|
||||
|
||||
}
|
||||
|
||||
void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
||||
void Hud::showOverlay(
|
||||
UiDocument* doc, bool playerInventory, const dv::value& arg
|
||||
) {
|
||||
if (isInventoryOpen()) {
|
||||
closeInventory();
|
||||
}
|
||||
@ -476,7 +482,8 @@ void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
||||
showExchangeSlot();
|
||||
inventoryOpen = true;
|
||||
}
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false),
|
||||
arg);
|
||||
}
|
||||
|
||||
void Hud::openPermanent(UiDocument* doc) {
|
||||
@ -508,13 +515,13 @@ void Hud::closeInventory() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Hud::add(const HudElement& element) {
|
||||
void Hud::add(const HudElement& element, const dv::value& arg) {
|
||||
gui->add(element.getNode());
|
||||
auto document = element.getDocument();
|
||||
if (document) {
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
||||
auto inventory = invview ? invview->getInventory() : nullptr;
|
||||
std::vector<dv::value> args;
|
||||
std::vector<dv::value> args {arg};
|
||||
args.emplace_back(inventory ? inventory.get()->getId() : 0);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
args.emplace_back(static_cast<integer_t>(blockPos[i]));
|
||||
@ -615,8 +622,11 @@ void Hud::updateElementsPosition(const Viewport& viewport) {
|
||||
}
|
||||
if (secondUI->getPositionFunc() == nullptr) {
|
||||
secondUI->setPos(glm::vec2(
|
||||
glm::min(width/2-invwidth/2, width-caWidth-(inventoryView ? 10 : 0)-invwidth),
|
||||
height/2-totalHeight/2
|
||||
glm::min(
|
||||
width / 2.f - invwidth / 2.f,
|
||||
width - caWidth - (inventoryView ? 10 : 0) - invwidth
|
||||
),
|
||||
height / 2.f - totalHeight / 2.f
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "util/ObjectsKeeper.hpp"
|
||||
#include "data/dv.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@ -173,7 +174,10 @@ public:
|
||||
/// @brief Show element in inventory-mode
|
||||
/// @param doc element layout
|
||||
/// @param playerInventory show player inventory too
|
||||
void showOverlay(UiDocument* doc, bool playerInventory);
|
||||
/// @param arg first argument passing to on_open
|
||||
void showOverlay(
|
||||
UiDocument* doc, bool playerInventory, const dv::value& arg = nullptr
|
||||
);
|
||||
|
||||
/// @brief Close all open inventories and overlay
|
||||
void closeInventory();
|
||||
@ -182,7 +186,7 @@ public:
|
||||
/// @param doc element layout
|
||||
void openPermanent(UiDocument* doc);
|
||||
|
||||
void add(const HudElement& element);
|
||||
void add(const HudElement& element, const dv::value& arg=nullptr);
|
||||
void onRemove(const HudElement& element);
|
||||
void remove(const std::shared_ptr<gui::UINode>& node);
|
||||
|
||||
|
||||
@ -62,7 +62,10 @@ gui::page_loader_func menus::create_page_loader(Engine* engine) {
|
||||
auto fullname = "core:pages/"+name;
|
||||
|
||||
auto document_ptr = UiDocument::read(
|
||||
scripting::get_root_environment(), fullname, file
|
||||
scripting::get_root_environment(),
|
||||
fullname,
|
||||
file,
|
||||
"core:layout/pages/" + name
|
||||
);
|
||||
auto document = document_ptr.get();
|
||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
||||
@ -110,7 +113,7 @@ UiDocument* menus::show(Engine* engine, const std::string& name, std::vector<dv:
|
||||
auto fullname = "core:layouts/"+name;
|
||||
|
||||
auto document_ptr = UiDocument::read(
|
||||
scripting::get_root_environment(), fullname, file
|
||||
scripting::get_root_environment(), fullname, file, "core:layouts/"+name
|
||||
);
|
||||
auto document = document_ptr.get();
|
||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
||||
|
||||
@ -83,7 +83,12 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
||||
const ContentPack& info = pack->getInfo();
|
||||
fs::path scriptFile = info.folder/fs::path("scripts/hud.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_hud_script(pack->getEnvironment(), info.id, scriptFile);
|
||||
scripting::load_hud_script(
|
||||
pack->getEnvironment(),
|
||||
info.id,
|
||||
scriptFile,
|
||||
pack->getId() + ":scripts/hud.lua"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -91,6 +91,7 @@ DrawContext DrawContext::sub(Flushable* flushable) const {
|
||||
auto ctx = DrawContext(*this);
|
||||
ctx.parent = this;
|
||||
ctx.flushable = flushable;
|
||||
ctx.scissorsCount = 0;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@ -148,7 +149,7 @@ void DrawContext::setBlendMode(BlendMode mode) {
|
||||
set_blend_mode(mode);
|
||||
}
|
||||
|
||||
void DrawContext::setScissors(glm::vec4 area) {
|
||||
void DrawContext::setScissors(const glm::vec4& area) {
|
||||
Window::pushScissor(area);
|
||||
scissorsCount++;
|
||||
}
|
||||
|
||||
@ -34,6 +34,6 @@ public:
|
||||
void setDepthTest(bool flag);
|
||||
void setCullFace(bool flag);
|
||||
void setBlendMode(BlendMode mode);
|
||||
void setScissors(glm::vec4 area);
|
||||
void setScissors(const glm::vec4& area);
|
||||
void setLineWidth(float width);
|
||||
};
|
||||
|
||||
@ -90,7 +90,7 @@ void Container::draw(const DrawContext* pctx, Assets* assets) {
|
||||
if (!nodes.empty()) {
|
||||
batch->flush();
|
||||
DrawContext ctx = pctx->sub();
|
||||
ctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
|
||||
ctx.setScissors(glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y)));
|
||||
for (const auto& node : nodes) {
|
||||
if (node->isVisible())
|
||||
node->draw(pctx, assets);
|
||||
@ -108,7 +108,7 @@ void Container::drawBackground(const DrawContext* pctx, Assets*) {
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->setColor(color);
|
||||
batch->rect(pos.x, pos.y, size.x, size.y);
|
||||
batch->rect(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y));
|
||||
}
|
||||
|
||||
void Container::add(const std::shared_ptr<UINode> &node) {
|
||||
@ -165,6 +165,14 @@ void Container::setSize(glm::vec2 size) {
|
||||
}
|
||||
}
|
||||
|
||||
int Container::getScrollStep() const {
|
||||
return scrollStep;
|
||||
}
|
||||
|
||||
void Container::setScrollStep(int step) {
|
||||
scrollStep = step;
|
||||
}
|
||||
|
||||
void Container::refresh() {
|
||||
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
||||
return a->getZIndex() < b->getZIndex();
|
||||
|
||||
@ -32,6 +32,8 @@ namespace gui {
|
||||
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
||||
virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);};
|
||||
virtual void setSize(glm::vec2 size) override;
|
||||
virtual int getScrollStep() const;
|
||||
virtual void setScrollStep(int step);
|
||||
virtual void refresh() override;
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "TextBox.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
@ -14,24 +15,39 @@
|
||||
|
||||
using namespace gui;
|
||||
|
||||
inline constexpr int LINE_NUMBERS_PANE_WIDTH = 40;
|
||||
|
||||
TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
|
||||
: Panel(glm::vec2(200,32), padding, 0),
|
||||
: Container(glm::vec2(200,32)),
|
||||
padding(padding),
|
||||
input(L""),
|
||||
placeholder(std::move(placeholder))
|
||||
{
|
||||
setOnUpPressed(nullptr);
|
||||
setOnDownPressed(nullptr);
|
||||
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
|
||||
label = std::make_shared<Label>(L"");
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
label->setPos(glm::vec2(
|
||||
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
|
||||
));
|
||||
add(label);
|
||||
|
||||
lineNumbersLabel = std::make_shared<Label>(L"");
|
||||
lineNumbersLabel->setMultiline(true);
|
||||
lineNumbersLabel->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
lineNumbersLabel->setVerticalAlign(Align::top);
|
||||
add(lineNumbersLabel);
|
||||
|
||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
|
||||
|
||||
textInitX = label->getPos().x;
|
||||
scrollable = true;
|
||||
scrollStep = 0;
|
||||
}
|
||||
|
||||
void TextBox::draw(const DrawContext* pctx, Assets* assets) {
|
||||
Panel::draw(pctx, assets);
|
||||
Container::draw(pctx, assets);
|
||||
|
||||
font = assets->get<Font>(label->getFontName());
|
||||
|
||||
@ -76,6 +92,44 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
|
||||
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(endLine), end, lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (isFocused() && multiline) {
|
||||
auto selectionCtx = subctx.sub(batch);
|
||||
selectionCtx.setBlendMode(BlendMode::addition);
|
||||
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
|
||||
|
||||
uint line = label->getLineByTextIndex(caret);
|
||||
while (label->isFakeLine(line)) {
|
||||
line--;
|
||||
}
|
||||
do {
|
||||
int lineY = label->getLineYOffset(line);
|
||||
int lineHeight = font->getLineHeight() * label->getLineInterval();
|
||||
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
|
||||
if (showLineNumbers) {
|
||||
batch->rect(
|
||||
lcoord.x - 8,
|
||||
lcoord.y + lineY,
|
||||
label->getSize().x,
|
||||
lineHeight
|
||||
);
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.10f));
|
||||
batch->rect(
|
||||
lcoord.x - LINE_NUMBERS_PANE_WIDTH,
|
||||
lcoord.y + lineY,
|
||||
LINE_NUMBERS_PANE_WIDTH - 8,
|
||||
lineHeight
|
||||
);
|
||||
} else {
|
||||
batch->rect(
|
||||
lcoord.x, lcoord.y + lineY, label->getSize().x, lineHeight
|
||||
);
|
||||
}
|
||||
line++;
|
||||
} while (line < label->getLinesNumber() && label->isFakeLine(line));
|
||||
}
|
||||
}
|
||||
|
||||
void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
||||
@ -103,31 +157,31 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
||||
if (!isFocused() && supplier) {
|
||||
input = supplier();
|
||||
}
|
||||
|
||||
if (isFocused() && multiline) {
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
|
||||
glm::vec2 lcoord = label->calcPos();
|
||||
lcoord.y -= 2;
|
||||
|
||||
uint line = label->getLineByTextIndex(caret);
|
||||
while (label->isFakeLine(line)) {
|
||||
line--;
|
||||
}
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
|
||||
do {
|
||||
int lineY = label->getLineYOffset(line);
|
||||
int lineHeight = font->getLineHeight() * label->getLineInterval();
|
||||
|
||||
batch->rect(lcoord.x, lcoord.y+lineY, label->getSize().x, lineHeight);
|
||||
line++;
|
||||
} while (line < label->getLinesNumber() && label->isFakeLine(line));
|
||||
}
|
||||
refreshLabel();
|
||||
}
|
||||
|
||||
void TextBox::refreshLabel() {
|
||||
label->setColor(glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||
label->setText(input.empty() && !hint.empty() ? hint : getText());
|
||||
|
||||
if (showLineNumbers) {
|
||||
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
|
||||
std::wstringstream ss;
|
||||
int n = 1;
|
||||
for (int i = 1; i <= label->getLinesNumber(); i++) {
|
||||
if (!label->isFakeLine(i-1)) {
|
||||
ss << n;
|
||||
n++;
|
||||
}
|
||||
if (i + 1 <= label->getLinesNumber()) {
|
||||
ss << "\n";
|
||||
}
|
||||
}
|
||||
lineNumbersLabel->setText(ss.str());
|
||||
}
|
||||
lineNumbersLabel->setPos(padding);
|
||||
lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f));
|
||||
}
|
||||
|
||||
if (autoresize && font) {
|
||||
auto size = getSize();
|
||||
@ -293,7 +347,7 @@ bool TextBox::isAutoResize() const {
|
||||
}
|
||||
|
||||
void TextBox::onFocus(GUI* gui) {
|
||||
Panel::onFocus(gui);
|
||||
Container::onFocus(gui);
|
||||
if (onEditStart){
|
||||
setCaret(input.size());
|
||||
onEditStart();
|
||||
@ -302,8 +356,11 @@ void TextBox::onFocus(GUI* gui) {
|
||||
}
|
||||
|
||||
void TextBox::refresh() {
|
||||
Panel::refresh();
|
||||
Container::refresh();
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
label->setPos(glm::vec2(
|
||||
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
|
||||
));
|
||||
}
|
||||
|
||||
/// @brief Clamp index to range [0, input.length()]
|
||||
@ -567,6 +624,14 @@ void TextBox::select(int start, int end) {
|
||||
setCaret(selectionEnd);
|
||||
}
|
||||
|
||||
uint TextBox::getLineAt(size_t position) const {
|
||||
return label->getLineByTextIndex(position);
|
||||
}
|
||||
|
||||
size_t TextBox::getLinePos(uint line) const {
|
||||
return label->getTextLineOffset(line);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> TextBox::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||
return UINode::getAt(pos, self);
|
||||
}
|
||||
@ -619,6 +684,15 @@ glm::vec4 TextBox::getFocusedColor() const {
|
||||
return focusedColor;
|
||||
}
|
||||
|
||||
|
||||
void TextBox::setTextColor(glm::vec4 color) {
|
||||
this->textColor = color;
|
||||
}
|
||||
|
||||
glm::vec4 TextBox::getTextColor() const {
|
||||
return textColor;
|
||||
}
|
||||
|
||||
void TextBox::setErrorColor(glm::vec4 color) {
|
||||
this->invalidColor = color;
|
||||
}
|
||||
@ -673,9 +747,11 @@ void TextBox::setCaret(size_t position) {
|
||||
uint line = label->getLineByTextIndex(caret);
|
||||
int offset = label->getLineYOffset(line) + getContentOffset().y;
|
||||
uint lineHeight = font->getLineHeight()*label->getLineInterval();
|
||||
scrollStep = lineHeight;
|
||||
if (scrollStep == 0) {
|
||||
scrollStep = lineHeight;
|
||||
}
|
||||
if (offset < 0) {
|
||||
scrolled(1);
|
||||
scrolled(-glm::floor(offset/static_cast<double>(scrollStep)+0.5f));
|
||||
} else if (offset >= getSize().y) {
|
||||
offset -= getSize().y;
|
||||
scrolled(-glm::ceil(offset/static_cast<double>(scrollStep)+0.5f));
|
||||
@ -696,3 +772,20 @@ void TextBox::setCaret(ptrdiff_t position) {
|
||||
setCaret(static_cast<size_t>(position));
|
||||
}
|
||||
}
|
||||
|
||||
void TextBox::setPadding(glm::vec4 padding) {
|
||||
this->padding = padding;
|
||||
refresh();
|
||||
}
|
||||
|
||||
glm::vec4 TextBox::getPadding() const {
|
||||
return padding;
|
||||
}
|
||||
|
||||
void TextBox::setShowLineNumbers(bool flag) {
|
||||
showLineNumbers = flag;
|
||||
}
|
||||
|
||||
bool TextBox::isShowLineNumbers() const {
|
||||
return showLineNumbers;
|
||||
}
|
||||
|
||||
@ -8,11 +8,14 @@ class Font;
|
||||
namespace gui {
|
||||
class Label;
|
||||
|
||||
class TextBox : public Panel {
|
||||
class TextBox : public Container {
|
||||
protected:
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
||||
glm::vec4 textColor {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
glm::vec4 padding {2};
|
||||
std::shared_ptr<Label> label;
|
||||
std::shared_ptr<Label> lineNumbersLabel;
|
||||
/// @brief Current user input
|
||||
std::wstring input;
|
||||
/// @brief Text will be used if nothing entered
|
||||
@ -52,6 +55,7 @@ namespace gui {
|
||||
bool multiline = false;
|
||||
bool editable = true;
|
||||
bool autoresize = false;
|
||||
bool showLineNumbers = false;
|
||||
|
||||
void stepLeft(bool shiftPressed, bool breakSelection);
|
||||
void stepRight(bool shiftPressed, bool breakSelection);
|
||||
@ -106,6 +110,9 @@ namespace gui {
|
||||
virtual void setFocusedColor(glm::vec4 color);
|
||||
virtual glm::vec4 getFocusedColor() const;
|
||||
|
||||
virtual void setTextColor(glm::vec4 color);
|
||||
virtual glm::vec4 getTextColor() const;
|
||||
|
||||
/// @brief Set color of textbox marked by validator as invalid
|
||||
virtual void setErrorColor(glm::vec4 color);
|
||||
|
||||
@ -152,6 +159,16 @@ namespace gui {
|
||||
/// @param end index of the last selected character + 1
|
||||
virtual void select(int start, int end);
|
||||
|
||||
/// @brief Get number of line at specific position in text
|
||||
/// @param position target position
|
||||
/// @return line number
|
||||
virtual uint getLineAt(size_t position) const;
|
||||
|
||||
/// @brief Get specific line text position
|
||||
/// @param line target line
|
||||
/// @return line position in text
|
||||
virtual size_t getLinePos(uint line) const;
|
||||
|
||||
/// @brief Check text with validator set with setTextValidator
|
||||
/// @return true if text is valid
|
||||
virtual bool validate();
|
||||
@ -177,12 +194,18 @@ namespace gui {
|
||||
/// @brief Check if text editing feature is enabled
|
||||
virtual bool isEditable() const;
|
||||
|
||||
virtual void setPadding(glm::vec4 padding);
|
||||
glm::vec4 getPadding() const;
|
||||
|
||||
/// @brief Set runnable called on textbox focus
|
||||
virtual void setOnEditStart(runnable oneditstart);
|
||||
|
||||
virtual void setAutoResize(bool flag);
|
||||
virtual bool isAutoResize() const;
|
||||
|
||||
virtual void setShowLineNumbers(bool flag);
|
||||
virtual bool isShowLineNumbers() const;
|
||||
|
||||
virtual void onFocus(GUI*) override;
|
||||
virtual void refresh() override;
|
||||
virtual void doubleClick(GUI*, int x, int y) override;
|
||||
|
||||
@ -81,7 +81,7 @@ namespace gui {
|
||||
/// @brief element color when clicked
|
||||
glm::vec4 pressedColor {1.0f};
|
||||
/// @brief element margin (only supported for Panel sub-nodes)
|
||||
glm::vec4 margin {1.0f};
|
||||
glm::vec4 margin {0.0f};
|
||||
/// @brief is element visible
|
||||
bool visible = true;
|
||||
/// @brief is mouse over the element
|
||||
|
||||
@ -172,6 +172,9 @@ static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element,
|
||||
if (element->has("scrollable")) {
|
||||
container.setScrollable(element->attr("scrollable").asBool());
|
||||
}
|
||||
if (element->has("scroll-step")) {
|
||||
container.setScrollStep(element->attr("scroll-step").asInt());
|
||||
}
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->isText())
|
||||
continue;
|
||||
@ -342,7 +345,16 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
||||
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
|
||||
textbox->setHint(hint);
|
||||
|
||||
_readPanel(reader, element, *textbox);
|
||||
_readContainer(reader, element, *textbox);
|
||||
if (element->has("padding")) {
|
||||
glm::vec4 padding = element->attr("padding").asVec4();
|
||||
textbox->setPadding(padding);
|
||||
glm::vec2 size = textbox->getSize();
|
||||
textbox->setSize(glm::vec2(
|
||||
size.x + padding.x + padding.z,
|
||||
size.y + padding.y + padding.w
|
||||
));
|
||||
}
|
||||
textbox->setText(text);
|
||||
|
||||
if (element->has("multiline")) {
|
||||
@ -357,6 +369,9 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
||||
if (element->has("autoresize")) {
|
||||
textbox->setAutoResize(element->attr("autoresize").asBool());
|
||||
}
|
||||
if (element->has("line-numbers")) {
|
||||
textbox->setShowLineNumbers(element->attr("line-numbers").asBool());
|
||||
}
|
||||
if (element->has("consumer")) {
|
||||
textbox->setTextConsumer(scripting::create_wstring_consumer(
|
||||
reader.getEnvironment(),
|
||||
@ -384,6 +399,9 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
||||
if (element->has("error-color")) {
|
||||
textbox->setErrorColor(element->attr("error-color").asColor());
|
||||
}
|
||||
if (element->has("text-color")) {
|
||||
textbox->setTextColor(element->attr("text-color").asColor());
|
||||
}
|
||||
if (element->has("validator")) {
|
||||
textbox->setTextValidator(scripting::create_wstring_validator(
|
||||
reader.getEnvironment(),
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
// Libraries
|
||||
extern const luaL_Reg audiolib[];
|
||||
extern const luaL_Reg base64lib[];
|
||||
extern const luaL_Reg bjsonlib[];
|
||||
extern const luaL_Reg blocklib[];
|
||||
extern const luaL_Reg cameralib[];
|
||||
|
||||
52
src/logic/scripting/lua/libs/libbase64.cpp
Normal file
52
src/logic/scripting/lua/libs/libbase64.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "api_lua.hpp"
|
||||
|
||||
#include "util/stringutil.hpp"
|
||||
|
||||
static int l_encode(lua::State* L) {
|
||||
if (lua::istable(L, 1)) {
|
||||
lua::pushvalue(L, 1);
|
||||
size_t size = lua::objlen(L, 1);
|
||||
util::Buffer<char> buffer(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
lua::rawgeti(L, i + 1);
|
||||
buffer[i] = lua::tointeger(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
return lua::pushstring(L, util::base64_encode(
|
||||
reinterpret_cast<const ubyte*>(buffer.data()), buffer.size()
|
||||
));
|
||||
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
|
||||
return lua::pushstring(
|
||||
L,
|
||||
util::base64_encode(
|
||||
bytes->data().data(),
|
||||
bytes->data().size()
|
||||
)
|
||||
);
|
||||
}
|
||||
throw std::runtime_error("array or ByteArray expected");
|
||||
}
|
||||
|
||||
static int l_decode(lua::State* L) {
|
||||
auto buffer = util::base64_decode(lua::require_lstring(L, 1));
|
||||
if (lua::toboolean(L, 2)) {
|
||||
lua::createtable(L, buffer.size(), 0);
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
lua::pushinteger(L, buffer[i] & 0xFF);
|
||||
lua::rawseti(L, i+1);
|
||||
}
|
||||
} else {
|
||||
lua::newuserdata<lua::LuaBytearray>(L, buffer.size());
|
||||
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
|
||||
bytearray->data().reserve(buffer.size());
|
||||
std::memcpy(bytearray->data().data(), buffer.data(), buffer.size());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg base64lib[] = {
|
||||
{"encode", lua::wrap<l_encode>},
|
||||
{"decode", lua::wrap<l_decode>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@ -134,6 +134,24 @@ static int l_move_into(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_line_at(lua::State* L) {
|
||||
auto node = getDocumentNode(L, 1);
|
||||
auto position = lua::tointeger(L, 2);
|
||||
if (auto box = dynamic_cast<TextBox*>(node.node.get())) {
|
||||
return lua::pushinteger(L, box->getLineAt(position));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_line_pos(lua::State* L) {
|
||||
auto node = getDocumentNode(L, 1);
|
||||
auto line = lua::tointeger(L, 2);
|
||||
if (auto box = dynamic_cast<TextBox*>(node.node.get())) {
|
||||
return lua::pushinteger(L, box->getLinePos(line));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_inventory(UINode* node, lua::State* L) {
|
||||
if (auto inventory = dynamic_cast<InventoryView*>(node)) {
|
||||
auto inv = inventory->getInventory();
|
||||
@ -221,6 +239,13 @@ static int p_get_track_color(UINode* node, lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_text_color(UINode* node, lua::State* L) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
return lua::pushcolor(L, box->getTextColor());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_is_valid(UINode* node, lua::State* L) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
return lua::pushboolean(L, box->validate());
|
||||
@ -267,6 +292,13 @@ static int p_get_editable(UINode* node, lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_line_numbers(UINode* node, lua::State* L) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
return lua::pushboolean(L, box->isShowLineNumbers());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_src(UINode* node, lua::State* L) {
|
||||
if (auto image = dynamic_cast<Image*>(node)) {
|
||||
return lua::pushstring(L, image->getTexture());
|
||||
@ -342,6 +374,12 @@ static int p_move_into(UINode*, lua::State* L) {
|
||||
static int p_get_focused(UINode* node, lua::State* L) {
|
||||
return lua::pushboolean(L, node->isFocused());
|
||||
}
|
||||
static int p_get_line_at(UINode*, lua::State* L) {
|
||||
return lua::pushcfunction(L, l_get_line_at);
|
||||
}
|
||||
static int p_get_line_pos(UINode*, lua::State* L) {
|
||||
return lua::pushcfunction(L, l_get_line_pos);
|
||||
}
|
||||
|
||||
static int l_gui_getattr(lua::State* L) {
|
||||
auto docname = lua::require_string(L, 1);
|
||||
@ -376,6 +414,9 @@ static int l_gui_getattr(lua::State* L) {
|
||||
{"caret", p_get_caret},
|
||||
{"text", p_get_text},
|
||||
{"editable", p_get_editable},
|
||||
{"lineNumbers", p_get_line_numbers},
|
||||
{"lineAt", p_get_line_at},
|
||||
{"linePos", p_get_line_pos},
|
||||
{"src", p_get_src},
|
||||
{"value", p_get_value},
|
||||
{"min", p_get_min},
|
||||
@ -383,6 +424,7 @@ static int l_gui_getattr(lua::State* L) {
|
||||
{"step", p_get_step},
|
||||
{"trackWidth", p_get_track_width},
|
||||
{"trackColor", p_get_track_color},
|
||||
{"textColor", p_get_text_color},
|
||||
{"checked", p_is_checked},
|
||||
{"page", p_get_page},
|
||||
{"back", p_get_back},
|
||||
@ -462,6 +504,11 @@ static void p_set_editable(UINode* node, lua::State* L, int idx) {
|
||||
box->setEditable(lua::toboolean(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_line_numbers(UINode* node, lua::State* L, int idx) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
box->setShowLineNumbers(lua::toboolean(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_src(UINode* node, lua::State* L, int idx) {
|
||||
if (auto image = dynamic_cast<Image*>(node)) {
|
||||
image->setTexture(lua::require_string(L, idx));
|
||||
@ -497,6 +544,11 @@ static void p_set_track_color(UINode* node, lua::State* L, int idx) {
|
||||
bar->setTrackColor(lua::tocolor(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_text_color(UINode* node, lua::State* L, int idx) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
box->setTextColor(lua::tocolor(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_checked(UINode* node, lua::State* L, int idx) {
|
||||
if (auto box = dynamic_cast<CheckBox*>(node)) {
|
||||
box->setChecked(lua::toboolean(L, idx));
|
||||
@ -556,6 +608,7 @@ static int l_gui_setattr(lua::State* L) {
|
||||
{"hint", p_set_hint},
|
||||
{"text", p_set_text},
|
||||
{"editable", p_set_editable},
|
||||
{"lineNumbers", p_set_line_numbers},
|
||||
{"src", p_set_src},
|
||||
{"caret", p_set_caret},
|
||||
{"value", p_set_value},
|
||||
@ -564,6 +617,7 @@ static int l_gui_setattr(lua::State* L) {
|
||||
{"step", p_set_step},
|
||||
{"trackWidth", p_set_track_width},
|
||||
{"trackColor", p_set_track_color},
|
||||
{"textColor", p_set_text_color},
|
||||
{"checked", p_set_checked},
|
||||
{"page", p_set_page},
|
||||
{"inventory", p_set_inventory},
|
||||
|
||||
@ -94,6 +94,11 @@ static int l_encode(lua::State* L) {
|
||||
return lua::pushlstring(L, bytes, count);
|
||||
}
|
||||
|
||||
static int l_escape(lua::State* L) {
|
||||
auto string = lua::require_lstring(L, 1);
|
||||
return lua::pushstring(L, util::escape(string));
|
||||
}
|
||||
|
||||
const luaL_Reg utf8lib[] = {
|
||||
{"tobytes", lua::wrap<l_tobytes>},
|
||||
{"tostring", lua::wrap<l_tostring>},
|
||||
@ -103,5 +108,6 @@ const luaL_Reg utf8lib[] = {
|
||||
{"upper", lua::wrap<l_upper>},
|
||||
{"lower", lua::wrap<l_lower>},
|
||||
{"encode", lua::wrap<l_encode>},
|
||||
{"escape", lua::wrap<l_escape>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -39,6 +39,7 @@ static void remove_lib_funcs(
|
||||
}
|
||||
|
||||
static void create_libs(State* L, StateType stateType) {
|
||||
openlib(L, "base64", base64lib);
|
||||
openlib(L, "bjson", bjsonlib);
|
||||
openlib(L, "block", blocklib);
|
||||
openlib(L, "core", corelib);
|
||||
@ -143,7 +144,8 @@ State* lua::create_state(const EnginePaths& paths, StateType stateType) {
|
||||
init_state(L, stateType);
|
||||
|
||||
auto resDir = paths.getResourcesFolder();
|
||||
auto src = files::read_string(resDir / fs::u8path("scripts/stdmin.lua"));
|
||||
lua::pop(L, lua::execute(L, 0, src, "<stdmin>"));
|
||||
auto file = resDir / fs::u8path("scripts/stdmin.lua");
|
||||
auto src = files::read_string(file);
|
||||
lua::pop(L, lua::execute(L, 0, src, "core:scripts/stdmin.lua"));
|
||||
return L;
|
||||
}
|
||||
|
||||
@ -145,10 +145,14 @@ static int l_error_handler(lua_State* L) {
|
||||
if (!isstring(L, 1)) { // 'message' not a string?
|
||||
return 1; // keep it intact
|
||||
}
|
||||
if (get_from(L, "debug", "traceback")) {
|
||||
if (getglobal(L, "__vc__error")) {
|
||||
lua_pushvalue(L, 1); // pass error message
|
||||
lua_pushinteger(L, 2); // skip this function and traceback
|
||||
lua_call(L, 2, 1); // call debug.traceback
|
||||
} if (get_from(L, "debug", "traceback")) {
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 2);
|
||||
lua_call(L, 2, 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -172,8 +176,13 @@ int lua::call_nothrow(State* L, int argc, int nresults) {
|
||||
pushcfunction(L, l_error_handler);
|
||||
insert(L, handler_pos);
|
||||
if (lua_pcall(L, argc, LUA_MULTRET, handler_pos)) {
|
||||
log_error(tostring(L, -1));
|
||||
pop(L);
|
||||
auto errorstr = tostring(L, -1);
|
||||
if (errorstr) {
|
||||
log_error(errorstr);
|
||||
pop(L);
|
||||
} else {
|
||||
log_error("");
|
||||
}
|
||||
remove(L, handler_pos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -240,6 +240,11 @@ namespace lua {
|
||||
inline const char* tostring(lua::State* L, int idx) {
|
||||
return lua_tostring(L, idx);
|
||||
}
|
||||
inline std::string_view tolstring(lua::State* L, int idx) {
|
||||
size_t len = 0;
|
||||
auto string = lua_tolstring(L, idx, &len);
|
||||
return std::string_view(string, len);
|
||||
}
|
||||
inline const void* topointer(lua::State* L, int idx) {
|
||||
return lua_topointer(L, idx);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ void scripting::load_script(const fs::path& name, bool throwable) {
|
||||
fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name;
|
||||
std::string src = files::read_string(file);
|
||||
auto L = lua::get_main_state();
|
||||
lua::loadbuffer(L, 0, src, file.u8string());
|
||||
lua::loadbuffer(L, 0, src, "core:scripts/"+name.u8string());
|
||||
if (throwable) {
|
||||
lua::call(L, 0, 0);
|
||||
} else {
|
||||
@ -53,11 +53,14 @@ void scripting::load_script(const fs::path& name, bool throwable) {
|
||||
}
|
||||
|
||||
int scripting::load_script(
|
||||
int env, const std::string& type, const fs::path& file
|
||||
int env,
|
||||
const std::string& type,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
) {
|
||||
std::string src = files::read_string(file);
|
||||
logger.info() << "script (" << type << ") " << file.u8string();
|
||||
return lua::execute(lua::get_main_state(), env, src, file.u8string());
|
||||
return lua::execute(lua::get_main_state(), env, src, fileName);
|
||||
}
|
||||
|
||||
void scripting::initialize(Engine* engine) {
|
||||
@ -657,10 +660,11 @@ void scripting::load_block_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
block_funcs_set& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "block", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "block", file, fileName));
|
||||
funcsset.init = register_event(env, "init", prefix + ".init");
|
||||
funcsset.update = register_event(env, "on_update", prefix + ".update");
|
||||
funcsset.randupdate =
|
||||
@ -677,10 +681,11 @@ void scripting::load_item_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
item_funcs_set& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "item", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "item", file, fileName));
|
||||
funcsset.init = register_event(env, "init", prefix + ".init");
|
||||
funcsset.on_use = register_event(env, "on_use", prefix + ".use");
|
||||
funcsset.on_use_on_block =
|
||||
@ -690,12 +695,12 @@ void scripting::load_item_script(
|
||||
}
|
||||
|
||||
void scripting::load_entity_component(
|
||||
const std::string& name, const fs::path& file
|
||||
const std::string& name, const fs::path& file, const std::string& fileName
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
std::string src = files::read_string(file);
|
||||
logger.info() << "script (component) " << file.u8string();
|
||||
lua::loadbuffer(L, 0, src, "C!" + name);
|
||||
lua::loadbuffer(L, 0, src, fileName);
|
||||
lua::store_in(L, lua::CHUNKS_TABLE, name);
|
||||
}
|
||||
|
||||
@ -703,10 +708,11 @@ void scripting::load_world_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
world_funcs_set& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "world", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "world", file, fileName));
|
||||
register_event(env, "init", prefix + ".init");
|
||||
register_event(env, "on_world_open", prefix + ":.worldopen");
|
||||
register_event(env, "on_world_tick", prefix + ":.worldtick");
|
||||
@ -724,11 +730,12 @@ void scripting::load_layout_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
uidocscript& script
|
||||
) {
|
||||
int env = *senv;
|
||||
|
||||
lua::pop(lua::get_main_state(), load_script(env, "layout", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "layout", file, fileName));
|
||||
script.onopen = register_event(env, "on_open", prefix + ".open");
|
||||
script.onprogress =
|
||||
register_event(env, "on_progress", prefix + ".progress");
|
||||
|
||||
@ -125,11 +125,13 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param prefix pack id
|
||||
/// @param file item script file
|
||||
/// @param fileName script file path using the engine format
|
||||
/// @param funcsset block callbacks set
|
||||
void load_block_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
block_funcs_set& funcsset
|
||||
);
|
||||
|
||||
@ -137,15 +139,25 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param prefix pack id
|
||||
/// @param file item script file
|
||||
/// @param fileName script file path using the engine format
|
||||
/// @param funcsset item callbacks set
|
||||
void load_item_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
item_funcs_set& funcsset
|
||||
);
|
||||
|
||||
void load_entity_component(const std::string& name, const fs::path& file);
|
||||
/// @brief Load component script
|
||||
/// @param name component full name (packid:name)
|
||||
/// @param file component script file path
|
||||
/// @param fileName script file path using the engine format
|
||||
void load_entity_component(
|
||||
const std::string& name,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
|
||||
std::unique_ptr<GeneratorScript> load_generator(
|
||||
const GeneratorDef& def,
|
||||
@ -157,10 +169,12 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param packid content-pack id
|
||||
/// @param file script file path
|
||||
/// @param fileName script file path using the engine format
|
||||
void load_world_script(
|
||||
const scriptenv& env,
|
||||
const std::string& packid,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
world_funcs_set& funcsset
|
||||
);
|
||||
|
||||
@ -168,11 +182,13 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param prefix pack id
|
||||
/// @param file item script file
|
||||
/// @param fileName script file path using the engine format
|
||||
/// @param script document script info
|
||||
void load_layout_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
uidocscript& script
|
||||
);
|
||||
|
||||
|
||||
@ -6,6 +6,10 @@
|
||||
namespace scripting {
|
||||
void load_script(const std::filesystem::path& name, bool throwable);
|
||||
|
||||
[[nodiscard]]
|
||||
int load_script(int env, const std::string& type, const std::filesystem::path& file);
|
||||
[[nodiscard]] int load_script(
|
||||
int env,
|
||||
const std::string& type,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
}
|
||||
|
||||
@ -76,13 +76,16 @@ void scripting::on_frontend_close() {
|
||||
}
|
||||
|
||||
void scripting::load_hud_script(
|
||||
const scriptenv& senv, const std::string& packid, const fs::path& file
|
||||
const scriptenv& senv,
|
||||
const std::string& packid,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
) {
|
||||
int env = *senv;
|
||||
std::string src = files::read_string(file);
|
||||
logger.info() << "loading script " << file.u8string();
|
||||
|
||||
lua::execute(lua::get_main_state(), env, src, file.u8string());
|
||||
lua::execute(lua::get_main_state(), env, src, fileName);
|
||||
|
||||
register_event(env, "init", packid + ":.init");
|
||||
register_event(env, "on_hud_open", packid + ":.hudopen");
|
||||
|
||||
@ -22,7 +22,11 @@ namespace scripting {
|
||||
/// @param env environment id
|
||||
/// @param packid content-pack id
|
||||
/// @param file script file path
|
||||
/// @param fileName script file path using the engine format
|
||||
void load_hud_script(
|
||||
const scriptenv &env, const std::string &packid, const fs::path &file
|
||||
const scriptenv& env,
|
||||
const std::string& packid,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,8 +7,7 @@
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
// TODO: finish
|
||||
std::string util::escape(const std::string& s) {
|
||||
std::string util::escape(std::string_view s) {
|
||||
std::stringstream ss;
|
||||
ss << '"';
|
||||
size_t pos = 0;
|
||||
@ -318,7 +317,7 @@ std::string util::base64_encode(const ubyte* data, size_t size) {
|
||||
ending[i - fullsegments] = data[i];
|
||||
}
|
||||
size_t trailing = size - fullsegments;
|
||||
{
|
||||
if (trailing) {
|
||||
char output[] = "====";
|
||||
output[0] = B64ABC[(ending[0] & 0b11111100) >> 2];
|
||||
output[1] =
|
||||
@ -364,8 +363,8 @@ util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
util::Buffer<ubyte> util::base64_decode(const std::string& str) {
|
||||
return base64_decode(str.c_str(), str.size());
|
||||
util::Buffer<ubyte> util::base64_decode(std::string_view str) {
|
||||
return base64_decode(str.data(), str.size());
|
||||
}
|
||||
|
||||
int util::replaceAll(
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
namespace util {
|
||||
/// @brief Function used for string serialization in text formats
|
||||
std::string escape(const std::string& s);
|
||||
std::string escape(std::string_view s);
|
||||
|
||||
/// @brief Function used for error messages
|
||||
std::string quote(const std::string& s);
|
||||
@ -63,7 +63,7 @@ namespace util {
|
||||
|
||||
std::string base64_encode(const ubyte* data, size_t size);
|
||||
util::Buffer<ubyte> base64_decode(const char* str, size_t size);
|
||||
util::Buffer<ubyte> base64_decode(const std::string& str);
|
||||
util::Buffer<ubyte> base64_decode(std::string_view str);
|
||||
|
||||
std::string tohex(uint64_t value);
|
||||
|
||||
|
||||
@ -258,14 +258,14 @@ void Window::pushScissor(glm::vec4 area) {
|
||||
}
|
||||
scissorStack.push(scissorArea);
|
||||
|
||||
area.z += area.x;
|
||||
area.w += area.y;
|
||||
area.z += glm::ceil(area.x);
|
||||
area.w += glm::ceil(area.y);
|
||||
|
||||
area.x = fmax(area.x, scissorArea.x);
|
||||
area.y = fmax(area.y, scissorArea.y);
|
||||
area.x = glm::max(area.x, scissorArea.x);
|
||||
area.y = glm::max(area.y, scissorArea.y);
|
||||
|
||||
area.z = fmin(area.z, scissorArea.z);
|
||||
area.w = fmin(area.w, scissorArea.w);
|
||||
area.z = glm::min(area.z, scissorArea.z);
|
||||
area.w = glm::min(area.w, scissorArea.w);
|
||||
|
||||
if (area.z < 0.0f || area.w < 0.0f) {
|
||||
glScissor(0, 0, 0, 0);
|
||||
@ -273,8 +273,8 @@ void Window::pushScissor(glm::vec4 area) {
|
||||
glScissor(
|
||||
area.x,
|
||||
Window::height - area.w,
|
||||
std::max(0, int(area.z - area.x)),
|
||||
std::max(0, int(area.w - area.y))
|
||||
std::max(0, static_cast<int>(glm::ceil(area.z - area.x))),
|
||||
std::max(0, static_cast<int>(glm::ceil(area.w - area.y)))
|
||||
);
|
||||
}
|
||||
scissorArea = area;
|
||||
|
||||
@ -15,3 +15,19 @@ TEST(stringutil, utf8) {
|
||||
std::string str2 = util::u32str2str_utf8(u32str);
|
||||
EXPECT_EQ(str, str2);
|
||||
}
|
||||
|
||||
TEST(stringutil, base64) {
|
||||
srand(2019);
|
||||
for (size_t size = 0; size < 30; size++) {
|
||||
auto bytes = std::make_unique<ubyte[]>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
bytes[i] = rand();
|
||||
}
|
||||
auto base64 = util::base64_encode(bytes.get(), size);
|
||||
auto decoded = util::base64_decode(base64);
|
||||
ASSERT_EQ(size, decoded.size());
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
ASSERT_EQ(bytes[i], decoded[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user