From f95e6d2580be644eb679d8dc9274ad189425ed19 Mon Sep 17 00:00:00 2001 From: kikugie Date: Sat, 24 Aug 2024 12:27:32 +0200 Subject: [PATCH] Various things --- kowoui/src/main/resources/kowoui.mixins.json | 4 +-- .../soundboard/mixin/KeyboardMixin.java | 23 +++++++++++++ .../dev/kikugie/soundboard/ModKeyBinds.kt | 1 + .../dev/kikugie/soundboard/Soundboard.kt | 8 ++++- .../audio/data/AudioConfiguration.kt | 3 ++ .../soundboard/audio/data/SoundEntry.kt | 2 +- .../kikugie/soundboard/audio/data/SoundId.kt | 6 ++-- .../soundboard/audio/download/Downloader.kt | 15 +++++++-- .../audio/registry/FileAudioHolder.kt | 8 ++--- .../kikugie/soundboard/config/AudioConfig.kt | 10 ++++-- .../soundboard/gui/screen/SoundBrowser.kt | 6 ++-- .../gui/widget/SoundSettingsWidget.kt | 32 +++++++++---------- .../dev/kikugie/soundboard/util/Util.kt | 2 +- .../assets/soundboard/lang/en_us.yml | 6 ++++ src/main/resources/soundboard.mixins.json | 1 + 15 files changed, 93 insertions(+), 34 deletions(-) create mode 100644 src/main/java/dev/kikugie/soundboard/mixin/KeyboardMixin.java diff --git a/kowoui/src/main/resources/kowoui.mixins.json b/kowoui/src/main/resources/kowoui.mixins.json index d4115a9..f4586cd 100644 --- a/kowoui/src/main/resources/kowoui.mixins.json +++ b/kowoui/src/main/resources/kowoui.mixins.json @@ -3,8 +3,8 @@ "package": "dev.kikugie.kowoui.mixin", "compatibilityLevel": "JAVA_21", "mixins": [ - "owo.BaseParentComponentMixin", - "TextFieldWidgetMixin" + "TextFieldWidgetMixin", + "owo.BaseParentComponentMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/java/dev/kikugie/soundboard/mixin/KeyboardMixin.java b/src/main/java/dev/kikugie/soundboard/mixin/KeyboardMixin.java new file mode 100644 index 0000000..05dbb7f --- /dev/null +++ b/src/main/java/dev/kikugie/soundboard/mixin/KeyboardMixin.java @@ -0,0 +1,23 @@ +package dev.kikugie.soundboard.mixin; + +import dev.kikugie.soundboard.ModKeyBinds; +import net.minecraft.client.Keyboard; +import net.minecraft.client.option.KeyBinding; +import org.lwjgl.glfw.GLFW; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Keyboard.class) +public class KeyboardMixin { + @Inject(method = "onKey", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Keyboard;debugCrashStartTime:J"), cancellable = true) + private void cancelOnKeyCombo(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { + boolean isNumrow = key >= GLFW.GLFW_KEY_0 && key <= GLFW.GLFW_KEY_9; + boolean isNumpad = key >= GLFW.GLFW_KEY_KP_0 && key <= GLFW.GLFW_KEY_KP_9; + if (!isNumrow && !isNumpad) return; + + KeyBinding play = ModKeyBinds.get("play"); + if (play != null && play.isPressed()) ci.cancel(); + } +} diff --git a/src/main/kotlin/dev/kikugie/soundboard/ModKeyBinds.kt b/src/main/kotlin/dev/kikugie/soundboard/ModKeyBinds.kt index 61b1df2..a0a59d9 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/ModKeyBinds.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/ModKeyBinds.kt @@ -8,6 +8,7 @@ import net.minecraft.client.option.KeyBinding object ModKeyBinds { private val keybinds = mutableMapOf() + @JvmStatic operator fun get(name: String): KeyBinding? = keybinds[name]?.keybind fun keybind(key: Int, name: String, action: KeyBuilder.() -> Unit) { val bind = KeyBindingHelper.registerKeyBinding(KeyBinding("soundboard.keybinds.$name", key, "soundboard.title")) diff --git a/src/main/kotlin/dev/kikugie/soundboard/Soundboard.kt b/src/main/kotlin/dev/kikugie/soundboard/Soundboard.kt index 6a5d508..02f8b8f 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/Soundboard.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/Soundboard.kt @@ -34,7 +34,12 @@ object Soundboard { ModKeyBinds.keybind(GLFW.GLFW_KEY_J, "browser") { inGame { SoundBrowser.open() } - inGui { if (this is SoundBrowser) close() } + inGui { + if (this is SoundBrowser) { + if (settings != null) closeSettings() + else close() + } + } } ModKeyBinds.keybind(GLFW.GLFW_KEY_U, "cancel") { val reset = { SoundboardAccess.forEach { scheduler.reset() } } @@ -52,6 +57,7 @@ object Soundboard { // 0 converts to 9 because it's the last on the number row InputUtil.isKeyPressed(handle, GLFW.GLFW_KEY_0) || InputUtil.isKeyPressed(handle, GLFW.GLFW_KEY_KP_0) -> 9 + else -> numrow.firstNotNullOfOrNull { if (InputUtil.isKeyPressed(handle, it)) it - GLFW.GLFW_KEY_1 else null } ?: numpad.firstNotNullOfOrNull { diff --git a/src/main/kotlin/dev/kikugie/soundboard/audio/data/AudioConfiguration.kt b/src/main/kotlin/dev/kikugie/soundboard/audio/data/AudioConfiguration.kt index 4a2febf..20fe3cd 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/audio/data/AudioConfiguration.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/audio/data/AudioConfiguration.kt @@ -13,6 +13,9 @@ data class AudioConfiguration( inline fun clone(block: AudioConfiguration.() -> Unit) = clone().apply(block) inline fun cloneOrNull(block: AudioConfiguration.() -> Unit) = clone(block).takeIf { it != DEFAULT } + fun isDefault(dest: Duration = Duration.INFINITE) = + start <= Duration.ZERO && end in dest..Duration.INFINITE && volume == 1.0 + companion object { val DEFAULT = AudioConfiguration(Duration.ZERO, Duration.INFINITE, 1.0) } diff --git a/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundEntry.kt b/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundEntry.kt index e045f5d..74386e2 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundEntry.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundEntry.kt @@ -9,7 +9,7 @@ import java.io.InputStream data class SoundEntry( val id: SoundId, val supplier: () -> InputStream, - var settings: AudioConfiguration? = null, + val settings: AudioConfiguration? = null, private val text: Text? = null, ) { val group: SoundId by lazy { id.parent() } diff --git a/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundId.kt b/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundId.kt index 3db5584..0f440fb 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundId.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/audio/data/SoundId.kt @@ -17,12 +17,12 @@ import kotlin.io.path.exists @JvmInline @Serializable value class SoundId(val str: String) { - val namespace get() = str.substringBefore(':') + val namespace get() = str.substringBefore(':', "") val directory get() = str.substringAfter(':').substringBeforeLast('/') val path get() = str.substringAfter(':') - val file get() = str.substringAfterLast('/') + val file get() = str.substringAfterLast('/', "") - constructor(namespace: String, path: String) : this("$namespace:$path") + constructor(namespace: String, path: String) : this("${ if (namespace.isEmpty() || namespace == MOD_ID) "" else "$namespace:" }$path") constructor(id: Identifier) : this(id.toString()) fun parent() = SoundId(namespace, directory.ifEmpty { "/" }) diff --git a/src/main/kotlin/dev/kikugie/soundboard/audio/download/Downloader.kt b/src/main/kotlin/dev/kikugie/soundboard/audio/download/Downloader.kt index da76e98..bf31bb2 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/audio/download/Downloader.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/audio/download/Downloader.kt @@ -3,10 +3,12 @@ package dev.kikugie.soundboard.audio.download import dev.kikugie.kowoui.access.* import dev.kikugie.kowoui.experimental.plusAssign import dev.kikugie.kowoui.overlay +import dev.kikugie.kowoui.translation import dev.kikugie.kowoui.util.CombinedAlignment import dev.kikugie.soundboard.GAME_DIR import dev.kikugie.soundboard.LOGGER import dev.kikugie.soundboard.gui.widget.DownloadErrorWidget +import dev.kikugie.soundboard.util.client import io.wispforest.owo.ui.container.StackLayout import io.wispforest.owo.ui.core.Positioning import io.wispforest.owo.ui.core.Sizing @@ -17,8 +19,11 @@ import kotlinx.coroutines.cancel import java.lang.ref.WeakReference import java.net.URI import java.nio.file.Path +import kotlin.io.path.invariantSeparatorsPathString object Downloader { + private const val FAILURE = "soundboard.download.failure" + private const val SUCCESS = "soundboard.download.success" private val downloads: MutableMap> = mutableMapOf() fun isDownloading(path: Path) = path in downloads @@ -27,14 +32,20 @@ object Downloader { if (downloads[dest]?.first == url) return // Don't repeat downloads val job = CobaltApi.download(url, dest).apply { invokeOnCompletion { + val file = GAME_DIR.relativize(dest) if (it !is CancellationException) downloads.remove(dest) when (it) { - null -> LOGGER.info("Saved $url to ${GAME_DIR.relativize(dest)}") is CancellationException -> LOGGER.info("Download cancelled for $url") + null -> { + LOGGER.info("Saved $url to $file") + client.player?.sendMessage(SUCCESS.translation(file.invariantSeparatorsPathString)) + } + else -> { LOGGER.error("Failed to download $url", it) // TODO should open a popup if screen has been closed - ref.get()?.createWidget(it) + ref.get()?.createWidget(it) ?: client.player + ?.sendMessage(FAILURE.translation(file.invariantSeparatorsPathString)) } } } diff --git a/src/main/kotlin/dev/kikugie/soundboard/audio/registry/FileAudioHolder.kt b/src/main/kotlin/dev/kikugie/soundboard/audio/registry/FileAudioHolder.kt index e67db29..e5aa309 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/audio/registry/FileAudioHolder.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/audio/registry/FileAudioHolder.kt @@ -7,7 +7,6 @@ import dev.kikugie.soundboard.audio.* import dev.kikugie.soundboard.audio.data.SoundEntry import dev.kikugie.soundboard.audio.data.SoundGroup import dev.kikugie.soundboard.audio.data.SoundId -import dev.kikugie.soundboard.config.AudioConfig import it.unimi.dsi.fastutil.objects.Object2LongMap import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap import net.minecraft.text.Text @@ -27,8 +26,9 @@ object FileAudioHolder { operator fun get(id: SoundId): SoundEntry? { val path = runCatching { BASE_DIR.resolve(id.path.removePrefix("/") + ".$FORMAT") } .getOrNull() ?: return null - if (cache(path)) return sounds[id.parent()][id] - update() + val props = path.withExtension("properties") + val cached = cache(path) && (props.notExists() || cache(props)) + if (!cached) update() return sounds[id.parent()][id] } @@ -62,7 +62,7 @@ object FileAudioHolder { private fun entry(id: SoundId, path: Path, props: Path): SoundEntry { val title = runCatching { PropertiesReader.read(props)["title"]?.text() }.getOrNull() - return SoundEntry(id, path::inputStream, AudioConfig[id], title) + return SoundEntry(id, path::inputStream, null, title) } private fun cache(path: Path) = diff --git a/src/main/kotlin/dev/kikugie/soundboard/config/AudioConfig.kt b/src/main/kotlin/dev/kikugie/soundboard/config/AudioConfig.kt index 3f76b93..1b701fa 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/config/AudioConfig.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/config/AudioConfig.kt @@ -32,8 +32,14 @@ object AudioConfig { operator fun get(entry: SoundEntry): AudioConfiguration? = get(entry.id) operator fun get(id: SoundId): AudioConfiguration? = configurations[id] - operator fun set(entry: SoundEntry, configuration: AudioConfiguration) { - configurations[entry.id] = configuration + operator fun set(entry: SoundEntry, configuration: AudioConfiguration) = set(entry.id, configuration) + operator fun set(id: SoundId, configuration: AudioConfiguration) { + configurations[id] = configuration + runOn(Dispatchers.IO) { save() } + } + operator fun minusAssign(entry: SoundEntry) = minusAssign(entry.id) + operator fun minusAssign(id: SoundId) { + configurations.remove(id) runOn(Dispatchers.IO) { save() } } diff --git a/src/main/kotlin/dev/kikugie/soundboard/gui/screen/SoundBrowser.kt b/src/main/kotlin/dev/kikugie/soundboard/gui/screen/SoundBrowser.kt index e81037a..ec9f6e0 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/gui/screen/SoundBrowser.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/gui/screen/SoundBrowser.kt @@ -29,6 +29,7 @@ import io.wispforest.owo.ui.container.StackLayout import io.wispforest.owo.ui.core.Insets.* import io.wispforest.owo.ui.core.OwoUIAdapter import io.wispforest.owo.ui.core.ParentComponent +import io.wispforest.owo.ui.core.Positioning.relative import io.wispforest.owo.ui.core.Sizing.expand import io.wispforest.owo.ui.core.Sizing.fill import io.wispforest.owo.ui.core.Surface @@ -112,6 +113,7 @@ class SoundBrowser : BaseOwoScreen() { scrollbarThickness = 10 scrollbar = vanilla() surface = Surface.PANEL_INSET + padding = bottom(1) } this += SidebarWidget(this@SoundBrowser) } @@ -174,10 +176,10 @@ class SoundBrowser : BaseOwoScreen() { private fun settings(entry: SoundEntry) { settings = SoundSettingsWidget(entry, SoundboardAccess.delegates.first()) root + overlay(settings!!) { - sizing = fill(50) closeOnClick = false surface = CONFIG_PANEL - positioning = io.wispforest.owo.ui.core.Positioning.relative(50, 50) + sizing = fill(65) + positioning = relative(50, 50) zIndex = 100 onMouseDown { _, _, _ -> true } } diff --git a/src/main/kotlin/dev/kikugie/soundboard/gui/widget/SoundSettingsWidget.kt b/src/main/kotlin/dev/kikugie/soundboard/gui/widget/SoundSettingsWidget.kt index f71eaaf..23a0275 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/gui/widget/SoundSettingsWidget.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/gui/widget/SoundSettingsWidget.kt @@ -5,19 +5,20 @@ import dev.kikugie.kowoui.access.* import dev.kikugie.kowoui.dynamic.ColoredTextComponent import dev.kikugie.kowoui.dynamic.dynamicButton import dev.kikugie.kowoui.dynamic.dynamicLabel +import dev.kikugie.kowoui.dynamic.fixedSpacer import dev.kikugie.kowoui.experimental.at import dev.kikugie.kowoui.experimental.plusAssign import dev.kikugie.soundboard.CONFIG import dev.kikugie.soundboard.ModKeyBinds import dev.kikugie.soundboard.Soundboard -import dev.kikugie.soundboard.audio.data.AudioConfiguration +import dev.kikugie.soundboard.audio.data.AudioConfiguration.Companion.DEFAULT import dev.kikugie.soundboard.audio.data.SoundEntry import dev.kikugie.soundboard.audio.registry.SoundRegistry import dev.kikugie.soundboard.config.AudioConfig import dev.kikugie.soundboard.entrypoint.SoundboardEntrypoint -import dev.kikugie.soundboard.gui.screen.SoundBrowser import dev.kikugie.soundboard.gui.component.* import dev.kikugie.soundboard.gui.component.TimeInputComponent.Companion.asString +import dev.kikugie.soundboard.gui.screen.SoundBrowser import dev.kikugie.soundboard.util.client import dev.kikugie.soundboard.util.currentScreen import dev.kikugie.soundboard.util.duration @@ -25,7 +26,6 @@ import dev.kikugie.soundboard.util.read import io.wispforest.owo.ui.component.ButtonComponent.Renderer import io.wispforest.owo.ui.component.SlimSliderComponent.Axis.VERTICAL import io.wispforest.owo.ui.container.FlowLayout -import io.wispforest.owo.ui.container.WrappingParentComponent import io.wispforest.owo.ui.core.* import io.wispforest.owo.ui.core.Insets.bottom import io.wispforest.owo.ui.core.Sizing.* @@ -35,7 +35,7 @@ import kotlin.time.Duration class SoundSettingsWidget( private val entry: SoundEntry, private val access: SoundboardEntrypoint, -) : WrappingParentComponent(fill(), fill(), verticalFlow { sizing = fill() }) { +) : FlowLayout(content(), content(), Algorithm.VERTICAL) { companion object { private const val CLOSE = "×" private const val CLOSE_TOOLTIP = "soundboard.browser.tooltip.close" @@ -54,25 +54,22 @@ class SoundSettingsWidget( private val data = entry.read(access.format) private val duration = access.format.duration(data.size) - private val default = AudioConfiguration(Duration.ZERO, duration, 1.0) - private val settings = AudioConfig[entry] ?: default.clone() + private val settings = AudioConfig[entry] ?: entry.settings ?: DEFAULT.clone() init { + if (settings.end == Duration.INFINITE) settings.end = duration create() } fun update() { - if (settings != default) AudioConfig[entry] = settings - } - - override fun draw(context: OwoUIDrawContext, mouseX: Int, mouseY: Int, partialTicks: Float, delta: Float) { - super.draw(context, mouseX, mouseY, partialTicks, delta) - child.draw(context, mouseX, mouseY, partialTicks, delta) + if (settings.isDefault(duration) || settings == entry.settings) AudioConfig -= entry + else AudioConfig[entry] = settings } private val Double.invert get() = 1 - this - private fun create() = with(child) { + private fun create() = with(this as FlowLayout) { + id = "settings-container" padding = Insets.of(5) var favourite = entry.id in Soundboard.config.favourites var index = -1 @@ -131,6 +128,7 @@ class SoundSettingsWidget( else if (index < 0) CONFIG.favourites += entry.id else CONFIG.favourites.add(index, entry.id) + CONFIG.save() SoundRegistry.update() (currentScreen as? SoundBrowser)?.createFavourites() } @@ -170,7 +168,7 @@ class SoundSettingsWidget( "${(settings.volume * 100).toInt()}%".text() } } - at(1, 0) += grid(1, 3) { + at(1, 0) += grid(1, 5) { id = "duration-controls" horizontalSizing = expand() horizontalAlignment = HorizontalAlignment.CENTER @@ -180,13 +178,15 @@ class SoundSettingsWidget( validate { it <= settings.end } onDurationChange { cutter.update() } } - at(0, 1) += dynamicLabel{ + at(0, 1) += fixedSpacer(5) + at(0, 2) += dynamicLabel{ id = "duration" horizontalTextAlignment = HorizontalAlignment.CENTER verticalTextAlignment = VerticalAlignment.CENTER text { "${(settings.end - settings.start).asString}s".text() } } - at(0, 2) += TimeInputComponent(duration, settings::end).apply { + at(0, 3) += fixedSpacer(5) + at(0, 4) += TimeInputComponent(duration, settings::end).apply { id = "end" validate { it >= settings.start } onDurationChange { cutter.update() } diff --git a/src/main/kotlin/dev/kikugie/soundboard/util/Util.kt b/src/main/kotlin/dev/kikugie/soundboard/util/Util.kt index 9fb1dc6..251eb4e 100644 --- a/src/main/kotlin/dev/kikugie/soundboard/util/Util.kt +++ b/src/main/kotlin/dev/kikugie/soundboard/util/Util.kt @@ -28,7 +28,7 @@ inline infix fun Boolean.then(action: () -> Unit): Boolean { return this } -fun Class<*>.accurateName(): String = simpleName.takeIf { it.isNotEmpty() } ?: run { +fun Class<*>.accurateName(): String = simpleName.ifEmpty { val parent = if (this == Any::class.java) "Object" else superclass.accurateName() "out $parent" } diff --git a/src/main/resources/assets/soundboard/lang/en_us.yml b/src/main/resources/assets/soundboard/lang/en_us.yml index e5369e0..253e774 100644 --- a/src/main/resources/assets/soundboard/lang/en_us.yml +++ b/src/main/resources/assets/soundboard/lang/en_us.yml @@ -44,6 +44,12 @@ soundboard: You're responsible for what you download, so be mindful when using the service. color: dark_gray bold: false + failure: + - text: Failed to save %s. Check the game log + color: red + success: + - text: Downloaded %s + color: green tooltip: overwrite_path: File already exists and will be overwritten downloading_path: File is already being downloaded and will be overwritten diff --git a/src/main/resources/soundboard.mixins.json b/src/main/resources/soundboard.mixins.json index 8c30cb8..48e3ac3 100644 --- a/src/main/resources/soundboard.mixins.json +++ b/src/main/resources/soundboard.mixins.json @@ -3,6 +3,7 @@ "package": "dev.kikugie.soundboard.mixin", "compatibilityLevel": "JAVA_21", "mixins": [ + "KeyboardMixin", "owo_ui.OwODrawContextMixin", "owo_ui.ScrollContainerAccessor" ],