From a0e1d4acfe66f08329176bc3388eadff6d895b21 Mon Sep 17 00:00:00 2001 From: Ezhulka Date: Fri, 31 Jul 2026 20:57:07 +0300 Subject: [PATCH] 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); + }; +}