proxy settings ui optimization

This commit is contained in:
huangyuhui 2018-02-14 20:57:48 +08:00
parent 552238676c
commit eeccdbca72
12 changed files with 226 additions and 77 deletions

View File

@ -40,6 +40,12 @@ public final class Config {
@SerializedName("commonpath")
private String commonDirectory = Main.MINECRAFT_DIRECTORY.getAbsolutePath();
@SerializedName("hasProxy")
private boolean hasProxy = false;
@SerializedName("hasProxyAuth")
private boolean hasProxyAuth = false;
@SerializedName("proxyType")
private int proxyType = 0;
@ -121,6 +127,24 @@ public final class Config {
Settings.INSTANCE.save();
}
public boolean isHasProxy() {
return hasProxy;
}
public void setHasProxy(boolean hasProxy) {
this.hasProxy = hasProxy;
Settings.INSTANCE.save();
}
public boolean isHasProxyAuth() {
return hasProxyAuth;
}
public void setHasProxyAuth(boolean hasProxyAuth) {
this.hasProxyAuth = hasProxyAuth;
Settings.INSTANCE.save();
}
public int getProxyType() {
return proxyType;
}

View File

@ -0,0 +1,36 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.Main;
public final class Profiles {
private Profiles() {
}
public static String getProfileDisplayName(Profile profile) {
switch (profile.getName()) {
case Settings.DEFAULT_PROFILE:
return Main.i18n("profile.default");
case Settings.HOME_PROFILE:
return Main.i18n("profile.home");
default:
return profile.getName();
}
}
}

View File

@ -124,11 +124,6 @@ public class Settings {
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e);
}
else {
Logging.LOG.config("No settings file here, may be first loading.");
if (!c.getConfigurations().containsKey(HOME_PROFILE))
c.getConfigurations().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
}
return c;
}
@ -229,22 +224,27 @@ public class Settings {
SETTINGS.setProxyPass(proxyPass);
}
public boolean hasProxy() { return SETTINGS.isHasProxy(); }
public void setHasProxy(boolean hasProxy) { SETTINGS.setHasProxy(hasProxy); }
public boolean hasProxyAuth() { return SETTINGS.isHasProxyAuth(); }
public void setHasProxyAuth(boolean hasProxyAuth) { SETTINGS.setHasProxyAuth(hasProxyAuth); }
private void loadProxy() {
String host = getProxyHost();
Integer port = Lang.toIntOrNull(getProxyPort());
if (StringUtils.isBlank(host) || port == null)
if (!hasProxy() || StringUtils.isBlank(host) || port == null || getProxyType() == Proxy.Type.DIRECT)
proxy = Proxy.NO_PROXY;
else {
System.setProperty("http.proxyHost", getProxyHost());
System.setProperty("http.proxyPort", getProxyPort());
if (getProxyType() == Proxy.Type.DIRECT)
proxy = Proxy.NO_PROXY;
else
proxy = new Proxy(proxyType, new InetSocketAddress(host, port));
String user = getProxyUser();
String pass = getProxyPass();
if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
if (hasProxyAuth() && StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
System.setProperty("http.proxyUser", user);
System.setProperty("http.proxyPassword", pass);
@ -463,7 +463,7 @@ public class Settings {
}
public boolean hasProfile(String name) {
return getProfileMap().containsKey(Lang.nonNull(name, DEFAULT_PROFILE));
return getProfileMap().containsKey(name);
}
public Map<String, Profile> getProfileMap() {
@ -494,8 +494,10 @@ public class Settings {
}
private void checkProfileMap() {
if (getProfileMap().isEmpty())
if (getProfileMap().isEmpty()) {
getProfileMap().put(DEFAULT_PROFILE, new Profile(DEFAULT_PROFILE));
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
}
}
private void onProfileChanged() {

View File

@ -160,20 +160,10 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
Settings.INSTANCE.addAccount((Account) account);
dialog.close();
loadAccounts();
} else if (account instanceof InvalidCredentialsException) {
lblCreationWarning.setText(Main.i18n("account.failed.wrong_password"));
} else if (account instanceof NoCharacterException) {
lblCreationWarning.setText(Main.i18n("account.failed.no_charactor"));
} else if (account instanceof ServerDisconnectException) {
lblCreationWarning.setText(Main.i18n("account.failed.connect_authentication_server"));
} else if (account instanceof InvalidTokenException) {
lblCreationWarning.setText(Main.i18n("account.failed.invalid_token"));
} else if (account instanceof InvalidPasswordException) {
lblCreationWarning.setText(Main.i18n("account.failed.invalid_password"));
} else if (account instanceof NoSelectedCharacterException) {
dialog.close();
} else if (account instanceof Exception) {
lblCreationWarning.setText(account.getClass() + ": " + ((Exception) account).getLocalizedMessage());
lblCreationWarning.setText(accountException((Exception) account));
}
progressBar.setVisible(false);
});
@ -197,6 +187,22 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
this.title.set(title);
}
public static String accountException(Exception account) {
if (account instanceof InvalidCredentialsException) {
return Main.i18n("account.failed.invalid_credentials");
} else if (account instanceof NoCharacterException) {
return Main.i18n("account.failed.no_charactor");
} else if (account instanceof ServerDisconnectException) {
return Main.i18n("account.failed.connect_authentication_server");
} else if (account instanceof InvalidTokenException) {
return Main.i18n("account.failed.invalid_token");
} else if (account instanceof InvalidPasswordException) {
return Main.i18n("account.failed.invalid_password");
} else {
return account.getClass() + ": " + ((Exception) account).getLocalizedMessage();
}
}
public static String accountType(Account account) {
if (account instanceof OfflineAccount) return Main.i18n("account.methods.offline");
else if (account instanceof YggdrasilAccount) return Main.i18n("account.methods.yggdrasil");

View File

@ -39,6 +39,7 @@ import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
@ -122,7 +123,7 @@ public final class LeftPaneController {
private void onProfilesLoading() {
LinkedList<RipplerContainer> list = new LinkedList<>();
for (Profile profile : Settings.INSTANCE.getProfiles()) {
VersionListItem item = new VersionListItem(profile.getName());
VersionListItem item = new VersionListItem(Profiles.getProfileDisplayName(profile));
RipplerContainer ripplerContainer = new RipplerContainer(item);
item.setOnSettingsButtonClicked(() -> Controllers.getDecorator().showPage(new ProfilePage(profile)));
ripplerContainer.setOnMouseClicked(e -> Settings.INSTANCE.setSelectedProfile(profile));

View File

@ -25,6 +25,7 @@ import javafx.fxml.FXML;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.ui.construct.FileItem;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
@ -58,7 +59,7 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
FXUtils.loadFXML(this, "/assets/fxml/profile.fxml");
txtProfileName.setText(Optional.ofNullable(profile).map(Profile::getName).orElse(""));
txtProfileName.setText(Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse(""));
FXUtils.onChangeAndOperate(txtProfileName.textProperty(), it -> {
btnSave.setDisable(!txtProfileName.validate() || StringUtils.isBlank(getLocation()));
});

View File

@ -17,18 +17,16 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.controls.*;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Main;
@ -40,6 +38,7 @@ import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
import java.net.Proxy;
import java.util.Arrays;
import java.util.Collections;
@ -57,8 +56,6 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
@FXML
private JFXTextField txtFontSize;
@FXML
private JFXComboBox<?> cboProxyType;
@FXML
private JFXComboBox<Label> cboLanguage;
@FXML
private JFXComboBox<?> cboDownloadSource;
@ -80,6 +77,20 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
private MultiFileItem backgroundItem;
@FXML
private MultiFileItem themeItem;
@FXML
private JFXRadioButton chkNoProxy;
@FXML
private JFXRadioButton chkManualProxy;
@FXML
private JFXRadioButton chkProxyHttp;
@FXML
private JFXRadioButton chkProxySocks;
@FXML
private JFXCheckBox chkProxyAuthentication;
@FXML
private GridPane authPane;
@FXML
private Pane proxyPane;
{
FXUtils.loadFXML(this, "/assets/fxml/setting.fxml");
@ -131,8 +142,37 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
cboLanguage.getSelectionModel().select(Locales.LOCALES.indexOf(Settings.INSTANCE.getLocale()));
cboLanguage.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setLocale(Locales.getLocale(newValue.intValue())));
cboProxyType.getSelectionModel().select(Proxies.PROXIES.indexOf(Settings.INSTANCE.getProxyType()));
cboProxyType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setProxyType(Proxies.getProxyType(newValue.intValue())));
ToggleGroup proxyConfigurationGroup = new ToggleGroup();
chkProxyHttp.setUserData(Proxy.Type.HTTP);
chkProxyHttp.setToggleGroup(proxyConfigurationGroup);
chkProxySocks.setUserData(Proxy.Type.SOCKS);
chkProxySocks.setToggleGroup(proxyConfigurationGroup);
for (Toggle toggle : proxyConfigurationGroup.getToggles())
if (toggle.getUserData() == Settings.INSTANCE.getProxyType())
toggle.setSelected(true);
ToggleGroup hasProxyGroup = new ToggleGroup();
chkNoProxy.setToggleGroup(hasProxyGroup);
chkManualProxy.setToggleGroup(hasProxyGroup);
if (!Settings.INSTANCE.hasProxy())
chkNoProxy.setSelected(true);
else
chkManualProxy.setSelected(true);
proxyPane.disableProperty().bind(chkNoProxy.selectedProperty());
hasProxyGroup.selectedToggleProperty().addListener((a, b, newValue) -> {
Settings.INSTANCE.setHasProxy(newValue != chkNoProxy);
});
proxyConfigurationGroup.selectedToggleProperty().addListener((a, b, newValue) -> {
Settings.INSTANCE.setProxyType((Proxy.Type) newValue.getUserData());
});
chkProxyAuthentication.setSelected(Settings.INSTANCE.hasProxyAuth());
chkProxyAuthentication.selectedProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setHasProxyAuth(newValue));
authPane.disableProperty().bind(chkProxyAuthentication.selectedProperty().not());
fileCommonLocation.setProperty(Settings.INSTANCE.commonPathProperty());

View File

@ -24,10 +24,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.SpecificCharacterSelector;
import org.jackhuang.hmcl.auth.InvalidCredentialsException;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
import org.jackhuang.hmcl.setting.Accounts;
@ -78,10 +75,10 @@ public class YggdrasilAccountLoginPane extends StackPane {
if (account instanceof AuthInfo) {
success.accept(((AuthInfo) account));
dialog.close();
} else if (account instanceof InvalidCredentialsException) {
lblCreationWarning.setText(Main.i18n("account.failed.wrong_password"));
} else if (account instanceof NoSelectedCharacterException) {
dialog.close();
} else if (account instanceof Exception) {
lblCreationWarning.setText(account.getClass().toString() + ": " + ((Exception) account).getLocalizedMessage());
lblCreationWarning.setText(AccountsPage.accountException((Exception) account));
}
progressBar.setVisible(false);

View File

@ -4,7 +4,7 @@
*/
.root {
-fx-font-family: "微软雅黑", "Roboto";
-fx-font-family: "微软雅黑", "Microsoft YaHei", "Roboto";
}
.disabled Label {

View File

@ -47,25 +47,57 @@
</items>
</JFXComboBox></right></BorderPane>
<BorderPane><left><Label text="%settings.launcher.language" BorderPane.alignment="CENTER_LEFT" /></left><right><JFXComboBox fx:id="cboLanguage" /></right></BorderPane>
<BorderPane><left><Label text="%settings.launcher.proxy" BorderPane.alignment="CENTER_LEFT" /></left><right><HBox alignment="CENTER_LEFT" spacing="5">
<JFXComboBox fx:id="cboProxyType">
<items>
<FXCollections fx:factory="observableArrayList">
<Label text="%settings.launcher.proxy.direct" />
<Label text="HTTP" />
<Label text="Socks" />
</FXCollections>
</items>
</JFXComboBox>
<Label text="%settings.launcher.proxy.host" />
<JFXTextField fx:id="txtProxyHost" maxWidth="50" />
<Label text="%settings.launcher.proxy.port" />
<JFXTextField fx:id="txtProxyPort" maxWidth="50" />
<Label text="%settings.launcher.proxy.username" />
<JFXTextField fx:id="txtProxyUsername" maxWidth="50" />
<Label text="%settings.launcher.proxy.password" />
<JFXTextField fx:id="txtProxyPassword" maxWidth="50" />
</HBox></right></BorderPane>
<ComponentList title="%settings.launcher.proxy"> <!-- proxy -->
<VBox spacing="10">
<JFXRadioButton fx:id="chkNoProxy" text="%settings.launcher.proxy.no_proxy" />
<JFXRadioButton fx:id="chkManualProxy" text="%settings.launcher.proxy.has_proxy" />
<VBox fx:id="proxyPane" style="-fx-padding: 0 0 0 30;">
<HBox>
<JFXRadioButton fx:id="chkProxyHttp" text="%settings.launcher.proxy.http" />
<JFXRadioButton fx:id="chkProxySocks" text="%settings.launcher.proxy.socks" />
</HBox>
<GridPane hgap="20" vgap="10" style="-fx-padding: 0 0 0 15;">
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints hgrow="ALWAYS" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
</rowConstraints>
<Label text="%settings.launcher.proxy.host" GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.halignment="RIGHT" />
<JFXTextField fx:id="txtProxyHost" styleClass="fit-width" GridPane.rowIndex="1" GridPane.columnIndex="1" />
<Label text="%settings.launcher.proxy.port" GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.halignment="RIGHT" />
<JFXTextField fx:id="txtProxyPort" styleClass="fit-width" GridPane.rowIndex="2" GridPane.columnIndex="1" />
</GridPane>
<VBox style="-fx-padding: 15 0 15 5;">
<JFXCheckBox fx:id="chkProxyAuthentication" text="%settings.launcher.proxy.authentication" />
</VBox>
<GridPane fx:id="authPane" hgap="20" vgap="10" style="-fx-padding: 0 0 0 15;">
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints hgrow="ALWAYS" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
</rowConstraints>
<Label fx:id="" text="%settings.launcher.proxy.username" GridPane.rowIndex="0" GridPane.columnIndex="0" />
<JFXTextField fx:id="txtProxyUsername" styleClass="fit-width" GridPane.rowIndex="0" GridPane.columnIndex="1" />
<Label text="%settings.launcher.proxy.password" GridPane.rowIndex="1" GridPane.columnIndex="0" />
<JFXTextField fx:id="txtProxyPassword" styleClass="fit-width" GridPane.rowIndex="1" GridPane.columnIndex="1" />
</GridPane>
</VBox>
</VBox>
</ComponentList>
<MultiFileItem fx:id="themeItem" title="%settings.launcher.theme" hasSubtitle="true" hasCustom="false" />
@ -88,17 +120,17 @@
<RowConstraints minHeight="-Infinity" valignment="TOP" vgrow="SOMETIMES" />
<RowConstraints minHeight="-Infinity" valignment="TOP" vgrow="SOMETIMES" />
</rowConstraints>
<Label text="%about.copyright" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<Label text="%about.copyright" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<Label wrapText="true" text="%about.copyright.statement" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<Label text="%about.author" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Label text="%about.author" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Label wrapText="true" text="%about.author.statement" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="%about.thanks_to" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="2" />
<Label text="%about.thanks_to" GridPane.columnIndex="0" GridPane.rowIndex="2" />
<Label wrapText="true" text="%about.thanks_to.statement" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="%about.dependency" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="3" />
<Label text="%about.dependency" GridPane.columnIndex="0" GridPane.rowIndex="3" />
<Label wrapText="true" text="%about.dependency.statement" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<Label text="%about.claim" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="4" />
<Label text="%about.claim" GridPane.columnIndex="0" GridPane.rowIndex="4" />
<Label textAlignment="JUSTIFY" wrapText="true" text="%about.claim.statement" GridPane.columnIndex="1" GridPane.rowIndex="4" />
<Label text="%about.open_source" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="5" />
<Label text="%about.open_source" GridPane.columnIndex="0" GridPane.rowIndex="5" />
<Label wrapText="true" text="%about.open_source.statement" GridPane.columnIndex="1" GridPane.rowIndex="5" />
</GridPane>
</StackPane>

View File

@ -34,10 +34,10 @@ account.current=Current account
account.create=Create a new account
account.email=Email
account.failed.connect_authentication_server=Cannot connect the authentication server. Check your network.
account.failed.invalid_credentials=Incorrect password. Or forbidden to log in temporarily.
account.failed.invalid_password=Invalid password
account.failed.invalid_token=Please log out and re-input your password to log in.
account.failed.no_charactor=No character in this account.
account.failed.wrong_password=Incorrect password or username
account.methods=Login Type
account.methods.no_method=No login method
account.methods.offline=Offline
@ -296,13 +296,14 @@ operation.confirm_stop=Terminate the operations?
operation.stopped=The operation was aborted.
profile=Profile
profile.copy_from=Copy From:
profile.default=Current directory
profile.home=User home
profile.instance_directory=Game Directory
profile.new=New Config
profile.new_name=New Profile Name:
profile.remove=Sure to remove profile %s?
profile.title=Game Directories
profile.selected=Current directory
profile.selected=Selected
selector.choose=Choose
selector.choose_file=Select a file
@ -351,10 +352,14 @@ settings.launcher.download_source=Download Source
settings.launcher.language=Language
settings.launcher.log_font=Log Font
settings.launcher.proxy=Proxy
settings.launcher.proxy.direct=Direct
settings.launcher.proxy.authentication=Proxy Authentication
settings.launcher.proxy.no_proxy=No proxy
settings.launcher.proxy.has_proxy=Proxy configuration
settings.launcher.proxy.host=Host
settings.launcher.proxy.http=HTTP
settings.launcher.proxy.password=Password
settings.launcher.proxy.port=Port
settings.launcher.proxy.socks=Socks
settings.launcher.proxy.username=Account
settings.launcher.theme=Theme

View File

@ -34,10 +34,10 @@ account.current=当前账户
account.create=新建账户
account.email=邮箱
account.failed.connect_authentication_server=无法连接认证服务器,可能是网络问题
account.failed.invalid_credentials=您的用户名或密码错误,或者登录次数过多被暂时禁止登录,请稍后再试
account.failed.invalid_password=无效的密码
account.failed.invalid_token=请尝试登出并重新输入密码登录
account.failed.no_charactor=该帐号没有角色
account.failed.wrong_password=可能是您的用户名或密码错误
account.methods=登录方式
account.methods.no_method=没有登入方式...
account.methods.offline=离线模式
@ -296,13 +296,14 @@ operation.confirm_stop=真的要终止操作吗?
operation.stopped=操作被强行终止
profile=配置
profile.copy_from=复制配置:
profile.default=当前目录
profile.home=主文件夹
profile.instance_directory=游戏路径
profile.new=新建配置
profile.new_name=新配置名:
profile.remove=真的要删除配置%s吗
profile.title=游戏目录
profile.selected=当前目录
profile.selected=已选中
selector.choose=选择
selector.choose_file=选择文件
@ -351,10 +352,14 @@ settings.launcher.download_source=下载源
settings.launcher.language=语言
settings.launcher.log_font=日志字体
settings.launcher.proxy=代理
settings.launcher.proxy.direct=直连
settings.launcher.proxy.authentication=代理账户
settings.launcher.proxy.no_proxy=直连
settings.launcher.proxy.has_proxy=代理设置
settings.launcher.proxy.host=主机
settings.launcher.proxy.http=HTTP
settings.launcher.proxy.password=密码
settings.launcher.proxy.port=端口
settings.launcher.proxy.socks=Socks
settings.launcher.proxy.username=账户
settings.launcher.theme=主题