From a51695713574758dbfc28833a2e7e22293334a5e Mon Sep 17 00:00:00 2001 From: Pranav Srinivas Kumar Date: Mon, 16 Dec 2019 08:48:42 -0600 Subject: [PATCH 1/3] Draft implementation of smooth block progress bar --- include/indicators/block_progress_bar.hpp | 179 ++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 include/indicators/block_progress_bar.hpp diff --git a/include/indicators/block_progress_bar.hpp b/include/indicators/block_progress_bar.hpp new file mode 100644 index 0000000..e68b992 --- /dev/null +++ b/include/indicators/block_progress_bar.hpp @@ -0,0 +1,179 @@ +/* +Activity Indicators for Modern C++ +https://github.com/p-ranav/indicators + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2019 Pranav Srinivas Kumar . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace indicators { + +class BlockProgressBar { +public: + void set_foreground_color(Color color) { + std::unique_lock lock{_mutex}; + _foreground_color = color; + } + + void set_bar_width(size_t bar_width) { + std::unique_lock lock{_mutex}; + _bar_width = bar_width; + } + + void start_bar_with(const std::string &start) { + std::unique_lock lock{_mutex}; + _start = start; + } + + void end_bar_with(const std::string &end) { + std::unique_lock lock{_mutex}; + _end = end; + } + + void set_prefix_text(const std::string &text) { + std::unique_lock lock{_mutex}; + _prefix_text = text; + } + + void set_postfix_text(const std::string &text) { + std::unique_lock lock{_mutex}; + _postfix_text = text; + if (_postfix_text.length() > _max_postfix_text_length) + _max_postfix_text_length = _postfix_text.length(); + } + + void show_percentage() { _show_percentage = true; } + + void hide_percentage() { _show_percentage = false; } + + void set_progress(float value) { + { + std::unique_lock lock{_mutex}; + _progress = value; + } + _print_progress(); + } + + void tick() { + { + std::unique_lock lock{_mutex}; + _progress += 1; + } + _print_progress(); + } + + size_t current() { + return std::min(static_cast(_progress), size_t(100)); + } + + bool is_completed() const { return _completed; } + + void mark_as_completed() { + _completed = true; + _print_progress(); + } + +private: + float _progress{0.0}; + size_t _bar_width{100}; + std::string _prefix_text{""}; + std::string _start{"["}; + std::string _fill{"█"}; + std::string _lead{" "}; + std::string _remainder{" "}; + std::string _end{"]"}; + std::string _postfix_text{""}; + std::atomic _max_postfix_text_length{0}; + std::atomic _completed{false}; + std::atomic _show_percentage{true}; + std::mutex _mutex; + Color _foreground_color; + + void _print_progress() { + std::unique_lock lock{_mutex}; + std::cout << termcolor::bold; + switch (_foreground_color) { + case Color::GREY: + std::cout << termcolor::grey; + break; + case Color::RED: + std::cout << termcolor::red; + break; + case Color::GREEN: + std::cout << termcolor::green; + break; + case Color::YELLOW: + std::cout << termcolor::yellow; + break; + case Color::BLUE: + std::cout << termcolor::blue; + break; + case Color::MAGENTA: + std::cout << termcolor::magenta; + break; + case Color::CYAN: + std::cout << termcolor::cyan; + break; + case Color::WHITE: + std::cout << termcolor::white; + break; + } + std::cout << _prefix_text; + std::cout << _start; + auto pos = static_cast(_progress * static_cast(_bar_width) / 100.0); + auto remainder = _bar_width - pos; + std::vector lead_characters{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"}; + _lead = lead_characters[remainder % lead_characters.size()]; + for (size_t i = 0; i < _bar_width; ++i) { + if (i < pos) + std::cout << _fill; + else if (i == pos) + std::cout << _lead; + else + std::cout << _remainder; + } + std::cout << _end; + if (_show_percentage) { + std::cout << " " << std::min(static_cast(_progress), size_t(100)) << "%"; + } + if (_max_postfix_text_length == 0) + _max_postfix_text_length = 10; + std::cout << " " << _postfix_text << std::string(_max_postfix_text_length, ' ') << "\r"; + std::cout.flush(); + if (_progress > 100.0) { + _completed = true; + } + if (_completed) + std::cout << termcolor::reset << std::endl; + } +}; + +} // namespace indicators From 55f9eb7c6774cc258c9070d24fde0c12fad3eef2 Mon Sep 17 00:00:00 2001 From: Pranav Srinivas Kumar Date: Mon, 16 Dec 2019 09:07:57 -0600 Subject: [PATCH 2/3] Updated smooth block progress bar update logic --- include/indicators/block_progress_bar.hpp | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/include/indicators/block_progress_bar.hpp b/include/indicators/block_progress_bar.hpp index e68b992..62a8e51 100644 --- a/include/indicators/block_progress_bar.hpp +++ b/include/indicators/block_progress_bar.hpp @@ -33,6 +33,7 @@ SOFTWARE. #include #include #include +#include namespace indicators { @@ -148,18 +149,21 @@ private: } std::cout << _prefix_text; std::cout << _start; - auto pos = static_cast(_progress * static_cast(_bar_width) / 100.0); - auto remainder = _bar_width - pos; + std::vector lead_characters{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"}; - _lead = lead_characters[remainder % lead_characters.size()]; - for (size_t i = 0; i < _bar_width; ++i) { - if (i < pos) - std::cout << _fill; - else if (i == pos) - std::cout << _lead; - else - std::cout << _remainder; - } + auto progress = std::min(1.0f, std::max(0.0f, _progress / 100.0f)); + auto whole_width = std::floor(progress * _bar_width); + auto remainder_width = fmod((progress * _bar_width), 1.0f); + auto part_width = std::floor(remainder_width * lead_characters.size()); + _lead = lead_characters[part_width]; + if ((_bar_width - whole_width - 1) < 0) + _lead = ""; + for (size_t i = 0; i < whole_width; ++i) + std::cout << _fill; + std::cout << _lead; + for (size_t i = 0; i < (_bar_width - whole_width - 1); ++i) + std::cout << " "; + std::cout << _end; if (_show_percentage) { std::cout << " " << std::min(static_cast(_progress), size_t(100)) << "%"; From 21391f3ca01ca6a94f2cd44b4269fafddddd8463 Mon Sep 17 00:00:00 2001 From: Pranav Srinivas Kumar Date: Mon, 16 Dec 2019 09:11:25 -0600 Subject: [PATCH 3/3] Added sample for smooth block progress bar --- samples/block_progress_bar.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 samples/block_progress_bar.cpp diff --git a/samples/block_progress_bar.cpp b/samples/block_progress_bar.cpp new file mode 100644 index 0000000..e1ce407 --- /dev/null +++ b/samples/block_progress_bar.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +int main() { + + // Hide cursor + std::cout << "\e[?25l"; + + indicators::BlockProgressBar bar; + + // Configure the bar + bar.set_bar_width(80); + bar.start_bar_with("["); + bar.end_bar_with("]"); + bar.set_foreground_color(indicators::Color::WHITE); + + // Update bar state + auto progress = 0.0f; + while (true) { + bar.set_progress(progress); + progress += 0.25f; + if (bar.is_completed()) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + // Show cursor + std::cout << "\e[?25h"; + + return 0; +}