Skip to content

Commit f699c90

Browse files
authored
Merge pull request #417 from BentoBoxWorld/feat/175-toggleable-undeployed
Implement TOGGLEABLE undeployed view mode (#175)
2 parents 334216f + 65ebe61 commit f699c90

6 files changed

Lines changed: 191 additions & 6 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ public class Settings implements ConfigObject
112112
@ConfigComment("Valid values are:")
113113
@ConfigComment(" 'VISIBLE' - there will be no hidden challenges. All challenges will be viewable in GUI.")
114114
@ConfigComment(" 'HIDDEN' - shows only deployed challenges.")
115+
@ConfigComment(" 'TOGGLEABLE' - adds a button to the player GUI so each player can show or hide")
116+
@ConfigComment(" undeployed challenges themselves (defaults to shown).")
115117
@ConfigEntry(path = "gui-settings.undeployed-view-mode")
116118
private VisibilityMode visibilityMode = VisibilityMode.VISIBLE;
117119

src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ protected void build()
102102

103103
panelBuilder.registerTypeBuilder("UNASSIGNED_CHALLENGES", this::createFreeChallengesButton);
104104

105+
panelBuilder.registerTypeBuilder("TOGGLE_UNDEPLOYED", this::createToggleUndeployedButton);
106+
105107
panelBuilder.registerTypeBuilder("NEXT", this::createNextButton);
106108
panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton);
107109

@@ -120,8 +122,9 @@ private void updateFreeChallengeList()
120122
this.manager.isChallengeComplete(this.user, this.world, challenge) &&
121123
(globalRemoveCompleted || challenge.isRemoveWhenCompleted()));
122124

123-
// Remove all undeployed challenges if VisibilityMode is set to Hidden.
124-
if (this.addon.getChallengesSettings().getVisibilityMode().equals(SettingsUtils.VisibilityMode.HIDDEN))
125+
// Remove all undeployed challenges if they should be hidden (HIDDEN mode, or
126+
// TOGGLEABLE mode with the player currently hiding them).
127+
if (this.hideUndeployedChallenges())
125128
{
126129
this.freeChallengeList.removeIf(challenge -> !challenge.isDeployed());
127130
}
@@ -134,6 +137,21 @@ private void updateFreeChallengeList()
134137
}
135138

136139

140+
/**
141+
* Whether undeployed challenges should be hidden from the viewing player right now.
142+
* True when the visibility mode is HIDDEN, or when it is TOGGLEABLE and the player has
143+
* chosen to hide them.
144+
*
145+
* @return {@code true} if undeployed challenges must be filtered out of the GUI.
146+
*/
147+
private boolean hideUndeployedChallenges()
148+
{
149+
SettingsUtils.VisibilityMode mode = this.addon.getChallengesSettings().getVisibilityMode();
150+
return mode == SettingsUtils.VisibilityMode.HIDDEN ||
151+
(mode == SettingsUtils.VisibilityMode.TOGGLEABLE && !this.showUndeployed);
152+
}
153+
154+
137155
/**
138156
* @return whether the viewing user currently has a team (island membership > 1).
139157
*/
@@ -161,8 +179,9 @@ private void updateChallengeList()
161179
this.manager.isChallengeComplete(this.user, this.world, challenge) &&
162180
(globalRemoveCompleted || challenge.isRemoveWhenCompleted()));
163181

164-
// Remove all undeployed challenges if VisibilityMode is set to Hidden.
165-
if (this.addon.getChallengesSettings().getVisibilityMode().equals(SettingsUtils.VisibilityMode.HIDDEN))
182+
// Remove all undeployed challenges if they should be hidden (HIDDEN mode, or
183+
// TOGGLEABLE mode with the player currently hiding them).
184+
if (this.hideUndeployedChallenges())
166185
{
167186
this.challengeList.removeIf(challenge -> !challenge.isDeployed());
168187
}
@@ -665,6 +684,78 @@ private PanelItem createFreeChallengesButton(@NonNull ItemTemplateRecord templat
665684
}
666685

667686

687+
/**
688+
* Creates the button that lets a player show or hide undeployed challenges when the
689+
* visibility mode is TOGGLEABLE. In any other visibility mode there is nothing to toggle,
690+
* so no button is shown (the slot falls back to the panel background).
691+
*
692+
* @param template the button template.
693+
* @param slot the slot the button occupies.
694+
* @return the toggle button, or {@code null} when the mode is not TOGGLEABLE.
695+
*/
696+
@Nullable
697+
private PanelItem createToggleUndeployedButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
698+
{
699+
// Only meaningful in TOGGLEABLE mode. In VISIBLE / HIDDEN there is nothing to toggle.
700+
if (this.addon.getChallengesSettings().getVisibilityMode() != SettingsUtils.VisibilityMode.TOGGLEABLE)
701+
{
702+
return null;
703+
}
704+
705+
PanelItemBuilder builder = new PanelItemBuilder();
706+
707+
if (template.icon() != null)
708+
{
709+
builder.icon(template.icon().clone());
710+
}
711+
712+
if (template.title() != null)
713+
{
714+
builder.name(this.user.getTranslation(this.world, template.title()));
715+
}
716+
717+
if (template.description() != null)
718+
{
719+
builder.description(this.user.getTranslation(this.world, template.description()));
720+
}
721+
722+
// Show whether undeployed challenges are currently shown or hidden for this player.
723+
builder.description(this.user.getTranslation(this.world,
724+
this.showUndeployed ?
725+
Constants.BUTTON + "toggle-undeployed.shown" :
726+
Constants.BUTTON + "toggle-undeployed.hidden"));
727+
728+
// Add ClickHandler: flip the preference, reset paging, and rebuild.
729+
builder.clickHandler((panel, user, clickType, i) ->
730+
{
731+
this.showUndeployed = !this.showUndeployed;
732+
// The visible challenge count changes, so the current page may be out of range.
733+
this.challengeIndex = 0;
734+
this.build();
735+
736+
// Always return true.
737+
return true;
738+
});
739+
740+
// Collect tooltips.
741+
List<String> tooltips = template.actions().stream().
742+
filter(action -> action.tooltip() != null).
743+
map(action -> this.user.getTranslation(this.world, action.tooltip())).
744+
filter(text -> !text.isBlank()).
745+
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
746+
747+
// Add tooltips.
748+
if (!tooltips.isEmpty())
749+
{
750+
// Empty line and tooltips.
751+
builder.description("");
752+
builder.description(tooltips);
753+
}
754+
755+
return builder.build();
756+
}
757+
758+
668759
@Nullable
669760
private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
670761
{
@@ -901,4 +992,12 @@ else if (Constants.LEVEL_BUILDER_KEY.equals(target))
901992
* This indicates last selected level.
902993
*/
903994
private LevelStatus lastSelectedLevel;
995+
996+
/**
997+
* Per-session preference for the TOGGLEABLE visibility mode: whether this player
998+
* currently wants to see undeployed challenges. Defaults to {@code true} (shown), so
999+
* TOGGLEABLE behaves like VISIBLE until the player hides them with the toggle button.
1000+
* Only consulted when the visibility mode is TOGGLEABLE.
1001+
*/
1002+
private boolean showUndeployed = true;
9041003
}

src/main/resources/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ gui-settings:
7272
# Valid values are:
7373
# 'VISIBLE' - there will be no hidden challenges. All challenges will be viewable in GUI.
7474
# 'HIDDEN' - shows only deployed challenges.
75-
# 'TOGGLEABLE' - there will be button in GUI that allows users to switch from ALL modes.
76-
# TOGGLEABLE - Currently not implemented.
75+
# 'TOGGLEABLE' - adds a button to the player GUI so each player can show or hide
76+
# undeployed challenges themselves (defaults to shown).
7777
undeployed-view-mode: VISIBLE
7878
#
7979
# Allow players to open the challenges GUI without being on their island.

src/main/resources/locales/en-US.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ challenges:
8080
description: |-
8181
<gray>Displays a list of
8282
<gray>free challenges
83+
# Button shown only when undeployed-view-mode is TOGGLEABLE. It lets each player
84+
# show or hide the challenges that are not yet deployed.
85+
toggle-undeployed:
86+
name: "<white><bold>Undeployed Challenges"
87+
shown: |-
88+
<gray>Undeployed challenges are
89+
<gray>currently <green>shown<gray>.
90+
hidden: |-
91+
<gray>Undeployed challenges are
92+
<gray>currently <red>hidden<gray>.
8393
# Button that is used to return to previous GUI or exit it completely.
8494
return:
8595
name: "<white><bold>Return"

src/main/resources/panels/main_panel.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ main_panel:
8282
left:
8383
tooltip: challenges.gui.tips.click-to-next
8484
6:
85+
3:
86+
# Only shown when undeployed-view-mode is TOGGLEABLE; hidden in VISIBLE / HIDDEN modes.
87+
icon: SPYGLASS
88+
title: challenges.gui.buttons.toggle-undeployed.name
89+
data:
90+
type: TOGGLE_UNDEPLOYED
91+
action:
92+
left:
93+
tooltip: challenges.gui.tips.click-to-toggle
8594
5:
8695
icon: IRON_BARS
8796
title: challenges.gui.buttons.free-challenges.name

src/test/java/world/bentobox/challenges/panel/user/ChallengesPanelTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,69 @@ void testIncompleteNonRepeatableNeverHidden() throws Exception {
251251
assertTrue(getFreeChallengeList(panel).contains(incomplete),
252252
"Incomplete challenge should always be visible");
253253
}
254+
255+
private void setShowUndeployed(ChallengesPanel panel, boolean value) throws Exception {
256+
Field field = ChallengesPanel.class.getDeclaredField("showUndeployed");
257+
field.setAccessible(true);
258+
field.setBoolean(panel, value);
259+
}
260+
261+
@Test
262+
@DisplayName("HIDDEN mode: undeployed challenges are removed")
263+
void testHiddenModeRemovesUndeployed() throws Exception {
264+
when(settings.isRemoveCompleteOneTimeChallenges()).thenReturn(false);
265+
when(settings.getVisibilityMode()).thenReturn(SettingsUtils.VisibilityMode.HIDDEN);
266+
267+
Challenge deployed = PanelTestHelper.createBasicChallenge("Deployed", true);
268+
Challenge undeployed = PanelTestHelper.createBasicChallenge("Undeployed", false);
269+
List<Challenge> challenges = new ArrayList<>(List.of(deployed, undeployed));
270+
when(manager.getFreeChallenges(world)).thenReturn(challenges);
271+
272+
ChallengesPanel panel = createPanel();
273+
callUpdateFreeChallengeList(panel);
274+
275+
assertTrue(getFreeChallengeList(panel).contains(deployed), "Deployed challenge stays");
276+
assertFalse(getFreeChallengeList(panel).contains(undeployed),
277+
"Undeployed challenge is hidden in HIDDEN mode");
278+
}
279+
280+
@Test
281+
@DisplayName("TOGGLEABLE mode, showing (default): undeployed challenges are visible")
282+
void testToggleableShowingKeepsUndeployed() throws Exception {
283+
when(settings.isRemoveCompleteOneTimeChallenges()).thenReturn(false);
284+
when(settings.getVisibilityMode()).thenReturn(SettingsUtils.VisibilityMode.TOGGLEABLE);
285+
286+
Challenge deployed = PanelTestHelper.createBasicChallenge("Deployed", true);
287+
Challenge undeployed = PanelTestHelper.createBasicChallenge("Undeployed", false);
288+
List<Challenge> challenges = new ArrayList<>(List.of(deployed, undeployed));
289+
when(manager.getFreeChallenges(world)).thenReturn(challenges);
290+
291+
ChallengesPanel panel = createPanel();
292+
// Default showUndeployed == true, so undeployed challenges must remain visible.
293+
callUpdateFreeChallengeList(panel);
294+
295+
assertTrue(getFreeChallengeList(panel).contains(deployed), "Deployed challenge stays");
296+
assertTrue(getFreeChallengeList(panel).contains(undeployed),
297+
"Undeployed challenge is shown in TOGGLEABLE mode when the player has not hidden them");
298+
}
299+
300+
@Test
301+
@DisplayName("TOGGLEABLE mode, hidden by player: undeployed challenges are removed")
302+
void testToggleableHiddenRemovesUndeployed() throws Exception {
303+
when(settings.isRemoveCompleteOneTimeChallenges()).thenReturn(false);
304+
when(settings.getVisibilityMode()).thenReturn(SettingsUtils.VisibilityMode.TOGGLEABLE);
305+
306+
Challenge deployed = PanelTestHelper.createBasicChallenge("Deployed", true);
307+
Challenge undeployed = PanelTestHelper.createBasicChallenge("Undeployed", false);
308+
List<Challenge> challenges = new ArrayList<>(List.of(deployed, undeployed));
309+
when(manager.getFreeChallenges(world)).thenReturn(challenges);
310+
311+
ChallengesPanel panel = createPanel();
312+
setShowUndeployed(panel, false);
313+
callUpdateFreeChallengeList(panel);
314+
315+
assertTrue(getFreeChallengeList(panel).contains(deployed), "Deployed challenge stays");
316+
assertFalse(getFreeChallengeList(panel).contains(undeployed),
317+
"Undeployed challenge is hidden in TOGGLEABLE mode once the player toggles them off");
318+
}
254319
}

0 commit comments

Comments
 (0)