Skip to content

Commit

Permalink
[OC-1]: improve error handling, fix builds on modern, fix modern inpu…
Browse files Browse the repository at this point in the history
…t handling [OC-46], fix accordions [OC-45], fix graying of options [OC-54], fix sliders [OC-51], fix missing icon for color selector [OC-52]
  • Loading branch information
nextdayy committed Jan 30, 2025
1 parent b4f6c43 commit 5b16994
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 115 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name=OneConfig
mod_id=oneconfig
version_major=1
version_minor=0
version_patch=0-alpha.59
version_patch=0-alpha.60

polyfrost.defaults.loom=3

Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ kotlin = "2.0.20"
kotlinx-coroutines = "1.8.1"
kotlinx-atomicfu = "0.24.0"
fabric-language-kotlin = "1.12.2+kotlin.2.0.20"
polyui = "1.7.36"
polyui = "1.7.385"
annotations = "24.1.0"
hypixel-modapi = "1.0"
hypixel-data = "0.1.2" # Dep of hypixel-modapi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,43 +34,45 @@ import org.polyfrost.oneconfig.api.config.v1.Visualizer
import org.polyfrost.polyui.animate.Animations
import org.polyfrost.polyui.color.PolyColor
import org.polyfrost.polyui.color.rgba
import org.polyfrost.polyui.component.Component
import org.polyfrost.polyui.component.Drawable
import org.polyfrost.polyui.component.extensions.*
import org.polyfrost.polyui.component.impl.*
import org.polyfrost.polyui.data.PolyImage
import org.polyfrost.polyui.event.Event
import org.polyfrost.polyui.operations.ComponentOp
import org.polyfrost.polyui.operations.Resize
import org.polyfrost.polyui.operations.Rotate
import org.polyfrost.polyui.unit.Align
import org.polyfrost.polyui.unit.Vec2
import org.polyfrost.polyui.unit.by
import org.polyfrost.polyui.unit.seconds
import org.polyfrost.polyui.utils.fastEach
import org.polyfrost.polyui.utils.image
import org.polyfrost.polyui.utils.levenshteinDistance
import org.polyfrost.polyui.utils.mapToArray
import org.polyfrost.polyui.utils.*
import java.lang.ref.WeakReference
import kotlin.math.PI

open class ConfigVisualizer {
private val LOGGER = LogManager.getLogger("OneConfig/Config")
protected val configs = HashMap<Tree, Drawable>()
protected val optBg = rgba(39, 49, 55, 0.2f)
protected val alignCV = Align(cross = Align.Cross.Start, mode = Align.Mode.Vertical)
protected val alignVNoPad = Align(cross = Align.Cross.Start, mode = Align.Mode.Vertical, pad = Vec2.ZERO)
protected val alignCStart = Align(cross = Align.Cross.Start, maxRowSize = 1)
protected val alignCStartNoPad = Align(cross = Align.Cross.Start, maxRowSize = 1, pad = Vec2.ZERO)
protected val stdAlign = Align(main = Align.Main.SpaceBetween, pad = Vec2(16f, 8f))
protected val stdAccord = Align(main = Align.Main.SpaceBetween, pad = Vec2.ZERO)
protected val ic2text = Align(pad = Vec2(8f, 0f))
protected val stdOpt = Align(cross = Align.Cross.Start, mode = Align.Mode.Vertical, pad = Vec2(0f, 8f))
protected val stdOpt = Align(cross = Align.Cross.Start, pad = Vec2(0f, 8f), maxRowSize = 1)
protected val accordOpt = Align(cross = Align.Cross.Start, pad = Vec2(22f, 12f))

/**
* For information, see [create].
*/
fun get(config: Tree) = configs.getOrPut(config) { create(config) }

/**
* Clears the cache of all created config screens.
*/
fun clearCache() {
configs.clear()
}

fun getMatching(str: String): List<Drawable> {
val it = str.trim()
if (it.length < 2) return emptyList()
Expand Down Expand Up @@ -148,10 +150,10 @@ open class ConfigVisualizer {
Group(
Text(header, fontSize = 22f),
*opts.toTypedArray(),
alignment = alignCV,
alignment = alignCStart,
)
},
alignment = alignCV,
alignment = alignCStart,
)
}
}
Expand Down Expand Up @@ -211,30 +213,22 @@ open class ConfigVisualizer {
val e: Property<*>? = tree.getProp("enabled")
val toWrap: Drawable
var enabled: Property<Boolean>? = null
var contentHeight = -1f
// asm: signature as it prevents re-wrapping of function
val openInsn: Drawable.(Any?) -> Unit = {
open = !open
val arrow = if (enabled != null) this[1][1] else this[1]
Rotate(arrow, if (!open) PI else 0.0, false, Animations.Default.create(0.2.seconds)).add()
val value = parent[1].height
val anim = Animations.Default.create(0.4.seconds)
val operation = Resize(parent, width = 0f, height = if (open) -value else value, add = true, anim)
addOperation(
object : ComponentOp.Animatable<Component>(parent, anim, onFinish = {
this[1].renders = !open
this[1].isEnabled = !open
}) {
override fun apply(value: Float) {
operation.apply()
// asm: instruct parent (options list) to replace all its children so that they move with it closing
self.parent.position()
// asm: instruct all children of this accordion to update their visibility based on THIS, NOT its parent
self[1].children!!.fastEach { option ->
option.renders = option.intersects(self.x, self.y, self.width, self.height)
}
}
},
)
val anim = Animations.Default.create(0.6.seconds)
Rotate(arrow, if (!open) PI else 0.0, false, anim).add()
val content = parent[1]
if (contentHeight == -1f) contentHeight = content.height
Resize(parent, width = 0f, height = if (open) -contentHeight else contentHeight, add = true, animation = anim).add()
Resize(content, width = 0f, height = if (open) -contentHeight else contentHeight, add = true, animation = anim).add()
// won't ever open properly unless it renders at least once (tee hee) :)
if (!open) {
content.height = 1f
content.renders = true
}
}

if (e != null && e.type == Boolean::class.java && e.getVisualizer() == null) {
Expand All @@ -259,15 +253,15 @@ open class ConfigVisualizer {
wrap(toWrap, title, desc, icon).also {
it.color = PolyColor.TRANSPARENT
it.onClick(openInsn)
},
}.namedId("AccordionHeader"),
Group(
size = Vec2(1078f, 0f),
alignment = accordOpt,
children = options.toTypedArray(),
).namedId("AccordionContent"),
color = optBg,
alignment = alignVNoPad,
).namedId("AccordionHeader")
alignment = alignCStartNoPad,
).namedId("AccordionContainer")
return out
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public final class EventManager {
public static final EventManager INSTANCE = new EventManager();
private static final Logger LOGGER = LogManager.getLogger("OneConfig/Events");
private final Deque<EventCollector> collectors = new ArrayDeque<>(2);
private final Map<Object, List<EventHandler<?>>> cache = new WeakHashMap<>(5);
private final Map<Object, Iterable<EventHandler<?>>> cache = new WeakHashMap<>(5);
private final Map<Class<? extends Event>, List<EventHandler<?>>> handlers = new HashMap<>(8);
@ApiStatus.Internal
@Nullable
Expand Down Expand Up @@ -126,11 +126,13 @@ public void register(Object object) {
*/
public void register(Object object, boolean removable) {
for (EventCollector m : collectors) {
List<EventHandler<?>> h = m.collect(object);
if (h == null || h.isEmpty()) continue;
Iterable<EventHandler<?>> h = m.collect(object);
if (h == null) continue;
Iterator<EventHandler<?>> iter = h.iterator();
if (!iter.hasNext()) continue;
if (removable) cache.put(object, h);
for (EventHandler<?> handler : h) {
register(handler);
while (iter.hasNext()) {
register(iter.next());
}
}
}
Expand Down Expand Up @@ -187,7 +189,7 @@ public boolean unregister(EventHandler<?> handler) {
* <br><b>This method only works if the object was registered with removable true!</b>
*/
public boolean unregister(Object object) {
Collection<EventHandler<?>> h = cache.remove(object);
Iterable<EventHandler<?>> h = cache.remove(object);
if (h == null) return false;
boolean state = true;
for (EventHandler<?> handler : h) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,12 @@

package org.polyfrost.oneconfig.api.event.v1.invoke;

import java.util.List;

@FunctionalInterface
public interface EventCollector {
/**
* Take an object and collect any valid event handlers that may exist inside the object.
*
* @return a list of handlers, or null if the object cannot be collected by this collector
*/
List<EventHandler<?>> collect(Object object);
Iterable<EventHandler<?>> collect(Object object);
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class HudVisualizer : ConfigVisualizer() {
mapOf(
options.keys.first() to Group(
*options.values.first().values.first().toTypedArray(),
alignment = alignVNoPad,
alignment = alignCStartNoPad,
)
)
} else super.flattenSubcategories(options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,23 @@

package org.polyfrost.oneconfig.api.ui.v1;

import org.apache.logging.log4j.LogManager;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.polyfrost.oneconfig.api.event.v1.EventDelay;
import org.polyfrost.oneconfig.api.event.v1.EventManager;
import org.polyfrost.oneconfig.api.event.v1.events.HudRenderEvent;
import org.polyfrost.oneconfig.api.event.v1.events.ResizeEvent;
import org.polyfrost.oneconfig.api.event.v1.events.WorldLoadEvent;
import org.polyfrost.oneconfig.api.platform.v1.Platform;
import org.polyfrost.oneconfig.api.ui.v1.api.TinyFdApi;
import org.polyfrost.polyui.PolyUI;
import org.polyfrost.polyui.component.Component;
import org.polyfrost.polyui.Settings;
import org.polyfrost.polyui.component.Component;
import org.polyfrost.polyui.renderer.Renderer;
import org.polyfrost.polyui.renderer.Window;
import org.polyfrost.universal.UChat;
import org.polyfrost.universal.UMatrixStack;

import java.util.ServiceLoader;
Expand Down Expand Up @@ -72,11 +76,11 @@ public interface UIManager {
/**
* Wrap this PolyUI instance in a Minecraft screen object, ready to be displayed to the user. {@link org.polyfrost.oneconfig.api.platform.v1.ScreenPlatform#display(Object) Platform.screen().display(this)}
*
* @param polyUI the PolyUI instance to use
* @param polyUI the PolyUI instance to use
* @param designedWidth the resolution that this PolyUI instance was designed to use
* @param pauses weather to pause the game when the screen is opened
* @param blurs if true blur will be used on the background
* @param onClose callback to run when the screen is closed
* @param pauses weather to pause the game when the screen is opened
* @param blurs if true blur will be used on the background
* @param onClose callback to run when the screen is closed
* @return a Minecraft screen object. Will be a GuiScreen or Screen depending on the Minecraft version.
*/
Object createPolyUIScreen(@NotNull PolyUI polyUI, float designedWidth, float designedHeight, boolean pauses, boolean blurs, @Nullable Consumer<PolyUI> onClose);
Expand All @@ -92,19 +96,25 @@ public interface UIManager {
*/
@ApiStatus.Internal
default PolyUI createDefault() {
Settings settings = new Settings();
settings.enableDebugMode(false);
settings.enableInitCleanup(false);
PolyUI p = new PolyUI(new Component[0], getRenderer(), settings, 1920f, 1080f);
p.getMaster().setRawResize(true);
p.setWindow(createWindow());
p.resize(Platform.screen().windowWidth(), Platform.screen().windowHeight(), false);
EventManager.register(HudRenderEvent.class, ev -> {
UMatrixStack stack = ev.matrices;
Platform.screen().setSmuggledMatrixStack(stack);
stack.runWithGlobalState(p::render);
});
EventManager.register(ResizeEvent.class, ev -> p.resize(ev.newWidth, ev.newHeight, false));
return p;
try {
Settings settings = new Settings();
settings.enableDebugMode(false);
settings.enableInitCleanup(false);
PolyUI p = new PolyUI(new Component[0], getRenderer(), settings, 1920f, 1080f);
p.getMaster().setRawResize(true);
p.setWindow(createWindow());
p.resize(Platform.screen().windowWidth(), Platform.screen().windowHeight(), false);
EventManager.register(HudRenderEvent.class, ev -> {
UMatrixStack stack = ev.matrices;
Platform.screen().setSmuggledMatrixStack(stack);
stack.runWithGlobalState(p::render);
});
EventManager.register(ResizeEvent.class, ev -> p.resize(ev.newWidth, ev.newHeight, false));
return p;
} catch (Throwable t) {
LogManager.getLogger("OneConfig/UI").error("Failed to load renderer!", t);
EventManager.register(WorldLoadEvent.class, () -> EventDelay.tick(20, () -> UChat.chat("&cFailed to load the renderer for OneConfig. This means the UI, HUD and Notifications will not work. Please report this to https://discord.gg/polyfrost and attach your log.")));
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

package org.polyfrost.oneconfig.api.ui.v1.keybind

import org.apache.logging.log4j.LogManager
import org.polyfrost.oneconfig.api.event.v1.eventHandler
import org.polyfrost.oneconfig.api.event.v1.events.KeyInputEvent
import org.polyfrost.oneconfig.api.event.v1.events.ScreenOpenEvent
Expand All @@ -40,6 +41,7 @@ import org.polyfrost.universal.UKeyboard

@Suppress("UnstableApiUsage")
object KeybindManager {
private val LOGGER = LogManager.getLogger("OneConfig/Keybinds")
private val settings = Settings()
private val keyBinder = KeyBinder(settings)
val inputManager = InputManager(null, keyBinder, settings)
Expand Down Expand Up @@ -116,31 +118,40 @@ object KeybindManager {
fun builder() = OCKeybindHelper()

@JvmStatic
fun translateKey(inputManager: InputManager, key: Int, character: Char, state: Boolean) {
if (character != '\u0000' && !character.isISOControl() && character.isDefined()) {
if (state) {
inputManager.keyTyped(character)
inputManager.keyDown(character.lowercaseChar().code)
} else inputManager.keyUp(character.lowercaseChar().code)
return
fun translateKey(inputManager: InputManager, key: Int, char: Char, state: Boolean) {
// fix for modified characters not being sent in glfwCharCallback as glfwSetCharModsCallback is deprecated
// for more info (see PolyUI/nanovg-impl/GLFWWindow)
val character = if (!char.isValid() && key < 255 && inputManager.mods > 1.toByte() && state) (key + 32).toChar() else char
try {
if (character.isValid()) {
if (state) {
inputManager.keyTyped(character)
inputManager.keyDown(character.lowercaseChar().code)
} else inputManager.keyUp(character.lowercaseChar().code)
return
}

val k = keysMap[key]
if (k != null) {
if (state) inputManager.keyDown(k)
else inputManager.keyUp(k)
return
}

val m = modsMap[key].toByte()
if (m != 0.toByte()) {
if (state) inputManager.addModifier(m)
else inputManager.removeModifier(m)
return
}

val raw = if (inputManager.mods > 1) key + 48 else key
if (state) inputManager.keyDown(raw)
else inputManager.keyUp(raw)
} catch (t: Throwable) {
LOGGER.error("Failed to process input key=$key, char=$character, state=$state", t)
}

val k = keysMap[key]
if (k != null) {
if (state) inputManager.keyDown(k)
else inputManager.keyUp(k)
return
}

val m = modsMap[key].toByte()
if (m != 0.toByte()) {
if (state) inputManager.addModifier(m)
else inputManager.removeModifier(m)
return
}

val raw = if (inputManager.mods > 1) key + 48 else key
if (state) inputManager.keyDown(raw)
else inputManager.keyUp(raw)
}

private fun Char.isValid() = this != '\u0000' && !this.isISOControl() && this.isDefined()
}
5 changes: 5 additions & 0 deletions modules/ui/src/main/resources/info.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions versions/mappings/fabric-1.19.4-1.18.2.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
net.minecraft.client.network.ClientPlayerEntity sendMessage() sendChatMessage()

net.minecraft.network.packet.s2c.play.GameMessageS2CPacket content() getMessage()
net.minecraft.text.Text getString() asString()
2 changes: 2 additions & 0 deletions versions/mappings/forge-1.19.4-1.18.2.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
net.minecraft.client.player.LocalPlayer sendSystemMessage() chat()

net.minecraftforge.client.event.ClientPlayerNetworkEvent$LoggingIn net.minecraftforge.client.event.ClientPlayerNetworkEvent$LoggedInEvent

net.minecraftforge.client.gui.overlay.ForgeGui net.minecraftforge.client.gui.ForgeIngameGui
Expand Down
Loading

0 comments on commit 5b16994

Please sign in to comment.