diff --git a/include/indicators/block_progress_bar.hpp b/include/indicators/block_progress_bar.hpp index 11ae0a8..162c2f3 100644 --- a/include/indicators/block_progress_bar.hpp +++ b/include/indicators/block_progress_bar.hpp @@ -8,12 +8,15 @@ #include #include #include +#include #include #include +#include #include #include #include #include +#include namespace indicators { @@ -160,37 +163,20 @@ private: } } -public: - void print_progress(bool from_multi_progress = false) { - std::lock_guard lock{mutex_}; - - auto &os = get_value(); + std::pair get_prefix_text() { + std::stringstream os; + os << get_value(); + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + std::pair get_postfix_text() { + std::stringstream os; const auto max_progress = get_value(); - if (multi_progress_mode_ && !from_multi_progress) { - if (progress_ > max_progress) { - get_value() = true; - } - return; - } - auto now = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(now - start_time_point_); - if (get_value() != Color::unspecified) - details::set_stream_color(os, get_value()); - - for (auto &style : get_value()) - details::set_font_style(os, style); - - os << get_value(); - os << get_value(); - - details::BlockProgressScaleWriter writer{os, - get_value()}; - writer.write(progress_ / max_progress * 100); - - os << get_value(); if (get_value()) { os << " " << std::min(static_cast(progress_ / max_progress * 100.0), size_t(100)) << "%"; @@ -227,11 +213,65 @@ public: os << "]"; } - if (get_value() == 0) - get_value() = 10; - os << " " << get_value() - << std::string(get_value(), ' ') << "\r"; + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + +public: + void print_progress(bool from_multi_progress = false) { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + const auto max_progress = get_value(); + if (multi_progress_mode_ && !from_multi_progress) { + if (progress_ > max_progress) { + get_value() = true; + } + return; + } + + if (get_value() != Color::unspecified) + details::set_stream_color(os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; + + os << get_value(); + + details::BlockProgressScaleWriter writer{os, + get_value()}; + writer.write(progress_ / max_progress * 100); + + os << get_value(); + + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } os.flush(); + if (progress_ > max_progress) { get_value() = true; } diff --git a/include/indicators/details/stream_helper.hpp b/include/indicators/details/stream_helper.hpp index f7e02c9..baad820 100644 --- a/include/indicators/details/stream_helper.hpp +++ b/include/indicators/details/stream_helper.hpp @@ -180,8 +180,9 @@ public: : os(os), bar_width(bar_width), fill(fill), lead(lead) {} std::ostream &write(size_t progress) { - for (size_t i = 0, current_display_width = 0; i < bar_width;) { + for (size_t i = 0; i < bar_width;) { std::string next; + size_t current_display_width = 0; if (i < progress) { next = fill; diff --git a/include/indicators/indeterminate_progress_bar.hpp b/include/indicators/indeterminate_progress_bar.hpp index b549e06..253fa73 100644 --- a/include/indicators/indeterminate_progress_bar.hpp +++ b/include/indicators/indeterminate_progress_bar.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,8 @@ #include #include #include +#include +#include namespace indicators { @@ -156,6 +159,23 @@ private: template friend class DynamicProgress; std::atomic multi_progress_mode_{false}; + std::pair get_prefix_text() { + std::stringstream os; + os << get_value(); + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + + std::pair get_postfix_text() { + std::stringstream os; + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + public: void print_progress(bool from_multi_progress = false) { std::lock_guard lock{mutex_}; @@ -171,7 +191,10 @@ public: for (auto &style : get_value()) details::set_font_style(os, style); - os << get_value(); + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; os << get_value(); @@ -183,11 +206,25 @@ public: os << get_value(); - if (get_value() == 0) - get_value() = 10; - os << " " << get_value() - << std::string(get_value(), ' ') << "\r"; + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } os.flush(); + if (get_value() && !from_multi_progress) // Don't std::endl if calling from MultiProgress os << termcolor::reset << std::endl; diff --git a/include/indicators/progress_bar.hpp b/include/indicators/progress_bar.hpp index dcfae7a..f260ca9 100644 --- a/include/indicators/progress_bar.hpp +++ b/include/indicators/progress_bar.hpp @@ -9,54 +9,60 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include +#include namespace indicators { class ProgressBar { using Settings = - std::tuple; + std::tuple; public: template ::type...>::value, - void *>::type = nullptr> + typename std::enable_if< + details::are_settings_from_tuple< + Settings, typename std::decay::type...>::value, + void *>::type = nullptr> explicit ProgressBar(Args &&... args) : settings_( - details::get(option::BarWidth{100}, - std::forward(args)...), - details::get(option::PrefixText{}, - std::forward(args)...), - details::get(option::PostfixText{}, - std::forward(args)...), - details::get(option::Start{"["}, - std::forward(args)...), - details::get(option::End{"]"}, - std::forward(args)...), - details::get(option::Fill{"="}, - std::forward(args)...), - details::get(option::Lead{">"}, - std::forward(args)...), - details::get(option::Remainder{" "}, - std::forward(args)...), + details::get( + option::BarWidth{100}, std::forward(args)...), + details::get( + option::PrefixText{}, std::forward(args)...), + details::get( + option::PostfixText{}, std::forward(args)...), + details::get( + option::Start{"["}, std::forward(args)...), + details::get( + option::End{"]"}, std::forward(args)...), + details::get( + option::Fill{"="}, std::forward(args)...), + details::get( + option::Lead{">"}, std::forward(args)...), + details::get( + option::Remainder{" "}, std::forward(args)...), details::get( option::MaxPostfixTextLen{0}, std::forward(args)...), - details::get(option::Completed{false}, - std::forward(args)...), - details::get(option::ShowPercentage{false}, - std::forward(args)...), + details::get( + option::Completed{false}, std::forward(args)...), + details::get( + option::ShowPercentage{false}, std::forward(args)...), details::get( option::ShowElapsedTime{false}, std::forward(args)...), details::get( @@ -64,17 +70,20 @@ public: details::get( option::SavedStartTime{false}, std::forward(args)...), details::get( - option::ForegroundColor{Color::unspecified}, std::forward(args)...), + option::ForegroundColor{Color::unspecified}, + std::forward(args)...), details::get( - option::FontStyles{std::vector{}}, std::forward(args)...), - details::get(option::MinProgress{0}, - std::forward(args)...), - details::get(option::MaxProgress{100}, - std::forward(args)...), + option::FontStyles{std::vector{}}, + std::forward(args)...), + details::get( + option::MinProgress{0}, std::forward(args)...), + details::get( + option::MaxProgress{100}, std::forward(args)...), details::get( - option::ProgressType{ProgressType::incremental}, std::forward(args)...), - details::get(option::Stream{std::cout}, - std::forward(args)...)) { + option::ProgressType{ProgressType::incremental}, + std::forward(args)...), + details::get( + option::Stream{std::cout}, std::forward(args)...)) { // if progress is incremental, start from min_progress // else start from max_progress @@ -87,38 +96,47 @@ public: template void set_option(details::Setting &&setting) { - static_assert(!std::is_same( - std::declval()))>::type>::value, - "Setting has wrong type!"); + static_assert( + !std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); std::lock_guard lock(mutex_); get_value() = std::move(setting).value; } template void set_option(const details::Setting &setting) { - static_assert(!std::is_same( - std::declval()))>::type>::value, - "Setting has wrong type!"); + static_assert( + !std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); std::lock_guard lock(mutex_); get_value() = setting.value; } - void set_option( - const details::Setting &setting) { + void + set_option(const details::Setting< + std::string, details::ProgressBarOption::postfix_text> &setting) { std::lock_guard lock(mutex_); get_value() = setting.value; - if (setting.value.length() > get_value()) { - get_value() = setting.value.length(); + if (setting.value.length() > + get_value()) { + get_value() = + setting.value.length(); } } - void - set_option(details::Setting &&setting) { + void set_option( + details::Setting + &&setting) { std::lock_guard lock(mutex_); - get_value() = std::move(setting).value; + get_value() = + std::move(setting).value; auto &new_value = get_value(); - if (new_value.length() > get_value()) { - get_value() = new_value.length(); + if (new_value.length() > + get_value()) { + get_value() = + new_value.length(); } } @@ -147,10 +165,14 @@ public: size_t current() { std::lock_guard lock{mutex_}; - return std::min(progress_, size_t(get_value())); + return std::min( + progress_, + size_t(get_value())); } - bool is_completed() const { return get_value(); } + bool is_completed() const { + return get_value(); + } void mark_as_completed() { get_value() = true; @@ -159,13 +181,14 @@ public: private: template - auto get_value() -> decltype((details::get_value(std::declval()).value)) { + auto get_value() + -> decltype((details::get_value(std::declval()).value)) { return details::get_value(settings_).value; } template - auto get_value() const - -> decltype((details::get_value(std::declval()).value)) { + auto get_value() const -> decltype( + (details::get_value(std::declval()).value)) { return details::get_value(settings_).value; } @@ -180,61 +203,41 @@ private: std::atomic multi_progress_mode_{false}; void save_start_time() { - auto &show_elapsed_time = get_value(); - auto &saved_start_time = get_value(); - auto &show_remaining_time = get_value(); + auto &show_elapsed_time = + get_value(); + auto &saved_start_time = + get_value(); + auto &show_remaining_time = + get_value(); if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { start_time_point_ = std::chrono::high_resolution_clock::now(); saved_start_time = true; } } -public: - void print_progress(bool from_multi_progress = false) { - std::lock_guard lock{mutex_}; - - auto &os = get_value(); - - const auto type = get_value(); - const auto min_progress = get_value(); - const auto max_progress = get_value(); - if (multi_progress_mode_ && !from_multi_progress) { - if ((type == ProgressType::incremental && progress_ >= max_progress) || - (type == ProgressType::decremental && progress_ <= min_progress)) { - get_value() = true; - } - return; - } - auto now = std::chrono::high_resolution_clock::now(); - if (!get_value()) - elapsed_ = std::chrono::duration_cast(now - start_time_point_); - - if (get_value() != Color::unspecified) - details::set_stream_color(os, get_value()); - - for (auto &style : get_value()) - details::set_font_style(os, style); - + std::pair get_prefix_text() { + std::stringstream os; os << get_value(); + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } - os << get_value(); - - details::ProgressScaleWriter writer{os, get_value(), - get_value(), - get_value(), - get_value()}; - writer.write(double(progress_) / double(max_progress) * 100.0f); - - os << get_value(); + std::pair get_postfix_text() { + std::stringstream os; + const auto max_progress = + get_value(); if (get_value()) { os << " " - << std::min(static_cast(static_cast(progress_) / max_progress * 100), + << std::min(static_cast(static_cast(progress_) / + max_progress * 100), size_t(100)) << "%"; } - auto &saved_start_time = get_value(); + auto &saved_start_time = + get_value(); if (get_value()) { os << " ["; @@ -252,7 +255,8 @@ public: if (saved_start_time) { auto eta = std::chrono::nanoseconds( - progress_ > 0 ? static_cast(elapsed_.count() * max_progress / progress_) + progress_ > 0 ? static_cast(elapsed_.count() * + max_progress / progress_) : 0); auto remaining = eta > elapsed_ ? (eta - elapsed_) : (elapsed_ - eta); details::write_duration(os, remaining); @@ -266,11 +270,79 @@ public: os << "]"; } - if (get_value() == 0) - get_value() = 10; - os << " " << get_value() - << std::string(get_value(), ' ') << "\r"; + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + +public: + void print_progress(bool from_multi_progress = false) { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + const auto type = get_value(); + const auto min_progress = + get_value(); + const auto max_progress = + get_value(); + if (multi_progress_mode_ && !from_multi_progress) { + if ((type == ProgressType::incremental && progress_ >= max_progress) || + (type == ProgressType::decremental && progress_ <= min_progress)) { + get_value() = true; + } + return; + } + auto now = std::chrono::high_resolution_clock::now(); + if (!get_value()) + elapsed_ = std::chrono::duration_cast( + now - start_time_point_); + + if (get_value() != + Color::unspecified) + details::set_stream_color( + os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; + + os << get_value(); + + details::ProgressScaleWriter writer{ + os, get_value(), + get_value(), + get_value(), + get_value()}; + writer.write(double(progress_) / double(max_progress) * 100.0f); + + os << get_value(); + + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } os.flush(); + if ((type == ProgressType::incremental && progress_ >= max_progress) || (type == ProgressType::decremental && progress_ <= min_progress)) { get_value() = true; diff --git a/include/indicators/terminal_size.hpp b/include/indicators/terminal_size.hpp index 29b21e7..f75f10d 100644 --- a/include/indicators/terminal_size.hpp +++ b/include/indicators/terminal_size.hpp @@ -7,7 +7,7 @@ namespace indicators { #if defined(_MSC_VER) #include -std::pair terminal_size() { +static inline std::pair terminal_size() { CONSOLE_SCREEN_BUFFER_INFO csbi; int columns, rows; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); @@ -22,13 +22,13 @@ size_t terminal_width() { return terminal_size().second; } #include //ioctl() and TIOCGWINSZ #include // for STDOUT_FILENO -std::pair terminal_size() { +static inline std::pair terminal_size() { struct winsize size; ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); return {static_cast(size.ws_row), static_cast(size.ws_col)}; } -size_t terminal_width() { return terminal_size().second; } +static inline size_t terminal_width() { return terminal_size().second; } #endif } // namespace indicators \ No newline at end of file diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 8e70f8a..fb68137 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -44,3 +44,6 @@ target_link_libraries(max_progress PRIVATE indicators::indicators) add_executable(indeterminate_progress_bar indeterminate_progress_bar.cpp) target_link_libraries(indeterminate_progress_bar PRIVATE indicators::indicators) +add_executable(dynamic_postfix_text dynamic_postfix_text.cpp) +target_link_libraries(dynamic_postfix_text PRIVATE indicators::indicators) + diff --git a/samples/dynamic_postfix_text.cpp b/samples/dynamic_postfix_text.cpp new file mode 100644 index 0000000..d0d460b --- /dev/null +++ b/samples/dynamic_postfix_text.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +using namespace indicators; + +int main() { + + std::cout << "Terminal width: " << terminal_size().second << "\n"; + + // prepare progress bar + auto prepare_p = [](ProgressBar *p, const std::string &prefix){ + p->set_option(option::PrefixText{prefix}); + p->set_option(option::Start{""}); + p->set_option(option::Fill{""}); + p->set_option(option::Lead{""}); + p->set_option(option::Remainder{""}); + p->set_option(option::End{""}); + p->set_option(option::BarWidth{0}); + }; + + ProgressBar p1, p2; + + prepare_p(&p1, "Progress #1"); + prepare_p(&p2, "Progress #2"); + + MultiProgress mp(p1, p2); + + std::string some_text[] = {"foo", "bar", "independence", "beta", "alfa"}; + std::string dynamic_text; + + // first pb with static postfix text + p1.set_option(option::PostfixText{"Static text"}); + + // second pb with dynamic postfix text + for (auto &t: some_text) { + dynamic_text += t + " "; + p2.set_option(option::PostfixText{dynamic_text}); + mp.set_progress<0>(size_t(0)); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + // update postfix to little text for pb #2 + p2.set_option(option::PostfixText{"abcd"}); + mp.set_progress<0>(size_t(0)); + +} diff --git a/samples/progress_bar_unicode.cpp b/samples/progress_bar_unicode.cpp index 9ab668a..68817cc 100644 --- a/samples/progress_bar_unicode.cpp +++ b/samples/progress_bar_unicode.cpp @@ -8,8 +8,6 @@ int main() { indicators::show_console_cursor(false); - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - { // Plain old ASCII indicators::ProgressBar bar{indicators::option::BarWidth{50}, diff --git a/single_include/indicators/indicators.hpp b/single_include/indicators/indicators.hpp index 9a91316..441e8b3 100644 --- a/single_include/indicators/indicators.hpp +++ b/single_include/indicators/indicators.hpp @@ -1297,7 +1297,7 @@ namespace indicators { #if defined(_MSC_VER) #include -std::pair terminal_size() { +static inline std::pair terminal_size() { CONSOLE_SCREEN_BUFFER_INFO csbi; int columns, rows; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); @@ -1312,13 +1312,13 @@ size_t terminal_width() { return terminal_size().second; } #include //ioctl() and TIOCGWINSZ #include // for STDOUT_FILENO -std::pair terminal_size() { +static inline std::pair terminal_size() { struct winsize size; ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); return {static_cast(size.ws_row), static_cast(size.ws_col)}; } -size_t terminal_width() { return terminal_size().second; } +static inline size_t terminal_width() { return terminal_size().second; } #endif } // namespace indicators @@ -2101,8 +2101,9 @@ public: : os(os), bar_width(bar_width), fill(fill), lead(lead) {} std::ostream &write(size_t progress) { - for (size_t i = 0, current_display_width = 0; i < bar_width;) { + for (size_t i = 0; i < bar_width;) { std::string next; + size_t current_display_width = 0; if (i < progress) { next = fill; @@ -2148,54 +2149,60 @@ private: #include // #include // #include +// #include #include #include +#include #include #include #include #include #include +#include namespace indicators { class ProgressBar { using Settings = - std::tuple; + std::tuple; public: template ::type...>::value, - void *>::type = nullptr> + typename std::enable_if< + details::are_settings_from_tuple< + Settings, typename std::decay::type...>::value, + void *>::type = nullptr> explicit ProgressBar(Args &&... args) : settings_( - details::get(option::BarWidth{100}, - std::forward(args)...), - details::get(option::PrefixText{}, - std::forward(args)...), - details::get(option::PostfixText{}, - std::forward(args)...), - details::get(option::Start{"["}, - std::forward(args)...), - details::get(option::End{"]"}, - std::forward(args)...), - details::get(option::Fill{"="}, - std::forward(args)...), - details::get(option::Lead{">"}, - std::forward(args)...), - details::get(option::Remainder{" "}, - std::forward(args)...), + details::get( + option::BarWidth{100}, std::forward(args)...), + details::get( + option::PrefixText{}, std::forward(args)...), + details::get( + option::PostfixText{}, std::forward(args)...), + details::get( + option::Start{"["}, std::forward(args)...), + details::get( + option::End{"]"}, std::forward(args)...), + details::get( + option::Fill{"="}, std::forward(args)...), + details::get( + option::Lead{">"}, std::forward(args)...), + details::get( + option::Remainder{" "}, std::forward(args)...), details::get( option::MaxPostfixTextLen{0}, std::forward(args)...), - details::get(option::Completed{false}, - std::forward(args)...), - details::get(option::ShowPercentage{false}, - std::forward(args)...), + details::get( + option::Completed{false}, std::forward(args)...), + details::get( + option::ShowPercentage{false}, std::forward(args)...), details::get( option::ShowElapsedTime{false}, std::forward(args)...), details::get( @@ -2203,17 +2210,20 @@ public: details::get( option::SavedStartTime{false}, std::forward(args)...), details::get( - option::ForegroundColor{Color::unspecified}, std::forward(args)...), + option::ForegroundColor{Color::unspecified}, + std::forward(args)...), details::get( - option::FontStyles{std::vector{}}, std::forward(args)...), - details::get(option::MinProgress{0}, - std::forward(args)...), - details::get(option::MaxProgress{100}, - std::forward(args)...), + option::FontStyles{std::vector{}}, + std::forward(args)...), + details::get( + option::MinProgress{0}, std::forward(args)...), + details::get( + option::MaxProgress{100}, std::forward(args)...), details::get( - option::ProgressType{ProgressType::incremental}, std::forward(args)...), - details::get(option::Stream{std::cout}, - std::forward(args)...)) { + option::ProgressType{ProgressType::incremental}, + std::forward(args)...), + details::get( + option::Stream{std::cout}, std::forward(args)...)) { // if progress is incremental, start from min_progress // else start from max_progress @@ -2226,38 +2236,47 @@ public: template void set_option(details::Setting &&setting) { - static_assert(!std::is_same( - std::declval()))>::type>::value, - "Setting has wrong type!"); + static_assert( + !std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); std::lock_guard lock(mutex_); get_value() = std::move(setting).value; } template void set_option(const details::Setting &setting) { - static_assert(!std::is_same( - std::declval()))>::type>::value, - "Setting has wrong type!"); + static_assert( + !std::is_same( + std::declval()))>::type>::value, + "Setting has wrong type!"); std::lock_guard lock(mutex_); get_value() = setting.value; } - void set_option( - const details::Setting &setting) { + void + set_option(const details::Setting< + std::string, details::ProgressBarOption::postfix_text> &setting) { std::lock_guard lock(mutex_); get_value() = setting.value; - if (setting.value.length() > get_value()) { - get_value() = setting.value.length(); + if (setting.value.length() > + get_value()) { + get_value() = + setting.value.length(); } } - void - set_option(details::Setting &&setting) { + void set_option( + details::Setting + &&setting) { std::lock_guard lock(mutex_); - get_value() = std::move(setting).value; + get_value() = + std::move(setting).value; auto &new_value = get_value(); - if (new_value.length() > get_value()) { - get_value() = new_value.length(); + if (new_value.length() > + get_value()) { + get_value() = + new_value.length(); } } @@ -2286,10 +2305,14 @@ public: size_t current() { std::lock_guard lock{mutex_}; - return std::min(progress_, size_t(get_value())); + return std::min( + progress_, + size_t(get_value())); } - bool is_completed() const { return get_value(); } + bool is_completed() const { + return get_value(); + } void mark_as_completed() { get_value() = true; @@ -2298,13 +2321,14 @@ public: private: template - auto get_value() -> decltype((details::get_value(std::declval()).value)) { + auto get_value() + -> decltype((details::get_value(std::declval()).value)) { return details::get_value(settings_).value; } template - auto get_value() const - -> decltype((details::get_value(std::declval()).value)) { + auto get_value() const -> decltype( + (details::get_value(std::declval()).value)) { return details::get_value(settings_).value; } @@ -2319,61 +2343,41 @@ private: std::atomic multi_progress_mode_{false}; void save_start_time() { - auto &show_elapsed_time = get_value(); - auto &saved_start_time = get_value(); - auto &show_remaining_time = get_value(); + auto &show_elapsed_time = + get_value(); + auto &saved_start_time = + get_value(); + auto &show_remaining_time = + get_value(); if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { start_time_point_ = std::chrono::high_resolution_clock::now(); saved_start_time = true; } } -public: - void print_progress(bool from_multi_progress = false) { - std::lock_guard lock{mutex_}; - - auto &os = get_value(); - - const auto type = get_value(); - const auto min_progress = get_value(); - const auto max_progress = get_value(); - if (multi_progress_mode_ && !from_multi_progress) { - if ((type == ProgressType::incremental && progress_ >= max_progress) || - (type == ProgressType::decremental && progress_ <= min_progress)) { - get_value() = true; - } - return; - } - auto now = std::chrono::high_resolution_clock::now(); - if (!get_value()) - elapsed_ = std::chrono::duration_cast(now - start_time_point_); - - if (get_value() != Color::unspecified) - details::set_stream_color(os, get_value()); - - for (auto &style : get_value()) - details::set_font_style(os, style); - + std::pair get_prefix_text() { + std::stringstream os; os << get_value(); + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } - os << get_value(); - - details::ProgressScaleWriter writer{os, get_value(), - get_value(), - get_value(), - get_value()}; - writer.write(double(progress_) / double(max_progress) * 100.0f); - - os << get_value(); + std::pair get_postfix_text() { + std::stringstream os; + const auto max_progress = + get_value(); if (get_value()) { os << " " - << std::min(static_cast(static_cast(progress_) / max_progress * 100), + << std::min(static_cast(static_cast(progress_) / + max_progress * 100), size_t(100)) << "%"; } - auto &saved_start_time = get_value(); + auto &saved_start_time = + get_value(); if (get_value()) { os << " ["; @@ -2391,7 +2395,8 @@ public: if (saved_start_time) { auto eta = std::chrono::nanoseconds( - progress_ > 0 ? static_cast(elapsed_.count() * max_progress / progress_) + progress_ > 0 ? static_cast(elapsed_.count() * + max_progress / progress_) : 0); auto remaining = eta > elapsed_ ? (eta - elapsed_) : (elapsed_ - eta); details::write_duration(os, remaining); @@ -2405,11 +2410,79 @@ public: os << "]"; } - if (get_value() == 0) - get_value() = 10; - os << " " << get_value() - << std::string(get_value(), ' ') << "\r"; + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + +public: + void print_progress(bool from_multi_progress = false) { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + const auto type = get_value(); + const auto min_progress = + get_value(); + const auto max_progress = + get_value(); + if (multi_progress_mode_ && !from_multi_progress) { + if ((type == ProgressType::incremental && progress_ >= max_progress) || + (type == ProgressType::decremental && progress_ <= min_progress)) { + get_value() = true; + } + return; + } + auto now = std::chrono::high_resolution_clock::now(); + if (!get_value()) + elapsed_ = std::chrono::duration_cast( + now - start_time_point_); + + if (get_value() != + Color::unspecified) + details::set_stream_color( + os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; + + os << get_value(); + + details::ProgressScaleWriter writer{ + os, get_value(), + get_value(), + get_value(), + get_value()}; + writer.write(double(progress_) / double(max_progress) * 100.0f); + + os << get_value(); + + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } os.flush(); + if ((type == ProgressType::incremental && progress_ >= max_progress) || (type == ProgressType::decremental && progress_ <= min_progress)) { get_value() = true; @@ -2431,12 +2504,15 @@ public: #include #include // #include +// #include #include #include +#include #include #include #include #include +#include namespace indicators { @@ -2583,37 +2659,20 @@ private: } } -public: - void print_progress(bool from_multi_progress = false) { - std::lock_guard lock{mutex_}; - - auto &os = get_value(); + std::pair get_prefix_text() { + std::stringstream os; + os << get_value(); + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + std::pair get_postfix_text() { + std::stringstream os; const auto max_progress = get_value(); - if (multi_progress_mode_ && !from_multi_progress) { - if (progress_ > max_progress) { - get_value() = true; - } - return; - } - auto now = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(now - start_time_point_); - if (get_value() != Color::unspecified) - details::set_stream_color(os, get_value()); - - for (auto &style : get_value()) - details::set_font_style(os, style); - - os << get_value(); - os << get_value(); - - details::BlockProgressScaleWriter writer{os, - get_value()}; - writer.write(progress_ / max_progress * 100); - - os << get_value(); if (get_value()) { os << " " << std::min(static_cast(progress_ / max_progress * 100.0), size_t(100)) << "%"; @@ -2650,11 +2709,65 @@ public: os << "]"; } - if (get_value() == 0) - get_value() = 10; - os << " " << get_value() - << std::string(get_value(), ' ') << "\r"; + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + +public: + void print_progress(bool from_multi_progress = false) { + std::lock_guard lock{mutex_}; + + auto &os = get_value(); + + const auto max_progress = get_value(); + if (multi_progress_mode_ && !from_multi_progress) { + if (progress_ > max_progress) { + get_value() = true; + } + return; + } + + if (get_value() != Color::unspecified) + details::set_stream_color(os, get_value()); + + for (auto &style : get_value()) + details::set_font_style(os, style); + + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; + + os << get_value(); + + details::BlockProgressScaleWriter writer{os, + get_value()}; + writer.write(progress_ / max_progress * 100); + + os << get_value(); + + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } os.flush(); + if (progress_ > max_progress) { get_value() = true; } @@ -2676,6 +2789,7 @@ public: #include // #include // #include +// #include #include #include #include @@ -2683,6 +2797,8 @@ public: #include #include #include +#include +#include namespace indicators { @@ -2823,6 +2939,23 @@ private: template friend class DynamicProgress; std::atomic multi_progress_mode_{false}; + std::pair get_prefix_text() { + std::stringstream os; + os << get_value(); + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + + std::pair get_postfix_text() { + std::stringstream os; + os << " " << get_value(); + + const auto result = os.str(); + const auto result_size = result.size(); + return {result, result_size}; + } + public: void print_progress(bool from_multi_progress = false) { std::lock_guard lock{mutex_}; @@ -2838,7 +2971,10 @@ public: for (auto &style : get_value()) details::set_font_style(os, style); - os << get_value(); + const auto prefix_pair = get_prefix_text(); + const auto prefix_text = prefix_pair.first; + const auto prefix_length = prefix_pair.second; + os << prefix_text; os << get_value(); @@ -2850,11 +2986,25 @@ public: os << get_value(); - if (get_value() == 0) - get_value() = 10; - os << " " << get_value() - << std::string(get_value(), ' ') << "\r"; + const auto postfix_pair = get_postfix_text(); + const auto postfix_text = postfix_pair.first; + const auto postfix_length = postfix_pair.second; + os << postfix_text; + + // Get length of prefix text and postfix text + const auto start_length = get_value().size(); + const auto bar_width = get_value(); + const auto end_length = get_value().size(); + const auto terminal_width = terminal_size().second; + // prefix + bar_width + postfix should be <= terminal_width + const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length); + if (remaining > 0) { + os << std::string(remaining, ' ') << "\r"; + } else if (remaining < 0) { + // Do nothing. Maybe in the future truncate postfix with ... + } os.flush(); + if (get_value() && !from_multi_progress) // Don't std::endl if calling from MultiProgress os << termcolor::reset << std::endl;