Skin portrait

This commit is contained in:
huangyuhui
2017-08-18 22:42:11 +08:00
parent dc468f1a76
commit 27939c6b61
24 changed files with 213 additions and 142 deletions

View File

@@ -54,6 +54,7 @@ class Main : Application() {
val VERSION = "@HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING@"
val NAME = "HMCL"
val TITLE = "$NAME $VERSION"
val APPDATA = getWorkingDirectory("hmcl")
lateinit var PRIMARY_STAGE: Stage
@JvmStatic

View File

@@ -0,0 +1,76 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.setting
import javafx.scene.image.Image
import org.jackhuang.hmcl.Main
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
import org.jackhuang.hmcl.task.FileDownloadTask
import org.jackhuang.hmcl.task.Scheduler
import org.jackhuang.hmcl.task.Task
import org.jackhuang.hmcl.util.toURL
import java.net.Proxy
object AccountSkin {
val SKIN_DIR = Main.APPDATA.resolve("skins")
fun loadSkins(proxy: Proxy = Settings.proxy) {
for (account in Settings.getAccounts().values) {
if (account is YggdrasilAccount) {
SkinLoadTask(account, proxy, false).start()
}
}
}
fun loadSkinAsync(account: YggdrasilAccount, proxy: Proxy = Settings.proxy): Task =
SkinLoadTask(account, proxy, false)
fun refreshSkinAsync(account: YggdrasilAccount, proxy: Proxy = Settings.proxy): Task =
SkinLoadTask(account, proxy, true)
private class SkinLoadTask(val account: YggdrasilAccount, val proxy: Proxy, val refresh: Boolean = false): Task() {
override val scheduler = Scheduler.IO
override val dependencies = mutableListOf<Task>()
override fun execute() {
if (account.canLogIn && (account.selectedProfile == null || refresh))
account.logIn(proxy)
val profile = account.selectedProfile ?: return
val name = profile.name ?: return
val url = "http://skins.minecraft.net/MinecraftSkins/$name.png"
val file = getSkinFile(name)
if (!refresh && file.exists())
return
dependencies += FileDownloadTask(url.toURL(), file, proxy = proxy)
}
}
private fun getSkinFile(name: String) = SKIN_DIR.resolve("$name.png")
private val DEFAULT_IMAGE = Image("/assets/img/icon.png")
fun getSkin(account: YggdrasilAccount, scaleRatio: Double = 1.0): Image {
if (account.selectedProfile == null) return DEFAULT_IMAGE
val name = account.selectedProfile?.name ?: return DEFAULT_IMAGE
val file = getSkinFile(name)
if (file.exists()) {
val original = Image("file:" + file.absolutePath)
return Image("file:" + file.absolutePath, original.width * scaleRatio, original.height * scaleRatio, false, false)
}
else return DEFAULT_IMAGE
}
}

View File

@@ -19,48 +19,81 @@ package org.jackhuang.hmcl.ui
import com.jfoenix.controls.JFXButton
import com.jfoenix.controls.JFXCheckBox
import com.jfoenix.controls.JFXProgressBar
import com.jfoenix.controls.JFXRadioButton
import com.jfoenix.effects.JFXDepthManager
import javafx.beans.binding.Bindings
import javafx.fxml.FXML
import javafx.geometry.Rectangle2D
import javafx.scene.control.Label
import javafx.scene.control.ToggleGroup
import javafx.scene.effect.BlurType
import javafx.scene.effect.DropShadow
import javafx.scene.image.ImageView
import javafx.scene.layout.Pane
import javafx.scene.layout.StackPane
import javafx.scene.layout.VBox
import javafx.scene.paint.Color
import org.jackhuang.hmcl.auth.Account
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
import org.jackhuang.hmcl.setting.AccountSkin
import org.jackhuang.hmcl.setting.Settings
import org.jackhuang.hmcl.task.Scheduler
import java.util.concurrent.Callable
class AccountItem(i: Int, group: ToggleGroup) : StackPane() {
class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane() {
@FXML lateinit var icon: Pane
@FXML lateinit var content: VBox
@FXML lateinit var header: StackPane
@FXML lateinit var body: StackPane
@FXML lateinit var btnDelete: JFXButton
@FXML lateinit var btnRefresh: JFXButton
@FXML lateinit var lblUser: Label
@FXML lateinit var chkSelected: JFXRadioButton
@FXML lateinit var lblType: Label
@FXML lateinit var pgsSkin: JFXProgressBar
@FXML lateinit var portraitView: ImageView
init {
loadFXML("/assets/fxml/account-item.fxml")
limitWidth(150.0)
limitHeight(140.0)
limitWidth(160.0)
limitHeight(156.0)
effect = DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -0.5, 1.0)
chkSelected.toggleGroup = group
btnDelete.graphic = SVG.delete("white", 15.0, 15.0)
btnDelete.graphic = SVG.delete("black", 15.0, 15.0)
btnRefresh.graphic = SVG.refresh("black", 15.0, 15.0)
// create content
val headerColor = getDefaultColor(i % 12)
header.style = "-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor
body.minHeight = Math.random() * 20 + 50
// create image view
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 }, header.boundsInParentProperty(), icon.heightProperty()))
chkSelected.properties["account"] = account
chkSelected.isSelected = Settings.selectedAccount == account
lblUser.text = account.username
lblType.text = accountType(account)
if (account is YggdrasilAccount)
btnRefresh.setOnMouseClicked {
pgsSkin.isVisible = true
AccountSkin.refreshSkinAsync(account).subscribe(Scheduler.JAVAFX) { loadSkin() }
}
}
fun loadSkin() {
if (account !is YggdrasilAccount)
return
pgsSkin.isVisible = false
val size = 8.0 * 4
portraitView.viewport = Rectangle2D(size, size, size, size)
portraitView.image = AccountSkin.getSkin(account, 4.0)
portraitView.fitHeight = 32.0
portraitView.fitWidth = 32.0
}
private fun getDefaultColor(i: Int): String {

View File

@@ -32,6 +32,7 @@ import org.jackhuang.hmcl.auth.Account
import org.jackhuang.hmcl.auth.OfflineAccount
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
import org.jackhuang.hmcl.i18n
import org.jackhuang.hmcl.setting.AccountSkin
import org.jackhuang.hmcl.setting.Settings
import org.jackhuang.hmcl.task.Scheduler
import org.jackhuang.hmcl.task.taskResult
@@ -111,15 +112,16 @@ class AccountsPage() : StackPane(), DecoratorPage {
}
private fun buildNode(i: Int, account: Account, group: ToggleGroup): Node {
return AccountItem(i, group).apply {
chkSelected.properties["account"] = account
chkSelected.isSelected = Settings.selectedAccount == account
lblUser.text = account.username
lblType.text = accountType(account)
return AccountItem(i, account, group).apply {
btnDelete.setOnMouseClicked {
Settings.deleteAccount(account.username)
Platform.runLater(this@AccountsPage::loadAccounts)
}
if (account is YggdrasilAccount)
AccountSkin.loadSkinAsync(account).subscribe(Scheduler.JAVAFX) {
loadSkin()
}
}
}

View File

@@ -19,10 +19,12 @@ package org.jackhuang.hmcl.ui
import com.jfoenix.controls.JFXComboBox
import javafx.scene.layout.*
import org.jackhuang.hmcl.auth.Account
import org.jackhuang.hmcl.i18n
import org.jackhuang.hmcl.setting.Settings
import org.jackhuang.hmcl.ui.construct.IconedItem
import org.jackhuang.hmcl.ui.construct.RipplerContainer
import javax.swing.event.ChangeListener
class LeftPaneController(private val leftPane: AdvancedListBox) {
val versionsPane = VBox()
@@ -63,16 +65,15 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
}
Controllers.mainPane.buttonLaunch.setOnMouseClicked { LauncherHelper.launch() }*/
Settings.selectedAccountProperty.addListener { _, _, newValue ->
if (newValue == null) {
Settings.selectedAccountProperty.setChangedListener {
if (it == null) {
accountItem.lblVersionName.text = "mojang@mojang.com"
accountItem.lblGameVersion.text = "Yggdrasil"
} else {
accountItem.lblVersionName.text = newValue.username
accountItem.lblGameVersion.text = accountType(newValue)
accountItem.lblVersionName.text = it.username
accountItem.lblGameVersion.text = accountType(it)
}
}
Settings.selectedAccountProperty.fireValueChangedEvent()
if (Settings.getAccounts().isEmpty())
Controllers.navigate(AccountsPage())

View File

@@ -91,10 +91,9 @@ class MainPage : StackPane(), DecoratorPage {
fun onProfileChanged(event: ProfileChangedEvent) {
val profile = event.value
profile.selectedVersionProperty.addListener { _ ->
profile.selectedVersionProperty.setChangedListener {
versionChanged(profile.selectedVersion)
}
profile.selectedVersionProperty.fireValueChangedEvent()
}
private fun loadVersions() {

View File

@@ -61,5 +61,5 @@ object SVG {
fun navigate(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z", fill, width, height)
fun launch(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M1008 6.286q18.857 13.714 15.429 36.571l-146.286 877.714q-2.857 16.571-18.286 25.714-8 4.571-17.714 4.571-6.286 0-13.714-2.857l-258.857-105.714-138.286 168.571q-10.286 13.143-28 13.143-7.429 0-12.571-2.286-10.857-4-17.429-13.429t-6.571-20.857v-199.429l493.714-605.143-610.857 528.571-225.714-92.571q-21.143-8-22.857-31.429-1.143-22.857 18.286-33.714l950.857-548.571q8.571-5.143 18.286-5.14311.429 0 20.571 6.286z", fill, width, height)
fun pencil(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M20.71,4.04C21.1,3.65 21.1,3 20.71,2.63L18.37,0.29C18,-0.1 17.35,-0.1 16.96,0.29L15,2.25L18.75,6M17.75,7L14,3.25L4,13.25V17H7.75L17.75,7Z", fill, width, height)
fun refresh(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("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", fill, width, height)
}

View File

@@ -47,7 +47,7 @@ class VersionItem(i: Int, group: ToggleGroup) : StackPane() {
init {
loadFXML("/assets/fxml/version-item.fxml")
limitWidth(190.0)
limitWidth(160.0)
limitHeight(156.0)
effect = DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -1.0, 1.0)

View File

@@ -60,7 +60,7 @@ class VersionSettingsController {
@FXML lateinit var chkNoGameCheck: JFXToggleButton
@FXML lateinit var componentJava: ComponentList
@FXML lateinit var javaPane: VBox
@FXML lateinit var javaPaneCustom: HBox
@FXML lateinit var javaPaneCustom: BorderPane
@FXML lateinit var radioCustom: JFXRadioButton
@FXML lateinit var btnJavaSelect: JFXButton
@@ -100,21 +100,21 @@ class VersionSettingsController {
javaPane.children += createJavaPane(javaVersion, javaGroup)
}
javaPane.children += javaPaneCustom
javaPaneCustom.limitHeight(20.0)
radioCustom.toggleGroup = javaGroup
txtJavaDir.disableProperty().bind(radioCustom.selectedProperty().not())
btnJavaSelect.disableProperty().bind(radioCustom.selectedProperty().not())
}
private fun createJavaPane(java: JavaVersion, group: ToggleGroup): Pane {
return HBox().apply {
return BorderPane().apply {
style = "-fx-padding: 3;"
spacing = 8.0
alignment = Pos.CENTER_LEFT
children += JFXRadioButton(java.longVersion).apply {
limitHeight(20.0)
left = JFXRadioButton(java.longVersion).apply {
toggleGroup = group
userData = java
}
children += Label(java.binary.absolutePath).apply {
right = Label(java.binary.absolutePath).apply {
styleClass += "subtitle-label"
style += "-fx-font-size: 10;"
}

View File

@@ -1,68 +0,0 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui.construct
import javafx.scene.canvas.GraphicsContext
import javafx.scene.paint.Color
object RenderedPartlyTexture {
fun renderAvatar(ctx: GraphicsContext, skinScale: Int, skinImg: Array<Array<Color>>, width: Double) {
val helmBs = width / (1.0 * skinScale)
val headBs = (width - 1.0 * helmBs) / (1.0 * skinScale)
val headOffset = helmBs / 2.0
// render head.front
renderRange(ctx, skinImg, head_front_x * skinScale, head_front_y * skinScale, head_front_w * skinScale, head_front_h * skinScale, headOffset, headOffset, headBs)
// render helm.front
renderRange(ctx, skinImg, helm_front_x * skinScale, helm_front_y * skinScale, helm_front_w * skinScale, helm_front_h * skinScale, 0.0, 0.0, helmBs)
}
private fun renderRange(ctx: GraphicsContext, img: Array<Array<Color>>, x0: Int, y0: Int, w: Int, h: Int, tx0: Double, ty0: Double, bs: Double) {
var x: Int
var y: Int
var tx: Double
var ty: Double
for (dx in 0..w - 1) {
for (dy in 0..h - 1) {
x = x0 + dx
y = y0 + dy
tx = tx0 + bs * dx
ty = ty0 + bs * dy
ctx.fill = img[x][y]
ctx.fillRect(tx, ty, bs, bs)
}
}
}
/*
* The specification of skin can be found at https://github.com/minotar/skin-spec
*/
internal val head_front_x = 1
internal val head_front_y = 1
internal val head_front_w = 1
internal val head_front_h = 1
internal val helm_front_x = 5
internal val helm_front_y = 1
internal val helm_front_w = 1
internal val helm_front_h = 1
}

View File

@@ -8,6 +8,7 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import com.jfoenix.controls.JFXRadioButton?>
<?import com.jfoenix.controls.JFXProgressBar?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
@@ -16,23 +17,34 @@
<BorderPane>
<top>
<HBox alignment="CENTER_RIGHT">
<JFXButton fx:id="btnDelete" styleClass="toggle-icon3" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
<JFXProgressBar fx:id="pgsSkin" visible="false" />
</HBox>
</top>
<center>
<VBox style="-fx-padding: 0 0 5 20;">
<VBox style="-fx-padding: 20 20 0 20;">
<Label fx:id="lblUser" style="-fx-font-size: 20;" wrapText="true" textAlignment="JUSTIFY" />
<Label fx:id="lblType" style="-fx-font-size: 10;" />
</VBox>
</center>
</BorderPane>
</StackPane>
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 0 0 10 0;">
<JFXRadioButton fx:id="chkSelected" StackPane.alignment="BOTTOM_RIGHT" />
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40">
<BorderPane>
<left>
<HBox spacing="8">
<JFXButton fx:id="btnRefresh" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
<JFXButton fx:id="btnDelete" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
</HBox>
</left>
<right>
<JFXRadioButton fx:id="chkSelected" BorderPane.alignment="CENTER_RIGHT" />
</right>
</BorderPane>
</StackPane>
</VBox>
<StackPane fx:id="icon" StackPane.alignment="TOP_RIGHT" pickOnBounds="false">
<ImageView StackPane.alignment="CENTER_RIGHT">
<ImageView fx:id="portraitView" StackPane.alignment="CENTER_RIGHT" smooth="false">
<StackPane.margin>
<Insets right="12" />
</StackPane.margin>

View File

@@ -18,7 +18,7 @@
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane>
</ScrollPane>
<AnchorPane pickOnBounds="false">

View File

@@ -8,7 +8,7 @@
styleClass="transparent" type="StackPane"
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="192" cellHeight="160">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane>
</ScrollPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">

View File

@@ -22,9 +22,13 @@
<ComponentList depth="1">
<ComponentList fx:id="componentJava" title="%settings.java_dir" hasSubtitle="true"> <!-- Java Directory -->
<VBox fx:id="javaPane" spacing="8">
<HBox fx:id="javaPaneCustom" style="-fx-padding: 3;" spacing="3" alignment="CENTER_LEFT">
<VBox fx:id="javaPane" spacing="8" style="-fx-padding: 0 0 10 0;">
<BorderPane fx:id="javaPaneCustom" style="-fx-padding: 3;">
<left>
<JFXRadioButton fx:id="radioCustom" text="%settings.custom" />
</left>
<right>
<HBox spacing="3">
<JFXTextField fx:id="txtJavaDir" BorderPane.alignment="CENTER_RIGHT" />
<JFXButton fx:id="btnJavaSelect" onMouseClicked="#onExploreJavaDir">
<graphic>
@@ -32,6 +36,8 @@
</graphic>
</JFXButton>
</HBox>
</right>
</BorderPane>
</VBox>
</ComponentList>

View File

@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.auth
class AuthenticationException : Exception {
open class AuthenticationException : Exception {
constructor() : super() {}
constructor(message: String) : super(message) {}
constructor(message: String, cause: Throwable) : super(message, cause) {}

View File

@@ -0,0 +1,24 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil
import org.jackhuang.hmcl.auth.AuthenticationException
class InvalidTokenException(val account: YggdrasilAccount) : AuthenticationException()
class InvalidCredentialsException(val account: YggdrasilAccount) : AuthenticationException()

View File

@@ -36,7 +36,8 @@ class YggdrasilAccount private constructor(override val username: String): Accou
private var clientToken: String = UUID.randomUUID().toString()
private var isOnline: Boolean = false
private var userProperties = PropertyMap()
private var selectedProfile: GameProfile? = null
var selectedProfile: GameProfile? = null
private set
private var profiles: Array<GameProfile>? = null
private var userType: UserType = UserType.LEGACY
@@ -166,7 +167,11 @@ class YggdrasilAccount private constructor(override val username: String): Accou
if (response.error?.isNotBlank() ?: false) {
LOG.severe("Failed to log in, the auth server returned an error: " + response.error + ", message: " + response.errorMessage + ", cause: " + response.cause)
throw AuthenticationException("Request error: ${response.errorMessage}")
throw when (response.errorMessage) {
"Invalid token." -> InvalidTokenException(this)
"Invalid credentials. Invalid username or password." -> InvalidCredentialsException(this)
else -> AuthenticationException("Request error: ${response.errorMessage}")
}
}
return response

View File

@@ -34,7 +34,7 @@ import java.math.BigInteger
import java.util.logging.Level
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
override val scheduler: Scheduler = Scheduler.IO
var onFailed = EventManager<FailedEvent<URL>>()
private var rFile: RandomAccessFile? = null

View File

@@ -26,7 +26,7 @@ import java.net.URL
import java.nio.charset.Charset
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 val scheduler: Scheduler = Scheduler.IO
override val id = ID
override fun execute() {

View File

@@ -62,7 +62,7 @@ interface Scheduler {
val NEW_THREAD: Scheduler = object : Scheduler {
override fun schedule(block: Callable<Unit>) = CACHED_EXECUTOR.submit(block)
}
val IO_THREAD: Scheduler = object : Scheduler {
val IO: Scheduler = object : Scheduler {
override fun schedule(block: Callable<Unit>) = IO_EXECUTOR.submit(block)
}
val DEFAULT = NEW_THREAD

View File

@@ -58,7 +58,6 @@ abstract class Task {
@Throws(Exception::class)
abstract fun execute()
infix fun parallel(couple: Task): Task = ParallelTask(this, couple)
infix fun then(b: Task): Task = CoupleTask(this, { b }, true)
infix fun with(b: Task): Task = CoupleTask(this, { b }, false)
@@ -116,7 +115,7 @@ abstract class Task {
fun executor() = TaskExecutor().submit(this)
fun executor(taskListener: TaskListener) = TaskExecutor().submit(this).apply { this.taskListener = taskListener }
fun start() = executor().start()
fun subscribe(subscriber: Task) = executor().apply {
submit(subscriber).start()
}

View File

@@ -82,7 +82,7 @@ data class JavaVersion internal constructor(
val parsedVersion = parseVersion(thisVersion)
if (parsedVersion == UNKNOWN)
throw IOException("Java version '$thisVersion' can not be recognized")
return JavaVersion(file.parentFile, thisVersion, platform)
return JavaVersion(file, thisVersion, platform)
}
private fun fromExecutable(file: File, version: String) =

View File

@@ -26,14 +26,14 @@ import java.util.logging.*
import java.util.logging.Formatter
val LOG = Logger.getLogger("HMCL").apply {
level = Level.FINER
level = Level.FINEST
useParentHandlers = false
addHandler(FileHandler("hmcl.log").apply {
level = Level.FINER
level = Level.FINEST
formatter = DefaultFormatter
})
addHandler(ConsoleHandler().apply {
level = Level.FINER
level = Level.FINEST
formatter = DefaultFormatter
})
}

View File

@@ -50,10 +50,6 @@ open class ImmediateStringProperty(bean: Any, name: String, initialValue: String
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
}
open class ImmediateBooleanProperty(bean: Any, name: String, initialValue: Boolean): SimpleBooleanProperty(bean, name, initialValue) {
@@ -85,10 +81,6 @@ open class ImmediateBooleanProperty(bean: Any, name: String, initialValue: Boole
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
}
open class ImmediateIntegerProperty(bean: Any, name: String, initialValue: Int): SimpleIntegerProperty(bean, name, initialValue) {
@@ -120,10 +112,6 @@ open class ImmediateIntegerProperty(bean: Any, name: String, initialValue: Int):
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
}
open class ImmediateDoubleProperty(bean: Any, name: String, initialValue: Double): SimpleDoubleProperty(bean, name, initialValue) {
@@ -155,10 +143,6 @@ open class ImmediateDoubleProperty(bean: Any, name: String, initialValue: Double
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
}
open class ImmediateObjectProperty<T>(bean: Any, name: String, initialValue: T): SimpleObjectProperty<T>(bean, name, initialValue) {
@@ -185,13 +169,10 @@ open class ImmediateObjectProperty<T>(bean: Any, name: String, initialValue: T):
fun setChangedListener(listener: (T) -> Unit) {
myListener = listener
listener(get())
}
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
}