mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Merge pull request #163 from Karlatemp/no-console
Working without Terminal
This commit is contained in:
commit
77bc76be24
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.pure
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||||
|
@Target(
|
||||||
|
AnnotationTarget.CLASS,
|
||||||
|
AnnotationTarget.TYPEALIAS,
|
||||||
|
AnnotationTarget.FUNCTION,
|
||||||
|
AnnotationTarget.PROPERTY,
|
||||||
|
AnnotationTarget.FIELD,
|
||||||
|
AnnotationTarget.CONSTRUCTOR
|
||||||
|
)
|
||||||
|
@MustBeDocumented
|
||||||
|
annotation class ConsolePureExperimentalApi
|
||||||
|
|
||||||
|
@ConsolePureExperimentalApi
|
||||||
|
public object ConsolePureSettings {
|
||||||
|
@JvmField
|
||||||
|
var setupAnsi: Boolean = System.getProperty("os.name")
|
||||||
|
.toLowerCase()
|
||||||
|
.contains("windows") // Just for Windows
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var noConsole: Boolean = false
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var noAnsi: Boolean = false
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var noConsoleSafeReading: Boolean = false
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var noConsoleReadingReplacement: String = ""
|
||||||
|
}
|
@ -27,8 +27,10 @@ import org.jline.reader.UserInterruptException
|
|||||||
|
|
||||||
val consoleLogger by lazy { DefaultLogger("console") }
|
val consoleLogger by lazy { DefaultLogger("console") }
|
||||||
|
|
||||||
@OptIn(ConsoleInternalApi::class)
|
@OptIn(ConsoleInternalApi::class, ConsolePureExperimentalApi::class)
|
||||||
internal fun startupConsoleThread() {
|
internal fun startupConsoleThread() {
|
||||||
|
if (ConsolePureSettings.noConsole) return
|
||||||
|
|
||||||
MiraiConsole.launch(CoroutineName("Input")) {
|
MiraiConsole.launch(CoroutineName("Input")) {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING",
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING",
|
||||||
"EXPOSED_SUPER_CLASS"
|
"EXPOSED_SUPER_CLASS"
|
||||||
)
|
)
|
||||||
@file:OptIn(ConsoleInternalApi::class, ConsoleFrontEndImplementation::class)
|
@file:OptIn(ConsoleInternalApi::class, ConsoleFrontEndImplementation::class, ConsolePureExperimentalApi::class)
|
||||||
|
|
||||||
package net.mamoe.mirai.console.pure
|
package net.mamoe.mirai.console.pure
|
||||||
|
|
||||||
@ -34,6 +34,8 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader
|
|||||||
import net.mamoe.mirai.console.plugin.loader.PluginLoader
|
import net.mamoe.mirai.console.plugin.loader.PluginLoader
|
||||||
import net.mamoe.mirai.console.pure.ConsoleInputImpl.requestInput
|
import net.mamoe.mirai.console.pure.ConsoleInputImpl.requestInput
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||||
|
import net.mamoe.mirai.console.pure.noconsole.AllEmptyLineReader
|
||||||
|
import net.mamoe.mirai.console.pure.noconsole.NoConsole
|
||||||
import net.mamoe.mirai.console.util.ConsoleInput
|
import net.mamoe.mirai.console.util.ConsoleInput
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||||
import net.mamoe.mirai.console.util.NamedSupervisorJob
|
import net.mamoe.mirai.console.util.NamedSupervisorJob
|
||||||
@ -115,6 +117,8 @@ private object ConsoleInputImpl : ConsoleInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val lineReader: LineReader by lazy {
|
val lineReader: LineReader by lazy {
|
||||||
|
if (ConsolePureSettings.noConsole) return@lazy AllEmptyLineReader
|
||||||
|
|
||||||
LineReaderBuilder.builder()
|
LineReaderBuilder.builder()
|
||||||
.terminal(terminal)
|
.terminal(terminal)
|
||||||
.completer(NullCompleter())
|
.completer(NullCompleter())
|
||||||
@ -122,6 +126,8 @@ val lineReader: LineReader by lazy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val terminal: Terminal = run {
|
val terminal: Terminal = run {
|
||||||
|
if (ConsolePureSettings.noConsole) return@run NoConsole
|
||||||
|
|
||||||
val dumb = System.getProperty("java.class.path")
|
val dumb = System.getProperty("java.class.path")
|
||||||
.contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null || System.getenv("mirai.idea") !== null
|
.contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null || System.getenv("mirai.idea") !== null
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"INVISIBLE_GETTER",
|
"INVISIBLE_GETTER",
|
||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
||||||
)
|
)
|
||||||
@file:OptIn(ConsoleInternalApi::class)
|
@file:OptIn(ConsoleInternalApi::class, ConsolePureExperimentalApi::class)
|
||||||
|
|
||||||
package net.mamoe.mirai.console.pure
|
package net.mamoe.mirai.console.pure
|
||||||
|
|
||||||
@ -26,6 +26,7 @@ import net.mamoe.mirai.console.MiraiConsole
|
|||||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||||
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
|
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
|
||||||
|
import net.mamoe.mirai.console.pure.noconsole.SystemOutputPrintStream
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||||
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
|
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
|
||||||
@ -33,6 +34,7 @@ import net.mamoe.mirai.message.data.Message
|
|||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import net.mamoe.mirai.utils.minutesToMillis
|
import net.mamoe.mirai.utils.minutesToMillis
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mirai-console-pure CLI 入口点
|
* mirai-console-pure CLI 入口点
|
||||||
@ -40,6 +42,7 @@ import java.io.PrintStream
|
|||||||
object MiraiConsolePureLoader {
|
object MiraiConsolePureLoader {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
parse(args, exitProcess = true)
|
||||||
startAsDaemon()
|
startAsDaemon()
|
||||||
try {
|
try {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
@ -50,6 +53,97 @@ object MiraiConsolePureLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConsolePureExperimentalApi
|
||||||
|
fun printHelpMessage() {
|
||||||
|
val help = listOf(
|
||||||
|
"" to "Mirai-Console[Pure FrontEnd] v" + kotlin.runCatching {
|
||||||
|
net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
|
||||||
|
}.getOrElse { "<unknown>" },
|
||||||
|
"" to "",
|
||||||
|
"--help" to "显示此帮助",
|
||||||
|
"" to "",
|
||||||
|
"--no-console" to "使用无终端操作环境",
|
||||||
|
"--dont-setup-terminal-ansi" to
|
||||||
|
"[NoConsole] [Windows Only] 不进行ansi console初始化工作",
|
||||||
|
"--no-ansi" to "[NoConsole] 禁用 ansi",
|
||||||
|
"--safe-reading" to
|
||||||
|
"[NoConsole] 如果启动此选项, console在获取用户输入的时候会获得一个安全的替换符\n" +
|
||||||
|
" 如果不启动, 将会直接 error",
|
||||||
|
"--reading-replacement <string>" to
|
||||||
|
"[NoConsole] Console尝试读取命令的替换符, 默认是空字符串\n" +
|
||||||
|
" 使用此选项会自动开启 --safe-reading",
|
||||||
|
)
|
||||||
|
val prefixPlaceholder = String(CharArray(
|
||||||
|
help.maxOfOrNull { it.first.length }!! + 3
|
||||||
|
) { ' ' })
|
||||||
|
|
||||||
|
fun printOption(optionName: String, value: String) {
|
||||||
|
if (optionName == "") {
|
||||||
|
println(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
print(optionName)
|
||||||
|
print(prefixPlaceholder.substring(optionName.length))
|
||||||
|
val lines = value.split('\n').iterator()
|
||||||
|
if (lines.hasNext()) println(lines.next())
|
||||||
|
lines.forEach { line ->
|
||||||
|
print(prefixPlaceholder)
|
||||||
|
println(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
help.forEach { (optionName, value) ->
|
||||||
|
printOption(optionName, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConsolePureExperimentalApi
|
||||||
|
fun parse(args: Array<String>, exitProcess: Boolean = false) {
|
||||||
|
val iterator = args.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
when (val option = iterator.next()) {
|
||||||
|
"--help" -> {
|
||||||
|
printHelpMessage()
|
||||||
|
if (exitProcess) exitProcess(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
"--no-console" -> {
|
||||||
|
ConsolePureSettings.noConsole = true
|
||||||
|
}
|
||||||
|
"--dont-setup-terminal-ansi" -> {
|
||||||
|
ConsolePureSettings.setupAnsi = false
|
||||||
|
}
|
||||||
|
"--no-ansi" -> {
|
||||||
|
ConsolePureSettings.noAnsi = true
|
||||||
|
ConsolePureSettings.setupAnsi = false
|
||||||
|
}
|
||||||
|
"--reading-replacement" -> {
|
||||||
|
ConsolePureSettings.noConsoleSafeReading = true
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
ConsolePureSettings.noConsoleReadingReplacement = iterator.next()
|
||||||
|
} else {
|
||||||
|
println("Bad option `--reading-replacement`")
|
||||||
|
println("Usage: --reading-replacement <string>")
|
||||||
|
if (exitProcess)
|
||||||
|
exitProcess(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"--safe-reading" -> {
|
||||||
|
ConsolePureSettings.noConsoleSafeReading = true
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
println("Unknown option `$option`")
|
||||||
|
printHelpMessage()
|
||||||
|
if (exitProcess)
|
||||||
|
exitProcess(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ConsolePureSettings.noConsole)
|
||||||
|
SystemOutputPrintStream // Setup Output Channel
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
@ConsoleExperimentalApi
|
@ConsoleExperimentalApi
|
||||||
fun startAsDaemon(instance: MiraiConsoleImplementationPure = MiraiConsoleImplementationPure()) {
|
fun startAsDaemon(instance: MiraiConsoleImplementationPure = MiraiConsoleImplementationPure()) {
|
||||||
|
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
|
||||||
|
*/
|
||||||
|
@file:OptIn(ConsolePureExperimentalApi::class)
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.pure.noconsole
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.pure.ConsolePureExperimentalApi
|
||||||
|
import net.mamoe.mirai.console.pure.ConsolePureSettings
|
||||||
|
import org.jline.keymap.KeyMap
|
||||||
|
import org.jline.reader.*
|
||||||
|
import org.jline.terminal.Attributes
|
||||||
|
import org.jline.terminal.MouseEvent
|
||||||
|
import org.jline.terminal.Size
|
||||||
|
import org.jline.terminal.Terminal
|
||||||
|
import org.jline.terminal.impl.AbstractTerminal
|
||||||
|
import org.jline.utils.AttributedString
|
||||||
|
import org.jline.utils.NonBlockingReader
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.io.PrintWriter
|
||||||
|
|
||||||
|
private const val LN_INT = '\n'.toInt()
|
||||||
|
private const val LN_BYTE = '\n'.toByte()
|
||||||
|
|
||||||
|
internal object NoConsoleNonBlockingReader : NonBlockingReader() {
|
||||||
|
override fun read(timeout: Long, isPeek: Boolean): Int {
|
||||||
|
return LN_INT
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {}
|
||||||
|
|
||||||
|
override fun readBuffered(b: CharArray?): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object AllNextLineInputStream : InputStream() {
|
||||||
|
override fun read(): Int = LN_INT
|
||||||
|
|
||||||
|
override fun available(): Int = 1
|
||||||
|
|
||||||
|
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||||
|
for (i in off until (off + len)) {
|
||||||
|
b[i] = LN_BYTE
|
||||||
|
}
|
||||||
|
return len
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object AllIgnoredOutputStream : OutputStream() {
|
||||||
|
override fun close() {}
|
||||||
|
override fun write(b: ByteArray, off: Int, len: Int) {}
|
||||||
|
override fun write(b: ByteArray) {}
|
||||||
|
override fun write(b: Int) {}
|
||||||
|
override fun flush() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val SystemOutputPrintStream by lazy {
|
||||||
|
@OptIn(ConsolePureExperimentalApi::class)
|
||||||
|
if (ConsolePureSettings.setupAnsi) {
|
||||||
|
org.fusesource.jansi.AnsiConsole.systemInstall()
|
||||||
|
}
|
||||||
|
System.out
|
||||||
|
}
|
||||||
|
private val ANSI_REGEX = """\u001b\[[0-9a-zA-Z;]*?m""".toRegex()
|
||||||
|
|
||||||
|
internal object AllEmptyLineReader : LineReader {
|
||||||
|
|
||||||
|
override fun printAbove(str: String?) {
|
||||||
|
if (str == null) return
|
||||||
|
@OptIn(ConsolePureExperimentalApi::class)
|
||||||
|
if (ConsolePureSettings.noAnsi) {
|
||||||
|
SystemOutputPrintStream.println(ANSI_REGEX.replace(str, ""))
|
||||||
|
} else SystemOutputPrintStream.println(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ConsolePureExperimentalApi::class)
|
||||||
|
override fun readLine(): String =
|
||||||
|
if (ConsolePureSettings.noConsoleSafeReading) ConsolePureSettings.noConsoleReadingReplacement
|
||||||
|
else error("Unsupported Reading line when console front-end closed.")
|
||||||
|
|
||||||
|
// region
|
||||||
|
private fun <T> ignored(): T = error("Ignored")
|
||||||
|
override fun readLine(mask: Char?): String = readLine()
|
||||||
|
override fun readLine(prompt: String?): String = readLine()
|
||||||
|
override fun readLine(prompt: String?, mask: Char?): String = readLine()
|
||||||
|
override fun readLine(prompt: String?, mask: Char?, buffer: String?): String = readLine()
|
||||||
|
override fun readLine(prompt: String?, rightPrompt: String?, mask: Char?, buffer: String?): String = readLine()
|
||||||
|
override fun readLine(
|
||||||
|
prompt: String?,
|
||||||
|
rightPrompt: String?,
|
||||||
|
maskingCallback: MaskingCallback?,
|
||||||
|
buffer: String?
|
||||||
|
): String = readLine()
|
||||||
|
|
||||||
|
override fun printAbove(str: AttributedString?) {
|
||||||
|
str?.let { printAbove(it.toAnsi()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defaultKeyMaps(): MutableMap<String, KeyMap<Binding>> = ignored()
|
||||||
|
override fun isReading(): Boolean = false
|
||||||
|
override fun variable(name: String?, value: Any?) = this
|
||||||
|
override fun option(option: LineReader.Option?, value: Boolean) = this
|
||||||
|
override fun callWidget(name: String?) {}
|
||||||
|
override fun getVariables(): MutableMap<String, Any> = ignored()
|
||||||
|
override fun getVariable(name: String?): Any = ignored()
|
||||||
|
override fun setVariable(name: String?, value: Any?) {}
|
||||||
|
override fun isSet(option: LineReader.Option?): Boolean = ignored()
|
||||||
|
override fun setOpt(option: LineReader.Option?) {}
|
||||||
|
override fun unsetOpt(option: LineReader.Option?) {}
|
||||||
|
override fun getTerminal(): Terminal = NoConsole
|
||||||
|
override fun getWidgets(): MutableMap<String, Widget> = ignored()
|
||||||
|
override fun getBuiltinWidgets(): MutableMap<String, Widget> = ignored()
|
||||||
|
override fun getBuffer(): Buffer = ignored()
|
||||||
|
override fun getAppName(): String = "Mirai Console"
|
||||||
|
override fun runMacro(macro: String?) {}
|
||||||
|
override fun readMouseEvent(): MouseEvent = ignored()
|
||||||
|
override fun getHistory(): History = ignored()
|
||||||
|
override fun getParser(): Parser = ignored()
|
||||||
|
override fun getHighlighter(): Highlighter = ignored()
|
||||||
|
override fun getExpander(): Expander = ignored()
|
||||||
|
override fun getKeyMaps(): MutableMap<String, KeyMap<Binding>> = ignored()
|
||||||
|
override fun getKeyMap(): String = ignored()
|
||||||
|
override fun setKeyMap(name: String?): Boolean = ignored()
|
||||||
|
override fun getKeys(): KeyMap<Binding> = ignored()
|
||||||
|
override fun getParsedLine(): ParsedLine = ignored()
|
||||||
|
override fun getSearchTerm(): String = ignored()
|
||||||
|
override fun getRegionActive(): LineReader.RegionType = ignored()
|
||||||
|
override fun getRegionMark(): Int = ignored()
|
||||||
|
override fun addCommandsInBuffer(commands: MutableCollection<String>?) {}
|
||||||
|
override fun editAndAddInBuffer(file: File?) {}
|
||||||
|
override fun getLastBinding(): String = ignored()
|
||||||
|
override fun getTailTip(): String = ignored()
|
||||||
|
override fun setTailTip(tailTip: String?) {}
|
||||||
|
override fun setAutosuggestion(type: LineReader.SuggestionType?) {}
|
||||||
|
override fun getAutosuggestion(): LineReader.SuggestionType = ignored()
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object NoConsole : AbstractTerminal(
|
||||||
|
"No Console", "No Console"
|
||||||
|
) {
|
||||||
|
override fun reader(): NonBlockingReader = NoConsoleNonBlockingReader
|
||||||
|
|
||||||
|
private val AllIgnoredPrintWriter = object : PrintWriter(AllIgnoredOutputStream) {
|
||||||
|
override fun close() {}
|
||||||
|
override fun flush() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't need it. Mirai-Console using LineReader to print messages.
|
||||||
|
override fun writer(): PrintWriter = AllIgnoredPrintWriter
|
||||||
|
|
||||||
|
override fun input(): InputStream = AllNextLineInputStream
|
||||||
|
|
||||||
|
override fun output(): OutputStream = AllIgnoredOutputStream
|
||||||
|
|
||||||
|
private val attributes0 = Attributes()
|
||||||
|
override fun getAttributes(): Attributes {
|
||||||
|
return Attributes(attributes0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAttributes(attr: Attributes?) {
|
||||||
|
attr?.let { attributes0.copy(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val size0 = Size(189, 53)
|
||||||
|
override fun getSize(): Size {
|
||||||
|
return Size().also { it.copy(size0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSize(size: Size?) {
|
||||||
|
size?.let { size0.copy(it) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user