feat: sidebar with modpack, settings and bottom-left account; server name title

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
layue13andClaude Sonnet 5.5 committed 2026-11-08 14:13:03 +08:00
1 parent 84b6d50421
commit 0531d0cbc2
8 files changed
+25 -42

No files matched your search

@@ -148,4 +148,11 @@ public final class Metadata {
return System.getProperty("hmcl.server.address", "").trim();
}
/// The server name shown in the title bar.
///
/// Can be set with the `hmcl.server.name` system property; empty by default.
public static String getServerName() {
return System.getProperty("hmcl.server.name", "").trim();
}
}
@@ -88,31 +88,12 @@ public final class Controllers {
/// Returns the shared launcher-title property.
///
/// For official builds, first access checks integrity, asynchronously if no result is cached.
/// Failed verification changes the title to indicate a modified build; asynchronous results
/// are applied on the JavaFX application thread.
/// The title is the configured server name, or the generic launcher name when none is set.
public static ReadOnlyStringProperty launcherTitleProperty() {
if (launcherTitle == null) {
launcherTitle = new SimpleStringProperty(Controllers.class, "launcherTitle", Metadata.OFFICIAL_BUILD
? i18n("launcher.title", Metadata.VERSION)
: i18n("launcher.title.unofficial", Metadata.VERSION));
if (Metadata.OFFICIAL_BUILD) {
@Nullable Boolean selfVerified = IntegrityChecker.getSelfVerified();
if (selfVerified != null) {
if (!selfVerified) {
launcherTitle.set(i18n("launcher.title.modified", Metadata.VERSION));
}
} else {
Schedulers.io().execute(() -> {
if (!IntegrityChecker.isSelfVerified()) {
FXUtils.runInFX(() -> {
//noinspection DataFlowIssue
launcherTitle.set(i18n("launcher.title.modified", Metadata.VERSION));
});
}
});
}
}
String serverName = Metadata.getServerName();
launcherTitle = new SimpleStringProperty(Controllers.class, "launcherTitle",
serverName.isEmpty() ? i18n("launcher.name") : serverName);
}
return launcherTitle;
@@ -40,7 +40,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
/// Creates the home page and its navigation entries.
public RootPage() {
getStyleClass().remove("gray-background");
getLeft().getStyleClass().add("gray-background");
getLeft().getStyleClass().add("home-sidebar");
// first item in left sidebar
AccountAdvancedListItem accountListItem = new AccountAdvancedListItem();
@@ -63,23 +63,11 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
@Nullable GameInstanceID currentId = currentGame != null ? currentGame.getId() : null;
return Lang.indexWhere(list, instance -> instance.getId().equals(currentId));
}, instance -> instance.getRepository().setSelectedInstance(instance));
FXUtils.onSecondaryButtonClicked(gameInstanceItem, () -> showGameListPopupMenu(gameInstanceItem));
if (AnimationUtils.isAnimationEnabled()) {
gameInstanceItem.setPrepareAction(Controllers::prepareGameInstancePage);
}
// third item in left sidebar
AdvancedListItem gameListItem = new AdvancedListItem();
gameListItem.setLeftIcon(SVG.FORMAT_LIST_BULLETED);
gameListItem.setTitle(i18n("instance.manage"));
gameListItem.setOnAction(e -> Controllers.navigate(Controllers.getGameListPage()));
FXUtils.onSecondaryButtonClicked(gameListItem, () -> showGameListPopupMenu(gameListItem));
if (AnimationUtils.isAnimationEnabled()) {
gameListItem.setPrepareAction(Controllers::prepareGameListPage);
}
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
launcherSettingsItem.setLeftIcon(SVG.SETTINGS);
launcherSettingsItem.setTitle(i18n("settings"));
@@ -92,15 +80,11 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
launcherSettingsItem.setPrepareAction(Controllers::prepareSettingsPage);
}
// the left sidebar
// The sidebar holds the modpack and settings entries; the account sits at the bottom-left.
getSidebar()
.startCategory(i18n("account").toUpperCase(Locale.ROOT))
.add(accountListItem)
.startCategory(i18n("instance").toUpperCase(Locale.ROOT))
.add(gameInstanceItem)
.add(gameListItem)
.startCategory(i18n("settings.launcher.general").toUpperCase(Locale.ROOT))
.add(launcherSettingsItem);
setSidebarBottom(accountListItem);
// the root page, with the sidebar in left, navigator in center.
setContent(getMainPage());
@@ -2246,3 +2246,10 @@
-fx-stroke: -monet-primary;
-fx-stroke-width: 1.5px;
}
/* Sidebar: a slightly darker panel with a hairline on its right edge. */
.home-sidebar {
-fx-background-color: -monet-surface-container;
-fx-border-color: transparent -monet-outline-variant transparent transparent;
-fx-border-width: 0 1px 0 0;
}
@@ -797,6 +797,7 @@ home.subtitle=One click syncs the modpack, prepares Java and connects you to the
home.pack=Modpack
home.route=Route
home.not_installed=Not installed
launcher.name=Server Launcher
server.join=Join Server
instance.launch_and_enter_world=Play World
instance.launch.empty=Start Game
@@ -617,6 +617,7 @@ home.subtitle=一點即同步、備器、入服。
home.pack=整合包
home.route=路
home.not_installed=未裝
launcher.name=服啟
server.join=入服
# instance.launch_and_enter_world=
instance.launch.empty=啟戲
@@ -620,6 +620,7 @@ home.subtitle=点一下就会同步整合包、准备好 Java,并直接连上
home.pack=整合包
home.route=线路
home.not_installed=尚未安装
launcher.name=服务器启动器
server.join=进入服务器
instance.launch_and_enter_world=进入世界
instance.launch.empty=开始游戏
@@ -614,6 +614,7 @@ home.subtitle=點一下就會同步整合包、準備好 Java,並直接連上
home.pack=整合包
home.route=線路
home.not_installed=尚未安裝
launcher.name=伺服器啟動器
server.join=進入伺服器
instance.launch_and_enter_world=進入世界
instance.launch.empty=開始遊戲