From f79d560605574d93c56c2ef0e8e811dd1d2e27f0 Mon Sep 17 00:00:00 2001 From: Damon Lu <59256766+WhatDamon@users.noreply.github.com> Date: Wed, 11 Feb 2026 21:28:30 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=BB=E6=AD=A2=E5=9C=A8=20JFXCustomColorPic?= =?UTF-8?q?kerDialog=20=E8=BE=93=E5=85=A5=E9=9D=9E=E6=B3=95=E9=A2=9C?= =?UTF-8?q?=E8=89=B2=E4=BF=A1=E6=81=AF=20(#5368)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Glavo --- .../skins/JFXCustomColorPickerDialog.java | 22 ++++++++ .../org/jackhuang/hmcl/util/StringUtils.java | 52 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/HMCL/src/main/java/com/jfoenix/skins/JFXCustomColorPickerDialog.java b/HMCL/src/main/java/com/jfoenix/skins/JFXCustomColorPickerDialog.java index a78ccd17c..e9f0555f8 100644 --- a/HMCL/src/main/java/com/jfoenix/skins/JFXCustomColorPickerDialog.java +++ b/HMCL/src/main/java/com/jfoenix/skins/JFXCustomColorPickerDialog.java @@ -34,14 +34,17 @@ import javafx.geometry.Insets; import javafx.geometry.Rectangle2D; import javafx.scene.Scene; import javafx.scene.control.Tab; +import javafx.scene.control.TextFormatter; import javafx.scene.input.KeyEvent; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.stage.*; import javafx.util.Duration; import org.jackhuang.hmcl.setting.StyleSheets; +import org.jackhuang.hmcl.util.StringUtils; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; /** * @author Shadi Shaheen @@ -95,14 +98,17 @@ public class JFXCustomColorPickerDialog extends StackPane { rgbField.getStyleClass().add("custom-color-field"); rgbField.setPromptText("RGB Color"); + rgbField.setTextFormatter(colorCharFormatter()); rgbField.textProperty().addListener((o, oldVal, newVal) -> updateColorFromUserInput(newVal)); hsbField.getStyleClass().add("custom-color-field"); hsbField.setPromptText("HSB Color"); + hsbField.setTextFormatter(colorCharFormatter()); hsbField.textProperty().addListener((o, oldVal, newVal) -> updateColorFromUserInput(newVal)); hexField.getStyleClass().add("custom-color-field"); hexField.setPromptText("#HEX Color"); + hexField.setTextFormatter(colorCharFormatter()); hexField.textProperty().addListener((o, oldVal, newVal) -> updateColorFromUserInput(newVal)); StackPane tabContent = new StackPane(); @@ -404,4 +410,20 @@ public class JFXCustomColorPickerDialog extends StackPane { dialog.setMinWidth(minWidth); dialog.setMinHeight(minHeight); } + + private static final Pattern COLOR_CHAR_PATTERN = Pattern.compile("[0-9a-zA-Z#(),%.\\s]*"); + + private static TextFormatter colorCharFormatter() { + return new TextFormatter<>(change -> { + if (!change.isContentChange()) return change; + + String ins = StringUtils.toHalfWidth(change.getText()); + if (!COLOR_CHAR_PATTERN.matcher(ins).matches()) return null; + String full = StringUtils.toHalfWidth(change.getControlNewText()); + long h = full.chars().filter(c -> c == '#').count(); + if (h > 1 || (h == 1 && full.indexOf('#') != 0)) return null; + change.setText(ins); + return change; + }); + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java index 26456b8f7..8ec4d9914 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java @@ -276,6 +276,58 @@ public final class StringUtils { return false; } + /// Check if the code point is a full-width character. + public static boolean isFullWidth(int codePoint) { + return codePoint >= '\uff10' && codePoint <= '\uff19' // full-width digits + || codePoint >= '\uff21' && codePoint <= '\uff3a' // full-width uppercase letters + || codePoint >= '\uff41' && codePoint <= '\uff5a' // full-width lowercase letters + || codePoint == '\uff08' // full-width left parenthesis + || codePoint == '\uff09' // full-width right parenthesis + || codePoint == '\uff0c' // full-width comma + || codePoint == '\uff05' // full-width percent sign + || codePoint == '\uff0e' // full-width period + || codePoint == '\u3000' // full-width ideographic space + || codePoint == '\uff03'; // full-width number sign + } + + /// Convert full-width characters to half-width characters. + public static String toHalfWidth(String str) { + int i = 0; + while (i < str.length()) { + int cp = str.codePointAt(i); + + if (isFullWidth(cp)) { + break; + } + + i += Character.charCount(cp); + } + + if (i == str.length()) + return str; + + var builder = new StringBuilder(str.length()); + builder.append(str, 0, i); + while (i < str.length()) { + int c = str.codePointAt(i); + + if (c >= '\uff10' && c <= '\uff19') builder.append((char) (c - 0xfee0)); + else if (c >= '\uff21' && c <= '\uff3a') builder.append((char) (c - 0xfee0)); + else if (c >= '\uff41' && c <= '\uff5a') builder.append((char) (c - 0xfee0)); + else if (c == '\uff08') builder.append('('); + else if (c == '\uff09') builder.append(')'); + else if (c == '\uff0c') builder.append(','); + else if (c == '\uff05') builder.append('%'); + else if (c == '\uff0e') builder.append('.'); + else if (c == '\u3000') builder.append(' '); + else if (c == '\uff03') builder.append('#'); + else builder.appendCodePoint(c); + + i += Character.charCount(c); + } + return builder.toString(); + } + private static boolean isVarNameStart(char ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; }