Skip to content

Commit c97ad1d

Browse files
authored
Merge branch 'main' into ornithe
2 parents 821aaa0 + dbb63e9 commit c97ad1d

88 files changed

Lines changed: 2637 additions & 907 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gemini/config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
code_review:
2+
pull_request_opened:
3+
code_review: false

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,8 @@ language-subtag-registry
6464
# test
6565
/hmcl.json
6666
/.hmcl.json
67-
/.hmcl/
67+
/.hmcl/
68+
69+
# AI
70+
/.gemini/
71+
!/.gemini/config.yaml

.idea/copyright/HMCL.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/copyright/profiles_settings.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

HMCL/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ dependencies {
5757
implementation(project(":HMCLBoot"))
5858
implementation("libs:JFoenix")
5959
implementation(libs.twelvemonkeys.imageio.webp)
60+
implementation(libs.fxsvgimage)
6061
implementation(libs.java.info)
6162
implementation(libs.monet.fx)
6263
implementation(libs.nayuki.qrcodegen)
@@ -66,6 +67,7 @@ dependencies {
6667
}
6768

6869
embedResources(libs.authlib.injector)
70+
embedResources(libs.lwjgl.unsafe.agent)
6971
}
7072

7173
fun digest(algorithm: String, bytes: ByteArray): ByteArray = MessageDigest.getInstance(algorithm).digest(bytes)
@@ -155,6 +157,7 @@ val hmclProperties = buildList {
155157
add("hmcl.microsoft.auth.id" to microsoftAuthId)
156158
add("hmcl.curseforge.apikey" to curseForgeApiKey)
157159
add("hmcl.authlib-injector.version" to libs.authlib.injector.get().version!!)
160+
add("hmcl.lwjgl-unsafe-agent.version" to libs.lwjgl.unsafe.agent.get().version!!)
158161
}
159162

160163
val hmclPropertiesFile = layout.buildDirectory.file("hmcl.properties")

HMCL/src/main/java/com/jfoenix/controls/JFXDialog.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,13 @@
2323
import com.jfoenix.converters.DialogTransitionConverter;
2424
import com.jfoenix.effects.JFXDepthManager;
2525
import com.jfoenix.transitions.CachedTransition;
26-
import javafx.animation.Interpolator;
27-
import javafx.animation.KeyFrame;
28-
import javafx.animation.KeyValue;
29-
import javafx.animation.Timeline;
30-
import javafx.animation.Transition;
26+
import javafx.animation.*;
3127
import javafx.beans.DefaultProperty;
3228
import javafx.beans.property.BooleanProperty;
3329
import javafx.beans.property.ObjectProperty;
3430
import javafx.beans.property.ObjectPropertyBase;
3531
import javafx.beans.property.SimpleBooleanProperty;
36-
import javafx.css.CssMetaData;
37-
import javafx.css.SimpleStyleableObjectProperty;
38-
import javafx.css.Styleable;
39-
import javafx.css.StyleableObjectProperty;
40-
import javafx.css.StyleableProperty;
32+
import javafx.css.*;
4133
import javafx.event.Event;
4234
import javafx.event.EventHandler;
4335
import javafx.geometry.Pos;
@@ -379,6 +371,14 @@ private final class CenterTransition extends CachedTransition {
379371
setCycleDuration(Duration.seconds(0.4));
380372
setDelay(Duration.ZERO);
381373
}
374+
375+
@Override
376+
protected void starting() {
377+
}
378+
379+
@Override
380+
protected void stopping() {
381+
}
382382
}
383383

384384
/***************************************************************************

HMCL/src/main/java/com/jfoenix/controls/JFXRippler.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,11 @@ protected Node getMask() {
231231
*
232232
* @return the ripple radius size
233233
*/
234-
protected double computeRippleRadius() {
235-
double width2 = control.getLayoutBounds().getWidth() * control.getLayoutBounds().getWidth();
236-
double height2 = control.getLayoutBounds().getHeight() * control.getLayoutBounds().getHeight();
237-
return Math.min(Math.sqrt(width2 + height2), RIPPLE_MAX_RADIUS) * 1.1 + 5;
234+
protected double computeRippleRadius(double centerX, double centerY) {
235+
double dx = Math.max(centerX, control.getLayoutBounds().getWidth() - centerX);
236+
double dy = Math.max(centerY, control.getLayoutBounds().getHeight() - centerY);
237+
238+
return (Math.sqrt(dx * dx + dy * dy) / 0.9) + 2;
238239
}
239240

240241
protected void setOverLayBounds(Rectangle overlay) {
@@ -473,7 +474,7 @@ private Ripple(double centerX, double centerY) {
473474
super(centerX,
474475
centerY,
475476
getRipplerRadius() == Region.USE_COMPUTED_SIZE ?
476-
computeRippleRadius() : getRipplerRadius(), null);
477+
computeRippleRadius(centerX, centerY) : getRipplerRadius(), null);
477478
setCache(true);
478479
setCacheHint(CacheHint.SPEED);
479480
setCacheShape(true);
@@ -518,7 +519,7 @@ private Ripple(double centerX, double centerY) {
518519
new KeyValue(opacityProperty(),
519520
1,
520521
RIPPLE_INTERPOLATOR)
521-
), new KeyFrame(Duration.millis(900), inKeyValues));
522+
), new KeyFrame(Duration.millis(600), inKeyValues));
522523

523524
setScaleX(0);
524525
setScaleY(0);

HMCL/src/main/java/com/jfoenix/skins/JFXCustomColorPickerDialog.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,16 @@
4040
import javafx.scene.paint.Color;
4141
import javafx.stage.*;
4242
import javafx.util.Duration;
43+
import org.glavo.monetfx.ColorScheme;
4344
import org.jackhuang.hmcl.setting.StyleSheets;
45+
import org.jackhuang.hmcl.theme.Themes;
4446
import org.jackhuang.hmcl.util.StringUtils;
4547

4648
import java.util.concurrent.atomic.AtomicInteger;
4749
import java.util.regex.Pattern;
4850

51+
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
52+
4953
/**
5054
* @author Shadi Shaheen
5155
*/
@@ -78,7 +82,7 @@ public JFXCustomColorPickerDialog(Window owner) {
7882

7983
// create JFX Decorator
8084
pickerDecorator = new JFXDecorator(dialog, this, false, false, false);
81-
pickerDecorator.setOnCloseButtonAction(this::updateColor);
85+
pickerDecorator.setOnCloseButtonAction(this::close);
8286
pickerDecorator.setPickOnBounds(false);
8387
customScene = new Scene(pickerDecorator, Color.TRANSPARENT);
8488
StyleSheets.init(customScene);
@@ -153,6 +157,22 @@ public JFXCustomColorPickerDialog(Window owner) {
153157
paraTransition.play();
154158
});
155159

160+
container.getChildren().add(tabs);
161+
162+
HBox actionsHBox = new HBox();
163+
actionsHBox.getStyleClass().add("jfx-color-dialog-actions");
164+
165+
JFXButton acceptButton = new JFXButton(i18n("button.ok"));
166+
acceptButton.setOnAction(event -> updateColor());
167+
acceptButton.getStyleClass().add("jfx-color-dialog-accept");
168+
169+
JFXButton cancelButton = new JFXButton(i18n("button.cancel"));
170+
cancelButton.setOnAction(event -> close());
171+
cancelButton.getStyleClass().add("jfx-color-dialog-cancel");
172+
173+
actionsHBox.getChildren().addAll(acceptButton, cancelButton);
174+
container.getChildren().add(actionsHBox);
175+
156176
initRun = () -> {
157177
// change tabs labels font color according to the selected color
158178
pane.backgroundProperty().addListener((o, oldVal, newVal) -> {
@@ -232,7 +252,13 @@ public JFXCustomColorPickerDialog(Window owner) {
232252
hexField.focusColorProperty().bind(Bindings.createObjectBinding(() -> {
233253
return pane.getBackground().getFills().get(0).getFill();
234254
}, pane.backgroundProperty()));
235-
255+
acceptButton.textFillProperty().bind(Bindings.createObjectBinding(() -> {
256+
Color fill = (Color) pane.getBackground().getFills().get(0).getFill();
257+
return ColorScheme.newBuilder(Themes.getColorScheme())
258+
.setPrimaryColorSeed(fill)
259+
.build()
260+
.getPrimary();
261+
}, pane.backgroundProperty()));
236262

237263
((Pane) pickerDecorator.lookup(".jfx-decorator-buttons-container")).backgroundProperty()
238264
.bind(Bindings.createObjectBinding(() -> {
@@ -262,9 +288,6 @@ public JFXCustomColorPickerDialog(Window owner) {
262288
});
263289
};
264290

265-
266-
container.getChildren().add(tabs);
267-
268291
this.getChildren().add(container);
269292
this.setPadding(new Insets(0));
270293

HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.jackhuang.hmcl.util.io.JarUtils;
2626
import org.jackhuang.hmcl.util.platform.OperatingSystem;
2727

28+
import javax.swing.JOptionPane;
2829
import java.io.IOException;
2930
import java.lang.invoke.MethodHandle;
3031
import java.lang.invoke.MethodHandles;
@@ -49,6 +50,8 @@ public static void main(String[] args) {
4950
createHMCLDirectories();
5051
LOG.start(Metadata.HMCL_CURRENT_DIRECTORY.resolve("logs"));
5152

53+
checkWine();
54+
5255
setupJavaFXVMOptions();
5356

5457
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
@@ -220,6 +223,20 @@ private static void verifyJavaFX() {
220223
}
221224
}
222225

226+
private static void checkWine() {
227+
if (OperatingSystem.isRunningUnderWine()) {
228+
SwingUtils.initLookAndFeel();
229+
LOG.warning("HMCL is running under Wine or its distributions!");
230+
231+
int result = JOptionPane.showOptionDialog(null, i18n("fatal.wine_warning"), i18n("message.warning"), JOptionPane.OK_CANCEL_OPTION,
232+
JOptionPane.WARNING_MESSAGE, null, null, null);
233+
234+
if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) {
235+
exit(1);
236+
}
237+
}
238+
}
239+
223240
private static void addEnableNativeAccess() {
224241
if (JavaRuntime.CURRENT_VERSION > 21) {
225242
try {

HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,36 @@
1717
*/
1818
package org.jackhuang.hmcl;
1919

20+
import javafx.animation.KeyFrame;
21+
import javafx.animation.KeyValue;
22+
import javafx.animation.Timeline;
2023
import javafx.application.Application;
2124
import javafx.application.Platform;
2225
import javafx.beans.value.ObservableBooleanValue;
2326
import javafx.geometry.Rectangle2D;
2427
import javafx.scene.control.Alert;
2528
import javafx.scene.control.Alert.AlertType;
29+
import javafx.scene.control.Button;
2630
import javafx.scene.control.ButtonType;
2731
import javafx.scene.input.Clipboard;
2832
import javafx.scene.input.DataFormat;
2933
import javafx.stage.Screen;
3034
import javafx.stage.Stage;
35+
import javafx.util.Duration;
3136
import org.jackhuang.hmcl.setting.ConfigHolder;
3237
import org.jackhuang.hmcl.setting.SambaException;
33-
import org.jackhuang.hmcl.ui.FXUtils;
34-
import org.jackhuang.hmcl.util.FileSaver;
3538
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
3639
import org.jackhuang.hmcl.task.Schedulers;
3740
import org.jackhuang.hmcl.ui.Controllers;
41+
import org.jackhuang.hmcl.ui.FXUtils;
3842
import org.jackhuang.hmcl.upgrade.UpdateChecker;
3943
import org.jackhuang.hmcl.upgrade.UpdateHandler;
4044
import org.jackhuang.hmcl.util.CrashReporter;
45+
import org.jackhuang.hmcl.util.FileSaver;
4146
import org.jackhuang.hmcl.util.Lang;
4247
import org.jackhuang.hmcl.util.StringUtils;
4348
import org.jackhuang.hmcl.util.io.JarUtils;
44-
import org.jackhuang.hmcl.util.platform.Architecture;
45-
import org.jackhuang.hmcl.util.platform.CommandBuilder;
46-
import org.jackhuang.hmcl.util.platform.NativeUtils;
47-
import org.jackhuang.hmcl.util.platform.OperatingSystem;
48-
import org.jackhuang.hmcl.util.platform.SystemInfo;
49+
import org.jackhuang.hmcl.util.platform.*;
4950

5051
import java.io.File;
5152
import java.io.IOException;
@@ -65,8 +66,8 @@
6566

6667
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
6768
import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES;
68-
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
6969
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
70+
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
7071

7172
public final class Launcher extends Application {
7273
public static final CookieManager COOKIE_MANAGER = new CookieManager();
@@ -111,7 +112,7 @@ public void start(Stage primaryStage) {
111112
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS
112113
&& ConfigHolder.isNewlyCreated()
113114
&& System.getProperty("user.dir").startsWith("/private/var/folders/")) {
114-
if (showAlert(AlertType.WARNING, i18n("fatal.mac_app_translocation"), ButtonType.YES, ButtonType.NO) == ButtonType.NO)
115+
if (!confirmWithCountdown(AlertType.WARNING, i18n("fatal.mac_app_translocation"), 5))
115116
return;
116117
} else {
117118
checkConfigInTempDir();
@@ -182,6 +183,27 @@ private static ButtonType showAlert(AlertType alertType, String contentText, But
182183
return new Alert(alertType, contentText, buttons).showAndWait().orElse(null);
183184
}
184185

186+
private static boolean confirmWithCountdown(Alert.AlertType alertType, String contentText, int seconds) {
187+
Alert alert = new Alert(alertType, contentText, ButtonType.YES, ButtonType.NO);
188+
Button okButton = (Button) alert.getDialogPane().lookupButton(ButtonType.YES);
189+
190+
okButton.setDisable(true);
191+
192+
KeyFrame[] keyFrames = new KeyFrame[seconds + 1];
193+
for (int i = 0; i < seconds; i++) {
194+
keyFrames[i] = new KeyFrame(Duration.seconds(i),
195+
new KeyValue(okButton.textProperty(), i18n("button.ok.countdown", seconds - i)));
196+
}
197+
keyFrames[seconds] = new KeyFrame(Duration.seconds(seconds),
198+
new KeyValue(okButton.textProperty(), i18n("button.ok")),
199+
new KeyValue(okButton.disableProperty(), false));
200+
201+
Timeline timeline = new Timeline(keyFrames);
202+
alert.setOnShown(e -> timeline.play());
203+
alert.setOnCloseRequest(e -> timeline.stop());
204+
return alert.showAndWait().orElse(null) == ButtonType.YES;
205+
}
206+
185207
private static boolean isConfigInTempDir() {
186208
String configPath = ConfigHolder.configLocation().toString();
187209

@@ -221,7 +243,7 @@ private static boolean isConfigInTempDir() {
221243

222244
private static void checkConfigInTempDir() {
223245
if (ConfigHolder.isNewlyCreated() && isConfigInTempDir()
224-
&& showAlert(AlertType.WARNING, i18n("fatal.config_in_temp_dir"), ButtonType.YES, ButtonType.NO) == ButtonType.NO) {
246+
&& !confirmWithCountdown(AlertType.WARNING, i18n("fatal.config_in_temp_dir"), 5)) {
225247
EntryPoint.exit(0);
226248
}
227249
}

0 commit comments

Comments
 (0)