Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3e61694
add WIP NumberInputDialog
supersaiyansubtlety Nov 2, 2025
63aa245
make error color configurable and several strings translatable
supersaiyansubtlety Nov 2, 2025
963b02d
display message on invalid input
supersaiyansubtlety Nov 2, 2025
577c0f9
implement step buttons
supersaiyansubtlety Nov 3, 2025
9b70f8d
play error sound on invalid input
supersaiyansubtlety Nov 3, 2025
e65202a
refactor NumberInputDialog
supersaiyansubtlety Nov 3, 2025
a851c94
eliminate ScaleChangeListener and ScaleUtil::setScaleFactor as Tracke…
supersaiyansubtlety Nov 3, 2025
cba6f4a
javadoc NumberTextField and stop returning result from tryCommit
supersaiyansubtlety Nov 3, 2025
782a213
javadoc NumberInputDialog
supersaiyansubtlety Nov 3, 2025
a0e8e3f
display the current custom scale in the custom scale menu item's text…
supersaiyansubtlety Nov 3, 2025
fa233a7
focus dialog field when opening
supersaiyansubtlety Nov 3, 2025
daa1f14
add keybinds to dialog
supersaiyansubtlety Nov 3, 2025
c45d615
allow custom error types in NumberTextField
supersaiyansubtlety Nov 3, 2025
4010c9e
use cancel.doClick() instead of dispose()
supersaiyansubtlety Nov 3, 2025
09f2fa7
minor refactor
supersaiyansubtlety Nov 4, 2025
3c6c9c0
fix not disabling submit button on invalid input
supersaiyansubtlety Nov 4, 2025
2406385
reduce max allowed scale factor to 5 (from 10)
supersaiyansubtlety Nov 4, 2025
8d9976f
add javadoc links between GuiUtil::createIntConfigRadioMenu and Numbe…
supersaiyansubtlety Nov 4, 2025
6684c0a
validate altStep input
supersaiyansubtlety Nov 4, 2025
5af704c
improve blink delay dialog explanation
supersaiyansubtlety Nov 7, 2025
4f83610
set value to min/max when stepping invalid input
supersaiyansubtlety Nov 7, 2025
3db19b3
fix shift masks for step button actions
supersaiyansubtlety Nov 7, 2025
bbc8b36
improve step param javadocs
supersaiyansubtlety Nov 7, 2025
1d08c46
remove leftover button enabling
supersaiyansubtlety Nov 7, 2025
ea54ae0
make submit the default button
supersaiyansubtlety Nov 8, 2025
b1679ac
use jspecify annotations in new classes
supersaiyansubtlety Nov 18, 2025
fa140b9
use Consumer instead of EditListener
supersaiyansubtlety Jan 26, 2026
fbd3804
extract border constants
supersaiyansubtlety Jan 26, 2026
8396615
Update enigma/src/main/resources/lang/en_us.json
supersaiyansubtlety Jan 27, 2026
35c7b22
Merge branch 'develop/2.8' into improve-number-input
supersaiyansubtlety Jan 30, 2026
296d2ff
Merge branch 'improve-number-input' of https://github.com/supersaiyan…
supersaiyansubtlety Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.quiltmc.config.api.ReflectiveConfig;
import org.quiltmc.config.api.annotations.Alias;
import org.quiltmc.config.api.annotations.Comment;
import org.quiltmc.config.api.annotations.FloatRange;
import org.quiltmc.config.api.annotations.Processor;
import org.quiltmc.config.api.annotations.SerializedNameConvention;
import org.quiltmc.config.api.metadata.NamingSchemes;
Expand Down Expand Up @@ -50,6 +51,9 @@
@SerializedNameConvention(NamingSchemes.SNAKE_CASE)
@Processor("processChange")
public final class Config extends ReflectiveConfig {
public static final float MIN_SCALE_FACTOR = 0.25f;
public static final float MAX_SCALE_FACTOR = 5.0f;

private static final String FORMAT = "toml";
private static final String FAMILY = "enigma";
private static final String THEME_FAMILY = FAMILY + "/theme";
Expand All @@ -67,6 +71,7 @@ public final class Config extends ReflectiveConfig {
@Processor("grabPossibleLanguages")
public final TrackedValue<String> language = this.value(I18n.DEFAULT_LANGUAGE);
@Comment("A float representing the current size of the UI. 1.0 represents 100% scaling.")
@FloatRange(min = MIN_SCALE_FACTOR, max = MAX_SCALE_FACTOR)
public final TrackedValue<Float> scaleFactor = this.value(1.0f);
@Comment("The maximum number of saved recent projects, for quickly reopening.")
public final TrackedValue<Integer> maxRecentProjects = this.value(10);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public final class KeyBinds {
public static final KeyBind SELECT = KeyBind.builder("select").key(KeyEvent.VK_ENTER).build();
public static final KeyBind DIALOG_SAVE = KeyBind.builder("dialog_save").key(KeyEvent.VK_ENTER).build();
public static final KeyBind MULTILINE_DIALOG_SAVE = KeyBind.builder("multiline_dialog_save").mod(InputEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_ENTER).build();
public static final KeyBind STEP_UP = KeyBind.builder("step_up").mod(KeyEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_UP).build();
public static final KeyBind ALT_STEP_UP = KeyBind.builder("alt_step_up").mod(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).key(KeyEvent.VK_UP).build();
public static final KeyBind STEP_DOWN = KeyBind.builder("step_down").mod(KeyEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_DOWN).build();
public static final KeyBind ALT_STEP_DOWN = KeyBind.builder("alt_step_down").mod(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).key(KeyEvent.VK_DOWN).build();

public static final KeyBind QUICK_FIND_DIALOG_IGNORE_CASE = KeyBind.builder("ignore_case", QUICK_FIND_DIALOG_CATEGORY).key(KeyEvent.VK_C).build();
public static final KeyBind QUICK_FIND_DIALOG_REGEX = KeyBind.builder("regex", QUICK_FIND_DIALOG_CATEGORY).key(KeyEvent.VK_R).build();
Expand Down Expand Up @@ -63,13 +67,16 @@ public final class KeyBinds {
public static final KeyBind SEARCH_METHOD = KeyBind.builder("search_method", MENU_CATEGORY).build();
public static final KeyBind SEARCH_FIELD = KeyBind.builder("search_field", MENU_CATEGORY).build();

private static final List<KeyBind> DEFAULT_KEY_BINDS = Stream.of(EXIT, SELECT, DIALOG_SAVE, MULTILINE_DIALOG_SAVE,
QUICK_FIND_DIALOG_NEXT, QUICK_FIND_DIALOG_PREVIOUS, QUICK_FIND_DIALOG_CLOSE, SEARCH_DIALOG_NEXT, SEARCH_DIALOG_PREVIOUS,
EDITOR_RENAME, EDITOR_PASTE, EDITOR_EDIT_JAVADOC, EDITOR_SHOW_INHERITANCE, EDITOR_SHOW_IMPLEMENTATIONS,
EDITOR_SHOW_CALLS, EDITOR_SHOW_CALLS_SPECIFIC, EDITOR_OPEN_ENTRY, EDITOR_OPEN_PREVIOUS, EDITOR_OPEN_NEXT,
EDITOR_TOGGLE_MAPPING, EDITOR_ZOOM_IN, EDITOR_ZOOM_OUT, EDITOR_CLOSE_TAB, EDITOR_RELOAD_CLASS,
EDITOR_QUICK_FIND, EDITOR_SHOW_STRUCTURE, EDITOR_SEARCH_STRUCTURE, SAVE_MAPPINGS, DROP_MAPPINGS,
RELOAD_MAPPINGS, RELOAD_ALL, MAPPING_STATS, SEARCH, SEARCH_ALL, SEARCH_CLASS, SEARCH_METHOD, SEARCH_FIELD).map(KeyBind::toImmutable).toList();
private static final List<KeyBind> DEFAULT_KEY_BINDS = Stream.of(
EXIT, SELECT, DIALOG_SAVE, MULTILINE_DIALOG_SAVE, STEP_UP, ALT_STEP_UP, STEP_DOWN, ALT_STEP_DOWN,
QUICK_FIND_DIALOG_NEXT, QUICK_FIND_DIALOG_PREVIOUS, QUICK_FIND_DIALOG_CLOSE, SEARCH_DIALOG_NEXT,
SEARCH_DIALOG_PREVIOUS, EDITOR_RENAME, EDITOR_PASTE, EDITOR_EDIT_JAVADOC, EDITOR_SHOW_INHERITANCE,
EDITOR_SHOW_IMPLEMENTATIONS, EDITOR_SHOW_CALLS, EDITOR_SHOW_CALLS_SPECIFIC, EDITOR_OPEN_ENTRY,
EDITOR_OPEN_PREVIOUS, EDITOR_OPEN_NEXT, EDITOR_TOGGLE_MAPPING, EDITOR_ZOOM_IN, EDITOR_ZOOM_OUT,
EDITOR_CLOSE_TAB, EDITOR_RELOAD_CLASS, EDITOR_QUICK_FIND, EDITOR_SHOW_STRUCTURE, EDITOR_SEARCH_STRUCTURE,
SAVE_MAPPINGS, DROP_MAPPINGS, RELOAD_MAPPINGS, RELOAD_ALL, MAPPING_STATS, SEARCH, SEARCH_ALL, SEARCH_CLASS,
SEARCH_METHOD, SEARCH_FIELD
).map(KeyBind::toImmutable).toList();

private static final List<KeyBind> CONFIGURABLE_KEY_BINDS = List.of(EDITOR_RENAME, EDITOR_PASTE, EDITOR_EDIT_JAVADOC,
EDITOR_SHOW_INHERITANCE, EDITOR_SHOW_IMPLEMENTATIONS, EDITOR_SHOW_CALLS, EDITOR_SHOW_CALLS_SPECIFIC,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ public static final class Colors implements Consumer<Config.SectionBuilder> {
public final TrackedValue<ThemeProperties.SerializableColor> text;
public final TrackedValue<ThemeProperties.SerializableColor> debugToken;
public final TrackedValue<ThemeProperties.SerializableColor> debugTokenOutline;

public final TrackedValue<ThemeProperties.SerializableColor> dockHighlight;
public final TrackedValue<ThemeProperties.SerializableColor> error;

private Colors(
ThemeProperties.SerializableColor lineNumbersForeground,
Expand Down Expand Up @@ -107,7 +109,9 @@ private Colors(
ThemeProperties.SerializableColor text,
ThemeProperties.SerializableColor debugToken,
ThemeProperties.SerializableColor debugTokenOutline,
ThemeProperties.SerializableColor dockHighlight

ThemeProperties.SerializableColor dockHighlight,
ThemeProperties.SerializableColor error
) {
this.lineNumbersForeground = TrackedValue.create(lineNumbersForeground, "line_numbers_foreground");
this.lineNumbersBackground = TrackedValue.create(lineNumbersBackground, "line_numbers_background");
Expand Down Expand Up @@ -142,7 +146,9 @@ private Colors(
this.text = TrackedValue.create(text, "text");
this.debugToken = TrackedValue.create(debugToken, "debug_token");
this.debugTokenOutline = TrackedValue.create(debugTokenOutline, "debug_token_outline");

this.dockHighlight = TrackedValue.create(dockHighlight, "dock_highlight");
this.error = TrackedValue.create(error, "error");
}

public void configure() {
Expand Down Expand Up @@ -183,7 +189,10 @@ public Stream<TrackedValue<ThemeProperties.SerializableColor>> stream() {
this.text,

this.debugToken,
this.debugTokenOutline
this.debugTokenOutline,

this.dockHighlight,
this.error
);
}

Expand Down Expand Up @@ -226,7 +235,9 @@ public static class Builder {
private ThemeProperties.SerializableColor text = new ThemeProperties.SerializableColor(0xFF000000);
private ThemeProperties.SerializableColor debugToken = new ThemeProperties.SerializableColor(0xFFD9BEF9);
private ThemeProperties.SerializableColor debugTokenOutline = new ThemeProperties.SerializableColor(0xFFBD93F9);

private ThemeProperties.SerializableColor dockHighlight = new ThemeProperties.SerializableColor(0xFF0000FF);
private ThemeProperties.SerializableColor error = new ThemeProperties.SerializableColor(0xFFFF0000);

public Colors build() {
return new Colors(
Expand Down Expand Up @@ -263,7 +274,9 @@ public Colors build() {
this.text,
this.debugToken,
this.debugTokenOutline,
this.dockHighlight

this.dockHighlight,
this.error
);
}

Expand Down Expand Up @@ -406,6 +419,11 @@ public Builder dockHighlight(ThemeProperties.SerializableColor dockHighlight) {
this.dockHighlight = dockHighlight;
return this;
}

public Builder error(ThemeProperties.SerializableColor error) {
this.error = error;
return this;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

import org.quiltmc.config.api.values.TrackedValue;
import org.quiltmc.enigma.gui.Gui;
import org.quiltmc.enigma.gui.config.keybind.KeyBinds;
import org.quiltmc.enigma.gui.util.NumberInputDialog;
import org.quiltmc.enigma.gui.element.menu_bar.ConventionalSearchableElement;
import org.quiltmc.enigma.gui.element.menu_bar.Retranslatable;
import org.quiltmc.enigma.util.I18n;
import org.quiltmc.enigma.util.Utils;

import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import java.util.Optional;

public class IntRangeConfigMenuItem extends JMenuItem implements ConventionalSearchableElement, Retranslatable {
public static final String DIALOG_TITLE_TRANSLATION_KEY_SUFFIX = ".dialog_title";
Expand All @@ -29,6 +28,10 @@ public class IntRangeConfigMenuItem extends JMenuItem implements ConventionalSea
* this should coincide with any minimum imposed on the passed {@code config}
* @param max the maximum allowed value
* this should coincide with any maximum imposed on the passed {@code config}
* @param defaultStep the amount to step the value by when the user clicks step up/down buttons;
* must be positive
* @param altStep the amount to step the value by when the user inputs {@link KeyBinds#ALT_STEP_UP} or
* {@link KeyBinds#ALT_STEP_DOWN}; must be positive
* @param rootTranslationKey a translation key for deriving translations as follows:
* <ul>
* <li> this component's text: the unmodified key
Expand All @@ -38,33 +41,38 @@ public class IntRangeConfigMenuItem extends JMenuItem implements ConventionalSea
* {@value #DIALOG_EXPLANATION_TRANSLATION_KEY_SUFFIX} appended
* </ul>
*/
public IntRangeConfigMenuItem(Gui gui, TrackedValue<Integer> config, int min, int max, String rootTranslationKey) {
public IntRangeConfigMenuItem(
Gui gui, TrackedValue<Integer> config,
int min, int max, int defaultStep, int altStep,
String rootTranslationKey
) {
this(
gui, config, min, max, rootTranslationKey,
gui, config, min, max, defaultStep, altStep, rootTranslationKey,
rootTranslationKey + DIALOG_TITLE_TRANSLATION_KEY_SUFFIX,
rootTranslationKey + DIALOG_EXPLANATION_TRANSLATION_KEY_SUFFIX
);
}

private IntRangeConfigMenuItem(
Gui gui, TrackedValue<Integer> config, int min, int max,
Gui gui, TrackedValue<Integer> config, int min, int max, int defaultStep, int altStep,
String translationKey, String dialogTitleTranslationKey, String dialogExplanationTranslationKey
) {
this.config = config;
this.translationKey = translationKey;

this.addActionListener(e ->
getRangedIntInput(
gui, config.value(), min, max,
I18n.translate(dialogTitleTranslationKey),
I18n.translate(dialogExplanationTranslationKey)
)
.ifPresent(input -> {
if (!input.equals(config.value())) {
config.setValue(input);
}
})
);
this.addActionListener(e -> {
final String title = I18n.translate(dialogTitleTranslationKey);
final String message = I18n.translate(dialogExplanationTranslationKey) + "\n"
+ I18n.translateFormatted("prompt.input.int_range", min, max);
final int input = NumberInputDialog.promptInt(
gui.getFrame(), config.value(), min, max, defaultStep, altStep,
title, message, I18n.translate("prompt.save")
);

if (!config.value().equals(input)) {
config.setValue(input);
}
});

config.registerCallback(updated -> {
this.retranslate();
Expand All @@ -77,28 +85,6 @@ public void retranslate() {
this.setText(I18n.translateFormatted(this.translationKey, this.config.value()));
}

private static Optional<Integer> getRangedIntInput(
Gui gui, int initialValue, int min, int max, String title, String explanation
) {
final String prompt = I18n.translateFormatted("prompt.input.int_range", min, max);
final String input = (String) JOptionPane.showInputDialog(
gui.getFrame(),
explanation + "\n" + prompt,
title,
JOptionPane.QUESTION_MESSAGE, null, null, initialValue
);

if (input != null) {
try {
return Optional.of(Utils.clamp(Integer.parseInt(input), min, max));
} catch (NumberFormatException e) {
return Optional.empty();
}
} else {
return Optional.empty();
}
}

@Override
public String getAliasesTranslationKeyPrefix() {
return this.translationKey;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
import org.quiltmc.enigma.gui.Gui;
import org.quiltmc.enigma.gui.config.Config;
import org.quiltmc.enigma.gui.dialog.ChangeDialog;
import org.quiltmc.enigma.gui.util.NumberInputDialog;
import org.quiltmc.enigma.gui.element.menu_bar.AbstractSearchableEnigmaMenu;
import org.quiltmc.enigma.gui.util.ScaleUtil;
import org.quiltmc.enigma.util.I18n;

import javax.swing.ButtonGroup;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.stream.IntStream;

public class ScaleMenu extends AbstractSearchableEnigmaMenu {
private static final String TRANSLATION_KEY = "menu.view.scale";

private static final float PERCENT_FACTOR = 100;
public static final float MAX_SCALE_PERCENT = Config.MAX_SCALE_FACTOR * PERCENT_FACTOR;
public static final float MIN_SCALE_PERCENT = Config.MIN_SCALE_FACTOR * PERCENT_FACTOR;

private final int[] defaultOptions = {100, 125, 150, 175, 200};
private final ButtonGroup optionsGroup = new ButtonGroup();
private final Map<Float, JRadioButtonMenuItem> options = new HashMap<>();
Expand All @@ -46,48 +48,68 @@ protected ScaleMenu(Gui gui) {
// note: as of refactoring this code, there is no other path which updates scale
// this code is therefore currently pointless
// and exists only for a possible future in which some other code path is updating scale *without* calling Gui#updateUiState
ScaleUtil.addListener((newScale, oldScale) -> this.updateState());
Config.main().scaleFactor.registerCallback(updated -> this.updateState());
}

@Override
public void retranslate() {
this.setText(I18n.translate(TRANSLATION_KEY));

this.customScaleButton.setText(I18n.translate("menu.view.scale.custom"));
this.retranslateCustomButton();
this.forEachDefaultScaleOption((scaleFactor, realFactor) -> this.options.get(realFactor).setText(String.format("%d%%", scaleFactor)));
}

@Override
public void updateState(boolean jarOpen, ConnectionState state) {
JRadioButtonMenuItem option = this.options.get(Config.main().scaleFactor.value());
Objects.requireNonNullElse(option, this.customScaleButton).setSelected(true);
final JRadioButtonMenuItem option = this.options.get(Config.main().scaleFactor.value());
if (option == null) {
this.customScaleButton.setSelected(true);
this.retranslateCustomButton();
} else {
final boolean wasCustom = this.customScaleButton.isSelected();

option.setSelected(true);

if (wasCustom) {
this.retranslateCustomButton();
}
}
}

private void retranslateCustomButton() {
final String text = this.customScaleButton.isSelected()
? I18n.translateFormatted(
"menu.view.scale.custom.selected",
Config.main().scaleFactor.value() * PERCENT_FACTOR
)
: I18n.translate("menu.view.scale.custom");

this.customScaleButton.setText(text);
}

private void onScaleClicked(float realScale) {
ScaleUtil.setScaleFactor(realScale);
Config.main().scaleFactor.setValue(realScale);
ChangeDialog.show(this.gui.getFrame());
}

private void onCustomScaleClicked() {
String answer = (String) JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("menu.view.scale.custom.title"), I18n.translate("menu.view.scale.custom.title"),
JOptionPane.QUESTION_MESSAGE, null, null, Double.toString(Config.main().scaleFactor.value() * 100));

if (answer == null) {
// cancelled
// button is considered selected, we need to go back to the old selection
this.updateState();
return;
}
final float oldScale = Config.main().scaleFactor.value();

float newScale = 1.0f;
try {
newScale = Float.parseFloat(answer) / 100f;
} catch (NumberFormatException ignored) {
// ignored!
}
final String message = I18n.translate("menu.view.scale.custom.explanation") + "\n"
+ I18n.translateFormatted("prompt.input.number_range", MIN_SCALE_PERCENT, MAX_SCALE_PERCENT);

ScaleUtil.setScaleFactor(newScale);
ChangeDialog.show(this.gui.getFrame());
final float newPercent = NumberInputDialog.promptFloat(
this.gui.getFrame(),
oldScale * PERCENT_FACTOR, MIN_SCALE_PERCENT, MAX_SCALE_PERCENT, 10, 1,
I18n.translate("menu.view.scale.custom.title"), message, I18n.translate("prompt.save")
);

final float newScale = newPercent / PERCENT_FACTOR;

if (newScale != oldScale) {
Config.main().scaleFactor.setValue(newScale);
ChangeDialog.show(this.gui.getFrame());
}

// if custom scale matches a default scale, select that instead
this.updateState();
Expand All @@ -96,7 +118,7 @@ private void onCustomScaleClicked() {
private void forEachDefaultScaleOption(BiConsumer<Integer, Float> consumer) {
IntStream.of(this.defaultOptions)
.forEach(
option -> consumer.accept(option, option / 100f)
option -> consumer.accept(option, option / PERCENT_FACTOR)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected SelectionHighlightMenu(Gui gui) {

this.blinkDelay = new IntRangeConfigMenuItem(
gui, config.blinkDelay,
SelectionHighlightSection.MIN_BLINK_DELAY, SelectionHighlightSection.MAX_BLINK_DELAY,
SelectionHighlightSection.MIN_BLINK_DELAY, SelectionHighlightSection.MAX_BLINK_DELAY, 100, 10,
"menu.view.selection_highlight.blink_delay"
);

Expand Down
Loading