diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/AdvancedListBox.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/AdvancedListBox.kt index 158c4711d..f94f9aa0d 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/AdvancedListBox.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/AdvancedListBox.kt @@ -33,6 +33,7 @@ class AdvancedListBox: ScrollPane() { isFitToHeight = true isFitToWidth = true + hbarPolicy = ScrollBarPolicy.NEVER container.spacing = 5.0 container.styleClass += "advanced-list-box-content" @@ -40,13 +41,13 @@ class AdvancedListBox: ScrollPane() { fun add(child: Node): AdvancedListBox { if (child is Pane) { - child.maxWidthProperty().bind(this.widthProperty()) + //child.maxWidthProperty().bind(this.widthProperty()) container.children += child } else { val pane = StackPane() pane.styleClass += "advanced-list-box-item" pane.children.setAll(child) - pane.maxWidthProperty().bind(this.widthProperty()) + //pane.maxWidthProperty().bind(this.widthProperty()) container.children += pane } return this diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Controllers.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Controllers.kt index 971a2c41e..ddea79e8a 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Controllers.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Controllers.kt @@ -20,7 +20,6 @@ package org.jackhuang.hmcl.ui import javafx.fxml.FXMLLoader import javafx.scene.Node import javafx.scene.Scene -import javafx.scene.layout.StackPane import javafx.stage.Stage import org.jackhuang.hmcl.Main diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Decorator.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Decorator.kt index d06e6e053..ccf1332a2 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Decorator.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/Decorator.kt @@ -65,7 +65,9 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva private var isDragging: Boolean = false private var windowDecoratorAnimation: Timeline? = null @FXML lateinit var contentPlaceHolder: StackPane + @FXML lateinit var drawerWrapper: StackPane @FXML lateinit var titleContainer: BorderPane + @FXML lateinit var leftRootPane: BorderPane @FXML lateinit var buttonsContainer: HBox @FXML lateinit var backNavButton: JFXButton @FXML lateinit var refreshNavButton: JFXButton @@ -133,7 +135,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva animationHandler = TransitionHandler(contentPlaceHolder) setOverflowHidden(lookup("#contentPlaceHolderRoot") as Pane) - setOverflowHidden(lookup("#drawerWrapper") as Pane) + setOverflowHidden(drawerWrapper) // init the title hamburger icon drawer.setOnDrawerOpening { diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/IconedItem.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/IconedItem.kt index 90b5debf6..d5a4228ab 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/IconedItem.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/IconedItem.kt @@ -26,4 +26,6 @@ class IconedItem(val icon: Node, val text: String) : RipplerContainer(HBox().apply { children += icon.apply { isMouseTransparent = true } children += Label(text).apply { alignment = Pos.CENTER; isMouseTransparent = true } + style += "-fx-padding: 10 16 10 16; -fx-spacing: 10; -fx-font-size: 14; " + alignment = Pos.CENTER_LEFT }) \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt index f2bfacac2..b84d4bea1 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt @@ -35,7 +35,7 @@ import org.jackhuang.hmcl.ui.download.DownloadWizardProvider class LeftPaneController(private val leftPane: AdvancedListBox) { val versionsPane = VBox() val cboProfiles = JFXComboBox().apply { items.add("Default"); prefWidthProperty().bind(leftPane.widthProperty()) } - val accountItem = VersionListItem("No account", "unknown") + val accountItem = VersionListItem("No Account", "unknown") init { leftPane @@ -64,7 +64,7 @@ class LeftPaneController(private val leftPane: AdvancedListBox) { } Controllers.mainPane.buttonLaunch.setOnMouseClicked { LauncherHelper.launch() } - val listener = ChangeListener { _, _, newValue -> + Settings.selectedAccountProperty.addListener { _, _, newValue -> if (newValue == null) { accountItem.lblVersionName.text = "mojang@mojang.com" accountItem.lblGameVersion.text = "Yggdrasil" @@ -73,8 +73,7 @@ class LeftPaneController(private val leftPane: AdvancedListBox) { accountItem.lblGameVersion.text = accountType(newValue) } } - Settings.selectedAccountProperty.addListener(listener) - listener.changed(null, null, Settings.selectedAccount) + Settings.selectedAccountProperty.fireValueChangedEvent() if (Settings.getAccounts().isEmpty()) Controllers.navigate(AccountsPage()) diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt index 6df569b77..dcd36aa68 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt @@ -18,11 +18,28 @@ package org.jackhuang.hmcl.ui import com.jfoenix.controls.JFXButton +import com.jfoenix.controls.JFXMasonryPane +import javafx.application.Platform import javafx.beans.property.SimpleStringProperty import javafx.beans.property.StringProperty +import javafx.beans.value.ChangeListener import javafx.fxml.FXML +import javafx.scene.Node +import javafx.scene.control.ToggleGroup import javafx.scene.layout.StackPane +import javafx.scene.paint.Paint +import org.jackhuang.hmcl.ProfileChangedEvent +import org.jackhuang.hmcl.ProfileLoadingEvent +import org.jackhuang.hmcl.auth.Account +import org.jackhuang.hmcl.event.EVENT_BUS +import org.jackhuang.hmcl.event.RefreshedVersionsEvent +import org.jackhuang.hmcl.game.LauncherHelper +import org.jackhuang.hmcl.game.Version +import org.jackhuang.hmcl.game.minecraftVersion import org.jackhuang.hmcl.i18n +import org.jackhuang.hmcl.setting.Profile +import org.jackhuang.hmcl.setting.Settings +import org.jackhuang.hmcl.ui.download.DownloadWizardProvider import org.jackhuang.hmcl.ui.wizard.DecoratorPage /** @@ -32,9 +49,70 @@ class MainPage : StackPane(), DecoratorPage { override val titleProperty: StringProperty = SimpleStringProperty(this, "title", i18n("launcher.title.main")) @FXML lateinit var buttonLaunch: JFXButton + @FXML lateinit var masonryPane: JFXMasonryPane init { loadFXML("/assets/fxml/main.fxml") + + //EVENT_BUS.channel() += this::loadVersions + //EVENT_BUS.channel() += this::onProfilesLoading + //EVENT_BUS.channel() += this::onProfileChanged + + //Settings.onProfileLoading() + + //Controllers.decorator.addMenuButton.setOnMouseClicked { + // Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") + //} + //Controllers.decorator.refreshMenuButton.setOnMouseClicked { + // Settings.selectedProfile.repository.refreshVersions() + //} + //buttonLaunch.setOnMouseClicked { LauncherHelper.launch() } } + private fun buildNode(i: Int, profile: Profile, version: String, game: String, group: ToggleGroup): Node { + return VersionItem(i, group).apply { + chkSelected.properties["version"] = version + chkSelected.isSelected = profile.selectedVersion == version + lblGameVersion.text = game + lblVersionName.text = version + btnDelete.setOnMouseClicked { + profile.repository.removeVersionFromDisk(version) + Platform.runLater(this@MainPage::loadVersions) + } + } + } + + fun onProfilesLoading() { + // TODO: Profiles + } + + fun onProfileChanged(event: ProfileChangedEvent) { + val profile = event.value + profile.selectedVersionProperty.addListener { _ -> + versionChanged(profile.selectedVersion) + } + profile.selectedVersionProperty.fireValueChangedEvent() + } + + private fun loadVersions() { + val profile = Settings.selectedProfile + val group = ToggleGroup() + val children = mutableListOf() + var i = 0 + profile.repository.getVersions().forEach { version -> + children += buildNode(++i, profile, version.id, minecraftVersion(profile.repository.getVersionJar(version.id)) ?: "Unknown", group) + } + group.selectedToggleProperty().addListener { _, _, newValue -> + if (newValue != null) + profile.selectedVersion = newValue.properties["version"] as String + } + masonryPane.children.setAll(children) + } + + @Suppress("UNCHECKED_CAST") + fun versionChanged(selectedVersion: String) { + masonryPane.children + .filter { it is RipplerContainer && it.properties["version"] is Pair<*, *> } + .forEach { (it as RipplerContainer).selected = (it.properties["version"] as Pair).first == selectedVersion } + } } \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/RenderedPartlyTexture.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/RenderedPartlyTexture.kt new file mode 100644 index 000000000..29772f3a4 --- /dev/null +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/RenderedPartlyTexture.kt @@ -0,0 +1,68 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2017 huangyuhui + * + * 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 {http://www.gnu.org/licenses/}. + */ +package org.jackhuang.hmcl.ui + +import javafx.scene.canvas.GraphicsContext +import javafx.scene.paint.Color + +object RenderedPartlyTexture { + + fun renderAvatar(ctx: GraphicsContext, skinScale: Int, skinImg: Array>, width: Double) { + val helmBs = width / (1.0 * skinScale) + val headBs = (width - 1.0 * helmBs) / (1.0 * skinScale) + val headOffset = helmBs / 2.0 + + // render head.front + renderRange(ctx, skinImg, head_front_x * skinScale, head_front_y * skinScale, head_front_w * skinScale, head_front_h * skinScale, headOffset, headOffset, headBs) + + // render helm.front + renderRange(ctx, skinImg, helm_front_x * skinScale, helm_front_y * skinScale, helm_front_w * skinScale, helm_front_h * skinScale, 0.0, 0.0, helmBs) + } + + private fun renderRange(ctx: GraphicsContext, img: Array>, x0: Int, y0: Int, w: Int, h: Int, tx0: Double, ty0: Double, bs: Double) { + var x: Int + var y: Int + var tx: Double + var ty: Double + for (dx in 0..w - 1) { + for (dy in 0..h - 1) { + x = x0 + dx + y = y0 + dy + tx = tx0 + bs * dx + ty = ty0 + bs * dy + ctx.fill = img[x][y] + ctx.fillRect(tx, ty, bs, bs) + } + } + } + + /* + * The specification of skin can be found at https://github.com/minotar/skin-spec + */ + + internal val head_front_x = 1 + internal val head_front_y = 1 + internal val head_front_w = 1 + internal val head_front_h = 1 + + internal val helm_front_x = 5 + internal val helm_front_y = 1 + internal val helm_front_w = 1 + internal val helm_front_h = 1 + +} \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionItem.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionItem.kt new file mode 100644 index 000000000..507d0f4ec --- /dev/null +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionItem.kt @@ -0,0 +1,81 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2017 huangyuhui + * + * 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 {http://www.gnu.org/licenses/}. + */ +package org.jackhuang.hmcl.ui + +import com.jfoenix.controls.JFXButton +import com.jfoenix.controls.JFXCheckBox +import com.jfoenix.controls.JFXRadioButton +import com.jfoenix.effects.JFXDepthManager +import javafx.beans.binding.Bindings +import javafx.fxml.FXML +import javafx.scene.control.Label +import javafx.scene.control.ToggleGroup +import javafx.scene.layout.Pane +import javafx.scene.layout.StackPane +import javafx.scene.layout.VBox +import java.util.concurrent.Callable + +class VersionItem(i: Int, group: ToggleGroup) : StackPane() { + @FXML lateinit var icon: Pane + @FXML lateinit var content: VBox + @FXML lateinit var header: StackPane + @FXML lateinit var body: StackPane + @FXML lateinit var btnDelete: JFXButton + @FXML lateinit var lblVersionName: Label + @FXML lateinit var chkSelected: JFXRadioButton + @FXML lateinit var lblGameVersion: Label + + init { + loadFXML("/assets/fxml/version-item.fxml") + + JFXDepthManager.setDepth(this, 1) + + chkSelected.toggleGroup = group + btnDelete.graphic = SVG.delete("white", 15.0, 15.0) + + // create content + val headerColor = getDefaultColor(i % 12) + header.style = "-fx-background-radius: 5 5 0 0; -fx-background-color: " + headerColor + body.minHeight = 50.0 + + // create image view + icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 }, header.boundsInParentProperty(), icon.heightProperty())) + } + + private fun getDefaultColor(i: Int): String { + var color = "#FFFFFF" + when (i) { + 0 -> color = "#8F3F7E" + 1 -> color = "#B5305F" + 2 -> color = "#CE584A" + 3 -> color = "#DB8D5C" + 4 -> color = "#DA854E" + 5 -> color = "#E9AB44" + 6 -> color = "#FEE435" + 7 -> color = "#99C286" + 8 -> color = "#01A05E" + 9 -> color = "#4A8895" + 10 -> color = "#16669B" + 11 -> color = "#2F65A5" + 12 -> color = "#4E6A9C" + else -> { + } + } + return color + } +} \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionListItem.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionListItem.kt index 47ca70a7b..4b8ae6c4a 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionListItem.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionListItem.kt @@ -18,14 +18,18 @@ package org.jackhuang.hmcl.ui import javafx.fxml.FXML +import javafx.scene.control.Button import javafx.scene.control.Label +import javafx.scene.control.Tooltip import javafx.scene.layout.BorderPane +import javafx.scene.layout.StackPane import org.jackhuang.hmcl.setting.VersionSetting -class VersionListItem(val versionName: String, val gameVersion: String) : BorderPane() { +class VersionListItem(val versionName: String, val gameVersion: String) : StackPane() { @FXML lateinit var lblVersionName: Label @FXML lateinit var lblGameVersion: Label + @FXML lateinit var btnSettings: Button private var handler: () -> Unit = {} diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/VersionsPage.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/VersionsPage.kt index 490f5a66f..d04c58c95 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/VersionsPage.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/VersionsPage.kt @@ -28,10 +28,11 @@ import org.jackhuang.hmcl.task.TaskExecutor import org.jackhuang.hmcl.ui.animation.ContainerAnimations import org.jackhuang.hmcl.ui.animation.TransitionHandler import org.jackhuang.hmcl.ui.loadFXML +import org.jackhuang.hmcl.ui.wizard.Refreshable import org.jackhuang.hmcl.ui.wizard.WizardController import org.jackhuang.hmcl.ui.wizard.WizardPage -class VersionsPage(private val controller: WizardController, private val gameVersion: String, private val downloadProvider: DownloadProvider, private val libraryId: String, private val callback: () -> Unit): StackPane(), WizardPage { +class VersionsPage(private val controller: WizardController, private val gameVersion: String, private val downloadProvider: DownloadProvider, private val libraryId: String, private val callback: () -> Unit): StackPane(), WizardPage, Refreshable { @FXML lateinit var list: JFXListView @FXML lateinit var spinner: JFXSpinner @@ -47,6 +48,10 @@ class VersionsPage(private val controller: WizardController, private val gameVer controller.settings[libraryId] = newValue.remoteVersion.selfVersion callback() } + refresh() + } + + override fun refresh() { executor = versionList.refreshAsync(downloadProvider).subscribe(Scheduler.JAVAFX) { val versions = ArrayList(versionList.getVersions(gameVersion)) versions.sortWith(RemoteVersion) diff --git a/HMCL/src/main/resources/assets/css/jfoenix-main-demo.css b/HMCL/src/main/resources/assets/css/jfoenix-main-demo.css index 98bf34ad6..efc8bef3c 100644 --- a/HMCL/src/main/resources/assets/css/jfoenix-main-demo.css +++ b/HMCL/src/main/resources/assets/css/jfoenix-main-demo.css @@ -25,10 +25,7 @@ -fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87); } -.rippler-container HBox { - -fx-font-size: 14px; - -fx-padding: 10 16 10 16; - -fx-spacing: 10; +.rippler-container { } .class-title { @@ -704,9 +701,14 @@ } .toggle-icon4 { - -fx-pref-width: 5px; + -fx-toggle-icon4-size: 30px; + -fx-pref-width: -fx-toggle-icon4-size; + -fx-max-width: -fx-toggle-icon4-size; + -fx-min-width: -fx-toggle-icon4-size; + -fx-pref-height: -fx-toggle-icon4-size; + -fx-max-height: -fx-toggle-icon4-size; + -fx-min-height: -fx-toggle-icon4-size; -fx-background-radius: 50px; - -fx-pref-height: 5px; -fx-background-color: transparent; -jfx-toggle-color: rgba(128, 128, 255, 0.2); -jfx-untoggle-color: transparent; diff --git a/HMCL/src/main/resources/assets/fxml/account-item.fxml b/HMCL/src/main/resources/assets/fxml/account-item.fxml index 4987018cb..f0e2cdbcd 100644 --- a/HMCL/src/main/resources/assets/fxml/account-item.fxml +++ b/HMCL/src/main/resources/assets/fxml/account-item.fxml @@ -12,22 +12,25 @@ xmlns:fx="http://javafx.com/fxml" type="StackPane"> - + - +
- - + +
- +
diff --git a/HMCL/src/main/resources/assets/fxml/account.fxml b/HMCL/src/main/resources/assets/fxml/account.fxml index 2459c586d..6b2ec1d61 100644 --- a/HMCL/src/main/resources/assets/fxml/account.fxml +++ b/HMCL/src/main/resources/assets/fxml/account.fxml @@ -13,7 +13,6 @@ - + + @@ -16,62 +18,64 @@ - +
- - - - - - - - - - - -
- -
- -
- - - - - - - - - - - - - - - - - - -
-
- - - -
-
-
-
- - - - - - + + + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + +
+
+ + + +
-
-
-
-
+ +
+ + + + + + + + +
+ + +
- + @@ -103,9 +107,8 @@
- - + + @@ -115,23 +118,15 @@ - + - - - - + - - - diff --git a/HMCL/src/main/resources/assets/fxml/main.fxml b/HMCL/src/main/resources/assets/fxml/main.fxml index 279e43226..91b1bfe6f 100644 --- a/HMCL/src/main/resources/assets/fxml/main.fxml +++ b/HMCL/src/main/resources/assets/fxml/main.fxml @@ -2,24 +2,30 @@ + + -
- -
- - - - +
+ +
+ + + + - - - - +
+
+
+
+
diff --git a/HMCL/src/main/resources/assets/fxml/version-item.fxml b/HMCL/src/main/resources/assets/fxml/version-item.fxml new file mode 100644 index 000000000..97b97c6d7 --- /dev/null +++ b/HMCL/src/main/resources/assets/fxml/version-item.fxml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+ + + +
+ + + + + + + + +
diff --git a/HMCL/src/main/resources/assets/fxml/version-list-item.fxml b/HMCL/src/main/resources/assets/fxml/version-list-item.fxml index 26c301af1..ebaf550df 100644 --- a/HMCL/src/main/resources/assets/fxml/version-list-item.fxml +++ b/HMCL/src/main/resources/assets/fxml/version-list-item.fxml @@ -9,25 +9,32 @@ - - - - - - - - - - - - - - - - - - + style="-fx-padding: 10 16 10 16;" + type="StackPane" pickOnBounds="false"> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HMCL/src/main/resources/assets/img/background.jpg b/HMCL/src/main/resources/assets/img/background.jpg new file mode 100644 index 000000000..bc7389d52 Binary files /dev/null and b/HMCL/src/main/resources/assets/img/background.jpg differ