commit
ce63b1b27b
@ -56,6 +56,7 @@ Common element methods:
|
||||
| ------------------- | ----------------------------------------------------------------------------------- |
|
||||
| moveInto(container) | moves the element to the specified container (the element is specified, not the id) |
|
||||
| destruct() | removes element |
|
||||
| reposition() | updates the element position based on the `positionfunc` |
|
||||
|
||||
## Containers
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ Examples:
|
||||
- `margin` - element margin. Type: 4D vector
|
||||
*left, top, right, bottom*
|
||||
- `visible` - element visibility. Type: boolean (true/false)
|
||||
- `min-size` - minimal element size. Type: 2D vector.
|
||||
- `position-func` - position supplier for an element (two numbers), called on every parent container size update or on element adding on a container. May be called before *on_hud_open*
|
||||
- `size-func` - element size provider (two numbers), called when the size of the container in which the element is located changes, or when an element is added to the container. Can be called before on_hud_open is called.
|
||||
- `onclick` - lua function called when an element is clicked.
|
||||
@ -65,6 +66,7 @@ Buttons and panels are also containers.
|
||||
Buttons are also panels.
|
||||
|
||||
- `max-length` - maximal length of panel stretching before scrolling (if scrollable = true). Type: number
|
||||
- `min-length` - minimal length of panel. Type: number
|
||||
- `orientation` - panel orientation: horizontal/vertical.
|
||||
|
||||
# Common elements
|
||||
|
||||
@ -56,6 +56,7 @@ document["worlds-panel"]:clear()
|
||||
| ------------------- | ----------------------------------------------------------------------- |
|
||||
| moveInto(container) | перемещает элемент в указанный контейнер (указывается элемент, а не id) |
|
||||
| destruct() | удаляет элемент |
|
||||
| reposition() | обновляет позицию элемента на основе функции позиционирования |
|
||||
|
||||
## Контейнеры
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
- `margin` - внешний отступ элемента. Тип: 4D вектор.
|
||||
Порядок: `"left,top,right,bottom"`
|
||||
- `visible` - видимость элемента. Тип: логический ("true"/"false").
|
||||
- `min-size` - минимальный размер элемента. Тип: 2D вектор.
|
||||
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
||||
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
||||
- `onclick` - lua функция вызываемая при нажатии на элемент.
|
||||
@ -67,6 +68,7 @@
|
||||
|
||||
В число панелей также входят кнопки.
|
||||
- `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число
|
||||
- `min-length` - минимальная длина панели. Тип: число
|
||||
- `orientation` - ориентация панели: horizontal/vertical.
|
||||
|
||||
# Основные элементы
|
||||
|
||||
@ -134,6 +134,10 @@ function add_to_history(text)
|
||||
end
|
||||
|
||||
function submit(text)
|
||||
if #text == 0 then
|
||||
document.prompt.focused = true
|
||||
return
|
||||
end
|
||||
text = text:trim()
|
||||
add_to_history(text)
|
||||
|
||||
@ -204,4 +208,11 @@ function on_open(mode)
|
||||
elseif mode then
|
||||
modes:set(mode)
|
||||
end
|
||||
hud.close("core:ingame_chat")
|
||||
end
|
||||
|
||||
function on_close()
|
||||
time.post_runnable(function()
|
||||
hud.open_permanent("core:ingame_chat")
|
||||
end)
|
||||
end
|
||||
|
||||
8
res/layouts/ingame_chat.xml
Normal file
8
res/layouts/ingame_chat.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<panel size="300,0" margin="0,0,0,70" max-length='300' min-length='0'
|
||||
size-func="gui.get_viewport()[1]/2,-1"
|
||||
padding="0"
|
||||
min-size="0"
|
||||
color="0"
|
||||
interval="0"
|
||||
gravity="bottom-left" interactive="false">
|
||||
</panel>
|
||||
59
res/layouts/ingame_chat.xml.lua
Normal file
59
res/layouts/ingame_chat.xml.lua
Normal file
@ -0,0 +1,59 @@
|
||||
local lines = {}
|
||||
local dead_lines = {}
|
||||
local nextid = 0
|
||||
local timeout = 7
|
||||
local fadeout = 1
|
||||
local initialized = false
|
||||
local max_lines = 15
|
||||
local animation_fps = 30
|
||||
|
||||
local function remove_line(line)
|
||||
document[line[1]]:destruct()
|
||||
time.post_runnable(function() document.root:reposition() end)
|
||||
end
|
||||
|
||||
local function update_line(line, uptime)
|
||||
local diff = uptime - line[2]
|
||||
if diff > timeout then
|
||||
remove_line(line)
|
||||
table.insert(dead_lines, i)
|
||||
elseif diff > timeout-fadeout then
|
||||
local opacity = (timeout - diff) / fadeout
|
||||
document[line[1]].color = {0, 0, 0, opacity * 80}
|
||||
document[line[1].."L"].color = {255, 255, 255, opacity * 255}
|
||||
end
|
||||
end
|
||||
|
||||
events.on("core:chat", function(message)
|
||||
local current_time = time.uptime()
|
||||
local id = 'l'..tostring(nextid)
|
||||
document.root:add(gui.template("chat_line", {id=id}))
|
||||
document.root:reposition()
|
||||
document[id.."L"].text = message
|
||||
nextid = nextid + 1
|
||||
if #lines == max_lines then
|
||||
remove_line(lines[1])
|
||||
table.remove(lines, 1)
|
||||
end
|
||||
table.insert(lines, {id, current_time})
|
||||
end)
|
||||
|
||||
function on_open()
|
||||
if not initialized then
|
||||
initialized = true
|
||||
|
||||
document.root:setInterval(1/animation_fps, function ()
|
||||
local uptime = time.uptime()
|
||||
for _, line in ipairs(lines) do
|
||||
update_line(line, uptime)
|
||||
end
|
||||
if #dead_lines > 0 then
|
||||
for i = #dead_lines, 1, -1 do
|
||||
local index = dead_lines[i]
|
||||
table.remove(lines, i)
|
||||
end
|
||||
dead_lines = {}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
3
res/layouts/templates/chat_line.xml
Normal file
3
res/layouts/templates/chat_line.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<container id='%{id}' color="#00000050" size="-1,20">
|
||||
<label pos='5' id='%{id}L' markup='md'/>
|
||||
</container>
|
||||
@ -51,4 +51,57 @@ function gui_util.reset_local()
|
||||
gui_util.local_dispatchers = {}
|
||||
end
|
||||
|
||||
-- class designed for simple UI-nodes access via properties syntax
|
||||
local Element = {}
|
||||
function Element.new(docname, name)
|
||||
return setmetatable({docname=docname, name=name}, {
|
||||
__index=function(self, k)
|
||||
return gui.getattr(self.docname, self.name, k)
|
||||
end,
|
||||
__newindex=function(self, k, v)
|
||||
gui.setattr(self.docname, self.name, k, v)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- the engine automatically creates an instance for every ui document (layout)
|
||||
local Document = {}
|
||||
function Document.new(docname)
|
||||
return setmetatable({name=docname}, {
|
||||
__index=function(self, k)
|
||||
local elem = Element.new(self.name, k)
|
||||
rawset(self, k, elem)
|
||||
return elem
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local RadioGroup = {}
|
||||
function RadioGroup:set(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(elements, onset, default)
|
||||
local group = setmetatable({
|
||||
elements=elements,
|
||||
callback=onset,
|
||||
current=nil
|
||||
}, {__index=self})
|
||||
group:set(default)
|
||||
return group
|
||||
end
|
||||
setmetatable(RadioGroup, RadioGroup)
|
||||
|
||||
gui_util.Document = Document
|
||||
gui_util.RadioGroup = RadioGroup
|
||||
|
||||
return gui_util
|
||||
@ -260,7 +260,7 @@ console.add_command(
|
||||
"chat text:str",
|
||||
"Send chat message",
|
||||
function (args, kwargs)
|
||||
console.log("[you] "..args[1])
|
||||
console.chat("[you] "..args[1])
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
@ -146,64 +146,16 @@ function events.emit(event, ...)
|
||||
return result
|
||||
end
|
||||
|
||||
-- class designed for simple UI-nodes access via properties syntax
|
||||
local Element = {}
|
||||
function Element.new(docname, name)
|
||||
return setmetatable({docname=docname, name=name}, {
|
||||
__index=function(self, k)
|
||||
return gui.getattr(self.docname, self.name, k)
|
||||
end,
|
||||
__newindex=function(self, k, v)
|
||||
gui.setattr(self.docname, self.name, k, v)
|
||||
end
|
||||
})
|
||||
end
|
||||
gui_util = require "core:internal/gui_util"
|
||||
|
||||
-- the engine automatically creates an instance for every ui document (layout)
|
||||
Document = {}
|
||||
function Document.new(docname)
|
||||
return setmetatable({name=docname}, {
|
||||
__index=function(self, k)
|
||||
local elem = Element.new(self.name, k)
|
||||
rawset(self, k, elem)
|
||||
return elem
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local _RadioGroup = {}
|
||||
function _RadioGroup:set(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(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
|
||||
Document = gui_util.Document
|
||||
RadioGroup = gui_util.RadioGroup
|
||||
__vc_page_loader = gui_util.load_page
|
||||
|
||||
_GUI_ROOT = Document.new("core:root")
|
||||
_MENU = _GUI_ROOT.menu
|
||||
menu = _MENU
|
||||
|
||||
gui_util = require "core:gui_util"
|
||||
__vc_page_loader = gui_util.load_page
|
||||
|
||||
--- Console library extension ---
|
||||
console.cheats = {}
|
||||
|
||||
@ -225,6 +177,11 @@ function console.log(...)
|
||||
log_element:paste(text)
|
||||
end
|
||||
|
||||
function console.chat(...)
|
||||
console.log(...)
|
||||
events.emit("core:chat", ...)
|
||||
end
|
||||
|
||||
function gui.template(name, params)
|
||||
local text = file.read(file.find("layouts/templates/"..name..".xml"))
|
||||
for k,v in pairs(params) do
|
||||
@ -394,6 +351,7 @@ function __vc_on_hud_open()
|
||||
hud.pause()
|
||||
end
|
||||
end)
|
||||
hud.open_permanent("core:ingame_chat")
|
||||
end
|
||||
|
||||
local RULES_FILE = "world:rules.toml"
|
||||
|
||||
@ -217,6 +217,7 @@ void Engine::renderFrame() {
|
||||
Viewport viewport(Window::width, Window::height);
|
||||
DrawContext ctx(nullptr, viewport, nullptr);
|
||||
gui->draw(ctx, *assets);
|
||||
gui->postAct();
|
||||
}
|
||||
|
||||
void Engine::saveSettings() {
|
||||
|
||||
@ -178,12 +178,6 @@ void GUI::actFocused() {
|
||||
}
|
||||
|
||||
void GUI::act(float delta, const Viewport& vp) {
|
||||
while (!postRunnables.empty()) {
|
||||
runnable callback = postRunnables.back();
|
||||
postRunnables.pop();
|
||||
callback();
|
||||
}
|
||||
|
||||
container->setSize(vp.size());
|
||||
container->act(delta);
|
||||
auto prevfocus = focus;
|
||||
@ -206,6 +200,14 @@ void GUI::act(float delta, const Viewport& vp) {
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::postAct() {
|
||||
while (!postRunnables.empty()) {
|
||||
runnable callback = postRunnables.back();
|
||||
postRunnables.pop();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
||||
auto ctx = pctx.sub(batch2D.get());
|
||||
|
||||
|
||||
@ -106,6 +106,8 @@ namespace gui {
|
||||
/// @param assets active assets storage
|
||||
void draw(const DrawContext& pctx, const Assets& assets);
|
||||
|
||||
void postAct();
|
||||
|
||||
/// @brief Add element to the main container
|
||||
/// @param node UI element
|
||||
void add(std::shared_ptr<UINode> node);
|
||||
|
||||
@ -23,6 +23,14 @@ int Panel::getMaxLength() const {
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
void Panel::setMinLength(int value) {
|
||||
minLength = value;
|
||||
}
|
||||
|
||||
int Panel::getMinLength() const {
|
||||
return minLength;
|
||||
}
|
||||
|
||||
void Panel::setPadding(glm::vec4 padding) {
|
||||
this->padding = padding;
|
||||
refresh();
|
||||
@ -34,9 +42,11 @@ glm::vec4 Panel::getPadding() const {
|
||||
|
||||
void Panel::cropToContent() {
|
||||
if (maxLength > 0.0f) {
|
||||
setSize(glm::vec2(getSize().x, glm::min(maxLength, actualLength)));
|
||||
setSize(glm::vec2(
|
||||
getSize().x, glm::max(minLength, glm::min(maxLength, actualLength))
|
||||
));
|
||||
} else {
|
||||
setSize(glm::vec2(getSize().x, actualLength));
|
||||
setSize(glm::vec2(getSize().x, glm::max(minLength, actualLength)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +62,11 @@ void Panel::add(const std::shared_ptr<UINode> &node) {
|
||||
fullRefresh();
|
||||
}
|
||||
|
||||
void Panel::remove(const std::shared_ptr<UINode> &node) {
|
||||
Container::remove(node);
|
||||
fullRefresh();
|
||||
}
|
||||
|
||||
void Panel::refresh() {
|
||||
UINode::refresh();
|
||||
std::stable_sort(nodes.begin(), nodes.end(), [](auto a, auto b) {
|
||||
|
||||
@ -9,6 +9,7 @@ namespace gui {
|
||||
Orientation orientation = Orientation::vertical;
|
||||
glm::vec4 padding {2.0f};
|
||||
float interval = 2.0f;
|
||||
int minLength = 0;
|
||||
int maxLength = 0;
|
||||
public:
|
||||
Panel(
|
||||
@ -24,6 +25,7 @@ namespace gui {
|
||||
Orientation getOrientation() const;
|
||||
|
||||
virtual void add(const std::shared_ptr<UINode>& node) override;
|
||||
virtual void remove(const std::shared_ptr<UINode>& node) override;
|
||||
|
||||
virtual void refresh() override;
|
||||
virtual void fullRefresh() override;
|
||||
@ -31,6 +33,9 @@ namespace gui {
|
||||
virtual void setMaxLength(int value);
|
||||
int getMaxLength() const;
|
||||
|
||||
virtual void setMinLength(int value);
|
||||
int getMinLength() const;
|
||||
|
||||
virtual void setPadding(glm::vec4 padding);
|
||||
glm::vec4 getPadding() const;
|
||||
};
|
||||
|
||||
@ -289,12 +289,16 @@ const std::string& UINode::getId() const {
|
||||
}
|
||||
|
||||
void UINode::reposition() {
|
||||
if (sizefunc) {
|
||||
auto newSize = sizefunc();
|
||||
setSize(
|
||||
{newSize.x < 0 ? size.x : newSize.x,
|
||||
newSize.y < 0 ? size.y : newSize.y}
|
||||
);
|
||||
}
|
||||
if (positionfunc) {
|
||||
setPos(positionfunc());
|
||||
}
|
||||
if (sizefunc) {
|
||||
setSize(sizefunc());
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setGravity(Gravity gravity) {
|
||||
|
||||
@ -91,6 +91,9 @@ static void _readUINode(
|
||||
if (element.has("pos")) {
|
||||
node.setPos(element.attr("pos").asVec2());
|
||||
}
|
||||
if (element.has("min-size")) {
|
||||
node.setMinSize(element.attr("min-size").asVec2());
|
||||
}
|
||||
if (element.has("size")) {
|
||||
node.setSize(element.attr("size").asVec2());
|
||||
}
|
||||
@ -220,6 +223,9 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
|
||||
if (element.has("max-length")) {
|
||||
panel.setMaxLength(element.attr("max-length").asInt());
|
||||
}
|
||||
if (element.has("min-length")) {
|
||||
panel.setMinLength(element.attr("min-length").asInt());
|
||||
}
|
||||
if (element.has("orientation")) {
|
||||
auto &oname = element.attr("orientation").getText();
|
||||
if (oname == "horizontal") {
|
||||
|
||||
@ -101,6 +101,12 @@ static int l_node_destruct(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_node_reposition(lua::State* L) {
|
||||
auto docnode = get_document_node(L);
|
||||
docnode.node->reposition();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_container_clear(lua::State* L) {
|
||||
auto node = get_document_node(L, 1);
|
||||
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) {
|
||||
@ -328,6 +334,10 @@ static int p_get_destruct(UINode*, lua::State* L) {
|
||||
return lua::pushcfunction(L, lua::wrap<l_node_destruct>);
|
||||
}
|
||||
|
||||
static int p_get_reposition(UINode*, lua::State* L) {
|
||||
return lua::pushcfunction(L, lua::wrap<l_node_reposition>);
|
||||
}
|
||||
|
||||
static int p_get_clear(UINode* node, lua::State* L) {
|
||||
if (dynamic_cast<Container*>(node)) {
|
||||
return lua::pushcfunction(L, lua::wrap<l_container_clear>);
|
||||
@ -424,6 +434,7 @@ static int l_gui_getattr(lua::State* L) {
|
||||
{"moveInto", p_move_into},
|
||||
{"add", p_get_add},
|
||||
{"destruct", p_get_destruct},
|
||||
{"reposition", p_get_reposition},
|
||||
{"clear", p_get_clear},
|
||||
{"setInterval", p_set_interval},
|
||||
{"placeholder", p_get_placeholder},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user