#ifndef INDICATORS_INDETERMINATE_PROGRESS_BAR #define INDICATORS_INDETERMINATE_PROGRESS_BAR #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace indicators { class IndeterminateProgressBar { using Settings = std::tuple; enum class Direction { forward, backward }; Direction direction_{Direction::forward}; public: template ::type...>::value, void *>::type = nullptr> explicit IndeterminateProgressBar(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::MaxPostfixTextLen{0}, std::forward(args)...), details::get(option::Completed{false}, std::forward(args)...), details::get( option::ForegroundColor{Color::unspecified}, std::forward(args)...), details::get( option::FontStyles{std::vector{}}, std::forward(args)...), details::get(option::Stream{std::cout}, std::forward(args)...)) { // starts with [<==>...........] // progress_ = 0 // ends with [...........<==>] // ^^^^^^^^^^^^^^^^^ bar_width // ^^^^^^^^^^^^ (bar_width - len(lead)) // progress_ = bar_width - len(lead) progress_ = 0; max_progress_ = get_value() - get_value().size() + get_value().size() + get_value().size(); } template void set_option(details::Setting &&setting) { 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!"); std::lock_guard lock(mutex_); get_value() = setting.value; } void set_option( const details::Setting &setting) { std::lock_guard lock(mutex_); get_value() = setting.value; if (setting.value.length() > get_value()) { get_value() = setting.value.length(); } } void set_option(details::Setting &&setting) { std::lock_guard lock(mutex_); get_value() = std::move(setting).value; auto &new_value = get_value(); if (new_value.length() > get_value()) { get_value() = new_value.length(); } } void tick() { { std::lock_guard lock{mutex_}; if (get_value()) return; progress_ += (direction_ == Direction::forward) ? 1 : -1; if (direction_ == Direction::forward && progress_ == max_progress_) { // time to go back direction_ = Direction::backward; } else if (direction_ == Direction::backward && progress_ == 0) { direction_ = Direction::forward; } } print_progress(); } bool is_completed() { return get_value(); } void mark_as_completed() { get_value() = true; print_progress(); } private: template 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)) { return details::get_value(settings_).value; } size_t progress_{0}; size_t max_progress_; Settings settings_; std::chrono::nanoseconds elapsed_; std::mutex mutex_; template friend class MultiProgress; 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 = unicode::display_width(result); return {result, result_size}; } std::pair get_postfix_text() { std::stringstream os; os << " " << get_value(); const auto result = os.str(); const auto result_size = unicode::display_width(result); return {result, result_size}; } public: void print_progress(bool from_multi_progress = false) { std::lock_guard lock{mutex_}; auto &os = get_value(); if (multi_progress_mode_ && !from_multi_progress) { 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 << "\r" << prefix_text; os << get_value(); details::IndeterminateProgressScaleWriter writer{ os, get_value(), get_value(), get_value()}; writer.write(progress_); 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 (prefix_length == -1 || postfix_length == -1) { os << "\r"; } else 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; } }; } // namespace indicators #endif