Skip to content

Commit

Permalink
feat(minimessage): add progress tag
Browse files Browse the repository at this point in the history
  • Loading branch information
AV3RG committed Jun 2, 2022
1 parent 1b18515 commit b9b4979
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;
import java.util.function.Supplier;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.standard.ProgressTag;
import org.jetbrains.annotations.NotNull;

/**
Expand Down Expand Up @@ -102,7 +104,7 @@ private Formatter() {
/**
* Creates a replacement that inserts a choice formatted text. The component will be formatted by the provided ChoiceFormat.
*
* <p>This tag expectes a format as attribute. Refer to {@link ChoiceFormat} for usable patterns.</p>
* <p>This tag expects a format as attribute. Refer to {@link ChoiceFormat} for usable patterns.</p>
*
* <p>This replacement is auto-closing, so its style will not influence the style of following components.</p>
*
Expand All @@ -118,4 +120,22 @@ private Formatter() {
return Tag.inserting(context.deserialize(choiceFormat.format(number)));
}));
}

/**
* Creates a replacement that inserts a progress bar. The component will be formatted by the provided fill and remaining colors and length
*
* <p>This tag expects a text to use to denote progress and can accept length, filled color and empty color. The text has to be provided as first argument.
* In case the fill and empty colors and length are not provided, they will default to {@link ProgressTag#DEFAULT_FILLED_COLOR}, {@link ProgressTag#DEFAULT_REMAINING_COLOR} and {@value ProgressTag#DEFAULT_LENGTH} respectively.</p>
*
* <p>This replacement is auto-closing, so its style will not influence the style of following components.</p>
*
* @param key the key
* @param progressSupplier the supplier for the progress value
* @return the placeholder
* @since 4.13.0
*/
public static @NotNull TagResolver progress(final @NotNull String key, final @NotNull Supplier<Double> progressSupplier) {
return TagResolver.resolver(key, (args, ctx) -> ProgressTag.createProgressProvided(args, ctx, progressSupplier));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2022 KyoriPowered
*
* 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.
*/
package net.kyori.adventure.text.minimessage.tag.standard;

import java.util.Objects;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.function.Supplier;
import java.util.stream.Stream;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.Context;
import net.kyori.adventure.text.minimessage.ParsingException;
import net.kyori.adventure.text.minimessage.tag.Inserting;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.examination.Examinable;
import net.kyori.examination.ExaminableProperty;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Progress tag.
*
* @since 4.13.0
*/
@ApiStatus.Internal
public final class ProgressTag implements Inserting, Examinable {

private static final String PROGRESS = "progress";
private static final int DEFAULT_LENGTH = 10;
private static final NamedTextColor DEFAULT_FILLED_COLOR = NamedTextColor.GREEN;
private static final NamedTextColor DEFAULT_REMAINING_COLOR = NamedTextColor.RED;
static final TagResolver RESOLVER = TagResolver.resolver(PROGRESS, ProgressTag::create);
private final String text;
private final int totalLength;
private final TextColor fillColor;
private final TextColor remainingColor;

private final Supplier<Double> progressSupplier;

private ProgressTag(final String text, final int totalLength, final TextColor fillColor, final TextColor remainingColor, final Supplier<Double> progressSupplier) {
this.text = text;
this.totalLength = totalLength;
this.fillColor = fillColor;
this.remainingColor = remainingColor;
this.progressSupplier = progressSupplier;
}

static Tag create(final ArgumentQueue args, final Context ctx) throws ParsingException {

final OptionalDouble optionalProgress = args.popOr("Invalid progress! Progress must be a double in the range [0d, 1.0d] (inclusive).").asDouble();
if (!optionalProgress.isPresent()) {
throw ctx.newException("Progress must be a double in the range [0d, 1.0d] (inclusive).");
}
final double progress = optionalProgress.getAsDouble();
if (progress < 0 || progress > 1) {
throw ctx.newException("Progress is out of range (%s). Must be in the range [0d, 1.0d] (inclusive).");
}
final Supplier<Double> progressSupplier = () -> progress;

return createProgressProvided(args, ctx, progressSupplier);
}

/**
* Creates a progress tag that uses progress from the supplier to calculate the progress when the value is requested.
*
* @param args the arguments
* @param ctx the context
* @param progressSupplier the progress supplier
*
* @return progress tag
* @throws ParsingException if length is not an integer greater than or equal to 1 or if the colors are invalid
* @since 4.13.0
*/
@ApiStatus.Internal
public static Tag createProgressProvided(final ArgumentQueue args, final Context ctx, final Supplier<Double> progressSupplier) throws ParsingException {
final String text = args.popOr("No text found to use for progress bar.").value();

final int length;
if (args.hasNext()) {
final OptionalInt optionalLength = args.pop().asInt();
if (!optionalLength.isPresent()) {
throw ctx.newException("Length must be an integer.");
}
length = optionalLength.getAsInt();
if (length < 1) {
throw ctx.newException("Progress bar length must be at least 1.");
}
} else {
length = DEFAULT_LENGTH;
}

final TextColor fillColor;
if (args.hasNext()) {
fillColor = ColorTagResolver.resolveColor(args.pop().value(), ctx);
} else {
fillColor = DEFAULT_FILLED_COLOR;
}

final TextColor remainingColor;
if (args.hasNext()) {
remainingColor = ColorTagResolver.resolveColor(args.pop().value(), ctx);
} else {
remainingColor = DEFAULT_REMAINING_COLOR;
}
return new ProgressTag(text, length, fillColor, remainingColor, progressSupplier);
}

@Override
public @NotNull Component value() {
final Component filledComponent = Component.text(this.text, this.fillColor);
final Component remainingComponent = Component.text(this.text, this.remainingColor);
final TextComponent.Builder barBuilder = Component.text();
final double progress = this.progressSupplier.get();
if (progress < 0 || progress > 1) {
throw new IllegalStateException("Progress must be a double in the range [0d, 1.0d] (inclusive).");
}
final int fillLength = (int) (this.totalLength * progress);
for (int i = 0; i < fillLength; i++) {
barBuilder.append(filledComponent);
}
for (int i = 0; i < this.totalLength - fillLength; i++) {
barBuilder.append(remainingComponent);
}
return barBuilder.build();
}

@Override
public boolean allowsChildren() {
return false;
}

@Override
public @NotNull Stream<? extends ExaminableProperty> examinableProperties() {
return Stream.of(
ExaminableProperty.of("text", this.text),
ExaminableProperty.of("totalLength", this.totalLength),
ExaminableProperty.of("fillColor", this.fillColor),
ExaminableProperty.of("remainingColor", this.remainingColor),
ExaminableProperty.of("progressSupplier", this.progressSupplier)
);
}

@Override
public boolean equals(final @Nullable Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
final ProgressTag that = (ProgressTag) other;
return this.text.equals(that.text)
&& this.totalLength == that.totalLength
&& this.fillColor.equals(that.fillColor)
&& this.remainingColor.equals(that.remainingColor)
&& this.progressSupplier.equals(that.progressSupplier);
}

@Override
public int hashCode() {
return Objects.hash(this.text, this.totalLength, this.fillColor, this.remainingColor, this.progressSupplier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ private StandardTags() {
RainbowTag.RESOLVER,
ResetTag.RESOLVER,
NewlineTag.RESOLVER,
TransitionTag.RESOLVER
TransitionTag.RESOLVER,
ProgressTag.RESOLVER
)
.build();

Expand Down Expand Up @@ -214,6 +215,16 @@ public static TagResolver transition() {
return NewlineTag.RESOLVER;
}

/**
* Get a resolver for the {@value ProgressTag#PROGRESS} tag.
*
* @return a resolver for the {@value ProgressTag#PROGRESS} tag
* @since 4.11.0
*/
public static @NotNull TagResolver progress() {
return ProgressTag.RESOLVER;
}

/**
* Get a resolver that handles all default standard tags.
*
Expand Down

0 comments on commit b9b4979

Please sign in to comment.