Merge 56f6e88ce8ea2763ce72e0fb747aba74b99196d8 into 9969dc60c5278340b6b9a4d7facdde620e99d1f5

This commit is contained in:
Zkitefly 2025-08-02 22:32:15 +08:00 committed by GitHub
commit 0520094757
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 3 deletions

View File

@ -17,15 +17,21 @@
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXTextField;
import javafx.animation.PauseTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
@ -40,6 +46,8 @@ import org.jackhuang.hmcl.util.javafx.MappedObservableList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
@ -54,6 +62,8 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
private final ObjectProperty<Profile> selectedProfile;
private ToggleGroup toggleGroup;
private final TextField searchField;
private final GameList gameList;
public GameListPage() {
profileListItems = MappedObservableList.create(profilesProperty(), profile -> {
@ -63,7 +73,36 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
});
selectedProfile = createSelectedItemPropertyFor(profileListItems, Profile.class);
GameList gameList = new GameList();
gameList = new GameList();
HBox searchBar = new HBox();
searchBar.setPadding(new Insets(12, 10, 0, 10));
searchField = new JFXTextField();
searchField.setPromptText(i18n("search.hint.regex"));
HBox.setHgrow(searchField, Priority.ALWAYS);
PauseTransition pause = new PauseTransition(Duration.millis(100));
pause.setOnFinished(e -> gameList.filter(searchField.getText()));
searchField.textProperty().addListener((obs, oldText, newText) -> {
pause.setRate(1);
pause.playFromStart();
});
searchBar.getChildren().setAll(searchField);
VBox centerBox = new VBox();
if (gameList.getItems().isEmpty() && searchField.getText().isEmpty()) {
setCenter(gameList);
} else {
centerBox.getChildren().setAll(searchBar, gameList);
setCenter(centerBox);
}
gameList.itemsProperty().addListener((obs, oldItems, newItems) -> {
if (newItems.isEmpty() && searchField.getText().isEmpty()) {
setCenter(gameList);
} else {
centerBox.getChildren().setAll(searchBar, gameList);
setCenter(centerBox);
}
});
{
ScrollPane pane = new ScrollPane();
@ -94,8 +133,6 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
FXUtils.setLimitHeight(bottomLeftCornerList, 40 * 4 + 12 * 2);
setLeft(pane, bottomLeftCornerList);
}
setCenter(gameList);
}
public ObjectProperty<Profile> selectedProfileProperty() {
@ -124,12 +161,17 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
}
private class GameList extends ListPageBase<GameListItem> {
private final ObjectProperty<String> filter = new SimpleObjectProperty<>("");
private final ObservableList<GameListItem> originalItems = FXCollections.observableArrayList();
public GameList() {
super();
Profiles.registerVersionsListener(this::loadVersions);
setOnFailedAction(e -> Controllers.navigate(Controllers.getDownloadPage()));
filter.addListener((obs, oldFilter, newFilter) -> applyFilter(newFilter));
}
private void loadVersions(Profile profile) {
@ -145,10 +187,20 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
List<GameListItem> children = repository.getDisplayVersions()
.map(version -> new GameListItem(toggleGroup, profile, version.getId()))
.collect(Collectors.toList());
originalItems.setAll(children);
itemsProperty().setAll(children);
children.forEach(GameListItem::checkSelection);
if (!center.getChildren().isEmpty()) {
searchField.clear();
}
if (children.isEmpty()) {
if (!center.getChildren().isEmpty()) {
setCenter(gameList);
}
setFailedReason(i18n("version.empty.hint"));
}
@ -173,6 +225,37 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
Profiles.getSelectedProfile().getRepository().refreshVersionsAsync().start();
}
public void filter(String searchText) {
filter.set(searchText);
}
private void applyFilter(String filterText) {
if (filterText.startsWith("regex:")) {
try {
String regex = filterText.substring("regex:".length());
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
List<GameListItem> filteredItems = originalItems.stream()
.filter(item -> pattern.matcher(item.getVersion()).find())
.collect(Collectors.toList());
itemsProperty().setAll(filteredItems);
} catch (PatternSyntaxException e) {
System.err.println("Invalid regex pattern: " + e.getMessage());
}
} else {
String lowerCaseFilterText = filterText.toLowerCase();
if (filterText.isEmpty()) {
itemsProperty().setAll(originalItems);
} else {
List<GameListItem> filteredItems = originalItems.stream()
.filter(item -> item.getVersion().toLowerCase().contains(lowerCaseFilterText))
.collect(Collectors.toList());
itemsProperty().setAll(filteredItems);
}
}
}
@Override
protected GameListSkin createDefaultSkin() {
return new GameListSkin();

View File

@ -1202,6 +1202,7 @@ schematics.manage=Schematics
schematics.sub_items=%d sub-item(s)
search=Search
search.hint.regex=Support regex search (regex:+regular expression)
search.hint.chinese=Search in English and Chinese
search.hint.english=Search in English only
search.enter=Enter text here

View File

@ -1004,6 +1004,7 @@ schematics.manage=原理圖管理
schematics.sub_items=%d 個子項
search=搜尋
search.hint.regex=支援正規表示式搜尋regex:+正規表示式)
search.hint.chinese=支援中英文搜尋
search.hint.english=僅支援英文搜尋
search.enter=請在此處輸入

View File

@ -1014,6 +1014,7 @@ schematics.manage=原理图管理
schematics.sub_items=%d 个子项
search=搜索
search.hint.regex=支持正则表达式搜索regex:+正则表达式)
search.hint.chinese=支持中英文搜索
search.hint.english=仅支持英文搜索
search.enter=可在此处输入