Compare commits
65
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a230ef6b5 | ||
|
|
c12ae9fdf8 | ||
|
|
773ef4282c | ||
|
|
9edfe29659 | ||
|
|
5591d92e25 | ||
|
|
65bdb29e71 | ||
|
|
355df211db | ||
|
|
fa66549f53 | ||
|
|
6e48dbd6c6 | ||
|
|
e3c7278759 | ||
|
|
dd1829d8ca | ||
|
|
60ac16a3ea | ||
|
|
b400a9a3ef | ||
|
|
c7cc2d0733 | ||
|
|
861223e909 | ||
|
|
70ecbc734e | ||
|
|
2ca7e7c830 | ||
|
|
9a0db56ac3 | ||
|
|
c1ae5875ba | ||
|
|
1d0ae427bf | ||
|
|
3dde2d19e2 | ||
|
|
d5ec6d7b85 | ||
|
|
13856b71a0 | ||
|
|
c6658771ca | ||
|
|
406f36584d | ||
|
|
898750d058 | ||
|
|
09e9e934d3 | ||
|
|
3d170267b7 | ||
|
|
3ca7e3cf88 | ||
|
|
1378d1445a | ||
|
|
16d304dbb4 | ||
|
|
aff5db7759 | ||
|
|
0697aa9755 | ||
|
|
995d3bd4b9 | ||
|
|
3bf5204e79 | ||
|
|
0b1ce6e94a | ||
|
|
a66ccd7385 | ||
|
|
7a3f125802 | ||
|
|
4a8dcce3d4 | ||
|
|
adcc3eb43f | ||
|
|
8bbd1a7cf4 | ||
|
|
467ff26278 | ||
|
|
d6a267afa4 | ||
|
|
743e934436 | ||
|
|
f89ac2231a | ||
|
|
1877a18ebb | ||
|
|
c99f85d9d5 | ||
|
|
228942c60a | ||
|
|
fecd567eab | ||
|
|
602840519b | ||
|
|
959adf1075 | ||
|
|
422f681fe6 | ||
|
|
474d298fd3 | ||
|
|
4052136217 | ||
|
|
3193e02867 | ||
|
|
c7122b3fff | ||
|
|
8775194d26 | ||
|
|
b3f27cc96e | ||
|
|
3313a1ed27 | ||
|
|
da66622994 | ||
|
|
446cdc32d7 | ||
|
|
b08d2b0dc0 | ||
|
|
e051c212d1 | ||
|
|
7645a600ef | ||
|
|
9e56b5cd15 |
No files matched your search
@@ -54,12 +54,14 @@ jobs:
|
||||
echo "" >> RELEASE_NOTE
|
||||
echo "| File Name | SHA-256 Checksum |" >> RELEASE_NOTE
|
||||
echo "| --- | --- |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.exe | $(cat HMCL-$HMCL_VERSION.exe.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.jar | $(cat HMCL-$HMCL_VERSION.jar.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.sh | $(cat HMCL-$HMCL_VERSION.sh.sha256) |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.exe]($GH_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.exe) | $(cat HMCL-$HMCL_VERSION.exe.sha256) |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.jar]($GH_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.jar) | $(cat HMCL-$HMCL_VERSION.jar.sha256) |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.sh]($GH_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.sh) | $(cat HMCL-$HMCL_VERSION.sh.sha256) |" >> RELEASE_NOTE
|
||||
env:
|
||||
GH_DOWNLOAD_BASE_URL: https://github.com/HMCL-dev/HMCL/releases/download
|
||||
- name: Create release
|
||||
if: ${{ env.continue == 'true' }}
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body_path: RELEASE_NOTE
|
||||
files: |
|
||||
@@ -69,6 +71,7 @@ jobs:
|
||||
target_commitish: ${{ env.HMCL_COMMIT_SHA }}
|
||||
name: ${{ env.HMCL_TAG_NAME }}
|
||||
tag_name: ${{ env.HMCL_TAG_NAME }}
|
||||
prerelease: ture
|
||||
stable-check-update:
|
||||
if: ${{ github.repository_owner == 'HMCL-dev' }}
|
||||
needs: dev-check-update
|
||||
@@ -118,12 +121,14 @@ jobs:
|
||||
echo "" >> RELEASE_NOTE
|
||||
echo "| File Name | SHA-256 Checksum |" >> RELEASE_NOTE
|
||||
echo "| --- | --- |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.exe | $(cat HMCL-$HMCL_VERSION.exe.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.jar | $(cat HMCL-$HMCL_VERSION.jar.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.sh | $(cat HMCL-$HMCL_VERSION.sh.sha256) |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.exe]($GH_DOWNLOAD_BASE_URL/release-$HMCL_VERSION/HMCL-$HMCL_VERSION.exe) | $(cat HMCL-$HMCL_VERSION.exe.sha256) |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.jar]($GH_DOWNLOAD_BASE_URL/release-$HMCL_VERSION/HMCL-$HMCL_VERSION.jar) | $(cat HMCL-$HMCL_VERSION.jar.sha256) |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.sh]($GH_DOWNLOAD_BASE_URL/release-$HMCL_VERSION/HMCL-$HMCL_VERSION.sh) | $(cat HMCL-$HMCL_VERSION.sh.sha256) |" >> RELEASE_NOTE
|
||||
env:
|
||||
GH_DOWNLOAD_BASE_URL: https://github.com/HMCL-dev/HMCL/releases/download
|
||||
- name: Create release
|
||||
if: ${{ env.continue == 'true' }}
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body_path: RELEASE_NOTE
|
||||
files: |
|
||||
|
||||
+1
-19
@@ -25,7 +25,7 @@ val buildNumber = System.getenv("BUILD_NUMBER")?.toInt().let { number ->
|
||||
if (!shortCommit.isNullOrEmpty()) "$prefix-$shortCommit" else "SNAPSHOT"
|
||||
}
|
||||
}
|
||||
val versionRoot = System.getenv("VERSION_ROOT") ?: "3.5"
|
||||
val versionRoot = System.getenv("VERSION_ROOT") ?: "3.6"
|
||||
val versionType = System.getenv("VERSION_TYPE") ?: if (isOfficial) "nightly" else "unofficial"
|
||||
|
||||
val microsoftAuthId = System.getenv("MICROSOFT_AUTH_ID") ?: ""
|
||||
@@ -167,24 +167,6 @@ fun createExecutable(suffix: String, header: String) {
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
fun convertToBSS(resource: String) {
|
||||
doFirst {
|
||||
val cssFile = File(projectDir, "src/main/resources/$resource")
|
||||
val bssFile = File(projectDir, "build/compiled-resources/${resource.substring(0, resource.length - 4)}.bss")
|
||||
bssFile.parentFile.mkdirs()
|
||||
javaexec {
|
||||
classpath = sourceSets["main"].compileClasspath
|
||||
mainClass.set("com.sun.javafx.css.parser.Css2Bin")
|
||||
args(cssFile, bssFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
from("build/compiled-resources")
|
||||
|
||||
convertToBSS("assets/css/root.css")
|
||||
convertToBSS("assets/css/blue.css")
|
||||
|
||||
into("META-INF/versions/11") {
|
||||
from(sourceSets["java11"].output)
|
||||
}
|
||||
|
||||
@@ -36,10 +36,14 @@ import org.jackhuang.hmcl.mod.ModpackProvider;
|
||||
import org.jackhuang.hmcl.setting.*;
|
||||
import org.jackhuang.hmcl.task.*;
|
||||
import org.jackhuang.hmcl.ui.*;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||
import org.jackhuang.hmcl.ui.construct.PromptDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
||||
import org.jackhuang.hmcl.util.platform.*;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
@@ -60,9 +64,10 @@ import static javafx.application.Platform.runLater;
|
||||
import static javafx.application.Platform.setImplicitExit;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.Lang.resolveException;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.platform.Platform.*;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.platform.Platform.SYSTEM_PLATFORM;
|
||||
import static org.jackhuang.hmcl.util.platform.Platform.isCompatibleWithX86Java;
|
||||
|
||||
public final class LauncherHelper {
|
||||
|
||||
@@ -178,6 +183,9 @@ public final class LauncherHelper {
|
||||
.thenComposeAsync(() -> logIn(account).withStage("launch.state.logging_in"))
|
||||
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> {
|
||||
LaunchOptions launchOptions = repository.getLaunchOptions(selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, scriptFile != null);
|
||||
|
||||
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModManager(selectedVersion).getModsDirectory(), 10));
|
||||
|
||||
return new HMCLGameLauncher(
|
||||
repository,
|
||||
version.get(),
|
||||
@@ -522,6 +530,9 @@ public final class LauncherHelper {
|
||||
case MODDED_JAVA_17:
|
||||
suggestions.add(i18n("launch.advice.modded_java", 17, gameVersion));
|
||||
break;
|
||||
case MODDED_JAVA_21:
|
||||
suggestions.add(i18n("launch.advice.modded_java", 21, gameVersion));
|
||||
break;
|
||||
case VANILLA_JAVA_8_51:
|
||||
suggestions.add(i18n("launch.advice.java8_51_1_13"));
|
||||
break;
|
||||
|
||||
@@ -24,9 +24,8 @@ import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -36,11 +35,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Base64;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
@@ -58,34 +56,6 @@ public class Theme {
|
||||
Color.web("#B71C1C") // red
|
||||
};
|
||||
|
||||
private static Charset cssCharset;
|
||||
|
||||
private static Charset getCssCharset() {
|
||||
if (cssCharset != null)
|
||||
return cssCharset;
|
||||
|
||||
Charset defaultCharset = Charset.defaultCharset();
|
||||
if (defaultCharset != StandardCharsets.UTF_8) {
|
||||
// https://bugs.openjdk.org/browse/JDK-8279328
|
||||
// For JavaFX 17 or earlier, native encoding should be used
|
||||
String jfxVersion = System.getProperty("javafx.version");
|
||||
if (jfxVersion != null) {
|
||||
Matcher matcher = Pattern.compile("^(?<version>[0-9]+)").matcher(jfxVersion);
|
||||
if (matcher.find()) {
|
||||
int v = Lang.parseInt(matcher.group(), -1);
|
||||
if (v >= 18) {
|
||||
cssCharset = StandardCharsets.UTF_8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cssCharset == null)
|
||||
cssCharset = defaultCharset;
|
||||
|
||||
return cssCharset;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static Optional<Font> font;
|
||||
|
||||
@@ -145,6 +115,14 @@ public class Theme {
|
||||
return isLight() ? Color.BLACK : Color.WHITE;
|
||||
}
|
||||
|
||||
private static String rgba(Color color, double opacity) {
|
||||
return String.format("rgba(%d, %d, %d, %.1f)",
|
||||
(int) Math.ceil(color.getRed() * 256),
|
||||
(int) Math.ceil(color.getGreen() * 256),
|
||||
(int) Math.ceil(color.getBlue() * 256),
|
||||
opacity);
|
||||
}
|
||||
|
||||
public String[] getStylesheets(String overrideFontFamily) {
|
||||
String css = "/assets/css/blue.css";
|
||||
|
||||
@@ -163,30 +141,44 @@ public class Theme {
|
||||
|
||||
if (fontFamily != null || !this.color.equalsIgnoreCase(BLUE.color)) {
|
||||
Color textFill = getForegroundColor();
|
||||
String fontCss = "";
|
||||
if (fontFamily != null) {
|
||||
fontCss = "-fx-font-family: \"" + fontFamily + "\";";
|
||||
if (fontStyle != null && !fontStyle.isEmpty())
|
||||
fontCss += " -fx-font-style: \"" + fontStyle + "\";";
|
||||
}
|
||||
|
||||
try {
|
||||
File temp = File.createTempFile("hmcl", ".css");
|
||||
String themeText = IOUtils.readFullyAsString(Theme.class.getResourceAsStream("/assets/css/custom.css"))
|
||||
.replace("%base-color%", color)
|
||||
.replace("%base-red%", Integer.toString((int) Math.ceil(paint.getRed() * 256)))
|
||||
.replace("%base-green%", Integer.toString((int) Math.ceil(paint.getGreen() * 256)))
|
||||
.replace("%base-blue%", Integer.toString((int) Math.ceil(paint.getBlue() * 256)))
|
||||
.replace("%base-rippler-color%", String.format("rgba(%d, %d, %d, 0.3)", (int) Math.ceil(paint.getRed() * 256), (int) Math.ceil(paint.getGreen() * 256), (int) Math.ceil(paint.getBlue() * 256)))
|
||||
.replace("%disabled-font-color%", String.format("rgba(%d, %d, %d, 0.7)", (int) Math.ceil(textFill.getRed() * 256), (int) Math.ceil(textFill.getGreen() * 256), (int) Math.ceil(textFill.getBlue() * 256)))
|
||||
.replace("%font-color%", getColorDisplayName(getForegroundColor()))
|
||||
.replace("%font%", fontCss);
|
||||
FileUtils.writeText(temp, themeText, getCssCharset());
|
||||
temp.deleteOnExit();
|
||||
css = temp.toURI().toString();
|
||||
} catch (IOException | NullPointerException e) {
|
||||
LOG.error("Unable to create theme stylesheet. Fallback to blue theme.", e);
|
||||
}
|
||||
StringBuilder themeBuilder = new StringBuilder(512);
|
||||
themeBuilder.append(".root {")
|
||||
.append("-fx-base-color:").append(color).append(';')
|
||||
.append("-fx-base-darker-color: derive(-fx-base-color, -10%);")
|
||||
.append("-fx-base-check-color: derive(-fx-base-color, 30%);")
|
||||
.append("-fx-rippler-color:").append(rgba(paint, 0.3)).append(';')
|
||||
.append("-fx-base-rippler-color: derive(").append(rgba(paint, 0.3)).append(", 100%);")
|
||||
.append("-fx-base-disabled-text-fill:").append(rgba(textFill, 0.7)).append(";")
|
||||
.append("-fx-base-text-fill:").append(getColorDisplayName(getForegroundColor())).append(";")
|
||||
.append("-theme-thumb:").append(rgba(paint, 0.7)).append(";");
|
||||
|
||||
if (fontFamily == null)
|
||||
// https://github.com/HMCL-dev/HMCL/pull/3423
|
||||
themeBuilder.append("-fx-font-family: -fx-base-font-family;");
|
||||
else
|
||||
themeBuilder.append("-fx-font-family:\"").append(fontFamily).append("\";");
|
||||
|
||||
if (fontStyle != null && !fontStyle.isEmpty())
|
||||
themeBuilder.append("-fx-font-style:\"").append(fontStyle).append("\";");
|
||||
|
||||
themeBuilder.append('}');
|
||||
|
||||
if (FXUtils.JAVAFX_MAJOR_VERSION >= 17)
|
||||
// JavaFX 17+ support loading stylesheets from data URIs
|
||||
// https://bugs.openjdk.org/browse/JDK-8267554
|
||||
css = "data:text/css;charset=UTF-8;base64," + Base64.getEncoder().encodeToString(themeBuilder.toString().getBytes(StandardCharsets.UTF_8));
|
||||
else
|
||||
try {
|
||||
File temp = File.createTempFile("hmcl", ".css");
|
||||
// For JavaFX 17 or earlier, CssParser uses the default charset
|
||||
// https://bugs.openjdk.org/browse/JDK-8279328
|
||||
FileUtils.writeText(temp, themeBuilder.toString(), Charset.defaultCharset());
|
||||
temp.deleteOnExit();
|
||||
css = temp.toURI().toString();
|
||||
} catch (IOException | NullPointerException e) {
|
||||
LOG.error("Unable to create theme stylesheet. Fallback to blue theme.", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new String[]{css, "/assets/css/root.css"};
|
||||
@@ -235,7 +227,7 @@ public class Theme {
|
||||
}
|
||||
|
||||
public static String getColorDisplayName(Color c) {
|
||||
return c != null ? String.format("#%02x%02x%02x", Math.round(c.getRed() * 255.0D), Math.round(c.getGreen() * 255.0D), Math.round(c.getBlue() * 255.0D)).toUpperCase(Locale.ROOT) : null;
|
||||
return c != null ? String.format("#%02X%02X%02X", Math.round(c.getRed() * 255.0D), Math.round(c.getGreen() * 255.0D), Math.round(c.getBlue() * 255.0D)) : null;
|
||||
}
|
||||
|
||||
private static ObjectBinding<Color> FOREGROUND_FILL;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.glavo.png.javafx.PNGJavaFXUtils;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
@@ -72,8 +73,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
@@ -85,6 +84,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
@@ -96,6 +97,20 @@ public final class FXUtils {
|
||||
private FXUtils() {
|
||||
}
|
||||
|
||||
public static final int JAVAFX_MAJOR_VERSION;
|
||||
|
||||
static {
|
||||
String jfxVersion = System.getProperty("javafx.version");
|
||||
int majorVersion = -1;
|
||||
if (jfxVersion != null) {
|
||||
Matcher matcher = Pattern.compile("^(?<version>[0-9]+)").matcher(jfxVersion);
|
||||
if (matcher.find()) {
|
||||
majorVersion = Lang.parseInt(matcher.group(), -1);
|
||||
}
|
||||
}
|
||||
JAVAFX_MAJOR_VERSION = majorVersion;
|
||||
}
|
||||
|
||||
public static final String DEFAULT_MONOSPACE_FONT = OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "Consolas" : "Monospace";
|
||||
|
||||
private static final Map<String, Image> builtinImageCache = new ConcurrentHashMap<>();
|
||||
@@ -298,8 +313,12 @@ public final class FXUtils {
|
||||
ScrollUtils.addSmoothScrolling(scrollPane);
|
||||
}
|
||||
|
||||
private static final Duration TOOLTIP_FAST_SHOW_DELAY = Duration.millis(50);
|
||||
private static final Duration TOOLTIP_SLOW_SHOW_DELAY = Duration.millis(500);
|
||||
private static final Duration TOOLTIP_SHOW_DURATION = Duration.millis(5000);
|
||||
|
||||
public static void installFastTooltip(Node node, Tooltip tooltip) {
|
||||
installTooltip(node, 50, 5000, 0, tooltip);
|
||||
runInFX(() -> TooltipInstaller.INSTALLER.installTooltip(node, TOOLTIP_FAST_SHOW_DELAY, TOOLTIP_SHOW_DURATION, Duration.ZERO, tooltip));
|
||||
}
|
||||
|
||||
public static void installFastTooltip(Node node, String tooltip) {
|
||||
@@ -307,39 +326,13 @@ public final class FXUtils {
|
||||
}
|
||||
|
||||
public static void installSlowTooltip(Node node, Tooltip tooltip) {
|
||||
installTooltip(node, 500, 5000, 0, tooltip);
|
||||
runInFX(() -> TooltipInstaller.INSTALLER.installTooltip(node, TOOLTIP_SLOW_SHOW_DELAY, TOOLTIP_SHOW_DURATION, Duration.ZERO, tooltip));
|
||||
}
|
||||
|
||||
public static void installSlowTooltip(Node node, String tooltip) {
|
||||
installSlowTooltip(node, new Tooltip(tooltip));
|
||||
}
|
||||
|
||||
public static void installTooltip(Node node, double openDelay, double visibleDelay, double closeDelay, Tooltip tooltip) {
|
||||
runInFX(() -> {
|
||||
try {
|
||||
// Java 8
|
||||
Class<?> behaviorClass = Class.forName("javafx.scene.control.Tooltip$TooltipBehavior");
|
||||
Constructor<?> behaviorConstructor = behaviorClass.getDeclaredConstructor(Duration.class, Duration.class, Duration.class, boolean.class);
|
||||
behaviorConstructor.setAccessible(true);
|
||||
Object behavior = behaviorConstructor.newInstance(new Duration(openDelay), new Duration(visibleDelay), new Duration(closeDelay), false);
|
||||
Method installMethod = behaviorClass.getDeclaredMethod("install", Node.class, Tooltip.class);
|
||||
installMethod.setAccessible(true);
|
||||
installMethod.invoke(behavior, node, tooltip);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
try {
|
||||
// Java 9
|
||||
Tooltip.class.getMethod("setShowDelay", Duration.class).invoke(tooltip, new Duration(openDelay));
|
||||
Tooltip.class.getMethod("setShowDuration", Duration.class).invoke(tooltip, new Duration(visibleDelay));
|
||||
Tooltip.class.getMethod("setHideDelay", Duration.class).invoke(tooltip, new Duration(closeDelay));
|
||||
} catch (ReflectiveOperationException e2) {
|
||||
e.addSuppressed(e2);
|
||||
LOG.error("Cannot install tooltip", e);
|
||||
}
|
||||
Tooltip.install(node, tooltip);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void playAnimation(Node node, String animationKey, Timeline timeline) {
|
||||
animationKey = "FXUTILS.ANIMATION." + animationKey;
|
||||
Object oldTimeline = node.getProperties().get(animationKey);
|
||||
@@ -957,6 +950,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);
|
||||
@@ -987,7 +989,7 @@ public final class FXUtils {
|
||||
if ("a".equals(element.getTagName())) {
|
||||
String href = element.getAttribute("href");
|
||||
Text text = new Text(element.getTextContent());
|
||||
text.setOnMouseClicked(e -> {
|
||||
onClicked(text, () -> {
|
||||
String link = href;
|
||||
try {
|
||||
link = new URI(href).toASCIIString();
|
||||
@@ -996,8 +998,8 @@ public final class FXUtils {
|
||||
hyperlinkAction.accept(link);
|
||||
});
|
||||
text.setCursor(Cursor.HAND);
|
||||
text.setFill(Color.web("#0070E0"));
|
||||
text.setUnderline(true);
|
||||
text.setFill(Color.web("#283593"));
|
||||
texts.add(text);
|
||||
} else if ("b".equals(element.getTagName())) {
|
||||
Text text = new Text(element.getTextContent());
|
||||
|
||||
@@ -65,7 +65,6 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
@@ -387,13 +386,13 @@ public class GameCrashWindow extends Stage {
|
||||
gameDir.getStyleClass().setAll("two-line-item-second-large");
|
||||
gameDir.setTitle(i18n("game.directory"));
|
||||
gameDir.setSubtitle(launchOptions.getGameDir().getAbsolutePath());
|
||||
runInFX(() -> FXUtils.installFastTooltip(gameDir, i18n("game.directory")));
|
||||
FXUtils.installFastTooltip(gameDir, i18n("game.directory"));
|
||||
|
||||
TwoLineListItem javaDir = new TwoLineListItem();
|
||||
javaDir.getStyleClass().setAll("two-line-item-second-large");
|
||||
javaDir.setTitle(i18n("settings.game.java_directory"));
|
||||
javaDir.setSubtitle(launchOptions.getJava().getBinary().toAbsolutePath().toString());
|
||||
runInFX(() -> FXUtils.installFastTooltip(javaDir, i18n("settings.game.java_directory")));
|
||||
FXUtils.installFastTooltip(javaDir, i18n("settings.game.java_directory"));
|
||||
|
||||
Label reasonTitle = new Label(i18n("game.crash.reason"));
|
||||
reasonTitle.getStyleClass().add("two-line-item-second-large-title");
|
||||
@@ -417,14 +416,14 @@ 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"));
|
||||
runInFX(() -> FXUtils.installFastTooltip(helpButton, i18n("logwindow.help")));
|
||||
FXUtils.installFastTooltip(helpButton, i18n("logwindow.help"));
|
||||
|
||||
|
||||
toolBar.setPadding(new Insets(8));
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public enum SVG {
|
||||
CANCEL("M12 2C17.5 2 22 6.5 22 12S17.5 22 12 22 2 17.5 2 12 6.5 2 12 2M12 4C10.1 4 8.4 4.6 7.1 5.7L18.3 16.9C19.3 15.5 20 13.8 20 12C20 7.6 16.4 4 12 4M16.9 18.3L5.7 7.1C4.6 8.4 4 10.1 4 12C4 16.4 7.6 20 12 20C13.9 20 15.6 19.4 16.9 18.3Z"),
|
||||
CHAT("M20,2A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H6L2,22V4C2,2.89 2.9,2 4,2H20M4,4V17.17L5.17,16H20V4H4M6,7H18V9H6V7M6,11H15V13H6V11Z"),
|
||||
CLOSE("M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"),
|
||||
COPY("M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"),
|
||||
COPY("M15 16H7V6h8m.4-2H6.6A1.6 1.6 90 005 5.6V16.4a1.6 1.6 90 001.6 1.6h8.8A1.6 1.6 90 0017 16.4V5.6A1.6 1.6 90 0015.4 4M13 1H3.6A1.6 1.6 90 002 2.6V13h2V3H13V1Z"),
|
||||
DOTS_VERTICAL("M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"),
|
||||
DOTS_HORIZONTAL("M16,12A2,2 0 0,1 18,10A2,2 0 0,1 20,12A2,2 0 0,1 18,14A2,2 0 0,1 16,12M10,12A2,2 0 0,1 12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12M4,12A2,2 0 0,1 6,10A2,2 0 0,1 8,12A2,2 0 0,1 6,14A2,2 0 0,1 4,12Z"),
|
||||
DELETE("M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"),
|
||||
@@ -59,6 +59,7 @@ public enum SVG {
|
||||
ALERT_OUTLINE("M12,2L1,21H23M12,6L19.53,19H4.47M11,10V14H13V10M11,16V18H13V16"),
|
||||
PLUS("M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"),
|
||||
PLUS_CIRCLE_OUTLINE("M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,7H11V11H7V13H11V17H13V13H17V11H13V7Z"),
|
||||
MINUS("M19 13H5V11H19V13Z"),
|
||||
IMPORT_ICON("M14,12L10,8V11H2V13H10V16M20,18V6C20,4.89 19.1,4 18,4H6A2,2 0 0,0 4,6V9H6V6H18V18H6V15H4V18A2,2 0 0,0 6,20H18A2,2 0 0,0 20,18Z"),
|
||||
EXPORT("M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z"),
|
||||
OPEN_IN_NEW("M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public class TooltipInstaller {
|
||||
public static final TooltipInstaller INSTALLER;
|
||||
|
||||
static {
|
||||
TooltipInstaller installer = null;
|
||||
|
||||
try {
|
||||
installer = new NewInstaller();
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
installer = new OldInstaller();
|
||||
} catch (Throwable e2) {
|
||||
e2.addSuppressed(e);
|
||||
LOG.warning("Failed to initialize TooltipInstaller", e2);
|
||||
}
|
||||
}
|
||||
|
||||
INSTALLER = installer != null ? installer : new TooltipInstaller();
|
||||
}
|
||||
|
||||
TooltipInstaller() {
|
||||
}
|
||||
|
||||
public void installTooltip(Node node, Duration showDelay, Duration showDuration, Duration hideDelay, Tooltip tooltip) {
|
||||
Tooltip.install(node, tooltip);
|
||||
}
|
||||
|
||||
// For Java 8
|
||||
private static final class OldInstaller extends TooltipInstaller {
|
||||
private static final Constructor<?> createTooltipBehavior;
|
||||
private static final Method installTooltipBehavior;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> behaviorClass = Class.forName("javafx.scene.control.Tooltip$TooltipBehavior");
|
||||
createTooltipBehavior = behaviorClass.getDeclaredConstructor(Duration.class, Duration.class, Duration.class, boolean.class);
|
||||
createTooltipBehavior.setAccessible(true);
|
||||
installTooltipBehavior = behaviorClass.getDeclaredMethod("install", Node.class, Tooltip.class);
|
||||
installTooltipBehavior.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installTooltip(Node node, Duration showDelay, Duration showDuration, Duration hideDelay, Tooltip tooltip) {
|
||||
try {
|
||||
Object behavior = createTooltipBehavior.newInstance(showDelay, showDuration, hideDelay, false);
|
||||
installTooltipBehavior.invoke(behavior, node, tooltip);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
LOG.warning("Failed to set tooltip show delay", e);
|
||||
Tooltip.install(node, tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For Java 9+
|
||||
private static final class NewInstaller extends TooltipInstaller {
|
||||
private static final MethodHandle setTooltipShowDelay;
|
||||
private static final MethodHandle setTooltipShowDuration;
|
||||
private static final MethodHandle setTooltipHideDelay;
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
||||
MethodType methodType = MethodType.methodType(void.class, Duration.class);
|
||||
|
||||
setTooltipShowDelay = lookup.findVirtual(Tooltip.class, "setShowDelay", methodType);
|
||||
setTooltipShowDuration = lookup.findVirtual(Tooltip.class, "setShowDuration", methodType);
|
||||
setTooltipHideDelay = lookup.findVirtual(Tooltip.class, "setHideDelay", methodType);
|
||||
} catch (Exception e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installTooltip(Node node, Duration showDelay, Duration showDuration, Duration hideDelay, Tooltip tooltip) {
|
||||
try {
|
||||
setTooltipShowDelay.invokeExact(tooltip, showDelay);
|
||||
setTooltipShowDuration.invokeExact(tooltip, showDuration);
|
||||
setTooltipHideDelay.invokeExact(tooltip, hideDelay);
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Failed to set tooltip show delay", e);
|
||||
}
|
||||
|
||||
Tooltip.install(node, tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,9 @@ import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
public final class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
|
||||
public AccountListItemSkin(AccountListItem skinnable) {
|
||||
super(skinnable);
|
||||
@@ -94,7 +93,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()) {
|
||||
@@ -118,10 +117,10 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
btnMove.getStyleClass().add("toggle-icon4");
|
||||
if (skinnable.getAccount().isPortable()) {
|
||||
btnMove.setGraphic(SVG.EARTH.createIcon(Theme.blackFill(), -1, -1));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnMove, i18n("account.move_to_global")));
|
||||
FXUtils.installFastTooltip(btnMove, i18n("account.move_to_global"));
|
||||
} else {
|
||||
btnMove.setGraphic(SVG.EXPORT.createIcon(Theme.blackFill(), -1, -1));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnMove, i18n("account.move_to_portable")));
|
||||
FXUtils.installFastTooltip(btnMove, i18n("account.move_to_portable"));
|
||||
}
|
||||
spinnerMove.setContent(btnMove);
|
||||
right.getChildren().add(spinnerMove);
|
||||
@@ -129,7 +128,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 -> {
|
||||
@@ -143,13 +142,13 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
});
|
||||
btnRefresh.getStyleClass().add("toggle-icon4");
|
||||
btnRefresh.setGraphic(SVG.REFRESH.createIcon(Theme.blackFill(), -1, -1));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnRefresh, i18n("button.refresh")));
|
||||
FXUtils.installFastTooltip(btnRefresh, i18n("button.refresh"));
|
||||
spinnerRefresh.setContent(btnRefresh);
|
||||
right.getChildren().add(spinnerRefresh);
|
||||
|
||||
JFXButton btnUpload = new JFXButton();
|
||||
SpinnerPane spinnerUpload = new SpinnerPane();
|
||||
btnUpload.setOnMouseClicked(e -> {
|
||||
btnUpload.setOnAction(e -> {
|
||||
Task<?> uploadTask = skinnable.uploadSkin();
|
||||
if (uploadTask != null) {
|
||||
spinnerUpload.showSpinner();
|
||||
@@ -160,7 +159,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
});
|
||||
btnUpload.getStyleClass().add("toggle-icon4");
|
||||
btnUpload.setGraphic(SVG.HANGER.createIcon(Theme.blackFill(), -1, -1));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnUpload, i18n("account.skin.upload")));
|
||||
FXUtils.installFastTooltip(btnUpload, i18n("account.skin.upload"));
|
||||
spinnerUpload.managedProperty().bind(spinnerUpload.visibleProperty());
|
||||
spinnerUpload.visibleProperty().bind(skinnable.canUploadSkin());
|
||||
spinnerUpload.setContent(btnUpload);
|
||||
@@ -170,18 +169,19 @@ 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()));
|
||||
btnUpload.getStyleClass().add("toggle-icon4");
|
||||
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")));
|
||||
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));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnRemove, i18n("button.delete")));
|
||||
btnRemove.setGraphic(SVG.DELETE_OUTLINE.createIcon(Theme.blackFill(), -1, -1));
|
||||
FXUtils.installFastTooltip(btnRemove, i18n("button.delete"));
|
||||
right.getChildren().add(btnRemove);
|
||||
root.setRight(right);
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement
|
||||
confirmServerPane.setActions(prevButton, cancelButton, finishButton);
|
||||
}
|
||||
|
||||
this.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
||||
this.setContent(addServerPane, ContainerAnimations.NONE);
|
||||
|
||||
lblCreationWarning.maxWidthProperty().bind(((FlowPane) lblCreationWarning.getParent()).widthProperty());
|
||||
btnAddNext.disableProperty().bind(txtServerUrl.textProperty().isEmpty());
|
||||
@@ -198,7 +198,7 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement
|
||||
|
||||
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverBeingAdded.getUrl()).getProtocol()));
|
||||
|
||||
this.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
|
||||
this.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT);
|
||||
} else {
|
||||
LOG.warning("Failed to resolve auth server: " + url, exception);
|
||||
lblCreationWarning.setText(resolveFetchExceptionMessage(exception));
|
||||
@@ -208,7 +208,7 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement
|
||||
}
|
||||
|
||||
private void onAddPrev() {
|
||||
this.setContent(addServerPane, ContainerAnimations.SWIPE_RIGHT.getAnimationProducer());
|
||||
this.setContent(addServerPane, ContainerAnimations.SWIPE_RIGHT);
|
||||
}
|
||||
|
||||
private void onAddFinish() {
|
||||
|
||||
@@ -297,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());
|
||||
}
|
||||
@@ -317,7 +317,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
JFXHyperlink deauthorizeLink = new JFXHyperlink(i18n("account.methods.microsoft.deauthorize"));
|
||||
deauthorizeLink.setExternalLink("https://account.live.com/consent/Edit?client_id=000000004C794E0A");
|
||||
JFXHyperlink forgotpasswordLink = new JFXHyperlink(i18n("account.methods.forgot_password"));
|
||||
forgotpasswordLink.setExternalLink("https://www.minecraft.net/password/forgot");
|
||||
forgotpasswordLink.setExternalLink("https://account.live.com/ResetPassword.aspx");
|
||||
JFXHyperlink createProfileLink = new JFXHyperlink(i18n("account.methods.microsoft.makegameidsettings"));
|
||||
createProfileLink.setExternalLink("https://www.minecraft.net/msaprofile/mygames/editprofile");
|
||||
box.getChildren().setAll(profileLink, birthLink, purchaseLink, deauthorizeLink, forgotpasswordLink, createProfileLink);
|
||||
@@ -510,7 +510,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
|
||||
if (factory instanceof OfflineAccountFactory) {
|
||||
txtUsername.setPromptText(i18n("account.methods.offline.name.special_characters"));
|
||||
runInFX(() -> FXUtils.installFastTooltip(txtUsername, i18n("account.methods.offline.name.special_characters")));
|
||||
FXUtils.installFastTooltip(txtUsername, i18n("account.methods.offline.name.special_characters"));
|
||||
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.microsoft.purchase"));
|
||||
purchaseLink.setExternalLink(YggdrasilService.PURCHASE_URL);
|
||||
@@ -658,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());
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.scene.layout.*;
|
||||
import moe.mickey.minecraft.skin.fx.SkinCanvas;
|
||||
import moe.mickey.minecraft.skin.fx.animation.SkinAniRunning;
|
||||
import moe.mickey.minecraft.skin.fx.animation.SkinAniWavingArms;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinCanvas;
|
||||
import org.jackhuang.hmcl.ui.skin.animation.SkinAniRunning;
|
||||
import org.jackhuang.hmcl.ui.skin.animation.SkinAniWavingArms;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.Skin;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2020 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
|
||||
@@ -27,165 +27,225 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public enum ContainerAnimations {
|
||||
NONE(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
}, c -> Collections.emptyList()),
|
||||
public enum ContainerAnimations implements AnimationProducer {
|
||||
NONE {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A fade between the old and new view
|
||||
*/
|
||||
FADE(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(0);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
FADE {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 0, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 1, Interpolator.EASE_BOTH)))),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 1, Interpolator.EASE_BOTH)));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A fade between the old and new view
|
||||
*/
|
||||
FADE_IN(c -> {
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(0);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
FADE_IN {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 0, FXUtils.SINE)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 1, FXUtils.SINE)))),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 1, FXUtils.SINE)));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A fade between the old and new view
|
||||
*/
|
||||
FADE_OUT(c -> {
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
FADE_OUT {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 1, FXUtils.SINE)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 0, FXUtils.SINE)))),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 0, FXUtils.SINE)));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_IN(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
ZOOM_IN {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getPreviousNode().scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 1, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getPreviousNode().scaleXProperty(), 4, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().scaleYProperty(), 4, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH)))),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH)));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_OUT(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
}, c ->
|
||||
(Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
ZOOM_OUT {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getPreviousNode().scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 1, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getPreviousNode().scaleXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().scaleYProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH))))),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH)));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_LEFT(c -> {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(c.getCurrentRoot().getWidth());
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
SWIPE_LEFT {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(c.getCurrentRoot().getWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)))),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_RIGHT(c -> {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(-c.getCurrentRoot().getWidth());
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
SWIPE_RIGHT {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(-c.getCurrentRoot().getWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)))),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)));
|
||||
}
|
||||
},
|
||||
|
||||
SWIPE_LEFT_FADE_SHORT(c -> {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(c.getCurrentRoot().getWidth());
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
SWIPE_LEFT_FADE_SHORT {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(c.getCurrentRoot().getWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), 50, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 0, Interpolator.EASE_BOTH),
|
||||
@@ -194,19 +254,26 @@ public enum ContainerAnimations {
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), -50, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH)))),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH)));
|
||||
}
|
||||
},
|
||||
|
||||
SWIPE_RIGHT_FADE_SHORT(c -> {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(c.getCurrentRoot().getWidth());
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
SWIPE_RIGHT_FADE_SHORT {
|
||||
@Override
|
||||
public void init(AnimationHandler c) {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(c.getCurrentRoot().getWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler c) {
|
||||
return Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), -50, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 0, Interpolator.EASE_BOTH),
|
||||
@@ -215,38 +282,12 @@ public enum ContainerAnimations {
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), 50, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH))));
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH)));
|
||||
}
|
||||
};
|
||||
|
||||
private final AnimationProducer animationProducer;
|
||||
private ContainerAnimations opposite;
|
||||
|
||||
ContainerAnimations(Consumer<AnimationHandler> init, Function<AnimationHandler, List<KeyFrame>> animationProducer) {
|
||||
this.animationProducer = new AnimationProducer() {
|
||||
@Override
|
||||
public void init(AnimationHandler handler) {
|
||||
init.accept(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler handler) {
|
||||
return animationProducer.apply(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable AnimationProducer opposite() {
|
||||
return opposite != null ? opposite.getAnimationProducer() : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public AnimationProducer getAnimationProducer() {
|
||||
return animationProducer;
|
||||
}
|
||||
|
||||
public ContainerAnimations getOpposite() {
|
||||
return opposite;
|
||||
}
|
||||
|
||||
static {
|
||||
NONE.opposite = NONE;
|
||||
FADE.opposite = FADE;
|
||||
@@ -257,4 +298,15 @@ public enum ContainerAnimations {
|
||||
ZOOM_IN.opposite = ZOOM_OUT;
|
||||
ZOOM_OUT.opposite = ZOOM_IN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void init(AnimationHandler handler);
|
||||
|
||||
@Override
|
||||
public abstract List<KeyFrame> animate(AnimationHandler handler);
|
||||
|
||||
@Override
|
||||
public @Nullable ContainerAnimations opposite() {
|
||||
return opposite;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -34,6 +35,8 @@ public class FontComboBox extends JFXComboBox<String> {
|
||||
private boolean loaded = false;
|
||||
|
||||
public FontComboBox() {
|
||||
setMinWidth(260);
|
||||
|
||||
styleProperty().bind(Bindings.concat("-fx-font-family: \"", valueProperty(), "\""));
|
||||
|
||||
setCellFactory(listView -> new JFXListCell<String>() {
|
||||
@@ -51,7 +54,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();
|
||||
});
|
||||
|
||||
@@ -17,7 +17,6 @@ import javafx.scene.layout.Priority;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.util.Lazy;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
public class IconedTwoLineListItem extends HBox {
|
||||
@@ -29,13 +28,8 @@ public class IconedTwoLineListItem extends HBox {
|
||||
|
||||
private final ImageView imageView = new ImageView();
|
||||
private final TwoLineListItem twoLineListItem = new TwoLineListItem();
|
||||
private final Lazy<JFXButton> externalLinkButton = new Lazy<>(() -> {
|
||||
JFXButton button = new JFXButton();
|
||||
button.getStyleClass().add("toggle-icon4");
|
||||
button.setGraphic(SVG.OPEN_IN_NEW.createIcon(Theme.blackFill(), -1, -1));
|
||||
button.setOnAction(e -> FXUtils.openLink(externalLink.get()));
|
||||
return button;
|
||||
});
|
||||
private JFXButton externalLinkButton;
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final InvalidationListener observer;
|
||||
|
||||
@@ -52,7 +46,7 @@ public class IconedTwoLineListItem extends HBox {
|
||||
getChildren().clear();
|
||||
if (image.get() != null) getChildren().add(imageView);
|
||||
getChildren().add(twoLineListItem);
|
||||
if (StringUtils.isNotBlank(externalLink.get())) getChildren().add(externalLinkButton.get());
|
||||
if (StringUtils.isNotBlank(externalLink.get())) getChildren().add(getExternalLinkButton());
|
||||
}, image, externalLink);
|
||||
}
|
||||
|
||||
@@ -111,4 +105,14 @@ public class IconedTwoLineListItem extends HBox {
|
||||
public ImageView getImageView() {
|
||||
return imageView;
|
||||
}
|
||||
|
||||
public JFXButton getExternalLinkButton() {
|
||||
if (externalLinkButton == null) {
|
||||
externalLinkButton = new JFXButton();
|
||||
externalLinkButton.getStyleClass().add("toggle-icon4");
|
||||
externalLinkButton.setGraphic(SVG.OPEN_IN_NEW.createIcon(Theme.blackFill(), -1, -1));
|
||||
externalLinkButton.setOnAction(e -> FXUtils.openLink(externalLink.get()));
|
||||
}
|
||||
return externalLinkButton;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ public class Navigator extends TransitionPane {
|
||||
if (obj instanceof AnimationProducer) {
|
||||
setContent(node, (AnimationProducer) obj);
|
||||
} else {
|
||||
setContent(node, ContainerAnimations.NONE.getAnimationProducer());
|
||||
setContent(node, ContainerAnimations.NONE);
|
||||
}
|
||||
|
||||
NavigationEvent navigated = new NavigationEvent(this, node, Navigation.NavigationDirection.PREVIOUS, NavigationEvent.NAVIGATED);
|
||||
|
||||
@@ -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) {
|
||||
@@ -148,12 +146,12 @@ public class SpinnerPane extends Control {
|
||||
|
||||
observer = FXUtils.observeWeak(() -> {
|
||||
if (getSkinnable().getFailedReason() != null) {
|
||||
root.setContent(failedPane, ContainerAnimations.FADE.getAnimationProducer());
|
||||
root.setContent(failedPane, ContainerAnimations.FADE);
|
||||
failedReasonLabel.setText(getSkinnable().getFailedReason());
|
||||
} else if (getSkinnable().isLoading()) {
|
||||
root.setContent(topPane, ContainerAnimations.FADE.getAnimationProducer());
|
||||
root.setContent(topPane, ContainerAnimations.FADE);
|
||||
} else {
|
||||
root.setContent(contentPane, ContainerAnimations.FADE.getAnimationProducer());
|
||||
root.setContent(contentPane, ContainerAnimations.FADE);
|
||||
}
|
||||
}, getSkinnable().loadingProperty(), getSkinnable().failedReasonProperty());
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
@@ -443,7 +457,7 @@ public class DecoratorController {
|
||||
public void startWizard(WizardProvider wizardProvider, String category) {
|
||||
FXUtils.checkFxUserThread();
|
||||
|
||||
navigator.navigate(new DecoratorWizardDisplayer(wizardProvider, category), ContainerAnimations.FADE.getAnimationProducer());
|
||||
navigator.navigate(new DecoratorWizardDisplayer(wizardProvider, category), ContainerAnimations.FADE);
|
||||
}
|
||||
|
||||
// ==== Authlib Injector DnD ====
|
||||
|
||||
@@ -18,10 +18,8 @@
|
||||
package org.jackhuang.hmcl.ui.decorator;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.svg.SVGGlyph;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Bounds;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
@@ -44,13 +42,8 @@ import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.ui.wizard.Navigation;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
public class DecoratorSkin extends SkinBase<Decorator> {
|
||||
private static final PseudoClass TRANSPARENT = PseudoClass.getPseudoClass("transparent");
|
||||
private static final SVGGlyph minus = Lang.apply(new SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE),
|
||||
glyph -> { glyph.setSize(12, 2); glyph.setTranslateY(4); });
|
||||
|
||||
private final StackPane root, parent;
|
||||
private final StackPane titleContainer;
|
||||
private final Stage primaryStage;
|
||||
@@ -69,8 +62,6 @@ public class DecoratorSkin extends SkinBase<Decorator> {
|
||||
|
||||
primaryStage = control.getPrimaryStage();
|
||||
|
||||
minus.fillProperty().bind(Theme.foregroundFillBinding());
|
||||
|
||||
Decorator skinnable = getSkinnable();
|
||||
root = new StackPane();
|
||||
root.getStyleClass().add("window");
|
||||
@@ -174,11 +165,11 @@ public class DecoratorSkin extends SkinBase<Decorator> {
|
||||
if (s.isAnimate()) {
|
||||
AnimationProducer animation;
|
||||
if (skinnable.getNavigationDirection() == Navigation.NavigationDirection.NEXT) {
|
||||
animation = ContainerAnimations.SWIPE_LEFT_FADE_SHORT.getAnimationProducer();
|
||||
animation = ContainerAnimations.SWIPE_LEFT_FADE_SHORT;
|
||||
} else if (skinnable.getNavigationDirection() == Navigation.NavigationDirection.PREVIOUS) {
|
||||
animation = ContainerAnimations.SWIPE_RIGHT_FADE_SHORT.getAnimationProducer();
|
||||
animation = ContainerAnimations.SWIPE_RIGHT_FADE_SHORT;
|
||||
} else {
|
||||
animation = ContainerAnimations.FADE.getAnimationProducer();
|
||||
animation = ContainerAnimations.FADE;
|
||||
}
|
||||
skinnable.setNavigationDirection(Navigation.NavigationDirection.START);
|
||||
navBarPane.setContent(node, animation);
|
||||
@@ -202,9 +193,7 @@ public class DecoratorSkin extends SkinBase<Decorator> {
|
||||
btnHelp.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/help.html"));
|
||||
|
||||
JFXButton btnMin = new JFXButton();
|
||||
StackPane pane = new StackPane(minus);
|
||||
pane.setAlignment(Pos.CENTER);
|
||||
btnMin.setGraphic(pane);
|
||||
btnMin.setGraphic(SVG.MINUS.createIcon(Theme.foregroundFillBinding(), -1, -1));
|
||||
btnMin.getStyleClass().add("jfx-decorator-button");
|
||||
btnMin.setOnAction(e -> skinnable.minimize());
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public abstract class DecoratorTabPage extends DecoratorTransitionPage implement
|
||||
if (newValue.getNode() != null) {
|
||||
onNavigating(getCurrentPage());
|
||||
if (getCurrentPage() != null) getCurrentPage().fireEvent(new Navigator.NavigationEvent(null, getCurrentPage(), Navigation.NavigationDirection.NEXT, Navigator.NavigationEvent.NAVIGATING));
|
||||
navigate(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
navigate(newValue.getNode(), ContainerAnimations.FADE);
|
||||
onNavigated(getCurrentPage());
|
||||
if (getCurrentPage() != null) getCurrentPage().fireEvent(new Navigator.NavigationEvent(null, getCurrentPage(), Navigation.NavigationDirection.NEXT, Navigator.NavigationEvent.NAVIGATED));
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements
|
||||
@Override
|
||||
public void navigateTo(Node page, Navigation.NavigationDirection nav) {
|
||||
displayer.navigateTo(page, nav);
|
||||
navigate(page, nav.getAnimation().getAnimationProducer());
|
||||
navigate(page, nav.getAnimation());
|
||||
|
||||
String prefix = category == null ? "" : category + " - ";
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -97,7 +97,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
|
||||
tab.select(newGameTab);
|
||||
FXUtils.onChangeAndOperate(tab.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE);
|
||||
});
|
||||
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -178,9 +178,9 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
btnRefresh.setGraphic(wrap(SVG.REFRESH.createIcon(Theme.blackFill(), -1, -1)));
|
||||
|
||||
Holder<RemoteVersionListCell> lastCell = new Holder<>();
|
||||
list.setCellFactory(listView -> new RemoteVersionListCell(lastCell));
|
||||
list.setCellFactory(listView -> new RemoteVersionListCell(lastCell, libraryId));
|
||||
|
||||
list.setOnMouseClicked(e -> {
|
||||
FXUtils.onClicked(list, () -> {
|
||||
if (list.getSelectionModel().getSelectedIndex() < 0)
|
||||
return;
|
||||
navigation.getSettings().put(libraryId, list.getSelectionModel().getSelectedItem());
|
||||
@@ -210,7 +210,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
@Override
|
||||
public void refresh() {
|
||||
VersionList<?> currentVersionList = versionList;
|
||||
root.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
|
||||
root.setContent(spinner, ContainerAnimations.FADE);
|
||||
executor = currentVersionList.refreshAsync(gameVersion).whenComplete((result, exception) -> {
|
||||
if (exception == null) {
|
||||
List<RemoteVersion> items = loadVersions();
|
||||
@@ -218,7 +218,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
Platform.runLater(() -> {
|
||||
if (versionList != currentVersionList) return;
|
||||
if (currentVersionList.getVersions(gameVersion).isEmpty()) {
|
||||
root.setContent(emptyPane, ContainerAnimations.FADE.getAnimationProducer());
|
||||
root.setContent(emptyPane, ContainerAnimations.FADE);
|
||||
} else {
|
||||
if (items.isEmpty()) {
|
||||
chkRelease.setSelected(true);
|
||||
@@ -227,14 +227,14 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
} else {
|
||||
list.getItems().setAll(items);
|
||||
}
|
||||
root.setContent(center, ContainerAnimations.FADE.getAnimationProducer());
|
||||
root.setContent(center, ContainerAnimations.FADE);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LOG.warning("Failed to fetch versions list", exception);
|
||||
Platform.runLater(() -> {
|
||||
if (versionList != currentVersionList) return;
|
||||
root.setContent(failedPane, ContainerAnimations.FADE.getAnimationProducer());
|
||||
root.setContent(failedPane, ContainerAnimations.FADE);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -272,8 +272,12 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
|
||||
private final Holder<RemoteVersionListCell> lastCell;
|
||||
|
||||
RemoteVersionListCell(Holder<RemoteVersionListCell> lastCell) {
|
||||
RemoteVersionListCell(Holder<RemoteVersionListCell> lastCell, String libraryId) {
|
||||
this.lastCell = lastCell;
|
||||
if ("game".equals(libraryId)) {
|
||||
content.getExternalLinkButton().setGraphic(SVG.EARTH.createIcon(Theme.blackFill(), -1, -1));
|
||||
FXUtils.installFastTooltip(content.getExternalLinkButton(), i18n("wiki.tooltip"));
|
||||
}
|
||||
|
||||
pane.getStyleClass().add("md-list-cell");
|
||||
StackPane.setMargin(content, new Insets(10, 16, 10, 16));
|
||||
@@ -307,14 +311,19 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
case RELEASE:
|
||||
content.getTags().setAll(i18n("version.game.release"));
|
||||
content.setImage(VersionIconType.GRASS.getIcon());
|
||||
content.setExternalLink(i18n("wiki.version.game.release", remoteVersion.getGameVersion()));
|
||||
break;
|
||||
case SNAPSHOT:
|
||||
content.getTags().setAll(i18n("version.game.snapshot"));
|
||||
content.setImage(VersionIconType.COMMAND.getIcon());
|
||||
|
||||
|
||||
content.setExternalLink(i18n("wiki.version.game.snapshot", remoteVersion.getGameVersion()));
|
||||
break;
|
||||
default:
|
||||
content.getTags().setAll(i18n("version.game.old"));
|
||||
content.setImage(VersionIconType.CRAFT_TABLE.getIcon());
|
||||
content.setExternalLink(null);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -339,6 +348,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
else
|
||||
content.getTags().setAll(remoteVersion.getGameVersion());
|
||||
content.setExternalLink(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
@@ -116,7 +117,7 @@ public final class AboutPage extends StackPane {
|
||||
return componentList;
|
||||
}
|
||||
|
||||
try (Reader reader = new InputStreamReader(input)) {
|
||||
try (Reader reader = new InputStreamReader(input, StandardCharsets.UTF_8)) {
|
||||
JsonArray array = JsonUtils.GSON.fromJson(reader, JsonArray.class);
|
||||
|
||||
for (JsonElement element : array) {
|
||||
|
||||
@@ -36,7 +36,6 @@ 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 {
|
||||
@@ -67,7 +66,7 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
gameTab.initializeIfNeeded();
|
||||
gameTab.getNode().loadVersion(Profiles.getSelectedProfile(), null);
|
||||
FXUtils.onChangeAndOperate(tab.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE);
|
||||
});
|
||||
|
||||
{
|
||||
@@ -76,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 -> {
|
||||
|
||||
@@ -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;
|
||||
@@ -65,7 +66,6 @@ import java.util.stream.IntStream;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.SINE;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class MainPage extends StackPane implements DecoratorPage {
|
||||
@@ -83,7 +83,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 +102,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);
|
||||
|
||||
getChildren().add(announcementPane);
|
||||
}
|
||||
getChildren().add(announcementPane);
|
||||
}
|
||||
|
||||
updatePane = new StackPane();
|
||||
@@ -117,7 +127,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 +154,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,14 +216,14 @@ 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);
|
||||
StackPane.setAlignment(svg, Pos.CENTER_RIGHT);
|
||||
graphic.getChildren().setAll(svg);
|
||||
graphic.setTranslateX(12);
|
||||
runInFX(() -> FXUtils.installFastTooltip(menuButton, i18n("version.switch")));
|
||||
FXUtils.installFastTooltip(menuButton, i18n("version.switch"));
|
||||
menuButton.setGraphic(graphic);
|
||||
|
||||
launchPane.getChildren().setAll(launchButton, separator, menuButton);
|
||||
@@ -224,10 +234,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 +300,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
downloadItem.setActionButtonVisible(false);
|
||||
downloadItem.setTitle(i18n("download"));
|
||||
downloadItem.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage()));
|
||||
runInFX(() -> FXUtils.installFastTooltip(downloadItem, i18n("download.hint")));
|
||||
FXUtils.installFastTooltip(downloadItem, i18n("download.hint"));
|
||||
|
||||
// fifth item in left sidebar
|
||||
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
|
||||
|
||||
@@ -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));
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.image.Image;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.scene.shape.Mesh;
|
||||
import javafx.scene.shape.MeshView;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.PixelReader;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Group;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.animation.Transition;
|
||||
import javafx.beans.value.WritableValue;
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
package moe.mickey.minecraft.skin.fx.animation;
|
||||
package org.jackhuang.hmcl.ui.skin.animation;
|
||||
|
||||
import javafx.util.Duration;
|
||||
import moe.mickey.minecraft.skin.fx.FunctionHelper;
|
||||
import moe.mickey.minecraft.skin.fx.SkinAnimation;
|
||||
import moe.mickey.minecraft.skin.fx.SkinCanvas;
|
||||
import moe.mickey.minecraft.skin.fx.SkinTransition;
|
||||
import org.jackhuang.hmcl.ui.skin.FunctionHelper;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinAnimation;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinCanvas;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinTransition;
|
||||
|
||||
public final class SkinAniRunning extends SkinAnimation {
|
||||
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
package moe.mickey.minecraft.skin.fx.animation;
|
||||
package org.jackhuang.hmcl.ui.skin.animation;
|
||||
|
||||
import javafx.util.Duration;
|
||||
import moe.mickey.minecraft.skin.fx.FunctionHelper;
|
||||
import moe.mickey.minecraft.skin.fx.SkinAnimation;
|
||||
import moe.mickey.minecraft.skin.fx.SkinCanvas;
|
||||
import moe.mickey.minecraft.skin.fx.SkinTransition;
|
||||
import org.jackhuang.hmcl.ui.skin.FunctionHelper;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinAnimation;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinCanvas;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinTransition;
|
||||
|
||||
public final class SkinAniWavingArms extends SkinAnimation {
|
||||
|
||||
@@ -64,7 +64,6 @@ import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor;
|
||||
@@ -302,7 +301,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
lblGameVersion.visibleProperty().bind(hasVersion);
|
||||
gameVersionField.managedProperty().bind(hasVersion);
|
||||
gameVersionField.visibleProperty().bind(hasVersion);
|
||||
runInFX(() -> FXUtils.installFastTooltip(gameVersionField, i18n("search.enter")));
|
||||
FXUtils.installFastTooltip(gameVersionField, i18n("search.enter"));
|
||||
|
||||
FXUtils.onChangeAndOperate(getSkinnable().version, version -> {
|
||||
if (StringUtils.isNotBlank(version.getVersion())) {
|
||||
@@ -488,7 +487,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();
|
||||
|
||||
@@ -62,7 +62,6 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class DownloadPage extends Control implements DecoratorPage {
|
||||
@@ -241,14 +240,14 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
openMcmodButton.setExternalLink(getSkinnable().translations.getMcmodUrl(getSkinnable().mod));
|
||||
descriptionPane.getChildren().add(openMcmodButton);
|
||||
openMcmodButton.setMinWidth(Region.USE_PREF_SIZE);
|
||||
runInFX(() -> FXUtils.installFastTooltip(openMcmodButton, i18n("mods.mcmod")));
|
||||
FXUtils.installFastTooltip(openMcmodButton, i18n("mods.mcmod"));
|
||||
}
|
||||
|
||||
JFXHyperlink openUrlButton = new JFXHyperlink(control.page.getLocalizedOfficialPage());
|
||||
openUrlButton.setExternalLink(getSkinnable().addon.getPageUrl());
|
||||
descriptionPane.getChildren().add(openUrlButton);
|
||||
openUrlButton.setMinWidth(Region.USE_PREF_SIZE);
|
||||
runInFX(() -> FXUtils.installFastTooltip(openUrlButton, control.page.getLocalizedOfficialPage()));
|
||||
FXUtils.installFastTooltip(openUrlButton, control.page.getLocalizedOfficialPage());
|
||||
}
|
||||
|
||||
SpinnerPane spinnerPane = new SpinnerPane();
|
||||
@@ -346,7 +345,10 @@ 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, () -> {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
Controllers.navigate(new DownloadPage(page, addon, version, callback));
|
||||
});
|
||||
getChildren().setAll(container);
|
||||
|
||||
if (addon != RemoteMod.BROKEN) {
|
||||
@@ -428,7 +430,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 +445,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 +507,6 @@ 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()));
|
||||
dependencies.get(dependency.getType()).add(dependencyModItem);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jackhuang.hmcl.ui.versions;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.ImageView;
|
||||
import org.jackhuang.hmcl.event.Event;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
@@ -34,15 +33,13 @@ import java.util.function.Consumer;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class GameAdvancedListItem extends AdvancedListItem {
|
||||
private final Tooltip tooltip;
|
||||
private final ImageView imageView;
|
||||
private final WeakListenerHolder holder = new WeakListenerHolder();
|
||||
private Profile profile;
|
||||
@SuppressWarnings("unused")
|
||||
private Consumer<Event> onVersionIconChangedListener;
|
||||
|
||||
public GameAdvancedListItem() {
|
||||
tooltip = new Tooltip();
|
||||
|
||||
Pair<Node, ImageView> view = createImageView(null);
|
||||
setLeftGraphic(view.getKey());
|
||||
imageView = view.getValue();
|
||||
@@ -63,17 +60,13 @@ public class GameAdvancedListItem extends AdvancedListItem {
|
||||
}
|
||||
if (version != null && Profiles.getSelectedProfile() != null &&
|
||||
Profiles.getSelectedProfile().getRepository().hasVersion(version)) {
|
||||
FXUtils.installFastTooltip(this, tooltip);
|
||||
setTitle(version);
|
||||
setSubtitle(null);
|
||||
setTitle(i18n("version.manage.manage"));
|
||||
setSubtitle(version);
|
||||
imageView.setImage(Profiles.getSelectedProfile().getRepository().getVersionIconImage(version));
|
||||
tooltip.setText(version);
|
||||
} else {
|
||||
Tooltip.uninstall(this,tooltip);
|
||||
setTitle(i18n("version.empty"));
|
||||
setSubtitle(i18n("version.empty.add"));
|
||||
imageView.setImage(VersionIconType.DEFAULT.getIcon());
|
||||
tooltip.setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import org.jackhuang.hmcl.ui.construct.PopupMenu;
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.util.Lazy;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class GameListItemSkin extends SkinBase<GameListItem> {
|
||||
@@ -79,33 +78,33 @@ 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")));
|
||||
FXUtils.installFastTooltip(btnUpgrade, i18n("version.update"));
|
||||
right.getChildren().add(btnUpgrade);
|
||||
}
|
||||
|
||||
{
|
||||
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));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnLaunch, i18n("version.launch.test")));
|
||||
FXUtils.installFastTooltip(btnLaunch, i18n("version.launch.test"));
|
||||
right.getChildren().add(btnLaunch);
|
||||
}
|
||||
|
||||
{
|
||||
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());
|
||||
});
|
||||
btnManage.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(btnManage, Pos.CENTER);
|
||||
btnManage.setGraphic(FXUtils.limitingSize(SVG.DOTS_VERTICAL.createIcon(Theme.blackFill(), 24, 24), 24, 24));
|
||||
runInFX(() -> FXUtils.installFastTooltip(btnManage, i18n("settings.game.management")));
|
||||
FXUtils.installFastTooltip(btnManage, i18n("settings.game.management"));
|
||||
right.getChildren().add(btnManage);
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -192,7 +192,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
||||
})
|
||||
.whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
if (exception != null || result == null) {
|
||||
Controllers.dialog("Failed to check updates", "failed", MessageDialogPane.MessageType.ERROR);
|
||||
Controllers.dialog(i18n("mods.check_updates.failed_check"), i18n("message.failed"), MessageDialogPane.MessageType.ERROR);
|
||||
} else if (result.isEmpty()) {
|
||||
Controllers.dialog(i18n("mods.check_updates.empty"));
|
||||
} else {
|
||||
|
||||
@@ -186,6 +186,14 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
}
|
||||
});
|
||||
|
||||
listView.setOnContextMenuRequested(event -> {
|
||||
ModInfoObject selectedItem = listView.getSelectionModel().getSelectedItem();
|
||||
if (selectedItem != null && listView.getSelectionModel().getSelectedItems().size() == 1) {
|
||||
listView.getSelectionModel().clearSelection();
|
||||
Controllers.dialog(new ModInfoDialog(selectedItem));
|
||||
}
|
||||
});
|
||||
|
||||
center.setContent(listView);
|
||||
root.getContent().add(center);
|
||||
}
|
||||
@@ -204,7 +212,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
private void changeToolbar(HBox newToolbar) {
|
||||
Node oldToolbar = toolbarPane.getCurrentNode();
|
||||
if (newToolbar != oldToolbar) {
|
||||
toolbarPane.setContent(newToolbar, ContainerAnimations.FADE.getAnimationProducer());
|
||||
toolbarPane.setContent(newToolbar, ContainerAnimations.FADE);
|
||||
if (newToolbar == searchBar) {
|
||||
searchField.requestFocus();
|
||||
}
|
||||
@@ -539,7 +547,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 +557,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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
task.whenComplete(Schedulers.javafx(), exception -> {
|
||||
fireEvent(new PageCloseEvent());
|
||||
if (!task.getFailedMods().isEmpty()) {
|
||||
Controllers.dialog(i18n("mods.check_updates.failed") + "\n" +
|
||||
Controllers.dialog(i18n("mods.check_updates.failed_download") + "\n" +
|
||||
task.getFailedMods().stream().map(LocalModFile::getFileName).collect(Collectors.joining("\n")),
|
||||
i18n("install.failed"),
|
||||
MessageDialogPane.MessageType.ERROR);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -78,7 +78,7 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
|
||||
tab.select(versionSettingsTab);
|
||||
FXUtils.onChangeAndOperate(tab.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE);
|
||||
});
|
||||
|
||||
listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST));
|
||||
@@ -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()
|
||||
|
||||
@@ -685,7 +685,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
version = null;
|
||||
} else {
|
||||
gameVersionNumber = GameVersionNumber.asGameVersion(repository.getGameVersion(versionId));
|
||||
version = repository.getVersion(versionId);
|
||||
version = repository.getResolvedVersion(versionId);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -83,7 +83,7 @@ public final class Versions {
|
||||
} catch (IOException e) {
|
||||
Controllers.dialog(
|
||||
i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e),
|
||||
i18n("download.failed.no_code", file.getFile().getUrl()), MessageDialogPane.MessageType.ERROR);
|
||||
i18n("download.failed.no_code"), MessageDialogPane.MessageType.ERROR);
|
||||
return;
|
||||
}
|
||||
Controllers.taskDialog(
|
||||
@@ -96,7 +96,7 @@ public final class Versions {
|
||||
} else {
|
||||
Controllers.dialog(
|
||||
i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e),
|
||||
i18n("download.failed.no_code", file.getFile().getUrl()), MessageDialogPane.MessageType.ERROR);
|
||||
i18n("download.failed.no_code"), MessageDialogPane.MessageType.ERROR);
|
||||
}
|
||||
}).executor(true),
|
||||
i18n("message.downloading"),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -39,16 +39,6 @@ public final class Locales {
|
||||
*/
|
||||
public static final SupportedLocale EN = new SupportedLocale(Locale.ROOT);
|
||||
|
||||
/**
|
||||
* Traditional Chinese
|
||||
*/
|
||||
public static final SupportedLocale ZH = new SupportedLocale(Locale.TRADITIONAL_CHINESE);
|
||||
|
||||
/**
|
||||
* Simplified Chinese
|
||||
*/
|
||||
public static final SupportedLocale ZH_CN = new SupportedLocale(Locale.SIMPLIFIED_CHINESE);
|
||||
|
||||
/**
|
||||
* Spanish
|
||||
*/
|
||||
@@ -64,23 +54,33 @@ public final class Locales {
|
||||
*/
|
||||
public static final SupportedLocale JA = new SupportedLocale(Locale.JAPANESE);
|
||||
|
||||
public static final List<SupportedLocale> LOCALES = Lang.immutableListOf(DEFAULT, EN, ZH_CN, ZH, ES, RU, JA);
|
||||
/**
|
||||
* Traditional Chinese
|
||||
*/
|
||||
public static final SupportedLocale ZH = new SupportedLocale(Locale.TRADITIONAL_CHINESE);
|
||||
|
||||
/**
|
||||
* Simplified Chinese
|
||||
*/
|
||||
public static final SupportedLocale ZH_CN = new SupportedLocale(Locale.SIMPLIFIED_CHINESE);
|
||||
|
||||
public static final List<SupportedLocale> LOCALES = Lang.immutableListOf(DEFAULT, EN, ES, JA, RU, ZH_CN, ZH);
|
||||
|
||||
public static SupportedLocale getLocaleByName(String name) {
|
||||
if (name == null) return DEFAULT;
|
||||
switch (name.toLowerCase(Locale.ROOT)) {
|
||||
case "en":
|
||||
return EN;
|
||||
case "es":
|
||||
return ES;
|
||||
case "ja":
|
||||
return JA;
|
||||
case "ru":
|
||||
return RU;
|
||||
case "zh":
|
||||
return ZH;
|
||||
case "zh_cn":
|
||||
return ZH_CN;
|
||||
case "es":
|
||||
return ES;
|
||||
case "ru":
|
||||
return RU;
|
||||
case "ja":
|
||||
return JA;
|
||||
default:
|
||||
return DEFAULT;
|
||||
}
|
||||
@@ -88,11 +88,11 @@ public final class Locales {
|
||||
|
||||
public static String getNameByLocale(SupportedLocale locale) {
|
||||
if (locale == EN) return "en";
|
||||
else if (locale == ZH) return "zh";
|
||||
else if (locale == ZH_CN) return "zh_CN";
|
||||
else if (locale == ES) return "es";
|
||||
else if (locale == RU) return "ru";
|
||||
else if (locale == JA) return "ja";
|
||||
else if (locale == ZH) return "zh";
|
||||
else if (locale == ZH_CN) return "zh_CN";
|
||||
else if (locale == DEFAULT) return "def";
|
||||
else throw new IllegalArgumentException("Unknown locale: " + locale);
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -24,5 +24,8 @@
|
||||
-fx-base-disabled-text-fill: rgba(256, 256, 256, 0.7);
|
||||
-fx-base-text-fill: white;
|
||||
|
||||
/* https://github.com/HMCL-dev/HMCL/pull/3423 */
|
||||
-fx-font-family: -fx-base-font-family;
|
||||
|
||||
-theme-thumb: rgba(92, 107, 192, 0.7);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 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/>.
|
||||
*/
|
||||
.root {
|
||||
-fx-base-color: %base-color%;
|
||||
-fx-base-darker-color: derive(-fx-base-color, -10%);
|
||||
-fx-base-check-color: derive(-fx-base-color, 30%);
|
||||
-fx-rippler-color: rgba(%base-red%, %base-green%, %base-blue%, 0.3);
|
||||
-fx-base-rippler-color: derive(%base-rippler-color%, 100%);
|
||||
-fx-base-disabled-text-fill: %disabled-font-color%;
|
||||
-fx-base-text-fill: %font-color%;
|
||||
|
||||
-theme-thumb: rgba(%base-red%, %base-green%, %base-blue%, 0.7);
|
||||
|
||||
%font%
|
||||
}
|
||||
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
|
||||
@@ -23,25 +23,29 @@ about=Acerca de
|
||||
about.copyright=Copyright
|
||||
about.copyright.statement=Copyright © 2024 huangyuhui.
|
||||
about.author=Autor
|
||||
about.author.statement=@huanghongxun en bilibili
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=Haz clic en este enlace para ver el texto completo.
|
||||
about.claim.statement=Haga clic en este enlace para ver el texto completo.
|
||||
about.dependency=Bibliotecas de terceros
|
||||
about.legal=Reconocimiento legal
|
||||
about.thanks_to=Gracias a
|
||||
about.thanks_to.bangbang93.statement=Por proporcionar la API de descarga de BMCLAPI, por favor considere hacer una donación.
|
||||
about.thanks_to.bangbang93.statement=Por proporcionar el espejo de descarga de BMCLAPI, por favor considere hacer una donación!
|
||||
about.thanks_to.burningtnt.statement=Aportar mucho apoyo técnico a HMCL.
|
||||
about.thanks_to.contributors=Todos los colaboradores en GitHub
|
||||
about.thanks_to.contributors.statement=Sin la impresionante comunidad de código abierto, Hello Minecraft! Launcher no habría llegado tan lejos.
|
||||
about.thanks_to.contributors.statement=Sin la impresionante comunidad de código abierto, HMCL no habría llegado tan lejos.
|
||||
about.thanks_to.gamerteam.statement=Por proporcionar la imagen de fondo por defecto.
|
||||
about.thanks_to.glavo.statement=Aportar mucho apoyo técnico a HMCL.
|
||||
about.thanks_to.glavo.statement=Responsable del mantenimiento de HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=Aportar mucho apoyo técnico a HMCL.
|
||||
about.thanks_to.zkitefly.statement=Aportar mucho apoyo técnico a HMCL.
|
||||
about.thanks_to.mcmod=mcmod.cn
|
||||
about.thanks_to.mcmod.statement=Por facilitar las traducciones al chino y el wiki de varios mods.
|
||||
about.thanks_to.zkitefly.statement=Responsable del mantenimiento de la documentación de HMCL.
|
||||
about.thanks_to.mcbbs=MCBBS (Foro Chino Minecraft)
|
||||
about.thanks_to.mcbbs.statement=Por proporcionar el espejo de descarga de mcbbs.net para los usuarios de China continental. (Ya no disponible)
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=Por proporcionar las traducciones al chino simplificado y la wiki de varios mods.
|
||||
about.thanks_to.red_lnn.statement=Por proporcionar la imagen de fondo por defecto.
|
||||
about.thanks_to.shulkersakura.statement=Por proporcionar el logotipo de HMCL.
|
||||
about.thanks_to.users=Miembros del grupo de usuarios de HMCL
|
||||
about.thanks_to.users.statement=Gracias por las donaciones, los informes de errores, etc.
|
||||
about.thanks_to.yushijinhun.statement=Por proporcionar soporte relacionado con authlib-injector.
|
||||
about.thanks_to.yushijinhun.statement=Por proporcionar el soporte relacionado con authlib-injector.
|
||||
about.open_source=Código abierto
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -148,6 +152,7 @@ assets.index.malformed=Los archivos de índice de los activos descargados estaba
|
||||
button.cancel=Cancelar
|
||||
button.change_source=Cambiar fuente de descarga
|
||||
button.clear=Limpiar
|
||||
button.copy_and_exit=Copiar y salir
|
||||
button.delete=Borrar
|
||||
button.edit=Editar
|
||||
button.install=Instalar
|
||||
@@ -156,17 +161,21 @@ button.no=No
|
||||
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.
|
||||
button.remove.confirm=¿Estás seguro de que deseas eliminarlo de forma permanente? ¡Esta acción no se puede deshacer!
|
||||
button.retry=Reintentar
|
||||
button.save=Guardar
|
||||
button.save_as=Guardar como
|
||||
button.select_all=Seleccionar todo
|
||||
button.view=Vista
|
||||
button.yes=Sí
|
||||
|
||||
chat=Chat de grupo
|
||||
|
||||
color.recent=Recomendado
|
||||
color.custom=Color personalizado
|
||||
|
||||
crash.NoClassDefFound=Por favor, verifique la integridad de este software, o intente actualizar su versión de Java.
|
||||
crash.user_fault=El launcher se ha bloqueado debido a un entorno Java o de sistema corrupto. Por favor, asegúrese de que su Java o sistema operativo está instalado correctamente.
|
||||
crash.NoClassDefFound=Por favor, verifique la integridad de este software, o intente actualizar su Java.
|
||||
crash.user_fault=El lanzador se ha bloqueado debido a que Java o el entorno del sistema están dañados. Por favor, asegúrese de que su Java o sistema operativo está instalado correctamente.
|
||||
|
||||
curse.category.0=Todos
|
||||
|
||||
@@ -316,7 +325,7 @@ download.javafx.prepare=Preparando la descarga
|
||||
|
||||
exception.access_denied=HMCL no puede acceder al archivo %s, puede estar bloqueado por otro proceso.\n\
|
||||
\n\
|
||||
Para los usuarios de Windows, puede abrir el 'Monitor de Recursos' para comprobar si otro proceso lo está utilizando actualmente. Si es así, puede intentarlo de nuevo después de cerrar ese proceso.\
|
||||
Para los usuarios de Windows, puede abrir el 'Monitor de Recursos' para comprobar si otro proceso lo está utilizando actualmente. Si es así, puede intentarlo de nuevo después de cerrar ese proceso.\n\
|
||||
Si no es así, comprueba si tu cuenta tiene suficientes privilegios para acceder a él.
|
||||
exception.artifact_malformed=No se puede verificar la integridad de los archivos descargados .
|
||||
exception.ssl_handshake=No se puede establecer una conexión SSL debido a que faltan certificados SSL en la instalación actual de Java. Puede intentar ejecutar HMCL en otra versión de Java y luego volver a intentarlo.
|
||||
@@ -333,7 +342,7 @@ Por favor, intente cambiar a versiones de Java como Oracle Java 8 que tiene Java
|
||||
fatal.javafx.missing=No se encontró un entorno JavaFX.\n\
|
||||
Si está utilizando Java 11 o superior, por favor, descargue Oracle JRE 8 (java.com), o instale BellSoft Liberica Full JRE (bell-sw.com/pages/downloads).\n\
|
||||
O, si está utilizando distribuciones de OpenJDK, asegúrese de que tiene incluido OpenJFX.
|
||||
fatal.config_loading_failure=No se pueden cargar los archivos de configuración.\nPara macOS, intente colocar HMCL en un lugar con permiso que no sea "Escritorio", "Descargas" y "Documentos" y vuelva a intentarlo.
|
||||
fatal.config_loading_failure=No se pueden cargar los archivos de configuración.\nPara macOS, intente colocar HMCL en un lugar con permiso que no sea "Escritorio", "Descargas" y "Documentos" y vuelva a intentarlo.\n\
|
||||
¡Por favor, asegúrese de que "Hello Minecraft! Launcher" tiene acceso de lectura y escritura a "%s" y a los archivos que contiene.
|
||||
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher ha sido actualizado. Por favor, reinicie el launcher.
|
||||
fatal.apply_update_failure=Lo sentimos, pero Hello Minecraft! Launcher no puede actualizarse.\n\
|
||||
@@ -345,8 +354,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
|
||||
|
||||
@@ -374,7 +388,7 @@ game.crash.reason.bootstrap_failed=El juego se ha bloqueado debido al mod %1$s.\
|
||||
\n\
|
||||
Puedes intentar borrarlo o actualizarlo.
|
||||
game.crash.reason.config=El juego se ha bloqueado porque un mod %1$s no puede analizar su archivo de configuración %2$s.
|
||||
game.crash.reason.debug_crash=El juego se ha bloqueado porque lo has activado manualmente.
|
||||
game.crash.reason.debug_crash=El juego se ha bloqueado porque lo has activado manualmente.\n\
|
||||
\n\
|
||||
Entonces, probablemente sabes por qué.
|
||||
game.crash.reason.mixin_apply_mod_failed=El juego se ha bloqueado porque Mixin no puede aplicar el mod %1$s, no se puede continuar.\n\
|
||||
@@ -431,7 +445,7 @@ game.crash.reason.loading_crashed_fabric=El juego se ha bloqueado debido al mod
|
||||
Puedes intentar borrarlo o actualizarlo.
|
||||
game.crash.reason.memory_exceeded=El juego se ha bloqueado debido a que se ha asignado demasiada memoria para un pequeño archivo de paginación.\n\
|
||||
\n\
|
||||
Puede intentar desactivar la opción de asignación automática de memoria en la configuración y ajustar el valor hasta que el juego se inicie.
|
||||
Puede intentar desactivar la opción de asignación automática de memoria en la configuración y ajustar el valor hasta que el juego se inicie.\n\
|
||||
También puede tratar de aumentar el tamaño del archivo de paginación en la configuración del sistema.
|
||||
game.crash.reason.mod=El juego se ha bloqueado debido al mod %1$s.\n\
|
||||
\n\
|
||||
@@ -470,22 +484,19 @@ game.crash.reason.mod_resolution_mod_version.any=%1$s (Cualquier versión)
|
||||
game.crash.reason.forge_repeat_installation=El juego actual no puede continuar ejecutándose debido a una instalación duplicada de Forge. <a href="https://github.com/HMCL-dev/HMCL/issues/1880">Este es un problema conocido</a>\nSe recomienda cargar los comentarios del registro en GitHub para que podamos encontrar más pistas y arreglar esta pregunta. \nActualmente puede ir a la instalación automática para desinstalar Forge y volver a instalarlo.
|
||||
game.crash.reason.optifine_repeat_installation=El juego actual no puede seguir ejecutándose debido a la instalación repetida de OptiFine. \nElimine OptiFine de la carpeta Mod o vaya a Gestión de juegos-Instalación automática para desinstalar OptiFine que se instala automáticamente.
|
||||
game.crash.reason.modmixin_failure=El juego actual no puede continuar ejecutándose debido a algunas fallas en la inyección de mods. \nEsto generalmente significa que el mod tiene un error o no es compatible con el entorno actual. \nPuede consultar el registro para ver si hay un mod incorrecto.
|
||||
game.crash.reason.file_or_content_verification_failed=El juego actual tiene un problema porque fallaron algunos archivos o la verificación de contenido. \nIntente eliminar esta versión (incluidas las modificaciones) y vuelva a descargarla, o intente usar un proxy cuando vuelva a descargar, etc.
|
||||
game.crash.reason.mod_repeat_installation=El juego actual tiene múltiples Mods idénticos instalados repetidamente, y cada Mod solo puede aparecer una vez. Elimina los Mods repetidos y luego inicia el juego.
|
||||
game.crash.reason.forge_error=Forge puede haber proporcionado un mensaje de error.\nPuede ver el registro y realizar las acciones correspondientes de acuerdo con la información de registro en el informe de errores.\nSi no ve un mensaje de error, puede consultar el informe de errores para saber cómo ocurrió el error.\n%1$s
|
||||
game.crash.reason.mod_solution0=El juego actual no puede seguir ejecutándose debido a algunos problemas de modificación.\nPuede consultar el registro para ver si hay un mod incorrecto.
|
||||
game.crash.reason.mod_profile_causes_game_crash=El juego actual no puede seguir ejecutándose debido a un problema con el perfil de mod.\nPuede consultar el registro del mod defectuoso y su archivo de configuración.
|
||||
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Forge puede haber dado un mensaje de error.\nPuede ver el registro y realizar las acciones correspondientes de acuerdo con la información de registro en el informe de errores.\nSi no ve un mensaje de error, puede consultar el informe de errores para saber cómo ocurrió el error.
|
||||
game.crash.reason.mod_resolution0=El juego actual no puede seguir ejecutándose debido a algunos problemas de modificación.\nPuede consultar el registro para ver si hay un mod incorrecto.
|
||||
game.crash.reason.java_version_is_too_high=El juego actual se bloqueó porque la versión de Java es demasiado alta y no puede seguir ejecutándose.\nCambie a una versión anterior de Java en la pestaña Java Path de Configuración global del juego o Configuración específica del juego antes de iniciar el juego.\nSi no es así, puede descargarlo desde <a href="https://www.java.com/download/">java.com (Java8)</a> o <a href="https://bell-sw.com/pages/downloads/#downloads">BellSoft Liberica Full JRE (Java17)</a> y otras plataformas para descargar e instalar una (reinicie el iniciador después de la instalación).
|
||||
game.crash.reason.mod_name=El juego actual no puede continuar ejecutándose debido a un problema con el nombre del archivo mod. El nombre del archivo de\nMod solo debe usar mayúsculas y minúsculas (Aa ~ Zz), números (0 ~ 9), líneas horizontales (-), subrayado (_) y puntos (.) en toda la mitad del inglés.\n Por favor, vaya a la carpeta mod para agregar un carácter de Cumplimiento anterior a todos los nombres de archivo mod que no cumplan.
|
||||
game.crash.reason.incomplete_forge_installation=O jogo atual não pode continuar devido a uma instalação incompleta do Forge / NeoForge.\nDesinstale e reinstale o Forge / NeoForge em Configurações de versão - Instalação automática.
|
||||
game.crash.reason.modlauncher_8=El juego se ha bloqueado porque tu versión actual de Forge no es compatible con tu instalación de Java. Por favor, intente actualizar Forge, o intente utilizar Java 8u312/11.0.13/17.0.1 o versiones anteriores.\n\
|
||||
\n\
|
||||
8u312 y anteriores\:\n\
|
||||
https://oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html
|
||||
https://oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html\n\
|
||||
\n\
|
||||
11.0.13 y anteriores\:\n\
|
||||
https://oracle.com/java/technologies/javase/jdk11-archive-downloads.html
|
||||
https://oracle.com/java/technologies/javase/jdk11-archive-downloads.html\n\
|
||||
\n\
|
||||
17.0.1 y anteriores\:\n\
|
||||
https://oracle.com/java/technologies/javase/jdk17-archive-downloads.html
|
||||
@@ -494,21 +505,21 @@ game.crash.reason.no_class_def_found_error=El juego no puede ejecutarse debido a
|
||||
A su instancia de juego le falta %1$s, esto puede deberse a que falta un mod, a que hay un mod incompatible instalado o a que algunos archivos pueden estar dañados.\n\
|
||||
\n\
|
||||
Puede que tengas que reinstalar el juego y todos los mods o pedir ayuda a alguien.
|
||||
game.crash.reason.no_sethod_error=El juego no puede ejecutarse debido a un código incompleto.\n\
|
||||
game.crash.reason.no_such_method_error=El juego no puede ejecutarse debido a un código incompleto.\n\
|
||||
\n\
|
||||
Es posible que a su instancia de juego le falte un mod, que haya instalado un mod incompatible o que algunos archivos estén dañados.\n\
|
||||
\n\
|
||||
Puede que tengas que reinstalar el juego y todos los mods o pedir ayuda a alguien.
|
||||
game.crash.reason.opengl_not_supported=El juego se ha bloqueado porque OpenGL no es compatible con su controlador de gráficos.\n\
|
||||
\n\
|
||||
Si está transmitiendo el juego a través de Internet o utilizando un entorno de escritorio remoto, por favor, intente jugar el juego en su entorno local.
|
||||
Si está transmitiendo el juego a través de Internet o utilizando un entorno de escritorio remoto, por favor, intente jugar el juego en su entorno local.\n\
|
||||
O bien, puede intentar actualizar su controlador a la última versión y volver a intentarlo.\n\
|
||||
\n\
|
||||
Si tu ordenador tiene una tarjeta gráfica discreta, por favor, asegúrate de que el juego la está utilizando para el renderizado. Si el problema persiste, considera la posibilidad de adquirir una nueva tarjeta gráfica o un nuevo ordenador.
|
||||
game.crash.reason.openj9=El juego no puede ejecutarse en una máquina virtual OpenJ9. Por favor, cambia a una VM Java Hotspot en la configuración del juego y vuelve a ejecutar el juego. Si no tienes una, puedes descargar una en línea.
|
||||
game.crash.reason.out_of_memory=El juego se ha bloqueado porque se ha quedado sin memoria.\n\
|
||||
\n\
|
||||
Esto puede deberse a que no hay suficiente memoria disponible, o a que hay demasiados mods instalados. Puedes intentar solucionarlo aumentando la memoria asignada en la configuración del juego.
|
||||
Esto puede deberse a que no hay suficiente memoria disponible, o a que hay demasiados mods instalados. Puedes intentar solucionarlo aumentando la memoria asignada en la configuración del juego.\n\
|
||||
Si sigues encontrando estos problemas, es posible que necesites una mejor computadora.
|
||||
game.crash.reason.resolution_too_high=El juego se ha bloqueado porque estás utilizando un resource packs cuya resolución de textura es demasiado alta.\n\
|
||||
\n\
|
||||
@@ -533,11 +544,6 @@ game.crash.reason.unsatisfied_link_error=No se puede iniciar Minecraft porque fa
|
||||
Si ha modificado la configuración de la biblioteca nativa, por favor, asegúrese de que estas bibliotecas existen. O, por favor, intente iniciar de nuevo después de revertir a los valores predeterminados.\n\
|
||||
Si no lo ha hecho, por favor, compruebe si le faltan mods de dependencia.\n\
|
||||
De lo contrario, si usted cree que esto es causado por HMCL, por favor, comentarios a nosotros.
|
||||
game.crash.reason.failed_to_load_a_library=Failed to load a library.\n\
|
||||
\n\
|
||||
Si ha modificado la configuración de la biblioteca nativa, por favor, asegúrese de que estas bibliotecas existen. O, por favor, intente iniciar de nuevo después de revertir a los valores predeterminados.\n\
|
||||
Si no lo ha hecho, por favor, compruebe si le faltan mods de dependencia.\n\
|
||||
De lo contrario, si usted cree que esto es causado por HMCL, por favor, comentarios a nosotros.
|
||||
game.crash.title=Juego interrumpido
|
||||
game.directory=Ruta del juego
|
||||
game.version=Versión del juego
|
||||
@@ -592,6 +598,36 @@ install.new_game.malformed=Nombre no válido
|
||||
install.select=Seleccione una operación
|
||||
install.success=Instalado con éxito.
|
||||
|
||||
java.add=Añadir Java
|
||||
java.add.failed=Este Java no es válido o es incompatible con la plataforma actual.
|
||||
java.disable=Deshabilitar este Java
|
||||
java.disable.confirm=¿Está seguro de que desea desactivar este Java?
|
||||
java.disabled.management=Java desactivado
|
||||
java.disabled.management.remove=Eliminar este Java de la lista
|
||||
java.disabled.management.restore=Activar este Java
|
||||
java.download=Descargar Java
|
||||
java.download.load_list.failed=Error al cargar la lista de versiones
|
||||
java.download.more=Más distribuciones de Java
|
||||
java.download.prompt=Elija la versión de Java que desea descargar:
|
||||
java.download.distribution=Distribución
|
||||
java.download.version=Versión
|
||||
java.download.packageType=Tipo de paquete
|
||||
java.management=Gestión Java
|
||||
java.info.architecture=Arquitectura
|
||||
java.info.vendor=Proveedor
|
||||
java.info.version=Versión
|
||||
java.info.disco.distribution=Distribución
|
||||
java.install=Instalar Java
|
||||
java.install.archive=Fuente Path
|
||||
java.install.failed.exists=Este nombre ya tiene dueño
|
||||
java.install.failed.invalid=Este archivo no es un paquete de instalación de Java válido, por lo que no se puede instalar.
|
||||
java.install.failed.unsupported_platform=Este Java no es compatible con la plataforma actual, por lo que no puede instalarse.
|
||||
java.install.name=Nombre
|
||||
java.install.warning.invalid_character=Carácter ilegal en el nombre
|
||||
java.reveal=Revelar el directorio de Java
|
||||
java.uninstall=Desinstalar Java
|
||||
java.uninstall.confirm=¿Está seguro de que desea desinstalar este Java? ¡Esta acción no se puede deshacer!
|
||||
|
||||
lang=Español
|
||||
lang.default=Usar idioma del sistema
|
||||
|
||||
@@ -615,8 +651,6 @@ launch.advice.too_large_memory_for_32bit=Has asignado un tamaño de memoria mayo
|
||||
launch.advice.vanilla_linux_java_8=Minecraft 1.12.2 o inferior sólo admite Java 8 para la plataforma Linux x86-64, porque las versiones posteriores no pueden cargar las bibliotecas nativas de 32 bits como liblwjgl.so\n\
|
||||
\n\
|
||||
Por favor, descárguelo de java.com, o instale OpenJDK 8.
|
||||
launch.advice.vanilla_x86=Minecraft actualmente no proporciona soporte oficial para arquitecturas distintas de x86 o x86-64.\n\
|
||||
Puedes intentar descargar la librería nativa de tu plataforma y especificar su ruta, luego vuelve a intentarlo.
|
||||
launch.advice.vanilla_x86.translation=Minecraft no proporciona actualmente soporte oficial para arquitecturas distintas de x86 y x86-64.\n\
|
||||
Por favor, instale Java para x86-64 para jugar a Minecraft a través del entorno de traducción Rosetta, o descargue sus bibliotecas nativas correspondientes y especifique su ruta.
|
||||
launch.failed=No se puede ejecutar
|
||||
@@ -654,7 +688,7 @@ launcher.background.translucent=Translúcido
|
||||
launcher.cache_directory=Directorio de la caché
|
||||
launcher.cache_directory.clean=Borrar caché
|
||||
launcher.cache_directory.choose=Elegir el directorio de la caché
|
||||
launcher.cache_directory.default=Por defecto (%AppData%/.minecraft o ~/.minecraft)
|
||||
launcher.cache_directory.default=Por defecto (%APPDATA%/.minecraft o ~/.minecraft)
|
||||
launcher.cache_directory.disabled=Desactivado
|
||||
launcher.cache_directory.invalid=No se ha podido crear el directorio de la caché, volviendo a los valores por defecto.
|
||||
launcher.contact=Contacta con nosotros
|
||||
@@ -733,7 +767,6 @@ modpack.scan=Análisis del índice de modpacks
|
||||
modpack.task.install=Instalar Modpack
|
||||
modpack.task.install.error=No se ha podido identificar este modpack. Actualmente sólo soportamos los modpacks Curse, Modrinth, MultiMC y MCBBS.
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.curse.tolerable_error=No se han podido descargar las dependencias, puedes intentar continuar la descarga ejecutando esta instancia del juego.
|
||||
modpack.type.curse.error=No se han podido descargar las dependencias de CurseForge, inténtalo de nuevo o utiliza una conexión proxy.
|
||||
modpack.type.curse.not_found=Algunas dependencias ya no están disponibles, por favor intenta instalar una versión más reciente del modpack.
|
||||
modpack.type.manual.warning=Este archivo contiene una copia completa de una instancia de Minecraft. La mayoría de las veces, sólo tienes que descomprimirlo y ejecutar el juego usando su launcher incorporado. Pero, HMCL todavía puede importarlo, sin garantía de su utilidad, ¿aún continúa?
|
||||
@@ -741,7 +774,7 @@ modpack.type.mcbbs=Tipo MCBBS
|
||||
modpack.type.mcbbs.export=Puede ser importado por ¡Hello Minecraft! Launcher
|
||||
modpack.type.modrinth=Modrinth
|
||||
modpack.type.multimc=MultiMC
|
||||
¡modpack.type.multimc.export=Puede ser importado por Hello Minecraft! Launcher y MultiMC
|
||||
modpack.type.multimc.export=Puede ser importado por Hello Minecraft! Launcher y MultiMC
|
||||
modpack.type.server=Actualización automática del modpack desde el servidor
|
||||
modpack.type.server.export=Permite al propietario del servidor actualizar la instancia del juego de forma remota
|
||||
modpack.type.server.malformed=Manifiesto de modpack inválido, por favor, consulte al creador del modpack del servidor para solucionar este problema.
|
||||
@@ -758,7 +791,7 @@ modpack.wizard.step.initialization.exported_version=Versión del juego a exporta
|
||||
modpack.wizard.step.initialization.force_update=Forzar la actualización del modpack a la última versión (necesitarás un servicio de alojamiento de archivos)
|
||||
modpack.wizard.step.initialization.include_launcher=Incluir el launcher
|
||||
modpack.wizard.step.initialization.save=Exportar a...
|
||||
modpack.wizard.step.initialization.warning=Antes de crear un modpack, por favor asegúrate de que el juego se ejecuta, y que es una versión de lanzamiento en lugar de una versión instantánea. El launcher guardará su configuración de descarga.
|
||||
modpack.wizard.step.initialization.warning=Antes de crear un modpack, por favor asegúrate de que el juego se ejecuta, y que es una versión de lanzamiento en lugar de una versión instantánea. El launcher guardará su configuración de descarga.\n\
|
||||
\n\
|
||||
Ten en cuenta que no se te permite añadir mods y resource packs que se digan explícitamente que no se pueden distribuir o poner en un modpack.
|
||||
modpack.wizard.step.initialization.server=Haga clic aquí para ver más tutoriales para hacer un modpack de servidor que se pueda actualizar automáticamente.
|
||||
@@ -797,7 +830,8 @@ mods.add.success=%s fue instalado con éxito.
|
||||
mods.category=Categoría
|
||||
mods.check_updates=Comprobar actualizaciones
|
||||
mods.check_updates.current_version=Versión actual
|
||||
mods.check_updates.failed=No se han podido descargar algunos de los archivos.
|
||||
mods.check_updates.failed_check=No se ha podido comprobar si hay actualizaciones.
|
||||
mods.check_updates.failed_download=No se han podido descargar algunos de los archivos.
|
||||
mods.check_updates.file=Archivo
|
||||
mods.check_updates.source=Fuente
|
||||
mods.check_updates.target_version=Versión de destino
|
||||
@@ -937,8 +971,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 +1056,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 +1067,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,31 +17,36 @@
|
||||
#
|
||||
|
||||
# Contributors: zhixuan2333
|
||||
about=About
|
||||
|
||||
about=について
|
||||
about.copyright=著作権
|
||||
about.copyright.statement=Copyright © 2024 huangyuhui。
|
||||
about.author=Author
|
||||
about.author.statement=bilibili : @huanghongxun
|
||||
about.copyright.statement=Copyright © 2024 huangyuhui
|
||||
about.author=著者
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=テキスト全体のリンクをクリックします。
|
||||
about.dependency=Dependencies
|
||||
about.claim.statement=全文はこのリンクをクリック
|
||||
about.dependency=依存関係
|
||||
about.legal=法的承認
|
||||
about.thanks_to=Thanks to
|
||||
about.thanks_to.bangbang93.statement=BMCLAPIダウンロードソースプロバイダー。
|
||||
about.thanks_to.contributors=問題、プルリクエストなどを介してこのプロジェクトに参加したすべての貢献者。
|
||||
about.thanks_to.contributors.statement=Hello Minecraftをサポートしてくれたオープンソースコミュニティに感謝します!ランチャー
|
||||
about.thanks_to.gamerteam.statement=デフォルトの背景画像プロバイダー。
|
||||
about.thanks_to.glavo.statement=HMCLに多くのテクニカルサポートを提供
|
||||
about.thanks_to=感謝します…
|
||||
about.thanks_to.bangbang93.statement=BMCLAPIダウンロードミラープロバイダー。寄付をご検討ください!
|
||||
about.thanks_to.burningtnt.statement=HMCLに多くのテクニカルサポートを提供
|
||||
about.thanks_to.contributors=GitHubのすべての貢献者
|
||||
about.thanks_to.contributors.statement=HMCLをサポートしてくれたオープンソースコミュニティに感謝する
|
||||
about.thanks_to.gamerteam.statement=デフォルトの背景画像プロバイダー
|
||||
about.thanks_to.glavo.statement=HMCLのメンテナ
|
||||
about.thanks_to.zekerzhayard.statement=HMCLに多くのテクニカルサポートを提供
|
||||
about.thanks_to.zkitefly.statement=HMCLに多くのテクニカルサポートを提供。
|
||||
about.thanks_to.mcmod=mcmod.cn
|
||||
about.thanks_to.mcmod.statement=Mod中国語の名前翻訳プロバイダー。
|
||||
about.thanks_to.red_lnn.statement=デフォルトの背景画像プロバイダー。
|
||||
about.thanks_to.zkitefly.statement=HMCLドキュメントメンテナ
|
||||
about.thanks_to.mcbbs=MCBBS (Minecraft中国語フォーラム)
|
||||
about.thanks_to.mcbbs.statement=中国本土ユーザー向けのmcbbs.netダウンロードミラープロバイダー(もう利用できません)
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=様々なMODの簡体字中国語名前翻訳とウィキを提供する
|
||||
about.thanks_to.red_lnn.statement=デフォルトの背景画像プロバイダー
|
||||
about.thanks_to.shulkersakura.statement=HMCLロゴプロバイダー
|
||||
about.thanks_to.users=HMCLユーザーグループのメンバー
|
||||
about.thanks_to.users.statement=寄付、バグレポートなどに感謝します。
|
||||
about.thanks_to.yushijinhun.statement=authlib-インジェクターのサポート
|
||||
about.thanks_to.users.statement=寄付、バグレポートなどに感謝します
|
||||
about.thanks_to.yushijinhun.statement=authlib-injector関連のサポートを提供する
|
||||
about.open_source=オープンソース
|
||||
about.open_source.statement=GPL v3(https://github.com/HMCL-dev/HMCL/)
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
account=アカウント
|
||||
account.cape=マント
|
||||
@@ -135,6 +140,7 @@ assets.index.malformed=アセットインデックスの形式が正しくあり
|
||||
button.cancel=キャンセル
|
||||
button.change_source=ダウンロードソースの変更
|
||||
button.clear=クリア
|
||||
button.copy_and_exit=コピーして終了
|
||||
button.delete=削除
|
||||
button.edit=編集
|
||||
button.install=インストール
|
||||
@@ -143,19 +149,23 @@ button.no=いいえ
|
||||
button.ok=OK
|
||||
button.refresh=更新
|
||||
button.remove=削除
|
||||
button.remove.confirm=削除してもよろしいですか?この操作をロールバックすることはできません!
|
||||
button.remove.confirm=本当に完全に削除しますか?この操作は元に戻せません!
|
||||
button.retry=リトライ
|
||||
button.save=保存
|
||||
button.save_as=名前を付けて保存
|
||||
button.select_all=すべて選択
|
||||
button.view=読む
|
||||
button.yes=はい
|
||||
|
||||
chat=グループチャット
|
||||
|
||||
color.recent=推奨
|
||||
color.custom=カスタムカラー
|
||||
|
||||
crash.NoClassDefFound=「HelloMinecraft!Launcher」ソフトウェアが破損していないことを確認してください。
|
||||
crash.user_fault=OSまたはJava環境が正しくインストールされていない可能性があり、クラッシュする可能性があります。Javaランタイム環境またはコンピュータを確認してください。
|
||||
crash.NoClassDefFound=このソフトウェアの整合性を確認するか、Javaの更新をお試しください。
|
||||
crash.user_fault=JavaまたはOSの環境が壊れているため、ランチャーがクラッシュしました。JavaまたはOSが正しくインストールされているかご確認ください。
|
||||
|
||||
curse.category.0=すべて
|
||||
curse.category.0=全て
|
||||
|
||||
# https://addons-ecs.forgesvc.net/api/v2/category/section/4471
|
||||
curse.category.4474=Sci-Fi
|
||||
@@ -313,9 +323,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=ファイル
|
||||
|
||||
@@ -343,12 +359,9 @@ game.crash.reason.entity=エンティティが原因でゲームを実行でき
|
||||
game.crash.reason.fabric_version_0_12=Fabric 0.12以降は、現在インストールされているmodと互換性がありません。ファブリックを0.11.7にダウングレードする必要があります。
|
||||
game.crash.reason.fabric_warnings=Fabricはいくつかの警告を出します:\n%1$s
|
||||
game.crash.reason.modmixin_failure=mod インジェクションの失敗により、現在のゲームを続行できません。\nこれは通常、MOD にバグがあるか、現在の環境と互換性がないことを意味します。\n間違った mod のログを確認できます。
|
||||
game.crash.reason.file_or_content_verification_failed=一部のファイルまたはコンテンツの検証に失敗したため、現在のゲームに問題があります。\nこのバージョン (mod を含む) を削除して再ダウンロードするか、再ダウンロード時にプロキシを使用するなどしてください。
|
||||
game.crash.reason.mod_repeat_installation=現在のゲームには複数の同一の Mod が繰り返しインストールされており、各 Mod は 1 回しか表示できません。ゲームを開始する前に繰り返しの Mod を削除してください。
|
||||
game.crash.reason.forge_error=Forge がエラー メッセージを表示した可能性があります。 \nログを表示し、エラー レポートのログ情報に従って対応するアクションを実行できます。 \nエラー メッセージが表示されない場合は、エラー レポートをチェックして、エラーがどのように発生したかを知ることができます。\n%1$s
|
||||
game.crash.reason.mod_resolution0=mod の問題により、現在のゲームを続行できません。 \n間違った mod のログを確認できます。
|
||||
game.crash.reason.mod_profile_causes_game_crash=mod プロファイルに問題があるため、現在のゲームを続行できません。 \n障害のある mod とその構成ファイルのログを確認できます。
|
||||
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Forge がエラー メッセージを表示した可能性があります。 \nログを表示し、エラー レポートのログ情報に従って対応するアクションを実行できます。 \nエラー メッセージが表示されない場合は、エラー レポートをチェックして、エラーがどのように発生したかを知ることができます。
|
||||
game.crash.reason.java_version_is_too_high=Java のバージョンが高すぎて実行を継続できないため、現在のゲームがクラッシュしました。 \nゲームを起動する前に、グローバル ゲーム設定またはゲーム固有の設定の Java パス タブで Java の以前のバージョンに切り替えてください。 \nそうでない場合は、<a href="https://www.java.com/download/">java.com (Java8)</a> または <a href="https://bell-sw.com/pages/downloads/#downloads">BellSoft Liberica Full JRE (Java17)</a> およびその他のプラットフォームをダウンロードしてインストールします (インストール後にランチャーを再起動します)。
|
||||
game.crash.reason.mod_name=現在のゲームはModファイル名の問題で続行できません。\nModファイル名は、英文の全半角の大文字と小文字(Aa ~ Zz)、数字(0 ~ 9)、横線(-)、アンダースコア(_)、点(.)のみを使用してください。\n上記のコンプライアンス文字をModフォルダに追加してください。
|
||||
game.crash.reason.incomplete_forge_installation=Forge / NeoForge のインストールが不完全なため、現在のゲームを続行できません。\nバージョン設定 - 自動インストールで Forge / NeoForge をアンインストールしてから再インストールしてください。
|
||||
@@ -393,7 +406,6 @@ game.crash.reason.stacktrace=不明。[ログ]ボタンをクリックすると
|
||||
game.crash.reason.too_old_java=Java仮想マシンのバージョンが低すぎるため、ゲームを実行できません。\nゲーム設定でJava仮想マシンの新しいバージョン(%1$s)に切り替えて、ゲームを再起動する必要があります。そうでない場合は、オンラインでダウンロードできます。
|
||||
game.crash.reason.unknown=不明。「ログ」ボタンをクリックすると詳細を確認できます。
|
||||
game.crash.reason.unsatisfied_link_error=必要なネイティブライブラリがないため、ゲームを実行できません。\n不足しているネイティブライブラリ:%1$s。\nゲーム設定でネイティブライブラリパスオプションを変更した場合は、元に戻すことをお勧めします。標準モードに切り替えます。\nすでに標準モードになっている場合は、不足しているネイティブライブラリがmodまたはゲームに属しているかどうかを確認してください。HMCLが原因であることが確実な場合は、フィードバックをお送りください。\nネイティブライブラリパスを本当にカスタマイズする必要がある場合は、ゲームに必要なすべてのネイティブライブラリをディレクトリに配置する必要があります。
|
||||
game.crash.reason.failed_to_load_a_library=ネイティブ ライブラリのロードに失敗したため、ゲームを実行できません。\nゲーム設定でネイティブライブラリパスオプションを変更した場合は、元に戻すことをお勧めします。標準モードに切り替えます。\nすでに標準モードになっている場合は、不足しているネイティブライブラリがmodまたはゲームに属しているかどうかを確認してください。HMCLが原因であることが確実な場合は、フィードバックをお送りください。\nネイティブライブラリパスを本当にカスタマイズする必要がある場合は、ゲームに必要なすべてのネイティブライブラリをディレクトリに配置する必要があります。
|
||||
game.crash.title=ゲームがクラッシュしました
|
||||
game.directory=ゲームディレクトリ
|
||||
game.version=ゲームバージョン
|
||||
@@ -448,6 +460,36 @@ install.new_game.malformed=無効な名前
|
||||
install.select=操作を選択します
|
||||
install.success=正常にインストールされました
|
||||
|
||||
java.add=Javaの追加
|
||||
java.add.failed=このJavaは無効であるか、現在のプラットフォームと互換性がない。
|
||||
java.disable=無効化
|
||||
java.disable.confirm=本当にこのJavaを無効にしますか?
|
||||
java.disabled.management=無効なJava
|
||||
java.disabled.management.remove=このJavaをリストから削除する
|
||||
java.disabled.management.restore=有効化
|
||||
java.download=Javaをダウンロード
|
||||
java.download.load_list.failed=バージョンリストの読み込みに失敗しました
|
||||
java.download.more=その他のJavaディストリビューション
|
||||
java.download.prompt=ダウンロードしたいJavaのバージョンを選択してください:
|
||||
java.download.distribution=配布の種類
|
||||
java.download.version=バージョン
|
||||
java.download.packageType=パッケージの種類
|
||||
java.management=Javaの管理
|
||||
java.info.architecture=アーキテクチャ
|
||||
java.info.vendor=発行元
|
||||
java.info.version=バージョン
|
||||
java.info.disco.distribution=配布の種類
|
||||
java.install=Javaのインストール
|
||||
java.install.archive=場所
|
||||
java.install.failed.exists=この名前はすでに使用されている
|
||||
java.install.failed.invalid=このアーカイブは有効なJavaインストール・パッケージではないため、インストールできません。
|
||||
java.install.failed.unsupported_platform=このJavaは現在のプラットフォームと互換性がないため、インストールできません。
|
||||
java.install.name=名前
|
||||
java.install.warning.invalid_character=名前に不正な文字が含まれています
|
||||
java.reveal=Javaディレクトリを表示する
|
||||
java.uninstall=このJavaをアンインストールする
|
||||
java.uninstall.confirm=本当にこのJavaをアンインストールしますか?この操作は元に戻せません!
|
||||
|
||||
lang=日本語
|
||||
lang.default=システム言語を使用する
|
||||
|
||||
@@ -460,8 +502,8 @@ launch.advice.forge2760_liteloader=Forge 2760以降はLiteLoaderと互換性が
|
||||
launch.advice.forge28_2_2_optifine=Forge28.2.2以降のバージョンはOptiFineと互換性がありません。Forgeを28.2.1以前のバージョンにダウングレードすることを検討してください。
|
||||
launch.advice.java8_1_13=Minecraft 1.13以降は、Java8以降でのみ実行できます。
|
||||
launch.advice.java8_51_1_13=Minecraft 1.13は、1.8.0_51より前のJava8でクラッシュする可能性があります。最新バージョンのJava8をインストールしてください。
|
||||
launch.advice.java9=Java9以降のバージョンのJavaでMinecraft1.12以前を起動することはできません。
|
||||
ゲームを高速化するには、launch.advice.newer_java=Java8をお勧めします。多くのMinecraft1.12以降、およびほとんどのModには、Java8が必要です。
|
||||
launch.advice.java9=Java9以降のバージョンのJavaでMinecraft1.12以前を起動することはできません。ゲームを高速化するには、Java8をお勧めします。
|
||||
launch.advice.newer_java=多くのMinecraft1.12以降、およびほとんどのModには、Java8が必要です。
|
||||
launch.advice.not_enough_space=割り当てたメモリが多すぎます。物理メモリサイズが%dMBであるため、ゲームがクラッシュする可能性があります。
|
||||
launch.advice.too_large_memory_for_32bit=32ビットJavaランタイム環境が原因で、割り当てたメモリが多すぎるため、ゲームがクラッシュする可能性があります。32ビットシステムの最大メモリ容量は1024MBです。
|
||||
launch.advice.vanilla_linux_java_8=Linux x86-64の場合、Minecraft1.12.2以下はJava8でのみ実行できます。\nJava9以降のバージョンでは、liblwjgl.soなどの32ビットネイティブライブラリをロードできません。
|
||||
@@ -499,7 +541,7 @@ launcher.background.translucent=半透明
|
||||
launcher.cache_directory=キャッシュ用のディレクトリ
|
||||
launcher.cache_directory.clean=クリアランス
|
||||
launcher.cache_directory.choose=キャッシュするディレクトリを選択します
|
||||
launcher.cache_directory.default=標準(%AppData%/.minecraft または ~/.minecraft)
|
||||
launcher.cache_directory.default=標準(%APPDATA%/.minecraft または ~/.minecraft)
|
||||
launcher.cache_directory.disabled=無効(常にゲームパスを使用する)
|
||||
launcher.cache_directory.invalid=無効なディレクトリ。デフォルト設定の復元。
|
||||
launcher.contact=お問い合わせ
|
||||
@@ -574,8 +616,6 @@ modpack.scan=このmodpackをスキャンしています
|
||||
modpack.task.install=Modpackのインポート
|
||||
modpack.task.install.error=このmodpackファイルは認識できません。CurseおよびMultiMCmodpackのみがサポートされています。
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.curse.completion=Cursemodpackに関連するファイルをインストールします
|
||||
modpack.type.curse.tolerable_error=このmodpackのすべてのファイルのダウンロードを完了できません。対応するゲームバージョンを開始するときに、ダウンロードを再試行できます。ネットワークの問題により、数回再試行する場合があります。
|
||||
modpack.type.curse.error=このmodpackをインストールできません。再試行してください。
|
||||
modpack.type.curse.not_found=必要なリソースの一部が欠落しているため、ダウンロードできませんでした。最新バージョンまたは他のmodpackを検討してください。
|
||||
modpack.type.manual.warning=このmodpackをインポートする代わりに、おそらくこのmodpackファイルを直接解凍する必要があります。そして、バンドルされたランチャーを使用してゲームを起動します。このmodpackは、ランチャーによってエクスポートされるのではなく、.minecraftディレクトリを圧縮することによって手動で作成されます。HMCLはこのmodpackのインポートを試みることができます、続行しますか?
|
||||
@@ -625,7 +665,8 @@ mods.add.success=mods %s が正常に追加されました。
|
||||
mods.category=Category
|
||||
mods.check_updates=更新を確認
|
||||
mods.check_updates.current_version=Current
|
||||
mods.check_updates.failed=一部のファイルのダウンロードに失敗しました
|
||||
mods.check_updates.failed_check=更新のチェックに失敗しました
|
||||
mods.check_updates.failed_download=一部のファイルのダウンロードに失敗しました
|
||||
mods.check_updates.file=ファイル
|
||||
mods.check_updates.source=Source
|
||||
mods.check_updates.target_version=Target
|
||||
@@ -761,8 +802,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 +884,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 +894,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,44 +1,47 @@
|
||||
#
|
||||
# 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=Авторские права
|
||||
about.copyright.statement=Авторские права © 2024 huangyuhui.
|
||||
about.author=Автор
|
||||
about.author.statement=bilibili: @huanghongxun
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=Кликните на ссылку, чтобы увидеть весь текст.
|
||||
about.dependency=Зависимости
|
||||
about.legal=Юридическое подтверждение
|
||||
about.thanks_to=Отдельная благодарность
|
||||
about.thanks_to.bangbang93.statement=За предоставление API скачивания BMCLAPI. Рассмотрите возможность пожертвования.
|
||||
about.thanks_to.bangbang93.statement=За предоставление зеркала загрузки BMCLAPI, пожалуйста, подумайте о пожертвовании!
|
||||
about.thanks_to.burningtnt.statement=Оказал большую техническую поддержку HMCL.
|
||||
about.thanks_to.contributors=Всем участникам на GitHub
|
||||
about.thanks_to.contributors.statement=Без потрясающего сообщества с открытым исходным кодом, Hello Minecraft! Launcher не смог бы добраться так далеко.
|
||||
about.thanks_to.contributors.statement=Без потрясающего сообщества с открытым исходным кодом, HMCL не смог бы добраться так далеко.
|
||||
about.thanks_to.gamerteam.statement=За предоставление фонового изображения.
|
||||
about.thanks_to.glavo.statement=Оказал большую техническую поддержку HMCL.
|
||||
about.thanks_to.glavo.statement=Отвечает за поддержание HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=Оказал большую техническую поддержку HMCL.
|
||||
about.thanks_to.zkitefly.statement=Оказал большую техническую поддержку HMCL.
|
||||
about.thanks_to.mcmod=mcmod.cn
|
||||
about.thanks_to.mcmod.statement=За предоставление перевода на китайский и вики для различных модов.
|
||||
about.thanks_to.zkitefly.statement=Отвечает за поддержание документации HMCL.
|
||||
about.thanks_to.mcbbs=MCBBS (Китайский форум Minecraft)
|
||||
about.thanks_to.mcbbs.statement=За предоставление зеркала загрузки mcbbs.net для пользователей материкового Китая. (Больше недоступно)
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=За предоставление упрощенного китайского перевода и вики для различных модов.
|
||||
about.thanks_to.red_lnn.statement=За предоставление фонового изображения.
|
||||
about.thanks_to.shulkersakura.statement=За предоставление логотипа для HMCL.
|
||||
about.thanks_to.users=Членам группы пользователей HMCL
|
||||
about.thanks_to.users.statement=Спасибо за пожертвования, отчёты об ошибках и так далее.
|
||||
about.thanks_to.yushijinhun.statement=За предоставление поддержки, связанной с authlib-injector.
|
||||
@@ -157,8 +160,11 @@ button.retry=Повторить снова
|
||||
button.save=Сохранить
|
||||
button.save_as=Сохранить как
|
||||
button.select_all=Выбрать все
|
||||
button.view=Просмотреть
|
||||
button.yes=Да
|
||||
|
||||
chat=Групповой чат
|
||||
|
||||
color.recent=Рекомендуемые
|
||||
color.custom=Пользовательский цвет
|
||||
|
||||
@@ -336,8 +342,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=Файл
|
||||
|
||||
@@ -365,13 +376,10 @@ game.crash.reason.fabric_version_0_12=Fabric 0.12 (и выше) несовмес
|
||||
game.crash.reason.mixin_apply_mod_failed=Текущая игра не может продолжать работать из-за невозможности применения мода %1$s с помощью Mixin.\nВы можете попробовать удалить или обновить этот мод, чтобы решить проблему.
|
||||
game.crash.reason.fabric_warnings=Предупреждения от Fabric:\n%1$s
|
||||
game.crash.reason.modmixin_failure=Текущая игра не может продолжать работать из-за некоторых сбоев внедрения модов.\nОбычно это означает, что мод содержит ошибку или несовместим с текущей средой.\nВы можете проверить журнал на наличие неправильного мода.
|
||||
game.crash.reason.file_or_content_verification_failed=В текущей игре возникла проблема, поскольку не удалось проверить некоторые файлы или содержимое.\nПожалуйста, попробуйте удалить эту версию (включая моды) и загрузить заново, или попробуйте использовать прокси при повторной загрузке и т. д.
|
||||
game.crash.reason.mod_repeat_installation=В текущей игре неоднократно установлено несколько одинаковых модов, и каждый мод может появиться только один раз. Пожалуйста, удалите повторяющиеся моды перед запуском игры.
|
||||
game.crash.reason.need_jdk11=Текущая игра не может продолжать работать из-за неподходящей версии виртуальной машины Java.\nВам необходимо загрузить и установить Java 11, а также установить версию Java, начинающуюся с 11, в глобальных (конкретных) настройках игры.
|
||||
game.crash.reason.forge_error=Возможно, Forge предоставил сообщение об ошибке.\nВы можете просмотреть журнал и выполнить соответствующие действия в соответствии с информацией журнала в отчете об ошибках.\nЕсли вы не видите сообщения об ошибке, вы можете просмотреть отчет об ошибке, чтобы узнать, как она возникла.\n%1$s
|
||||
game.crash.reason.mod_resolution0=Текущая игра не может продолжать работать из-за некоторых проблем с модом.\nВы можете проверить журнал на наличие неправильного мода.
|
||||
game.crash.reason.mod_profile_causes_game_crash=Текущая игра не может продолжать работать из-за проблемы с профилем мода.\nВы можете проверить журнал на наличие неисправного мода и его конфигурационного файла.
|
||||
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Forge мог выдать сообщение об ошибке.\nВы можете просмотреть журнал и выполнить соответствующие действия в соответствии с информацией журнала в отчете об ошибках.\nЕсли вы не видите сообщения об ошибке, вы можете просмотреть отчет об ошибке, чтобы узнать, как она возникла.
|
||||
game.crash.reason.java_version_is_too_high=Текущая игра аварийно завершилась из-за того, что версия Java слишком высока и не может продолжать работать.\nПеред запуском игры переключитесь на более раннюю версию Java на вкладке "Путь к Java" в глобальных настройках игры или в настройках игры.\nЕсли нет, вы можете загрузить его с <a href="https://www.java.com/download/">java.com (Java8)</a> или <a href="https://bell- sw.com/pages/downloads">BellSoft Liberica Full JRE (Java17)</a> и другие платформы, чтобы загрузить и установить ее (перезапустите программу запуска после установки).
|
||||
game.crash.reason.mod_name=Текущая игра не может продолжать работать из - за проблем с именем файла Mod. Имя файла\nMod должно быть использовано только в английском языке в полном углу с большими буквами (Aa ~ Zz), цифрами (0 ~ 9), горизонтальными линиями (-), подчеркнутыми (_) и точками (.) .\nПожалуйста, добавьте символ соответствия в папку Mod для всех несовместимых имен файлов Mod.
|
||||
game.crash.reason.incomplete_forge_installation=Текущая игра не может быть продолжена из-за незавершенной установки Forge / NeoForge.\nУдалите и переустановите Forge / NeoForge в настройках версии - Автоматическая установка.
|
||||
@@ -416,7 +424,6 @@ game.crash.reason.stacktrace=Неизвестно. Вы можете просм
|
||||
game.crash.reason.too_old_java=Невозможно запустить игру из-за того, что версия виртуальной машины Java слишком низкая.\nYou need to switch to a newer version (%1$s) of Java virtual machine in the game settings and restart the game. If not, you can download it online.
|
||||
game.crash.reason.unknown=Неизвестно. Вы можете просмотреть подробности, кликнув на кнопку «Журналы».
|
||||
game.crash.reason.unsatisfied_link_error=Невозможно запустить игру из-за того, что отсутствуют необходимые встроенные библиотеки.\nОтсутствующая нативная библиотека: %1$s.\nЕсли в настройках игры вы изменили параметр пути к нативной библиотеке, лучше переключиться обратно в стандартный режим.\nЕсли это уже стандартный режим, проверьте, принадлежит ли отсутствующая нативная библиотека моду или игре. Если вы уверены, что это произошло по вине HMCL, вы можете написать нам через обратную связь.\nЕсли вам действительно нужно настроить путь к нативным библиотекам, вы должны поместить в каталог все нативные библиотеки, которые требуются игре.
|
||||
game.crash.reason.failed_to_load_a_library=Не удалось запустить игру, так как не удалось загрузить встроенную библиотеку.\nЕсли в настройках игры вы изменили параметр пути к нативной библиотеке, лучше переключиться обратно в стандартный режим.\nЕсли это уже стандартный режим, проверьте, принадлежит ли отсутствующая нативная библиотека моду или игре. Если вы уверены, что это произошло по вине HMCL, вы можете написать нам через обратную связь.\nЕсли вам действительно нужно настроить путь к нативным библиотекам, вы должны поместить в каталог все нативные библиотеки, которые требуются игре.
|
||||
game.crash.title=Игра вылетела
|
||||
game.directory=Путь игры
|
||||
game.version=Версия игры
|
||||
@@ -471,6 +478,36 @@ install.new_game.malformed=Недопустимое имя
|
||||
install.select=Выберите операцию
|
||||
install.success=Успешно установлено.
|
||||
|
||||
java.add=Добавить Java
|
||||
java.add.failed=Этот Java недопустим или несовместим с текущей платформой.
|
||||
java.disable=Отключить Java
|
||||
java.disable.confirm=Вы уверены, что хотите отключить эту Java?
|
||||
java.disabled.management=Отключенная Java
|
||||
java.disabled.management.remove=Удалить эту Java из списка
|
||||
java.disabled.management.restore=Включить эту Java
|
||||
java.download=Скачать Java
|
||||
java.download.load_list.failed=Не удалось загрузить список версий
|
||||
java.download.more=Больше дистрибутивов Java
|
||||
java.download.prompt=Выберите версию Java, которую вы хотите загрузить:
|
||||
java.download.distribution=Дистрибуция
|
||||
java.download.version=Версия
|
||||
java.download.packageType=Тип упаковки
|
||||
java.management=Управление Java
|
||||
java.info.architecture=Архитектура
|
||||
java.info.vendor=Поставщик
|
||||
java.info.version=Версия
|
||||
java.info.disco.distribution=Дистрибуция
|
||||
java.install=Установите Java
|
||||
java.install.archive=Расположение
|
||||
java.install.failed.exists=Это имя уже используется
|
||||
java.install.failed.invalid=Этот архив не является допустимым установочным пакетом Java, поэтому его установка невозможна.
|
||||
java.install.failed.unsupported_platform=Эта Java несовместима с текущей платформой, поэтому ее невозможно установить.
|
||||
java.install.name=Название
|
||||
java.install.warning.invalid_character=Неправильный символ в названии
|
||||
java.reveal=Откройте каталог Java
|
||||
java.uninstall=Удалить Java
|
||||
java.uninstall.confirm=Вы уверены, что хотите удалить эту Java? Это действие нельзя отменить!
|
||||
|
||||
lang=Русский
|
||||
lang.default=Использовать язык системы
|
||||
|
||||
@@ -526,7 +563,7 @@ launcher.background.translucent=Полупрозрачный
|
||||
launcher.cache_directory=Папка с кэшем
|
||||
launcher.cache_directory.clean=Очистить кэш
|
||||
launcher.cache_directory.choose=Выберите папку для кэша
|
||||
launcher.cache_directory.default=По умолчанию (%AppData%/.minecraft или ~/.minecraft)
|
||||
launcher.cache_directory.default=По умолчанию (%APPDATA%/.minecraft или ~/.minecraft)
|
||||
launcher.cache_directory.disabled=Отключено
|
||||
launcher.cache_directory.invalid=Не удаётся создать папку для кэша, возвращаем к значению по умолчанию.
|
||||
launcher.contact=Связаться с нами
|
||||
@@ -602,8 +639,6 @@ modpack.scan=Разбор индекса модпака
|
||||
modpack.task.install=Установить модпак
|
||||
modpack.task.install.error=Не удаётся идентифицировать этот модпак. Поддерживаются только модпаки Curse, Modrinth, MultiMC и MCBBS.
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.curse.completion=Установите файлы, связанные с модпаком Curse
|
||||
modpack.type.curse.tolerable_error=Мы не можем завершить скачивание всех файлов этого модпака. Вы можете повторить скачивание при запуске соответствующей версии игры. Вы можете повторить попытку несколько раз из-за проблем с сетью.
|
||||
modpack.type.curse.error=Невозможно установить этот модпак. Повторите ещё раз.
|
||||
modpack.type.curse.not_found=Некоторые из необходимых ресурсов отсутствуют и поэтому не могут быть загружены. Скачайте последнюю версию или другие модпаки.
|
||||
modpack.type.manual.warning=Возможно, вам нужно напрямую распаковать файл этого модпака, а не импортировать его. И запустите игру с помощью прилагаемого лаунчера. Этот модпак создаётся вручную путем сжатия директории .minecraft, а не экспортируется лаунчером. HMCL может попытаться импортировать этот модпак, продолжать?
|
||||
@@ -661,7 +696,8 @@ mods.add.success=%s был успешно установлен.
|
||||
mods.category=Категория
|
||||
mods.check_updates=Проверить обновления
|
||||
mods.check_updates.current_version=Текущая версия
|
||||
mods.check_updates.failed=Не удаётся скачать некоторые файлы.
|
||||
mods.check_updates.failed_check=Не удалось проверить обновления.
|
||||
mods.check_updates.failed_download=Не удаётся скачать некоторые файлы.
|
||||
mods.check_updates.file=Файл
|
||||
mods.check_updates.source=Источник
|
||||
mods.check_updates.target_version=Целевая версия
|
||||
@@ -708,7 +744,7 @@ world.import.failed=Не удаётся импортировать этот ми
|
||||
world.import.invalid=Не удаётся разобрать сохранение.
|
||||
world.info.title=Сведения о мире %s
|
||||
world.info.basic=Основные сведения
|
||||
world.info.allow_cheats=Разрешить читы
|
||||
world.info.allow_cheats=Разрешить команды/читы
|
||||
world.info.dimension.the_nether=Нижний мир
|
||||
world.info.dimension.the_end=Край
|
||||
world.info.difficulty=Сложность
|
||||
@@ -831,8 +867,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 +953,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 +963,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=Проверка наличия обновлений
|
||||
|
||||
@@ -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: pan93412
|
||||
|
||||
about=關於
|
||||
about.copyright=著作權
|
||||
about.copyright.statement=著作權所有 © 2024 huangyuhui
|
||||
@@ -64,7 +65,7 @@ account.failed.injector_download_failure=無法下載 authlib-injector,請檢
|
||||
account.failed.invalid_credentials=你的使用者名稱或密碼錯誤,或者登入次數過多被暫時禁止登入,請稍後再試。
|
||||
account.failed.invalid_password=密碼無效
|
||||
account.failed.invalid_token=請嘗試登出並重新輸入密碼登入。
|
||||
account.failed.migration=你的帳戶需要遷移至 Microsoft 帳戶。如果你已經遷移,你需要使用 Microsoft 方式登入遷移後的帳戶。
|
||||
account.failed.migration=你的帳戶需要遷移至 Microsoft 帳戶。如果你已經遷移,你需要使用遷移後的 Microsoft 帳戶登入。
|
||||
account.failed.no_character=該帳戶沒有角色。
|
||||
account.failed.server_disconnected=無法訪問登入伺服器,帳戶資訊重新整理失敗。\n\
|
||||
你可以選取「再次重新整理帳戶」重新嘗試。\n\
|
||||
@@ -97,11 +98,11 @@ account.copy_uuid=複製該帳戶的 UUID
|
||||
account.methods=登入方式
|
||||
account.methods.authlib_injector=authlib-injector 登入
|
||||
account.methods.microsoft=Microsoft 帳戶
|
||||
account.methods.microsoft.birth=如何修改帳戶出生日期
|
||||
account.methods.microsoft.deauthorize=解除帳戶授權
|
||||
account.methods.microsoft.birth=如何變更帳戶出生日期
|
||||
account.methods.microsoft.deauthorize=移除應用存取權
|
||||
account.methods.microsoft.close_page=已完成 Microsoft 帳戶授權,接下來啟動器還需要完成其餘登入步驟。你現在可以關閉本頁面了。
|
||||
account.methods.microsoft.error.add_family=由於你未滿 18 歲,你的帳戶必須被加入到家庭中才能登入遊戲。你也可以點擊上方連結更改你的帳戶的出生日期,使年齡滿 18 歲以上以繼續登入。
|
||||
account.methods.microsoft.error.add_family_probably=請檢查你的帳戶設定,如果你未滿 18 歲,你的帳戶必須被加入到家庭中才能登入遊戲。你也可以點擊上方連結更改你的帳戶的出生日期,使年齡滿 18 歲以上以繼續登入。
|
||||
account.methods.microsoft.error.add_family=由於你未滿 18 歲,你的帳戶必須被加入到家庭中才能登入遊戲。你也可以點擊上方「編輯帳戶配置檔」更改你的帳戶出生日期,使年齡滿 18 歲以上以繼續登入。
|
||||
account.methods.microsoft.error.add_family_probably=請檢查你的帳戶設定,如果你未滿 18 歲,你的帳戶必須被加入到家庭中才能登入遊戲。你也可以點擊上方「編輯帳戶配置檔」更改你的帳戶出生日期,使年齡滿 18 歲以上以繼續登入。
|
||||
account.methods.microsoft.error.country_unavailable=你所在的國家或地區不受 Xbox Live 的支援。
|
||||
account.methods.microsoft.error.missing_xbox_account=你的 Microsoft 帳戶尚未關聯 Xbox 帳戶,你必須先建立 Xbox 帳戶,才能登入遊戲。
|
||||
account.methods.microsoft.error.no_character=該帳戶未包含 Minecraft: Java 版購買記錄。\n若已購買,則可能未建立遊戲檔案,請點擊上方連結建立。
|
||||
@@ -111,17 +112,17 @@ account.methods.microsoft.logging_in=登入中……
|
||||
account.methods.microsoft.makegameidsettings=建立檔案 / 編輯檔案名稱
|
||||
account.methods.microsoft.hint=請點擊「登入」按鈕,稍後複製此處顯示的代碼,在打開的登入網頁中完成登入過程。\n\
|
||||
\n\
|
||||
如果登入 Microsoft 帳戶的令牌洩露,可點擊下方連結解除授權,然後等待令牌過期。\n\
|
||||
如果登入 Microsoft 帳戶的令牌洩露,可點擊下方「移除應用存取權」,然後等待令牌過期。\n\
|
||||
\n\
|
||||
如遇到問題,你可以點擊右上角幫助按鈕進行求助。
|
||||
account.methods.microsoft.manual=你的代碼為 <b>%1$s</b>,請點擊此處複製。\n\
|
||||
\n\
|
||||
點擊「登入」按鈕後,你應該在打開的登入網頁中完成登入過程。如果網頁沒有打開,你可以自行在瀏覽器中訪問 %2$s\n\
|
||||
\n\
|
||||
如果登入 Microsoft 帳戶的令牌洩露,可點擊下方連結解除授權,然後等待令牌過期。\n\
|
||||
如果登入 Microsoft 帳戶的令牌洩露,可點擊下方「移除應用存取權」,然後等待令牌過期。\n\
|
||||
\n\
|
||||
如遇到問題,你可以點擊右上角幫助按鈕進行求助。
|
||||
account.methods.microsoft.profile=帳戶設定頁
|
||||
account.methods.microsoft.profile=編輯帳戶配置檔
|
||||
account.methods.microsoft.purchase=購買 Minecraft
|
||||
account.methods.forgot_password=忘記密碼
|
||||
account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL,請下載官方版本進行登入。
|
||||
@@ -327,7 +328,7 @@ download.existing=檔案已存在,無法儲存。你可以將檔案儲存至
|
||||
download.external_link=打開下載網站
|
||||
download.failed=下載失敗:%1$s\n錯誤碼:%2$d
|
||||
download.failed.empty=沒有可供安裝的版本,按一下此處返回。
|
||||
download.failed.no_code=下載失敗:%s
|
||||
download.failed.no_code=下載失敗
|
||||
download.failed.refresh=載入版本清單失敗,按一下此處重試。
|
||||
download.game=新遊戲
|
||||
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)
|
||||
@@ -374,12 +375,12 @@ fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已為 Window
|
||||
|
||||
feedback=回報
|
||||
feedback.channel=回報管道
|
||||
feedback.discord=Discord
|
||||
feedback.discord.statement=歡迎加入 Discord 討論區,加入後請遵守討論區規定
|
||||
feedback.discord=Discord 伺服器
|
||||
feedback.discord.statement=歡迎加入 Discord 伺服器,加入後請遵守討論區規則
|
||||
feedback.github=GitHub Issues
|
||||
feedback.github.statement=提交一個 GitHub Issue
|
||||
feedback.qq_group=HMCL 使用者群組
|
||||
feedback.qq_group.statement=歡迎加入 HMCL 使用者群組,加入後請遵守群規
|
||||
feedback.qq_group=HMCL 使用者 QQ 群組
|
||||
feedback.qq_group.statement=歡迎加入 HMCL 使用者 QQ 群組,加入後請遵守群組規則
|
||||
|
||||
file=檔案
|
||||
|
||||
@@ -607,7 +608,7 @@ launcher.background.translucent=半透明
|
||||
launcher.cache_directory=檔案下載快取目錄
|
||||
launcher.cache_directory.clean=清理
|
||||
launcher.cache_directory.choose=選取檔案下載快取目錄
|
||||
launcher.cache_directory.default=預設 ("%AppData%/.minecraft" 或 "~/.minecraft")
|
||||
launcher.cache_directory.default=預設 ("%APPDATA%/.minecraft" 或 "~/.minecraft")
|
||||
launcher.cache_directory.disabled=停用
|
||||
launcher.cache_directory.invalid=無法建立自訂的快取目錄,還原至預設設定
|
||||
launcher.contact=聯絡我們
|
||||
@@ -789,7 +790,8 @@ mods.channel.release=Release
|
||||
mods.check_updates=檢查模組更新
|
||||
mods.check_updates.current_version=目前版本
|
||||
mods.check_updates.empty=沒有需要更新的模組
|
||||
mods.check_updates.failed=部分檔案下載失敗
|
||||
mods.check_updates.failed_check=檢查更新失敗
|
||||
mods.check_updates.failed_download=部分檔案下載失敗
|
||||
mods.check_updates.file=檔案
|
||||
mods.check_updates.source=來源
|
||||
mods.check_updates.target_version=目標版本
|
||||
@@ -854,7 +856,7 @@ world.import.failed=無法匯入此世界: %s
|
||||
world.import.invalid=無法識別的存檔壓縮檔
|
||||
world.info.title=世界 [%s] - 世界資訊
|
||||
world.info.basic=基本資訊
|
||||
world.info.allow_cheats=允許指令
|
||||
world.info.allow_cheats=允許指令(作弊)
|
||||
world.info.dimension.the_nether=地獄
|
||||
world.info.dimension.the_end=末地
|
||||
world.info.difficulty=難易度
|
||||
@@ -986,8 +988,8 @@ settings.advanced.renderer.llvmpipe=軟繪製器 (效能較差,相容性最好
|
||||
settings.advanced.renderer.zink=Vulkan (效能最好,相容性較差)
|
||||
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=前置指令
|
||||
@@ -1072,8 +1074,8 @@ settings.type.special.edit=編輯實例特定遊戲設定
|
||||
settings.type.special.edit.hint=目前實例「%s」啟用了「實例特定遊戲設定」,本頁面選項不對目前實例生效。點擊連結以修改目前實例設定。
|
||||
|
||||
sponsor=贊助
|
||||
sponsor.bmclapi=中國大陸下載源由 BMCLAPI 提供高速下載服務。
|
||||
sponsor.hmcl=Hello Minecraft! Launcher 是一個免費、自由、開源的 Minecraft 啟動器,允許玩家方便快捷地安裝、管理、執行遊戲。點選此處查閱更多詳細訊息。
|
||||
sponsor.bmclapi=中國大陸下載源由 BMCLAPI 提供高速下載服務。點選此處查閱詳細訊息。
|
||||
sponsor.hmcl=Hello Minecraft! Launcher 是一個免費、自由、開源的 Minecraft 啟動器,允許玩家方便快捷地安裝、管理、執行遊戲。點選此處查閱詳細訊息。
|
||||
|
||||
system.architecture=架構
|
||||
system.operating_system=作業系統
|
||||
@@ -1084,15 +1086,13 @@ update=啟動器更新
|
||||
update.accept=更新
|
||||
update.changelog=更新日誌
|
||||
update.channel.dev=開發版
|
||||
update.channel.dev.hint=你正在使用 HMCL 開發版。開發版包含一些未在穩定版中包含的測試性功能,僅用於體驗新功能。\n\
|
||||
開發版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||
如果你遇到了使用問題,可以透過設定中 <a href="hmcl://settings/feedback">回報頁面</a> 提供的管道進行回報。\n\
|
||||
update.channel.dev.hint=你正在使用 HMCL 開發版。開發版包含一些未在穩定版中包含的測試性功能,僅用於體驗新功能。開發版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||
\n\
|
||||
<a href="hmcl://hide-announcement">為目前版本隱藏該提示</a>。
|
||||
如果你遇到了使用問題,可以透過設定中 <a href="hmcl://settings/feedback">回報頁面</a> 提供的管道進行回報。
|
||||
update.channel.dev.title=開發版提示
|
||||
update.channel.nightly=預覽版
|
||||
update.channel.nightly.hint=你正在使用 HMCL 預覽版。預覽版更新較為頻繁,包含一些未在穩定版和開發版中包含的測試性功能,僅用於體驗新功能。\n\
|
||||
預覽版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||
update.channel.nightly.hint=你正在使用 HMCL 預覽版。預覽版更新較為頻繁,包含一些未在穩定版和開發版中包含的測試性功能,僅用於體驗新功能。預覽版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||
\n\
|
||||
如果你遇到了使用問題,可以透過設定中 <a href="hmcl://settings/feedback">回報頁面</a> 提供的管道進行回報。
|
||||
update.channel.nightly.title=預覽版提示
|
||||
update.channel.stable=穩定版
|
||||
@@ -1148,6 +1148,10 @@ version.manage.rename.fail=重新命名版本失敗,可能檔案被佔用或
|
||||
version.settings=遊戲設定
|
||||
version.update=更新模組包
|
||||
|
||||
wiki.tooltip=Minecraft Wiki 頁面
|
||||
wiki.version.game.release=https://zh.minecraft.wiki/w/Java版%s?variant=zh-tw
|
||||
wiki.version.game.snapshot=https://zh.minecraft.wiki/w/%s?variant=zh-tw
|
||||
|
||||
wizard.prev=< 上一步
|
||||
wizard.failed=失敗
|
||||
wizard.finish=完成
|
||||
|
||||
@@ -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: huangyuhui
|
||||
|
||||
about=关于
|
||||
about.copyright=版权
|
||||
about.copyright.statement=版权所有 © 2024 huangyuhui
|
||||
@@ -64,7 +65,7 @@ account.failed.injector_download_failure=无法下载 authlib-injector,可能
|
||||
account.failed.invalid_credentials=你的用户名或密码错误,或登录次数过多被暂时禁止登录,请稍后再试。
|
||||
account.failed.invalid_password=无效的密码
|
||||
account.failed.invalid_token=请尝试登出并重新输入密码登录
|
||||
account.failed.migration=你的账户需要迁移至微软账户。如果你已经迁移,你需要使用微软账户登录迁移后的账户。
|
||||
account.failed.migration=你的账户需要迁移至微软账户。如果你已经迁移,你需要使用迁移后的微软账户登录。
|
||||
account.failed.no_character=该账户没有角色
|
||||
account.failed.server_disconnected=无法访问登录服务器,账户信息刷新失败。\n\
|
||||
你可以选择“再次刷新账户”重新尝试。\n\
|
||||
@@ -98,11 +99,11 @@ account.copy_uuid=复制该账户的 UUID
|
||||
account.methods=登录方式
|
||||
account.methods.authlib_injector=外置登录 (authlib-injector)
|
||||
account.methods.microsoft=微软账户
|
||||
account.methods.microsoft.birth=如何修改账户出生日期
|
||||
account.methods.microsoft.birth=如何更改账户出生日期
|
||||
account.methods.microsoft.close_page=已完成微软账户授权,接下来启动器还需要完成其余登录步骤。你现在可以关闭本页面了。
|
||||
account.methods.microsoft.deauthorize=解除账户授权
|
||||
account.methods.microsoft.error.add_family=请点击上方“账户设置页”更改你的账户出生日期,使年龄满 18 岁以上,或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.add_family_probably=请点击上方“账户设置页”更改你的账户出生日期,使年龄满 18 岁以上,或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.add_family=请点击上方“编辑账户个人信息”更改你的账户出生日期,使年龄满 18 岁以上,或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.add_family_probably=请点击上方“编辑账户个人信息”更改你的账户出生日期,使年龄满 18 岁以上,或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.country_unavailable=你所在的国家或地区不受 Xbox Live 的支持。
|
||||
account.methods.microsoft.error.missing_xbox_account=请点击上方“创建档案”关联 Xbox 账户。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.no_character=请确认你已经购买了 Minecraft: Java 版。\n若已购买,则可能未创建游戏档案,请点击上方“创建档案”以创建游戏档案。\n你可以点击右上角帮助按钮进行求助。
|
||||
@@ -111,25 +112,25 @@ account.methods.microsoft.error.wrong_verify_method=请在微软账户登录页
|
||||
account.methods.microsoft.logging_in=登录中……
|
||||
account.methods.microsoft.makegameidsettings=创建档案 / 编辑档案名称
|
||||
account.methods.microsoft.hint=你需要按照以下步骤添加账户:\n\
|
||||
\ 1.点击“登录”按钮;\n\
|
||||
\ 2.在弹出的网页中输入 HMCL 显示的代码,并点击“下一步”;\n\
|
||||
\ 3.按照网站的提示登录;\n\
|
||||
\ 4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”;\n\
|
||||
\ 5.在网站提示“大功告成”后,只需等待账户完成添加即可。\n\
|
||||
若网站提示“出现错误”的标识或账户添加失败时,请重新按照以上步骤重新添加。\n\
|
||||
<b>若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用网络代理并重试。</b>\n\
|
||||
\ 1. 点击“登录”按钮;\n\
|
||||
\ 2. 在弹出的网页中输入 HMCL 显示的代码,并点击“允许访问”;\n\
|
||||
\ 3. 按照网站的提示登录;\n\
|
||||
\ 4. 当网站提示“是否允许此应用访问你的信息?”时,请点击“接受”;\n\
|
||||
\ 5. 在网站提示“大功告成”后,等待账户完成添加即可。\n\
|
||||
若网站提示“出现错误”或账户添加失败时,请按照以上步骤重新添加。\n\
|
||||
<b>若设备网络环境不佳,可能会导致网页加载缓慢甚至无法加载,请使用网络代理并重试。</b>\n\
|
||||
如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.manual=你需要按照以下步骤添加:\n\
|
||||
\ 1.点击“登录”按钮;\n\
|
||||
\ 2.在弹出的网页中输入:<b>%1$s</b> (已自动复制,点此再次复制),并点击“下一步”;\n\
|
||||
\ 3.按照网站的提示登录;\n\
|
||||
\ 4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”;\n\
|
||||
\ 5.在网站提示“大功告成”后,只需等待账户完成添加即可。\n\
|
||||
\ 1. 点击“登录”按钮;\n\
|
||||
\ 2. 在弹出的网页中输入 <b>%1$s</b> (已自动复制),并点击“允许访问”;\n\
|
||||
\ 3. 按照网站的提示登录;\n\
|
||||
\ 4. 当网站提示“是否允许此应用访问你的信息?”时,请点击“接受”;\n\
|
||||
\ 5. 在网站提示“大功告成”后,等待账户完成添加即可。\n\
|
||||
若网站未能显示,请手动在浏览器中打开:%2$s\n\
|
||||
若网站提示“出现错误”的标识或账户添加失败时,请重新按照以上步骤重新添加。\n\
|
||||
<b>若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用网络代理并重试。</b>\n\
|
||||
若网站提示“出现错误”或账户添加失败时,请按照以上步骤重新添加。\n\
|
||||
<b>若设备网络环境不佳,可能会导致网页加载缓慢甚至无法加载,请使用网络代理并重试。</b>\n\
|
||||
如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.profile=账户设置页
|
||||
account.methods.microsoft.profile=编辑账户个人信息
|
||||
account.methods.microsoft.purchase=购买 Minecraft
|
||||
account.methods.forgot_password=忘记密码
|
||||
account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL,请下载官方版本来登录微软账户。
|
||||
@@ -336,7 +337,7 @@ download.existing=文件已存在,无法保存。你可以将文件保存至
|
||||
download.external_link=打开下载网站
|
||||
download.failed=下载失败: %1$s,\n错误码:%2$d\n你可以点击右上角帮助按钮进行求助。
|
||||
download.failed.empty=[没有可供安装的版本,点击此处返回]\n(你可以点击右上角帮助按钮进行求助)
|
||||
download.failed.no_code=下载失败: %s
|
||||
download.failed.no_code=下载失败
|
||||
download.failed.refresh=[加载版本列表失败,点击此处重试]\n(你可以点击右上角帮助按钮进行求助)
|
||||
download.game=新游戏
|
||||
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com)
|
||||
@@ -385,7 +386,7 @@ fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已为 Window
|
||||
feedback=反馈
|
||||
feedback.channel=反馈渠道
|
||||
feedback.discord=Discord
|
||||
feedback.discord.statement=欢迎加入 Discord 讨论区,加入后请遵守讨论区规定
|
||||
feedback.discord.statement=欢迎加入 Discord 服务器,加入后请遵守讨论区规定
|
||||
feedback.github=GitHub Issue
|
||||
feedback.github.statement=提交一个 GitHub Issue
|
||||
feedback.qq_group=HMCL 用户群
|
||||
@@ -433,7 +434,7 @@ game.crash.reason.jdk_9=当前游戏由于 Java 版本过高,无法继续运
|
||||
game.crash.reason.jvm_32bit=当前游戏由于内存分配过大,超过了 32 位 Java 内存限制,无法继续运行。\n如果你的电脑是 64 位系统,请下载安装并更换 64 位 Java。<a href="https://bell-sw.com/pages/downloads/#downloads">下载 Java</a>\n如果你的电脑是 32 位系统,你或许可以重新安装 64 位系统,或换一台新电脑。\n或者,你可以在“(全局/版本特定) 游戏设置 → 游戏内存”中关闭“自动分配内存”,并且把内存限制调节为 1024 MB 或以下。
|
||||
game.crash.reason.loading_crashed_forge=当前游戏由于模组“%1$s (%2$s)”错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
|
||||
game.crash.reason.loading_crashed_fabric=当前游戏由于模组“%1$s”错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
|
||||
game.crash.reason.memory_exceeded=当前游戏由于分配的内存过大,无法继续运行。\n该问题是由于系统页面文件太小导致的。\n你需要在全局(特定)游戏设置中关闭游戏内存的自动分配,并将游戏内存调低至游戏能正常启动为止。\n你还可以尝试将 虚拟内存 设置调整至「自动管理所有驱动器分页文件大小」,<a href="https://docs.hmcl.net/assets/img/hmcl/自动管理所有驱动器分页文件大小.webp">详情</a>。
|
||||
game.crash.reason.memory_exceeded=当前游戏由于分配的内存过大,无法继续运行。\n该问题是由于系统页面文件太小导致的。\n你需要在全局(特定)游戏设置中关闭游戏内存的自动分配,并将游戏内存调低至游戏能正常启动为止。\n你还可以尝试将虚拟内存设置调整为“自动管理所有驱动器分页文件大小”,<a href="https://docs.hmcl.net/assets/img/hmcl/自动管理所有驱动器分页文件大小.webp">详情</a>。
|
||||
game.crash.reason.mac_jdk_8u261=当前游戏由于你所使用的 Forge/OptiFine 与 Java 冲突而崩溃。\n请尝试更新 Forge/OptiFine,或使用 Java 8u251 及更早版本启动。
|
||||
game.crash.reason.mod=当前游戏由于“%1$s”的问题,无法继续运行。\n你可以更新或删除已经安装的“%1$s”再试。
|
||||
game.crash.reason.mod_resolution=当前游戏由于模组依赖问题,无法继续运行。Fabric 提供了如下信息:\n%1$s
|
||||
@@ -557,10 +558,10 @@ java.reveal=浏览 Java 文件夹
|
||||
java.uninstall=卸载此 Java
|
||||
java.uninstall.confirm=你确定要卸载此 Java 吗?此操作无法撤销!
|
||||
|
||||
lang=简体中文 (中国大陆)
|
||||
lang=简体中文
|
||||
lang.default=跟随系统语言
|
||||
|
||||
launch.advice==%s 是否继续启动?
|
||||
launch.advice=%s 是否继续启动?
|
||||
launch.advice.multi=检测到以下问题:\n\n%s\n\n这些问题可能导致游戏无法正常启动或影响游戏体验,是否继续启动?\n你可以点击右上角帮助按钮进行求助。
|
||||
launch.advice.java.auto=当前选择的 Java 虚拟机版本不满足游戏要求。\n点击“是”即可由 HMCL 来自动选取合适的 Java 虚拟机版本。\n或者你可以在“(全局/版本特定) 游戏设置 → 游戏 Java”中选择一个合适的 Java 虚拟机版本。
|
||||
launch.advice.java.modded_java_7=Minecraft 1.7.2 及更低版本需要 Java 7 及更低版本。
|
||||
@@ -618,7 +619,7 @@ launcher.background.translucent=半透明
|
||||
launcher.cache_directory=文件下载缓存文件夹
|
||||
launcher.cache_directory.clean=清理缓存
|
||||
launcher.cache_directory.choose=选择文件下载缓存文件夹
|
||||
launcher.cache_directory.default=默认 ("%AppData%/.minecraft" 或 "~/.minecraft")
|
||||
launcher.cache_directory.default=默认 ("%APPDATA%/.minecraft" 或 "~/.minecraft")
|
||||
launcher.cache_directory.disabled=禁用 (总是使用游戏文件夹路径)
|
||||
launcher.cache_directory.invalid=无法创建自定义的缓存文件夹,恢复默认设置
|
||||
launcher.contact=联系我们
|
||||
@@ -800,7 +801,8 @@ mods.channel.release=稳定版本
|
||||
mods.check_updates=检查模组更新
|
||||
mods.check_updates.current_version=当前版本
|
||||
mods.check_updates.empty=没有需要更新的模组
|
||||
mods.check_updates.failed=部分文件下载失败
|
||||
mods.check_updates.failed_check=检查更新失败
|
||||
mods.check_updates.failed_download=部分文件下载失败
|
||||
mods.check_updates.file=文件
|
||||
mods.check_updates.source=来源
|
||||
mods.check_updates.target_version=目标版本
|
||||
@@ -866,7 +868,7 @@ world.import.failed=无法导入此世界:%s
|
||||
world.import.invalid=无法识别该存档压缩包
|
||||
world.info.title=世界 [%s] - 世界信息
|
||||
world.info.basic=基本信息
|
||||
world.info.allow_cheats=允许作弊
|
||||
world.info.allow_cheats=允许命令(作弊)
|
||||
world.info.dimension.the_nether=下界
|
||||
world.info.dimension.the_end=末地
|
||||
world.info.difficulty=难度
|
||||
@@ -943,17 +945,17 @@ settings.advanced.modify=编辑高级设置
|
||||
settings.advanced.title=高级设置 - %s
|
||||
settings.advanced.custom_commands=自定义命令
|
||||
settings.advanced.custom_commands.hint=自定义命令被调用时将包含如下的环境变量:\n\
|
||||
\ · $INST_NAME:版本名称;\n\
|
||||
\ · $INST_ID:版本名称;\n\
|
||||
\ · $INST_DIR:当前版本运行路径;\n\
|
||||
\ · $INST_MC_DIR:当前游戏文件夹路径;\n\
|
||||
\ · $INST_JAVA:游戏运行使用的 Java 路径;\n\
|
||||
\ · $INST_FORGE:若安装了 Forge,将会存在本环境变量;\n\
|
||||
\ · $INST_NEOFORGE:若安装了 NeoForge,将会存在本环境变量;\n\
|
||||
\ · $INST_LITELOADER:若安装了 LiteLoader,将会存在本环境变量;\n\
|
||||
\ · $INST_OPTIFINE:若安装了 OptiFine,将会存在本环境变量;\n\
|
||||
\ · $INST_FABRIC:若安装了 Fabric,将会存在本环境变量;\n\
|
||||
\ · $INST_QUILT:若安装了 Quilt,将会存在本环境变量。
|
||||
\ · $INST_NAME: 版本名称;\n\
|
||||
\ · $INST_ID: 版本名称;\n\
|
||||
\ · $INST_DIR: 当前版本运行路径;\n\
|
||||
\ · $INST_MC_DIR: 当前游戏文件夹路径;\n\
|
||||
\ · $INST_JAVA: 游戏运行使用的 Java 路径;\n\
|
||||
\ · $INST_FORGE: 若安装了 Forge,将会存在本环境变量;\n\
|
||||
\ · $INST_NEOFORGE: 若安装了 NeoForge,将会存在本环境变量;\n\
|
||||
\ · $INST_LITELOADER: 若安装了 LiteLoader,将会存在本环境变量;\n\
|
||||
\ · $INST_OPTIFINE: 若安装了 OptiFine,将会存在本环境变量;\n\
|
||||
\ · $INST_FABRIC: 若安装了 Fabric,将会存在本环境变量;\n\
|
||||
\ · $INST_QUILT: 若安装了 Quilt,将会存在本环境变量。
|
||||
settings.advanced.dont_check_game_completeness=不检查游戏完整性
|
||||
settings.advanced.dont_check_jvm_validity=不检查 JVM 与游戏的兼容性
|
||||
settings.advanced.dont_patch_natives=不尝试自动替换本地库
|
||||
@@ -997,8 +999,8 @@ settings.advanced.renderer.llvmpipe=软渲染器 (性能较差,兼容性最好
|
||||
settings.advanced.renderer.zink=Vulkan (性能最好,兼容性较差)
|
||||
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=包装命令
|
||||
@@ -1083,8 +1085,8 @@ settings.type.special.edit=编辑版本特定游戏设置
|
||||
settings.type.special.edit.hint=当前游戏版本“%s”启用了“版本特定游戏设置”,因此本页面选项不对该版本生效。点击链接前往该版本的“游戏设置”页。
|
||||
|
||||
sponsor=赞助
|
||||
sponsor.bmclapi=国内下载源由 BMCLAPI 提供高速下载服务。BMCLAPI 为公益服务,赞助 BMCLAPI 可以帮助作者更好的提供稳定高速的下载服务。[点击此处查阅详细信息]
|
||||
sponsor.hmcl=Hello Minecraft! Launcher 是一个免费、自由、开放源代码的 Minecraft 启动器。[点击此处查阅更多详细信息]
|
||||
sponsor.bmclapi=国内下载源由 BMCLAPI 提供高速下载服务。BMCLAPI 为公益服务,赞助 BMCLAPI 可以帮助作者更好地提供稳定高速的下载服务。[点击此处查阅详细信息]
|
||||
sponsor.hmcl=Hello Minecraft! Launcher 是一个免费、自由、开放源代码的 Minecraft 启动器。[点击此处查阅详细信息]
|
||||
|
||||
system.architecture=架构
|
||||
system.operating_system=操作系统
|
||||
@@ -1095,15 +1097,13 @@ update=启动器更新
|
||||
update.accept=更新
|
||||
update.changelog=更新日志
|
||||
update.channel.dev=开发版
|
||||
update.channel.dev.hint=你正在使用 HMCL 开发版。开发版包含一些未在稳定版中包含的测试性功能,仅用于体验新功能。\n\
|
||||
开发版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
|
||||
如果你使用时遇到了问题,可以通过设置中<a href="hmcl://settings/feedback">反馈页面</a>提供的渠道进行反馈。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以关注 HMCL 的开发进展。\n\
|
||||
update.channel.dev.hint=你正在使用 HMCL 开发版。开发版包含一些未在稳定版中包含的测试性功能,仅用于体验新功能。开发版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
|
||||
\n\
|
||||
<a href="hmcl://hide-announcement">为当前版本隐藏该提示</a>。
|
||||
如果你使用时遇到了问题,可以通过设置中<a href="hmcl://settings/feedback">反馈页面</a>提供的渠道进行反馈。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以关注 HMCL 的开发进展。
|
||||
update.channel.dev.title=开发版提示
|
||||
update.channel.nightly=预览版
|
||||
update.channel.nightly.hint=你正在使用 HMCL 预览版。预览版更新较为频繁,包含一些未在稳定版和开发版中包含的测试性功能,仅用于体验新功能。\n\
|
||||
预览版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
|
||||
update.channel.nightly.hint=你正在使用 HMCL 预览版。预览版更新较为频繁,包含一些未在稳定版和开发版中包含的测试性功能,仅用于体验新功能。预览版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
|
||||
\n\
|
||||
如果你使用时遇到了问题,可以通过设置中<a href="hmcl://settings/feedback">反馈页面</a>提供的渠道进行反馈。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以关注 HMCL 的开发进展。
|
||||
update.channel.nightly.title=预览版提示
|
||||
update.channel.stable=稳定版
|
||||
@@ -1159,6 +1159,10 @@ version.manage.rename.fail=重命名版本失败,可能文件被占用或者
|
||||
version.settings=游戏设置
|
||||
version.update=更新整合包
|
||||
|
||||
wiki.tooltip=Minecraft Wiki 页面
|
||||
wiki.version.game.release=https://zh.minecraft.wiki/w/Java版%s?variant=zh-cn
|
||||
wiki.version.game.snapshot=https://zh.minecraft.wiki/w/%s?variant=zh-cn
|
||||
|
||||
wizard.prev=< 上一步
|
||||
wizard.failed=失败
|
||||
wizard.finish=完成
|
||||
|
||||
@@ -82,25 +82,25 @@
|
||||
"module": "javafx.base",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-base",
|
||||
"version": "17.0.13",
|
||||
"version": "19.0.2.1",
|
||||
"classifier": "mac",
|
||||
"sha1": "e1744403a6691aaa6b65a7e9b4ff9aad98831bb1"
|
||||
"sha1": "73e422d8426aaa23e8c712b9f4d9bebf70d3bfb9"
|
||||
},
|
||||
{
|
||||
"module": "javafx.graphics",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-graphics",
|
||||
"version": "17.0.13",
|
||||
"version": "19.0.2.1",
|
||||
"classifier": "mac",
|
||||
"sha1": "dbe733470094abee56560067d0fea3ad24991d2b"
|
||||
"sha1": "6ab6f3e23421fcfa04e692d9d26a8f55a830c114"
|
||||
},
|
||||
{
|
||||
"module": "javafx.controls",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-controls",
|
||||
"version": "17.0.13",
|
||||
"version": "19.0.2.1",
|
||||
"classifier": "mac",
|
||||
"sha1": "0d43a776d6c9e2622e3581ed04501e2cadcd6e65"
|
||||
"sha1": "b7786b1b63e741c0e234829825fae5fef9d96c31"
|
||||
}
|
||||
],
|
||||
"osx-arm64": [
|
||||
@@ -108,25 +108,25 @@
|
||||
"module": "javafx.base",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-base",
|
||||
"version": "17.0.13",
|
||||
"version": "19.0.2.1",
|
||||
"classifier": "mac-aarch64",
|
||||
"sha1": "d4dc7d2864c07b56a27e39fa5691856ddccf0ba4"
|
||||
"sha1": "1d0d887c492330ed527b0614d115a4f32d2d05a4"
|
||||
},
|
||||
{
|
||||
"module": "javafx.graphics",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-graphics",
|
||||
"version": "17.0.13",
|
||||
"version": "19.0.2.1",
|
||||
"classifier": "mac-aarch64",
|
||||
"sha1": "d6a49f19e90db56c134757d1bbab4935de075d45"
|
||||
"sha1": "64db28799e61e0f8f51e471c732599b2a6961803"
|
||||
},
|
||||
{
|
||||
"module": "javafx.controls",
|
||||
"groupId": "org.openjfx",
|
||||
"artifactId": "javafx-controls",
|
||||
"version": "17.0.13",
|
||||
"version": "19.0.2.1",
|
||||
"classifier": "mac-aarch64",
|
||||
"sha1": "ad564497edc49f7a6135c3b52d360962e19a2539"
|
||||
"sha1": "3a14bd5f3ebe45d344c1f2bade0fe074e6ea2a83"
|
||||
}
|
||||
],
|
||||
"linux-x86_64": [
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package moe.mickey.minecraft.skin.fx;
|
||||
package org.jackhuang.hmcl.ui.skin;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
@@ -8,7 +8,7 @@ import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.input.ScrollEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.stage.Stage;
|
||||
import moe.mickey.minecraft.skin.fx.test.Test;
|
||||
import org.jackhuang.hmcl.ui.skin.test.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
+6
-6
@@ -1,13 +1,13 @@
|
||||
package moe.mickey.minecraft.skin.fx.test;
|
||||
package org.jackhuang.hmcl.ui.skin.test;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import moe.mickey.minecraft.skin.fx.FunctionHelper;
|
||||
import moe.mickey.minecraft.skin.fx.SkinCanvas;
|
||||
import moe.mickey.minecraft.skin.fx.SkinCanvasSupport;
|
||||
import moe.mickey.minecraft.skin.fx.animation.SkinAniRunning;
|
||||
import moe.mickey.minecraft.skin.fx.animation.SkinAniWavingArms;
|
||||
import org.jackhuang.hmcl.ui.skin.FunctionHelper;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinCanvas;
|
||||
import org.jackhuang.hmcl.ui.skin.SkinCanvasSupport;
|
||||
import org.jackhuang.hmcl.ui.skin.animation.SkinAniRunning;
|
||||
import org.jackhuang.hmcl.ui.skin.animation.SkinAniWavingArms;
|
||||
import org.jackhuang.hmcl.game.TexturesLoader;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
@@ -6,7 +6,7 @@ dependencies {
|
||||
api("org.glavo:simple-png-javafx:0.3.0")
|
||||
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.tukaani:xz:1.10")
|
||||
api("org.hildan.fxgson:fx-gson:5.0.0")
|
||||
api("org.jenkins-ci:constant-pool-scanner:1.2")
|
||||
api("com.github.steveice10:opennbt:1.5")
|
||||
|
||||
@@ -170,6 +170,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
Version resolvedVersion = version.resolve(provider);
|
||||
String mainClass = resolvedVersion.getMainClass();
|
||||
return mainClass != null && (LAUNCH_WRAPPER_MAIN.equals(mainClass)
|
||||
|| mainClass.startsWith("net.minecraftforge")
|
||||
|| mainClass.startsWith("net.fabricmc")
|
||||
|| mainClass.startsWith("org.quiltmc")
|
||||
|| mainClass.startsWith("cpw.mods"));
|
||||
|
||||
@@ -33,7 +33,7 @@ public final class GameJavaVersion {
|
||||
public static final GameJavaVersion LATEST = JAVA_21;
|
||||
|
||||
public static GameJavaVersion getMinimumJavaVersion(GameVersionNumber gameVersion) {
|
||||
if (gameVersion.compareTo("1.21") >= 0)
|
||||
if (gameVersion.compareTo("1.20.5") >= 0)
|
||||
return JAVA_21;
|
||||
if (gameVersion.compareTo("1.18") >= 0)
|
||||
return JAVA_17;
|
||||
@@ -59,21 +59,6 @@ public final class GameJavaVersion {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSupportedPlatform(Platform platform) {
|
||||
OperatingSystem os = platform.getOperatingSystem();
|
||||
Architecture arch = platform.getArchitecture();
|
||||
switch (arch) {
|
||||
case X86:
|
||||
return os == OperatingSystem.WINDOWS || os == OperatingSystem.LINUX;
|
||||
case X86_64:
|
||||
return os == OperatingSystem.WINDOWS || os == OperatingSystem.LINUX || os == OperatingSystem.OSX;
|
||||
case ARM64:
|
||||
return os == OperatingSystem.WINDOWS || os == OperatingSystem.OSX;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<GameJavaVersion> getSupportedVersions(Platform platform) {
|
||||
OperatingSystem operatingSystem = platform.getOperatingSystem();
|
||||
Architecture architecture = platform.getArchitecture();
|
||||
|
||||
@@ -62,7 +62,14 @@ public enum JavaVersionConstraint {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_17(false, GameVersionNumber.atLeast("1.18"), VersionNumber.between("17", "17.999")) {
|
||||
MODDED_JAVA_17(false, GameVersionNumber.between("1.18", "1.20.4"), VersionNumber.between("17", "17.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_21(false, GameVersionNumber.atLeast("1.20.5"), VersionNumber.between("21", "21.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
|
||||
@@ -160,12 +160,17 @@ public final class ForgeNewModMetadata {
|
||||
|
||||
private static ModLoaderType analyzeLoader(Toml toml, String modID, int loaderACC, ModLoaderType defaultLoader) throws IOException {
|
||||
List<HashMap<String, Object>> dependencies = toml.getList("dependencies." + modID);
|
||||
if (dependencies != null) {
|
||||
for (HashMap<String, Object> dependency : dependencies) {
|
||||
switch ((String) dependency.get("modId")) {
|
||||
case "forge": return checkLoaderACC(loaderACC, ACC_FORGE, ModLoaderType.FORGE);
|
||||
case "neoforge": return checkLoaderACC(loaderACC, ACC_NEO_FORGED, ModLoaderType.NEO_FORGED);
|
||||
}
|
||||
if (dependencies == null) {
|
||||
dependencies = toml.getList("dependencies"); // ??? I have no idea why some of the Forge mods use [[dependencies]]
|
||||
if (dependencies == null) {
|
||||
return defaultLoader;
|
||||
}
|
||||
}
|
||||
|
||||
for (HashMap<String, Object> dependency : dependencies) {
|
||||
switch ((String) dependency.get("modId")) {
|
||||
case "forge": return checkLoaderACC(loaderACC, ACC_FORGE, ModLoaderType.FORGE);
|
||||
case "neoforge": return checkLoaderACC(loaderACC, ACC_NEO_FORGED, ModLoaderType.NEO_FORGED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.mod.modrinth;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.mod.ModManager;
|
||||
import org.jackhuang.hmcl.mod.ModpackCompletionException;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
@@ -27,6 +28,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -41,6 +43,7 @@ public class ModrinthCompletionTask extends Task<Void> {
|
||||
|
||||
private final DefaultDependencyManager dependency;
|
||||
private final DefaultGameRepository repository;
|
||||
private final ModManager modManager;
|
||||
private final String version;
|
||||
private ModrinthManifest manifest;
|
||||
private final List<Task<?>> dependencies = new ArrayList<>();
|
||||
@@ -69,6 +72,7 @@ public class ModrinthCompletionTask extends Task<Void> {
|
||||
public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, String version, ModrinthManifest manifest) {
|
||||
this.dependency = dependencyManager;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
this.modManager = repository.getModManager(version);
|
||||
this.version = version;
|
||||
this.manifest = manifest;
|
||||
|
||||
@@ -99,18 +103,28 @@ public class ModrinthCompletionTask extends Task<Void> {
|
||||
if (manifest == null)
|
||||
return;
|
||||
|
||||
Path runDirectory = repository.getRunDirectory(version).toPath();
|
||||
Path runDirectory = repository.getRunDirectory(version).toPath().toAbsolutePath().normalize();
|
||||
Path modsDirectory = runDirectory.resolve("mods");
|
||||
|
||||
for (ModrinthManifest.File file : manifest.getFiles()) {
|
||||
if (file.getEnv() != null && file.getEnv().getOrDefault("client", "required").equals("unsupported"))
|
||||
continue;
|
||||
Path filePath = runDirectory.resolve(file.getPath());
|
||||
if (!Files.exists(filePath) && !file.getDownloads().isEmpty()) {
|
||||
FileDownloadTask task = new FileDownloadTask(file.getDownloads().get(0), filePath.toFile());
|
||||
task.setCacheRepository(dependency.getCacheRepository());
|
||||
task.setCaching(true);
|
||||
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
||||
}
|
||||
if (file.getDownloads().isEmpty())
|
||||
continue;
|
||||
|
||||
Path filePath = runDirectory.resolve(file.getPath()).toAbsolutePath().normalize();
|
||||
if (!filePath.startsWith(runDirectory))
|
||||
throw new IOException("Unsecure path: " + file.getPath());
|
||||
|
||||
if (Files.exists(filePath))
|
||||
continue;
|
||||
if (modsDirectory.equals(filePath.getParent()) && this.modManager.hasSimpleMod(FileUtils.getName(filePath)))
|
||||
continue;
|
||||
|
||||
FileDownloadTask task = new FileDownloadTask(file.getDownloads(), filePath.toFile());
|
||||
task.setCacheRepository(dependency.getCacheRepository());
|
||||
task.setCaching(true);
|
||||
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
||||
}
|
||||
|
||||
if (!dependencies.isEmpty()) {
|
||||
|
||||
+41
-23
@@ -62,36 +62,54 @@ public final class MultiMCInstanceConfiguration implements ModpackManifest {
|
||||
private final MultiMCManifest mmcPack;
|
||||
|
||||
MultiMCInstanceConfiguration(String defaultName, InputStream contentStream, MultiMCManifest mmcPack) throws IOException {
|
||||
// instance.cfg is in .ini format. As it's like the .properties format, we use Properties to parse thie file.
|
||||
// Maybe this will provide some problems (and it does, such as https://github.com/HMCL-dev/HMCL/issues/2991), but this workaround works, doesn't it?
|
||||
Properties p = new Properties();
|
||||
p.load(new InputStreamReader(contentStream, StandardCharsets.UTF_8));
|
||||
|
||||
this.mmcPack = mmcPack;
|
||||
|
||||
instanceType = p.getProperty("InstanceType");
|
||||
autoCloseConsole = Boolean.parseBoolean(p.getProperty("AutoCloseConsole"));
|
||||
instanceType = readValue(p, "InstanceType");
|
||||
autoCloseConsole = Boolean.parseBoolean(readValue(p, "AutoCloseConsole"));
|
||||
gameVersion = mmcPack != null ? mmcPack.getComponents().stream().filter(e -> "net.minecraft".equals(e.getUid())).findAny()
|
||||
.orElseThrow(() -> new IOException("Malformed mmc-pack.json")).getVersion() : p.getProperty("IntendedVersion");
|
||||
javaPath = p.getProperty("JavaPath");
|
||||
jvmArgs = p.getProperty("JvmArgs");
|
||||
fullscreen = Boolean.parseBoolean(p.getProperty("LaunchMaximized"));
|
||||
maxMemory = Lang.toIntOrNull(p.getProperty("MaxMemAlloc"));
|
||||
minMemory = Lang.toIntOrNull(p.getProperty("MinMemAlloc"));
|
||||
height = Lang.toIntOrNull(p.getProperty("MinecraftWinHeight"));
|
||||
width = Lang.toIntOrNull(p.getProperty("MinecraftWinWidth"));
|
||||
overrideCommands = Boolean.parseBoolean(p.getProperty("OverrideCommands"));
|
||||
overrideConsole = Boolean.parseBoolean(p.getProperty("OverrideConsole"));
|
||||
overrideJavaArgs = Boolean.parseBoolean(p.getProperty("OverrideJavaArgs"));
|
||||
overrideJavaLocation = Boolean.parseBoolean(p.getProperty("OverrideJavaLocation"));
|
||||
overrideMemory = Boolean.parseBoolean(p.getProperty("OverrideMemory"));
|
||||
overrideWindow = Boolean.parseBoolean(p.getProperty("OverrideWindow"));
|
||||
permGen = Lang.toIntOrNull(p.getProperty("PermGen"));
|
||||
postExitCommand = p.getProperty("PostExitCommand");
|
||||
preLaunchCommand = p.getProperty("PreLaunchCommand");
|
||||
showConsole = Boolean.parseBoolean(p.getProperty("ShowConsole"));
|
||||
showConsoleOnError = Boolean.parseBoolean(p.getProperty("ShowConsoleOnError"));
|
||||
wrapperCommand = p.getProperty("WrapperCommand");
|
||||
.orElseThrow(() -> new IOException("Malformed mmc-pack.json")).getVersion() : readValue(p, "IntendedVersion");
|
||||
javaPath = readValue(p, "JavaPath");
|
||||
jvmArgs = readValue(p, "JvmArgs");
|
||||
fullscreen = Boolean.parseBoolean(readValue(p, "LaunchMaximized"));
|
||||
maxMemory = Lang.toIntOrNull(readValue(p, "MaxMemAlloc"));
|
||||
minMemory = Lang.toIntOrNull(readValue(p, "MinMemAlloc"));
|
||||
height = Lang.toIntOrNull(readValue(p, "MinecraftWinHeight"));
|
||||
width = Lang.toIntOrNull(readValue(p, "MinecraftWinWidth"));
|
||||
overrideCommands = Boolean.parseBoolean(readValue(p, "OverrideCommands"));
|
||||
overrideConsole = Boolean.parseBoolean(readValue(p, "OverrideConsole"));
|
||||
overrideJavaArgs = Boolean.parseBoolean(readValue(p, "OverrideJavaArgs"));
|
||||
overrideJavaLocation = Boolean.parseBoolean(readValue(p, "OverrideJavaLocation"));
|
||||
overrideMemory = Boolean.parseBoolean(readValue(p, "OverrideMemory"));
|
||||
overrideWindow = Boolean.parseBoolean(readValue(p, "OverrideWindow"));
|
||||
permGen = Lang.toIntOrNull(readValue(p, "PermGen"));
|
||||
postExitCommand = readValue(p, "PostExitCommand");
|
||||
preLaunchCommand = readValue(p, "PreLaunchCommand");
|
||||
showConsole = Boolean.parseBoolean(readValue(p, "ShowConsole"));
|
||||
showConsoleOnError = Boolean.parseBoolean(readValue(p, "ShowConsoleOnError"));
|
||||
wrapperCommand = readValue(p, "WrapperCommand");
|
||||
name = defaultName;
|
||||
notes = Optional.ofNullable(p.getProperty("notes")).orElse("");
|
||||
notes = Optional.ofNullable(readValue(p, "notes")).orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
* A workaround for <a href="https://github.com/HMCL-dev/HMCL/issues/2991">...</a>
|
||||
*/
|
||||
private String readValue(Properties p, String key) {
|
||||
String value = p.getProperty(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int l = value.length();
|
||||
if (l >= 2 && value.charAt(0) == '"' && value.charAt(l - 1) == ':') {
|
||||
return value.substring(0, l - 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public MultiMCInstanceConfiguration(String instanceType, String name, String gameVersion, Integer permGen, String wrapperCommand, String preLaunchCommand, String postExitCommand, String notes, String javaPath, String jvmArgs, boolean fullscreen, Integer width, Integer height, Integer maxMemory, Integer minMemory, boolean showConsole, boolean showConsoleOnError, boolean autoCloseConsole, boolean overrideMemory, boolean overrideJavaLocation, boolean overrideJavaArgs, boolean overrideConsole, boolean overrideCommands, boolean overrideWindow) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.FileVisitor;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
final class DirectoryStructurePrinter {
|
||||
private DirectoryStructurePrinter() {
|
||||
}
|
||||
|
||||
public static String list(Path path, int maxDepth) throws IOException {
|
||||
StringBuilder output = new StringBuilder(128);
|
||||
list(path, maxDepth, output);
|
||||
output.setLength(output.length() - 1);
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
private static void list(Path path, int maxDepth, StringBuilder output) throws IOException {
|
||||
output.append("Filesystem structure of: ").append(path).append('\n');
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
pushMessage(output, "nonexistent path", 1);
|
||||
return;
|
||||
}
|
||||
if (Files.isRegularFile(path)) {
|
||||
pushMessage(output, "regular file path", 1);
|
||||
return;
|
||||
}
|
||||
if (Files.isDirectory(path)) {
|
||||
Files.walkFileTree(path, new FileVisitor<Path>() {
|
||||
private boolean isFolderEmpty;
|
||||
|
||||
private int depth = 1;
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
|
||||
isFolderEmpty = true;
|
||||
|
||||
pushFile(output, dir, depth);
|
||||
if (depth == maxDepth) {
|
||||
pushMessage(output, "too deep", depth);
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
|
||||
depth++;
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
isFolderEmpty = false;
|
||||
|
||||
pushFile(output, file, depth);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed(Path file, IOException exc) {
|
||||
visitFile(file, null);
|
||||
|
||||
pushMessage(output, exc.toString(), depth);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
|
||||
if (isFolderEmpty) {
|
||||
pushMessage(output, "empty directory", depth);
|
||||
}
|
||||
|
||||
depth--;
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
pushMessage(output, "unknown file type", 1);
|
||||
}
|
||||
|
||||
private static void pushFile(StringBuilder output, Path file, int depth) {
|
||||
output.append("|");
|
||||
for (int i = 1; i < depth; i++) {
|
||||
output.append(" |");
|
||||
}
|
||||
output.append("-> ").append(FileUtils.getName(file)).append('\n');
|
||||
}
|
||||
|
||||
private static void pushMessage(StringBuilder output, String message, int depth) {
|
||||
output.append("| ");
|
||||
for (int i = 1; i < depth; i++) {
|
||||
output.append(" | ");
|
||||
}
|
||||
output.append('<').append(message).append(">\n");
|
||||
}
|
||||
}
|
||||
@@ -30,11 +30,7 @@ import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -199,7 +195,7 @@ public final class FileUtils {
|
||||
* It will create the file if it does not exist, or truncate the existing file to empty for rewriting.
|
||||
* All bytes in byte array will be written into the file in binary format. Existing data will be erased.
|
||||
*
|
||||
* @param file the path to the file
|
||||
* @param file the path to the file
|
||||
* @param data the data being written to file
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@@ -212,7 +208,7 @@ public final class FileUtils {
|
||||
* It will create the file if it does not exist, or truncate the existing file to empty for rewriting.
|
||||
* All bytes in byte array will be written into the file in binary format. Existing data will be erased.
|
||||
*
|
||||
* @param file the path to the file
|
||||
* @param file the path to the file
|
||||
* @param data the data being written to file
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@@ -591,4 +587,8 @@ public final class FileUtils {
|
||||
|
||||
Files.move(tmpFile, file, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
public static String printFileStructure(Path path, int maxDepth) throws IOException {
|
||||
return DirectoryStructurePrinter.list(path, maxDepth);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(HMCLauncher)
|
||||
if(MSVC)
|
||||
add_compile_options(/utf-8 /D_UNICODE /W4)
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
add_compile_options(/utf-8 /W4)
|
||||
add_link_options(/ENTRY:wWinMainCRTStartup)
|
||||
else()
|
||||
add_compile_options(-municode -Wall -Wextra -Wpedantic)
|
||||
|
||||
@@ -82,8 +82,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,4,0,0
|
||||
PRODUCTVERSION 3,4,0,0
|
||||
FILEVERSION 3,5,0,0
|
||||
PRODUCTVERSION 3,5,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
param(
|
||||
[string]$JavaDir,
|
||||
[string]$Arch
|
||||
)
|
||||
|
||||
$chinese = [System.Globalization.CultureInfo]::CurrentCulture.Name -eq 'zh-CN'
|
||||
|
||||
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
|
||||
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||
|
||||
# Choose Source Dialog
|
||||
|
||||
$dialog = New-Object System.Windows.Forms.Form
|
||||
$dialog.AutoSize = $true
|
||||
$dialog.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
|
||||
if ($chinese) {
|
||||
$dialog.Text = '未能在这台电脑上找到 Java'
|
||||
} else {
|
||||
$dialog.Text = 'Java not found'
|
||||
}
|
||||
|
||||
$dialogLayout = New-Object System.Windows.Forms.FlowLayoutPanel
|
||||
$dialogLayout.AutoSize = $true
|
||||
$dialogLayout.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
|
||||
$dialogLayout.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
|
||||
|
||||
$messageLabel = New-Object System.Windows.Forms.Label
|
||||
$messageLabel.AutoSize = $true
|
||||
$messageLabel.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom
|
||||
if ($chinese) {
|
||||
$messageLabel.Text = "HMCL 需要 Java 运行时环境才能正常运行,是否自动下载安装 Java?"
|
||||
} else {
|
||||
$messageLabel.Text = "Running HMCL requires a Java runtime environment. `nDo you want to download and install Java automatically?"
|
||||
}
|
||||
|
||||
$useMirrorCheckBox = New-Object System.Windows.Forms.CheckBox
|
||||
$useMirrorCheckBox.AutoSize = $true
|
||||
$useMirrorCheckBox.Anchor = [System.Windows.Forms.AnchorStyles]::Right
|
||||
$useMirrorCheckBox.Checked = $false
|
||||
if ($chinese) {
|
||||
$useMirrorCheckBox.Text = '使用备用下载源(如果无法正常下载,请打开它再试一次)'
|
||||
} else {
|
||||
$useMirrorCheckBox.Text = 'Use the alternate download source'
|
||||
}
|
||||
|
||||
$selectButtonPanel = New-Object System.Windows.Forms.FlowLayoutPanel
|
||||
$selectButtonPanel.AutoSize = $true
|
||||
$selectButtonPanel.Anchor = [System.Windows.Forms.AnchorStyles]::Right
|
||||
$selectButtonPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::LeftToRight
|
||||
$selectButtonPanel.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
|
||||
|
||||
$yesButton = New-Object System.Windows.Forms.Button
|
||||
$noButton = New-Object System.Windows.Forms.Button
|
||||
$yesButton.DialogResult = [System.Windows.Forms.DialogResult]::Yes
|
||||
$noButton.DialogResult = [System.Windows.Forms.DialogResult]::No
|
||||
|
||||
if ($chinese) {
|
||||
$yesButton.Text = '是'
|
||||
$noButton.Text = '否'
|
||||
} else {
|
||||
$yesButton.Text = 'Yes'
|
||||
$noButton.Text = 'No'
|
||||
}
|
||||
$selectButtonPanel.Controls.Add($yesButton)
|
||||
$selectButtonPanel.Controls.Add($noButton)
|
||||
|
||||
$dialogLayout.Controls.Add($messageLabel)
|
||||
$dialogLayout.Controls.Add($useMirrorCheckBox)
|
||||
$dialogLayout.Controls.Add($selectButtonPanel)
|
||||
|
||||
$dialog.Controls.Add($dialogLayout)
|
||||
|
||||
$result = $dialog.ShowDialog()
|
||||
|
||||
if ($result -ne [System.Windows.Forms.DialogResult]::Yes) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($useMirrorCheckBox.Checked) {
|
||||
switch ($Arch) {
|
||||
'x86-64' {
|
||||
$script:url = 'https://download.bell-sw.com/java/17.0.2+9/bellsoft-jre17.0.2+9-windows-amd64-full.zip'
|
||||
}
|
||||
'x86' {
|
||||
$script:url = 'https://download.bell-sw.com/java/17.0.2+9/bellsoft-jre17.0.2+9-windows-i586-full.zip'
|
||||
}
|
||||
default { exit 1 }
|
||||
}
|
||||
} else {
|
||||
switch ($Arch) {
|
||||
'x86-64' {
|
||||
$script:url = 'https://cdn.azul.com/zulu/bin/zulu17.32.13-ca-fx-jre17.0.2-win_x64.zip'
|
||||
}
|
||||
'x86' {
|
||||
$script:url = 'https://cdn.azul.com/zulu/bin/zulu17.32.13-ca-fx-jre17.0.2-win_i686.zip'
|
||||
}
|
||||
default { exit 1 }
|
||||
}
|
||||
}
|
||||
|
||||
$securityProtocol = [System.Net.ServicePointManager]::SecurityProtocol.value__
|
||||
if (($securityProtocol -ne 0) -and (($securityProtocol -band 0x00000C00) -eq 0)) { # Try using HTTP when the platform does not support TLS 1.2
|
||||
$script:url = $script:url -replace '^https:', 'http:'
|
||||
}
|
||||
|
||||
# Download Winodw
|
||||
|
||||
do {
|
||||
$tempFileName = "hmcl-java-$(Get-Random).zip"
|
||||
$script:tempFile = Join-Path ([System.IO.Path]::GetTempPath()) $tempFileName
|
||||
} while (Test-Path $script:tempFile)
|
||||
|
||||
$form = New-Object System.Windows.Forms.Form
|
||||
$form.AutoSize = $true
|
||||
$form.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
|
||||
if ($chinese) {
|
||||
$form.Text = '正在下载 Java 中'
|
||||
} else {
|
||||
$form.Text = 'Downloading Java'
|
||||
}
|
||||
|
||||
$tip = New-Object System.Windows.Forms.Label
|
||||
$tip.AutoSize = $true
|
||||
if ($chinese) {
|
||||
$tip.Text = '正在下载 Java。这需要一段时间,请耐心等待。'
|
||||
} else {
|
||||
$tip.Text = 'Downloading Java. Please wait patiently for the download to complete.'
|
||||
}
|
||||
|
||||
$layout = New-Object System.Windows.Forms.FlowLayoutPanel
|
||||
$layout.AutoSize = $true
|
||||
$layout.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
|
||||
$layout.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
|
||||
|
||||
$progressBar = New-Object System.Windows.Forms.ProgressBar
|
||||
$progressBar.Maximum = 100
|
||||
|
||||
$label = New-Object System.Windows.Forms.Label
|
||||
$label.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom
|
||||
|
||||
if ($chinese) {
|
||||
$label.Text = '准备下载'
|
||||
} else {
|
||||
$label.Text = 'In preparation'
|
||||
}
|
||||
|
||||
$box = New-Object System.Windows.Forms.FlowLayoutPanel
|
||||
$box.AutoSize = $true
|
||||
$box.FlowDirection = [System.Windows.Forms.FlowDirection]::LeftToRight
|
||||
$box.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
|
||||
$box.Controls.Add($progressBar)
|
||||
$box.Controls.Add($label)
|
||||
|
||||
$cancelButton = New-Object System.Windows.Forms.Button
|
||||
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
||||
$cancelButton.Anchor = [System.Windows.Forms.AnchorStyles]::Right
|
||||
if ($chinese) {
|
||||
$cancelButton.Text = '取消'
|
||||
} else {
|
||||
$cancelButton.Text = 'Cancel'
|
||||
}
|
||||
|
||||
$layout.Controls.Add($tip)
|
||||
$layout.Controls.Add($box)
|
||||
$box.Controls.Add($cancelButton)
|
||||
|
||||
$form.Controls.Add($layout)
|
||||
|
||||
[System.Net.DownloadProgressChangedEventHandler]$progressChangedEventHandler = {
|
||||
param($sender, [System.Net.DownloadProgressChangedEventArgs]$ChangedEventArgs)
|
||||
$bytesReceived = $ChangedEventArgs.BytesReceived
|
||||
$totalBytes = $ChangedEventArgs.TotalBytesToReceive
|
||||
|
||||
$percentage = ([double]$bytesReceived)/([double]$totalBytes) * 100
|
||||
|
||||
$progressBar.Value = [int][System.Math]::Truncate($percentage)
|
||||
$label.Text = [string]::Format("{0:0.00}%", $percentage)
|
||||
}
|
||||
|
||||
[System.ComponentModel.AsyncCompletedEventHandler]$downloadFileCompletedEventHandler = {
|
||||
param($sender, [System.ComponentModel.AsyncCompletedEventArgs]$CompletedEventArgs)
|
||||
if (!$form.IsDisposed) {
|
||||
$label.Refresh()
|
||||
if ($CompletedEventArgs.Cancelled) {
|
||||
$form.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
||||
} elseif ($CompletedEventArgs.Error -ne $null) {
|
||||
if ($chinese) {
|
||||
[System.Windows.Forms.MessageBox]::Show($CompletedEventArgs.Error.Message, '下载失败', [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Exclamation)
|
||||
} else {
|
||||
[System.Windows.Forms.MessageBox]::Show($CompletedEventArgs.Error.Message, 'Download failed', [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Exclamation)
|
||||
}
|
||||
$form.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
||||
} else {
|
||||
$form.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$client = New-Object System.Net.WebClient
|
||||
$client.Headers.Add('User-Agent', 'Wget/1.20.3 (linux-gnu)')
|
||||
$client.add_DownloadProgressChanged($progressChangedEventHandler)
|
||||
$client.add_DownloadFileCompleted($downloadFileCompletedEventHandler)
|
||||
|
||||
$client.DownloadFileAsync($script:url, $script:tempFile)
|
||||
|
||||
$result = $form.ShowDialog()
|
||||
$client.CancelAsync()
|
||||
$form.Dispose()
|
||||
|
||||
if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
|
||||
$null = New-Item -Type Directory -Force $JavaDir
|
||||
$app = New-Object -ComObject Shell.Application
|
||||
$items = $app.NameSpace($script:tempFile).items()
|
||||
foreach ($item in $items) {
|
||||
$app.NameSpace($JavaDir).copyHere($item)
|
||||
}
|
||||
}
|
||||
|
||||
if ([System.IO.File]::Exists($script:tempFile)) {
|
||||
try {
|
||||
[System.IO.File]::Delete($script:tempFile)
|
||||
} catch {
|
||||
Write-Error $_
|
||||
}
|
||||
}
|
||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user