Remove mirai-console-graphical

This commit is contained in:
Him188 2021-08-13 16:00:57 +08:00
parent 00d7430f06
commit 53149d1d36
28 changed files with 0 additions and 1894 deletions

View File

@ -1,6 +0,0 @@
### Mirai Console Graphical
支持windows/mac
有正式UI界面实现的CONSOLE
优点: 适合新手/完全不懂编程的/界面美丽
缺点: 不能在linux服务器运行
所使用插件系统与terminal版本一致 可以来回切换

View File

@ -1,73 +0,0 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
plugins {
id("kotlinx-serialization")
id("org.openjfx.javafxplugin") version "0.0.8"
id("kotlin")
id("java")
`maven-publish`
}
javafx {
version = "13.0.2"
modules = listOf("javafx.controls")
//mainClassName = "Application"
}
apply(plugin = "com.github.johnrengelman.shadow")
/*
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>() {
manifest {
attributes["Main-Class"] = "net.mamoe.mirai.console.graphical.MiraiGraphicalLoader"
}
}
*/
version = Versions.Mirai.consoleGraphical
description = "Graphical frontend for mirai-console"
dependencies {
compileOnly("net.mamoe:mirai-core:${Versions.core}")
implementation(project(":mirai-console"))
api(group = "no.tornado", name = "tornadofx", version = "1.7.19")
api(group = "com.jfoenix", name = "jfoenix", version = "9.0.8")
testApi(project(":mirai-console"))
testApi(kotlinx("coroutines-core", Versions.coroutines))
testApi(group = "org.yaml", name = "snakeyaml", version = "1.25")
testApi("net.mamoe:mirai-core:${Versions.core}")
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
}
kotlin {
sourceSets {
all {
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.progressiveMode = true
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
}
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
@Suppress("DEPRECATION")
val sourcesJar by tasks.registering(Jar::class) {
classifier = "sources"
from(sourceSets.main.get().allSource)
}

View File

@ -1,35 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical
import kotlinx.coroutines.cancel
import net.mamoe.mirai.console.MiraiConsole
import tornadofx.launch
import kotlin.concurrent.thread
class MiraiConsoleGraphicalLoader {
companion object {
internal var coreVersion :String = "0.0.0"
internal var consoleVersion: String = "0.0.0"
@JvmStatic
fun load(
coreVersion: String,
consoleVersion: String
) {
this.coreVersion = coreVersion
this.consoleVersion = consoleVersion
Runtime.getRuntime().addShutdownHook(thread(start = false) {
MiraiConsole.cancel()
})
launch<MiraiGraphicalUI>()
}
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalFrontEndController
import net.mamoe.mirai.console.graphical.stylesheet.PrimaryStyleSheet
import net.mamoe.mirai.console.graphical.view.Decorator
import tornadofx.App
import tornadofx.find
import kotlin.system.exitProcess
//object MiraiGraphicalLoader {
// @JvmStatic
// fun main(args: Array<String>) {
// launch<MiraiGraphicalUI>(args)
// }
//}
class MiraiGraphicalUI : App(Decorator::class, PrimaryStyleSheet::class) {
override fun init() {
super.init()
MiraiConsole.start(
find<MiraiGraphicalFrontEndController>(),
MiraiConsoleGraphicalLoader.coreVersion,
MiraiConsoleGraphicalLoader.consoleVersion
)
}
override fun stop() {
super.stop()
MiraiConsole.stop()
exitProcess(0)
}
}

View File

@ -1,214 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.controller
import javafx.application.Platform
import javafx.collections.ObservableList
import javafx.stage.Modality
import javafx.stage.StageStyle
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.suspendCancellableCoroutine
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandManager.runCommand
import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.graphical.event.ReloadEvent
import net.mamoe.mirai.console.graphical.model.*
import net.mamoe.mirai.console.graphical.view.dialog.InputDialog
import net.mamoe.mirai.console.graphical.view.dialog.VerificationCodeFragment
import net.mamoe.mirai.console.plugins.PluginManager
import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
import net.mamoe.mirai.network.CustomLoginFailedException
import net.mamoe.mirai.utils.LoginSolver
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.SimpleLogger
import net.mamoe.mirai.utils.SimpleLogger.LogPriority
import tornadofx.Controller
import tornadofx.Scope
import tornadofx.find
import tornadofx.observableListOf
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.List
import kotlin.collections.forEach
import kotlin.collections.mutableMapOf
import kotlin.collections.set
import kotlin.coroutines.resume
class MiraiGraphicalFrontEndController : Controller(), MiraiConsoleFrontEnd {
private val settingModel = find<GlobalSettingModel>()
private val loginSolver = GraphicalLoginSolver()
private val cache = mutableMapOf<Long, BotModel>()
val mainLog = observableListOf<Pair<String, String>>()
val botList = observableListOf<BotModel>()
val pluginList: ObservableList<PluginModel> by lazy(::getPluginsFromConsole)
private val consoleInfo = ConsoleInfo()
internal val sdf by lazy { SimpleDateFormat("HH:mm:ss") }
init {
// 监听插件重载事件以重新从console获取插件列表
subscribe<ReloadEvent> {
pluginList.clear()
// 不能直接赋值pluginList已经被bind不能更换对象
pluginList.addAll(getPluginsFromConsole())
}
}
fun login(qq: String, psd: String) {
CommandManager.runCommand(ConsoleCommandSender, "login $qq $psd")
}
fun logout(qq: Long) {
cache.remove(qq)?.apply {
botList.remove(this)
if (botProperty.value != null && bot.isActive) {
bot.close()
}
}
}
fun sendCommand(command: String) = runCommand(ConsoleCommandSender, command)
private val mainLogger = SimpleLogger(null) { priority: LogPriority, message: String?, e: Throwable? ->
Platform.runLater {
val time = sdf.format(Date())
mainLog.apply {
add("[$time] $message" to priority.name)
trim()
}
}
}
override fun loggerFor(identity: Long): MiraiLogger {
return if (identity == 0L) return mainLogger
else cache[identity]?.logger ?: kotlin.error("bot not found: $identity")
}
override fun prePushBot(identity: Long) = Platform.runLater {
if (!cache.containsKey(identity)) {
BotModel(identity).also {
cache[identity] = it
botList.add(it)
}
}
}
override fun pushBot(bot: Bot) = Platform.runLater {
cache[bot.id]?.bot = bot
}
override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) {
Platform.runLater {
consoleInfo.consoleVersion = consoleVersion
consoleInfo.consoleBuild = consoleBuild
consoleInfo.coreVersion = coreVersion
}
}
override suspend fun requestInput(hint: String): String =
suspendCancellableCoroutine {
Platform.runLater {
it.resume(InputDialog(hint).open())
}
}
override fun pushBotAdminStatus(identity: Long, admins: List<Long>) = Platform.runLater {
cache[identity]?.admins?.setAll(admins)
}
override fun createLoginSolver(): LoginSolver = loginSolver
private fun getPluginsFromConsole(): ObservableList<PluginModel> =
PluginManager.getAllPluginDescriptions().map(::PluginModel).toObservable()
fun checkUpdate(plugin: PluginModel) {
pluginList.forEach {
if (it.name == plugin.name && it.author == plugin.author) {
if (plugin.version > it.version) {
it.expired = true
return
}
}
}
}
/**
* return `true` when command is ambiguous
*/
fun checkAmbiguous(plugin: PluginModel): Boolean {
plugin.insight?.commands?.forEach { name ->
CommandManager.commands.forEach {
if (name == it.name) return true
}
} ?: return false
return false
}
internal fun ObservableList<*>.trim() {
while (size > settingModel.item.maxLongNum) {
this.removeAt(0)
}
}
fun reloadPlugins() {
with(PluginManager) {
reloadPlugins()
}
fire(ReloadEvent) // 广播插件重载事件
}
}
class GraphicalLoginSolver : LoginSolver() {
override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? {
val code = VerificationCodeModel(VerificationCode(data))
// UI必须在UI线程执行requestInput在协程种被调用
Platform.runLater {
find<VerificationCodeFragment>(Scope(code)).openModal(
stageStyle = StageStyle.UNDECORATED,
escapeClosesWindow = false,
modality = Modality.NONE,
resizable = false,
block = true
)
}
// 阻塞协程直到验证码已经输入
while (code.isDirty || code.code.value == null) {
delay(1000)
if (code.code.value === VerificationCodeFragment.MAGIC_KEY) {
throw LoginCancelledManuallyException()
}
}
return code.code.value
}
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class LoginCancelledManuallyException : CustomLoginFailedException(true, "取消登录")

View File

@ -1,14 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.event
import tornadofx.FXEvent
object ReloadEvent : FXEvent()

View File

@ -1,45 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.model
import javafx.beans.property.SimpleObjectProperty
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalFrontEndController
import net.mamoe.mirai.utils.SimpleLogger
import tornadofx.*
import java.util.*
class BotModel(val uin: Long) {
val botProperty = SimpleObjectProperty<Bot>(null)
var bot: Bot by botProperty
val logHistory = observableListOf<Pair<String, String>>()
val logger: SimpleLogger =
SimpleLogger(uin.toString()) { priority: SimpleLogger.LogPriority, message: String?, e: Throwable? ->
val frontend = find<MiraiGraphicalFrontEndController>()
frontend.run {
logHistory.apply {
val time = sdf.format(Date())
add("[$time] $uin $message" to priority.name)
trim()
}
}
}
val admins = observableListOf<Long>()
}
class BotViewModel(botModel: BotModel? = null) : ItemViewModel<BotModel>(botModel) {
val bot = bind(BotModel::botProperty)
val logHistory = bind(BotModel::logHistory)
val admins = bind(BotModel::admins)
}

View File

@ -1,26 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.model
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.setValue
class ConsoleInfo {
val consoleVersionProperty = SimpleStringProperty()
var consoleVersion by consoleVersionProperty
val consoleBuildProperty = SimpleStringProperty()
var consoleBuild by consoleBuildProperty
val coreVersionProperty = SimpleStringProperty()
var coreVersion by coreVersionProperty
}

View File

@ -1,26 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.model
import javafx.beans.property.SimpleLongProperty
import tornadofx.ItemViewModel
import tornadofx.getValue
import tornadofx.setValue
class GlobalSetting {
val maxLogNumProperty = SimpleLongProperty(4096)
var maxLongNum: Long by maxLogNumProperty
}
class GlobalSettingModel(setting: GlobalSetting) : ItemViewModel<GlobalSetting>(setting) {
constructor() : this(GlobalSetting())
val maxLogNum = bind(GlobalSetting::maxLogNumProperty)
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.model
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleStringProperty
import net.mamoe.mirai.console.center.PluginCenter
import net.mamoe.mirai.console.plugins.PluginDescription
import tornadofx.getValue
import tornadofx.setValue
class PluginModel(
val name: String,
val version: String,
val author: String,
val description: String,
var insight: PluginCenter.PluginInsight? = null
) : RecursiveTreeObject<PluginModel>() {
constructor(plugin: PluginDescription) : this(plugin.name, plugin.version, plugin.author, plugin.info)
val nameProperty = SimpleStringProperty(this, "nameProperty", name)
val versionProperty = SimpleStringProperty(this, "versionProperty", version)
val authorProperty = SimpleStringProperty(this, "authorProperty", author)
val descriptionProperty = SimpleStringProperty(this, "descriptionProperty", description)
val enabledProperty = SimpleBooleanProperty(this, "enabledProperty")
var enabled by enabledProperty
val expiredProperty = SimpleBooleanProperty(this, "expiredProperty", false)
var expired by expiredProperty
}

View File

@ -1,35 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.model
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.ItemViewModel
import tornadofx.getValue
import tornadofx.setValue
class VerificationCode(data: ByteArray = ByteArray(0)) {
val codeProperty = SimpleStringProperty(null)
var code: String? by codeProperty
val dataProperty: SimpleObjectProperty<ByteArray> = SimpleObjectProperty()
val data: ByteArray by dataProperty
init {
dataProperty.set(data)
}
}
class VerificationCodeModel(code: VerificationCode) : ItemViewModel<VerificationCode>(code) {
constructor() : this(VerificationCode())
val code = bind(VerificationCode::codeProperty)
val data = bind(VerificationCode::dataProperty)
}

View File

@ -1,72 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.stylesheet
import javafx.scene.layout.BackgroundRepeat
import javafx.scene.paint.Color
import net.mamoe.mirai.console.MiraiConsole
import tornadofx.Stylesheet
import tornadofx.cssclass
import tornadofx.csselement
import java.io.File
import kotlin.random.Random
open class BaseStyleSheet : Stylesheet() {
companion object {
const val primaryColor = "0EA987"
const val stressColor = "35867C"
const val secondaryColor = "32CABA"
const val lightColor ="9FD1CC"
const val fontColor = "FFFFFF"
val TRANSPARENT: Color = Color.TRANSPARENT
val rootPane by cssclass("root-pane")
val jfxTabPane by cssclass("jfx-tab-pane")
val myButtonBar by cssclass("my-button-bar")
val vBox by csselement("VBox")
}
init {
rootPane {
child(imageView) {}
jfxTabPane {
val bg = File(MiraiConsole.path, "background")
if (!bg.exists()) bg.mkdir()
if (bg.isDirectory) {
bg.listFiles()!!.filter { file -> file.extension in listOf("jpg", "jpeg", "png", "gif") }
.randomElement()?.also {
backgroundImage += it.toURI()
backgroundRepeat += BackgroundRepeat.REPEAT to BackgroundRepeat.REPEAT
}
}
}
}
listView {
backgroundColor += TRANSPARENT
listCell {
backgroundColor += TRANSPARENT
}
}
}
}
fun <T> Collection<T>.randomElement(): T? {
if (isEmpty())
return null
return elementAt(Random.nextInt(size))
}

View File

@ -1,65 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.stylesheet
import javafx.scene.Cursor
import javafx.scene.effect.BlurType
import javafx.scene.effect.DropShadow
import javafx.scene.paint.Color
import javafx.scene.text.FontWeight
import tornadofx.box
import tornadofx.c
import tornadofx.csselement
import tornadofx.px
class LoginViewStyleSheet : BaseStyleSheet() {
companion object {
val vBox by csselement("VBox")
}
init {
/*
* center box
*/
vBox {
maxWidth = 500.px
maxHeight = 500.px
backgroundColor += c(primaryColor, 0.3)
backgroundRadius += box(15.px)
padding = box(50.px, 100.px)
spacing = 25.px
borderRadius += box(15.px)
effect = DropShadow(BlurType.THREE_PASS_BOX, Color.GRAY, 10.0, 0.0, 15.0, 15.0)
}
textField {
prefHeight = 30.px
textFill = Color.BLACK
fontWeight = FontWeight.BOLD
}
/*
* login button
*/
button {
backgroundColor += c(stressColor, 0.8)
padding = box(10.px, 0.px)
prefWidth = 500.px
textFill = Color.WHITE
fontWeight = FontWeight.BOLD
cursor = Cursor.HAND
}
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.stylesheet
import javafx.geometry.Pos
import javafx.scene.paint.Color
import javafx.scene.text.FontWeight
import tornadofx.box
import tornadofx.c
import tornadofx.cssclass
import tornadofx.px
class PluginViewStyleSheet : BaseStyleSheet() {
companion object {
val jfxTreeTableView by cssclass("jfx-tree-table-view")
val treeTableRowCell by cssclass("tree-table-row-cell")
val columnHeader by cssclass("column-header")
val columnHeaderBg by cssclass("column-header-background")
}
init {
jfxTreeTableView {
backgroundColor += TRANSPARENT
columnHeader {
borderWidth += box(0.px)
label {
textFill = Color.BLACK
}
}
columnHeaderBg {
backgroundColor += c(lightColor, 0.4)
}
treeTableCell {
alignment = Pos.CENTER
}
treeTableRowCell {
fontWeight = FontWeight.SEMI_BOLD
backgroundColor += TRANSPARENT
and(":selected") {
backgroundColor += c(stressColor, 1.0)
}
and(":hover:filled") {
backgroundColor += c(stressColor, 0.6)
and(":selected") {
backgroundColor += c(stressColor, 1.0)
}
}
}
}
}
}

View File

@ -1,195 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.stylesheet
import javafx.scene.Cursor
import javafx.scene.text.FontWeight
import tornadofx.box
import tornadofx.c
import tornadofx.cssclass
import tornadofx.px
class PrimaryStyleSheet : BaseStyleSheet() {
companion object {
// window
val jfxTitle by cssclass("jfx-decorator-buttons-container")
val container by cssclass("jfx-decorator-content-container")
// jfx tab
val jfxTabHeader by cssclass("tab-header-background")
val closeButton by cssclass("tab-close-button")
// jfx list view
val leftPane by cssclass("left-pane")
val jfxListView by cssclass("jfx-list-view")
val jfxListCell by cssclass("jfx-list-cell")
}
init {
/*
* window
*/
jfxTitle {
backgroundColor += c(primaryColor)
}
container {
borderColor += box(c(primaryColor))
borderWidth += box(0.px, 4.px, 4.px, 4.px)
}
/*
* bot list
*/
rootPane {
leftPane {
backgroundColor += c(primaryColor)
// 这个padding有bug十分神奇
padding = box(0.px, 4.px, 0.px, 0.px)
spacing = 4.px
jfxListView {
// placeholder
vBox {
spacing = 15.px
label {
textFill = c(fontColor)
fontWeight = FontWeight.BOLD
}
button {
textFill = c(fontColor)
fontWeight = FontWeight.BOLD
backgroundColor += c(secondaryColor, 0.8)
padding = box(2.px, 10.px)
cursor = Cursor.HAND
}
}
jfxListCell {
backgroundColor += c(100, 100, 100, 0.4)
backgroundRadius += box(5.px)
label {
textFill = c(fontColor)
fontWeight = FontWeight.BOLD
}
button {
opacity = 0.0
backgroundRadius += box(10.px)
backgroundColor += c(fontColor, 0.1)
cursor = Cursor.HAND
and(hover) {
opacity = 1.0
}
}
}
}
}
}
/*
* tab pane
*/
jfxTabHeader {
backgroundColor += c(primaryColor)
}
jfxTabPane {
// 日志列表样式
vBox {
padding = box(15.px)
spacing = 15.px
}
myButtonBar {
spacing = 15.px
button {
backgroundColor += c(secondaryColor, 0.8)
padding = box(2.px, 10.px)
cursor = Cursor.HAND
textFill = c(fontColor)
fontSize = 12.px
}
}
listView {
// 字体在label里大坑
label {
fontSize = 13.px
}
listCell {
and(":WARNING") {
backgroundColor += c("FFFF00", 0.3) // Yellow
}
and(":ERROR") {
backgroundColor += c("FF0000", 0.3) // Red
}
and(":selected") {
backgroundColor += c(stressColor, 1.0)
}
and(":hover:filled") {
backgroundColor += c(stressColor, 0.6)
and(":selected") {
backgroundColor += c(stressColor, 1.0)
}
}
}
// 调整滚动条
scrollBar {
backgroundColor += TRANSPARENT
// 隐藏水平滚动条
and(horizontal) {
prefHeight = 0.px
s(incrementArrow, decrementArrow) { backgroundColor += TRANSPARENT }
}
and(vertical) {
thumb {
backgroundColor += c(stressColor, 0.6)
}
track {
backgroundColor += TRANSPARENT
}
}
}
}
}
// 去除JFoenix默认样式
tab {
and(":closable") {
borderWidth += box(0.px)
borderInsets += box(6.px, 0.px)
}
closeButton {
and(hover) { cursor = Cursor.HAND }
}
}
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.util
import com.jfoenix.controls.*
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject
import javafx.beans.value.ObservableValue
import javafx.collections.ObservableList
import javafx.event.EventTarget
import javafx.scene.Node
import javafx.scene.control.ListView
import tornadofx.SortedFilteredList
import tornadofx.attachTo
import tornadofx.bind
internal fun EventTarget.jfxTabPane(op: JFXTabPane.() -> Unit = {}) = JFXTabPane().attachTo(this, op)
internal fun EventTarget.jfxButton(text: String = "", graphic: Node? = null, op: JFXButton.() -> Unit = {}) =
JFXButton(text).attachTo(this, op) {
if (graphic != null) it.graphic = graphic
it.buttonType = JFXButton.ButtonType.RAISED
}
fun EventTarget.jfxTextfield(value: String? = null, op: JFXTextField.() -> Unit = {}) =
JFXTextField().attachTo(this, op) {
if (value != null) it.text = value
}
fun EventTarget.jfxTextfield(property: ObservableValue<String>, op: JFXTextField.() -> Unit = {}) =
jfxTextfield().apply {
bind(property)
op(this)
}
fun EventTarget.jfxPasswordfield(value: String? = null, op: JFXPasswordField.() -> Unit = {}) =
JFXPasswordField().attachTo(this, op) {
if (value != null) it.text = value
}
fun EventTarget.jfxPasswordfield(property: ObservableValue<String>, op: JFXPasswordField.() -> 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
}
}
fun <T : RecursiveTreeObject<T>?> EventTarget.jfxTreeTableView(
items: ObservableList<T>? = null,
op: JFXTreeTableView<T>.() -> Unit = {}
) = JFXTreeTableView<T>(RecursiveTreeItem(items, RecursiveTreeObject<T>::getChildren)).attachTo(this, op)

View File

@ -1,24 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.util
import com.jfoenix.svg.SVGGlyph
import javafx.scene.paint.Color
class SVG {
companion object {
var 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 { setSize(8.0, 8.0) }
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.util
import javafx.event.EventTarget
import javafx.geometry.Pos
import javafx.scene.layout.HBox
import tornadofx.addClass
import tornadofx.hbox
fun EventTarget.myButtonBar(alignment: Pos = Pos.BASELINE_LEFT, op: HBox.() -> Unit = {}) = hbox {
addClass("my-button-bar")
this.alignment = alignment
op()
}

View File

@ -1,21 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view
import com.jfoenix.controls.JFXDecorator
import tornadofx.View
class Decorator : View() {
override val root = JFXDecorator(primaryStage, find<PrimaryView>().root).apply {
prefWidth = 1000.0
prefHeight = 650.0
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view
import javafx.beans.property.SimpleStringProperty
import javafx.geometry.Pos
import javafx.scene.image.Image
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalFrontEndController
import net.mamoe.mirai.console.graphical.stylesheet.LoginViewStyleSheet
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.*
class LoginView : View("CNM") {
private val controller = find<MiraiGraphicalFrontEndController>()
private val qq = SimpleStringProperty("")
private val psd = SimpleStringProperty("")
override val root = borderpane {
addStylesheet(LoginViewStyleSheet::class)
center = vbox {
imageview(Image(LoginView::class.java.classLoader.getResourceAsStream("character.png"))) {
alignment = Pos.CENTER
}
jfxTextfield(qq) {
promptText = "QQ"
isLabelFloat = true
}
jfxPasswordfield(psd) {
promptText = "Password"
isLabelFloat = true
}
jfxButton("Login").action {
runAsync {
runBlocking { controller.login(qq.value, psd.value) }
}.ui {
qq.value = ""
psd.value = ""
}
}
}
}
}

View File

@ -1,182 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view
import com.jfoenix.controls.JFXTreeTableColumn
import javafx.application.Platform
import javafx.collections.ObservableList
import javafx.geometry.Pos
import javafx.scene.control.Button
import javafx.scene.control.TreeTableCell
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalFrontEndController
import net.mamoe.mirai.console.graphical.event.ReloadEvent
import net.mamoe.mirai.console.graphical.model.PluginModel
import net.mamoe.mirai.console.graphical.stylesheet.PluginViewStyleSheet
import net.mamoe.mirai.console.graphical.util.jfxButton
import net.mamoe.mirai.console.graphical.util.jfxTreeTableView
import net.mamoe.mirai.console.graphical.view.dialog.PluginDetailFragment
import tornadofx.*
class PluginsCenterView : View() {
private val controller = find<MiraiGraphicalFrontEndController>()
private val center get() = MiraiConsole.frontEnd.pluginCenter
private val plugins: ObservableList<PluginModel> = observableListOf()
init {
// 监听插件重载,情况插件列表,重新载入。
// 同时把页面刷新按键的listener也初始化
subscribe<ReloadEvent> { plugins.clear() }
}
override val root = jfxTreeTableView(plugins) {
addStylesheet(PluginViewStyleSheet::class)
placeholder = button("从崔云获取插件列表") {
action {
isDisable = true
runAsync {
fetch()
}.ui {
plugins.addAll(it)
isDisable = false
}
}
}
isShowRoot = false
columns.addAll(
JFXTreeTableColumn<PluginModel, String>("插件名").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
setCellValueFactory {
return@setCellValueFactory it.value.value.nameProperty
}
},
JFXTreeTableColumn<PluginModel, String>("版本").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
setCellValueFactory {
return@setCellValueFactory it.value.value.versionProperty
}
},
JFXTreeTableColumn<PluginModel, String>("作者").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
setCellValueFactory {
return@setCellValueFactory it.value.value.authorProperty
}
},
JFXTreeTableColumn<PluginModel, String>("介绍").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.48))
setCellValueFactory {
return@setCellValueFactory it.value.value.descriptionProperty
}
},
JFXTreeTableColumn<PluginModel, PluginModel>("操作").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.2))
setCellValueFactory { return@setCellValueFactory it.value.valueProperty() }
setCellFactory {
return@setCellFactory object : TreeTableCell<PluginModel, PluginModel>() {
override fun updateItem(item: PluginModel?, empty: Boolean) {
if (item != null && !empty) {
graphic = hbox {
spacing = 15.0
alignment = Pos.CENTER
jfxButton("详情") {
action { detail(item) }
}
jfxButton(if (item.expired) "更新" else "下载") {
action { download(item, this) }
}
}
text = ""
} else {
graphic = null
text = ""
}
}
}
}
}
)
}
private fun fetch(): List<PluginModel> = mutableListOf<PluginModel>().apply {
runBlocking {
var page = 1
while (true) {
val map = center.fetchPlugin(page++)
if (map.isEmpty()) return@runBlocking
map.forEach {
with(
PluginModel(
it.value.name,
it.value.version,
it.value.author,
it.value.description,
it.value
)
) {
add(this)
controller.checkUpdate(this)
controller.checkAmbiguous(this)
}
}
}
}
}
private fun detail(pluginModel: PluginModel) {
runAsync {
runBlocking { center.findPlugin(pluginModel.name) }
}.ui {
it?.apply {
PluginDetailFragment(this).openModal()
}
}
}
private fun download(pluginModel: PluginModel, button: Button) {
button.isDisable = true
button.text = "连接中..."
runAsync {
runBlocking {
center.downloadPlugin(pluginModel.name) {
// download process
Platform.runLater {
button.text = "$it%"
}
}
}
}.ui {
with(button) {
isDisable = false
text = "重载插件"
setOnAction {
controller.reloadPlugins()
}
}
}
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view
import com.jfoenix.controls.JFXTreeTableColumn
import javafx.scene.control.TreeTableCell
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalFrontEndController
import net.mamoe.mirai.console.graphical.model.PluginModel
import net.mamoe.mirai.console.graphical.stylesheet.PluginViewStyleSheet
import net.mamoe.mirai.console.graphical.util.jfxButton
import net.mamoe.mirai.console.graphical.util.jfxTreeTableView
import tornadofx.View
import tornadofx.addStylesheet
import tornadofx.visibleWhen
class PluginsView : View() {
private val controller = find<MiraiGraphicalFrontEndController>()
val plugins = controller.pluginList
override val root = jfxTreeTableView(plugins) {
addStylesheet(PluginViewStyleSheet::class)
isShowRoot = false
columns.addAll(
JFXTreeTableColumn<PluginModel, String>("插件名").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
setCellValueFactory {
return@setCellValueFactory it.value.value.nameProperty
}
},
JFXTreeTableColumn<PluginModel, String>("版本").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
setCellValueFactory {
return@setCellValueFactory it.value.value.versionProperty
}
},
JFXTreeTableColumn<PluginModel, String>("作者").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
setCellValueFactory {
return@setCellValueFactory it.value.value.authorProperty
}
},
JFXTreeTableColumn<PluginModel, String>("介绍").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.6))
setCellValueFactory {
return@setCellValueFactory it.value.value.descriptionProperty
}
},
JFXTreeTableColumn<PluginModel, PluginModel>("操作").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.08))
setCellValueFactory { return@setCellValueFactory it.value.valueProperty() }
setCellFactory {
return@setCellFactory object : TreeTableCell<PluginModel, PluginModel>() {
override fun updateItem(item: PluginModel?, empty: Boolean) {
if (item != null && !empty) {
graphic = jfxButton("更新") {
visibleWhen(item.expiredProperty)
// to do update
}
text = ""
} else {
graphic = null
text = ""
}
}
}
}
}
)
}
}

View File

@ -1,220 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view
import com.jfoenix.controls.JFXButton
import com.jfoenix.controls.JFXListCell
import javafx.collections.ObservableList
import javafx.geometry.Pos
import javafx.scene.control.Alert
import javafx.scene.control.ButtonType
import javafx.scene.control.Tab
import javafx.scene.control.TabPane
import javafx.scene.image.Image
import javafx.scene.input.Clipboard
import javafx.scene.input.KeyCode
import javafx.scene.layout.Priority
import javafx.stage.FileChooser
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalFrontEndController
import net.mamoe.mirai.console.graphical.model.BotModel
import net.mamoe.mirai.console.graphical.util.*
import tornadofx.*
class PrimaryView : View() {
private val controller = find<MiraiGraphicalFrontEndController>()
private lateinit var mainTabPane: TabPane
override val root = borderpane {
addClass("root-pane")
left = vbox {
addClass("left-pane")
imageview(Image(PrimaryView::class.java.classLoader.getResourceAsStream("logo.png"))) {
fitHeight = 40.0
alignment = Pos.CENTER
isPreserveRatio = true
}
// bot list
jfxListView(controller.botList) {
fitToParentHeight()
placeholder = vbox {
alignment = Pos.CENTER
label("Bot列表为空请登录一个Bot")
jfxButton("登录") {
buttonType = JFXButton.ButtonType.FLAT
}.action {
// select login pane
mainTabPane.selectionModel.selectLast()
}
}
setCellFactory {
object : JFXListCell<BotModel>() {
var tab: Tab? = null
init {
onDoubleClick {
tab?.select() ?: mainTabPane.logTab(
text = item.uin.toString(),
logs = item.logHistory
).select().also { tab = it }
}
}
override fun updateItem(item: BotModel?, empty: Boolean) {
super.updateItem(item, empty)
if (item != null && !empty) {
graphic = hbox {
alignment = Pos.CENTER_LEFT
label(item.uin.toString())
pane {
hgrow = Priority.ALWAYS
}
jfxButton(graphic = SVG.close) {
buttonType = JFXButton.ButtonType.FLAT
tooltip("退出登录")
}.action {
alert(Alert.AlertType.CONFIRMATION, "${item.uin}将会退出登录,是否确认") {
if (it == ButtonType.OK) {
tab?.close()
controller.logout(item.uin)
}
}
}
}
text = ""
} else {
graphic = null
text = ""
}
}
}
}
}
}
center = vbox {
jfxTabPane {
fitToParentHeight()
tabClosingPolicy = TabPane.TabClosingPolicy.ALL_TABS
logTab("Main", controller.mainLog, closeable = false)
fixedTab("Plugins").content = find<PluginsView>().root
fixedTab("Plugins Center").content = find<PluginsCenterView>().root
fixedTab("Settings").content = find<SettingsView>().root
fixedTab("Login").content = find<LoginView>().root
mainTabPane = this
}
// command input
textfield {
promptText = "在这里输出命令"
setOnKeyPressed {
if (it.code == KeyCode.ENTER) {
runAsync {
runBlocking { controller.sendCommand(text) }
}.ui { text = "" }
}
}
}
}
}
fun Tab.select() = apply {
if (!mainTabPane.tabs.contains(this)) mainTabPane.tabs.add(this)
mainTabPane.selectionModel.select(this)
}
}
private fun TabPane.fixedTab(title: String) = tab(title) { isClosable = false }
private fun TabPane.logTab(
text: String? = null,
logs: ObservableList<Pair<String, String>>,
closeable: Boolean = true,
op: Tab.() -> Unit = {}
) = tab(text) {
this.isClosable = closeable
vbox {
myButtonBar(alignment = Pos.BASELINE_RIGHT) {
jfxButton("导出日志").action {
val path = chooseFile(
"选择保存路径",
arrayOf(FileChooser.ExtensionFilter("日志", "txt")),
FileChooserMode.Save,
owner = FX.primaryStage
) {
initialFileName = "$text.txt"
}
runAsyncWithOverlay {
path.firstOrNull()?.run {
if (!exists()) createNewFile()
writer().use {
logs.forEach { log -> it.appendln(log.first) }
}
true
} ?: false
}.ui {// isSucceed: Boolean ->
// notify something
}
}
}
listview(logs) {
fitToParentSize()
cellFormat {
addPseudoClass(it.second)
graphic = label(it.first) {
maxWidthProperty().bind(this@listview.widthProperty())
isWrapText = true
contextmenu {
item("复制").action {
Clipboard.getSystemClipboard().putString(it.first)
}
item("删除").action {
logs.remove(it)
}
}
}
}
}
}
also(op)
}

View File

@ -1,68 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view
import javafx.geometry.Pos
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalFrontEndController
import net.mamoe.mirai.console.graphical.model.GlobalSettingModel
import net.mamoe.mirai.console.graphical.util.jfxButton
import net.mamoe.mirai.console.graphical.util.jfxTextfield
import net.mamoe.mirai.console.graphical.util.myButtonBar
import tornadofx.*
import java.awt.Desktop
import java.io.File
class SettingsView : View() {
private val controller = find<MiraiGraphicalFrontEndController>()
private val settingModel = find<GlobalSettingModel>()
override val root = vbox {
myButtonBar(alignment = Pos.BASELINE_RIGHT) {
jfxButton("撤掉").action {
settingModel.rollback()
}
jfxButton("保存").action {
settingModel.commit()
}
}
form {
fieldset("插件目录") {
field {
jfxTextfield((System.getProperty("user.dir") + "/plugins/").replace("//", "/")) { isEditable = false }
jfxButton("打开目录").action {
(System.getProperty("user.dir") + "/plugins/").replace("//", "/").also { path ->
Desktop.getDesktop().takeIf { it.isSupported(Desktop.Action.OPEN) }?.open(File(path))
}
}
}
}
fieldset("背景目录") {
field {
jfxTextfield((System.getProperty("user.dir") + "/background/").replace("//", "/")) { isEditable = false }
jfxButton("打开目录").action {
(System.getProperty("user.dir") + "/background/").replace("//", "/").also { path ->
Desktop.getDesktop().takeIf { it.isSupported(Desktop.Action.OPEN) }?.open(File(path))
}
}
}
}
fieldset("最大日志容量") {
field {
jfxTextfield().bind(settingModel.maxLogNum)
}
}
}
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view.dialog
import javafx.scene.control.TextField
import javafx.stage.Modality
import javafx.stage.StageStyle
import tornadofx.*
class InputDialog(title: String) : Fragment() {
private lateinit var input: TextField
init {
titleProperty.value = title
}
override val root = form {
fieldset {
field(title) {
input = textfield("")
}
buttonbar {
button("提交").action { close() }
}
}
}
fun open(): String {
// 阻塞窗口直到关闭
openModal(
stageStyle = StageStyle.DECORATED,
modality = Modality.APPLICATION_MODAL,
block = true
)
return input.text
}
}

View File

@ -1,85 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view.dialog
import javafx.geometry.Insets
import net.mamoe.mirai.console.center.PluginCenter
import net.mamoe.mirai.console.graphical.util.jfxTextfield
import tornadofx.Fragment
import tornadofx.vbox
class PluginDetailFragment(info: PluginCenter.PluginInfo) : Fragment() {
init {
title = info.name
}
override val root = vbox {
prefWidth = 450.0
padding = Insets(25.0)
spacing = 25.0
jfxTextfield(info.name) {
promptText = "插件名"
isLabelFloat = true
}
jfxTextfield(info.version) {
promptText = "版本号"
isLabelFloat = true
}
jfxTextfield(info.coreVersion) {
promptText = "Mirai核心版本"
isLabelFloat = true
}
jfxTextfield(info.consoleVersion) {
promptText = "Mirai控制台版本"
isLabelFloat = true
}
jfxTextfield(info.tags.joinToString(",")) {
promptText = "标签"
isLabelFloat = true
}
jfxTextfield(info.author) {
promptText = "作者"
isLabelFloat = true
}
jfxTextfield(info.description) {
promptText = "描述"
isLabelFloat = true
}
jfxTextfield(info.usage) {
promptText = "使用方法"
isLabelFloat = true
}
jfxTextfield(info.vcs) {
promptText = "仓库地址"
isLabelFloat = true
}
jfxTextfield(info.commands.joinToString("\n\n")) {
promptText = "命令"
isLabelFloat = true
}
jfxTextfield(info.changeLog.joinToString("\n\n")) {
promptText = "修改日志"
isLabelFloat = true
}
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.graphical.view.dialog
import javafx.scene.image.Image
import net.mamoe.mirai.console.graphical.model.VerificationCodeModel
import tornadofx.*
import java.io.ByteArrayInputStream
class VerificationCodeFragment : Fragment() {
companion object {
val MAGIC_KEY = String("CANCEL".toByteArray())
}
val code = find<VerificationCodeModel>()
override val root = vbox {
// 显示验证码
imageview(Image(ByteArrayInputStream(code.data.value)))
form {
fieldset {
field("验证码") {
textfield(code.code)
}
}
buttonbar {
button("提交").action {
code.commit()
this@VerificationCodeFragment.close()
}
button("取消").action {
code.code.value =
MAGIC_KEY
code.commit()
this@VerificationCodeFragment.close()
}
}
}
}
}

View File

@ -1,6 +0,0 @@
import net.mamoe.mirai.console.graphical.MiraiGraphicalUI
import tornadofx.launch
fun main(args: Array<String>) {
launch<MiraiGraphicalUI>(args)
}