Make wrist fallback optional and scroll Settings with laser

This commit is contained in:
baketnk committed 2026-09-26 23:17:13 -04:00
1 parent 0f36a45358
commit 8f4c1877f7
15 files changed
+218 -30

No files matched your search

+16
View File
@@ -46,6 +46,7 @@ int main(int argc, char** argv) {
assert(!load_config(path).auto_insert);
assert(!load_config(path).close_mic_when_idle);
assert(!load_config(path).lock_layout);
assert(load_config(path).wrist_world_fallback);
assert(!load_config(path).clock_24h);
assert(load_config(path).date_format == DateFormat::MonthDayYear);
assert(load_config(path).wrist.width == .30f);
@@ -63,6 +64,7 @@ int main(int argc, char** argv) {
assert(!example.auto_insert);
assert(!example.close_mic_when_idle);
assert(!example.lock_layout);
assert(example.wrist_world_fallback);
assert(!example.clock_24h && example.date_format == DateFormat::MonthDayYear);
assert(example.wrist.y == .18f && example.wrist.z == .089f);
assert(example.gradient.enabled && example.gradient.period_seconds == 30.f && example.gradient.strength == .12f);
@@ -190,6 +192,20 @@ int main(int argc, char** argv) {
assert(save_lock_layout(path, true));
assert(get(path) == R"({"font":"escaped \u0061","theme":{"ink":"#123ABC"},"auto_insert":true,"lock_layout":true})");
assert(load_config(path).lock_layout && load_config(path).auto_insert);
put(path, R"({"wrist_world_fallback":false})");
assert(!load_config(path).wrist_world_fallback);
assert(save_wrist_world_fallback(path, true));
assert(load_config(path).wrist_world_fallback);
assert(save_wrist_world_fallback(path, false));
assert(!load_config(path).wrist_world_fallback);
put(path, R"({"wrist_world_fallback":true})");
assert(load_config(path).wrist_world_fallback);
for (const auto* invalid : {R"({"wrist_world_fallback":0})", R"({"wrist_world_fallback":"false"})",
R"({"wrist_world_fallback":null})"}) {
put(path, invalid); fails([&] { load_config(path); });
assert(!save_wrist_world_fallback(path, false));
assert(get(path) == invalid);
}
put(path, R"({"input_priority":"normal"})");
assert(!load_config(path).experimental_input_priority);
for (const auto* invalid : {R"({"input_priority":true})", R"({"input_priority":16777216})",
+9
View File
@@ -58,6 +58,15 @@ int main() {
}
CHECK(!parse_mount("World")); CHECK(!parse_mount("head\n")); CHECK(!parse_mount("left_wrist"));
CHECK(!parse_mount("")); CHECK(!parse_mount("world extra"));
for (auto wrist : {Mount::LeftWrist, Mount::RightWrist}) {
CHECK(effective_mount(wrist, true, true) == wrist);
CHECK(effective_mount(wrist, true, false) == wrist);
CHECK(effective_mount(wrist, false, true) == Mount::World);
CHECK(!effective_mount(wrist, false, false));
CHECK(effective_mount(wrist, true, false) == wrist); // restored tracking
}
CHECK(effective_mount(Mount::World, false, false) == Mount::World);
CHECK(effective_mount(Mount::Head, false, false) == Mount::Head);
CHECK(near(mount_width(Mount::World), .85f)); CHECK(near(mount_width(Mount::Head), .85f));
CHECK(near(mount_width(Mount::LeftWrist), .30f)); CHECK(near(mount_width(Mount::RightWrist), .30f));
+26 -1
View File
@@ -14,7 +14,7 @@ SurfaceEvent click(PanelSurface& surface, float x, float y, unsigned cursor = 0)
}
void no_action(const SurfaceEvent& event) {
assert(!event.action && !event.mount && !event.lasers_anytime && !event.advanced_debug && !event.auto_insert &&
!event.close_mic_when_idle && !event.lock_layout && !event.clock_24h && !event.date_format && !event.recenter && !event.open_bindings && !event.model_action);
!event.close_mic_when_idle && !event.lock_layout && !event.wrist_world_fallback && !event.clock_24h && !event.date_format && !event.recenter && !event.open_bindings && !event.model_action);
}
void snapshot(PanelSurface& surface, const std::string& path) {
std::ofstream out(path, std::ios::binary);
@@ -327,6 +327,30 @@ int main(int argc, char** argv) {
surface.set_close_mic_when_idle(true); assert(surface.render(p));
assert(click(surface, 200, 532).close_mic_when_idle == false);
surface.set_close_mic_when_idle(false); assert(surface.render(p));
// The Settings viewport scrolls continuously under the laser; it never pages.
assert(!surface.scroll_settings(290, 160, -1.f)); // tab/header, not settings content
assert(!surface.scroll_settings(200, 400, std::numeric_limits<float>::quiet_NaN()));
surface.pointer_down(0, 200, 532); // pending mic press must not activate after movement
assert(surface.scroll_settings(200, 400, -.5f));
no_action(surface.pointer_up(0, 200, 532));
assert(surface.render(p));
assert(!surface.scroll_settings(200, 610, -1.f)); // fixed footer
assert(surface.scroll_settings(200, 400, -2.f));
assert(surface.render(p));
assert(!surface.scroll_settings(200, 400, -1.f)); // bottom clamp
no_action(click(surface, 180, 231)); // scrolled settings cannot be clicked above the viewport
auto fallback = click(surface, 200, 520);
assert(fallback.wrist_world_fallback == false && !fallback.action);
assert(!surface.render(p));
surface.set_wrist_world_fallback(false); assert(surface.render(p));
fallback = click(surface, 200, 520);
assert(fallback.wrist_world_fallback == true);
surface.set_wrist_world_fallback(true); assert(surface.render(p));
assert(click(surface, 280, 610).action == UiAction::Cancel); // footer stays fixed
assert(surface.scroll_settings(200, 400, 4.f));
assert(surface.render(p));
assert(!surface.scroll_settings(200, 400, 1.f)); // top clamp
assert(click(surface, 180, 260).mount == Mount::World);
assert(click(surface, 280, 610).action == UiAction::Cancel);
surface.set_placement_note("Wrist not tracked - using world space until it returns.");
assert(surface.render(p)); assert(!surface.render(p));
@@ -344,6 +368,7 @@ int main(int argc, char** argv) {
no_action(click(surface, 200, 492)); // clock/date controls only exist on Settings
no_action(click(surface, 680, 492));
no_action(click(surface, 200, 532)); // mic preference only exists on Settings
assert(!surface.scroll_settings(200, 400, -1.f)); // Review has no Settings scroll
// Bindings opens SteamVR directly, from either tab, without replacing review.
surface.pointer_down(1, 480, 610);
+10
View File
@@ -89,6 +89,7 @@ class InstallTests(unittest.TestCase):
self.assertEqual(json.loads(config.read_text())["gradient"],
{"enabled": True, "period_seconds": 30, "strength": 0.12})
self.assertIs(json.loads(config.read_text())["lock_layout"], False)
self.assertIs(json.loads(config.read_text())["wrist_world_fallback"], True)
self.assertEqual(list(config.parent.glob("config.json.backup-*")), [])
self.assertIn("PYTHONDONTWRITEBYTECODE=1", launcher.read_text())
self.assertTrue(os.access(root / "versions/0.1.202609241530/runtime/bin/helper", os.X_OK))
@@ -186,6 +187,7 @@ class InstallTests(unittest.TestCase):
fixed["close_mic_when_idle"] = True
fixed["backend"] = "custom_v2-1"
fixed["lock_layout"] = True
fixed["wrist_world_fallback"] = False
fixed["clock_24h"] = True
fixed["date_format"] = "iso"
fixed["buttons"]["enter"] = "" # intentional disabling survives upgrades
@@ -200,6 +202,7 @@ class InstallTests(unittest.TestCase):
self.assertIs(json.loads(config.read_text())["close_mic_when_idle"], True)
self.assertEqual(json.loads(config.read_text())["backend"], "custom_v2-1")
self.assertIs(json.loads(config.read_text())["lock_layout"], True)
self.assertIs(json.loads(config.read_text())["wrist_world_fallback"], False)
self.assertIs(json.loads(config.read_text())["clock_24h"], True)
self.assertEqual(json.loads(config.read_text())["date_format"], "iso")
self.assertEqual(len(list(config.parent.glob("config.json.backup-*"))), 1)
@@ -221,6 +224,7 @@ class InstallTests(unittest.TestCase):
self.assertIs(fixed["advanced_debug"], False)
self.assertIs(fixed["auto_insert"], False)
self.assertIs(fixed["lock_layout"], False)
self.assertIs(fixed["wrist_world_fallback"], True)
self.assertEqual(fixed["wrist"]["x"], 0.04)
self.assertEqual(fixed["wrist"]["y"], 0.18)
self.assertEqual(fixed["wrist"]["width"], 0.30)
@@ -350,6 +354,12 @@ class InstallTests(unittest.TestCase):
self.assertEqual(fixed["font"], "/custom/font.ttf")
self.assertIn(original, [p.read_bytes() for p in config.parent.glob("config.json.backup-*")])
def test_wrist_world_fallback_normalization(self):
self.assertIs(installer.normalized_config({})["wrist_world_fallback"], True)
self.assertIs(installer.normalized_config({"wrist_world_fallback": False})["wrist_world_fallback"], False)
for invalid in ("false", 0, None, [], {}):
self.assertIs(installer.normalized_config({"wrist_world_fallback": invalid})["wrist_world_fallback"], True)
def test_new_config_fields_repair_only_invalid_values(self):
archive, digest = self.package("0.1.202609241530")
config = self.home / ".config/frameyap/config.json"