mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-14 14:26:43 -04:00
Add a convenient entrance to launch other version
This commit is contained in:
parent
d426952d47
commit
b1cd7057db
4
.gitignore
vendored
4
.gitignore
vendored
@ -25,8 +25,12 @@ NVIDIA
|
|||||||
/HMCLCore/out/
|
/HMCLCore/out/
|
||||||
|
|
||||||
# eclipse
|
# eclipse
|
||||||
|
/bin/
|
||||||
|
/HMCL/bin/
|
||||||
|
/HMCLCore/bin/
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
|
.settings
|
||||||
|
|
||||||
# netbeans
|
# netbeans
|
||||||
.nb-gradle
|
.nb-gradle
|
@ -17,14 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.setting;
|
package org.jackhuang.hmcl.setting;
|
||||||
|
|
||||||
|
import com.jfoenix.concurrency.JFXUtilities;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.property.ReadOnlyListProperty;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.beans.property.ReadOnlyListWrapper;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import org.jackhuang.hmcl.Launcher;
|
import org.jackhuang.hmcl.Launcher;
|
||||||
|
import org.jackhuang.hmcl.event.EventBus;
|
||||||
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -41,6 +43,8 @@ public final class Profiles {
|
|||||||
public static final String DEFAULT_PROFILE = "Default";
|
public static final String DEFAULT_PROFILE = "Default";
|
||||||
public static final String HOME_PROFILE = "Home";
|
public static final String HOME_PROFILE = "Home";
|
||||||
|
|
||||||
|
private static InvalidationListener listener = o -> loadVersion();
|
||||||
|
|
||||||
private Profiles() {
|
private Profiles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,11 +65,17 @@ public final class Profiles {
|
|||||||
private static ObjectProperty<Profile> selectedProfile = new SimpleObjectProperty<Profile>() {
|
private static ObjectProperty<Profile> selectedProfile = new SimpleObjectProperty<Profile>() {
|
||||||
{
|
{
|
||||||
profiles.addListener(onInvalidating(this::invalidated));
|
profiles.addListener(onInvalidating(this::invalidated));
|
||||||
|
|
||||||
|
this.addListener(this::change);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void invalidated() {
|
protected void invalidated() {
|
||||||
Profile profile = get();
|
Profile profile = get();
|
||||||
|
|
||||||
|
if (get() != null)
|
||||||
|
get().removeListener(listener);
|
||||||
|
|
||||||
if (profiles.isEmpty()) {
|
if (profiles.isEmpty()) {
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
set(null);
|
set(null);
|
||||||
@ -80,7 +90,16 @@ public final class Profiles {
|
|||||||
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
config().setSelectedProfile(profile == null ? "" : profile.getName());
|
config().setSelectedProfile(profile == null ? "" : profile.getName());
|
||||||
|
loadVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void change(ObservableValue<? extends Profile> observableValue, Profile oldProfile, Profile newProfile) {
|
||||||
|
if (oldProfile != null)
|
||||||
|
oldProfile.selectedVersionProperty().removeListener(listener);
|
||||||
|
if (newProfile != null)
|
||||||
|
newProfile.selectedVersionProperty().addListener(listener);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,6 +162,13 @@ public final class Profiles {
|
|||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> {
|
||||||
|
JFXUtilities.runInFX(() -> {
|
||||||
|
if (selectedProfile.get() != null && selectedProfile.get().getRepository() == event.getSource())
|
||||||
|
loadVersion();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ObservableList<Profile> getProfiles() {
|
public static ObservableList<Profile> getProfiles() {
|
||||||
@ -164,4 +190,21 @@ public final class Profiles {
|
|||||||
public static ObjectProperty<Profile> selectedProfileProperty() {
|
public static ObjectProperty<Profile> selectedProfileProperty() {
|
||||||
return selectedProfile;
|
return selectedProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ReadOnlyStringWrapper selectedVersion = new ReadOnlyStringWrapper();
|
||||||
|
|
||||||
|
public static ReadOnlyStringProperty selectedVersionProperty() {
|
||||||
|
return selectedVersion.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSelectedVersion() {
|
||||||
|
return selectedVersion.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadVersion() {
|
||||||
|
Profile profile = selectedProfile.get();
|
||||||
|
if (profile == null || !profile.getRepository().isLoaded()) return;
|
||||||
|
JFXUtilities.runInFX(() ->
|
||||||
|
selectedVersion.set(profile.getSelectedVersion()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,25 +17,52 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui;
|
package org.jackhuang.hmcl.ui;
|
||||||
|
|
||||||
|
import com.jfoenix.concurrency.JFXUtilities;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXPopup;
|
||||||
import javafx.beans.property.ReadOnlyStringProperty;
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
import javafx.beans.property.ReadOnlyStringWrapper;
|
import javafx.beans.property.ReadOnlyStringWrapper;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.shape.Rectangle;
|
||||||
|
import org.jackhuang.hmcl.event.EventBus;
|
||||||
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
|
||||||
|
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
|
||||||
|
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.setting.Profiles;
|
import org.jackhuang.hmcl.setting.Profiles;
|
||||||
|
import org.jackhuang.hmcl.setting.Theme;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.IconedMenuItem;
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
import org.jackhuang.hmcl.ui.versions.Versions;
|
||||||
|
import org.jackhuang.hmcl.util.VersionNumber;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public final class MainPage extends StackPane implements DecoratorPage {
|
public final class MainPage extends StackPane implements DecoratorPage {
|
||||||
|
|
||||||
private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(this, "title", i18n("main_page"));
|
private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(this, "title", i18n("main_page"));
|
||||||
|
|
||||||
|
private final VBox menu = new VBox();
|
||||||
|
private final JFXPopup popup = new JFXPopup(menu);
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private StackPane main;
|
private StackPane main;
|
||||||
|
@FXML
|
||||||
|
private JFXButton btnLaunch;
|
||||||
|
@FXML
|
||||||
|
private JFXButton btnMenu;
|
||||||
|
@FXML
|
||||||
|
private Label lblCurrentGame;
|
||||||
|
|
||||||
|
private Profile profile;
|
||||||
{
|
{
|
||||||
FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
|
FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
|
||||||
|
|
||||||
@ -45,6 +72,53 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
|||||||
else
|
else
|
||||||
getChildren().setAll(main);
|
getChildren().setAll(main);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
btnLaunch.setClip(new Rectangle(-100, -100, 280, 200));
|
||||||
|
btnMenu.setClip(new Rectangle(180, -100, 100, 200));
|
||||||
|
menu.setMinWidth(200);
|
||||||
|
|
||||||
|
StackPane graphic = new StackPane();
|
||||||
|
Node svg = SVG.triangle(Theme.whiteFillBinding(), 10, 10);
|
||||||
|
StackPane.setAlignment(svg, Pos.CENTER_RIGHT);
|
||||||
|
graphic.getChildren().setAll(svg);
|
||||||
|
graphic.setTranslateX(11);
|
||||||
|
btnMenu.setGraphic(graphic);
|
||||||
|
|
||||||
|
Profiles.selectedVersionProperty().addListener((o, a, version) -> {
|
||||||
|
if (version != null) {
|
||||||
|
lblCurrentGame.setText(version);
|
||||||
|
} else {
|
||||||
|
lblCurrentGame.setText(i18n("version.empty"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> {
|
||||||
|
if (event.getSource() == profile.getRepository())
|
||||||
|
loadVersions((HMCLGameRepository) event.getSource());
|
||||||
|
});
|
||||||
|
Profiles.selectedProfileProperty().addListener((a, b, newValue) -> profile = newValue);
|
||||||
|
|
||||||
|
profile = Profiles.getSelectedProfile();
|
||||||
|
if (profile.getRepository().isLoaded())
|
||||||
|
loadVersions(profile.getRepository());
|
||||||
|
else
|
||||||
|
profile.getRepository().refreshVersionsAsync().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadVersions(HMCLGameRepository repository) {
|
||||||
|
List<IconedMenuItem> children = repository.getVersions().parallelStream()
|
||||||
|
.filter(version -> !version.isHidden())
|
||||||
|
.sorted((a, b) -> VersionNumber.COMPARATOR.compare(VersionNumber.asVersion(a.getId()), VersionNumber.asVersion(b.getId())))
|
||||||
|
.map(version -> new IconedMenuItem(null, version.getId(), () -> {
|
||||||
|
repository.getProfile().setSelectedVersion(version.getId());
|
||||||
|
Versions.launch(repository.getProfile(), version.getId());
|
||||||
|
popup.hide();
|
||||||
|
}))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
JFXUtilities.runInFX(() -> {
|
||||||
|
if (profile == repository.getProfile())
|
||||||
|
menu.getChildren().setAll(children);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@ -53,6 +127,11 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
|||||||
Versions.launch(profile, profile.getSelectedVersion());
|
Versions.launch(profile, profile.getSelectedVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onMenu() {
|
||||||
|
popup.show(btnMenu, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.RIGHT, 0, -btnMenu.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title.get();
|
return title.get();
|
||||||
}
|
}
|
||||||
|
@ -150,4 +150,8 @@ public final class SVG {
|
|||||||
public static Node openInNew(ObjectBinding<? extends Paint> fill, double width, double height) {
|
public static Node openInNew(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||||
return createSVGPath("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", fill, width, height);
|
return createSVGPath("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", fill, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Node triangle(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||||
|
return createSVGPath("M1,21H23L12,2", fill, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,12 +39,17 @@ public class IconedItem extends RipplerContainer {
|
|||||||
|
|
||||||
private static HBox createHBox(Node icon) {
|
private static HBox createHBox(Node icon) {
|
||||||
HBox hBox = new HBox();
|
HBox hBox = new HBox();
|
||||||
hBox.getStyleClass().setAll("iconed-item-container");
|
|
||||||
|
if (icon != null) {
|
||||||
icon.setMouseTransparent(true);
|
icon.setMouseTransparent(true);
|
||||||
|
hBox.getChildren().add(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
hBox.getStyleClass().setAll("iconed-item-container");
|
||||||
Label textLabel = new Label();
|
Label textLabel = new Label();
|
||||||
textLabel.setId("label");
|
textLabel.setId("label");
|
||||||
textLabel.setMouseTransparent(true);
|
textLabel.setMouseTransparent(true);
|
||||||
hBox.getChildren().addAll(icon, textLabel);
|
hBox.getChildren().addAll(textLabel);
|
||||||
return hBox;
|
return hBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,40 +32,10 @@ import java.io.File;
|
|||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public class GameAdvancedListItem extends AdvancedListItem {
|
public class GameAdvancedListItem extends AdvancedListItem {
|
||||||
private final WeakListenerHolder listenerHolder = new WeakListenerHolder();
|
|
||||||
|
|
||||||
private Profile profile;
|
|
||||||
private InvalidationListener listener = o -> loadVersion();
|
|
||||||
|
|
||||||
public GameAdvancedListItem() {
|
public GameAdvancedListItem() {
|
||||||
Profiles.selectedProfileProperty().addListener(listenerHolder.weak((a, b, newValue) -> {
|
Profiles.selectedVersionProperty().addListener((o, a, version) -> {
|
||||||
JFXUtilities.runInFX(() -> loadProfile(newValue));
|
File iconFile = Profiles.getSelectedProfile().getRepository().getVersionIcon(version);
|
||||||
}));
|
|
||||||
listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> {
|
|
||||||
JFXUtilities.runInFX(() -> {
|
|
||||||
if (profile != null && profile.getRepository() == event.getSource())
|
|
||||||
loadVersion();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
loadProfile(Profiles.getSelectedProfile());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadProfile(Profile newProfile) {
|
|
||||||
if (profile != null)
|
|
||||||
profile.selectedVersionProperty().removeListener(listener);
|
|
||||||
profile = newProfile;
|
|
||||||
if (profile != null)
|
|
||||||
profile.selectedVersionProperty().addListener(listener);
|
|
||||||
loadVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadVersion() {
|
|
||||||
Profile profile = this.profile;
|
|
||||||
if (profile == null || !profile.getRepository().isLoaded()) return;
|
|
||||||
String version = profile.getSelectedVersion();
|
|
||||||
File iconFile = profile.getRepository().getVersionIcon(version);
|
|
||||||
|
|
||||||
JFXUtilities.runInFX(() -> {
|
|
||||||
if (iconFile.exists())
|
if (iconFile.exists())
|
||||||
imageProperty().set(new Image("file:" + iconFile.getAbsolutePath()));
|
imageProperty().set(new Image("file:" + iconFile.getAbsolutePath()));
|
||||||
else
|
else
|
||||||
|
@ -510,6 +510,15 @@
|
|||||||
-fx-font-size:14px;
|
-fx-font-size:14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jfx-button-raised .jfx-rippler {
|
||||||
|
-jfx-rippler-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jfx-button-raised .label {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.jfx-button-raised-round {
|
.jfx-button-raised-round {
|
||||||
-fx-background-color: -fx-base-color;
|
-fx-background-color: -fx-base-color;
|
||||||
-fx-background-radius: 50px;
|
-fx-background-radius: 50px;
|
||||||
|
@ -2,11 +2,23 @@
|
|||||||
|
|
||||||
<?import com.jfoenix.controls.JFXButton?>
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
<?import javafx.scene.layout.StackPane?>
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
<fx:root type="StackPane" pickOnBounds="false"
|
<fx:root type="StackPane" pickOnBounds="false"
|
||||||
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<StackPane fx:id="main" style="-fx-padding: 25;">
|
<StackPane fx:id="main" style="-fx-padding: 25;">
|
||||||
<JFXButton prefWidth="150" prefHeight="50" buttonType="RAISED" styleClass="jfx-button-raised"
|
<JFXButton prefWidth="200" prefHeight="50" buttonType="RAISED" styleClass="jfx-button-raised"
|
||||||
style="-fx-font-size: 15;" onMouseClicked="#launch"
|
onMouseClicked="#launch" fx:id="btnLaunch"
|
||||||
text="%version.launch" StackPane.alignment="BOTTOM_RIGHT"/>
|
StackPane.alignment="BOTTOM_RIGHT">
|
||||||
|
<graphic>
|
||||||
|
<VBox alignment="CENTER" translateX="-10" maxWidth="160">
|
||||||
|
<Label style="-fx-font-size: 15;" text="%version.launch" />
|
||||||
|
<Label style="-fx-font-size: 10px;" fx:id="lblCurrentGame" />
|
||||||
|
</VBox>
|
||||||
|
</graphic>
|
||||||
|
</JFXButton>
|
||||||
|
<JFXButton prefWidth="200" prefHeight="50" buttonType="RAISED" styleClass="jfx-button-raised"
|
||||||
|
style="-fx-font-size: 15;" onMouseClicked="#onMenu"
|
||||||
|
fx:id="btnMenu" StackPane.alignment="BOTTOM_RIGHT" />
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</fx:root>
|
</fx:root>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user