修复 TabHeader 相关动画问题 (#4822)
This commit is contained in:
@@ -26,7 +26,6 @@ import javafx.scene.layout.Pane;
|
|||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.construct.TabHeader;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class TransitionPane extends StackPane {
|
public class TransitionPane extends StackPane {
|
||||||
@@ -41,17 +40,6 @@ public class TransitionPane extends StackPane {
|
|||||||
return currentNode;
|
return currentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindTabHeader(TabHeader tabHeader) {
|
|
||||||
this.setContent(tabHeader.getSelectionModel().getSelectedItem().getNode(), ContainerAnimations.NONE);
|
|
||||||
FXUtils.onChange(tabHeader.getSelectionModel().selectedItemProperty(), newValue -> {
|
|
||||||
this.setContent(newValue.getNode(),
|
|
||||||
ContainerAnimations.SLIDE_UP_FADE_IN,
|
|
||||||
Motion.MEDIUM4,
|
|
||||||
Motion.EASE_IN_OUT_CUBIC_EMPHASIZED
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setContent(Node newView, AnimationProducer transition) {
|
public final void setContent(Node newView, AnimationProducer transition) {
|
||||||
setContent(newView, transition, Motion.SHORT4);
|
setContent(newView, transition, Motion.SHORT4);
|
||||||
}
|
}
|
||||||
@@ -66,6 +54,7 @@ public class TransitionPane extends StackPane {
|
|||||||
currentNode = newView;
|
currentNode = newView;
|
||||||
|
|
||||||
if (!AnimationUtils.isAnimationEnabled() || previousNode == null || transition == ContainerAnimations.NONE) {
|
if (!AnimationUtils.isAnimationEnabled() || previousNode == null || transition == ContainerAnimations.NONE) {
|
||||||
|
AnimationUtils.reset(newView, true);
|
||||||
getChildren().setAll(newView);
|
getChildren().setAll(newView);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,24 @@ import javafx.scene.transform.Rotate;
|
|||||||
import javafx.scene.transform.Scale;
|
import javafx.scene.transform.Scale;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.Motion;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class TabHeader extends Control implements TabControl, PageAware {
|
public class TabHeader extends Control implements TabControl, PageAware {
|
||||||
|
|
||||||
|
private final TransitionPane contentPane;
|
||||||
|
|
||||||
public TabHeader(Tab<?>... tabs) {
|
public TabHeader(Tab<?>... tabs) {
|
||||||
|
this(null, tabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabHeader(@Nullable TransitionPane contentPane, Tab<?>... tabs) {
|
||||||
|
this.contentPane = contentPane;
|
||||||
|
|
||||||
getStyleClass().setAll("tab-header");
|
getStyleClass().setAll("tab-header");
|
||||||
if (tabs != null) {
|
if (tabs != null) {
|
||||||
getTabs().addAll(tabs);
|
getTabs().addAll(tabs);
|
||||||
@@ -49,41 +61,48 @@ public class TabHeader extends Control implements TabControl, PageAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final ObservableList<Tab<?>> tabs = FXCollections.observableArrayList();
|
private final ObservableList<Tab<?>> tabs = FXCollections.observableArrayList();
|
||||||
private final ObjectProperty<Side> side = new SimpleObjectProperty<>(Side.TOP);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObservableList<Tab<?>> getTabs() {
|
public ObservableList<Tab<?>> getTabs() {
|
||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ObjectProperty<SingleSelectionModel<Tab<?>>> selectionModel = new SimpleObjectProperty<>(this, "selectionModel", new TabControlSelectionModel(this));
|
private final SingleSelectionModel<Tab<?>> selectionModel = new TabControlSelectionModel(this);
|
||||||
|
|
||||||
public SingleSelectionModel<Tab<?>> getSelectionModel() {
|
public SingleSelectionModel<Tab<?>> getSelectionModel() {
|
||||||
return selectionModel.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<SingleSelectionModel<Tab<?>>> selectionModelProperty() {
|
|
||||||
return selectionModel;
|
return selectionModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectionModel(SingleSelectionModel<Tab<?>> selectionModel) {
|
public void select(Tab<?> tab) {
|
||||||
this.selectionModel.set(selectionModel);
|
select(tab, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void select(Tab<?> tab) {
|
public void select(Tab<?> tab, boolean playAnimation) {
|
||||||
Tab<?> oldTab = getSelectionModel().getSelectedItem();
|
Tab<?> oldTab = getSelectionModel().getSelectedItem();
|
||||||
if (oldTab != null) {
|
if (oldTab != null) {
|
||||||
if (oldTab.getNode() instanceof PageAware) {
|
if (oldTab.getNode() instanceof PageAware pageAware) {
|
||||||
((PageAware) oldTab.getNode()).onPageHidden();
|
pageAware.onPageHidden();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.initializeIfNeeded();
|
tab.initializeIfNeeded();
|
||||||
if (tab.getNode() instanceof PageAware) {
|
if (tab.getNode() instanceof PageAware pageAware) {
|
||||||
((PageAware) tab.getNode()).onPageShown();
|
pageAware.onPageShown();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectionModel().select(tab);
|
getSelectionModel().select(tab);
|
||||||
|
|
||||||
|
if (contentPane != null) {
|
||||||
|
if (playAnimation && contentPane.getCurrentNode() != null) {
|
||||||
|
contentPane.setContent(tab.getNode(),
|
||||||
|
ContainerAnimations.SLIDE_UP_FADE_IN,
|
||||||
|
Motion.MEDIUM4,
|
||||||
|
Motion.EASE_IN_OUT_CUBIC_EMPHASIZED
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
contentPane.setContent(tab.getNode(), ContainerAnimations.NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -106,12 +125,7 @@ public class TabHeader extends Control implements TabControl, PageAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private final ObjectProperty<Side> side = new SimpleObjectProperty<>(Side.TOP);
|
||||||
* The position to place the tabs.
|
|
||||||
*/
|
|
||||||
public Side getSide() {
|
|
||||||
return side.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The position of the tabs.
|
* The position of the tabs.
|
||||||
@@ -120,6 +134,13 @@ public class TabHeader extends Control implements TabControl, PageAware {
|
|||||||
return side;
|
return side;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The position to place the tabs.
|
||||||
|
*/
|
||||||
|
public Side getSide() {
|
||||||
|
return side.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The position the place the tabs in this TabHeader.
|
* The position the place the tabs in this TabHeader.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
|||||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||||
import org.jackhuang.hmcl.ui.construct.TabControl;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.TabHeader;
|
import org.jackhuang.hmcl.ui.construct.TabHeader;
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
|
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||||
@@ -99,12 +98,11 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
|||||||
resourcePackTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofResourcePack((profile, version, file) -> download(profile, version, file, "resourcepacks"), true)));
|
resourcePackTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofResourcePack((profile, version, file) -> download(profile, version, file, "resourcepacks"), true)));
|
||||||
shaderTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(ModrinthRemoteModRepository.SHADER_PACKS, (profile, version, file) -> download(profile, version, file, "shaderpacks"), true)));
|
shaderTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(ModrinthRemoteModRepository.SHADER_PACKS, (profile, version, file) -> download(profile, version, file, "shaderpacks"), true)));
|
||||||
worldTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(CurseForgeRemoteModRepository.WORLDS)));
|
worldTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(CurseForgeRemoteModRepository.WORLDS)));
|
||||||
tab = new TabHeader(newGameTab, modpackTab, modTab, resourcePackTab, shaderTab, worldTab);
|
tab = new TabHeader(transitionPane, newGameTab, modpackTab, modTab, resourcePackTab, shaderTab, worldTab);
|
||||||
|
|
||||||
Profiles.registerVersionsListener(this::loadVersions);
|
Profiles.registerVersionsListener(this::loadVersions);
|
||||||
|
|
||||||
tab.select(newGameTab);
|
tab.select(newGameTab);
|
||||||
transitionPane.bindTabHeader(tab);
|
|
||||||
|
|
||||||
AdvancedListBox sideBar = new AdvancedListBox()
|
AdvancedListBox sideBar = new AdvancedListBox()
|
||||||
.startCategory(i18n("download.game").toUpperCase(Locale.ROOT))
|
.startCategory(i18n("download.game").toUpperCase(Locale.ROOT))
|
||||||
@@ -121,13 +119,6 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
|||||||
setCenter(transitionPane);
|
setCenter(transitionPane);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectTabIfCurseForgeAvailable(TabControl.Tab<?> newTab) {
|
|
||||||
if (CurseForgeRemoteModRepository.isAvailable())
|
|
||||||
tab.select(newTab);
|
|
||||||
else
|
|
||||||
Controllers.dialog(i18n("download.curseforge.unavailable"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T extends Node> Supplier<T> loadVersionFor(Supplier<T> nodeSupplier) {
|
private static <T extends Node> Supplier<T> loadVersionFor(Supplier<T> nodeSupplier) {
|
||||||
return () -> {
|
return () -> {
|
||||||
T node = nodeSupplier.get();
|
T node = nodeSupplier.get();
|
||||||
@@ -202,20 +193,20 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showGameDownloads() {
|
public void showGameDownloads() {
|
||||||
tab.select(newGameTab);
|
tab.select(newGameTab, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showModpackDownloads() {
|
public void showModpackDownloads() {
|
||||||
tab.select(modpackTab);
|
tab.select(modpackTab, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadListPage showModDownloads() {
|
public DownloadListPage showModDownloads() {
|
||||||
tab.select(modTab);
|
tab.select(modTab, false);
|
||||||
return modTab.getNode();
|
return modTab.getNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showWorldDownloads() {
|
public void showWorldDownloads() {
|
||||||
tab.select(worldTab);
|
tab.select(worldTab, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class DownloadNavigator implements Navigation {
|
private static final class DownloadNavigator implements Navigation {
|
||||||
|
|||||||
@@ -55,11 +55,10 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
|||||||
helpTab.setNodeSupplier(HelpPage::new);
|
helpTab.setNodeSupplier(HelpPage::new);
|
||||||
feedbackTab.setNodeSupplier(FeedbackPage::new);
|
feedbackTab.setNodeSupplier(FeedbackPage::new);
|
||||||
aboutTab.setNodeSupplier(AboutPage::new);
|
aboutTab.setNodeSupplier(AboutPage::new);
|
||||||
tab = new TabHeader(gameTab, javaManagementTab, settingsTab, personalizationTab, downloadTab, helpTab, feedbackTab, aboutTab);
|
tab = new TabHeader(transitionPane, gameTab, javaManagementTab, settingsTab, personalizationTab, downloadTab, helpTab, feedbackTab, aboutTab);
|
||||||
|
|
||||||
tab.select(gameTab);
|
tab.select(gameTab);
|
||||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, event -> gameTab.getNode().loadVersion(Profiles.getSelectedProfile(), null));
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, event -> gameTab.getNode().loadVersion(Profiles.getSelectedProfile(), null));
|
||||||
transitionPane.bindTabHeader(tab);
|
|
||||||
|
|
||||||
AdvancedListBox sideBar = new AdvancedListBox()
|
AdvancedListBox sideBar = new AdvancedListBox()
|
||||||
.addNavigationDrawerTab(tab, gameTab, i18n("settings.type.global.manage"), SVG.STADIA_CONTROLLER, SVG.STADIA_CONTROLLER_FILL)
|
.addNavigationDrawerTab(tab, gameTab, i18n("settings.type.global.manage"), SVG.STADIA_CONTROLLER, SVG.STADIA_CONTROLLER_FILL)
|
||||||
@@ -90,11 +89,11 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
|||||||
|
|
||||||
public void showGameSettings(Profile profile) {
|
public void showGameSettings(Profile profile) {
|
||||||
gameTab.getNode().loadVersion(profile, null);
|
gameTab.getNode().loadVersion(profile, null);
|
||||||
tab.select(gameTab);
|
tab.select(gameTab, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showFeedback() {
|
public void showFeedback() {
|
||||||
tab.select(feedbackTab);
|
tab.select(feedbackTab, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -56,9 +56,8 @@ public class TerracottaPage extends DecoratorAnimatedPage implements DecoratorPa
|
|||||||
|
|
||||||
public TerracottaPage() {
|
public TerracottaPage() {
|
||||||
statusPage.setNodeSupplier(TerracottaControllerPage::new);
|
statusPage.setNodeSupplier(TerracottaControllerPage::new);
|
||||||
tab = new TabHeader(statusPage);
|
tab = new TabHeader(transitionPane, statusPage);
|
||||||
tab.select(statusPage);
|
tab.select(statusPage);
|
||||||
transitionPane.bindTabHeader(tab);
|
|
||||||
|
|
||||||
BorderPane left = new BorderPane();
|
BorderPane left = new BorderPane();
|
||||||
FXUtils.setLimitWidth(left, 200);
|
FXUtils.setLimitWidth(left, 200);
|
||||||
|
|||||||
@@ -71,13 +71,11 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
|||||||
worldListTab.setNodeSupplier(loadVersionFor(WorldListPage::new));
|
worldListTab.setNodeSupplier(loadVersionFor(WorldListPage::new));
|
||||||
schematicsTab.setNodeSupplier(loadVersionFor(SchematicsPage::new));
|
schematicsTab.setNodeSupplier(loadVersionFor(SchematicsPage::new));
|
||||||
|
|
||||||
tab = new TabHeader(versionSettingsTab, installerListTab, modListTab, worldListTab, schematicsTab);
|
tab = new TabHeader(transitionPane, versionSettingsTab, installerListTab, modListTab, worldListTab, schematicsTab);
|
||||||
|
tab.select(versionSettingsTab);
|
||||||
|
|
||||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onNavigated);
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onNavigated);
|
||||||
|
|
||||||
tab.select(versionSettingsTab);
|
|
||||||
transitionPane.bindTabHeader(tab);
|
|
||||||
|
|
||||||
listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST));
|
listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,15 +64,13 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
|
|||||||
this.world = world;
|
this.world = world;
|
||||||
this.backupsDir = backupsDir;
|
this.backupsDir = backupsDir;
|
||||||
|
|
||||||
|
this.worldInfoTab.setNodeSupplier(() -> new WorldInfoPage(this));
|
||||||
|
this.worldBackupsTab.setNodeSupplier(() -> new WorldBackupsPage(this));
|
||||||
|
this.datapackTab.setNodeSupplier(() -> new DatapackListPage(this));
|
||||||
|
|
||||||
this.state = new SimpleObjectProperty<>(State.fromTitle(i18n("world.manage.title", world.getWorldName())));
|
this.state = new SimpleObjectProperty<>(State.fromTitle(i18n("world.manage.title", world.getWorldName())));
|
||||||
this.header = new TabHeader(worldInfoTab, worldBackupsTab);
|
this.header = new TabHeader(transitionPane, worldInfoTab, worldBackupsTab);
|
||||||
|
|
||||||
worldInfoTab.setNodeSupplier(() -> new WorldInfoPage(this));
|
|
||||||
worldBackupsTab.setNodeSupplier(() -> new WorldBackupsPage(this));
|
|
||||||
datapackTab.setNodeSupplier(() -> new DatapackListPage(this));
|
|
||||||
|
|
||||||
header.select(worldInfoTab);
|
header.select(worldInfoTab);
|
||||||
transitionPane.bindTabHeader(header);
|
|
||||||
|
|
||||||
setCenter(transitionPane);
|
setCenter(transitionPane);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user