From a0e1d4acfe66f08329176bc3388eadff6d895b21 Mon Sep 17 00:00:00 2001 From: Ezhulka Date: Fri, 31 Jul 2026 20:57:07 +0300 Subject: [PATCH 1/5] ProgressBar UI element --- src/graphics/ui/elements/ProgressBar.cpp | 177 +++++++++++++++++++++++ src/graphics/ui/elements/ProgressBar.hpp | 65 +++++++++ 2 files changed, 242 insertions(+) create mode 100644 src/graphics/ui/elements/ProgressBar.cpp create mode 100644 src/graphics/ui/elements/ProgressBar.hpp diff --git a/src/graphics/ui/elements/ProgressBar.cpp b/src/graphics/ui/elements/ProgressBar.cpp new file mode 100644 index 000000000..19b8ca735 --- /dev/null +++ b/src/graphics/ui/elements/ProgressBar.cpp @@ -0,0 +1,177 @@ +#include "ProgressBar.hpp" + +#include +#include +#include + +#include "graphics/core/Batch2D.hpp" +#include "graphics/core/DrawContext.hpp" +#include "graphics/core/Font.hpp" +#include "assets/Assets.hpp" +#include "maths/voxmaths.hpp" +#include "util/stringutil.hpp" + +using namespace gui; + +ProgressBar::ProgressBar( + GUI& gui, + double min, + double max, + double value, + int barThickness +) : UINode(gui, glm::vec2(100, 16)), + min(min), + max(max), + value(value), + displayValue(value), + barThickness(barThickness) +{ + if (barThickness <= 0) barThickness = static_cast(size.y); + setColor(glm::vec4()); + setInteractive(false); +} + +void ProgressBar::act(float delta) { + if (supplier) value = supplier(); + if (smoothTransition) { + double diff = value - displayValue; + if (std::fabs(diff) < 2.0) { + displayValue = value; + } else { + double step = smoothSpeed * delta; + if (std::fabs(diff) < step) { + displayValue = value; + } else { + displayValue += std::copysign(step, diff); + } + } + } else { + displayValue = value; + } +} + +void ProgressBar::draw(const DrawContext& pctx, const Assets& assets) { + glm::vec2 pos = calcPos(); + auto batch = pctx.getBatch2D(); + batch->texture(nullptr); + + batch->setColor(bgColor); + batch->rect(pos.x, pos.y, size.x, size.y); + + float t = 0.0f; + if (max > min) { + t = glm::clamp( + static_cast((displayValue - min) / (max - min)), 0.0f, 1.0f + ); + } + + if (t > 0.0f) { + batch->setColor(barColor); + if (orientation == Orientation::HORIZONTAL) { + batch->rect(pos.x, pos.y, size.x * t, size.y); + } else { + batch->rect(pos.x, pos.y + size.y * (1.0f - t), size.x, size.y * t); + } + } + + if (textVisible && !textFormat.empty()) { + auto font = assets.get("normal"); + if (font) { + double pct = (max > min) ? (displayValue - min) / (max - min) * 100.0 : 0.0; + char buf[64]; + snprintf(buf, sizeof(buf), textFormat.c_str(), displayValue, pct); + auto wtext = util::str2wstr_utf8(buf); + int textWidth = font->calcWidth(wtext); + int lineHeight = font->getLineHeight(); + int tx = static_cast(pos.x + (size.x - textWidth) * 0.5f); + int ty = static_cast(pos.y + (size.y - lineHeight) * 0.5f); + batch->setColor(textColor); + font->draw(*batch, wtext, tx, ty, nullptr, 0); + } + } +} + +void ProgressBar::setSupplier(doublesupplier supplier) { + this->supplier = std::move(supplier); +} + +double ProgressBar::getValue() const { + return value; +} + +double ProgressBar::getMin() const { + return min; +} + +double ProgressBar::getMax() const { + return max; +} + +double ProgressBar::getProgress() const { + return (max - min != 0.0) ? (value - min) / (max - min) : 0.0; +} + +glm::vec4 ProgressBar::getBarColor() const { + return barColor; +} + +glm::vec4 ProgressBar::getBgColor() const { + return bgColor; +} + +glm::vec4 ProgressBar::getTextColor() const { + return textColor; +} + +const std::string& ProgressBar::getTextFormat() const { + return textFormat; +} + +bool ProgressBar::isSmooth() const { + return smoothTransition; +} + +Orientation ProgressBar::getOrientation() const { + return orientation; +} + +void ProgressBar::setValue(double x) { + value = x; +} + +void ProgressBar::setMin(double x) { + min = x; +} + +void ProgressBar::setMax(double x) { + max = x; +} + +void ProgressBar::setBarThickness(int thickness) { + barThickness = thickness; +} + +void ProgressBar::setBarColor(glm::vec4 color) { + barColor = color; +} + +void ProgressBar::setBgColor(glm::vec4 color) { + bgColor = color; +} + +void ProgressBar::setTextColor(glm::vec4 color) { + textColor = color; +} + +void ProgressBar::setTextFormat(const std::string& fmt) { + textFormat = fmt; + textVisible = !fmt.empty(); +} + +void ProgressBar::setSmooth(bool flag) { + smoothTransition = flag; +} + +void ProgressBar::setOrientation(Orientation ori) { + orientation = ori; +} diff --git a/src/graphics/ui/elements/ProgressBar.hpp b/src/graphics/ui/elements/ProgressBar.hpp new file mode 100644 index 000000000..46f71385a --- /dev/null +++ b/src/graphics/ui/elements/ProgressBar.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include + +#include "UINode.hpp" +#include "commons.hpp" + +namespace gui { + class ProgressBar : public UINode { + protected: + glm::vec4 barColor {0.2f, 0.6f, 1.0f, 0.8f}; + glm::vec4 bgColor {0.0f, 0.0f, 0.0f, 0.4f}; + glm::vec4 textColor {1.0f, 1.0f, 1.0f, 1.0f}; + doublesupplier supplier = nullptr; + double min; + double max; + double value; + double displayValue; + int barThickness; + float smoothSpeed = 120.0f; + Orientation orientation = Orientation::HORIZONTAL; + bool textVisible = false; + bool smoothTransition = false; + std::string textFormat; + public: + /** min, max, value, displayValue — числовой диапазон и отображаемое значение + * textFormat — формат sprintf. Аргументы: + * %1$ — displayValue (сырое значение в диапазоне min–max) + * %2$ — прогресс в процентах 0..100 + * Пример: "%.0f%%" покажет "45%", "%2$.0f%%" покажет "62%" при min=10 max=50 + */ + ProgressBar( + GUI& gui, + double min = 0.0, + double max = 100.0, + double value = 0.0, + int barThickness = -1 + ); + virtual void act(float delta) override; + virtual void draw(const DrawContext& pctx, const Assets& assets) override; + + virtual void setSupplier(doublesupplier); + virtual double getValue() const; + virtual double getMin() const; + virtual double getMax() const; + virtual double getProgress() const; + virtual glm::vec4 getBarColor() const; + virtual glm::vec4 getBgColor() const; + virtual glm::vec4 getTextColor() const; + virtual Orientation getOrientation() const; + virtual const std::string& getTextFormat() const; + virtual bool isSmooth() const; + + virtual void setValue(double); + virtual void setMin(double); + virtual void setMax(double); + virtual void setBarThickness(int); + virtual void setBarColor(glm::vec4); + virtual void setBgColor(glm::vec4); + virtual void setTextColor(glm::vec4); + virtual void setTextFormat(const std::string& fmt); + virtual void setSmooth(bool flag); + virtual void setOrientation(Orientation); + }; +} From d04fb4f8867f00f3f37d1e0fc101e4ece80cf237 Mon Sep 17 00:00:00 2001 From: Ezhulka Date: Fri, 31 Jul 2026 21:05:57 +0300 Subject: [PATCH 2/5] xml and lua support --- src/graphics/ui/gui_xml.cpp | 43 +++++++++++++ src/logic/scripting/lua/libs/libgui.cpp | 86 +++++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index e91b4553f..38e90d43a 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -15,6 +15,7 @@ #include "elements/Menu.hpp" #include "elements/ModelViewer.hpp" #include "elements/Panel.hpp" +#include "elements/ProgressBar.hpp" #include "elements/SelectBox.hpp" #include "elements/SplitBox.hpp" #include "elements/TextBox.hpp" @@ -716,6 +717,47 @@ static std::shared_ptr read_track_bar( return bar; } +static std::shared_ptr read_progress_bar( + const UiXmlReader& reader, + const xml::xmlelement& element +) { + const auto& env = reader.getEnvironment(); + const auto& file = reader.getFilename(); + double minv = element.attr("min", "0.0").asFloat(); + double maxv = element.attr("max", "100.0").asFloat(); + double def = element.attr("value", "0.0").asFloat(); + int thickness = element.attr("bar-thickness", "-1").asInt(); + auto bar = std::make_shared( + reader.getGUI(), minv, maxv, def, thickness + ); + read_uinode(reader, element, *bar); + if (element.has("supplier")) { + bar->setSupplier(scripting::create_number_supplier(env, element.attr("supplier").getText(), file)); + } + if (element.has("bar-color")) { + bar->setBarColor(element.attr("bar-color").asColor()); + } + if (element.has("bg-color")) { + bar->setBgColor(element.attr("bg-color").asColor()); + } + if (element.has("text-format")) { + bar->setTextFormat(element.attr("text-format").getText()); + } + if (element.has("text-color")) { + bar->setTextColor(element.attr("text-color").asColor()); + } + if (element.has("smooth")) { + bar->setSmooth(element.attr("smooth").asBool()); + } + if (element.has("orientation")) { + auto& oname = element.attr("orientation").getText(); + if (oname == "vertical") { + bar->setOrientation(Orientation::VERTICAL); + } + } + return bar; +} + static std::shared_ptr read_input_bind_box( UiXmlReader& reader, const xml::xmlelement& element ) { @@ -901,6 +943,7 @@ UiXmlReader::UiXmlReader(gui::GUI& gui, const scriptenv& env) add("select", read_select); add("textbox", read_text_box); add("pagebox", read_page_box); + add("progressbar", read_progress_bar); add("splitbox", read_split_box); add("checkbox", read_check_box); add("trackbar", read_track_bar); diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 057dc205e..6bbac4f6f 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -12,6 +12,7 @@ #include "graphics/ui/elements/InventoryView.hpp" #include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/elements/Panel.hpp" +#include "graphics/ui/elements/ProgressBar.hpp" #include "graphics/ui/elements/TextBox.hpp" #include "graphics/ui/elements/TrackBar.hpp" #include "graphics/ui/elements/InlineFrame.hpp" @@ -263,15 +264,54 @@ static int p_is_checked(UINode* node, lua::State* L) { static int p_get_value(UINode* node, lua::State* L) { if (auto bar = dynamic_cast(node)) { return lua::pushnumber(L, bar->getValue()); + } else if (auto bar = dynamic_cast(node)) { + return lua::pushnumber(L, bar->getValue()); } else if (auto box = dynamic_cast(node)) { return lua::pushstring(L, box->getSelected().value); } return 0; } +static int p_get_progress(UINode* node, lua::State* L) { + if (auto bar = dynamic_cast(node)) { + return lua::pushnumber(L, bar->getProgress()); + } + return 0; +} + +static int p_get_bar_color(UINode* node, lua::State* L) { + if (auto bar = dynamic_cast(node)) { + return lua::pushcolor(L, bar->getBarColor()); + } + return 0; +} + +static int p_get_bg_color(UINode* node, lua::State* L) { + if (auto bar = dynamic_cast(node)) { + return lua::pushcolor(L, bar->getBgColor()); + } + return 0; +} + +static int p_get_text_format(UINode* node, lua::State* L) { + if (auto bar = dynamic_cast(node)) { + return lua::pushstring(L, bar->getTextFormat()); + } + return 0; +} + +static int p_is_smooth(UINode* node, lua::State* L) { + if (auto bar = dynamic_cast(node)) { + return lua::pushboolean(L, bar->isSmooth()); + } + return 0; +} + static int p_get_min(UINode* node, lua::State* L) { if (auto bar = dynamic_cast(node)) { return lua::pushnumber(L, bar->getMin()); + } else if (auto bar = dynamic_cast(node)) { + return lua::pushnumber(L, bar->getMin()); } return 0; } @@ -279,6 +319,8 @@ static int p_get_min(UINode* node, lua::State* L) { static int p_get_max(UINode* node, lua::State* L) { if (auto bar = dynamic_cast(node)) { return lua::pushnumber(L, bar->getMax()); + } else if (auto bar = dynamic_cast(node)) { + return lua::pushnumber(L, bar->getMax()); } return 0; } @@ -307,6 +349,8 @@ static int p_get_track_color(UINode* node, lua::State* L) { static int p_get_text_color(UINode* node, lua::State* L) { if (auto box = dynamic_cast(node)) { return lua::pushcolor(L, box->getTextColor()); + } else if (auto bar = dynamic_cast(node)) { + return lua::pushcolor(L, bar->getTextColor()); } return 0; } @@ -676,12 +720,18 @@ static int l_gui_getattr(lua::State* L) { {"src", p_get_src}, {"fallback", p_get_fallback}, {"value", p_get_value}, + {"progress", p_get_progress}, {"min", p_get_min}, {"max", p_get_max}, {"step", p_get_step}, {"scroll", p_get_scroll}, {"trackWidth", p_get_track_width}, {"trackColor", p_get_track_color}, + {"barColor", p_get_bar_color}, + {"bgColor", p_get_bg_color}, + {"textColor", p_get_text_color}, + {"textFormat", p_get_text_format}, + {"smooth", p_is_smooth}, {"textColor", p_get_text_color}, {"checked", p_is_checked}, {"page", p_get_page}, @@ -851,6 +901,8 @@ static void p_set_zindex(UINode* node, lua::State* L, int idx) { static void p_set_value(UINode* node, lua::State* L, int idx) { if (auto bar = dynamic_cast(node)) { bar->setValue(lua::tonumber(L, idx)); + } else if (auto bar = dynamic_cast(node)) { + bar->setValue(lua::tonumber(L, idx)); } else if (auto selectbox = dynamic_cast(node)) { auto value = lua::require_lstring(L, idx); const auto& options = selectbox->getOptions(); @@ -867,11 +919,39 @@ static void p_set_value(UINode* node, lua::State* L, int idx) { static void p_set_min(UINode* node, lua::State* L, int idx) { if (auto bar = dynamic_cast(node)) { bar->setMin(lua::tonumber(L, idx)); + } else if (auto bar = dynamic_cast(node)) { + bar->setMin(lua::tonumber(L, idx)); } } static void p_set_max(UINode* node, lua::State* L, int idx) { if (auto bar = dynamic_cast(node)) { bar->setMax(lua::tonumber(L, idx)); + } else if (auto bar = dynamic_cast(node)) { + bar->setMax(lua::tonumber(L, idx)); + } +} + +static void p_set_bar_color(UINode* node, lua::State* L, int idx) { + if (auto bar = dynamic_cast(node)) { + bar->setBarColor(lua::tocolor(L, idx)); + } +} + +static void p_set_bg_color(UINode* node, lua::State* L, int idx) { + if (auto bar = dynamic_cast(node)) { + bar->setBgColor(lua::tocolor(L, idx)); + } +} + +static void p_set_text_format(UINode* node, lua::State* L, int idx) { + if (auto bar = dynamic_cast(node)) { + bar->setTextFormat(lua::require_string(L, idx)); + } +} + +static void p_set_smooth(UINode* node, lua::State* L, int idx) { + if (auto bar = dynamic_cast(node)) { + bar->setSmooth(lua::toboolean(L, idx)); } } static void p_set_step(UINode* node, lua::State* L, int idx) { @@ -892,6 +972,8 @@ static void p_set_track_color(UINode* node, lua::State* L, int idx) { static void p_set_text_color(UINode* node, lua::State* L, int idx) { if (auto box = dynamic_cast(node)) { box->setTextColor(lua::tocolor(L, idx)); + } else if (auto bar = dynamic_cast(node)) { + bar->setTextColor(lua::tocolor(L, idx)); } } static void p_set_checked(UINode* node, lua::State* L, int idx) { @@ -989,7 +1071,11 @@ static int l_gui_setattr(lua::State* L) { {"scroll", p_set_scroll}, {"trackWidth", p_set_track_width}, {"trackColor", p_set_track_color}, + {"barColor", p_set_bar_color}, + {"bgColor", p_set_bg_color}, {"textColor", p_set_text_color}, + {"textFormat", p_set_text_format}, + {"smooth", p_set_smooth}, {"checked", p_set_checked}, {"page", p_set_page}, {"inventory", p_set_inventory}, From d74b6ca91c7d3255d693b495bc01a75efa4ced12 Mon Sep 17 00:00:00 2001 From: Ezhulka Date: Fri, 31 Jul 2026 21:10:25 +0300 Subject: [PATCH 3/5] docs upd --- doc/en/scripting/ui.md | 16 ++++++++++++++++ doc/en/xml-ui-layouts.md | 20 ++++++++++++++++++++ doc/ru/scripting/ui.md | 16 ++++++++++++++++ doc/ru/xml-ui-layouts.md | 20 ++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 2d338a5aa..c92e79f9d 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -231,6 +231,22 @@ Properties: | value | string | yes | yes | Selected value | | options | table | yes | yes | List of options (tables `{value=..., text=...}`) | +## Progress bar (progressbar) + +Properties: + +| Name | Type | Read | Write | Description | +|------------|--------|------|-------|---------------------------------------------------------------| +| value | float | yes | yes | current value | +| min | float | yes | yes | minimum value | +| max | float | yes | yes | maximum value | +| progress | float | yes | no | progress 0..1 | +| barColor | rgba | yes | yes | color of the filled part | +| bgColor | rgba | yes | yes | background color (empty part) | +| textColor | rgba | yes | yes | text color | +| textFormat | string | yes | yes | text format (sprintf: `%1$` — raw value, `%2$` — percent 0–100) | +| smooth | bool | yes | yes | smooth value change | + ## Inventory Properties: diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index 0bd1b5353..e666b6210 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -193,6 +193,26 @@ Example of list description: - `selected` - initially selected value. Default: "". - `onselect` - function to which the user-selected value is passed +## Progress bar - *progressbar* + +Non-interactive element for displaying progress. Draws a background and a filled part. + +- `min` - minimal value. Type: number. Default: 0 +- `max` - maximal value. Type: number. Default: 100 +- `value` - initial value. Type: number. Default: 0 +- `bar-color` - color of the filled part. Type: RGBA color. Default: blue `#3399FFCC` +- `bg-color` - background color (empty part). Type: RGBA color. Default: semi-transparent black `#00000066` +- `orientation` - fill direction: `horizontal` / `vertical` +- `supplier` - Lua function - value supplier, called every frame +- `smooth` - smooth value change on updates. Type: boolean. Default: false +- `text-format` - format of the text centered in the bar (sprintf, two arguments: `%1$` - raw value, `%2$` - percent 0-100) + ```xml + + + + ``` +- `text-color` - text color. Type: RGBA color. Default: white `#FFFFFFFF` + ## *bindbox* An element for displaying and editing key or mouse button bindings. diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index 631d5e00f..7fca96d34 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -231,6 +231,22 @@ Canvas.decode(data: Bytearray, format: str) -> Canvas | value | string | да | да | Выбранное значение | | options | table | да | да | Список опций (таблиц `{value=..., text=...}`) | +## Прогресс-бар (progressbar) + +Свойства: + +| Название | Тип | Чтение | Запись | Описание | +| ---------- | ------ | ------ | ------ | ---------------------------------------------------------------------- | +| value | float | да | да | текущее значение | +| min | float | да | да | минимальное значение | +| max | float | да | да | максимальное значение | +| progress | float | да | нет | прогресс 0..1 | +| barColor | rgba | да | да | цвет заполненной части | +| bgColor | rgba | да | да | цвет фона (пустой части) | +| textColor | rgba | да | да | цвет текста | +| textFormat | string | да | да | формат текста (sprintf: `%1$` — сырое значение, `%2$` — процент 0–100) | +| smooth | bool | да | да | плавное изменение значения | + ## Инвентарь (inventory) Свойства: diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index 2ca6da1ce..06e73a497 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -194,6 +194,26 @@ - `selected` - изначально выбранное значение. По-умолчанию: "". - `onselect` - функция, в которую передаётся выбранное пользователем значение +## Прогресс-бар - *progressbar* + +Неинтерактивный элемент для отображения прогресса. Рисует фон и заполненную часть. + +- `min` — минимальное значение. Тип: число. По-умолчанию: 0 +- `max` — максимальное значение. Тип: число. По-умолчанию: 100 +- `value` — начальное значение. Тип: число. По-умолчанию: 0 +- `bar-color` — цвет заполненной части. Тип: RGBA цвет. По-умолчанию: синий `#3399FFCC` +- `bg-color` — цвет фона (пустой части). Тип: RGBA цвет. По-умолчанию: чёрный полупрозрачный `#00000066` +- `orientation` — направление заполнения: `horizontal` / `vertical` +- `supplier` — Lua-функция-поставщик значения, вызывается каждый кадр +- `smooth` — плавное изменение значения при обновлениях. Тип: логический. По-умолчанию: false +- `text-format` — формат текста по центру бара (sprintf, два аргумента: `%1$` — сырое значение, `%2$` — процент 0–100) + ```xml + + + + ``` +- `text-color` — цвет текста. Тип: RGBA цвет. По-умолчанию: белый `#FFFFFFFF` + ## Привязка ввода - *bindbox* Элемент для отображения и изменения привязки клавиш или кнопок мыши. From 3d0e31b102d497cf23e3bed4675fc95b294dd407 Mon Sep 17 00:00:00 2001 From: Ezhulka Date: Sun, 2 Aug 2026 22:13:37 +0300 Subject: [PATCH 4/5] refactor --- doc/en/scripting/ui.md | 25 ++++---- doc/en/xml-ui-layouts.md | 11 +--- doc/ru/scripting/ui.md | 25 ++++---- doc/ru/xml-ui-layouts.md | 11 +--- res/layouts/pages/bars.xml | 27 +++++++++ res/layouts/pages/bars.xml.lua | 20 +++++++ res/layouts/pages/main.xml | 1 + src/graphics/ui/elements/ProgressBar.cpp | 75 +++++++++++------------- src/graphics/ui/elements/ProgressBar.hpp | 67 +++++++++------------ src/graphics/ui/gui_xml.cpp | 20 ++----- src/logic/scripting/lua/libs/libgui.cpp | 65 ++++++++++++-------- 11 files changed, 194 insertions(+), 153 deletions(-) create mode 100644 res/layouts/pages/bars.xml create mode 100644 res/layouts/pages/bars.xml.lua diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index c92e79f9d..4b5750286 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -235,17 +235,20 @@ Properties: Properties: -| Name | Type | Read | Write | Description | -|------------|--------|------|-------|---------------------------------------------------------------| -| value | float | yes | yes | current value | -| min | float | yes | yes | minimum value | -| max | float | yes | yes | maximum value | -| progress | float | yes | no | progress 0..1 | -| barColor | rgba | yes | yes | color of the filled part | -| bgColor | rgba | yes | yes | background color (empty part) | -| textColor | rgba | yes | yes | text color | -| textFormat | string | yes | yes | text format (sprintf: `%1$` — raw value, `%2$` — percent 0–100) | -| smooth | bool | yes | yes | smooth value change | +| Name | Type | Read | Write | Description | +|--------------|--------|------|-------|---------------------------------------------------------------| +| value | float | yes | yes | current value | +| min | float | yes | yes | minimum value | +| max | float | yes | yes | maximum value | +| progress | float | yes | no | progress 0..1 | +| displayValue | float | yes | no | displayed (animated) value | +| color | rgba | yes | yes | color of the filled part | +| bgColor | rgba | yes | yes | background color (empty part) | +| textColor | rgba | yes | yes | text color | +| text | string | yes | yes | text centered in the bar | +| smooth | bool | yes | yes | smooth value change | +| smoothSpeed | number | yes | yes | smooth transition speed in units per second | +| orientation | string | yes | yes | fill direction: "horizontal" / "vertical" | ## Inventory diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index e666b6210..89af982dc 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -200,18 +200,13 @@ Non-interactive element for displaying progress. Draws a background and a filled - `min` - minimal value. Type: number. Default: 0 - `max` - maximal value. Type: number. Default: 100 - `value` - initial value. Type: number. Default: 0 -- `bar-color` - color of the filled part. Type: RGBA color. Default: blue `#3399FFCC` +- `color` - color of the filled part. Type: RGBA color. Default: blue `#3399FFCC` - `bg-color` - background color (empty part). Type: RGBA color. Default: semi-transparent black `#00000066` - `orientation` - fill direction: `horizontal` / `vertical` -- `supplier` - Lua function - value supplier, called every frame - `smooth` - smooth value change on updates. Type: boolean. Default: false -- `text-format` - format of the text centered in the bar (sprintf, two arguments: `%1$` - raw value, `%2$` - percent 0-100) - ```xml - - - - ``` +- `smooth-speed` - smooth transition speed in units per second. Type: number. Default: 120 - `text-color` - text color. Type: RGBA color. Default: white `#FFFFFFFF` +- `font` - font name. Type: string. Default: "normal" ## *bindbox* diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index 7fca96d34..2fa4f14f6 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -235,17 +235,20 @@ Canvas.decode(data: Bytearray, format: str) -> Canvas Свойства: -| Название | Тип | Чтение | Запись | Описание | -| ---------- | ------ | ------ | ------ | ---------------------------------------------------------------------- | -| value | float | да | да | текущее значение | -| min | float | да | да | минимальное значение | -| max | float | да | да | максимальное значение | -| progress | float | да | нет | прогресс 0..1 | -| barColor | rgba | да | да | цвет заполненной части | -| bgColor | rgba | да | да | цвет фона (пустой части) | -| textColor | rgba | да | да | цвет текста | -| textFormat | string | да | да | формат текста (sprintf: `%1$` — сырое значение, `%2$` — процент 0–100) | -| smooth | bool | да | да | плавное изменение значения | +| Название | Тип | Чтение | Запись | Описание | +|--------------|--------|--------|--------|------------------------------------------------------------------------| +| value | float | да | да | текущее значение | +| min | float | да | да | минимальное значение | +| max | float | да | да | максимальное значение | +| progress | float | да | нет | прогресс 0..1 | +| displayValue | float | да | нет | отображаемое (анимированное) значение | +| color | rgba | да | да | цвет заполненной части | +| bgColor | rgba | да | да | цвет фона (пустой части) | +| textColor | rgba | да | да | цвет текста | +| text | string | да | да | текст по центру бара | +| smooth | bool | да | да | плавное изменение значения | +| smoothSpeed | number | да | да | скорость плавного перехода в единицах в секунду | +| orientation | string | да | да | направление заполнения: "horizontal" / "vertical" | ## Инвентарь (inventory) diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index 06e73a497..79887b676 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -201,18 +201,13 @@ - `min` — минимальное значение. Тип: число. По-умолчанию: 0 - `max` — максимальное значение. Тип: число. По-умолчанию: 100 - `value` — начальное значение. Тип: число. По-умолчанию: 0 -- `bar-color` — цвет заполненной части. Тип: RGBA цвет. По-умолчанию: синий `#3399FFCC` +- `color` — цвет заполненной части. Тип: RGBA цвет. По-умолчанию: синий `#3399FFCC` - `bg-color` — цвет фона (пустой части). Тип: RGBA цвет. По-умолчанию: чёрный полупрозрачный `#00000066` - `orientation` — направление заполнения: `horizontal` / `vertical` -- `supplier` — Lua-функция-поставщик значения, вызывается каждый кадр - `smooth` — плавное изменение значения при обновлениях. Тип: логический. По-умолчанию: false -- `text-format` — формат текста по центру бара (sprintf, два аргумента: `%1$` — сырое значение, `%2$` — процент 0–100) - ```xml - - - - ``` +- `smooth-speed` — скорость плавного перехода в единицах в секунду. Тип: число. По-умолчанию: 120 - `text-color` — цвет текста. Тип: RGBA цвет. По-умолчанию: белый `#FFFFFFFF` +- `font` — имя шрифта. Тип: строка. По-умолчанию: "normal" ## Привязка ввода - *bindbox* diff --git a/res/layouts/pages/bars.xml b/res/layouts/pages/bars.xml new file mode 100644 index 000000000..435dca1a1 --- /dev/null +++ b/res/layouts/pages/bars.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layouts/pages/bars.xml.lua b/res/layouts/pages/bars.xml.lua new file mode 100644 index 000000000..adfa4ffd7 --- /dev/null +++ b/res/layouts/pages/bars.xml.lua @@ -0,0 +1,20 @@ +function on_open() + document.st_blue.text = "70" + document.st_red.text = "35" + document.st_green.text = "90" + document.bg_none.text = "70" + document.bg_white.text = "35" + document.bg_blue.text = "90" + + local t = 0.0 + document.root:setInterval(16, function() + t = t + time.delta() + local v = (math.sin(t * 2.0) + 1.0) / 2.0 * 100.0 + document.dyn_off.value = v + document.dyn_slow.value = v + document.dyn_fast.value = v + document.dyn_off.text = string.format("%.0f", v) + document.dyn_slow.text = string.format("%.0f", v) + document.dyn_fast.text = string.format("%.0f", v) + end) +end diff --git a/res/layouts/pages/main.xml b/res/layouts/pages/main.xml index b05ac7158..ef3152439 100644 --- a/res/layouts/pages/main.xml +++ b/res/layouts/pages/main.xml @@ -3,5 +3,6 @@ + diff --git a/src/graphics/ui/elements/ProgressBar.cpp b/src/graphics/ui/elements/ProgressBar.cpp index 19b8ca735..a1c698863 100644 --- a/src/graphics/ui/elements/ProgressBar.cpp +++ b/src/graphics/ui/elements/ProgressBar.cpp @@ -1,15 +1,12 @@ #include "ProgressBar.hpp" -#include -#include #include +#include #include "graphics/core/Batch2D.hpp" #include "graphics/core/DrawContext.hpp" #include "graphics/core/Font.hpp" #include "assets/Assets.hpp" -#include "maths/voxmaths.hpp" -#include "util/stringutil.hpp" using namespace gui; @@ -17,22 +14,19 @@ ProgressBar::ProgressBar( GUI& gui, double min, double max, - double value, - int barThickness + double value ) : UINode(gui, glm::vec2(100, 16)), min(min), max(max), value(value), - displayValue(value), - barThickness(barThickness) + displayValue(value) { - if (barThickness <= 0) barThickness = static_cast(size.y); - setColor(glm::vec4()); + setColor(glm::vec4(0.2f, 0.6f, 1.0f, 0.8f)); setInteractive(false); } void ProgressBar::act(float delta) { - if (supplier) value = supplier(); + UINode::act(delta); if (smoothTransition) { double diff = value - displayValue; if (std::fabs(diff) < 2.0) { @@ -61,12 +55,14 @@ void ProgressBar::draw(const DrawContext& pctx, const Assets& assets) { float t = 0.0f; if (max > min) { t = glm::clamp( - static_cast((displayValue - min) / (max - min)), 0.0f, 1.0f + static_cast((displayValue - min) / (max - min)), + 0.0f, + 1.0f ); } if (t > 0.0f) { - batch->setColor(barColor); + batch->setColor(color); if (orientation == Orientation::HORIZONTAL) { batch->rect(pos.x, pos.y, size.x * t, size.y); } else { @@ -74,27 +70,19 @@ void ProgressBar::draw(const DrawContext& pctx, const Assets& assets) { } } - if (textVisible && !textFormat.empty()) { - auto font = assets.get("normal"); + if (!text.empty()) { + auto font = assets.getShared(fontName); if (font) { - double pct = (max > min) ? (displayValue - min) / (max - min) * 100.0 : 0.0; - char buf[64]; - snprintf(buf, sizeof(buf), textFormat.c_str(), displayValue, pct); - auto wtext = util::str2wstr_utf8(buf); - int textWidth = font->calcWidth(wtext); + int textWidth = font->calcWidth(text); int lineHeight = font->getLineHeight(); int tx = static_cast(pos.x + (size.x - textWidth) * 0.5f); int ty = static_cast(pos.y + (size.y - lineHeight) * 0.5f); batch->setColor(textColor); - font->draw(*batch, wtext, tx, ty, nullptr, 0); + font->draw(*batch, text, tx, ty, nullptr, 0); } } } -void ProgressBar::setSupplier(doublesupplier supplier) { - this->supplier = std::move(supplier); -} - double ProgressBar::getValue() const { return value; } @@ -111,8 +99,8 @@ double ProgressBar::getProgress() const { return (max - min != 0.0) ? (value - min) / (max - min) : 0.0; } -glm::vec4 ProgressBar::getBarColor() const { - return barColor; +double ProgressBar::getDisplayValue() const { + return displayValue; } glm::vec4 ProgressBar::getBgColor() const { @@ -123,14 +111,18 @@ glm::vec4 ProgressBar::getTextColor() const { return textColor; } -const std::string& ProgressBar::getTextFormat() const { - return textFormat; +const std::wstring& ProgressBar::getText() const { + return text; } bool ProgressBar::isSmooth() const { return smoothTransition; } +float ProgressBar::getSmoothSpeed() const { + return smoothSpeed; +} + Orientation ProgressBar::getOrientation() const { return orientation; } @@ -147,14 +139,6 @@ void ProgressBar::setMax(double x) { max = x; } -void ProgressBar::setBarThickness(int thickness) { - barThickness = thickness; -} - -void ProgressBar::setBarColor(glm::vec4 color) { - barColor = color; -} - void ProgressBar::setBgColor(glm::vec4 color) { bgColor = color; } @@ -163,15 +147,26 @@ void ProgressBar::setTextColor(glm::vec4 color) { textColor = color; } -void ProgressBar::setTextFormat(const std::string& fmt) { - textFormat = fmt; - textVisible = !fmt.empty(); +void ProgressBar::setText(std::wstring text) { + this->text = std::move(text); +} + +void ProgressBar::setFontName(std::string name) { + fontName = std::move(name); +} + +const std::string& ProgressBar::getFontName() const { + return fontName; } void ProgressBar::setSmooth(bool flag) { smoothTransition = flag; } +void ProgressBar::setSmoothSpeed(float speed) { + smoothSpeed = speed; +} + void ProgressBar::setOrientation(Orientation ori) { orientation = ori; } diff --git a/src/graphics/ui/elements/ProgressBar.hpp b/src/graphics/ui/elements/ProgressBar.hpp index 46f71385a..aa0b1b7a4 100644 --- a/src/graphics/ui/elements/ProgressBar.hpp +++ b/src/graphics/ui/elements/ProgressBar.hpp @@ -6,60 +6,51 @@ #include "commons.hpp" namespace gui { - class ProgressBar : public UINode { + class ProgressBar final : public UINode { protected: - glm::vec4 barColor {0.2f, 0.6f, 1.0f, 0.8f}; glm::vec4 bgColor {0.0f, 0.0f, 0.0f, 0.4f}; glm::vec4 textColor {1.0f, 1.0f, 1.0f, 1.0f}; - doublesupplier supplier = nullptr; + std::wstring text; double min; double max; double value; double displayValue; - int barThickness; - float smoothSpeed = 120.0f; + float smoothSpeed = 100.0f; Orientation orientation = Orientation::HORIZONTAL; - bool textVisible = false; bool smoothTransition = false; - std::string textFormat; + std::string fontName = "normal"; public: - /** min, max, value, displayValue — числовой диапазон и отображаемое значение - * textFormat — формат sprintf. Аргументы: - * %1$ — displayValue (сырое значение в диапазоне min–max) - * %2$ — прогресс в процентах 0..100 - * Пример: "%.0f%%" покажет "45%", "%2$.0f%%" покажет "62%" при min=10 max=50 - */ ProgressBar( GUI& gui, double min = 0.0, double max = 100.0, - double value = 0.0, - int barThickness = -1 + double value = 0.0 ); - virtual void act(float delta) override; - virtual void draw(const DrawContext& pctx, const Assets& assets) override; + void act(float delta) override; + void draw(const DrawContext& pctx, const Assets& assets) override; - virtual void setSupplier(doublesupplier); - virtual double getValue() const; - virtual double getMin() const; - virtual double getMax() const; - virtual double getProgress() const; - virtual glm::vec4 getBarColor() const; - virtual glm::vec4 getBgColor() const; - virtual glm::vec4 getTextColor() const; - virtual Orientation getOrientation() const; - virtual const std::string& getTextFormat() const; - virtual bool isSmooth() const; + double getValue() const; + double getMin() const; + double getMax() const; + double getProgress() const; + double getDisplayValue() const; + glm::vec4 getBgColor() const; + glm::vec4 getTextColor() const; + Orientation getOrientation() const; + const std::wstring& getText() const; + const std::string& getFontName() const; + bool isSmooth() const; + float getSmoothSpeed() const; - virtual void setValue(double); - virtual void setMin(double); - virtual void setMax(double); - virtual void setBarThickness(int); - virtual void setBarColor(glm::vec4); - virtual void setBgColor(glm::vec4); - virtual void setTextColor(glm::vec4); - virtual void setTextFormat(const std::string& fmt); - virtual void setSmooth(bool flag); - virtual void setOrientation(Orientation); + void setValue(double); + void setMin(double); + void setMax(double); + void setBgColor(glm::vec4); + void setTextColor(glm::vec4); + void setText(std::wstring text); + void setFontName(std::string name); + void setSmooth(bool flag); + void setSmoothSpeed(float speed); + void setOrientation(Orientation); }; } diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 38e90d43a..8c1e7734e 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -721,34 +721,26 @@ static std::shared_ptr read_progress_bar( const UiXmlReader& reader, const xml::xmlelement& element ) { - const auto& env = reader.getEnvironment(); - const auto& file = reader.getFilename(); double minv = element.attr("min", "0.0").asFloat(); double maxv = element.attr("max", "100.0").asFloat(); double def = element.attr("value", "0.0").asFloat(); - int thickness = element.attr("bar-thickness", "-1").asInt(); - auto bar = std::make_shared( - reader.getGUI(), minv, maxv, def, thickness - ); + auto bar = std::make_shared(reader.getGUI(), minv, maxv, def); read_uinode(reader, element, *bar); - if (element.has("supplier")) { - bar->setSupplier(scripting::create_number_supplier(env, element.attr("supplier").getText(), file)); - } - if (element.has("bar-color")) { - bar->setBarColor(element.attr("bar-color").asColor()); + if (element.has("font")) { + bar->setFontName(element.attr("font").getText()); } if (element.has("bg-color")) { bar->setBgColor(element.attr("bg-color").asColor()); } - if (element.has("text-format")) { - bar->setTextFormat(element.attr("text-format").getText()); - } if (element.has("text-color")) { bar->setTextColor(element.attr("text-color").asColor()); } if (element.has("smooth")) { bar->setSmooth(element.attr("smooth").asBool()); } + if (element.has("smooth-speed")) { + bar->setSmoothSpeed(element.attr("smooth-speed").asFloat()); + } if (element.has("orientation")) { auto& oname = element.attr("orientation").getText(); if (oname == "vertical") { diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 6bbac4f6f..f7fe892ec 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -279,9 +279,9 @@ static int p_get_progress(UINode* node, lua::State* L) { return 0; } -static int p_get_bar_color(UINode* node, lua::State* L) { +static int p_get_display_value(UINode* node, lua::State* L) { if (auto bar = dynamic_cast(node)) { - return lua::pushcolor(L, bar->getBarColor()); + return lua::pushnumber(L, bar->getDisplayValue()); } return 0; } @@ -293,16 +293,16 @@ static int p_get_bg_color(UINode* node, lua::State* L) { return 0; } -static int p_get_text_format(UINode* node, lua::State* L) { +static int p_is_smooth(UINode* node, lua::State* L) { if (auto bar = dynamic_cast(node)) { - return lua::pushstring(L, bar->getTextFormat()); + return lua::pushboolean(L, bar->isSmooth()); } return 0; } -static int p_is_smooth(UINode* node, lua::State* L) { +static int p_get_smooth_speed(UINode* node, lua::State* L) { if (auto bar = dynamic_cast(node)) { - return lua::pushboolean(L, bar->isSmooth()); + return lua::pushnumber(L, bar->getSmoothSpeed()); } return 0; } @@ -355,6 +355,27 @@ static int p_get_text_color(UINode* node, lua::State* L) { return 0; } +static int p_get_orientation(UINode* node, lua::State* L) { + if (auto bar = dynamic_cast(node)) { + if (bar->getOrientation() == Orientation::VERTICAL) { + return lua::pushstring(L, "vertical"); + } + return lua::pushstring(L, "horizontal"); + } + return 0; +} + +static void p_set_orientation(UINode* node, lua::State* L, int idx) { + if (auto bar = dynamic_cast(node)) { + std::string_view name = lua::require_string(L, idx); + if (name == "vertical") { + bar->setOrientation(Orientation::VERTICAL); + } else if (name == "horizontal") { + bar->setOrientation(Orientation::HORIZONTAL); + } + } +} + static int p_is_valid(UINode* node, lua::State* L) { if (auto box = dynamic_cast(node)) { return lua::pushboolean(L, box->validate()); @@ -390,6 +411,8 @@ static int p_get_text(UINode* node, lua::State* L) { return lua::pushwstring(L, label->getText()); } else if (auto box = dynamic_cast(node)) { return lua::pushwstring(L, box->getText()); + } else if (auto bar = dynamic_cast(node)) { + return lua::pushwstring(L, bar->getText()); } return 0; } @@ -721,18 +744,19 @@ static int l_gui_getattr(lua::State* L) { {"fallback", p_get_fallback}, {"value", p_get_value}, {"progress", p_get_progress}, + {"displayValue", p_get_display_value}, {"min", p_get_min}, {"max", p_get_max}, {"step", p_get_step}, {"scroll", p_get_scroll}, {"trackWidth", p_get_track_width}, {"trackColor", p_get_track_color}, - {"barColor", p_get_bar_color}, {"bgColor", p_get_bg_color}, + {"textColor", p_get_text_color}, - {"textFormat", p_get_text_format}, {"smooth", p_is_smooth}, - {"textColor", p_get_text_color}, + {"smoothSpeed", p_get_smooth_speed}, + {"orientation", p_get_orientation}, {"checked", p_is_checked}, {"page", p_get_page}, {"back", p_get_back}, @@ -807,6 +831,8 @@ static void p_set_text(UINode* node, lua::State* L, int idx) { button->setText(lua::require_wstring(L, idx)); } else if (auto box = dynamic_cast(node)) { box->setText(lua::require_wstring(L, idx)); + } else if (auto bar = dynamic_cast(node)) { + bar->setText(lua::require_wstring(L, idx)); } } static void p_set_caret(UINode* node, lua::State* L, int idx) { @@ -931,29 +957,22 @@ static void p_set_max(UINode* node, lua::State* L, int idx) { } } -static void p_set_bar_color(UINode* node, lua::State* L, int idx) { - if (auto bar = dynamic_cast(node)) { - bar->setBarColor(lua::tocolor(L, idx)); - } -} - static void p_set_bg_color(UINode* node, lua::State* L, int idx) { if (auto bar = dynamic_cast(node)) { bar->setBgColor(lua::tocolor(L, idx)); } } -static void p_set_text_format(UINode* node, lua::State* L, int idx) { - if (auto bar = dynamic_cast(node)) { - bar->setTextFormat(lua::require_string(L, idx)); - } -} - static void p_set_smooth(UINode* node, lua::State* L, int idx) { if (auto bar = dynamic_cast(node)) { bar->setSmooth(lua::toboolean(L, idx)); } } +static void p_set_smooth_speed(UINode* node, lua::State* L, int idx) { + if (auto bar = dynamic_cast(node)) { + bar->setSmoothSpeed(lua::tonumber(L, idx)); + } +} static void p_set_step(UINode* node, lua::State* L, int idx) { if (auto bar = dynamic_cast(node)) { bar->setStep(lua::tonumber(L, idx)); @@ -1071,11 +1090,11 @@ static int l_gui_setattr(lua::State* L) { {"scroll", p_set_scroll}, {"trackWidth", p_set_track_width}, {"trackColor", p_set_track_color}, - {"barColor", p_set_bar_color}, {"bgColor", p_set_bg_color}, {"textColor", p_set_text_color}, - {"textFormat", p_set_text_format}, {"smooth", p_set_smooth}, + {"smoothSpeed", p_set_smooth_speed}, + {"orientation", p_set_orientation}, {"checked", p_set_checked}, {"page", p_set_page}, {"inventory", p_set_inventory}, From bfc15a2a392fac027912533d8b1995c76d5ecc13 Mon Sep 17 00:00:00 2001 From: Ezhulka Date: Sun, 2 Aug 2026 22:21:26 +0300 Subject: [PATCH 5/5] cleanup --- res/layouts/pages/bars.xml | 27 --------------------------- res/layouts/pages/bars.xml.lua | 20 -------------------- res/layouts/pages/main.xml | 1 - 3 files changed, 48 deletions(-) delete mode 100644 res/layouts/pages/bars.xml delete mode 100644 res/layouts/pages/bars.xml.lua diff --git a/res/layouts/pages/bars.xml b/res/layouts/pages/bars.xml deleted file mode 100644 index 435dca1a1..000000000 --- a/res/layouts/pages/bars.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/layouts/pages/bars.xml.lua b/res/layouts/pages/bars.xml.lua deleted file mode 100644 index adfa4ffd7..000000000 --- a/res/layouts/pages/bars.xml.lua +++ /dev/null @@ -1,20 +0,0 @@ -function on_open() - document.st_blue.text = "70" - document.st_red.text = "35" - document.st_green.text = "90" - document.bg_none.text = "70" - document.bg_white.text = "35" - document.bg_blue.text = "90" - - local t = 0.0 - document.root:setInterval(16, function() - t = t + time.delta() - local v = (math.sin(t * 2.0) + 1.0) / 2.0 * 100.0 - document.dyn_off.value = v - document.dyn_slow.value = v - document.dyn_fast.value = v - document.dyn_off.text = string.format("%.0f", v) - document.dyn_slow.text = string.format("%.0f", v) - document.dyn_fast.text = string.format("%.0f", v) - end) -end diff --git a/res/layouts/pages/main.xml b/res/layouts/pages/main.xml index ef3152439..b05ac7158 100644 --- a/res/layouts/pages/main.xml +++ b/res/layouts/pages/main.xml @@ -3,6 +3,5 @@ -