-
Notifications
You must be signed in to change notification settings - Fork 814
feat:优化世界管理界面和世界信息界面 #4823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat:优化世界管理界面和世界信息界面 #4823
Changes from 72 commits
a02e44b
6c85f9d
cc099b0
85777e4
eb07b8f
6f7a53a
bec4e83
a005ec7
c6ecfc5
aa1be7d
34adf97
32905b3
f5d19d2
34b43cd
28c2a2e
90235be
32e10c7
2d20e7d
66ec94a
81fca70
0ae56a6
836bbed
ae070c7
345f2d6
af00011
7f97f55
88a2c6c
e6ff6c1
48e76e4
765e537
797b412
fc3727a
c6cb77c
2fc124a
32c231b
98d77b7
933f8f3
b69e4c7
85008ab
987e032
d34ca49
d1be359
98ee173
9864b08
742bc19
03a999b
d3efcc0
375ac4d
238a1cb
a1e5d62
e2eb62c
7641e2a
8578cfa
a99902c
26d6163
123dcd9
afa7ad7
3da183a
29daf82
9723e1a
effebb8
d85eb63
a9a9e57
8dbd78e
23f879b
0f9e1bd
6d97a71
c196ed5
5259759
d6a4582
3e2114e
834960a
82a5f36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ | |
| import org.jackhuang.hmcl.util.i18n.I18n; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import static org.jackhuang.hmcl.ui.FXUtils.determineOptimalPopupPosition; | ||
| import static org.jackhuang.hmcl.util.StringUtils.parseColorEscapes; | ||
|
|
@@ -143,11 +144,24 @@ public void showPopupMenu(JFXPopup.PopupHPosition hPosition, double initOffsetX, | |
| } | ||
| } | ||
|
|
||
| IconedMenuItem exportMenuItem = new IconedMenuItem(SVG.OUTPUT, i18n("world.export"), item::export, popup); | ||
| IconedMenuItem deleteMenuItem = new IconedMenuItem(SVG.DELETE, i18n("world.delete"), item::delete, popup); | ||
| IconedMenuItem duplicateMenuItem = new IconedMenuItem(SVG.CONTENT_COPY, i18n("world.duplicate"), item::copy, popup); | ||
| boolean worldLocked = world.isLocked(); | ||
| Stream.of(exportMenuItem, deleteMenuItem, duplicateMenuItem) | ||
| .forEach(iconedMenuItem -> iconedMenuItem.setDisable(worldLocked)); | ||
|
|
||
| popupMenu.getContent().addAll( | ||
| new MenuSeparator(), | ||
| exportMenuItem, | ||
| deleteMenuItem, | ||
| duplicateMenuItem | ||
| ); | ||
|
|
||
| popupMenu.getContent().addAll( | ||
| new MenuSeparator(), | ||
| new IconedMenuItem(SVG.OUTPUT, i18n("world.export"), item::export, popup), | ||
| new IconedMenuItem(SVG.DELETE, i18n("world.delete"), item::delete, popup), | ||
| new IconedMenuItem(SVG.FOLDER_OPEN, i18n("folder.world"), item::reveal, popup)); | ||
| new IconedMenuItem(SVG.FOLDER_OPEN, i18n("folder.world"), item::reveal, popup) | ||
| ); | ||
Mine-diamond marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+154
to
+164
|
||
|
|
||
| JFXPopup.PopupVPosition vPosition = determineOptimalPopupPosition(root, popup); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |||||||||
| package org.jackhuang.hmcl.ui.versions; | ||||||||||
|
|
||||||||||
| import com.jfoenix.controls.JFXPopup; | ||||||||||
| import javafx.application.Platform; | ||||||||||
| import javafx.beans.property.ObjectProperty; | ||||||||||
| import javafx.beans.property.ReadOnlyObjectProperty; | ||||||||||
| import javafx.beans.property.SimpleObjectProperty; | ||||||||||
|
|
@@ -27,13 +28,15 @@ | |||||||||
| import javafx.scene.layout.VBox; | ||||||||||
| import org.jackhuang.hmcl.game.World; | ||||||||||
| import org.jackhuang.hmcl.setting.Profile; | ||||||||||
| import org.jackhuang.hmcl.ui.Controllers; | ||||||||||
| import org.jackhuang.hmcl.ui.FXUtils; | ||||||||||
| import org.jackhuang.hmcl.ui.SVG; | ||||||||||
| import org.jackhuang.hmcl.ui.animation.TransitionPane; | ||||||||||
| import org.jackhuang.hmcl.ui.construct.*; | ||||||||||
| import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage; | ||||||||||
| import org.jackhuang.hmcl.ui.decorator.DecoratorPage; | ||||||||||
| import org.jackhuang.hmcl.util.ChunkBaseApp; | ||||||||||
| import org.jackhuang.hmcl.util.StringUtils; | ||||||||||
|
|
||||||||||
| import java.io.IOException; | ||||||||||
| import java.nio.channels.FileChannel; | ||||||||||
|
|
@@ -53,6 +56,8 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco | |||||||||
| private final Profile profile; | ||||||||||
| private final String id; | ||||||||||
|
|
||||||||||
| private boolean loadFailed = false; | ||||||||||
|
|
||||||||||
| private final TabHeader header; | ||||||||||
| private final TabHeader.Tab<WorldInfoPage> worldInfoTab = new TabHeader.Tab<>("worldInfoPage"); | ||||||||||
| private final TabHeader.Tab<WorldBackupsPage> worldBackupsTab = new TabHeader.Tab<>("worldBackupsPage"); | ||||||||||
|
|
@@ -65,25 +70,25 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco | |||||||||
| public WorldManagePage(World world, Path backupsDir, Profile profile, String id) { | ||||||||||
| this.world = world; | ||||||||||
| this.backupsDir = backupsDir; | ||||||||||
|
|
||||||||||
| this.profile = profile; | ||||||||||
| this.id = id; | ||||||||||
|
|
||||||||||
| sessionLockChannel = WorldManageUIUtils.getSessionLockChannel(world); | ||||||||||
| try { | ||||||||||
| world.reloadLevelDat(); | ||||||||||
| } catch (IOException e) { | ||||||||||
| LOG.warning("Can not load world level.dat of world: " + world.getFile(), e); | ||||||||||
| loadFailed = true; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| 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", StringUtils.parseColorEscapes(world.getWorldName())))); | ||||||||||
| this.header = new TabHeader(transitionPane, worldInfoTab, worldBackupsTab); | ||||||||||
| header.select(worldInfoTab); | ||||||||||
|
|
||||||||||
| // Does it need to be done in the background? | ||||||||||
| try { | ||||||||||
| sessionLockChannel = world.lock(); | ||||||||||
| LOG.info("Acquired lock on world " + world.getFileName()); | ||||||||||
| } catch (IOException ignored) { | ||||||||||
| } | ||||||||||
|
|
||||||||||
| setCenter(transitionPane); | ||||||||||
|
|
||||||||||
| BorderPane left = new BorderPane(); | ||||||||||
|
|
@@ -106,37 +111,81 @@ public WorldManagePage(World world, Path backupsDir, Profile profile, String id) | |||||||||
| AdvancedListBox toolbar = new AdvancedListBox(); | ||||||||||
|
|
||||||||||
| if (world.getGameVersion() != null && world.getGameVersion().isAtLeast("1.20", "23w14a")) { | ||||||||||
| toolbar.addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, this::launch, null); | ||||||||||
| toolbar.addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, this::launch, advancedListItem -> advancedListItem.setDisable(isReadOnly())); | ||||||||||
| toolbar.addNavigationDrawerItem(i18n("version.launch_script"), SVG.SCRIPT, this::generateLaunchScript, null); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (ChunkBaseApp.isSupported(world)) { | ||||||||||
| PopupMenu popupMenu = new PopupMenu(); | ||||||||||
| JFXPopup popup = new JFXPopup(popupMenu); | ||||||||||
| PopupMenu chunkBasePopupMenu = new PopupMenu(); | ||||||||||
| JFXPopup chunkBasePopup = new JFXPopup(chunkBasePopupMenu); | ||||||||||
|
|
||||||||||
| popupMenu.getContent().addAll( | ||||||||||
| new IconedMenuItem(SVG.EXPLORE, i18n("world.chunkbase.seed_map"), () -> ChunkBaseApp.openSeedMap(world), popup), | ||||||||||
| new IconedMenuItem(SVG.VISIBILITY, i18n("world.chunkbase.stronghold"), () -> ChunkBaseApp.openStrongholdFinder(world), popup), | ||||||||||
| new IconedMenuItem(SVG.FORT, i18n("world.chunkbase.nether_fortress"), () -> ChunkBaseApp.openNetherFortressFinder(world), popup) | ||||||||||
|
|
||||||||||
| chunkBasePopupMenu.getContent().addAll( | ||||||||||
| new IconedMenuItem(SVG.EXPLORE, i18n("world.chunkbase.seed_map"), () -> ChunkBaseApp.openSeedMap(world), chunkBasePopup), | ||||||||||
| new IconedMenuItem(SVG.VISIBILITY, i18n("world.chunkbase.stronghold"), () -> ChunkBaseApp.openStrongholdFinder(world), chunkBasePopup), | ||||||||||
| new IconedMenuItem(SVG.FORT, i18n("world.chunkbase.nether_fortress"), () -> ChunkBaseApp.openNetherFortressFinder(world), chunkBasePopup) | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| if (world.getGameVersion() != null && world.getGameVersion().compareTo("1.13") >= 0) { | ||||||||||
| popupMenu.getContent().add( | ||||||||||
| new IconedMenuItem(SVG.LOCATION_CITY, i18n("world.chunkbase.end_city"), () -> ChunkBaseApp.openEndCityFinder(world), popup)); | ||||||||||
| chunkBasePopupMenu.getContent().add( | ||||||||||
| new IconedMenuItem(SVG.LOCATION_CITY, i18n("world.chunkbase.end_city"), () -> ChunkBaseApp.openEndCityFinder(world), chunkBasePopup)); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| toolbar.addNavigationDrawerItem(i18n("world.chunkbase"), SVG.EXPLORE, null, chunkBaseMenuItem -> | ||||||||||
| chunkBaseMenuItem.setOnAction(e -> | ||||||||||
| popup.show(chunkBaseMenuItem, | ||||||||||
| chunkBasePopup.show(chunkBaseMenuItem, | ||||||||||
| JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, | ||||||||||
| chunkBaseMenuItem.getWidth(), 0))); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| toolbar.addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OPEN, () -> FXUtils.openFolder(world.getFile()), null); | ||||||||||
|
|
||||||||||
| { | ||||||||||
| PopupMenu managePopupMenu = new PopupMenu(); | ||||||||||
| JFXPopup managePopup = new JFXPopup(managePopupMenu); | ||||||||||
|
|
||||||||||
| managePopupMenu.getContent().addAll( | ||||||||||
| new IconedMenuItem(SVG.OUTPUT, i18n("world.export"), () -> WorldManageUIUtils.export(world, sessionLockChannel), managePopup), | ||||||||||
| new IconedMenuItem(SVG.DELETE, i18n("world.delete"), () -> WorldManageUIUtils.delete(world, () -> fireEvent(new PageCloseEvent()), sessionLockChannel), managePopup), | ||||||||||
| new IconedMenuItem(SVG.CONTENT_COPY, i18n("world.duplicate"), () -> WorldManageUIUtils.copyWorld(world, null), managePopup) | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| toolbar.addNavigationDrawerItem(i18n("settings.game.management"), SVG.MENU, null, managePopupMenuItem -> | ||||||||||
| { | ||||||||||
| managePopupMenuItem.setOnAction(e -> | ||||||||||
| managePopup.show(managePopupMenuItem, | ||||||||||
| JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, | ||||||||||
| managePopupMenuItem.getWidth(), 0)); | ||||||||||
| managePopupMenuItem.setDisable(isReadOnly()); | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| } | ||||||||||
|
|
||||||||||
| BorderPane.setMargin(toolbar, new Insets(0, 0, 12, 0)); | ||||||||||
| left.setBottom(toolbar); | ||||||||||
|
|
||||||||||
| this.addEventHandler(Navigator.NavigationEvent.EXITED, this::onExited); | ||||||||||
| this.addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onNavigated); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private void onNavigated(Navigator.NavigationEvent event) { | ||||||||||
| if (loadFailed) { | ||||||||||
| Platform.runLater(() -> { | ||||||||||
| fireEvent(new PageCloseEvent()); | ||||||||||
| Controllers.dialog(i18n("world.load.fail"), null, MessageDialogPane.MessageType.ERROR); | ||||||||||
| }); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
| if (sessionLockChannel == null || !sessionLockChannel.isOpen()) { | ||||||||||
| sessionLockChannel = WorldManageUIUtils.getSessionLockChannel(world); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| public void onExited(Navigator.NavigationEvent event) { | ||||||||||
| try { | ||||||||||
| WorldManageUIUtils.closeSessionLockChannel(world, sessionLockChannel); | ||||||||||
| } catch (IOException ignored) { | ||||||||||
|
||||||||||
| } catch (IOException ignored) { | |
| } catch (IOException ignored) { | |
| } finally { | |
| sessionLockChannel = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这都onExited 了,再次打开只会重新new一个而不是再次Navigating,这个说法是错误的
Uh oh!
There was an error while loading. Please reload this page.