167 Commits
v1.6 ... v2.3

Author SHA1 Message Date
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
55 changed files with 7885 additions and 1305 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 .

View File

@@ -61,5 +61,5 @@ matrix:
script:
- if [[ "$CXX" == clang* ]]; then export CXXFLAGS="-stdlib=libc++"; fi
- mkdir build && cd build
- cmake -DDEMO=ON -DSAMPLES=ON ..
- cmake -DINDICATORS_DEMO=ON -DINDICATORS_SAMPLES=ON ..
- make

View File

@@ -1,8 +1,28 @@
cmake_minimum_required(VERSION 3.8)
project(indicators VERSION 1.6.0 LANGUAGES CXX DESCRIPTION "Activity Indicators for Modern C++")
option(INDICATORS_BUILD_TESTS OFF)
option(SAMPLES "Build Samples" OFF)
option(DEMO "Build Demo" OFF)
if(DEFINED PROJECT_NAME)
set(INDICATORS_SUBPROJECT ON)
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_BUILD_TESTS "Build indicators tests + enable CTest")
option(INDICATORS_SAMPLES "Build indicators samples")
option(INDICATORS_DEMO "Build indicators demo" OFF)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
@@ -18,33 +38,68 @@ target_include_directories(indicators INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>)
target_link_libraries(indicators INTERFACE Threads::Threads)
if( DEMO )
add_subdirectory(demo)
if(INDICATORS_DEMO)
add_subdirectory(demo)
endif()
if( SAMPLES )
add_subdirectory(samples)
if(INDICATORS_SAMPLES)
add_subdirectory(samples)
endif()
configure_package_config_file(indicatorsConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/indicatorsConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indicators)
if(NOT INDICATORS_SUBPROJECT)
configure_package_config_file(indicatorsConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/indicatorsConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indicators)
write_basic_package_version_file(indicatorsConfigVersion.cmake
COMPATIBILITY AnyNewerVersion)
configure_file(indicators.pc.in
"${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/indicators.pc"
@ONLY)
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
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/indicators)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/indicators.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/)
install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/indicators
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
USE_SOURCE_PERMISSIONS
PATTERN "*.hpp")
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)
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()

660
README.md
View File

@@ -12,30 +12,41 @@
<a href="https://github.com/p-ranav/indicators/blob/master/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="license"/>
</a>
<img src="https://img.shields.io/badge/version-1.6-blue.svg?cacheSeconds=2592000" alt="version"/>
<img src="https://img.shields.io/badge/version-2.3-blue.svg?cacheSeconds=2592000" alt="version"/>
</p>
<p align="center">
<img src="img/demo.gif"/>
</p>
# Highlights
## Highlights
* Thread-safe progress bars and spinners
* Header-only library. Grab a copy of `include/indicators`
* Header-only library. Grab a copy of `include/indicators`.
* Single-header version in `single_include/indicators`.
* Source for the above GIF can be found [here](demo/demo.cpp)
* MIT License
# Table of Contents
## Table of Contents
* [Progress Bar](#progress-bar)
* [Block Progress Bar](#block-progress-bar)
* [Multi Progress](#multiprogress)
* [Progress Spinner](#progress-spinner)
* [Contributing](#contributing)
* [License](#license)
* Supported Indicators
* [Basic Progress Bar](#basic-progress-bar)
* [Indeterminate Progress Bar](#indeterminate-progress-bar)
* [Block Progress Bar](#block-progress-bar)
* [Multi Progress](#multiprogress)
* [Dynamic Progress](#dynamicprogress)
* [Progress Spinner](#progress-spinner)
* Additional Samples
* [Decremental Progress](#decremental-progress)
* [Working with Iterables](#working-with-iterables)
* [Unicode Support](#unicode-support)
* [Building Samples](#building-samples)
* [Generating Single Header](#generating-single-header)
* [Contributing](#contributing)
* [License](#license)
# Progress bar
## Basic Progress bar
To introduce a progress bar in your application, include `indicators/progress_bar.hpp` and create a `ProgressBar` object. Here's the general structure of a progress bar:
@@ -44,11 +55,11 @@ To introduce a progress bar in your application, include `indicators/progress_ba
^^^^^^^^^^^^^ Bar Width ^^^^^^^^^^^^^^^
```
The amount of progress in ProgressBar is maintained as a float in range `[0, 100]`. When progress reaches 100, the progression is complete.
The amount of progress in ProgressBar is maintained as a `size_t` in range `[0, 100]`. When progress reaches 100, the progression is complete.
From application-level code, there are two ways in which you can update this progress:
## Update progress using `bar.tick()`
### Update progress using `bar.tick()`
You can update the progress bar using `bar.tick()` which increments progress by exactly `1%`.
@@ -70,8 +81,9 @@ int main() {
option::Lead{">"},
option::Remainder{" "},
option::End{"]"},
option::PostfixText{"Extracting Archive"};
option::ForegroundColor{Color::GREEN};
option::PostfixText{"Extracting Archive"},
option::ForegroundColor{Color::green},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Update bar state
@@ -88,7 +100,7 @@ int main() {
The above code will print a progress bar that goes from 0 to 100% at the rate of 1% every 100 ms.
## Updating progress using `bar.set_progress(value)`
### Updating progress using `bar.set_progress(value)`
If you'd rather control progress of the bar in discrete steps, consider using `bar.set_progress(value)`. Example:
@@ -99,14 +111,16 @@ If you'd rather control progress of the bar in discrete steps, consider using `b
```cpp
#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>
int main() {
using namespace indicators;
// Hide cursor
std::cout << "\e[?25l";
using namespace indicators;
show_console_cursor(false);
ProgressBar bar{
option::BarWidth{50},
option::Start{"["},
@@ -115,7 +129,8 @@ int main() {
option::Remainder{"-"},
option::End{" ]"},
option::PostfixText{"Loading dependency 1/4"},
option::ForegroundColor{Color::CYAN}
option::ForegroundColor{Color::cyan},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Update bar state
@@ -142,16 +157,14 @@ int main() {
bar.set_progress(100); // all done
bar.mark_as_completed();
// Show cursor
std::cout << "\e[?25h";
show_console_cursor(true);
return 0;
}
```
## Showing Time Elapsed/Remaining
### Showing Time Elapsed/Remaining
All progress bars and spinners in `indicators` support showing time elapsed and time remaining. Inspired by python's [tqdm](https://github.com/tqdm/tqdm) module, the format of this meter is `[{elapsed}<{remaining}]`:
@@ -161,11 +174,16 @@ All progress bars and spinners in `indicators` support showing time elapsed and
```cpp
#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>
int main() {
using namespace indicators;
// Hide cursor
show_console_cursor(false);
indicators::ProgressBar bar{
option::BarWidth{50},
option::Start{" ["},
@@ -173,10 +191,11 @@ int main() {
option::Lead{""},
option::Remainder{"-"},
option::End{"]"},
option::PrefixText{"Training Gaze Network "},
option::ForegroundColor{Color::YELLOW}
option::ShowElapsedTime{true};
option::ShowRemainingTime{true};
option::PrefixText{"Training Gaze Network 👀"},
option::ForegroundColor{Color::yellow},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Update bar state
@@ -188,13 +207,68 @@ int main() {
}
// Show cursor
std::cout << "\e[?25h";
show_console_cursor(true);
return 0;
}
```
# Block Progress Bar
## Indeterminate Progress Bar
You might have a use-case for a progress bar where the maximum amount of progress is unknown, e.g., you're downloading from a remote server that isn't advertising the total bytes.
Use an `indicators::IndeterminateProgressBar` for such cases. An `IndeterminateProgressBar` is similar to a regular progress bar except the total amount to progress towards is unknown. Ticking on this progress bar will happily run forever.
When you know progress is complete, simply call `bar.mark_as_completed()`.
<p align="center">
<img src="img/indeterminate_progress_bar.gif"/>
</p>
```cpp
#include <chrono>
#include <indicators/indeterminate_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <indicators/termcolor.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;
}
```
## Block Progress Bar
Are you in need of a smooth block progress bar using [unicode block elements](https://en.wikipedia.org/wiki/Block_Elements)? Use `BlockProgressBar` instead of `ProgressBar`. Thanks to [this blog post](https://mike42.me/blog/2018-06-make-better-cli-progress-bars-with-unicode-block-characters) for making `BlockProgressBar` an easy addition to the library.
@@ -209,15 +283,17 @@ Are you in need of a smooth block progress bar using [unicode block elements](ht
int main() {
// Hide cursor
std::cout << "\e[?25l";
using namespace indicators;
// Hide cursor
show_console_cursor(false);
BlockProgressBar bar{
option::BarWidth{80},
option::Start{"["},
option::End{"]"},
option::ForegroundColor{Color::WHITE}
option::ForegroundColor{Color::white} ,
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Update bar state
@@ -231,18 +307,20 @@ int main() {
}
// Show cursor
std::cout << "\e[?25h";
show_console_cursor(true);
return 0;
}
```
# MultiProgress
## MultiProgress
`indicators` supports management of multiple progress bars with the `MultiProgress` class template.
`template <typename Indicator, size_t count> class MultiProgress` is a class template that holds references to multiple progress bars and provides a safe interface to update the state of each bar. `MultiProgress` works with both `ProgressBar` and `BlockProgressBar` classes.
Use this class if you know the number of progress bars to manage at compile time.
Below is an example `MultiProgress` object that manages three `ProgressBar` objects.
<p align="center">
@@ -263,25 +341,27 @@ int main() {
option::Lead{""},
option::Remainder{" "},
option::End{" ]"},
option::ForegroundColor{Color::YELLOW},
option::ForegroundColor{Color::yellow},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"Progress Bar #1 "}
option::PrefixText{"Progress Bar #1 "},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Configure second progress bar
ProgressBar bar1{
ProgressBar bar2{
option::BarWidth{50},
option::Start{"["},
option::Fill{"="},
option::Lead{">"},
option::Remainder{" "},
option::End{" ]"},
option::ForegroundColor{Color::CYAN},
option::ForegroundColor{Color::cyan},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"Progress Bar #2 "}
option::PrefixText{"Progress Bar #2 "},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Configure third progress bar
@@ -292,10 +372,11 @@ int main() {
option::Lead{"#"},
option::Remainder{" "},
option::End{" ]"},
option::ForegroundColor{Color::RED},
option::ForegroundColor{Color::red},
option::ShowElapsedTime{true},
option::ShowRemainingTime{true},
option::PrefixText{"Progress Bar #3 "}
option::PrefixText{"Progress Bar #3 "},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Construct MultiProgress object
@@ -342,7 +423,164 @@ int main() {
}
```
# Progress Spinner
## DynamicProgress
`DynamicProgress` is a container class, similar to `MultiProgress`, for managing multiple progress bars. As the name suggests, with `DynamicProgress`, you can dynamically add new progress bars.
To add new progress bars, call `bars.push_back(new_bar)`. This call will return the index of the appended bar. You can then refer to this bar with the indexing operator, e.g., `bars[4].set_progress(55)`.
Use this class if you don't know the number of progress bars at compile time.
Below is an example `DynamicProgress` object that manages six `ProgressBar` objects. Three of these bars are added dynamically.
<p align="center">
<img src="img/dynamic_progress_bar.gif"/>
</p>
```cpp
#include <indicators/dynamic_progress.hpp>
#include <indicators/progress_bar.hpp>
using namespace indicators;
int main() {
ProgressBar bar1{option::BarWidth{50}, option::ForegroundColor{Color::red},
option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
option::PrefixText{"5c90d4a2d1a8: Downloading "}};
ProgressBar bar2{option::BarWidth{50}, option::ForegroundColor{Color::yellow},
option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
option::PrefixText{"22337bfd13a9: Downloading "}};
ProgressBar bar3{option::BarWidth{50}, option::ForegroundColor{Color::green},
option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
option::PrefixText{"10f26c680a34: Downloading "}};
ProgressBar bar4{option::BarWidth{50}, option::ForegroundColor{Color::white},
option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
option::PrefixText{"6364e0d7a283: Downloading "}};
ProgressBar bar5{option::BarWidth{50}, option::ForegroundColor{Color::blue},
option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
option::PrefixText{"ff1356ba118b: Downloading "}};
ProgressBar bar6{option::BarWidth{50}, option::ForegroundColor{Color::cyan},
option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
option::PrefixText{"5a17453338b4: Downloading "}};
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(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(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(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;
}
```
In the above code, notice the option `bars.set_option(option::HideBarWhenComplete{true});`. Yes, you can hide progress bars as and when they complete by setting this option to `true`. If you do so, the above example will look like this:
<p align="center">
<img src="img/dynamic_progress_bar_hide_completed.gif"/>
</p>
## Progress Spinner
To introduce a progress spinner in your application, include `indicators/progress_spinner.hpp` and create a `ProgressSpinner` object. Here's the general structure of a progress spinner:
@@ -363,15 +601,16 @@ int main() {
using namespace indicators;
indicators::ProgressSpinner spinner{
option::PostfixText{"Checking credentials"},
option::ForegroundColor{Color::YELLOW},
option::SpinnerStates{std::vector<std::string>{"", "", "", "", "", "", "", ""}}
option::ForegroundColor{Color::yellow},
option::SpinnerStates{std::vector<std::string>{"", "", "", "", "", "", "", ""}},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Update spinner state
auto job = [&spinner]() {
while (true) {
if (spinner.is_completed()) {
spinner.set_option(option::ForegroundColor{Color::GREEN});
spinner.set_option(option::ForegroundColor{Color::green});
spinner.set_option(option::PrefixText{""});
spinner.set_option(option::ShowSpinner{false});
spinner.set_option(option::ShowPercentage{false});
@@ -390,6 +629,337 @@ int main() {
}
```
## Decremental Progress
`indicators` allows you to easily control the progress direction, i.e., incremental or decremental progress by using `option::ProgressType`. To program a countdown progress bar, use `option::ProgressType::decremental`
<p align="center">
<img src="img/progress_bar_countdown.gif"/>
</p>
```cpp
#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;
}
```
## Working with Iterables
If you'd like to use progress bars to indicate progress while iterating over iterables, e.g., a list of numbers, this
can be achieved by using the `option::MaxProgress`:
<p align="center">
<img src="img/block_progress_bar_iterable.gif"/>
</p>
```cpp
#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;
}
```
## Unicode Support
`indicators` supports multi-byte unicode characters in progress bars.
If the `option::BarWidth` is set, the library aims to respect this setting. When filling the bar, if the next `Fill` string has a display width that would exceed the bar width, then the library will fill the remainder of the bar with `' '` space characters instead.
See below an example of some progress bars, each with a bar width of 50, displaying different unicode characters:
<p align="center">
<img src="img/unicode.gif"/>
</p>
```cpp
#include <chrono>
#include <indicators/progress_bar.hpp>
#include <indicators/indeterminate_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <thread>
int main() {
indicators::show_console_cursor(false);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
{
// Plain old ASCII
indicators::ProgressBar bar{
indicators::option::BarWidth{50},
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;
}
```
## Building Samples
```bash
git clone https://github.com/p-ranav/indicators
cd indicators
mkdir build && cd build
cmake -DINDICATORS_SAMPLES=ON -DINDICATORS_DEMO=ON ..
make
```
### WinLibs + MinGW
For Windows, if you use WinLibs like I do, the cmake command would look like this:
```console
foo@bar:~$ mkdir build && cd build
foo@bar:~$ cmake -G "MinGW Makefiles" -DCMAKE_CXX_COMPILER="C:/WinLibs/mingw64/bin/g++.exe" -DINDICATORS_SAMPLES=ON -DINDICATORS_DEMO=ON ..
foo@bar:~$ make -j4
```
## Generating Single Header
```bash
python3 utils/amalgamate/amalgamate.py -c single_include.json -s .
```
## Contributing
Contributions are welcome, have a look at the [CONTRIBUTING.md](CONTRIBUTING.md) document for more information.

View File

@@ -1,2 +1,2 @@
#!/usr/bin/env bash
find ./include ./demo/ ./samples/ -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"))

View File

@@ -1,23 +1,27 @@
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <indicators/progress_spinner.hpp>
#include <vector>
int main() {
using namespace indicators;
// Hide cursor
std::cout << "\e[?25l";
using namespace indicators;
show_console_cursor(false);
{
//
// PROGRESS BAR 1
//
indicators::ProgressBar p{option::BarWidth{50},
option::Start{"["},
option::Fill{""},
option::Lead{""},
option::Remainder{" "},
option::End{" ]"},
option::ForegroundColor{indicators::Color::YELLOW}};
indicators::ProgressBar p{
option::BarWidth{50},
option::Start{"["},
option::Fill{""},
option::Lead{""},
option::Remainder{" "},
option::End{" ]"},
option::ForegroundColor{indicators::Color::yellow},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
std::atomic<size_t> index{0};
std::vector<std::string> status_text = {"Rocket.exe is not responding",
@@ -49,18 +53,21 @@ int main() {
// PROGRESS BAR 2
//
indicators::ProgressBar p;
p.set_option(option::BarWidth{40});
p.set_option(option::BarWidth{0});
p.set_option(option::PrefixText{"Reading package list... "});
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::ForegroundColor{indicators::Color::WHITE});
p.set_option(option::ForegroundColor{indicators::Color::white});
p.set_option(option::ShowPercentage{false});
p.set_option(
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
auto job = [&p]() {
while (true) {
p.set_option(option::PrefixText{"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)
p.set_option(option::PrefixText{"Reading package list... Done"});
p.tick();
@@ -86,7 +93,9 @@ int main() {
p.set_option(option::Remainder{" "});
p.set_option(option::End{"]"});
p.set_option(option::PostfixText{"Getting started"});
p.set_option(option::ForegroundColor{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]() {
while (true) {
auto ticks = p.current();
@@ -120,9 +129,11 @@ int main() {
p4.set_option(option::Lead{""});
p4.set_option(option::Remainder{" "});
p4.set_option(option::End{""});
p4.set_option(option::ForegroundColor{indicators::Color::CYAN});
p4.set_option(option::ForegroundColor{indicators::Color::cyan});
p4.set_option(option::PostfixText{"Restoring system state"});
p4.set_option(option::ShowPercentage{false});
p4.set_option(
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}});
std::atomic<size_t> index4{0};
auto job4 = [&p4, &index4, &lead_spinner]() {
while (true) {
@@ -131,8 +142,9 @@ int main() {
index4 += 1;
if (p4.current() + 2 >= 100) {
std::cout << std::endl;
p4.set_option(option::ForegroundColor{indicators::Color::RED});
p4.set_option(option::ForegroundColor{indicators::Color::red});
p4.set_option(option::PrefixText{"{ ERROR }"});
p4.set_option(option::BarWidth{0});
p4.set_option(option::Start{});
p4.set_option(option::Fill{});
p4.set_option(option::Lead{});
@@ -156,24 +168,21 @@ int main() {
// GOING BACKWARDS
//
indicators::ProgressBar p{
option::BarWidth {50},
option::BarWidth{50},
option::ProgressType{ProgressType::decremental},
option::Start{"["},
option::Fill{""},
option::Lead{""},
option::Remainder{"-"},
option::End{"]"},
option::ForegroundColor{indicators::Color::WHITE},
option::PostfixText{"Reverting system restore"}
};
p.set_progress(100); // TODO backwards as an option?
std::atomic<size_t> progress{100};
auto job = [&p, &progress]() {
option::ForegroundColor{indicators::Color::white},
option::PostfixText{"Reverting system restore"},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
auto job = [&p]() {
while (true) {
progress -= 1;
p.set_progress(progress);
if (progress == 0) {
p.tick();
if (p.is_completed()) {
p.set_option(option::PostfixText{"Revert complete!"});
p.mark_as_completed();
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(60));
@@ -189,16 +198,15 @@ int main() {
// PROGRESS BAR 5
//
indicators::ProgressSpinner p{
option::PrefixText{""},
option::PostfixText{"Checking credentials"},
option::ForegroundColor{indicators::Color::YELLOW},
option::SpinnerStates{std::vector<std::string>{"", "", "", "", "", "", "", ""}}
};
option::PrefixText{""}, option::PostfixText{"Checking credentials"},
option::ForegroundColor{indicators::Color::yellow},
option::SpinnerStates{std::vector<std::string>{"", "", "", "", "", "", "", ""}},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
auto job = [&p]() {
while (true) {
if (p.is_completed()) {
p.set_option(option::ForegroundColor{indicators::Color::GREEN});
p.set_option(option::ForegroundColor{indicators::Color::green});
p.set_option(option::PrefixText{""});
p.set_option(option::ShowSpinner{false});
p.set_option(option::ShowPercentage{false});
@@ -220,12 +228,12 @@ int main() {
// PROGRESS BAR 6
//
indicators::ProgressSpinner p{
option::PrefixText{" - "},
option::PostfixText{"Searching for the Moon"},
option::ForegroundColor{indicators::Color::WHITE},
option::ShowPercentage{false},
option::SpinnerStates{std::vector<std::string>{"", "", "", ""}}
};
option::PrefixText{" - "},
option::PostfixText{"Searching for the Moon"},
option::ForegroundColor{indicators::Color::white},
option::ShowPercentage{false},
option::SpinnerStates{std::vector<std::string>{"", "", "", ""}},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
auto job = [&p]() {
while (true) {
auto current = p.current();
@@ -270,8 +278,8 @@ int main() {
option::Remainder{" "},
option::End{"🌑"},
option::PostfixText{"Achieved low-Earth orbit"},
option::ForegroundColor{indicators::Color::WHITE}
};
option::ForegroundColor{indicators::Color::white},
option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
std::vector<std::string> ship_trail{"", "", "", "", "", "", "", ""};
std::atomic<int> ship_trail_index{0};
auto job2 = [&p2, &ship_trail_index, &ship_trail]() {
@@ -309,7 +317,7 @@ int main() {
}
// Show cursor
std::cout << "\e[?25h";
show_console_cursor(true);
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

BIN
img/unicode.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 MiB

View File

@@ -1,224 +1,290 @@
/*
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 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
#ifndef INDICATORS_BLOCK_PROGRESS_BAR
#define INDICATORS_BLOCK_PROGRESS_BAR
#include <indicators/color.hpp>
#include <indicators/details/stream_helper.hpp>
#define NOMINMAX
#include <algorithm>
#include <atomic>
#include <chrono>
#include <indicators/setting.hpp>
#include <indicators/terminal_size.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <mutex>
#include <string>
#include <tuple>
#include <thread>
#include "setting.hpp"
#include <tuple>
#include <utility>
namespace indicators {
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>;
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:
template <typename... Args, typename std::enable_if<details::are_settings_from_tuple<Settings, typename std::decay<Args>::type...>::value, void*>::type = nullptr>
explicit BlockProgressBar(Args&&... args) : settings_(
details::get<details::ProgressBarOption::foreground_color>(option::ForegroundColor{Color::WHITE}, 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)...)
) {}
template <typename... Args,
typename std::enable_if<details::are_settings_from_tuple<
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);
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);
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);
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>()){
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);
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>()){
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(float value) {
{
std::lock_guard<std::mutex> lock{_mutex};
_progress = value;
std::lock_guard<std::mutex> lock{mutex_};
progress_ = value;
}
_save_start_time();
_print_progress();
save_start_time();
print_progress();
}
void tick() {
{
std::lock_guard<std::mutex> lock{_mutex};
_progress += 1;
std::lock_guard<std::mutex> lock{mutex_};
progress_ += 1;
}
_save_start_time();
_print_progress();
save_start_time();
print_progress();
}
size_t current() {
std::lock_guard<std::mutex> lock{_mutex};
return std::min(static_cast<size_t>(_progress), size_t(100));
std::lock_guard<std::mutex> lock{mutex_};
return (std::min)(static_cast<size_t>(progress_),
size_t(get_value<details::ProgressBarOption::max_progress>()));
}
bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
_print_progress();
print_progress();
}
private:
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings&>()).value)) {
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)) {
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
Settings settings_;
float _progress{0.0};
std::chrono::time_point<std::chrono::high_resolution_clock> _start_time_point;
std::mutex _mutex;
float progress_{0.0};
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
std::mutex mutex_;
template <typename Indicator, size_t count> friend class MultiProgress;
std::atomic<bool> _multi_progress_mode{false};
template <typename Indicator> friend class DynamicProgress;
std::atomic<bool> multi_progress_mode_{false};
void _save_start_time() {
auto& show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto& saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
auto& show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
void save_start_time() {
auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
_start_time_point = std::chrono::high_resolution_clock::now();
start_time_point_ = std::chrono::high_resolution_clock::now();
saved_start_time = true;
}
}
void _print_progress(bool from_multi_progress = false) {
if (_multi_progress_mode && !from_multi_progress) {
if (_progress > 100.0) {
get_value<details::ProgressBarOption::completed>() = true;
}
return;
}
std::lock_guard<std::mutex> lock{_mutex};
std::pair<std::string, size_t> 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, size_t> get_postfix_text() {
std::stringstream os;
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);
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
std::cout << termcolor::bold;
details::set_stream_color(std::cout, get_value<details::ProgressBarOption::foreground_color>());
std::cout << get_value<details::ProgressBarOption::prefix_text>();
std::cout << get_value<details::ProgressBarOption::start>();
details::BlockProgressScaleWriter writer{std::cout, get_value<details::ProgressBarOption::bar_width>()};
writer.write(_progress);
std::cout << get_value<details::ProgressBarOption::end>();
if (get_value<details::ProgressBarOption::show_percentage>()) {
std::cout << " " << std::min(static_cast<size_t>(_progress), size_t(100)) << "%";
os << " " << (std::min)(static_cast<size_t>(progress_ / max_progress * 100.0), size_t(100))
<< "%";
}
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
std::cout << " [";
details::write_duration(std::cout, elapsed);
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>())
std::cout << "<";
os << "<";
else
std::cout << " [";
auto eta = std::chrono::nanoseconds(
_progress > 0 ? static_cast<long long>(elapsed.count() * 100 / _progress) : 0);
auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta);
details::write_duration(std::cout, remaining);
std::cout << "]";
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>())
std::cout << "]";
os << "]";
}
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
std::cout << " " << get_value<details::ProgressBarOption::postfix_text>() << std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ') << "\r";
std::cout.flush();
if (_progress > 100.0) {
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 (progress_ > 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_ / max_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 (remaining > 0) {
os << std::string(remaining, ' ') << "\r";
} else if (remaining < 0) {
// Do nothing. Maybe in the future truncate postfix with ...
}
os.flush();
if (progress_ > max_progress) {
get_value<details::ProgressBarOption::completed>() = true;
}
if (get_value<details::ProgressBarOption::completed>() && !from_multi_progress) // Don't std::endl if calling from MultiProgress
std::cout << termcolor::reset << std::endl;
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

@@ -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>.
SPDX-License-Identifier: MIT
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>
#ifndef INDICATORS_COLOR
#define INDICATORS_COLOR
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

@@ -1,6 +1,9 @@
#pragma once
#include <indicators/color.hpp>
#ifndef INDICATORS_STREAM_HELPER
#define INDICATORS_STREAM_HELPER
#include <indicators/display_width.hpp>
#include <indicators/setting.hpp>
#include <indicators/termcolor.hpp>
#include <algorithm>
@@ -17,123 +20,204 @@ 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);
}
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);
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;
}
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 * static_cast<float>(bar_width) / 100.0);
for (size_t i = 0; i < bar_width; ++i) {
if (i < pos)
os << fill;
else if (i == pos)
os << lead;
else
os << remainder;
}
return os;
}
}
private:
std::ostream& os;
size_t bar_width = 0;
std::string fill;
std::string lead;
std::string remainder;
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,353 @@
#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) {
std::string curLocale = setlocale(LC_ALL, "");
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) {
std::string curLocale = setlocale(LC_ALL, "");
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,122 @@
#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 <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_ = {bars...};
for (auto &bar : bars_) {
bar.get().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].get();
}
size_t push_back(Indicator &bar) {
std::lock_guard<std::mutex> lock{mutex_};
bar.multi_progress_mode_ = true;
bars_.push_back(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::reference_wrapper<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.get().is_completed()) {
bar.get().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.get().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,237 @@
#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, size_t> 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, size_t> 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 (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

@@ -1,37 +1,16 @@
/*
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 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
#ifndef INDICATORS_MULTI_PROGRESS
#define INDICATORS_MULTI_PROGRESS
#include <atomic>
#include <functional>
#include <indicators/color.hpp>
#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 {
@@ -39,56 +18,65 @@ 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;
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();
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();
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();
return bars_[index].get().is_completed();
}
private:
std::atomic<bool> _started{false};
std::mutex _mutex;
std::vector<std::reference_wrapper<Indicator>> _bars;
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();
result &= bars_[i].get().is_completed();
return result;
}
void _print_progress() {
std::lock_guard<std::mutex> lock{_mutex};
if (_started)
for (size_t i = 0; i < count; ++i)
std::cout << "\x1b[A";
for (auto &bar : _bars) {
bar.get()._print_progress(true);
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;
if (!started_)
started_ = true;
}
};
} // namespace indicators
#endif

View File

@@ -1,244 +1,360 @@
/*
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 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
#ifndef INDICATORS_PROGRESS_BAR
#define INDICATORS_PROGRESS_BAR
#include <indicators/details/stream_helper.hpp>
#define NOMINMAX
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <tuple>
#include <indicators/terminal_size.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
namespace indicators {
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
>;
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:
template <typename... Args, typename std::enable_if<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::WHITE}, std::forward<Args>(args)...)
)
{}
template <typename... Args,
typename std::enable_if<
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>();
}
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);
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);
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);
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();
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 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(float newProgress){
void set_progress(size_t new_progress) {
{
std::lock_guard<std::mutex> lck(_mutex);
_progress = newProgress;
std::lock_guard<std::mutex> lock(mutex_);
progress_ = new_progress;
}
_save_start_time();
_print_progress();
save_start_time();
print_progress();
}
void tick() {
{
std::lock_guard<std::mutex> lock{_mutex};
_progress += 1;
std::lock_guard<std::mutex> lock{mutex_};
const auto type = get_value<details::ProgressBarOption::progress_type>();
if (type == ProgressType::incremental)
progress_ += 1;
else
progress_ -= 1;
}
_save_start_time();
_print_progress();
save_start_time();
print_progress();
}
size_t current() {
std::lock_guard<std::mutex> lock{_mutex};
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 get_value<details::ProgressBarOption::completed>(); }
bool is_completed() const {
return get_value<details::ProgressBarOption::completed>();
}
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
_print_progress();
print_progress();
}
private:
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings&>()).value)) {
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)) {
auto get_value() const -> decltype(
(details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
float _progress{0};
size_t progress_{0};
Settings settings_;
std::chrono::nanoseconds _elapsed;
std::chrono::time_point<std::chrono::high_resolution_clock> _start_time_point;
std::mutex _mutex;
std::chrono::nanoseconds elapsed_;
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
std::mutex mutex_;
template <typename Indicator, size_t count> friend class MultiProgress;
std::atomic<bool> _multi_progress_mode{false};
template <typename Indicator> friend class DynamicProgress;
std::atomic<bool> multi_progress_mode_{false};
void _save_start_time() {
auto& show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto& saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
auto& show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
void save_start_time() {
auto &show_elapsed_time =
get_value<details::ProgressBarOption::show_elapsed_time>();
auto &saved_start_time =
get_value<details::ProgressBarOption::saved_start_time>();
auto &show_remaining_time =
get_value<details::ProgressBarOption::show_remaining_time>();
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
_start_time_point = std::chrono::high_resolution_clock::now();
start_time_point_ = std::chrono::high_resolution_clock::now();
saved_start_time = true;
}
}
void _print_progress(bool from_multi_progress = false) {
if (_multi_progress_mode && !from_multi_progress) {
if (_progress > 100.0) {
get_value<details::ProgressBarOption::completed>() = true;
}
return;
}
std::lock_guard<std::mutex> lock{_mutex};
auto now = std::chrono::high_resolution_clock::now();
if (!get_value<details::ProgressBarOption::completed>())
_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - _start_time_point);
std::pair<std::string, size_t> 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::cout << termcolor::bold;
details::set_stream_color(std::cout, get_value<details::ProgressBarOption::foreground_color>());
std::cout << get_value<details::ProgressBarOption::prefix_text>();
std::cout << get_value<details::ProgressBarOption::start>();
details::ProgressScaleWriter writer{std::cout, get_value<details::ProgressBarOption::bar_width>(),
get_value<details::ProgressBarOption::fill>(),
get_value<details::ProgressBarOption::lead>(),
get_value<details::ProgressBarOption::remainder>()};
writer.write(_progress);
std::cout << get_value<details::ProgressBarOption::end>();
std::pair<std::string, size_t> get_postfix_text() {
std::stringstream os;
const auto max_progress =
get_value<details::ProgressBarOption::max_progress>();
if (get_value<details::ProgressBarOption::show_percentage>()) {
std::cout << " " << std::min(static_cast<size_t>(_progress), size_t(100)) << "%";
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>()) {
std::cout << " [";
details::write_duration(std::cout, _elapsed);
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>())
std::cout << "<";
os << "<";
else
std::cout << " [";
auto eta = std::chrono::nanoseconds(
_progress > 0 ? static_cast<long long>(_elapsed.count() * 100 / _progress) : 0);
auto remaining = eta > _elapsed ? (eta - _elapsed) : (_elapsed - eta);
details::write_duration(std::cout, remaining);
std::cout << "]";
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>())
std::cout << "]";
os << "]";
}
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
std::cout << " " << get_value<details::ProgressBarOption::postfix_text>() << std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ') << "\r";
std::cout.flush();
if (_progress > 100.0) {
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 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;
}
auto now = std::chrono::high_resolution_clock::now();
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 (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
std::cout << termcolor::reset << std::endl;
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

@@ -1,34 +1,9 @@
/*
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 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
#ifndef INDICATORS_PROGRESS_SPINNER
#define INDICATORS_PROGRESS_SPINNER
#include <indicators/details/stream_helper.hpp>
#define NOMINMAX
#include <algorithm>
#include <atomic>
#include <chrono>
@@ -39,175 +14,214 @@ SOFTWARE.
#include <iostream>
#include <mutex>
#include <string>
#include <tuple>
#include <thread>
#include <tuple>
#include <vector>
namespace indicators {
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
>;
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:
template <typename... Args, typename std::enable_if<details::are_settings_from_tuple<Settings, typename std::decay<Args>::type...>::value, void*>::type = nullptr>
explicit ProgressSpinner(Args&&... args) : settings_(
details::get<details::ProgressBarOption::foreground_color>(option::ForegroundColor{Color::WHITE}, 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)...)
){}
template <typename... Args,
typename std::enable_if<details::are_settings_from_tuple<
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);
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);
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);
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>()){
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);
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>()){
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(float value) {
void set_progress(size_t value) {
{
std::lock_guard<std::mutex> lock{_mutex};
_progress = value;
std::lock_guard<std::mutex> lock{mutex_};
progress_ = value;
}
_save_start_time();
_print_progress();
save_start_time();
print_progress();
}
void tick() {
{
std::lock_guard<std::mutex> lock{_mutex};
_progress += 1;
std::lock_guard<std::mutex> lock{mutex_};
progress_ += 1;
}
_save_start_time();
_print_progress();
save_start_time();
print_progress();
}
size_t current() {
std::lock_guard<std::mutex> lock{_mutex};
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 get_value<details::ProgressBarOption::completed>(); }
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
_print_progress();
print_progress();
}
private:
Settings settings_;
float _progress{0.0};
size_t _index{0};
std::chrono::time_point<std::chrono::high_resolution_clock> _start_time_point;
std::mutex _mutex;
size_t progress_{0};
size_t index_{0};
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
std::mutex mutex_;
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings&>()).value)) {
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)) {
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
void _save_start_time() {
auto& show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto& show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
auto& saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
void save_start_time() {
auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
_start_time_point = std::chrono::high_resolution_clock::now();
start_time_point_ = std::chrono::high_resolution_clock::now();
saved_start_time = true;
}
}
void _print_progress() {
std::lock_guard<std::mutex> lock{_mutex};
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - _start_time_point);
public:
void print_progress() {
std::lock_guard<std::mutex> lock{mutex_};
std::cout << termcolor::bold;
details::set_stream_color(std::cout, get_value<details::ProgressBarOption::foreground_color>());
std::cout << get_value<details::ProgressBarOption::prefix_text>();
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>())
std::cout << get_value<details::ProgressBarOption::spinner_states>()[_index % get_value<details::ProgressBarOption::spinner_states>().size()];
os << get_value<details::ProgressBarOption::spinner_states>()
[index_ % get_value<details::ProgressBarOption::spinner_states>().size()];
if (get_value<details::ProgressBarOption::show_percentage>()) {
std::cout << " " << std::min(static_cast<size_t>(_progress), size_t(100)) << "%";
os << " " << std::size_t(progress_ / double(max_progress) * 100) << "%";
}
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
std::cout << " [";
details::write_duration(std::cout, elapsed);
os << " [";
details::write_duration(os, elapsed);
}
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "<";
os << "<";
else
std::cout << " [";
os << " [";
auto eta = std::chrono::nanoseconds(
_progress > 0 ? static_cast<long long>(elapsed.count() * 100 / _progress) : 0);
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(std::cout, remaining);
std::cout << "]";
details::write_duration(os, remaining);
os << "]";
} else {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "]";
os << "]";
}
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
std::cout << " " << get_value<details::ProgressBarOption::postfix_text>() << std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ') << "\r";
std::cout.flush();
_index += 1;
if (_progress > 100.0) {
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>())
std::cout << termcolor::reset << std::endl;
os << termcolor::reset << std::endl;
}
};
} // 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

@@ -1,3 +1,4 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
@@ -24,65 +25,57 @@ 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
#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 indicators{
namespace details {
namespace details{
template <bool condition> struct if_else;
template <bool condition>
struct if_else;
template <> struct if_else<true> { using type = std::true_type; };
template<>
struct if_else<true>{
using type = std::true_type;
};
template <> struct if_else<false> { using type = std::false_type; };
template<>
struct if_else<false>{
using type = std::false_type ;
};
template <bool condition, typename True, typename False> struct if_else_type;
template <bool condition, typename True, typename False>
struct if_else_type;
template <typename True, typename False>
struct if_else_type<true, True, False>{
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>{
template <typename True, typename False> struct if_else_type<false, True, False> {
using type = False;
};
template <typename... Ops>
struct conjuction;
template <typename... Ops> struct conjuction;
template <>
struct conjuction<> : std::true_type {};
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 {};
struct conjuction<Op, TailOps...>
: if_else_type<!Op::value, std::false_type, conjuction<TailOps...>>::type {};
template <typename... Ops>
struct disjunction;
template <typename... Ops> struct disjunction;
template <>
struct disjunction<> : std::false_type {};
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 {};
struct disjunction<Op, TailOps...>
: if_else_type<Op::value, std::true_type, disjunction<TailOps...>>::type {};
enum class ProgressBarOption{
bar_width=0,
enum class ProgressBarOption {
bar_width = 0,
prefix_text,
postfix_text,
start,
@@ -98,15 +91,21 @@ enum class ProgressBarOption{
saved_start_time,
foreground_color,
spinner_show,
spinner_states
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;
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;
@@ -114,109 +113,110 @@ struct Setting{
T value{};
};
template <typename T>
struct is_setting : std::false_type{};
template <typename T> struct is_setting : std::false_type {};
template <ProgressBarOption Id, typename T>
struct is_setting<Setting<T, Id>> : std::true_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 <> struct are_settings<> : std::true_type {};
template<typename Setting, typename Tuple>
struct is_setting_from_tuple;
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> 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 {};
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 {};
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>
struct always_true{
static constexpr auto value = true;
};
template<ProgressBarOption Id, typename Default>
Default&& get_impl(Default&& def){
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);
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{
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)...)){
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 StringSetting = Setting<std::string, Id>;
template <ProgressBarOption Id>
using IntegerSetting = Setting<std::size_t, Id>;
template <ProgressBarOption Id> using IntegerSetting = Setting<std::size_t, Id>;
template <ProgressBarOption Id>
using BooleanSetting = Setting<bool, 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 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{};
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>{
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));
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
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>;
}
}
#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,7 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: indicators
Description: Activity Indicators for Modern C++
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
URL: @PROJECT_HOMEPAGE_URL@
Version: @PROJECT_VERSION@
Cflags: -I${includedir}

View File

@@ -2,7 +2,7 @@
include(CMakeFindDependencyMacro)
find_dependency(Threads)
find_dependency(Threads REQUIRED)
if (NOT TARGET indicators::indicators)
include(${CMAKE_CURRENT_LIST_DIR}/indicatorsTargets.cmake)

View File

@@ -2,6 +2,9 @@
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)
@@ -11,6 +14,15 @@ 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)
@@ -22,3 +34,16 @@ 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,15 +1,17 @@
#include <chrono>
#include <indicators/block_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <thread>
int main() {
// Hide cursor
std::cout << "\e[?25l";
indicators::show_console_cursor(false);
indicators::BlockProgressBar bar{
indicators::option::BarWidth {80}
};
indicators::option::BarWidth{80},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
// Update bar state
auto progress = 0.0f;
@@ -22,7 +24,7 @@ int main() {
}
// Show cursor
std::cout << "\e[?25h";
indicators::show_console_cursor(true);
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,155 @@
#include <indicators/dynamic_progress.hpp>
#include <indicators/progress_bar.hpp>
using namespace indicators;
int main() {
ProgressBar bar1{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}}};
ProgressBar bar2{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}}};
ProgressBar bar3{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}}};
ProgressBar bar4{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}}};
ProgressBar bar5{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}}};
ProgressBar bar6{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";
DynamicProgress<ProgressBar> bars(bar1, bar2, bar3);
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(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(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(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

@@ -3,29 +3,29 @@
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 "}
};
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 "}
};
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 "}
};
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);

View File

@@ -3,44 +3,44 @@
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::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::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::CYAN},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
indicators::option::PrefixText{"Progress Bar #3 "}
};
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);

View File

@@ -3,15 +3,15 @@
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::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:
//

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

@@ -1,22 +1,23 @@
#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>
int main() {
// Hide cursor
std::cout << "\e[?25l";
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::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
@@ -42,10 +43,8 @@ int main() {
bar.set_progress(100); // all done
bar.mark_as_completed();
// Show cursor
std::cout << "\e[?25h";
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

@@ -3,16 +3,16 @@
#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::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) {

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

@@ -1,21 +1,24 @@
#include <indicators/cursor_control.hpp>
#include <indicators/progress_spinner.hpp>
int main() {
// Hide cursor
std::cout << "\e[?25l";
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::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::ForegroundColor{indicators::Color::green});
spinner.set_option(indicators::option::PrefixText{""});
spinner.set_option(indicators::option::ShowSpinner{false});
spinner.set_option(indicators::option::ShowPercentage{false});
@@ -31,7 +34,7 @@ int main() {
thread.join();
// Show cursor
std::cout << "\e[?25h";
indicators::show_console_cursor(true);
return 0;
}

View File

@@ -1,20 +1,24 @@
#include <chrono>
#include <indicators/cursor_control.hpp>
#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{"Training Gaze Network "},
indicators::option::ForegroundColor{indicators::Color::YELLOW},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
};
// 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) {
@@ -25,7 +29,7 @@ int main() {
}
// Show cursor
std::cout << "\e[?25h";
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"]
}