299 Commits
v1.1 ... master

Author SHA1 Message Date
Pranav
3872f37abd Merge pull request #141 from zhao-shihan/master
Add CMake options INDICATORS_INSTALL and INDICATORS_PACKAGE
2025-05-09 15:37:45 -04:00
Pranav
a746832d97 Merge pull request #148 from rlalik/fix_elapsed
Fix regression with elapsed time calculation introduced in #147
2025-05-09 15:37:31 -04:00
赵诗涵
824ca21bbf Merge branch 'p-ranav:master' into master 2025-05-08 03:34:27 +08:00
Rafał Lalik
091a87e51a Fix regression with elapsed time calculation introduced in #147 2025-05-07 17:30:44 +02:00
Pranav
a6de479a9c Merge pull request #143 from BrandenXia/patch-1
Update some examples, add percentage to ensure actual behavior match displayed in gif
2025-05-07 07:07:47 -04:00
Pranav
cef20c7c4b Merge pull request #147 from rlalik/fix_float_lsb_precision
Fix lsb error for ticks with huge max_progress values.
2025-05-07 07:07:18 -04:00
Rafał Lalik
08bf7ec793 Fix progress variable type in sample code 2025-05-02 21:27:24 +02:00
Rafał Lalik
fbdac646ee Fix lsb error for ticks with huge max_progress values.
When the max_progress value is >1.67772e+07, the progress_ which is
float, is not chanegd when tick() is called as +1 is outside of the
precission for a float value. The bar stays at value of 1.67772e+07
and 67%.
In this fix I introduced tics_ varialbe of size_t which is increased
with every tick. And the progress_ is calcualted as a ratio of
tick_/max_progress.
The breaking and required change is the change of set_progress(float)
to set_progress(size_t), so it modifies directly the tics_ value.
2025-05-01 00:02:37 +02:00
BrandenXia
e0349eb806 Update examples, add percentage to match actual behavior 2025-02-19 08:45:39 -08:00
Pranav
ac6c93ea2b Merge pull request #142 from CodingRookie98/master
Fix Localization Information Restoration Issue: Change setlocale(LC_ALL, "") to setlocale(LC_ALL, NULL)
2025-01-25 09:55:59 -05:00
CodingRookie98
c793684c9c Fix the issue where the localization information is set before the program calls the 'utf8_decode' function in the project code. After calling the 'utf8_decode' function, the restored localization information is the default setting from the user environment variables, rather than the current program's localization information. Therefore, the 'setlocale(LC_ALL, "")' call should be changed to 'setlocale(LC_ALL, NULL)' to align with the code logic. 2025-01-25 14:56:42 +08:00
zhaoshihan
0c4ec7d773 Add CMake options INDICATORS_INSTALL and INDICATORS_PACKAGE 2025-01-08 23:55:37 +08:00
Pranav
9c855c95e7 Merge pull request #136 from Kicer86/progressbars_ownership
Make DynamicProgress take ownership over progress bars
2024-08-05 09:08:01 -04:00
Michał Walenciak
7962c540a8 Update code 2024-08-03 14:01:09 +02:00
Michał Walenciak
cefe02f449 Synchronize comments with README 2024-08-03 14:00:57 +02:00
Michał Walenciak
05ba667474 Adapt samples 2024-08-03 13:57:27 +02:00
Michał Walenciak
13bdb4b396 Add missing include 2024-08-03 13:57:20 +02:00
Michał Walenciak
cb8f728c68 Make DynamicProgress take ownership over progress bars 2024-07-29 17:35:52 +02:00
Pranav
222382c3a6 Merge pull request #129 from zhangxianbing/master
Fix setlocale usage in utf8_decode function
2023-11-21 05:38:11 -06:00
zhangxianbing
68d059d6d2 Fix setlocale usage in utf8_decode function 2023-11-21 11:02:13 +08:00
Pranav
ef71abd9bc Merge pull request #124 from tocic/fix/undeclared_uint8_t
Add missing <cstdint> header
2023-05-07 05:51:51 -05:00
tocic
525ce444f4 Add missing <cstdint> header
Fixes the "‘uint8_t’ has not been declared" error with gcc 13.1.1.
2023-05-07 12:36:15 +03:00
Pranav
4ea716116d Merge pull request #121 from danielBreitlauch/master
Bug fix: check prefix and postfix length errors
2023-04-17 07:55:32 -05:00
Daniel Breitlauch
6549b77d0b remove signed to unsigned conversion + check for errors 2023-04-04 12:32:37 +02:00
Pranav
5050a88877 Merge pull request #120 from avighnac/master
Fix 'block progress bar' README.md example not working
2023-03-10 17:19:26 -06:00
Avighna
d6e70912bb Merge pull request #1 from avighnac/avighnac-patch-1
Update README.md
2023-03-10 23:11:40 +05:30
Avighna
bd4826c885 Update README.md
Fixed the 'block progress bar' example not working due to a missing `#include` directive.
2023-03-10 23:10:05 +05:30
Pranav
b44735a2a5 Update README.md 2023-02-15 11:16:18 -06:00
Pranav Srinivas Kumar
26d39ad8fb Bumped to version v2.3 2023-02-15 07:17:27 -08:00
Pranav Srinivas Kumar
bfe95fee17 Updated single_include to support mbstowcs_s Windows impl 2023-02-15 07:11:53 -08:00
Pranav
11c60675d5 Merge pull request #119 from jsttzsf/bugfix/mbstowcs_deprecation
Replaced mbstowcs with mbstowcs_s
2023-02-15 09:09:54 -06:00
Julian Schiedel
98f6e50af9 Added preprocesser fork to account for missing mbstowcs_s on linux 2023-02-15 15:48:37 +01:00
Julian Schiedel
dcfaebf9d9 Replaced mbstowcs with mbstowcs_s to fix fix compiler warning about deprecation 2023-02-15 10:36:38 +01:00
Pranav
f03feacc0b Merge pull request #118 from p-ranav/bugfix/codecvt
Removed deprecated codecvt
2023-02-14 10:10:32 -06:00
Pranav Srinivas Kumar
15c180b113 Removed unused vector header 2023-02-14 08:09:46 -08:00
Pranav Srinivas Kumar
80b822c576 Removed deprecated codecvt 2023-02-14 08:07:50 -08:00
Pranav
f38bf229eb Merge pull request #115 from aengusjiang/master
fix the bug of has_include error with g++4.8.5
2023-01-12 07:56:57 -06:00
Aengus.Jiang
0cce8d7326 fix the bug of has_include error with g++4.8.5 2023-01-12 17:34:05 +08:00
Pranav
a5bc05f32a Merge pull request #103 from SheepGoMeh/master
Fix Windows support for DynamicProgress
2021-10-28 18:43:20 -05:00
Dragon
40b99f6b04 Generate single header 2021-10-28 23:55:59 +03:00
Dragon
9f3b0247f5 Fix Windows support for DynamicProgress 2021-10-28 23:47:55 +03:00
Pranav
cdcff01339 Merge pull request #101 from Chuvi-w/Fix_Windows_h_min_max
Fix #define min/max problem from Windows.h
2021-07-12 10:17:20 -05:00
Chuvi
28a81289a6 Fix #define min/max problem from Windows.h 2021-07-12 09:56:08 +03:00
Pranav Srinivas Kumar
e34840ab0d Closes #93 - Defined _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING to remove codecvt warning 2021-05-03 17:34:59 -05:00
Pranav Srinivas Kumar
6432875ef4 Bumped to v2.2 2021-05-03 09:38:25 -05:00
Pranav Srinivas Kumar
276d476d4d Fixed the NOMINMAX issue for Windows build with latest termcolor #76, replaced #pragma once with #ifndef-define pairs 2021-05-03 09:37:44 -05:00
Pranav Srinivas Kumar
c338b5b280 Updated termcolor to v2.0. Bumped indicators to v2.1, closes #94 2021-05-02 23:17:24 -05:00
Pranav
91ceb5c7cf Merge pull request #92 from turuslan/fix/terminal_size
Uninitialized local variable in terminal_size
2021-03-18 08:36:30 -05:00
turuslan
96a2053d5b initialize local variable 2021-03-18 12:23:58 +03:00
Pranav Srinivas Kumar
4790bfddca Added carriage return before prefix text 2021-03-13 10:48:45 -06:00
Pranav
23b2364f2a Merge pull request #88 from usama-makhzoum/master
Fix some source file permissions
2021-02-18 07:28:53 -06:00
usama makhzoum
0cb5e11636 Fix some source file permissions 2021-02-17 21:43:01 +03:00
Pranav Srinivas Kumar
eb04b594dc Bumped version 2021-01-07 06:47:43 -08:00
Pranav Srinivas Kumar
b85eadb47c Closes #80 2020-12-08 09:15:05 -06:00
Pranav
312c49bf9f Merge pull request #78 from csparker247/77-time-remaining-overflow
Mitigate overflow when calculating ETA
2020-11-16 12:27:33 -06:00
Seth Parker
d11ffd91c6 Round time estimate up to nearest nanosecond 2020-11-16 11:27:16 -05:00
Seth Parker
e01c2950f2 Use floats for remaining time math 2020-11-16 10:54:05 -05:00
Seth Parker
c3ff1fbd91 Mitigate overflow when calculating eta 2020-11-12 13:21:42 -05:00
Pranav Srinivas Kumar
af7c004832 Updated single_include after changing percent calculation in progress spinner 2020-10-26 07:59:48 -05:00
Pranav Srinivas Kumar
77d910d0cf Fixed percentage calculation for progress spinner for user-defined option::MaxProgress 2020-10-26 07:56:44 -05:00
Pranav Srinivas Kumar
f126fde1b4 Fixed missing endif in single header version 2020-09-16 20:06:05 -05:00
Pranav
a7a3f08370 Merge pull request #73 from jcnoronha/bugfix/terminate_def
Terminate def in termcolor
2020-09-16 05:35:47 -05:00
j.noronha
05955650d6 Terminate def in termcolor 2020-09-16 21:16:45 +12:00
Pranav Srinivas Kumar
3d48e2bd25 Closes #72 2020-09-15 08:54:21 -05:00
Pranav
41650d005a Merge pull request #69 from duncanmcn/mingw_compilation
Change include guard to win32
2020-07-22 05:14:10 -05:00
duncanmcn
430a73b7b6 Change include guard to win32 2020-07-22 14:53:47 +12:00
Pranav Srinivas Kumar
f87de6ccbd Fixed missing include for termcolor in dynamic progress and multi progress 2020-07-21 10:20:07 -05:00
Pranav Srinivas Kumar
04fddb9014 Removed redundant includes of termcolor.hpp and updated single include. Closes #68 2020-07-21 10:14:52 -05:00
Pranav
1e622293aa Update indicators.hpp 2020-07-05 13:32:06 -05:00
Pranav
755900d524 Update terminal_size.hpp
Closes #67
2020-07-05 13:31:45 -05:00
Pranav
b43da291f6 Update README.md 2020-06-21 11:53:00 -05:00
Pranav
d9a5219d09 Merge pull request #66 from godbyk/bugfix/nested-namespace
Elevate #includes from being nested in the namespace.
2020-06-17 08:35:52 -05:00
Pranav
51e3460ad0 Merge pull request #65 from godbyk/feature/workaround-missing-codecvt
Support older compilers with missing <codecvt> header.
2020-06-17 08:35:13 -05:00
Kevin M. Godby
74c36cbb93 Support older compilers with missing <codecvt> header. 2020-06-16 13:29:02 -05:00
Kevin M. Godby
3cef73d30d Elevate #includes from being nested in the namespace. 2020-06-16 13:23:45 -05:00
Pranav Srinivas Kumar
50f89e150d Updated single header file version with fix for #63 2020-06-01 08:04:13 -05:00
Pranav Srinivas Kumar
ccd9c5d99d Closes 63 2020-06-01 08:03:40 -05:00
Pranav Srinivas Kumar
a776a52c98 Using \033 instead of \e for ISO-standards compliance #62 2020-05-25 13:06:08 -05:00
Pranav Srinivas Kumar
79dda6dbae Bumped version to v1.9
* Fixed build issues in g++ caused by missing header #54
* Using terminal width to fill the remainder of the progress bar with empty spaces #56
* Previously this was hard-coded to 10 spaces
* Fixed multiple definitions in helper functions, e.g., cursor control #58
* Implemented option::ProgressType which can be incremental or decremental - Provides an easy interface to implement decremental progress (a regressing bar) #59
* Updated to latest version of termcolor #60
2020-05-25 10:24:03 -05:00
Pranav
be24a3ab48 Merge pull request #61 from p-ranav/bugfix/56
Bugfix/56
2020-05-25 10:02:28 -05:00
Pranav Srinivas Kumar
8e39967c1e Removed unused variables 2020-05-25 09:53:56 -05:00
Pranav Srinivas Kumar
20b1d1f69f Updated single include version 2020-05-25 09:51:02 -05:00
Pranav Srinivas Kumar
f6cef6161d Removed sleep 2020-05-25 09:47:48 -05:00
Pranav Srinivas Kumar
6be9c8005f Added terminal size checks in indeterminate progress bar 2020-05-25 09:35:29 -05:00
Pranav Srinivas Kumar
0973cde3d3 Using terminal size in block progress bar 2020-05-25 09:29:32 -05:00
Pranav Srinivas Kumar
958228f55d Removed code duplication in prefix/postfix length calculation 2020-05-25 09:25:22 -05:00
Pranav Srinivas Kumar
7c1d7d6367 Added dynamic postfix text sample based on #56 2020-05-25 09:14:23 -05:00
Pranav Srinivas Kumar
63bb0d8946 Progress bar now uses terminal size to figure out how many spaces to use to fill up the remainder of the terminal row 2020-05-25 08:51:34 -05:00
Pranav Srinivas Kumar
c738215419 Updated single include version w/ latest termcolor 2020-05-25 08:22:46 -05:00
Pranav
664278a432 Merge pull request #60 from data-man/termcolor_latest
Update termcolor to latest
2020-05-25 08:22:14 -05:00
data-man
5660eee6d9 Update termcolor to latest 2020-05-25 09:13:01 +05:00
Pranav Srinivas Kumar
4bf1433c21 Updated single include version 2020-05-24 23:12:20 -05:00
Pranav
fb7b4f7221 Update README.md 2020-05-24 23:07:30 -05:00
Pranav Srinivas Kumar
bd586bbb88 Updated countdown progress bar sample 2020-05-24 23:07:02 -05:00
Pranav
970490c111 Update README.md 2020-05-24 23:06:40 -05:00
Pranav
e1eeaf1bb7 Update README.md 2020-05-24 23:05:54 -05:00
Pranav Srinivas Kumar
728c4f7f7f Added countdown progress bar sample 2020-05-24 23:03:39 -05:00
Pranav Srinivas Kumar
a2e11d4707 Clang format 2020-05-24 22:56:57 -05:00
Pranav
e5cc05a62d Merge pull request #59 from p-ranav/feature/countdown_progress_bar
Feature/countdown progress bar
2020-05-24 22:56:04 -05:00
Pranav Srinivas Kumar
885748d4c2 Reverted minor changes to stream helper w.r.t checking if fill, lead and remainder are empty 2020-05-24 22:47:18 -05:00
Pranav Srinivas Kumar
d0562438d5 Added ProgressType enum and updated single include 2020-05-24 22:43:53 -05:00
Pranav Srinivas Kumar
4dff4e7c5b Restored decremental progress demo 2020-05-24 22:43:11 -05:00
Pranav Srinivas Kumar
120f922326 Added progress_type option to indicate incremental/decremental progress 2020-05-24 22:37:58 -05:00
Pranav
2d9bd0c4a0 Update README.md 2020-05-23 17:24:48 -05:00
Pranav
b27888e523 Update README.md 2020-05-23 17:23:46 -05:00
Pranav Srinivas Kumar
ac5e759794 Minor updates 2020-05-23 17:21:58 -05:00
Pranav
545113c00d Update README.md 2020-05-23 17:21:50 -05:00
Pranav
a553ec6d83 Update README.md 2020-05-23 17:20:57 -05:00
Pranav Srinivas Kumar
16eb028686 Improved support for unicode including indeterminate progress bar. Updated unicode sample 2020-05-23 17:15:18 -05:00
Pranav Srinivas Kumar
cf1f888a0f Better support for displaying unicode characters in the progress bar, while respecting bar_width 2020-05-23 17:00:13 -05:00
Pranav Srinivas Kumar
27f4ad1f59 Made cursor control/movement functions static inline #58 2020-05-23 12:40:17 -05:00
Pranav
a5fb2bbcaf Update README.md 2020-05-22 17:14:18 -05:00
Pranav Srinivas Kumar
f4f1402b66 Updated single header version with missing include 2020-05-22 17:10:15 -05:00
Pranav
3515cb5bde Merge pull request #57 from dawidpilarski/master
[issue-54] added missing header
2020-05-22 17:08:21 -05:00
Dawid Pilarski
b66930b173 [issue-54] added missing header 2020-05-23 01:22:49 +02:00
Pranav
9a1e58a836 Fixes travis CI to correctly build indicators samples and demos 2020-05-19 15:09:10 -05:00
Pranav Srinivas Kumar
078ef915a3 Fixed messed up includes in demo. Closes #54 2020-05-18 18:41:18 -05:00
Pranav
695fac00b4 Update README.md 2020-05-13 15:42:26 -05:00
Pranav Srinivas Kumar
17e1f47fd4 Added amalgamate to help generate single header for indicators 2020-05-13 15:39:20 -05:00
Pranav Srinivas Kumar
a1ad525065 Updated single header file version of indicators
* Added IndeterminateProgressBar
* Added `Color::unspecified` enum field
* Added `option::MaxProgress` and `option::Stream`
2020-05-09 21:41:57 -05:00
Pranav Srinivas Kumar
ad6cddbd0e Updated iterable sample GIF 2020-05-09 21:01:23 -05:00
Pranav Srinivas Kumar
b389f5e559 Updated iterable sample GIF 2020-05-09 20:58:41 -05:00
Pranav Srinivas Kumar
04cdd25844 Added sample to show max_progress working with iterables. Updated README 2020-05-09 20:54:32 -05:00
Pranav
e463046745 Update README.md 2020-05-09 20:39:40 -05:00
Pranav
3a798551ba Update README.md 2020-05-09 20:37:20 -05:00
Pranav
26c4c0286e Merge pull request #52 from p-ranav/feature/43
Closes #22
2020-05-09 20:36:09 -05:00
Pranav
5972b18450 Update README.md 2020-05-09 20:35:07 -05:00
Pranav Srinivas Kumar
6f057a51a1 Closes #22 2020-05-09 20:33:15 -05:00
Pranav Srinivas Kumar
b215cb850e Added functions to retrieve terminal width 2020-05-08 18:09:08 -05:00
Pranav
e950065e34 Merge pull request #51 from p-ranav/feature/43
Feature/43
2020-05-08 17:28:47 -05:00
Pranav
643e5ce6a1 Update README.md 2020-05-08 17:25:50 -05:00
Pranav Srinivas Kumar
11ec12c0cb Added indeterminate progress bar demo GIF 2020-05-08 17:23:21 -05:00
Pranav Srinivas Kumar
d2179d75ce Updated sample for indeterminate_progress_bar and added demo GIF 2020-05-08 17:18:00 -05:00
Pranav Srinivas Kumar
6d73795218 Implemented indeterminate progress bar w/ sample 2020-05-08 17:13:28 -05:00
Pranav Srinivas Kumar
6857345da5 Initial commit 2020-05-08 16:30:47 -05:00
Pranav Srinivas Kumar
3800785216 Updated dynamic progress bar GIF 2020-05-08 16:19:13 -05:00
Pranav Srinivas Kumar
a949efd512 Removed unnecessary mark_as_completed in set_progress sample 2020-05-08 15:23:01 -05:00
Pranav Srinivas Kumar
296bde6088 Minor bug fixes and updates:
* Removed progress bar sample that progresses in reverse direction - This needs a class of its own
* Fixed a casting error in progress_bar class when dealing with max_progress variable
* Minor update to the cursor movement functions in linux
* Updated single include to include these changes
2020-05-08 15:14:52 -05:00
Pranav
66af25ab62 Merge pull request #50 from godbyk/feature/adjustable-tick-size
Add a MaxProgress option (default: 100).
2020-05-02 18:45:30 -05:00
Kevin M. Godby
371bcb2c3b Add a MaxProgress option (default: 100).
The MaxProgress option allows you to set the maximum number of ticks
that are within a progress bar. Each call to tick() increments the tick
count. The progress bar percentage is the number of ticks divided by the
MaxProgress option.

The default MaxProgress is 100, so each tick would be 1%.  If
MaxProgress is set to 500, for example, then each tick would be 0.2%.
2020-04-30 21:37:06 -05:00
Pranav
df4a97b068 Merge pull request #49 from godbyk/bugfix/cmake-homepage-url
Support versions of CMake going back to version 3.8.
2020-04-30 17:59:10 -07:00
Kevin M. Godby
55494a572b Support version of CMake going back to version 3.8.
The DESCRIPTION parameter of the project() command was introduced in
CMake version 3.9.

The HOMEPAGE_URL parameter was introduced in CMake version 3.11.
2020-04-30 19:24:17 -05:00
Pranav
3d1d01ba0d Merge pull request #48 from motis-project/win-cursor-mv
Abstraction for Cursor Movements (Issue #46)
2020-04-26 10:28:57 -07:00
Felix Gündling
c2beb2ac22 adjust readme, demo, and examples: replace show/hide cursor calls 2020-04-26 18:52:23 +02:00
Felix Gündling
d99cad1ede setting console mode to processed output not neccesary if we use SetConsoleCursorPosition 2020-04-25 08:57:32 +02:00
Felix Gündling
ad5cf841be cursor movement and cursor hiding support for windows 2020-04-25 08:49:30 +02:00
Pranav Srinivas Kumar
4aef1470f1 Showing [00:00<00:00] if start time is not saved yet #45 2020-04-15 10:18:28 -05:00
Pranav Srinivas Kumar
dd08556b27 Fixed typo 2020-04-06 11:51:19 -07:00
Pranav
57a942f392 Merge pull request #40 from p-ranav/feature/font_styles
Feature/font styles
2020-04-06 11:49:27 -07:00
Pranav Srinivas Kumar
78a98fec4d Minor updates 2020-04-06 11:48:16 -07:00
Pranav Srinivas Kumar
06414bb6db Resolved merge conflicts 2020-04-06 11:46:53 -07:00
Pranav Srinivas Kumar
cd6dc2aebf Fixed merge conflict 2020-04-06 11:43:20 -07:00
Pranav Srinivas Kumar
446b0d3147 Recovered CMakeLists 2020-04-06 11:41:18 -07:00
Pranav Srinivas Kumar
a3e50e0862 Updated single include 2020-04-06 11:35:10 -07:00
Pranav
936b8e66d8 Update README.md 2020-04-06 13:30:57 -05:00
Pranav Srinivas Kumar
16d3484788 Fixed build 2020-04-06 11:25:54 -07:00
Pranav Srinivas Kumar
833e1cd6db Initial commit 2020-04-06 11:10:41 -07:00
Pranav Srinivas Kumar
07b9eede8a Minor updates to support FontStyles 2020-04-06 11:05:44 -07:00
Pranav
562cb80a36 Merge pull request #38 from wolfv/add_color_unspecified
remove bold output and default color to unspecified
2020-04-06 11:05:31 -07:00
Wolf Vollprecht
7e43298470 remove bold output and default color to unspecified 2020-04-06 19:05:11 +02:00
Pranav
fd21e7d81f Closes #36 2020-04-03 16:02:31 -05:00
Pranav
37b3fc395e Merge pull request #35 from LesleyLai/master
fix MSVC unused parameter warning
2020-03-04 04:58:21 -06:00
Lesley
99f4f3f05b fix MSVC unused parameter warning 2020-03-03 22:02:11 -07:00
Pranav
5a9419b191 Update README.md 2020-02-24 16:37:13 +05:30
Pranav
4ada9e3f77 Update README.md 2020-02-24 16:36:39 +05:30
Pranav
8ad116cc5f Update README.md 2020-02-24 16:34:06 +05:30
Pranav
4b1ce0d2f7 Update README.md 2020-02-23 20:32:41 +05:30
Pranav
3fb4eb9dd1 Update README.md 2020-02-22 11:20:13 +05:30
Pranav
0e041e8fea Merge pull request #33 from p-ranav/bugfix/25
Change progress_ to size_t
2020-02-22 11:16:41 +05:30
Pranav Srinivas Kumar
201ce9c4fb Bumped to v1.8 2020-02-22 11:13:04 +05:30
Pranav Srinivas Kumar
d4a38eb034 Closes 25 - Changed to 2020-02-22 11:02:16 +05:30
Pranav
ec1973607d Merge pull request #32 from p-ranav/feature/dynamic-progress
Feature/dynamic progress
2020-02-22 00:53:39 +05:30
Pranav Srinivas Kumar
5049e70c50 Minor update 2020-02-22 00:53:13 +05:30
Pranav
5bc3183c12 Update README.md 2020-02-22 00:50:19 +05:30
Pranav
fcda546c9c Update README.md 2020-02-22 00:49:12 +05:30
Pranav Srinivas Kumar
f5c1e61676 Minor updates 2020-02-22 00:40:38 +05:30
Pranav Srinivas Kumar
c7b25e7c3a Added dynamic progress GIFs 2020-02-22 00:38:03 +05:30
Pranav Srinivas Kumar
5e75c285fe First pass implementation of dynamic progress bar 2020-02-21 22:40:16 +05:30
Pranav
7298c7140d Update README.md 2020-02-21 16:01:20 +05:30
Pranav
0ace816e52 Merge pull request #31 from madebr/cmake
Update cmake + add conan + fix msvc + add appveyor
2020-02-21 13:24:43 +05:30
Anonymous Maarten
91d51548fc Add appveyor script file 2020-02-21 00:36:05 +01:00
Anonymous Maarten
83da7c3469 Put NOMINMAX just before inclusion of windows.h header 2020-02-21 00:36:03 +01:00
Anonymous Maarten
b13ff53c26 Add conanfile 2020-02-20 23:54:12 +01:00
Anonymous Maarten
7c762ed146 Add indicatorsConfigVersion cmake support + create source packages 2020-02-20 23:40:41 +01:00
Pranav
3c969556c3 Merge pull request #30 from xbreak/pkgconfigfix
Fix indicators.pc.in if explicit includedir is set
2020-02-19 21:32:08 +05:30
xbreak
297790bcd7 Fix indicators.pc.in if explicit includedir is set
This fixes cases where both -DCMAKE_INSTALL_PREFIX and
-DCMAKE_INSTALL_INCLUDEDIR is set.
2020-02-19 14:43:58 +00:00
Pranav Srinivas Kumar
f9d3eb1bcc Minor updates 2020-02-13 14:22:52 +05:30
Pranav Srinivas Kumar
89ae8cec39 Clang format 2020-02-13 14:21:01 +05:30
Pranav Srinivas Kumar
f5b6b4d779 Added single header file v1.7 2020-02-13 14:20:40 +05:30
Pranav Srinivas Kumar
2b27d8f516 Minor update 2020-02-13 13:49:51 +05:30
Pranav Srinivas Kumar
77bc52b591 FIxed merge conflict 2020-02-11 17:30:24 +05:30
Pranav Srinivas Kumar
a6c55b6936 Updated enums to lowercase per the core guidelines. Bumped to wip v1.7 2020-02-11 17:29:41 +05:30
Pranav
e7b3d077f0 Update README.md 2020-02-11 17:23:23 +05:30
Pranav Srinivas Kumar
0588904b9d Closes #24 2020-02-11 16:31:31 +05:30
Pranav Srinivas Kumar
4442e3570f Minor update to sample 2020-02-11 13:45:22 +05:30
Pranav Srinivas Kumar
2698afb34b Bumped version to 1.6 2020-02-11 13:40:30 +05:30
Pranav
764d796f87 Merge pull request #21 from dawidpilarski/dev
API change - progress bar can be constructed with proper settings.
2020-02-11 13:38:14 +05:30
Dawid Pilarski
5c54b69e55 post review changes 2020-02-11 08:57:38 +01:00
Dawid Pilarski
186f1aafc7 What about license? 2020-02-10 23:15:09 +01:00
Dawid Pilarski
cfb1593806 fixed multi_block_progress_bar.cpp 2020-02-10 21:51:20 +01:00
Dawid Pilarski
4a207b8223 fixed multi_block_progress_bar.cpp 2020-02-10 21:48:36 +01:00
Dawid Pilarski
07734c412b Readme updated 2020-02-10 21:25:54 +01:00
Dawid Pilarski
d543453c0c code changes done 2020-02-10 20:54:20 +01:00
Pranav
e279bbb7e7 Merge pull request #28 from svgsponer/hotfix/remove-semicolon
Remove extra semicolon
2020-02-05 22:12:16 -06:00
Severin Gsponer
840743f312 Remove extra ;
Removes a extra ; that breaks build with gcc and `-Werror=pedantic`.
2020-02-06 02:18:55 +00:00
Dawid Pilarski
46c9983fab Progress spinner changed, default values approach changed, templates refactored 2020-02-04 23:36:44 +01:00
Pranav
b1c16d8881 Merge pull request #27 from xbreak/pkgconfig
Added pkg-config file
2020-02-04 14:23:09 -06:00
xbreak
06da3a97f4 Added pkg-config file 2020-02-04 20:07:57 +00:00
Pranav Srinivas Kumar
a2272eba8a Closes #26 2020-02-04 07:45:47 -06:00
pilarski
46bccdcc1c ProgressBar API change - make ProgressBar constructible with options 2020-02-03 23:48:21 +01:00
Dawid Pilarski
3c8975aa34 API change - progress bar can be constructed with proper settings. 2020-02-03 20:50:19 +01:00
Pranav
9a4729f577 Merge pull request #19 from p-ranav/feature/18
Closes #18
2020-01-30 08:27:08 -06:00
Pranav Srinivas Kumar
866c008492 Closes #18 2020-01-30 08:19:25 -06:00
Pranav
dcb7c656cc Merge pull request #17 from hyperxor/extract_progress_scale_writers
Extract progress scale writing logic into separate classes
2020-01-21 12:18:51 -06:00
hyperxor
cfcc9f3b4c small fixes 2020-01-21 20:43:10 +03:00
hyperxor
91ac917bbc fix passed parameters 2020-01-21 08:19:53 +03:00
hyperxor
0939406db4 Extract progress scale writing code into separate classes 2020-01-21 07:49:47 +03:00
Pranav
d4dea6db2a Merge pull request #16 from hyperxor/remove_redundant_code_in_print_duration_and_small_fixes
Remove code duplication: move _print_duration to details + small fixes
2020-01-19 13:06:20 -06:00
hyperxor
0dec563049 Remove code duplication: move _print_duration to details + small fixes 2020-01-19 21:53:43 +03:00
Pranav
4f9796e904 Merge pull request #15 from hyperxor/remove_redundant_code_when_set_stream_color
Remove code duplication: extract set_stream_color function
2020-01-19 07:27:32 -06:00
hyperxor
a91d3b6ff7 Remove code duplication: extract set_stream_color function 2020-01-19 16:20:01 +03:00
Pranav
1cba7fd321 Merge pull request #14 from hyperxor/small_code_improvement_in_locks
Small improvement: using lock_guard instead of unique_lock
2020-01-19 06:42:38 -06:00
hyperxor
b1f01d5e52 Small improvement: using lock_guard instead of unique_lock 2020-01-19 13:36:10 +03:00
Pranav
2291c8c39c Update README.md 2019-12-27 15:22:19 -06:00
Pranav Srinivas Kumar
e697a43fec Clang format 2019-12-18 12:47:10 -06:00
Pranav Srinivas Kumar
358e3763c3 Minor update 2019-12-18 12:46:08 -06:00
Pranav Srinivas Kumar
419737af61 Merge branch 'feature/multiprogress_api' 2019-12-18 11:59:33 -06:00
Pranav Srinivas Kumar
9786633eba Bumped version 2019-12-18 11:59:20 -06:00
Pranav Srinivas Kumar
502ce33af4 Updated README w/ new MultiProgress API 2019-12-18 11:57:50 -06:00
Pranav Srinivas Kumar
8190a1e513 Minor update 2019-12-18 11:57:07 -06:00
Pranav Srinivas Kumar
72644e5134 Setting multiprogress mode in MultiProgress constructor 2019-12-18 11:55:56 -06:00
Pranav Srinivas Kumar
222a8ae4f9 Added explicit constructor for MultiProgress to remove insert API 2019-12-18 11:51:58 -06:00
Pranav Srinivas Kumar
2694badc93 Added locking to .current() 2019-12-18 07:55:22 -06:00
Pranav
ba604b7dd6 Update README.md 2019-12-17 22:04:36 -06:00
Pranav
2853aba409 Update README.md 2019-12-17 22:03:28 -06:00
Pranav Srinivas Kumar
2719604ba9 Updated README 2019-12-17 22:02:40 -06:00
Pranav
a80e4af117 Update README.md 2019-12-17 21:59:29 -06:00
Pranav Srinivas Kumar
d862ccde37 Updated FID 2019-12-17 21:59:17 -06:00
Pranav Srinivas Kumar
63ea50103a Merge branch 'master' of github.com:p-ranav/indicators 2019-12-17 21:56:41 -06:00
Pranav Srinivas Kumar
d9aa064380 Updated GIF 2019-12-17 21:56:36 -06:00
Pranav
5449ad0629 Update README.md 2019-12-17 21:49:37 -06:00
Pranav
c5969eefe4 Update README.md 2019-12-17 21:41:44 -06:00
Pranav Srinivas Kumar
34813c8642 Added progress_bar.tick GIF 2019-12-17 21:40:05 -06:00
Pranav Srinivas Kumar
c2832a5412 Updated GIF 2019-12-17 21:36:13 -06:00
Pranav Srinivas Kumar
67663b9d7d Updated GIF 2019-12-17 21:33:34 -06:00
Pranav
45ad6da847 Update README.md 2019-12-17 21:33:22 -06:00
Pranav Srinivas Kumar
5eb8cc5f91 Merge branch 'master' of github.com:p-ranav/indicators 2019-12-17 21:30:49 -06:00
Pranav Srinivas Kumar
99a14b5563 Updated GIF 2019-12-17 21:30:37 -06:00
Pranav
3bc140f90f Update README.md 2019-12-17 21:26:55 -06:00
Pranav
3f21b1d901 Update README.md 2019-12-17 21:15:47 -06:00
Pranav
7eb31cf24b Update README.md 2019-12-17 21:12:13 -06:00
Pranav
b726255bfd Merge pull request #12 from p-ranav/feature/multi_progress
Feature/multi progress
2019-12-17 21:11:07 -06:00
Pranav Srinivas Kumar
082b7a69e9 Updated README GIF 2019-12-17 21:05:14 -06:00
Pranav Srinivas Kumar
39922dbf80 Updated README 2019-12-17 20:59:50 -06:00
Pranav Srinivas Kumar
4f0cd06969 Minor update 2019-12-17 20:57:28 -06:00
Pranav Srinivas Kumar
70f843ce01 Minor update to API 2019-12-17 20:56:17 -06:00
Pranav Srinivas Kumar
4e5e5cfa19 Added missing includes 2019-12-17 20:40:44 -06:00
Pranav
36e77a64e9 Update README.md 2019-12-17 20:36:39 -06:00
Pranav
b542bedf5b Update README.md 2019-12-17 20:28:18 -06:00
Pranav Srinivas Kumar
8e41f2712e Added sample showing MultiProgress with BlockProgressBar 2019-12-17 20:21:33 -06:00
Pranav Srinivas Kumar
03000c8493 Changed MultiProgress into a template container class 2019-12-17 20:14:05 -06:00
Pranav Srinivas Kumar
92725d6cb2 Draft implementation of MultiProgress for progress bars 2019-12-17 20:06:50 -06:00
Pranav Srinivas Kumar
01966b8239 First attempt at MultiProgress 2019-12-17 19:10:52 -06:00
Pranav
788f6c9e1e Update README.md 2019-12-17 15:41:42 -06:00
Pranav
936043b7d4 Update block_progress_bar.hpp 2019-12-17 15:24:05 -06:00
Pranav
c90f375952 Update progress_spinner.hpp 2019-12-17 15:23:45 -06:00
Pranav
2241c00bb6 Update block_progress_bar.hpp 2019-12-17 15:23:26 -06:00
Pranav
bc128814fc Update progress_bar.hpp 2019-12-17 15:23:11 -06:00
Pranav
5a556bf5e2 Update README.md 2019-12-17 11:35:04 -06:00
Pranav
b5c2e78a72 Merge pull request #11 from offa/travis_ci
Travis CI
2019-12-17 11:31:07 -06:00
offa
4b2635020f Build status added. 2019-12-17 18:25:16 +01:00
offa
5751a58477 Travis CI supported added. 2019-12-17 18:25:16 +01:00
Pranav
a528d0d683 Update README.md 2019-12-17 10:05:44 -06:00
Pranav
74674154f5 Update README.md 2019-12-17 10:01:41 -06:00
Pranav
c42ae6862e Merge pull request #10 from p-ranav/feature/time
Feature/time
2019-12-17 10:00:23 -06:00
Pranav Srinivas Kumar
1e45ef3530 Updated README 2019-12-17 09:59:15 -06:00
Pranav Srinivas Kumar
d5a1c9a440 Update README 2019-12-17 09:48:36 -06:00
Pranav Srinivas Kumar
8c37f2f1dc Update README 2019-12-17 09:44:50 -06:00
Pranav Srinivas Kumar
aa5dffa4e2 Time elapsed/remaining is hidden by default 2019-12-17 09:43:53 -06:00
Pranav
78b7abcfbd Update README.md 2019-12-17 09:37:11 -06:00
Pranav Srinivas Kumar
fdad265e99 Update README 2019-12-17 09:35:52 -06:00
Pranav Srinivas Kumar
f28ab68c60 Updated README 2019-12-17 09:33:08 -06:00
Pranav Srinivas Kumar
6224a46371 Updated samples/demos to show usage of hide/show time elapsed/remaining 2019-12-17 09:31:43 -06:00
Pranav Srinivas Kumar
8198d8a802 Added time elapsed/remaining meter for progress spinner 2019-12-17 09:20:10 -06:00
Pranav Srinivas Kumar
5360fec641 Added time elapsed/remaining meter for block progress bar 2019-12-17 09:13:48 -06:00
Pranav Srinivas Kumar
c770704697 Draft implementation of time elapsed/remaining meter 2019-12-17 09:06:46 -06:00
Pranav Srinivas Kumar
97c89284a9 Clang format 2019-12-17 09:06:36 -06:00
Pranav
24d92beaca Merge pull request #9 from offa/cmake_samples
CMake support for Demo and Samples
2019-12-16 11:58:08 -06:00
offa
fe38859715 CMake support for building the demo added. 2019-12-16 18:49:06 +01:00
offa
ab63e2c45b CMake support for building the samples added. 2019-12-16 18:45:19 +01:00
Pranav
9d951f5e82 Update README.md 2019-12-16 11:07:16 -06:00
Pranav Srinivas Kumar
36ec8bb4d2 Merge branch 'master' of github.com:p-ranav/indicators 2019-12-16 11:03:36 -06:00
Pranav Srinivas Kumar
094f25e012 Added progress bar samples 2019-12-16 11:03:31 -06:00
Pranav
e9feed8aa1 Update README.md 2019-12-16 11:00:38 -06:00
Pranav
2b67c02ba6 Update README.md 2019-12-16 10:53:53 -06:00
Pranav
4c9ed35080 Update README.md 2019-12-16 10:22:14 -06:00
Pranav Srinivas Kumar
5f35000071 Merge branch 'master' of github.com:p-ranav/indicators 2019-12-16 10:19:55 -06:00
Pranav Srinivas Kumar
fb2d9bddbe Added progress spinner sample 2019-12-16 10:19:47 -06:00
Pranav
e7d0135316 Update README.md 2019-12-16 09:48:23 -06:00
62 changed files with 8931 additions and 1152 deletions

16
.appveyor.yml Normal file
View File

@@ -0,0 +1,16 @@
environment:
CONAN_RUN_TESTS: True
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
PATH: C:\\Python37;C:\\Python37\Scripts;C:\Users\appveyor\AppData\Roaming\Python\Python37\Scripts;%PATH%
# - APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
# PATH: /home/appveyor/.local/bin:$PATH
# - APPVEYOR_BUILD_WORKER_IMAGE: macOS
install:
- python --version
- python -m pip install conan --upgrade --user
- conan user
build_script:
- conan create .

65
.travis.yml Normal file
View File

@@ -0,0 +1,65 @@
language: generic
dist: bionic
matrix:
include:
- env: CXX=g++-9 CC=gcc-9
addons:
apt:
packages:
- g++-9
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- env: CXX=g++-8 CC=gcc-8
addons:
apt:
packages:
- g++-8
- env: CXX=g++-7 CC=gcc-7
addons:
apt:
packages:
- g++-7
- env: CXX=g++-6 CC=gcc-6
addons:
apt:
packages:
- g++-6
- env: CXX=g++-5 CC=gcc-5
addons:
apt:
packages:
- g++-5
- env: CXX=clang++-9 CC=clang-9
addons:
apt:
packages:
- clang-9
- libc++-9-dev
- libc++abi-9-dev
sources:
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
- env: CXX=clang++-8 CC=clang-8
addons:
apt:
packages:
- clang-8
- libc++-8-dev
- libc++abi-8-dev
- env: CXX=clang++-7 CC=clang-7
addons:
apt:
packages:
- clang-7
- libc++-7-dev
- libc++abi-7-dev
script:
- if [[ "$CXX" == clang* ]]; then export CXXFLAGS="-stdlib=libc++"; fi
- mkdir build && cd build
- cmake -DINDICATORS_DEMO=ON -DINDICATORS_SAMPLES=ON ..
- make

View File

@@ -1,33 +1,111 @@
cmake_minimum_required(VERSION 3.8) cmake_minimum_required(VERSION 3.8)
project(indica VERSION 1.0.0 LANGUAGES CXX)
option(INDICA_BUILD_TESTS OFF) if(NOT DEFINED PROJECT_NAME)
set(INDICATORS_IS_TOP_LEVEL ON)
else()
set(INDICATORS_IS_TOP_LEVEL OFF)
endif()
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.12")
project(indicators VERSION 2.3.0 LANGUAGES CXX
HOMEPAGE_URL "https://github.com/p-ranav/indicators"
DESCRIPTION "Activity Indicators for Modern C++")
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL "3.9")
project(indicators VERSION 2.3.0 LANGUAGES CXX
DESCRIPTION "Activity Indicators for Modern C++")
else()
project(indicators VERSION 2.3.0 LANGUAGES CXX)
endif()
if(EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
conan_basic_setup()
endif()
option(INDICATORS_INSTALL "Include an install target" ${INDICATORS_IS_TOP_LEVEL})
option(INDICATORS_PACKAGE "Include package targets" ${INDICATORS_IS_TOP_LEVEL})
option(INDICATORS_BUILD_TESTS "Build indicators tests + enable CTest")
option(INDICATORS_SAMPLES "Build indicators samples")
option(INDICATORS_DEMO "Build indicators demo" OFF)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
include(GNUInstallDirs) include(GNUInstallDirs)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
add_library(indica INTERFACE) add_library(indicators INTERFACE)
add_library(indica::indica ALIAS indica) add_library(indicators::indicators ALIAS indicators)
target_compile_features(indica INTERFACE cxx_std_11) target_compile_features(indicators INTERFACE cxx_std_11)
target_include_directories(indica INTERFACE target_include_directories(indicators INTERFACE
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>)
target_link_libraries(indica INTERFACE Threads::Threads) target_link_libraries(indicators INTERFACE Threads::Threads)
configure_package_config_file(indicaConfig.cmake.in if(INDICATORS_DEMO)
${CMAKE_CURRENT_BINARY_DIR}/indicaConfig.cmake add_subdirectory(demo)
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indica) endif()
install(TARGETS indica EXPORT indicaTargets) if(INDICATORS_SAMPLES)
install(EXPORT indicaTargets add_subdirectory(samples)
FILE indicaTargets.cmake endif()
NAMESPACE indica::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indica) if(INDICATORS_INSTALL)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/indicaConfig.cmake configure_package_config_file(indicatorsConfig.cmake.in
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indica) ${CMAKE_CURRENT_BINARY_DIR}/indicatorsConfig.cmake
install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/indicators INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indicators)
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} write_basic_package_version_file(indicatorsConfigVersion.cmake
USE_SOURCE_PERMISSIONS COMPATIBILITY AnyNewerVersion)
PATTERN "*.hpp")
configure_file(indicators.pc.in indicators.pc @ONLY)
install(TARGETS indicators EXPORT indicatorsTargets)
install(EXPORT indicatorsTargets
FILE indicatorsTargets.cmake
NAMESPACE indicators::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indicators)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/indicatorsConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/indicatorsConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indicators)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/indicators.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/indicators
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
USE_SOURCE_PERMISSIONS
PATTERN "*.hpp")
install(FILES LICENSE LICENSE.termcolor
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/licenses/indicators)
endif()
if(INDICATORS_PACKAGE)
if(EXISTS "${PROJECT_SOURCE_DIR}/.gitignore")
# Simple glob to regex conversion (.gitignore => CPACK_SOURCE_IGNORE_FILES)
file(READ ".gitignore" DOT_GITIGNORE)
string(REPLACE ";" "RANDOMSEQUENCE" DOT_GITIGNORE "${DOT_GITIGNORE}")
string(REPLACE "\n" ";" DOT_GITIGNORE "${DOT_GITIGNORE}")
string(REPLACE "RANDOMSEQUENCE" "\\;" DOT_GITIGNORE "${DOT_GITIGNORE}")
foreach(IGNORE_LINE ${DOT_GITIGNORE})
if(NOT IGNORE_LINE OR IGNORE_LINE MATCHES "^#")
continue()
endif()
string(REPLACE "\\" "\\\\" IGNORE_LINE "${IGNORE_LINE}")
string(REPLACE "." "\\\\." IGNORE_LINE "${IGNORE_LINE}")
string(REPLACE "*" ".*" IGNORE_LINE "${IGNORE_LINE}")
string(REPLACE "+" "\\\\+" IGNORE_LINE "${IGNORE_LINE}")
list(APPEND CPACK_SOURCE_IGNORE_FILES "${IGNORE_LINE}")
endforeach()
endif()
# extra ignored files
list(APPEND CPACK_SOURCE_IGNORE_FILES
.editorconfig
.git
.gitignore
.travis.yml
.appveyor.yml
)
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_GENERATOR "TGZ;TXZ")
set(CPACK_SOURCE_GENERATOR "TGZ;TXZ")
include(CPack)
endif()

1010
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,2 @@
#!/usr/bin/env bash #!/usr/bin/env bash
find ./include ./demo/ -type f \( -iname \*.cpp -o -iname \*.hpp \) | xargs clang-format -style="{ColumnLimit : 100}" -i find ./include ./demo/ ./samples/ ./single_include -type f \( -iname \*.cpp -o -iname \*.hpp \) | xargs clang-format -style="{ColumnLimit : 100}" -i

78
conanfile.py Normal file
View File

@@ -0,0 +1,78 @@
from conans import CMake, ConanFile, tools
from conans.errors import ConanException
from contextlib import contextmanager
import os
class IndicatorsConan(ConanFile):
name = "indicators"
description = "Activity Indicators for Modern C++"
topics = ("indicators", "progress", "activity", "indicator", "header-only", "c++11")
homepage = "https://github.com/p-ranav/indicators"
url = "https://github.com/p-ranav/indicators"
license = ("MIT", "BSD-3-Clause")
exports_sources = "demo/**", "include/**", "samples/**", "CMakeLists.txt", "LICENSE*", \
"indicators.pc.in", "indicatorsConfig.cmake.in", "README*", "img/**"
exports = "LICENSE"
no_copy_source = True
settings = "os", "arch", "compiler", "build_type"
generators = "cmake"
def set_version(self):
import re
m = re.search(r"project\(.*VERSION ([0-9a-zA-Z.-]+)[ )]",
open(os.path.join(self.recipe_folder, "CMakeLists.txt")).read())
if not m:
raise ConanException("Could not extract version from CMakeLists.txt")
self.version = m.group(1)
_cmake = None
def _configure_cmake(self):
if self._cmake:
return self._cmake
generator = None
if self.settings.compiler == "Visual Studio":
generator = "NMake Makefiles"
self._cmake = CMake(self, generator=generator)
if tools.get_env("CONAN_RUN_TESTS", default=False):
self._cmake.definitions["INDICATORS_DEMO"] = True
self._cmake.definitions["INDICATORS_SAMPLES"] = True
self._cmake.configure()
return self._cmake
@property
def _test_programs(self):
programs = []
import re
for subdir in ("demo", "samples", ):
for match in re.finditer(r"add_executable\((\S+)",
open(os.path.join(self.source_folder, subdir, "CMakeLists.txt")).read()):
programs.append(os.path.join(self.build_folder, "bin", match.group(1)))
return programs
@contextmanager
def _build_context(self):
with tools.vcvars(self.settings) if self.settings.compiler == "Visual Studio" else tools.no_op():
yield
def build(self):
with self._build_context():
cmake = self._configure_cmake()
cmake.build()
cmake.build(target="package_source")
# if tools.get_env("CONAN_RUN_TESTS", default=False):
# for program in self._test_programs:
# self.output.info("Running program '{}'".format(program))
# self.run(program, run_environment=True)
def package(self):
with self._build_context():
cmake = self._configure_cmake()
cmake.install()
def package_id(self):
self.info.header_only()
def package_info(self):
self.cpp_info.includedirs.append(os.path.join("include", "indicators"))

2
demo/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
add_executable(demo demo.cpp)
target_link_libraries(demo PRIVATE indicators::indicators)

View File

@@ -1,24 +1,27 @@
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp> #include <indicators/progress_bar.hpp>
#include <indicators/progress_spinner.hpp> #include <indicators/progress_spinner.hpp>
#include <vector> #include <vector>
int main() { int main() {
using namespace indicators;
// Hide cursor // Hide cursor
std::cout << "\e[?25l"; show_console_cursor(false);
{ {
// //
// PROGRESS BAR 1 // PROGRESS BAR 1
// //
indicators::ProgressBar p; indicators::ProgressBar p{
p.set_bar_width(50); option::BarWidth{50},
p.start_bar_with("["); option::Start{"["},
p.fill_bar_progress_with(""); option::Fill{""},
p.lead_bar_progress_with(""); option::Lead{""},
p.fill_bar_remainder_with(" "); option::Remainder{" "},
p.end_bar_with(" ]"); option::End{" ]"},
p.set_foreground_color(indicators::Color::YELLOW); option::ForegroundColor{indicators::Color::yellow},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
std::atomic<size_t> index{0}; std::atomic<size_t> index{0};
std::vector<std::string> status_text = {"Rocket.exe is not responding", std::vector<std::string> status_text = {"Rocket.exe is not responding",
@@ -35,7 +38,7 @@ int main() {
while (true) { while (true) {
if (p.is_completed()) if (p.is_completed())
break; break;
p.set_postfix_text(status_text[index % status_text.size()]); p.set_option(option::PostfixText{status_text[index % status_text.size()]});
p.set_progress(index * 10); p.set_progress(index * 10);
index += 1; index += 1;
std::this_thread::sleep_for(std::chrono::milliseconds(600)); std::this_thread::sleep_for(std::chrono::milliseconds(600));
@@ -50,20 +53,23 @@ int main() {
// PROGRESS BAR 2 // PROGRESS BAR 2
// //
indicators::ProgressBar p; indicators::ProgressBar p;
p.set_bar_width(40); p.set_option(option::BarWidth{0});
p.set_prefix_text("Reading package list... "); p.set_option(option::PrefixText{"Reading package list... "});
p.start_bar_with(""); p.set_option(option::Start{""});
p.fill_bar_progress_with(""); p.set_option(option::Fill{""});
p.lead_bar_progress_with(""); p.set_option(option::Lead{""});
p.fill_bar_remainder_with(""); p.set_option(option::Remainder{""});
p.end_bar_with(""); p.set_option(option::End{""});
p.set_foreground_color(indicators::Color::WHITE); p.set_option(option::ForegroundColor{indicators::Color::white});
p.hide_percentage(); p.set_option(option::ShowPercentage{false});
p.set_option(
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto job = [&p]() { auto job = [&p]() {
while (true) { while (true) {
p.set_prefix_text("Reading package list... " + std::to_string(p.current()) + "% "); p.set_option(
option::PrefixText{"Reading package list... " + std::to_string(p.current()) + "% "});
if (p.current() + 2 >= 100) if (p.current() + 2 >= 100)
p.set_prefix_text("Reading package list... Done"); p.set_option(option::PrefixText{"Reading package list... Done"});
p.tick(); p.tick();
if (p.is_completed()) { if (p.is_completed()) {
break; break;
@@ -80,25 +86,27 @@ int main() {
// PROGRESS BAR 3 // PROGRESS BAR 3
// //
indicators::ProgressBar p; indicators::ProgressBar p;
p.set_bar_width(50); p.set_option(option::BarWidth{50});
p.start_bar_with("["); p.set_option(option::Start{"["});
p.fill_bar_progress_with("="); p.set_option(option::Fill{"="});
p.lead_bar_progress_with(">"); p.set_option(option::Lead{">"});
p.fill_bar_remainder_with(" "); p.set_option(option::Remainder{" "});
p.end_bar_with("]"); p.set_option(option::End{"]"});
p.set_postfix_text("Getting started"); p.set_option(option::PostfixText{"Getting started"});
p.set_foreground_color(indicators::Color::GREEN); p.set_option(option::ForegroundColor{indicators::Color::green});
p.set_option(
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto job = [&p]() { auto job = [&p]() {
while (true) { while (true) {
auto ticks = p.current(); auto ticks = p.current();
if (ticks > 20 && ticks < 50) if (ticks > 20 && ticks < 50)
p.set_postfix_text("Delaying the inevitable"); p.set_option(option::PostfixText{"Delaying the inevitable"});
else if (ticks > 50 && ticks < 80) else if (ticks > 50 && ticks < 80)
p.set_postfix_text("Crying quietly"); p.set_option(option::PostfixText{"Crying quietly"});
else if (ticks > 80 && ticks < 98) else if (ticks > 80 && ticks < 98)
p.set_postfix_text("Almost there"); p.set_option(option::PostfixText{"Almost there"});
else if (ticks >= 98) else if (ticks >= 98)
p.set_postfix_text("Done"); p.set_option(option::PostfixText{"Done"});
p.tick(); p.tick();
if (p.is_completed()) if (p.is_completed())
break; break;
@@ -115,32 +123,34 @@ int main() {
// //
std::vector<std::string> lead_spinner{"", "", "", "", "", "", "", "", "", ""}; std::vector<std::string> lead_spinner{"", "", "", "", "", "", "", "", "", ""};
indicators::ProgressBar p4; indicators::ProgressBar p4;
p4.set_bar_width(40); p4.set_option(option::BarWidth{40});
p4.start_bar_with(""); p4.set_option(option::Start{""});
p4.fill_bar_progress_with(""); p4.set_option(option::Fill{""});
p4.lead_bar_progress_with(""); p4.set_option(option::Lead{""});
p4.fill_bar_remainder_with(" "); p4.set_option(option::Remainder{" "});
p4.end_bar_with(""); p4.set_option(option::End{""});
p4.set_foreground_color(indicators::Color::CYAN); p4.set_option(option::ForegroundColor{indicators::Color::cyan});
p4.set_postfix_text("Restoring system state"); p4.set_option(option::PostfixText{"Restoring system state"});
p4.hide_percentage(); p4.set_option(option::ShowPercentage{false});
p4.set_option(
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
std::atomic<size_t> index4{0}; std::atomic<size_t> index4{0};
auto job4 = [&p4, &index4, &lead_spinner]() { auto job4 = [&p4, &index4, &lead_spinner]() {
while (true) { while (true) {
p4.set_prefix_text("{ " + std::to_string(p4.current()) + "% } "); p4.set_option(option::PrefixText{"{ " + std::to_string(p4.current()) + "% } "});
p4.lead_bar_progress_with(lead_spinner[index4 % lead_spinner.size()]); p4.set_option(option::Lead{lead_spinner[index4 % lead_spinner.size()]});
index4 += 1; index4 += 1;
if (p4.current() + 2 >= 100) { if (p4.current() + 2 >= 100) {
std::cout << std::endl; std::cout << std::endl;
p4.set_foreground_color(indicators::Color::RED); p4.set_option(option::ForegroundColor{indicators::Color::red});
p4.set_prefix_text("{ ERROR }"); p4.set_option(option::PrefixText{"{ ERROR }"});
p4.start_bar_with(""); p4.set_option(option::BarWidth{0});
p4.fill_bar_progress_with(""); p4.set_option(option::Start{});
p4.lead_bar_progress_with(""); p4.set_option(option::Fill{});
p4.fill_bar_remainder_with(""); p4.set_option(option::Lead{});
p4.end_bar_with(""); p4.set_option(option::Remainder{});
p4.show_percentage(); p4.set_option(option::ShowPercentage{true});
p4.set_postfix_text("Failed to restore system"); p4.set_option(option::PostfixText{"Failed to restore system"});
p4.mark_as_completed(); p4.mark_as_completed();
break; break;
} }
@@ -157,24 +167,22 @@ int main() {
// //
// GOING BACKWARDS // GOING BACKWARDS
// //
indicators::ProgressBar p; indicators::ProgressBar p{
p.set_bar_width(50); option::BarWidth{50},
p.start_bar_with("["); option::ProgressType{ProgressType::decremental},
p.fill_bar_progress_with(""); option::Start{"["},
p.lead_bar_progress_with(""); option::Fill{""},
p.fill_bar_remainder_with("-"); option::Lead{""},
p.end_bar_with("]"); option::Remainder{"-"},
p.set_progress(100); option::End{"]"},
p.set_foreground_color(indicators::Color::WHITE); option::ForegroundColor{indicators::Color::white},
p.set_postfix_text("Reverting system restore"); option::PostfixText{"Reverting system restore"},
std::atomic<size_t> progress{100}; option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
auto job = [&p, &progress]() { auto job = [&p]() {
while (true) { while (true) {
progress -= 1; p.tick();
p.set_progress(progress); if (p.is_completed()) {
if (progress == 0) { p.set_option(option::PostfixText{"Revert complete!"});
p.set_postfix_text("Revert complete!");
p.mark_as_completed();
break; break;
} }
std::this_thread::sleep_for(std::chrono::milliseconds(60)); std::this_thread::sleep_for(std::chrono::milliseconds(60));
@@ -189,19 +197,20 @@ int main() {
// //
// PROGRESS BAR 5 // PROGRESS BAR 5
// //
indicators::ProgressSpinner p; indicators::ProgressSpinner p{
p.set_prefix_text(""); option::PrefixText{""}, option::PostfixText{"Checking credentials"},
p.set_postfix_text("Checking credentials"); option::ForegroundColor{indicators::Color::yellow},
p.set_foreground_color(indicators::Color::YELLOW); option::SpinnerStates{std::vector<std::string>{"", "", "", "", "", "", "", ""}},
p.set_spinner_states({"", "", "", "", "", "", "", ""}); option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
auto job = [&p]() { auto job = [&p]() {
while (true) { while (true) {
if (p.is_completed()) { if (p.is_completed()) {
p.set_foreground_color(indicators::Color::GREEN); p.set_option(option::ForegroundColor{indicators::Color::green});
p.set_prefix_text(""); p.set_option(option::PrefixText{""});
p.hide_spinner(); p.set_option(option::ShowSpinner{false});
p.hide_percentage(); p.set_option(option::ShowPercentage{false});
p.set_postfix_text("Authenticated!"); p.set_option(option::PostfixText{"Authenticated!"});
p.mark_as_completed(); p.mark_as_completed();
break; break;
} else } else
@@ -218,70 +227,72 @@ int main() {
// //
// PROGRESS BAR 6 // PROGRESS BAR 6
// //
indicators::ProgressSpinner p; indicators::ProgressSpinner p{
p.set_prefix_text(" - "); option::PrefixText{" - "},
p.set_postfix_text("Searching for the Moon"); option::PostfixText{"Searching for the Moon"},
p.set_foreground_color(indicators::Color::WHITE); option::ForegroundColor{indicators::Color::white},
p.set_spinner_states({"", "", "", ""}); option::ShowPercentage{false},
p.hide_percentage(); option::SpinnerStates{std::vector<std::string>{"", "", "", ""}},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
auto job = [&p]() { auto job = [&p]() {
while (true) { while (true) {
auto current = p.current(); auto current = p.current();
if (current == 24) { if (current == 24) {
p.set_prefix_text(" - ✔"); p.set_option(option::PrefixText{" - ✔"});
p.hide_spinner(); p.set_option(option::ShowSpinner{false});
} else if (current == 25) { } else if (current == 25) {
std::cout << std::endl; std::cout << std::endl;
p.show_spinner(); p.set_option(option::ShowSpinner{true});
p.set_prefix_text(" - "); p.set_option(option::PrefixText{" - "});
p.set_postfix_text("Contacting Kerbal headquarters"); p.set_option(option::PostfixText{"Contacting Kerbal headquarters"});
} else if (current == 49) { } else if (current == 49) {
p.set_prefix_text(" - ✔"); p.set_option(option::PrefixText{" - ✔"});
p.hide_spinner(); p.set_option(option::ShowSpinner{false});
} else if (current == 50) { } else if (current == 50) {
std::cout << std::endl; std::cout << std::endl;
p.show_spinner(); p.set_option(option::ShowSpinner{true});
p.set_prefix_text(" - "); p.set_option(option::PrefixText{" - "});
p.set_postfix_text("Designing spaceship"); p.set_option(option::PostfixText{"Designing spaceship"});
} else if (current == 74) { } else if (current == 74) {
p.set_prefix_text(" - ✔"); p.set_option(option::PrefixText{" - ✔"});
p.hide_spinner(); p.set_option(option::ShowSpinner{false});
} else if (current == 75) { } else if (current == 75) {
std::cout << std::endl; std::cout << std::endl;
p.show_spinner(); p.set_option(option::ShowSpinner{true});
p.set_prefix_text(" - "); p.set_option(option::PrefixText{" - "});
p.set_postfix_text("Launching rocket"); p.set_option(option::PostfixText{"Launching rocket"});
} else if (current == 95) { } else if (current == 95) {
p.set_prefix_text(" - ✔"); p.set_option(option::PrefixText{" - ✔"});
p.hide_spinner(); p.set_option(option::ShowSpinner{false});
} else if (current == 99) { } else if (current == 99) {
std::cout << std::endl; std::cout << std::endl;
// //
// NESTED PROGRESS BAR // NESTED PROGRESS BAR
// //
indicators::ProgressBar p2; indicators::ProgressBar p2{
p2.set_bar_width(30); option::BarWidth{30},
p2.set_prefix_text(" - "); option::PrefixText{" - "},
p2.start_bar_with("🌎"); option::Start{"🌎"},
p2.fill_bar_progress_with("·"); option::Fill{"·"},
p2.lead_bar_progress_with("🚀"); option::Lead{"🚀"},
p2.fill_bar_remainder_with(" "); option::Remainder{" "},
p2.end_bar_with("🌑"); option::End{"🌑"},
p2.set_postfix_text("Achieved low-Earth orbit"); option::PostfixText{"Achieved low-Earth orbit"},
p2.set_foreground_color(indicators::Color::WHITE); option::ForegroundColor{indicators::Color::white},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
std::vector<std::string> ship_trail{"", "", "", "", "", "", "", ""}; std::vector<std::string> ship_trail{"", "", "", "", "", "", "", ""};
std::atomic<int> ship_trail_index{0}; std::atomic<int> ship_trail_index{0};
auto job2 = [&p2, &ship_trail_index, &ship_trail]() { auto job2 = [&p2, &ship_trail_index, &ship_trail]() {
while (true) { while (true) {
auto ticks = p2.current(); auto ticks = p2.current();
if (ticks > 20 && ticks < 50) if (ticks > 20 && ticks < 50)
p2.set_postfix_text("Switching to trans-lunar trajectory"); p2.set_option(option::PostfixText{"Switching to trans-lunar trajectory"});
else if (ticks > 50 && ticks < 80) else if (ticks > 50 && ticks < 80)
p2.set_postfix_text("Transferred to Lunar lander"); p2.set_option(option::PostfixText{"Transferred to Lunar lander"});
else if (ticks > 80 && ticks < 98) else if (ticks > 80 && ticks < 98)
p2.set_postfix_text("Almost there"); p2.set_option(option::PostfixText{"Almost there"});
else if (ticks >= 98) else if (ticks >= 98)
p2.set_postfix_text("Landed on the Moon"); p2.set_option(option::PostfixText{"Landed on the Moon"});
p2.tick(); p2.tick();
ship_trail_index += 1; ship_trail_index += 1;
if (p2.is_completed()) if (p2.is_completed())
@@ -291,7 +302,7 @@ int main() {
}; };
std::thread thread2(job2); std::thread thread2(job2);
thread2.join(); thread2.join();
p.set_postfix_text("Mission successful!"); p.set_option(indicators::option::PostfixText{"Mission successful!"});
p.mark_as_completed(); p.mark_as_completed();
break; break;
} }
@@ -306,7 +317,7 @@ int main() {
} }
// Show cursor // Show cursor
std::cout << "\e[?25h"; show_console_cursor(true);
return 0; return 0;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
img/multi_progress.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
img/progress_bar_tick.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
img/progress_spinner.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
img/time_meter.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
img/unicode.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 MiB

View File

@@ -1,183 +1,292 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>. #ifndef INDICATORS_BLOCK_PROGRESS_BAR
SPDX-License-Identifier: MIT #define INDICATORS_BLOCK_PROGRESS_BAR
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy #include <indicators/color.hpp>
of this software and associated documentation files (the "Software"), to deal #include <indicators/details/stream_helper.hpp>
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 <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <indicators/color.hpp> #include <chrono>
#include <indicators/setting.hpp>
#include <indicators/terminal_size.hpp>
#include <iomanip>
#include <iostream> #include <iostream>
#include <sstream>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector> #include <tuple>
#include <cmath> #include <utility>
namespace indicators { namespace indicators {
class BlockProgressBar { class BlockProgressBar {
using Settings = std::tuple<option::ForegroundColor, option::BarWidth, option::Start, option::End,
option::PrefixText, option::PostfixText, option::ShowPercentage,
option::ShowElapsedTime, option::ShowRemainingTime, option::Completed,
option::SavedStartTime, option::MaxPostfixTextLen, option::FontStyles,
option::MaxProgress, option::Stream>;
public: public:
void set_foreground_color(Color color) { template <typename... Args,
std::unique_lock<std::mutex> lock{_mutex}; typename std::enable_if<details::are_settings_from_tuple<
_foreground_color = color; Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit BlockProgressBar(Args &&... args)
: settings_(details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::unspecified}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::bar_width>(option::BarWidth{100},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::start>(option::Start{"["},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::end>(option::End{"]"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(
option::PrefixText{""}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(
option::PostfixText{""}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_percentage>(
option::ShowPercentage{true}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_elapsed_time>(
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_remaining_time>(
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(option::Completed{false},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::saved_start_time>(
option::SavedStartTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::font_styles>(
option::FontStyles{std::vector<FontStyle>{}}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_progress>(
option::MaxProgress{100}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::stream>(option::Stream{std::cout},
std::forward<Args>(args)...)) {}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
} }
void set_bar_width(size_t bar_width) { template <typename T, details::ProgressBarOption id>
std::unique_lock<std::mutex> lock{_mutex}; void set_option(const details::Setting<T, id> &setting) {
_bar_width = bar_width; static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
} }
void start_bar_with(const std::string &start) { void set_option(
std::unique_lock<std::mutex> lock{_mutex}; const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
_start = start; std::lock_guard<std::mutex> lock(mutex_);
} get_value<details::ProgressBarOption::postfix_text>() = setting.value;
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
void end_bar_with(const std::string &end) { get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
std::unique_lock<std::mutex> lock{_mutex};
_end = end;
}
void set_prefix_text(const std::string &text) {
std::unique_lock<std::mutex> lock{_mutex};
_prefix_text = text;
}
void set_postfix_text(const std::string &text) {
std::unique_lock<std::mutex> 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<std::mutex> lock{_mutex};
_progress = value;
} }
_print_progress(); }
void
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
}
}
void set_progress(size_t value) {
{
std::lock_guard<std::mutex> lock{mutex_};
tick_ = value;
}
save_start_time();
print_progress();
} }
void tick() { void tick() {
{ {
std::unique_lock<std::mutex> lock{_mutex}; std::lock_guard<std::mutex> lock{mutex_};
_progress += 1; tick_++;
} }
_print_progress(); save_start_time();
print_progress();
} }
size_t current() { size_t current() {
return std::min(static_cast<size_t>(_progress), size_t(100)); std::lock_guard<std::mutex> lock{mutex_};
return (std::min)(tick_, size_t(get_value<details::ProgressBarOption::max_progress>()));
} }
bool is_completed() const { return _completed; } bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
void mark_as_completed() { void mark_as_completed() {
_completed = true; get_value<details::ProgressBarOption::completed>() = true;
_print_progress(); print_progress();
} }
private: private:
float _progress{0.0}; template <details::ProgressBarOption id>
size_t _bar_width{100}; auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
std::string _prefix_text{""}; return details::get_value<id>(settings_).value;
std::string _start{"["}; }
std::string _fill{""};
std::string _lead{" "};
std::string _remainder{" "};
std::string _end{"]"};
std::string _postfix_text{""};
std::atomic<size_t> _max_postfix_text_length{0};
std::atomic<bool> _completed{false};
std::atomic<bool> _show_percentage{true};
std::mutex _mutex;
Color _foreground_color;
void _print_progress() { template <details::ProgressBarOption id>
std::unique_lock<std::mutex> lock{_mutex}; auto get_value() const
std::cout << termcolor::bold; -> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
switch (_foreground_color) { return details::get_value<id>(settings_).value;
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;
std::vector<std::string> lead_characters{" ", "", "", "", "", "", "", ""}; Settings settings_;
auto progress = std::min(1.0f, std::max(0.0f, _progress / 100.0f)); float progress_{0.0};
auto whole_width = std::floor(progress * _bar_width); size_t tick_{0};
auto remainder_width = fmod((progress * _bar_width), 1.0f); std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
auto part_width = std::floor(remainder_width * lead_characters.size()); std::mutex mutex_;
_lead = lead_characters[part_width];
if ((_bar_width - whole_width - 1) < 0) template <typename Indicator, size_t count> friend class MultiProgress;
_lead = ""; template <typename Indicator> friend class DynamicProgress;
for (size_t i = 0; i < whole_width; ++i) std::atomic<bool> multi_progress_mode_{false};
std::cout << _fill;
std::cout << _lead; void save_start_time() {
for (size_t i = 0; i < (_bar_width - whole_width - 1); ++i) auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
std::cout << " "; auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
std::cout << _end; if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
if (_show_percentage) { start_time_point_ = std::chrono::high_resolution_clock::now();
std::cout << " " << std::min(static_cast<size_t>(_progress), size_t(100)) << "%"; saved_start_time = true;
} }
if (_max_postfix_text_length == 0) }
_max_postfix_text_length = 10;
std::cout << " " << _postfix_text << std::string(_max_postfix_text_length, ' ') << "\r"; std::pair<std::string, int> get_prefix_text() {
std::cout.flush(); std::stringstream os;
if (_progress > 100.0) { os << get_value<details::ProgressBarOption::prefix_text>();
_completed = true; const auto result = os.str();
const auto result_size = unicode::display_width(result);
return {result, result_size};
}
std::pair<std::string, int> get_postfix_text() {
std::stringstream os;
const auto max_progress = get_value<details::ProgressBarOption::max_progress>();
progress_ = static_cast<float>(tick_)/max_progress;
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
if (get_value<details::ProgressBarOption::show_percentage>()) {
os << " " << (std::min)(static_cast<size_t>(progress_ * 100.0), size_t(100))
<< "%";
} }
if (_completed)
std::cout << termcolor::reset << std::endl; auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
os << " [";
if (saved_start_time)
details::write_duration(os, elapsed);
else
os << "00:00s";
}
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
os << "<";
else
os << " [";
if (saved_start_time) {
auto eta = std::chrono::nanoseconds(
tick_ > 0
? static_cast<long long>(std::ceil(float(elapsed.count()) / progress_))
: 0);
auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta);
details::write_duration(os, remaining);
} else {
os << "00:00s";
}
os << "]";
} else {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
os << "]";
}
os << " " << get_value<details::ProgressBarOption::postfix_text>();
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<std::mutex> lock{mutex_};
auto &os = get_value<details::ProgressBarOption::stream>();
const auto max_progress = get_value<details::ProgressBarOption::max_progress>();
if (multi_progress_mode_ && !from_multi_progress) {
if (tick_ > max_progress) {
get_value<details::ProgressBarOption::completed>() = true;
}
return;
}
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
details::set_stream_color(os, get_value<details::ProgressBarOption::foreground_color>());
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
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::ProgressBarOption::start>();
details::BlockProgressScaleWriter writer{os,
get_value<details::ProgressBarOption::bar_width>()};
writer.write(progress_ * 100);
os << get_value<details::ProgressBarOption::end>();
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<details::ProgressBarOption::start>().size();
const auto bar_width = get_value<details::ProgressBarOption::bar_width>();
const auto end_length = get_value<details::ProgressBarOption::end>().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 (tick_ > max_progress) {
get_value<details::ProgressBarOption::completed>() = true;
}
if (get_value<details::ProgressBarOption::completed>() &&
!from_multi_progress) // Don't std::endl if calling from MultiProgress
os << termcolor::reset << std::endl;
} }
}; };
} // namespace indicators } // namespace indicators
#endif

View File

@@ -1,32 +1,9 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indica
Licensed under the MIT License <http://opensource.org/licenses/MIT>. #ifndef INDICATORS_COLOR
SPDX-License-Identifier: MIT #define INDICATORS_COLOR
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
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 <indicators/termcolor.hpp>
namespace indicators { namespace indicators {
enum class Color { GREY, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; enum class Color { grey, red, green, yellow, blue, magenta, cyan, white, unspecified };
} }
#endif

View File

@@ -0,0 +1,66 @@
#ifndef INDICATORS_CURSOR_CONTROL
#define INDICATORS_CURSOR_CONTROL
#if defined(_MSC_VER)
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <io.h>
#include <windows.h>
#else
#include <cstdio>
#endif
namespace indicators {
#if defined(_MSC_VER)
static inline void show_console_cursor(bool const show) {
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(out, &cursorInfo);
cursorInfo.bVisible = show; // set the cursor visibility
SetConsoleCursorInfo(out, &cursorInfo);
}
static inline void erase_line() {
auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (!hStdout)
return;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
COORD cursor;
cursor.X = 0;
cursor.Y = csbiInfo.dwCursorPosition.Y;
DWORD count = 0;
FillConsoleOutputCharacterA(hStdout, ' ', csbiInfo.dwSize.X, cursor, &count);
FillConsoleOutputAttribute(hStdout, csbiInfo.wAttributes, csbiInfo.dwSize.X,
cursor, &count);
SetConsoleCursorPosition(hStdout, cursor);
}
#else
static inline void show_console_cursor(bool const show) {
std::fputs(show ? "\033[?25h" : "\033[?25l", stdout);
}
static inline void erase_line() {
std::fputs("\r\033[K", stdout);
}
#endif
} // namespace indicators
#endif

View File

@@ -0,0 +1,50 @@
#ifndef INDICATORS_CURSOR_MOVEMENT
#define INDICATORS_CURSOR_MOVEMENT
#if defined(_MSC_VER)
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <io.h>
#include <windows.h>
#else
#include <iostream>
#endif
namespace indicators {
#ifdef _MSC_VER
static inline void move(int x, int y) {
auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (!hStdout)
return;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
COORD cursor;
cursor.X = csbiInfo.dwCursorPosition.X + x;
cursor.Y = csbiInfo.dwCursorPosition.Y + y;
SetConsoleCursorPosition(hStdout, cursor);
}
static inline void move_up(int lines) { move(0, -lines); }
static inline void move_down(int lines) { move(0, -lines); }
static inline void move_right(int cols) { move(cols, 0); }
static inline void move_left(int cols) { move(-cols, 0); }
#else
static inline void move_up(int lines) { std::cout << "\033[" << lines << "A"; }
static inline void move_down(int lines) { std::cout << "\033[" << lines << "B"; }
static inline void move_right(int cols) { std::cout << "\033[" << cols << "C"; }
static inline void move_left(int cols) { std::cout << "\033[" << cols << "D"; }
#endif
} // namespace indicators
#endif

View File

@@ -0,0 +1,223 @@
#ifndef INDICATORS_STREAM_HELPER
#define INDICATORS_STREAM_HELPER
#include <indicators/display_width.hpp>
#include <indicators/setting.hpp>
#include <indicators/termcolor.hpp>
#include <algorithm>
#include <chrono>
#include <iomanip>
#include <ostream>
#include <string>
#include <vector>
#include <cassert>
#include <cmath>
namespace indicators {
namespace details {
inline void set_stream_color(std::ostream &os, Color color) {
switch (color) {
case Color::grey:
os << termcolor::grey;
break;
case Color::red:
os << termcolor::red;
break;
case Color::green:
os << termcolor::green;
break;
case Color::yellow:
os << termcolor::yellow;
break;
case Color::blue:
os << termcolor::blue;
break;
case Color::magenta:
os << termcolor::magenta;
break;
case Color::cyan:
os << termcolor::cyan;
break;
case Color::white:
os << termcolor::white;
break;
default:
assert(false);
}
}
inline void set_font_style(std::ostream &os, FontStyle style) {
switch (style) {
case FontStyle::bold:
os << termcolor::bold;
break;
case FontStyle::dark:
os << termcolor::dark;
break;
case FontStyle::italic:
os << termcolor::italic;
break;
case FontStyle::underline:
os << termcolor::underline;
break;
case FontStyle::blink:
os << termcolor::blink;
break;
case FontStyle::reverse:
os << termcolor::reverse;
break;
case FontStyle::concealed:
os << termcolor::concealed;
break;
case FontStyle::crossed:
os << termcolor::crossed;
break;
default:
break;
}
}
inline std::ostream &write_duration(std::ostream &os, std::chrono::nanoseconds ns) {
using namespace std;
using namespace std::chrono;
using days = duration<int, ratio<86400>>;
char fill = os.fill();
os.fill('0');
auto d = duration_cast<days>(ns);
ns -= d;
auto h = duration_cast<hours>(ns);
ns -= h;
auto m = duration_cast<minutes>(ns);
ns -= m;
auto s = duration_cast<seconds>(ns);
if (d.count() > 0)
os << setw(2) << d.count() << "d:";
if (h.count() > 0)
os << setw(2) << h.count() << "h:";
os << setw(2) << m.count() << "m:" << setw(2) << s.count() << 's';
os.fill(fill);
return os;
}
class BlockProgressScaleWriter {
public:
BlockProgressScaleWriter(std::ostream &os, size_t bar_width) : os(os), bar_width(bar_width) {}
std::ostream &write(float progress) {
std::string fill_text{""};
std::vector<std::string> lead_characters{" ", "", "", "", "", "", "", ""};
auto value = (std::min)(1.0f, (std::max)(0.0f, progress / 100.0f));
auto whole_width = std::floor(value * bar_width);
auto remainder_width = fmod((value * bar_width), 1.0f);
auto part_width = std::floor(remainder_width * lead_characters.size());
std::string lead_text = lead_characters[size_t(part_width)];
if ((bar_width - whole_width - 1) < 0)
lead_text = "";
for (size_t i = 0; i < whole_width; ++i)
os << fill_text;
os << lead_text;
for (size_t i = 0; i < (bar_width - whole_width - 1); ++i)
os << " ";
return os;
}
private:
std::ostream &os;
size_t bar_width = 0;
};
class ProgressScaleWriter {
public:
ProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
const std::string &lead, const std::string &remainder)
: os(os), bar_width(bar_width), fill(fill), lead(lead), remainder(remainder) {}
std::ostream &write(float progress) {
auto pos = static_cast<size_t>(progress * bar_width / 100.0);
for (size_t i = 0, current_display_width = 0; i < bar_width;) {
std::string next;
if (i < pos) {
next = fill;
current_display_width = unicode::display_width(fill);
} else if (i == pos) {
next = lead;
current_display_width = unicode::display_width(lead);
} else {
next = remainder;
current_display_width = unicode::display_width(remainder);
}
i += current_display_width;
if (i > bar_width) {
// `next` is larger than the allowed bar width
// fill with empty space instead
os << std::string((bar_width - (i - current_display_width)), ' ');
break;
}
os << next;
}
return os;
}
private:
std::ostream &os;
size_t bar_width = 0;
std::string fill;
std::string lead;
std::string remainder;
};
class IndeterminateProgressScaleWriter {
public:
IndeterminateProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
const std::string &lead)
: os(os), bar_width(bar_width), fill(fill), lead(lead) {}
std::ostream &write(size_t progress) {
for (size_t i = 0; i < bar_width;) {
std::string next;
size_t current_display_width = 0;
if (i < progress) {
next = fill;
current_display_width = unicode::display_width(fill);
} else if (i == progress) {
next = lead;
current_display_width = unicode::display_width(lead);
} else {
next = fill;
current_display_width = unicode::display_width(fill);
}
i += current_display_width;
if (i > bar_width) {
// `next` is larger than the allowed bar width
// fill with empty space instead
os << std::string((bar_width - (i - current_display_width)), ' ');
break;
}
os << next;
}
return os;
}
private:
std::ostream &os;
size_t bar_width = 0;
std::string fill;
std::string lead;
};
} // namespace details
} // namespace indicators
#endif

View File

@@ -0,0 +1,359 @@
#ifndef INDICATORS_DISPLAY_WIDTH
#define INDICATORS_DISPLAY_WIDTH
#include <clocale>
#include <cstdlib>
#include <locale>
#include <string>
#include <wchar.h>
namespace unicode {
namespace details {
/*
* This is an implementation of wcwidth() and wcswidth() (defined in
* IEEE Std 1002.1-2001) for Unicode.
*
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
*
* In fixed-width output devices, Latin characters all occupy a single
* "cell" position of equal width, whereas ideographic CJK characters
* occupy two such cells. Interoperability between terminal-line
* applications and (teletype-style) character terminals using the
* UTF-8 encoding requires agreement on which character should advance
* the cursor by how many cell positions. No established formal
* standards exist at present on which Unicode character shall occupy
* how many cell positions on character terminals. These routines are
* a first attempt of defining such behavior based on simple rules
* applied to data provided by the Unicode Consortium.
*
* For some graphical characters, the Unicode standard explicitly
* defines a character-cell width via the definition of the East Asian
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
* In all these cases, there is no ambiguity about which width a
* terminal shall use. For characters in the East Asian Ambiguous (A)
* class, the width choice depends purely on a preference of backward
* compatibility with either historic CJK or Western practice.
* Choosing single-width for these characters is easy to justify as
* the appropriate long-term solution, as the CJK practice of
* displaying these characters as double-width comes from historic
* implementation simplicity (8-bit encoded characters were displayed
* single-width and 16-bit ones double-width, even for Greek,
* Cyrillic, etc.) and not any typographic considerations.
*
* Much less clear is the choice of width for the Not East Asian
* (Neutral) class. Existing practice does not dictate a width for any
* of these characters. It would nevertheless make sense
* typographically to allocate two character cells to characters such
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
* represented adequately with a single-width glyph. The following
* routines at present merely assign a single-cell width to all
* neutral characters, in the interest of simplicity. This is not
* entirely satisfactory and should be reconsidered before
* establishing a formal standard in this area. At the moment, the
* decision which Not East Asian (Neutral) characters should be
* represented by double-width glyphs cannot yet be answered by
* applying a simple rule from the Unicode database content. Setting
* up a proper standard for the behavior of UTF-8 character terminals
* will require a careful analysis not only of each Unicode character,
* but also of each presentation form, something the author of these
* routines has avoided to do so far.
*
* http://www.unicode.org/unicode/reports/tr11/
*
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted. The author
* disclaims all warranties with regard to this software.
*
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
struct interval {
int first;
int last;
};
/* auxiliary function for binary search in interval table */
static inline int bisearch(wchar_t ucs, const struct interval *table, int max) {
int min = 0;
int mid;
if (ucs < table[0].first || ucs > table[max].last)
return 0;
while (max >= min) {
mid = (min + max) / 2;
if (ucs > table[mid].last)
min = mid + 1;
else if (ucs < table[mid].first)
max = mid - 1;
else
return 1;
}
return 0;
}
/* The following two functions define the column width of an ISO 10646
* character as follows:
*
* - The null character (U+0000) has a column width of 0.
*
* - Other C0/C1 control characters and DEL will lead to a return
* value of -1.
*
* - Non-spacing and enclosing combining characters (general
* category code Mn or Me in the Unicode database) have a
* column width of 0.
*
* - SOFT HYPHEN (U+00AD) has a column width of 1.
*
* - Other format characters (general category code Cf in the Unicode
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
*
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
* have a column width of 0.
*
* - Spacing characters in the East Asian Wide (W) or East Asian
* Full-width (F) category as defined in Unicode Technical
* Report #11 have a column width of 2.
*
* - All remaining characters (including all printable
* ISO 8859-1 and WGL4 characters, Unicode control characters,
* etc.) have a column width of 1.
*
* This implementation assumes that wchar_t characters are encoded
* in ISO 10646.
*/
static inline int mk_wcwidth(wchar_t ucs) {
/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
static const struct interval combining[] = {
{0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489},
{0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2},
{0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603},
{0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670},
{0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED},
{0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A},
{0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902},
{0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D},
{0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981},
{0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD},
{0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C},
{0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
{0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC},
{0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD},
{0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C},
{0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D},
{0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0},
{0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48},
{0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC},
{0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD},
{0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D},
{0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6},
{0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
{0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
{0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
{0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E},
{0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97},
{0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030},
{0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039},
{0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F},
{0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
{0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD},
{0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD},
{0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922},
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B},
{0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34},
{0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42},
{0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF},
{0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063},
{0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F},
{0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B},
{0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F},
{0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB},
{0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F},
{0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169},
{0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
{0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F},
{0xE0100, 0xE01EF}};
/* test for 8-bit control characters */
if (ucs == 0)
return 0;
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
return -1;
/* binary search in table of non-spacing characters */
if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1))
return 0;
/* if we arrive here, ucs is not a combining or C0/C1 control character */
return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
(ucs >= 0xf900 &&
ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
}
static inline int mk_wcswidth(const wchar_t *pwcs, size_t n) {
int w, width = 0;
for (; *pwcs && n-- > 0; pwcs++)
if ((w = mk_wcwidth(*pwcs)) < 0)
return -1;
else
width += w;
return width;
}
/*
* The following functions are the same as mk_wcwidth() and
* mk_wcswidth(), except that spacing characters in the East Asian
* Ambiguous (A) category as defined in Unicode Technical Report #11
* have a column width of 2. This variant might be useful for users of
* CJK legacy encodings who want to migrate to UCS without changing
* the traditional terminal character-width behaviour. It is not
* otherwise recommended for general use.
*/
static inline int mk_wcwidth_cjk(wchar_t ucs) {
/* sorted list of non-overlapping intervals of East Asian Ambiguous
* characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
static const struct interval ambiguous[] = {
{0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8},
{0x00AA, 0x00AA}, {0x00AE, 0x00AE}, {0x00B0, 0x00B4},
{0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6},
{0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
{0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED},
{0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA},
{0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101},
{0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
{0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133},
{0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144},
{0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153},
{0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
{0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4},
{0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA},
{0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261},
{0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
{0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB},
{0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0391, 0x03A1},
{0x03A3, 0x03A9}, {0x03B1, 0x03C1}, {0x03C3, 0x03C9},
{0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451},
{0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019},
{0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027},
{0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035},
{0x203B, 0x203B}, {0x203E, 0x203E}, {0x2074, 0x2074},
{0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC},
{0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109},
{0x2113, 0x2113}, {0x2116, 0x2116}, {0x2121, 0x2122},
{0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154},
{0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179},
{0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2},
{0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200},
{0x2202, 0x2203}, {0x2207, 0x2208}, {0x220B, 0x220B},
{0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215},
{0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223},
{0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E},
{0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248},
{0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261},
{0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F},
{0x2282, 0x2283}, {0x2286, 0x2287}, {0x2295, 0x2295},
{0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF},
{0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B},
{0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595},
{0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3},
{0x25B6, 0x25B7}, {0x25BC, 0x25BD}, {0x25C0, 0x25C1},
{0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1},
{0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606},
{0x2609, 0x2609}, {0x260E, 0x260F}, {0x2614, 0x2615},
{0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640},
{0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665},
{0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F},
{0x273D, 0x273D}, {0x2776, 0x277F}, {0xE000, 0xF8FF},
{0xFFFD, 0xFFFD}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}};
/* binary search in table of non-spacing characters */
if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1))
return 2;
return mk_wcwidth(ucs);
}
static inline int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) {
int w, width = 0;
for (; *pwcs && n-- > 0; pwcs++)
if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
return -1;
else
width += w;
return width;
}
// convert UTF-8 string to wstring
#ifdef _MSC_VER
static inline std::wstring utf8_decode(const std::string& s) {
auto r = setlocale(LC_ALL, NULL);
std::string curLocale;
if (r)
curLocale = r;
const char* _Source = s.c_str();
size_t _Dsize = std::strlen(_Source) + 1;
wchar_t* _Dest = new wchar_t[_Dsize];
size_t _Osize;
mbstowcs_s(&_Osize, _Dest, _Dsize, _Source, _Dsize);
std::wstring result = _Dest;
delete[] _Dest;
setlocale(LC_ALL, curLocale.c_str());
return result;
}
#else
static inline std::wstring utf8_decode(const std::string& s) {
auto r = setlocale(LC_ALL, NULL);
std::string curLocale;
if (r)
curLocale = r;
const char* _Source = s.c_str();
size_t _Dsize = mbstowcs(NULL, _Source, 0) + 1;
wchar_t* _Dest = new wchar_t[_Dsize];
wmemset(_Dest, 0, _Dsize);
mbstowcs(_Dest, _Source, _Dsize);
std::wstring result = _Dest;
delete[] _Dest;
setlocale(LC_ALL, curLocale.c_str());
return result;
}
#endif
} // namespace details
static inline int display_width(const std::string &input) {
using namespace unicode::details;
return mk_wcswidth(utf8_decode(input).c_str(), input.size());
}
static inline int display_width(const std::wstring &input) {
return details::mk_wcswidth(input.c_str(), input.size());
}
} // namespace unicode
#endif

View File

@@ -0,0 +1,123 @@
#ifndef INDICATORS_DYNAMIC_PROGRESS
#define INDICATORS_DYNAMIC_PROGRESS
#include <atomic>
#include <functional>
#include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <indicators/cursor_control.hpp>
#include <indicators/cursor_movement.hpp>
#include <indicators/details/stream_helper.hpp>
#include <iostream>
#include <memory>
#include <mutex>
#include <vector>
namespace indicators {
template <typename Indicator> class DynamicProgress {
using Settings = std::tuple<option::HideBarWhenComplete>;
public:
template <typename... Indicators> explicit DynamicProgress(Indicators &&... bars) {
(bars_.emplace_back(std::move(bars)), ...);
for (auto &bar : bars_) {
bar->multi_progress_mode_ = true;
++total_count_;
++incomplete_count_;
}
}
Indicator &operator[](size_t index) {
print_progress();
std::lock_guard<std::mutex> lock{mutex_};
return *bars_[index];
}
size_t push_back(std::unique_ptr<Indicator> bar) {
std::lock_guard<std::mutex> lock{mutex_};
bar->multi_progress_mode_ = true;
bars_.push_back(std::move(bar));
return bars_.size() - 1;
}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
}
template <typename T, details::ProgressBarOption id>
void set_option(const details::Setting<T, id> &setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
}
private:
Settings settings_;
std::atomic<bool> started_{false};
std::mutex mutex_;
std::vector<std::unique_ptr<Indicator>> bars_;
std::atomic<size_t> total_count_{0};
std::atomic<size_t> incomplete_count_{0};
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
template <details::ProgressBarOption id>
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
public:
void print_progress() {
std::lock_guard<std::mutex> lock{mutex_};
auto &hide_bar_when_complete = get_value<details::ProgressBarOption::hide_bar_when_complete>();
if (hide_bar_when_complete) {
// Hide completed bars
if (started_) {
for (size_t i = 0; i < incomplete_count_; ++i) {
move_up(1);
erase_line();
std::cout << std::flush;
}
}
incomplete_count_ = 0;
for (auto &bar : bars_) {
if (!bar->is_completed()) {
bar->print_progress(true);
std::cout << "\n";
++incomplete_count_;
}
}
if (!started_)
started_ = true;
} else {
// Don't hide any bars
if (started_)
move_up(static_cast<int>(total_count_));
for (auto &bar : bars_) {
bar->print_progress(true);
std::cout << "\n";
}
if (!started_)
started_ = true;
}
total_count_ = bars_.size();
std::cout << termcolor::reset;
}
};
} // namespace indicators
#endif

View File

@@ -0,0 +1,9 @@
#ifndef INDICATORS_FONT_STYLE
#define INDICATORS_FONT_STYLE
namespace indicators {
enum class FontStyle { bold, dark, italic, underline, blink, reverse, concealed, crossed };
}
#endif

View File

@@ -0,0 +1,239 @@
#ifndef INDICATORS_INDETERMINATE_PROGRESS_BAR
#define INDICATORS_INDETERMINATE_PROGRESS_BAR
#include <indicators/details/stream_helper.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <indicators/terminal_size.hpp>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
#include <sstream>
#include <utility>
namespace indicators {
class IndeterminateProgressBar {
using Settings =
std::tuple<option::BarWidth, option::PrefixText, option::PostfixText, option::Start,
option::End, option::Fill, option::Lead, option::MaxPostfixTextLen,
option::Completed, option::ForegroundColor, option::FontStyles, option::Stream>;
enum class Direction { forward, backward };
Direction direction_{Direction::forward};
public:
template <typename... Args,
typename std::enable_if<details::are_settings_from_tuple<
Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit IndeterminateProgressBar(Args &&... args)
: settings_(details::get<details::ProgressBarOption::bar_width>(option::BarWidth{100},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(
option::PrefixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(
option::PostfixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::start>(option::Start{"["},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::end>(option::End{"]"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::fill>(option::Fill{"."},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::lead>(option::Lead{"<==>"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(option::Completed{false},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::unspecified}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::font_styles>(
option::FontStyles{std::vector<FontStyle>{}}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::stream>(option::Stream{std::cout},
std::forward<Args>(args)...)) {
// starts with [<==>...........]
// progress_ = 0
// ends with [...........<==>]
// ^^^^^^^^^^^^^^^^^ bar_width
// ^^^^^^^^^^^^ (bar_width - len(lead))
// progress_ = bar_width - len(lead)
progress_ = 0;
max_progress_ = get_value<details::ProgressBarOption::bar_width>() -
get_value<details::ProgressBarOption::lead>().size() +
get_value<details::ProgressBarOption::start>().size() +
get_value<details::ProgressBarOption::end>().size();
}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
}
template <typename T, details::ProgressBarOption id>
void set_option(const details::Setting<T, id> &setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
}
void set_option(
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
}
}
void
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
}
}
void tick() {
{
std::lock_guard<std::mutex> lock{mutex_};
if (get_value<details::ProgressBarOption::completed>())
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<details::ProgressBarOption::completed>(); }
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
print_progress();
}
private:
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
template <details::ProgressBarOption id>
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
size_t progress_{0};
size_t max_progress_;
Settings settings_;
std::chrono::nanoseconds elapsed_;
std::mutex mutex_;
template <typename Indicator, size_t count> friend class MultiProgress;
template <typename Indicator> friend class DynamicProgress;
std::atomic<bool> multi_progress_mode_{false};
std::pair<std::string, int> get_prefix_text() {
std::stringstream os;
os << get_value<details::ProgressBarOption::prefix_text>();
const auto result = os.str();
const auto result_size = unicode::display_width(result);
return {result, result_size};
}
std::pair<std::string, int> get_postfix_text() {
std::stringstream os;
os << " " << get_value<details::ProgressBarOption::postfix_text>();
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<std::mutex> lock{mutex_};
auto &os = get_value<details::ProgressBarOption::stream>();
if (multi_progress_mode_ && !from_multi_progress) {
return;
}
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
details::set_stream_color(os, get_value<details::ProgressBarOption::foreground_color>());
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
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::ProgressBarOption::start>();
details::IndeterminateProgressScaleWriter writer{
os, get_value<details::ProgressBarOption::bar_width>(),
get_value<details::ProgressBarOption::fill>(),
get_value<details::ProgressBarOption::lead>()};
writer.write(progress_);
os << get_value<details::ProgressBarOption::end>();
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<details::ProgressBarOption::start>().size();
const auto bar_width = get_value<details::ProgressBarOption::bar_width>();
const auto end_length = get_value<details::ProgressBarOption::end>().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<details::ProgressBarOption::completed>() &&
!from_multi_progress) // Don't std::endl if calling from MultiProgress
os << termcolor::reset << std::endl;
}
};
} // namespace indicators
#endif

View File

@@ -0,0 +1,82 @@
#ifndef INDICATORS_MULTI_PROGRESS
#define INDICATORS_MULTI_PROGRESS
#include <atomic>
#include <functional>
#include <iostream>
#include <mutex>
#include <vector>
#include <indicators/color.hpp>
#include <indicators/cursor_movement.hpp>
#include <indicators/details/stream_helper.hpp>
namespace indicators {
template <typename Indicator, size_t count> class MultiProgress {
public:
template <typename... Indicators,
typename = typename std::enable_if<(sizeof...(Indicators) == count)>::type>
explicit MultiProgress(Indicators &... bars) {
bars_ = {bars...};
for (auto &bar : bars_) {
bar.get().multi_progress_mode_ = true;
}
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(size_t value) {
if (!bars_[index].get().is_completed())
bars_[index].get().set_progress(value);
print_progress();
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(float value) {
if (!bars_[index].get().is_completed())
bars_[index].get().set_progress(value);
print_progress();
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), void>::type tick() {
if (!bars_[index].get().is_completed())
bars_[index].get().tick();
print_progress();
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), bool>::type is_completed() const {
return bars_[index].get().is_completed();
}
private:
std::atomic<bool> started_{false};
std::mutex mutex_;
std::vector<std::reference_wrapper<Indicator>> bars_;
bool _all_completed() {
bool result{true};
for (size_t i = 0; i < count; ++i)
result &= bars_[i].get().is_completed();
return result;
}
public:
void print_progress() {
std::lock_guard<std::mutex> lock{mutex_};
if (started_)
move_up(count);
for (auto &bar : bars_) {
bar.get().print_progress(true);
std::cout << "\n";
}
std::cout << termcolor::reset;
if (!started_)
started_ = true;
}
};
} // namespace indicators
#endif

View File

@@ -1,190 +1,362 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>. #ifndef INDICATORS_PROGRESS_BAR
SPDX-License-Identifier: MIT #define INDICATORS_PROGRESS_BAR
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy #include <indicators/details/stream_helper.hpp>
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 <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <chrono>
#include <cmath>
#include <indicators/color.hpp> #include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <indicators/terminal_size.hpp>
#include <iomanip>
#include <iostream> #include <iostream>
#include <sstream>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
namespace indicators { namespace indicators {
class ProgressBar { class ProgressBar {
using Settings =
std::tuple<option::BarWidth, option::PrefixText, option::PostfixText,
option::Start, option::End, option::Fill, option::Lead,
option::Remainder, option::MaxPostfixTextLen,
option::Completed, option::ShowPercentage,
option::ShowElapsedTime, option::ShowRemainingTime,
option::SavedStartTime, option::ForegroundColor,
option::FontStyles, option::MinProgress, option::MaxProgress,
option::ProgressType, option::Stream>;
public: public:
void set_foreground_color(Color color) { template <typename... Args,
std::unique_lock<std::mutex> lock{_mutex}; typename std::enable_if<
_foreground_color = color; details::are_settings_from_tuple<
Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit ProgressBar(Args &&... args)
: settings_(
details::get<details::ProgressBarOption::bar_width>(
option::BarWidth{100}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(
option::PrefixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(
option::PostfixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::start>(
option::Start{"["}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::end>(
option::End{"]"}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::fill>(
option::Fill{"="}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::lead>(
option::Lead{">"}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::remainder>(
option::Remainder{" "}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(
option::Completed{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_percentage>(
option::ShowPercentage{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_elapsed_time>(
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_remaining_time>(
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::saved_start_time>(
option::SavedStartTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::unspecified},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::font_styles>(
option::FontStyles{std::vector<FontStyle>{}},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::min_progress>(
option::MinProgress{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_progress>(
option::MaxProgress{100}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::progress_type>(
option::ProgressType{ProgressType::incremental},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::stream>(
option::Stream{std::cout}, std::forward<Args>(args)...)) {
// if progress is incremental, start from min_progress
// else start from max_progress
const auto type = get_value<details::ProgressBarOption::progress_type>();
if (type == ProgressType::incremental)
progress_ = get_value<details::ProgressBarOption::min_progress>();
else
progress_ = get_value<details::ProgressBarOption::max_progress>();
} }
void set_bar_width(size_t bar_width) { template <typename T, details::ProgressBarOption id>
std::unique_lock<std::mutex> lock{_mutex}; void set_option(details::Setting<T, id> &&setting) {
_bar_width = bar_width; static_assert(
!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
} }
void start_bar_with(const std::string &start) { template <typename T, details::ProgressBarOption id>
std::unique_lock<std::mutex> lock{_mutex}; void set_option(const details::Setting<T, id> &setting) {
_start = start; static_assert(
!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
} }
void fill_bar_progress_with(const std::string &fill) { void
std::unique_lock<std::mutex> lock{_mutex}; set_option(const details::Setting<
_fill = fill; std::string, details::ProgressBarOption::postfix_text> &setting) {
} std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
void lead_bar_progress_with(const std::string &lead) { if (setting.value.length() >
std::unique_lock<std::mutex> lock{_mutex}; get_value<details::ProgressBarOption::max_postfix_text_len>()) {
_lead = lead; get_value<details::ProgressBarOption::max_postfix_text_len>() =
} setting.value.length();
void fill_bar_remainder_with(const std::string &remainder) {
std::unique_lock<std::mutex> lock{_mutex};
_remainder = remainder;
}
void end_bar_with(const std::string &end) {
std::unique_lock<std::mutex> lock{_mutex};
_end = end;
}
void set_prefix_text(const std::string &text) {
std::unique_lock<std::mutex> lock{_mutex};
_prefix_text = text;
}
void set_postfix_text(const std::string &text) {
std::unique_lock<std::mutex> 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<std::mutex> lock{_mutex};
_progress = value;
} }
_print_progress(); }
void set_option(
details::Setting<std::string, details::ProgressBarOption::postfix_text>
&&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() =
std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() >
get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() =
new_value.length();
}
}
void set_progress(size_t new_progress) {
{
std::lock_guard<std::mutex> lock(mutex_);
progress_ = new_progress;
}
save_start_time();
print_progress();
} }
void tick() { void tick() {
{ {
std::unique_lock<std::mutex> lock{_mutex}; std::lock_guard<std::mutex> lock{mutex_};
_progress += 1; const auto type = get_value<details::ProgressBarOption::progress_type>();
if (type == ProgressType::incremental)
progress_ += 1;
else
progress_ -= 1;
} }
_print_progress(); save_start_time();
print_progress();
} }
size_t current() { size_t current() {
return std::min(static_cast<size_t>(_progress), size_t(100)); std::lock_guard<std::mutex> lock{mutex_};
return (std::min)(
progress_,
size_t(get_value<details::ProgressBarOption::max_progress>()));
} }
bool is_completed() const { return _completed; } bool is_completed() const {
return get_value<details::ProgressBarOption::completed>();
}
void mark_as_completed() { void mark_as_completed() {
_completed = true; get_value<details::ProgressBarOption::completed>() = true;
_print_progress(); print_progress();
} }
private: private:
float _progress{0.0}; template <details::ProgressBarOption id>
size_t _bar_width{100}; auto get_value()
std::string _prefix_text{""}; -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
std::string _start{"["}; return details::get_value<id>(settings_).value;
std::string _fill{"="}; }
std::string _lead{">"};
std::string _remainder{" "};
std::string _end{"]"};
std::string _postfix_text{""};
std::atomic<size_t> _max_postfix_text_length{0};
std::atomic<bool> _completed{false};
std::atomic<bool> _show_percentage{true};
std::mutex _mutex;
Color _foreground_color;
void _print_progress() { template <details::ProgressBarOption id>
std::unique_lock<std::mutex> lock{_mutex}; auto get_value() const -> decltype(
std::cout << termcolor::bold; (details::get_value<id>(std::declval<const Settings &>()).value)) {
switch (_foreground_color) { return details::get_value<id>(settings_).value;
case Color::GREY: }
std::cout << termcolor::grey;
break; size_t progress_{0};
case Color::RED: Settings settings_;
std::cout << termcolor::red; std::chrono::nanoseconds elapsed_;
break; std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
case Color::GREEN: std::mutex mutex_;
std::cout << termcolor::green;
break; template <typename Indicator, size_t count> friend class MultiProgress;
case Color::YELLOW: template <typename Indicator> friend class DynamicProgress;
std::cout << termcolor::yellow; std::atomic<bool> multi_progress_mode_{false};
break;
case Color::BLUE: void save_start_time() {
std::cout << termcolor::blue; auto &show_elapsed_time =
break; get_value<details::ProgressBarOption::show_elapsed_time>();
case Color::MAGENTA: auto &saved_start_time =
std::cout << termcolor::magenta; get_value<details::ProgressBarOption::saved_start_time>();
break; auto &show_remaining_time =
case Color::CYAN: get_value<details::ProgressBarOption::show_remaining_time>();
std::cout << termcolor::cyan; if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
break; start_time_point_ = std::chrono::high_resolution_clock::now();
case Color::WHITE: saved_start_time = true;
std::cout << termcolor::white;
break;
} }
std::cout << _prefix_text; }
std::cout << _start;
auto pos = static_cast<size_t>(_progress * static_cast<float>(_bar_width) / 100.0); std::pair<std::string, int> get_prefix_text() {
for (size_t i = 0; i < _bar_width; ++i) { std::stringstream os;
if (i < pos) os << get_value<details::ProgressBarOption::prefix_text>();
std::cout << _fill; const auto result = os.str();
else if (i == pos) const auto result_size = unicode::display_width(result);
std::cout << _lead; return {result, result_size};
}
std::pair<std::string, int> get_postfix_text() {
std::stringstream os;
const auto max_progress =
get_value<details::ProgressBarOption::max_progress>();
if (get_value<details::ProgressBarOption::show_percentage>()) {
os << " "
<< (std::min)(static_cast<size_t>(static_cast<float>(progress_) /
max_progress * 100),
size_t(100))
<< "%";
}
auto &saved_start_time =
get_value<details::ProgressBarOption::saved_start_time>();
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
os << " [";
if (saved_start_time)
details::write_duration(os, elapsed_);
else else
std::cout << _remainder; os << "00:00s";
} }
std::cout << _end;
if (_show_percentage) { if (get_value<details::ProgressBarOption::show_remaining_time>()) {
std::cout << " " << std::min(static_cast<size_t>(_progress), size_t(100)) << "%"; if (get_value<details::ProgressBarOption::show_elapsed_time>())
os << "<";
else
os << " [";
if (saved_start_time) {
auto eta = std::chrono::nanoseconds(
progress_ > 0
? static_cast<long long>(std::ceil(float(elapsed_.count()) *
max_progress / progress_))
: 0);
auto remaining = eta > elapsed_ ? (eta - elapsed_) : (elapsed_ - eta);
details::write_duration(os, remaining);
} else {
os << "00:00s";
}
os << "]";
} else {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
os << "]";
} }
if (_max_postfix_text_length == 0)
_max_postfix_text_length = 10; os << " " << get_value<details::ProgressBarOption::postfix_text>();
std::cout << " " << _postfix_text << std::string(_max_postfix_text_length, ' ') << "\r";
std::cout.flush(); const auto result = os.str();
if (_progress > 100.0) { const auto result_size = unicode::display_width(result);
_completed = true; return {result, result_size};
}
public:
void print_progress(bool from_multi_progress = false) {
std::lock_guard<std::mutex> lock{mutex_};
auto &os = get_value<details::ProgressBarOption::stream>();
const auto type = get_value<details::ProgressBarOption::progress_type>();
const auto min_progress =
get_value<details::ProgressBarOption::min_progress>();
const auto max_progress =
get_value<details::ProgressBarOption::max_progress>();
if (multi_progress_mode_ && !from_multi_progress) {
if ((type == ProgressType::incremental && progress_ >= max_progress) ||
(type == ProgressType::decremental && progress_ <= min_progress)) {
get_value<details::ProgressBarOption::completed>() = true;
}
return;
} }
if (_completed) auto now = std::chrono::high_resolution_clock::now();
std::cout << termcolor::reset << std::endl; if (!get_value<details::ProgressBarOption::completed>())
elapsed_ = std::chrono::duration_cast<std::chrono::nanoseconds>(
now - start_time_point_);
if (get_value<details::ProgressBarOption::foreground_color>() !=
Color::unspecified)
details::set_stream_color(
os, get_value<details::ProgressBarOption::foreground_color>());
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
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::ProgressBarOption::start>();
details::ProgressScaleWriter writer{
os, get_value<details::ProgressBarOption::bar_width>(),
get_value<details::ProgressBarOption::fill>(),
get_value<details::ProgressBarOption::lead>(),
get_value<details::ProgressBarOption::remainder>()};
writer.write(double(progress_) / double(max_progress) * 100.0f);
os << get_value<details::ProgressBarOption::end>();
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<details::ProgressBarOption::start>().size();
const auto bar_width = get_value<details::ProgressBarOption::bar_width>();
const auto end_length = get_value<details::ProgressBarOption::end>().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 ((type == ProgressType::incremental && progress_ >= max_progress) ||
(type == ProgressType::decremental && progress_ <= min_progress)) {
get_value<details::ProgressBarOption::completed>() = true;
}
if (get_value<details::ProgressBarOption::completed>() &&
!from_multi_progress) // Don't std::endl if calling from MultiProgress
os << termcolor::reset << std::endl;
} }
}; };
} // namespace indicators } // namespace indicators
#endif

View File

@@ -1,159 +1,227 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>. #ifndef INDICATORS_PROGRESS_SPINNER
SPDX-License-Identifier: MIT #define INDICATORS_PROGRESS_SPINNER
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy #include <indicators/details/stream_helper.hpp>
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 <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <chrono>
#include <cmath>
#include <indicators/color.hpp> #include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <iomanip>
#include <iostream> #include <iostream>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <tuple>
#include <vector> #include <vector>
namespace indicators { namespace indicators {
class ProgressSpinner { class ProgressSpinner {
using Settings =
std::tuple<option::ForegroundColor, option::PrefixText, option::PostfixText,
option::ShowPercentage, option::ShowElapsedTime, option::ShowRemainingTime,
option::ShowSpinner, option::SavedStartTime, option::Completed,
option::MaxPostfixTextLen, option::SpinnerStates, option::FontStyles,
option::MaxProgress, option::Stream>;
public: public:
void set_foreground_color(Color color) { template <typename... Args,
std::unique_lock<std::mutex> lock{_mutex}; typename std::enable_if<details::are_settings_from_tuple<
_foreground_color = color; Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit ProgressSpinner(Args &&... args)
: settings_(
details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::unspecified}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(option::PrefixText{},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(option::PostfixText{},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_percentage>(option::ShowPercentage{true},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_elapsed_time>(
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_remaining_time>(
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::spinner_show>(option::ShowSpinner{true},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::saved_start_time>(
option::SavedStartTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(option::Completed{false},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::spinner_states>(
option::SpinnerStates{
std::vector<std::string>{"", "", "", "", "", "", "", "", "", ""}},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::font_styles>(
option::FontStyles{std::vector<FontStyle>{}}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_progress>(option::MaxProgress{100},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::stream>(option::Stream{std::cout},
std::forward<Args>(args)...)) {}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
} }
void set_prefix_text(const std::string &text) { template <typename T, details::ProgressBarOption id>
std::unique_lock<std::mutex> lock{_mutex}; void set_option(const details::Setting<T, id> &setting) {
_prefix_text = text; static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
} }
void set_postfix_text(const std::string &text) { void set_option(
std::unique_lock<std::mutex> lock{_mutex}; const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
_postfix_text = text; std::lock_guard<std::mutex> lock(mutex_);
if (_postfix_text.length() > _max_postfix_text_length) get_value<details::ProgressBarOption::postfix_text>() = setting.value;
_max_postfix_text_length = _postfix_text.length(); if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
} get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
void show_percentage() { _show_percentage = true; }
void hide_percentage() { _show_percentage = false; }
void show_spinner() { _show_spinner = true; }
void hide_spinner() { _show_spinner = false; }
void set_progress(float value) {
{
std::unique_lock<std::mutex> lock{_mutex};
_progress = value;
} }
_print_progress(); }
void
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
}
}
void set_progress(size_t value) {
{
std::lock_guard<std::mutex> lock{mutex_};
progress_ = value;
}
save_start_time();
print_progress();
} }
void tick() { void tick() {
{ {
std::unique_lock<std::mutex> lock{_mutex}; std::lock_guard<std::mutex> lock{mutex_};
_progress += 1; progress_ += 1;
} }
_print_progress(); save_start_time();
print_progress();
} }
size_t current() { size_t current() {
return std::min(static_cast<size_t>(_progress), size_t(100)); std::lock_guard<std::mutex> lock{mutex_};
return (std::min)(progress_, size_t(get_value<details::ProgressBarOption::max_progress>()));
} }
bool is_completed() const { return _completed; } bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
void mark_as_completed() { void mark_as_completed() {
_completed = true; get_value<details::ProgressBarOption::completed>() = true;
_print_progress(); print_progress();
}
void set_spinner_states(const std::vector<std::string> &states) {
std::unique_lock<std::mutex> lock{_mutex};
_states = states;
} }
private: private:
float _progress{0.0}; Settings settings_;
std::string _prefix_text{""}; size_t progress_{0};
size_t _index{0}; size_t index_{0};
std::vector<std::string> _states{"", "", "", "", "", "", "", "", "", ""}; std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
std::string _postfix_text{""}; std::mutex mutex_;
std::atomic<size_t> _max_postfix_text_length{0};
std::atomic<bool> _completed{false};
std::atomic<bool> _show_percentage{true};
std::atomic<bool> _show_spinner{true};
std::mutex _mutex;
Color _foreground_color;
void _print_progress() { template <details::ProgressBarOption id>
std::unique_lock<std::mutex> lock{_mutex}; auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
std::cout << termcolor::bold; return details::get_value<id>(settings_).value;
switch (_foreground_color) { }
case Color::GREY:
std::cout << termcolor::grey; template <details::ProgressBarOption id>
break; auto get_value() const
case Color::RED: -> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
std::cout << termcolor::red; return details::get_value<id>(settings_).value;
break; }
case Color::GREEN:
std::cout << termcolor::green; void save_start_time() {
break; auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
case Color::YELLOW: auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
std::cout << termcolor::yellow; auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
break; if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
case Color::BLUE: start_time_point_ = std::chrono::high_resolution_clock::now();
std::cout << termcolor::blue; saved_start_time = true;
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; }
if (_show_spinner)
std::cout << _states[_index % _states.size()]; public:
if (_show_percentage) { void print_progress() {
std::cout << " " << std::min(static_cast<size_t>(_progress), size_t(100)) << "%"; std::lock_guard<std::mutex> lock{mutex_};
auto &os = get_value<details::ProgressBarOption::stream>();
const auto max_progress = get_value<details::ProgressBarOption::max_progress>();
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
details::set_stream_color(os, get_value<details::ProgressBarOption::foreground_color>());
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
details::set_font_style(os, style);
os << get_value<details::ProgressBarOption::prefix_text>();
if (get_value<details::ProgressBarOption::spinner_show>())
os << get_value<details::ProgressBarOption::spinner_states>()
[index_ % get_value<details::ProgressBarOption::spinner_states>().size()];
if (get_value<details::ProgressBarOption::show_percentage>()) {
os << " " << std::size_t(progress_ / double(max_progress) * 100) << "%";
} }
if (_max_postfix_text_length == 0)
_max_postfix_text_length = 10; if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
std::cout << " " << _postfix_text << std::string(_max_postfix_text_length, ' ') << "\r"; os << " [";
std::cout.flush(); details::write_duration(os, elapsed);
_index += 1;
if (_progress > 100.0) {
_completed = true;
} }
if (_completed)
std::cout << termcolor::reset << std::endl; if (get_value<details::ProgressBarOption::show_remaining_time>()) {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
os << "<";
else
os << " [";
auto eta = std::chrono::nanoseconds(
progress_ > 0
? static_cast<long long>(std::ceil(float(elapsed.count()) *
max_progress / progress_))
: 0);
auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta);
details::write_duration(os, remaining);
os << "]";
} else {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
os << "]";
}
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
os << " " << get_value<details::ProgressBarOption::postfix_text>()
<< std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ') << "\r";
os.flush();
index_ += 1;
if (progress_ > max_progress) {
get_value<details::ProgressBarOption::completed>() = true;
}
if (get_value<details::ProgressBarOption::completed>())
os << termcolor::reset << std::endl;
} }
}; };
} // namespace indicators } // namespace indicators
#endif

View File

@@ -0,0 +1,9 @@
#ifndef INDICATORS_PROGRESS_TYPE
#define INDICATORS_PROGRESS_TYPE
namespace indicators {
enum class ProgressType { incremental, decremental };
}
#endif

View File

@@ -0,0 +1,222 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Dawid Pilarski <dawid.pilarski@panicsoftware.com>.
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.
*/
#ifndef INDICATORS_SETTING
#define INDICATORS_SETTING
#include <cstddef>
#include <indicators/color.hpp>
#include <indicators/font_style.hpp>
#include <indicators/progress_type.hpp>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace indicators {
namespace details {
template <bool condition> struct if_else;
template <> struct if_else<true> { using type = std::true_type; };
template <> struct if_else<false> { using type = std::false_type; };
template <bool condition, typename True, typename False> struct if_else_type;
template <typename True, typename False> struct if_else_type<true, True, False> {
using type = True;
};
template <typename True, typename False> struct if_else_type<false, True, False> {
using type = False;
};
template <typename... Ops> struct conjuction;
template <> struct conjuction<> : std::true_type {};
template <typename Op, typename... TailOps>
struct conjuction<Op, TailOps...>
: if_else_type<!Op::value, std::false_type, conjuction<TailOps...>>::type {};
template <typename... Ops> struct disjunction;
template <> struct disjunction<> : std::false_type {};
template <typename Op, typename... TailOps>
struct disjunction<Op, TailOps...>
: if_else_type<Op::value, std::true_type, disjunction<TailOps...>>::type {};
enum class ProgressBarOption {
bar_width = 0,
prefix_text,
postfix_text,
start,
end,
fill,
lead,
remainder,
max_postfix_text_len,
completed,
show_percentage,
show_elapsed_time,
show_remaining_time,
saved_start_time,
foreground_color,
spinner_show,
spinner_states,
font_styles,
hide_bar_when_complete,
min_progress,
max_progress,
progress_type,
stream
};
template <typename T, ProgressBarOption Id> struct Setting {
template <typename... Args,
typename = typename std::enable_if<std::is_constructible<T, Args...>::value>::type>
explicit Setting(Args &&... args) : value(std::forward<Args>(args)...) {}
Setting(const Setting &) = default;
Setting(Setting &&) = default;
static constexpr auto id = Id;
using type = T;
T value{};
};
template <typename T> struct is_setting : std::false_type {};
template <ProgressBarOption Id, typename T> struct is_setting<Setting<T, Id>> : std::true_type {};
template <typename... Args>
struct are_settings : if_else<conjuction<is_setting<Args>...>::value>::type {};
template <> struct are_settings<> : std::true_type {};
template <typename Setting, typename Tuple> struct is_setting_from_tuple;
template <typename Setting> struct is_setting_from_tuple<Setting, std::tuple<>> : std::true_type {};
template <typename Setting, typename... TupleTypes>
struct is_setting_from_tuple<Setting, std::tuple<TupleTypes...>>
: if_else<disjunction<std::is_same<Setting, TupleTypes>...>::value>::type {};
template <typename Tuple, typename... Settings>
struct are_settings_from_tuple
: if_else<conjuction<is_setting_from_tuple<Settings, Tuple>...>::value>::type {};
template <ProgressBarOption Id> struct always_true { static constexpr auto value = true; };
template <ProgressBarOption Id, typename Default> Default &&get_impl(Default &&def) {
return std::forward<Default>(def);
}
template <ProgressBarOption Id, typename Default, typename T, typename... Args>
auto get_impl(Default && /*def*/, T &&first, Args &&... /*tail*/) ->
typename std::enable_if<(std::decay<T>::type::id == Id),
decltype(std::forward<T>(first))>::type {
return std::forward<T>(first);
}
template <ProgressBarOption Id, typename Default, typename T, typename... Args>
auto get_impl(Default &&def, T && /*first*/, Args &&... tail) ->
typename std::enable_if<(std::decay<T>::type::id != Id),
decltype(get_impl<Id>(std::forward<Default>(def),
std::forward<Args>(tail)...))>::type {
return get_impl<Id>(std::forward<Default>(def), std::forward<Args>(tail)...);
}
template <ProgressBarOption Id, typename Default, typename... Args,
typename = typename std::enable_if<are_settings<Args...>::value, void>::type>
auto get(Default &&def, Args &&... args)
-> decltype(details::get_impl<Id>(std::forward<Default>(def), std::forward<Args>(args)...)) {
return details::get_impl<Id>(std::forward<Default>(def), std::forward<Args>(args)...);
}
template <ProgressBarOption Id> using StringSetting = Setting<std::string, Id>;
template <ProgressBarOption Id> using IntegerSetting = Setting<std::size_t, Id>;
template <ProgressBarOption Id> using BooleanSetting = Setting<bool, Id>;
template <ProgressBarOption Id, typename Tuple, std::size_t counter = 0> struct option_idx;
template <ProgressBarOption Id, typename T, typename... Settings, std::size_t counter>
struct option_idx<Id, std::tuple<T, Settings...>, counter>
: if_else_type<(Id == T::id), std::integral_constant<std::size_t, counter>,
option_idx<Id, std::tuple<Settings...>, counter + 1>>::type {};
template <ProgressBarOption Id, std::size_t counter> struct option_idx<Id, std::tuple<>, counter> {
static_assert(always_true<(ProgressBarOption)Id>::value, "No such option was found");
};
template <ProgressBarOption Id, typename Settings>
auto get_value(Settings &&settings)
-> decltype((std::get<option_idx<Id, typename std::decay<Settings>::type>::value>(
std::declval<Settings &&>()))) {
return std::get<option_idx<Id, typename std::decay<Settings>::type>::value>(
std::forward<Settings>(settings));
}
} // namespace details
namespace option {
using BarWidth = details::IntegerSetting<details::ProgressBarOption::bar_width>;
using PrefixText = details::StringSetting<details::ProgressBarOption::prefix_text>;
using PostfixText = details::StringSetting<details::ProgressBarOption::postfix_text>;
using Start = details::StringSetting<details::ProgressBarOption::start>;
using End = details::StringSetting<details::ProgressBarOption::end>;
using Fill = details::StringSetting<details::ProgressBarOption::fill>;
using Lead = details::StringSetting<details::ProgressBarOption::lead>;
using Remainder = details::StringSetting<details::ProgressBarOption::remainder>;
using MaxPostfixTextLen = details::IntegerSetting<details::ProgressBarOption::max_postfix_text_len>;
using Completed = details::BooleanSetting<details::ProgressBarOption::completed>;
using ShowPercentage = details::BooleanSetting<details::ProgressBarOption::show_percentage>;
using ShowElapsedTime = details::BooleanSetting<details::ProgressBarOption::show_elapsed_time>;
using ShowRemainingTime = details::BooleanSetting<details::ProgressBarOption::show_remaining_time>;
using SavedStartTime = details::BooleanSetting<details::ProgressBarOption::saved_start_time>;
using ForegroundColor = details::Setting<Color, details::ProgressBarOption::foreground_color>;
using ShowSpinner = details::BooleanSetting<details::ProgressBarOption::spinner_show>;
using SpinnerStates =
details::Setting<std::vector<std::string>, details::ProgressBarOption::spinner_states>;
using HideBarWhenComplete =
details::BooleanSetting<details::ProgressBarOption::hide_bar_when_complete>;
using FontStyles =
details::Setting<std::vector<FontStyle>, details::ProgressBarOption::font_styles>;
using MinProgress = details::IntegerSetting<details::ProgressBarOption::min_progress>;
using MaxProgress = details::IntegerSetting<details::ProgressBarOption::max_progress>;
using ProgressType = details::Setting<ProgressType, details::ProgressBarOption::progress_type>;
using Stream = details::Setting<std::ostream &, details::ProgressBarOption::stream>;
} // namespace option
} // namespace indicators
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
#ifndef INDICATORS_TERMINAL_SIZE
#define INDICATORS_TERMINAL_SIZE
#include <utility>
#if defined(_WIN32)
#include <windows.h>
namespace indicators {
static inline std::pair<size_t, size_t> terminal_size() {
CONSOLE_SCREEN_BUFFER_INFO csbi;
int cols, rows;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
return {static_cast<size_t>(rows), static_cast<size_t>(cols)};
}
static inline size_t terminal_width() { return terminal_size().second; }
} // namespace indicators
#else
#include <sys/ioctl.h> //ioctl() and TIOCGWINSZ
#include <unistd.h> // for STDOUT_FILENO
namespace indicators {
static inline std::pair<size_t, size_t> terminal_size() {
struct winsize size{};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &size);
return {static_cast<size_t>(size.ws_row), static_cast<size_t>(size.ws_col)};
}
static inline size_t terminal_width() { return terminal_size().second; }
} // namespace indicators
#endif
#endif

View File

@@ -1,9 +0,0 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(Threads)
if (NOT TARGET indica::indica)
include(${CMAKE_CURRENT_LIST_DIR}/indicaTargets.cmake)
endif ()

7
indicators.pc.in Normal file
View File

@@ -0,0 +1,7 @@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
URL: @PROJECT_HOMEPAGE_URL@
Version: @PROJECT_VERSION@
Cflags: -I${includedir}

View File

@@ -0,0 +1,9 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(Threads REQUIRED)
if (NOT TARGET indicators::indicators)
include(${CMAKE_CURRENT_LIST_DIR}/indicatorsTargets.cmake)
endif ()

49
samples/CMakeLists.txt Normal file
View File

@@ -0,0 +1,49 @@
add_executable(block_progress_bar block_progress_bar.cpp)
target_link_libraries(block_progress_bar PRIVATE indicators::indicators)
add_executable(block_progress_bar_iterable block_progress_bar_iterable.cpp)
target_link_libraries(block_progress_bar_iterable PRIVATE indicators::indicators)
add_executable(multi_threaded_bar multi_threaded_bar.cpp)
target_link_libraries(multi_threaded_bar PRIVATE indicators::indicators)
add_executable(progress_bar_set_progress progress_bar_set_progress.cpp)
target_link_libraries(progress_bar_set_progress PRIVATE indicators::indicators)
add_executable(progress_bar_tick progress_bar_tick.cpp)
target_link_libraries(progress_bar_tick PRIVATE indicators::indicators)
add_executable(progress_bar_countdown progress_bar_countdown.cpp)
target_link_libraries(progress_bar_countdown PRIVATE indicators::indicators)
add_executable(progress_bar_sstream progress_bar_sstream.cpp)
target_link_libraries(progress_bar_sstream PRIVATE indicators::indicators)
add_executable(progress_bar_unicode progress_bar_unicode.cpp)
target_link_libraries(progress_bar_unicode PRIVATE indicators::indicators)
add_executable(progress_spinner progress_spinner.cpp)
target_link_libraries(progress_spinner PRIVATE indicators::indicators)
add_executable(time_meter time_meter.cpp)
target_link_libraries(time_meter PRIVATE indicators::indicators)
add_executable(multi_progress_bar multi_progress_bar.cpp)
target_link_libraries(multi_progress_bar PRIVATE indicators::indicators)
add_executable(multi_block_progress_bar multi_block_progress_bar.cpp)
target_link_libraries(multi_block_progress_bar PRIVATE indicators::indicators)
add_executable(dynamic_progress dynamic_progress.cpp)
target_link_libraries(dynamic_progress PRIVATE indicators::indicators)
add_executable(max_progress max_progress.cpp)
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)

View File

@@ -1,32 +1,30 @@
#include <indicators/block_progress_bar.hpp>
#include <thread>
#include <chrono> #include <chrono>
#include <indicators/block_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <thread>
int main() { int main() {
// Hide cursor // Hide cursor
std::cout << "\e[?25l"; indicators::show_console_cursor(false);
indicators::BlockProgressBar bar; indicators::BlockProgressBar bar{
indicators::option::BarWidth{80},
// Configure the bar indicators::option::FontStyles{
bar.set_bar_width(80); std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
bar.start_bar_with("[");
bar.end_bar_with("]");
bar.set_foreground_color(indicators::Color::WHITE);
// Update bar state // Update bar state
auto progress = 0.0f; size_t progress = 0;
while (true) { while (true) {
bar.set_progress(progress); bar.set_progress(progress);
progress += 0.25f; progress++;
if (bar.is_completed()) if (bar.is_completed())
break; break;
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
} }
// Show cursor // Show cursor
std::cout << "\e[?25h"; indicators::show_console_cursor(true);
return 0; return 0;
} }

View File

@@ -0,0 +1,43 @@
#include <chrono>
#include <indicators/block_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <thread>
int main() {
// Hide cursor
indicators::show_console_cursor(false);
// Random list of numbers
std::vector<size_t> numbers;
for (size_t i = 0; i < 1259438; ++i) {
numbers.push_back(i);
}
using namespace indicators;
BlockProgressBar bar{option::BarWidth{80}, option::ForegroundColor{Color::white},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}},
option::MaxProgress{numbers.size()}};
std::cout << "Iterating over a list of numbers (size = " << numbers.size() << ")\n";
std::vector<size_t> result;
for (size_t i = 0; i < numbers.size(); ++i) {
// Perform some computation
result.push_back(numbers[i] * numbers[i]);
// Show iteration as postfix text
bar.set_option(option::PostfixText{std::to_string(i) + "/" + std::to_string(numbers.size())});
// update progress bar
bar.tick();
}
bar.mark_as_completed();
// Show cursor
indicators::show_console_cursor(true);
return 0;
}

View File

@@ -0,0 +1,46 @@
#include <indicators/progress_bar.hpp>
#include <indicators/multi_progress.hpp>
#include <indicators/terminal_size.hpp>
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<ProgressBar, 2> 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));
}

View File

@@ -0,0 +1,158 @@
#include <indicators/dynamic_progress.hpp>
#include <indicators/progress_bar.hpp>
using namespace indicators;
int main() {
auto bar1 = std::make_unique<ProgressBar>(option::BarWidth{50},
option::ForegroundColor{Color::red},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"5c90d4a2d1a8: Downloading "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto bar2 = std::make_unique<ProgressBar>(option::BarWidth{50},
option::ForegroundColor{Color::yellow},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"22337bfd13a9: Downloading "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto bar3 = std::make_unique<ProgressBar>(option::BarWidth{50},
option::ForegroundColor{Color::green},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"10f26c680a34: Downloading "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto bar4 = std::make_unique<ProgressBar>(option::BarWidth{50},
option::ForegroundColor{Color::white},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"6364e0d7a283: Downloading "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto bar5 = std::make_unique<ProgressBar>(option::BarWidth{50},
option::ForegroundColor{Color::blue},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"ff1356ba118b: Downloading "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto bar6 = std::make_unique<ProgressBar>(option::BarWidth{50},
option::ForegroundColor{Color::cyan},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"5a17453338b4: Downloading "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
std::cout << termcolor::bold << termcolor::white << "Pulling image foo:bar/baz\n";
// Construct with 3 progress bars. We'll add 3 more at a later point
DynamicProgress<ProgressBar> bars(bar1, bar2, bar3);
// Do not hide bars when completed
bars.set_option(option::HideBarWhenComplete{false});
std::thread fourth_job, fifth_job, sixth_job;
auto job4 = [&bars](size_t i) {
while (true) {
bars[i].tick();
if (bars[i].is_completed()) {
bars[i].set_option(option::PrefixText{"6364e0d7a283: Pull complete "});
bars[i].mark_as_completed();
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
};
auto job5 = [&bars](size_t i) {
while (true) {
bars[i].tick();
if (bars[i].is_completed()) {
bars[i].set_option(option::PrefixText{"ff1356ba118b: Pull complete "});
bars[i].mark_as_completed();
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
};
auto job6 = [&bars](size_t i) {
while (true) {
bars[i].tick();
if (bars[i].is_completed()) {
bars[i].set_option(option::PrefixText{"5a17453338b4: Pull complete "});
bars[i].mark_as_completed();
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(40));
}
};
auto job1 = [&bars, &bar6, &sixth_job, &job6]() {
while (true) {
bars[0].tick();
if (bars[0].is_completed()) {
bars[0].set_option(option::PrefixText{"5c90d4a2d1a8: Pull complete "});
// bar1 is completed, adding bar6
auto i = bars.push_back(std::move(bar6));
sixth_job = std::thread(job6, i);
sixth_job.join();
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(140));
}
};
auto job2 = [&bars, &bar5, &fifth_job, &job5]() {
while (true) {
bars[1].tick();
if (bars[1].is_completed()) {
bars[1].set_option(option::PrefixText{"22337bfd13a9: Pull complete "});
// bar2 is completed, adding bar5
auto i = bars.push_back(std::move(bar5));
fifth_job = std::thread(job5, i);
fifth_job.join();
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(25));
}
};
auto job3 = [&bars, &bar4, &fourth_job, &job4]() {
while (true) {
bars[2].tick();
if (bars[2].is_completed()) {
bars[2].set_option(option::PrefixText{"10f26c680a34: Pull complete "});
// bar3 is completed, adding bar4
auto i = bars.push_back(std::move(bar4));
fourth_job = std::thread(job4, i);
fourth_job.join();
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
};
std::thread first_job(job1);
std::thread second_job(job2);
std::thread third_job(job3);
third_job.join();
second_job.join();
first_job.join();
std::cout << termcolor::bold << termcolor::green << "✔ Downloaded image foo/bar:baz" << std::endl;
std::cout << termcolor::reset;
return 0;
}

View File

@@ -0,0 +1,38 @@
#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/indeterminate_progress_bar.hpp>
#include <thread>
int main() {
indicators::IndeterminateProgressBar bar{
indicators::option::BarWidth{40},
indicators::option::Start{"["},
indicators::option::Fill{"·"},
indicators::option::Lead{"<==>"},
indicators::option::End{"]"},
indicators::option::PostfixText{"Checking for Updates"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
indicators::show_console_cursor(false);
auto job = [&bar]() {
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
bar.mark_as_completed();
std::cout << termcolor::bold << termcolor::green << "System is up to date!\n"
<< termcolor::reset;
};
std::thread job_completion_thread(job);
// Update bar state
while (!bar.is_completed()) {
bar.tick();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
job_completion_thread.join();
indicators::show_console_cursor(true);
return 0;
}

29
samples/max_progress.cpp Normal file
View File

@@ -0,0 +1,29 @@
#include <chrono>
#include <indicators/block_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <thread>
int main() {
// Hide cursor
indicators::show_console_cursor(false);
indicators::BlockProgressBar bar{
indicators::option::BarWidth{80},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}},
indicators::option::MaxProgress{400}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// Show cursor
indicators::show_console_cursor(true);
return 0;
}

View File

@@ -0,0 +1,70 @@
#include <indicators/block_progress_bar.hpp>
#include <indicators/multi_progress.hpp>
int main() {
using namespace indicators;
BlockProgressBar bar1{option::BarWidth{50},
option::ForegroundColor{Color::yellow},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"Progress Bar #1 "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
BlockProgressBar bar2{option::BarWidth{50},
option::ForegroundColor{Color::cyan},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"Progress Bar #2 "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
BlockProgressBar bar3{option::BarWidth{50},
option::ForegroundColor{Color::red},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"Progress Bar #3 "},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
indicators::MultiProgress<indicators::BlockProgressBar, 3> bars(bar1, bar2, bar3);
std::cout << "Multiple Progress Bars:\n";
auto job1 = [&bars]() {
while (true) {
bars.tick<0>();
if (bars.is_completed<0>())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
};
auto job2 = [&bars]() {
while (true) {
bars.tick<1>();
if (bars.is_completed<1>())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
};
auto job3 = [&bars]() {
while (true) {
bars.tick<2>();
if (bars.is_completed<2>())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(60));
}
};
std::thread first_job(job1);
std::thread second_job(job2);
std::thread third_job(job3);
first_job.join();
second_job.join();
third_job.join();
return 0;
}

View File

@@ -0,0 +1,85 @@
#include <indicators/multi_progress.hpp>
#include <indicators/progress_bar.hpp>
int main() {
indicators::ProgressBar bar1{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{""},
indicators::option::Lead{""},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
indicators::option::PrefixText{"Progress Bar #1 "},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
indicators::ProgressBar bar2{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"="},
indicators::option::Lead{">"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::ForegroundColor{indicators::Color::cyan},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
indicators::option::PrefixText{"Progress Bar #2 "},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
indicators::ProgressBar bar3{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"#"},
indicators::option::Lead{"#"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::ForegroundColor{indicators::Color::red},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
indicators::option::PrefixText{"Progress Bar #3 "},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
indicators::MultiProgress<indicators::ProgressBar, 3> bars(bar1, bar2, bar3);
std::cout << "Multiple Progress Bars:\n";
auto job1 = [&bars]() {
while (true) {
bars.tick<0>();
if (bars.is_completed<0>())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
};
auto job2 = [&bars]() {
while (true) {
bars.tick<1>();
if (bars.is_completed<1>())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
};
auto job3 = [&bars]() {
while (true) {
bars.tick<2>();
if (bars.is_completed<2>())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(60));
}
};
std::thread first_job(job1);
std::thread second_job(job2);
std::thread third_job(job3);
first_job.join();
second_job.join();
third_job.join();
return 0;
}

View File

@@ -0,0 +1,61 @@
#include <indicators/progress_bar.hpp>
#include <vector>
int main() {
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{""},
indicators::option::Lead{""},
indicators::option::Remainder{"-"},
indicators::option::End{" ]"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
// As configured, the bar will look like this:
//
// [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-------------] 70%
//
//
std::atomic<size_t> index{0};
std::vector<std::string> status_text = {"Rocket.exe is not responding",
"Finding a replacement engineer",
"Buying more snacks",
"Assimilating the modding community",
"Crossing fingers",
"Porting KSP to a Nokia 3310"};
// Let's say you want to append some status text to the right of the progress bar
// You can use bar.set_postfix_text(...) to append text to the right
//
// [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■-------------] 70% Finding a replacement engineer
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
//
auto job = [&bar, &index, &status_text]() {
while (true) {
if (bar.is_completed()) {
break;
}
bar.set_option(indicators::option::PostfixText{status_text[index % status_text.size()]});
bar.tick();
index += 1;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
};
std::thread first_job(job);
std::thread second_job(job);
std::thread third_job(job);
std::thread last_job(job);
first_job.join();
second_job.join();
third_job.join();
last_job.join();
return 0;
}

View File

@@ -0,0 +1,31 @@
#include <chrono>
#include <indicators/progress_bar.hpp>
#include <thread>
using namespace indicators;
int main() {
ProgressBar bar{option::BarWidth{50},
option::ProgressType{ProgressType::decremental},
option::Start{"["},
option::Fill{""},
option::Lead{""},
option::Remainder{"-"},
option::End{"]"},
option::PostfixText{"Reverting System Restore"},
option::ForegroundColor{Color::yellow},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << termcolor::bold << termcolor::white << "Task Failed Successfully\n"
<< termcolor::reset;
return 0;
}

View File

@@ -0,0 +1,50 @@
#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>
int main() {
// Hide cursor
indicators::show_console_cursor(false);
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{""},
indicators::option::Lead{""},
indicators::option::Remainder{"-"},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Loading dependency 1/4"},
indicators::option::ForegroundColor{indicators::Color::cyan},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
// Update bar state
bar.set_progress(10); // 10% done
// do some work
std::this_thread::sleep_for(std::chrono::milliseconds(800));
bar.set_option(indicators::option::PostfixText{"Loading dependency 2/4"});
bar.set_progress(30); // 30% done
// do some more work
std::this_thread::sleep_for(std::chrono::milliseconds(700));
bar.set_option(indicators::option::PostfixText{"Loading dependency 3/4"});
bar.set_progress(65); // 65% done
// do final bit of work
std::this_thread::sleep_for(std::chrono::milliseconds(900));
bar.set_option(indicators::option::PostfixText{"Loaded dependencies!"});
bar.set_progress(100); // all done
// Show cursor
indicators::show_console_cursor(true);
return 0;
}

View File

@@ -0,0 +1,35 @@
#include <chrono>
#include <indicators/progress_bar.hpp>
#include <sstream>
#include <thread>
int main() {
using namespace indicators;
std::stringstream os;
ProgressBar bar{
option::BarWidth{50},
option::Start{"["},
option::Fill{"="},
option::Lead{">"},
option::Remainder{" "},
option::End{"]"},
option::PostfixText{"Getting started"},
option::ForegroundColor{indicators::Color::green},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}},
option::Stream{os}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "Stream contents:\n";
std::cout << os.str() << "\n";
return 0;
}

View File

@@ -0,0 +1,26 @@
#include <chrono>
#include <indicators/progress_bar.hpp>
#include <thread>
int main() {
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"="},
indicators::option::Lead{">"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Getting started"},
indicators::option::ForegroundColor{indicators::Color::green},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}

View File

@@ -0,0 +1,171 @@
#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/indeterminate_progress_bar.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>
int main() {
indicators::show_console_cursor(false);
{
// Plain old ASCII
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"="},
indicators::option::Lead{">"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Plain-old ASCII"},
indicators::option::ForegroundColor{indicators::Color::green},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
{
// Unicode
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"驚くばかり"},
indicators::option::Lead{">"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Japanese"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
{
// Russian
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"Потрясающие"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Russian"},
indicators::option::ForegroundColor{indicators::Color::red},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
{
// Greek
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"Φοβερός"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Greek"},
indicators::option::ForegroundColor{indicators::Color::cyan},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
{
// Chinese
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"太棒了"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Chinese"},
indicators::option::ForegroundColor{indicators::Color::green},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
{
// Emojis
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{"🔥"},
indicators::option::Lead{"🔥"},
indicators::option::Remainder{" "},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Emojis"},
indicators::option::ForegroundColor{indicators::Color::white},
indicators::option::FontStyles{std::vector<indicators::FontStyle>{
indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
{
// Indeterminate progress bar
indicators::IndeterminateProgressBar bar{
indicators::option::BarWidth{50},
indicators::option::Start{"["},
indicators::option::Fill{""},
indicators::option::Lead{"載入中"},
indicators::option::End{" ]"},
indicators::option::PostfixText{"Loading Progress Bar"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
auto job = [&bar]() {
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
bar.mark_as_completed();
};
std::thread job_completion_thread(job);
// Update bar state
while (!bar.is_completed()) {
bar.tick();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
job_completion_thread.join();
}
indicators::show_console_cursor(true);
return 0;
}

View File

@@ -0,0 +1,40 @@
#include <indicators/cursor_control.hpp>
#include <indicators/progress_spinner.hpp>
int main() {
// Hide cursor
indicators::show_console_cursor(false);
indicators::ProgressSpinner spinner{
indicators::option::PostfixText{"Checking credentials"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::SpinnerStates{
std::vector<std::string>{"", "", "", "", "", "", "", ""}},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
// Update spinner state
auto job = [&spinner]() {
while (true) {
if (spinner.is_completed()) {
spinner.set_option(indicators::option::ForegroundColor{indicators::Color::green});
spinner.set_option(indicators::option::PrefixText{""});
spinner.set_option(indicators::option::ShowSpinner{false});
spinner.set_option(indicators::option::ShowPercentage{false});
spinner.set_option(indicators::option::PostfixText{"Authenticated!"});
spinner.mark_as_completed();
break;
} else
spinner.tick();
std::this_thread::sleep_for(std::chrono::milliseconds(40));
}
};
std::thread thread(job);
thread.join();
// Show cursor
indicators::show_console_cursor(true);
return 0;
}

35
samples/time_meter.cpp Normal file
View File

@@ -0,0 +1,35 @@
#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>
int main() {
// Hide cursor
indicators::show_console_cursor(false);
indicators::ProgressBar bar{indicators::option::BarWidth{50},
indicators::option::Start{" ["},
indicators::option::Fill{""},
indicators::option::Lead{""},
indicators::option::Remainder{"-"},
indicators::option::End{"]"},
indicators::option::PrefixText{"Training Gaze Network 👀"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
// Update bar state
while (true) {
bar.tick();
if (bar.is_completed())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
// Show cursor
indicators::show_console_cursor(true);
return 0;
}

22
single_include.json Normal file
View File

@@ -0,0 +1,22 @@
{
"project": "Activity Indicators for Modern C++",
"target": "single_include/indicators/indicators.hpp",
"sources": [
"include/indicators/color.hpp",
"include/indicators/font_style.hpp",
"include/indicators/progress_type.hpp",
"include/indicators/termcolor.hpp",
"include/indicators/terminal_size.hpp",
"include/indicators/setting.hpp",
"include/indicators/cursor_control.hpp",
"include/indicators/cursor_movement.hpp",
"include/indicators/details/stream_helper.hpp",
"include/indicators/progress_bar.hpp",
"include/indicators/block_progress_bar.hpp",
"include/indicators/indeterminate_progress_bar.hpp",
"include/indicators/multi_progress.hpp",
"include/indicators/dynamic_progress.hpp",
"include/indicators/progress_spinner.hpp"
],
"include_paths": ["include"]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
The following changes have been made to the code with respect to <https://github.com/edlund/amalgamate/commit/c91f07eea1133aa184f652b8f1398eaf03586208>:
- Resolved inspection results from PyCharm:
- replaced tabs with spaces
- added encoding annotation
- reindented file to remove trailing whitespaces
- unused import `sys`
- membership check
- made function from `_is_within`
- removed unused variable `actual_path`

View File

@@ -0,0 +1,27 @@
amalgamate.py - Amalgamate C source and header files
Copyright (c) 2012, Erik Edlund <erik.edlund@32767.se>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Erik Edlund, nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,66 @@
# amalgamate.py - Amalgamate C source and header files
Origin: https://bitbucket.org/erikedlund/amalgamate
Mirror: https://github.com/edlund/amalgamate
`amalgamate.py` aims to make it easy to use SQLite-style C source and header
amalgamation in projects.
For more information, please refer to: http://sqlite.org/amalgamation.html
## Here be dragons
`amalgamate.py` is quite dumb, it only knows the bare minimum about C code
required in order to be able to handle trivial include directives. It can
produce weird results for unexpected code.
Things to be aware of:
`amalgamate.py` will not handle complex include directives correctly:
#define HEADER_PATH "path/to/header.h"
#include HEADER_PATH
In the above example, `path/to/header.h` will not be included in the
amalgamation (HEADER_PATH is never expanded).
`amalgamate.py` makes the assumption that each source and header file which
is not empty will end in a new-line character, which is not immediately
preceded by a backslash character (see 5.1.1.2p1.2 of ISO C99).
`amalgamate.py` should be usable with C++ code, but raw string literals from
C++11 will definitely cause problems:
R"delimiter(Terrible raw \ data " #include <sneaky.hpp>)delimiter"
R"delimiter(Terrible raw \ data " escaping)delimiter"
In the examples above, `amalgamate.py` will stop parsing the raw string literal
when it encounters the first quotation mark, which will produce unexpected
results.
## Installing amalgamate.py
Python v.2.7.0 or higher is required.
`amalgamate.py` can be tested and installed using the following commands:
./test.sh && sudo -k cp ./amalgamate.py /usr/local/bin/
## Using amalgamate.py
amalgamate.py [-v] -c path/to/config.json -s path/to/source/dir \
[-p path/to/prologue.(c|h)]
* The `-c, --config` option should specify the path to a JSON config file which
lists the source files, include paths and where to write the resulting
amalgamation. Have a look at `test/source.c.json` and `test/include.h.json`
to see two examples.
* The `-s, --source` option should specify the path to the source directory.
This is useful for supporting separate source and build directories.
* The `-p, --prologue` option should specify the path to a file which will be
added to the beginning of the amalgamation. It is optional.

View File

@@ -0,0 +1,299 @@
#!/usr/bin/env python
# coding=utf-8
# amalgamate.py - Amalgamate C source and header files.
# Copyright (c) 2012, Erik Edlund <erik.edlund@32767.se>
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of Erik Edlund, nor the names of its contributors may
# be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import argparse
import datetime
import json
import os
import re
class Amalgamation(object):
# Prepends self.source_path to file_path if needed.
def actual_path(self, file_path):
if not os.path.isabs(file_path):
file_path = os.path.join(self.source_path, file_path)
return file_path
# Search included file_path in self.include_paths and
# in source_dir if specified.
def find_included_file(self, file_path, source_dir):
search_dirs = self.include_paths[:]
if source_dir:
search_dirs.insert(0, source_dir)
for search_dir in search_dirs:
search_path = os.path.join(search_dir, file_path)
if os.path.isfile(self.actual_path(search_path)):
return search_path
return None
def __init__(self, args):
with open(args.config, 'r') as f:
config = json.loads(f.read())
for key in config:
setattr(self, key, config[key])
self.verbose = args.verbose == "yes"
self.prologue = args.prologue
self.source_path = args.source_path
self.included_files = []
# Generate the amalgamation and write it to the target file.
def generate(self):
amalgamation = ""
if self.prologue:
with open(self.prologue, 'r') as f:
amalgamation += datetime.datetime.now().strftime(f.read())
if self.verbose:
print("Config:")
print(" target = {0}".format(self.target))
print(" working_dir = {0}".format(os.getcwd()))
print(" include_paths = {0}".format(self.include_paths))
print("Creating amalgamation:")
for file_path in self.sources:
# Do not check the include paths while processing the source
# list, all given source paths must be correct.
# actual_path = self.actual_path(file_path)
print(" - processing \"{0}\"".format(file_path))
t = TranslationUnit(file_path, self, True)
amalgamation += t.content + "\n\n"
with open(self.target, 'w', encoding="utf8") as f:
f.write(amalgamation)
print("...done!\n")
if self.verbose:
print("Files processed: {0}".format(self.sources))
print("Files included: {0}".format(self.included_files))
print("")
def _is_within(match, matches):
for m in matches:
if match.start() > m.start() and \
match.end() < m.end():
return True
return False
class TranslationUnit(object):
# // C++ comment.
cpp_comment_pattern = re.compile(r"//.*?\n")
# /* C comment. */
c_comment_pattern = re.compile(r"/\*.*?\*/", re.S)
# "complex \"stri\\\ng\" value".
string_pattern = re.compile("[^']" r'".*?(?<=[^\\])"', re.S)
# Handle simple include directives. Support for advanced
# directives where macros and defines needs to expanded is
# not a concern right now.
include_pattern = re.compile(
r'#\s*include\s+(<|")(?P<path>.*?)("|>)', re.S)
# #pragma once
pragma_once_pattern = re.compile(r'#\s*pragma\s+once', re.S)
# Search for pattern in self.content, add the match to
# contexts if found and update the index accordingly.
def _search_content(self, index, pattern, contexts):
match = pattern.search(self.content, index)
if match:
contexts.append(match)
return match.end()
return index + 2
# Return all the skippable contexts, i.e., comments and strings
def _find_skippable_contexts(self):
# Find contexts in the content in which a found include
# directive should not be processed.
skippable_contexts = []
# Walk through the content char by char, and try to grab
# skippable contexts using regular expressions when found.
i = 1
content_len = len(self.content)
while i < content_len:
j = i - 1
current = self.content[i]
previous = self.content[j]
if current == '"':
# String value.
i = self._search_content(j, self.string_pattern,
skippable_contexts)
elif current == '*' and previous == '/':
# C style comment.
i = self._search_content(j, self.c_comment_pattern,
skippable_contexts)
elif current == '/' and previous == '/':
# C++ style comment.
i = self._search_content(j, self.cpp_comment_pattern,
skippable_contexts)
else:
# Skip to the next char.
i += 1
return skippable_contexts
# Returns True if the match is within list of other matches
# Removes pragma once from content
def _process_pragma_once(self):
content_len = len(self.content)
if content_len < len("#include <x>"):
return 0
# Find contexts in the content in which a found include
# directive should not be processed.
skippable_contexts = self._find_skippable_contexts()
pragmas = []
pragma_once_match = self.pragma_once_pattern.search(self.content)
while pragma_once_match:
if not _is_within(pragma_once_match, skippable_contexts):
pragmas.append(pragma_once_match)
pragma_once_match = self.pragma_once_pattern.search(self.content,
pragma_once_match.end())
# Handle all collected pragma once directives.
prev_end = 0
tmp_content = ''
for pragma_match in pragmas:
tmp_content += self.content[prev_end:pragma_match.start()]
prev_end = pragma_match.end()
tmp_content += self.content[prev_end:]
self.content = tmp_content
# Include all trivial #include directives into self.content.
def _process_includes(self):
content_len = len(self.content)
if content_len < len("#include <x>"):
return 0
# Find contexts in the content in which a found include
# directive should not be processed.
skippable_contexts = self._find_skippable_contexts()
# Search for include directives in the content, collect those
# which should be included into the content.
includes = []
include_match = self.include_pattern.search(self.content)
while include_match:
if not _is_within(include_match, skippable_contexts):
include_path = include_match.group("path")
search_same_dir = include_match.group(1) == '"'
found_included_path = self.amalgamation.find_included_file(
include_path, self.file_dir if search_same_dir else None)
if found_included_path:
includes.append((include_match, found_included_path))
include_match = self.include_pattern.search(self.content,
include_match.end())
# Handle all collected include directives.
prev_end = 0
tmp_content = ''
for include in includes:
include_match, found_included_path = include
tmp_content += self.content[prev_end:include_match.start()]
tmp_content += "// {0}".format(include_match.group(0))
if found_included_path not in self.amalgamation.included_files:
t = TranslationUnit(found_included_path, self.amalgamation, False)
tmp_content += t.content
prev_end = include_match.end()
tmp_content += self.content[prev_end:]
self.content = tmp_content
return len(includes)
# Make all content processing
def _process(self):
if not self.is_root:
self._process_pragma_once()
self._process_includes()
def __init__(self, file_path, amalgamation, is_root):
self.file_path = file_path
self.file_dir = os.path.dirname(file_path)
self.amalgamation = amalgamation
self.is_root = is_root
self.amalgamation.included_files.append(self.file_path)
actual_path = self.amalgamation.actual_path(file_path)
if not os.path.isfile(actual_path):
raise IOError("File not found: \"{0}\"".format(file_path))
with open(actual_path, 'r', encoding="utf8") as f:
self.content = f.read()
self._process()
def main():
description = "Amalgamate C source and header files."
usage = " ".join([
"amalgamate.py",
"[-v]",
"-c path/to/config.json",
"-s path/to/source/dir",
"[-p path/to/prologue.(c|h)]"
])
argsparser = argparse.ArgumentParser(
description=description, usage=usage)
argsparser.add_argument("-v", "--verbose", dest="verbose",
choices=["yes", "no"], metavar="", help="be verbose")
argsparser.add_argument("-c", "--config", dest="config",
required=True, metavar="", help="path to a JSON config file")
argsparser.add_argument("-s", "--source", dest="source_path",
required=True, metavar="", help="source code path")
argsparser.add_argument("-p", "--prologue", dest="prologue",
required=False, metavar="", help="path to a C prologue file")
amalgamation = Amalgamation(argsparser.parse_args())
amalgamation.generate()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,8 @@
{
"project": "cgame competitive programming for codingame",
"target": "AllTrees.cpp",
"sources": [
"test/gametheory/TreesTest.cpp"
],
"include_paths": ["include"]
}