Skip to content

Commit

Permalink
Merge pull request #152 from PakhomovAlexander/custom-progress-bar-style
Browse files Browse the repository at this point in the history
Custom progress bar style and colors
  • Loading branch information
ctongfei authored Aug 7, 2023
2 parents 3a55618 + 03b4efc commit f48cf5f
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 17 deletions.
17 changes: 17 additions & 0 deletions docs/builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,20 @@ for (T x : ProgressBar.wrap(collection, pbb)) {
...
}
```

Since `0.9.6` you can customize the progress bar style:

``` java
ProgressBarBuilder pbb = new ProgressBarBuilder()
// ...
.setStyle(DrawStyle.builder()
// the color index from 0 to 255 (Ansi color table)
.colorCode(33)
.leftBracket("{")
.rightBracket("}")
.block('-')
.rightSideFractionSymbol('+')
.build()
)
// ...
```
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
public class DefaultProgressBarRenderer implements ProgressBarRenderer {

private ProgressBarStyle style;
private DrawStyle style;
private String unitName;
private long unitSize;
private boolean isSpeedShown;
Expand All @@ -27,7 +27,7 @@ public class DefaultProgressBarRenderer implements ProgressBarRenderer {
private Function<ProgressState, Optional<Duration>> eta;

protected DefaultProgressBarRenderer(
ProgressBarStyle style,
DrawStyle style,
String unitName,
long unitSize,
boolean isSpeedShown,
Expand All @@ -53,7 +53,7 @@ protected int progressIntegralPart(ProgressState progress, int length) {

protected int progressFractionalPart(ProgressState progress, int length) {
double p = progress.getNormalizedProgress() * length;
double fraction = (p - Math.floor(p)) * style.fractionSymbols.length();
double fraction = (p - Math.floor(p)) * style.fractionSymbols().length();
return (int) Math.floor(fraction);
}

Expand Down Expand Up @@ -112,7 +112,7 @@ public String render(ProgressState progress, int maxLength) {
return "";
}

String prefix = progress.getTaskName() + " " + percentage(progress) + " " + style.leftBracket;
String prefix = progress.getTaskName() + " " + percentage(progress) + " " + style.leftBracket();
int prefixLength = getStringDisplayLength(prefix);

if (prefixLength > maxLength) {
Expand All @@ -124,7 +124,7 @@ public String render(ProgressState progress, int maxLength) {
int maxSuffixLength = Math.max(maxLength - prefixLength - 1, 0);

String speedString = isSpeedShown ? speed(progress) : "";
String suffix = style.rightBracket + " " + ratio(progress) + " ("
String suffix = style.rightBracket() + " " + ratio(progress) + " ("
+ Util.formatDuration(progress.getTotalElapsed())
+ (isEtaShown ? " / " + etaString(progress) : "")
+ ") "
Expand All @@ -144,24 +144,24 @@ public String render(ProgressState progress, int maxLength) {
// case of indefinite progress bars
if (progress.indefinite) {
int pos = (int)(progress.current % length);
sb.append(Util.repeat(style.space, pos));
sb.append(style.block);
sb.append(Util.repeat(style.space, length - pos - 1));
sb.append(Util.repeat(style.space(), pos));
sb.append(style.block());
sb.append(Util.repeat(style.space(), length - pos - 1));
}
// case of definite progress bars
else {
sb.append(Util.repeat(style.block, progressIntegralPart(progress, length)));
sb.append(Util.repeat(style.block(), progressIntegralPart(progress, length)));
if (progress.current < progress.max) {
int fraction = progressFractionalPart(progress, length);
if (fraction != 0) {
sb.append(style.fractionSymbols.charAt(fraction));
sb.append(style.delimitingSequence);
sb.append(style.fractionSymbols().charAt(fraction));
sb.append(style.delimitingSequence());
}
else {
sb.append(style.delimitingSequence);
sb.append(style.rightSideFractionSymbol);
sb.append(style.delimitingSequence());
sb.append(style.rightSideFractionSymbol());
}
sb.append(Util.repeat(style.space, length - progressIntegralPart(progress, length) - 1));
sb.append(Util.repeat(style.space(), length - progressIntegralPart(progress, length) - 1));
}
}

Expand Down
42 changes: 42 additions & 0 deletions src/main/java/me/tongfei/progressbar/DrawStyle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package me.tongfei.progressbar;

/**
* A draw style for a progress bar.
*
* @author Aleksandr Pakhomov
*/
public interface DrawStyle {
/** Create draw style object from {@link ProgressBarStyle} */
static DrawStyle from(ProgressBarStyle style) {
return builder().apply(style).build();
}

/** Symbol to refresh the progress bar. */
String refreshPrompt();

/** Left bracket of the progress bar. */
String leftBracket();

/** Delimiting sequence between the progress bar and the status text. */
String delimitingSequence();

/** Right bracket of the progress bar. */
String rightBracket();

/** Block character of the progress bar. [=====> ] here '=' is the block. */
char block();

/** Space character of the progress bar. [=====> ] here ' ' is the space. */
char space();

/** Fraction symbols of the progress bar. */
String fractionSymbols();

/** Right side fraction symbol of the progress bar. [=====> ] here '>'. */
char rightSideFractionSymbol();

/** Create a new builder for {@link DrawStyle} */
static DrawStyleBuilder builder() {
return new DrawStyleBuilder();
}
}
168 changes: 168 additions & 0 deletions src/main/java/me/tongfei/progressbar/DrawStyleBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package me.tongfei.progressbar;

/**
* Builder for {@link DrawStyle}.
*
* @author Aleksandr Pakhomov
*/
public class DrawStyleBuilder {
private static final String ESC_CODE = "\u001b[";

private String refreshPrompt = "\r";
private String leftBracket = "[";
private String delimitingSequence = "";
private String rightBracket = "]";
private char block = '=';
private char space = ' ';
private String fractionSymbols = ">";
private char rightSideFractionSymbol = '>';
private int colorCode = 0;

/** Set refresh prompt. Default "\r". */
public DrawStyleBuilder refreshPrompt(String refreshPrompt) {
this.refreshPrompt = refreshPrompt;
return this;
}

/** Set left bracket. Default "[". */
public DrawStyleBuilder leftBracket(String leftBracket) {
this.leftBracket = leftBracket;
return this;
}

/** Set delimiting sequence. Default "". */
public DrawStyleBuilder delimitingSequence(String delimitingSequence) {
this.delimitingSequence = delimitingSequence;
return this;
}

/** Set right bracket. Default "]". */
public DrawStyleBuilder rightBracket(String rightBracket) {
this.rightBracket = rightBracket;
return this;
}

/** Set block character. Default "=" */
public DrawStyleBuilder block(char block) {
this.block = block;
return this;
}

/** Set space character. Default " " */
public DrawStyleBuilder space(char space) {
this.space = space;
return this;
}

/** Set fraction symbols. Default ">" */
public DrawStyleBuilder fractionSymbols(String fractionSymbols) {
this.fractionSymbols = fractionSymbols;
return this;
}

/** Set right side fraction symbol. Default ">" */
public DrawStyleBuilder rightSideFractionSymbol(char rightSideFractionSymbol) {
this.rightSideFractionSymbol = rightSideFractionSymbol;
return this;
}

/** Set ANSI color code. Default 0 (no color). Must be in [0, 255]. */
public DrawStyleBuilder colorCode(int code) {
if (code < 0 || code > 255)
throw new IllegalArgumentException("Color code must be between 0 and 255.");

this.colorCode = code;
return this;
}

/** Build {@link DrawStyle}. */
public DrawStyle build() {
boolean colorDefined = colorCode != 0;

if (colorDefined && leftBracket.contains(ESC_CODE)) {
throw new IllegalArgumentException("The color code is overridden with left bracket escape code. "
+ "Please, remove the escape sequence from the left bracket or do not use color code.");
}

String prefix = colorDefined ? (ESC_CODE + colorCode + "m") : "";
String postfix = colorDefined ? ESC_CODE + "0m" : "";

return new InternalDrawStyle(refreshPrompt, prefix + leftBracket, delimitingSequence,
rightBracket + postfix, block, space, fractionSymbols, rightSideFractionSymbol);
}

/** Apply {@link ProgressBarStyle}. */
public DrawStyleBuilder apply(ProgressBarStyle style) {
refreshPrompt(style.refreshPrompt);
leftBracket(style.leftBracket);
delimitingSequence(style.delimitingSequence);
rightBracket(style.rightBracket);
block(style.block);
space(style.space);
fractionSymbols(style.fractionSymbols);
return rightSideFractionSymbol(style.rightSideFractionSymbol);
}

static class InternalDrawStyle implements DrawStyle {
private final String refreshPrompt;
private final String leftBracket;
private final String delimitingSequence;
private final String rightBracket;
private final char block;
private final char space;
private final String fractionSymbols;
private final char rightSideFractionSymbol;

private InternalDrawStyle(String refreshPrompt, String leftBracket, String delimitingSequence,
String rightBracket, char block, char space, String fractionSymbols, char rightSideFractionSymbol) {
this.refreshPrompt = refreshPrompt;
this.leftBracket = leftBracket;
this.delimitingSequence = delimitingSequence;
this.rightBracket = rightBracket;
this.block = block;
this.space = space;
this.fractionSymbols = fractionSymbols;
this.rightSideFractionSymbol = rightSideFractionSymbol;
}

@Override
public String refreshPrompt() {
return refreshPrompt;
}

@Override
public String leftBracket() {
return leftBracket;
}

@Override
public String delimitingSequence() {
return delimitingSequence;
}

@Override
public String rightBracket() {
return rightBracket;
}

@Override
public char block() {
return block;
}

@Override
public char space() {
return space;
}

@Override
public String fractionSymbols() {
return fractionSymbols;
}

@Override
public char rightSideFractionSymbol() {
return rightSideFractionSymbol;
}
}
}
36 changes: 34 additions & 2 deletions src/main/java/me/tongfei/progressbar/ProgressBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public ProgressBar(String task, long initialMax) {
* @param initialMax Initial maximum value
* @param updateIntervalMillis Update interval (default value 1000 ms)
* @param continuousUpdate Rerender every time the update interval happens regardless of progress count.
* @param style Output style (default value ProgressBarStyle.UNICODE_BLOCK)
* @param style Draw style
* @param showSpeed Should the calculated speed be displayed
* @param speedFormat Speed number format
* @deprecated Use {@link ProgressBarBuilder} instead.
Expand All @@ -62,7 +62,7 @@ public ProgressBar(
boolean continuousUpdate,
boolean clearDisplayOnFinish,
PrintStream os,
ProgressBarStyle style,
DrawStyle style,
String unitName,
long unitSize,
boolean showSpeed,
Expand All @@ -81,6 +81,38 @@ public ProgressBar(
);
}

/**
* Creates a progress bar with the specific taskName name, initial maximum value,
* customized update interval (default 1000 ms), the PrintStream to be used, and output style.
* @param task Task name
* @param initialMax Initial maximum value
* @param updateIntervalMillis Update interval (default value 1000 ms)
* @param continuousUpdate Rerender every time the update interval happens regardless of progress count.
* @param style Output style (default value ProgressBarStyle.UNICODE_BLOCK)
* @param showSpeed Should the calculated speed be displayed
* @param speedFormat Speed number format
* @deprecated Use {@link ProgressBarBuilder} instead.
*/
public ProgressBar(
String task,
long initialMax,
int updateIntervalMillis,
boolean continuousUpdate,
boolean clearDisplayOnFinish,
PrintStream os,
ProgressBarStyle style,
String unitName,
long unitSize,
boolean showSpeed,
DecimalFormat speedFormat,
ChronoUnit speedUnit,
long processed,
Duration elapsed
) {
this(task, initialMax, updateIntervalMillis, continuousUpdate, clearDisplayOnFinish, os,
DrawStyle.from(style), unitName, unitSize, showSpeed, speedFormat, speedUnit, processed, elapsed);
}

/**
* Creates a progress bar with the specific name, initial maximum value, customized update interval (default 1s),
* and the provided progress bar renderer ({@link ProgressBarRenderer}) and consumer ({@link ProgressBarConsumer}).
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/me/tongfei/progressbar/ProgressBarBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ProgressBarBuilder {
private long initialMax = -1;
private int updateIntervalMillis = 1000;
private boolean continuousUpdate = false;
private ProgressBarStyle style = ProgressBarStyle.COLORFUL_UNICODE_BLOCK;
private DrawStyle style = DrawStyle.from(ProgressBarStyle.COLORFUL_UNICODE_BLOCK);
private ProgressBarConsumer consumer = null;
private boolean clearDisplayOnFinish = false;
private String unitName = "";
Expand Down Expand Up @@ -48,6 +48,11 @@ public ProgressBarBuilder setInitialMax(long initialMax) {
}

public ProgressBarBuilder setStyle(ProgressBarStyle style) {
this.style = DrawStyle.from(style);
return this;
}

public ProgressBarBuilder setStyle(DrawStyle style) {
this.style = style;
return this;
}
Expand Down
Loading

0 comments on commit f48cf5f

Please sign in to comment.