Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
474d298fd3 | ||
|
|
4052136217 | ||
|
|
3193e02867 | ||
|
|
c7122b3fff | ||
|
|
8775194d26 | ||
|
|
b3f27cc96e | ||
|
|
3313a1ed27 | ||
|
|
da66622994 | ||
|
|
446cdc32d7 | ||
|
|
b08d2b0dc0 | ||
|
|
e051c212d1 | ||
|
|
7645a600ef | ||
|
|
9e56b5cd15 | ||
|
|
dcfcd1d8b2 | ||
|
|
b2dc4b45c2 | ||
|
|
0314921728 | ||
|
|
7d339677e4 | ||
|
|
c9d259d7f1 | ||
|
|
eae2670855 | ||
|
|
e857f19b18 | ||
|
|
a11816884c | ||
|
|
f686cb0ceb | ||
|
|
5aac4b82fc | ||
|
|
99c3ef8a57 | ||
|
|
0a17aad9d6 | ||
|
|
965995ad20 | ||
|
|
a5067cb1dd | ||
|
|
1f9c0736aa | ||
|
|
a27671560d | ||
|
|
5c767deaf3 | ||
|
|
c157d4e0d1 | ||
|
|
655f42734a | ||
|
|
55b01ab101 | ||
|
|
c6ed42d3e6 | ||
|
|
0562051989 | ||
|
|
f68e056e47 | ||
|
|
457490276c | ||
|
|
49ea9e5da0 | ||
|
|
9f3fb7aff6 | ||
|
|
1033389c38 | ||
|
|
58e32c4459 | ||
|
|
936285847c | ||
|
|
fab32a6d51 | ||
|
|
5b345affec | ||
|
|
32c3b694b3 | ||
|
|
4dce13855a | ||
|
|
1ce9858893 | ||
|
|
71c9632d61 | ||
|
|
cce17edaa6 | ||
|
|
deeef8f67a | ||
|
|
25c76a748f | ||
|
|
2109df8145 | ||
|
|
ae01a49cc4 |
No files matched your search
@@ -14,11 +14,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-version: 8
|
||||
java-package: 'jdk+fx'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build --no-daemon
|
||||
|
||||
@@ -244,9 +244,9 @@ public final class LauncherHelper {
|
||||
} else if (ex instanceof PermissionException) {
|
||||
message = i18n("launch.failed.executable_permission");
|
||||
} else if (ex instanceof ProcessCreationException) {
|
||||
message = i18n("launch.failed.creating_process") + ex.getLocalizedMessage();
|
||||
message = i18n("launch.failed.creating_process") + "\n" + ex.getLocalizedMessage();
|
||||
} else if (ex instanceof NotDecompressingNativesException) {
|
||||
message = i18n("launch.failed.decompressing_natives") + ex.getLocalizedMessage();
|
||||
message = i18n("launch.failed.decompressing_natives") + "\n" + ex.getLocalizedMessage();
|
||||
} else if (ex instanceof LibraryDownloadException) {
|
||||
message = i18n("launch.failed.download_library", ((LibraryDownloadException) ex).getLibrary().getName()) + "\n";
|
||||
if (ex.getCause() instanceof ResponseCodeException) {
|
||||
@@ -881,8 +881,8 @@ public final class LauncherHelper {
|
||||
|
||||
}
|
||||
|
||||
private static final String ORACLEJDK_DOWNLOAD_LINK = "https://www.java.com/download";
|
||||
private static final String OPENJDK_DOWNLOAD_LINK = "https://docs.microsoft.com/java/openjdk/download";
|
||||
private static final String ORACLEJDK_DOWNLOAD_LINK = "https://www.java.com/download/";
|
||||
private static final String OPENJDK_DOWNLOAD_LINK = "https://learn.microsoft.com/java/openjdk/download";
|
||||
|
||||
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
|
||||
|
||||
|
||||
@@ -391,9 +391,6 @@ public final class Controllers {
|
||||
Controllers.getSettingsPage().showFeedback();
|
||||
Controllers.navigate(Controllers.getSettingsPage());
|
||||
break;
|
||||
case "hmcl://hide-announcement":
|
||||
Controllers.getRootPage().getMainPage().hideAnnouncementPane();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FXUtils.openLink(href);
|
||||
|
||||
@@ -53,7 +53,7 @@ public class CrashWindow extends Stage {
|
||||
|
||||
Button btnContact = new Button();
|
||||
btnContact.setText(i18n("launcher.contact"));
|
||||
btnContact.setOnMouseClicked(event -> FXUtils.openLink(Metadata.CONTACT_URL));
|
||||
btnContact.setOnAction(event -> FXUtils.openLink(Metadata.CONTACT_URL));
|
||||
HBox box = new HBox();
|
||||
box.setStyle("-fx-padding: 8px;");
|
||||
box.getChildren().add(btnContact);
|
||||
|
||||
@@ -23,13 +23,12 @@ import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.WeakInvalidationListener;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.beans.value.WeakChangeListener;
|
||||
import javafx.beans.value.WritableValue;
|
||||
import javafx.beans.value.*;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
@@ -54,7 +53,6 @@ import org.glavo.png.PNGWriter;
|
||||
import org.glavo.png.javafx.PNGJavaFXUtils;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
@@ -657,6 +655,61 @@ public final class FXUtils {
|
||||
comboBox.getSelectionModel().selectedIndexProperty().removeListener(listener);
|
||||
}
|
||||
|
||||
public static void bindAllEnabled(BooleanProperty allEnabled, BooleanProperty... children) {
|
||||
int itemCount = children.length;
|
||||
int childSelectedCount = 0;
|
||||
for (BooleanProperty child : children) {
|
||||
if (child.get())
|
||||
childSelectedCount++;
|
||||
}
|
||||
|
||||
allEnabled.set(childSelectedCount == itemCount);
|
||||
|
||||
class Listener implements InvalidationListener {
|
||||
private int childSelectedCount;
|
||||
private boolean updating = false;
|
||||
|
||||
public Listener(int childSelectedCount) {
|
||||
this.childSelectedCount = childSelectedCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidated(Observable observable) {
|
||||
if (updating)
|
||||
return;
|
||||
|
||||
updating = true;
|
||||
try {
|
||||
boolean value = ((BooleanProperty) observable).get();
|
||||
|
||||
if (observable == allEnabled) {
|
||||
for (BooleanProperty child : children) {
|
||||
child.setValue(value);
|
||||
}
|
||||
childSelectedCount = value ? itemCount : 0;
|
||||
} else {
|
||||
if (value)
|
||||
childSelectedCount++;
|
||||
else
|
||||
childSelectedCount--;
|
||||
|
||||
allEnabled.set(childSelectedCount == itemCount);
|
||||
}
|
||||
} finally {
|
||||
updating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InvalidationListener listener = new Listener(childSelectedCount);
|
||||
|
||||
WeakInvalidationListener weakListener = new WeakInvalidationListener(listener);
|
||||
allEnabled.addListener(listener);
|
||||
for (BooleanProperty child : children) {
|
||||
child.addListener(weakListener);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setIcon(Stage stage) {
|
||||
String icon;
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
|
||||
@@ -904,6 +957,15 @@ public final class FXUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static void onClicked(Node node, Runnable action) {
|
||||
node.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
|
||||
if (e.getButton() == MouseButton.PRIMARY && e.getClickCount() == 1) {
|
||||
action.run();
|
||||
e.consume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void copyText(String text) {
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(text);
|
||||
@@ -925,7 +987,7 @@ public final class FXUtils {
|
||||
Element r = doc.getDocumentElement();
|
||||
|
||||
NodeList children = r.getChildNodes();
|
||||
List<javafx.scene.Node> texts = new ArrayList<>();
|
||||
List<Node> texts = new ArrayList<>();
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
org.w3c.dom.Node node = children.item(i);
|
||||
|
||||
@@ -933,8 +995,8 @@ public final class FXUtils {
|
||||
Element element = (Element) node;
|
||||
if ("a".equals(element.getTagName())) {
|
||||
String href = element.getAttribute("href");
|
||||
JFXHyperlink hyperlink = new JFXHyperlink(element.getTextContent());
|
||||
hyperlink.setOnAction(e -> {
|
||||
Text text = new Text(element.getTextContent());
|
||||
onClicked(text, () -> {
|
||||
String link = href;
|
||||
try {
|
||||
link = new URI(href).toASCIIString();
|
||||
@@ -942,7 +1004,10 @@ public final class FXUtils {
|
||||
}
|
||||
hyperlinkAction.accept(link);
|
||||
});
|
||||
texts.add(hyperlink);
|
||||
text.setCursor(Cursor.HAND);
|
||||
text.setFill(Color.web("#0070E0"));
|
||||
text.setUnderline(true);
|
||||
texts.add(text);
|
||||
} else if ("b".equals(element.getTagName())) {
|
||||
Text text = new Text(element.getTextContent());
|
||||
text.getStyleClass().add("bold");
|
||||
|
||||
@@ -417,10 +417,10 @@ public class GameCrashWindow extends Stage {
|
||||
HBox toolBar = new HBox();
|
||||
{
|
||||
JFXButton exportGameCrashInfoButton = FXUtils.newRaisedButton(i18n("logwindow.export_game_crash_logs"));
|
||||
exportGameCrashInfoButton.setOnMouseClicked(e -> exportGameCrashInfo());
|
||||
exportGameCrashInfoButton.setOnAction(e -> exportGameCrashInfo());
|
||||
|
||||
JFXButton logButton = FXUtils.newRaisedButton(i18n("logwindow.title"));
|
||||
logButton.setOnMouseClicked(e -> showLogWindow());
|
||||
logButton.setOnAction(e -> showLogWindow());
|
||||
|
||||
JFXButton helpButton = FXUtils.newRaisedButton(i18n("help"));
|
||||
helpButton.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/help.html"));
|
||||
|
||||
@@ -123,7 +123,7 @@ public final class HTMLRenderer {
|
||||
if (hyperlink != null) {
|
||||
URI target = resolveLink(hyperlink);
|
||||
if (target != null) {
|
||||
text.setOnMouseClicked(event -> onClickHyperlink.accept(target));
|
||||
FXUtils.onClicked(text, () -> onClickHyperlink.accept(target));
|
||||
text.setCursor(Cursor.HAND);
|
||||
}
|
||||
text.getStyleClass().add("html-hyperlink");
|
||||
@@ -192,7 +192,7 @@ public final class HTMLRenderer {
|
||||
if (hyperlink != null) {
|
||||
URI target = resolveLink(hyperlink);
|
||||
if (target != null) {
|
||||
imageView.setOnMouseClicked(event -> onClickHyperlink.accept(target));
|
||||
FXUtils.onClicked(imageView, () -> onClickHyperlink.accept(target));
|
||||
imageView.setCursor(Cursor.HAND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
@@ -33,7 +32,7 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.*;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
@@ -60,8 +59,8 @@ public class InstallerItem extends Control {
|
||||
private final ObjectProperty<InstalledState> versionProperty = new SimpleObjectProperty<>(this, "version", null);
|
||||
private final ObjectProperty<State> resolvedStateProperty = new SimpleObjectProperty<>(this, "resolvedState", InstallableState.INSTANCE);
|
||||
|
||||
private final ObjectProperty<EventHandler<? super MouseEvent>> installActionProperty = new SimpleObjectProperty<>(this, "installAction");
|
||||
private final ObjectProperty<EventHandler<? super MouseEvent>> removeActionProperty = new SimpleObjectProperty<>(this, "removeAction");
|
||||
private final ObjectProperty<Runnable> onInstall = new SimpleObjectProperty<>(this, "onInstall");
|
||||
private final ObjectProperty<Runnable> onRemove = new SimpleObjectProperty<>(this, "onRemove");
|
||||
|
||||
public interface State {
|
||||
}
|
||||
@@ -170,12 +169,28 @@ public class InstallerItem extends Control {
|
||||
return resolvedStateProperty;
|
||||
}
|
||||
|
||||
public ObjectProperty<EventHandler<? super MouseEvent>> installActionProperty() {
|
||||
return installActionProperty;
|
||||
public ObjectProperty<Runnable> onInstallProperty() {
|
||||
return onInstall;
|
||||
}
|
||||
|
||||
public ObjectProperty<EventHandler<? super MouseEvent>> removeActionProperty() {
|
||||
return removeActionProperty;
|
||||
public Runnable getOnInstall() {
|
||||
return onInstall.get();
|
||||
}
|
||||
|
||||
public void setOnInstall(Runnable onInstall) {
|
||||
this.onInstall.set(onInstall);
|
||||
}
|
||||
|
||||
public ObjectProperty<Runnable> onRemoveProperty() {
|
||||
return onRemove;
|
||||
}
|
||||
|
||||
public Runnable getOnRemove() {
|
||||
return onRemove.get();
|
||||
}
|
||||
|
||||
public void setOnRemove(Runnable onRemove) {
|
||||
this.onRemove.set(onRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -371,7 +386,11 @@ public class InstallerItem extends Control {
|
||||
removeButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> control.resolvedStateProperty.get() instanceof InstalledState, control.resolvedStateProperty));
|
||||
}
|
||||
removeButton.managedProperty().bind(removeButton.visibleProperty());
|
||||
removeButton.onMouseClickedProperty().bind(control.removeActionProperty);
|
||||
removeButton.setOnAction(e -> {
|
||||
Runnable onRemove = control.getOnRemove();
|
||||
if (onRemove != null)
|
||||
onRemove.run();
|
||||
});
|
||||
buttonsContainer.getChildren().add(removeButton);
|
||||
|
||||
JFXButton installButton = new JFXButton();
|
||||
@@ -383,7 +402,7 @@ public class InstallerItem extends Control {
|
||||
));
|
||||
installButton.getStyleClass().add("toggle-icon4");
|
||||
installButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> {
|
||||
if (control.installActionProperty.get() == null) {
|
||||
if (control.getOnInstall() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -396,18 +415,27 @@ public class InstallerItem extends Control {
|
||||
}
|
||||
|
||||
return false;
|
||||
}, control.resolvedStateProperty, control.installActionProperty));
|
||||
}, control.resolvedStateProperty, control.onInstall));
|
||||
installButton.managedProperty().bind(installButton.visibleProperty());
|
||||
installButton.onMouseClickedProperty().bind(control.installActionProperty);
|
||||
installButton.setOnAction(e -> {
|
||||
Runnable onInstall = control.getOnInstall();
|
||||
if (onInstall != null)
|
||||
onInstall.run();
|
||||
});
|
||||
buttonsContainer.getChildren().add(installButton);
|
||||
|
||||
FXUtils.onChangeAndOperate(installButton.visibleProperty(), clickable -> {
|
||||
if (clickable) {
|
||||
container.onMouseClickedProperty().bind(control.installActionProperty);
|
||||
container.setOnMouseClicked(event -> {
|
||||
Runnable onInstall = control.getOnInstall();
|
||||
if (onInstall != null && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) {
|
||||
onInstall.run();
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
pane.setCursor(Cursor.HAND);
|
||||
} else {
|
||||
container.onMouseClickedProperty().unbind();
|
||||
container.onMouseClickedProperty().set(null);
|
||||
container.setOnMouseClicked(null);
|
||||
pane.setCursor(Cursor.DEFAULT);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ public class ListPageSkin extends SkinBase<ListPage<?>> {
|
||||
btnAdd.getStyleClass().add("jfx-button-raised-round");
|
||||
btnAdd.setButtonType(JFXButton.ButtonType.RAISED);
|
||||
btnAdd.setGraphic(SVG.PLUS.createIcon(Theme.whiteFill(), -1, -1));
|
||||
btnAdd.setOnMouseClicked(e -> skinnable.add());
|
||||
btnAdd.setOnAction(e -> skinnable.add());
|
||||
|
||||
JFXButton btnRefresh = new JFXButton();
|
||||
FXUtils.setLimitWidth(btnRefresh, 40);
|
||||
@@ -78,7 +78,7 @@ public class ListPageSkin extends SkinBase<ListPage<?>> {
|
||||
btnRefresh.getStyleClass().add("jfx-button-raised-round");
|
||||
btnRefresh.setButtonType(JFXButton.ButtonType.RAISED);
|
||||
btnRefresh.setGraphic(SVG.REFRESH.createIcon(Theme.whiteFill(), -1, -1));
|
||||
btnRefresh.setOnMouseClicked(e -> skinnable.refresh());
|
||||
btnRefresh.setOnAction(e -> skinnable.refresh());
|
||||
|
||||
vBox.getChildren().setAll(btnAdd);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.game.GameDumpGenerator;
|
||||
@@ -322,6 +323,9 @@ public final class LogWindow extends Stage {
|
||||
setGraphic(null);
|
||||
|
||||
setOnMouseClicked(event -> {
|
||||
if (event.getButton() != MouseButton.PRIMARY)
|
||||
return;
|
||||
|
||||
if (!event.isControlDown()) {
|
||||
for (ListCell<Log> logListCell : selected) {
|
||||
if (logListCell != this) {
|
||||
@@ -340,6 +344,8 @@ public final class LogWindow extends Stage {
|
||||
if (getItem() != null) {
|
||||
getItem().setSelected(true);
|
||||
}
|
||||
|
||||
event.consume();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public abstract class ToolbarListPageSkin<T extends ListPageBase<? extends Node>
|
||||
ret.textFillProperty().bind(Theme.foregroundFillBinding());
|
||||
ret.setGraphic(wrap(svg.createIcon(Theme.foregroundFillBinding(), -1, -1)));
|
||||
ret.setText(text);
|
||||
ret.setOnMouseClicked(e -> onClick.run());
|
||||
ret.setOnAction(e -> onClick.run());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public abstract class ToolbarListPageSkin<T extends ListPageBase<? extends Node>
|
||||
ret.getStyleClass().add("jfx-tool-bar-button");
|
||||
ret.setGraphic(wrap(svg.createIcon(Theme.blackFill(), -1, -1)));
|
||||
ret.setText(text);
|
||||
ret.setOnMouseClicked(e -> onClick.run());
|
||||
ret.setOnAction(e -> onClick.run());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class ToolbarListPageSkin<T extends ListPageBase<? extends Node>
|
||||
ret.textFillProperty().bind(Theme.foregroundFillBinding());
|
||||
ret.setGraphic(wrap(svg.createIcon(Theme.foregroundFillBinding(), -1, -1)));
|
||||
FXUtils.installFastTooltip(ret, tooltip);
|
||||
ret.setOnMouseClicked(e -> onClick.run());
|
||||
ret.setOnAction(e -> onClick.run());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,17 +32,14 @@ import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.CredentialExpiredException;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.auth.microsoft.MicrosoftAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.CompleteGameProfile;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.TextureType;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.DialogController;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||
import org.jackhuang.hmcl.util.skin.InvalidSkinException;
|
||||
import org.jackhuang.hmcl.util.skin.NormalizedSkin;
|
||||
@@ -128,7 +125,7 @@ public class AccountListItem extends RadioButton {
|
||||
.orElse(emptySet());
|
||||
return uploadableTextures.contains(TextureType.SKIN);
|
||||
}, profile);
|
||||
} else if (account instanceof OfflineAccount || account instanceof MicrosoftAccount) {
|
||||
} else if (account instanceof OfflineAccount || account.canUploadSkin()) {
|
||||
return createBooleanBinding(() -> true);
|
||||
} else {
|
||||
return createBooleanBinding(() -> false);
|
||||
@@ -144,11 +141,7 @@ public class AccountListItem extends RadioButton {
|
||||
Controllers.dialog(new OfflineAccountSkinPane((OfflineAccount) account));
|
||||
return null;
|
||||
}
|
||||
if (account instanceof MicrosoftAccount) {
|
||||
FXUtils.openLink("https://www.minecraft.net/msaprofile/mygames/editskin");
|
||||
return null;
|
||||
}
|
||||
if (!(account instanceof YggdrasilAccount)) {
|
||||
if (!account.canUploadSkin()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -174,7 +167,7 @@ public class AccountListItem extends RadioButton {
|
||||
NormalizedSkin skin = new NormalizedSkin(skinImg);
|
||||
String model = skin.isSlim() ? "slim" : "";
|
||||
LOG.info("Uploading skin [" + selectedFile + "], model [" + model + "]");
|
||||
((YggdrasilAccount) account).uploadSkin(model, selectedFile.toPath());
|
||||
account.uploadSkin(skin.isSlim(), selectedFile.toPath());
|
||||
})
|
||||
.thenComposeAsync(refreshAsync())
|
||||
.whenComplete(Schedulers.javafx(), e -> {
|
||||
|
||||
@@ -94,7 +94,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
JFXButton btnMove = new JFXButton();
|
||||
SpinnerPane spinnerMove = new SpinnerPane();
|
||||
spinnerMove.getStyleClass().add("small-spinner-pane");
|
||||
btnMove.setOnMouseClicked(e -> {
|
||||
btnMove.setOnAction(e -> {
|
||||
Account account = skinnable.getAccount();
|
||||
Accounts.getAccounts().remove(account);
|
||||
if (account.isPortable()) {
|
||||
@@ -129,7 +129,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
JFXButton btnRefresh = new JFXButton();
|
||||
SpinnerPane spinnerRefresh = new SpinnerPane();
|
||||
spinnerRefresh.getStyleClass().setAll("small-spinner-pane");
|
||||
btnRefresh.setOnMouseClicked(e -> {
|
||||
btnRefresh.setOnAction(e -> {
|
||||
spinnerRefresh.showSpinner();
|
||||
skinnable.refreshAsync()
|
||||
.whenComplete(Schedulers.javafx(), ex -> {
|
||||
@@ -149,7 +149,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
|
||||
JFXButton btnUpload = new JFXButton();
|
||||
SpinnerPane spinnerUpload = new SpinnerPane();
|
||||
btnUpload.setOnMouseClicked(e -> {
|
||||
btnUpload.setOnAction(e -> {
|
||||
Task<?> uploadTask = skinnable.uploadSkin();
|
||||
if (uploadTask != null) {
|
||||
spinnerUpload.showSpinner();
|
||||
@@ -170,14 +170,14 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
JFXButton btnCopyUUID = new JFXButton();
|
||||
SpinnerPane spinnerCopyUUID = new SpinnerPane();
|
||||
spinnerCopyUUID.getStyleClass().add("small-spinner-pane");
|
||||
btnCopyUUID.setOnMouseClicked(e -> FXUtils.copyText(skinnable.getAccount().getUUID().toString()));
|
||||
btnCopyUUID.setOnAction(e -> FXUtils.copyText(skinnable.getAccount().getUUID().toString()));
|
||||
btnCopyUUID.setGraphic(SVG.COPY.createIcon(Theme.blackFill(), -1, -1));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnCopyUUID, i18n("account.copy_uuid")));
|
||||
spinnerCopyUUID.setContent(btnCopyUUID);
|
||||
right.getChildren().add(spinnerCopyUUID);
|
||||
|
||||
JFXButton btnRemove = new JFXButton();
|
||||
btnRemove.setOnMouseClicked(e -> Controllers.confirm(i18n("button.remove.confirm"), i18n("button.remove"), skinnable::remove, null));
|
||||
btnRemove.setOnAction(e -> Controllers.confirm(i18n("button.remove.confirm"), i18n("button.remove"), skinnable::remove, null));
|
||||
btnRemove.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(btnRemove, Pos.CENTER);
|
||||
btnRemove.setGraphic(SVG.DELETE.createIcon(Theme.blackFill(), -1, -1));
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
@@ -95,7 +96,7 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
VBox boxMethods = new VBox();
|
||||
{
|
||||
boxMethods.getStyleClass().add("advanced-list-box-content");
|
||||
boxMethods.getChildren().add(new ClassTitle(i18n("account.create")));
|
||||
boxMethods.getChildren().add(new ClassTitle(i18n("account.create").toUpperCase(Locale.ROOT)));
|
||||
FXUtils.setLimitWidth(boxMethods, 200);
|
||||
|
||||
AdvancedListItem offlineItem = new AdvancedListItem();
|
||||
|
||||
@@ -65,6 +65,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -296,7 +297,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
hintPane.setSegment(i18n("account.methods.microsoft.hint"));
|
||||
}
|
||||
});
|
||||
hintPane.setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(hintPane, () -> {
|
||||
if (deviceCode.get() != null) {
|
||||
FXUtils.copyText(deviceCode.get().getUserCode());
|
||||
}
|
||||
@@ -637,7 +638,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
StackPane.setAlignment(cancel, Pos.BOTTOM_RIGHT);
|
||||
cancel.setOnAction(e -> latch.countDown());
|
||||
|
||||
listBox.startCategory(i18n("account.choose"));
|
||||
listBox.startCategory(i18n("account.choose").toUpperCase(Locale.ROOT));
|
||||
|
||||
setCenter(listBox);
|
||||
|
||||
@@ -657,7 +658,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
TexturesLoader.bindAvatar(portraitCanvas, service, profile.getId());
|
||||
|
||||
IconedItem accountItem = new IconedItem(portraitCanvas, profile.getName());
|
||||
accountItem.setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(accountItem, () -> {
|
||||
selectedProfile = profile;
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ public class OAuthAccountLoginDialog extends DialogPane {
|
||||
);
|
||||
}
|
||||
});
|
||||
hintPane.setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(hintPane, () -> {
|
||||
if (deviceCode.get() != null) {
|
||||
FXUtils.copyText(deviceCode.get().getUserCode());
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
@@ -42,7 +41,7 @@ public class AdvancedListItem extends Control {
|
||||
|
||||
public AdvancedListItem() {
|
||||
getStyleClass().add("advanced-list-item");
|
||||
addEventHandler(MouseEvent.MOUSE_CLICKED, e -> fireEvent(new ActionEvent()));
|
||||
FXUtils.onClicked(this, () -> fireEvent(new ActionEvent()));
|
||||
}
|
||||
|
||||
public Node getLeftGraphic() {
|
||||
|
||||
@@ -17,21 +17,36 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.TextFlow;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
public class AnnouncementCard extends VBox {
|
||||
public final class AnnouncementCard extends VBox {
|
||||
|
||||
public AnnouncementCard(String title, String content) {
|
||||
TextFlow tf = FXUtils.segmentToTextFlow(content, Controllers::onHyperlinkAction);
|
||||
public AnnouncementCard(String title, String content, Runnable onClose) {
|
||||
TextFlow body = FXUtils.segmentToTextFlow(content, Controllers::onHyperlinkAction);
|
||||
body.setLineSpacing(4);
|
||||
|
||||
Label label = new Label(title);
|
||||
label.getStyleClass().add("title");
|
||||
getChildren().setAll(label, tf);
|
||||
setSpacing(14);
|
||||
BorderPane titleBar = new BorderPane();
|
||||
titleBar.getStyleClass().add("title");
|
||||
titleBar.setLeft(new Label(title));
|
||||
|
||||
if (onClose != null) {
|
||||
Node hideNode = SVG.CLOSE.createIcon(Theme.blackFill(), 20, 20);
|
||||
hideNode.setOnMouseClicked(e -> onClose.run());
|
||||
hideNode.setCursor(Cursor.HAND);
|
||||
titleBar.setRight(hideNode);
|
||||
}
|
||||
|
||||
getChildren().setAll(titleBar, body);
|
||||
setSpacing(16);
|
||||
getStyleClass().addAll("card", "announcement");
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,6 @@ import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
@@ -139,7 +136,7 @@ final class ComponentListCell extends StackPane {
|
||||
container.getChildren().setAll(content);
|
||||
groupNode.getChildren().add(container);
|
||||
|
||||
EventHandler<Event> onExpand = e -> {
|
||||
Runnable onExpand = () -> {
|
||||
if (expandAnimation != null && expandAnimation.getStatus() == Animation.Status.RUNNING) {
|
||||
expandAnimation.stop();
|
||||
}
|
||||
@@ -182,8 +179,8 @@ final class ComponentListCell extends StackPane {
|
||||
});
|
||||
};
|
||||
|
||||
headerRippler.setOnMouseClicked(onExpand);
|
||||
expandButton.setOnAction((EventHandler<ActionEvent>) (Object) onExpand);
|
||||
FXUtils.onClicked(headerRippler, onExpand);
|
||||
expandButton.setOnAction(e -> onExpand.run());
|
||||
|
||||
expandedProperty().addListener((a, b, newValue) -> expandIcon.setRotate(newValue ? 180 : 0));
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public class FileItem extends BorderPane {
|
||||
JFXButton right = new JFXButton();
|
||||
right.setGraphic(SVG.PENCIL.createIcon(Theme.blackFill(), 15, 15));
|
||||
right.getStyleClass().add("toggle-icon4");
|
||||
right.setOnMouseClicked(e -> onExplore());
|
||||
right.setOnAction(e -> onExplore());
|
||||
FXUtils.installFastTooltip(right, i18n("button.edit"));
|
||||
setRight(right);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import static javafx.collections.FXCollections.emptyObservableList;
|
||||
import static javafx.collections.FXCollections.observableList;
|
||||
import static javafx.collections.FXCollections.singletonObservableList;
|
||||
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
|
||||
import com.jfoenix.controls.JFXComboBox;
|
||||
@@ -51,7 +52,7 @@ public class FontComboBox extends JFXComboBox<String> {
|
||||
itemsProperty().bind(BindingMapping.of(valueProperty())
|
||||
.map(value -> value == null ? emptyObservableList() : singletonObservableList(value)));
|
||||
|
||||
setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(this, () -> {
|
||||
if (loaded)
|
||||
return;
|
||||
itemsProperty().unbind();
|
||||
|
||||
@@ -30,9 +30,9 @@ public class IconedMenuItem extends IconedItem {
|
||||
getStyleClass().setAll("iconed-menu-item");
|
||||
|
||||
if (popup == null) {
|
||||
setOnMouseClicked(e -> action.run());
|
||||
FXUtils.onClicked(this, action);
|
||||
} else {
|
||||
setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(this, () -> {
|
||||
action.run();
|
||||
popup.hide();
|
||||
});
|
||||
|
||||
@@ -23,12 +23,12 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
@@ -44,8 +44,8 @@ public final class ImagePickerItem extends BorderPane {
|
||||
private final ImageView imageView;
|
||||
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||
private final ObjectProperty<EventHandler<? super MouseEvent>> onSelectButtonClicked = new SimpleObjectProperty<>(this, "onSelectButtonClicked");
|
||||
private final ObjectProperty<EventHandler<? super MouseEvent>> onDeleteButtonClicked = new SimpleObjectProperty<>(this, "onDeleteButtonClicked");
|
||||
private final ObjectProperty<EventHandler<ActionEvent>> onSelectButtonClicked = new SimpleObjectProperty<>(this, "onSelectButtonClicked");
|
||||
private final ObjectProperty<EventHandler<ActionEvent>> onDeleteButtonClicked = new SimpleObjectProperty<>(this, "onDeleteButtonClicked");
|
||||
private final ObjectProperty<Image> image = new SimpleObjectProperty<>(this, "image");
|
||||
|
||||
public ImagePickerItem() {
|
||||
@@ -55,12 +55,12 @@ public final class ImagePickerItem extends BorderPane {
|
||||
|
||||
JFXButton selectButton = new JFXButton();
|
||||
selectButton.setGraphic(SVG.PENCIL.createIcon(Theme.blackFill(), 20, 20));
|
||||
selectButton.onMouseClickedProperty().bind(onSelectButtonClicked);
|
||||
selectButton.onActionProperty().bind(onSelectButtonClicked);
|
||||
selectButton.getStyleClass().add("toggle-icon4");
|
||||
|
||||
JFXButton deleteButton = new JFXButton();
|
||||
deleteButton.setGraphic(SVG.CLOSE.createIcon(Theme.blackFill(), 20, 20));
|
||||
deleteButton.onMouseClickedProperty().bind(onDeleteButtonClicked);
|
||||
deleteButton.onActionProperty().bind(onDeleteButtonClicked);
|
||||
deleteButton.getStyleClass().add("toggle-icon4");
|
||||
|
||||
FXUtils.installFastTooltip(selectButton, i18n("button.edit"));
|
||||
@@ -93,27 +93,27 @@ public final class ImagePickerItem extends BorderPane {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public EventHandler<? super MouseEvent> getOnSelectButtonClicked() {
|
||||
public EventHandler<ActionEvent> getOnSelectButtonClicked() {
|
||||
return onSelectButtonClicked.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<EventHandler<? super MouseEvent>> onSelectButtonClickedProperty() {
|
||||
public ObjectProperty<EventHandler<ActionEvent>> onSelectButtonClickedProperty() {
|
||||
return onSelectButtonClicked;
|
||||
}
|
||||
|
||||
public void setOnSelectButtonClicked(EventHandler<? super MouseEvent> onSelectButtonClicked) {
|
||||
public void setOnSelectButtonClicked(EventHandler<ActionEvent> onSelectButtonClicked) {
|
||||
this.onSelectButtonClicked.set(onSelectButtonClicked);
|
||||
}
|
||||
|
||||
public EventHandler<? super MouseEvent> getOnDeleteButtonClicked() {
|
||||
public EventHandler<ActionEvent> getOnDeleteButtonClicked() {
|
||||
return onDeleteButtonClicked.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<EventHandler<? super MouseEvent>> onDeleteButtonClickedProperty() {
|
||||
public ObjectProperty<EventHandler<ActionEvent>> onDeleteButtonClickedProperty() {
|
||||
return onDeleteButtonClicked;
|
||||
}
|
||||
|
||||
public void setOnDeleteButtonClicked(EventHandler<? super MouseEvent> onDeleteButtonClicked) {
|
||||
public void setOnDeleteButtonClicked(EventHandler<ActionEvent> onDeleteButtonClicked) {
|
||||
this.onDeleteButtonClicked.set(onDeleteButtonClicked);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +61,7 @@ public class OptionToggleButton extends StackPane {
|
||||
toggleButton.setSize(8);
|
||||
FXUtils.setLimitHeight(toggleButton, 30);
|
||||
|
||||
container.setOnMouseClicked(e -> {
|
||||
toggleButton.setSelected(!toggleButton.isSelected());
|
||||
});
|
||||
FXUtils.onClicked(container, () -> toggleButton.setSelected(!toggleButton.isSelected()));
|
||||
|
||||
FXUtils.onChangeAndOperate(subtitleProperty(), subtitle -> {
|
||||
if (StringUtils.isNotBlank(subtitle)) {
|
||||
|
||||
@@ -32,7 +32,6 @@ import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
|
||||
@DefaultProperty("content")
|
||||
public class SpinnerPane extends Control {
|
||||
@@ -101,18 +100,19 @@ public class SpinnerPane extends Control {
|
||||
return onFailedActionProperty().get();
|
||||
}
|
||||
|
||||
private ObjectProperty<EventHandler<Event>> onFailedAction = new SimpleObjectProperty<EventHandler<Event>>(this, "onFailedAction") {
|
||||
private final ObjectProperty<EventHandler<Event>> onFailedAction = new SimpleObjectProperty<EventHandler<Event>>(this, "onFailedAction") {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
setEventHandler(FAILED_ACTION, get());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected Skin createDefaultSkin() {
|
||||
protected SkinBase<SpinnerPane> createDefaultSkin() {
|
||||
return new Skin(this);
|
||||
}
|
||||
|
||||
private static class Skin extends SkinBase<SpinnerPane> {
|
||||
private static final class Skin extends SkinBase<SpinnerPane> {
|
||||
private final JFXSpinner spinner = new JFXSpinner();
|
||||
private final StackPane contentPane = new StackPane();
|
||||
private final StackPane topPane = new StackPane();
|
||||
@@ -122,20 +122,18 @@ public class SpinnerPane extends Control {
|
||||
@SuppressWarnings("FieldCanBeLocal") // prevent from gc.
|
||||
private final InvalidationListener observer;
|
||||
|
||||
protected Skin(SpinnerPane control) {
|
||||
Skin(SpinnerPane control) {
|
||||
super(control);
|
||||
|
||||
topPane.getChildren().setAll(spinner);
|
||||
topPane.getStyleClass().add("notice-pane");
|
||||
failedPane.getStyleClass().add("notice-pane");
|
||||
failedPane.getChildren().setAll(failedReasonLabel);
|
||||
failedPane.onMouseClickedProperty().bind(
|
||||
BindingMapping.of(control.onFailedAction)
|
||||
.map(actionHandler -> (e -> {
|
||||
if (actionHandler != null) {
|
||||
actionHandler.handle(new Event(FAILED_ACTION));
|
||||
}
|
||||
})));
|
||||
FXUtils.onClicked(failedPane, () -> {
|
||||
EventHandler<Event> action = control.getOnFailedAction();
|
||||
if (action != null)
|
||||
action.handle(new Event(FAILED_ACTION));
|
||||
});
|
||||
|
||||
FXUtils.onChangeAndOperate(getSkinnable().content, newValue -> {
|
||||
if (newValue == null) {
|
||||
|
||||
@@ -30,7 +30,6 @@ import javafx.geometry.Insets;
|
||||
import javafx.geometry.Side;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.transform.Rotate;
|
||||
@@ -594,11 +593,9 @@ public class TabHeader extends Control implements TabControl, PageAware {
|
||||
|
||||
FXUtils.onChangeAndOperate(tab.selectedProperty(), selected -> inner.pseudoClassStateChanged(SELECTED_PSEUDOCLASS_STATE, selected));
|
||||
|
||||
this.setOnMouseClicked(event -> {
|
||||
if (event.getButton() == MouseButton.PRIMARY) {
|
||||
this.setOpacity(1);
|
||||
getSkinnable().getSelectionModel().select(tab);
|
||||
}
|
||||
FXUtils.onClicked(this, () -> {
|
||||
this.setOpacity(1);
|
||||
getSkinnable().getSelectionModel().select(tab);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import javafx.scene.Node;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
@@ -144,6 +146,18 @@ public class DecoratorController {
|
||||
|
||||
// press ESC to go back
|
||||
onEscPressed(navigator, this::back);
|
||||
|
||||
try {
|
||||
// For JavaFX 12+
|
||||
MouseButton button = MouseButton.valueOf("BACK");
|
||||
navigator.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> {
|
||||
if (e.getButton() == button) {
|
||||
back();
|
||||
e.consume();
|
||||
}
|
||||
});
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public Decorator getDecorator() {
|
||||
|
||||
@@ -59,7 +59,7 @@ class AdditionalInstallersPage extends InstallersPage {
|
||||
for (InstallerItem library : group.getLibraries()) {
|
||||
String libraryId = library.getLibraryId();
|
||||
if (libraryId.equals("game")) continue;
|
||||
library.removeActionProperty().set(e -> {
|
||||
library.setOnRemove(() -> {
|
||||
controller.getSettings().put(libraryId, new UpdateInstallerWizardProvider.RemoveVersionAction(libraryId));
|
||||
reload();
|
||||
});
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -101,7 +102,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
|
||||
{
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.startCategory(i18n("download.game"))
|
||||
.startCategory(i18n("download.game").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(item -> {
|
||||
item.setTitle(i18n("game"));
|
||||
item.setLeftGraphic(wrap(SVG.GAMEPAD));
|
||||
@@ -114,7 +115,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(modpackTab));
|
||||
settingsItem.setOnAction(e -> tab.select(modpackTab));
|
||||
})
|
||||
.startCategory(i18n("download.content"))
|
||||
.startCategory(i18n("download.content").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(item -> {
|
||||
item.setTitle(i18n("mods"));
|
||||
item.setLeftGraphic(wrap(SVG.PUZZLE));
|
||||
|
||||
@@ -69,7 +69,7 @@ public class InstallersPage extends Control implements WizardPage {
|
||||
for (InstallerItem library : group.getLibraries()) {
|
||||
String libraryId = library.getLibraryId();
|
||||
if (libraryId.equals(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId())) continue;
|
||||
library.installActionProperty().set(e -> {
|
||||
library.setOnInstall(() -> {
|
||||
if (LibraryAnalyzer.LibraryType.FABRIC_API.getPatchId().equals(libraryId)) {
|
||||
Controllers.dialog(i18n("install.installer.fabric-api.warning"), i18n("message.warning"), MessageDialogPane.MessageType.WARNING);
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class InstallersPage extends Control implements WizardPage {
|
||||
if (!(library.resolvedStateProperty().get() instanceof InstallerItem.IncompatibleState))
|
||||
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> controller.onPrev(false)));
|
||||
});
|
||||
library.removeActionProperty().set(e -> {
|
||||
library.setOnRemove(() -> {
|
||||
controller.getSettings().remove(libraryId);
|
||||
reload();
|
||||
});
|
||||
@@ -222,7 +222,7 @@ public class InstallersPage extends Control implements WizardPage {
|
||||
installButton.disableProperty().bind(control.installable.not());
|
||||
installButton.setPrefWidth(100);
|
||||
installButton.setPrefHeight(40);
|
||||
installButton.setOnMouseClicked(e -> control.onInstall());
|
||||
installButton.setOnAction(e -> control.onInstall());
|
||||
BorderPane.setAlignment(installButton, Pos.CENTER_RIGHT);
|
||||
root.setBottom(installButton);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
@@ -79,6 +80,8 @@ public final class LocalModpackPage extends ModpackPage {
|
||||
});
|
||||
}
|
||||
|
||||
btnDescription.setVisible(false);
|
||||
|
||||
File selectedFile;
|
||||
Optional<File> filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class);
|
||||
if (filePath.isPresent()) {
|
||||
@@ -134,6 +137,8 @@ public final class LocalModpackPage extends ModpackPage {
|
||||
// trim: https://github.com/HMCL-dev/HMCL/issues/962
|
||||
txtModpackName.setText(manifest.getName().trim());
|
||||
}
|
||||
|
||||
btnDescription.setVisible(StringUtils.isNotBlank(manifest.getDescription()));
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public abstract class ModpackPage extends SpinnerPane implements WizardPage {
|
||||
protected final Label lblAuthor;
|
||||
protected final JFXTextField txtModpackName;
|
||||
protected final JFXButton btnInstall;
|
||||
protected final JFXButton btnDescription;
|
||||
|
||||
protected ModpackPage(WizardController controller) {
|
||||
this.controller = controller;
|
||||
@@ -77,7 +78,7 @@ public abstract class ModpackPage extends SpinnerPane implements WizardPage {
|
||||
|
||||
BorderPane descriptionPane = new BorderPane();
|
||||
{
|
||||
JFXButton btnDescription = new JFXButton(i18n("modpack.description"));
|
||||
btnDescription = new JFXButton(i18n("modpack.description"));
|
||||
btnDescription.getStyleClass().add("jfx-button-border");
|
||||
btnDescription.setOnAction(e -> onDescribe());
|
||||
descriptionPane.setLeft(btnDescription);
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@@ -69,6 +70,8 @@ public final class RemoteModpackPage extends ModpackPage {
|
||||
new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().versionIdConflicts(str)),
|
||||
new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId));
|
||||
}
|
||||
|
||||
btnDescription.setVisible(StringUtils.isNotBlank(manifest.getDescription()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -95,7 +95,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
HintPane hintPane = new HintPane();
|
||||
hintPane.setText(i18n("sponsor.bmclapi"));
|
||||
hintPane.getStyleClass().add("sponsor-pane");
|
||||
hintPane.setOnMouseClicked(e -> onSponsor());
|
||||
FXUtils.onClicked(hintPane, this::onSponsor);
|
||||
BorderPane.setMargin(hintPane, new Insets(10, 10, 0, 10));
|
||||
this.setTop(hintPane);
|
||||
|
||||
@@ -146,7 +146,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
failedPane.getStyleClass().add("notice-pane");
|
||||
{
|
||||
Label label = new Label(i18n("download.failed.refresh"));
|
||||
label.setOnMouseClicked(e -> onRefresh());
|
||||
FXUtils.onClicked(label, this::onRefresh);
|
||||
|
||||
failedPane.getChildren().setAll(label);
|
||||
}
|
||||
@@ -155,7 +155,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
emptyPane.getStyleClass().add("notice-pane");
|
||||
{
|
||||
Label label = new Label(i18n("download.failed.empty"));
|
||||
label.setOnMouseClicked(e -> onBack());
|
||||
FXUtils.onClicked(label, this::onBack);
|
||||
|
||||
emptyPane.getChildren().setAll(label);
|
||||
}
|
||||
@@ -180,7 +180,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
Holder<RemoteVersionListCell> lastCell = new Holder<>();
|
||||
list.setCellFactory(listView -> new RemoteVersionListCell(lastCell));
|
||||
|
||||
list.setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(list, () -> {
|
||||
if (list.getSelectionModel().getSelectedIndex() < 0)
|
||||
return;
|
||||
navigation.getSettings().put(libraryId, list.getSelectionModel().getSelectedItem());
|
||||
|
||||
@@ -21,12 +21,10 @@ import com.jfoenix.controls.*;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
@@ -78,7 +76,6 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
||||
private final SimpleStringProperty authlibInjectorServer = new SimpleStringProperty();
|
||||
private final SimpleStringProperty launchArguments = new SimpleStringProperty("");
|
||||
private final SimpleStringProperty javaArguments = new SimpleStringProperty("");
|
||||
private final ObjectProperty<EventHandler<? super MouseEvent>> next = new SimpleObjectProperty<>();
|
||||
private final SimpleStringProperty mcbbsThreadId = new SimpleStringProperty("");
|
||||
|
||||
public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) {
|
||||
@@ -97,8 +94,6 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
||||
javaArguments.set(versionSetting.getJavaArgs());
|
||||
|
||||
canIncludeLauncher = JarUtils.thisJarPath() != null;
|
||||
|
||||
next.set(e -> onNext());
|
||||
}
|
||||
|
||||
private void onNext() {
|
||||
@@ -178,9 +173,7 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
||||
|
||||
if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_SERVER) {
|
||||
Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server"));
|
||||
hyperlink.setOnMouseClicked(e -> {
|
||||
FXUtils.openLink("https://docs.hmcl.net/modpack/serverpack.html");
|
||||
});
|
||||
hyperlink.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/modpack/serverpack.html"));
|
||||
borderPane.setTop(hyperlink);
|
||||
} else {
|
||||
HintPane pane = new HintPane(MessageDialogPane.MessageType.INFO);
|
||||
@@ -378,7 +371,7 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
||||
borderPane.setBottom(hbox);
|
||||
|
||||
JFXButton nextButton = FXUtils.newRaisedButton(i18n("wizard.next"));
|
||||
nextButton.onMouseClickedProperty().bind(skinnable.next);
|
||||
nextButton.setOnAction(e -> skinnable.onNext());
|
||||
nextButton.setPrefWidth(100);
|
||||
nextButton.setPrefHeight(40);
|
||||
nextButton.disableProperty().bind(
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jackhuang.hmcl.ui.main;
|
||||
|
||||
import com.jfoenix.controls.*;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
@@ -100,7 +99,7 @@ public class DownloadSettingsPage extends StackPane {
|
||||
downloadSource.getChildren().setAll(chooseWrapper, versionListSourcePane, downloadSourcePane);
|
||||
}
|
||||
|
||||
content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("settings.launcher.version_list_source")), downloadSource);
|
||||
content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("settings.launcher.download_source")), downloadSource);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -204,7 +203,6 @@ public class DownloadSettingsPage extends StackPane {
|
||||
Label host = new Label(i18n("settings.launcher.proxy.host"));
|
||||
GridPane.setRowIndex(host, 1);
|
||||
GridPane.setColumnIndex(host, 0);
|
||||
GridPane.setHalignment(host, HPos.RIGHT);
|
||||
gridPane.getChildren().add(host);
|
||||
}
|
||||
|
||||
@@ -220,7 +218,6 @@ public class DownloadSettingsPage extends StackPane {
|
||||
Label port = new Label(i18n("settings.launcher.proxy.port"));
|
||||
GridPane.setRowIndex(port, 2);
|
||||
GridPane.setColumnIndex(port, 0);
|
||||
GridPane.setHalignment(port, HPos.RIGHT);
|
||||
gridPane.getChildren().add(port);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.jfoenix.controls.*;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.Label;
|
||||
@@ -46,7 +45,6 @@ import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
|
||||
import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
@@ -56,6 +54,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.util.Lang.resolveException;
|
||||
@@ -118,6 +117,8 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
button.setUserData(version);
|
||||
vbox.getChildren().add(button);
|
||||
toggleGroup.getToggles().add(button);
|
||||
if (JavaManager.REPOSITORY.isInstalled(platform, version))
|
||||
button.setDisable(true);
|
||||
}
|
||||
|
||||
setBody(vbox);
|
||||
@@ -166,19 +167,6 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
}
|
||||
|
||||
private final class DownloadDiscoJava extends JFXDialogLayout {
|
||||
|
||||
private boolean isLTS(int major) {
|
||||
if (major <= 8) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (major < 21) {
|
||||
return major == 11 || major == 17;
|
||||
}
|
||||
|
||||
return major % 4 == 1;
|
||||
}
|
||||
|
||||
private final JFXComboBox<DiscoJavaDistribution> distributionBox;
|
||||
private final JFXComboBox<DiscoJavaRemoteVersion> remoteVersionBox;
|
||||
private final JFXComboBox<JavaPackageType> packageTypeBox;
|
||||
@@ -189,11 +177,8 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
|
||||
private final DownloadProvider downloadProvider = DownloadProviders.getDownloadProvider();
|
||||
|
||||
private final ObjectProperty<DiscoJavaVersionList> currentDiscoJavaVersionList = new SimpleObjectProperty<>();
|
||||
|
||||
private final Map<Pair<DiscoJavaDistribution, JavaPackageType>, DiscoJavaVersionList> javaVersionLists = new HashMap<>();
|
||||
|
||||
private boolean changingDistribution = false;
|
||||
private final Map<DiscoJavaDistribution, DiscoJavaVersionList> javaVersionLists = new HashMap<>();
|
||||
private final ObjectProperty<DiscoJavaVersionList> currentJavaVersionList = new SimpleObjectProperty<>();
|
||||
|
||||
DownloadDiscoJava() {
|
||||
assert !distributions.isEmpty();
|
||||
@@ -204,7 +189,7 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
this.remoteVersionBox = new JFXComboBox<>();
|
||||
this.remoteVersionBox.setConverter(FXUtils.stringConverter(JavaRemoteVersion::getDistributionVersion));
|
||||
|
||||
this.packageTypeBox = new JFXComboBox<>();
|
||||
this.packageTypeBox = new JFXComboBox<>(FXCollections.observableArrayList());
|
||||
|
||||
this.downloadButton = new JFXButton(i18n("download"));
|
||||
downloadButton.setOnAction(e -> onDownload());
|
||||
@@ -227,66 +212,91 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
body.addRow(2, new Label(i18n("java.download.packageType")), packageTypeBox);
|
||||
|
||||
distributionBox.setItems(FXCollections.observableList(new ArrayList<>(distributions)));
|
||||
ChangeListener<DiscoJavaVersionList.Status> updateStatusListener = (observable, oldValue, newValue) -> updateStatus(newValue);
|
||||
this.currentDiscoJavaVersionList.addListener((observable, oldValue, newValue) -> {
|
||||
if (oldValue != null) {
|
||||
oldValue.status.removeListener(updateStatusListener);
|
||||
|
||||
FXUtils.onChange(packageTypeBox.getSelectionModel().selectedItemProperty(), packageType -> {
|
||||
ObservableList<DiscoJavaRemoteVersion> versions;
|
||||
if (packageType == null
|
||||
|| currentJavaVersionList.get() == null
|
||||
|| (versions = currentJavaVersionList.get().versions.get(packageType)) == null) {
|
||||
remoteVersionBox.setItems(null);
|
||||
return;
|
||||
}
|
||||
|
||||
remoteVersionBox.setItems(versions);
|
||||
|
||||
for (int i = 0; i < versions.size(); i++) {
|
||||
DiscoJavaRemoteVersion version = versions.get(i);
|
||||
if (version.getJdkVersion() == GameJavaVersion.LATEST.getMajorVersion()) {
|
||||
remoteVersionBox.getSelectionModel().select(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < versions.size(); i++) {
|
||||
DiscoJavaRemoteVersion version = versions.get(i);
|
||||
if (version.isLTS()) {
|
||||
remoteVersionBox.getSelectionModel().select(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
remoteVersionBox.getSelectionModel().selectFirst();
|
||||
});
|
||||
|
||||
Consumer<DiscoJavaVersionList> updateListStatus = list -> {
|
||||
remoteVersionBox.setItems(null);
|
||||
packageTypeBox.getItems().clear();
|
||||
remoteVersionBox.setDisable(true);
|
||||
packageTypeBox.setDisable(true);
|
||||
warningLabel.setText(null);
|
||||
|
||||
if (list == null || (list.versions != null && list.versions.isEmpty()))
|
||||
downloadButtonPane.getChildren().setAll(downloadButton);
|
||||
else if (list.status == DiscoJavaVersionList.Status.LOADING)
|
||||
downloadButtonPane.getChildren().setAll(new JFXSpinner());
|
||||
else {
|
||||
downloadButtonPane.getChildren().setAll(downloadButton);
|
||||
|
||||
if (list.status == DiscoJavaVersionList.Status.SUCCESS) {
|
||||
packageTypeBox.getItems().setAll(list.versions.keySet());
|
||||
packageTypeBox.getSelectionModel().selectFirst();
|
||||
|
||||
remoteVersionBox.setDisable(false);
|
||||
packageTypeBox.setDisable(false);
|
||||
} else
|
||||
warningLabel.setText(i18n("java.download.load_list.failed"));
|
||||
}
|
||||
};
|
||||
|
||||
currentJavaVersionList.addListener((observable, oldValue, newValue) -> {
|
||||
if (oldValue != null)
|
||||
oldValue.listener = null;
|
||||
|
||||
if (newValue != null) {
|
||||
newValue.status.addListener(updateStatusListener);
|
||||
updateStatus(newValue.status.get());
|
||||
updateListStatus.accept(newValue);
|
||||
|
||||
if (newValue.status == DiscoJavaVersionList.Status.LOADING)
|
||||
newValue.listener = updateListStatus;
|
||||
} else {
|
||||
updateStatus(null);
|
||||
currentJavaVersionList.set(null);
|
||||
updateListStatus.accept(null);
|
||||
}
|
||||
});
|
||||
|
||||
packageTypeBox.getSelectionModel().selectedItemProperty().addListener(ignored -> updateVersions());
|
||||
FXUtils.onChangeAndOperate(distributionBox.getSelectionModel().selectedItemProperty(), distribution -> {
|
||||
if (distribution != null) {
|
||||
changingDistribution = true;
|
||||
packageTypeBox.setItems(FXCollections.observableList(new ArrayList<>(distribution.getSupportedPackageTypes())));
|
||||
packageTypeBox.getSelectionModel().select(0);
|
||||
changingDistribution = false;
|
||||
updateVersions();
|
||||
packageTypeBox.setDisable(false);
|
||||
remoteVersionBox.setDisable(false);
|
||||
} else {
|
||||
packageTypeBox.setItems(null);
|
||||
updateVersions();
|
||||
remoteVersionBox.setItems(null);
|
||||
packageTypeBox.setDisable(true);
|
||||
remoteVersionBox.setDisable(true);
|
||||
}
|
||||
});
|
||||
FXUtils.onChange(distributionBox.getSelectionModel().selectedItemProperty(),
|
||||
it -> currentJavaVersionList.set(getJavaVersionList(it)));
|
||||
|
||||
setHeading(new Label(i18n("java.download")));
|
||||
setBody(body);
|
||||
setActions(warningLabel, downloadButtonPane, cancelButton);
|
||||
}
|
||||
|
||||
private void updateStatus(DiscoJavaVersionList.Status status) {
|
||||
if (status == DiscoJavaVersionList.Status.LOADING) {
|
||||
downloadButtonPane.getChildren().setAll(new JFXSpinner());
|
||||
remoteVersionBox.setDisable(true);
|
||||
warningLabel.setText(null);
|
||||
} else {
|
||||
downloadButtonPane.getChildren().setAll(downloadButton);
|
||||
if (status == DiscoJavaVersionList.Status.SUCCESS || status == null) {
|
||||
remoteVersionBox.setDisable(false);
|
||||
warningLabel.setText(null);
|
||||
} else if (status == DiscoJavaVersionList.Status.FAILED) {
|
||||
remoteVersionBox.setDisable(true);
|
||||
warningLabel.setText(i18n("java.download.load_list.failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onDownload() {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
|
||||
DiscoJavaDistribution distribution = distributionBox.getSelectionModel().getSelectedItem();
|
||||
DiscoJavaRemoteVersion version = remoteVersionBox.getSelectionModel().getSelectedItem();
|
||||
JavaPackageType packageType = packageTypeBox.getSelectionModel().getSelectedItem();
|
||||
|
||||
if (version == null)
|
||||
return;
|
||||
@@ -312,8 +322,15 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
getIntegrityCheck = Task.completed(new FileDownloadTask.IntegrityCheck(fileInfo.getChecksumType(), fileInfo.getChecksum()));
|
||||
else if (StringUtils.isNotBlank(fileInfo.getChecksumUri()))
|
||||
getIntegrityCheck = new GetTask(downloadProvider.injectURLWithCandidates(fileInfo.getChecksumUri()))
|
||||
.thenApplyAsync(checksum ->
|
||||
new FileDownloadTask.IntegrityCheck(fileInfo.getChecksumType(), checksum.trim()));
|
||||
.thenApplyAsync(checksum -> {
|
||||
checksum = checksum.trim();
|
||||
|
||||
int idx = checksum.indexOf(' ');
|
||||
if (idx > 0)
|
||||
checksum = checksum.substring(0, idx);
|
||||
|
||||
return new FileDownloadTask.IntegrityCheck(fileInfo.getChecksumType(), checksum);
|
||||
});
|
||||
else
|
||||
throw new IOException("Unable to get checksum for file");
|
||||
|
||||
@@ -336,7 +353,7 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
if (idx > 0) {
|
||||
javaVersion = javaVersion.substring(0, idx);
|
||||
}
|
||||
String defaultName = distribution.getApiParameter() + "-" + javaVersion;
|
||||
String defaultName = distribution.getApiParameter() + "-" + javaVersion + "-" + packageType.name().toLowerCase(Locale.ROOT);
|
||||
Controllers.getDecorator().startWizard(new SinglePageWizardProvider(controller ->
|
||||
new JavaInstallPage(controller::onFinish, info, version, updateInfo, defaultName, result)));
|
||||
} else {
|
||||
@@ -350,65 +367,39 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
|
||||
}
|
||||
|
||||
private void updateVersions() {
|
||||
if (changingDistribution) return;
|
||||
private DiscoJavaVersionList getJavaVersionList(DiscoJavaDistribution distribution) {
|
||||
if (distribution == null)
|
||||
return null;
|
||||
return javaVersionLists.computeIfAbsent(distribution, it -> {
|
||||
DiscoJavaVersionList versionList = new DiscoJavaVersionList(it);
|
||||
new DiscoFetchJavaListTask(downloadProvider, it, platform).setExecutor(Schedulers.io()).thenApplyAsync(versions -> {
|
||||
EnumMap<JavaPackageType, ObservableList<DiscoJavaRemoteVersion>> result = new EnumMap<>(JavaPackageType.class);
|
||||
if (versions.isEmpty())
|
||||
return result;
|
||||
|
||||
DiscoJavaDistribution distribution = distributionBox.getSelectionModel().getSelectedItem();
|
||||
if (distribution == null) {
|
||||
this.currentDiscoJavaVersionList.set(null);
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<JavaPackageType, TreeMap<Integer, DiscoJavaRemoteVersion>> entry : versions.entrySet())
|
||||
for (DiscoJavaRemoteVersion version : entry.getValue().values())
|
||||
if (version.isLTS()
|
||||
|| version.getJdkVersion() == entry.getValue().lastKey() // latest version
|
||||
|| version.getJdkVersion() == 16)
|
||||
result.computeIfAbsent(entry.getKey(), ignored -> FXCollections.observableArrayList())
|
||||
.add(version);
|
||||
|
||||
JavaPackageType packageType = packageTypeBox.getSelectionModel().getSelectedItem();
|
||||
|
||||
DiscoJavaVersionList list = javaVersionLists.computeIfAbsent(Pair.pair(distribution, packageType), pair -> {
|
||||
DiscoJavaVersionList res = new DiscoJavaVersionList();
|
||||
new DiscoFetchJavaListTask(downloadProvider, distribution, platform, packageType).setExecutor(Schedulers.io()).thenApplyAsync(versions -> {
|
||||
if (versions.isEmpty()) return Collections.<DiscoJavaRemoteVersion>emptyList();
|
||||
|
||||
int lastLTS = -1;
|
||||
for (int v : versions.keySet()) {
|
||||
if (isLTS(v)) {
|
||||
lastLTS = v;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<DiscoJavaRemoteVersion> remoteVersions = new ArrayList<>();
|
||||
for (Map.Entry<Integer, DiscoJavaRemoteVersion> entry : versions.entrySet()) {
|
||||
int v = entry.getKey();
|
||||
if (v >= lastLTS || isLTS(v) || v == 16) {
|
||||
remoteVersions.add(entry.getValue());
|
||||
}
|
||||
}
|
||||
Collections.reverse(remoteVersions);
|
||||
return remoteVersions;
|
||||
for (List<DiscoJavaRemoteVersion> l : result.values())
|
||||
Collections.reverse(l);
|
||||
return result;
|
||||
}).whenComplete(Schedulers.javafx(), ((result, exception) -> {
|
||||
if (exception == null) {
|
||||
res.status.set(DiscoJavaVersionList.Status.SUCCESS);
|
||||
res.versions.setAll(result);
|
||||
selectLTS(res);
|
||||
versionList.status = DiscoJavaVersionList.Status.SUCCESS;
|
||||
versionList.versions = result;
|
||||
} else {
|
||||
LOG.warning("Failed to load java list", exception);
|
||||
res.status.set(DiscoJavaVersionList.Status.FAILED);
|
||||
versionList.status = DiscoJavaVersionList.Status.FAILED;
|
||||
}
|
||||
versionList.invalidate();
|
||||
})).start();
|
||||
return res;
|
||||
return versionList;
|
||||
});
|
||||
this.currentDiscoJavaVersionList.set(list);
|
||||
this.remoteVersionBox.setItems(list.versions);
|
||||
selectLTS(list);
|
||||
}
|
||||
|
||||
private void selectLTS(DiscoJavaVersionList list) {
|
||||
if (remoteVersionBox.getItems() == list.versions) {
|
||||
for (int i = 0; i < list.versions.size(); i++) {
|
||||
JavaRemoteVersion item = list.versions.get(i);
|
||||
if (item.getJdkVersion() == GameJavaVersion.LATEST.getMajorVersion()) {
|
||||
remoteVersionBox.getSelectionModel().select(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +408,19 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
LOADING, SUCCESS, FAILED
|
||||
}
|
||||
|
||||
final ObservableList<DiscoJavaRemoteVersion> versions = FXCollections.observableArrayList();
|
||||
final ObjectProperty<Status> status = new SimpleObjectProperty<>(Status.LOADING);
|
||||
final DiscoJavaDistribution distribution;
|
||||
|
||||
Status status = Status.LOADING;
|
||||
EnumMap<JavaPackageType, ObservableList<DiscoJavaRemoteVersion>> versions;
|
||||
Consumer<DiscoJavaVersionList> listener;
|
||||
|
||||
DiscoJavaVersionList(DiscoJavaDistribution distribution) {
|
||||
this.distribution = distribution;
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
if (listener != null)
|
||||
listener.accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@ import org.jackhuang.hmcl.util.tree.ArchiveFileTree;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jackhuang.hmcl.util.tree.TarFileTree;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
@@ -151,7 +150,7 @@ public final class JavaManagementPage extends ListPageBase<JavaManagementPage.Ja
|
||||
|
||||
private void onInstallArchive(Path file) {
|
||||
Task.supplyAsync(() -> {
|
||||
try (ArchiveFileTree<?, ?> tree = TarFileTree.open(file)) {
|
||||
try (ArchiveFileTree<?, ?> tree = ArchiveFileTree.open(file)) {
|
||||
JavaInfo info = JavaInfo.fromArchive(tree);
|
||||
|
||||
if (!JavaManager.isCompatible(info.getPlatform()))
|
||||
|
||||
@@ -33,8 +33,9 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.ui.versions.VersionSettingsPage;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class LauncherSettingsPage extends DecoratorAnimatedPage implements DecoratorPage, PageAware {
|
||||
@@ -74,7 +75,6 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
settingsItem.setTitle(i18n("settings.type.global.manage"));
|
||||
settingsItem.setLeftGraphic(wrap(SVG.GAMEPAD));
|
||||
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(gameTab));
|
||||
runInFX(() -> FXUtils.installFastTooltip(settingsItem, i18n("settings.type.global.manage")));
|
||||
settingsItem.setOnAction(e -> tab.select(gameTab));
|
||||
})
|
||||
.addNavigationDrawerItem(javaItem -> {
|
||||
@@ -83,7 +83,7 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
javaItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(javaManagementTab));
|
||||
javaItem.setOnAction(e -> tab.select(javaManagementTab));
|
||||
})
|
||||
.startCategory(i18n("launcher"))
|
||||
.startCategory(i18n("launcher").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(settingsItem -> {
|
||||
settingsItem.setTitle(i18n("settings.launcher.general"));
|
||||
settingsItem.setLeftGraphic(wrap(SVG.APPLICATION_OUTLINE));
|
||||
@@ -102,7 +102,7 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
downloadItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(downloadTab));
|
||||
downloadItem.setOnAction(e -> tab.select(downloadTab));
|
||||
})
|
||||
.startCategory(i18n("help"))
|
||||
.startCategory(i18n("help").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(helpItem -> {
|
||||
helpItem.setTitle(i18n("help"));
|
||||
helpItem.setLeftGraphic(wrap(SVG.HELP_CIRCLE_OUTLINE));
|
||||
|
||||
@@ -32,7 +32,6 @@ import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
@@ -46,6 +45,8 @@ import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.ui.construct.AnnouncementCard;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.PopupMenu;
|
||||
@@ -83,7 +84,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
private final ObservableList<Node> versionNodes;
|
||||
private Profile profile;
|
||||
|
||||
private VBox announcementPane;
|
||||
private TransitionPane announcementPane;
|
||||
private final StackPane updatePane;
|
||||
private final JFXButton menuButton;
|
||||
|
||||
@@ -102,13 +103,23 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
setPadding(new Insets(20));
|
||||
|
||||
if (Metadata.isNightly() || (Metadata.isDev() && !Objects.equals(Metadata.VERSION, config().getShownTips().get(ANNOUNCEMENT)))) {
|
||||
announcementPane = new VBox(16);
|
||||
AnnouncementCard announcementCard = null;
|
||||
|
||||
if (Metadata.isNightly()) {
|
||||
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint")));
|
||||
announcementCard = new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint"), null);
|
||||
} else if (Metadata.isDev()) {
|
||||
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint")));
|
||||
announcementCard = new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint"), this::hideAnnouncementPane);
|
||||
}
|
||||
|
||||
if (announcementCard != null) {
|
||||
VBox announcementBox = new VBox(16);
|
||||
announcementBox.getChildren().add(announcementCard);
|
||||
|
||||
announcementPane = new TransitionPane();
|
||||
announcementPane.setContent(announcementBox, ContainerAnimations.NONE.getAnimationProducer());
|
||||
|
||||
getChildren().add(announcementPane);
|
||||
}
|
||||
getChildren().add(announcementPane);
|
||||
}
|
||||
|
||||
updatePane = new StackPane();
|
||||
@@ -117,7 +128,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
FXUtils.setLimitWidth(updatePane, 230);
|
||||
FXUtils.setLimitHeight(updatePane, 55);
|
||||
StackPane.setAlignment(updatePane, Pos.TOP_RIGHT);
|
||||
updatePane.setOnMouseClicked(e -> onUpgrade());
|
||||
FXUtils.onClicked(updatePane, this::onUpgrade);
|
||||
FXUtils.onChange(showUpdateProperty(), this::showUpdate);
|
||||
|
||||
{
|
||||
@@ -144,7 +155,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
StackPane.setAlignment(closeUpdateButton, Pos.TOP_RIGHT);
|
||||
closeUpdateButton.getStyleClass().add("toggle-icon-tiny");
|
||||
StackPane.setMargin(closeUpdateButton, new Insets(5));
|
||||
closeUpdateButton.setOnMouseClicked(e -> closeUpdateBubble());
|
||||
closeUpdateButton.setOnAction(e -> closeUpdateBubble());
|
||||
|
||||
updatePane.getChildren().setAll(hBox, closeUpdateButton);
|
||||
}
|
||||
@@ -206,7 +217,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
menuButton.setPrefWidth(230);
|
||||
//menuButton.setButtonType(JFXButton.ButtonType.RAISED);
|
||||
menuButton.setStyle("-fx-font-size: 15px;");
|
||||
menuButton.setOnMouseClicked(e -> onMenu());
|
||||
menuButton.setOnAction(e -> onMenu());
|
||||
menuButton.setClip(new Rectangle(211, -100, 100, 200));
|
||||
StackPane graphic = new StackPane();
|
||||
Node svg = SVG.TRIANGLE.createIcon(Theme.foregroundFillBinding(), 10, 10);
|
||||
@@ -224,10 +235,10 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
menu.setMaxHeight(365);
|
||||
menu.setMaxWidth(545);
|
||||
menu.setAlwaysShowingVBar(true);
|
||||
menu.setOnMouseClicked(e -> popup.hide());
|
||||
FXUtils.onClicked(menu, popup::hide);
|
||||
versionNodes = MappedObservableList.create(versions, version -> {
|
||||
Node node = PopupMenu.wrapPopupMenuItem(new GameItem(profile, version.getId()));
|
||||
node.setOnMouseClicked(e -> profile.setSelectedVersion(version.getId()));
|
||||
FXUtils.onClicked(node, () -> profile.setSelectedVersion(version.getId()));
|
||||
return node;
|
||||
});
|
||||
Bindings.bindContent(menu.getContent(), versionNodes);
|
||||
@@ -290,10 +301,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
public void hideAnnouncementPane() {
|
||||
if (announcementPane != null) {
|
||||
config().getShownTips().put(ANNOUNCEMENT, Metadata.VERSION);
|
||||
Pane parent = (Pane) announcementPane.getParent();
|
||||
if (parent != null)
|
||||
parent.getChildren().remove(announcementPane);
|
||||
announcementPane = null;
|
||||
announcementPane.setContent(new StackPane(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ public abstract class SettingsView extends StackPane {
|
||||
{
|
||||
StackPane sponsorPane = new StackPane();
|
||||
sponsorPane.setCursor(Cursor.HAND);
|
||||
sponsorPane.setOnMouseClicked(e -> onSponsor());
|
||||
FXUtils.onClicked(sponsorPane, this::onSponsor);
|
||||
sponsorPane.setPadding(new Insets(8, 0, 8, 0));
|
||||
|
||||
GridPane gridPane = new GridPane();
|
||||
@@ -121,7 +121,7 @@ public abstract class SettingsView extends StackPane {
|
||||
|
||||
{
|
||||
btnUpdate = new JFXButton();
|
||||
btnUpdate.setOnMouseClicked(e -> onUpdate());
|
||||
btnUpdate.setOnAction(e -> onUpdate());
|
||||
btnUpdate.getStyleClass().add("toggle-icon4");
|
||||
btnUpdate.setGraphic(SVG.UPDATE.createIcon(Theme.blackFill(), 20, 20));
|
||||
|
||||
@@ -161,7 +161,7 @@ public abstract class SettingsView extends StackPane {
|
||||
|
||||
{
|
||||
JFXButton cleanButton = new JFXButton(i18n("launcher.cache_directory.clean"));
|
||||
cleanButton.setOnMouseClicked(e -> clearCacheDirectory());
|
||||
cleanButton.setOnAction(e -> clearCacheDirectory());
|
||||
cleanButton.getStyleClass().add("jfx-button-border");
|
||||
|
||||
fileCommonLocationSublist.setHeaderRight(cleanButton);
|
||||
@@ -193,11 +193,11 @@ public abstract class SettingsView extends StackPane {
|
||||
debugPane.setLeft(left);
|
||||
|
||||
JFXButton openLogFolderButton = new JFXButton(i18n("settings.launcher.launcher_log.reveal"));
|
||||
openLogFolderButton.setOnMouseClicked(e -> openLogFolder());
|
||||
openLogFolderButton.setOnAction(e -> openLogFolder());
|
||||
openLogFolderButton.getStyleClass().add("jfx-button-border");
|
||||
|
||||
JFXButton logButton = new JFXButton(i18n("settings.launcher.launcher_log.export"));
|
||||
logButton.setOnMouseClicked(e -> onExportLogs());
|
||||
logButton.setOnAction(e -> onExportLogs());
|
||||
logButton.getStyleClass().add("jfx-button-border");
|
||||
|
||||
HBox buttonBox = new HBox();
|
||||
|
||||
@@ -22,7 +22,6 @@ import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
@@ -47,9 +46,7 @@ public class ProfileListItemSkin extends SkinBase<ProfileListItem> {
|
||||
skinnable.pseudoClassStateChanged(SELECTED, active);
|
||||
});
|
||||
|
||||
getSkinnable().addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
|
||||
getSkinnable().setSelected(true);
|
||||
});
|
||||
FXUtils.onClicked(getSkinnable(), () -> getSkinnable().setSelected(true));
|
||||
|
||||
Node left = VersionPage.wrap(SVG.FOLDER_OUTLINE);
|
||||
root.setLeft(left);
|
||||
@@ -64,7 +61,7 @@ public class ProfileListItemSkin extends SkinBase<ProfileListItem> {
|
||||
right.setAlignment(Pos.CENTER_RIGHT);
|
||||
|
||||
JFXButton btnRemove = new JFXButton();
|
||||
btnRemove.setOnMouseClicked(e -> skinnable.remove());
|
||||
btnRemove.setOnAction(e -> skinnable.remove());
|
||||
btnRemove.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(btnRemove, Pos.CENTER);
|
||||
btnRemove.setGraphic(SVG.CLOSE.createIcon(Theme.blackFill(), 14, 14));
|
||||
|
||||
@@ -488,7 +488,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
JFXListView<RemoteMod> listView = new JFXListView<>();
|
||||
spinnerPane.setContent(listView);
|
||||
Bindings.bindContent(listView.getItems(), getSkinnable().items);
|
||||
listView.setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(listView, () -> {
|
||||
if (listView.getSelectionModel().getSelectedIndex() < 0)
|
||||
return;
|
||||
RemoteMod selectedItem = listView.getSelectionModel().getSelectedItem();
|
||||
|
||||
@@ -346,7 +346,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
pane.getChildren().setAll(FXUtils.limitingSize(imageView, 40, 40), content);
|
||||
|
||||
RipplerContainer container = new RipplerContainer(pane);
|
||||
container.setOnMouseClicked(e -> Controllers.navigate(new DownloadPage(page, addon, version, callback)));
|
||||
FXUtils.onClicked(container, () -> Controllers.navigate(new DownloadPage(page, addon, version, callback)));
|
||||
getChildren().setAll(container);
|
||||
|
||||
if (addon != RemoteMod.BROKEN) {
|
||||
@@ -388,15 +388,15 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
|
||||
switch (dataItem.getVersionType()) {
|
||||
case Alpha:
|
||||
content.getTags().add(i18n("version.game.snapshot"));
|
||||
content.getTags().add(i18n("mods.channel.alpha"));
|
||||
graphicPane.getChildren().setAll(SVG.ALPHA_CIRCLE_OUTLINE.createIcon(Theme.blackFill(), 24, 24));
|
||||
break;
|
||||
case Beta:
|
||||
content.getTags().add(i18n("version.game.snapshot"));
|
||||
content.getTags().add(i18n("mods.channel.beta"));
|
||||
graphicPane.getChildren().setAll(SVG.BETA_CIRCLE_OUTLINE.createIcon(Theme.blackFill(), 24, 24));
|
||||
break;
|
||||
case Release:
|
||||
content.getTags().add(i18n("version.game.release"));
|
||||
content.getTags().add(i18n("mods.channel.release"));
|
||||
graphicPane.getChildren().setAll(SVG.RELEASE_CIRCLE_OUTLINE.createIcon(Theme.blackFill(), 24, 24));
|
||||
break;
|
||||
}
|
||||
@@ -428,7 +428,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
}
|
||||
|
||||
RipplerContainer container = new RipplerContainer(pane);
|
||||
container.setOnMouseClicked(e -> Controllers.dialog(new ModVersion(dataItem, selfPage)));
|
||||
FXUtils.onClicked(container, () -> Controllers.dialog(new ModVersion(dataItem, selfPage)));
|
||||
getChildren().setAll(container);
|
||||
|
||||
// Workaround for https://github.com/HMCL-dev/HMCL/issues/2129
|
||||
@@ -443,7 +443,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
VBox box = new VBox(8);
|
||||
box.setPadding(new Insets(8));
|
||||
ModItem modItem = new ModItem(version, selfPage);
|
||||
modItem.setOnMouseClicked(e -> fireEvent(new DialogCloseEvent()));
|
||||
FXUtils.onClicked(modItem, () -> fireEvent(new DialogCloseEvent()));
|
||||
box.getChildren().setAll(modItem);
|
||||
SpinnerPane spinnerPane = new SpinnerPane();
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
@@ -505,7 +505,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
dependencies.put(dependency.getType(), list);
|
||||
}
|
||||
DependencyModItem dependencyModItem = new DependencyModItem(selfPage.page, dependency.load(), selfPage.version, selfPage.callback);
|
||||
dependencyModItem.setOnMouseClicked(e -> fireEvent(new DialogCloseEvent()));
|
||||
FXUtils.onClicked(dependencyModItem, () -> fireEvent(new DialogCloseEvent()));
|
||||
dependencies.get(dependency.getType()).add(dependencyModItem);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
|
||||
right.setAlignment(Pos.CENTER_RIGHT);
|
||||
if (skinnable.canUpdate()) {
|
||||
JFXButton btnUpgrade = new JFXButton();
|
||||
btnUpgrade.setOnMouseClicked(e -> skinnable.update());
|
||||
btnUpgrade.setOnAction(e -> skinnable.update());
|
||||
btnUpgrade.getStyleClass().add("toggle-icon4");
|
||||
btnUpgrade.setGraphic(FXUtils.limitingSize(SVG.UPDATE.createIcon(Theme.blackFill(), 24, 24), 24, 24));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnUpgrade, i18n("version.update")));
|
||||
@@ -88,7 +88,7 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
|
||||
|
||||
{
|
||||
JFXButton btnLaunch = new JFXButton();
|
||||
btnLaunch.setOnMouseClicked(e -> skinnable.launch());
|
||||
btnLaunch.setOnAction(e -> skinnable.launch());
|
||||
btnLaunch.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(btnLaunch, Pos.CENTER);
|
||||
btnLaunch.setGraphic(FXUtils.limitingSize(SVG.ROCKET_LAUNCH_OUTLINE.createIcon(Theme.blackFill(), 24, 24), 24, 24));
|
||||
@@ -98,7 +98,7 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
|
||||
|
||||
{
|
||||
JFXButton btnManage = new JFXButton();
|
||||
btnManage.setOnMouseClicked(e -> {
|
||||
btnManage.setOnAction(e -> {
|
||||
currentSkinnable = skinnable;
|
||||
popup.get().show(root, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, root.getHeight());
|
||||
});
|
||||
|
||||
@@ -91,13 +91,11 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
installNewGameItem.setTitle(i18n("install.new_game"));
|
||||
installNewGameItem.setLeftGraphic(VersionPage.wrap(SVG.PLUS_CIRCLE_OUTLINE));
|
||||
installNewGameItem.setOnAction(e -> Versions.addNewGame());
|
||||
runInFX(() -> FXUtils.installFastTooltip(installNewGameItem, i18n("install.new_game")));
|
||||
})
|
||||
.addNavigationDrawerItem(installModpackItem -> {
|
||||
installModpackItem.setTitle(i18n("install.modpack"));
|
||||
installModpackItem.setLeftGraphic(VersionPage.wrap(SVG.PACK));
|
||||
installModpackItem.setOnAction(e -> Versions.importModpack());
|
||||
runInFX(() -> FXUtils.installFastTooltip(installModpackItem, i18n("install.modpack")));
|
||||
})
|
||||
.addNavigationDrawerItem(refreshItem -> {
|
||||
refreshItem.setTitle(i18n("button.refresh"));
|
||||
@@ -108,7 +106,6 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
globalManageItem.setTitle(i18n("settings.type.global.manage"));
|
||||
globalManageItem.setLeftGraphic(VersionPage.wrap(SVG.GEAR_OUTLINE));
|
||||
globalManageItem.setOnAction(e -> modifyGlobalGameSettings());
|
||||
runInFX(() -> FXUtils.installFastTooltip(globalManageItem, i18n("settings.type.global.manage")));
|
||||
});
|
||||
FXUtils.setLimitHeight(bottomLeftCornerList, 40 * 4 + 12 * 2);
|
||||
setLeft(pane, bottomLeftCornerList);
|
||||
|
||||
@@ -97,11 +97,11 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
|
||||
item.versionProperty().set(null);
|
||||
}
|
||||
|
||||
item.installActionProperty().set(e -> {
|
||||
item.setOnInstall(() -> {
|
||||
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
|
||||
});
|
||||
|
||||
item.removeActionProperty().set(e -> profile.getDependency().removeLibraryAsync(version, libraryId)
|
||||
item.setOnRemove(() -> profile.getDependency().removeLibraryAsync(version, libraryId)
|
||||
.thenComposeAsync(profile.getRepository()::saveAsync)
|
||||
.withComposeAsync(profile.getRepository().refreshVersionsAsync())
|
||||
.withRunAsync(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
|
||||
@@ -121,7 +121,7 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
|
||||
|
||||
InstallerItem installerItem = new InstallerItem(libraryId, InstallerItem.Style.LIST_ITEM);
|
||||
installerItem.versionProperty().set(new InstallerItem.InstalledState(libraryVersion, false, false));
|
||||
installerItem.removeActionProperty().set(e -> profile.getDependency().removeLibraryAsync(version, libraryId)
|
||||
installerItem.setOnRemove(() -> profile.getDependency().removeLibraryAsync(version, libraryId)
|
||||
.thenComposeAsync(profile.getRepository()::saveAsync)
|
||||
.withComposeAsync(profile.getRepository().refreshVersionsAsync())
|
||||
.withRunAsync(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
|
||||
|
||||
@@ -539,7 +539,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
}
|
||||
checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active);
|
||||
restoreButton.setVisible(!dataItem.getModInfo().getMod().getOldFiles().isEmpty());
|
||||
restoreButton.setOnMouseClicked(e -> {
|
||||
restoreButton.setOnAction(e -> {
|
||||
menu.get().getContent().setAll(dataItem.getModInfo().getMod().getOldFiles().stream()
|
||||
.map(localModFile -> new IconedMenuItem(null, localModFile.getVersion(),
|
||||
() -> getSkinnable().rollback(dataItem.getModInfo(), localModFile),
|
||||
@@ -549,12 +549,8 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
|
||||
popup.get().show(restoreButton, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, restoreButton.getHeight());
|
||||
});
|
||||
revealButton.setOnMouseClicked(e -> {
|
||||
FXUtils.showFileInExplorer(dataItem.getModInfo().getFile());
|
||||
});
|
||||
infoButton.setOnMouseClicked(e -> {
|
||||
Controllers.dialog(new ModInfoDialog(dataItem));
|
||||
});
|
||||
revealButton.setOnAction(e -> FXUtils.showFileInExplorer(dataItem.getModInfo().getFile()));
|
||||
infoButton.setOnAction(e -> Controllers.dialog(new ModInfoDialog(dataItem)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||
@@ -73,6 +74,8 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
getStyleClass().add("gray-background");
|
||||
|
||||
TableColumn<ModUpdateObject, Boolean> enabledColumn = new TableColumn<>();
|
||||
CheckBox allEnabledBox = new CheckBox();
|
||||
enabledColumn.setGraphic(allEnabledBox);
|
||||
enabledColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enabledColumn));
|
||||
setupCellValueFactory(enabledColumn, ModUpdateObject::enabledProperty);
|
||||
enabledColumn.setEditable(true);
|
||||
@@ -95,6 +98,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
setupCellValueFactory(sourceColumn, ModUpdateObject::sourceProperty);
|
||||
|
||||
objects = FXCollections.observableList(updates.stream().map(ModUpdateObject::new).collect(Collectors.toList()));
|
||||
FXUtils.bindAllEnabled(allEnabledBox.selectedProperty(), objects.stream().map(o -> o.enabled).toArray(BooleanProperty[]::new));
|
||||
|
||||
TableView<ModUpdateObject> table = new TableView<>(objects);
|
||||
table.setEditable(true);
|
||||
@@ -170,7 +174,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
csvTable.write(Files.newOutputStream(path));
|
||||
|
||||
FXUtils.showFileInExplorer(path);
|
||||
}).whenComplete(Schedulers.javafx() ,exception -> {
|
||||
}).whenComplete(Schedulers.javafx(), exception -> {
|
||||
if (exception == null) {
|
||||
Controllers.dialog(path.toString(), i18n("message.success"));
|
||||
} else {
|
||||
|
||||
@@ -95,9 +95,7 @@ public class VersionIconDialog extends DialogPane {
|
||||
RipplerContainer container = new RipplerContainer(shape);
|
||||
FXUtils.setLimitWidth(container, 36);
|
||||
FXUtils.setLimitHeight(container, 36);
|
||||
container.setOnMouseClicked(e -> {
|
||||
exploreIcon();
|
||||
});
|
||||
FXUtils.onClicked(container, this::exploreIcon);
|
||||
return container;
|
||||
}
|
||||
|
||||
@@ -107,7 +105,7 @@ public class VersionIconDialog extends DialogPane {
|
||||
RipplerContainer container = new RipplerContainer(imageView);
|
||||
FXUtils.setLimitWidth(container, 36);
|
||||
FXUtils.setLimitHeight(container, 36);
|
||||
container.setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(container, () -> {
|
||||
if (vs != null) {
|
||||
vs.setVersionIcon(type);
|
||||
onAccept();
|
||||
|
||||
@@ -244,7 +244,6 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
versionSettingsItem.setLeftGraphic(wrap(SVG.GEAR_OUTLINE));
|
||||
versionSettingsItem.setActionButtonVisible(false);
|
||||
versionSettingsItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.versionSettingsTab));
|
||||
runInFX(() -> FXUtils.installFastTooltip(versionSettingsItem, i18n("settings.game")));
|
||||
versionSettingsItem.setOnAction(e -> control.tab.select(control.versionSettingsTab));
|
||||
|
||||
AdvancedListItem installerListItem = new AdvancedListItem();
|
||||
@@ -253,7 +252,6 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
installerListItem.setLeftGraphic(wrap(SVG.CUBE));
|
||||
installerListItem.setActionButtonVisible(false);
|
||||
installerListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.installerListTab));
|
||||
runInFX(() -> FXUtils.installFastTooltip(installerListItem, i18n("settings.tabs.installers")));
|
||||
installerListItem.setOnAction(e -> control.tab.select(control.installerListTab));
|
||||
|
||||
AdvancedListItem modListItem = new AdvancedListItem();
|
||||
@@ -262,7 +260,6 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
modListItem.setLeftGraphic(wrap(SVG.PUZZLE));
|
||||
modListItem.setActionButtonVisible(false);
|
||||
modListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.modListTab));
|
||||
runInFX(() -> FXUtils.installFastTooltip(modListItem, i18n("mods.manage")));
|
||||
modListItem.setOnAction(e -> control.tab.select(control.modListTab));
|
||||
|
||||
AdvancedListItem worldListItem = new AdvancedListItem();
|
||||
@@ -271,7 +268,6 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
worldListItem.setLeftGraphic(wrap(SVG.EARTH));
|
||||
worldListItem.setActionButtonVisible(false);
|
||||
worldListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.worldListTab));
|
||||
runInFX(() -> FXUtils.installFastTooltip(worldListItem, i18n("world.manage")));
|
||||
worldListItem.setOnAction(e -> control.tab.select(control.worldListTab));
|
||||
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
|
||||
@@ -83,7 +83,7 @@ public class WorldExportPageSkin extends SkinBase<WorldExportPage> {
|
||||
JFXButton btnExport = FXUtils.newRaisedButton(i18n("button.export"));
|
||||
btnExport.disableProperty().bind(Bindings.createBooleanBinding(() -> txtWorldName.getText().isEmpty() || Files.exists(Paths.get(fileItem.getPath())),
|
||||
txtWorldName.textProperty().isEmpty(), fileItem.pathProperty()));
|
||||
btnExport.setOnMouseClicked(e -> skinnable.export());
|
||||
btnExport.setOnAction(e -> skinnable.export());
|
||||
HBox bottom = new HBox();
|
||||
bottom.setAlignment(Pos.CENTER_RIGHT);
|
||||
bottom.getChildren().setAll(btnExport);
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.versions;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
@@ -10,11 +27,14 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.game.World;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.DoubleValidator;
|
||||
@@ -32,22 +52,36 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.formatDateTime;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public final class WorldInfoPage extends StackPane implements DecoratorPage {
|
||||
private final World world;
|
||||
private final CompoundTag levelDat;
|
||||
private final CompoundTag dataTag;
|
||||
private CompoundTag levelDat;
|
||||
|
||||
private final ObjectProperty<State> stateProperty = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<State> stateProperty;
|
||||
|
||||
public WorldInfoPage(World world) throws IOException {
|
||||
public WorldInfoPage(World world) {
|
||||
this.world = world;
|
||||
this.levelDat = world.readLevelDat();
|
||||
this.dataTag = levelDat.get("Data");
|
||||
this.stateProperty = new SimpleObjectProperty<>(State.fromTitle(i18n("world.info.title", world.getWorldName())));
|
||||
|
||||
this.getChildren().add(new ProgressIndicator());
|
||||
Task.supplyAsync(world::readLevelDat)
|
||||
.whenComplete(Schedulers.javafx(), ((result, exception) -> {
|
||||
if (exception == null) {
|
||||
this.levelDat = result;
|
||||
loadWorldInfo();
|
||||
} else {
|
||||
LOG.warning("Failed to load level.dat", exception);
|
||||
this.getChildren().setAll(new Label(i18n("world.info.failed")));
|
||||
}
|
||||
})).start();
|
||||
}
|
||||
|
||||
private void loadWorldInfo() {
|
||||
CompoundTag dataTag = levelDat.get("Data");
|
||||
CompoundTag worldGenSettings = dataTag.get("WorldGenSettings");
|
||||
|
||||
stateProperty.set(State.fromTitle(i18n("world.info.title", world.getWorldName())));
|
||||
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
scrollPane.setFitToHeight(true);
|
||||
scrollPane.setFitToWidth(true);
|
||||
@@ -483,7 +517,6 @@ public final class WorldInfoPage extends StackPane implements DecoratorPage {
|
||||
Tag z = listTag.get(2);
|
||||
|
||||
if (x instanceof DoubleTag && y instanceof DoubleTag && z instanceof DoubleTag) {
|
||||
//noinspection MalformedFormatString
|
||||
return this == OVERWORLD
|
||||
? String.format("(%.2f, %.2f, %.2f)", x.getValue(), y.getValue(), z.getValue())
|
||||
: String.format("%s (%.2f, %.2f, %.2f)", name, x.getValue(), y.getValue(), z.getValue());
|
||||
|
||||
@@ -51,7 +51,7 @@ public class WorldListItem extends Control {
|
||||
|
||||
subtitle.set(i18n("world.description", world.getFileName(), formatDateTime(Instant.ofEpochMilli(world.getLastPlayed())), world.getGameVersion() == null ? i18n("message.unknown") : world.getGameVersion()));
|
||||
|
||||
setOnMouseClicked(event -> showInfo());
|
||||
FXUtils.onClicked(this, this::showInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,10 +102,6 @@ public class WorldListItem extends Control {
|
||||
}
|
||||
|
||||
public void showInfo() {
|
||||
try {
|
||||
Controllers.navigate(new WorldInfoPage(world));
|
||||
} catch (Exception e) {
|
||||
// TODO
|
||||
}
|
||||
Controllers.navigate(new WorldInfoPage(world));
|
||||
}
|
||||
}
|
||||
@@ -76,9 +76,7 @@ public class WorldListItemSkin extends SkinBase<WorldListItem> {
|
||||
right.setAlignment(Pos.CENTER_RIGHT);
|
||||
|
||||
JFXButton btnManage = new JFXButton();
|
||||
btnManage.setOnMouseClicked(e -> {
|
||||
popup.show(root, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, root.getHeight());
|
||||
});
|
||||
btnManage.setOnAction(e -> popup.show(root, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, root.getHeight()));
|
||||
btnManage.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(btnManage, Pos.CENTER);
|
||||
btnManage.setGraphic(SVG.DOTS_VERTICAL.createIcon(Theme.blackFill(), -1, -1));
|
||||
|
||||
@@ -9,13 +9,13 @@ import java.util.stream.Stream;
|
||||
* @author Glavo
|
||||
*/
|
||||
final class CallerFinder {
|
||||
private static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||
private static final String PACKAGE_PREFIX = CallerFinder.class.getPackageName() + ".";
|
||||
private static final Predicate<StackWalker.StackFrame> PREDICATE = stackFrame -> !stackFrame.getClassName().startsWith(PACKAGE_PREFIX);
|
||||
private static final Function<Stream<StackWalker.StackFrame>, Optional<StackWalker.StackFrame>> FUNCTION = stream -> stream.filter(PREDICATE).findFirst();
|
||||
private static final Function<StackWalker.StackFrame, String> FRAME_MAPPING = frame -> frame.getClassName() + "." + frame.getMethodName();
|
||||
|
||||
static String getCaller() {
|
||||
return WALKER.walk(FUNCTION).map(it -> it.getClassName() + "." + it.getMethodName()).orElse(null);
|
||||
return StackWalker.getInstance().walk(FUNCTION).map(FRAME_MAPPING).orElse(null);
|
||||
}
|
||||
|
||||
private CallerFinder() {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Hello Minecraft! Launcher
|
||||
# Copyright (C) 2023 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
# Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -153,7 +153,7 @@ button.edit=Editar
|
||||
button.install=Instalar
|
||||
button.export=Exportar
|
||||
button.no=No
|
||||
botón.ok=Aceptar
|
||||
button.ok=Aceptar
|
||||
button.refresh=Refrescar
|
||||
button.remove=Eliminar
|
||||
button.remove.confirm=¿Está seguro de querer eliminarlo definitivamente? Esta acción no se puede deshacer.
|
||||
@@ -345,8 +345,13 @@ fatal.illegal_char=Su ruta de usuario contiene un carácter ilegal '\=', por lo
|
||||
Por ejemplo, no podrá utilizar authlib-injector o cambiar el skin de su cuenta offline.
|
||||
|
||||
feedback=Comentarios
|
||||
feedback.channel=Canal de comentarios
|
||||
feedback.discord=Discord
|
||||
feedback.discord.statement=¡Únete a nuestra comunidad de Discord!
|
||||
feedback.discord.statement=¡Únase a nuestro servidor Discord!
|
||||
feedback.github=GitHub Issues
|
||||
feedback.github.statement=Envíe una propuesta en GitHub.
|
||||
feedback.qq_group=Grupo QQ de usuarios de HMCL
|
||||
feedback.qq_group.statement=¡Únase a nuestro grupo QQ de usuarios!
|
||||
|
||||
file=Archivo
|
||||
|
||||
@@ -517,12 +522,12 @@ game.crash.reason.shaders_mod=El juego actual no puede seguir ejecutándose porq
|
||||
game.crash.reason.rtss_forest_sodium=El juego actual se bloquea debido a una incompatibilidad entre RivaTuner Statistics Server (RTSS) y Sodium, lo que provoca el fallo del juego. Haz clic <a href="https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#rtss-incompatible">aquí</a> para obtener más detalles.
|
||||
game.crash.reason.stacktrace=El motivo del fallo es desconocido. Puede ver los detalles haciendo clic en el botón "Registros".\n\
|
||||
\n\
|
||||
Hay algunas palabras clave que pueden contener algunos Mod IDs. Puedes buscarlas en Internet para averiguar el problema por ti mismo..\n\
|
||||
Hay algunas palabras clave que pueden contener algunos Mod IDs. Puedes buscarlas en Internet para averiguar el problema por ti mismo.\n\
|
||||
\n\
|
||||
%s
|
||||
game.crash.reason.too_old_java=El juego se ha bloqueado porque estás utilizando una versión histórica de Java VM..\n\
|
||||
\n\
|
||||
Tienes que cambiar a una versión más reciente (%1$s) de Java en la configuración del juego y luego volver a ejecutar el juego. Puedes descargar Java desde <a href="https://docs.microsoft.com/java/openjdk/download">here</a>.
|
||||
Tienes que cambiar a una versión más reciente (%1$s) de Java en la configuración del juego y luego volver a ejecutar el juego. Puedes descargar Java desde <a href="https://learn.microsoft.com/java/openjdk/download">here</a>.
|
||||
game.crash.reason.unknown=No somos capaces de averiguar por qué el juego se estrelló, por favor refiérase a los registros del juego.\n\
|
||||
\n\
|
||||
<b>Cuando pidas ayuda a otra persona, por favor, comparte con ella el registro completo del juego y el archivo de informe de fallos relacionado.</b>\n\
|
||||
@@ -937,8 +942,8 @@ settings.advanced.post_exit_command=Comando post-salida
|
||||
settings.advanced.post_exit_command.prompt=Comandos a ejecutar tras la salida del juego
|
||||
settings.advanced.server_ip=Dirección del servidor
|
||||
settings.advanced.server_ip.prompt=Entrar automáticamente después de ejecutar el juego
|
||||
settings.advanced.use_native_glfw=[Sólo Linux] Utilizar GLFW nativo
|
||||
settings.advanced.use_native_openal=[Sólo Linux] Utilizar OpenAL nativo
|
||||
settings.advanced.use_native_glfw=[Sólo Linux/FreeBSD] Utilizar GLFW nativo
|
||||
settings.advanced.use_native_openal=[Sólo Linux/FreeBSD] Utilizar OpenAL nativo
|
||||
settings.advanced.workaround=Métodos alternativos
|
||||
settings.advanced.workaround.warning=Éstas opciones están pensadas sólo para usuarios expertos. Jugar con estas opciones puede romper el juego. A menos que sepas lo que estás haciendo, por favor no modifiques estas opciones.
|
||||
settings.advanced.wrapper_launcher=Wrapper Launcher
|
||||
@@ -1022,8 +1027,8 @@ settings.type.special.edit=Editar ajustes de la instancia actual
|
||||
settings.type.special.edit.hint=La instancia actual [%s] tiene activada la configuración por instancia, todas las opciones de esta página NO afectarán a esa instancia. Haga clic aquí para modificar sus propias opciones.
|
||||
|
||||
sponsor=Donantes
|
||||
sponsor.bmclapi=Las descargas son proporcionadas por BMCLAPI. Haz clic aquí para obtener más información.
|
||||
sponsor.hmcl=¡Hello Minecraft! Launcher es un launcher FOSS de Minecraft que permite a los usuarios gestionar múltiples instancias de Minecraft fácilmente. Usted puede donar a nosotros en Afdian para apoyar nuestro alojamiento de archivos y el desarrollo del proyecto. Haga clic aquí para obtener más información.
|
||||
sponsor.bmclapi=Las descargas de China continental son proporcionadas por BMCLAPI. Haga clic aquí para obtener más información.
|
||||
sponsor.hmcl=Hello Minecraft! Launcher es un launcher FOSS de Minecraft que permite a los usuarios gestionar múltiples instancias de Minecraft fácilmente. Haga clic aquí para obtener más información.
|
||||
|
||||
system.architecture=Arquitectura
|
||||
system.operating_system=Sistema operativo
|
||||
@@ -1033,15 +1038,13 @@ update.accept=Actualizar
|
||||
update.changelog=Registro de cambios
|
||||
update.channel.dev=Beta
|
||||
update.channel.dev.hint=Actualmente está utilizando una versión beta del launcher, que puede incluir algunas características adicionales, pero también es más inestable que las versiones de lanzamiento.\n\
|
||||
\n\
|
||||
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://docs.hmcl.net/groups.html">QQ</a>.\n\
|
||||
\n\
|
||||
<a href="hmcl://hide-announcement">Haga clic aquí</a> para ocultar el recordatorio para la versión de prueba actual.
|
||||
\n\
|
||||
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello.
|
||||
update.channel.dev.title=Aviso de versión beta
|
||||
update.channel.nightly=Nocturna
|
||||
update.channel.nightly.hint=Estás utilizando una versión nocturna del launcher, que puede incluir algunas características adicionales, pero también es siempre más inestable que las otras versiones.\n\
|
||||
\n\
|
||||
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://docs.hmcl.net/groups.html">QQ</a> community.
|
||||
\n\
|
||||
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello.
|
||||
update.channel.nightly.title=Aviso de versión nocturna
|
||||
update.channel.stable=Estable
|
||||
update.checking=Buscando actualizaciones
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Hello Minecraft! Launcher
|
||||
# Copyright (C) 2023 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
# Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -17,6 +17,7 @@
|
||||
#
|
||||
|
||||
# Contributors: zhixuan2333
|
||||
|
||||
about=About
|
||||
about.copyright=著作権
|
||||
about.copyright.statement=Copyright © 2024 huangyuhui。
|
||||
@@ -128,9 +129,9 @@ archive.date=公開日
|
||||
archive.file.name=名前
|
||||
archive.version=バージョン
|
||||
|
||||
Assets.download=アセットのダウンロード
|
||||
Assets.download_all=アセットの整合性チェック
|
||||
Assets.index.malformed=アセットインデックスの形式が正しくありません。バージョン設定の[ゲームアセットファイルの更新]で再試行できます。
|
||||
assets.download=アセットのダウンロード
|
||||
assets.download_all=アセットの整合性チェック
|
||||
assets.index.malformed=アセットインデックスの形式が正しくありません。バージョン設定の[ゲームアセットファイルの更新]で再試行できます。
|
||||
|
||||
button.cancel=キャンセル
|
||||
button.change_source=ダウンロードソースの変更
|
||||
@@ -152,8 +153,8 @@ button.yes=はい
|
||||
color.recent=推奨
|
||||
color.custom=カスタムカラー
|
||||
|
||||
crack.NoClassDefFound=「HelloMinecraft!Launcher」ソフトウェアが破損していないことを確認してください。
|
||||
crack.user_fault=OSまたはJava環境が正しくインストールされていない可能性があり、クラッシュする可能性があります。Javaランタイム環境またはコンピュータを確認してください。
|
||||
crash.NoClassDefFound=「HelloMinecraft!Launcher」ソフトウェアが破損していないことを確認してください。
|
||||
crash.user_fault=OSまたはJava環境が正しくインストールされていない可能性があり、クラッシュする可能性があります。Javaランタイム環境またはコンピュータを確認してください。
|
||||
|
||||
curse.category.0=すべて
|
||||
|
||||
@@ -313,9 +314,15 @@ fatal.apply_update_failure=ごめんなさい、Hello Minecraft! Launcher 何か
|
||||
fatal.samba=If you are trying to run HMCL in a shared folder by Samba, HMCL may not working, please try updating your Java or running HMCL in a local folder.
|
||||
fatal.illegal_char=ユーザーフォルダーのパスに不正な文字'='が含まれています, ログインアカウントやオフラインログインではスキンの変更ができなくなり。
|
||||
|
||||
|
||||
feedback=フィードバック
|
||||
feedback.channel=フィードバックチャンネル
|
||||
feedback.discord=Discord
|
||||
feedback.discord.statement=チャットに参加しよう!
|
||||
feedback.discord.statement=Discordサーバーに参加してください!
|
||||
feedback.github=GitHub Issues
|
||||
feedback.github.statement=GitHubで問題を送信します。
|
||||
feedback.qq_group=HMCLユーザーQQグループ
|
||||
feedback.qq_group.statement=ユーザーQQグループに参加してください!
|
||||
|
||||
file=ファイル
|
||||
|
||||
@@ -761,8 +768,8 @@ settings.advanced.post_exit_command=終了後のコマンド
|
||||
settings.advanced.post_exit_command.prompt=ゲーム終了後に実行されます
|
||||
settings.advanced.server_ip=サーバーアドレス
|
||||
settings.advanced.server_ip.prompt=ゲームの起動時にサーバーに参加する
|
||||
settings.advanced.use_native_glfw=システムGLFWを使用する
|
||||
settings.advanced.use_native_openal=システムOpenALを使用する
|
||||
settings.advanced.use_native_glfw=[Linux/FreeBSDのみ]システムGLFWを使用する
|
||||
settings.advanced.use_native_openal=[Linux/FreeBSDのみ]システムOpenALを使用する
|
||||
settings.advanced.workaround=デバッグ用オプション
|
||||
settings.advanced.workaround.warning=デバッグオプションはプロフェッショナルのみ使用可能です。デバッグオプションにより、ゲームが起動しない場合があります。これらのオプションは、ご自分が何をしているのかが分からない限り、変更しないでください。
|
||||
settings.advanced.wrapper_launcher=パッキングオーダー
|
||||
@@ -843,8 +850,8 @@ settings.type.special.edit=現在のゲーム設定を構成します
|
||||
settings.type.special.edit.hint=現在のゲームバージョン %s で特殊な設定が有効になっているため、現在のページのオプションはそのゲームバージョンに適用されません。リンクをクリックして、現在のゲーム設定を構成します。
|
||||
|
||||
sponsor=寄付
|
||||
sponsor.bmclapi=ダウンロードサービスはBMCLAPIによって提供されます。詳細については、ここをクリックしてください。
|
||||
sponsor.hmcl=こんにちはマインクラフト! Launcherは、無料のオープンソースのMinecraftランチャーであり、ユーザーは複数の個別のMinecraftインストールを簡単に管理できます。私たちはAfdianを使用して、ホスティングとプロジェクト開発の費用を引き続き支払います。詳細については、ここをクリックしてください。
|
||||
sponsor.bmclapi=中国本土向けのダウンロードサービスはBMCLAPIによって提供されています。詳細については、ここをクリックしてください。
|
||||
sponsor.hmcl=Hello Minecraft! Launcherは、無料のオープンソースのMinecraftランチャーであり、ユーザーは複数の個別のMinecraftインストールを簡単に管理できます。詳細については、ここをクリックしてください。
|
||||
|
||||
system.architecture=Arch
|
||||
system.operating_system=OS
|
||||
@@ -853,15 +860,14 @@ update=更新
|
||||
update.accept=更新
|
||||
update.changelog=変更
|
||||
update.channel.dev=ベータ
|
||||
update.channel.dev.hint=ベータ版を使用しています。ベータ版には、リリースバージョンと比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n\
|
||||
ベータ版は不安定なはずです!\n\
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://docs.hmcl.net/groups.html">QQ</a>で問題を報告します。\n\n\
|
||||
現在のテスト版のヒントを非表示にするには、<a href="hmcl://hide-announcement">ここをクリックしてください</a>。
|
||||
update.channel.dev.hint=ベータ版を使用しています。ベータ版には、リリースバージョンと比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。ベータ版は不安定なはずです!\n\
|
||||
\n\
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告してください。
|
||||
update.channel.dev.title=ベータ版のヒント
|
||||
update.channel.nightly=アルファ
|
||||
update.channel.nightly.hint=アルファ版を使用しています。これには、ベータ版およびリリース版と比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n\
|
||||
アルファ版は不安定です!\n\
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>で問題を報告します。
|
||||
update.channel.nightly.hint=アルファ版を使用しています。これには、ベータ版およびリリース版と比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。アルファ版は不安定です!\n\
|
||||
\n\
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告してください。
|
||||
update.channel.nightly.title=アルファ版のヒント
|
||||
update.channel.stable=リリース
|
||||
update.checking=更新の確認
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
#
|
||||
# Hello Minecraft! Launcher
|
||||
# Авторские права (C) 2023 huangyuhui <huanghongxun2008@126.com> и участников
|
||||
# Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
#
|
||||
# Эта программа является бесплатным программным обеспечением:
|
||||
# вы можете распространять и/или изменять её на условиях GNU
|
||||
# General Public License, опубликованной Free Software Foundation,
|
||||
# либо 3й версией лицензии, либо (по вашему выбору) любой версией выше.
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Эта программа распространяется в надежде, что она окажется полезной,
|
||||
# но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без подразумеваемых гарантий
|
||||
# ТОВАРНОЙ ПРИГОДНОСТИ или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ.
|
||||
# Подробности смотрите в Стандартной общественной лицензии GNU.
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# Вместе с этой программой вы должны были получить копию GNU
|
||||
# General Public License. Если нет, смотрите <https://www.gnu.org/licenses/>.
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Соавторы: vanja-san
|
||||
#
|
||||
# Contributors: vanja-san
|
||||
|
||||
about=О лаунчере
|
||||
about.copyright=Авторские права
|
||||
@@ -336,8 +335,13 @@ fatal.samba=Если вы пытаетесь запустить лаунчер
|
||||
fatal.illegal_char=Недопустимый символ '=' в пути к папке пользователя. Лаунчер может работать, но некоторые функции не будут работать.\nВы не сможете использовать аккаунт authlib-injector или изменить офлайн скин.
|
||||
|
||||
feedback=Обратная связь
|
||||
feedback.channel=Канал обратной связи
|
||||
feedback.discord=Discord
|
||||
feedback.discord.statement=Присоединиться!
|
||||
feedback.discord.statement=Добро пожаловать присоединиться к нашему Discord.
|
||||
feedback.github=Проблемы GitHub
|
||||
feedback.github.statement=Отправьте проблему на GitHub.
|
||||
feedback.qq_group=Группа QQ пользователя HMCL
|
||||
feedback.qq_group.statement=Добро пожаловать присоединиться к нашей группе QQ!
|
||||
|
||||
file=Файл
|
||||
|
||||
@@ -831,8 +835,8 @@ settings.advanced.post_exit_command=Команда после выхода
|
||||
settings.advanced.post_exit_command.prompt=Команды, которые необходимо выполнить после выхода из игры
|
||||
settings.advanced.server_ip=Адрес сервера
|
||||
settings.advanced.server_ip.prompt=Присоединяться к серверу при запуске игры
|
||||
settings.advanced.use_native_glfw=Использовать системный GLFW
|
||||
settings.advanced.use_native_openal=Использовать системный OpenAL
|
||||
settings.advanced.use_native_glfw=[Только для Linux/FreeBSD] Использовать системный GLFW
|
||||
settings.advanced.use_native_openal=[Только для Linux/FreeBSD] Использовать системный OpenAL
|
||||
settings.advanced.workaround=Обходные пути
|
||||
settings.advanced.workaround.warning=Варианты обхода предназначены только для опытных пользователей. Изменение этих параметров может привести к вылету игры. Если не знаете, что делаете, то не изменяйте эти параметры.
|
||||
settings.advanced.wrapper_launcher=Команда-оболочка
|
||||
@@ -917,8 +921,8 @@ settings.type.special.edit=Изменить настройки текущего
|
||||
settings.type.special.edit.hint=В текущем экземпляре [%s] включены раздельные настройки для экземпляров, параметры на этой странице НЕ повлияют на этот экземпляр. Нажмите здесь, чтобы изменить раздельные параметры.
|
||||
|
||||
sponsor=Спонсоры
|
||||
sponsor.bmclapi=Скачивание предоставляется BMCLAPI. Нажмите здесь для получения подробностей.
|
||||
sponsor.hmcl=Hello Minecraft! Launcher — лаунчер Minecraft с открытым исходным кодом, который позволяет пользователям легко управлять несколькими экземплярами Minecraft. Пожертвуйте нам на Afdian для поддержки нашего файлового хостинга и развития проекта. Для получения подробностей нажмите здесь.
|
||||
sponsor.bmclapi=Загрузки для материкового Китая предоставляются BMCLAPI. Нажмите здесь для получения подробностей.
|
||||
sponsor.hmcl=Hello Minecraft! Launcher — лаунчер Minecraft с открытым исходным кодом, который позволяет пользователям легко управлять несколькими экземплярами Minecraft. Для получения подробностей нажмите здесь.
|
||||
|
||||
system.architecture=Архитектура
|
||||
system.operating_system=Операционная система
|
||||
@@ -927,15 +931,14 @@ update=Обновление
|
||||
update.accept=Обновить
|
||||
update.changelog=Изменения
|
||||
update.channel.dev=Бета
|
||||
update.channel.dev.hint=Вы используете бета-версию, которая может включать некоторые дополнительные функции по сравнению с релизной версией, используемой только для тестирования.\n\
|
||||
Бета-версия может быть нестабильной!\n\
|
||||
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://docs.hmcl.net/groups.html">QQ</a>, чтобы сообщить о проблемах.\n\n\
|
||||
<a href="hmcl://hide-announcement">Нажмите здесь</a>, чтобы скрыть подсказку для текущей тестовой версии.
|
||||
update.channel.dev.hint=Вы используете бета-версию, которая может включать некоторые дополнительные функции по сравнению с релизной версией, используемой только для тестирования. Бета-версия может быть нестабильной!\n\
|
||||
\n\
|
||||
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>.
|
||||
update.channel.dev.title=Подсказки для бета-версии
|
||||
update.channel.nightly=Альфа
|
||||
update.channel.nightly.hint=Вы используете альфа-версию, которая может включать некоторые дополнительные функциональные возможности по сравнению с бета-версией и версией релиза, используемой только для тестирования.\n\
|
||||
Альфа-версия может быть нестабильной!\n\
|
||||
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://docs.hmcl.net/groups.html">QQ</a>, чтобы сообщить о проблемах.
|
||||
update.channel.nightly.hint=Вы используете альфа-версию, которая может включать некоторые дополнительные функциональные возможности по сравнению с бета-версией и версией релиза, используемой только для тестирования. Альфа-версия может быть нестабильной!\n\
|
||||
\n\
|
||||
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>.
|
||||
update.channel.nightly.title=Подсказки для альфа-версии
|
||||
update.channel.stable=Релиз
|
||||
update.checking=Проверка наличия обновлений
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -30,25 +30,25 @@
|
||||
"module": "javafx.base",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-base",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "win",
|
||||
"sha1": "f33d0776b2b027c2ea9267c184f9048ef94c4deb"
|
||||
"sha1": "ae86377359860449040372d3693bccf046148513"
|
||||
},
|
||||
{
|
||||
"module": "javafx.graphics",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-graphics",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "win",
|
||||
"sha1": "951ca77bd50ef4fd44f4b7fb1e38301088fde4e2"
|
||||
"sha1": "bc0055a059b201d6c0be2293ccf72b6d2874fd1c"
|
||||
},
|
||||
{
|
||||
"module": "javafx.controls",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-controls",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "win",
|
||||
"sha1": "78ac7de48d0aac48e7d5282efc81bab7f337b587"
|
||||
"sha1": "bcd765dca36fa9e5fb4c997491a2d908947603f5"
|
||||
}
|
||||
],
|
||||
"windows-arm64": [
|
||||
@@ -82,25 +82,25 @@
|
||||
"module": "javafx.base",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-base",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "mac",
|
||||
"sha1": "73e422d8426aaa23e8c712b9f4d9bebf70d3bfb9"
|
||||
"sha1": "e1744403a6691aaa6b65a7e9b4ff9aad98831bb1"
|
||||
},
|
||||
{
|
||||
"module": "javafx.graphics",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-graphics",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "mac",
|
||||
"sha1": "6ab6f3e23421fcfa04e692d9d26a8f55a830c114"
|
||||
"sha1": "dbe733470094abee56560067d0fea3ad24991d2b"
|
||||
},
|
||||
{
|
||||
"module": "javafx.controls",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-controls",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "mac",
|
||||
"sha1": "b7786b1b63e741c0e234829825fae5fef9d96c31"
|
||||
"sha1": "0d43a776d6c9e2622e3581ed04501e2cadcd6e65"
|
||||
}
|
||||
],
|
||||
"osx-arm64": [
|
||||
@@ -108,25 +108,25 @@
|
||||
"module": "javafx.base",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-base",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "mac-aarch64",
|
||||
"sha1": "1d0d887c492330ed527b0614d115a4f32d2d05a4"
|
||||
"sha1": "d4dc7d2864c07b56a27e39fa5691856ddccf0ba4"
|
||||
},
|
||||
{
|
||||
"module": "javafx.graphics",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-graphics",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "mac-aarch64",
|
||||
"sha1": "64db28799e61e0f8f51e471c732599b2a6961803"
|
||||
"sha1": "d6a49f19e90db56c134757d1bbab4935de075d45"
|
||||
},
|
||||
{
|
||||
"module": "javafx.controls",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-controls",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "mac-aarch64",
|
||||
"sha1": "3a14bd5f3ebe45d344c1f2bade0fe074e6ea2a83"
|
||||
"sha1": "ad564497edc49f7a6135c3b52d360962e19a2539"
|
||||
}
|
||||
],
|
||||
"linux-x86_64": [
|
||||
@@ -134,25 +134,25 @@
|
||||
"module": "javafx.base",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-base",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "linux",
|
||||
"sha1": "9e4ca5ce2b87c479d2cc445ff411a48aeae76936"
|
||||
"sha1": "875f02b498b00c9692f6ed0af3da373b1d07bf03"
|
||||
},
|
||||
{
|
||||
"module": "javafx.graphics",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-graphics",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "linux",
|
||||
"sha1": "27c64e4e1569ddf198f28eda84cf8d014ab80693"
|
||||
"sha1": "103f3bba5a177ba8912a92ef2ee0855de7ba050c"
|
||||
},
|
||||
{
|
||||
"module": "javafx.controls",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-controls",
|
||||
"version": "19.0.2.1",
|
||||
"version": "17.0.13",
|
||||
"classifier": "linux",
|
||||
"sha1": "b5b2a8ead40bd43476740aa51697bb1cac23de5c"
|
||||
"sha1": "940e9e05a974a3e484d5ec241d88a0c77ad013f2"
|
||||
}
|
||||
],
|
||||
"linux-arm32": [
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
api("org.glavo:simple-png-javafx:0.3.0")
|
||||
api("com.google.code.gson:gson:2.10.1")
|
||||
api("com.google.code.gson:gson:2.11.0")
|
||||
api("com.moandjiezana.toml:toml4j:0.7.2")
|
||||
api("org.tukaani:xz:1.9")
|
||||
api("org.hildan.fxgson:fx-gson:5.0.0")
|
||||
@@ -14,4 +14,10 @@ dependencies {
|
||||
api("org.apache.commons:commons-compress:1.25.0")
|
||||
api("org.jsoup:jsoup:1.18.1")
|
||||
compileOnlyApi("org.jetbrains:annotations:24.1.0")
|
||||
|
||||
if (JavaVersion.current().isJava8) {
|
||||
org.gradle.internal.jvm.Jvm.current().toolsJar?.let {
|
||||
compileOnly(files(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.TextureType;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.javafx.ObservableHelper;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -69,6 +70,14 @@ public abstract class Account implements Observable {
|
||||
*/
|
||||
public abstract AuthInfo playOffline() throws AuthenticationException;
|
||||
|
||||
public boolean canUploadSkin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void uploadSkin(boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException("Unsupported Operation");
|
||||
}
|
||||
|
||||
public abstract Map<Object, Object> toStorage();
|
||||
|
||||
public void clearCache() {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.TextureType;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -32,7 +33,7 @@ import java.util.UUID;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public class MicrosoftAccount extends OAuthAccount {
|
||||
public final class MicrosoftAccount extends OAuthAccount {
|
||||
|
||||
protected final MicrosoftService service;
|
||||
protected UUID characterUUID;
|
||||
@@ -125,6 +126,16 @@ public class MicrosoftAccount extends OAuthAccount {
|
||||
return session.toAuthInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUploadSkin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadSkin(boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||
service.uploadSkin(session.getAccessToken(), isSlim, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> toStorage() {
|
||||
return session.toStorage();
|
||||
@@ -150,6 +161,7 @@ public class MicrosoftAccount extends OAuthAccount {
|
||||
@Override
|
||||
public void clearCache() {
|
||||
authenticated = false;
|
||||
service.getProfileRepository().invalidate(characterUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,19 +25,18 @@ import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.OAuth;
|
||||
import org.jackhuang.hmcl.auth.ServerDisconnectException;
|
||||
import org.jackhuang.hmcl.auth.ServerResponseMalformedException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.CompleteGameProfile;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.Texture;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.TextureType;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.*;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.*;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
||||
import org.jackhuang.hmcl.util.io.*;
|
||||
import org.jackhuang.hmcl.util.javafx.ObservableOptionalCache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -261,6 +260,33 @@ public class MicrosoftService {
|
||||
return Optional.ofNullable(GSON.fromJson(request(NetworkUtils.toURL("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid)), null), CompleteGameProfile.class));
|
||||
}
|
||||
|
||||
public void uploadSkin(String accessToken, boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||
try {
|
||||
HttpURLConnection con = NetworkUtils.createHttpConnection(NetworkUtils.toURL("https://api.minecraftservices.com/minecraft/profile/skins"));
|
||||
con.setRequestMethod("POST");
|
||||
con.setRequestProperty("Authorization", "Bearer " + accessToken);
|
||||
con.setDoOutput(true);
|
||||
try (HttpMultipartRequest request = new HttpMultipartRequest(con)) {
|
||||
request.param("variant", isSlim ? "slim" : "classic");
|
||||
try (InputStream fis = Files.newInputStream(file)) {
|
||||
request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
|
||||
}
|
||||
}
|
||||
|
||||
String response = NetworkUtils.readData(con);
|
||||
if (StringUtils.isBlank(response)) {
|
||||
if (con.getResponseCode() / 100 != 2)
|
||||
throw new ResponseCodeException(con.getURL(), con.getResponseCode());
|
||||
} else {
|
||||
MinecraftErrorResponse profileResponse = GSON.fromJson(response, MinecraftErrorResponse.class);
|
||||
if (StringUtils.isNotBlank(profileResponse.errorMessage) || con.getResponseCode() / 100 != 2)
|
||||
throw new AuthenticationException("Failed to upload skin, response code: " + con.getResponseCode() + ", response: " + response);
|
||||
}
|
||||
} catch (IOException | JsonParseException e) {
|
||||
throw new AuthenticationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String request(URL url, Object payload) throws AuthenticationException {
|
||||
try {
|
||||
if (payload == null)
|
||||
@@ -272,14 +298,6 @@ public class MicrosoftService {
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T fromJson(String text, Class<T> typeOfT) throws ServerResponseMalformedException {
|
||||
try {
|
||||
return GSON.fromJson(text, typeOfT);
|
||||
} catch (JsonParseException e) {
|
||||
throw new ServerResponseMalformedException(text, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class XboxAuthorizationException extends AuthenticationException {
|
||||
private final long errorCode;
|
||||
private final String redirect;
|
||||
|
||||
@@ -203,8 +203,14 @@ public abstract class YggdrasilAccount extends ClassicAccount {
|
||||
|
||||
}
|
||||
|
||||
public void uploadSkin(String model, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||
service.uploadSkin(characterUUID, session.getAccessToken(), model, file);
|
||||
@Override
|
||||
public boolean canUploadSkin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadSkin(boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||
service.uploadSkin(characterUUID, session.getAccessToken(), isSlim, file);
|
||||
}
|
||||
|
||||
private static String randomClientToken() {
|
||||
|
||||
@@ -148,14 +148,14 @@ public class YggdrasilService {
|
||||
requireEmpty(request(provider.getInvalidationURL(), createRequestWithCredentials(accessToken, clientToken)));
|
||||
}
|
||||
|
||||
public void uploadSkin(UUID uuid, String accessToken, String model, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||
public void uploadSkin(UUID uuid, String accessToken, boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||
try {
|
||||
HttpURLConnection con = NetworkUtils.createHttpConnection(provider.getSkinUploadURL(uuid));
|
||||
con.setRequestMethod("PUT");
|
||||
con.setRequestProperty("Authorization", "Bearer " + accessToken);
|
||||
con.setDoOutput(true);
|
||||
try (HttpMultipartRequest request = new HttpMultipartRequest(con)) {
|
||||
request.param("model", model);
|
||||
request.param("model", isSlim ? "slim" : "");
|
||||
try (InputStream fis = Files.newInputStream(file)) {
|
||||
request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
|
||||
}
|
||||
|
||||
@@ -17,12 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.java;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
@@ -31,6 +26,4 @@ public interface JavaDistribution<V extends JavaRemoteVersion> {
|
||||
String getDisplayName();
|
||||
|
||||
Set<JavaPackageType> getSupportedPackageTypes();
|
||||
|
||||
Task<TreeMap<Integer, V>> getFetchJavaVersionsTask(DownloadProvider provider, Platform platform, JavaPackageType packageType);
|
||||
}
|
||||
@@ -21,10 +21,10 @@ package org.jackhuang.hmcl.download.java;
|
||||
* @author Glavo
|
||||
*/
|
||||
public enum JavaPackageType {
|
||||
JDK(true, false),
|
||||
JRE(false, false),
|
||||
JDKFX(true, true),
|
||||
JREFX(false, true);
|
||||
JDK(true, false),
|
||||
JREFX(false, true),
|
||||
JDKFX(true, true);
|
||||
|
||||
private final boolean jdk;
|
||||
private final boolean javafx;
|
||||
@@ -34,6 +34,13 @@ public enum JavaPackageType {
|
||||
this.javafx = javafx;
|
||||
}
|
||||
|
||||
public static JavaPackageType of(boolean jdk, boolean javafx) {
|
||||
if (jdk)
|
||||
return javafx ? JDKFX : JDK;
|
||||
else
|
||||
return javafx ? JREFX : JRE;
|
||||
}
|
||||
|
||||
public boolean isJDK() {
|
||||
return jdk;
|
||||
}
|
||||
|
||||
+20
-15
@@ -33,7 +33,7 @@ import java.util.*;
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public final class DiscoFetchJavaListTask extends Task<TreeMap<Integer, DiscoJavaRemoteVersion>> {
|
||||
public final class DiscoFetchJavaListTask extends Task<EnumMap<JavaPackageType, TreeMap<Integer, DiscoJavaRemoteVersion>>> {
|
||||
|
||||
public static final String API_ROOT = System.getProperty("hmcl.discoapi.override", "https://api.foojay.io/disco/v3.0");
|
||||
|
||||
@@ -46,22 +46,21 @@ public final class DiscoFetchJavaListTask extends Task<TreeMap<Integer, DiscoJav
|
||||
}
|
||||
|
||||
private final DiscoJavaDistribution distribution;
|
||||
private final String archiveType;
|
||||
private final Task<String> fetchPackagesTask;
|
||||
|
||||
public DiscoFetchJavaListTask(DownloadProvider downloadProvider, DiscoJavaDistribution distribution, Platform platform, JavaPackageType packageType) {
|
||||
public DiscoFetchJavaListTask(DownloadProvider downloadProvider, DiscoJavaDistribution distribution, Platform platform) {
|
||||
this.distribution = distribution;
|
||||
this.archiveType = platform.getOperatingSystem() == OperatingSystem.WINDOWS ? "zip" : "tar.gz";
|
||||
|
||||
HashMap<String, String> params = new HashMap<>();
|
||||
params.put("distribution", distribution.getApiParameter());
|
||||
params.put("package", packageType.isJDK() ? "jdk" : "jre");
|
||||
params.put("javafx_bundled", Boolean.toString(packageType.isJavaFXBundled()));
|
||||
params.put("operating_system", getOperatingSystemName(platform.getOperatingSystem()));
|
||||
params.put("architecture", getArchitectureName(platform.getArchitecture()));
|
||||
params.put("archive_type", platform.getOperatingSystem() == OperatingSystem.WINDOWS ? "zip" : "tar.gz");
|
||||
params.put("archive_type", archiveType);
|
||||
params.put("directly_downloadable", "true");
|
||||
if (platform.getOperatingSystem() == OperatingSystem.LINUX) {
|
||||
if (platform.getOperatingSystem() == OperatingSystem.LINUX)
|
||||
params.put("lib_c_type", "glibc");
|
||||
}
|
||||
|
||||
this.fetchPackagesTask = new GetTask(downloadProvider.injectURLWithCandidates(NetworkUtils.withQuery(API_ROOT + "/packages", params)));
|
||||
}
|
||||
@@ -74,22 +73,28 @@ public final class DiscoFetchJavaListTask extends Task<TreeMap<Integer, DiscoJav
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
String json = fetchPackagesTask.getResult();
|
||||
List<DiscoJavaRemoteVersion> result = JsonUtils.fromNonNullJson(json, DiscoResult.typeOf(DiscoJavaRemoteVersion.class)).getResult();
|
||||
List<DiscoJavaRemoteVersion> list = JsonUtils.fromNonNullJson(json, DiscoResult.typeOf(DiscoJavaRemoteVersion.class)).getResult();
|
||||
EnumMap<JavaPackageType, TreeMap<Integer, DiscoJavaRemoteVersion>> result = new EnumMap<>(JavaPackageType.class);
|
||||
|
||||
TreeMap<Integer, DiscoJavaRemoteVersion> map = new TreeMap<>();
|
||||
|
||||
for (DiscoJavaRemoteVersion version : result) {
|
||||
if (!distribution.getApiParameter().equals(version.getDistribution()))
|
||||
for (DiscoJavaRemoteVersion version : list) {
|
||||
if (!distribution.getApiParameter().equals(version.getDistribution())
|
||||
|| !version.isDirectlyDownloadable()
|
||||
|| !archiveType.equals(version.getArchiveType()))
|
||||
continue;
|
||||
|
||||
if (!distribution.testVersion(version))
|
||||
continue;
|
||||
|
||||
JavaPackageType packageType = JavaPackageType.of("jdk".equals(version.getPackageType()), version.isJavaFXBundled());
|
||||
TreeMap<Integer, DiscoJavaRemoteVersion> map = result.computeIfAbsent(packageType, ignored -> new TreeMap<>());
|
||||
|
||||
int jdkVersion = version.getJdkVersion();
|
||||
DiscoJavaRemoteVersion oldVersion = map.get(jdkVersion);
|
||||
if (oldVersion == null || VersionNumber.compare(version.getDistributionVersion(), oldVersion.getDistributionVersion()) > 0) {
|
||||
if (oldVersion == null || VersionNumber.compare(version.getDistributionVersion(), oldVersion.getDistributionVersion()) > 0)
|
||||
map.put(jdkVersion, version);
|
||||
}
|
||||
}
|
||||
|
||||
setResult(map);
|
||||
setResult(result);
|
||||
}
|
||||
|
||||
}
|
||||
+34
-15
@@ -17,10 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.java.disco;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.java.JavaDistribution;
|
||||
import org.jackhuang.hmcl.download.java.JavaPackageType;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
@@ -35,6 +33,7 @@ import static org.jackhuang.hmcl.util.platform.OperatingSystem.*;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
* @see <a href="https://github.com/foojayio/discoapi">discoapi</a>
|
||||
*/
|
||||
public enum DiscoJavaDistribution implements JavaDistribution<DiscoJavaRemoteVersion> {
|
||||
TEMURIN("Eclipse Temurin", "temurin", "Adoptium",
|
||||
@@ -42,21 +41,42 @@ public enum DiscoJavaDistribution implements JavaDistribution<DiscoJavaRemoteVer
|
||||
pair(WINDOWS, EnumSet.of(X86_64, X86, ARM64)),
|
||||
pair(LINUX, EnumSet.of(X86_64, X86, ARM64, ARM32, RISCV64, PPC64, PPC64LE, S390X, SPARCV9)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64))),
|
||||
LIBERICA("Liberica", "liberica", "BellSoft",
|
||||
LIBERICA("BellSoft Liberica", "liberica", "BellSoft",
|
||||
EnumSet.of(JDK, JRE, JDKFX, JREFX),
|
||||
pair(WINDOWS, EnumSet.of(X86_64, X86, ARM64)),
|
||||
pair(LINUX, EnumSet.of(X86_64, X86, ARM64, ARM32, RISCV64, PPC64LE)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64))) {
|
||||
@Override
|
||||
public boolean testVersion(DiscoJavaRemoteVersion version) {
|
||||
if (!super.testVersion(version))
|
||||
return false;
|
||||
|
||||
String fileName = version.getFileName();
|
||||
return !fileName.endsWith("-lite.tar.gz") && !fileName.endsWith("-lite.zip");
|
||||
}
|
||||
},
|
||||
ZULU("Azul Zulu", "zulu", "Azul",
|
||||
EnumSet.of(JDK, JRE, JDKFX, JREFX),
|
||||
pair(WINDOWS, EnumSet.of(X86_64, X86, ARM64)),
|
||||
pair(LINUX, EnumSet.of(X86_64, X86, ARM64, ARM32, RISCV64, PPC64LE)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64))),
|
||||
ZULU("Zulu", "zulu", "Azul",
|
||||
EnumSet.of(JDK, JRE, JDKFX, JREFX),
|
||||
pair(WINDOWS, EnumSet.of(X86_64, X86, ARM64)),
|
||||
pair(LINUX, EnumSet.of(X86_64, X86, ARM64, ARM32, RISCV64, PPC64LE)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64))),
|
||||
GRAALVM("GraalVM", "graalvm", "Oracle",
|
||||
GRAALVM("Oracle GraalVM", "graalvm", "Oracle",
|
||||
EnumSet.of(JDK),
|
||||
pair(WINDOWS, EnumSet.of(X86_64, X86)),
|
||||
pair(LINUX, EnumSet.of(X86_64, X86, ARM64, ARM32, RISCV64, PPC64LE)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64)));
|
||||
pair(WINDOWS, EnumSet.of(X86_64)),
|
||||
pair(LINUX, EnumSet.of(X86_64, ARM64)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64))),
|
||||
SEMERU("IBM Semeru (OpenJ9)", "semeru", "IBM",
|
||||
EnumSet.of(JDK, JRE),
|
||||
pair(WINDOWS, EnumSet.of(X86_64)),
|
||||
pair(LINUX, EnumSet.of(X86_64, ARM64, PPC64LE, S390X)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64))
|
||||
),
|
||||
CORRETTO("Amazon Corretto", "corretto", "Amazon",
|
||||
EnumSet.of(JDK),
|
||||
pair(WINDOWS, EnumSet.of(X86_64)),
|
||||
pair(LINUX, EnumSet.of(X86_64, ARM64)),
|
||||
pair(OSX, EnumSet.of(X86_64, ARM64))
|
||||
);
|
||||
|
||||
public static DiscoJavaDistribution of(String name) {
|
||||
for (DiscoJavaDistribution distribution : values()) {
|
||||
@@ -109,8 +129,7 @@ public enum DiscoJavaDistribution implements JavaDistribution<DiscoJavaRemoteVer
|
||||
return architectures != null && architectures.contains(platform.getArchitecture());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TreeMap<Integer, DiscoJavaRemoteVersion>> getFetchJavaVersionsTask(DownloadProvider provider, Platform platform, JavaPackageType packageType) {
|
||||
return new DiscoFetchJavaListTask(provider, this, platform, packageType);
|
||||
public boolean testVersion(DiscoJavaRemoteVersion version) {
|
||||
return this.getApiParameter().equals(version.getDistribution());
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -171,6 +171,10 @@ public final class DiscoJavaRemoteVersion implements JavaRemoteVersion {
|
||||
return termOfSupport;
|
||||
}
|
||||
|
||||
public boolean isLTS() {
|
||||
return "lts".equals(termOfSupport);
|
||||
}
|
||||
|
||||
public String getOperatingSystem() {
|
||||
return operatingSystem;
|
||||
}
|
||||
@@ -191,7 +195,7 @@ public final class DiscoJavaRemoteVersion implements JavaRemoteVersion {
|
||||
return packageType;
|
||||
}
|
||||
|
||||
public boolean isJavafxBundled() {
|
||||
public boolean isJavaFXBundled() {
|
||||
return javafxBundled;
|
||||
}
|
||||
|
||||
|
||||
-9
@@ -17,16 +17,12 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.java.mojang;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.java.JavaDistribution;
|
||||
import org.jackhuang.hmcl.download.java.JavaPackageType;
|
||||
import org.jackhuang.hmcl.download.java.JavaRemoteVersion;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
@@ -47,9 +43,4 @@ public final class MojangJavaDistribution implements JavaDistribution<JavaRemote
|
||||
public Set<JavaPackageType> getSupportedPackageTypes() {
|
||||
return Collections.singleton(JavaPackageType.JRE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TreeMap<Integer, JavaRemoteVersion>> getFetchJavaVersionsTask(DownloadProvider provider, Platform platform, JavaPackageType packageType) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public final class CrashReportAnalyzer {
|
||||
//https://github.com/HMCL-dev/HMCL/pull/2038
|
||||
MODMIXIN_FAILURE(Pattern.compile("(MixinApplyError|Mixin prepare failed |Mixin apply failed |mixin\\.injection\\.throwables\\.|\\.mixins\\.json\\] FAILED during \\))")),//ModMixin失败
|
||||
MIXIN_APPLY_MOD_FAILED(Pattern.compile("Mixin apply for mod (?<id>.*) failed"), "id"),//Mixin应用失败
|
||||
FORGE_ERROR(Pattern.compile("An exception was thrown, the game will display an error screen and halt\\.(?<reason>(.*)[\\n\\r]*((.*)[\\n\\r]*)+)at "), "reason"),//Forge报错,Forge可能已经提供了错误信息
|
||||
FORGE_ERROR(Pattern.compile("An exception was thrown, the game will display an error screen and halt\\.\\R*(?<reason>.*\\R*(\\s*at .*\\R)+)"), "reason"),//Forge报错,Forge可能已经提供了错误信息
|
||||
MOD_RESOLUTION0(Pattern.compile("(\tMod File:|-- MOD |\tFailure message:)")),
|
||||
FORGE_REPEAT_INSTALLATION(Pattern.compile("MultipleArgumentsForOptionException: Found multiple arguments for option (.*?), but you asked for only one")),//https://github.com/HMCL-dev/HMCL/issues/1880
|
||||
OPTIFINE_REPEAT_INSTALLATION(Pattern.compile("ResolutionException: Module optifine reads another module named optifine")),//Optifine 重复安装(及Mod文件夹有,自动安装也有)
|
||||
|
||||
@@ -126,6 +126,7 @@ public final class JavaInfo {
|
||||
return "Azul";
|
||||
case "IBM Corporation":
|
||||
case "International Business Machines Corporation":
|
||||
case "Eclipse OpenJ9":
|
||||
return "IBM";
|
||||
case "Eclipse Adoptium":
|
||||
return "Adoptium";
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Mark a method that can only be called on the JavaFX Application Thread,
|
||||
* or mark a field that can only be read/modified on the JavaFX Application Thread.
|
||||
*
|
||||
* @author Glavo
|
||||
*/
|
||||
@Documented
|
||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface FXThread {
|
||||
}
|
||||
@@ -767,4 +767,23 @@
|
||||
1.21-pre3
|
||||
1.21-pre4
|
||||
1.21-rc1
|
||||
1.21
|
||||
1.21
|
||||
1.21.1-rc1
|
||||
1.21.1
|
||||
24w33a
|
||||
24w34a
|
||||
24w35a
|
||||
24w36a
|
||||
24w37a
|
||||
24w38a
|
||||
24w39a
|
||||
24w40a
|
||||
1.21.2-pre1
|
||||
1.21.2-pre2
|
||||
1.21.2-pre3
|
||||
1.21.2-pre4
|
||||
1.21.2-pre5
|
||||
1.21.2-rc1
|
||||
1.21.2-rc2
|
||||
1.21.2
|
||||
1.21.3
|
||||
@@ -140,7 +140,8 @@ public class CrashReportAnalyzerTest {
|
||||
"\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_131]\n" +
|
||||
"\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_131]\n" +
|
||||
"\tat java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_131]\n" +
|
||||
"\tat oolloo.jlw.Wrapper.invokeMain(Wrapper.java:58) [JavaWrapper.jar:?]\n").replaceAll("\\s+", ""),
|
||||
"\tat oolloo.jlw.Wrapper.invokeMain(Wrapper.java:58) [JavaWrapper.jar:?]\n" +
|
||||
"\tat oolloo.jlw.Wrapper.main(Wrapper.java:51) [JavaWrapper.jar:?]").replaceAll("\\s+", ""),
|
||||
result.getMatcher().group("reason").replaceAll("\\s+", ""));
|
||||
}
|
||||
|
||||
|
||||
+19
-21
@@ -1,19 +1,19 @@
|
||||
# Platform Support Status
|
||||
|
||||
English | [简体中文](PLATFORM_cn.md)
|
||||
**English** | [简体中文](PLATFORM_cn.md) | [繁體中文](PLATFORM_tw.md)
|
||||
|
||||
| | Windows | Linux | Mac OS | FreeBSD |
|
||||
|----------------------------|:--------------------------------------------------|:---------------------------|:------------------------------------------------------------------------|:---------------------------|
|
||||
| | Windows | Linux | macOS | FreeBSD |
|
||||
| -------------------------- | :----------------------------------------------- | :------------------------ | :--------------------------------------------------------------------- | :------------------------ |
|
||||
| x86-64 | ✅️ | ✅️ | ✅️ | 👌 (Minecraft 1.13~1.21.1) |
|
||||
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
|
||||
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.20.6) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (use Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.1) | / | / |
|
||||
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.1) | / | / |
|
||||
| LoongArch64 (Old World) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
|
||||
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (use Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.1) | / | / |
|
||||
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.1) | / | / |
|
||||
| LoongArch64 (Old World) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
|
||||
Legend:
|
||||
|
||||
@@ -23,18 +23,16 @@ Legend:
|
||||
|
||||
* 👌: Supported platforms.
|
||||
|
||||
Support is provided by HMCL, tested to work, but may have more issues than a fully supported platform.
|
||||
Support for versions below Minecraft 1.6 is not guaranteed.
|
||||
Support is provided by HMCL, tested to work, but may have more issues than a fully supported platform.
|
||||
Support for versions below Minecraft 1.6 is not guaranteed.
|
||||
If you encounter a problem that does not exist on fully supported platforms, you can report it to HMCL.
|
||||
|
||||
* ❔: Low level supported platforms.
|
||||
|
||||
HMCL can run on this platform and has some basic support.
|
||||
However, launching the game directly is not yet available.
|
||||
If you want to start the game,
|
||||
you'll need to get the native libraries needed by Minecraft in other way and specify the native path in the instance settings.
|
||||
HMCL can run on this platform and has some basic support. However, launching the game directly is not yet available.
|
||||
If you want to start the game, you will need to get the native libraries needed by Minecraft in other way and specify the native path in the instance settings.
|
||||
|
||||
* /: Not applicable.
|
||||
* `/`: Not applicable.
|
||||
|
||||
We have no plans to support these platforms at this time, mainly because we don't have the equipment to test them.
|
||||
If you can help us adapt, please file a support request via issue.
|
||||
We have no plans to support these platforms at this time, mainly because we do not have the equipment to test them.
|
||||
If you can help us adapt, please file a support request via GitHub Issue.
|
||||
+26
-28
@@ -1,40 +1,38 @@
|
||||
# 平台支持状态
|
||||
|
||||
[English](PLATFORM.md) | 简体中文
|
||||
[English](PLATFORM.md) | **简体中文** | [繁體中文](PLATFORM_tw.md)
|
||||
|
||||
| | Windows | Linux | Mac OS | FreeBSD |
|
||||
|----------------------------|:--------------------------------------------------|:---------------------------|:-----------------------------------------------------------------------|:--------------------------|
|
||||
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.21.1) |
|
||||
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
|
||||
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| MIPS64el | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21) | / | / |
|
||||
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.1) | / | / |
|
||||
| LoongArch64 (旧世界) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
| | Windows | Linux | macOS | FreeBSD |
|
||||
| -------------------------- | :----------------------------------------------- | :------------------------ | :---------------------------------------------------------------------- | :----------------------- |
|
||||
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.21.1) |
|
||||
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
|
||||
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.1) | / | / |
|
||||
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.1) | / | / |
|
||||
| LoongArch64 (旧世界) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
|
||||
图例:
|
||||
图例:
|
||||
|
||||
* ✅: 官方支持的平台.
|
||||
* ✅: 官方支持的平台
|
||||
|
||||
受 Mojang 官方支持. 在游戏中遇到的问题应该直接向 Mojang 反馈.
|
||||
受 Mojang 官方支持。在游戏中遇到的问题应该直接向 Mojang 反馈。
|
||||
|
||||
* 👌: 支持的平台.
|
||||
* 👌: 支持的平台
|
||||
|
||||
由 HMCL 提供支持, 经过测试可以正常运行, 但可能比得到全面支持的平台有更多问题.
|
||||
不保证支持 Minecraft 1.6 以下的版本.
|
||||
如果你遇到在得到全面支持的平台上不存在的问题, 可以向 HMCL 反馈.
|
||||
由 HMCL 提供支持,经过测试可以正常运行,但可能比得到全面支持的平台有更多问题。
|
||||
不保证支持 Minecraft 1.6 以下的版本。
|
||||
如果你遇到在得到全面支持的平台上不存在的问题,可以向 HMCL 反馈。
|
||||
|
||||
* ❔: 低级别支持的平台.
|
||||
* ❔: 低级别支持的平台
|
||||
|
||||
HMCL 可以在这个平台上运行, 并且有一些基本的支持.
|
||||
但是, 还不能正常地启动游戏.
|
||||
如果你想正常启动游戏,
|
||||
则需要通过其他方式获得游戏所需的本地库(LWJGL), 并在版本设置中指定本地库路径.
|
||||
HMCL 可以在这个平台上运行,并且有一些基本的支持。但是,还不能正常地启动游戏。
|
||||
如果你想正常启动游戏,则需要通过其他方式获得游戏所需的本地库 (LWJGL),并在(全局)游戏设置中指定本地库路径。
|
||||
|
||||
* /: 不支持的平台.
|
||||
* `/`: 不支持的平台
|
||||
|
||||
我们目前还没有打算支持这些平台, 主要是因为我们没有测试这些平台的设备.
|
||||
如果你能帮助我们进行测试, 请通过提交 Issue 提出支持请求.
|
||||
我们目前还没有打算支持这些平台,主要是因为我们没有测试这些平台的设备。
|
||||
如果你能帮助我们进行测试,请通过提交 Issue 提出支持请求。
|
||||
@@ -0,0 +1,38 @@
|
||||
# 平臺支援狀態
|
||||
|
||||
[English](PLATFORM.md) | [简体中文](PLATFORM_cn.md) | **繁體中文**
|
||||
|
||||
| | Windows | Linux | macOS | FreeBSD |
|
||||
| -------------------------- | :----------------------------------------------- | :------------------------ | :---------------------------------------------------------------------- | :----------------------- |
|
||||
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.21.1) |
|
||||
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
|
||||
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.1) | / | / |
|
||||
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.1) | / | / |
|
||||
| LoongArch64 (舊世界) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
|
||||
圖例:
|
||||
|
||||
* ✅: 官方支援的平臺
|
||||
|
||||
受 Mojang 官方支援。在遊戲中遇到的問題應該直接向 Mojang 回報。
|
||||
|
||||
* 👌: 支援的平臺
|
||||
|
||||
由 HMCL 提供支援,經過測試可以正常執行,但可能比得到全面支援的平臺有更多問題。
|
||||
不保證支援 Minecraft 1.6 以下的版本。
|
||||
如果你遇到在得到全面支援的平臺上不存在的問題,可以向 HMCL 回報。
|
||||
|
||||
* ❔: 低級別支援的平臺
|
||||
|
||||
HMCL 可以在這個平臺上執行,並且有一些基本的支援。但是,還不能正常地啟動遊戲。
|
||||
如果你想正常啟動遊戲,則需要透過其他方式獲得遊戲所需的本機庫 (LWJGL),並在(全域)遊戲設定中指定本機庫路徑。
|
||||
|
||||
* `/`: 不支援的平臺
|
||||
|
||||
我們目前還沒有打算支援這些平臺,主要是因為我們沒有測試這些平臺的裝置。
|
||||
如果你能幫助我們進行測試,請透過提交 Issue 提出支援請求。
|
||||
@@ -1,21 +1,18 @@
|
||||
# ⛏ Hello Minecraft! Launcher 💎
|
||||
|
||||
[](https://ci.huangyuhui.net/job/HMCL)
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
English | [中文](README_cn.md)
|
||||
**English** | [简体中文](README_cn.md) | [繁體中文](README_tw.md)
|
||||
|
||||
## Introduction
|
||||
|
||||
HMCL is a cross-platform Minecraft launcher which supports Mod Management, Game Customizing, Auto Installing (Forge, NeoForge, Fabric, Quilt, LiteLoader and OptiFine), Modpack Creating, UI Customization, and more.
|
||||
HMCL is an open-source, cross-platform Minecraft launcher that supports Mod Management, Game Customizing, ModLoader Installing (Forge, NeoForge, Fabric, Quilt, LiteLoader and OptiFine), Modpack Creating, UI Customization, and more.
|
||||
|
||||
HMCL has amazing cross-platform capabilities.
|
||||
It can not only run on different operating systems such as Windows, Linux, and macOS,
|
||||
but also supports multiple CPU architectures such as x86, arm, mips, and loongarch.
|
||||
You can easily play Minecraft on different platforms through HMCL.
|
||||
HMCL has amazing cross-platform capabilities. Not only does it run on different operating systems like Windows, Linux, and macOS, but it also supports various CPU architectures such as x86, ARM, MIPS, and LoongArch. You can easily enjoy Minecraft across different platforms through HMCL.
|
||||
|
||||
For systems and CPU architectures supported by HMCL, see [this table](PLATFORM.md).
|
||||
|
||||
@@ -23,13 +20,13 @@ For systems and CPU architectures supported by HMCL, see [this table](PLATFORM.m
|
||||
|
||||
Download the latest version from [the official website](https://hmcl.huangyuhui.net/download).
|
||||
|
||||
You can also download the latest version of HMCL in [GitHub Releases](https://github.com/HMCL-dev/HMCL/releases).
|
||||
You can also find the latest version of HMCL in [GitHub Releases](https://github.com/HMCL-dev/HMCL/releases).
|
||||
|
||||
Although not necessary, it is recommended to download the ones from the official website.
|
||||
Although not necessary, it is recommended only to download releases from the official websites listed above.
|
||||
|
||||
## License
|
||||
|
||||
The software is distributed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html) with additional terms.
|
||||
The software is distributed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html) license with the following additional terms:
|
||||
|
||||
### Additional terms under GPLv3 Section 7
|
||||
|
||||
@@ -41,9 +38,9 @@ The software is distributed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.h
|
||||
|
||||
## Contribution
|
||||
|
||||
If you want to submit a pull request, there are some requirements:
|
||||
If you want to submit a pull request, here are some requirements:
|
||||
|
||||
* IDE: Intellij IDEA
|
||||
* IDE: IntelliJ IDEA
|
||||
* Compiler: Java 1.8 or Java 11+
|
||||
* Do NOT modify `gradle` files
|
||||
|
||||
@@ -55,20 +52,20 @@ Simply execute the following command in project root directory:
|
||||
./gradlew clean build
|
||||
```
|
||||
|
||||
Make sure you have Java installed with JavaFX 8 at least. Liberica Full JDK 8 or later is recommended.
|
||||
Make sure you have at least JavaFX 8 installed. Liberica Full JDK 8 or later is recommended.
|
||||
|
||||
## JVM Options (for debugging)
|
||||
|
||||
| Parameter | Description |
|
||||
|----------------------------------------------|------------------------------------------------------------------------------------------------|
|
||||
| `-Dhmcl.home=<path>` | Override HMCL directory. |
|
||||
| `-Dhmcl.self_integrity_check.disable=true` | Bypass the self integrity check when checking for update. |
|
||||
| `-Dhmcl.bmclapi.override=<url>` | Override API Root of BMCLAPI download provider, defaults to `https://bmclapi2.bangbang93.com`. |
|
||||
| `-Dhmcl.font.override=<font family>` | Override font family. |
|
||||
| `-Dhmcl.version.override=<version>` | Override the version number. |
|
||||
| `-Dhmcl.update_source.override=<url>` | Override the update source for HMCL itself. |
|
||||
| `-Dhmcl.authlibinjector.location=<path>` | Use specified authlib-injector (instead of downloading one). |
|
||||
| `-Dhmcl.openjfx.repo=<maven repository url>` | Add custom Maven repository for download OpenJFX. |
|
||||
| `-Dhmcl.native.encoding=<encoding>` | Override the native encoding. |
|
||||
| `-Dhmcl.microsoft.auth.id=<App ID>` | Override Microsoft OAuth App ID. |
|
||||
| `-Dhmcl.microsoft.auth.secret=<App Secret>` | Override Microsoft OAuth App secret. |
|
||||
| Parameter | Description |
|
||||
| -------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| `-Dhmcl.home=<path>` | Override HMCL directory |
|
||||
| `-Dhmcl.self_integrity_check.disable=true` | Bypass the self integrity check when checking for update |
|
||||
| `-Dhmcl.bmclapi.override=<url>` | Override API Root of BMCLAPI download provider, defaults to `https://bmclapi2.bangbang93.com` |
|
||||
| `-Dhmcl.font.override=<font family>` | Override font family |
|
||||
| `-Dhmcl.version.override=<version>` | Override the version number |
|
||||
| `-Dhmcl.update_source.override=<url>` | Override the update source for HMCL itself |
|
||||
| `-Dhmcl.authlibinjector.location=<path>` | Use specified authlib-injector (instead of downloading one) |
|
||||
| `-Dhmcl.openjfx.repo=<maven repository url>` | Add custom Maven repository for download OpenJFX |
|
||||
| `-Dhmcl.native.encoding=<encoding>` | Override the native encoding |
|
||||
| `-Dhmcl.microsoft.auth.id=<App ID>` | Override Microsoft OAuth App ID |
|
||||
| `-Dhmcl.microsoft.auth.secret=<App Secret>` | Override Microsoft OAuth App secret |
|
||||
+32
-33
@@ -1,72 +1,71 @@
|
||||
# ⛏ Hello Minecraft! Launcher 💎
|
||||
|
||||
[](https://ci.huangyuhui.net/job/HMCL)
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
[English](README.md) | 中文
|
||||
[English](README.md) | **简体中文** | [繁體中文](README_tw.md)
|
||||
|
||||
## 简介
|
||||
|
||||
HMCL 是一款跨平台 Minecraft 启动器, 支持 Mod 管理, 游戏自定义, 游戏自动安装 (Forge, NeoForge, Fabric, Quilt, LiteLoader 与 OptiFine), 整合包创建, 界面自定义等功能.
|
||||
HMCL 是一款开源、跨平台的 Minecraft 启动器,支持模组管理、游戏自定义、游戏自动安装 (Forge、NeoForge、Fabric、Quilt、LiteLoader 和 OptiFine)、整合包创建、界面自定义等功能。
|
||||
|
||||
HMCL 有着强大的跨平台能力. 它不仅支持 Windows、Linux、macOS 等常见的操作系统,同时也支持 x86、ARM、MIPS 和 LoongArch 等不同的 CPU 架构.
|
||||
您可以使用 HMCL 在不同平台上轻松的游玩 Minecraft.
|
||||
HMCL 有着强大的跨平台能力。它不仅支持 Windows、Linux、macOS 等常见的操作系统,同时也支持 x86、ARM、MIPS 和 LoongArch 等不同的 CPU 架构。你可以使用 HMCL 在不同平台上轻松地游玩 Minecraft。
|
||||
|
||||
如果您想要了解 HMCL 对不同平台的支持程度,请参见[此表格](PLATFORM_cn.md).
|
||||
如果你想要了解 HMCL 对不同平台的支持程度,请参见 [此表格](PLATFORM_cn.md)。
|
||||
|
||||
## 下载
|
||||
|
||||
请从 [HMCL 官网](https://hmcl.huangyuhui.net/download) 下载最新版本的 HMCL.
|
||||
请从 [HMCL 官网](https://hmcl.huangyuhui.net/download) 下载最新版本的 HMCL。
|
||||
|
||||
你也可以在 [GitHub Releases](https://github.com/HMCL-dev/HMCL/releases) 中下载最新版本的 HMCL.
|
||||
你也可以在 [GitHub Releases](https://github.com/HMCL-dev/HMCL/releases) 中下载最新版本的 HMCL。
|
||||
|
||||
虽然并不强制, 但仍建议通过 HMCL 官网下载启动器.
|
||||
虽然并不强制,但仍建议通过 HMCL 官网下载启动器。
|
||||
|
||||
## 开源协议
|
||||
|
||||
该程序在 [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html) 开源协议下发布, 同时附有附加条款.
|
||||
该程序在 [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html) 开源协议下发布,同时附有以下附加条款。
|
||||
|
||||
### 附加条款 (依据 GPLv3 开源协议第七条)
|
||||
|
||||
1. 当您分发该程序的修改版本时, 您必须以一种合理的方式修改该程序的名称或版本号, 以示其与原始版本不同. (依据 [GPLv3, 7(c)](https://github.com/HMCL-dev/HMCL/blob/11820e31a85d8989e41d97476712b07e7094b190/LICENSE#L372-L374))
|
||||
1. 当你分发该程序的修改版本时,你必须以一种合理的方式修改该程序的名称或版本号,以示其与原始版本不同。(依据 [GPLv3, 7(c)](https://github.com/HMCL-dev/HMCL/blob/11820e31a85d8989e41d97476712b07e7094b190/LICENSE#L372-L374))
|
||||
|
||||
该程序的名称及版本号可在[此处](https://github.com/HMCL-dev/HMCL/blob/javafx/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java#L33-L35)修改.
|
||||
该程序的名称及版本号可在 [此处](https://github.com/HMCL-dev/HMCL/blob/javafx/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java#L33-L35) 修改。
|
||||
|
||||
2. 您不得移除该程序所显示的版权声明. (依据 [GPLv3, 7(b)](https://github.com/HMCL-dev/HMCL/blob/11820e31a85d8989e41d97476712b07e7094b190/LICENSE#L368-L370))
|
||||
2. 你不得移除该程序所显示的版权声明。(依据 [GPLv3, 7(b)](https://github.com/HMCL-dev/HMCL/blob/11820e31a85d8989e41d97476712b07e7094b190/LICENSE#L368-L370))
|
||||
|
||||
## 贡献
|
||||
|
||||
如果您想提交一个 Pull Request, 必须遵守如下要求:
|
||||
如果你想提交一个 Pull Request,必须遵守如下要求:
|
||||
|
||||
* IDE: Intellij IDEA
|
||||
* 编译器: Java 1.8 或 Java 11+
|
||||
* IDE:IntelliJ IDEA
|
||||
* 编译器:Java 1.8 或 Java 11+
|
||||
* **不要**修改 `gradle` 相关文件
|
||||
|
||||
### 编译
|
||||
|
||||
于项目根目录执行以下命令:
|
||||
于项目根文件夹执行以下命令:
|
||||
|
||||
```bash
|
||||
./gradlew clean build
|
||||
```
|
||||
|
||||
请确保您至少安装了含有 JavaFX 8 的 Java. 建议使用 Liberica Full JDK 8 或更高版本.
|
||||
请确保你至少安装了含有 JavaFX 8 的 Java。建议使用 Liberica Full JDK 8 或更高版本。
|
||||
|
||||
## JVM 选项 (用于调试)
|
||||
|
||||
| 参数 | 简介 |
|
||||
|----------------------------------------------|----------------------------------------------------------------|
|
||||
| `-Dhmcl.home=<path>` | 覆盖 HMCL 数据文件夹. |
|
||||
| `-Dhmcl.self_integrity_check.disable=true` | 检查更新时绕过本体完整性检查. |
|
||||
| `-Dhmcl.bmclapi.override=<url>` | 覆盖 BMCLAPI 的 API Root, 默认值为 `https://bmclapi2.bangbang93.com`. |
|
||||
| `-Dhmcl.font.override=<font family>` | 覆盖字族. |
|
||||
| `-Dhmcl.version.override=<version>` | 覆盖版本号. |
|
||||
| `-Dhmcl.update_source.override=<url>` | 覆盖 HMCL 更新源. |
|
||||
| `-Dhmcl.authlibinjector.location=<path>` | 使用指定的 authlib-injector (而非下载一个). |
|
||||
| `-Dhmcl.openjfx.repo=<maven repository url>` | 添加用于下载 OpenJFX 的自定义 Maven 仓库 |
|
||||
| `-Dhmcl.native.encoding=<encoding>` | 覆盖原生编码. |
|
||||
| `-Dhmcl.microsoft.auth.id=<App ID>` | 覆盖 Microsoft OAuth App ID. |
|
||||
| `-Dhmcl.microsoft.auth.secret=<App Secret>` | 覆盖 Microsoft OAuth App 密钥. |
|
||||
| 参数 | 简介 |
|
||||
| -------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| `-Dhmcl.home=<path>` | 覆盖 HMCL 数据文件夹 |
|
||||
| `-Dhmcl.self_integrity_check.disable=true` | 检查更新时绕过本体完整性检查 |
|
||||
| `-Dhmcl.bmclapi.override=<url>` | 覆盖 BMCLAPI 的 API Root,默认值为 `https://bmclapi2.bangbang93.com` |
|
||||
| `-Dhmcl.font.override=<font family>` | 覆盖字族 |
|
||||
| `-Dhmcl.version.override=<version>` | 覆盖版本号 |
|
||||
| `-Dhmcl.update_source.override=<url>` | 覆盖 HMCL 更新源 |
|
||||
| `-Dhmcl.authlibinjector.location=<path>` | 使用指定的 authlib-injector (而非下载一个) |
|
||||
| `-Dhmcl.openjfx.repo=<maven repository url>` | 添加用于下载 OpenJFX 的自定义 Maven 仓库 |
|
||||
| `-Dhmcl.native.encoding=<encoding>` | 覆盖原生编码 |
|
||||
| `-Dhmcl.microsoft.auth.id=<App ID>` | 覆盖 Microsoft OAuth App ID |
|
||||
| `-Dhmcl.microsoft.auth.secret=<App Secret>` | 覆盖 Microsoft OAuth App 密钥 |
|
||||
@@ -0,0 +1,71 @@
|
||||
# ⛏ Hello Minecraft! Launcher 💎
|
||||
|
||||
[](https://ci.huangyuhui.net/job/HMCL)
|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
[English](README.md) | [简体中文](README_cn.md) | **繁體中文**
|
||||
|
||||
## 簡介
|
||||
|
||||
HMCL 是一款開源、跨平臺的 Minecraft 啟動器,支援模組管理、遊戲自訂、遊戲自動安裝 (Forge、NeoForge、Fabric、Quilt、LiteLoader 和 OptiFine)、模組包建立、介面自訂等功能。
|
||||
|
||||
HMCL 有著強大的跨平臺能力。它不僅支援 Windows、Linux、macOS 等常見的作業系統,同時也支援 x86、ARM、MIPS 和 LoongArch 等不同的 CPU 架構。你可以使用 HMCL 在不同平臺上輕鬆地遊玩 Minecraft。
|
||||
|
||||
如果你想要了解 HMCL 對不同平臺的支援程度,請參見 [此表格](PLATFORM_tw.md)。
|
||||
|
||||
## 下載
|
||||
|
||||
請從 [HMCL 官網](https://hmcl.huangyuhui.net/download) 下載最新版本的 HMCL。
|
||||
|
||||
你也可以在 [GitHub Releases](https://github.com/HMCL-dev/HMCL/releases) 中下載最新版本的 HMCL。
|
||||
|
||||
雖然並不強制,但仍建議透過 HMCL 官網下載啟動器。
|
||||
|
||||
## 開源協議
|
||||
|
||||
該程式在 [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html) 開源協議下發布,同時附有以下附加條款。
|
||||
|
||||
### 附加條款 (依據 GPLv3 開源協議第七條)
|
||||
|
||||
1. 當你分發該程式的修改版本時,你必須以一種合理的方式修改該程式的名稱或版本號,以示其與原始版本不同。(依據 [GPLv3, 7(c)](https://github.com/HMCL-dev/HMCL/blob/11820e31a85d8989e41d97476712b07e7094b190/LICENSE#L372-L374))
|
||||
|
||||
該程式的名稱及版本號可在 [此處](https://github.com/HMCL-dev/HMCL/blob/javafx/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java#L33-L35) 修改。
|
||||
|
||||
2. 你不得移除該程式所顯示的版權宣告。(依據 [GPLv3, 7(b)](https://github.com/HMCL-dev/HMCL/blob/11820e31a85d8989e41d97476712b07e7094b190/LICENSE#L368-L370))
|
||||
|
||||
## 貢獻
|
||||
|
||||
如果你想提交一個 Pull Request,必須遵守如下要求:
|
||||
|
||||
* IDE:IntelliJ IDEA
|
||||
* 編譯器:Java 1.8 或 Java 11+
|
||||
* **不要**修改 `gradle` 相關文件
|
||||
|
||||
### 編譯
|
||||
|
||||
於項目根目錄執行以下指令:
|
||||
|
||||
```bash
|
||||
./gradlew clean build
|
||||
```
|
||||
|
||||
請確保你至少安裝了含有 JavaFX 8 的 Java。建議使用 Liberica Full JDK 8 或更高版本。
|
||||
|
||||
## JVM 選項 (用於除錯)
|
||||
|
||||
| 參數 | 簡介 |
|
||||
| -------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| `-Dhmcl.home=<path>` | 覆蓋 HMCL 使用者目錄 |
|
||||
| `-Dhmcl.self_integrity_check.disable=true` | 檢查更新時繞過本體完整性檢查 |
|
||||
| `-Dhmcl.bmclapi.override=<url>` | 覆蓋 BMCLAPI 的 API Root,預設值為 `https://bmclapi2.bangbang93.com` |
|
||||
| `-Dhmcl.font.override=<font family>` | 覆蓋字族 |
|
||||
| `-Dhmcl.version.override=<version>` | 覆蓋版本號 |
|
||||
| `-Dhmcl.update_source.override=<url>` | 覆蓋 HMCL 更新來源 |
|
||||
| `-Dhmcl.authlibinjector.location=<path>` | 使用指定的 authlib-injector (而非下載一個) |
|
||||
| `-Dhmcl.openjfx.repo=<maven repository url>` | 添加用於下載 OpenJFX 的自訂 Maven 倉庫 |
|
||||
| `-Dhmcl.native.encoding=<encoding>` | 覆蓋原生編碼 |
|
||||
| `-Dhmcl.microsoft.auth.id=<App ID>` | 覆蓋 Microsoft OAuth App ID |
|
||||
| `-Dhmcl.microsoft.auth.secret=<App Secret>` | 覆蓋 Microsoft OAuth App 金鑰 |
|
||||
Vendored
BIN
Binary file not shown.
+2
-2
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
networkTimeout=10000
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
networkTimeout=120000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -84,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
Vendored
+12
-10
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
+6
-5
@@ -4,11 +4,12 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("com.google.code.gson:gson:2.10.1")
|
||||
classpath("com.google.code.gson:gson:2.11.0")
|
||||
}
|
||||
}
|
||||
|
||||
val jfxVersion = "19.0.2.1"
|
||||
val jfxVersion = "17.0.13"
|
||||
val oldJfxVersion = "19.0.2.1"
|
||||
|
||||
data class Platform(
|
||||
val name: String,
|
||||
@@ -29,14 +30,14 @@ val jfxModules = listOf("base", "graphics", "controls")
|
||||
val jfxMirrorRepos = listOf("https://mirrors.cloud.tencent.com/nexus/repository/maven-public")
|
||||
val jfxDependenciesFile = project.file("HMCL/src/main/resources/assets/openjfx-dependencies.json")
|
||||
val jfxPlatforms = listOf(
|
||||
Platform("windows-x86", "win-x86"),
|
||||
Platform("windows-x86", "win-x86", version = oldJfxVersion),
|
||||
Platform("windows-x86_64", "win"),
|
||||
Platform("windows-arm64", "win", groupId = "org.glavo.hmcl.openjfx", version = "18.0.2+1-arm64"),
|
||||
Platform("osx-x86_64", "mac"),
|
||||
Platform("osx-arm64", "mac-aarch64"),
|
||||
Platform("linux-x86_64", "linux"),
|
||||
Platform("linux-arm32", "linux-arm32-monocle"),
|
||||
Platform("linux-arm64", "linux-aarch64"),
|
||||
Platform("linux-arm32", "linux-arm32-monocle", version = oldJfxVersion),
|
||||
Platform("linux-arm64", "linux-aarch64", version = oldJfxVersion),
|
||||
Platform("linux-loongarch64", "linux", groupId = "org.glavo.hmcl.openjfx", version = "17.0.8-loongarch64"),
|
||||
Platform("linux-loongarch64_ow", "linux", groupId = "org.glavo.hmcl.openjfx", version = "19-ea+10-loongson64"),
|
||||
Platform("linux-riscv64", "linux", groupId = "org.glavo.hmcl.openjfx", version = "19.0.2.1-riscv64"),
|
||||
|
||||
Reference in new issue
Block a user