Instantly share code, notes, and snippets.
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save darvil82/968ee39d48bd8a7719378b87a4e4baa6 to your computer and use it in GitHub Desktop.
A class for making menus a bit less tiresome.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.Scanner; | |
import java.util.function.Consumer; | |
import java.util.function.Function; | |
import java.util.function.Supplier; | |
/** Class for making menus less tiresome. */ | |
public class Menu { | |
public record MenuOption(int index, String info, Runnable func) { } | |
private static final Scanner scanner = new Scanner(System.in); | |
private final ArrayList<MenuOption> options = new ArrayList<>(); | |
private Consumer<String> defaultOption = null; | |
private Function<MenuOption, String> formatter = opt -> " " + opt.index() + ": " + opt.info(); | |
private String prompt = "Select an option: "; | |
private final Supplier<String> titleSupplier; | |
private int lastIndex = 0; | |
private int step = 1; | |
private boolean inMenuLoop = false; | |
private Menu(Supplier<String> titleSupplier) { | |
this.titleSupplier = titleSupplier; | |
} | |
public static Menu create(Supplier<String> titleSupplier) { | |
return new Menu(titleSupplier); | |
} | |
public static Menu create(String title) { | |
return new Menu(() -> title); | |
} | |
public static Menu create() { | |
return new Menu(null); | |
} | |
private void setLastIndex(int index) { | |
if (this.options.stream().anyMatch(opt -> opt.index == index)) | |
throw new IllegalArgumentException("index cannot be repeated"); | |
this.lastIndex = index; | |
} | |
/** | |
* Add a menu option to be displayed on the menu. | |
* @param index Number used to select the option. | |
* @param info Text displayed on the menu. | |
* @param func Function to be run when the option is selected. | |
*/ | |
public Menu addOption(int index, String info, Runnable func) { | |
this.setLastIndex(index); | |
this.options.add(new MenuOption(index, info, func)); | |
return this; | |
} | |
/** | |
* Add a menu option to be displayed on the menu. The index is automatically calculated based on the | |
* {@link #withStep(int)} specified. | |
* @param info Text displayed on the menu. | |
* @param func Function to be run when the option is selected. | |
*/ | |
public Menu addOption(String info, Runnable func) { | |
this.addOption(this.lastIndex += this.step, info, func); | |
return this; | |
} | |
/** | |
* Add a menu option to exit the menu loop. | |
* @param info Text displayed on the menu. | |
*/ | |
public Menu addExitOption(String info) { | |
return this.addOption(info, this::stopMenuLoop); | |
} | |
/** Add a menu option to exit the menu loop. */ | |
public Menu addExitOption() { | |
return this.addExitOption("Exit"); | |
} | |
/** | |
* Specify the step to use when adding options without specifying the index. | |
* @param step Step to use. | |
*/ | |
public Menu withStep(int step) { | |
this.step = step; | |
return this; | |
} | |
/** | |
* Specify the action to run when the user enters an invalid option. | |
* @param defaultAction Action to run. | |
*/ | |
public Menu withDefault(Consumer<String> defaultAction) { | |
this.defaultOption = defaultAction; | |
return this; | |
} | |
/** | |
* Specify the function to use to format the menu options. By default, the format is " {index}: {info}". | |
* @param formatter Function to use. The function receives a {@link MenuOption} and returns a {@link String}. | |
*/ | |
public Menu withFormatter(Function<MenuOption, String> formatter) { | |
this.formatter = formatter; | |
return this; | |
} | |
/** | |
* Specify the prompt to show to the user. By default, the prompt is "Select an option: ". | |
* @param prompt Prompt to show. | |
*/ | |
public Menu withPrompt(String prompt) { | |
this.prompt = prompt; | |
return this; | |
} | |
/** | |
* Start the menu loop. This shows a prompt to the user indefinitely until the menu is stopped by an option | |
* or by calling {@link #stopMenuLoop()}. | |
*/ | |
public void startMenuLoop() { | |
this.inMenuLoop = true; | |
while (this.inMenuLoop) { | |
this.showMenuPrompt(); | |
} | |
} | |
/** Show the menu to the user and wait for input. */ | |
public void showMenuPrompt() { | |
this.printMenu(); | |
if (this.prompt != null) | |
System.out.print(this.prompt); | |
final String input = Menu.scanner.nextLine().trim(); | |
try { | |
if (!this.runOption(Integer.parseInt(input))) | |
this.callDefaultOption(input); | |
} catch (NumberFormatException ignored) { | |
this.callDefaultOption(input); | |
} | |
} | |
private void printMenu() { | |
final var buff = new StringBuilder("\n"); | |
if (this.titleSupplier != null) | |
buff.append(this.titleSupplier.get()).append('\n'); | |
for (final var opt : this.options) { | |
buff.append(this.formatter.apply(opt)).append('\n'); | |
} | |
System.out.println(buff); | |
} | |
private void callDefaultOption(String value) { | |
if (this.defaultOption != null) | |
this.defaultOption.accept(value); | |
} | |
/** | |
* Run the function associated with the given index of the specified options. | |
* @param index Index of the option to run. | |
* @return True if the option was found and run, false otherwise. | |
* */ | |
public boolean runOption(int index) { | |
for (final var opt : this.options) { | |
if (opt.index == index) { | |
opt.func.run(); | |
return true; | |
} | |
} | |
return false; | |
} | |
/** Stop the menu loop. */ | |
public void stopMenuLoop() { | |
this.inMenuLoop = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment