diff --git a/res/layouts/console.xml.lua b/res/layouts/console.xml.lua
index 11d7bd14..7e41ddf6 100644
--- a/res/layouts/console.xml.lua
+++ b/res/layouts/console.xml.lua
@@ -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
diff --git a/res/layouts/ingame_chat.xml b/res/layouts/ingame_chat.xml
new file mode 100644
index 00000000..45ad4749
--- /dev/null
+++ b/res/layouts/ingame_chat.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/res/layouts/ingame_chat.xml.lua b/res/layouts/ingame_chat.xml.lua
new file mode 100644
index 00000000..5e08ac9d
--- /dev/null
+++ b/res/layouts/ingame_chat.xml.lua
@@ -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
diff --git a/res/layouts/templates/chat_line.xml b/res/layouts/templates/chat_line.xml
new file mode 100644
index 00000000..b9939218
--- /dev/null
+++ b/res/layouts/templates/chat_line.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/res/modules/internal/gui_util.lua b/res/modules/internal/gui_util.lua
index 31f542dd..3cf84a33 100644
--- a/res/modules/internal/gui_util.lua
+++ b/res/modules/internal/gui_util.lua
@@ -95,7 +95,7 @@ function RadioGroup:__call(elements, onset, default)
elements=elements,
callback=onset,
current=nil
- }, {__index=RadioGroup})
+ }, {__index=self})
group:set(default)
return group
end
diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua
index d9190bce..78228254 100644
--- a/res/scripts/stdlib.lua
+++ b/res/scripts/stdlib.lua
@@ -351,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"
diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp
index 00382420..ce29e8c1 100644
--- a/src/engine/Engine.cpp
+++ b/src/engine/Engine.cpp
@@ -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() {
diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp
index 95d65540..2cc28024 100644
--- a/src/graphics/ui/GUI.cpp
+++ b/src/graphics/ui/GUI.cpp
@@ -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());
diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp
index f8cea3ef..cb6b037e 100644
--- a/src/graphics/ui/GUI.hpp
+++ b/src/graphics/ui/GUI.hpp
@@ -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 node);
diff --git a/src/graphics/ui/elements/Panel.cpp b/src/graphics/ui/elements/Panel.cpp
index 7bbc4231..d90b8830 100644
--- a/src/graphics/ui/elements/Panel.cpp
+++ b/src/graphics/ui/elements/Panel.cpp
@@ -62,6 +62,11 @@ void Panel::add(const std::shared_ptr &node) {
fullRefresh();
}
+void Panel::remove(const std::shared_ptr &node) {
+ Container::remove(node);
+ fullRefresh();
+}
+
void Panel::refresh() {
UINode::refresh();
std::stable_sort(nodes.begin(), nodes.end(), [](auto a, auto b) {
diff --git a/src/graphics/ui/elements/Panel.hpp b/src/graphics/ui/elements/Panel.hpp
index 7e9759dd..5a6399ce 100644
--- a/src/graphics/ui/elements/Panel.hpp
+++ b/src/graphics/ui/elements/Panel.hpp
@@ -25,6 +25,7 @@ namespace gui {
Orientation getOrientation() const;
virtual void add(const std::shared_ptr& node) override;
+ virtual void remove(const std::shared_ptr& node) override;
virtual void refresh() override;
virtual void fullRefresh() override;