diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java index 055f29c58c..31c6c7231b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java @@ -21,22 +21,28 @@ import javafx.beans.property.StringProperty; import javafx.scene.control.RadioButton; import javafx.scene.control.Skin; - import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jetbrains.annotations.NotNull; public class ProfileListItem extends RadioButton { private final Profile profile; private final StringProperty title = new SimpleStringProperty(); private final StringProperty subtitle = new SimpleStringProperty(); - public ProfileListItem(Profile profile) { + public ProfileListItem(@NotNull Profile profile) { this.profile = profile; getStyleClass().setAll("profile-list-item", "navigation-drawer-item"); setUserData(profile); title.set(Profiles.getProfileDisplayName(profile)); subtitle.set(profile.getGameDir().toString()); + + profile.nameProperty().addListener((obs, oldVal, newVal) -> title.set(Profiles.getProfileDisplayName(profile))); + profile.gameDirProperty().addListener((obs, oldVal, newVal) -> subtitle.set(profile.getGameDir().toString())); + + FXUtils.onSecondaryButtonClicked(this, () -> ProfileListPopupMenu.show(this, profile)); } @Override @@ -44,6 +50,7 @@ protected Skin createDefaultSkin() { return new ProfileListItemSkin(this); } + /// Removes this profile from the list. public void remove() { Profiles.getProfiles().remove(profile); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListPopupMenu.java new file mode 100644 index 0000000000..53824e4caa --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListPopupMenu.java @@ -0,0 +1,48 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2020 huangyuhui 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 . + */ +package org.jackhuang.hmcl.ui.profile; + +import com.jfoenix.controls.JFXPopup; +import javafx.scene.Node; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.construct.IconedMenuItem; +import org.jackhuang.hmcl.ui.construct.PopupMenu; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +/// Popup menu for ProfileListItem. +public final class ProfileListPopupMenu extends StackPane { + + /// Shows the popup menu for the given profile item. + public static void show(Node owner, Profile profile) { + PopupMenu menu = new PopupMenu(); + JFXPopup popup = new JFXPopup(menu); + menu.getContent().add(new IconedMenuItem( + org.jackhuang.hmcl.ui.SVG.EDIT, + i18n("button.edit"), + () -> Controllers.navigate(new ProfilePage(profile)), + popup)); + popup.show(owner, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, 0, 0); + } + + public ProfileListPopupMenu() { + getStyleClass().add("popup-menu-content"); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java index e972bbab8b..6ca58ea90f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java @@ -53,6 +53,8 @@ public final class ProfilePage extends BorderPane implements DecoratorPage { private final JFXTextField txtProfileName; private final LineFileChooserButton gameDir; private final LineToggleButton toggleUseRelativePath; + private ChangeListener locationChangeListener; + private ChangeListener txtProfileNameChangeListener; /** * @param profile null if creating a new profile. @@ -99,7 +101,9 @@ public ProfilePage(Profile profile) { @Override protected void eval() { JFXTextField control = (JFXTextField) this.getSrcControl(); - hasErrors.set(Profiles.getProfiles().stream().anyMatch(profile -> profile.getName().equals(control.getText()))); + hasErrors.set(Profiles.getProfiles().stream() + .filter(p -> p != profile) + .anyMatch(p -> p.getName().equals(control.getText()))); } }); } @@ -144,7 +148,7 @@ protected void eval() { txtProfileName.textProperty(), location)); } - ChangeListener locationChangeListener = (observable, oldValue, newValue) -> { + locationChangeListener = (observable, oldValue, newValue) -> { Path newPath; try { newPath = FileUtils.toAbsolute(Path.of(newValue)); @@ -166,7 +170,7 @@ protected void eval() { }; locationProperty().addListener(locationChangeListener); - txtProfileName.textProperty().addListener(new ChangeListener<>() { + txtProfileNameChangeListener = new ChangeListener<>() { @Override public void changed(ObservableValue observable, String oldValue, String newValue) { if (txtProfileName.isFocused()) { @@ -174,10 +178,14 @@ public void changed(ObservableValue observable, String oldValu locationProperty().removeListener(locationChangeListener); } } - }); + }; + txtProfileName.textProperty().addListener(txtProfileNameChangeListener); } private void onSave() { + locationProperty().removeListener(locationChangeListener); + txtProfileName.textProperty().removeListener(txtProfileNameChangeListener); + if (profile != null) { profile.setName(txtProfileName.getText()); profile.setUseRelativePath(toggleUseRelativePath.isSelected());