Skip to content

Commit 003b90b

Browse files
tastybentoclaude
andcommitted
Add default colour for challenge description and reward text (#340)
Reimplements the useful part of the closed PR #340 in a way that fits the current pipeline. Setting a default colour saves admins from prefixing every challenge's description/reward text with the same colour code. Two new gui-settings, both empty by default (no change): - description-color: colour applied to each line of a challenge's own description text. - reward-text-color: colour applied to each line of a challenge's own first-time and repeat reward text. The colour is applied per line (so multi-line text stays coloured on every lore line) before Util.translateColorCodes, so it uses the same '&' / hex codes as the challenge text itself; a colour written in the text still overrides the default. Only the challenge's own data-field text is affected, not per-challenge locale overrides. The lore-length line-wrapping half of #340 is intentionally left out: it was built on legacy ChatColor wrapping incompatible with MiniMessage, its default reintroduced the space-less-language issues it was removed for, and the panel already wraps reward text via wrapToWidth(). Adds Utils.applyDefaultColor with JUnit coverage (blank/empty handling, single/multi-line, blank-line preservation, hex colours). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKxodNE4h3TsSHMqDEeC8v
1 parent 334216f commit 003b90b

5 files changed

Lines changed: 153 additions & 4 deletions

File tree

src/main/java/world/bentobox/challenges/config/Settings.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,20 @@ public class Settings implements ConfigObject
121121
@ConfigEntry(path = "gui-settings.open-anywhere")
122122
private boolean openAnywhere = false;
123123

124+
@ConfigComment("")
125+
@ConfigComment("Default colour applied to every line of a challenge's own description text, so you")
126+
@ConfigComment("do not have to prefix each challenge with the same colour. Uses '&' colour codes or")
127+
@ConfigComment("hex (e.g. '&b' or '&#55FFFF'). Leave empty for no default. A colour written in the")
128+
@ConfigComment("description itself still overrides this. Does not affect per-challenge locale overrides.")
129+
@ConfigEntry(path = "gui-settings.description-color")
130+
private String descriptionColor = "";
131+
132+
@ConfigComment("")
133+
@ConfigComment("Default colour applied to every line of a challenge's own reward text (both first-time")
134+
@ConfigComment("and repeat reward text). Same format as description-color. Leave empty for no default.")
135+
@ConfigEntry(path = "gui-settings.reward-text-color")
136+
private String rewardTextColor = "";
137+
124138

125139
@ConfigComment("")
126140
@ConfigComment("This allows to change default locked level icon. This option may be")
@@ -741,4 +755,48 @@ public void setOpenAnywhere(boolean openAnywhere)
741755
{
742756
this.openAnywhere = openAnywhere;
743757
}
758+
759+
760+
/**
761+
* Returns the default colour applied to challenge description text.
762+
*
763+
* @return the description colour code, or an empty string for no default.
764+
*/
765+
public String getDescriptionColor()
766+
{
767+
return descriptionColor;
768+
}
769+
770+
771+
/**
772+
* Sets the default colour applied to challenge description text.
773+
*
774+
* @param descriptionColor the colour code (e.g. "&b" or "&#55FFFF"), or empty for none.
775+
*/
776+
public void setDescriptionColor(String descriptionColor)
777+
{
778+
this.descriptionColor = descriptionColor;
779+
}
780+
781+
782+
/**
783+
* Returns the default colour applied to challenge reward text.
784+
*
785+
* @return the reward text colour code, or an empty string for no default.
786+
*/
787+
public String getRewardTextColor()
788+
{
789+
return rewardTextColor;
790+
}
791+
792+
793+
/**
794+
* Sets the default colour applied to challenge reward text.
795+
*
796+
* @param rewardTextColor the colour code (e.g. "&b" or "&#55FFFF"), or empty for none.
797+
*/
798+
public void setRewardTextColor(String rewardTextColor)
799+
{
800+
this.rewardTextColor = rewardTextColor;
801+
}
744802
}

src/main/java/world/bentobox/challenges/panel/CommonPanel.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,11 @@ protected List<String> generateChallengeDescription(Challenge challenge, @Nullab
143143
String description = this.user
144144
.getTranslationOrNothing(Constants.CHALLENGES_CHALLENGES + challenge.getUniqueId() + ".description");
145145
if (description.isEmpty()) {
146-
// Combine the challenge description list into a single string and translate color codes
147-
description = Util.translateColorCodes(String.join("\n", challenge.getDescription()));
146+
// Combine the challenge description list into a single string, apply the configured
147+
// default colour to each line, and translate color codes.
148+
description = Util.translateColorCodes(Utils.applyDefaultColor(
149+
String.join("\n", challenge.getDescription()),
150+
this.addon.getChallengesSettings().getDescriptionColor()));
148151
}
149152
// Replace any [label] placeholder with the actual top label
150153
description = description.replace("[label]", this.topLabel);
@@ -707,7 +710,9 @@ private String generateRepeatReward(Challenge challenge) {
707710
.getTranslationOrNothing(Constants.CHALLENGES_CHALLENGES + challenge.getUniqueId() + ".repeat-reward-text");
708711

709712
if (rewardText.isEmpty()) {
710-
rewardText = wrapToWidth(Util.translateColorCodes(String.join("\n", challenge.getRepeatRewardText())), 30);
713+
rewardText = wrapToWidth(Util.translateColorCodes(Utils.applyDefaultColor(
714+
String.join("\n", challenge.getRepeatRewardText()),
715+
this.addon.getChallengesSettings().getRewardTextColor())), 30);
711716
}
712717

713718
return this.user.getTranslationOrNothing(reference + "lore", Constants.PARAMETER_TEXT, rewardText, Constants.PARAMETER_ITEMS, items,
@@ -786,7 +791,9 @@ private String generateReward(Challenge challenge) {
786791
.getTranslationOrNothing(Constants.CHALLENGES_CHALLENGES + challenge.getUniqueId() + ".reward-text");
787792

788793
if (rewardText.isEmpty()) {
789-
rewardText = wrapToWidth(Util.translateColorCodes(String.join("\n", challenge.getRewardText())), 30);
794+
rewardText = wrapToWidth(Util.translateColorCodes(Utils.applyDefaultColor(
795+
String.join("\n", challenge.getRewardText()),
796+
this.addon.getChallengesSettings().getRewardTextColor())), 30);
790797
}
791798

792799
return this.user.getTranslationOrNothing(reference + "lore", Constants.PARAMETER_TEXT, rewardText, Constants.PARAMETER_ITEMS, items,

src/main/java/world/bentobox/challenges/utils/Utils.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,4 +1047,42 @@ public static String parseDuration(Duration duration, User user)
10471047

10481048
return returnString;
10491049
}
1050+
1051+
1052+
/**
1053+
* Prefixes every line of the given text with a default colour code so the colour applies
1054+
* to each rendered lore line, not only the first (each lore line is rendered
1055+
* independently). A colour written at the start of a line still overrides the default,
1056+
* because a later colour code wins over an earlier one. The text is returned unchanged
1057+
* when the colour is blank or the text is empty.
1058+
*
1059+
* <p>The colour is applied before {@code Util.translateColorCodes}, so it uses the same
1060+
* '&amp;' colour codes (or hex, e.g. {@code &#55FFFF}) as the challenge text itself.
1061+
*
1062+
* @param text the text whose lines should be coloured (may contain '\n').
1063+
* @param color the default colour code; blank means no change.
1064+
* @return the text with the colour prefixed to each line.
1065+
*/
1066+
public static String applyDefaultColor(String text, String color)
1067+
{
1068+
if (color == null || color.isBlank() || text == null || text.isEmpty())
1069+
{
1070+
return text;
1071+
}
1072+
1073+
String[] lines = text.split("\n", -1);
1074+
StringBuilder builder = new StringBuilder(text.length() + lines.length * color.length());
1075+
1076+
for (int i = 0; i < lines.length; i++)
1077+
{
1078+
if (i > 0)
1079+
{
1080+
builder.append('\n');
1081+
}
1082+
1083+
builder.append(color).append(lines[i]);
1084+
}
1085+
1086+
return builder.toString();
1087+
}
10501088
}

src/main/resources/config.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ gui-settings:
8080
# Note: Challenges completion still requires being on the island when world protection is enabled.
8181
open-anywhere: false
8282
#
83+
# Default colour applied to every line of a challenge's own description text, so you
84+
# do not have to prefix each challenge with the same colour. Uses '&' colour codes or
85+
# hex (e.g. '&b' or '&#55FFFF'). Leave empty for no default. A colour written in the
86+
# description itself still overrides this. Does not affect per-challenge locale overrides.
87+
description-color: ''
88+
#
89+
# Default colour applied to every line of a challenge's own reward text (both first-time
90+
# and repeat reward text). Same format as description-color. Leave empty for no default.
91+
reward-text-color: ''
92+
#
8393
# This allows to change default locked level icon. This option may be
8494
# overwritten by each challenge level. If challenge level has specified
8595
# their locked level icon, then it will be used, instead of this one.

src/test/java/world/bentobox/challenges/utils/UtilsTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,40 @@ void testGetPreviousValue() {
156156
assertEquals(VisibilityMode.VISIBLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.HIDDEN));
157157
assertEquals(VisibilityMode.HIDDEN, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE));
158158
}
159+
160+
@Test
161+
void testApplyDefaultColorBlankColorReturnsTextUnchanged() {
162+
assertEquals("Hello", Utils.applyDefaultColor("Hello", ""));
163+
assertEquals("Hello", Utils.applyDefaultColor("Hello", " "));
164+
assertEquals("Hello", Utils.applyDefaultColor("Hello", null));
165+
}
166+
167+
@Test
168+
void testApplyDefaultColorEmptyOrNullTextReturnedUnchanged() {
169+
assertEquals("", Utils.applyDefaultColor("", "&b"));
170+
assertNull(Utils.applyDefaultColor(null, "&b"));
171+
}
172+
173+
@Test
174+
void testApplyDefaultColorSingleLinePrefixed() {
175+
assertEquals("&bHello", Utils.applyDefaultColor("Hello", "&b"));
176+
}
177+
178+
@Test
179+
void testApplyDefaultColorPrefixesEveryLine() {
180+
assertEquals("&bLine1\n&bLine2\n&bLine3",
181+
Utils.applyDefaultColor("Line1\nLine2\nLine3", "&b"));
182+
}
183+
184+
@Test
185+
void testApplyDefaultColorPreservesBlankLines() {
186+
// Blank lines still get the prefix, and no trailing/leading lines are dropped.
187+
assertEquals("&bLine1\n&b\n&bLine2",
188+
Utils.applyDefaultColor("Line1\n\nLine2", "&b"));
189+
}
190+
191+
@Test
192+
void testApplyDefaultColorSupportsHexColor() {
193+
assertEquals("&#55FFFFHello", Utils.applyDefaultColor("Hello", "&#55FFFF"));
194+
}
159195
}

0 commit comments

Comments
 (0)