Merge pull request #443 from MihailRis/notifications

Notifications
This commit is contained in:
MihailRis 2025-01-18 07:36:12 +03:00 committed by GitHub
commit ce63b1b27b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 208 additions and 64 deletions

View File

@ -56,6 +56,7 @@ Common element methods:
| ------------------- | ----------------------------------------------------------------------------------- | | ------------------- | ----------------------------------------------------------------------------------- |
| moveInto(container) | moves the element to the specified container (the element is specified, not the id) | | moveInto(container) | moves the element to the specified container (the element is specified, not the id) |
| destruct() | removes element | | destruct() | removes element |
| reposition() | updates the element position based on the `positionfunc` |
## Containers ## Containers

View File

@ -35,6 +35,7 @@ Examples:
- `margin` - element margin. Type: 4D vector - `margin` - element margin. Type: 4D vector
*left, top, right, bottom* *left, top, right, bottom*
- `visible` - element visibility. Type: boolean (true/false) - `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* - `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. - `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. - `onclick` - lua function called when an element is clicked.
@ -65,6 +66,7 @@ Buttons and panels are also containers.
Buttons are also panels. Buttons are also panels.
- `max-length` - maximal length of panel stretching before scrolling (if scrollable = true). Type: number - `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. - `orientation` - panel orientation: horizontal/vertical.
# Common elements # Common elements

View File

@ -56,6 +56,7 @@ document["worlds-panel"]:clear()
| ------------------- | ----------------------------------------------------------------------- | | ------------------- | ----------------------------------------------------------------------- |
| moveInto(container) | перемещает элемент в указанный контейнер (указывается элемент, а не id) | | moveInto(container) | перемещает элемент в указанный контейнер (указывается элемент, а не id) |
| destruct() | удаляет элемент | | destruct() | удаляет элемент |
| reposition() | обновляет позицию элемента на основе функции позиционирования |
## Контейнеры ## Контейнеры

View File

@ -39,6 +39,7 @@
- `margin` - внешний отступ элемента. Тип: 4D вектор. - `margin` - внешний отступ элемента. Тип: 4D вектор.
Порядок: `"left,top,right,bottom"` Порядок: `"left,top,right,bottom"`
- `visible` - видимость элемента. Тип: логический ("true"/"false"). - `visible` - видимость элемента. Тип: логический ("true"/"false").
- `min-size` - минимальный размер элемента. Тип: 2D вектор.
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open. - `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open. - `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
- `onclick` - lua функция вызываемая при нажатии на элемент. - `onclick` - lua функция вызываемая при нажатии на элемент.
@ -67,6 +68,7 @@
В число панелей также входят кнопки. В число панелей также входят кнопки.
- `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число - `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число
- `min-length` - минимальная длина панели. Тип: число
- `orientation` - ориентация панели: horizontal/vertical. - `orientation` - ориентация панели: horizontal/vertical.
# Основные элементы # Основные элементы

View File

@ -134,6 +134,10 @@ function add_to_history(text)
end end
function submit(text) function submit(text)
if #text == 0 then
document.prompt.focused = true
return
end
text = text:trim() text = text:trim()
add_to_history(text) add_to_history(text)
@ -204,4 +208,11 @@ function on_open(mode)
elseif mode then elseif mode then
modes:set(mode) modes:set(mode)
end end
hud.close("core:ingame_chat")
end
function on_close()
time.post_runnable(function()
hud.open_permanent("core:ingame_chat")
end)
end end

View 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>

View 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

View File

@ -0,0 +1,3 @@
<container id='%{id}' color="#00000050" size="-1,20">
<label pos='5' id='%{id}L' markup='md'/>
</container>

View File

@ -51,4 +51,57 @@ function gui_util.reset_local()
gui_util.local_dispatchers = {} gui_util.local_dispatchers = {}
end 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 return gui_util

View File

@ -260,7 +260,7 @@ console.add_command(
"chat text:str", "chat text:str",
"Send chat message", "Send chat message",
function (args, kwargs) function (args, kwargs)
console.log("[you] "..args[1]) console.chat("[you] "..args[1])
end end
) )

View File

@ -146,64 +146,16 @@ function events.emit(event, ...)
return result return result
end end
-- class designed for simple UI-nodes access via properties syntax gui_util = require "core:internal/gui_util"
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) Document = gui_util.Document
Document = {} RadioGroup = gui_util.RadioGroup
function Document.new(docname) __vc_page_loader = gui_util.load_page
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
_GUI_ROOT = Document.new("core:root") _GUI_ROOT = Document.new("core:root")
_MENU = _GUI_ROOT.menu _MENU = _GUI_ROOT.menu
menu = _MENU menu = _MENU
gui_util = require "core:gui_util"
__vc_page_loader = gui_util.load_page
--- Console library extension --- --- Console library extension ---
console.cheats = {} console.cheats = {}
@ -225,6 +177,11 @@ function console.log(...)
log_element:paste(text) log_element:paste(text)
end end
function console.chat(...)
console.log(...)
events.emit("core:chat", ...)
end
function gui.template(name, params) function gui.template(name, params)
local text = file.read(file.find("layouts/templates/"..name..".xml")) local text = file.read(file.find("layouts/templates/"..name..".xml"))
for k,v in pairs(params) do for k,v in pairs(params) do
@ -394,6 +351,7 @@ function __vc_on_hud_open()
hud.pause() hud.pause()
end end
end) end)
hud.open_permanent("core:ingame_chat")
end end
local RULES_FILE = "world:rules.toml" local RULES_FILE = "world:rules.toml"

View File

@ -217,6 +217,7 @@ void Engine::renderFrame() {
Viewport viewport(Window::width, Window::height); Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, nullptr); DrawContext ctx(nullptr, viewport, nullptr);
gui->draw(ctx, *assets); gui->draw(ctx, *assets);
gui->postAct();
} }
void Engine::saveSettings() { void Engine::saveSettings() {

View File

@ -178,12 +178,6 @@ void GUI::actFocused() {
} }
void GUI::act(float delta, const Viewport& vp) { void GUI::act(float delta, const Viewport& vp) {
while (!postRunnables.empty()) {
runnable callback = postRunnables.back();
postRunnables.pop();
callback();
}
container->setSize(vp.size()); container->setSize(vp.size());
container->act(delta); container->act(delta);
auto prevfocus = focus; 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) { void GUI::draw(const DrawContext& pctx, const Assets& assets) {
auto ctx = pctx.sub(batch2D.get()); auto ctx = pctx.sub(batch2D.get());

View File

@ -106,6 +106,8 @@ namespace gui {
/// @param assets active assets storage /// @param assets active assets storage
void draw(const DrawContext& pctx, const Assets& assets); void draw(const DrawContext& pctx, const Assets& assets);
void postAct();
/// @brief Add element to the main container /// @brief Add element to the main container
/// @param node UI element /// @param node UI element
void add(std::shared_ptr<UINode> node); void add(std::shared_ptr<UINode> node);

View File

@ -23,6 +23,14 @@ int Panel::getMaxLength() const {
return maxLength; return maxLength;
} }
void Panel::setMinLength(int value) {
minLength = value;
}
int Panel::getMinLength() const {
return minLength;
}
void Panel::setPadding(glm::vec4 padding) { void Panel::setPadding(glm::vec4 padding) {
this->padding = padding; this->padding = padding;
refresh(); refresh();
@ -34,9 +42,11 @@ glm::vec4 Panel::getPadding() const {
void Panel::cropToContent() { void Panel::cropToContent() {
if (maxLength > 0.0f) { 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 { } 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(); fullRefresh();
} }
void Panel::remove(const std::shared_ptr<UINode> &node) {
Container::remove(node);
fullRefresh();
}
void Panel::refresh() { void Panel::refresh() {
UINode::refresh(); UINode::refresh();
std::stable_sort(nodes.begin(), nodes.end(), [](auto a, auto b) { std::stable_sort(nodes.begin(), nodes.end(), [](auto a, auto b) {

View File

@ -9,6 +9,7 @@ namespace gui {
Orientation orientation = Orientation::vertical; Orientation orientation = Orientation::vertical;
glm::vec4 padding {2.0f}; glm::vec4 padding {2.0f};
float interval = 2.0f; float interval = 2.0f;
int minLength = 0;
int maxLength = 0; int maxLength = 0;
public: public:
Panel( Panel(
@ -24,6 +25,7 @@ namespace gui {
Orientation getOrientation() const; Orientation getOrientation() const;
virtual void add(const std::shared_ptr<UINode>& node) override; 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 refresh() override;
virtual void fullRefresh() override; virtual void fullRefresh() override;
@ -31,6 +33,9 @@ namespace gui {
virtual void setMaxLength(int value); virtual void setMaxLength(int value);
int getMaxLength() const; int getMaxLength() const;
virtual void setMinLength(int value);
int getMinLength() const;
virtual void setPadding(glm::vec4 padding); virtual void setPadding(glm::vec4 padding);
glm::vec4 getPadding() const; glm::vec4 getPadding() const;
}; };

View File

@ -289,12 +289,16 @@ const std::string& UINode::getId() const {
} }
void UINode::reposition() { 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) { if (positionfunc) {
setPos(positionfunc()); setPos(positionfunc());
} }
if (sizefunc) {
setSize(sizefunc());
}
} }
void UINode::setGravity(Gravity gravity) { void UINode::setGravity(Gravity gravity) {

View File

@ -91,6 +91,9 @@ static void _readUINode(
if (element.has("pos")) { if (element.has("pos")) {
node.setPos(element.attr("pos").asVec2()); node.setPos(element.attr("pos").asVec2());
} }
if (element.has("min-size")) {
node.setMinSize(element.attr("min-size").asVec2());
}
if (element.has("size")) { if (element.has("size")) {
node.setSize(element.attr("size").asVec2()); 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")) { if (element.has("max-length")) {
panel.setMaxLength(element.attr("max-length").asInt()); panel.setMaxLength(element.attr("max-length").asInt());
} }
if (element.has("min-length")) {
panel.setMinLength(element.attr("min-length").asInt());
}
if (element.has("orientation")) { if (element.has("orientation")) {
auto &oname = element.attr("orientation").getText(); auto &oname = element.attr("orientation").getText();
if (oname == "horizontal") { if (oname == "horizontal") {

View File

@ -101,6 +101,12 @@ static int l_node_destruct(lua::State* L) {
return 0; 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) { static int l_container_clear(lua::State* L) {
auto node = get_document_node(L, 1); auto node = get_document_node(L, 1);
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) { 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>); 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) { static int p_get_clear(UINode* node, lua::State* L) {
if (dynamic_cast<Container*>(node)) { if (dynamic_cast<Container*>(node)) {
return lua::pushcfunction(L, lua::wrap<l_container_clear>); 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}, {"moveInto", p_move_into},
{"add", p_get_add}, {"add", p_get_add},
{"destruct", p_get_destruct}, {"destruct", p_get_destruct},
{"reposition", p_get_reposition},
{"clear", p_get_clear}, {"clear", p_get_clear},
{"setInterval", p_set_interval}, {"setInterval", p_set_interval},
{"placeholder", p_get_placeholder}, {"placeholder", p_get_placeholder},