Make image preview scalable
This commit is contained in:
@@ -5,8 +5,11 @@ import io.ktor.client.statement.*
|
||||
import io.ktor.utils.io.jvm.javaio.*
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import moe.nea.jarvis.api.Point
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import net.minecraft.client.gui.screen.ChatScreen
|
||||
import net.minecraft.client.texture.NativeImage
|
||||
@@ -35,12 +38,13 @@ object ImagePreview : FirmamentFeature {
|
||||
val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
|
||||
val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
|
||||
val screenPercentage by integer("percentage", 10, 100) { 50 }
|
||||
val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
|
||||
}
|
||||
|
||||
fun isHostAllowed(host: String) =
|
||||
private fun isHostAllowed(host: String) =
|
||||
TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
|
||||
|
||||
fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
|
||||
private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
|
||||
|
||||
override val config get() = TConfig
|
||||
val urlRegex = "https://[^. ]+\\.[^ ]+(\\.(png|gif|jpe?g))(\\?[^ ]*)?( |$)".toRegex()
|
||||
@@ -54,7 +58,7 @@ object ImagePreview : FirmamentFeature {
|
||||
val imageCache: MutableMap<String, Deferred<Image?>> =
|
||||
Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
|
||||
|
||||
fun tryCacheUrl(url: String) {
|
||||
private fun tryCacheUrl(url: String) {
|
||||
if (!isUrlAllowed(url)) {
|
||||
return
|
||||
}
|
||||
@@ -81,6 +85,7 @@ object ImagePreview : FirmamentFeature {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun onLoad() {
|
||||
ClientChatLineReceivedEvent.subscribe {
|
||||
it.replaceWith = it.text.transformEachRecursively { child ->
|
||||
@@ -122,16 +127,10 @@ object ImagePreview : FirmamentFeature {
|
||||
val imageFuture = imageCache[url] ?: return@subscribe
|
||||
if (!imageFuture.isCompleted) return@subscribe
|
||||
val image = imageFuture.getCompleted() ?: return@subscribe
|
||||
val screen = MC.screen!!
|
||||
val scale =
|
||||
min(
|
||||
1F,
|
||||
min(
|
||||
(TConfig.screenPercentage / 100F * screen.width.toFloat()) / image.width,
|
||||
screen.height.toFloat() / image.height
|
||||
)
|
||||
)
|
||||
it.drawContext.matrices.push()
|
||||
val pos = TConfig.position
|
||||
pos.applyTransformations(it.drawContext.matrices)
|
||||
val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
|
||||
it.drawContext.matrices.scale(scale, scale, 1F)
|
||||
it.drawContext.drawTexture(
|
||||
image.texture,
|
||||
|
||||
@@ -22,10 +22,14 @@ import io.github.cottonmc.cotton.gui.widget.WGridPanel
|
||||
import io.github.cottonmc.cotton.gui.widget.WLabel
|
||||
import io.github.cottonmc.cotton.gui.widget.WWidget
|
||||
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class GuiAppender(val width: Int) {
|
||||
class GuiAppender(val width: Int, val screenAccessor: () -> Screen) {
|
||||
private var row = 0
|
||||
|
||||
|
||||
|
||||
internal val panel = WGridPanel().also { it.setGaps(4, 4) }
|
||||
internal val reloadables = mutableListOf<(() -> Unit)>()
|
||||
fun set(x: Int, y: Int, w: Int, h: Int, widget: WWidget) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.cottonmc.cotton.gui.widget.WButton
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.jarvis.JarvisIntegration
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val width: Int, val height: Int) :
|
||||
ManagedConfig.OptionHandler<HudMeta> {
|
||||
override fun toJson(element: HudMeta): JsonElement? {
|
||||
return Json.encodeToJsonElement(element.position)
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement): HudMeta {
|
||||
return HudMeta(Json.decodeFromJsonElement(element), label, width, height)
|
||||
}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedConfig.Option<HudMeta>, guiAppender: GuiAppender) {
|
||||
guiAppender.appendLabeledRow(opt.labelText, WButton(Text.translatable("firmament.hud.edit", label))
|
||||
.also {
|
||||
it.setOnClick {
|
||||
MC.screen = JarvisIntegration.jarvis.getHudEditor(
|
||||
guiAppender.screenAccessor.invoke(),
|
||||
listOf(opt.value)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
46
src/main/kotlin/moe/nea/firmament/gui/config/JAnyHud.kt
Normal file
46
src/main/kotlin/moe/nea/firmament/gui/config/JAnyHud.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import moe.nea.jarvis.api.JarvisHud
|
||||
import moe.nea.jarvis.api.JarvisScalable
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.minecraft.text.Text
|
||||
|
||||
@Serializable
|
||||
data class HudPosition(
|
||||
var x: Double,
|
||||
var y: Double,
|
||||
var scale: Float,
|
||||
)
|
||||
|
||||
|
||||
data class HudMeta(
|
||||
val position: HudPosition,
|
||||
private val label: Text,
|
||||
private val width: Int,
|
||||
private val height: Int,
|
||||
) : JarvisScalable, JarvisHud {
|
||||
override fun getX(): Double = position.x
|
||||
|
||||
override fun setX(newX: Double) {
|
||||
position.x = newX
|
||||
}
|
||||
|
||||
override fun getY(): Double = position.y
|
||||
|
||||
override fun setY(newY: Double) {
|
||||
position.y = newY
|
||||
}
|
||||
|
||||
override fun getLabel(): Text = label
|
||||
|
||||
override fun getWidth(): Int = width
|
||||
|
||||
override fun getHeight(): Int = height
|
||||
|
||||
override fun getScale(): Float = position.scale
|
||||
|
||||
override fun setScale(newScale: Float) {
|
||||
position.scale = newScale
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ package moe.nea.firmament.gui.config
|
||||
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
|
||||
import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription
|
||||
import io.github.cottonmc.cotton.gui.widget.data.Insets
|
||||
import moe.nea.jarvis.api.Point
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
@@ -150,6 +151,19 @@ abstract class ManagedConfig(val name: String) {
|
||||
}
|
||||
|
||||
|
||||
protected fun position(
|
||||
propertyName: String,
|
||||
width: Int,
|
||||
height: Int,
|
||||
default: () -> Point,
|
||||
): Option<HudMeta> {
|
||||
val label = Text.translatable("firmament.config.${name}.${propertyName}")
|
||||
return option(propertyName, {
|
||||
val p = default()
|
||||
HudMeta(HudPosition(p.x, p.y, 1F), label, width, height)
|
||||
}, HudMetaHandler(this, label, width, height))
|
||||
}
|
||||
|
||||
protected fun integer(
|
||||
propertyName: String,
|
||||
min: Int,
|
||||
@@ -175,18 +189,26 @@ abstract class ManagedConfig(val name: String) {
|
||||
|
||||
fun getConfigEditor(parent: Screen? = null): CottonClientScreen {
|
||||
val lwgd = LightweightGuiDescription()
|
||||
val guiapp = GuiAppender(20)
|
||||
var screen: Screen? = null
|
||||
val guiapp = GuiAppender(20, { requireNotNull(screen) { "Screen Accessor called too early" } })
|
||||
latestGuiAppender = guiapp
|
||||
guiapp.panel.insets = Insets.ROOT_PANEL
|
||||
sortedOptions.forEach { it.appendToGui(guiapp) }
|
||||
guiapp.reloadables.forEach { it() }
|
||||
lwgd.setRootPanel(guiapp.panel)
|
||||
return object : CottonClientScreen(lwgd) {
|
||||
override fun close() {
|
||||
latestGuiAppender = null
|
||||
MC.screen = parent
|
||||
screen =
|
||||
object : CottonClientScreen(lwgd) {
|
||||
override fun init() {
|
||||
latestGuiAppender = guiapp
|
||||
super.init()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
latestGuiAppender = null
|
||||
MC.screen = parent
|
||||
}
|
||||
}
|
||||
}
|
||||
return screen
|
||||
}
|
||||
|
||||
fun showConfigEditor(parent: Screen? = null) {
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
package moe.nea.firmament.jarvis
|
||||
|
||||
import moe.nea.jarvis.api.Jarvis
|
||||
import moe.nea.jarvis.api.JarvisConfigOption
|
||||
import moe.nea.jarvis.api.JarvisHud
|
||||
import moe.nea.jarvis.api.JarvisPlugin
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.features.FeatureManager
|
||||
import moe.nea.firmament.gui.config.HudMeta
|
||||
import moe.nea.firmament.gui.config.HudMetaHandler
|
||||
import moe.nea.firmament.repo.RepoManager
|
||||
|
||||
class JarvisIntegration : JarvisPlugin {
|
||||
override fun getModId(): String =
|
||||
Firmament.MOD_ID
|
||||
|
||||
override fun getAllConfigOptions(): List<JarvisConfigOption> {
|
||||
val configs = listOf(
|
||||
companion object {
|
||||
lateinit var jarvis: Jarvis
|
||||
}
|
||||
|
||||
override fun onInitialize(jarvis: Jarvis) {
|
||||
Companion.jarvis = jarvis
|
||||
}
|
||||
|
||||
val configs
|
||||
get() = listOf(
|
||||
RepoManager.Config
|
||||
) + FeatureManager.allFeatures.mapNotNull { it.config }
|
||||
|
||||
|
||||
override fun getAllHuds(): List<JarvisHud> {
|
||||
return configs.flatMap { config ->
|
||||
config.sortedOptions.mapNotNull { if (it.handler is HudMetaHandler) it.value as HudMeta else null }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllConfigOptions(): List<JarvisConfigOption> {
|
||||
return configs.flatMap { config ->
|
||||
config.sortedOptions.map {
|
||||
object : JarvisConfigOption {
|
||||
|
||||
7
src/main/kotlin/moe/nea/firmament/util/propertyutil.kt
Normal file
7
src/main/kotlin/moe/nea/firmament/util/propertyutil.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package moe.nea.firmament.util
|
||||
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
fun <T, V, M> ReadOnlyProperty<T, V>.map(mapper: (V) -> M): ReadOnlyProperty<T, M> {
|
||||
return ReadOnlyProperty { thisRef, property -> mapper(this@map.getValue(thisRef, property)) }
|
||||
}
|
||||
Reference in New Issue
Block a user