aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/utils
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-05-03 17:25:52 +0200
committerGitHub <noreply@github.com>2024-05-03 17:25:52 +0200
commit323cd55125f1485c71c49568aa919e5311c6263f (patch)
treec4c246921fad48623fb81b769ee78f5fa1873993 /src/main/java/at/hannibal2/skyhanni/utils
parent5b2a2d067e68d500bd1c19088dae9e624eeae51f (diff)
downloadskyhanni-323cd55125f1485c71c49568aa919e5311c6263f.tar.gz
skyhanni-323cd55125f1485c71c49568aa919e5311c6263f.tar.bz2
skyhanni-323cd55125f1485c71c49568aa919e5311c6263f.zip
Add hover/click events back to custom chat messages (#1516)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/utils')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ChatComponentUtils.kt8
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ComponentMatcherUtils.kt84
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt13
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt124
4 files changed, 196 insertions, 33 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ChatComponentUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ChatComponentUtils.kt
new file mode 100644
index 000000000..4a0d8ce76
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/ChatComponentUtils.kt
@@ -0,0 +1,8 @@
+package at.hannibal2.skyhanni.utils
+
+import net.minecraft.util.ChatComponentText
+
+object ChatComponentUtils {
+ fun text(text: String, init: ChatComponentText.() -> Unit = {}): ChatComponentText =
+ ChatComponentText(text).also(init)
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ComponentMatcherUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ComponentMatcherUtils.kt
index 4e2b6eb06..f5c25ab2b 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/ComponentMatcherUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/ComponentMatcherUtils.kt
@@ -111,6 +111,29 @@ class ComponentMatcher internal constructor(
if (start < 0) return null
return this.span.slice(start, matcher.end(name))
}
+
+ /**
+ * Return a span equivalent to the group with the given name found by [matches] or [find]
+ */
+ fun component(name: String): IChatComponent? {
+ return group(name)?.intoComponent()
+ }
+
+ /**
+ * Return a span equivalent to the group with the given name found by [matches] or [find].
+ * Returns not nullable object, or throws an error.
+ */
+ fun groupOrThrow(name: String): ComponentSpan {
+ return group(name) ?: error("group '$name' not found in ComponentSpan!")
+ }
+
+ /**
+ * Return a IChatComponent equivalent to the group with the given name found by [matches] or [find].
+ * Returns not nullable object, or throws an error.
+ */
+ fun componentOrThrow(name: String): IChatComponent {
+ return groupOrThrow(name).intoComponent()
+ }
}
/**
@@ -124,7 +147,7 @@ class ComponentMatcher internal constructor(
class ComponentSpan internal constructor(
val textComponent: IChatComponent,
private val cachedText: String,
- val start: Int, val end: Int
+ val start: Int, val end: Int,
) {
init {
require(start <= end)
@@ -140,10 +163,10 @@ class ComponentSpan internal constructor(
/**
* Slice this component span. This is equivalent to the [String.substring] operation on the [text][getText].
*/
- fun slice(start: Int, end: Int): ComponentSpan {
- require(0 <= start)
- require(start <= end)
- require(end <= length)
+ fun slice(start: Int = 0, end: Int = length): ComponentSpan {
+ require(0 <= start) { "start is bigger than 0: start=$start, cachedText=$cachedText" }
+ require(start <= end) { "start is bigger than length: start=$start, length=$length, cachedText=$cachedText" }
+ require(end <= length) { "end is bigger than length: end=$end, length=$length, cachedText=$cachedText" }
return ComponentSpan(textComponent, cachedText, this.start + start, this.start + end)
}
@@ -241,4 +264,55 @@ class ComponentSpan internal constructor(
return sampleComponents().map { it.chatStyle }
}
+ /**
+ * Strip extra `§r` color codes from the beginning / end of this span.
+ */
+ fun stripHypixelMessage(): ComponentSpan {
+ var start = 0
+ var newString = getText()
+ var end = this.length
+ while (newString.startsWith("§r")) {
+ start += 2
+ newString = newString.substring(2)
+ }
+ while (newString.endsWith("§r")) {
+ newString = newString.substring(0, newString.length - 2)
+ end -= 2
+ }
+ return slice(start, end)
+ }
+
+ /**
+ * Remove a prefix from this span if it exists. Otherwise, return this unchanged.
+ */
+ fun removePrefix(prefix: String): ComponentSpan {
+ if (!getText().startsWith(prefix)) return this
+ return slice(prefix.length)
+ }
+
+ /**
+ * Remove a suffix from this span if it exists. Otherwise, return this unchanged.
+ */
+ fun removeSuffix(suffix: String): ComponentSpan {
+ if (!getText().endsWith(suffix)) return this
+ return slice(end = length - suffix.length)
+ }
+
+ /**
+ * Append another [ComponentSpan] to this one to create a new one. This will [flatten][intoComponent] the hierarchy
+ * of the underlying [IChatComponent] but preserve formatting.
+ */
+ operator fun plus(other: ComponentSpan): ComponentSpan {
+ val left = this.intoComponent()
+ val right = other.intoComponent()
+ left.appendSibling(right)
+ return left.intoSpan()
+ }
+
+ companion object {
+ fun empty(): ComponentSpan {
+ return ComponentSpan(ChatComponentText(""), "", 0, 0)
+ }
+ }
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
index cb5099ebe..6ea0f9aa7 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
@@ -17,6 +17,7 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull
import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull
import at.hannibal2.skyhanni.utils.StringUtils.capAtMinecraftLength
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import at.hannibal2.skyhanni.utils.StringUtils.stripHypixelMessage
import at.hannibal2.skyhanni.utils.StringUtils.toDashlessUUID
import at.hannibal2.skyhanni.utils.renderables.Renderable
import com.google.gson.JsonPrimitive
@@ -91,16 +92,10 @@ object LorenzUtils {
fun SimpleDateFormat.formatCurrentTime(): String = this.format(System.currentTimeMillis())
+ // TODO move to string utils
+ @Deprecated("outdated", ReplaceWith("originalMessage.stripHypixelMessage()"))
fun stripVanillaMessage(originalMessage: String): String {
- var message = originalMessage
-
- while (message.startsWith("§r")) {
- message = message.substring(2)
- }
- while (message.endsWith("§r")) {
- message = message.substring(0, message.length - 2)
- }
- return message
+ return originalMessage.stripHypixelMessage()
}
fun Double.round(decimals: Int): Double {
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt
index 388700128..4feb0ba13 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt
@@ -8,6 +8,8 @@ import at.hannibal2.skyhanni.utils.StringUtils.width
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiUtilRenderComponents
import net.minecraft.util.ChatComponentText
+import net.minecraft.util.ChatStyle
+import net.minecraft.util.EnumChatFormatting
import net.minecraft.util.IChatComponent
import java.util.Base64
import java.util.UUID
@@ -321,33 +323,83 @@ object StringUtils {
fun generateRandomId() = UUID.randomUUID().toString()
fun replaceIfNeeded(
- original: IChatComponent,
+ original: ChatComponentText,
newText: String,
): ChatComponentText? {
- val foundCommands = mutableListOf<IChatComponent>()
+ return replaceIfNeeded(original, ChatComponentText(newText))
+ }
- addComponent(foundCommands, original)
- for (sibling in original.siblings) {
- addComponent(foundCommands, sibling)
- }
- val size = foundCommands.size
- if (size > 1) {
- return null
- }
+ private val colorMap = EnumChatFormatting.entries.associateBy { it.toString()[1] }
+ fun enumChatFormattingByCode(char: Char): EnumChatFormatting? {
+ return colorMap[char]
+ }
- val originalClean = LorenzUtils.stripVanillaMessage(original.formattedText)
- val newTextClean = LorenzUtils.stripVanillaMessage(newText)
- if (originalClean == newTextClean) return null
+ fun doLookTheSame(left: IChatComponent, right: IChatComponent): Boolean {
+ class ChatIterator(var component: IChatComponent) {
+ var queue = mutableListOf<IChatComponent>()
+ var idx = 0
+ var colorOverride = ChatStyle()
+ fun next(): Pair<Char, ChatStyle>? {
+ while (true) {
+ while (idx >= component.unformattedTextForChat.length) {
+ queue.addAll(0, component.siblings)
+ colorOverride = ChatStyle()
+ component = queue.removeFirstOrNull() ?: return null
+ }
+ val char = component.unformattedTextForChat[idx++]
+ if (char == '§' && idx < component.unformattedTextForChat.length) {
+ val formattingChar = component.unformattedTextForChat[idx++]
+ val formatting = enumChatFormattingByCode(formattingChar) ?: continue
+ when (formatting) {
+ EnumChatFormatting.OBFUSCATED -> {
+ colorOverride.setObfuscated(true)
+ }
+
+ EnumChatFormatting.BOLD -> {
+ colorOverride.setBold(true)
+ }
+
+ EnumChatFormatting.STRIKETHROUGH -> {
+ colorOverride.setStrikethrough(true)
+ }
+
+ EnumChatFormatting.UNDERLINE -> {
+ colorOverride.setUnderlined(true)
+ }
+
+ EnumChatFormatting.ITALIC -> {
+ colorOverride.setItalic(true)
+ }
+
+ else -> {
+ colorOverride = ChatStyle().setColor(formatting)
+ }
+ }
+ } else {
+ return Pair(char, colorOverride.setParentStyle(component.chatStyle))
+ }
+ }
+ }
+ }
- val text = ChatComponentText(newText)
- if (size == 1) {
- val chatStyle = foundCommands[0].chatStyle
- text.chatStyle.chatClickEvent = chatStyle.chatClickEvent
- text.chatStyle.chatHoverEvent = chatStyle.chatHoverEvent
+ val leftIt = ChatIterator(left)
+ val rightIt = ChatIterator(right)
+ while (true) {
+ val leftChar = leftIt.next()
+ val rightChar = rightIt.next()
+ if (leftChar == null && rightChar == null) return true
+ if (leftChar != rightChar) return false
}
+ }
- return text
+
+ fun <T : IChatComponent> replaceIfNeeded(
+ original: T,
+ newText: T,
+ ): T? {
+ if (doLookTheSame(original, newText)) return null
+ return newText
}
private fun addComponent(foundCommands: MutableList<IChatComponent>, message: IChatComponent) {
@@ -371,6 +423,40 @@ object StringUtils {
}
}
+ /**
+ * Removes starting and ending reset formattings that dont sever a benefit at all.
+ */
+ fun String.stripHypixelMessage(): String {
+ var message = this
+
+ while (message.startsWith("§r")) {
+ message = message.substring(2)
+ }
+ while (message.endsWith("§r")) {
+ message = message.substring(0, message.length - 2)
+ }
+ return message
+ }
+
+ fun String.applyFormattingFrom(original: ComponentSpan): IChatComponent {
+ val text = ChatComponentText(this)
+ text.chatStyle = original.sampleStyleAtStart()
+ return text
+ }
+
+ fun String.applyFormattingFrom(original: IChatComponent): IChatComponent {
+ val text = ChatComponentText(this)
+ text.chatStyle = original.chatStyle
+ return text
+ }
+
+ fun String.toCleanChatComponent(): IChatComponent = ChatComponentText(this)
+
+ fun IChatComponent.cleanPlayerName(displayName: Boolean = false): IChatComponent =
+ formattedText.cleanPlayerName(displayName).applyFormattingFrom(this)
+
+ fun IChatComponent.contains(string: String): Boolean = formattedText.contains(string)
+
fun String.width(): Int {
return Minecraft.getMinecraft().fontRendererObj.getStringWidth(this)
}