Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,15 @@ private class Entry(val hudClass: Class<out Hud>, val values: Map<String, Value>

private class Layout(val owner: ConfigManager, val huds: Map<String, Entry>)

private fun sameValue(a: Any?, b: Any?): Boolean =
if (a is Float && b is Float) abs(a - b) < FLOAT_TOLERANCE else a == b
private fun sameValue(a: Any?, b: Any?): Boolean = when {
a is Float && b is Float -> abs(a - b) < FLOAT_TOLERANCE
a is Map<*, *> && b is Map<*, *> ->
a.size == b.size && a.all { (key, value) -> b.containsKey(key) && sameValue(value, b[key]) }
a is List<*> && b is List<*> -> a.size == b.size && a.indices.all { sameValue(a[it], b[it]) }
a is Array<*> && b is Array<*> -> sameValue(a.asList(), b.asList())
a != null && b != null && a.javaClass.isArray && b.javaClass.isArray -> java.util.Objects.deepEquals(a, b)
else -> a == b
}

private fun sameLayout(a: Layout, b: Layout): Boolean {
if (a.owner !== b.owner || a.huds.size != b.huds.size) return false
Expand Down Expand Up @@ -188,12 +195,15 @@ internal class HudHistory {
private val redoStack = ArrayDeque<Layout>()
private var recorded: Layout? = null
private var settling: Layout? = null
private var pending: Layout? = null
private var wasBusy = false

private fun reset(now: Layout) {
undoStack.clear()
redoStack.clear()
recorded = now
settling = null
pending = null
}

private fun record(now: Layout) {
Expand All @@ -202,6 +212,7 @@ internal class HudHistory {
redoStack.clear()
recorded = now
settling = null
pending = null
}

private fun unrecorded(): Boolean = recorded?.let { !sameLayout(it, capture()) } == true
Expand All @@ -218,10 +229,16 @@ internal class HudHistory {
reset(now)
return
}
val gestureStarted = busy && !wasBusy
wasBusy = busy
if (gestureStarted) {
pending?.let(::record)
}
if (busy || sameLayout(last, now)) {
settling = null
return
}
pending = now
val previous = settling
if (previous != null && sameLayout(previous, now)) record(now) else settling = now
}
Expand All @@ -244,6 +261,7 @@ internal class HudHistory {
val revived = restore(target)
recorded = capture()
settling = null
pending = null
return revived
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.polyfrost.oneconfig.api.config.v1.ConfigManager;
import org.polyfrost.oneconfig.api.config.v1.annotations.Color;
import org.polyfrost.oneconfig.api.hud.v1.Hud;
import org.polyfrost.oneconfig.api.hud.v1.HudAnchor;
import org.polyfrost.oneconfig.api.hud.v1.HudManager;
import org.polyfrost.oneconfig.api.hud.v1.TextHud;
import org.polyfrost.compose.render.PolyColor;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -179,6 +182,121 @@ void addedHudIsRemovedOnUndoAndComesBackOnRedo() {
assertNotNull(HudManager.INSTANCE.instanceById(id));
}

@Test
void eachActionIsItsOwnStep() {
Hud hud = place();
history.poll(false);

hud.setAbsolutePosition(100f, 60f);
settle();
hud.setAbsolutePosition(200f, 120f);
settle();
hud.setTextColor(0xFF112233);
settle();

assertNotNull(history.undo());
assertEquals(0xFFFFFFFF, hud.getTextColor());
assertEquals(200f, hud.getX(), 0.01f);

assertNotNull(history.undo());
assertEquals(100f, hud.getX(), 0.01f);

assertNotNull(history.redo());
assertEquals(200f, hud.getX(), 0.01f);
assertEquals(0xFFFFFFFF, hud.getTextColor());
}

@Test
void untouchedColorOptionIsNotAChange() {
Hud hud = place();
history.poll(false);
settle();

assertNull(history.undo());

hud.setAbsolutePosition(200f, 120f);
settle();
assertNotNull(history.undo());
assertEquals(10f, hud.getX(), 0.01f);
assertNull(history.undo());
}

@Test
void colorChangeIsItsOwnStep() {
TestHud hud = (TestHud) place();
history.poll(false);

hud.setAbsolutePosition(100f, 60f);
settle();
hud.accent = new PolyColor(0xFF112233);
settle();

assertNotNull(history.undo());
assertEquals(0xFF2B4BFF, hud.accent.getArgb());
assertEquals(100f, hud.getX(), 0.01f);
}

@Test
void movingAnchoredHudIsOneStep() {
Hud parent = place();
Hud child = place();
child.setAbsolutePosition(100f, 100f);
child.anchorTo(parent, HudAnchor.BottomRight);
history.poll(false);
float x = child.getX();
float y = child.getY();

child.setAbsolutePosition(x + 50f, y + 30f);
settle();
child.setAbsolutePosition(x + 90f, y + 70f);
settle();

assertNotNull(history.undo());
assertEquals(x + 50f, child.getX(), 0.01f);
assertEquals(y + 30f, child.getY(), 0.01f);
assertNotNull(history.undo());
assertEquals(x, child.getX(), 0.01f);
assertEquals(y, child.getY(), 0.01f);
}

@Test
void movingAnchorTargetIsOneStep() {
Hud parent = place();
Hud child = place();
child.setAbsolutePosition(100f, 100f);
child.anchorTo(parent, HudAnchor.BottomRight);
history.poll(false);

parent.setAbsolutePosition(60f, 60f);
settle();
parent.setAbsolutePosition(120f, 90f);
settle();

assertNotNull(history.undo());
assertEquals(60f, parent.getX(), 0.01f);
assertNotNull(history.undo());
assertEquals(10f, parent.getX(), 0.01f);
}

@Test
void gestureStartedBeforeTheLastSettledIsItsOwnStep() {
Hud hud = place();
history.poll(false);

hud.setAbsolutePosition(100f, 60f);
history.poll(true);
history.poll(false);
hud.setAbsolutePosition(200f, 120f);
history.poll(true);
history.poll(true);
settle();

assertNotNull(history.undo());
assertEquals(100f, hud.getX(), 0.01f);
assertNotNull(history.undo());
assertEquals(10f, hud.getX(), 0.01f);
}

private void settle() {
history.poll(false);
history.poll(false);
Expand All @@ -192,6 +310,9 @@ private Hud place() {
}

static class TestHud extends TextHud {
@Color(title = "Accent")
public PolyColor accent = new PolyColor(0xFF2B4BFF);

TestHud() {
super("test-history", "Test History HUD", Hud.Category.getINFO(), "", "");
}
Expand Down
Loading