2
0
mirror of https://github.com/HMCL-dev/HMCL.git synced 2025-04-24 18:50:52 +08:00

FXML for decorator

This commit is contained in:
huangyuhui 2017-08-03 23:54:15 +08:00
parent 41498b5d93
commit 736a8c1b30
13 changed files with 336 additions and 322 deletions
HMCL/src/main
HMCLCore/src/main/java/org/jackhuang/hmcl/task

@ -46,9 +46,7 @@ object Controllers {
decorator.isCustomMaximize = false
scene = Scene(decorator, 800.0, 480.0)
scene.stylesheets.addAll(Controllers::class.java.getResource("/css/jfoenix-design.css").toExternalForm(),
Controllers::class.java.getResource("/assets/css/jfoenix-components.css").toExternalForm(),
Controllers::class.java.getResource("/assets/css/jfoenix-main-demo.css").toExternalForm())
scene.stylesheets.addAll(*stylesheets)
stage.minWidth = 800.0
stage.maxWidth = 800.0
stage.maxHeight = 480.0
@ -56,7 +54,7 @@ object Controllers {
}
fun navigate(node: Node?) {
mainController.setContentPage(node)
//mainController.setContentPage(node)
}
private fun <T> loadPane(s: String): T = FXMLLoader(Controllers::class.java.getResource("/assets/fxml/$s.fxml")).load()

@ -25,14 +25,12 @@ import javafx.beans.property.BooleanProperty
import javafx.beans.property.ObjectProperty
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.fxml.FXML
import javafx.geometry.BoundingBox
import javafx.geometry.Bounds
import javafx.geometry.Insets
import javafx.geometry.Pos
import javafx.scene.Cursor
import javafx.scene.Node
import javafx.scene.control.Button
import javafx.scene.control.Label
import javafx.scene.control.Tooltip
import javafx.scene.input.MouseEvent
import javafx.scene.layout.*
@ -41,9 +39,8 @@ import javafx.scene.shape.Rectangle
import javafx.stage.Screen
import javafx.stage.Stage
import javafx.stage.StageStyle
import java.util.ArrayList
class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node: Node, max: Boolean = true, min: Boolean = true) : VBox() {
class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node: Node, private val max: Boolean = true, min: Boolean = true) : VBox() {
private var xOffset: Double = 0.0
private var yOffset: Double = 0.0
private var newX: Double = 0.0
@ -53,126 +50,58 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node:
private var allowMove: Boolean = false
private var isDragging: Boolean = false
private var windowDecoratorAnimation: Timeline? = null
private val contentPlaceHolder: StackPane
private val titleContainer: BorderPane
@FXML lateinit var contentPlaceHolder: StackPane
@FXML lateinit var titleContainer: BorderPane
@FXML lateinit var buttonsContainer: HBox
private val onCloseButtonAction: ObjectProperty<Runnable>
private val customMaximize: BooleanProperty
private var maximized: Boolean = false
private var originalBox: BoundingBox? = null
private var maximizedBox: BoundingBox? = null
private val btnMax: JFXButton
@FXML lateinit var btnMin: JFXButton
@FXML lateinit var btnMax: JFXButton
@FXML lateinit var btnClose: JFXButton
private val minus: SVGGlyph
private val resizeMax: SVGGlyph
private val resizeMin: SVGGlyph
private val close: SVGGlyph
init {
loadFXML("/assets/fxml/decorator.fxml")
this.xOffset = 0.0
this.yOffset = 0.0
this.allowMove = false
this.isDragging = false
this.contentPlaceHolder = StackPane()
this.onCloseButtonAction = SimpleObjectProperty(Runnable { this.primaryStage.close() })
this.customMaximize = SimpleBooleanProperty(false)
this.maximized = false
this.primaryStage.initStyle(StageStyle.UNDECORATED)
this.isPickOnBounds = false
this.styleClass.add("jfx-decorator")
val minus = SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE)
minus = SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE)
minus.setSize(12.0, 2.0)
minus.translateY = 4.0
val resizeMax = SVGGlyph(0, "RESIZE_MAX", "M726 810v-596h-428v596h428zM726 44q34 0 59 25t25 59v768q0 34-25 60t-59 26h-428q-34 0-59-26t-25-60v-768q0-34 25-60t59-26z", Color.WHITE)
resizeMax = SVGGlyph(0, "RESIZE_MAX", "M726 810v-596h-428v596h428zM726 44q34 0 59 25t25 59v768q0 34-25 60t-59 26h-428q-34 0-59-26t-25-60v-768q0-34 25-60t59-26z", Color.WHITE)
resizeMax.setSize(12.0, 12.0)
val resizeMin = SVGGlyph(0, "RESIZE_MIN", "M80.842 943.158v-377.264h565.894v377.264h-565.894zM0 404.21v619.79h727.578v-619.79h-727.578zM377.264 161.684h565.894v377.264h-134.736v80.842h215.578v-619.79h-727.578v323.37h80.842v-161.686z", Color.WHITE)
resizeMin = SVGGlyph(0, "RESIZE_MIN", "M80.842 943.158v-377.264h565.894v377.264h-565.894zM0 404.21v619.79h727.578v-619.79h-727.578zM377.264 161.684h565.894v377.264h-134.736v80.842h215.578v-619.79h-727.578v323.37h80.842v-161.686z", Color.WHITE)
resizeMin.setSize(12.0, 12.0)
val close = SVGGlyph(0, "CLOSE", "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z", Color.WHITE)
close = SVGGlyph(0, "CLOSE", "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z", Color.WHITE)
close.setSize(12.0, 12.0)
val btnClose = JFXButton()
btnClose.styleClass.add("jfx-decorator-button")
btnClose.cursor = Cursor.HAND
btnClose.setOnAction { action -> (this.onCloseButtonAction.get() as Runnable).run() }
btnClose.graphic = close
btnClose.ripplerFill = Color.WHITE
val btnMin = JFXButton()
btnMin.styleClass.add("jfx-decorator-button")
btnMin.cursor = Cursor.HAND
btnMin.setOnAction { action -> this.primaryStage.isIconified = true }
btnMin.graphic = minus
btnMin.ripplerFill = Color.WHITE
this.btnMax = JFXButton()
this.btnMax.styleClass.add("jfx-decorator-button")
this.btnMax.cursor = Cursor.HAND
this.btnMax.ripplerFill = Color.WHITE
this.btnMax.setOnAction { action ->
if (!this.isCustomMaximize) {
this.primaryStage.isMaximized = !this.primaryStage.isMaximized
this.maximized = this.primaryStage.isMaximized
if (this.primaryStage.isMaximized) {
this.btnMax.graphic = resizeMin
this.btnMax.tooltip = Tooltip("Restore Down")
} else {
this.btnMax.graphic = resizeMax
this.btnMax.tooltip = Tooltip("Maximize")
}
} else {
if (!this.maximized) {
this.originalBox = BoundingBox(primaryStage.x, primaryStage.y, primaryStage.width, primaryStage.height)
val screen = Screen.getScreensForRectangle(primaryStage.x, primaryStage.y, primaryStage.width, primaryStage.height)[0] as Screen
val bounds = screen.visualBounds
this.maximizedBox = BoundingBox(bounds.minX, bounds.minY, bounds.width, bounds.height)
primaryStage.x = this.maximizedBox!!.minX
primaryStage.y = this.maximizedBox!!.minY
primaryStage.width = this.maximizedBox!!.width
primaryStage.height = this.maximizedBox!!.height
this.btnMax.graphic = resizeMin
this.btnMax.tooltip = Tooltip("Restore Down")
} else {
primaryStage.x = this.originalBox!!.minX
primaryStage.y = this.originalBox!!.minY
primaryStage.width = this.originalBox!!.width
primaryStage.height = this.originalBox!!.height
this.originalBox = null
this.btnMax.graphic = resizeMax
this.btnMax.tooltip = Tooltip("Maximize")
}
this.maximized = !this.maximized
}
}
this.btnMax.graphic = resizeMax
titleContainer = BorderPane()
titleContainer.styleClass += "jfx-decorator-buttons-container"
titleContainer.isPickOnBounds = false
val titleWrapper = HBox()
titleWrapper.style += "-fx-padding: 15;"
titleWrapper.alignment = Pos.CENTER_LEFT
val title = Label("Hello Minecraft! Launcher")
title.alignment = Pos.CENTER_LEFT
title.style += "--fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 15px;"
title.isMouseTransparent = false
titleWrapper.children.setAll(title)
titleContainer.left = titleWrapper
val buttonsContainer = HBox()
buttonsContainer.styleClass.add("jfx-decorator-buttons-container")
buttonsContainer.background = Background(*arrayOf(BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)))
buttonsContainer.padding = Insets(4.0)
buttonsContainer.alignment = Pos.CENTER_RIGHT
titleContainer.addEventHandler(MouseEvent.MOUSE_CLICKED) { mouseEvent ->
if (mouseEvent.clickCount == 2) {
this.btnMax.fire()
}
}
val btns = ArrayList<Button>()
if (min) {
btns.add(btnMin)
}
if (!min) buttonsContainer.children.remove(btnMin)
if (max) {
btns.add(this.btnMax)
}
if (!max) buttonsContainer.children.remove(btnMax)
btns.add(btnClose)
buttonsContainer.children.addAll(btns)
titleContainer.addEventHandler(MouseEvent.MOUSE_ENTERED) { enter -> this.allowMove = true }
titleContainer.addEventHandler(MouseEvent.MOUSE_EXITED) { enter ->
if (!this.isDragging) {
@ -180,130 +109,174 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node:
}
}
buttonsContainer.minWidth = 180.0
titleContainer.right = buttonsContainer
this.contentPlaceHolder.styleClass.add("jfx-decorator-content-container")
this.contentPlaceHolder.setMinSize(0.0, 0.0)
this.contentPlaceHolder.children.add(node)
(node as Region).setMinSize(0.0, 0.0)
VBox.setVgrow(this.contentPlaceHolder, Priority.ALWAYS)
this.contentPlaceHolder.styleClass.add("resize-border")
this.contentPlaceHolder.border = Border(BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths(0.0, 4.0, 4.0, 4.0)))
val clip = Rectangle()
clip.widthProperty().bind(node.widthProperty())
clip.heightProperty().bind(node.heightProperty())
node.setClip(clip)
this.children.addAll(titleContainer, this.contentPlaceHolder)
this.setOnMouseMoved { mouseEvent ->
if (!this.primaryStage.isMaximized && !this.primaryStage.isFullScreen && !this.maximized) {
if (!this.primaryStage.isResizable) {
this.updateInitMouseValues(mouseEvent)
} else {
val x = mouseEvent.x
val y = mouseEvent.y
val boundsInParent = this.boundsInParent
if (this.contentPlaceHolder.border != null && this.contentPlaceHolder.border.strokes.size > 0) {
val borderWidth = this.contentPlaceHolder.snappedLeftInset()
if (this.isRightEdge(x, y, boundsInParent)) {
if (y < borderWidth) {
this.cursor = Cursor.NE_RESIZE
} else if (y > this.height - borderWidth) {
this.cursor = Cursor.SE_RESIZE
} else {
this.cursor = Cursor.E_RESIZE
}
} else if (this.isLeftEdge(x, y, boundsInParent)) {
if (y < borderWidth) {
this.cursor = Cursor.NW_RESIZE
} else if (y > this.height - borderWidth) {
this.cursor = Cursor.SW_RESIZE
} else {
this.cursor = Cursor.W_RESIZE
}
} else if (this.isTopEdge(x, y, boundsInParent)) {
this.cursor = Cursor.N_RESIZE
} else if (this.isBottomEdge(x, y, boundsInParent)) {
this.cursor = Cursor.S_RESIZE
} else {
this.cursor = Cursor.DEFAULT
}
}
this.updateInitMouseValues(mouseEvent)
}
}
fun onMouseMoved(mouseEvent: MouseEvent) {
if (!this.primaryStage.isMaximized && !this.primaryStage.isFullScreen && !this.maximized) {
if (!this.primaryStage.isResizable) {
this.updateInitMouseValues(mouseEvent)
} else {
this.cursor = Cursor.DEFAULT
}
}
this.setOnMouseReleased { mouseEvent -> this.isDragging = false }
this.setOnMouseDragged { mouseEvent ->
this.isDragging = true
if (mouseEvent.isPrimaryButtonDown && (this.xOffset != -1.0 || this.yOffset != -1.0)) {
if (!this.primaryStage.isFullScreen && !mouseEvent.isStillSincePress && !this.primaryStage.isMaximized && !this.maximized) {
this.newX = mouseEvent.screenX
this.newY = mouseEvent.screenY
val deltax = this.newX - this.initX
val deltay = this.newY - this.initY
val cursor = this.cursor
if (Cursor.E_RESIZE == cursor) {
this.setStageWidth(this.primaryStage.width + deltax)
mouseEvent.consume()
} else if (Cursor.NE_RESIZE == cursor) {
if (this.setStageHeight(this.primaryStage.height - deltay)) {
this.primaryStage.y = this.primaryStage.y + deltay
val x = mouseEvent.x
val y = mouseEvent.y
val boundsInParent = this.boundsInParent
if (this.contentPlaceHolder.border != null && this.contentPlaceHolder.border.strokes.size > 0) {
val borderWidth = this.contentPlaceHolder.snappedLeftInset()
if (this.isRightEdge(x, y, boundsInParent)) {
if (y < borderWidth) {
this.cursor = Cursor.NE_RESIZE
} else if (y > this.height - borderWidth) {
this.cursor = Cursor.SE_RESIZE
} else {
this.cursor = Cursor.E_RESIZE
}
this.setStageWidth(this.primaryStage.width + deltax)
mouseEvent.consume()
} else if (Cursor.SE_RESIZE == cursor) {
this.setStageWidth(this.primaryStage.width + deltax)
this.setStageHeight(this.primaryStage.height + deltay)
mouseEvent.consume()
} else if (Cursor.S_RESIZE == cursor) {
this.setStageHeight(this.primaryStage.height + deltay)
mouseEvent.consume()
} else if (Cursor.W_RESIZE == cursor) {
if (this.setStageWidth(this.primaryStage.width - deltax)) {
this.primaryStage.x = this.primaryStage.x + deltax
} else if (this.isLeftEdge(x, y, boundsInParent)) {
if (y < borderWidth) {
this.cursor = Cursor.NW_RESIZE
} else if (y > this.height - borderWidth) {
this.cursor = Cursor.SW_RESIZE
} else {
this.cursor = Cursor.W_RESIZE
}
mouseEvent.consume()
} else if (Cursor.SW_RESIZE == cursor) {
if (this.setStageWidth(this.primaryStage.width - deltax)) {
this.primaryStage.x = this.primaryStage.x + deltax
}
this.setStageHeight(this.primaryStage.height + deltay)
mouseEvent.consume()
} else if (Cursor.NW_RESIZE == cursor) {
if (this.setStageWidth(this.primaryStage.width - deltax)) {
this.primaryStage.x = this.primaryStage.x + deltax
}
if (this.setStageHeight(this.primaryStage.height - deltay)) {
this.primaryStage.y = this.primaryStage.y + deltay
}
mouseEvent.consume()
} else if (Cursor.N_RESIZE == cursor) {
if (this.setStageHeight(this.primaryStage.height - deltay)) {
this.primaryStage.y = this.primaryStage.y + deltay
}
mouseEvent.consume()
} else if (this.allowMove) {
this.primaryStage.x = mouseEvent.screenX - this.xOffset
this.primaryStage.y = mouseEvent.screenY - this.yOffset
mouseEvent.consume()
} else if (this.isTopEdge(x, y, boundsInParent)) {
this.cursor = Cursor.N_RESIZE
} else if (this.isBottomEdge(x, y, boundsInParent)) {
this.cursor = Cursor.S_RESIZE
} else {
this.cursor = Cursor.DEFAULT
}
this.updateInitMouseValues(mouseEvent)
}
}
} else {
this.cursor = Cursor.DEFAULT
}
}
fun onMouseReleased() {
this.isDragging = false
}
fun onMouseDragged(mouseEvent: MouseEvent) {
this.isDragging = true
if (mouseEvent.isPrimaryButtonDown && (this.xOffset != -1.0 || this.yOffset != -1.0)) {
if (!this.primaryStage.isFullScreen && !mouseEvent.isStillSincePress && !this.primaryStage.isMaximized && !this.maximized) {
this.newX = mouseEvent.screenX
this.newY = mouseEvent.screenY
val deltax = this.newX - this.initX
val deltay = this.newY - this.initY
val cursor = this.cursor
if (Cursor.E_RESIZE == cursor) {
this.setStageWidth(this.primaryStage.width + deltax)
mouseEvent.consume()
} else if (Cursor.NE_RESIZE == cursor) {
if (this.setStageHeight(this.primaryStage.height - deltay)) {
this.primaryStage.y = this.primaryStage.y + deltay
}
this.setStageWidth(this.primaryStage.width + deltax)
mouseEvent.consume()
} else if (Cursor.SE_RESIZE == cursor) {
this.setStageWidth(this.primaryStage.width + deltax)
this.setStageHeight(this.primaryStage.height + deltay)
mouseEvent.consume()
} else if (Cursor.S_RESIZE == cursor) {
this.setStageHeight(this.primaryStage.height + deltay)
mouseEvent.consume()
} else if (Cursor.W_RESIZE == cursor) {
if (this.setStageWidth(this.primaryStage.width - deltax)) {
this.primaryStage.x = this.primaryStage.x + deltax
}
mouseEvent.consume()
} else if (Cursor.SW_RESIZE == cursor) {
if (this.setStageWidth(this.primaryStage.width - deltax)) {
this.primaryStage.x = this.primaryStage.x + deltax
}
this.setStageHeight(this.primaryStage.height + deltay)
mouseEvent.consume()
} else if (Cursor.NW_RESIZE == cursor) {
if (this.setStageWidth(this.primaryStage.width - deltax)) {
this.primaryStage.x = this.primaryStage.x + deltax
}
if (this.setStageHeight(this.primaryStage.height - deltay)) {
this.primaryStage.y = this.primaryStage.y + deltay
}
mouseEvent.consume()
} else if (Cursor.N_RESIZE == cursor) {
if (this.setStageHeight(this.primaryStage.height - deltay)) {
this.primaryStage.y = this.primaryStage.y + deltay
}
mouseEvent.consume()
} else if (this.allowMove) {
this.primaryStage.x = mouseEvent.screenX - this.xOffset
this.primaryStage.y = mouseEvent.screenY - this.yOffset
mouseEvent.consume()
}
}
}
}
fun onMin() {
this.primaryStage.isIconified = true
}
fun onMax() {
if (!max) return
if (!this.isCustomMaximize) {
this.primaryStage.isMaximized = !this.primaryStage.isMaximized
this.maximized = this.primaryStage.isMaximized
if (this.primaryStage.isMaximized) {
this.btnMax.graphic = resizeMin
this.btnMax.tooltip = Tooltip("Restore Down")
} else {
this.btnMax.graphic = resizeMax
this.btnMax.tooltip = Tooltip("Maximize")
}
} else {
if (!this.maximized) {
this.originalBox = BoundingBox(primaryStage.x, primaryStage.y, primaryStage.width, primaryStage.height)
val screen = Screen.getScreensForRectangle(primaryStage.x, primaryStage.y, primaryStage.width, primaryStage.height)[0] as Screen
val bounds = screen.visualBounds
this.maximizedBox = BoundingBox(bounds.minX, bounds.minY, bounds.width, bounds.height)
primaryStage.x = this.maximizedBox!!.minX
primaryStage.y = this.maximizedBox!!.minY
primaryStage.width = this.maximizedBox!!.width
primaryStage.height = this.maximizedBox!!.height
this.btnMax.graphic = resizeMin
this.btnMax.tooltip = Tooltip("Restore Down")
} else {
primaryStage.x = this.originalBox!!.minX
primaryStage.y = this.originalBox!!.minY
primaryStage.width = this.originalBox!!.width
primaryStage.height = this.originalBox!!.height
this.originalBox = null
this.btnMax.graphic = resizeMax
this.btnMax.tooltip = Tooltip("Maximize")
}
this.maximized = !this.maximized
}
}
fun onClose() {
(this.onCloseButtonAction.get() as Runnable).run()
}
private fun updateInitMouseValues(mouseEvent: MouseEvent) {
this.initX = mouseEvent.screenX
this.initY = mouseEvent.screenY
@ -371,7 +344,6 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node:
if (this.maximized != maximized) {
Platform.runLater { this.btnMax.fire() }
}
}
fun setContent(content: Node) {

@ -25,10 +25,15 @@ import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.fxml.FXMLLoader
import javafx.scene.Node
import javafx.scene.Parent
import javafx.scene.Scene
import javafx.scene.control.ListView
import javafx.scene.control.ScrollBar
import javafx.scene.image.WritableImage
import javafx.scene.input.MouseEvent
import javafx.scene.input.ScrollEvent
import javafx.scene.layout.Pane
import javafx.scene.shape.Rectangle
import javafx.util.Duration
fun Node.loadFXML(absolutePath: String) {
@ -86,4 +91,21 @@ fun ListView<*>.smoothScrolling() {
fun runOnUiThread(runnable: () -> Unit) = {
JFXUtilities.runInFX(runnable)
}
}
fun takeSnapshot(node: Parent, width: Double, height: Double): WritableImage {
val scene = Scene(node, width, height)
scene.stylesheets.addAll(*stylesheets)
return scene.snapshot(null)
}
fun setOverflowHidden(node: Pane) {
val rectangle = Rectangle()
rectangle.widthProperty().bind(node.widthProperty())
rectangle.heightProperty().bind(node.heightProperty())
node.clip = rectangle
}
val stylesheets = arrayOf(Controllers::class.java.getResource("/css/jfoenix-design.css").toExternalForm(),
Controllers::class.java.getResource("/assets/css/jfoenix-components.css").toExternalForm(),
Controllers::class.java.getResource("/assets/css/jfoenix-main-demo.css").toExternalForm())

@ -43,87 +43,4 @@ import org.jackhuang.hmcl.ui.wizard.Wizard
*/
class MainController {
/**
* A combo box that allows user to select Minecraft directory.
*/
@FXML lateinit var comboProfiles: JFXComboBox<String> // TODO: JFXComboBox<Profile>
/**
* The button that is to launch the selected game version.
*/
@FXML lateinit var buttonLaunch: JFXButton
/**
* A central pane that contains popups like (global) version settings, app settings, game installations and so on.
*/
@FXML lateinit var page: StackPane
@FXML lateinit var listVersions: JFXListView<VersionListItem> // TODO: JFXListView<Version> including icon, title, game version(if equals to title, hidden)
lateinit var animationHandler: TransitionHandler
// TODO: implementing functions.
fun initialize() {
Controllers.mainController = this
animationHandler = TransitionHandler(page)
EVENT_BUS.channel<RefreshedVersionsEvent>() += this::loadVersions
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
listVersions.setOnMouseClicked {
if (it.clickCount == 2) {
setContentPage(Controllers.versionPane)
val id = listVersions.selectionModel.selectedItem.id
Controllers.versionController.loadVersionSetting(id, Settings.getLastProfile().getVersionSetting(id))
} else
it.consume()
}
listVersions.smoothScrolling()
Settings.onProfileLoading()
}
private val empty = Pane()
fun setContentPage(node: Node?) {
animationHandler.setContent(node ?: empty, ContainerAnimations.FADE.animationProducer)
}
fun installNewVersion() {
setContentPage(Wizard.createWizard("Install New Game", DownloadWizardProvider()))
}
fun onProfilesLoading() {
// TODO: Profiles
}
fun onProfileChanged(event: ProfileChangedEvent) {
val profile = event.value
profile.selectedVersionProperty.addListener { _, _, newValue ->
versionChanged(newValue)
}
}
val versionListItems = mutableMapOf<String, VersionListItem>()
fun loadVersions() {
val profile = Settings.getLastProfile()
val list = mutableListOf<VersionListItem>()
versionListItems.clear()
profile.repository.getVersions().forEach {
val item = VersionListItem(it.id, minecraftVersion(Settings.getLastProfile().repository.getVersionJar(it.id)) ?: "Unknown")
list += item
versionListItems += it.id to item
}
listVersions.items = FXCollections.observableList(list)
}
fun versionChanged(selectedVersion: String) {
listVersions.selectionModel.select(versionListItems[selectedVersion])
}
}

@ -21,12 +21,17 @@ import javafx.animation.KeyFrame
import javafx.animation.Timeline
import javafx.event.EventHandler
import javafx.scene.Node
import javafx.scene.Parent
import javafx.scene.SnapshotParameters
import javafx.scene.image.ImageView
import javafx.scene.image.WritableImage
import javafx.scene.layout.StackPane
import javafx.util.Duration
import org.jackhuang.hmcl.ui.takeSnapshot
/**
* @param view A stack pane that contains another control that is [Parent]
*/
class TransitionHandler(override val view: StackPane): AnimationHandler {
private var animation: Timeline? = null
@ -62,13 +67,16 @@ class TransitionHandler(override val view: StackPane): AnimationHandler {
private fun updateContent(newView: Node) {
if (view.width > 0 && view.height > 0) {
val image = view.snapshot(SnapshotParameters(), null)
val x = (image.width - view.width) / 2
val y = image.height - view.height
val newImage = WritableImage(image.pixelReader, x.toInt(), y.toInt(), view.width.toInt(), view.height.toInt())
snapshot.image = newImage
snapshot.fitWidth = newImage.width
snapshot.fitHeight = newImage.height
val content = view.children.firstOrNull()
val image: WritableImage
if (content != null && content is Parent) {
view.children.setAll()
image = takeSnapshot(content, view.width, view.height)
view.children.setAll(content)
} else image = view.snapshot(SnapshotParameters(), WritableImage(view.width.toInt(), view.height.toInt()))
snapshot.image = image
snapshot.fitWidth = view.width
snapshot.fitHeight = view.height
} else
snapshot.image = null

@ -31,8 +31,8 @@ class InstallTypePage(private val controller: WizardController): StackPane(), Wi
init {
loadFXML("/assets/fxml/download/dltype.fxml")
list.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
controller.settings[INSTALL_TYPE] = newValue
list.setOnMouseClicked {
controller.settings[INSTALL_TYPE] = list.selectionModel.selectedIndex
controller.onNext()
}
}

@ -21,6 +21,7 @@ import com.jfoenix.concurrency.JFXUtilities
import com.jfoenix.controls.JFXButton
import com.jfoenix.controls.JFXProgressBar
import com.jfoenix.controls.JFXToolbar
import com.jfoenix.effects.JFXDepthManager
import javafx.fxml.FXML
import javafx.scene.Node
import javafx.scene.control.Label

@ -43,7 +43,7 @@
.jfx-decorator .resize-border {
-fx-border-color: -fx-decorator-color;
-fx-border-width: 0 4 4 4;
-fx-border-width: 0 1 1 1;
}
.jfx-text-area, .text-area {

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import com.jfoenix.controls.JFXButton?>
<?import javafx.scene.Cursor?>
<?import javafx.geometry.Insets?>
<?import java.lang.String?>
<fx:root xmlns="http://javafx.com/javafx"
type="VBox"
xmlns:fx="http://javafx.com/fxml"
styleClass="jfx-decorator"
pickOnBounds="false"
onMouseReleased="#onMouseReleased"
onMouseDragged="#onMouseDragged"
onMouseMoved="#onMouseMoved">
<BorderPane fx:id="titleContainer" styleClass="jfx-decorator-buttons-container" pickOnBounds="false">
<left>
<HBox fx:id="titleWrapper" alignment="CENTER_LEFT" style="-fx-padding: 15;">
<Label text="Hello Minecraft! Launcher" mouseTransparent="false" style="-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 15px;" />
</HBox>
</left>
<right>
<HBox fx:id="buttonsContainer" styleClass="jfx-decorator-buttons-container" alignment="CENTER_RIGHT" minWidth="180">
<padding>
<Insets topRightBottomLeft="4.0" />
</padding>
<JFXButton fx:id="btnMin" styleClass="jfx-decorator-button" ripplerFill="white" onAction="#onMin">
<cursor>
<Cursor fx:constant="HAND" />
</cursor>
</JFXButton>
<JFXButton fx:id="btnMax" styleClass="jfx-decorator-button" ripplerFill="white" onAction="#onMax">
<cursor>
<Cursor fx:constant="HAND" />
</cursor>
</JFXButton>
<JFXButton fx:id="btnClose" styleClass="jfx-decorator-button" ripplerFill="white" onAction="#onClose">
<cursor>
<Cursor fx:constant="HAND" />
</cursor>
</JFXButton>
</HBox>
</right>
</BorderPane>
<StackPane fx:id="contentPlaceHolder" styleClass="jfx-decorator-content-container resize-border" minWidth="0" minHeight="0" VBox.vgrow="ALWAYS">
<styleClass>
<String fx:value="jfx-decorator-content-container" />
<String fx:value="resize-border" />
</styleClass>
<!-- Node -->
</StackPane>
</fx:root>

@ -3,6 +3,7 @@
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import com.jfoenix.controls.*?>
<?import javafx.geometry.Insets?>
<BorderPane
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
fx:controller="org.jackhuang.hmcl.ui.MainController"
@ -20,7 +21,7 @@
<StackPane>
<JFXListView fx:id="listVersions" styleClass="mylistview" style="-fx-background-color: #F1F1F1;" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
<AnchorPane pickOnBounds="false">
<JFXButton onMouseClicked="#installNewVersion" AnchorPane.bottomAnchor="16" AnchorPane.rightAnchor="16" buttonType="RAISED" prefWidth="40" prefHeight="40" style="-fx-text-fill:WHITE;-fx-background-color:#5264AE;-fx-font-size:14px;-fx-background-radius: 80px;">
<JFXButton AnchorPane.bottomAnchor="16" AnchorPane.rightAnchor="16" buttonType="RAISED" prefWidth="40" prefHeight="40" style="-fx-text-fill:WHITE;-fx-background-color:#5264AE;-fx-font-size:14px;-fx-background-radius: 80px;">
<graphic>
<fx:include source="/assets/svg/plus.fxml" />
</graphic>
@ -30,6 +31,41 @@
</center>
</BorderPane>
</left>
<top>
<JFXToolbar fx:id="toolbar" styleClass="jfx-tool-bar">
<leftItems>
<JFXButton fx:id="closeButton" maxHeight="20" styleClass="toggle-icon3"
StackPane.alignment="CENTER_RIGHT">
<graphic>
<fx:include source="/assets/svg/close.fxml"/>
</graphic>
<StackPane.margin>
<Insets left="20"/>
</StackPane.margin>
</JFXButton>
<JFXButton fx:id="backButton" maxHeight="20" styleClass="toggle-icon3"
StackPane.alignment="CENTER_LEFT">
<graphic>
<fx:include source="/assets/svg/arrow-left.fxml"/>
</graphic>
</JFXButton>
<Label fx:id="titleLabel" style="-fx-text-fill:WHITE; -fx-font-size: 15;"
StackPane.alignment="CENTER_LEFT"/>
</leftItems>
<rightItems>
<JFXButton fx:id="refreshButton" maxHeight="20" styleClass="toggle-icon3" disable="true"
StackPane.alignment="CENTER_RIGHT">
<graphic>
<fx:include source="/assets/svg/refresh.fxml"/>
</graphic>
<StackPane.margin>
<Insets left="20"/>
</StackPane.margin>
</JFXButton>
</rightItems>
</JFXToolbar>
</top>
<bottom>
<BorderPane prefHeight="50.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<right>

@ -8,31 +8,11 @@
<StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jackhuang.hmcl.ui.VersionController">
<VBox spacing="20" GridPane.rowIndex="0">
<JFXToolbar maxHeight="20" styleClass="jfx-tool-bar">
<leftItems>
<JFXButton fx:id="backButton" maxHeight="20" styleClass="toggle-icon3"
StackPane.alignment="CENTER_LEFT">
<graphic>
<fx:include source="/assets/svg/arrow-left.fxml" />
</graphic>
<StackPane.margin>
<Insets left="20" />
</StackPane.margin>
</JFXButton>
<Label fx:id="titleLabel" style="-fx-text-fill:WHITE; -fx-font-size: 20;"
StackPane.alignment="CENTER_LEFT"/>
</leftItems>
<rightItems>
</rightItems>
</JFXToolbar>
<BorderPane>
<center>
<ScrollPane fx:id="scroll"
style="-fx-margin: 10; -fx-background-color: white; -fx-font-size: 14; -fx-pref-width: 100%; "
style="-fx-background-color: white; -fx-font-size: 14; -fx-pref-width: 100%; "
fitToHeight="true" fitToWidth="true">
<VBox.margin>
<Insets left="10" right="10"/>
</VBox.margin>
<VBox>
<GridPane fx:id="settingsPane" style="-fx-margin-left: 10; -fx-background-color: white; " hgap="5" vgap="10">
<Label GridPane.rowIndex="0" GridPane.columnIndex="0">Java Directory</Label>
@ -105,5 +85,26 @@
</VBox>
</VBox>
</ScrollPane>
</VBox>
</center>
<top>
<JFXToolbar maxHeight="20" styleClass="jfx-tool-bar">
<leftItems>
<JFXButton fx:id="backButton" maxHeight="20" styleClass="toggle-icon3"
StackPane.alignment="CENTER_LEFT">
<graphic>
<fx:include source="/assets/svg/arrow-left.fxml" />
</graphic>
<StackPane.margin>
<Insets left="20" />
</StackPane.margin>
</JFXButton>
<Label fx:id="titleLabel" style="-fx-text-fill:WHITE; -fx-font-size: 20;"
StackPane.alignment="CENTER_LEFT"/>
</leftItems>
<rightItems>
</rightItems>
</JFXToolbar>
</top>
</BorderPane>
</StackPane>

@ -9,7 +9,7 @@
type="StackPane"
xmlns:fx="http://javafx.com/fxml">
<VBox>
<JFXToolbar fx:id="toolbar" maxHeight="20" styleClass="jfx-tool-bar">
<JFXToolbar fx:id="toolbar" styleClass="jfx-tool-bar">
<leftItems>
<JFXButton fx:id="closeButton" maxHeight="20" styleClass="toggle-icon3"
StackPane.alignment="CENTER_RIGHT" onMouseClicked="#close">

@ -52,7 +52,10 @@ interface Scheduler {
override fun schedule(block: Runnable) = IO_EXECUTOR.submit(block)
}
val DEFAULT = NEW_THREAD
private val CACHED_EXECUTOR = Executors.newCachedThreadPool()
private val CACHED_EXECUTOR: ExecutorService by lazy {
Executors.newCachedThreadPool()
}
private val IO_EXECUTOR: ExecutorService by lazy {
Executors.newFixedThreadPool(6, { r: Runnable ->
val thread: Thread = Executors.defaultThreadFactory().newThread(r)