feat: 初版提交
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.gradle
|
||||
.idea
|
||||
.kotlin
|
||||
build
|
||||
run
|
80
build.gradle.kts
Normal file
80
build.gradle.kts
Normal file
@@ -0,0 +1,80 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("maven-publish")
|
||||
id("fabric-loom") version "1.9-SNAPSHOT"
|
||||
|
||||
id("org.jetbrains.compose")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
|
||||
id("com.github.johnrengelman.shadow") version "8+"
|
||||
}
|
||||
|
||||
version = project.property("mod_version") as String
|
||||
group = project.property("maven_group") as String
|
||||
|
||||
base {
|
||||
archivesName = project.property("archives_base_name") as String
|
||||
}
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
withSourcesJar()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
loom {
|
||||
splitEnvironmentSourceSets()
|
||||
|
||||
mods {
|
||||
register("compose-ui-mod") {
|
||||
sourceSet("main")
|
||||
sourceSet("client")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// To change the versions see the gradle.properties file
|
||||
minecraft("com.mojang:minecraft:${project.property("minecraft_version")}")
|
||||
mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2")
|
||||
modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}")
|
||||
modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("kotlin_loader_version")}")
|
||||
|
||||
modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}")
|
||||
|
||||
implementation(compose.desktop.currentOs)
|
||||
includeInternal(compose.desktop.currentOs) {
|
||||
exclude(module = "kotlin-stdlib")
|
||||
exclude(module = "kotlin-stdlib-jdk7")
|
||||
exclude(module = "kotlin-stdlib-jdk8")
|
||||
exclude(module = "annotations")
|
||||
}
|
||||
}
|
||||
tasks {
|
||||
processResources {
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand(project.properties)
|
||||
}
|
||||
}
|
||||
runClient {
|
||||
args("--username", "MiniDay", "--width", "1280", "--height", "720")
|
||||
}
|
||||
}
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
artifactId = project.property("archives_base_name") as String
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
}
|
||||
}
|
18
gradle.properties
Normal file
18
gradle.properties
Normal file
@@ -0,0 +1,18 @@
|
||||
# Done to increase the memory available to gradle.
|
||||
org.gradle.jvmargs=-Xmx2G
|
||||
# Fabric Properties
|
||||
# check these on https://modmuss50.me/fabric.html
|
||||
minecraft_version=1.21.4
|
||||
yarn_mappings=1.21.4+build.4
|
||||
loader_version=0.16.9
|
||||
kotlin_loader_version=1.13.0+kotlin.2.1.0
|
||||
# Mod Properties
|
||||
mod_version=1.0.0
|
||||
maven_group=net.airgame
|
||||
archives_base_name=compose-ui-mod
|
||||
# Dependencies
|
||||
# check this on https://modmuss50.me/fabric.html
|
||||
fabric_version=0.114.0+1.21.4
|
||||
|
||||
kotlin.version=2.1.0
|
||||
compose.version=1.6.10
|
1
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
1
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1 @@
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
17
settings.gradle.kts
Normal file
17
settings.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven("https://maven.fabricmc.net/") {
|
||||
name = "Fabric"
|
||||
}
|
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
plugins {
|
||||
kotlin("jvm").version(extra["kotlin.version"] as String)
|
||||
id("org.jetbrains.compose").version(extra["compose.version"] as String)
|
||||
id("org.jetbrains.kotlin.plugin.compose").version(extra["kotlin.version"] as String)
|
||||
}
|
||||
}
|
154
src/client/kotlin/net/airgame/compose/ui/client/ComposeScreen.kt
Normal file
154
src/client/kotlin/net/airgame/compose/ui/client/ComposeScreen.kt
Normal file
@@ -0,0 +1,154 @@
|
||||
package net.airgame.compose.ui.client
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.InternalComposeUiApi
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Canvas
|
||||
import androidx.compose.ui.graphics.asComposeCanvas
|
||||
import androidx.compose.ui.input.pointer.PointerButton
|
||||
import androidx.compose.ui.input.pointer.PointerEventType
|
||||
import androidx.compose.ui.scene.ComposeScene
|
||||
import androidx.compose.ui.scene.SingleLayerComposeScene
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import com.mojang.blaze3d.platform.TextureUtil
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import kotlinx.coroutines.launch
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.texture.NativeImage
|
||||
import net.minecraft.text.Text
|
||||
import org.jetbrains.skia.Color
|
||||
import org.jetbrains.skia.EncodedImageFormat
|
||||
import org.jetbrains.skia.Surface
|
||||
import org.jetbrains.skiko.FrameDispatcher
|
||||
|
||||
@OptIn(InternalComposeUiApi::class)
|
||||
class ComposeScreen(
|
||||
val content: @Composable (ComposeScreen) -> Unit
|
||||
) : Screen(Text.literal("")) {
|
||||
private val frameDispatcher = FrameDispatcher(ComposeUIMod.singleThreadDispatcher) {
|
||||
renderFrame()
|
||||
}
|
||||
|
||||
private val windowWidth: Int get() = MinecraftClient.getInstance().window.width
|
||||
private val windowHeight: Int get() = MinecraftClient.getInstance().window.height
|
||||
|
||||
private lateinit var surface: Surface
|
||||
private lateinit var canvas :Canvas
|
||||
private lateinit var scene: ComposeScene
|
||||
|
||||
override fun init() {
|
||||
surface = Surface.makeRasterN32Premul(windowWidth, windowHeight)
|
||||
canvas = surface.canvas.asComposeCanvas()
|
||||
scene = SingleLayerComposeScene(
|
||||
coroutineContext = ComposeUIMod.singleThreadDispatcher,
|
||||
size = IntSize(windowWidth, windowHeight),
|
||||
density = Density(MinecraftClient.getInstance().options.guiScale.value.toFloat())
|
||||
) {
|
||||
frameDispatcher.scheduleFrame()
|
||||
}
|
||||
ComposeUIMod.coroutineScope.launch {
|
||||
scene.setContent {
|
||||
content(this@ComposeScreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (surface.isClosed) {
|
||||
return
|
||||
}
|
||||
ComposeUIMod.coroutineScope.launch {
|
||||
scene.sendPointerEvent(
|
||||
PointerEventType.Move,
|
||||
position = Offset(
|
||||
// mouseX.toFloat(), mouseY.toFloat()
|
||||
MinecraftClient.getInstance().mouse.x.toFloat(),
|
||||
MinecraftClient.getInstance().mouse.y.toFloat()
|
||||
)
|
||||
)
|
||||
}
|
||||
context.draw {
|
||||
val vertexConsumer = it.getBuffer(ComposeUIMod.composeLayer)
|
||||
val matrix4f = context.matrices.peek().positionMatrix
|
||||
vertexConsumer.vertex(matrix4f, 0f, 0f, 0f).texture(0f, 0f)
|
||||
vertexConsumer.vertex(matrix4f, 0f, height.toFloat(), 0f).texture(0f, 1f)
|
||||
vertexConsumer.vertex(matrix4f, width.toFloat(), height.toFloat(), 0f).texture(1f, 1f)
|
||||
vertexConsumer.vertex(matrix4f, width.toFloat(), 0f, 0f).texture(1f, 0f)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderFrame() {
|
||||
if (surface.isClosed) {
|
||||
return
|
||||
}
|
||||
val timer = DebugTimer()
|
||||
surface.canvas.clear(Color.TRANSPARENT)
|
||||
timer.log("clear")
|
||||
scene.render(canvas, System.nanoTime())
|
||||
timer.log("render")
|
||||
surface.makeImageSnapshot().use { image ->
|
||||
timer.log("makeImageSnapshot")
|
||||
image.encodeToData(EncodedImageFormat.PNG)?.use { data ->
|
||||
timer.log("encodeToData")
|
||||
val nativeImage = NativeImage.read(data.bytes)
|
||||
timer.log("NativeImage.read")
|
||||
RenderSystem.recordRenderCall {
|
||||
timer.log("recordRenderCall")
|
||||
TextureUtil.prepareImage(ComposeUIMod.textureGlID, 0, nativeImage.width, nativeImage.height)
|
||||
timer.log("prepareImage")
|
||||
nativeImage.upload(0, 0, 0, 0, 0, nativeImage.width, nativeImage.height, true)
|
||||
timer.log("upload")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun resize(client: MinecraftClient, width: Int, height: Int) {
|
||||
super.resize(client, width, height)
|
||||
val old = surface
|
||||
surface = Surface.makeRasterN32Premul(windowWidth, windowHeight)
|
||||
canvas = surface.canvas.asComposeCanvas()
|
||||
old.close()
|
||||
scene.size = IntSize(windowWidth, windowHeight)
|
||||
scene.density = Density(client.options.guiScale.value.toFloat())
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
ComposeUIMod.coroutineScope.launch {
|
||||
scene.sendPointerEvent(
|
||||
PointerEventType.Press,
|
||||
button = PointerButton.Primary,
|
||||
position = Offset(
|
||||
// mouseX.toFloat(), mouseY.toFloat()
|
||||
MinecraftClient.getInstance().mouse.x.toFloat(),
|
||||
MinecraftClient.getInstance().mouse.y.toFloat()
|
||||
)
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
ComposeUIMod.coroutineScope.launch {
|
||||
scene.sendPointerEvent(
|
||||
PointerEventType.Release,
|
||||
button = PointerButton.Primary,
|
||||
position = Offset(
|
||||
// mouseX.toFloat(), mouseY.toFloat()
|
||||
MinecraftClient.getInstance().mouse.x.toFloat(),
|
||||
MinecraftClient.getInstance().mouse.y.toFloat()
|
||||
)
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
MinecraftClient.getInstance().setScreen(null)
|
||||
scene.close()
|
||||
surface.close()
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package net.airgame.compose.ui.client
|
||||
|
||||
import com.mojang.blaze3d.platform.TextureUtil
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.fabricmc.api.ClientModInitializer
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper
|
||||
import net.minecraft.client.option.KeyBinding
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.RenderPhase
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
import net.minecraft.client.util.InputUtil
|
||||
import org.jetbrains.skiko.MainUIDispatcher
|
||||
|
||||
class ComposeUIMod : ClientModInitializer {
|
||||
companion object {
|
||||
val singleThreadDispatcher =
|
||||
MainUIDispatcher.limitedParallelism(1) + CoroutineExceptionHandler { _, throwable ->
|
||||
throwable.printStackTrace()
|
||||
}
|
||||
val coroutineScope = CoroutineScope(singleThreadDispatcher)
|
||||
val textureGlID: Int by lazy { TextureUtil.generateTextureId() }
|
||||
val composeLayer: RenderLayer by lazy {
|
||||
RenderLayer.of(
|
||||
"compose",
|
||||
VertexFormats.POSITION_TEXTURE,
|
||||
VertexFormat.DrawMode.QUADS,
|
||||
786432,
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.texture(RenderPhase.TextureBase({
|
||||
RenderSystem.setShaderTexture(0, textureGlID)
|
||||
}, {}))
|
||||
.program(RenderPhase.POSITION_TEXTURE_PROGRAM)
|
||||
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
||||
.depthTest(RenderPhase.LEQUAL_DEPTH_TEST)
|
||||
.build(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val bind = KeyBinding("测试", InputUtil.GLFW_KEY_G, "测试")
|
||||
|
||||
override fun onInitializeClient() {
|
||||
KeyBindingHelper.registerKeyBinding(bind)
|
||||
ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { client ->
|
||||
while (bind.wasPressed()) {
|
||||
val screen = ComposeScreen {
|
||||
TestUI()
|
||||
}
|
||||
client.setScreen(screen)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package net.airgame.compose.ui.client
|
||||
|
||||
class DebugTimer {
|
||||
var time = System.currentTimeMillis()
|
||||
fun log(string: String) {
|
||||
val now = System.currentTimeMillis()
|
||||
println("$string: ${now - time} ms")
|
||||
time = now
|
||||
}
|
||||
}
|
33
src/client/kotlin/net/airgame/compose/ui/client/TestUI.kt
Normal file
33
src/client/kotlin/net/airgame/compose/ui/client/TestUI.kt
Normal file
@@ -0,0 +1,33 @@
|
||||
package net.airgame.compose.ui.client
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun TestUI() {
|
||||
var show by remember { mutableStateOf(true) }
|
||||
Column(
|
||||
Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Button(onClick = {
|
||||
show = !show
|
||||
println("clicked")
|
||||
}) {
|
||||
Text("点我切换")
|
||||
}
|
||||
AnimatedVisibility(show) {
|
||||
Text("Hello Compose Minecraft!")
|
||||
}
|
||||
}
|
||||
}
|
BIN
src/client/resources/assets/compose-ui-mod/icon.png
Normal file
BIN
src/client/resources/assets/compose-ui-mod/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
11
src/client/resources/compose-ui-mod.client.mixins.json
Normal file
11
src/client/resources/compose-ui-mod.client.mixins.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "net.airgame.compose.ui.mixin.client",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"client": [
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
9
src/main/kotlin/net/airgame/compose/ui/ComposeUI.kt
Normal file
9
src/main/kotlin/net/airgame/compose/ui/ComposeUI.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package net.airgame.compose.ui
|
||||
|
||||
import net.fabricmc.api.ModInitializer
|
||||
|
||||
class ComposeUI : ModInitializer {
|
||||
|
||||
override fun onInitialize() {
|
||||
}
|
||||
}
|
11
src/main/resources/compose-ui-mod.mixins.json
Normal file
11
src/main/resources/compose-ui-mod.mixins.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "net.airgame.compose.ui.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
33
src/main/resources/fabric.mod.json
Normal file
33
src/main/resources/fabric.mod.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "compose-ui-mod",
|
||||
"version": "${version}",
|
||||
"name": "ComposeUI",
|
||||
"description": "add jetpack compose to minecraft",
|
||||
"authors": [],
|
||||
"contact": {},
|
||||
"license": "MIT",
|
||||
"icon": "assets/compose-ui-mod/icon.png",
|
||||
"environment": "client",
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"net.airgame.compose.ui.client.ComposeUIMod"
|
||||
],
|
||||
"main": [
|
||||
"net.airgame.compose.ui.ComposeUI"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"compose-ui-mod.mixins.json",
|
||||
{
|
||||
"config": "compose-ui-mod.client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=${loader_version}",
|
||||
"fabric-language-kotlin": ">=${kotlin_loader_version}",
|
||||
"fabric": "*",
|
||||
"minecraft": "${minecraft_version}"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user