From 3f943516738ae70ee5a84ab49fdd6ee28da40a72 Mon Sep 17 00:00:00 2001 From: NWMA_FYWF Date: Sat, 2 May 2026 10:39:17 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E5=88=97=E8=A1=A8=E5=8F=B3=E9=94=AE=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20(#6000)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实例列表项支持右键编辑 - 编辑页面名称唯一性验证排除自身 - 路径变化后不再自动修改名称 --- .../hmcl/ui/profile/ProfileListItem.java | 11 ++++- .../hmcl/ui/profile/ProfileListPopupMenu.java | 48 +++++++++++++++++++ .../hmcl/ui/profile/ProfilePage.java | 16 +++++-- 3 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListPopupMenu.java 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..cf2848a518 --- /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"); + } +} \ No newline at end of file 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()); From 856bc3efc72846ff763f0a8a2536691459969a98 Mon Sep 17 00:00:00 2001 From: NWMA_FYWF Date: Sat, 2 May 2026 10:57:25 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AE=9E=E4=BE=8B?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E7=9B=B8=E5=85=B3=E9=97=AE=E9=A2=98=20(#6000?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jackhuang/hmcl/ui/profile/ProfileListPopupMenu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index cf2848a518..53824e4caa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListPopupMenu.java @@ -45,4 +45,4 @@ public static void show(Node owner, Profile profile) { public ProfileListPopupMenu() { getStyleClass().add("popup-menu-content"); } -} \ No newline at end of file +}