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:
parent
41498b5d93
commit
736a8c1b30
HMCL/src/main
java/org/jackhuang/hmcl/ui
resources/assets
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 {
|
||||
|
56
HMCL/src/main/resources/assets/fxml/decorator.fxml
Normal file
56
HMCL/src/main/resources/assets/fxml/decorator.fxml
Normal file
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user