adding tokens

This commit is contained in:
Eric 2024-07-21 19:21:24 -07:00
parent cf5fe0fcd0
commit 9acbc2c94f
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
61 changed files with 1473 additions and 821 deletions

220
.efrocachemap generated
View File

@ -1282,10 +1282,10 @@
"build/assets/ba_data/textures/buttonPunch.ktx": "9de2058679dd2e55a9c180fafa810559", "build/assets/ba_data/textures/buttonPunch.ktx": "9de2058679dd2e55a9c180fafa810559",
"build/assets/ba_data/textures/buttonPunch.pvr": "7b7ced951fcee4e572988f232d729ad6", "build/assets/ba_data/textures/buttonPunch.pvr": "7b7ced951fcee4e572988f232d729ad6",
"build/assets/ba_data/textures/buttonPunch_preview.png": "e78d05abb78fef5fe18119c4f3fd1dbe", "build/assets/ba_data/textures/buttonPunch_preview.png": "e78d05abb78fef5fe18119c4f3fd1dbe",
"build/assets/ba_data/textures/buttonSquare.dds": "2475ef6978210f993d7c61fc08a15c18", "build/assets/ba_data/textures/buttonSquare.dds": "8b4091b6fc25d964059673062cc9faa6",
"build/assets/ba_data/textures/buttonSquare.ktx": "15794e5bb14980f49ee17a3bcfe7e9b3", "build/assets/ba_data/textures/buttonSquare.ktx": "21fe9898e50c6d44151e059e0a4b94ea",
"build/assets/ba_data/textures/buttonSquare.pvr": "ef1c0a321975aa3a7cad6e4ec29c1196", "build/assets/ba_data/textures/buttonSquare.pvr": "c454666fa706aed388e4ce366248b34d",
"build/assets/ba_data/textures/buttonSquare_preview.png": "d903b64a5f41bce2a64be077bcdfd4ba", "build/assets/ba_data/textures/buttonSquare_preview.png": "e5dc8e92b0306b218be7b6c927aa4e06",
"build/assets/ba_data/textures/chTitleChar1.dds": "d8c615a51d900da15b8aba5ce35296bf", "build/assets/ba_data/textures/chTitleChar1.dds": "d8c615a51d900da15b8aba5ce35296bf",
"build/assets/ba_data/textures/chTitleChar1.ktx": "44384ea28c9fe01440deb1fc80c7224a", "build/assets/ba_data/textures/chTitleChar1.ktx": "44384ea28c9fe01440deb1fc80c7224a",
"build/assets/ba_data/textures/chTitleChar1.pvr": "304d7551786a2eca008bd2057996d05e", "build/assets/ba_data/textures/chTitleChar1.pvr": "304d7551786a2eca008bd2057996d05e",
@ -1517,10 +1517,10 @@
"build/assets/ba_data/textures/fontExtras.dds": "d2d20fdde7c6114925ba626ade35151f", "build/assets/ba_data/textures/fontExtras.dds": "d2d20fdde7c6114925ba626ade35151f",
"build/assets/ba_data/textures/fontExtras.ktx": "2dde1a343493a9329792d7042116d301", "build/assets/ba_data/textures/fontExtras.ktx": "2dde1a343493a9329792d7042116d301",
"build/assets/ba_data/textures/fontExtras.pvr": "80ab1f61fafba22ce0259177944beabf", "build/assets/ba_data/textures/fontExtras.pvr": "80ab1f61fafba22ce0259177944beabf",
"build/assets/ba_data/textures/fontExtras2.dds": "18063a12912dadc9528afd90d1cf2369", "build/assets/ba_data/textures/fontExtras2.dds": "56e365473f2babd5404a4ea63924c4ca",
"build/assets/ba_data/textures/fontExtras2.ktx": "36da7f6cfbfb8d32fb14371de0a8f660", "build/assets/ba_data/textures/fontExtras2.ktx": "1de6da8081788bea25cfb0e8df0d16ca",
"build/assets/ba_data/textures/fontExtras2.pvr": "7a4e8e64ac05313b1782fb5b958150d0", "build/assets/ba_data/textures/fontExtras2.pvr": "eb327acb727a626c5f46e2e281a0b789",
"build/assets/ba_data/textures/fontExtras2_preview.png": "f5bc05c8c34cb3ff7284e9edab7b8bcd", "build/assets/ba_data/textures/fontExtras2_preview.png": "aa3ca66b5368da12eb70c21d73dc74b8",
"build/assets/ba_data/textures/fontExtras3.dds": "4fce73998583207fd7692a816e3d86b0", "build/assets/ba_data/textures/fontExtras3.dds": "4fce73998583207fd7692a816e3d86b0",
"build/assets/ba_data/textures/fontExtras3.ktx": "13d61e663088bc97c1fa906a46ed5955", "build/assets/ba_data/textures/fontExtras3.ktx": "13d61e663088bc97c1fa906a46ed5955",
"build/assets/ba_data/textures/fontExtras3.pvr": "56bac0d5f8672f36e1408a1e57cdd3d3", "build/assets/ba_data/textures/fontExtras3.pvr": "56bac0d5f8672f36e1408a1e57cdd3d3",
@ -1626,6 +1626,10 @@
"build/assets/ba_data/textures/glow.ktx": "fb924f8b6e5dc268f469fb5791cac131", "build/assets/ba_data/textures/glow.ktx": "fb924f8b6e5dc268f469fb5791cac131",
"build/assets/ba_data/textures/glow.pvr": "7ef85fd8127acfb9dafdc75cdcb61a72", "build/assets/ba_data/textures/glow.pvr": "7ef85fd8127acfb9dafdc75cdcb61a72",
"build/assets/ba_data/textures/glow_preview.png": "6f32a4134d5e2ee70d7f804c8fdae3b0", "build/assets/ba_data/textures/glow_preview.png": "6f32a4134d5e2ee70d7f804c8fdae3b0",
"build/assets/ba_data/textures/goldPass.dds": "e39d1181ace28b2f3aa2f68b29cff597",
"build/assets/ba_data/textures/goldPass.ktx": "cb0ff1dc86be777325ba0d1b54d1f3ac",
"build/assets/ba_data/textures/goldPass.pvr": "d5e9e6dd71d571fad5898af6626cf343",
"build/assets/ba_data/textures/goldPass_preview.png": "81614ca81d55029721b7ceb3e21831ab",
"build/assets/ba_data/textures/googlePlayAchievementsIcon.dds": "f43d34da87ae80344c5f47e39dc53455", "build/assets/ba_data/textures/googlePlayAchievementsIcon.dds": "f43d34da87ae80344c5f47e39dc53455",
"build/assets/ba_data/textures/googlePlayAchievementsIcon.ktx": "fb15945ffc91b8b513c41d122c371925", "build/assets/ba_data/textures/googlePlayAchievementsIcon.ktx": "fb15945ffc91b8b513c41d122c371925",
"build/assets/ba_data/textures/googlePlayAchievementsIcon.pvr": "662e91f51ed4f6dac5695c099a0cecbc", "build/assets/ba_data/textures/googlePlayAchievementsIcon.pvr": "662e91f51ed4f6dac5695c099a0cecbc",
@ -2410,6 +2414,22 @@
"build/assets/ba_data/textures/tnt.ktx": "b65ce2a5713a00e37c50aa213b299d8e", "build/assets/ba_data/textures/tnt.ktx": "b65ce2a5713a00e37c50aa213b299d8e",
"build/assets/ba_data/textures/tnt.pvr": "9688020124094656a932ef5d446e575a", "build/assets/ba_data/textures/tnt.pvr": "9688020124094656a932ef5d446e575a",
"build/assets/ba_data/textures/tnt_preview.png": "37f907b09acbdc72d62e48311e28f439", "build/assets/ba_data/textures/tnt_preview.png": "37f907b09acbdc72d62e48311e28f439",
"build/assets/ba_data/textures/tokens1.dds": "ac5dfc96143d1e8896b83ad5c2b5b707",
"build/assets/ba_data/textures/tokens1.ktx": "7eb2e53c71462087fbca6b1e4f428981",
"build/assets/ba_data/textures/tokens1.pvr": "2c5475ffef4c43ca3136056c0dabbb10",
"build/assets/ba_data/textures/tokens1_preview.png": "de5142cc0be755f2c61cbcd49c6b6c5b",
"build/assets/ba_data/textures/tokens2.dds": "5ecb1fa56e9f0cb2665cac9ed195541e",
"build/assets/ba_data/textures/tokens2.ktx": "6bbda19cf5c1ea432a5b58f7d5d04013",
"build/assets/ba_data/textures/tokens2.pvr": "011a96e01e39e84857e47a3da4a9ea62",
"build/assets/ba_data/textures/tokens2_preview.png": "213649d48adeded11c0125f209f43b3e",
"build/assets/ba_data/textures/tokens3.dds": "659d16c00046721e65040fd858b0036c",
"build/assets/ba_data/textures/tokens3.ktx": "8edee22873a572abcbb57d4a2c804164",
"build/assets/ba_data/textures/tokens3.pvr": "bf0463650ce22789b7196117c5a1114a",
"build/assets/ba_data/textures/tokens3_preview.png": "9d3e38908db44848152ce85cdaf63656",
"build/assets/ba_data/textures/tokens4.dds": "5b3c5f56f86e10b1e99b53b50d0d04f7",
"build/assets/ba_data/textures/tokens4.ktx": "f5288b1e86b2c5f8bdf7c185fda49dfd",
"build/assets/ba_data/textures/tokens4.pvr": "b2fc14fb36b6c67d64134a1d41104037",
"build/assets/ba_data/textures/tokens4_preview.png": "75dc96161bf8182f42124af24f418cf5",
"build/assets/ba_data/textures/touchArrows.dds": "08e1b07035b236cd9c94a33396624030", "build/assets/ba_data/textures/touchArrows.dds": "08e1b07035b236cd9c94a33396624030",
"build/assets/ba_data/textures/touchArrows.ktx": "404024fec655041449ed6dca9ba8a35b", "build/assets/ba_data/textures/touchArrows.ktx": "404024fec655041449ed6dca9ba8a35b",
"build/assets/ba_data/textures/touchArrows.pvr": "bce89715f94e530bc243a32e112f3391", "build/assets/ba_data/textures/touchArrows.pvr": "bce89715f94e530bc243a32e112f3391",
@ -2478,14 +2498,18 @@
"build/assets/ba_data/textures/white.ktx": "a948a0fcadd2c7ff5ade346dc0856b92", "build/assets/ba_data/textures/white.ktx": "a948a0fcadd2c7ff5ade346dc0856b92",
"build/assets/ba_data/textures/white.pvr": "4281d8f181ccb325d77fabb5c8c87816", "build/assets/ba_data/textures/white.pvr": "4281d8f181ccb325d77fabb5c8c87816",
"build/assets/ba_data/textures/white_preview.png": "39e65093bf4792cb696927a08909bb2c", "build/assets/ba_data/textures/white_preview.png": "39e65093bf4792cb696927a08909bb2c",
"build/assets/ba_data/textures/windowHSmallVMed.dds": "ea2f027e5b201e53b30000dce19985d9", "build/assets/ba_data/textures/windowBottomCap.dds": "d0daf8ff9d6b6d99ef24518e43cd4b0a",
"build/assets/ba_data/textures/windowHSmallVMed.ktx": "e0d2b9519570fd4bfc6792bebac4253c", "build/assets/ba_data/textures/windowBottomCap.ktx": "a948f0953ccdf9a483d25c359a432804",
"build/assets/ba_data/textures/windowHSmallVMed.pvr": "352934e17d70bd1d2376c02f9d97b923", "build/assets/ba_data/textures/windowBottomCap.pvr": "54a390728cca11339445de329b4f4bf5",
"build/assets/ba_data/textures/windowHSmallVMed_preview.png": "8f10851830b7e640d07bb26cac2e564b", "build/assets/ba_data/textures/windowBottomCap_preview.png": "fdeb2221b9cfc1bc92805826033b6cee",
"build/assets/ba_data/textures/windowHSmallVSmall.dds": "1fdab760550124d83455a3b32a488abe", "build/assets/ba_data/textures/windowHSmallVMed.dds": "c5720b09bef0d51afb4cb294ba516651",
"build/assets/ba_data/textures/windowHSmallVSmall.ktx": "c064b5bfab58e3526114c712d522b4e2", "build/assets/ba_data/textures/windowHSmallVMed.ktx": "1d0977585e0ee8c9064a415b35c4b5e9",
"build/assets/ba_data/textures/windowHSmallVSmall.pvr": "b9eee1de93b19477409cdc04e91d76da", "build/assets/ba_data/textures/windowHSmallVMed.pvr": "e6844e1d4d0b4161e1f4acb020cf4321",
"build/assets/ba_data/textures/windowHSmallVSmall_preview.png": "0a61e76caa8bc93e9deaa3369b712a18", "build/assets/ba_data/textures/windowHSmallVMed_preview.png": "b9d66c1f932c2091b097e4007d1db82a",
"build/assets/ba_data/textures/windowHSmallVSmall.dds": "1d3c13f37c4b6f39a45adf20ebec4514",
"build/assets/ba_data/textures/windowHSmallVSmall.ktx": "120593e4ecb90079353d02cc8eecd41f",
"build/assets/ba_data/textures/windowHSmallVSmall.pvr": "d40c4924e93017e384e32bbe610fb4fc",
"build/assets/ba_data/textures/windowHSmallVSmall_preview.png": "b3db4b605a7937736a4f2e9c233a03fb",
"build/assets/ba_data/textures/wings.dds": "ed0aee36ae96b2c5c3e9ee03ef23c998", "build/assets/ba_data/textures/wings.dds": "ed0aee36ae96b2c5c3e9ee03ef23c998",
"build/assets/ba_data/textures/wings.ktx": "127b7381b882448b80428c96c7483dbb", "build/assets/ba_data/textures/wings.ktx": "127b7381b882448b80428c96c7483dbb",
"build/assets/ba_data/textures/wings.pvr": "3e07ec0f582d897c2b21395ae6c5aa3f", "build/assets/ba_data/textures/wings.pvr": "3e07ec0f582d897c2b21395ae6c5aa3f",
@ -2572,13 +2596,13 @@
"build/assets/pylib-android/_osx_support.py": "c0871f8d2b36955d67f0446bb9fa3827", "build/assets/pylib-android/_osx_support.py": "c0871f8d2b36955d67f0446bb9fa3827",
"build/assets/pylib-android/_py_abc.py": "180d5cf138b011bd6a280c2f433bed47", "build/assets/pylib-android/_py_abc.py": "180d5cf138b011bd6a280c2f433bed47",
"build/assets/pylib-android/_pydatetime.py": "af813d3d6cb8ccf3e90b76b91f5fa7f2", "build/assets/pylib-android/_pydatetime.py": "af813d3d6cb8ccf3e90b76b91f5fa7f2",
"build/assets/pylib-android/_pydecimal.py": "4572eb8c67bcfbbcda7ed47055c6e6b5", "build/assets/pylib-android/_pydecimal.py": "e58d83934dbffe094d2976a9e1132190",
"build/assets/pylib-android/_pyio.py": "618a6fa97c93e8c63c8e5cf3c283d5fe", "build/assets/pylib-android/_pyio.py": "618a6fa97c93e8c63c8e5cf3c283d5fe",
"build/assets/pylib-android/_pylong.py": "e057ab3c9eea264704dff2af204884f8", "build/assets/pylib-android/_pylong.py": "0ae926c324155186a115b54eb88d0526",
"build/assets/pylib-android/_sitebuiltins.py": "8b5e3f6e73917962fa014ad2c4a55e61", "build/assets/pylib-android/_sitebuiltins.py": "8b5e3f6e73917962fa014ad2c4a55e61",
"build/assets/pylib-android/_strptime.py": "8c65b46a50f13ca2389b19b32b7f2996", "build/assets/pylib-android/_strptime.py": "8c65b46a50f13ca2389b19b32b7f2996",
"build/assets/pylib-android/_sysconfigdata__linux_.py": "e112c946a141bcf18e0656a14a52ca71", "build/assets/pylib-android/_sysconfigdata__linux_.py": "6c03d32177713f6abdf9e692aaa53ed6",
"build/assets/pylib-android/_sysconfigdata_d_linux_.py": "063295bed5c2c6f30d323abb2cf82fd9", "build/assets/pylib-android/_sysconfigdata_d_linux_.py": "c37336d1880d691f3108e1cedf5e7355",
"build/assets/pylib-android/_threading_local.py": "4a9688e3987d7d692db46feb9214945e", "build/assets/pylib-android/_threading_local.py": "4a9688e3987d7d692db46feb9214945e",
"build/assets/pylib-android/_weakrefset.py": "e4fa8532ace46dfbc35149c41ea497f7", "build/assets/pylib-android/_weakrefset.py": "e4fa8532ace46dfbc35149c41ea497f7",
"build/assets/pylib-android/abc.py": "a0daa1ed187eee8690c1e8438b97da90", "build/assets/pylib-android/abc.py": "a0daa1ed187eee8690c1e8438b97da90",
@ -2587,7 +2611,7 @@
"build/assets/pylib-android/argparse.py": "479fa19b01a256e9162cfed4b3866708", "build/assets/pylib-android/argparse.py": "479fa19b01a256e9162cfed4b3866708",
"build/assets/pylib-android/ast.py": "e59b9f09a7d1f8979b94f900a72f74c6", "build/assets/pylib-android/ast.py": "e59b9f09a7d1f8979b94f900a72f74c6",
"build/assets/pylib-android/asyncio/__init__.py": "4a732a7b4c77634cab1909d8be43cb4c", "build/assets/pylib-android/asyncio/__init__.py": "4a732a7b4c77634cab1909d8be43cb4c",
"build/assets/pylib-android/asyncio/__main__.py": "8e391b47f448ad922dc2614dbd93011e", "build/assets/pylib-android/asyncio/__main__.py": "18122a137578030c2a458f7af9d93edd",
"build/assets/pylib-android/asyncio/base_events.py": "071d87caa982ce12333bf31b691dd0b0", "build/assets/pylib-android/asyncio/base_events.py": "071d87caa982ce12333bf31b691dd0b0",
"build/assets/pylib-android/asyncio/base_futures.py": "5b4cefd0a571e9e8d913a052f2e0b15e", "build/assets/pylib-android/asyncio/base_futures.py": "5b4cefd0a571e9e8d913a052f2e0b15e",
"build/assets/pylib-android/asyncio/base_subprocess.py": "8a805e04a2911b2d8e297f0029def4b1", "build/assets/pylib-android/asyncio/base_subprocess.py": "8a805e04a2911b2d8e297f0029def4b1",
@ -2601,7 +2625,7 @@
"build/assets/pylib-android/asyncio/locks.py": "1630852effc563876002c88455a2f7f4", "build/assets/pylib-android/asyncio/locks.py": "1630852effc563876002c88455a2f7f4",
"build/assets/pylib-android/asyncio/log.py": "1e101049c5cd7ad159b63ef97fe8fb0b", "build/assets/pylib-android/asyncio/log.py": "1e101049c5cd7ad159b63ef97fe8fb0b",
"build/assets/pylib-android/asyncio/mixins.py": "9be94c61811a65522320e29e3dec16b0", "build/assets/pylib-android/asyncio/mixins.py": "9be94c61811a65522320e29e3dec16b0",
"build/assets/pylib-android/asyncio/proactor_events.py": "ff72970f982322a1141d710557847d4d", "build/assets/pylib-android/asyncio/proactor_events.py": "97d197fadbae03691260f805ac1049e2",
"build/assets/pylib-android/asyncio/protocols.py": "b8aa105b79d24f88c7a2c2cdbc8e7814", "build/assets/pylib-android/asyncio/protocols.py": "b8aa105b79d24f88c7a2c2cdbc8e7814",
"build/assets/pylib-android/asyncio/queues.py": "f63be54780730992e2377c51ac373126", "build/assets/pylib-android/asyncio/queues.py": "f63be54780730992e2377c51ac373126",
"build/assets/pylib-android/asyncio/runners.py": "e523f1abdd9ab2cf2bc355acea052bd8", "build/assets/pylib-android/asyncio/runners.py": "e523f1abdd9ab2cf2bc355acea052bd8",
@ -2619,8 +2643,8 @@
"build/assets/pylib-android/asyncio/unix_events.py": "e5490096c3c8ce4d2537317c608862d9", "build/assets/pylib-android/asyncio/unix_events.py": "e5490096c3c8ce4d2537317c608862d9",
"build/assets/pylib-android/asyncio/windows_events.py": "e70d58554906a59b6d823850aa6b8395", "build/assets/pylib-android/asyncio/windows_events.py": "e70d58554906a59b6d823850aa6b8395",
"build/assets/pylib-android/asyncio/windows_utils.py": "4efbef16e6692c9424804d9bdc496761", "build/assets/pylib-android/asyncio/windows_utils.py": "4efbef16e6692c9424804d9bdc496761",
"build/assets/pylib-android/base64.py": "00f5d31b06dd0f489d8b14b7bcf43db7", "build/assets/pylib-android/base64.py": "b89a7dfe64760a2402b5bf255174969c",
"build/assets/pylib-android/bdb.py": "1e26c626405bfc06b1e7231183eb928a", "build/assets/pylib-android/bdb.py": "e7f362c5918a54efc0085efc7bae3a8d",
"build/assets/pylib-android/bisect.py": "69d3166bd72a28217f1bffa40dc9c33b", "build/assets/pylib-android/bisect.py": "69d3166bd72a28217f1bffa40dc9c33b",
"build/assets/pylib-android/bz2.py": "cd6a5f2491bc52afd8fc180097371473", "build/assets/pylib-android/bz2.py": "cd6a5f2491bc52afd8fc180097371473",
"build/assets/pylib-android/cProfile.py": "bec17d6b102c0123c4b743ac685de752", "build/assets/pylib-android/cProfile.py": "bec17d6b102c0123c4b743ac685de752",
@ -2648,7 +2672,7 @@
"build/assets/pylib-android/copyreg.py": "de5fb1333a0e388e69749e78ed5e55ae", "build/assets/pylib-android/copyreg.py": "de5fb1333a0e388e69749e78ed5e55ae",
"build/assets/pylib-android/crypt.py": "e12ad225ff7be254f543a48ed68b9465", "build/assets/pylib-android/crypt.py": "e12ad225ff7be254f543a48ed68b9465",
"build/assets/pylib-android/csv.py": "a896b3c30246ae11b1633b402675b6b9", "build/assets/pylib-android/csv.py": "a896b3c30246ae11b1633b402675b6b9",
"build/assets/pylib-android/ctypes/__init__.py": "235310a16d0c17607d86807724dfc51f", "build/assets/pylib-android/ctypes/__init__.py": "53033669e712496a979fc62d320ff76b",
"build/assets/pylib-android/ctypes/_aix.py": "5fd9184c6794ee90ac1441f2c5fe7335", "build/assets/pylib-android/ctypes/_aix.py": "5fd9184c6794ee90ac1441f2c5fe7335",
"build/assets/pylib-android/ctypes/_endian.py": "75edb1136e6c70ff5797b639d74d3a76", "build/assets/pylib-android/ctypes/_endian.py": "75edb1136e6c70ff5797b639d74d3a76",
"build/assets/pylib-android/ctypes/macholib/__init__.py": "b2ee4220c9357720236e4da2f849f7da", "build/assets/pylib-android/ctypes/macholib/__init__.py": "b2ee4220c9357720236e4da2f849f7da",
@ -2662,17 +2686,17 @@
"build/assets/pylib-android/curses/has_key.py": "c74b8d6db329fbbd872b7b91bfa94624", "build/assets/pylib-android/curses/has_key.py": "c74b8d6db329fbbd872b7b91bfa94624",
"build/assets/pylib-android/curses/panel.py": "8f36fdade9588f8a4362d2cc057a6eff", "build/assets/pylib-android/curses/panel.py": "8f36fdade9588f8a4362d2cc057a6eff",
"build/assets/pylib-android/curses/textpad.py": "c53f9edcb5abba15eb755fdebef0eb19", "build/assets/pylib-android/curses/textpad.py": "c53f9edcb5abba15eb755fdebef0eb19",
"build/assets/pylib-android/dataclasses.py": "52ef11127750fed81643fe829bc4c737", "build/assets/pylib-android/dataclasses.py": "a57847f8c5f4dc7681d8a39a96bf399b",
"build/assets/pylib-android/datetime.py": "b4a98cc076882de0f4c5787b888b2eb6", "build/assets/pylib-android/datetime.py": "b4a98cc076882de0f4c5787b888b2eb6",
"build/assets/pylib-android/decimal.py": "f57d255d45b5d1d7d8e13c41a283c3e4", "build/assets/pylib-android/decimal.py": "bc71d12e6e91b709990e51c1e5c22a04",
"build/assets/pylib-android/difflib.py": "6b3c8fd541b2b8d0320727025cd25275", "build/assets/pylib-android/difflib.py": "6b3c8fd541b2b8d0320727025cd25275",
"build/assets/pylib-android/dis.py": "a13ae44a4be77ce18f74d1e56659ea03", "build/assets/pylib-android/dis.py": "a13ae44a4be77ce18f74d1e56659ea03",
"build/assets/pylib-android/doctest.py": "25cfaf6115c8d69f5e84c66ce4d51dae", "build/assets/pylib-android/doctest.py": "d5799d14d763cb9788073a7d9fcb3cac",
"build/assets/pylib-android/email/__init__.py": "4ff603eeeb4ce0302c8bd1b220fc5e13", "build/assets/pylib-android/email/__init__.py": "4ff603eeeb4ce0302c8bd1b220fc5e13",
"build/assets/pylib-android/email/_encoded_words.py": "d7b77501689dd1ce32da789b46264a6a", "build/assets/pylib-android/email/_encoded_words.py": "d7b77501689dd1ce32da789b46264a6a",
"build/assets/pylib-android/email/_header_value_parser.py": "f472e3136172faada943c60181e886eb", "build/assets/pylib-android/email/_header_value_parser.py": "ca9f4ed18429b711d22e25dc6117da3d",
"build/assets/pylib-android/email/_parseaddr.py": "a6d2999aeed17f060be8797e761a01aa", "build/assets/pylib-android/email/_parseaddr.py": "a6d2999aeed17f060be8797e761a01aa",
"build/assets/pylib-android/email/_policybase.py": "09b0c21693ac080c52c52f5daab616aa", "build/assets/pylib-android/email/_policybase.py": "b632710aafb0885a10b6ed8c7576e9cb",
"build/assets/pylib-android/email/base64mime.py": "d3e4fc07d04833487677dd2a888c3826", "build/assets/pylib-android/email/base64mime.py": "d3e4fc07d04833487677dd2a888c3826",
"build/assets/pylib-android/email/charset.py": "8bcb2315a8340755057e66398e975542", "build/assets/pylib-android/email/charset.py": "8bcb2315a8340755057e66398e975542",
"build/assets/pylib-android/email/contentmanager.py": "e88780ef1d37a11ff216060f740f2572", "build/assets/pylib-android/email/contentmanager.py": "e88780ef1d37a11ff216060f740f2572",
@ -2683,7 +2707,7 @@
"build/assets/pylib-android/email/header.py": "09923b7b77bf91bed41a71b6e3c6e4d9", "build/assets/pylib-android/email/header.py": "09923b7b77bf91bed41a71b6e3c6e4d9",
"build/assets/pylib-android/email/headerregistry.py": "dfd48f9c41454d5bc2355cb1762fb869", "build/assets/pylib-android/email/headerregistry.py": "dfd48f9c41454d5bc2355cb1762fb869",
"build/assets/pylib-android/email/iterators.py": "752ece28a18545e70fa67a7cf2fe3ef3", "build/assets/pylib-android/email/iterators.py": "752ece28a18545e70fa67a7cf2fe3ef3",
"build/assets/pylib-android/email/message.py": "d35660dfc0ff8e0ed0125f0e465771b6", "build/assets/pylib-android/email/message.py": "68764155da8820f7d4810b08b0fe7af0",
"build/assets/pylib-android/email/mime/__init__.py": "340c83beff7dcff8f5c7b87cd43cedaf", "build/assets/pylib-android/email/mime/__init__.py": "340c83beff7dcff8f5c7b87cd43cedaf",
"build/assets/pylib-android/email/mime/application.py": "4ce678512f30cac9fd95993186d2eef7", "build/assets/pylib-android/email/mime/application.py": "4ce678512f30cac9fd95993186d2eef7",
"build/assets/pylib-android/email/mime/audio.py": "4073e45bda9524e3cbe29374951fdc16", "build/assets/pylib-android/email/mime/audio.py": "4073e45bda9524e3cbe29374951fdc16",
@ -2694,7 +2718,7 @@
"build/assets/pylib-android/email/mime/nonmultipart.py": "a96d8d31156781a7511cec04e46a95f6", "build/assets/pylib-android/email/mime/nonmultipart.py": "a96d8d31156781a7511cec04e46a95f6",
"build/assets/pylib-android/email/mime/text.py": "634e0b909f94788cf97fc8a0b914d12c", "build/assets/pylib-android/email/mime/text.py": "634e0b909f94788cf97fc8a0b914d12c",
"build/assets/pylib-android/email/parser.py": "d78f74ba45a3618608cdc9ece2aa411a", "build/assets/pylib-android/email/parser.py": "d78f74ba45a3618608cdc9ece2aa411a",
"build/assets/pylib-android/email/policy.py": "34946fe746ce5d39634d54f18716c8f3", "build/assets/pylib-android/email/policy.py": "96ff779e28c2fa000dd84acced05650c",
"build/assets/pylib-android/email/quoprimime.py": "c5e54c3f0e70d55145517382d4455765", "build/assets/pylib-android/email/quoprimime.py": "c5e54c3f0e70d55145517382d4455765",
"build/assets/pylib-android/email/utils.py": "05398f8a134b22fac6ab3a1d4b84fcb1", "build/assets/pylib-android/email/utils.py": "05398f8a134b22fac6ab3a1d4b84fcb1",
"build/assets/pylib-android/encodings/__init__.py": "6a342ed3b218da63b7f7937beafcc20c", "build/assets/pylib-android/encodings/__init__.py": "6a342ed3b218da63b7f7937beafcc20c",
@ -2825,7 +2849,7 @@
"build/assets/pylib-android/fnmatch.py": "a1bc67633695d4defd4c0886428c5363", "build/assets/pylib-android/fnmatch.py": "a1bc67633695d4defd4c0886428c5363",
"build/assets/pylib-android/fractions.py": "02689771b334f161f5bd4b052aaa3e33", "build/assets/pylib-android/fractions.py": "02689771b334f161f5bd4b052aaa3e33",
"build/assets/pylib-android/ftplib.py": "84c6c5d111b887aa3986a02d164e4cdd", "build/assets/pylib-android/ftplib.py": "84c6c5d111b887aa3986a02d164e4cdd",
"build/assets/pylib-android/functools.py": "a60c3a01c02be0f20a3e91de2b9e188f", "build/assets/pylib-android/functools.py": "25c531165121ae37d026331f7364f379",
"build/assets/pylib-android/genericpath.py": "700f98a87ac51709fc817a23e48b52f3", "build/assets/pylib-android/genericpath.py": "700f98a87ac51709fc817a23e48b52f3",
"build/assets/pylib-android/getopt.py": "2c02d59b410128b2ebff26e3030568a6", "build/assets/pylib-android/getopt.py": "2c02d59b410128b2ebff26e3030568a6",
"build/assets/pylib-android/getpass.py": "c19e383e949c147a30ecc554b8598e91", "build/assets/pylib-android/getpass.py": "c19e383e949c147a30ecc554b8598e91",
@ -2869,9 +2893,9 @@
"build/assets/pylib-android/importlib/resources/simple.py": "16b97df12d2762e00bbb7ee6189750a2", "build/assets/pylib-android/importlib/resources/simple.py": "16b97df12d2762e00bbb7ee6189750a2",
"build/assets/pylib-android/importlib/simple.py": "f34f28cd359ecaf9fd6cf18a4dca2c33", "build/assets/pylib-android/importlib/simple.py": "f34f28cd359ecaf9fd6cf18a4dca2c33",
"build/assets/pylib-android/importlib/util.py": "8b414591277a59478a9acab9ef9c56e2", "build/assets/pylib-android/importlib/util.py": "8b414591277a59478a9acab9ef9c56e2",
"build/assets/pylib-android/inspect.py": "1f9f508f836c6f55658d722e9383ada9", "build/assets/pylib-android/inspect.py": "c3ef07d9f7ac90aad135a198faf2029b",
"build/assets/pylib-android/io.py": "4f501a9e8a4ff4c2ca8152b8f5634cb9", "build/assets/pylib-android/io.py": "4f501a9e8a4ff4c2ca8152b8f5634cb9",
"build/assets/pylib-android/ipaddress.py": "f8aab9a66231645bc9bd92301f1c6ac0", "build/assets/pylib-android/ipaddress.py": "523062c7debdcd4c554b3e7c7b3714b1",
"build/assets/pylib-android/json/__init__.py": "e8b000d2bf8c53b55a72bc3053800596", "build/assets/pylib-android/json/__init__.py": "e8b000d2bf8c53b55a72bc3053800596",
"build/assets/pylib-android/json/decoder.py": "82a8faab8ae9599b2b5f58322b8055ee", "build/assets/pylib-android/json/decoder.py": "82a8faab8ae9599b2b5f58322b8055ee",
"build/assets/pylib-android/json/encoder.py": "c85377b74511c6f463715e02118ca566", "build/assets/pylib-android/json/encoder.py": "c85377b74511c6f463715e02118ca566",
@ -2879,9 +2903,9 @@
"build/assets/pylib-android/json/tool.py": "a83b2c5dafa3adfd772a058cddbc0afc", "build/assets/pylib-android/json/tool.py": "a83b2c5dafa3adfd772a058cddbc0afc",
"build/assets/pylib-android/keyword.py": "4132d92e0b8a50d8f7119ed5fabf1674", "build/assets/pylib-android/keyword.py": "4132d92e0b8a50d8f7119ed5fabf1674",
"build/assets/pylib-android/linecache.py": "965977aa395c6db802aa732f30660097", "build/assets/pylib-android/linecache.py": "965977aa395c6db802aa732f30660097",
"build/assets/pylib-android/locale.py": "7c90ef0f6f75d9a5ee9a709ca266a2e2", "build/assets/pylib-android/locale.py": "b337ca2d5d86061fd6e0592125a87f50",
"build/assets/pylib-android/logging/__init__.py": "88f53a4eec4313c2e2ab9130250d3ab5", "build/assets/pylib-android/logging/__init__.py": "88f53a4eec4313c2e2ab9130250d3ab5",
"build/assets/pylib-android/logging/config.py": "cc6d515fd6b7b16d8ab66fe934249f9b", "build/assets/pylib-android/logging/config.py": "63b9328b44c899e6858af68fc65b7646",
"build/assets/pylib-android/logging/handlers.py": "0d0ca5db1f8e3588301a4cfbc4d6d365", "build/assets/pylib-android/logging/handlers.py": "0d0ca5db1f8e3588301a4cfbc4d6d365",
"build/assets/pylib-android/lzma.py": "d7388640aa6af4c64fdc821471930f57", "build/assets/pylib-android/lzma.py": "d7388640aa6af4c64fdc821471930f57",
"build/assets/pylib-android/mailbox.py": "7de36be1f3365d2202d1e6548c538360", "build/assets/pylib-android/mailbox.py": "7de36be1f3365d2202d1e6548c538360",
@ -2890,23 +2914,23 @@
"build/assets/pylib-android/modulefinder.py": "3fc074c018de1dd15ab2e309be199dea", "build/assets/pylib-android/modulefinder.py": "3fc074c018de1dd15ab2e309be199dea",
"build/assets/pylib-android/netrc.py": "9dd6b80891863e23b5e0a57bb80c7346", "build/assets/pylib-android/netrc.py": "9dd6b80891863e23b5e0a57bb80c7346",
"build/assets/pylib-android/nntplib.py": "4926aa991bd4b96b9318d6242135abf9", "build/assets/pylib-android/nntplib.py": "4926aa991bd4b96b9318d6242135abf9",
"build/assets/pylib-android/ntpath.py": "1e3997be9aec964b4a3caa2ff7172c78", "build/assets/pylib-android/ntpath.py": "9790c31a274924d4168d8fa1f396f9c0",
"build/assets/pylib-android/nturl2path.py": "937624c4b6f213b652addea66296ccc4", "build/assets/pylib-android/nturl2path.py": "937624c4b6f213b652addea66296ccc4",
"build/assets/pylib-android/numbers.py": "bb9d88751c4f892d66e43961c521de7f", "build/assets/pylib-android/numbers.py": "bb9d88751c4f892d66e43961c521de7f",
"build/assets/pylib-android/opcode.py": "27b5ed52b503d4cf1adb7d65116655c3", "build/assets/pylib-android/opcode.py": "27b5ed52b503d4cf1adb7d65116655c3",
"build/assets/pylib-android/operator.py": "61e197bc43df97ec39ae3e5e59b11c19", "build/assets/pylib-android/operator.py": "61e197bc43df97ec39ae3e5e59b11c19",
"build/assets/pylib-android/optparse.py": "5f65f891612b68c71a2846da86254285", "build/assets/pylib-android/optparse.py": "5f65f891612b68c71a2846da86254285",
"build/assets/pylib-android/os.py": "1d20abe92b62ba9fc876be6714e29337", "build/assets/pylib-android/os.py": "e9627e3183be83287f785cfa0878a820",
"build/assets/pylib-android/pathlib.py": "1bf23fd9f937bb58eec2a62a60d73280", "build/assets/pylib-android/pathlib.py": "b6a1d7be62bb73cf45e43617f5270957",
"build/assets/pylib-android/pdb.py": "c62b3dff47478089a415ed83671a636c", "build/assets/pylib-android/pdb.py": "c62b3dff47478089a415ed83671a636c",
"build/assets/pylib-android/pickle.py": "ec61f6c0d4bd9f21c664a3673d6ce158", "build/assets/pylib-android/pickle.py": "ec61f6c0d4bd9f21c664a3673d6ce158",
"build/assets/pylib-android/pickletools.py": "e2cb81715625efdc409f8d34e36a9569", "build/assets/pylib-android/pickletools.py": "e2cb81715625efdc409f8d34e36a9569",
"build/assets/pylib-android/pipes.py": "2dd796bdbb87982034234fec50d4526c", "build/assets/pylib-android/pipes.py": "2dd796bdbb87982034234fec50d4526c",
"build/assets/pylib-android/pkgutil.py": "417ff74f276b7659b93ac5e8ee425f0f", "build/assets/pylib-android/pkgutil.py": "417ff74f276b7659b93ac5e8ee425f0f",
"build/assets/pylib-android/platform.py": "d15e44b3dcd3271c78bcd4b0cd37dbbb", "build/assets/pylib-android/platform.py": "263c9442b51433c6956a9e7a561d9b7e",
"build/assets/pylib-android/plistlib.py": "89a4be15fa63e930d5b5fc3f9c99c4f7", "build/assets/pylib-android/plistlib.py": "89a4be15fa63e930d5b5fc3f9c99c4f7",
"build/assets/pylib-android/poplib.py": "8150e0a07082ec4dbb017c4bc7163d7d", "build/assets/pylib-android/poplib.py": "8150e0a07082ec4dbb017c4bc7163d7d",
"build/assets/pylib-android/posixpath.py": "f7403faf74c3968010c45e204aad6415", "build/assets/pylib-android/posixpath.py": "41b7b16fe21131e0012177f090aacc47",
"build/assets/pylib-android/pprint.py": "8a1792d9fe0cad9605a9deb9af479341", "build/assets/pylib-android/pprint.py": "8a1792d9fe0cad9605a9deb9af479341",
"build/assets/pylib-android/profile.py": "8f26436d147cfd6d00d19592cf0d8c91", "build/assets/pylib-android/profile.py": "8f26436d147cfd6d00d19592cf0d8c91",
"build/assets/pylib-android/pstats.py": "1eb59db1b83dd577c863d26ca04b3105", "build/assets/pylib-android/pstats.py": "1eb59db1b83dd577c863d26ca04b3105",
@ -2930,17 +2954,17 @@
"build/assets/pylib-android/selectors.py": "3c94b3b678c473543cdc7f1d2b20a6f6", "build/assets/pylib-android/selectors.py": "3c94b3b678c473543cdc7f1d2b20a6f6",
"build/assets/pylib-android/shelve.py": "3e569c07c863ecbd7f35a6c382d1785a", "build/assets/pylib-android/shelve.py": "3e569c07c863ecbd7f35a6c382d1785a",
"build/assets/pylib-android/shlex.py": "adb6b917c5d3adadbdcb77a9257595fd", "build/assets/pylib-android/shlex.py": "adb6b917c5d3adadbdcb77a9257595fd",
"build/assets/pylib-android/shutil.py": "b24c18a65894e6860b16fb8462f9c1c5", "build/assets/pylib-android/shutil.py": "b23c4712f744f3030ba1f87d1a2bf6a4",
"build/assets/pylib-android/signal.py": "6c286caa92d351510f74cb4e1b3661da", "build/assets/pylib-android/signal.py": "6c286caa92d351510f74cb4e1b3661da",
"build/assets/pylib-android/site.py": "ddbf5304e85bedcc06f9a49b2e544e74", "build/assets/pylib-android/site.py": "e1523c36412e4ec333acbd5b0d2d6ea2",
"build/assets/pylib-android/smtplib.py": "a8121c0c6888489f89cdb2d5b06638f7", "build/assets/pylib-android/smtplib.py": "a8121c0c6888489f89cdb2d5b06638f7",
"build/assets/pylib-android/sndhdr.py": "6b62a34738529e39528c0cb498a23eee", "build/assets/pylib-android/sndhdr.py": "6b62a34738529e39528c0cb498a23eee",
"build/assets/pylib-android/socket.py": "93b3814a3cd3753bfbb8caa1e37ea8dd", "build/assets/pylib-android/socket.py": "e0463a87103f5d2e08f772b9a53ea816",
"build/assets/pylib-android/socketserver.py": "08d185dbe1e568e299133cc8dcb35940", "build/assets/pylib-android/socketserver.py": "08d185dbe1e568e299133cc8dcb35940",
"build/assets/pylib-android/sqlite3/__init__.py": "e64822b75a1c8f1be3e23ed6923e12eb", "build/assets/pylib-android/sqlite3/__init__.py": "e64822b75a1c8f1be3e23ed6923e12eb",
"build/assets/pylib-android/sqlite3/__main__.py": "52b4c89dbcbaab2bcbd71da0317fdf46", "build/assets/pylib-android/sqlite3/__main__.py": "52b4c89dbcbaab2bcbd71da0317fdf46",
"build/assets/pylib-android/sqlite3/dbapi2.py": "b3b44b48fcb3999ca0269023c2fb7268", "build/assets/pylib-android/sqlite3/dbapi2.py": "b3b44b48fcb3999ca0269023c2fb7268",
"build/assets/pylib-android/sqlite3/dump.py": "8d2085ec40031d544694759608e53178", "build/assets/pylib-android/sqlite3/dump.py": "ac821eee63ad27947460c6aca84d4e8d",
"build/assets/pylib-android/sre_compile.py": "a1784e9ccbea7d9963cab75b536b40c8", "build/assets/pylib-android/sre_compile.py": "a1784e9ccbea7d9963cab75b536b40c8",
"build/assets/pylib-android/sre_constants.py": "5c5be32a5334d9b0a848dad520746a63", "build/assets/pylib-android/sre_constants.py": "5c5be32a5334d9b0a848dad520746a63",
"build/assets/pylib-android/sre_parse.py": "cca15b9ab31509e6642f9d2fd4fb9d91", "build/assets/pylib-android/sre_parse.py": "cca15b9ab31509e6642f9d2fd4fb9d91",
@ -2955,8 +2979,8 @@
"build/assets/pylib-android/symtable.py": "7808839ac346e577ef1ab623c324d9e1", "build/assets/pylib-android/symtable.py": "7808839ac346e577ef1ab623c324d9e1",
"build/assets/pylib-android/sysconfig.py": "6c0114db24638ab125b9e933d44c79ef", "build/assets/pylib-android/sysconfig.py": "6c0114db24638ab125b9e933d44c79ef",
"build/assets/pylib-android/tabnanny.py": "017f45469d744cc511654fe887bda204", "build/assets/pylib-android/tabnanny.py": "017f45469d744cc511654fe887bda204",
"build/assets/pylib-android/tarfile.py": "43041075df31a5c8e7d8ea626391f185", "build/assets/pylib-android/tarfile.py": "c895804e267dc809bf1fbd5e990078ba",
"build/assets/pylib-android/telnetlib.py": "1a746e1fc988b9f5c12b9fd5a97c00ea", "build/assets/pylib-android/telnetlib.py": "0700ac21e04abde09aac1eb1e52ebbde",
"build/assets/pylib-android/tempfile.py": "916365f4533239ffe6d66440b0ead6ae", "build/assets/pylib-android/tempfile.py": "916365f4533239ffe6d66440b0ead6ae",
"build/assets/pylib-android/textwrap.py": "3eb16a40553205dc96be5cb9039f3c8c", "build/assets/pylib-android/textwrap.py": "3eb16a40553205dc96be5cb9039f3c8c",
"build/assets/pylib-android/this.py": "8b0a9a1fa0a45a37e6c656eca1922277", "build/assets/pylib-android/this.py": "8b0a9a1fa0a45a37e6c656eca1922277",
@ -2968,21 +2992,21 @@
"build/assets/pylib-android/tomllib/_parser.py": "f9a4dc92c44403f970e94c2e494a7358", "build/assets/pylib-android/tomllib/_parser.py": "f9a4dc92c44403f970e94c2e494a7358",
"build/assets/pylib-android/tomllib/_re.py": "0e509117e16c41c491615e06bb98861d", "build/assets/pylib-android/tomllib/_re.py": "0e509117e16c41c491615e06bb98861d",
"build/assets/pylib-android/tomllib/_types.py": "07be9616d6f5e401fd31fbeea619fc97", "build/assets/pylib-android/tomllib/_types.py": "07be9616d6f5e401fd31fbeea619fc97",
"build/assets/pylib-android/trace.py": "e0015a322cf5fbb35310705e81953ab1", "build/assets/pylib-android/trace.py": "3e0ba305fd653f127f2ddcc278abbbc4",
"build/assets/pylib-android/traceback.py": "84cbe40fcb14833ce4a1c0be22c4c9bc", "build/assets/pylib-android/traceback.py": "84cbe40fcb14833ce4a1c0be22c4c9bc",
"build/assets/pylib-android/tracemalloc.py": "e4d10d2bee7773566e46797a939e5cbf", "build/assets/pylib-android/tracemalloc.py": "e4d10d2bee7773566e46797a939e5cbf",
"build/assets/pylib-android/tty.py": "06d3dd054c50fbff2712f3a38cca880f", "build/assets/pylib-android/tty.py": "06d3dd054c50fbff2712f3a38cca880f",
"build/assets/pylib-android/types.py": "134f86ee0d4879d294f95ea39c396027", "build/assets/pylib-android/types.py": "134f86ee0d4879d294f95ea39c396027",
"build/assets/pylib-android/typing.py": "5780108232e902b6c03c9dede3203e15", "build/assets/pylib-android/typing.py": "56bcd9a7595ef9b7f9332894b7ebc1a7",
"build/assets/pylib-android/urllib/__init__.py": "340c83beff7dcff8f5c7b87cd43cedaf", "build/assets/pylib-android/urllib/__init__.py": "340c83beff7dcff8f5c7b87cd43cedaf",
"build/assets/pylib-android/urllib/error.py": "b7dde0483ff647eb87162d6e19c04fa0", "build/assets/pylib-android/urllib/error.py": "b7dde0483ff647eb87162d6e19c04fa0",
"build/assets/pylib-android/urllib/parse.py": "9533d0e59e5ec79810be417dfc8b351a", "build/assets/pylib-android/urllib/parse.py": "2b10efb06b24573929dafb6167b26099",
"build/assets/pylib-android/urllib/request.py": "bf59985a796494de7374fb909f32f933", "build/assets/pylib-android/urllib/request.py": "bf59985a796494de7374fb909f32f933",
"build/assets/pylib-android/urllib/response.py": "c8537707a4b1e493c0ec4489ab523c93", "build/assets/pylib-android/urllib/response.py": "c8537707a4b1e493c0ec4489ab523c93",
"build/assets/pylib-android/urllib/robotparser.py": "5a7616bdf398c166f953ad48c25506eb", "build/assets/pylib-android/urllib/robotparser.py": "5a7616bdf398c166f953ad48c25506eb",
"build/assets/pylib-android/uu.py": "7cb795ca0c9e914550e6245dfe53c53e", "build/assets/pylib-android/uu.py": "7cb795ca0c9e914550e6245dfe53c53e",
"build/assets/pylib-android/uuid.py": "fc298b89a3f1327be7808f0125c57866", "build/assets/pylib-android/uuid.py": "fc298b89a3f1327be7808f0125c57866",
"build/assets/pylib-android/warnings.py": "0786f92f520b209fc88b9352fd7fc474", "build/assets/pylib-android/warnings.py": "4deb449b93c3a0d8737555589f6f111c",
"build/assets/pylib-android/wave.py": "91c8be6651123c0d264fada07b63fd8e", "build/assets/pylib-android/wave.py": "91c8be6651123c0d264fada07b63fd8e",
"build/assets/pylib-android/weakref.py": "dd14612f02ca8acd723f633b6fff0adf", "build/assets/pylib-android/weakref.py": "dd14612f02ca8acd723f633b6fff0adf",
"build/assets/pylib-android/webbrowser.py": "45185d3143cc2513d3141e7cbfdafb1b", "build/assets/pylib-android/webbrowser.py": "45185d3143cc2513d3141e7cbfdafb1b",
@ -2996,7 +3020,7 @@
"build/assets/pylib-android/xml/dom/minidom.py": "3f4037810f081b078cc3fcab75bae3a0", "build/assets/pylib-android/xml/dom/minidom.py": "3f4037810f081b078cc3fcab75bae3a0",
"build/assets/pylib-android/xml/dom/pulldom.py": "e133090081e446d69e5d1884f64fe1b6", "build/assets/pylib-android/xml/dom/pulldom.py": "e133090081e446d69e5d1884f64fe1b6",
"build/assets/pylib-android/xml/dom/xmlbuilder.py": "610ebe0afcf5c7a72c6f708ae34e4f7e", "build/assets/pylib-android/xml/dom/xmlbuilder.py": "610ebe0afcf5c7a72c6f708ae34e4f7e",
"build/assets/pylib-android/xml/etree/ElementInclude.py": "164c77dfb9abdf9c70def8812a00843a", "build/assets/pylib-android/xml/etree/ElementInclude.py": "cdb7b9c55c44fbeb94bf96500b7bb5aa",
"build/assets/pylib-android/xml/etree/ElementPath.py": "fca9220534a8160762dee5e7da5f4144", "build/assets/pylib-android/xml/etree/ElementPath.py": "fca9220534a8160762dee5e7da5f4144",
"build/assets/pylib-android/xml/etree/ElementTree.py": "dade97ab01bfa32bafca09daf7e7bbb2", "build/assets/pylib-android/xml/etree/ElementTree.py": "dade97ab01bfa32bafca09daf7e7bbb2",
"build/assets/pylib-android/xml/etree/__init__.py": "82a62c7b190620aa1d3a40cc9ba829a5", "build/assets/pylib-android/xml/etree/__init__.py": "82a62c7b190620aa1d3a40cc9ba829a5",
@ -4038,52 +4062,52 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "984af200e5e35417de6ace1da79c1351", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "de1a31ddd37764c6a31c6c619a0069fe",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "0902b446e625e001396b70ab1e567423", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "1a4894c9eb324e6d2a442f72e5651c2c",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "7e940b9dabe493f106c2e9d41fad32d2", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "1999d90958ff312203a290aaff97a24f",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "3492bf499cbf195e311ea7587d4247c4", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "9a33cde90e447754e0b669f1e08948b2",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "80ce3502f6ff6ae99d5a61cb746f476a", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "c52c4b14e87cada2d2de8060caf64ec3",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "08f433223f802b142a64a4d7e8ff129e", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4894e58ac2fa3cbff0c0542e33afd94e",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "ee89411f673161492bf8762656c8308a", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "eee4b5407e69075d925fad94de12c0b8",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "bcb36f5a15e3fe28cbc5606b2196736d", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "5d041c76ab815765179308a01b6a0028",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "fb21bde8f575ef1aaabfee5bd2651571", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "8ae3a85f68553255fa47a86e35cd41b0",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "3c3ad695c496f35ca06b9b95d4ff1db7", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "cd9dac595579b8e19f15eade2cf8155f",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "f3ee8ad1b441c7788f2a76106a740f14", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "1ab4f141dd2541ae40e886b5d448d86f",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "b29045a9462537f4cc4a487a0d75abc3", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "fb869f599357cb079851542411b2a852",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "46c370f0616494816ea1ff73aa2b0d2f", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "d06777b1f953c4f102d3ddb5b9d79337",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ffb4d1ab15033abbc9a7ccdf64493b25", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ac0582306bab0108da45dc53e0c544a7",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "0a542a05b30e4f1d443e3e63be3406aa", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "f847c697c1d541bbd1207355065fa988",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "8711c3e818a759b050cab7dcff2e1147", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "6edc275e5c427a04df8b02b115d2d832",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "edb7277a16fb2847388b3c55447e5fbd", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "473ef626512034b43a8fa54ad66af1ce",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "8980b84f0c39fb6f8bead208e4b794de", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "45f6a37d5e21b45542260f86b7c4784b",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "109c5e9d5a415a0c5bb81ad076680174", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "4d9922c7ba8e15e2059fcef7f87d195a",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "49aa40bff81a8342ff16a41c143d7917", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "fd57a5b64d0fc65bd04bbfdd4b63965d",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "f231b10895bdcb542de87b887ca181fd", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "fbfd42ab5d27e3ff921fa9cda02b2b45",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "ae936a119668ede7b36f38c8672f4bf8", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "abb5870a4b01cda2c9a958149f759325",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "f231b10895bdcb542de87b887ca181fd", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "fbfd42ab5d27e3ff921fa9cda02b2b45",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "ae936a119668ede7b36f38c8672f4bf8", "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "abb5870a4b01cda2c9a958149f759325",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "419b7edff05748a74370f29945bca90c", "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "1b51a49163d412cdd0b1441e3459e6c4",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "1e75c6c1f0b0aae130bacfb8b65507cc", "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "4a39d8397fb76df3bafa366d8085d1cd",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "419b7edff05748a74370f29945bca90c", "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "1b51a49163d412cdd0b1441e3459e6c4",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "1e75c6c1f0b0aae130bacfb8b65507cc", "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "4a39d8397fb76df3bafa366d8085d1cd",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "d965aa38a18627966633308f5a4e61b9", "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "c4ed230b63e7109de342e045733963e5",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "5d12b05022f064c2aaf096c466da687c", "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "adb7ef762cb92b4c3bbe74a36e8ac6df",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "d965aa38a18627966633308f5a4e61b9", "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "c4ed230b63e7109de342e045733963e5",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "5d12b05022f064c2aaf096c466da687c", "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "adb7ef762cb92b4c3bbe74a36e8ac6df",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "ff5b18144bf249558517bff7c97dc84f", "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "0863bb24d2a42598dd20e7b55beb1c02",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "efffc4f330e77530accd9a9f82840a6c", "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "1805fd5900917411f0e8032bf3c4734c",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "c20363fe2af3d54e666b1c8ee67f6b76", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "3277c2f61c315e2ca7ed6776ba2363e2",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "efffc4f330e77530accd9a9f82840a6c", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "1805fd5900917411f0e8032bf3c4734c",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "54b1fb0ff0a189a59da87f3578a1119a", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "b639992e280ee3a73b764a3e3e58baec",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "c0e6444e540a2046b858fc4b4629f6b4", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "5ebaf4315d286080e5789939edfe43ce",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "b44c5bb764eb9a30c49d3dc36c54d784", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "be8f76ded92473fbab8b28018097456c",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "23afa2f159052928a16cc3e5bde1f864", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "dcab88e3c14c6f4c4a03dd8617328eaf",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "40437dd15a65d97481d88e245de923bf", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "bab18c50134744a7206ea1ded2d90ab2",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "4e7a439617c2ad9d6100a7ec6e593713", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "c6788ba7cd99fc29a0b6ca8351f412b4",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "a19afc6c611054e2c5df4dd3c07afed9", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "e61726c963e53ee7746ffbd7ed35c919",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "5156925d106a00a4602ef7204f70ab5a", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "841fbbd6b87ac5d50ccdda647d8ff909",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "5548f407d97e380069f6c596c4e36cd7", "src/assets/ba_data/python/babase/_mgen/enums.py": "cb299985623bbcc86015cb103a424ae6",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "efa61468cf098f77cc6a234461d8b86d", "src/ballistica/base/mgen/pyembed/binding_base.inc": "efa61468cf098f77cc6a234461d8b86d",
"src/ballistica/base/mgen/pyembed/binding_base_app.inc": "97efb93f4bfd8e8b09f2db24398e29fc", "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "97efb93f4bfd8e8b09f2db24398e29fc",
"src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3", "src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3",

View File

@ -1232,7 +1232,7 @@
<w>getcollisionmesh</w> <w>getcollisionmesh</w>
<w>getconf</w> <w>getconf</w>
<w>getconfig</w> <w>getconfig</w>
<w>getcurrency</w> <w>gettickets</w>
<w>getcwd</w> <w>getcwd</w>
<w>getdata</w> <w>getdata</w>
<w>getenv</w> <w>getenv</w>
@ -3082,7 +3082,6 @@
<w>tournamentbutton</w> <w>tournamentbutton</w>
<w>tournamententry</w> <w>tournamententry</w>
<w>tournamentscores</w> <w>tournamentscores</w>
<w>tpartial</w>
<w>tpath</w> <w>tpath</w>
<w>tpathsegs</w> <w>tpathsegs</w>
<w>tpexl</w> <w>tpexl</w>

View File

@ -1,4 +1,4 @@
### 1.7.36 (build 21911, api 8, 2024-07-05) ### 1.7.36 (build 21919, api 8, 2024-07-21)
- Wired up initial support for using asset-packages for bundled assets. - Wired up initial support for using asset-packages for bundled assets.
- bacloud workspace commands are now a bit smarter; you can now do things like - bacloud workspace commands are now a bit smarter; you can now do things like
`bacloud workspace put .` or even just `bacloud workspace put` and it will `bacloud workspace put .` or even just `bacloud workspace put` and it will
@ -6,6 +6,29 @@
as a second argument. Both `workspace get` and `workspace put` now also have as a second argument. Both `workspace get` and `workspace put` now also have
an optional `--workspace` arg if you want to sync with a workspace different an optional `--workspace` arg if you want to sync with a workspace different
than the local directory name. than the local directory name.
- Cleaned up look and feel on horizontal scrollbars, especially when and how
they fade in and out.
- Fixed an issue where ConfigNumberEdit objects would draw incorrectly with
textscale set to non-1.0 values.
- Fixed a nasty bug with the new stdin handling from 1.7.35 which could cause
the stdin thread to spin at 100% cpu usage in some cases (such as when
launching the Mac build from the Finder and not a terminal).
- Added a `draw_controller_mult` arg to `bauiv1.imagewidget()` to control how
brightly the image pulses when its controller widget is selected (can prevent
brightly colored images from blowing out too much).
- The Mac version is now correctly rendering to a sRGB colorspace instead of P3.
This was causing some bright colors to render extra-eye-destroying bright.
- Fixed an issue with the Repeater() class which could cause key presses in UIs
to get lost if many were happening in short succession. An easy way to observe
this (at least on my machine) was to press left and right repeatedly in the
main menu - some presses would be lost and the selection would 'drift' one
direction.
- Replaced all `efro.call.tpartial` calls with Python's built in
`functools.partial`. Mypy's 1.11 update added full type checking for
`functools.partial` so there's no benefit to maintaining our own special
version anymore. This also applies to `ba*.Call` which is redundant in the
same way. Both `efro.call.tpartial` and `ba*.Call` will probably be marked
deprecated and go away at some point.
### 1.7.35 (build 21889, api 8, 2024-06-20) ### 1.7.35 (build 21889, api 8, 2024-06-20)
- Fixed an issue where the engine would block at exit on some version of Linux - Fixed an issue where the engine would block at exit on some version of Linux

View File

@ -1807,7 +1807,6 @@
<w>toucs</w> <w>toucs</w>
<w>tournamentbutton</w> <w>tournamentbutton</w>
<w>toutf</w> <w>toutf</w>
<w>tpartial</w>
<w>tpath</w> <w>tpath</w>
<w>tpathsegs</w> <w>tpathsegs</w>
<w>tpexl</w> <w>tpexl</w>

View File

@ -1,21 +1,21 @@
cpplint==1.6.1 cpplint==1.6.1
dmgbuild==1.6.1 dmgbuild==1.6.1
filelock==3.15.4 filelock==3.15.4
furo==2024.5.6 furo==2024.7.18
mypy==1.10.1 mypy==1.11.0
pbxproj==4.2.0 pbxproj==4.2.0
pdoc==14.5.1 pdoc==14.5.1
pur==7.3.2 pur==7.3.2
pylint==3.2.5 pylint==3.2.6
pylsp-mypy==0.6.8 pylsp-mypy==0.6.8
pytest==8.2.2 pytest==8.3.1
python-daemon==3.0.1 python-daemon==3.0.1
python-lsp-black==2.0.0 python-lsp-black==2.0.0
python-lsp-server==1.11.0 python-lsp-server==1.11.0
requests==2.32.3 requests==2.32.3
Sphinx==7.3.7 Sphinx==7.4.7
tomlkit==0.12.5 tomlkit==0.13.0
types-certifi==2021.10.8.3 types-certifi==2021.10.8.3
types-filelock==3.2.7 types-filelock==3.2.7
types-requests==2.32.0.20240622 types-requests==2.32.0.20240712
typing_extensions==4.12.2 typing_extensions==4.12.2

View File

@ -1629,6 +1629,10 @@
"ba_data/textures/glow.ktx", "ba_data/textures/glow.ktx",
"ba_data/textures/glow.pvr", "ba_data/textures/glow.pvr",
"ba_data/textures/glow_preview.png", "ba_data/textures/glow_preview.png",
"ba_data/textures/goldPass.dds",
"ba_data/textures/goldPass.ktx",
"ba_data/textures/goldPass.pvr",
"ba_data/textures/goldPass_preview.png",
"ba_data/textures/googlePlayAchievementsIcon.dds", "ba_data/textures/googlePlayAchievementsIcon.dds",
"ba_data/textures/googlePlayAchievementsIcon.ktx", "ba_data/textures/googlePlayAchievementsIcon.ktx",
"ba_data/textures/googlePlayAchievementsIcon.pvr", "ba_data/textures/googlePlayAchievementsIcon.pvr",
@ -2413,6 +2417,22 @@
"ba_data/textures/tnt.ktx", "ba_data/textures/tnt.ktx",
"ba_data/textures/tnt.pvr", "ba_data/textures/tnt.pvr",
"ba_data/textures/tnt_preview.png", "ba_data/textures/tnt_preview.png",
"ba_data/textures/tokens1.dds",
"ba_data/textures/tokens1.ktx",
"ba_data/textures/tokens1.pvr",
"ba_data/textures/tokens1_preview.png",
"ba_data/textures/tokens2.dds",
"ba_data/textures/tokens2.ktx",
"ba_data/textures/tokens2.pvr",
"ba_data/textures/tokens2_preview.png",
"ba_data/textures/tokens3.dds",
"ba_data/textures/tokens3.ktx",
"ba_data/textures/tokens3.pvr",
"ba_data/textures/tokens3_preview.png",
"ba_data/textures/tokens4.dds",
"ba_data/textures/tokens4.ktx",
"ba_data/textures/tokens4.pvr",
"ba_data/textures/tokens4_preview.png",
"ba_data/textures/touchArrows.dds", "ba_data/textures/touchArrows.dds",
"ba_data/textures/touchArrows.ktx", "ba_data/textures/touchArrows.ktx",
"ba_data/textures/touchArrows.pvr", "ba_data/textures/touchArrows.pvr",
@ -2481,6 +2501,10 @@
"ba_data/textures/white.ktx", "ba_data/textures/white.ktx",
"ba_data/textures/white.pvr", "ba_data/textures/white.pvr",
"ba_data/textures/white_preview.png", "ba_data/textures/white_preview.png",
"ba_data/textures/windowBottomCap.dds",
"ba_data/textures/windowBottomCap.ktx",
"ba_data/textures/windowBottomCap.pvr",
"ba_data/textures/windowBottomCap_preview.png",
"ba_data/textures/windowHSmallVMed.dds", "ba_data/textures/windowHSmallVMed.dds",
"ba_data/textures/windowHSmallVMed.ktx", "ba_data/textures/windowHSmallVMed.ktx",
"ba_data/textures/windowHSmallVMed.pvr", "ba_data/textures/windowHSmallVMed.pvr",

View File

@ -380,8 +380,9 @@
"ba_data/python/bauiv1lib/__pycache__/discord.cpython-312.opt-1.pyc", "ba_data/python/bauiv1lib/__pycache__/discord.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/feedback.cpython-312.opt-1.pyc", "ba_data/python/bauiv1lib/__pycache__/feedback.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/fileselector.cpython-312.opt-1.pyc", "ba_data/python/bauiv1lib/__pycache__/fileselector.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/getcurrency.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/getremote.cpython-312.opt-1.pyc", "ba_data/python/bauiv1lib/__pycache__/getremote.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/gettickets.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/gettokens.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/helpui.cpython-312.opt-1.pyc", "ba_data/python/bauiv1lib/__pycache__/helpui.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/iconpicker.cpython-312.opt-1.pyc", "ba_data/python/bauiv1lib/__pycache__/iconpicker.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/kiosk.cpython-312.opt-1.pyc", "ba_data/python/bauiv1lib/__pycache__/kiosk.cpython-312.opt-1.pyc",
@ -454,8 +455,9 @@
"ba_data/python/bauiv1lib/gather/nearbytab.py", "ba_data/python/bauiv1lib/gather/nearbytab.py",
"ba_data/python/bauiv1lib/gather/privatetab.py", "ba_data/python/bauiv1lib/gather/privatetab.py",
"ba_data/python/bauiv1lib/gather/publictab.py", "ba_data/python/bauiv1lib/gather/publictab.py",
"ba_data/python/bauiv1lib/getcurrency.py",
"ba_data/python/bauiv1lib/getremote.py", "ba_data/python/bauiv1lib/getremote.py",
"ba_data/python/bauiv1lib/gettickets.py",
"ba_data/python/bauiv1lib/gettokens.py",
"ba_data/python/bauiv1lib/helpui.py", "ba_data/python/bauiv1lib/helpui.py",
"ba_data/python/bauiv1lib/iconpicker.py", "ba_data/python/bauiv1lib/iconpicker.py",
"ba_data/python/bauiv1lib/keyboard/__init__.py", "ba_data/python/bauiv1lib/keyboard/__init__.py",

View File

@ -364,8 +364,9 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/nearbytab.py \ $(BUILD_DIR)/ba_data/python/bauiv1lib/gather/nearbytab.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/privatetab.py \ $(BUILD_DIR)/ba_data/python/bauiv1lib/gather/privatetab.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/publictab.py \ $(BUILD_DIR)/ba_data/python/bauiv1lib/gather/publictab.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/getcurrency.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/getremote.py \ $(BUILD_DIR)/ba_data/python/bauiv1lib/getremote.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gettickets.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gettokens.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/helpui.py \ $(BUILD_DIR)/ba_data/python/bauiv1lib/helpui.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/iconpicker.py \ $(BUILD_DIR)/ba_data/python/bauiv1lib/iconpicker.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/keyboard/__init__.py \ $(BUILD_DIR)/ba_data/python/bauiv1lib/keyboard/__init__.py \
@ -641,8 +642,9 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__pycache__/nearbytab.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__pycache__/nearbytab.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__pycache__/privatetab.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__pycache__/privatetab.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__pycache__/publictab.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__pycache__/publictab.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/getcurrency.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/getremote.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/getremote.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/gettickets.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/gettokens.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/helpui.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/helpui.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/iconpicker.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/iconpicker.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/keyboard/__pycache__/__init__.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bauiv1lib/keyboard/__pycache__/__init__.cpython-312.opt-1.pyc \
@ -5711,6 +5713,7 @@ TEX2D_DDS_TARGETS = \
$(BUILD_DIR)/ba_data/textures/gladiatorIcon.dds \ $(BUILD_DIR)/ba_data/textures/gladiatorIcon.dds \
$(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask.dds \ $(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask.dds \
$(BUILD_DIR)/ba_data/textures/glow.dds \ $(BUILD_DIR)/ba_data/textures/glow.dds \
$(BUILD_DIR)/ba_data/textures/goldPass.dds \
$(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon.dds \ $(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon.dds \
$(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon.dds \ $(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon.dds \
$(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon.dds \ $(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon.dds \
@ -5907,6 +5910,10 @@ TEX2D_DDS_TARGETS = \
$(BUILD_DIR)/ba_data/textures/tipTopLevelColor.dds \ $(BUILD_DIR)/ba_data/textures/tipTopLevelColor.dds \
$(BUILD_DIR)/ba_data/textures/tipTopPreview.dds \ $(BUILD_DIR)/ba_data/textures/tipTopPreview.dds \
$(BUILD_DIR)/ba_data/textures/tnt.dds \ $(BUILD_DIR)/ba_data/textures/tnt.dds \
$(BUILD_DIR)/ba_data/textures/tokens1.dds \
$(BUILD_DIR)/ba_data/textures/tokens2.dds \
$(BUILD_DIR)/ba_data/textures/tokens3.dds \
$(BUILD_DIR)/ba_data/textures/tokens4.dds \
$(BUILD_DIR)/ba_data/textures/touchArrows.dds \ $(BUILD_DIR)/ba_data/textures/touchArrows.dds \
$(BUILD_DIR)/ba_data/textures/touchArrowsActions.dds \ $(BUILD_DIR)/ba_data/textures/touchArrowsActions.dds \
$(BUILD_DIR)/ba_data/textures/towerDLevelColor.dds \ $(BUILD_DIR)/ba_data/textures/towerDLevelColor.dds \
@ -5924,6 +5931,7 @@ TEX2D_DDS_TARGETS = \
$(BUILD_DIR)/ba_data/textures/warriorIcon.dds \ $(BUILD_DIR)/ba_data/textures/warriorIcon.dds \
$(BUILD_DIR)/ba_data/textures/warriorIconColorMask.dds \ $(BUILD_DIR)/ba_data/textures/warriorIconColorMask.dds \
$(BUILD_DIR)/ba_data/textures/white.dds \ $(BUILD_DIR)/ba_data/textures/white.dds \
$(BUILD_DIR)/ba_data/textures/windowBottomCap.dds \
$(BUILD_DIR)/ba_data/textures/windowHSmallVMed.dds \ $(BUILD_DIR)/ba_data/textures/windowHSmallVMed.dds \
$(BUILD_DIR)/ba_data/textures/windowHSmallVSmall.dds \ $(BUILD_DIR)/ba_data/textures/windowHSmallVSmall.dds \
$(BUILD_DIR)/ba_data/textures/wings.dds \ $(BUILD_DIR)/ba_data/textures/wings.dds \
@ -6116,6 +6124,7 @@ TEX2D_PVR_TARGETS = \
$(BUILD_DIR)/ba_data/textures/gladiatorIcon.pvr \ $(BUILD_DIR)/ba_data/textures/gladiatorIcon.pvr \
$(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask.pvr \ $(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask.pvr \
$(BUILD_DIR)/ba_data/textures/glow.pvr \ $(BUILD_DIR)/ba_data/textures/glow.pvr \
$(BUILD_DIR)/ba_data/textures/goldPass.pvr \
$(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon.pvr \ $(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon.pvr \
$(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon.pvr \ $(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon.pvr \
$(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon.pvr \ $(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon.pvr \
@ -6312,6 +6321,10 @@ TEX2D_PVR_TARGETS = \
$(BUILD_DIR)/ba_data/textures/tipTopLevelColor.pvr \ $(BUILD_DIR)/ba_data/textures/tipTopLevelColor.pvr \
$(BUILD_DIR)/ba_data/textures/tipTopPreview.pvr \ $(BUILD_DIR)/ba_data/textures/tipTopPreview.pvr \
$(BUILD_DIR)/ba_data/textures/tnt.pvr \ $(BUILD_DIR)/ba_data/textures/tnt.pvr \
$(BUILD_DIR)/ba_data/textures/tokens1.pvr \
$(BUILD_DIR)/ba_data/textures/tokens2.pvr \
$(BUILD_DIR)/ba_data/textures/tokens3.pvr \
$(BUILD_DIR)/ba_data/textures/tokens4.pvr \
$(BUILD_DIR)/ba_data/textures/touchArrows.pvr \ $(BUILD_DIR)/ba_data/textures/touchArrows.pvr \
$(BUILD_DIR)/ba_data/textures/touchArrowsActions.pvr \ $(BUILD_DIR)/ba_data/textures/touchArrowsActions.pvr \
$(BUILD_DIR)/ba_data/textures/towerDLevelColor.pvr \ $(BUILD_DIR)/ba_data/textures/towerDLevelColor.pvr \
@ -6329,6 +6342,7 @@ TEX2D_PVR_TARGETS = \
$(BUILD_DIR)/ba_data/textures/warriorIcon.pvr \ $(BUILD_DIR)/ba_data/textures/warriorIcon.pvr \
$(BUILD_DIR)/ba_data/textures/warriorIconColorMask.pvr \ $(BUILD_DIR)/ba_data/textures/warriorIconColorMask.pvr \
$(BUILD_DIR)/ba_data/textures/white.pvr \ $(BUILD_DIR)/ba_data/textures/white.pvr \
$(BUILD_DIR)/ba_data/textures/windowBottomCap.pvr \
$(BUILD_DIR)/ba_data/textures/windowHSmallVMed.pvr \ $(BUILD_DIR)/ba_data/textures/windowHSmallVMed.pvr \
$(BUILD_DIR)/ba_data/textures/windowHSmallVSmall.pvr \ $(BUILD_DIR)/ba_data/textures/windowHSmallVSmall.pvr \
$(BUILD_DIR)/ba_data/textures/wings.pvr \ $(BUILD_DIR)/ba_data/textures/wings.pvr \
@ -6521,6 +6535,7 @@ TEX2D_KTX_TARGETS = \
$(BUILD_DIR)/ba_data/textures/gladiatorIcon.ktx \ $(BUILD_DIR)/ba_data/textures/gladiatorIcon.ktx \
$(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask.ktx \ $(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask.ktx \
$(BUILD_DIR)/ba_data/textures/glow.ktx \ $(BUILD_DIR)/ba_data/textures/glow.ktx \
$(BUILD_DIR)/ba_data/textures/goldPass.ktx \
$(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon.ktx \ $(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon.ktx \
$(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon.ktx \ $(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon.ktx \
$(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon.ktx \ $(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon.ktx \
@ -6717,6 +6732,10 @@ TEX2D_KTX_TARGETS = \
$(BUILD_DIR)/ba_data/textures/tipTopLevelColor.ktx \ $(BUILD_DIR)/ba_data/textures/tipTopLevelColor.ktx \
$(BUILD_DIR)/ba_data/textures/tipTopPreview.ktx \ $(BUILD_DIR)/ba_data/textures/tipTopPreview.ktx \
$(BUILD_DIR)/ba_data/textures/tnt.ktx \ $(BUILD_DIR)/ba_data/textures/tnt.ktx \
$(BUILD_DIR)/ba_data/textures/tokens1.ktx \
$(BUILD_DIR)/ba_data/textures/tokens2.ktx \
$(BUILD_DIR)/ba_data/textures/tokens3.ktx \
$(BUILD_DIR)/ba_data/textures/tokens4.ktx \
$(BUILD_DIR)/ba_data/textures/touchArrows.ktx \ $(BUILD_DIR)/ba_data/textures/touchArrows.ktx \
$(BUILD_DIR)/ba_data/textures/touchArrowsActions.ktx \ $(BUILD_DIR)/ba_data/textures/touchArrowsActions.ktx \
$(BUILD_DIR)/ba_data/textures/towerDLevelColor.ktx \ $(BUILD_DIR)/ba_data/textures/towerDLevelColor.ktx \
@ -6734,6 +6753,7 @@ TEX2D_KTX_TARGETS = \
$(BUILD_DIR)/ba_data/textures/warriorIcon.ktx \ $(BUILD_DIR)/ba_data/textures/warriorIcon.ktx \
$(BUILD_DIR)/ba_data/textures/warriorIconColorMask.ktx \ $(BUILD_DIR)/ba_data/textures/warriorIconColorMask.ktx \
$(BUILD_DIR)/ba_data/textures/white.ktx \ $(BUILD_DIR)/ba_data/textures/white.ktx \
$(BUILD_DIR)/ba_data/textures/windowBottomCap.ktx \
$(BUILD_DIR)/ba_data/textures/windowHSmallVMed.ktx \ $(BUILD_DIR)/ba_data/textures/windowHSmallVMed.ktx \
$(BUILD_DIR)/ba_data/textures/windowHSmallVSmall.ktx \ $(BUILD_DIR)/ba_data/textures/windowHSmallVSmall.ktx \
$(BUILD_DIR)/ba_data/textures/wings.ktx \ $(BUILD_DIR)/ba_data/textures/wings.ktx \
@ -6926,6 +6946,7 @@ TEX2D_PREVIEW_PNG_TARGETS = \
$(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask_preview.png \ $(BUILD_DIR)/ba_data/textures/gladiatorIconColorMask_preview.png \
$(BUILD_DIR)/ba_data/textures/gladiatorIcon_preview.png \ $(BUILD_DIR)/ba_data/textures/gladiatorIcon_preview.png \
$(BUILD_DIR)/ba_data/textures/glow_preview.png \ $(BUILD_DIR)/ba_data/textures/glow_preview.png \
$(BUILD_DIR)/ba_data/textures/goldPass_preview.png \
$(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon_preview.png \ $(BUILD_DIR)/ba_data/textures/googlePlayAchievementsIcon_preview.png \
$(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon_preview.png \ $(BUILD_DIR)/ba_data/textures/googlePlayGamesIcon_preview.png \
$(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon_preview.png \ $(BUILD_DIR)/ba_data/textures/googlePlayLeaderboardsIcon_preview.png \
@ -7122,6 +7143,10 @@ TEX2D_PREVIEW_PNG_TARGETS = \
$(BUILD_DIR)/ba_data/textures/tipTopLevelColor_preview.png \ $(BUILD_DIR)/ba_data/textures/tipTopLevelColor_preview.png \
$(BUILD_DIR)/ba_data/textures/tipTopPreview_preview.png \ $(BUILD_DIR)/ba_data/textures/tipTopPreview_preview.png \
$(BUILD_DIR)/ba_data/textures/tnt_preview.png \ $(BUILD_DIR)/ba_data/textures/tnt_preview.png \
$(BUILD_DIR)/ba_data/textures/tokens1_preview.png \
$(BUILD_DIR)/ba_data/textures/tokens2_preview.png \
$(BUILD_DIR)/ba_data/textures/tokens3_preview.png \
$(BUILD_DIR)/ba_data/textures/tokens4_preview.png \
$(BUILD_DIR)/ba_data/textures/touchArrowsActions_preview.png \ $(BUILD_DIR)/ba_data/textures/touchArrowsActions_preview.png \
$(BUILD_DIR)/ba_data/textures/touchArrows_preview.png \ $(BUILD_DIR)/ba_data/textures/touchArrows_preview.png \
$(BUILD_DIR)/ba_data/textures/towerDLevelColor_preview.png \ $(BUILD_DIR)/ba_data/textures/towerDLevelColor_preview.png \
@ -7139,6 +7164,7 @@ TEX2D_PREVIEW_PNG_TARGETS = \
$(BUILD_DIR)/ba_data/textures/warriorIconColorMask_preview.png \ $(BUILD_DIR)/ba_data/textures/warriorIconColorMask_preview.png \
$(BUILD_DIR)/ba_data/textures/warriorIcon_preview.png \ $(BUILD_DIR)/ba_data/textures/warriorIcon_preview.png \
$(BUILD_DIR)/ba_data/textures/white_preview.png \ $(BUILD_DIR)/ba_data/textures/white_preview.png \
$(BUILD_DIR)/ba_data/textures/windowBottomCap_preview.png \
$(BUILD_DIR)/ba_data/textures/windowHSmallVMed_preview.png \ $(BUILD_DIR)/ba_data/textures/windowHSmallVMed_preview.png \
$(BUILD_DIR)/ba_data/textures/windowHSmallVSmall_preview.png \ $(BUILD_DIR)/ba_data/textures/windowHSmallVSmall_preview.png \
$(BUILD_DIR)/ba_data/textures/wings_preview.png \ $(BUILD_DIR)/ba_data/textures/wings_preview.png \

View File

@ -6,9 +6,9 @@ from __future__ import annotations
import hashlib import hashlib
import logging import logging
from functools import partial
from typing import TYPE_CHECKING, assert_never from typing import TYPE_CHECKING, assert_never
from efro.call import tpartial
from efro.error import CommunicationError from efro.error import CommunicationError
from bacommon.login import LoginType from bacommon.login import LoginType
import _babase import _babase
@ -223,7 +223,7 @@ class AccountV2Subsystem:
if service_str is not None: if service_str is not None:
_babase.apptimer( _babase.apptimer(
2.0, 2.0,
tpartial( partial(
_babase.screenmessage, _babase.screenmessage,
Lstr( Lstr(
resource='notUsingAccountText', resource='notUsingAccountText',

View File

@ -7,13 +7,11 @@ from __future__ import annotations
import os import os
import logging import logging
from enum import Enum from enum import Enum
from functools import partial
from typing import TYPE_CHECKING, TypeVar, override from typing import TYPE_CHECKING, TypeVar, override
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from threading import RLock from threading import RLock
from efro.call import tpartial
import _babase import _babase
from babase._language import LanguageSubsystem from babase._language import LanguageSubsystem
from babase._plugin import PluginSubsystem from babase._plugin import PluginSubsystem
@ -512,7 +510,7 @@ class App:
# Do the actual work of calcing our app-mode/etc. in a bg thread # Do the actual work of calcing our app-mode/etc. in a bg thread
# since it may block for a moment to load modules/etc. # since it may block for a moment to load modules/etc.
self.threadpool_submit_no_wait(tpartial(self._set_intent, intent)) self.threadpool_submit_no_wait(partial(self._set_intent, intent))
def push_apply_app_config(self) -> None: def push_apply_app_config(self) -> None:
"""Internal. Use app.config.apply() to apply app config changes.""" """Internal. Use app.config.apply() to apply app config changes."""
@ -644,13 +642,13 @@ class App:
# kick back to the logic thread to apply. # kick back to the logic thread to apply.
mode = modetype() mode = modetype()
_babase.pushcall( _babase.pushcall(
tpartial(self._apply_intent, intent, mode), partial(self._apply_intent, intent, mode),
from_other_thread=True, from_other_thread=True,
) )
except Exception: except Exception:
logging.exception('Error setting app intent to %s.', intent) logging.exception('Error setting app intent to %s.', intent)
_babase.pushcall( _babase.pushcall(
tpartial(self._display_set_intent_error, intent), partial(self._display_set_intent_error, intent),
from_other_thread=True, from_other_thread=True,
) )

View File

@ -7,10 +7,10 @@ import gc
import os import os
import logging import logging
from threading import Thread from threading import Thread
from functools import partial
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING, override from typing import TYPE_CHECKING, override
from efro.call import tpartial
from efro.log import LogLevel from efro.log import LogLevel
from efro.dataclassio import ioprepped, dataclass_to_json, dataclass_from_json from efro.dataclassio import ioprepped, dataclass_to_json, dataclass_from_json
@ -18,7 +18,7 @@ import _babase
from babase._appsubsystem import AppSubsystem from babase._appsubsystem import AppSubsystem
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, TextIO from typing import Any, TextIO, Callable
import babase import babase
@ -320,7 +320,7 @@ def dump_app_state(
# We want this to work from any thread, so need to kick this part # We want this to work from any thread, so need to kick this part
# over to the logic thread so timer works. # over to the logic thread so timer works.
_babase.pushcall( _babase.pushcall(
tpartial(_babase.apptimer, delay + 1.0, log_dumped_app_state), partial(_babase.apptimer, delay + 1.0, log_dumped_app_state),
from_other_thread=True, from_other_thread=True,
suppress_other_thread_warning=True, suppress_other_thread_warning=True,
) )

View File

@ -15,8 +15,8 @@ from efro.terminal import Clr
import _babase import _babase
if TYPE_CHECKING: if TYPE_CHECKING:
import functools
from typing import Any from typing import Any
from efro.call import Call as Call # 'as Call' so we re-export.
# Declare distinct types for different time measurements we use so the # Declare distinct types for different time measurements we use so the
@ -166,7 +166,7 @@ class _WeakCall:
if not self._did_invalid_call_warning: if not self._did_invalid_call_warning:
logging.warning( logging.warning(
'Warning: callable passed to babase.WeakCall() is not' 'Warning: callable passed to babase.WeakCall() is not'
' weak-referencable (%s); use babase.Call() instead' ' weak-referencable (%s); use functools.partial instead'
' to avoid this warning.', ' to avoid this warning.',
stack_info=True, stack_info=True,
) )
@ -199,9 +199,13 @@ class _Call:
The callable is strong-referenced so it won't die until this The callable is strong-referenced so it won't die until this
object does. object does.
WARNING: This is exactly the same as Python's built in functools.partial().
Use functools.partial instead of this for new code, as this will probably
be deprecated at some point.
Note that a bound method (ex: ``myobj.dosomething``) contains a reference Note that a bound method (ex: ``myobj.dosomething``) contains a reference
to ``self`` (``myobj`` in that case), so you will be keeping that object to ``self`` (``myobj`` in that case), so you will be keeping that object
alive too. Use babase.WeakCall if you want to pass a method to callback alive too. Use babase.WeakCall if you want to pass a method to a callback
without keeping its object alive. without keeping its object alive.
""" """
@ -239,12 +243,16 @@ class _Call:
if TYPE_CHECKING: if TYPE_CHECKING:
# Some interaction between our ballistica pylint plugin # For type-checking, point at functools.partial which gives us full
# and this code is crashing starting on pylint 2.15.0. # type checking on both positional and keyword arguments (as of mypy
# This seems to fix things for now. # 1.11).
# Note: Something here is wonky with pylint, possibly related to our
# custom pylint plugin. Disabling all checks seems to fix it.
# pylint: disable=all # pylint: disable=all
WeakCall = Call
Call = Call WeakCall = functools.partial
Call = functools.partial
else: else:
WeakCall = _WeakCall WeakCall = _WeakCall
WeakCall.__name__ = 'WeakCall' WeakCall.__name__ = 'WeakCall'

View File

@ -6,6 +6,7 @@ from __future__ import annotations
import time import time
import logging import logging
from functools import partial
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING, final, override from typing import TYPE_CHECKING, final, override
@ -162,8 +163,8 @@ class LoginAdapter:
the adapter will attempt to sign in if possible. An exception will the adapter will attempt to sign in if possible. An exception will
be returned if the sign-in attempt fails. be returned if the sign-in attempt fails.
""" """
assert _babase.in_logic_thread() assert _babase.in_logic_thread()
from babase._general import Call
# Have been seeing multiple sign-in attempts come through # Have been seeing multiple sign-in attempts come through
# nearly simultaneously which can be problematic server-side. # nearly simultaneously which can be problematic server-side.
@ -185,7 +186,7 @@ class LoginAdapter:
appnow, appnow,
) )
_babase.pushcall( _babase.pushcall(
Call( partial(
result_cb, result_cb,
self, self,
RuntimeError('sign_in called too soon after last.'), RuntimeError('sign_in called too soon after last.'),
@ -215,7 +216,7 @@ class LoginAdapter:
self.login_type.name, self.login_type.name,
) )
_babase.pushcall( _babase.pushcall(
Call( partial(
result_cb, result_cb,
self, self,
RuntimeError('fetch-sign-in-token failed.'), RuntimeError('fetch-sign-in-token failed.'),
@ -245,7 +246,7 @@ class LoginAdapter:
self.login_type.name, self.login_type.name,
response, response,
) )
_babase.pushcall(Call(result_cb, self, response)) _babase.pushcall(partial(result_cb, self, response))
else: else:
# This means our credentials were explicitly rejected. # This means our credentials were explicitly rejected.
if response.credentials is None: if response.credentials is None:
@ -262,7 +263,7 @@ class LoginAdapter:
result2 = self.SignInResult( result2 = self.SignInResult(
credentials=response.credentials credentials=response.credentials
) )
_babase.pushcall(Call(result_cb, self, result2)) _babase.pushcall(partial(result_cb, self, result2))
assert _babase.app.plus is not None assert _babase.app.plus is not None
_babase.app.plus.cloud.send_message_cb( _babase.app.plus.cloud.send_message_cb(
@ -293,10 +294,9 @@ class LoginAdapter:
as needed. The provided completion_cb should then be called with as needed. The provided completion_cb should then be called with
either a token or None if sign in failed or was cancelled. either a token or None if sign in failed or was cancelled.
""" """
from babase._general import Call
# Default implementation simply fails immediately. # Default implementation simply fails immediately.
_babase.pushcall(Call(completion_cb, None)) _babase.pushcall(partial(completion_cb, None))
def _update_implicit_login_state(self) -> None: def _update_implicit_login_state(self) -> None:
# If we've received an implicit login state, schedule it to be # If we've received an implicit login state, schedule it to be
@ -304,7 +304,6 @@ class LoginAdapter:
# called so that account-client-v2 has had a chance to load # called so that account-client-v2 has had a chance to load
# any existing state so it can properly respond to this. # any existing state so it can properly respond to this.
if self._implicit_login_state_dirty and self._on_app_loading_called: if self._implicit_login_state_dirty and self._on_app_loading_called:
from babase._general import Call
if DEBUG_LOG: if DEBUG_LOG:
logging.debug( logging.debug(
@ -315,7 +314,7 @@ class LoginAdapter:
assert _babase.app.plus is not None assert _babase.app.plus is not None
_babase.pushcall( _babase.pushcall(
Call( partial(
_babase.app.plus.accounts.on_implicit_login_state_changed, _babase.app.plus.accounts.on_implicit_login_state_changed,
self.login_type, self.login_type,
self._implicit_login_state, self._implicit_login_state,

View File

@ -7,12 +7,12 @@ from __future__ import annotations
import os import os
import time import time
import logging import logging
from threading import Thread
from pathlib import Path from pathlib import Path
from threading import Thread
from functools import partial
from typing import TYPE_CHECKING, TypeVar from typing import TYPE_CHECKING, TypeVar
from dataclasses import dataclass, field from dataclasses import dataclass, field
from efro.call import tpartial
import _babase import _babase
if TYPE_CHECKING: if TYPE_CHECKING:
@ -116,7 +116,7 @@ class MetadataSubsystem:
loading work happens, pass completion_cb_in_bg_thread=True. loading work happens, pass completion_cb_in_bg_thread=True.
""" """
Thread( Thread(
target=tpartial( target=partial(
self._load_exported_classes, self._load_exported_classes,
cls, cls,
completion_cb, completion_cb,
@ -145,7 +145,7 @@ class MetadataSubsystem:
except Exception: except Exception:
logging.exception('Error loading exported classes.') logging.exception('Error loading exported classes.')
completion_call = tpartial(completion_cb, classes) completion_call = partial(completion_cb, classes)
if completion_cb_in_bg_thread: if completion_cb_in_bg_thread:
completion_call() completion_call()
else: else:

View File

@ -9,9 +9,9 @@ import sys
import logging import logging
from pathlib import Path from pathlib import Path
from threading import Thread from threading import Thread
from functools import partial
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from efro.call import tpartial
from efro.error import CleanError from efro.error import CleanError
import _babase import _babase
import bacommon.cloud import bacommon.cloud
@ -118,7 +118,7 @@ class WorkspaceSubsystem:
state.iteration += 1 state.iteration += 1
_babase.pushcall( _babase.pushcall(
tpartial( partial(
self._successmsg, self._successmsg,
Lstr( Lstr(
resource='activatedText', resource='activatedText',
@ -130,7 +130,7 @@ class WorkspaceSubsystem:
except _SkipSyncError: except _SkipSyncError:
_babase.pushcall( _babase.pushcall(
tpartial( partial(
self._errmsg, self._errmsg,
Lstr( Lstr(
resource='workspaceSyncReuseText', resource='workspaceSyncReuseText',
@ -145,7 +145,7 @@ class WorkspaceSubsystem:
# be in wonky state. # be in wonky state.
set_path = False set_path = False
_babase.pushcall( _babase.pushcall(
tpartial(self._errmsg, Lstr(value=str(exc))), partial(self._errmsg, Lstr(value=str(exc))),
from_other_thread=True, from_other_thread=True,
) )
except Exception: except Exception:
@ -153,7 +153,7 @@ class WorkspaceSubsystem:
set_path = False set_path = False
logging.exception("Error syncing workspace '%s'.", workspacename) logging.exception("Error syncing workspace '%s'.", workspacename)
_babase.pushcall( _babase.pushcall(
tpartial( partial(
self._errmsg, self._errmsg,
Lstr( Lstr(
resource='workspaceSyncErrorText', resource='workspaceSyncErrorText',

View File

@ -52,7 +52,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21911 TARGET_BALLISTICA_BUILD = 21919
TARGET_BALLISTICA_VERSION = '1.7.36' TARGET_BALLISTICA_VERSION = '1.7.36'

View File

@ -100,6 +100,15 @@ class CloudSubsystem(babase.AppSubsystem):
], ],
) -> None: ... ) -> None: ...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.StoreQueryMessage,
on_response: Callable[
[bacommon.cloud.StoreQueryResponse | Exception], None
],
) -> None: ...
def send_message_cb( def send_message_cb(
self, self,
msg: Message, msg: Message,
@ -194,6 +203,10 @@ def cloud_console_exec(code: str) -> None:
except Exception: except Exception:
import traceback import traceback
# Note to self: Seems like we should just use
# logging.exception() here. Except currently that winds up
# triggering our cloud logging stuff so we'd probably want a
# specific logger or whatnot to avoid that.
apptime = babase.apptime() apptime = babase.apptime()
print(f'Exec error at time {apptime:.2f}.', file=sys.stderr) print(f'Exec error at time {apptime:.2f}.', file=sys.stderr)
traceback.print_exc() traceback.print_exc()

View File

@ -254,6 +254,11 @@ class PlusSubsystem(AppSubsystem):
"""(internal)""" """(internal)"""
return _baplus.tournament_query(callback, args) return _baplus.tournament_query(callback, args)
@staticmethod
def supports_purchases() -> bool:
"""Does this platform support in-app-purchases?"""
return _baplus.supports_purchases()
@staticmethod @staticmethod
def have_incentivized_ad() -> bool: def have_incentivized_ad() -> bool:
"""Is an incentivized ad available?""" """Is an incentivized ad available?"""

View File

@ -119,10 +119,10 @@ class ConfigNumberEdit:
self.nametext = bui.textwidget( self.nametext = bui.textwidget(
parent=parent, parent=parent,
position=position, position=(position[0], position[1] + 12.0),
size=(100, 30), size=(0, 0),
text=displayname, text=displayname,
# maxwidth=160 + xoffset, maxwidth=150 + xoffset,
color=(0.8, 0.8, 0.8, 1.0), color=(0.8, 0.8, 0.8, 1.0),
h_align='left', h_align='left',
v_align='center', v_align='center',
@ -130,8 +130,8 @@ class ConfigNumberEdit:
) )
self.valuetext = bui.textwidget( self.valuetext = bui.textwidget(
parent=parent, parent=parent,
position=(position[0] + 146 + xoffset, position[1]), position=(position[0] + 216 + xoffset, position[1] + 12.0),
size=(60, 28), size=(0, 0),
editable=False, editable=False,
color=(0.3, 1.0, 0.3, 1.0), color=(0.3, 1.0, 0.3, 1.0),
h_align='right', h_align='right',

View File

@ -28,8 +28,10 @@ def wait_for_connectivity(
assert plus is not None assert plus is not None
# Quick-out: if we're already connected, don't bother with the UI. # Quick-out: if we're already connected, don't bother with the UI.
# We do, however, push this call instead of calling it immediately
# so as to be consistent with the waiting path.
if plus.cloud.connected: if plus.cloud.connected:
on_connected() bui.pushcall(on_connected)
return return
WaitForConnectivityWindow(on_connected=on_connected, on_cancel=on_cancel) WaitForConnectivityWindow(on_connected=on_connected, on_cancel=on_cancel)

View File

@ -214,7 +214,7 @@ class ContinuesWindow(bui.Window):
self._on_cancel() self._on_cancel()
def _on_continue_press(self) -> None: def _on_continue_press(self) -> None:
from bauiv1lib import getcurrency from bauiv1lib import gettickets
plus = bui.app.plus plus = bui.app.plus
assert plus is not None assert plus is not None
@ -238,7 +238,7 @@ class ContinuesWindow(bui.Window):
self._counting_down = False self._counting_down = False
bui.textwidget(edit=self._counter_text, text='') bui.textwidget(edit=self._counter_text, text='')
bui.getsound('error').play() bui.getsound('error').play()
getcurrency.show_get_tickets_prompt() gettickets.show_get_tickets_prompt()
return return
if not self._transitioning_out: if not self._transitioning_out:
bui.getsound('swish').play() bui.getsound('swish').play()

View File

@ -290,6 +290,7 @@ class TournamentButton:
text='-', text='-',
v_align='center', v_align='center',
maxwidth=170, maxwidth=170,
glow_type='uniform',
scale=1.4, scale=1.4,
color=value_color, color=value_color,
flatness=1.0, flatness=1.0,

View File

@ -20,7 +20,7 @@ from bacommon.net import (
PrivatePartyConnectResult, PrivatePartyConnectResult,
) )
from bauiv1lib.gather import GatherTab from bauiv1lib.gather import GatherTab
from bauiv1lib.getcurrency import GetCurrencyWindow, show_get_tickets_prompt from bauiv1lib.gettickets import GetTicketsWindow, show_get_tickets_prompt
import bascenev1 as bs import bascenev1 as bs
import bauiv1 as bui import bauiv1 as bui
@ -487,7 +487,7 @@ class PrivateGatherTab(GatherTab):
# Bring up get-tickets window and then kill ourself (we're on the # Bring up get-tickets window and then kill ourself (we're on the
# overlay layer so we'd show up above it). # overlay layer so we'd show up above it).
GetCurrencyWindow(modal=True, origin_widget=self._get_tickets_button) GetTicketsWindow(modal=True, origin_widget=self._get_tickets_button)
def _build_host_tab(self) -> None: def _build_host_tab(self) -> None:
# pylint: disable=too-many-branches # pylint: disable=too-many-branches

View File

@ -14,8 +14,8 @@ if TYPE_CHECKING:
from typing import Any from typing import Any
class GetCurrencyWindow(bui.Window): class GetTicketsWindow(bui.Window):
"""Window for purchasing/acquiring currency.""" """Window for purchasing/acquiring classic tickets."""
def __init__( def __init__(
self, self,
@ -97,14 +97,42 @@ class GetCurrencyWindow(bui.Window):
bui.textwidget( bui.textwidget(
parent=self._root_widget, parent=self._root_widget,
position=(self._width * 0.5, self._height - 55), position=(self._width * 0.5 - 15, self._height - 47),
size=(0, 0), size=(0, 0),
color=bui.app.ui_v1.title_color, color=bui.app.ui_v1.title_color,
scale=1.2, scale=1.2,
h_align='center', h_align='right',
v_align='center', v_align='center',
text=bui.Lstr(resource=self._r + '.titleText'), text=bui.Lstr(resource=self._r + '.titleText'),
maxwidth=290, # text='Testing really long text here blah blah',
maxwidth=260,
)
# Get Tokens button
bui.buttonwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height - 72),
color=(0.65, 0.5, 0.7),
textcolor=bui.app.ui_v1.title_color,
size=(190, 50),
autoselect=True,
label='Get Tokens',
on_activate_call=self._get_tokens_press,
)
# 'New!' by tokens button
bui.textwidget(
parent=self._root_widget,
text='New!',
position=(self._width * 0.5 + 25, self._height - 32),
size=(0, 0),
color=(1, 1, 0, 1.0),
rotate=22,
shadow=1.0,
maxwidth=150,
h_align='center',
v_align='center',
scale=0.7,
) )
if not modal: if not modal:
@ -437,7 +465,7 @@ class GetCurrencyWindow(bui.Window):
tc_y_offs = 0 tc_y_offs = 0
h = self._width - (185 + x_inset) h = self._width - (185 + x_inset)
v = self._height - 95 + tc_y_offs v = self._height - 105 + tc_y_offs
txt1 = ( txt1 = (
bui.Lstr(resource=self._r + '.youHaveText') bui.Lstr(resource=self._r + '.youHaveText')
@ -733,6 +761,41 @@ class GetCurrencyWindow(bui.Window):
else: else:
plus.purchase(item) plus.purchase(item)
def _get_tokens_press(self) -> None:
from functools import partial
from bauiv1lib.gettokens import GetTokensWindow
# No-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
if self._transitioning_out:
return
bui.containerwidget(edit=self._root_widget, transition='out_left')
# Note: Make sure we don't pass anything here that would
# capture 'self'. (a lambda would implicitly do this by capturing
# the stack frame).
restorecall = partial(
_restore_get_tickets_window,
self._modal,
self._from_modal_store,
self._store_back_location,
)
window = GetTokensWindow(
transition='in_right',
restore_previous_call=restorecall,
).get_root_widget()
if not self._modal and not self._from_modal_store:
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
window, from_window=self._root_widget
)
self._transitioning_out = True
def _back(self) -> None: def _back(self) -> None:
from bauiv1lib.store import browser from bauiv1lib.store import browser
@ -760,6 +823,27 @@ class GetCurrencyWindow(bui.Window):
self._transitioning_out = True self._transitioning_out = True
# A call we can bundle up and pass to windows we open; allows them to
# get back to us without having to explicitly know about us.
def _restore_get_tickets_window(
modal: bool,
from_modal_store: bool,
store_back_location: str | None,
from_window: bui.Widget,
) -> None:
restored = GetTicketsWindow(
transition='in_left',
modal=modal,
from_modal_store=from_modal_store,
store_back_location=store_back_location,
)
if not modal and not from_modal_store:
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
restored.get_root_widget(), from_window=from_window
)
def show_get_tickets_prompt() -> None: def show_get_tickets_prompt() -> None:
"""Show a 'not enough tickets' prompt with an option to purchase more. """Show a 'not enough tickets' prompt with an option to purchase more.
@ -769,6 +853,7 @@ def show_get_tickets_prompt() -> None:
from bauiv1lib.confirm import ConfirmWindow from bauiv1lib.confirm import ConfirmWindow
assert bui.app.classic is not None assert bui.app.classic is not None
if bui.app.classic.allow_ticket_purchases: if bui.app.classic.allow_ticket_purchases:
ConfirmWindow( ConfirmWindow(
bui.Lstr( bui.Lstr(
@ -777,7 +862,7 @@ def show_get_tickets_prompt() -> None:
'You don\'t have enough tickets for this!', 'You don\'t have enough tickets for this!',
) )
), ),
lambda: GetCurrencyWindow(modal=True), lambda: GetTicketsWindow(modal=True),
ok_text=bui.Lstr(resource='getTicketsWindow.titleText'), ok_text=bui.Lstr(resource='getTicketsWindow.titleText'),
width=460, width=460,
height=130, height=130,

View File

@ -0,0 +1,618 @@
# Released under the MIT License. See LICENSE for details.
#
"""UI functionality for purchasing/acquiring currency."""
from __future__ import annotations
import logging
from enum import Enum
from functools import partial
from dataclasses import dataclass
from typing import TYPE_CHECKING, assert_never
import bacommon.cloud
import bauiv1 as bui
from bauiv1lib.connectivity import wait_for_connectivity
if TYPE_CHECKING:
from typing import Any, Callable
@dataclass
class _ButtonDef:
itemid: str
width: float
color: tuple[float, float, float]
imgdefs: list[_ImgDef]
txtdefs: list[_TxtDef]
prepad: float = 0.0
@dataclass
class _ImgDef:
tex: str
pos: tuple[float, float]
size: tuple[float, float]
color: tuple[float, float, float] = (1, 1, 1)
opacity: float = 1.0
draw_controller_mult: float | None = None
class TextContents(Enum):
"""Some type of text to show."""
PRICE = 'price'
@dataclass
class _TxtDef:
text: str | TextContents
pos: tuple[float, float]
maxwidth: float | None
scale: float = 1.0
color: tuple[float, float, float] = (1, 1, 1)
rotate: float | None = None
class GetTokensWindow(bui.Window):
"""Window for purchasing/acquiring classic tickets."""
def __del__(self) -> None:
print('~GetTokensWindow()')
def __init__(
self,
transition: str = 'in_right',
origin_widget: bui.Widget | None = None,
restore_previous_call: Callable[[bui.Widget], None] | None = None,
):
# pylint: disable=too-many-locals
bwidthstd = 170
bwidthwide = 300
ycolor = (0, 0, 0.3)
pcolor = (0, 0, 0.3)
pos1 = 65
pos2 = 34
titlescale = 0.9
pricescale = 0.65
bcapcol1 = (0.25, 0.13, 0.02)
self._buttondefs: list[_ButtonDef] = [
_ButtonDef(
itemid='tokens1',
width=bwidthstd,
color=ycolor,
imgdefs=[
_ImgDef(
'tokens1',
pos=(-3, 85),
size=(172, 172),
opacity=1.0,
draw_controller_mult=0.5,
),
_ImgDef(
'windowBottomCap',
pos=(1.5, 4),
size=(bwidthstd * 0.960, 100),
color=bcapcol1,
opacity=1.0,
),
],
txtdefs=[
_TxtDef(
'50 Tokens',
pos=(bwidthstd * 0.5, pos1),
color=(1.1, 1.05, 1.0),
scale=titlescale,
maxwidth=bwidthstd * 0.9,
),
_TxtDef(
TextContents.PRICE,
pos=(bwidthstd * 0.5, pos2),
color=(1.1, 1.05, 1.0),
scale=pricescale,
maxwidth=bwidthstd * 0.9,
),
],
),
_ButtonDef(
itemid='tokens2',
width=bwidthstd,
color=ycolor,
imgdefs=[
_ImgDef(
'tokens2',
pos=(-3, 85),
size=(172, 172),
opacity=1.0,
draw_controller_mult=0.5,
),
_ImgDef(
'windowBottomCap',
pos=(1.5, 4),
size=(bwidthstd * 0.960, 100),
color=bcapcol1,
opacity=1.0,
),
],
txtdefs=[
_TxtDef(
'500 Tokens',
pos=(bwidthstd * 0.5, pos1),
color=(1.1, 1.05, 1.0),
scale=titlescale,
maxwidth=bwidthstd * 0.9,
),
_TxtDef(
TextContents.PRICE,
pos=(bwidthstd * 0.5, pos2),
color=(1.1, 1.05, 1.0),
scale=pricescale,
maxwidth=bwidthstd * 0.9,
),
],
),
_ButtonDef(
itemid='tokens3',
width=bwidthstd,
color=ycolor,
imgdefs=[
_ImgDef(
'tokens3',
pos=(-3, 85),
size=(172, 172),
opacity=1.0,
draw_controller_mult=0.5,
),
_ImgDef(
'windowBottomCap',
pos=(1.5, 4),
size=(bwidthstd * 0.960, 100),
color=bcapcol1,
opacity=1.0,
),
],
txtdefs=[
_TxtDef(
'1200 Tokens',
pos=(bwidthstd * 0.5, pos1),
color=(1.1, 1.05, 1.0),
scale=titlescale,
maxwidth=bwidthstd * 0.9,
),
_TxtDef(
TextContents.PRICE,
pos=(bwidthstd * 0.5, pos2),
color=(1.1, 1.05, 1.0),
scale=pricescale,
maxwidth=bwidthstd * 0.9,
),
],
),
_ButtonDef(
itemid='tokens4',
width=bwidthstd,
color=ycolor,
imgdefs=[
_ImgDef(
'tokens4',
pos=(-3, 85),
size=(172, 172),
opacity=1.0,
draw_controller_mult=0.5,
),
_ImgDef(
'windowBottomCap',
pos=(1.5, 4),
size=(bwidthstd * 0.960, 100),
color=bcapcol1,
opacity=1.0,
),
],
txtdefs=[
_TxtDef(
'2600 Tokens',
pos=(bwidthstd * 0.5, pos1),
color=(1.1, 1.05, 1.0),
scale=titlescale,
maxwidth=bwidthstd * 0.9,
),
_TxtDef(
TextContents.PRICE,
pos=(bwidthstd * 0.5, pos2),
color=(1.1, 1.05, 1.0),
scale=pricescale,
maxwidth=bwidthstd * 0.9,
),
],
),
_ButtonDef(
itemid='gold_pass',
width=bwidthwide,
color=pcolor,
imgdefs=[
_ImgDef(
'goldPass',
pos=(-7, 102),
size=(312, 156),
draw_controller_mult=0.3,
),
_ImgDef(
'windowBottomCap',
pos=(8, 4),
size=(bwidthwide * 0.923, 116),
color=(0.25, 0.12, 0.15),
opacity=1.0,
),
],
txtdefs=[
_TxtDef(
'Gold Pass',
pos=(bwidthwide * 0.5, pos1 + 27),
color=(1.1, 1.05, 1.0),
scale=titlescale,
maxwidth=bwidthwide * 0.8,
),
_TxtDef(
'Infinite tokens.',
pos=(bwidthwide * 0.5, pos1 + 6),
color=(1.1, 1.05, 1.0),
scale=0.4,
maxwidth=bwidthwide * 0.8,
),
_TxtDef(
'No ads.',
pos=(bwidthwide * 0.5, pos1 + 6 - 13 * 1),
color=(1.1, 1.05, 1.0),
scale=0.4,
maxwidth=bwidthwide * 0.8,
),
_TxtDef(
'Forever.',
pos=(bwidthwide * 0.5, pos1 + 6 - 13 * 2),
color=(1.1, 1.05, 1.0),
scale=0.4,
maxwidth=bwidthwide * 0.8,
),
_TxtDef(
TextContents.PRICE,
pos=(bwidthwide * 0.5, pos2 - 9),
color=(1.1, 1.05, 1.0),
scale=pricescale,
maxwidth=bwidthwide * 0.8,
),
],
prepad=-8,
),
]
self._transitioning_out = False
self._restore_previous_call = restore_previous_call
self._textcolor = (0.92, 0.92, 2.0)
# If they provided an origin-widget, scale up from that.
scale_origin: tuple[float, float] | None
if origin_widget is not None:
self._transition_out = 'out_scale'
scale_origin = origin_widget.get_screen_space_center()
transition = 'in_scale'
else:
self._transition_out = 'out_right'
scale_origin = None
uiscale = bui.app.ui_v1.uiscale
self._width = 1000.0 if uiscale is bui.UIScale.SMALL else 800.0
self._x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
self._height = 480.0
self._r = 'getTokensWindow'
top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height + top_extra),
transition=transition,
scale_origin_stack_offset=scale_origin,
color=(0.3, 0.23, 0.36),
scale=(
1.63
if uiscale is bui.UIScale.SMALL
else 1.2 if uiscale is bui.UIScale.MEDIUM else 1.0
),
stack_offset=(
(0, -3) if uiscale is bui.UIScale.SMALL else (0, 0)
),
)
)
self._back_button = btn = bui.buttonwidget(
parent=self._root_widget,
position=(45 + self._x_inset, self._height - 80),
size=(
(140, 60) if self._restore_previous_call is None else (60, 60)
),
scale=1.0,
autoselect=True,
label=(
bui.Lstr(resource='doneText')
if self._restore_previous_call is None
else bui.charstr(bui.SpecialChar.BACK)
),
button_type=(
'regular'
if self._restore_previous_call is None
else 'backSmall'
),
on_activate_call=self._back,
)
bui.containerwidget(edit=self._root_widget, cancel_button=btn)
bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height - 47),
size=(0, 0),
color=self._textcolor,
flatness=0.0,
shadow=1.0,
scale=1.2,
h_align='center',
v_align='center',
text='Get Tokens',
maxwidth=260,
)
self._status_text = bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height * 0.5),
h_align='center',
v_align='center',
color=(0.6, 0.6, 0.6),
scale=0.75,
text='Loading...',
)
# Get all textures used by our buttons preloading so hopefully
# they'll be in place by the time we show them.
for bdef in self._buttondefs:
for bimg in bdef.imgdefs:
bui.gettexture(bimg.tex)
# Wait for a master-server connection if need be. Otherwise we
# could error if called at the wrong time even with an internet
# connection, which is unintuitive.
wait_for_connectivity(
on_connected=bui.WeakCall(self._on_have_connectivity),
on_cancel=bui.WeakCall(self._back),
)
def _on_have_connectivity(self) -> None:
plus = bui.app.plus
assert plus is not None
# Sanity check; we need to be signed in. (we should not be
# allowed to get here if we aren't, but it could happen for
# fluke-ish reasons.)
if plus.accounts.primary is None:
bui.screenmessage(
bui.Lstr(resource='notSignedInErrorText'), color=(1, 0, 0)
)
bui.getsound('error').play()
self._back()
return
with plus.accounts.primary:
plus.cloud.send_message_cb(
bacommon.cloud.StoreQueryMessage(),
on_response=bui.WeakCall(self._on_store_query_response),
)
def _on_load_error(self) -> None:
bui.textwidget(
edit=self._status_text,
text=bui.Lstr(resource='internal.unavailableNoConnectionText'),
color=(1, 0, 0),
)
def _on_store_query_response(
self, response: bacommon.cloud.StoreQueryResponse | Exception
) -> None:
# pylint: disable=too-many-locals
plus = bui.app.plus
# If our message failed, just error and back out.
if isinstance(response, Exception):
logging.info('Store query failed.', exc_info=response)
bui.screenmessage(bui.Lstr(resource='errorText'), color=(1, 0, 0))
bui.getsound('error').play()
self._back()
return
bui.textwidget(edit=self._status_text, text='')
xinset = 40
scrollwidth = self._width - 2 * (self._x_inset + xinset)
scrollheight = 280
buttonpadding = -5
yoffs = 5
# We currently don't handle the zero-button case.
assert self._buttondefs
total_button_width = sum(
b.width + b.prepad for b in self._buttondefs
) + buttonpadding * (len(self._buttondefs) - 1)
h_scroll = bui.hscrollwidget(
parent=self._root_widget,
size=(scrollwidth, scrollheight),
position=(self._x_inset + xinset, 45),
claims_left_right=True,
highlight=False,
border_opacity=0.25,
)
subcontainer = bui.containerwidget(
parent=h_scroll,
background=False,
size=(max(total_button_width, scrollwidth), scrollheight),
)
tinfobtn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
label='Learn More',
position=(self._width * 0.5 - 75, self._height * 0.703),
size=(180, 43),
scale=0.8,
color=(0.4, 0.25, 0.5),
textcolor=self._textcolor,
on_activate_call=partial(
self._on_learn_more_press, response.token_info_url
),
)
x = 0.0
bwidgets: list[bui.Widget] = []
for i, buttondef in enumerate(self._buttondefs):
price = None if plus is None else plus.get_price(buttondef.itemid)
x += buttondef.prepad
tdelay = 0.3 - i / len(self._buttondefs) * 0.25
btn = bui.buttonwidget(
autoselect=True,
label='',
color=buttondef.color,
transition_delay=tdelay,
up_widget=tinfobtn,
parent=subcontainer,
size=(buttondef.width, 275),
position=(x, -10 + yoffs),
button_type='square',
on_activate_call=partial(
self._purchase_press, buttondef.itemid
),
)
bwidgets.append(btn)
for imgdef in buttondef.imgdefs:
_img = bui.imagewidget(
parent=subcontainer,
size=imgdef.size,
position=(x + imgdef.pos[0], imgdef.pos[1] + yoffs),
draw_controller=btn,
draw_controller_mult=imgdef.draw_controller_mult,
color=imgdef.color,
texture=bui.gettexture(imgdef.tex),
transition_delay=tdelay,
opacity=imgdef.opacity,
)
for txtdef in buttondef.txtdefs:
txt: bui.Lstr | str
if isinstance(txtdef.text, TextContents):
if txtdef.text is TextContents.PRICE:
tcolor = (
(1, 1, 1, 0.5) if price is None else txtdef.color
)
txt = (
bui.Lstr(resource='unavailableText')
if price is None
else price
)
else:
# Make sure we cover all cases.
assert_never(txtdef.text)
else:
tcolor = txtdef.color
txt = txtdef.text
_txt = bui.textwidget(
parent=subcontainer,
text=txt,
position=(x + txtdef.pos[0], txtdef.pos[1] + yoffs),
size=(0, 0),
scale=txtdef.scale,
h_align='center',
v_align='center',
draw_controller=btn,
color=tcolor,
transition_delay=tdelay,
flatness=0.0,
shadow=1.0,
rotate=txtdef.rotate,
maxwidth=txtdef.maxwidth,
)
x += buttondef.width + buttonpadding
bui.containerwidget(edit=subcontainer, visible_child=bwidgets[0])
_tinfotxt = bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height * 0.812),
color=self._textcolor,
shadow=1.0,
scale=0.7,
size=(0, 0),
h_align='center',
v_align='center',
text='BombSquad\'s shiny new currency.',
)
_tnumtxt = bui.textwidget(
parent=self._root_widget,
position=(self._width - self._x_inset - 120.0, self._height - 48),
color=(2.0, 0.7, 0.0),
shadow=1.0,
flatness=0.0,
size=(0, 0),
h_align='left',
v_align='center',
text=str(response.tokens),
)
_tlabeltxt = bui.textwidget(
parent=self._root_widget,
position=(self._width - self._x_inset - 123.0, self._height - 48),
size=(0, 0),
h_align='right',
v_align='center',
text=bui.charstr(bui.SpecialChar.TOKEN),
)
def _purchase_press(self, itemid: str) -> None:
plus = bui.app.plus
price = None if plus is None else plus.get_price(itemid)
if price is None:
if plus is not None and plus.supports_purchases():
# Looks like internet is down or something temporary.
errmsg = 'This purchase is not available.'
else:
# Looks like purchases will never work here.
errmsg = (
'Sorry, purchases are not available on this build.\n'
'As a fallback, sign in to this account on another'
' platform and make purchases from there.'
)
bui.screenmessage(errmsg, color=(1, 0.5, 0))
bui.getsound('error').play()
return
print(f'WOULD PURCHASE {itemid}')
def _back(self) -> None:
# No-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
bui.containerwidget(
edit=self._root_widget, transition=self._transition_out
)
if self._restore_previous_call is not None:
self._restore_previous_call(self._root_widget)
def _on_learn_more_press(self, url: str) -> None:
bui.open_url(url)

View File

@ -39,7 +39,8 @@ class MainMenuWindow(bui.Window):
bui.set_analytics_screen('Main Menu') bui.set_analytics_screen('Main Menu')
self._show_remote_app_info_on_first_launch() self._show_remote_app_info_on_first_launch()
# Make a vanilla container; we'll modify it to our needs in refresh. # Make a vanilla container; we'll modify it to our needs in
# refresh.
super().__init__( super().__init__(
root_widget=bui.containerwidget( root_widget=bui.containerwidget(
transition=transition, transition=transition,
@ -91,7 +92,6 @@ class MainMenuWindow(bui.Window):
0.27, bui.WeakCall(self._check_refresh), repeat=True 0.27, bui.WeakCall(self._check_refresh), repeat=True
) )
# noinspection PyUnresolvedReferences
@staticmethod @staticmethod
def _preload_modules() -> None: def _preload_modules() -> None:
"""Preload modules we use; avoids hitches (called in bg thread).""" """Preload modules we use; avoids hitches (called in bg thread)."""
@ -162,8 +162,9 @@ class MainMenuWindow(bui.Window):
if now < self._next_refresh_allow_time: if now < self._next_refresh_allow_time:
return return
# Don't refresh for the first few seconds the game is up so we don't # Don't refresh for the first few seconds the game is up so we
# interrupt the transition in. # don't interrupt the transition in.
# bui.app.main_menu_window_refresh_check_count += 1 # bui.app.main_menu_window_refresh_check_count += 1
# if bui.app.main_menu_window_refresh_check_count < 4: # if bui.app.main_menu_window_refresh_check_count < 4:
# return # return
@ -413,8 +414,8 @@ class MainMenuWindow(bui.Window):
else: else:
self._quit_button = None self._quit_button = None
# If we're not in-game, have no quit button, and this is android, # If we're not in-game, have no quit button, and this is
# we want back presses to quit our activity. # android, we want back presses to quit our activity.
if ( if (
not self._in_game not self._in_game
and not self._have_quit_button and not self._have_quit_button
@ -428,9 +429,9 @@ class MainMenuWindow(bui.Window):
edit=self._root_widget, on_cancel_call=_do_quit edit=self._root_widget, on_cancel_call=_do_quit
) )
# Add speed-up/slow-down buttons for replays. # Add speed-up/slow-down buttons for replays. Ideally this
# (ideally this should be part of a fading-out playback bar like most # should be part of a fading-out playback bar like most media
# media players but this works for now). # players but this works for now.
if bs.is_in_replay(): if bs.is_in_replay():
b_size = 50.0 b_size = 50.0
b_buffer_1 = 50.0 b_buffer_1 = 50.0
@ -605,13 +606,13 @@ class MainMenuWindow(bui.Window):
def _set_allow_time() -> None: def _set_allow_time() -> None:
self._next_refresh_allow_time = bui.apptime() + 2.5 self._next_refresh_allow_time = bui.apptime() + 2.5
# Slight hack: widget transitions currently only progress when # Slight hack: widget transitions currently only progress
# frames are being drawn, but this tends to get called before # when frames are being drawn, but this tends to get called
# frame drawing even starts, meaning we don't know exactly how # before frame drawing even starts, meaning we don't know
# long we should wait before refreshing to avoid interrupting # exactly how long we should wait before refreshing to avoid
# the transition. To make things a bit better, let's do a # interrupting the transition. To make things a bit better,
# redundant set of the time in a deferred call which hopefully # let's do a redundant set of the time in a deferred call
# happens closer to actual frame draw times. # which hopefully happens closer to actual frame draw times.
_set_allow_time() _set_allow_time()
bui.pushcall(_set_allow_time) bui.pushcall(_set_allow_time)

View File

@ -564,7 +564,7 @@ class PartyQueueWindow(bui.Window):
def on_boost_press(self) -> None: def on_boost_press(self) -> None:
"""Boost was pressed.""" """Boost was pressed."""
from bauiv1lib import account from bauiv1lib import account
from bauiv1lib import getcurrency from bauiv1lib import gettickets
plus = bui.app.plus plus = bui.app.plus
assert plus is not None assert plus is not None
@ -575,7 +575,7 @@ class PartyQueueWindow(bui.Window):
if plus.get_v1_account_ticket_count() < self._boost_tickets: if plus.get_v1_account_ticket_count() < self._boost_tickets:
bui.getsound('error').play() bui.getsound('error').play()
getcurrency.show_get_tickets_prompt() gettickets.show_get_tickets_prompt()
return return
bui.getsound('laserReverse').play() bui.getsound('laserReverse').play()

View File

@ -545,8 +545,6 @@ class EditProfileWindow(bui.Window):
for n in [ for n in [
bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO, bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO,
bui.SpecialChar.GAME_CENTER_LOGO, bui.SpecialChar.GAME_CENTER_LOGO,
bui.SpecialChar.GAME_CIRCLE_LOGO,
bui.SpecialChar.OUYA_LOGO,
bui.SpecialChar.LOCAL_ACCOUNT, bui.SpecialChar.LOCAL_ACCOUNT,
bui.SpecialChar.OCULUS_LOGO, bui.SpecialChar.OCULUS_LOGO,
bui.SpecialChar.NVIDIA_LOGO, bui.SpecialChar.NVIDIA_LOGO,

View File

@ -210,7 +210,7 @@ class ProfileUpgradeWindow(bui.Window):
) )
def _on_upgrade_press(self) -> None: def _on_upgrade_press(self) -> None:
from bauiv1lib import getcurrency from bauiv1lib import gettickets
if self._status is None: if self._status is None:
plus = bui.app.plus plus = bui.app.plus
@ -220,7 +220,7 @@ class ProfileUpgradeWindow(bui.Window):
tickets = plus.get_v1_account_ticket_count() tickets = plus.get_v1_account_ticket_count()
if tickets < self._cost: if tickets < self._cost:
bui.getsound('error').play() bui.getsound('error').play()
getcurrency.show_get_tickets_prompt() gettickets.show_get_tickets_prompt()
return return
bui.screenmessage( bui.screenmessage(
bui.Lstr(resource='purchasingText'), color=(0, 1, 0) bui.Lstr(resource='purchasingText'), color=(0, 1, 0)

View File

@ -162,7 +162,7 @@ class PurchaseWindow(bui.Window):
bui.containerwidget(edit=self._root_widget, transition='out_left') bui.containerwidget(edit=self._root_widget, transition='out_left')
def _purchase(self) -> None: def _purchase(self) -> None:
from bauiv1lib import getcurrency from bauiv1lib import gettickets
plus = bui.app.plus plus = bui.app.plus
assert plus is not None assert plus is not None
@ -176,7 +176,7 @@ class PurchaseWindow(bui.Window):
except Exception: except Exception:
ticket_count = None ticket_count = None
if ticket_count is not None and ticket_count < self._price: if ticket_count is not None and ticket_count < self._price:
getcurrency.show_get_tickets_prompt() gettickets.show_get_tickets_prompt()
bui.getsound('error').play() bui.getsound('error').play()
return return

View File

@ -440,7 +440,7 @@ class SpecialOfferWindow(bui.Window):
def _on_get_more_tickets_press(self) -> None: def _on_get_more_tickets_press(self) -> None:
from bauiv1lib import account from bauiv1lib import account
from bauiv1lib import getcurrency from bauiv1lib import gettickets
plus = bui.app.plus plus = bui.app.plus
assert plus is not None assert plus is not None
@ -448,10 +448,10 @@ class SpecialOfferWindow(bui.Window):
if plus.get_v1_account_state() != 'signed_in': if plus.get_v1_account_state() != 'signed_in':
account.show_sign_in_prompt() account.show_sign_in_prompt()
return return
getcurrency.GetCurrencyWindow(modal=True).get_root_widget() gettickets.GetTicketsWindow(modal=True).get_root_widget()
def _purchase(self) -> None: def _purchase(self) -> None:
from bauiv1lib import getcurrency from bauiv1lib import gettickets
from bauiv1lib import confirm from bauiv1lib import confirm
plus = bui.app.plus plus = bui.app.plus
@ -474,7 +474,7 @@ class SpecialOfferWindow(bui.Window):
except Exception: except Exception:
ticket_count = None ticket_count = None
if ticket_count is not None and ticket_count < self._offer['price']: if ticket_count is not None and ticket_count < self._offer['price']:
getcurrency.show_get_tickets_prompt() gettickets.show_get_tickets_prompt()
bui.getsound('error').play() bui.getsound('error').play()
return return

View File

@ -574,7 +574,7 @@ class StoreBrowserWindow(bui.Window):
"""Attempt to purchase the provided item.""" """Attempt to purchase the provided item."""
from bauiv1lib import account from bauiv1lib import account
from bauiv1lib.confirm import ConfirmWindow from bauiv1lib.confirm import ConfirmWindow
from bauiv1lib import getcurrency from bauiv1lib import gettickets
assert bui.app.classic is not None assert bui.app.classic is not None
store = bui.app.classic.store store = bui.app.classic.store
@ -620,7 +620,7 @@ class StoreBrowserWindow(bui.Window):
our_tickets = plus.get_v1_account_ticket_count() our_tickets = plus.get_v1_account_ticket_count()
if price is not None and our_tickets < price: if price is not None and our_tickets < price:
bui.getsound('error').play() bui.getsound('error').play()
getcurrency.show_get_tickets_prompt() gettickets.show_get_tickets_prompt()
else: else:
def do_it() -> None: def do_it() -> None:
@ -1320,7 +1320,7 @@ class StoreBrowserWindow(bui.Window):
def _on_get_more_tickets_press(self) -> None: def _on_get_more_tickets_press(self) -> None:
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from bauiv1lib.account import show_sign_in_prompt from bauiv1lib.account import show_sign_in_prompt
from bauiv1lib.getcurrency import GetCurrencyWindow from bauiv1lib.gettickets import GetTicketsWindow
# no-op if our underlying widget is dead or on its way out. # no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out: if not self._root_widget or self._root_widget.transitioning_out:
@ -1334,7 +1334,7 @@ class StoreBrowserWindow(bui.Window):
return return
self._save_state() self._save_state()
bui.containerwidget(edit=self._root_widget, transition='out_left') bui.containerwidget(edit=self._root_widget, transition='out_left')
window = GetCurrencyWindow( window = GetTicketsWindow(
from_modal_store=self._modal, from_modal_store=self._modal,
store_back_location=self._back_location, store_back_location=self._back_location,
).get_root_widget() ).get_root_widget()
@ -1391,7 +1391,7 @@ def _check_merch_availability_in_bg_thread() -> None:
def _store_in_logic_thread() -> None: def _store_in_logic_thread() -> None:
cfg = bui.app.config cfg = bui.app.config
current: str | None = cfg.get(MERCH_LINK_KEY) current = cfg.get(MERCH_LINK_KEY)
if not isinstance(current, str | None): if not isinstance(current, str | None):
current = None current = None
if current != response.url: if current != response.url:
@ -1414,6 +1414,9 @@ def _check_merch_availability_in_bg_thread() -> None:
# Slight hack; start checking merch availability in the bg (but only if # Slight hack; start checking merch availability in the bg (but only if
# it looks like we've been imported for use in a running app; don't want # it looks like we've been imported for use in a running app; don't want
# to do this during docs generation/etc.) # to do this during docs generation/etc.)
# TODO: Should wire this up explicitly to app bootstrapping; not good to
# be kicking off work at module import time.
if ( if (
os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') != '1' os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') != '1'
and bui.app.state is not bui.app.State.NOT_STARTED and bui.app.state is not bui.app.State.NOT_STARTED

View File

@ -632,7 +632,7 @@ class TournamentEntryWindow(PopupWindow):
bui.apptimer(0 if practice else 1.25, self._transition_out) bui.apptimer(0 if practice else 1.25, self._transition_out)
def _on_pay_with_tickets_press(self) -> None: def _on_pay_with_tickets_press(self) -> None:
from bauiv1lib import getcurrency from bauiv1lib import gettickets
plus = bui.app.plus plus = bui.app.plus
assert plus is not None assert plus is not None
@ -675,7 +675,7 @@ class TournamentEntryWindow(PopupWindow):
ticket_count = None ticket_count = None
ticket_cost = self._purchase_price ticket_cost = self._purchase_price
if ticket_count is not None and ticket_count < ticket_cost: if ticket_count is not None and ticket_count < ticket_cost:
getcurrency.show_get_tickets_prompt() gettickets.show_get_tickets_prompt()
bui.getsound('error').play() bui.getsound('error').play()
self._transition_out() self._transition_out()
return return
@ -781,7 +781,7 @@ class TournamentEntryWindow(PopupWindow):
self._launch() self._launch()
def _on_get_tickets_press(self) -> None: def _on_get_tickets_press(self) -> None:
from bauiv1lib import getcurrency from bauiv1lib import gettickets
# If we're already entering, ignore presses. # If we're already entering, ignore presses.
if self._entering: if self._entering:
@ -789,7 +789,7 @@ class TournamentEntryWindow(PopupWindow):
# Bring up get-tickets window and then kill ourself (we're on the # Bring up get-tickets window and then kill ourself (we're on the
# overlay layer so we'd show up above it). # overlay layer so we'd show up above it).
getcurrency.GetCurrencyWindow( gettickets.GetTicketsWindow(
modal=True, origin_widget=self._get_tickets_button modal=True, origin_widget=self._get_tickets_button
) )
self._transition_out() self._transition_out()

View File

@ -148,5 +148,6 @@ void AppAdapter::NativeReviewRequest() {
void AppAdapter::DoNativeReviewRequest() { FatalError("Fixme unimplemented."); } void AppAdapter::DoNativeReviewRequest() { FatalError("Fixme unimplemented."); }
auto AppAdapter::ShouldSilenceAudioForInactive() -> bool const { return false; } auto AppAdapter::ShouldSilenceAudioForInactive() -> bool const { return false; }
auto AppAdapter::SupportsPurchases() -> bool { return false; }
} // namespace ballistica::base } // namespace ballistica::base

View File

@ -222,6 +222,8 @@ class AppAdapter {
virtual void DoClipboardSetText(const std::string& text); virtual void DoClipboardSetText(const std::string& text);
virtual auto DoClipboardGetText() -> std::string; virtual auto DoClipboardGetText() -> std::string;
virtual auto SupportsPurchases() -> bool;
protected: protected:
virtual ~AppAdapter(); virtual ~AppAdapter();

View File

@ -408,6 +408,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
} }
case SDL_KEYDOWN: { case SDL_KEYDOWN: {
// printf("KEYDOWN %d\n", static_cast<int>(event.key.keysym.sym));
if (!event.key.repeat) { if (!event.key.repeat) {
g_base->input->PushKeyPressEvent(event.key.keysym); g_base->input->PushKeyPressEvent(event.key.keysym);
} }
@ -415,6 +416,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
} }
case SDL_KEYUP: { case SDL_KEYUP: {
// printf("KEYUP %d\n", static_cast<int>(event.key.keysym.sym));
g_base->input->PushKeyReleaseEvent(event.key.keysym); g_base->input->PushKeyReleaseEvent(event.key.keysym);
break; break;
} }

View File

@ -1259,7 +1259,7 @@ void Assets::InitSpecialChars() {
special_char_strings_[SpecialChar::kOuyaButtonU] = "\xee\x80\x9A"; special_char_strings_[SpecialChar::kOuyaButtonU] = "\xee\x80\x9A";
special_char_strings_[SpecialChar::kOuyaButtonY] = "\xee\x80\x9B"; special_char_strings_[SpecialChar::kOuyaButtonY] = "\xee\x80\x9B";
special_char_strings_[SpecialChar::kOuyaButtonA] = "\xee\x80\x9C"; special_char_strings_[SpecialChar::kOuyaButtonA] = "\xee\x80\x9C";
special_char_strings_[SpecialChar::kOuyaLogo] = "\xee\x80\x9D"; special_char_strings_[SpecialChar::kToken] = "\xee\x80\x9D";
special_char_strings_[SpecialChar::kLogo] = "\xee\x80\x9E"; special_char_strings_[SpecialChar::kLogo] = "\xee\x80\x9E";
special_char_strings_[SpecialChar::kTicket] = "\xee\x80\x9F"; special_char_strings_[SpecialChar::kTicket] = "\xee\x80\x9F";
special_char_strings_[SpecialChar::kGooglePlayGamesLogo] = "\xee\x80\xA0"; special_char_strings_[SpecialChar::kGooglePlayGamesLogo] = "\xee\x80\xA0";

View File

@ -54,8 +54,8 @@ TextGraphics::TextGraphics() {
} }
// Shrink account logos and move them up a bit. // Shrink account logos and move them up a bit.
if (index == 29 || index == 32 || index == 33 || index == 38 if (index == 32 || index == 33 || index == 38 || index == 40
|| index == 40 || index == 48 || index == 49) { || index == 48 || index == 49) {
g.pen_offset_y += 0.4f; g.pen_offset_y += 0.4f;
extra_advance += 0.08f; extra_advance += 0.08f;
g.x_size *= 0.55f; g.x_size *= 0.55f;

View File

@ -347,6 +347,14 @@ int BasePlatform::SmartGetC_(FILE* stream) {
return EOF; return EOF;
} }
// Need to catch these error cases and bail, oherwise we'll spin
// forever getting the same thing. (Noticed this happening on Mac build
// where we get an immediate POLLNVAL if there's no terminal attached
// to stdin.
if (fds[0].revents & (POLLERR | POLLNVAL | POLLHUP)) {
return EOF;
}
if (fds[0].revents & POLLIN) { if (fds[0].revents & POLLIN) {
// stdin is ready for reading. // stdin is ready for reading.
char buffer[256]; char buffer[256];

View File

@ -6,6 +6,7 @@
#include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/foundation/event_loop.h"
namespace ballistica::base { namespace ballistica::base {
Repeater::Repeater(seconds_t initial_delay, seconds_t repeat_delay, Repeater::Repeater(seconds_t initial_delay, seconds_t repeat_delay,
Runnable* runnable) Runnable* runnable)
: initial_delay_(initial_delay), : initial_delay_(initial_delay),
@ -14,39 +15,40 @@ Repeater::Repeater(seconds_t initial_delay, seconds_t repeat_delay,
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
assert(initial_delay >= 0.0); assert(initial_delay >= 0.0);
assert(repeat_delay >= 0.0); assert(repeat_delay >= 0.0);
}
void Repeater::PostInit_() {
assert(g_base->InLogicThread());
// Let's go ahead and run our initial time in a deferred call; // Let's go ahead and run our initial time in a deferred call;
// this is generally safer than running in the middle of whatever UI // this is generally safer than running in the middle of whatever UI
// code set this up. // code called us. Note that we use a strong ref here - if we use a
// weak ref then it is possible for our initial key press to get lost
// if the repeater gets canceled due to other keypresses/etc before
// the initial call runs.
auto strong_this = Object::Ref<Repeater>(this);
g_base->logic->event_loop()->PushCall(
[strong_this] { strong_this->runnable_->RunAndLogErrors(); });
auto weak_this = Object::WeakRef<Repeater>(this); auto weak_this = Object::WeakRef<Repeater>(this);
g_base->logic->event_loop()->PushCall([weak_this] { timer_ = DisplayTimer::New(weak_this->initial_delay_, false, [weak_this] {
if (weak_this.Exists()) { // Timer should not have fired if we died.
weak_this->runnable_->RunAndLogErrors(); assert(weak_this.Exists());
if (!weak_this.Exists()) { weak_this->runnable_->RunAndLogErrors();
// Runnable might have killed us. if (!weak_this.Exists()) {
return; // Runnable we just ran might have killed us.
} return;
// Kick off our initial delay timer (generally the longer one).
weak_this->timer_ =
DisplayTimer::New(weak_this->initial_delay_, false, [weak_this] {
// Timer should not have fired if we died.
assert(weak_this.Exists());
weak_this->runnable_->RunAndLogErrors();
if (!weak_this.Exists()) {
// Runnable might have killed us.
return;
}
// Kick off our repeat timer (generally the short one).
weak_this->timer_ =
DisplayTimer::New(weak_this->repeat_delay_, true, [weak_this] {
// Timer should not have fired if we died.
assert(weak_this.Exists());
weak_this->runnable_->RunAndLogErrors();
// Doesn't matter if Runnable killed us since we don't
// touch anything for the remainder of this function.
});
});
} }
// Kick off our repeat timer (generally the short one).
weak_this->timer_ =
DisplayTimer::New(weak_this->repeat_delay_, true, [weak_this] {
// Timer should not have fired if we died.
assert(weak_this.Exists());
weak_this->runnable_->RunAndLogErrors();
// Doesn't matter if Runnable killed us since we don't
// touch anything for the remainder of this function.
});
}); });
} }

View File

@ -14,17 +14,22 @@ namespace ballistica::base {
/// Uses display-time so emphasizes visual smoothness over accuracy. /// Uses display-time so emphasizes visual smoothness over accuracy.
class Repeater : public Object { class Repeater : public Object {
public: public:
Repeater(seconds_t initial_delay, seconds_t repeat_delay, Runnable* runnable);
~Repeater();
template <typename F> template <typename F>
static auto New(seconds_t initial_delay, seconds_t repeat_delay, static auto New(seconds_t initial_delay, seconds_t repeat_delay,
const F& lambda) { const F& lambda) {
return Object::New<Repeater>(initial_delay, repeat_delay, auto&& rep = Object::New<Repeater>(initial_delay, repeat_delay,
NewLambdaRunnable<F>(lambda).Get()); NewLambdaRunnable<F>(lambda).Get());
// We need to run this bit *after* constructing our obj since it creates
// a strong ref.
rep->PostInit_();
return Object::Ref<Repeater>(rep);
} }
private: private:
friend class Object; // Allows our constructor to be private.
Repeater(seconds_t initial_delay, seconds_t repeat_delay, Runnable* runnable);
~Repeater();
void PostInit_();
seconds_t initial_delay_; seconds_t initial_delay_;
seconds_t repeat_delay_; seconds_t repeat_delay_;
Object::Ref<DisplayTimer> timer_; Object::Ref<DisplayTimer> timer_;

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21911; const int kEngineBuildNumber = 21919;
const char* kEngineVersion = "1.7.36"; const char* kEngineVersion = "1.7.36";
const int kEngineApiVersion = 8; const int kEngineApiVersion = 8;

View File

@ -188,7 +188,7 @@ enum class SpecialChar : uint8_t {
kOuyaButtonU, kOuyaButtonU,
kOuyaButtonY, kOuyaButtonY,
kOuyaButtonA, kOuyaButtonA,
kOuyaLogo, kToken,
kLogo, kLogo,
kTicket, kTicket,
kGooglePlayGamesLogo, kGooglePlayGamesLogo,

View File

@ -704,6 +704,7 @@ static auto PyImageWidget(PyObject* self, PyObject* args,
PyObject* tilt_scale_obj = Py_None; PyObject* tilt_scale_obj = Py_None;
PyObject* mask_texture_obj = Py_None; PyObject* mask_texture_obj = Py_None;
PyObject* radial_amount_obj = Py_None; PyObject* radial_amount_obj = Py_None;
PyObject* draw_controller_mult_obj = Py_None;
static const char* kwlist[] = {"edit", static const char* kwlist[] = {"edit",
"parent", "parent",
@ -723,14 +724,16 @@ static auto PyImageWidget(PyObject* self, PyObject* args,
"tilt_scale", "tilt_scale",
"mask_texture", "mask_texture",
"radial_amount", "radial_amount",
"draw_controller_mult",
nullptr}; nullptr};
if (!PyArg_ParseTupleAndKeywords( if (!PyArg_ParseTupleAndKeywords(
args, keywds, "|OOOOOOOOOOOOOOOOOO", const_cast<char**>(kwlist), args, keywds, "|OOOOOOOOOOOOOOOOOOO", const_cast<char**>(kwlist),
&edit_obj, &parent_obj, &size_obj, &pos_obj, &color_obj, &texture_obj, &edit_obj, &parent_obj, &size_obj, &pos_obj, &color_obj, &texture_obj,
&opacity_obj, &mesh_transparent_obj, &mesh_opaque_obj, &opacity_obj, &mesh_transparent_obj, &mesh_opaque_obj,
&has_alpha_channel_obj, &tint_texture_obj, &tint_color_obj, &has_alpha_channel_obj, &tint_texture_obj, &tint_color_obj,
&transition_delay_obj, &draw_controller_obj, &tint2_color_obj, &transition_delay_obj, &draw_controller_obj, &tint2_color_obj,
&tilt_scale_obj, &mask_texture_obj, &radial_amount_obj)) &tilt_scale_obj, &mask_texture_obj, &radial_amount_obj,
&draw_controller_mult_obj))
return nullptr; return nullptr;
if (!g_base->CurrentContext().IsEmpty()) { if (!g_base->CurrentContext().IsEmpty()) {
@ -831,6 +834,9 @@ static auto PyImageWidget(PyObject* self, PyObject* args,
if (tilt_scale_obj != Py_None) { if (tilt_scale_obj != Py_None) {
b->set_tilt_scale(Python::GetPyFloat(tilt_scale_obj)); b->set_tilt_scale(Python::GetPyFloat(tilt_scale_obj));
} }
if (draw_controller_mult_obj != Py_None) {
b->set_draw_controller_mult(Python::GetPyFloat(draw_controller_mult_obj));
}
// if making a new widget add it at the end // if making a new widget add it at the end
if (edit_obj == Py_None) { if (edit_obj == Py_None) {
@ -866,7 +872,8 @@ static PyMethodDef PyImageWidgetDef = {
" tint2_color: Sequence[float] | None = None,\n" " tint2_color: Sequence[float] | None = None,\n"
" tilt_scale: float | None = None,\n" " tilt_scale: float | None = None,\n"
" mask_texture: bauiv1.Texture | None = None,\n" " mask_texture: bauiv1.Texture | None = None,\n"
" radial_amount: float | None = None)\n" " radial_amount: float | None = None,\n"
" draw_controller_mult: float | None = None)\n"
" -> bauiv1.Widget\n" " -> bauiv1.Widget\n"
"\n" "\n"
"Create or edit an image widget.\n" "Create or edit an image widget.\n"
@ -1816,7 +1823,7 @@ static auto PyHScrollWidget(PyObject* self, PyObject* args,
if (c.size() != 3) { if (c.size() != 3) {
throw Exception("Expected 3 floats for color.", PyExcType::kValue); throw Exception("Expected 3 floats for color.", PyExcType::kValue);
} }
widget->setColor(c[0], c[1], c[2]); widget->SetColor(c[0], c[1], c[2]);
} }
if (capture_arrows_obj != Py_None) { if (capture_arrows_obj != Py_None) {
widget->set_capture_arrows(Python::GetPyBool(capture_arrows_obj)); widget->set_capture_arrows(Python::GetPyBool(capture_arrows_obj));

View File

@ -5,6 +5,7 @@
#include "ballistica/base/graphics/component/empty_component.h" #include "ballistica/base/graphics/component/empty_component.h"
#include "ballistica/base/graphics/component/simple_component.h" #include "ballistica/base/graphics/component/simple_component.h"
#include "ballistica/base/support/app_timer.h" #include "ballistica/base/support/app_timer.h"
#include "ballistica/shared/foundation/inline.h"
namespace ballistica::ui_v1 { namespace ballistica::ui_v1 {
@ -41,7 +42,7 @@ void HScrollWidget::OnTouchDelayTimerExpired() {
touch_delay_timer_.Clear(); touch_delay_timer_.Clear();
} }
void HScrollWidget::ClampThumb(bool velocity_clamp, bool position_clamp) { void HScrollWidget::ClampThumb_(bool velocity_clamp, bool position_clamp) {
BA_DEBUG_UI_READ_LOCK; BA_DEBUG_UI_READ_LOCK;
bool is_scrolling = (touch_held_ || !has_momentum_); bool is_scrolling = (touch_held_ || !has_momentum_);
float strong_force; float strong_force;
@ -59,7 +60,7 @@ void HScrollWidget::ClampThumb(bool velocity_clamp, bool position_clamp) {
if (velocity_clamp) { if (velocity_clamp) {
if (child_offset_h_ < 0) { if (child_offset_h_ < 0) {
// even in velocity case do some sane clamping // Even in velocity case do some sane clamping.
float diff = child_offset_h_; float diff = child_offset_h_;
inertia_scroll_rate_ += inertia_scroll_rate_ +=
diff * (is_scrolling ? strong_force : weak_force); diff * (is_scrolling ? strong_force : weak_force);
@ -77,7 +78,7 @@ void HScrollWidget::ClampThumb(bool velocity_clamp, bool position_clamp) {
} }
} }
// hard clipping if we're dragging the scrollbar // Hard clipping if we're dragging the scrollbar.
if (position_clamp) { if (position_clamp) {
if (child_offset_h_smoothed_ if (child_offset_h_smoothed_
> child_w - (width() - 2 * (border_width_ + kHMargin))) { > child_w - (width() - 2 * (border_width_ + kHMargin))) {
@ -111,17 +112,18 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
if (i == widgets().end()) break; if (i == widgets().end()) break;
float child_w = (**i).GetWidth(); float child_w = (**i).GetWidth();
// see where we'd have to scroll to get selection at left and right // See where we'd have to scroll to get selection at left and right.
float child_offset_left = float child_offset_left =
child_w - m.fval1 - (width() - 2 * (border_width_ + kHMargin)); child_w - m.fval1 - (width() - 2 * (border_width_ + kHMargin));
float child_offset_right = child_w - m.fval1 - m.fval3; float child_offset_right = child_w - m.fval1 - m.fval3;
// if we're in the middle, dont do anything // If we're in the middle, dont do anything.
if (child_offset_h_ > child_offset_left if (child_offset_h_ > child_offset_left
&& child_offset_h_ < child_offset_right) { && child_offset_h_ < child_offset_right) {
} else { } else {
float prev_child_offset = child_offset_h_; float prev_child_offset = child_offset_h_;
// do whatever offset is less of a move
// Do whatever offset is less of a move.
if (std::abs(child_offset_left - child_offset_h_) if (std::abs(child_offset_left - child_offset_h_)
< std::abs(child_offset_right - child_offset_h_)) { < std::abs(child_offset_right - child_offset_h_)) {
child_offset_h_ = child_offset_left; child_offset_h_ = child_offset_left;
@ -129,12 +131,13 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
child_offset_h_ = child_offset_right; child_offset_h_ = child_offset_right;
} }
// if we're moving left, stop at the end // If we're moving left, stop at the end.
{ {
float max_val = child_w - (width() - 2 * (border_width_ + kHMargin)); float max_val = child_w - (width() - 2 * (border_width_ + kHMargin));
if (child_offset_h_ > max_val) child_offset_h_ = max_val; if (child_offset_h_ > max_val) child_offset_h_ = max_val;
} }
// if we're moving right, stop at the top
// If we're moving right, stop at the top.
{ {
if (child_offset_h_ < prev_child_offset) { if (child_offset_h_ < prev_child_offset) {
if (child_offset_h_ < 0) child_offset_h_ = 0; if (child_offset_h_ < 0) child_offset_h_ = 0;
@ -154,6 +157,7 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
break; break;
} }
case base::WidgetMessage::Type::kMouseMove: { case base::WidgetMessage::Type::kMouseMove: {
last_mouse_move_time_ = g_base->logic->display_time();
float x = m.fval1; float x = m.fval1;
float y = m.fval2; float y = m.fval2;
bool claimed2 = (m.fval3 > 0.0f); bool claimed2 = (m.fval3 > 0.0f);
@ -177,17 +181,17 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
touch_x_ = x; touch_x_ = x;
touch_y_ = y; touch_y_ = y;
// if this is a new scroll-touch, see which direction the drag is // If this is a new scroll-touch, see which direction the drag
// happening; if it's primarily vertical lets disown it so it can // is happening; if it's primarily vertical lets disown it so it
// get handled by the scroll widget above us (presumably a vertical // can get handled by the scroll widget above us (presumably a
// scroll widget) // vertical scroll widget).
if (new_scroll_touch_) { if (new_scroll_touch_) {
float x_diff = std::abs(touch_x_ - touch_start_x_); float x_diff = std::abs(touch_x_ - touch_start_x_);
float y_diff = std::abs(touch_y_ - touch_start_y_); float y_diff = std::abs(touch_y_ - touch_start_y_);
float dist = x_diff * x_diff + y_diff * y_diff; float dist = x_diff * x_diff + y_diff * y_diff;
// if they're somehow equal, wait and look at the next one.. // If they're somehow equal, wait and look at the next one.
if (x_diff != y_diff && dist > 30.0f) { if (x_diff != y_diff && dist > 30.0f) {
new_scroll_touch_ = false; new_scroll_touch_ = false;
@ -200,8 +204,8 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
// Handle generating delayed press/releases. // Handle generating delayed press/releases.
if (static_cast<int>(m.type)) { // <- FIXME WHAT IS THIS FOR?? if (static_cast<int>(m.type)) { // <- FIXME WHAT IS THIS FOR??
// If we move more than a slight amount it means our touch isn't a // If we move more than a slight amount it means our touch
// click. // isn't a click.
if (!touch_is_scrolling_ if (!touch_is_scrolling_
&& ((std::abs(touch_x_ - touch_start_x_) > 10.0f) && ((std::abs(touch_x_ - touch_start_x_) > 10.0f)
|| (std::abs(touch_y_ - touch_start_y_) > 10.0f))) { || (std::abs(touch_y_ - touch_start_y_) > 10.0f))) {
@ -253,7 +257,7 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
child_offset_h_ = thumb_click_start_child_offset_h_ child_offset_h_ = thumb_click_start_child_offset_h_
- rate * (x - thumb_click_start_h_); - rate * (x - thumb_click_start_h_);
ClampThumb(false, true); ClampThumb_(false, true);
MarkForUpdate(); MarkForUpdate();
} }
@ -272,12 +276,12 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
touch_held_ = false; touch_held_ = false;
// If we moved at all, we mark it as claimed to keep // If we moved at all, we mark it as claimed to keep sub-widgets
// sub-widgets from acting on it (since we used it for scrolling). // from acting on it (since we used it for scrolling).
bool claimed2 = touch_is_scrolling_ || m_claimed; bool claimed2 = touch_is_scrolling_ || m_claimed;
// If we're not claiming it and we haven't sent a mouse_down yet due // If we're not claiming it and we haven't sent a mouse_down yet
// to our delay, send that first. // due to our delay, send that first.
if (!claimed2 && !touch_down_sent_) { if (!claimed2 && !touch_down_sent_) {
ContainerWidget::HandleMessage(base::WidgetMessage( ContainerWidget::HandleMessage(base::WidgetMessage(
base::WidgetMessage::Type::kMouseDown, nullptr, m.fval1, base::WidgetMessage::Type::kMouseDown, nullptr, m.fval1,
@ -294,9 +298,9 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
} }
} }
// If coords are outside of our bounds, pass a mouse-up along for anyone // If coords are outside of our bounds, pass a mouse-up along for
// tracking a drag, but mark it as claimed so it doesn't actually get // anyone tracking a drag, but mark it as claimed so it doesn't
// acted on. // actually get acted on.
float x = m.fval1; float x = m.fval1;
float y = m.fval2; float y = m.fval2;
if (!((y >= 0.0f) && (y < height()) && (x >= 0.0f) && (x < width()))) { if (!((y >= 0.0f) && (y < height()) && (x >= 0.0f) && (x < width()))) {
@ -399,8 +403,8 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
touch_down_x_ = x - child_offset_h_; touch_down_x_ = x - child_offset_h_;
touch_is_scrolling_ = false; touch_is_scrolling_ = false;
// If there's significant scrolling happening we never pass touches. // If there's significant scrolling happening we never pass
// they're only used to scroll more/less. // touches. they're only used to scroll more/less.
if (std::abs(inertia_scroll_rate_) > 0.05f) { if (std::abs(inertia_scroll_rate_) > 0.05f) {
touch_is_scrolling_ = true; touch_is_scrolling_ = true;
} }
@ -441,7 +445,7 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
smoothing_amount_ = 1.0f; // So we can see the transition. smoothing_amount_ = 1.0f; // So we can see the transition.
child_offset_h_ -= (width() - 2 * (border_width_ + kHMargin)); child_offset_h_ -= (width() - 2 * (border_width_ + kHMargin));
MarkForUpdate(); MarkForUpdate();
ClampThumb(false, true); ClampThumb_(false, true);
} else if (x >= sb_thumb_right - sb_thumb_width) { } else if (x >= sb_thumb_right - sb_thumb_width) {
// On thumb. // On thumb.
mouse_held_thumb_ = true; mouse_held_thumb_ = true;
@ -452,7 +456,7 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
smoothing_amount_ = 1.0f; // So we can see the transition. smoothing_amount_ = 1.0f; // So we can see the transition.
child_offset_h_ += (width() - 2 * (border_width_ + kHMargin)); child_offset_h_ += (width() - 2 * (border_width_ + kHMargin));
MarkForUpdate(); MarkForUpdate();
ClampThumb(false, true); ClampThumb_(false, true);
} }
} }
} }
@ -519,18 +523,20 @@ void HScrollWidget::UpdateLayout() {
void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) { void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
have_drawn_ = true; have_drawn_ = true;
millisecs_t current_time = pass->frame_def()->display_time_millisecs(); auto* frame_def{pass->frame_def()};
millisecs_t current_time_ms = frame_def->display_time_millisecs();
float prev_child_offset_h_smoothed = child_offset_h_smoothed_; float prev_child_offset_h_smoothed = child_offset_h_smoothed_;
// Ok, lets update our inertial scrolling during the opaque pass. // Ok, lets update our inertial scrolling during the opaque pass. (we
// (we really should have some sort of update() function for this but widgets // really should have some sort of update() function for this but widgets
// don't have that currently) // don't have that currently)
if (!draw_transparent) { if (!draw_transparent) {
// (skip huge differences) // (skip huge differences)
if (current_time - inertia_scroll_update_time_ > 1000) if (current_time_ms - inertia_scroll_update_time_ms_ > 1000) {
inertia_scroll_update_time_ = current_time - 1000; inertia_scroll_update_time_ms_ = current_time_ms - 1000;
while (current_time - inertia_scroll_update_time_ > 5) { }
inertia_scroll_update_time_ += 5; while (current_time_ms - inertia_scroll_update_time_ms_ > 5) {
inertia_scroll_update_time_ms_ += 5;
if (touch_mode_) { if (touch_mode_) {
if (touch_held_) { if (touch_held_) {
@ -545,18 +551,20 @@ void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
inertia_scroll_rate_ *= 0.98f; inertia_scroll_rate_ *= 0.98f;
} }
ClampThumb(true, mouse_held_thumb_); ClampThumb_(true, mouse_held_thumb_);
child_offset_h_ += inertia_scroll_rate_; child_offset_h_ += inertia_scroll_rate_;
if (!has_momentum_ if (!has_momentum_
&& (current_time - last_velocity_event_time_millisecs_ > 1000 / 30)) { && (current_time_ms - last_velocity_event_time_millisecs_
> 1000 / 30)) {
inertia_scroll_rate_ = 0; inertia_scroll_rate_ = 0;
} }
// Lastly we apply smoothing so that if we're snapping to a specific place // Lastly we apply smoothing so that if we're snapping to a specific
// we don't go instantly there we blend between smoothed and non-smoothed // place we don't go instantly there we blend between smoothed and
// depending on whats driving us (we dont want to add smoothing on top of // non-smoothed depending on whats driving us (we dont want to add
// inertial scrolling for example or it'll feel muddy) // smoothing on top of inertial scrolling for example or it'll feel
// muddy)
float diff = child_offset_h_ - child_offset_h_smoothed_; float diff = child_offset_h_ - child_offset_h_smoothed_;
if (std::abs(diff) < 1.0f) if (std::abs(diff) < 1.0f)
child_offset_h_smoothed_ = child_offset_h_; child_offset_h_smoothed_ = child_offset_h_;
@ -601,58 +609,60 @@ void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
} }
// scroll trough (depth 0.7f to 0.8f) // scroll trough (depth 0.7f to 0.8f)
if (draw_transparent && border_opacity_ > 0.0f) { if (explicit_bool(false)) {
if (trough_dirty_) { if (draw_transparent && border_opacity_ > 0.0f) {
float b2 = b + 4; if (trough_dirty_) {
float t2 = b2 + scroll_bar_height_; float b2 = b + 4;
float l2; float t2 = b2 + scroll_bar_height_;
float r2; float l2;
l2 = l + (border_width_); float r2;
r2 = r - (border_width_); l2 = l + (border_width_);
float b_border, t_border, l_border, r_border; r2 = r - (border_width_);
b_border = 3; float b_border, t_border, l_border, r_border;
t_border = 0; b_border = 3;
l_border = width() * 0.006f; t_border = 0;
r_border = width() * 0.002f; l_border = width() * 0.006f;
trough_height_ = t2 - b2 + b_border + t_border; r_border = width() * 0.002f;
trough_width_ = r2 - l2 + l_border + r_border; trough_height_ = t2 - b2 + b_border + t_border;
trough_center_y_ = b2 - b_border + trough_height_ * 0.5f; trough_width_ = r2 - l2 + l_border + r_border;
trough_center_x_ = l2 - l_border + trough_width_ * 0.5f; trough_center_y_ = b2 - b_border + trough_height_ * 0.5f;
trough_dirty_ = false; trough_center_x_ = l2 - l_border + trough_width_ * 0.5f;
} trough_dirty_ = false;
}
base::SimpleComponent c(pass); base::SimpleComponent c(pass);
c.SetTransparent(true); c.SetTransparent(true);
c.SetColor(1, 1, 1, border_opacity_); c.SetColor(1, 1, 1, border_opacity_);
c.SetTexture(g_base->assets->SysTexture(base::SysTextureID::kUIAtlas)); c.SetTexture(g_base->assets->SysTexture(base::SysTextureID::kUIAtlas));
{ {
auto xf = c.ScopedTransform(); auto xf = c.ScopedTransform();
c.Translate(trough_center_x_, trough_center_y_, 0.7f); c.Translate(trough_center_x_, trough_center_y_, 0.7f);
c.Scale(trough_width_, trough_height_, 0.1f); c.Scale(trough_width_, trough_height_, 0.1f);
c.Rotate(-90, 0, 0, 1); c.Rotate(-90, 0, 0, 1);
c.DrawMeshAsset(g_base->assets->SysMesh( c.DrawMeshAsset(g_base->assets->SysMesh(
base::SysMeshID::kScrollBarTroughTransparent)); base::SysMeshID::kScrollBarTroughTransparent));
}
c.Submit();
} }
c.Submit();
} }
// scroll bars // Scroll bars.
if (amount_visible_ > 0 && amount_visible_ < 1) { if (amount_visible_ > 0.0f && amount_visible_ < 1.0f) {
// scroll thumb at depth 0.8f-0.9 // Scroll thumb at depth 0.8 - 0.9.
{ {
float sb_thumb_width = amount_visible_ * (width() - 2 * border_width_); float sb_thumb_width = amount_visible_ * (width() - 2.0f * border_width_);
if (thumb_dirty_) { if (thumb_dirty_) {
float sb_thumb_right = float sb_thumb_right =
r - border_width_ r - border_width_
- ((width() - (border_width_ * 2) - sb_thumb_width) - ((width() - (border_width_ * 2.0f) - sb_thumb_width)
* child_offset_h_smoothed_ / child_max_offset_); * child_offset_h_smoothed_ / child_max_offset_);
float b2 = 4; float b2 = 4.0f;
float t2 = b2 + scroll_bar_height_; float t2 = b2 + scroll_bar_height_;
float r2 = sb_thumb_right; float r2 = sb_thumb_right;
float l2 = r2 - sb_thumb_width; float l2 = r2 - sb_thumb_width;
float b_border, t_border, l_border, r_border; float b_border, t_border, l_border, r_border;
b_border = 6; b_border = 6.0f;
t_border = 3; t_border = 3.0f;
if (sb_thumb_width > 100) { if (sb_thumb_width > 100) {
auto wd = r2 - l2; auto wd = r2 - l2;
l_border = wd * 0.04f; l_border = wd * 0.04f;
@ -679,25 +689,32 @@ void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
// c_scale = 1.25f; // c_scale = 1.25f;
// } // }
float frame_duration = frame_def->display_time_elapsed();
bool smooth_diff = bool smooth_diff =
(std::abs(child_offset_h_smoothed_ - child_offset_h_) > 0.01f); (std::abs(child_offset_h_smoothed_ - child_offset_h_) > 0.01f);
if (touch_mode_) { if (touch_mode_) {
if (smooth_diff || (touch_held_ && touch_is_scrolling_) if (smooth_diff || (touch_held_ && touch_is_scrolling_)
|| std::abs(inertia_scroll_rate_) > 1.0f) { || std::abs(inertia_scroll_rate_) > 1.0f) {
touch_fade_ = std::min(1.5f, touch_fade_ + 0.02f); last_scroll_bar_show_time_ = frame_def->display_time();
} else {
// FIXME: Shouldn't be frame based.
touch_fade_ = std::max(0.0f, touch_fade_ - 0.015f);
} }
} else { } else {
if (smooth_diff || (touch_held_ && touch_is_scrolling_) if (smooth_diff || (touch_held_ && touch_is_scrolling_)
|| std::abs(inertia_scroll_rate_) > 1.0f || mouse_over_) { || std::abs(inertia_scroll_rate_) > 1.0f
touch_fade_ = std::min(1.5f, touch_fade_ + 0.02f); || (mouse_over_
} else { && frame_def->display_time() - last_mouse_move_time_ < 0.1)) {
// FIXME: Shouldn't be frame based. last_scroll_bar_show_time_ = frame_def->display_time();
touch_fade_ = std::max(0.0f, touch_fade_ - 0.015f);
} }
} }
// Fade in if we want to see the scrollbar. Start fading out a moment
// after we stop wanting to see it.
if (frame_def->display_time() - last_scroll_bar_show_time_ < 1.0) {
touch_fade_ = std::min(1.5f, touch_fade_ + 2.0f * frame_duration);
} else {
touch_fade_ = std::max(0.0f, touch_fade_ - frame_duration);
}
c.SetColor(0, 0, 0, std::min(1.0f, 0.3f * touch_fade_)); c.SetColor(0, 0, 0, std::min(1.0f, 0.3f * touch_fade_));
{ {
@ -722,7 +739,7 @@ void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
} }
} }
// outline shadow (depth 0.9 to 1.0) // Outline shadow (depth 0.9 to 1.0).
if (draw_transparent && border_opacity_ > 0.0f) { if (draw_transparent && border_opacity_ > 0.0f) {
if (shadow_dirty_) { if (shadow_dirty_) {
float r2 = l + width(); float r2 = l + width();
@ -754,12 +771,12 @@ void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
c.Submit(); c.Submit();
} }
// if selected, do glow at depth 0.9-1.0 // If selected, do glow at depth 0.9 - 1.0.
if (draw_transparent && IsHierarchySelected() if (draw_transparent && IsHierarchySelected()
&& g_base->ui->ShouldHighlightWidgets() && highlight_ && g_base->ui->ShouldHighlightWidgets() && highlight_
&& border_opacity_ > 0.0f) { && border_opacity_ > 0.0f) {
float m = 0.8f float m = 0.8f
+ std::abs(sinf(static_cast<float>(current_time) * 0.006467f)) + std::abs(sinf(static_cast<float>(current_time_ms) * 0.006467f))
* 0.2f * border_opacity_; * 0.2f * border_opacity_;
if (glow_dirty_) { if (glow_dirty_) {

View File

@ -33,7 +33,7 @@ class HScrollWidget : public ContainerWidget {
MarkForUpdate(); MarkForUpdate();
} }
void OnTouchDelayTimerExpired(); void OnTouchDelayTimerExpired();
void setColor(float r, float g, float b) { void SetColor(float r, float g, float b) {
color_red_ = r; color_red_ = r;
color_green_ = g; color_green_ = g;
color_blue_ = b; color_blue_ = b;
@ -47,39 +47,30 @@ class HScrollWidget : public ContainerWidget {
void UpdateLayout() override; void UpdateLayout() override;
private: private:
void ClampThumb(bool velocity_clamp, bool position_clamp); void ClampThumb_(bool velocity_clamp, bool position_clamp);
bool touch_mode_{}; Object::Ref<base::AppTimer> touch_delay_timer_;
seconds_t last_scroll_bar_show_time_{};
seconds_t last_mouse_move_time_{};
float color_red_{0.55f}; float color_red_{0.55f};
float color_green_{0.47f}; float color_green_{0.47f};
float color_blue_{0.67f}; float color_blue_{0.67f};
bool has_momentum_{true};
bool trough_dirty_{true};
bool shadow_dirty_{true};
bool glow_dirty_{true};
bool thumb_dirty_{true};
millisecs_t last_velocity_event_time_millisecs_{};
float touch_fade_{}; float touch_fade_{};
bool center_small_content_{};
float center_offset_x_{}; float center_offset_x_{};
bool touch_held_{};
int touch_held_click_count_{};
float touch_down_x_{}; float touch_down_x_{};
float touch_x_{}; float touch_x_{};
float touch_y_{}; float touch_y_{};
float touch_start_x_{}; float touch_start_x_{};
float touch_start_y_{}; float touch_start_y_{};
bool touch_is_scrolling_{};
bool touch_down_sent_{};
bool touch_up_sent_{};
bool new_scroll_touch_{};
float trough_width_{}; float trough_width_{};
float trough_height_{}; float trough_height_{};
float trough_center_x_{}; float trough_center_x_{};
float trough_center_y_{}; float trough_center_y_{};
float thumb_width_{}, thumb_height_{}, thumb_center_x_{}, thumb_center_y_{}; float thumb_width_{};
float thumb_height_{};
float thumb_center_x_{};
float thumb_center_y_{};
float smoothing_amount_{1.0f}; float smoothing_amount_{1.0f};
bool highlight_{true};
float glow_width_{}; float glow_width_{};
float glow_height_{}; float glow_height_{};
float glow_center_x_{}; float glow_center_x_{};
@ -89,16 +80,8 @@ class HScrollWidget : public ContainerWidget {
float outline_center_x_{}; float outline_center_x_{};
float outline_center_y_{}; float outline_center_y_{};
float border_opacity_{1.0f}; float border_opacity_{1.0f};
bool capture_arrows_{};
bool mouse_held_scroll_down_{};
bool mouse_held_scroll_up_{};
bool mouse_held_thumb_{};
float thumb_click_start_h_{}; float thumb_click_start_h_{};
float thumb_click_start_child_offset_h_{}; float thumb_click_start_child_offset_h_{};
bool mouse_held_page_down_{};
bool mouse_held_page_up_{};
bool mouse_over_thumb_{};
bool mouse_over_{};
float scroll_bar_height_{10.0f}; float scroll_bar_height_{10.0f};
float border_width_{2.0f}; float border_width_{2.0f};
float border_height_{2.0f}; float border_height_{2.0f};
@ -106,10 +89,32 @@ class HScrollWidget : public ContainerWidget {
float child_offset_h_smoothed_{}; float child_offset_h_smoothed_{};
float child_max_offset_{}; float child_max_offset_{};
float amount_visible_{}; float amount_visible_{};
bool have_drawn_{};
millisecs_t inertia_scroll_update_time_{};
float inertia_scroll_rate_{}; float inertia_scroll_rate_{};
Object::Ref<base::AppTimer> touch_delay_timer_; millisecs_t inertia_scroll_update_time_ms_{};
millisecs_t last_velocity_event_time_millisecs_{};
int touch_held_click_count_{};
bool touch_is_scrolling_{};
bool touch_down_sent_{};
bool touch_up_sent_{};
bool new_scroll_touch_{};
bool touch_held_{};
bool touch_mode_{};
bool has_momentum_{true};
bool trough_dirty_{true};
bool shadow_dirty_{true};
bool glow_dirty_{true};
bool thumb_dirty_{true};
bool center_small_content_{};
bool highlight_{true};
bool capture_arrows_{};
bool mouse_held_scroll_down_{};
bool mouse_held_scroll_up_{};
bool mouse_held_thumb_{};
bool mouse_held_page_down_{};
bool mouse_held_page_up_{};
bool mouse_over_thumb_{};
bool mouse_over_{};
bool have_drawn_{};
}; };
} // namespace ballistica::ui_v1 } // namespace ballistica::ui_v1

View File

@ -87,7 +87,9 @@ void ImageWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
// Draw brightness. // Draw brightness.
float db = 1.0f; float db = 1.0f;
if (Widget* draw_controller = draw_control_parent()) { if (Widget* draw_controller = draw_control_parent()) {
db *= draw_controller->GetDrawBrightness(current_time); db *= (draw_controller_mult_
* draw_controller->GetDrawBrightness(current_time))
+ (1.0f - draw_controller_mult_) * 1.0f;
} }
// Opaque portion may get drawn transparent or opaque depending on our // Opaque portion may get drawn transparent or opaque depending on our

View File

@ -43,6 +43,9 @@ class ImageWidget : public Widget {
tint2_color_green_ = g; tint2_color_green_ = g;
tint2_color_blue_ = b; tint2_color_blue_ = b;
} }
void set_draw_controller_mult(float val) {
draw_controller_mult_ = std::max(0.0f, std::min(1.0f, val));
}
void set_opacity(float o) { opacity_ = o; } void set_opacity(float o) { opacity_ = o; }
void SetTexture(base::TextureAsset* val) { texture_ = val; } void SetTexture(base::TextureAsset* val) { texture_ = val; }
void SetTintTexture(base::TextureAsset* val) { tint_texture_ = val; } void SetTintTexture(base::TextureAsset* val) { tint_texture_ = val; }
@ -89,6 +92,7 @@ class ImageWidget : public Widget {
float tint2_color_green_{1.0f}; float tint2_color_green_{1.0f};
float tint2_color_blue_{1.0f}; float tint2_color_blue_{1.0f};
float opacity_{1.0f}; float opacity_{1.0f};
float draw_controller_mult_{1.0f};
}; };
} // namespace ballistica::ui_v1 } // namespace ballistica::ui_v1

View File

@ -24,8 +24,8 @@ void ScrollWidget::OnTouchDelayTimerExpired() {
if (touch_held_) { if (touch_held_) {
// Pass a mouse-down event if we haven't moved. // Pass a mouse-down event if we haven't moved.
if (!touch_is_scrolling_ && !touch_down_sent_) { if (!touch_is_scrolling_ && !touch_down_sent_) {
// Gather up any user code triggered by this stuff and run it at the end // Gather up any user code triggered by this stuff and run it at the
// before we return. // end before we return.
base::UI::OperationContext ui_op_context; base::UI::OperationContext ui_op_context;
ContainerWidget::HandleMessage(base::WidgetMessage( ContainerWidget::HandleMessage(base::WidgetMessage(
@ -42,7 +42,7 @@ void ScrollWidget::OnTouchDelayTimerExpired() {
touch_delay_timer_.Clear(); touch_delay_timer_.Clear();
} }
void ScrollWidget::ClampThumb(bool velocity_clamp, bool position_clamp) { void ScrollWidget::ClampThumb_(bool velocity_clamp, bool position_clamp) {
BA_DEBUG_UI_READ_LOCK; BA_DEBUG_UI_READ_LOCK;
bool is_scrolling; bool is_scrolling;
@ -118,7 +118,7 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
smoothing_amount_ = 1.0f; // So we can see the transition. smoothing_amount_ = 1.0f; // So we can see the transition.
child_offset_v_ -= (60); child_offset_v_ -= (60);
MarkForUpdate(); MarkForUpdate();
ClampThumb(false, true); ClampThumb_(false, true);
} }
break; break;
@ -127,7 +127,7 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
smoothing_amount_ = 1.0f; // So we can see the transition. smoothing_amount_ = 1.0f; // So we can see the transition.
child_offset_v_ += (60); child_offset_v_ += (60);
MarkForUpdate(); MarkForUpdate();
ClampThumb(false, true); ClampThumb_(false, true);
} }
break; break;
@ -187,8 +187,8 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
if (ContainerWidget::HandleMessage(m)) { if (ContainerWidget::HandleMessage(m)) {
claimed = true; claimed = true;
// Keep track of the average scrolling going on. (only update when we // Keep track of the average scrolling going on. (only update when
// get non-momentum events) // we get non-momentum events)
if (std::abs(m.fval3) > 0.001f && !has_momentum_) { if (std::abs(m.fval3) > 0.001f && !has_momentum_) {
float smoothing = 0.8f; float smoothing = 0.8f;
avg_scroll_speed_h_ = avg_scroll_speed_h_ =
@ -207,8 +207,8 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
float x = m.fval1; float x = m.fval1;
float y = m.fval2; float y = m.fval2;
// Keep track of the average scrolling going on. (only update when we get // Keep track of the average scrolling going on. (only update when we
// non-momentum events). // get non-momentum events).
if (std::abs(m.fval3) > 0.001f && !has_momentum_) { if (std::abs(m.fval3) > 0.001f && !has_momentum_) {
float smoothing = 0.8f; float smoothing = 0.8f;
avg_scroll_speed_v_ = avg_scroll_speed_v_ =
@ -219,9 +219,9 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
smoothing * avg_scroll_speed_h_ + (1.0f - smoothing) * 0.0f; smoothing * avg_scroll_speed_h_ + (1.0f - smoothing) * 0.0f;
} }
// If a child appears to be looking at horizontal scroll events and we're // If a child appears to be looking at horizontal scroll events and
// scrolling more horizontally than vertically in general, ignore vertical // we're scrolling more horizontally than vertically in general,
// scrolling (should probably make this less fuzzy). // ignore vertical scrolling (should probably make this less fuzzy).
bool ignore_regular_scrolling = false; bool ignore_regular_scrolling = false;
bool child_claimed_h_scroll_recently = bool child_claimed_h_scroll_recently =
(g_core->GetAppTimeMillisecs() - last_sub_widget_h_scroll_claim_time_ (g_core->GetAppTimeMillisecs() - last_sub_widget_h_scroll_claim_time_
@ -301,8 +301,8 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
float y = m.fval2; float y = m.fval2;
if ((x >= 0.0f) && (x < width() + right_overlap) && (y >= 0.0f) if ((x >= 0.0f) && (x < width() + right_overlap) && (y >= 0.0f)
&& (y < height())) { && (y < height())) {
// On touch devices, touches begin scrolling, (and eventually can count // On touch devices, touches begin scrolling, (and eventually can
// as clicks if they don't move). // count as clicks if they don't move).
if (touch_mode_) { if (touch_mode_) {
touch_held_ = true; touch_held_ = true;
auto click_count = static_cast<int>(m.fval3); auto click_count = static_cast<int>(m.fval3);
@ -318,8 +318,8 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
child_is_scrolling_ = false; child_is_scrolling_ = false;
child_disowned_scroll_ = false; child_disowned_scroll_ = false;
// If there's already significant scrolling happening, we handle all // If there's already significant scrolling happening, we handle
// these ourself as scroll events. // all these ourself as scroll events.
if (std::abs(inertia_scroll_rate_) > 0.05f) { if (std::abs(inertia_scroll_rate_) > 0.05f) {
touch_is_scrolling_ = true; touch_is_scrolling_ = true;
} }
@ -361,19 +361,18 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
smoothing_amount_ = 1.0f; smoothing_amount_ = 1.0f;
child_offset_v_ -= (height() - 2 * (border_height_ + V_MARGIN)); child_offset_v_ -= (height() - 2 * (border_height_ + V_MARGIN));
MarkForUpdate(); MarkForUpdate();
ClampThumb(false, true); ClampThumb_(false, true);
} else if (y >= sb_thumb_top - sb_thumb_height) { } else if (y >= sb_thumb_top - sb_thumb_height) {
// On thumb. // On thumb.
mouse_held_thumb_ = true; mouse_held_thumb_ = true;
thumb_click_start_v_ = y; thumb_click_start_v_ = y;
thumb_click_start_child_offset_v_ = child_offset_v_; thumb_click_start_child_offset_v_ = child_offset_v_;
} else if (y >= s_bottom) { } else if (y >= s_bottom) {
// Below thumb (page down). // Below thumb (page down). So we can see the transition.
// So we can see the transition.
smoothing_amount_ = 1.0f; smoothing_amount_ = 1.0f;
child_offset_v_ += (height() - 2 * (border_height_ + V_MARGIN)); child_offset_v_ += (height() - 2 * (border_height_ + V_MARGIN));
MarkForUpdate(); MarkForUpdate();
ClampThumb(false, true); ClampThumb_(false, true);
} }
} }
} }
@ -388,12 +387,13 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
float y = m.fval2; float y = m.fval2;
bool was_claimed = (m.fval3 > 0.0f); bool was_claimed = (m.fval3 > 0.0f);
// If coords are outside of our bounds we don't want to pass mouse-moved // If coords are outside of our bounds we don't want to pass
// events through the standard container logic. (otherwise, if we mouse // mouse-moved events through the standard container logic.
// down over a button that doesn't overlap the scroll area but overlaps // (otherwise, if we mouse down over a button that doesn't overlap the
// some widget in the scroll area, the widget would claim the move and the // scroll area but overlaps some widget in the scroll area, the widget
// button would lose its mouse-over-highlight; ew.) There may be some // would claim the move and the button would lose its
// case where we *would* want to pass this though. // mouse-over-highlight; ew.) There may be some case where we *would*
// want to pass this though.
if (!((x >= 0.0f) && (x < width() + right_overlap) && (y >= 0.0f) if (!((x >= 0.0f) && (x < width() + right_overlap) && (y >= 0.0f)
&& (y < height()))) { && (y < height()))) {
pass = false; pass = false;
@ -404,14 +404,15 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
} else { } else {
if (touch_mode_) { if (touch_mode_) {
if (touch_held_) { if (touch_held_) {
// If we have a child claiming this scrolling action for themselves, // If we have a child claiming this scrolling action for
// just keep passing them the events as long as they get claimed. // themselves, just keep passing them the events as long as they
// get claimed.
if (child_is_scrolling_ && !child_disowned_scroll_) { if (child_is_scrolling_ && !child_disowned_scroll_) {
bool move_claimed = ContainerWidget::HandleMessage( bool move_claimed = ContainerWidget::HandleMessage(
base::WidgetMessage(base::WidgetMessage::Type::kMouseMove, base::WidgetMessage(base::WidgetMessage::Type::kMouseMove,
nullptr, m.fval1, m.fval2, m.fval3)); nullptr, m.fval1, m.fval2, m.fval3));
// If they stopped claiming them, send a scroll-mouse-up to tie // If they stopped claiming them, send a scroll-mouse-up to
// things up. // tie things up.
if (!move_claimed) { if (!move_claimed) {
ContainerWidget::HandleMessage( ContainerWidget::HandleMessage(
base::WidgetMessage(base::WidgetMessage::Type::kMouseUp, base::WidgetMessage(base::WidgetMessage::Type::kMouseUp,
@ -419,12 +420,13 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
child_disowned_scroll_ = true; child_disowned_scroll_ = true;
} }
} else { } else {
// If no child is scrolling; this touch motion is ours to handle. // If no child is scrolling; this touch motion is ours to
// handle.
touch_x_ = x; touch_x_ = x;
touch_y_ = y; touch_y_ = y;
// If we move more than a slight amount it means our touch isn't a // If we move more than a slight amount it means our touch
// click. // isn't a click.
if (!touch_is_scrolling_ if (!touch_is_scrolling_
&& ((std::abs(touch_x_ - touch_start_x_) > 10.0f) && ((std::abs(touch_x_ - touch_start_x_) > 10.0f)
|| (std::abs(touch_y_ - touch_start_y_) > 10.0f))) { || (std::abs(touch_y_ - touch_start_y_) > 10.0f))) {
@ -476,7 +478,7 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
/ ((1.0f - ((s_top - s_bottom) / child_h)) * (s_top - s_bottom)); / ((1.0f - ((s_top - s_bottom) / child_h)) * (s_top - s_bottom));
child_offset_v_ = thumb_click_start_child_offset_v_ child_offset_v_ = thumb_click_start_child_offset_v_
- rate * (y - thumb_click_start_v_); - rate * (y - thumb_click_start_v_);
ClampThumb(false, true); ClampThumb_(false, true);
MarkForUpdate(); MarkForUpdate();
} }
break; break;
@ -491,8 +493,8 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
if (touch_held_) { if (touch_held_) {
touch_held_ = false; touch_held_ = false;
// If we moved at all, we mark it as claimed to keep // If we moved at all, we mark it as claimed to keep sub-widgets
// sub-widgets from acting on it (since we used it for scrolling) // from acting on it (since we used it for scrolling)
bool claimed2 = touch_is_scrolling_ || child_is_scrolling_; bool claimed2 = touch_is_scrolling_ || child_is_scrolling_;
// if a child is still scrolling, send them a scroll-mouse-up // if a child is still scrolling, send them a scroll-mouse-up
@ -502,8 +504,8 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
nullptr, m.fval1, m.fval2, false)); nullptr, m.fval1, m.fval2, false));
} }
// If we're not claiming it and we haven't sent a mouse_down yet due // If we're not claiming it and we haven't sent a mouse_down yet
// to our delay, send that first.. // due to our delay, send that first..
if (!claimed2 && !touch_down_sent_) { if (!claimed2 && !touch_down_sent_) {
ContainerWidget::HandleMessage(base::WidgetMessage( ContainerWidget::HandleMessage(base::WidgetMessage(
base::WidgetMessage::Type::kMouseDown, nullptr, m.fval1, base::WidgetMessage::Type::kMouseDown, nullptr, m.fval1,
@ -521,9 +523,9 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
} }
} }
// If coords are outside of our bounds, pass a mouse-up along for anyone // If coords are outside of our bounds, pass a mouse-up along for
// tracking a drag, but mark it as claimed so it doesn't actually get // anyone tracking a drag, but mark it as claimed so it doesn't
// acted on. // actually get acted on.
float x = m.fval1; float x = m.fval1;
float y = m.fval2; float y = m.fval2;
if (!((x >= 0.0f) && (x < width() + right_overlap) && (y >= 0.0f) if (!((x >= 0.0f) && (x < width() + right_overlap) && (y >= 0.0f)
@ -558,7 +560,7 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
void ScrollWidget::UpdateLayout() { void ScrollWidget::UpdateLayout() {
BA_DEBUG_UI_READ_LOCK; BA_DEBUG_UI_READ_LOCK;
// move everything based on our offset // Move everything based on our offset.
auto i = widgets().begin(); auto i = widgets().begin();
if (i == widgets().end()) { if (i == widgets().end()) {
amount_visible_ = 0; amount_visible_ = 0;
@ -600,11 +602,11 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
millisecs_t current_time = pass->frame_def()->display_time_millisecs(); millisecs_t current_time = pass->frame_def()->display_time_millisecs();
float prev_child_offset_v_smoothed = child_offset_v_smoothed_; float prev_child_offset_v_smoothed = child_offset_v_smoothed_;
// ok lets update our inertial scrolling during the opaque pass // Ok, lets update our inertial scrolling during the opaque pass (we
// (we really should have some sort of update() function for this but widgets // really should have some sort of update() function for this but widgets
// don't have that) // don't have that).
if (!draw_transparent) { if (!draw_transparent) {
// (skip huge differences) // Skip huge differences.
if (current_time - inertia_scroll_update_time_ > 1000) { if (current_time - inertia_scroll_update_time_ > 1000) {
inertia_scroll_update_time_ = current_time - 1000; inertia_scroll_update_time_ = current_time - 1000;
} }
@ -623,16 +625,17 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
} else { } else {
inertia_scroll_rate_ *= 0.98f; inertia_scroll_rate_ *= 0.98f;
} }
ClampThumb(true, mouse_held_thumb_); ClampThumb_(true, mouse_held_thumb_);
child_offset_v_ += inertia_scroll_rate_; child_offset_v_ += inertia_scroll_rate_;
if (!has_momentum_ if (!has_momentum_
&& (current_time - last_velocity_event_time_millisecs_ > 1000 / 30)) && (current_time - last_velocity_event_time_millisecs_ > 1000 / 30))
inertia_scroll_rate_ = 0; inertia_scroll_rate_ = 0;
// lastly we apply smoothing so that if we're snapping to a specific place // Lastly we apply smoothing so that if we're snapping to a specific
// we don't go instantly there we blend between smoothed and non-smoothed // place we don't go instantly there we blend between smoothed and
// depending on whats driving us (we dont want to add smoothing on top of // non-smoothed depending on whats driving us (we dont want to add
// inertial scrolling for example or it'll feel muddy) // smoothing on top of inertial scrolling for example or it'll feel
// muddy).
float diff = child_offset_v_ - child_offset_v_smoothed_; float diff = child_offset_v_ - child_offset_v_smoothed_;
if (std::abs(diff) < 1.0f) { if (std::abs(diff) < 1.0f) {
child_offset_v_smoothed_ = child_offset_v_; child_offset_v_smoothed_ = child_offset_v_;
@ -641,7 +644,8 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
} }
smoothing_amount_ = std::max(0.0f, smoothing_amount_ - 0.005f); smoothing_amount_ = std::max(0.0f, smoothing_amount_ - 0.005f);
} }
// only re-layout our widgets if we've moved a significant amount
// Only re-layout our widgets if we've moved a significant amount.
if (std::abs(prev_child_offset_v_smoothed - child_offset_v_smoothed_) if (std::abs(prev_child_offset_v_smoothed - child_offset_v_smoothed_)
> 0.01f) { > 0.01f) {
MarkForUpdate(); MarkForUpdate();
@ -675,7 +679,7 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
1.0f); 1.0f);
} }
// scroll trough (depth 0.7f to 0.8f) // Scroll trough (depth 0.7 to 0.8).
if (draw_transparent) { if (draw_transparent) {
if (trough_dirty_) { if (trough_dirty_) {
float r2 = l + width(); float r2 = l + width();
@ -709,9 +713,9 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
c.Submit(); c.Submit();
} }
// scroll bars // Scroll bars.
if (amount_visible_ > 0 && amount_visible_ < 1) { if (amount_visible_ > 0 && amount_visible_ < 1) {
// scroll thumb at depth 0.8f-0.9 // Scroll thumb at depth 0.8 - 0.9.
{ {
float sb_thumb_height = amount_visible_ * (height() - 2 * border_height_); float sb_thumb_height = amount_visible_ * (height() - 2 * border_height_);
if (thumb_dirty_) { if (thumb_dirty_) {
@ -777,7 +781,7 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
} }
} }
// outline shadow (depth 0.9f to 1.0f) // Outline shadow (depth 0.9 to 1.0).
if (draw_transparent) { if (draw_transparent) {
if (shadow_dirty_) { if (shadow_dirty_) {
float r2 = l + width(); float r2 = l + width();
@ -811,7 +815,7 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
} }
} }
// if selected, do glow at depth 0.9f-1.0 // If selected, do glow at depth 0.9 - 1.0.
if (draw_transparent && IsHierarchySelected() if (draw_transparent && IsHierarchySelected()
&& g_base->ui->ShouldHighlightWidgets() && highlight_) { && g_base->ui->ShouldHighlightWidgets() && highlight_) {
float m = 0.8f float m = 0.8f

View File

@ -47,37 +47,33 @@ class ScrollWidget : public ContainerWidget {
void UpdateLayout() override; void UpdateLayout() override;
private: private:
void ClampThumb(bool velocity_clamp, bool position_clamp); void ClampThumb_(bool velocity_clamp, bool position_clamp);
bool touch_mode_{};
Object::Ref<base::AppTimer> touch_delay_timer_;
millisecs_t last_sub_widget_h_scroll_claim_time_{};
millisecs_t last_velocity_event_time_millisecs_{};
millisecs_t inertia_scroll_update_time_{};
int touch_held_click_count_{};
float color_red_{0.55f}; float color_red_{0.55f};
float color_green_{0.47f}; float color_green_{0.47f};
float color_blue_{0.67f}; float color_blue_{0.67f};
bool has_momentum_{true};
bool trough_dirty_{true};
bool shadow_dirty_{true};
bool glow_dirty_{true};
bool thumb_dirty_{true};
millisecs_t last_sub_widget_h_scroll_claim_time_{};
millisecs_t last_velocity_event_time_millisecs_{};
float avg_scroll_speed_h_{}; float avg_scroll_speed_h_{};
float avg_scroll_speed_v_{}; float avg_scroll_speed_v_{};
bool center_small_content_{};
float center_offset_y_{}; float center_offset_y_{};
bool touch_held_{};
int touch_held_click_count_{};
float touch_down_y_{}; float touch_down_y_{};
float touch_x_{}; float touch_x_{};
float touch_y_{}; float touch_y_{};
float touch_start_x_{}; float touch_start_x_{};
float touch_start_y_{}; float touch_start_y_{};
bool touch_is_scrolling_{}; float trough_width_{};
bool touch_down_sent_{}; float trough_height_{};
bool touch_up_sent_{}; float trough_center_x_{};
float trough_width_{}, trough_height_{}, trough_center_x_{}, float trough_center_y_{};
trough_center_y_{}; float thumb_width_{};
float thumb_width_{}, thumb_height_{}, thumb_center_x_{}, thumb_center_y_{}; float thumb_height_{};
float thumb_center_x_{};
float thumb_center_y_{};
float smoothing_amount_{1.0f}; float smoothing_amount_{1.0f};
bool highlight_{true};
float glow_width_{}; float glow_width_{};
float glow_height_{}; float glow_height_{};
float glow_center_x_{}; float glow_center_x_{};
@ -87,15 +83,8 @@ class ScrollWidget : public ContainerWidget {
float outline_center_x_{}; float outline_center_x_{};
float outline_center_y_{}; float outline_center_y_{};
float border_opacity_{1.0f}; float border_opacity_{1.0f};
bool capture_arrows_{false};
bool mouse_held_scroll_down_{};
bool mouse_held_scroll_up_{};
bool mouse_held_thumb_{};
float thumb_click_start_v_{}; float thumb_click_start_v_{};
float thumb_click_start_child_offset_v_{}; float thumb_click_start_child_offset_v_{};
bool mouse_held_page_down_{};
bool mouse_held_page_up_{};
bool mouse_over_thumb_{};
float scroll_bar_width_{10.0f}; float scroll_bar_width_{10.0f};
float border_width_{2.0f}; float border_width_{2.0f};
float border_height_{2.0f}; float border_height_{2.0f};
@ -103,13 +92,30 @@ class ScrollWidget : public ContainerWidget {
float child_offset_v_smoothed_{}; float child_offset_v_smoothed_{};
float child_max_offset_{}; float child_max_offset_{};
float amount_visible_{}; float amount_visible_{};
float inertia_scroll_rate_{};
bool mouse_held_page_down_{};
bool mouse_held_page_up_{};
bool mouse_over_thumb_{};
bool touch_is_scrolling_{};
bool touch_down_sent_{};
bool touch_up_sent_{};
bool touch_mode_{};
bool has_momentum_{true};
bool trough_dirty_{true};
bool shadow_dirty_{true};
bool glow_dirty_{true};
bool thumb_dirty_{true};
bool center_small_content_{};
bool touch_held_{};
bool highlight_{true};
bool capture_arrows_{false};
bool mouse_held_scroll_down_{};
bool mouse_held_scroll_up_{};
bool mouse_held_thumb_{};
bool have_drawn_{}; bool have_drawn_{};
bool touch_down_passed_{}; bool touch_down_passed_{};
bool child_is_scrolling_{}; bool child_is_scrolling_{};
bool child_disowned_scroll_{}; bool child_disowned_scroll_{};
millisecs_t inertia_scroll_update_time_{};
float inertia_scroll_rate_{};
Object::Ref<base::AppTimer> touch_delay_timer_;
}; };
} // namespace ballistica::ui_v1 } // namespace ballistica::ui_v1

View File

@ -250,3 +250,39 @@ class ManageAccountResponse(Response):
"""Here's that sign-in result you asked for, boss.""" """Here's that sign-in result you asked for, boss."""
url: Annotated[str | None, IOAttrs('u')] url: Annotated[str | None, IOAttrs('u')]
@ioprepped
@dataclass
class StoreQueryMessage(Message):
"""Message asking about purchasable stuff and store related state."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [StoreQueryResponse]
@ioprepped
@dataclass
class StoreQueryResponse(Response):
"""Here's that store info you asked for, boss."""
class Result(Enum):
"""Our overall result."""
SUCCESS = 's'
ERROR = 'e'
@dataclass
class Purchase:
"""Info about a purchasable thing."""
purchaseid: Annotated[str, IOAttrs('id')]
# Overall result; all data is undefined if not SUCCESS.
result: Annotated[Result, IOAttrs('r')]
tokens: Annotated[int, IOAttrs('t')]
available_purchases: Annotated[list[Purchase], IOAttrs('p')]
token_info_url: Annotated[str, IOAttrs('tiu')]

View File

@ -30,6 +30,10 @@ class AssetsV1GlobalVals:
str | None, IOAttrs('base_assets', store_default=False) str | None, IOAttrs('base_assets', store_default=False)
] = None ] = None
base_assets_filter: Annotated[
str, IOAttrs('base_assets_filter', store_default=False)
] = ''
class AssetsV1PathValsTypeID(Enum): class AssetsV1PathValsTypeID(Enum):
"""Types of vals we can store for paths.""" """Types of vals we can store for paths."""

View File

@ -615,7 +615,7 @@ def cmake_prep_dir(dirname: str, verbose: bool = False) -> None:
# ...or if homebrew SDL.h resolved path changes (happens for updates) # ...or if homebrew SDL.h resolved path changes (happens for updates)
sdl_h_path = Path('/opt/homebrew/include/SDL2/SDL.h') sdl_h_path = Path('/opt/homebrew/include/SDL2/SDL.h')
homebrew_sdl_h_resolved: str = ( homebrew_sdl_h_resolved: str = (
str(sdl_h_path.resolve()) if sdl_h_path.is_symlink() else '' str(sdl_h_path.resolve()) if sdl_h_path.exists() else ''
) )
entries.append(Entry('homebrew_sdl_h_resolved', homebrew_sdl_h_resolved)) entries.append(Entry('homebrew_sdl_h_resolved', homebrew_sdl_h_resolved))

View File

@ -4,328 +4,14 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, TypeVar, Generic, Callable, cast
import functools import functools
from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, overload pass
CT = TypeVar('CT', bound=Callable) # TODO: should deprecate tpartial since it nowadays simply wraps
# functools.partial (mypy added support for functools.partial in 1.11 so
# there's no benefit to rolling our own type-safe version anymore).
class _CallbackCall(Generic[CT]): # Perhaps we can use Python 13's @warnings.deprecated() stuff for this.
"""Descriptor for exposing a call with a type defined by a TypeVar.""" tpartial = functools.partial
def __get__(self, obj: Any, type_in: Any = None) -> CT:
return cast(CT, None)
class CallbackSet(Generic[CT]):
"""Wrangles callbacks for a particular event in a type-safe manner."""
# In the type-checker's eyes, our 'run' attr is a CallbackCall which
# returns a callable with the type we were created with. This lets us
# type-check our run calls. (Is there another way to expose a function
# with a signature defined by a generic?..)
# At runtime, run() simply passes its args verbatim to its registered
# callbacks; no types are checked.
if TYPE_CHECKING:
run: _CallbackCall[CT] = _CallbackCall()
else:
def run(self, *args, **keywds):
"""Run all callbacks."""
print('HELLO FROM RUN', *args, **keywds)
def __init__(self) -> None:
print('CallbackSet()')
def __del__(self) -> None:
print('~CallbackSet()')
def add(self, call: CT) -> None:
"""Add a callback to be run."""
print('Would add call', call)
# Define Call() which can be used in type-checking call-wrappers that behave
# similarly to functools.partial (in that they take a callable and some
# positional arguments to be passed to it).
# In type-checking land, We define several different _CallXArg classes
# corresponding to different argument counts and define Call() as an
# overloaded function which returns one of them based on how many args are
# passed.
# To use this, simply assign your call type to this Call for type checking:
# Example:
# class _MyCallWrapper:
# <runtime class defined here>
# if TYPE_CHECKING:
# MyCallWrapper = efro.call.Call
# else:
# MyCallWrapper = _MyCallWrapper
# Note that this setup currently only works with positional arguments; if you
# would like to pass args via keyword you can wrap a lambda or local function
# which takes keyword args and converts to a call containing keywords.
if TYPE_CHECKING:
In1T = TypeVar('In1T')
In2T = TypeVar('In2T')
In3T = TypeVar('In3T')
In4T = TypeVar('In4T')
In5T = TypeVar('In5T')
In6T = TypeVar('In6T')
In7T = TypeVar('In7T')
OutT = TypeVar('OutT')
class _CallNoArgs(Generic[OutT]):
"""Single argument variant of call wrapper."""
def __init__(self, _call: Callable[[], OutT]): ...
def __call__(self) -> OutT: ...
class _Call1Arg(Generic[In1T, OutT]):
"""Single argument variant of call wrapper."""
def __init__(self, _call: Callable[[In1T], OutT]): ...
def __call__(self, _arg1: In1T) -> OutT: ...
class _Call2Args(Generic[In1T, In2T, OutT]):
"""Two argument variant of call wrapper"""
def __init__(self, _call: Callable[[In1T, In2T], OutT]): ...
def __call__(self, _arg1: In1T, _arg2: In2T) -> OutT: ...
class _Call3Args(Generic[In1T, In2T, In3T, OutT]):
"""Three argument variant of call wrapper"""
def __init__(self, _call: Callable[[In1T, In2T, In3T], OutT]): ...
def __call__(self, _arg1: In1T, _arg2: In2T, _arg3: In3T) -> OutT: ...
class _Call4Args(Generic[In1T, In2T, In3T, In4T, OutT]):
"""Four argument variant of call wrapper"""
def __init__(self, _call: Callable[[In1T, In2T, In3T, In4T], OutT]): ...
def __call__(
self, _arg1: In1T, _arg2: In2T, _arg3: In3T, _arg4: In4T
) -> OutT: ...
class _Call5Args(Generic[In1T, In2T, In3T, In4T, In5T, OutT]):
"""Five argument variant of call wrapper"""
def __init__(
self, _call: Callable[[In1T, In2T, In3T, In4T, In5T], OutT]
): ...
def __call__(
self,
_arg1: In1T,
_arg2: In2T,
_arg3: In3T,
_arg4: In4T,
_arg5: In5T,
) -> OutT: ...
class _Call6Args(Generic[In1T, In2T, In3T, In4T, In5T, In6T, OutT]):
"""Six argument variant of call wrapper"""
def __init__(
self, _call: Callable[[In1T, In2T, In3T, In4T, In5T, In6T], OutT]
): ...
def __call__(
self,
_arg1: In1T,
_arg2: In2T,
_arg3: In3T,
_arg4: In4T,
_arg5: In5T,
_arg6: In6T,
) -> OutT: ...
class _Call7Args(Generic[In1T, In2T, In3T, In4T, In5T, In6T, In7T, OutT]):
"""Seven argument variant of call wrapper"""
def __init__(
self,
_call: Callable[[In1T, In2T, In3T, In4T, In5T, In6T, In7T], OutT],
): ...
def __call__(
self,
_arg1: In1T,
_arg2: In2T,
_arg3: In3T,
_arg4: In4T,
_arg5: In5T,
_arg6: In6T,
_arg7: In7T,
) -> OutT: ...
# No arg call; no args bundled.
# noinspection PyPep8Naming
@overload
def Call(call: Callable[[], OutT]) -> _CallNoArgs[OutT]: ...
# 1 arg call; 1 arg bundled.
# noinspection PyPep8Naming
@overload
def Call(call: Callable[[In1T], OutT], arg1: In1T) -> _CallNoArgs[OutT]: ...
# 1 arg call; no args bundled.
# noinspection PyPep8Naming
@overload
def Call(call: Callable[[In1T], OutT]) -> _Call1Arg[In1T, OutT]: ...
# 2 arg call; 2 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T], OutT], arg1: In1T, arg2: In2T
) -> _CallNoArgs[OutT]: ...
# 2 arg call; 1 arg bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T], OutT], arg1: In1T
) -> _Call1Arg[In2T, OutT]: ...
# 2 arg call; no args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T], OutT]
) -> _Call2Args[In1T, In2T, OutT]: ...
# 3 arg call; 3 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T], OutT],
arg1: In1T,
arg2: In2T,
arg3: In3T,
) -> _CallNoArgs[OutT]: ...
# 3 arg call; 2 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T], OutT], arg1: In1T, arg2: In2T
) -> _Call1Arg[In3T, OutT]: ...
# 3 arg call; 1 arg bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T], OutT], arg1: In1T
) -> _Call2Args[In2T, In3T, OutT]: ...
# 3 arg call; no args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T], OutT]
) -> _Call3Args[In1T, In2T, In3T, OutT]: ...
# 4 arg call; 4 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T], OutT],
arg1: In1T,
arg2: In2T,
arg3: In3T,
arg4: In4T,
) -> _CallNoArgs[OutT]: ...
# 4 arg call; 3 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T], OutT],
arg1: In1T,
arg2: In2T,
arg3: In3T,
) -> _Call1Arg[In4T, OutT]: ...
# 4 arg call; 2 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T], OutT],
arg1: In1T,
arg2: In2T,
) -> _Call2Args[In3T, In4T, OutT]: ...
# 4 arg call; 1 arg bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T], OutT],
arg1: In1T,
) -> _Call3Args[In2T, In3T, In4T, OutT]: ...
# 4 arg call; no args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T], OutT],
) -> _Call4Args[In1T, In2T, In3T, In4T, OutT]: ...
# 5 arg call; 5 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T, In5T], OutT],
arg1: In1T,
arg2: In2T,
arg3: In3T,
arg4: In4T,
arg5: In5T,
) -> _CallNoArgs[OutT]: ...
# 6 arg call; 6 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T, In5T, In6T], OutT],
arg1: In1T,
arg2: In2T,
arg3: In3T,
arg4: In4T,
arg5: In5T,
arg6: In6T,
) -> _CallNoArgs[OutT]: ...
# 7 arg call; 7 args bundled.
# noinspection PyPep8Naming
@overload
def Call(
call: Callable[[In1T, In2T, In3T, In4T, In5T, In6T, In7T], OutT],
arg1: In1T,
arg2: In2T,
arg3: In3T,
arg4: In4T,
arg5: In5T,
arg6: In6T,
arg7: In7T,
) -> _CallNoArgs[OutT]: ...
# noinspection PyPep8Naming
def Call(*_args: Any, **_keywds: Any) -> Any: ...
# (Type-safe Partial)
# A convenient wrapper around functools.partial which adds type-safety
# (though it does not support keyword arguments).
tpartial = Call
else:
tpartial = functools.partial

View File

@ -54,15 +54,19 @@ class _Outputter:
def run(self) -> Any: def run(self) -> Any:
"""Do the thing.""" """Do the thing."""
obj = self._obj
# mypy workaround - if we check 'obj' here it assumes the
# isinstance call below fails.
assert dataclasses.is_dataclass(self._obj) assert dataclasses.is_dataclass(self._obj)
# For special extended data types, call their 'will_output' callback. # For special extended data types, call their 'will_output' callback.
# FIXME - should probably move this into _process_dataclass so it # FIXME - should probably move this into _process_dataclass so it
# can work on nested values. # can work on nested values.
if isinstance(self._obj, IOExtendedData): if isinstance(obj, IOExtendedData):
self._obj.will_output() obj.will_output()
return self._process_dataclass(type(self._obj), self._obj, '') return self._process_dataclass(type(obj), obj, '')
def soft_default_check( def soft_default_check(
self, value: Any, anntype: Any, fieldpath: str self, value: Any, anntype: Any, fieldpath: str

View File

@ -10,13 +10,13 @@ import logging
import datetime import datetime
import itertools import itertools
from enum import Enum from enum import Enum
from functools import partial
from collections import deque from collections import deque
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Annotated, override from typing import TYPE_CHECKING, Annotated, override
from threading import Thread, current_thread, Lock from threading import Thread, current_thread, Lock
from efro.util import utc_now from efro.util import utc_now
from efro.call import tpartial
from efro.terminal import Clr from efro.terminal import Clr
from efro.dataclassio import ioprepped, IOAttrs, dataclass_to_json from efro.dataclassio import ioprepped, IOAttrs, dataclass_to_json
@ -178,7 +178,7 @@ class LogHandler(logging.Handler):
# process cached entries at the same time to ensure there are no # process cached entries at the same time to ensure there are no
# race conditions that could cause entries to be skipped/etc. # race conditions that could cause entries to be skipped/etc.
self._event_loop.call_soon_threadsafe( self._event_loop.call_soon_threadsafe(
tpartial(self._add_callback_in_thread, call, feed_existing_logs) partial(self._add_callback_in_thread, call, feed_existing_logs)
) )
def _add_callback_in_thread( def _add_callback_in_thread(
@ -342,7 +342,7 @@ class LogHandler(logging.Handler):
if __debug__: if __debug__:
formattime = echotime = time.monotonic() formattime = echotime = time.monotonic()
self._event_loop.call_soon_threadsafe( self._event_loop.call_soon_threadsafe(
tpartial( partial(
self._emit_in_thread, self._emit_in_thread,
record.name, record.name,
record.levelno, record.levelno,
@ -395,7 +395,7 @@ class LogHandler(logging.Handler):
echotime = time.monotonic() echotime = time.monotonic()
self._event_loop.call_soon_threadsafe( self._event_loop.call_soon_threadsafe(
tpartial( partial(
self._emit_in_thread, self._emit_in_thread,
record.name, record.name,
record.levelno, record.levelno,
@ -427,7 +427,7 @@ class LogHandler(logging.Handler):
# the bg event loop thread we've already got. # the bg event loop thread we've already got.
self._last_slow_emit_warning_time = now self._last_slow_emit_warning_time = now
self._event_loop.call_soon_threadsafe( self._event_loop.call_soon_threadsafe(
tpartial( partial(
logging.warning, logging.warning,
'efro.log.LogHandler emit took too long' 'efro.log.LogHandler emit took too long'
' (%.2fs total; %.2fs format, %.2fs echo,' ' (%.2fs total; %.2fs format, %.2fs echo,'
@ -477,7 +477,7 @@ class LogHandler(logging.Handler):
# another thread for each character. Perhaps should do some sort # another thread for each character. Perhaps should do some sort
# of basic accumulation here? # of basic accumulation here?
self._event_loop.call_soon_threadsafe( self._event_loop.call_soon_threadsafe(
tpartial(self._file_write_in_thread, name, output) partial(self._file_write_in_thread, name, output)
) )
def _file_write_in_thread(self, name: str, output: str) -> None: def _file_write_in_thread(self, name: str, output: str) -> None:
@ -537,7 +537,7 @@ class LogHandler(logging.Handler):
"""Send raw stdout/stderr flush to the logger to be collated.""" """Send raw stdout/stderr flush to the logger to be collated."""
self._event_loop.call_soon_threadsafe( self._event_loop.call_soon_threadsafe(
tpartial(self._file_flush_in_thread, name) partial(self._file_flush_in_thread, name)
) )
def _file_flush_in_thread(self, name: str) -> None: def _file_flush_in_thread(self, name: str) -> None:
@ -739,11 +739,7 @@ def setup_logging(
# Optionally intercept Python's stdout/stderr output and generate # Optionally intercept Python's stdout/stderr output and generate
# log entries from it. # log entries from it.
if log_stdout_stderr: if log_stdout_stderr:
sys.stdout = FileLogEcho( # type: ignore sys.stdout = FileLogEcho(sys.stdout, 'stdout', loghandler)
sys.stdout, 'stdout', loghandler sys.stderr = FileLogEcho(sys.stderr, 'stderr', loghandler)
)
sys.stderr = FileLogEcho( # type: ignore
sys.stderr, 'stderr', loghandler
)
return loghandler return loghandler

View File

@ -52,6 +52,9 @@ class MessageSender:
def __init__(self, protocol: MessageProtocol) -> None: def __init__(self, protocol: MessageProtocol) -> None:
self.protocol = protocol self.protocol = protocol
self._send_raw_message_call: Callable[[Any, str], str] | None = None self._send_raw_message_call: Callable[[Any, str], str] | None = None
self._send_raw_message_ex_call: (
Callable[[Any, str, Message], str] | None
) = None
self._send_async_raw_message_call: ( self._send_async_raw_message_call: (
Callable[[Any, str], Awaitable[str]] | None Callable[[Any, str], Awaitable[str]] | None
) = None ) = None
@ -80,6 +83,19 @@ class MessageSender:
self._send_raw_message_call = call self._send_raw_message_call = call
return call return call
def send_ex_method(
self, call: Callable[[Any, str, Message], str]
) -> Callable[[Any, str, Message], str]:
"""Function decorator for extended send method.
Version of send_method which is also is passed the original
unencoded message; can be useful for cases where metadata is sent
along with messages referring to their payloads/etc.
"""
assert self._send_raw_message_ex_call is None
self._send_raw_message_ex_call = call
return call
def send_async_method( def send_async_method(
self, call: Callable[[Any, str], Awaitable[str]] self, call: Callable[[Any, str], Awaitable[str]]
) -> Callable[[Any, str], Awaitable[str]]: ) -> Callable[[Any, str], Awaitable[str]]:
@ -200,14 +216,23 @@ class MessageSender:
for when message sending and response handling need to happen for when message sending and response handling need to happen
in different contexts/threads. in different contexts/threads.
""" """
if self._send_raw_message_call is None: if (
self._send_raw_message_call is None
and self._send_raw_message_ex_call is None
):
raise RuntimeError('send() is unimplemented for this type.') raise RuntimeError('send() is unimplemented for this type.')
msg_encoded = self._encode_message(bound_obj, message) msg_encoded = self._encode_message(bound_obj, message)
try: try:
response_encoded = self._send_raw_message_call( if self._send_raw_message_ex_call is not None:
bound_obj, msg_encoded response_encoded = self._send_raw_message_ex_call(
) bound_obj, msg_encoded, message
)
else:
assert self._send_raw_message_call is not None
response_encoded = self._send_raw_message_call(
bound_obj, msg_encoded
)
except Exception as exc: except Exception as exc:
response = ErrorSysResponse( response = ErrorSysResponse(
error_message='Error in MessageSender @send_method.', error_message='Error in MessageSender @send_method.',

View File

@ -9,6 +9,7 @@ import asyncio
import logging import logging
import weakref import weakref
from enum import Enum from enum import Enum
from functools import partial
from collections import deque from collections import deque
from dataclasses import dataclass from dataclasses import dataclass
from threading import current_thread from threading import current_thread
@ -84,7 +85,6 @@ def ssl_stream_writer_underlying_transport_info(
def ssl_stream_writer_force_close_check(writer: asyncio.StreamWriter) -> None: def ssl_stream_writer_force_close_check(writer: asyncio.StreamWriter) -> None:
"""Ensure a writer is closed; hacky workaround for odd hang.""" """Ensure a writer is closed; hacky workaround for odd hang."""
from efro.call import tpartial
from threading import Thread from threading import Thread
# Disabling for now.. # Disabling for now..
@ -100,9 +100,8 @@ def ssl_stream_writer_force_close_check(writer: asyncio.StreamWriter) -> None:
raw_transport = getattr(sslproto, '_transport', None) raw_transport = getattr(sslproto, '_transport', None)
if raw_transport is not None: if raw_transport is not None:
Thread( Thread(
target=tpartial( target=partial(
_do_writer_force_close_check, _do_writer_force_close_check, weakref.ref(raw_transport)
weakref.ref(raw_transport),
), ),
daemon=True, daemon=True,
).start() ).start()

View File

@ -72,6 +72,7 @@ def _default_color_enabled() -> bool:
import platform import platform
# If our stdout is not attached to a terminal, go with no-color. # If our stdout is not attached to a terminal, go with no-color.
assert sys.__stdout__ is not None
if not sys.__stdout__.isatty(): if not sys.__stdout__.isatty():
return False return False

View File

@ -8,7 +8,6 @@ import os
import time import time
import weakref import weakref
import datetime import datetime
import functools
from enum import Enum from enum import Enum
from typing import TYPE_CHECKING, cast, TypeVar, Generic, overload from typing import TYPE_CHECKING, cast, TypeVar, Generic, overload
@ -16,8 +15,6 @@ if TYPE_CHECKING:
import asyncio import asyncio
from typing import Any, Callable, Literal from typing import Any, Callable, Literal
from efro.call import Call as Call # 'as Call' so we re-export.
T = TypeVar('T') T = TypeVar('T')
ValT = TypeVar('ValT') ValT = TypeVar('ValT')
ArgT = TypeVar('ArgT') ArgT = TypeVar('ArgT')
@ -36,13 +33,6 @@ _g_empty_weak_ref = weakref.ref(_EmptyObj())
assert _g_empty_weak_ref() is None assert _g_empty_weak_ref() is None
# TODO: kill this and just use efro.call.tpartial
if TYPE_CHECKING:
Call = Call
else:
Call = functools.partial
def explicit_bool(val: bool) -> bool: def explicit_bool(val: bool) -> bool:
"""Return a non-inferable boolean value. """Return a non-inferable boolean value.

View File

@ -14,7 +14,7 @@ from efrotools.util import readfile, writefile, replace_exact
# Python version we build here (not necessarily same as we use in repo). # Python version we build here (not necessarily same as we use in repo).
PY_VER_ANDROID = '3.12' PY_VER_ANDROID = '3.12'
PY_VER_EXACT_ANDROID = '3.12.3' PY_VER_EXACT_ANDROID = '3.12.4'
PY_VER_APPLE = '3.12' PY_VER_APPLE = '3.12'
PY_VER_EXACT_APPLE = '3.12.0' PY_VER_EXACT_APPLE = '3.12.0'
@ -38,7 +38,7 @@ VERSION_MIN_TVOS = '9.0'
# #
# For now will try to ride out this 3.0 LTS version as long as possible. # For now will try to ride out this 3.0 LTS version as long as possible.
OPENSSL_VER_APPLE = '3.0.12-1' OPENSSL_VER_APPLE = '3.0.12-1'
OPENSSL_VER_ANDROID = '3.0.13' OPENSSL_VER_ANDROID = '3.0.14'
LIBFFI_VER_APPLE = '3.4.4-1' LIBFFI_VER_APPLE = '3.4.4-1'
BZIP2_VER_APPLE = '1.0.8-1' BZIP2_VER_APPLE = '1.0.8-1'
@ -46,15 +46,16 @@ XZ_VER_APPLE = '5.4.4-1'
# Android repo doesn't seem to be getting updated much so manually # Android repo doesn't seem to be getting updated much so manually
# bumping various versions to keep things up to date. # bumping various versions to keep things up to date.
ANDROID_API_VER = 23
ZLIB_VER_ANDROID = '1.3.1' ZLIB_VER_ANDROID = '1.3.1'
XZ_VER_ANDROID = '5.4.4' XZ_VER_ANDROID = '5.6.2'
BZIP2_VER_ANDROID = '1.0.8' BZIP2_VER_ANDROID = '1.0.8'
GDBM_VER_ANDROID = '1.23' GDBM_VER_ANDROID = '1.23'
LIBFFI_VER_ANDROID = '3.4.6' LIBFFI_VER_ANDROID = '3.4.6'
LIBUUID_VER_ANDROID = ('2.39', '2.39.3') LIBUUID_VER_ANDROID = ('2.39', '2.39.3')
NCURSES_VER_ANDROID = '6.4' NCURSES_VER_ANDROID = '6.4'
READLINE_VER_ANDROID = '8.2' READLINE_VER_ANDROID = '8.2'
SQLITE_VER_ANDROID = ('2024', '3450200') SQLITE_VER_ANDROID = ('2024', '3460000')
# Filenames we prune from Python lib dirs in source repo to cut down on # Filenames we prune from Python lib dirs in source repo to cut down on
# size. # size.
@ -349,7 +350,7 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
# Set specific OpenSSL version. # Set specific OpenSSL version.
ftxt = replace_exact( ftxt = replace_exact(
ftxt, ftxt,
"source = 'https://www.openssl.org/source/openssl-3.0.7.tar.gz'", "source = 'https://www.openssl.org/source/openssl-3.0.12.tar.gz'",
f"source = 'https://www.openssl.org/" f"source = 'https://www.openssl.org/"
f"source/openssl-{OPENSSL_VER_ANDROID}.tar.gz'", f"source/openssl-{OPENSSL_VER_ANDROID}.tar.gz'",
count=1, count=1,
@ -358,7 +359,7 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
# Set specific ZLib version. # Set specific ZLib version.
ftxt = replace_exact( ftxt = replace_exact(
ftxt, ftxt,
"source = 'https://www.zlib.net/zlib-1.2.13.tar.gz'", "source = 'https://www.zlib.net/zlib-1.3.1.tar.gz'",
f"source = 'https://www.zlib.net/zlib-{ZLIB_VER_ANDROID}.tar.gz'", f"source = 'https://www.zlib.net/zlib-{ZLIB_VER_ANDROID}.tar.gz'",
count=1, count=1,
) )
@ -366,7 +367,7 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
# Set specific XZ version. # Set specific XZ version.
ftxt = replace_exact( ftxt = replace_exact(
ftxt, ftxt,
"source = 'https://tukaani.org/xz/xz-5.2.7.tar.xz'", "source = 'https://tukaani.org/xz/xz-5.6.2.tar.xz'",
f"source = 'https://tukaani.org/xz/xz-{XZ_VER_ANDROID}.tar.xz'", f"source = 'https://tukaani.org/xz/xz-{XZ_VER_ANDROID}.tar.xz'",
count=1, count=1,
) )
@ -402,7 +403,7 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
ftxt = replace_exact( ftxt = replace_exact(
ftxt, ftxt,
"source = 'https://mirrors.edge.kernel.org/pub/linux/utils/" "source = 'https://mirrors.edge.kernel.org/pub/linux/utils/"
"util-linux/v2.38/util-linux-2.38.1.tar.xz'", "util-linux/v2.39/util-linux-2.39.2.tar.xz'",
"source = 'https://mirrors.edge.kernel.org/pub/linux/utils/" "source = 'https://mirrors.edge.kernel.org/pub/linux/utils/"
f'util-linux/v{LIBUUID_VER_ANDROID[0]}/' f'util-linux/v{LIBUUID_VER_ANDROID[0]}/'
f"util-linux-{LIBUUID_VER_ANDROID[1]}.tar.xz'", f"util-linux-{LIBUUID_VER_ANDROID[1]}.tar.xz'",
@ -412,7 +413,7 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
# Set specific NCurses version. # Set specific NCurses version.
ftxt = replace_exact( ftxt = replace_exact(
ftxt, ftxt,
"source = 'https://ftp.gnu.org/gnu/ncurses/ncurses-6.3.tar.gz'", "source = 'https://ftp.gnu.org/gnu/ncurses/ncurses-6.4.tar.gz'",
"source = 'https://ftp.gnu.org/gnu/ncurses/" "source = 'https://ftp.gnu.org/gnu/ncurses/"
f"ncurses-{NCURSES_VER_ANDROID}.tar.gz'", f"ncurses-{NCURSES_VER_ANDROID}.tar.gz'",
count=1, count=1,
@ -430,7 +431,7 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
# Set specific SQLite version. # Set specific SQLite version.
ftxt = replace_exact( ftxt = replace_exact(
ftxt, ftxt,
"source = 'https://sqlite.org/2022/sqlite-autoconf-3390400.tar.gz'", "source = 'https://sqlite.org/2024/sqlite-autoconf-3460000.tar.gz'",
"source = 'https://sqlite.org/" "source = 'https://sqlite.org/"
f'{SQLITE_VER_ANDROID[0]}/' f'{SQLITE_VER_ANDROID[0]}/'
f"sqlite-autoconf-{SQLITE_VER_ANDROID[1]}.tar.gz'", f"sqlite-autoconf-{SQLITE_VER_ANDROID[1]}.tar.gz'",
@ -449,12 +450,29 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
writefile('Android/build_deps.py', ftxt) writefile('Android/build_deps.py', ftxt)
ftxt = readfile('Android/util.py')
ftxt = replace_exact(
ftxt,
"choices=range(30, 40), dest='android_api_level'",
"choices=range(23, 40), dest='android_api_level'",
)
writefile('Android/util.py', ftxt)
# Tweak some things in the base build script; grab the right version # Tweak some things in the base build script; grab the right version
# of Python and also inject some code to modify bits of python # of Python and also inject some code to modify bits of python
# after it is extracted. # after it is extracted.
ftxt = readfile('build.sh') ftxt = readfile('build.sh')
ftxt = replace_exact(ftxt, 'PYVER=3.11.0', f'PYVER={PY_VER_EXACT_ANDROID}') # Repo has gone 30+, but we currently want our own which is lower.
ftxt = replace_exact(
ftxt,
'COMMON_ARGS="--arch ${ARCH:-arm} --api ${ANDROID_API:-30}"',
'COMMON_ARGS="--arch ${ARCH:-arm} --api ${ANDROID_API:-'
+ str(ANDROID_API_VER)
+ '}"',
)
ftxt = replace_exact(ftxt, 'PYVER=3.12.4', f'PYVER={PY_VER_EXACT_ANDROID}')
ftxt = replace_exact( ftxt = replace_exact(
ftxt, ftxt,
' popd\n', ' popd\n',