Commit 3844ef33 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

parents 885288d1 cb50a337
...@@ -8,16 +8,10 @@ jobs: ...@@ -8,16 +8,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- name: Setup Java JDK - name: setup-android
uses: actions/setup-java@v1.3.0 uses: msfjarvis/setup-android@0.2
with: with:
# The Java version to make available on the path. Takes a whole or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x) # Gradle tasks to run - If you want to run ./gradlew assemble, specify assemble here.
java-version: 11 gradleTasks: build -x mirai-core:jvmTest
# The package type (jre, jdk, jdk+fx)
java-package: jdk+fx
- name: Gradle Command
uses: eskatos/gradle-command-action@v1
with:
# Gradle command line arguments, see gradle --help
arguments: build -x mirai-core:jvmTest
plugins { plugins {
id("kotlinx-serialization") id("kotlinx-serialization")
id("org.openjfx.javafxplugin") version "0.0.8"
id("kotlin") id("kotlin")
id("java") id("java")
} }
javafx {
version = "11"
modules = listOf("javafx.controls")
//mainClassName = "Application"
}
apply(plugin = "com.github.johnrengelman.shadow") apply(plugin = "com.github.johnrengelman.shadow")
...@@ -29,6 +35,7 @@ dependencies { ...@@ -29,6 +35,7 @@ dependencies {
api(project(":mirai-console")) api(project(":mirai-console"))
runtimeOnly(files("../mirai-core-qqandroid/build/classes/kotlin/jvm/main")) runtimeOnly(files("../mirai-core-qqandroid/build/classes/kotlin/jvm/main"))
api(group = "no.tornado", name = "tornadofx", version = "1.7.19") api(group = "no.tornado", name = "tornadofx", version = "1.7.19")
api(group = "com.jfoenix", name = "jfoenix", version = "9.0.8")
api("org.bouncycastle:bcprov-jdk15on:1.64") api("org.bouncycastle:bcprov-jdk15on:1.64")
// classpath is not set correctly by IDE // classpath is not set correctly by IDE
} }
......
package net.mamoe.mirai.console.graphical.util
import com.jfoenix.controls.*
import javafx.beans.value.ObservableValue
import javafx.collections.ObservableList
import javafx.event.EventTarget
import javafx.scene.Node
import javafx.scene.control.*
import tornadofx.SortedFilteredList
import tornadofx.attachTo
import tornadofx.bind
internal fun EventTarget.jfxTabPane(op: TabPane.() -> Unit = {}) = JFXTabPane().attachTo(this, op)
internal fun EventTarget.jfxButton(text: String = "", graphic: Node? = null, op: Button.() -> Unit = {}) =
JFXButton(text).attachTo(this, op) {
if (graphic != null) it.graphic = graphic
}
fun EventTarget.jfxTextfield(value: String? = null, op: TextField.() -> Unit = {}) = JFXTextField().attachTo(this, op) {
if (value != null) it.text = value
}
fun EventTarget.jfxTextfield(property: ObservableValue<String>, op: TextField.() -> Unit = {}) = jfxTextfield().apply {
bind(property)
op(this)
}
fun EventTarget.jfxPasswordfield(value: String? = null, op: TextField.() -> Unit = {}) = JFXPasswordField().attachTo(this, op) {
if (value != null) it.text = value
}
fun EventTarget.jfxPasswordfield(property: ObservableValue<String>, op: TextField.() -> Unit = {}) = jfxPasswordfield().apply {
bind(property)
op(this)
}
internal fun <T> EventTarget.jfxListView(values: ObservableList<T>? = null, op: ListView<T>.() -> Unit = {}) =
JFXListView<T>().attachTo(this, op) {
if (values != null) {
if (values is SortedFilteredList<T>) values.bindTo(it)
else it.items = values
}
}
package net.mamoe.mirai.console.graphical.view package net.mamoe.mirai.console.graphical.view
import com.jfoenix.controls.JFXTextField
import javafx.beans.property.SimpleStringProperty import javafx.beans.property.SimpleStringProperty
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import net.mamoe.mirai.console.graphical.util.jfxButton
import net.mamoe.mirai.console.graphical.util.jfxPasswordfield
import net.mamoe.mirai.console.graphical.util.jfxTextfield
import tornadofx.* import tornadofx.*
class LoginFragment : Fragment() { class LoginFragment : Fragment() {
private val controller = find<MiraiGraphicalUIController>(FX.defaultScope) private val controller = find<MiraiGraphicalUIController>(FX.defaultScope)
private val qq = SimpleStringProperty() private val qq = SimpleStringProperty("0")
private val psd = SimpleStringProperty() private val psd = SimpleStringProperty("")
override val root = form { override val root = form {
fieldset("登录") { fieldset("登录") {
field("QQ") { field("QQ") {
textfield(qq) jfxTextfield(qq)
} }
field("密码") { field("密码") {
passwordfield(psd) jfxPasswordfield(psd)
}
button("登录").action {
controller.login(qq.value, psd.value)
close()
} }
} }
jfxButton("登录").action {
controller.login(qq.value, psd.value)
close()
}
} }
} }
\ No newline at end of file
package net.mamoe.mirai.console.graphical.view package net.mamoe.mirai.console.graphical.view
import com.jfoenix.controls.JFXListCell
import javafx.geometry.Insets
import javafx.geometry.Pos
import javafx.scene.control.Tab
import javafx.scene.control.TabPane import javafx.scene.control.TabPane
import javafx.stage.Modality import javafx.scene.image.Image
import net.mamoe.mirai.Bot import javafx.scene.paint.Color
import javafx.scene.text.FontWeight
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import net.mamoe.mirai.console.graphical.model.BotModel
import net.mamoe.mirai.console.graphical.util.jfxButton
import net.mamoe.mirai.console.graphical.util.jfxListView
import net.mamoe.mirai.console.graphical.util.jfxTabPane
import tornadofx.* import tornadofx.*
import java.io.FileInputStream
class PrimaryView : View() { class PrimaryView : View() {
...@@ -12,45 +22,87 @@ class PrimaryView : View() { ...@@ -12,45 +22,87 @@ class PrimaryView : View() {
override val root = borderpane { override val root = borderpane {
top = menubar { prefWidth = 1000.0
menu("机器人") { prefHeight = 650.0
item("登录").action {
find<LoginFragment>().openModal( left = vbox {
modality = Modality.APPLICATION_MODAL,
resizable = false imageview(Image(PrimaryView::class.java.classLoader.getResourceAsStream("logo.png")))
)
// bot list
jfxListView(controller.botList) {
fitToParentSize()
setCellFactory {
object : JFXListCell<BotModel>() {
var tab: Tab? = null
init {
onDoubleClick {
if (tab == null) {
(center as TabPane).tab(item.uin.toString()) {
listview(item.logHistory)
onDoubleClick { close() }
tab = this
}
} else {
(center as TabPane).tabs.add(tab)
}
tab?.select()
}
}
override fun updateItem(item: BotModel?, empty: Boolean) {
super.updateItem(item, empty)
if (item != null && !empty) {
graphic = null
text = item.uin.toString()
} else {
graphic = null
text = ""
}
}
}
} }
} }
}
left = listview(controller.botList) {
fitToParentHeight()
cellFormat { hbox {
padding = Insets(10.0)
spacing = 10.0
alignment = Pos.CENTER
graphic = vbox { jfxButton("L").action {
label(it.uin.toString()) find<LoginFragment>().openModal()
// label(stringBinding(it.botProperty) { if (value != null) value.nick else "登陆中" })
} }
jfxButton("P")
jfxButton("S")
onDoubleClick {
(center as TabPane).tab(it.uin.toString()) {
listview(it.logHistory)
isClosable = true style { backgroundColor += c("00BCD4") }
select() children.style(true) {
} backgroundColor += c("00BCD4")
fontSize = 15.px
fontWeight = FontWeight.BOLD
textFill = Color.WHITE
borderRadius += box(25.px)
backgroundRadius += box(25.px)
} }
} }
} }
center = tabpane { center = jfxTabPane {
tab("Main") { tab("Main") {
listview(controller.mainLog) listview(controller.mainLog) {
isClosable = false fitToParentSize()
cellFormat {
graphic = label(it) {
maxWidthProperty().bind(this@listview.widthProperty())
isWrapText = true
}
}
}
} }
} }
} }
}
}
\ No newline at end of file
...@@ -28,6 +28,8 @@ import kotlin.contracts.contract ...@@ -28,6 +28,8 @@ import kotlin.contracts.contract
/** /**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话. * 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
*
* @see CoroutineScope.incoming
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
...@@ -49,6 +51,8 @@ inline fun <R> CoroutineScope.subscribeMessages(crossinline listeners: MessageSu ...@@ -49,6 +51,8 @@ inline fun <R> CoroutineScope.subscribeMessages(crossinline listeners: MessageSu
/** /**
* 订阅来自所有 [Bot] 的所有群消息事件 * 订阅来自所有 [Bot] 的所有群消息事件
*
* @see CoroutineScope.incoming
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
...@@ -65,6 +69,8 @@ inline fun <R> CoroutineScope.subscribeGroupMessages(crossinline listeners: Mess ...@@ -65,6 +69,8 @@ inline fun <R> CoroutineScope.subscribeGroupMessages(crossinline listeners: Mess
/** /**
* 订阅来自所有 [Bot] 的所有好友消息事件 * 订阅来自所有 [Bot] 的所有好友消息事件
*
* @see CoroutineScope.incoming
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
...@@ -81,6 +87,8 @@ inline fun <R> CoroutineScope.subscribeFriendMessages(crossinline listeners: Mes ...@@ -81,6 +87,8 @@ inline fun <R> CoroutineScope.subscribeFriendMessages(crossinline listeners: Mes
/** /**
* 订阅来自这个 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话. * 订阅来自这个 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
*
* @see CoroutineScope.incoming
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
...@@ -97,6 +105,8 @@ inline fun <R> Bot.subscribeMessages(crossinline listeners: MessageSubscribersBu ...@@ -97,6 +105,8 @@ inline fun <R> Bot.subscribeMessages(crossinline listeners: MessageSubscribersBu
/** /**
* 订阅来自这个 [Bot] 的所有群消息事件 * 订阅来自这个 [Bot] 的所有群消息事件
*
* @see CoroutineScope.incoming
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
...@@ -113,6 +123,8 @@ inline fun <R> Bot.subscribeGroupMessages(crossinline listeners: MessageSubscrib ...@@ -113,6 +123,8 @@ inline fun <R> Bot.subscribeGroupMessages(crossinline listeners: MessageSubscrib
/** /**
* 订阅来自这个 [Bot] 的所有好友消息事件. * 订阅来自这个 [Bot] 的所有好友消息事件.
*
* @see CoroutineScope.incoming
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
...@@ -129,9 +141,15 @@ inline fun <R> Bot.subscribeFriendMessages(crossinline listeners: MessageSubscri ...@@ -129,9 +141,15 @@ inline fun <R> Bot.subscribeFriendMessages(crossinline listeners: MessageSubscri
/** /**
* 返回一个指定事件的接收通道 * 返回一个指定事件的接收通道
*
* @param capacity 同 [Channel] 的参数, 参见 [Channel.Factory] 中的常量.
*
* @see subscribeFriendMessages
* @see subscribeMessages
* @see subscribeGroupMessages
*/ */
inline fun <reified E : Event> Bot.incoming(): ReceiveChannel<E> { inline fun <reified E : Event> CoroutineScope.incoming(capacity: Int = Channel.RENDEZVOUS): ReceiveChannel<E> {
return Channel<E>(8).apply { return Channel<E>(capacity).apply {
subscribeAlways<E> { subscribeAlways<E> {
send(this) send(this)
} }
......
...@@ -90,6 +90,15 @@ internal object EventListenerManager { ...@@ -90,6 +90,15 @@ internal object EventListenerManager {
private val lock = atomic(false) private val lock = atomic(false)
private fun setLockValue(value: Boolean) {
lock.value = value
}
@Suppress("BooleanLiteralArgument")
private fun trySetLockTrue(): Boolean {
return lock.compareAndSet(false, true)
}
@Suppress("UNCHECKED_CAST", "BooleanLiteralArgument") @Suppress("UNCHECKED_CAST", "BooleanLiteralArgument")
internal tailrec fun <E : Event> get(clazz: KClass<out E>): EventListeners<E> { internal tailrec fun <E : Event> get(clazz: KClass<out E>): EventListeners<E> {
registries.forEach { registries.forEach {
...@@ -97,10 +106,10 @@ internal object EventListenerManager { ...@@ -97,10 +106,10 @@ internal object EventListenerManager {
return it.listeners as EventListeners<E> return it.listeners as EventListeners<E>
} }
} }
if (lock.compareAndSet(false, true)) { if (trySetLockTrue()) {
val registry = Registry(clazz, EventListeners()) val registry = Registry(clazz, EventListeners())
registries.addLast(registry) registries.addLast(registry)
lock.value = false setLockValue(false)
return registry.listeners as EventListeners<E> return registry.listeners as EventListeners<E>
} }
return get(clazz) return get(clazz)
......
...@@ -28,8 +28,10 @@ import net.mamoe.mirai.message.sendAsImageTo ...@@ -28,8 +28,10 @@ import net.mamoe.mirai.message.sendAsImageTo
import net.mamoe.mirai.qqandroid.Bot import net.mamoe.mirai.qqandroid.Bot
import net.mamoe.mirai.qqandroid.QQAndroid import net.mamoe.mirai.qqandroid.QQAndroid
import net.mamoe.mirai.utils.FileBasedDeviceInfo import net.mamoe.mirai.utils.FileBasedDeviceInfo
import net.mamoe.mirai.utils.MiraiInternalAPI
import java.io.File import java.io.File
@MiraiInternalAPI
private fun readTestAccount(): BotAccount? { private fun readTestAccount(): BotAccount? {
val file = File("testAccount.txt") val file = File("testAccount.txt")
if (!file.exists() || !file.canRead()) { if (!file.exists() || !file.canRead()) {
...@@ -59,7 +61,7 @@ suspend fun main() { ...@@ -59,7 +61,7 @@ suspend fun main() {
bot.messageDSL() bot.messageDSL()
directlySubscribe(bot) directlySubscribe(bot)
bot.network.awaitDisconnection()//等到直到断开连接 bot.join()//等到直到断开连接
} }
/** /**
......
...@@ -176,7 +176,7 @@ suspend fun main() { ...@@ -176,7 +176,7 @@ suspend fun main() {
} }
bot.network.awaitDisconnection()//等到直到断开连接 bot.join()//等到直到断开连接
} }
private fun newTestTempFile(filename: String = "${UUID.randomUUID()}", suffix: String = ".tmp"): File = private fun newTestTempFile(filename: String = "${UUID.randomUUID()}", suffix: String = ".tmp"): File =
......
...@@ -44,7 +44,6 @@ include(':mirai-core-qqandroid') ...@@ -44,7 +44,6 @@ include(':mirai-core-qqandroid')
include(':mirai-japt') include(':mirai-japt')
include(':mirai-console') include(':mirai-console')
include(':mirai-console-graphical')
include(':mirai-console-terminal') include(':mirai-console-terminal')
//include(':mirai-api') //include(':mirai-api')
include(':mirai-api-http') include(':mirai-api-http')
...@@ -54,24 +53,23 @@ include(':mirai-demos:mirai-demo-java') ...@@ -54,24 +53,23 @@ include(':mirai-demos:mirai-demo-java')
include(':mirai-plugins') include(':mirai-plugins')
include(':mirai-plugins:image-sender') include(':mirai-plugins:image-sender')
def javaVersion = System.getProperty("java.version") try{
def versionPos = javaVersion.indexOf(".") def javaVersion = System.getProperty("java.version")
if (versionPos==-1) versionPos = javaVersion.indexOf("-") def versionPos = javaVersion.indexOf(".")
if (versionPos==-1){ if (versionPos==-1) versionPos = javaVersion.indexOf("-")
println("jdk version unknown") if (versionPos==-1){
}else{ println("jdk version unknown")
def javaVersionNum = javaVersion.substring(0, versionPos).toInteger() }else{
if (javaVersionNum >= 11) { def javaVersionNum = javaVersion.substring(0, versionPos).toInteger()
println("jdk版本为 "+ javaVersionNum) if (javaVersionNum >= 11) {
//include(':mirai-debug') println("jdk版本为 "+ javaVersionNum)
} else { include(':mirai-console-graphical')
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 最低需要 JDK 11 才能引入模块 `:mirai-debug`") } else {
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 最低需要 JDK 11 才能引入模块 `:mirai-debug`")
}
} }
} }catch(Exception ignored){
project(':mirai-demos:mirai-demo-1').projectDir = file('mirai-demos/mirai-demo-1') }
project(':mirai-demos:mirai-demo-gentleman').projectDir = file('mirai-demos/mirai-demo-gentleman')
project(':mirai-demos:mirai-demo-java').projectDir = file('mirai-demos/mirai-demo-java')
project(':mirai-plugins:image-sender').projectDir = file('mirai-plugins/image-sender')
enableFeaturePreview('GRADLE_METADATA') enableFeaturePreview('GRADLE_METADATA')
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment