Skip to content

Instantly share code, notes, and snippets.

@fourlastor
Created August 6, 2023 14:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fourlastor/d78031ecdfccbd18135de9d39661b67c to your computer and use it in GitHub Desktop.
Save fourlastor/d78031ecdfccbd18135de9d39661b67c to your computer and use it in GitHub Desktop.
Stage focus order
import com.badlogic.gdx.scenes.scene2d.Actor;
import de.golfgl.gdx.controllers.ControllerMenuStage;
import de.golfgl.gdx.controllers.IControllerScrollable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ControllerScrollableHelper implements IControllerScrollable {
private final ControllerMenuStage stage;
private final ControllerMenuStage.MoveFocusDirection mainDirection;
private final ControllerMenuStage.MoveFocusDirection crossDirection;
private final List<FocusableGroup> groups;
public static Builder builder(ControllerMenuStage stage1) {
return new Builder(stage1);
}
private ControllerScrollableHelper(
ControllerMenuStage stage,
ControllerMenuStage.MoveFocusDirection mainDirection,
ControllerMenuStage.MoveFocusDirection crossDirection,
List<FocusableGroup> groups) {
this.stage = stage;
this.mainDirection = mainDirection;
this.crossDirection = crossDirection;
this.groups = groups;
}
@Override
public boolean onControllerScroll(ControllerMenuStage.MoveFocusDirection direction) {
Actor focusedActor = getFocusedActor(getFocusedGroup());
Actor nextFocusableActor = getNextFocusableActor(direction);
if (focusedActor == nextFocusableActor) {
return true;
} else {
return stage.setFocusedActor(nextFocusableActor);
}
}
private Actor getNextFocusableActor(ControllerMenuStage.MoveFocusDirection direction) {
ControllerMenuStage.MoveFocusDirection opposite = opposite(direction);
if (direction == mainDirection || opposite == mainDirection) {
return getNextFocusableActor(direction == mainDirection);
}
return getNextFocusableActorInGroup(direction == crossDirection);
}
private Actor getNextFocusableActorInGroup(boolean next) {
FocusableGroup focusedGroup = getFocusedGroup();
int groupIndex = groups.indexOf(focusedGroup);
if (next) {
if (groupIndex < groups.size() - 1) {
return groups.get(groupIndex + 1).actors.get(0);
}
} else {
if (groupIndex > 0) {
List<Actor> actors = groups.get(groupIndex - 1).actors;
return actors.get(actors.size() - 1);
}
}
return getFocusedActor(focusedGroup);
}
private Actor getNextFocusableActor(boolean next) {
FocusableGroup focusedGroup = getFocusedGroup();
int groupIndex = groups.indexOf(focusedGroup);
Actor focusedActor = getFocusedActor(focusedGroup);
int focusIndex = focusedGroup.actors.indexOf(focusedActor);
if (next) {
if (focusIndex < focusedGroup.actors.size() - 1) {
return focusedGroup.actors.get(focusIndex + 1);
} else if (groupIndex < groups.size() - 1) {
return groups.get(groupIndex + 1).actors.get(0);
}
} else {
if (focusIndex > 0) {
return focusedGroup.actors.get(focusIndex - 1);
} else if (groupIndex > 0) {
List<Actor> actors = groups.get(groupIndex - 1).actors;
return actors.get(actors.size() - 1);
}
}
return focusedActor;
}
private FocusableGroup getFocusedGroup() {
return groups.stream()
.filter(it -> it.actors.stream().anyMatch(Actor::hasKeyboardFocus))
.findFirst()
.orElseGet(() -> groups.get(0));
}
private Actor getFocusedActor(FocusableGroup focusedGroup) {
return focusedGroup.actors.stream()
.filter(Actor::hasKeyboardFocus)
.findFirst()
.orElseGet(() -> focusedGroup.actors.get(0));
}
private static ControllerMenuStage.MoveFocusDirection opposite(ControllerMenuStage.MoveFocusDirection direction) {
switch (direction) {
case west:
return ControllerMenuStage.MoveFocusDirection.east;
case north:
return ControllerMenuStage.MoveFocusDirection.south;
case east:
return ControllerMenuStage.MoveFocusDirection.west;
case south:
return ControllerMenuStage.MoveFocusDirection.north;
}
throw new IllegalArgumentException("Direction does not have an opposite" + direction);
}
private static class FocusableGroup {
private final List<Actor> actors;
private FocusableGroup(List<Actor> actors) {
this.actors = actors;
}
}
public static class Builder {
private final ControllerMenuStage stage;
private ControllerMenuStage.MoveFocusDirection mainDirection;
private ControllerMenuStage.MoveFocusDirection crossDirection;
private final List<FocusableGroup> groups = new ArrayList<>();
public Builder(ControllerMenuStage stage) {
this.stage = stage;
}
public Builder mainDirection(ControllerMenuStage.MoveFocusDirection mainDirection) {
this.mainDirection = mainDirection;
return this;
}
public Builder crossDirection(ControllerMenuStage.MoveFocusDirection crossDirection) {
this.crossDirection = crossDirection;
return this;
}
public Builder group(Actor... actors) {
if (actors.length == 0) {
throw new IllegalArgumentException("You must supply at least one actor for a group");
}
groups.add(new FocusableGroup(Arrays.asList(actors)));
return this;
}
public ControllerScrollableHelper build() {
if (mainDirection == null) {
throw new NullPointerException("mainDirection was null");
}
if (crossDirection == null) {
throw new NullPointerException("crossDirection was null");
}
if (groups.isEmpty()) {
throw new IllegalStateException("empty groups");
}
return new ControllerScrollableHelper(stage, mainDirection, crossDirection, groups);
}
}
}
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.utils.viewport.Viewport;
import de.golfgl.gdx.controllers.ControllerMenuStage;
import de.golfgl.gdx.controllers.IControllerScrollable;
/** {@link ControllerMenuStage} which first checks for possible scrollable parents. */
public class ExtendedControllerMenuStage extends ControllerMenuStage {
public ExtendedControllerMenuStage(Viewport viewport) {
super(viewport);
}
public ExtendedControllerMenuStage(Viewport viewport, Batch batch) {
super(viewport, batch);
}
protected boolean moveFocusByDirection(MoveFocusDirection direction) {
Actor actor = getFocusedActor();
if (actor == null) return false;
boolean didScroll = false;
while (actor.hasParent() && !didScroll) {
if (actor instanceof IControllerScrollable) {
IControllerScrollable focus = ((IControllerScrollable) actor);
didScroll = focus.onControllerScroll(direction);
}
actor = actor.getParent();
}
return didScroll || super.moveFocusByDirection(direction);
}
}
class Usage extends Group implements IControllerScrollable {
/*
UI:
| one | five
| two | six
| three | seven
| four | eight
one + east = five
two + east = five
two + south = three
three + north = two
six + west = four
*/
public Usage() {
helper = ControllerScrollableHelper.builder(stage)
// mainDirection goes through the actors in a group, then onto the next group
.mainDirection(ControllerMenuStage.MoveFocusDirection.south)
// crossDirection skips through groups
.crossDirection(ControllerMenuStage.MoveFocusDirection.east)
.group(one, two, three, four)
.group(five, six, seven, eighth)
.build();
}
@Override
public boolean onControllerScroll(ControllerMenuStage.MoveFocusDirection direction) {
return helper.onControllerScroll(direction);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment