Java Selection

This commit is contained in:
huangyuhui
2017-08-18 13:58:49 +08:00
parent 99f60ea6e5
commit dc468f1a76
24 changed files with 465 additions and 134 deletions

View File

@@ -36,8 +36,9 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
private val forgeVersionList = dependencyManager.getVersionList("forge")
private val installer: File = File("forge-installer.jar").absoluteFile
lateinit var remote: RemoteVersion<*>
override val dependents: MutableCollection<Task> = mutableListOf()
override val dependencies: MutableCollection<Task> = mutableListOf()
override val dependents = mutableListOf<Task>()
override val dependencies = mutableListOf<Task>()
override val id = ID
init {
if (!forgeVersionList.loaded)
@@ -76,5 +77,7 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
check(installer.delete(), { "Unable to delete installer file $installer" })
}
companion object {
const val ID = "forge_install_task"
}
}

View File

@@ -37,7 +37,7 @@ import java.util.logging.Level
* @param resolvedVersion the <b>resolved</b> version
*/
class GameLibrariesTask(private val dependencyManager: DefaultDependencyManager, private val resolvedVersion: Version): Task() {
override val dependencies: MutableCollection<Task> = LinkedList()
override val dependencies = LinkedList<Task>()
override fun execute() {
for (library in resolvedVersion.libraries)
if (library.appliesToCurrentEnvironment) {
@@ -50,7 +50,7 @@ class GameLibrariesTask(private val dependencyManager: DefaultDependencyManager,
}
class GameLoggingDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
override val dependencies: MutableCollection<Task> = LinkedList()
override val dependencies = LinkedList<Task>()
override fun execute() {
val logging = version.logging?.get(DownloadType.CLIENT) ?: return
val file = dependencyManager.repository.getLoggingObject(version.id, version.actualAssetIndex.id, logging)
@@ -60,7 +60,7 @@ class GameLoggingDownloadTask(private val dependencyManager: DefaultDependencyMa
}
class GameAssetIndexDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
override val dependencies: MutableCollection<Task> = LinkedList()
override val dependencies = LinkedList<Task>()
override fun execute() {
val assetIndexInfo = version.actualAssetIndex
val assetDir = dependencyManager.repository.getAssetDirectory(version.id, assetIndexInfo.id)
@@ -76,7 +76,8 @@ class GameAssetRefreshTask(private val dependencyManager: DefaultDependencyManag
private val assetIndexTask = GameAssetIndexDownloadTask(dependencyManager, version)
private val assetIndexInfo = version.actualAssetIndex
private val assetIndexFile = dependencyManager.repository.getIndexFile(version.id, assetIndexInfo.id)
override val dependents: MutableCollection<Task> = LinkedList()
override val dependents = LinkedList<Task>()
override val id = ID
init {
if (!assetIndexFile.exists())
@@ -93,12 +94,16 @@ class GameAssetRefreshTask(private val dependencyManager: DefaultDependencyManag
}
result = res
}
companion object {
val ID = "game_asset_refresh_task"
}
}
class GameAssetDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
private val refreshTask = GameAssetRefreshTask(dependencyManager, version)
override val dependents: Collection<Task> = listOf(refreshTask)
override val dependencies: MutableCollection<Task> = LinkedList()
override val dependents = listOf(refreshTask)
override val dependencies = LinkedList<Task>()
override fun execute() {
val size = refreshTask.result?.size ?: 0
refreshTask.result?.forEach single@{ (file, assetObject) ->

View File

@@ -35,8 +35,9 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
private val remoteVersion: String): TaskResult<Version>() {
private val liteLoaderVersionList = dependencyManager.getVersionList("liteloader") as LiteLoaderVersionList
lateinit var remote: RemoteVersion<LiteLoaderRemoteVersionTag>
override val dependents: MutableCollection<Task> = mutableListOf()
override val dependencies: MutableCollection<Task> = mutableListOf()
override val dependents = mutableListOf<Task>()
override val dependencies = mutableListOf<Task>()
override val id = ID
init {
if (!liteLoaderVersionList.loaded)
@@ -70,4 +71,7 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
dependencies += GameLibrariesTask(dependencyManager, tempVersion)
}
companion object {
const val ID = "lite_loader_install_task"
}
}

View File

@@ -32,8 +32,9 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
private val remoteVersion: String): TaskResult<Version>() {
private val optiFineVersionList = dependencyManager.getVersionList("optifine")
lateinit var remote: RemoteVersion<*>
override val dependents: MutableCollection<Task> = mutableListOf()
override val dependencies: MutableCollection<Task> = mutableListOf()
override val dependents = mutableListOf<Task>()
override val dependencies = mutableListOf<Task>()
override val id = ID
init {
if (!optiFineVersionList.loaded)
@@ -45,6 +46,7 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote OptiFine version $gameVersion-$remoteVersion not found")
}
}
override fun execute() {
val library = Library(
groupId = "net.optifine",
@@ -73,4 +75,8 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
result = version.copy(libraries = merge(version.libraries, libraries), mainClass = mainClass, minecraftArguments = arg)
dependencies += GameLibrariesTask(dependencyManager, version.copy(libraries = libraries))
}
companion object {
const val ID = "optifine_install_task"
}
}

View File

@@ -68,7 +68,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
if (OS.CURRENT_OS == OS.OSX) {
res.add("-Xdock:name=Minecraft ${version.id}")
res.add("-Xdock:icon=" + repository.getAssetObject(version.id, version.actualAssetIndex.id, "icons/minecraft.icns").absolutePath);
res.add("-Xdock:icon=" + repository.getAssetObject(version.id, version.actualAssetIndex.id, "icons/minecraft.icns").absolutePath)
}
val logging = version.logging
@@ -107,8 +107,8 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
if (options.minMemory != null && options.minMemory > 0)
res.add("-Xms${options.minMemory}m")
res.add("-Dfml.ignoreInvalidMinecraftCertificates=true");
res.add("-Dfml.ignorePatchDiscrepancies=true");
res.add("-Dfml.ignoreInvalidMinecraftCertificates=true")
res.add("-Dfml.ignorePatchDiscrepancies=true")
}
// Classpath
@@ -161,9 +161,9 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
// Optional Minecraft arguments
if (options.height != null && options.width != null) {
res.add("--height");
res.add("--height")
res.add(options.height.toString())
res.add("--width");
res.add("--width")
res.add(options.width.toString())
}
@@ -180,16 +180,16 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
if (options.proxyHost != null && options.proxyHost.isNotBlank() &&
options.proxyPort != null && options.proxyPort.isNotBlank()) {
res.add("--proxyHost");
res.add("--proxyHost")
res.add(options.proxyHost)
res.add("--proxyPort");
res.add("--proxyPort")
res.add(options.proxyPort)
if (options.proxyUser != null && options.proxyUser.isNotBlank() &&
options.proxyPass != null && options.proxyPass.isNotBlank()) {
res.add("--proxyUser");
res.add(options.proxyUser);
res.add("--proxyPass");
res.add(options.proxyPass);
res.add("--proxyUser")
res.add(options.proxyUser)
res.add("--proxyPass")
res.add(options.proxyPass)
}
}
@@ -246,6 +246,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
fun launchAsync(): TaskResult<JavaProcess> {
return object : TaskResult<JavaProcess>() {
override val id = LAUNCH_ASYNC_ID
override fun execute() {
result = launch()
}
@@ -259,11 +260,11 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
throw IOException("Script file: $scriptFile cannot be created.")
scriptFile.bufferedWriter().use { writer ->
if (isWindows) {
writer.write("@echo off");
writer.write("@echo off")
writer.newLine()
writer.write("set APPDATA=" + options.gameDir.parent);
writer.write("set APPDATA=" + options.gameDir.parent)
writer.newLine()
writer.write("cd /D %APPDATA%");
writer.write("cd /D %APPDATA%")
writer.newLine()
}
if (options.precalledCommand != null && options.precalledCommand.isNotBlank()) {
@@ -287,4 +288,8 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
thread(name = "stderr-pump", isDaemon = isDaemon, block = StreamPump(javaProcess.process.errorStream, processListener::onErrorLog)::run)
thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(javaProcess.process, processListener::onExit)::run)
}
companion object {
const val LAUNCH_ASYNC_ID = "process"
}
}

View File

@@ -27,6 +27,7 @@ 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 id = ID
override fun execute() {
var exception: IOException? = null
@@ -63,4 +64,8 @@ class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Ch
if (exception != null)
throw exception
}
companion object {
const val ID = "http_get"
}
}

View File

@@ -17,8 +17,10 @@
*/
package org.jackhuang.hmcl.task
internal class SimpleTask @JvmOverloads constructor(private val runnable: () -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
import org.jackhuang.hmcl.util.AutoTypingMap
internal class SimpleTask @JvmOverloads constructor(private val runnable: (AutoTypingMap<String>) -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
override fun execute() {
runnable()
runnable(variables!!)
}
}

View File

@@ -49,6 +49,8 @@ abstract class Task {
var title: String = this.javaClass.toString()
var variables: AutoTypingMap<String>? = null
/**
* @see Thread.isInterrupted
* @throws InterruptedException if current thread is interrupted
@@ -119,12 +121,13 @@ abstract class Task {
submit(subscriber).start()
}
fun subscribe(scheduler: Scheduler = Scheduler.DEFAULT, closure: () -> Unit) = subscribe(task(scheduler, closure))
fun subscribe(scheduler: Scheduler = Scheduler.DEFAULT, closure: (AutoTypingMap<String>) -> Unit) = subscribe(task(scheduler, closure))
override fun toString(): String {
return title
}
}
fun task(scheduler: Scheduler = Scheduler.DEFAULT, closure: () -> Unit): Task = SimpleTask(closure, scheduler)
fun <V> task(callable: Callable<V>): TaskResult<V> = TaskCallable(callable)
fun task(scheduler: Scheduler = Scheduler.DEFAULT, closure: (AutoTypingMap<String>) -> Unit): Task = SimpleTask(closure, scheduler)
fun <V> taskResult(id: String, callable: Callable<V>): TaskResult<V> = TaskCallable(id, callable)
fun <V> taskResult(id: String, callable: (AutoTypingMap<String>) -> V): TaskResult<V> = TaskCallable2(id, callable)

View File

@@ -17,10 +17,17 @@
*/
package org.jackhuang.hmcl.task
import org.jackhuang.hmcl.util.AutoTypingMap
import java.util.concurrent.Callable
internal class TaskCallable<V>(private val callable: Callable<V>) : TaskResult<V>() {
internal class TaskCallable<V>(override val id: String, private val callable: Callable<V>) : TaskResult<V>() {
override fun execute() {
result = callable.call()
}
}
internal class TaskCallable2<V>(override val id: String, private val callable: (AutoTypingMap<String>) -> V) : TaskResult<V>() {
override fun execute() {
result = callable(variables!!)
}
}

View File

@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.task
import org.jackhuang.hmcl.util.AutoTypingMap
import org.jackhuang.hmcl.util.LOG
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicBoolean
@@ -30,6 +31,7 @@ class TaskExecutor() {
var canceled = false
private set
val totTask = AtomicInteger(0)
val variables = AutoTypingMap<String>(mutableMapOf())
private val taskQueue = ConcurrentLinkedQueue<Task>()
private val workerQueue = ConcurrentLinkedQueue<Future<*>>()
@@ -119,7 +121,10 @@ class TaskExecutor() {
if (!doDependentsSucceeded && t.reliant || canceled)
throw SilentException()
t.variables = variables
t.execute()
if (t is TaskResult<*>)
variables[t.id] = t.result
flag = true
if (!t.hidden)
LOG.finer("Task finished: ${t.title}")
@@ -142,6 +147,8 @@ class TaskExecutor() {
t.onDone(TaskEvent(source = this, task = t, failed = true))
taskListener?.onFailed(t)
}
} finally {
t.variables = null
}
return flag
}

View File

@@ -19,4 +19,9 @@ package org.jackhuang.hmcl.task
abstract class TaskResult<V> : Task() {
open var result: V? = null
/**
* The task id, will be stored as key of [variables]
*/
abstract val id: String
}

View File

@@ -0,0 +1,35 @@
/*
* 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.util
class AutoTypingMap<K>(private val impl: MutableMap<K, Any>) {
fun clear() = impl.clear()
@Suppress("UNCHECKED_CAST")
operator fun <V> get(key: K): V = impl[key] as V
operator fun set(key: K, value: Any?) {
if (value != null)
impl.set(key, value)
}
val values get() = impl.values
val keys get() = impl.keys
fun containsKey(key: K) = impl.containsKey(key)
fun remove(key: K) = impl.remove(key)
}

View File

@@ -21,17 +21,22 @@ import com.google.gson.annotations.SerializedName
import java.io.File
import java.io.IOException
import java.io.Serializable
import java.util.*
import java.util.regex.Pattern
data class JavaVersion internal constructor(
@SerializedName("location")
val binary: File,
val version: Int,
val longVersion: String,
val platform: Platform) : Serializable
{
val version = parseVersion(longVersion)
companion object {
private val regex = Pattern.compile("java version \"(?<version>[1-9]*\\.[1-9]*\\.[0-9]*(.*?))\"")
val JAVAS: Map<String, JavaVersion>
val UNKNOWN: Int = -1
val JAVA_5: Int = 50
val JAVA_6: Int = 60
@@ -54,12 +59,15 @@ data class JavaVersion internal constructor(
@Throws(IOException::class)
fun fromExecutable(file: File): JavaVersion {
var actualFile = file
var platform = Platform.BIT_32
var version: String? = null
if (actualFile.nameWithoutExtension == "javaw") // javaw will not output version information
actualFile = actualFile.absoluteFile.parentFile.resolve("java")
try {
val process = ProcessBuilder(file.absolutePath, "-version").start()
val process = ProcessBuilder(actualFile.absolutePath, "-version").start()
process.waitFor()
process.inputStream.bufferedReader().forEachLine { line ->
process.errorStream.bufferedReader().forEachLine { line ->
val m = regex.matcher(line)
if (m.find())
version = m.group("version")
@@ -74,10 +82,26 @@ 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, parsedVersion, platform)
return JavaVersion(file.parentFile, thisVersion, platform)
}
fun getJavaFile(home: File): File {
private fun fromExecutable(file: File, version: String) =
JavaVersion (
binary = file,
longVersion = version,
platform = Platform.UNKNOWN
)
@Throws(IOException::class)
fun fromJavaHome(home: File): JavaVersion {
return fromExecutable(getJavaFile(home))
}
private fun fromJavaHome(home: File, version: String): JavaVersion {
return fromExecutable(getJavaFile(home), version)
}
private fun getJavaFile(home: File): File {
val path = home.resolve("bin")
val javaw = path.resolve("javaw.exe")
if (OS.CURRENT_OS === OS.WINDOWS && javaw.isFile)
@@ -86,11 +110,80 @@ data class JavaVersion internal constructor(
return path.resolve("java")
}
fun fromCurrentEnvironment(): JavaVersion {
return JavaVersion(
binary = getJavaFile(File(System.getProperty("java.home"))),
version = parseVersion(System.getProperty("java.version")),
platform = Platform.PLATFORM)
private val currentJava: JavaVersion = JavaVersion(
binary = getJavaFile(File(System.getProperty("java.home"))),
longVersion = System.getProperty("java.version"),
platform = Platform.PLATFORM)
fun fromCurrentEnvironment() = currentJava
init {
val temp = mutableMapOf<String, JavaVersion>()
(when (OS.CURRENT_OS) {
OS.WINDOWS -> queryWindows()
OS.OSX -> queryMacintosh()
else -> emptyList<JavaVersion>() /* Cannot detect Java in linux. */
}).forEach { javaVersion ->
temp.put(javaVersion.longVersion, javaVersion)
}
JAVAS = temp
}
private fun queryMacintosh() = LinkedList<JavaVersion>().apply {
val currentJRE = File("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home")
if (currentJRE.exists())
this += fromJavaHome(currentJRE)
File("/Library/Java/JavaVirtualMachines/").listFiles()?.forEach { file ->
this += fromJavaHome(file.resolve("Contents/Home"))
}
}
private fun queryWindows() = LinkedList<JavaVersion>().apply {
ignoreException { this += queryRegisterKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\") }
ignoreException { this += queryRegisterKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\") }
}
private fun queryRegisterKey(location: String) = LinkedList<JavaVersion>().apply {
querySubFolders(location).forEach { java ->
val s = java.count { it == '.' }
if (s > 1) {
val home = queryRegisterValue(java, "JavaHome")
if (home != null)
this += fromJavaHome(File(home))
}
}
}
private fun querySubFolders(location: String) = LinkedList<String>().apply {
val cmd = arrayOf("cmd", "/c", "reg", "query", location)
val process = Runtime.getRuntime().exec(cmd)
process.waitFor()
process.inputStream.bufferedReader().readLines().forEach { s ->
if (s.startsWith(location) && s != location)
this += s
}
}
private fun queryRegisterValue(location: String, name: String): String? {
val cmd = arrayOf("cmd", "/c", "reg", "query", location, "/v", name)
var last = false
val process = Runtime.getRuntime().exec(cmd)
process.waitFor()
process.inputStream.bufferedReader().readLines().forEach { s ->
if (s.isNotBlank()) {
if (last && s.trim().startsWith(name)) {
var begins = s.indexOf(name)
if (begins > 0) {
val s2 = s.substring(begins + name.length)
begins = s2.indexOf("REG_SZ")
if (begins > 0)
return s2.substring(begins + "REG_SZ".length).trim()
}
}
if (s.trim() == location)
last = true
}
}
return null
}
}
}

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.util.property
import javafx.beans.property.*
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
open class ImmediateStringProperty(bean: Any, name: String, initialValue: String): SimpleStringProperty(bean, name, initialValue) {
@@ -27,10 +28,6 @@ open class ImmediateStringProperty(bean: Any, name: String, initialValue: String
super.set(newValue)
}
protected fun superSet(newValue: String) {
super.set(newValue)
}
override fun bind(newObservable: ObservableValue<out String>) {
super.get()
super.bind(newObservable)
@@ -41,30 +38,17 @@ open class ImmediateStringProperty(bean: Any, name: String, initialValue: String
super.unbind()
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
}
open class ImmediateNullableStringProperty(bean: Any, name: String, initialValue: String?): SimpleStringProperty(bean, name, initialValue) {
override fun set(newValue: String?) {
super.get()
super.set(newValue)
private var myListener: (String) -> Unit = {}
private val changeListener = ChangeListener<String> { _, _, newValue ->
myListener(newValue)
}
protected fun superSet(newValue: String?) {
super.set(newValue)
fun setChangedListener(listener: (String) -> Unit) {
myListener = listener
}
override fun bind(newObservable: ObservableValue<out String?>) {
super.get()
super.bind(newObservable)
}
override fun unbind() {
super.get()
super.unbind()
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
@@ -79,10 +63,6 @@ open class ImmediateBooleanProperty(bean: Any, name: String, initialValue: Boole
super.set(newValue)
}
protected fun superSet(newValue: Boolean) {
super.set(newValue)
}
override fun bind(rawObservable: ObservableValue<out Boolean>?) {
super.get()
super.bind(rawObservable)
@@ -93,6 +73,19 @@ open class ImmediateBooleanProperty(bean: Any, name: String, initialValue: Boole
super.unbind()
}
private var myListener: (Boolean) -> Unit = {}
private val changeListener = ChangeListener<Boolean> { _, _, newValue ->
myListener(newValue)
}
fun setChangedListener(listener: (Boolean) -> Unit) {
myListener = listener
}
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
@@ -105,10 +98,6 @@ open class ImmediateIntegerProperty(bean: Any, name: String, initialValue: Int):
super.set(newValue)
}
protected fun superSet(newValue: Int) {
super.set(newValue)
}
override fun bind(rawObservable: ObservableValue<out Number>) {
super.get()
super.bind(rawObservable)
@@ -119,6 +108,19 @@ open class ImmediateIntegerProperty(bean: Any, name: String, initialValue: Int):
super.unbind()
}
private var myListener: (Int) -> Unit = {}
private val changeListener = ChangeListener<Number> { _, _, newValue ->
myListener(newValue.toInt())
}
fun setChangedListener(listener: (Int) -> Unit) {
myListener = listener
}
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
@@ -131,10 +133,6 @@ open class ImmediateDoubleProperty(bean: Any, name: String, initialValue: Double
super.set(newValue)
}
protected fun superSet(newValue: Double) {
super.set(newValue)
}
override fun bind(rawObservable: ObservableValue<out Number>) {
super.get()
super.bind(rawObservable)
@@ -145,6 +143,19 @@ open class ImmediateDoubleProperty(bean: Any, name: String, initialValue: Double
super.unbind()
}
private var myListener: (Double) -> Unit = {}
private val changeListener = ChangeListener<Number> { _, _, newValue ->
myListener(newValue.toDouble())
}
fun setChangedListener(listener: (Double) -> Unit) {
myListener = listener
}
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}
@@ -167,6 +178,19 @@ open class ImmediateObjectProperty<T>(bean: Any, name: String, initialValue: T):
super.unbind()
}
private var myListener: (T) -> Unit = {}
private val changeListener = ChangeListener<T> { _, _, newValue ->
myListener(newValue)
}
fun setChangedListener(listener: (T) -> Unit) {
myListener = listener
}
init {
addListener(changeListener)
}
public override fun fireValueChangedEvent() {
super.fireValueChangedEvent()
}