Scheduler JavaFX and Swing now have Future

This commit is contained in:
huangyuhui 2017-08-06 11:45:12 +08:00
parent 736a8c1b30
commit f1ffa5ca03
22 changed files with 255 additions and 209 deletions

View File

@ -34,12 +34,14 @@ object Controllers {
lateinit var versionController: VersionController
val versionPane: Pane = loadPane("version")
lateinit var decorator: Decorator
fun initialize(stage: Stage) {
this.stage = stage
val decorator = Decorator(stage, mainPane, max = false)
// Let root pane fix window size.
(mainPane.parent as StackPane).run {
with(mainPane.parent as StackPane) {
mainPane.prefWidthProperty().bind(widthProperty())
mainPane.prefHeightProperty().bind(heightProperty())
}

View File

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.ui
import com.jfoenix.controls.JFXButton
import com.jfoenix.effects.JFXDepthManager
import com.jfoenix.svg.SVGGlyph
import javafx.animation.*
import javafx.application.Platform
@ -31,6 +32,7 @@ import javafx.geometry.Bounds
import javafx.geometry.Insets
import javafx.scene.Cursor
import javafx.scene.Node
import javafx.scene.control.Label
import javafx.scene.control.Tooltip
import javafx.scene.input.MouseEvent
import javafx.scene.layout.*
@ -39,8 +41,11 @@ import javafx.scene.shape.Rectangle
import javafx.stage.Screen
import javafx.stage.Stage
import javafx.stage.StageStyle
import javafx.scene.layout.BorderStrokeStyle
import javafx.scene.layout.BorderStroke
import org.jackhuang.hmcl.util.*
class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node: Node, private val 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) : GridPane() {
private var xOffset: Double = 0.0
private var yOffset: Double = 0.0
private var newX: Double = 0.0
@ -53,66 +58,62 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node:
@FXML lateinit var contentPlaceHolder: StackPane
@FXML lateinit var titleContainer: BorderPane
@FXML lateinit var buttonsContainer: HBox
private val onCloseButtonAction: ObjectProperty<Runnable>
private val customMaximize: BooleanProperty
@FXML lateinit var backNavButton: JFXButton
@FXML lateinit var refreshNavButton: JFXButton
@FXML lateinit var closeNavButton: JFXButton
@FXML lateinit var refreshMenuButton: JFXButton
@FXML lateinit var addMenuButton: JFXButton
@FXML lateinit var titleLabel: Label
@FXML lateinit var leftPane: VBox
private val onCloseButtonActionProperty: ObjectProperty<Runnable> = SimpleObjectProperty(Runnable { this.primaryStage.close() })
@JvmName("onCloseButtonActionProperty") get
var onCloseButtonAction: Runnable by onCloseButtonActionProperty
val customMaximizeProperty: BooleanProperty = SimpleBooleanProperty(false)
@JvmName("customMaximizeProperty") get
var isCustomMaximize: Boolean by customMaximizeProperty
private var maximized: Boolean = false
private var originalBox: BoundingBox? = null
private var maximizedBox: BoundingBox? = null
@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
private 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)
.apply { setSize(12.0, 2.0); translateY = 4.0 }
private 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)
.apply { setPrefSize(12.0, 12.0); setSize(12.0, 12.0) }
private 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)
.apply { setPrefSize(12.0, 12.0); setSize(12.0, 12.0) }
private 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)
.apply { setPrefSize(12.0, 12.0); setSize(12.0, 12.0) }
init {
loadFXML("/assets/fxml/decorator.fxml")
this.xOffset = 0.0
this.yOffset = 0.0
this.allowMove = false
this.isDragging = false
this.onCloseButtonAction = SimpleObjectProperty(Runnable { this.primaryStage.close() })
this.customMaximize = SimpleBooleanProperty(false)
this.maximized = false
this.primaryStage.initStyle(StageStyle.UNDECORATED)
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
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)
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)
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)
btnClose.graphic = close
btnMin.graphic = minus
this.btnMax.graphic = resizeMax
btnMax.graphic = resizeMax
buttonsContainer.background = Background(*arrayOf(BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)))
titleContainer.addEventHandler(MouseEvent.MOUSE_CLICKED) { mouseEvent ->
if (mouseEvent.clickCount == 2) {
this.btnMax.fire()
btnMax.fire()
}
}
if (!min) buttonsContainer.children.remove(btnMin)
if (!max) buttonsContainer.children.remove(btnMax)
titleContainer.addEventHandler(MouseEvent.MOUSE_ENTERED) { enter -> this.allowMove = true }
titleContainer.addEventHandler(MouseEvent.MOUSE_EXITED) { enter ->
if (!this.isDragging) {
this.allowMove = false
}
}
JFXDepthManager.setDepth(titleContainer, 1)
titleContainer.addEventHandler(MouseEvent.MOUSE_ENTERED) { this.allowMove = true }
titleContainer.addEventHandler(MouseEvent.MOUSE_EXITED) { if (!this.isDragging) this.allowMove = false }
this.contentPlaceHolder.children.add(node)
(node as Region).setMinSize(0.0, 0.0)
this.contentPlaceHolder.border = Border(BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths(0.0, 4.0, 4.0, 4.0)))
this.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())
@ -127,7 +128,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node:
val x = mouseEvent.x
val y = mouseEvent.y
val boundsInParent = this.boundsInParent
if (this.contentPlaceHolder.border != null && this.contentPlaceHolder.border.strokes.size > 0) {
if (this.border != null && this.border.strokes.size > 0) {
val borderWidth = this.contentPlaceHolder.snappedLeftInset()
if (this.isRightEdge(x, y, boundsInParent)) {
if (y < borderWidth) {
@ -274,7 +275,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node:
}
fun onClose() {
(this.onCloseButtonAction.get() as Runnable).run()
this.onCloseButtonAction.run()
}
private fun updateInitMouseValues(mouseEvent: MouseEvent) {
@ -328,18 +329,6 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, node:
}
}
fun setOnCloseButtonAction(onCloseButtonAction: Runnable) {
this.onCloseButtonAction.set(onCloseButtonAction)
}
fun customMaximizeProperty(): BooleanProperty {
return this.customMaximize
}
var isCustomMaximize: Boolean
get() = this.customMaximizeProperty().get()
set(customMaximize) = this.customMaximizeProperty().set(customMaximize)
fun setMaximized(maximized: Boolean) {
if (this.maximized != maximized) {
Platform.runLater { this.btnMax.fire() }

View File

@ -106,6 +106,8 @@ fun setOverflowHidden(node: Pane) {
node.clip = rectangle
}
val stylesheets = arrayOf(Controllers::class.java.getResource("/css/jfoenix-design.css").toExternalForm(),
val stylesheets = arrayOf(
Controllers::class.java.getResource("/css/jfoenix-fonts.css").toExternalForm(),
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())
Controllers::class.java.getResource("/assets/css/jfoenix-main-demo.css").toExternalForm())

View File

@ -212,7 +212,7 @@
.jfx-tool-bar HBox {
-fx-alignment: center;
-fx-spacing: 25;
/* -fx-spacing: 25;*/
-fx-padding: 0 10;
}

View File

@ -199,8 +199,12 @@
.jfx-tool-bar HBox {
-fx-alignment: center;
-fx-spacing: 25.0;
-fx-padding: 0.0 10.0;
/* -fx-spacing: 25.0;*/
-fx-padding: 0.0 5.0;
}
.jfx-tool-bar .jfx-decorator-button {
-fx-cursor: head;
}
.jfx-tool-bar Label {
@ -219,6 +223,26 @@
-jfx-rippler-fill: WHITE;
}
.jfx-decorator-button {
-fx-max-width: 35px;
-fx-background-radius: 40px;
-fx-max-height: 35px;
-fx-background-color: transparent;
-jfx-toggle-color: rgba(128, 128, 255, 0.2);
-jfx-untoggle-color: transparent;
}
.jfx-decorator-button .icon {
-fx-fill: rgb(204.0, 204.0, 51.0);
-fx-padding: 0.0;
}
.jfx-decorator-button .jfx-rippler {
-jfx-rippler-fill: white;
-jfx-mask-type: CIRCLE;
-fx-padding: 0.0;
}
.option-list-view {
-fx-pref-width: 160.0px;
-fx-background-color: WHITE;
@ -617,7 +641,7 @@
-jfx-mask-type: CIRCLE;
}
.toggle-icon2, .toggle-icon3 {
.toggle-icon2, .toggle-icon3, .jfx-decorator-button {
-fx-pref-width: 50px;
-fx-background-radius: 50px;
-fx-pref-height: 50px;
@ -805,16 +829,21 @@
*******************************************************************************/
.jfx-decorator {
-fx-decorator-color: derive(#5264AE, -20%);
-fx-decorator-color: derive(#5264AE, 0);
}
.jfx-decorator .jfx-decorator-buttons-container {
-fx-background-color: -fx-decorator-color;
}
.jfx-decorator .resize-border {
-fx-border-color: -fx-decorator-color;
-fx-border-width: 0 4 4 4;
.resize-border {
-fx-border-color: #5264AE;
-fx-border-width: 0 2 2 2;
}
.debug-border {
-fx-border-color: red;
-fx-border-width: 1;
}
/*******************************************************************************

View File

@ -4,53 +4,117 @@
<?import javafx.scene.layout.*?>
<?import com.jfoenix.controls.JFXButton?>
<?import javafx.scene.Cursor?>
<?import javafx.geometry.Insets?>
<?import java.lang.String?>
<?import javafx.scene.shape.Rectangle?>
<?import com.jfoenix.controls.JFXComboBox?>
<?import com.jfoenix.controls.JFXListView?>
<?import com.jfoenix.controls.JFXListCell?>
<fx:root xmlns="http://javafx.com/javafx"
type="VBox"
type="GridPane"
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">
onMouseReleased="#onMouseReleased"
onMouseDragged="#onMouseDragged"
onMouseMoved="#onMouseMoved">
<styleClass>
<String fx:value="jfx-decorator" />
<String fx:value="resize-border" />
</styleClass>
<columnConstraints>
<ColumnConstraints prefWidth="200" />
<ColumnConstraints hgrow="ALWAYS" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints vgrow="ALWAYS" />
</rowConstraints>
<StackPane GridPane.rowIndex="1" GridPane.columnIndex="0" VBox.vgrow="ALWAYS" styleClass="jfx-decorator-content-container">
<BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<center>
<ScrollPane fitToHeight="true" fitToWidth="true">
<VBox fx:id="leftPane">
</VBox>
</ScrollPane>
</center>
<bottom>
<BorderPane fx:id="menuBottomBar">
<left>
<JFXButton fx:id="refreshMenuButton" styleClass="toggle-icon3">
<graphic>
<fx:include source="/assets/svg/refresh-black.fxml"/>
</graphic></JFXButton>
</left>
<right>
<JFXButton fx:id="addMenuButton" styleClass="toggle-icon3">
<graphic>
<fx:include source="/assets/svg/plus-black.fxml"/>
</graphic></JFXButton>
</right>
</BorderPane>
</bottom>
</BorderPane>
</StackPane>
<StackPane GridPane.rowIndex="1" GridPane.columnIndex="1" fx:id="contentPlaceHolder" styleClass="jfx-decorator-content-container" 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>
<BorderPane GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="titleContainer" styleClass="jfx-tool-bar" pickOnBounds="false">
<left>
<HBox minWidth="200" fx:id="titleWrapper" alignment="CENTER_LEFT">
<Label text="Hello Minecraft! Launcher" mouseTransparent="false" style="-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 15px;" />
</HBox>
</left>
<center>
<BorderPane fx:id="navBar">
<left>
<HBox fx:id="navLeft" alignment="CENTER_LEFT" style="-fx-padding: 0;">
<Rectangle height="${navBar.height}" width="1" fill="gray" />
<JFXButton fx:id="backNavButton" maxHeight="20" styleClass="toggle-icon3">
<graphic>
<fx:include source="/assets/svg/arrow-left.fxml"/>
</graphic>
</JFXButton>
<Label fx:id="titleLabel" style="-fx-text-fill:WHITE; -fx-font-size: 15;"/>
</HBox>
</left>
<right>
<HBox fx:id="navRight" alignment="CENTER_LEFT">
<JFXButton fx:id="refreshNavButton" maxHeight="20" styleClass="toggle-icon3" disable="true">
<graphic>
<fx:include source="/assets/svg/refresh.fxml"/>
</graphic>
<StackPane.margin>
<Insets left="20"/>
</StackPane.margin>
</JFXButton>
<JFXButton fx:id="closeNavButton" maxHeight="20" styleClass="toggle-icon3">
<graphic>
<fx:include source="/assets/svg/close.fxml"/>
</graphic>
<StackPane.margin>
<Insets left="20"/>
</StackPane.margin>
</JFXButton>
<Rectangle height="${navBar.height}" width="1" fill="gray" />
</HBox>
</right>
</BorderPane>
</center>
<right>
<HBox fx:id="buttonsContainer" style="-fx-background-color: transparent;" alignment="CENTER_RIGHT">
<padding>
<Insets topRightBottomLeft="4.0" />
</padding>
<JFXButton fx:id="btnMin" styleClass="jfx-decorator-button" ripplerFill="white" onAction="#onMin">
</JFXButton>
<JFXButton fx:id="btnMax" styleClass="jfx-decorator-button" ripplerFill="white" onAction="#onMax">
</JFXButton>
<JFXButton fx:id="btnClose" styleClass="jfx-decorator-button" ripplerFill="white" onAction="#onClose">
</JFXButton>
</HBox>
</right>
</BorderPane>
</fx:root>

View File

@ -1,9 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?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"
@ -13,59 +11,7 @@
<StackPane fx:id="page" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</center>
<left>
<BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<top>
<JFXComboBox fx:id="comboProfiles" prefWidth="150.0" BorderPane.alignment="CENTER" />
</top>
<center>
<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 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>
</JFXButton>
</AnchorPane>
</StackPane>
</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>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<javafx.scene.shape.SVGPath fill="black" content="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<javafx.scene.shape.SVGPath fill="black" content="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z" />

View File

@ -30,7 +30,7 @@ private class CoupleTask<P: Task>(private val pred: P, private val succ: Task.(P
}
}
infix fun <T: Task> T.then(b: Task): Task = CoupleTask(this, { b }, true)
infix fun Task.then(b: Task): Task = CoupleTask(this, { b }, true)
/**
* @param b A runnable that decides what to do next, You can also do something here.

View File

@ -33,7 +33,7 @@ import java.net.URL
import java.math.BigInteger
import java.util.logging.Level
class FileDownloadTask(val url: URL, val file: File, val hash: String? = null, val retry: Int = 5, val proxy: Proxy = Proxy.NO_PROXY): Task() {
class FileDownloadTask @JvmOverloads constructor(val url: URL, val file: File, val hash: String? = null, val retry: Int = 5, val proxy: Proxy = Proxy.NO_PROXY): Task() {
override val scheduler: Scheduler = Scheduler.IO_THREAD
var onFailed = EventManager<FailedEvent<URL>>()

View File

@ -25,7 +25,7 @@ import java.net.Proxy
import java.net.URL
import java.nio.charset.Charset
class GetTask(val url: URL, val encoding: Charset = Charsets.UTF_8, private val retry: Int = 5, private val proxy: Proxy = Proxy.NO_PROXY): TaskResult<String>() {
class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Charsets.UTF_8, private val retry: Int = 5, private val proxy: Proxy = Proxy.NO_PROXY): TaskResult<String>() {
override val scheduler: Scheduler = Scheduler.IO_THREAD
override fun execute() {

View File

@ -18,38 +18,51 @@
package org.jackhuang.hmcl.task
import javafx.application.Platform
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicReference
import javax.swing.SwingUtilities
interface Scheduler {
fun schedule(block: Runnable): Future<*>?
fun schedule(block: Callable<Unit>): Future<*>?
companion object Schedulers {
val IMMEDIATE = object : Scheduler {
override fun schedule(block: Runnable): Future<*>? {
block.run()
return null
}
}
val JAVAFX: Scheduler = object : Scheduler {
override fun schedule(block: Runnable): Future<*>? {
Platform.runLater(block)
return null
}
}
val SWING: Scheduler = object : Scheduler {
override fun schedule(block: Runnable): Future<*>? {
SwingUtilities.invokeLater(block)
return null
val JAVAFX: Scheduler = SchedulerImpl(Platform::runLater)
val SWING: Scheduler = SchedulerImpl(SwingUtilities::invokeLater)
private class SchedulerImpl(val executor: (() -> Unit) -> Unit) : Scheduler {
override fun schedule(block: Callable<Unit>): Future<*>? {
val latch = CountDownLatch(1)
val wrapper = AtomicReference<Exception>()
executor {
try {
block.call()
} catch (e: Exception) {
wrapper.set(e)
} finally {
latch.countDown()
}
}
return object : Future<Unit> {
override fun get(timeout: Long, unit: TimeUnit) {
latch.await(timeout, unit)
val e = wrapper.get()
if (e != null) throw ExecutionException(e)
}
override fun get() {
latch.await()
val e = wrapper.get()
if (e != null) throw ExecutionException(e)
}
override fun isDone() = latch.count == 0L
override fun isCancelled() = false
override fun cancel(mayInterruptIfRunning: Boolean) = false
}
}
}
val NEW_THREAD: Scheduler = object : Scheduler {
override fun schedule(block: Runnable) = CACHED_EXECUTOR.submit(block)
override fun schedule(block: Callable<Unit>) = CACHED_EXECUTOR.submit(block)
}
val IO_THREAD: Scheduler = object : Scheduler {
override fun schedule(block: Runnable) = IO_EXECUTOR.submit(block)
override fun schedule(block: Callable<Unit>) = IO_EXECUTOR.submit(block)
}
val DEFAULT = NEW_THREAD
private val CACHED_EXECUTOR: ExecutorService by lazy {
@ -57,11 +70,11 @@ interface Scheduler {
}
private val IO_EXECUTOR: ExecutorService by lazy {
Executors.newFixedThreadPool(6, { r: Runnable ->
Executors.newFixedThreadPool(6) { r: Runnable ->
val thread: Thread = Executors.defaultThreadFactory().newThread(r)
thread.isDaemon = true
thread
})
}
}
fun shutdown() {

View File

@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.task
internal class SimpleTask(private val runnable: () -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
internal class SimpleTask @JvmOverloads constructor(private val runnable: () -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
override fun execute() {
runnable()
}

View File

@ -17,7 +17,9 @@
*/
package org.jackhuang.hmcl.task
import javafx.beans.property.ReadOnlyDoubleProperty
import javafx.beans.property.ReadOnlyDoubleWrapper
import javafx.beans.property.ReadOnlyStringProperty
import javafx.beans.property.ReadOnlyStringWrapper
import org.jackhuang.hmcl.event.EventManager
import org.jackhuang.hmcl.util.*
@ -69,19 +71,23 @@ abstract class Task {
protected open val progressInterval = 1000L
private var lastTime = Long.MIN_VALUE
private val progressUpdate = AtomicReference<Double>()
val progressProperty = ReadOnlyDoubleWrapper(this, "progress", 0.0)
private val progressPropertyImpl = ReadOnlyDoubleWrapper(this, "progress", 0.0)
val progressProperty: ReadOnlyDoubleProperty = progressPropertyImpl.readOnlyProperty
@JvmName("progressProperty") get
protected fun updateProgress(progress: Int, total: Int) = updateProgress(1.0 * progress / total)
protected fun updateProgress(progress: Double) {
val now = System.currentTimeMillis()
if (now - lastTime >= progressInterval) {
progressProperty.updateAsync(progress, progressUpdate)
progressPropertyImpl.updateAsync(progress, progressUpdate)
lastTime = now
}
}
private val messageUpdate = AtomicReference<String>()
val messageProperty = ReadOnlyStringWrapper(this, "message", null)
protected fun updateMessage(newMessage: String) = messageProperty.updateAsync(newMessage, messageUpdate)
private val messagePropertyImpl = ReadOnlyStringWrapper(this, "message", null)
val messageProperty: ReadOnlyStringProperty = messagePropertyImpl.readOnlyProperty
@JvmName("messageProperty") get
protected fun updateMessage(newMessage: String) = messagePropertyImpl.updateAsync(newMessage, messageUpdate)
val onDone = EventManager<TaskEvent>()
@ -97,11 +103,11 @@ abstract class Task {
}
private val subTaskRunnable = { task: Task ->
this.messageProperty.bind(task.messageProperty)
this.progressProperty.bind(task.progressProperty)
this.messagePropertyImpl.bind(task.messagePropertyImpl)
this.progressPropertyImpl.bind(task.progressPropertyImpl)
task.run()
this.messageProperty.unbind()
this.progressProperty.unbind()
this.messagePropertyImpl.unbind()
this.progressPropertyImpl.unbind()
}
fun executor() = TaskExecutor().submit(this)

View File

@ -52,7 +52,7 @@ class TaskExecutor() {
while (!taskQueue.isEmpty() && !canceled) {
val task = taskQueue.poll()
if (task != null) {
val future = task.scheduler.schedule(Runnable { executeTask(task) })
val future = task.scheduler.schedule(Callable { executeTask(task); Unit })
try {
future?.get()
} catch (e: InterruptedException) {
@ -142,8 +142,8 @@ class TaskExecutor() {
return flag
}
private inner class Invoker(val task: Task, val latch: CountDownLatch, val boolean: AtomicBoolean): Runnable {
override fun run() {
private inner class Invoker(val task: Task, val latch: CountDownLatch, val boolean: AtomicBoolean): Callable<Unit> {
override fun call() {
try {
Thread.currentThread().name = task.title
if (!executeTask(task))

View File

@ -15,6 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
@file:JvmName("Constants")
package org.jackhuang.hmcl.util
import javafx.application.Platform

View File

@ -15,6 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
@file:JvmName("HMCLog")
package org.jackhuang.hmcl.util
import java.io.ByteArrayOutputStream

View File

@ -23,7 +23,6 @@ import java.lang.reflect.Method
import java.security.PrivilegedExceptionAction
import java.security.AccessController
object ReflectionHelper {
private lateinit var unsafe: Unsafe

View File

@ -21,7 +21,7 @@ import java.io.IOException
import java.io.InputStream
import java.util.logging.Level
internal class StreamPump(
internal class StreamPump @JvmOverloads constructor(
val inputStream: InputStream,
val callback: (String) -> Unit = {}
) : Runnable {

View File

@ -23,13 +23,6 @@ import java.io.IOException
import java.util.zip.ZipOutputStream
import java.util.zip.ZipInputStream
@Throws(IOException::class)
fun zip(src: String, destZip: String) {
zip(File(src), File(destZip), null)
}
/**
* 功能 src 目录下的所有文件进行 zip 格式的压缩保存为指定 zip 文件
@ -43,8 +36,9 @@ fun zip(src: String, destZip: String) {
* *
* @throws java.io.IOException 压缩失败或无法读取
*/
@JvmOverloads
@Throws(IOException::class)
fun zip(src: File, destZip: File, pathNameCallback: ((String, Boolean) -> String?)?) {
fun zip(src: File, destZip: File, pathNameCallback: ((String, Boolean) -> String?)? = null) {
ZipOutputStream(destZip.outputStream()).use { zos ->
val basePath: String
if (src.isDirectory)
@ -103,11 +97,6 @@ private fun zipFile(src: File,
}
}
@Throws(IOException::class)
fun unzip(zip: File, dest: File) {
unzip(zip, dest, null, true)
}
/**
* 将文件压缩成zip文件
@ -121,8 +110,9 @@ fun unzip(zip: File, dest: File) {
* *
* @throws java.io.IOException 解压失败或无法写入
*/
@JvmOverloads
@Throws(IOException::class)
fun unzip(zip: File, dest: File, callback: ((String) -> Boolean)?, ignoreExistsFile: Boolean) {
fun unzip(zip: File, dest: File, callback: ((String) -> Boolean)? = null, ignoreExistsFile: Boolean = true) {
val buf = ByteArray(1024)
dest.mkdirs()
ZipInputStream(zip.inputStream()).use { zipFile ->

Binary file not shown.