Merge pull request #396 from yushijinhun/fix

支持背景热替换
This commit is contained in:
huanghongxun 2018-07-18 23:05:56 +08:00 committed by GitHub
commit 3a9017adb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 132 additions and 124 deletions

View File

@ -66,6 +66,7 @@ public final class Config implements Cloneable, Observable {
.registerTypeAdapter(ObservableSet.class, new ObservableSetCreator())
.registerTypeAdapter(ObservableMap.class, new ObservableMapCreator())
.registerTypeAdapterFactory(new JavaFxPropertyTypeAdapterFactory(true, true))
.registerTypeAdapter(Theme.class, new Theme.TypeAdapter())
.registerTypeAdapter(EnumBackgroundImage.class, new EnumOrdinalDeserializer<>(EnumBackgroundImage.class)) // backward compatibility for backgroundType
.registerTypeAdapter(Proxy.Type.class, new EnumOrdinalDeserializer<>(Proxy.Type.class)) // backward compatibility for hasProxy
.setPrettyPrinting()
@ -113,7 +114,7 @@ public final class Config implements Cloneable, Observable {
private StringProperty proxyPass = new SimpleStringProperty();
@SerializedName("theme")
private StringProperty theme = new SimpleStringProperty();
private ObjectProperty<Theme> theme = new SimpleObjectProperty<>(Theme.BLUE);
@SerializedName("localization")
private StringProperty localization = new SimpleStringProperty();
@ -320,15 +321,15 @@ public final class Config implements Cloneable, Observable {
return proxyPass;
}
public String getTheme() {
public Theme getTheme() {
return theme.get();
}
public void setTheme(String theme) {
public void setTheme(Theme theme) {
this.theme.set(theme);
}
public StringProperty themeProperty() {
public ObjectProperty<Theme> themeProperty() {
return theme;
}

View File

@ -257,31 +257,6 @@ public class Settings {
selectedAccount.get();
}
/****************************************
* THEME *
****************************************/
private final ImmediateObjectProperty<Theme> theme = new ImmediateObjectProperty<Theme>(this, "theme", Theme.getTheme(CONFIG.getTheme()).orElse(Theme.BLUE)) {
@Override
public void invalidated() {
super.invalidated();
CONFIG.setTheme(get().getName().toLowerCase());
}
};
public Theme getTheme() {
return theme.get();
}
public void setTheme(Theme theme) {
this.theme.set(theme);
}
public ImmediateObjectProperty<Theme> themeProperty() {
return theme;
}
/****************************************
* PROFILES *
****************************************/

View File

@ -24,6 +24,11 @@ import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.IOUtils;
import org.jackhuang.hmcl.util.Logging;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
@ -115,7 +120,7 @@ public class Theme {
}
public static ObjectBinding<Color> foregroundFillBinding() {
return Bindings.createObjectBinding(() -> Settings.INSTANCE.getTheme().getForegroundColor(), Settings.INSTANCE.themeProperty());
return Bindings.createObjectBinding(() -> CONFIG.getTheme().getForegroundColor(), CONFIG.themeProperty());
}
public static ObjectBinding<Color> blackFillBinding() {
@ -125,4 +130,16 @@ public class Theme {
public static ObjectBinding<Color> whiteFillBinding() {
return Bindings.createObjectBinding(() -> Color.WHITE);
}
public static class TypeAdapter extends com.google.gson.TypeAdapter<Theme> {
@Override
public void write(JsonWriter out, Theme value) throws IOException {
out.value(value.getName().toLowerCase());
}
@Override
public Theme read(JsonReader in) throws IOException {
return getTheme(in.nextString()).orElse(Theme.BLUE);
}
}
}

View File

@ -34,6 +34,8 @@ import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.util.FutureCallback;
import org.jackhuang.hmcl.util.JavaVersion;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import java.util.function.Consumer;
public final class Controllers {
@ -106,7 +108,7 @@ public final class Controllers {
decorator.setCustomMaximize(false);
scene = new Scene(decorator, 804, 521);
scene.getStylesheets().setAll(Settings.INSTANCE.getTheme().getStylesheets());
scene.getStylesheets().setAll(CONFIG.getTheme().getStylesheets());
stage.setMinWidth(804);
stage.setMaxWidth(804);
stage.setMinHeight(521);

View File

@ -66,17 +66,22 @@ import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.StackContainerPane;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogWizardDisplayer;
import org.jackhuang.hmcl.ui.wizard.*;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import static java.util.stream.Collectors.toList;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.util.Logging.LOG;
public final class Decorator extends StackPane implements TaskExecutorDialogWizardDisplayer {
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),
@ -212,81 +217,87 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
animationHandler = new TransitionHandler(contentPlaceHolder);
loadBackground();
setupBackground();
setupAuthlibInjectorDnD();
}
private void loadBackground() {
// ==== Background ====
private void setupBackground() {
drawerWrapper.backgroundProperty().bind(
Bindings.createObjectBinding(
() -> {
Image image = null;
if (CONFIG.getBackgroundImageType() == EnumBackgroundImage.CUSTOM) {
image = tryLoadImage(Paths.get(CONFIG.getBackgroundImage()))
.orElse(null);
}
if (image == null) {
image = loadDefaultBackgroundImage();
}
return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true)));
},
CONFIG.backgroundImageTypeProperty(),
CONFIG.backgroundImageProperty()));
}
private Image defaultBackground = new Image("/assets/img/background.jpg");
/**
* Load background image from bg/, background.png, background.jpg
*/
private Image loadDefaultBackgroundImage() {
Optional<Image> image = randomImageIn(Paths.get("bg"));
if (!image.isPresent()) {
image = tryLoadImage(Paths.get("background.png"));
}
if (!image.isPresent()) {
image = tryLoadImage(Paths.get("background.jpg"));
}
return image.orElse(defaultBackground);
}
private Optional<Image> randomImageIn(Path imageDir) {
if (!Files.isDirectory(imageDir)) {
return Optional.empty();
}
List<Path> candidates;
try {
Image background;
if (CONFIG.getBackgroundImageType() == EnumBackgroundImage.DEFAULT)
background = searchBackgroundImage(new Image("/assets/img/background.jpg"), "");
else
background = searchBackgroundImage(new Image("/assets/img/background.jpg"), CONFIG.getBackgroundImage());
drawerWrapper.setBackground(new Background(new BackgroundImage(background, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true))));
} catch (IllegalArgumentException ignore) {
}
candidates = Files.list(imageDir)
.filter(Files::isRegularFile)
.filter(it -> {
String filename = it.getFileName().toString();
return filename.endsWith(".png") || filename.endsWith(".jpg");
})
.collect(toList());
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to list files in ./bg", e);
return Optional.empty();
}
private static Image searchBackgroundImage(Image def, String customPath) {
Random random = new Random();
boolean loaded = false;
Image background = def;
Random rnd = new Random();
while (candidates.size() > 0) {
int selected = rnd.nextInt(candidates.size());
Optional<Image> loaded = tryLoadImage(candidates.get(selected));
if (loaded.isPresent()) {
return Optional.of(loaded.get());
} else {
candidates.remove(selected);
}
}
return Optional.empty();
}
// custom path
if (StringUtils.isNotBlank(customPath)) {
private Optional<Image> tryLoadImage(Path path) {
if (Files.isRegularFile(path)) {
try {
background = new Image("file:" + customPath);
loaded = true;
} catch (IllegalArgumentException ignore) {
return Optional.of(new Image(path.toAbsolutePath().toUri().toString()));
} catch (IllegalArgumentException ignored) {
}
}
// images in ./bg
if (!loaded) {
File backgroundImageFile = new File("bg");
if (backgroundImageFile.isDirectory()) {
File[] backgroundPath = backgroundImageFile.listFiles(file -> StringUtils.containsOne(FileUtils.getExtension(file), "png", "jpg"));
if (backgroundPath != null && backgroundPath.length > 0) {
int index = random.nextInt(backgroundPath.length);
try {
background = new Image("file:" + backgroundPath[index].getAbsolutePath());
loaded = true;
} catch (IllegalArgumentException ignore) {
}
}
}
}
// background.png
if (!loaded) {
File backgroundImageFile = new File("background.png");
if (backgroundImageFile.exists()) {
try {
background = new Image("file:" + backgroundImageFile.getAbsolutePath());
loaded = true;
} catch (IllegalArgumentException ignore) {
}
}
}
// background.jpg
if (!loaded) {
File backgroundImageFile = new File("background.jpg");
if (backgroundImageFile.exists()) {
try {
background = new Image("file:" + backgroundImageFile.getAbsolutePath());
loaded = true;
} catch (IllegalArgumentException ignore) {
}
}
}
return background;
return Optional.empty();
}
// ====
@FXML
private void onMouseMoved(MouseEvent mouseEvent) {

View File

@ -43,6 +43,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.util.concurrent.CountDownLatch;
@ -64,7 +65,7 @@ public final class LogWindow extends Stage {
public LogWindow() {
setScene(new Scene(impl, 800, 480));
getScene().getStylesheets().addAll(Settings.INSTANCE.getTheme().getStylesheets());
getScene().getStylesheets().addAll(CONFIG.getTheme().getStylesheets());
setTitle(i18n("logwindow.title"));
getIcons().add(new Image("/assets/img/icon.png"));
}

View File

@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*;
import com.jfoenix.effects.JFXDepthManager;
import javafx.application.Platform;
import javafx.beans.binding.When;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
@ -46,7 +47,6 @@ import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.i18n.Locales;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.net.Proxy;
@ -188,44 +188,44 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
backgroundItem.loadChildren(Collections.singletonList(
backgroundItem.createChildren(i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT)
));
FXUtils.bindString(backgroundItem.getTxtCustom(), CONFIG.backgroundImageProperty());
backgroundItem.setCustomUserData(EnumBackgroundImage.CUSTOM);
backgroundItem.getGroup().getToggles().stream().filter(it -> it.getUserData() == CONFIG.getBackgroundImageType()).findFirst().ifPresent(it -> it.setSelected(true));
backgroundItem.getTxtCustom().textProperty().bindBidirectional(CONFIG.backgroundImageProperty());
CONFIG.backgroundImageProperty().addListener(onInvalidating(this::initBackgroundItemSubtitle));
CONFIG.backgroundImageTypeProperty().addListener(onInvalidating(this::initBackgroundItemSubtitle));
initBackgroundItemSubtitle();
ObjectProperty<EnumBackgroundImage> backgroundType = new SimpleObjectProperty<EnumBackgroundImage>(EnumBackgroundImage.DEFAULT) {
{
invalidated();
}
backgroundItem.setToggleSelectedListener(newValue ->
CONFIG.setBackgroundImageType((EnumBackgroundImage) newValue.getUserData()));
@Override
protected void invalidated() {
backgroundItem.getGroup().getToggles().stream()
.filter(it -> it.getUserData() == get())
.findFirst()
.ifPresent(it -> it.setSelected(true));
}
};
backgroundItem.getGroup().selectedToggleProperty().addListener((observable, oldValue, newValue) -> backgroundType.set((EnumBackgroundImage) newValue.getUserData()));
backgroundType.bindBidirectional(CONFIG.backgroundImageTypeProperty());
backgroundItem.subtitleProperty().bind(
new When(backgroundType.isEqualTo(EnumBackgroundImage.DEFAULT))
.then(i18n("launcher.background.default"))
.otherwise(CONFIG.backgroundImageProperty()));
// theme
JFXColorPicker picker = new JFXColorPicker(Color.web(Settings.INSTANCE.getTheme().getColor()), null);
JFXColorPicker picker = new JFXColorPicker(Color.web(CONFIG.getTheme().getColor()), null);
picker.setCustomColorText(i18n("color.custom"));
picker.setRecentColorsText(i18n("color.recent"));
picker.getCustomColors().setAll(Theme.SUGGESTED_COLORS);
picker.setOnAction(e -> {
Theme theme = Theme.custom(Theme.getColorDisplayName(picker.getValue()));
Settings.INSTANCE.setTheme(theme);
CONFIG.setTheme(theme);
Controllers.getScene().getStylesheets().setAll(theme.getStylesheets());
});
themeColorPickerContainer.getChildren().setAll(picker);
Platform.runLater(() -> JFXDepthManager.setDepth(picker, 0));
}
private void initBackgroundItemSubtitle() {
switch (CONFIG.getBackgroundImageType()) {
case DEFAULT:
backgroundItem.setSubtitle(i18n("launcher.background.default"));
break;
case CUSTOM:
backgroundItem.setSubtitle(CONFIG.getBackgroundImage());
break;
}
}
public String getTitle() {
return title.get();
}

View File

@ -21,14 +21,15 @@ import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.jackhuang.hmcl.setting.Settings;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
public class WebStage extends Stage {
private final WebView webView = new WebView();
public WebStage() {
setScene(new Scene(webView, 800, 480));
getScene().getStylesheets().addAll(Settings.INSTANCE.getTheme().getStylesheets());
getScene().getStylesheets().addAll(CONFIG.getTheme().getStylesheets());
getIcons().add(new Image("/assets/img/icon.png"));
}