added on-screen dev-console button option under advanced settings

This commit is contained in:
Eric 2023-09-07 18:07:52 -07:00
parent cd098866ea
commit 58cec911a6
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
69 changed files with 889 additions and 698 deletions

100
.efrocachemap generated
View File

@ -421,7 +421,7 @@
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55",
"build/assets/ba_data/data/langdata.json": "7be961f6a6b9742859638cbd5a747e18",
"build/assets/ba_data/data/langdata.json": "0f4630cc7c78222e782da9cedd4df284",
"build/assets/ba_data/data/languages/arabic.json": "db961f7fe0541a31880929e1c17ea957",
"build/assets/ba_data/data/languages/belarussian.json": "5e373ddcfa6e1f771b74c02298a6599a",
"build/assets/ba_data/data/languages/chinese.json": "6520f793066c95773002b4e9a920fd1d",
@ -430,27 +430,27 @@
"build/assets/ba_data/data/languages/czech.json": "f3ce219840946cb8f9aa6d3e25927ab3",
"build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e",
"build/assets/ba_data/data/languages/dutch.json": "22b44a33bf81142ba2befad14eb5746e",
"build/assets/ba_data/data/languages/english.json": "d16e8899211693c20d3b00fc198f58c6",
"build/assets/ba_data/data/languages/english.json": "6d261a19b40a27eca92f6199a26f5779",
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
"build/assets/ba_data/data/languages/filipino.json": "58f363cfd8a3ccf0c904ab753d95789b",
"build/assets/ba_data/data/languages/french.json": "6057b18878ad8379e51b507fa94958d8",
"build/assets/ba_data/data/languages/german.json": "549754d2a530d825200c6126be56df5c",
"build/assets/ba_data/data/languages/gibberish.json": "236f8547ba09722b7c7f5b8333986984",
"build/assets/ba_data/data/languages/gibberish.json": "d23fe0936bb6177443f4b74bf5981b13",
"build/assets/ba_data/data/languages/greek.json": "a65d78f912e9a89f98de004405167a6a",
"build/assets/ba_data/data/languages/hindi.json": "88ee0cda537bab9ac827def5e236fe1a",
"build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e",
"build/assets/ba_data/data/languages/indonesian.json": "583bae1ecc04375cee089a82359110b7",
"build/assets/ba_data/data/languages/italian.json": "ce99027a5d1af17689b4311eafa5ff24",
"build/assets/ba_data/data/languages/italian.json": "8d9332d461fa5b84780818bf6c2978b5",
"build/assets/ba_data/data/languages/korean.json": "ca1122a9ee551da3f75ae632012bd0e2",
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38",
"build/assets/ba_data/data/languages/persian.json": "5119aec9cbb2f8d00f2afaccf5fd5410",
"build/assets/ba_data/data/languages/polish.json": "336eeb0028af5f3c7b9c12d3a051db2c",
"build/assets/ba_data/data/languages/polish.json": "826c5b0402c2f0bcc29bc6f48b833545",
"build/assets/ba_data/data/languages/portuguese.json": "99b27c598c90fd522132af3536aef0ee",
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
"build/assets/ba_data/data/languages/russian.json": "aa99f9f597787fe4e09c8ab53fe2e081",
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
"build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef",
"build/assets/ba_data/data/languages/spanish.json": "90d7408d07d9445630aecbe7efaa722d",
"build/assets/ba_data/data/languages/spanish.json": "b5390c76f3475c8b6dd64ab9f170b4d8",
"build/assets/ba_data/data/languages/swedish.json": "77d671f10613291ebf9c71da66f18a18",
"build/assets/ba_data/data/languages/tamil.json": "b9d4b4e107456ea6420ee0f9d9d7a03e",
"build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc",
@ -4064,50 +4064,50 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "2ceb16e09034aa4e3213e8fb69dd209b",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "7ecdc512653325abef8e66e9c3e0e479",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "70a1c3300cb0610a85449d08b3483a54",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "000738cc726938ff5282d211629739e3",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "e4d0b9fdcb16f2bef3891f28b950daac",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "9cc960c0de9e10dd300561dc9ae045e2",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "be453afedfb331abe39e4940755b34e7",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "d478af40d922a68e4deab6dd452b0e51",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "5fdb7d8752f9a8315a5f94a32b5db368",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "433dae3372f05c7faf75658c6f962be1",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "d66d0201decc90f677520f41ac227a71",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "37efebdbd1866b71ac324f5ca949151a",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "5ccfadd91168c07956328dd11b118b28",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "404d9db4de7089346264f3f62dbb6d88",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "ef3dda575b711a0fa515f93ebd0a7379",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "8ee2be9a35e12158c0ace56b04a3ea99",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "1c305bde639f5fc76e908a3a3fb9203f",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "e58ce9a499068e682295dd3d88a0801d",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "38a2c503c8d892d7e94cd53fb4da5b48",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "deea2cf3afb5598ed9eb6ce4798b1cef",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "b5a129d83796c9e7015ab5e319d2c22f",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "b5a129d83796c9e7015ab5e319d2c22f",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "13405a4a16a71d073b6b3cabbbcd9666",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "86b26dc84cc7fa7095e51cfcae759c0b",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "13405a4a16a71d073b6b3cabbbcd9666",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "86b26dc84cc7fa7095e51cfcae759c0b",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "678e09ecd5da367ce290ca7318617b61",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "a9cdc9dd029dabc6dfa5b61d33de7927",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "678e09ecd5da367ce290ca7318617b61",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "a9cdc9dd029dabc6dfa5b61d33de7927",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "4811585805942428ddb217917e4ad843",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "d34c0a142e7d391a109a33ea3cc77c08",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "9d23a52a0c270710332bf35297db9f36",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "61d84134e4088e5138ceb200ba20960b",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "a7a9854fb6f114ac3f3f35c451170328",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "f5a2d196943141917b1ee0f3beb1f5eb",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ec0052c95df6ea5cb3bdcaabcb1ea55c",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "d21984e95164c4e7df91b35a60828fc7",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "b613c7699c66c9a2a17bf9360015ade0",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "6f907f7940d2f5e44874e24f9a1856a0",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "c0e542e25f839b32d91a07046bd56333",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "f159a31dcd46f144cc796a6f2cbb0a2a",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "f0a7e6c9ad568b229b8299ac15a485b4",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "4c453da417e3e2ab696009f3efb0cbc5",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "56b10a97ac10e4ff9ae854616f3d58de",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "e6a5ecde216f1c180321eec53de8e54d",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "3170eb1b194b77204b5b3454d271e10d",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "368f1eb9a45f3e0d7452e5770f67bd65",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "4376bee2a3d9b1ac98f418e981bd6b5e",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "241bcdfb4a528a4f9714efa9c086a6a6",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "ed0ae25a5332c9e90bc576a41b0e6397",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "b0fc89d99bfacf8326a313c0c9d89065",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "ab540c6524dfdc31ef55be4726f6a978",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "d0e6d409f0f5bfa339060bd6024b10f7",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "287d4e643461591bb897d26f9b24a600",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "8f53ad04b1de065d104b7fd28da0e79c",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "74fa35f559c38176ff91c794b78d06e2",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "2cb71ce71f4ac5d8d2bda2af1ebc87b5",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "53ac1a5d801ae8ae82de21ec9d9a6b8d",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "113c82cf8b6c95a2c940c45b97e4b894",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "6ccd6f2bd0e20520063d4bf8e2c016d0",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "82e76d58eab4962ee7567fbc655072d6",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "6ccd6f2bd0e20520063d4bf8e2c016d0",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "82e76d58eab4962ee7567fbc655072d6",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "e2ca657abc7945934c4b33602ecfbace",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "5b24b2e91fb5c6eca673b0c35bbaf4ca",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "e2ca657abc7945934c4b33602ecfbace",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "5b24b2e91fb5c6eca673b0c35bbaf4ca",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "cfa1c3ca813c3974316cc0abbb56277b",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "73a49adbf5e205d927eda1a2272a3e98",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "cfa1c3ca813c3974316cc0abbb56277b",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "73a49adbf5e205d927eda1a2272a3e98",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "a882153cd74bdb5c1b84d2c46a290527",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "6b00cce1baf5f95d36ae911cdcb23dba",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "8708149fb6208e4e5889b4742784623d",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "6b00cce1baf5f95d36ae911cdcb23dba",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "dc9d6facd1062a48245d5fcb603fe5d6",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "987b189ddac1f90808357749dd44fb2c",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "bf8be09124840f7af212918fa98a34ec",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "33ad88b1557e2828c8e0d8be10d9a5ca",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "3cec3a2d11567ff3dda36ace808c6082",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "b69a204fea2b6fcfee2cbce63a8edd9a",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "59874804f88d67858d988cbc746ca601",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "d9cb15a56ce8f6e3870cb2a5c83defda",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ad347097a38e0d7ede9eb6dec6a80ee9",

View File

@ -2318,6 +2318,7 @@
<w>projpath</w>
<w>projprefix</w>
<w>projroot</w>
<w>projrootstr</w>
<w>projs</w>
<w>projsrc</w>
<w>projtxt</w>

View File

@ -1,5 +1,11 @@
### 1.7.28 (build 21306, api 8, 2023-09-06)
### 1.7.28 (build 21322, api 8, 2023-09-07)
- Renamed Console to DevConsole, and added an option under advanced settings to
always show an ugly 'dev' button onscreen which can be used to toggle it. The
backtick key still works also for anyone with a keyboard. I plan to add more
functionality besides just the Python console to the dev-console, and perhaps
improve the Python console a bit too (add support for on-screen keyboards,
etc.)
- Added some high level functionality for copying and deleting feature-sets to
the `spinoff` tool. For example, to create your own `poo` feature-set based on
the existing `template_fs` one, do `tools/spinoff fset-copy template_fs poo`.
@ -16,6 +22,10 @@
significantly faster & more efficient.
- Updated internal Python builds for Apple & iOS to 3.11.5, and updated a few
dependent libraries as well (OpenSSL bumped from 3.0.8 to 3.0.10, etc.).
- Cleaned up the `babase.quit()` mechanism a bit. The default for the 'soft' arg
is now true, so a raw `babase.quit()` should now be a good citizen on mobile
platforms. Also added the `g_base->QuitApp()` call which gives the C++ layer
an equivalent to the Python call.
### 1.7.27 (build 21282, api 8, 2023-08-30)

View File

@ -1378,6 +1378,7 @@
<w>projname</w>
<w>projpath</w>
<w>projprefix</w>
<w>projrootstr</w>
<w>projsrc</w>
<w>projtxt</w>
<w>prolly</w>

View File

@ -424,8 +424,8 @@ set(BALLISTICA_SOURCES
${BA_SRC_ROOT}/ballistica/base/support/stress_test.cc
${BA_SRC_ROOT}/ballistica/base/support/stress_test.h
${BA_SRC_ROOT}/ballistica/base/support/ui_v1_soft.h
${BA_SRC_ROOT}/ballistica/base/ui/console.cc
${BA_SRC_ROOT}/ballistica/base/ui/console.h
${BA_SRC_ROOT}/ballistica/base/ui/dev_console.cc
${BA_SRC_ROOT}/ballistica/base/ui/dev_console.h
${BA_SRC_ROOT}/ballistica/base/ui/ui.cc
${BA_SRC_ROOT}/ballistica/base/ui/ui.h
${BA_SRC_ROOT}/ballistica/base/ui/widget_message.h

View File

@ -410,8 +410,8 @@
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h" />
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h" />
<ClCompile Include="..\..\src\ballistica\base\ui\console.cc" />
<ClInclude Include="..\..\src\ballistica\base\ui\console.h" />
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc" />
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h" />
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc" />
<ClInclude Include="..\..\src\ballistica\base\ui\ui.h" />
<ClInclude Include="..\..\src\ballistica\base\ui\widget_message.h" />

View File

@ -664,10 +664,10 @@
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\ui\console.cc">
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc">
<Filter>ballistica\base\ui</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\ui\console.h">
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h">
<Filter>ballistica\base\ui</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc">

View File

@ -405,8 +405,8 @@
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h" />
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h" />
<ClCompile Include="..\..\src\ballistica\base\ui\console.cc" />
<ClInclude Include="..\..\src\ballistica\base\ui\console.h" />
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc" />
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h" />
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc" />
<ClInclude Include="..\..\src\ballistica\base\ui\ui.h" />
<ClInclude Include="..\..\src\ballistica\base\ui\widget_message.h" />

View File

@ -664,10 +664,10 @@
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\ui\console.cc">
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc">
<Filter>ballistica\base\ui</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\ui\console.h">
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h">
<Filter>ballistica\base\ui</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc">

View File

@ -9,7 +9,7 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
fset.requirements = {'core'}

View File

@ -9,7 +9,7 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
fset.requirements = {
@ -27,7 +27,7 @@ fset.soft_requirements = {'plus'}
# We provide 'babase.app.classic'.
fset.has_python_app_subsystem = True
# If 'plus' is present, our subsystem should be inited after it
# If 'plus' is present, our subsystem should be inited *after* it
# (classic accounts key off of plus's v2 accounts)
fset.python_app_subsystem_dependencies = {'plus'}

View File

@ -9,7 +9,7 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
fset.requirements = set()

View File

@ -9,7 +9,7 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
fset.requirements = {'core', 'base'}

View File

@ -9,7 +9,7 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
fset.requirements = {'core', 'base', 'classic', 'scene_v1_lib'}

View File

@ -9,9 +9,10 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
# We're just a library of Python stuff; no C++ here.
fset.has_python_binary_module = False
fset.requirements = {'core', 'base', 'scene_v1'}

View File

@ -9,7 +9,7 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
fset.requirements = {'core', 'base'}

View File

@ -10,7 +10,7 @@ from __future__ import annotations
from batools.featureset import FeatureSet
from batools.dummymodule import DummyModuleDef
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
fset.requirements = {'core', 'base'}

View File

@ -9,9 +9,10 @@ from __future__ import annotations
from batools.featureset import FeatureSet
# Grab the FeatureSet we should apply to.
# Grab the FeatureSet we're defining here.
fset = FeatureSet.get_active()
# We're just a library of Python stuff; no C++.
fset.has_python_binary_module = False
fset.requirements = {'core', 'base', 'ui_v1', 'classic'}

View File

@ -3,31 +3,30 @@
# pylint: disable=missing-module-docstring, invalid-name
from __future__ import annotations
# This file is exec'ed by tools/spinoff, allowing us to customize
# how this src project gits filtered into dst projects.
# This file is exec'ed by tools/spinoff, allowing us to customize how
# this src project gits filtered into dst projects.
from batools.spinoff import SpinoffContext
# Grab the context we should apply to.
ctx = SpinoffContext.get_active()
# As a src project, we set up a baseline set of rules based on what
# we contain. The dst project config (exec'ed after us) is then free
# to override based on what they want of ours or what they add
# themselves.
# As a src project, we set up a baseline set of rules based on what we
# contain. The dst project config (exec'ed after us) is then free to
# override based on what they want of ours or what they add themselves.
# Any files/dirs with these base names will be ignored by spinoff
# on both src and dst.
# Any files/dirs with these base names will be ignored by spinoff on
# both src and dst.
ctx.ignore_names = {
'__pycache__',
'.git',
'.mypy_cache',
}
# Special set of paths managed by spinoff but ALSO stored in git in
# the dst project. This is for bare minimum stuff needed to be always
# present in dst for bootstrapping, indexing by github, etc). Changes
# to these files in dst will be silently and happily overwritten by
# Special set of paths managed by spinoff but ALSO stored in git in the
# dst project. This is for bare minimum stuff needed to be always
# present in dst for bootstrapping, indexing by github, etc). Changes to
# these files in dst will be silently and happily overwritten by
# spinoff, so tread carefully.
ctx.git_mirrored_paths = {
'.gitignore',
@ -36,16 +35,16 @@ ctx.git_mirrored_paths = {
'config/jenkins',
}
# File names that can be quietly ignored or cleared out when found.
# This should encompass things like .DS_Store files created by the
# Mac Finder when browsing directories. This helps spinoff remove
# empty directories when doing a 'clean', etc.
# File names that can be quietly ignored or cleared out when found. This
# should encompass things like .DS_Store files created by the Mac Finder
# when browsing directories. This helps spinoff remove empty directories
# when doing a 'clean', etc.
ctx.cruft_file_names = {'.DS_Store'}
# These paths in the src project will be skipped over during updates and
# not synced into the dst project. The dst project can use this to
# trim out parts of the src project that it doesn't want or that it
# intends to 'override' with its own versions.
# not synced into the dst project. The dst project can use this to trim
# out parts of the src project that it doesn't want or that it intends
# to 'override' with its own versions.
ctx.src_omit_paths = {
'.gitignore',
'config/spinoffconfig.py',
@ -63,27 +62,27 @@ ctx.src_omit_paths = {
# within it from being synced by spinoff; it just means that each of
# those individual spinoff-managed files will have their own gitignore
# entry since there is no longer one covering the whole dir. So to keep
# things tidy, carve out the minimal set of exact file/dir paths that you
# need.
# things tidy, carve out the minimal set of exact file/dir paths that
# you need.
ctx.src_write_paths = {
'tools/spinoff',
'config/spinoffconfig.py',
}
# Normally spinoff errors if it finds any files in its managed dirs
# that it did not put there. This is to prevent accidentally working
# in these parts of a dst project; since these sections are git-ignored,
# git itself won't raise any warnings in such cases and it would be easy
# to accidentally lose work otherwise.
# Normally spinoff errors if it finds any files in its managed dirs that
# it did not put there. This is to prevent accidentally working in these
# parts of a dst project; since these sections are git-ignored, git
# itself won't raise any warnings in such cases and it would be easy to
# accidentally lose work otherwise.
#
# This list can be used to suppress spinoff's errors for specific
# locations. This is generally used to allow build output or other
# dynamically generated files to exist within spinoff-managed
# directories. It is possible to use src_write_paths for such purposes,
# but this has the side-effect of greatly complicating the dst
# project's gitignore list; selectively marking a few dirs as
# unchecked makes for a cleaner setup. Just be careful to not set
# excessively broad regions as unchecked; you don't want to mask
# actual useful error messages.
# but this has the side-effect of greatly complicating the dst project's
# gitignore list; selectively marking a few dirs as unchecked makes for
# a cleaner setup. Just be careful to not set excessively broad regions
# as unchecked; you don't want to mask actual useful error messages.
ctx.src_unchecked_paths = {
'src/ballistica/mgen',
'src/ballistica/*/mgen',
@ -102,12 +101,12 @@ ctx.src_unchecked_paths = {
'ballisticakit-android/BallisticaKit/.cxx',
}
# Paths/names/suffixes we consider 'project' files.
# These files are synced after all other files and go through
# batools.project.Updater class as part of their filtering.
# This allows them to update themselves in the same way as they
# do when running 'make update' for the project; adding the final
# filtered set of project source files to themself, etc.
# Paths/names/suffixes we consider 'project' files. These files are
# synced after all other files and go through batools.project.Updater
# class as part of their filtering. This allows them to update
# themselves in the same way as they do when running 'make update' for
# the project; adding the final filtered set of project source files to
# themself, etc.
ctx.project_file_paths = {'src/assets/ba_data/python/babase/_app.py'}
ctx.project_file_names = {
'Makefile',
@ -124,15 +123,16 @@ ctx.project_file_suffixes = {
'.pbxproj',
}
# Everything actually synced into dst will use the following filter rules:
# Everything actually synced into dst will use the following filter
# rules:
# If files are 'filtered' it means they will have all instances
# of BallisticaKit in their names and contents replaced with their
# project name. Other custom filtering can also be applied. Obviously
# filtering should not be run on certain files (binary data, etc.)
# and disabling it where not needed can improve efficiency and make
# backporting easier (editing spinoff-managed files in dst and getting
# those changes back into src).
# If files are 'filtered' it means they will have all instances of
# BallisticaKit in their names and contents replaced with their project
# name. Other custom filtering can also be applied. Obviously filtering
# should not be run on certain files (binary data, etc.) and disabling
# it where not needed can improve efficiency and make backporting easier
# (editing spinoff-managed files in dst and getting those changes back
# into src).
# Anything under these dirs WILL be filtered.
ctx.filter_dirs = {
@ -153,8 +153,8 @@ ctx.no_filter_dirs = {
'src/assets/windows',
}
# ELSE files matching these exact base names WILL be filtered
# (so FOO matches a/b/FOO as well as just FOO).
# ELSE files matching these exact base names WILL be filtered (so FOO
# matches a/b/FOO as well as just FOO).
ctx.filter_file_names = {
'Makefile',
'.gitignore',

View File

@ -118,15 +118,16 @@ class App:
# This section generated by batools.appmodule; do not edit.
# Ask our default app modes to handle it.
# (based on 'default_app_modes' in projectconfig).
# (generated from 'default_app_modes' in projectconfig).
import bascenev1
import babase
if bascenev1.SceneV1AppMode.can_handle_intent(intent):
return bascenev1.SceneV1AppMode
if babase.EmptyAppMode.can_handle_intent(intent):
return babase.EmptyAppMode
for appmode in [
bascenev1.SceneV1AppMode,
babase.EmptyAppMode,
]:
if appmode.can_handle_intent(intent):
return appmode
return None
@ -1057,6 +1058,7 @@ class App:
@property
def protocol_version(self) -> int:
"""(internal)."""
# pylint: disable=cyclic-import
import bascenev1
warnings.warn(

View File

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

View File

@ -350,7 +350,7 @@ class CoopBrowserWindow(bui.Window):
# noinspection PyUnresolvedReferences
@staticmethod
def _preload_modules() -> None:
"""Preload modules we use (called in bg thread)."""
"""Preload modules we use; avoids hitches (called in bg thread)."""
import bauiv1lib.purchase as _unused1
import bauiv1lib.coop.gamebutton as _unused2
import bauiv1lib.confirm as _unused3

View File

@ -92,7 +92,7 @@ class MainMenuWindow(bui.Window):
# noinspection PyUnresolvedReferences
@staticmethod
def _preload_modules() -> None:
"""Preload modules we use (called in bg thread)."""
"""Preload modules we use; avoids hitches (called in bg thread)."""
import bauiv1lib.getremote as _unused
import bauiv1lib.confirm as _unused2
import bauiv1lib.store.button as _unused3

View File

@ -513,7 +513,7 @@ class PlayWindow(bui.Window):
# noinspection PyUnresolvedReferences
@staticmethod
def _preload_modules() -> None:
"""Preload modules we use (called in bg thread)."""
"""Preload modules we use; avoids hitches (called in bg thread)."""
import bauiv1lib.mainmenu as _unused1
import bauiv1lib.account as _unused2
import bauiv1lib.coop.browser as _unused3

View File

@ -16,7 +16,7 @@ if TYPE_CHECKING:
class AdvancedSettingsWindow(bui.Window):
"""Window for editing advanced game settings."""
"""Window for editing advanced app settings."""
def __init__(
self,
@ -61,6 +61,7 @@ class AdvancedSettingsWindow(bui.Window):
self._spacing = 32
self._menu_open = False
top_extra = 10 if uiscale is bui.UIScale.SMALL else 0
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height + top_extra),
@ -93,7 +94,7 @@ class AdvancedSettingsWindow(bui.Window):
self._scroll_width = self._width - (100 + 2 * x_inset)
self._scroll_height = self._height - 115.0
self._sub_width = self._scroll_width * 0.95
self._sub_height = 724.0
self._sub_height = 766.0
if self._show_always_use_internal_keyboard:
self._sub_height += 62
@ -185,7 +186,7 @@ class AdvancedSettingsWindow(bui.Window):
# noinspection PyUnresolvedReferences
@staticmethod
def _preload_modules() -> None:
"""Preload modules we use (called in bg thread)."""
"""Preload modules we use; avoids hitches (called in bg thread)."""
from babase import modutils as _unused2
from bauiv1lib import config as _unused1
from bauiv1lib.settings import vrtesting as _unused3
@ -474,6 +475,19 @@ class AdvancedSettingsWindow(bui.Window):
maxwidth=430,
)
v -= 42
self._show_dev_console_button_check_box = ConfigCheckBox(
parent=self._subcontainer,
position=(50, v),
size=(self._sub_width - 100, 30),
configkey='Show Dev Console Button',
displayname=bui.Lstr(
resource=f'{self._r}.showDevConsoleButtonText'
),
scale=1.0,
maxwidth=430,
)
v -= 42
self._disable_camera_shake_check_box = ConfigCheckBox(
parent=self._subcontainer,

View File

@ -224,7 +224,7 @@ class AllSettingsWindow(bui.Window):
# noinspection PyUnresolvedReferences
@staticmethod
def _preload_modules() -> None:
"""Preload modules we use (called in bg thread)."""
"""Preload modules we use; avoids hitches (called in bg thread)."""
import bauiv1lib.mainmenu as _unused1
import bauiv1lib.settings.controls as _unused2
import bauiv1lib.settings.graphics as _unused3

View File

@ -66,6 +66,6 @@ void AppMode::LanguageChanged() {}
auto AppMode::LastClientJoinTime() const -> millisecs_t { return -1; }
auto AppMode::InMainMenu() const -> bool { return false; }
auto AppMode::InClassicMainMenuSession() const -> bool { return false; }
} // namespace ballistica::base

View File

@ -79,8 +79,8 @@ class AppMode {
/// Called when language changes.
virtual void LanguageChanged();
/// Are we currently in a 'main menu'?
virtual auto InMainMenu() const -> bool;
/// Are we currently in a classic 'main menu' session?
virtual auto InClassicMainMenuSession() const -> bool;
/// Get current party size (for legacy parties).
virtual auto GetPartySize() const -> int;

View File

@ -50,8 +50,10 @@ void AppModeEmpty::DrawWorld(base::FrameDef* frame_def) {
sinf(static_cast<float>(frame_def->display_time_millisecs()) / 600.0f);
auto yoffs =
cosf(static_cast<float>(frame_def->display_time_millisecs()) / 600.0f);
// Z value -1 will draw us under most everything.
c.Translate(pass->virtual_width() * 0.5f - 70.0f + xoffs * 200.0f,
pass->virtual_height() * 0.5f - 20.0f + yoffs * 200.0f);
pass->virtual_height() * 0.5f - 20.0f + yoffs * 200.0f, -1.0f);
c.Scale(2.0, 2.0);
int text_elem_count = grp.GetElementCount();

View File

@ -24,7 +24,7 @@
#include "ballistica/base/support/plus_soft.h"
#include "ballistica/base/support/stdio_console.h"
#include "ballistica/base/support/stress_test.h"
#include "ballistica/base/ui/console.h"
#include "ballistica/base/ui/dev_console.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/core/python/core_python.h"
#include "ballistica/shared/foundation/event_loop.h"
@ -156,7 +156,7 @@ void BaseFeatureSet::OnAssetsAvailable() {
// Spin up the in-app console.
if (!g_core->HeadlessMode()) {
console_ = new Console();
console_ = new DevConsole();
// Print any messages that have built up.
if (!console_startup_messages_.empty()) {
@ -234,7 +234,7 @@ void BaseFeatureSet::OnAppShutdownComplete() {
if (app_adapter->ManagesEventLoop()) {
g_core->main_event_loop()->Quit();
} else {
platform->QuitApp();
platform->TerminateApp();
}
}
@ -747,4 +747,18 @@ void BaseFeatureSet::ShutdownSuppressDisallow() {
auto BaseFeatureSet::GetReturnValue() const -> int { return return_value(); }
void BaseFeatureSet::QuitApp(QuitType quit_type) {
// If they ask for 'back' and we support that, do it.
// Otherwise if they want 'back' or 'soft' and we support soft, do it.
// Otherwise go with a regular app shutdown.
if (quit_type == QuitType::kBack && g_base->platform->CanBackQuit()) {
logic->event_loop()->PushCall([this] { platform->DoBackQuit(); });
} else if ((quit_type == QuitType::kBack || quit_type == QuitType::kSoft)
&& g_base->platform->CanSoftQuit()) {
logic->event_loop()->PushCall([this] { platform->DoSoftQuit(); });
} else {
logic->event_loop()->PushCall([this] { logic->Shutdown(); });
}
}
} // namespace ballistica::base

View File

@ -53,7 +53,7 @@ class Camera;
class ClassicSoftInterface;
class CollisionMeshAsset;
class CollisionCache;
class Console;
class DevConsole;
class Context;
class ContextRef;
class DataAsset;
@ -121,6 +121,18 @@ class UIV1SoftInterface;
class AppAdapterVR;
class GraphicsVR;
enum class QuitType {
/// Leads to the process exiting.
kHard,
/// May hide/reset the app but keep the process running. Generally how
/// mobile apps behave.
kSoft,
/// Same as kSoft but gives 'back-button-pressed' behavior which may
/// differ depending on the OS (returning to a previous Activity in
/// Android instead of dumping to the home screen, etc.)
kBack,
};
enum class AssetType {
kTexture,
kCollisionMesh,
@ -601,6 +613,14 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
/// Start app systems in motion.
void StartApp() override;
/// Issue a high level app quit request. Can be called from any thread.
/// 'soft' means the app can simply reset/hide itself instead of actually
/// exiting the process (common behavior on mobile platforms). 'back'
/// means that a soft-quit should behave as if a back-button was pressed,
/// whic may trigger different behavior in the OS than a standard soft
/// quit.
void QuitApp(QuitType quit_type = QuitType::kSoft);
/// Called when app shutdown process completes. Sets app to exit.
void OnAppShutdownComplete();
@ -759,7 +779,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
void PrintContextUnavailable_();
AppMode* app_mode_;
Console* console_{};
DevConsole* console_{};
PlusSoftInterface* plus_soft_{};
ClassicSoftInterface* classic_soft_{};
UIV1SoftInterface* ui_v1_soft_{};

View File

@ -11,6 +11,17 @@ namespace ballistica::base {
class RenderComponent {
public:
class ScopedTransformObj {
public:
explicit ScopedTransformObj(RenderComponent* c) : c_{c} {
c_->PushTransform();
}
~ScopedTransformObj() { c_->PopTransform(); }
private:
RenderComponent* c_;
};
explicit RenderComponent(RenderPass* pass)
: state_(State::kConfiguring), pass_(pass), cmd_buffer_(nullptr) {}
~RenderComponent() {
@ -82,6 +93,9 @@ class RenderComponent {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kPopTransform);
}
auto ScopedTransform() -> ScopedTransformObj {
return ScopedTransformObj(this);
}
void Translate(float x, float y) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kTranslate2);

View File

@ -20,7 +20,7 @@
#include "ballistica/base/logic/logic.h"
#include "ballistica/base/python/support/python_context_call.h"
#include "ballistica/base/support/app_config.h"
#include "ballistica/base/ui/console.h"
#include "ballistica/base/ui/dev_console.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/core/core.h"
#include "ballistica/shared/foundation/event_loop.h"
@ -34,7 +34,6 @@ const float kScreenMeshZDepth{-0.05f};
const float kProgressBarZDepth{0.0f};
const int kProgressBarFadeTime{500};
const float kDebugImgZDepth{-0.04f};
const float kCursorZDepth{-0.1f};
auto Graphics::IsShaderTransparent(ShadingType c) -> bool {
switch (c) {
@ -105,8 +104,8 @@ void Graphics::OnAppShutdown() { assert(g_base->InLogicThread()); }
void Graphics::DoApplyAppConfig() {
assert(g_base->InLogicThread());
// Not relevant for fullscreen anymore
// since we're fullscreen windows everywhere.
// Not relevant for fullscreen anymore since we use fullscreen windows.
// everywhere.
int width = 800;
int height = 600;
@ -158,28 +157,20 @@ void Graphics::DoApplyAppConfig() {
// Note: when the graphics-thread applies the first set-screen event it will
// trigger the remainder of startup such as media-loading; make sure nothing
// below this will affect that.
// below this point will affect that.
g_base->graphics_server->PushSetScreenCall(
fullscreen, width, height, texture_quality_requested,
graphics_quality_requested, android_res);
set_show_fps(g_base->app_config->Resolve(AppConfig::BoolID::kShowFPS));
set_show_ping(g_base->app_config->Resolve(AppConfig::BoolID::kShowPing));
show_fps_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowFPS);
show_ping_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowPing);
tv_border_ = g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder);
g_base->graphics_server->PushSetScreenGammaCall(
g_base->app_config->Resolve(AppConfig::FloatID::kScreenGamma));
g_base->graphics_server->PushSetScreenPixelScaleCall(
g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale));
// Set tv border (for both client and server).
// FIXME: this should exist either on the client or the server; not both.
// (and should be communicated via frameldefs/etc.)
bool tv_border =
g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder);
g_base->graphics_server->event_loop()->PushCall(
[tv_border] { g_base->graphics_server->set_tv_border(tv_border); });
set_tv_border(tv_border);
// V-sync setting.
std::string v_sync =
g_base->app_config->Resolve(AppConfig::StringID::kVerticalSync);
@ -313,7 +304,7 @@ void Graphics::SetShadowRange(float lower_bottom, float lower_top,
}
auto Graphics::GetShadowDensity(float x, float y, float z) -> float {
if (y < shadow_lower_bottom_) { // NOLINT(bugprone-branch-clone)
if (y < shadow_lower_bottom_) {
return 0.0f;
} else if (y < shadow_lower_top_) {
float amt =
@ -1108,9 +1099,16 @@ void Graphics::DrawUI(FrameDef* frame_def) {
g_base->ui->Draw(frame_def);
}
void Graphics::DrawDevUI(FrameDef* frame_def) {
// Just do generic thing in our default implementation.
// Special variants like GraphicsVR may do fancier stuff here.
g_base->ui->DrawDev(frame_def);
}
void Graphics::BuildAndPushFrameDef() {
assert(g_base->InLogicThread());
assert(camera_.Exists());
assert(!g_core->HeadlessMode());
// Keep track of when we're in here; can be useful for making sure stuff
// doesn't muck with our lists/etc. while we're using them.
@ -1167,16 +1165,14 @@ void Graphics::BuildAndPushFrameDef() {
DrawUI(frame_def);
// Let input draw anything it needs to. (touch input graphics, etc)
// Let input draw anything it needs to (touch input graphics, etc).
g_base->input->Draw(frame_def);
RenderPass* overlay_pass = frame_def->overlay_pass();
DrawMiscOverlays(overlay_pass);
// Draw console.
if (!g_core->HeadlessMode() && g_base->console()) {
g_base->console()->Draw(overlay_pass);
}
// Let UI draw dev console and whatever else.
DrawDevUI(frame_def);
DrawCursor(overlay_pass, app_time_millisecs);

View File

@ -19,30 +19,32 @@
namespace ballistica::base {
// Light/shadow res is divided by this to get pure light res.
const int kLightResDiv = 4;
const int kLightResDiv{4};
// How we divide up our z depth spectrum:
const float kBackingDepth5 = 1.0f;
const float kBackingDepth5{1.0f};
// Background
// blit-shapes (with cam buffer)
const float kBackingDepth4 = 0.9f;
const float kBackingDepth4{0.9f};
// World (without cam buffer) or overlay-3d (with cam buffer)
const float kBackingDepth3C = 0.65f;
const float kBackingDepth3B = 0.4f;
const float kBackingDepth3 = 0.15f;
const float kBackingDepth3C{0.65f};
const float kBackingDepth3B{0.4f};
const float kBackingDepth3{0.15f};
// Overlay-3d (without cam buffer) / overlay(vr)
const float kBackingDepth2C = 0.147f;
const float kBackingDepth2B = 0.143f;
const float kBackingDepth2 = 0.14f;
const float kBackingDepth2C{0.147f};
const float kBackingDepth2B{0.143f};
const float kBackingDepth2{0.14f};
// Overlay(non-vr) // cover (vr)
const float kBackingDepth1B = 0.01f;
const float kBackingDepth1 = 0.0f;
const float kBackingDepth1B{0.01f};
const float kBackingDepth1{0.0f};
const float kShadowNeutral = 0.5f;
const float kShadowNeutral{0.5f};
const float kCursorZDepth{-0.1f};
// Client class for graphics operations (used from the logic thread).
class Graphics {
@ -218,13 +220,7 @@ class Graphics {
void SetShadowRange(float lower_bottom, float lower_top, float upper_bottom,
float upper_top);
void ReleaseFadeEndCommand();
void set_show_fps(bool val) { show_fps_ = val; }
void set_show_ping(bool val) { show_ping_ = val; }
// FIXME - move to graphics_server
void set_tv_border(bool val) {
assert(g_base->InLogicThread());
tv_border_ = val;
}
auto tv_border() const -> bool {
assert(g_base->InLogicThread());
return tv_border_;
@ -297,8 +293,13 @@ class Graphics {
void set_drawing_transparent_only(bool val) {
drawing_transparent_only_ = val;
}
/// Draw regular UI.
virtual void DrawUI(FrameDef* frame_def);
/// Draw dev console or whatever else on top of normal stuff.
virtual void DrawDevUI(FrameDef* frame_def);
auto drawing_opaque_only() const -> bool { return drawing_opaque_only_; }
void set_drawing_opaque_only(bool val) { drawing_opaque_only_ = val; }

View File

@ -117,6 +117,11 @@ auto GraphicsServer::GetRenderFrameDef() -> FrameDef* {
return nullptr;
}
void GraphicsServer::ApplyFrameDefSettings(FrameDef* frame_def) {
assert(g_base->InGraphicsThread());
tv_border_ = frame_def->tv_border();
}
// Runs any mesh updates contained in the frame-def.
void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* frame_def) {
assert(g_base->InGraphicsThread());
@ -170,6 +175,9 @@ void GraphicsServer::TryRender() {
assert(g_base->InGraphicsThread());
if (FrameDef* frame_def = GetRenderFrameDef()) {
// Apply settings such as tv-mode passed along on the frame-def.
ApplyFrameDefSettings(frame_def);
// Note: we always run mesh updates contained in the framedef
// even if we don't actually render it.
// (Hmm this seems flaky; will TryRender always get called

View File

@ -52,6 +52,8 @@ class GraphicsServer {
// of using the RenderFrameDef* calls
auto GetRenderFrameDef() -> FrameDef*;
void ApplyFrameDefSettings(FrameDef* frame_def);
void RunFrameDefMeshUpdates(FrameDef* frame_def);
// renders shadow passes and other common parts of a frame_def
@ -215,10 +217,6 @@ class GraphicsServer {
assert(g_base->InGraphicsThread());
return res_y_virtual_;
}
void set_tv_border(bool val) {
assert(g_base->InGraphicsThread());
tv_border_ = val;
}
auto tv_border() const {
assert(g_base->InGraphicsThread());
return tv_border_;
@ -302,8 +300,8 @@ class GraphicsServer {
EventLoop* event_loop_{};
float res_x_{};
float res_y_{};
float res_x_virtual_{0.0f};
float res_y_virtual_{0.0f};
float res_x_virtual_{};
float res_y_virtual_{};
bool tv_border_{};
bool renderer_context_lost_{};
uint32_t texture_compression_types_{};

View File

@ -72,6 +72,7 @@ void FrameDef::Reset() {
assert(g_base->graphics->has_supports_high_quality_graphics_value());
orbiting_ = (g_base->graphics->camera()->mode() == CameraMode::kOrbit);
tv_border_ = g_base->graphics->tv_border();
shadow_offset_ = g_base->graphics->shadow_offset();
shadow_scale_ = g_base->graphics->shadow_scale();

View File

@ -154,6 +154,7 @@ class FrameDef {
auto media_components() const -> const std::vector<Object::Ref<Asset>>& {
return media_components_;
}
auto tv_border() const { return tv_border_; }
void set_camera_mode(CameraMode val) { camera_mode_ = val; }
void set_rendering(bool val) { rendering_ = val; }
@ -205,6 +206,7 @@ class FrameDef {
std::unique_ptr<RenderPass> blit_pass_;
GraphicsQuality quality_{GraphicsQuality::kLow};
bool orbiting_{};
bool tv_border_{};
millisecs_t app_time_millisecs_{};
millisecs_t display_time_millisecs_{};
millisecs_t display_time_elapsed_millisecs_{};

View File

@ -12,7 +12,7 @@
#include "ballistica/base/logic/logic.h"
#include "ballistica/base/python/base_python.h"
#include "ballistica/base/support/app_config.h"
#include "ballistica/base/ui/console.h"
#include "ballistica/base/ui/dev_console.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/shared/buildconfig/buildconfig_common.h"
#include "ballistica/shared/foundation/event_loop.h"
@ -23,26 +23,14 @@ namespace ballistica::base {
Input::Input() = default;
template <typename F>
void SafePushLogicCall(const char* desc, const F& lambda) {
// Note: originally this call was created to silently ignore early events
// coming in before app stuff was up and running, but that was a bad idea,
// as it caused us to ignore device-create messages sometimes which lead
// to other issues later. So now I'm trying to fix those problems at the
// source, but am leaving this intact for now as a clean way to catch
// anything that needs fixing.
if (!g_base) {
FatalError(std::string(desc) + " called with null g_base.");
return;
}
if (auto* loop = g_base->logic->event_loop()) {
loop->PushCall(lambda);
} else {
FatalError(std::string(desc) + " called before logic event loop created.");
}
void PushLogicCall(const F& lambda) {
assert(g_base);
assert(g_base->logic->event_loop());
g_base->logic->event_loop()->PushCall(lambda);
}
void Input::PushCreateKeyboardInputDevices() {
SafePushLogicCall(__func__, [this] { CreateKeyboardInputDevices(); });
PushLogicCall([this] { CreateKeyboardInputDevices(); });
}
void Input::CreateKeyboardInputDevices() {
@ -59,7 +47,7 @@ void Input::CreateKeyboardInputDevices() {
}
void Input::PushDestroyKeyboardInputDevices() {
SafePushLogicCall(__func__, [this] { DestroyKeyboardInputDevices(); });
PushLogicCall([this] { DestroyKeyboardInputDevices(); });
}
void Input::DestroyKeyboardInputDevices() {
@ -171,13 +159,14 @@ void Input::CreateTouchInput() {
void Input::AnnounceConnects() {
static bool first_print = true;
// For the first announcement just say "X controllers detected" and don't have
// a sound.
// For the first announcement just say "X controllers detected" and don't
// have a sound.
if (first_print && g_core->GetAppTimeMillisecs() < 10000) {
first_print = false;
// Disabling this completely for now; being more lenient with devices
// allowed on android means this will often come back with large numbers.
// allowed on Android means this will often come back with large
// numbers.
bool do_print{false};
// If there's been several connected, just give a number.
@ -203,7 +192,7 @@ void Input::AnnounceConnects() {
&s, "${COUNT}", std::to_string(newly_connected_controllers_.size()));
ScreenMessage(s);
} else {
// If its just one, name it.
// If its just one, give its name.
std::string s =
g_base->assets->GetResourceString("controllerConnectedText");
Utils::StringReplaceOne(&s, "${CONTROLLER}",
@ -276,7 +265,7 @@ void Input::ShowStandardInputDeviceDisconnectedMessage(InputDevice* j) {
void Input::PushAddInputDeviceCall(InputDevice* input_device,
bool standard_message) {
SafePushLogicCall(__func__, [this, input_device, standard_message] {
PushLogicCall([this, input_device, standard_message] {
AddInputDevice(input_device, standard_message);
});
}
@ -362,7 +351,7 @@ void Input::AddInputDevice(InputDevice* device, bool standard_message) {
void Input::PushRemoveInputDeviceCall(InputDevice* input_device,
bool standard_message) {
SafePushLogicCall(__func__, [this, input_device, standard_message] {
PushLogicCall([this, input_device, standard_message] {
RemoveInputDevice(input_device, standard_message);
});
}
@ -390,8 +379,6 @@ void Input::RemoveInputDevice(InputDevice* input, bool standard_message) {
device->DetachFromPlayer();
// This should kill the device.
// FIXME: since many devices get allocated in the main thread,
// should we not kill it there too?...
device.Clear();
UpdateInputDeviceCounts();
return;
@ -411,10 +398,10 @@ void Input::UpdateInputDeviceCounts() {
int total = 0;
int controller_count = 0;
for (auto& input_device : input_devices_) {
// Ok, we now limit non-keyboard non-touchscreen devices to ones that have
// been active recently.. (we're starting to get lots of virtual devices and
// other cruft on android; don't wanna show controller UIs just due to
// those)
// Ok, we now limit non-keyboard non-touchscreen devices to ones that
// have been active recently.. (we're starting to get lots of virtual
// devices and other cruft on android; don't wanna show controller UIs
// just due to those)
if (input_device.Exists()
&& ((*input_device).IsTouchScreen() || (*input_device).IsKeyboard()
|| ((*input_device).last_input_time_millisecs() != 0
@ -480,7 +467,6 @@ auto Input::GetLocalActiveInputDeviceCount() -> int {
auto Input::HaveControllerWithPlayer() -> bool {
assert(g_base->InLogicThread());
// NOLINTNEXTLINE(readability-use-anyofallof)
for (auto& input_device : input_devices_) {
if (input_device.Exists() && (*input_device).IsController()
&& (*input_device).AttachedToPlayer()) {
@ -492,7 +478,6 @@ auto Input::HaveControllerWithPlayer() -> bool {
auto Input::HaveRemoteAppController() -> bool {
assert(g_base->InLogicThread());
// NOLINTNEXTLINE(readability-use-anyofallof)
for (auto& input_device : input_devices_) {
if (input_device.Exists() && (*input_device).IsRemoteApp()) {
return true;
@ -546,8 +531,8 @@ auto Input::ShouldCompletelyIgnoreInputDevice(InputDevice* input_device)
void Input::UpdateEnabledControllerSubsystems() {
assert(g_base);
// First off, on mac, let's update whether we want to completely ignore either
// the classic or the iOS/Mac controller subsystems.
// First off, on mac, let's update whether we want to completely ignore
// either the classic or the iOS/Mac controller subsystems.
if (g_buildconfig.ostype_macos()) {
std::string sys = g_base->app_config->Resolve(
AppConfig::StringID::kMacControllerSubsystem);
@ -581,9 +566,9 @@ void Input::DoApplyAppConfig() {
UpdateEnabledControllerSubsystems();
// It's technically possible that updating these controls will add or remove
// devices, thus changing the input_devices_ list, so lets work with a copy of
// it.
// It's technically possible that updating these controls will add or
// remove devices, thus changing the input_devices_ list, so lets work
// with a copy of it.
std::vector<Object::Ref<InputDevice> > input_devices = input_devices_;
for (auto& input_device : input_devices) {
if (input_device.Exists()) {
@ -819,7 +804,7 @@ void Input::ProcessStressTesting(int player_count) {
}
void Input::PushTextInputEvent(const std::string& text) {
SafePushLogicCall(__func__, [this, text] {
PushLogicCall([this, text] {
MarkInputActive();
// Ignore if input is locked.
@ -837,7 +822,7 @@ void Input::PushTextInputEvent(const std::string& text) {
void Input::PushJoystickEvent(const SDL_Event& event,
InputDevice* input_device) {
SafePushLogicCall(__func__, [this, event, input_device] {
PushLogicCall([this, event, input_device] {
HandleJoystickEvent(event, input_device);
});
}
@ -871,11 +856,11 @@ void Input::HandleJoystickEvent(const SDL_Event& event,
}
void Input::PushKeyPressEvent(const SDL_Keysym& keysym) {
SafePushLogicCall(__func__, [this, keysym] { HandleKeyPress(&keysym); });
PushLogicCall([this, keysym] { HandleKeyPress(&keysym); });
}
void Input::PushKeyReleaseEvent(const SDL_Keysym& keysym) {
SafePushLogicCall(__func__, [this, keysym] { HandleKeyRelease(&keysym); });
PushLogicCall([this, keysym] { HandleKeyRelease(&keysym); });
}
void Input::CaptureKeyboardInput(HandleKeyPressCall* press_call,
@ -947,10 +932,9 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
case SDLK_KP_ENTER:
case SDLK_BACKSPACE: {
// FIXME: I don't remember what this was put here for, but now that
// we
// have hardware keyboards it crashes text fields by sending them a
// TEXT_INPUT message with no string.. I made them resistant to
// that case but wondering if we can take this out?...
// we have hardware keyboards it crashes text fields by sending
// them a TEXT_INPUT message with no string.. I made them resistant
// to that case but wondering if we can take this out?
g_base->ui->SendWidgetMessage(
WidgetMessage(WidgetMessage::Type::kTextInput, keysym));
break;
@ -971,7 +955,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
return;
}
// Control-Q quits. On mac, the usual cmd-q gets handled by SDL/etc.
// Control-Q quits. On Mac, the usual cmd-q gets handled by SDL/etc.
// implicitly.
if (!repeat_press && keysym->sym == SDLK_q && (keysym->mod & KMOD_CTRL)) {
g_base->ui->ConfirmQuit();
@ -1027,14 +1011,12 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
}
case SDLK_F7:
SafePushLogicCall(__func__,
[] { g_base->graphics->ToggleManualCamera(); });
PushLogicCall([] { g_base->graphics->ToggleManualCamera(); });
handled = true;
break;
case SDLK_F8:
SafePushLogicCall(
__func__, [] { g_base->graphics->ToggleNetworkDebugDisplay(); });
PushLogicCall([] { g_base->graphics->ToggleNetworkDebugDisplay(); });
handled = true;
break;
@ -1045,8 +1027,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
break;
case SDLK_F10:
SafePushLogicCall(__func__,
[] { g_base->graphics->ToggleDebugDraw(); });
PushLogicCall([] { g_base->graphics->ToggleDebugDraw(); });
handled = true;
break;
@ -1080,49 +1061,38 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
}
void Input::HandleKeyRelease(const SDL_Keysym* keysym) {
assert(g_base);
assert(g_base->InLogicThread());
// Note: we want to let these through even if input is locked.
// Note: we want to let releases through even if input is locked.
MarkInputActive();
// If someone is capturing these events, give them a crack at it.
if (keyboard_input_capture_release_) {
if (keyboard_input_capture_release_(*keysym)) {
return;
}
}
// Regardless of what else we do, keep track of mod key states.
// (for things like manual camera moves. For individual key presses
// ideally we should use the modifiers bundled with the key presses)
UpdateModKeyStates(keysym, false);
// In some cases we may receive duplicate key-release events
// (if a keyboard reset was run it deals out key releases but then the
// keyboard driver issues them as well)
// In some cases we may receive duplicate key-release events (if a
// keyboard reset was run, it deals out key releases, but then the
// keyboard driver issues them as well).
if (keys_held_.count(keysym->sym) == 0) {
return;
}
// If someone is capturing these events, give them a crack at it.
if (keyboard_input_capture_release_) {
(keyboard_input_capture_release_(*keysym));
}
// Keep track of mod key states for things like manual camera moves. For
// individual key presses ideally we should instead use modifiers bundled
// with the key press events.
UpdateModKeyStates(keysym, false);
keys_held_.erase(keysym->sym);
if (IsInputLocked()) {
return;
if (g_base->console() != nullptr) {
g_base->console()->HandleKeyRelease(keysym);
}
bool handled = false;
if (g_base && g_base->console() != nullptr
&& g_base->console()->HandleKeyRelease(keysym)) {
handled = true;
}
// If we haven't claimed it, pass it along as potential player input.
if (!handled) {
if (keyboard_input_) {
keyboard_input_->HandleKey(keysym, false, false);
}
if (keyboard_input_) {
keyboard_input_->HandleKey(keysym, false, false);
}
}
@ -1155,15 +1125,17 @@ void Input::UpdateModKeyStates(const SDL_Keysym* keysym, bool press) {
}
void Input::PushMouseScrollEvent(const Vector2f& amount) {
SafePushLogicCall(__func__, [this, amount] { HandleMouseScroll(amount); });
PushLogicCall([this, amount] { HandleMouseScroll(amount); });
}
void Input::HandleMouseScroll(const Vector2f& amount) {
assert(g_base->InLogicThread());
// If input is locked, allow it to mark us active but nothing more.
MarkInputActive();
if (IsInputLocked()) {
return;
}
MarkInputActive();
if (std::abs(amount.y) > 0.0001f) {
g_base->ui->SendWidgetMessage(
@ -1187,17 +1159,19 @@ void Input::HandleMouseScroll(const Vector2f& amount) {
void Input::PushSmoothMouseScrollEvent(const Vector2f& velocity,
bool momentum) {
SafePushLogicCall(__func__, [this, velocity, momentum] {
PushLogicCall([this, velocity, momentum] {
HandleSmoothMouseScroll(velocity, momentum);
});
}
void Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum) {
assert(g_base->InLogicThread());
// If input is locked, allow it to mark us active but nothing more.
MarkInputActive();
if (IsInputLocked()) {
return;
}
MarkInputActive();
bool handled = false;
handled = g_base->ui->SendWidgetMessage(
@ -1219,15 +1193,19 @@ void Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum) {
}
void Input::PushMouseMotionEvent(const Vector2f& position) {
SafePushLogicCall(__func__,
[this, position] { HandleMouseMotion(position); });
PushLogicCall([this, position] { HandleMouseMotion(position); });
}
void Input::HandleMouseMotion(const Vector2f& position) {
assert(g_base->graphics);
assert(g_base);
assert(g_base->InLogicThread());
MarkInputActive();
if (IsInputLocked()) {
return;
}
float old_cursor_pos_x = cursor_pos_x_;
float old_cursor_pos_y = cursor_pos_y_;
@ -1240,8 +1218,6 @@ void Input::HandleMouseMotion(const Vector2f& position) {
last_mouse_move_time_ = g_core->GetAppTimeMillisecs();
mouse_move_count_++;
bool handled{};
// If we have a touch-input in editing mode, pass along events to it.
// (it usually handles its own events but here we want it to play nice
// with stuff under it by blocking touches, etc)
@ -1250,48 +1226,35 @@ void Input::HandleMouseMotion(const Vector2f& position) {
cursor_pos_y_);
}
// UI interaction.
if (!IsInputLocked()) {
handled = g_base->ui->SendWidgetMessage(
WidgetMessage(WidgetMessage::Type::kMouseMove, nullptr, cursor_pos_x_,
cursor_pos_y_));
}
// Let any UI stuff handle it.
g_base->ui->HandleMouseMotion(cursor_pos_x_, cursor_pos_y_);
// Manual camera motion.
Camera* camera = g_base->graphics->camera();
if (!handled && camera && camera->manual()) {
if (camera && camera->manual()) {
float move_h = (cursor_pos_x_ - old_cursor_pos_x)
/ g_base->graphics->screen_virtual_width();
float move_v = (cursor_pos_y_ - old_cursor_pos_y)
/ g_base->graphics->screen_virtual_width();
camera->ManualHandleMouseMove(move_h, move_v);
}
// Old screen edge UI.
g_base->ui->HandleLegacyRootUIMouseMotion(cursor_pos_x_, cursor_pos_y_);
}
void Input::PushMouseDownEvent(int button, const Vector2f& position) {
SafePushLogicCall(__func__, [this, button, position] {
HandleMouseDown(button, position);
});
PushLogicCall(
[this, button, position] { HandleMouseDown(button, position); });
}
void Input::HandleMouseDown(int button, const Vector2f& position) {
assert(g_base);
assert(g_base->graphics);
assert(g_base->InLogicThread());
MarkInputActive();
if (IsInputLocked()) {
return;
}
// if (!g_base->ui->MainMenuVisible()) {
// return;
// }
MarkInputActive();
last_mouse_move_time_ = g_core->GetAppTimeMillisecs();
mouse_move_count_++;
@ -1306,7 +1269,6 @@ void Input::HandleMouseDown(int button, const Vector2f& position) {
last_click_time_ = click_time;
bool handled{};
// auto* root_widget = g_base->ui->root_widget();
// If we have a touch-input in editing mode, pass along events to it.
// (it usually handles its own events but here we want it to play nice
@ -1317,15 +1279,8 @@ void Input::HandleMouseDown(int button, const Vector2f& position) {
}
if (!handled) {
if (g_base->ui->HandleLegacyRootUIMouseDown(cursor_pos_x_, cursor_pos_y_)) {
handled = true;
}
}
if (!handled) {
handled = g_base->ui->SendWidgetMessage(
WidgetMessage(WidgetMessage::Type::kMouseDown, nullptr, cursor_pos_x_,
cursor_pos_y_, double_click ? 2 : 1));
handled = g_base->ui->HandleMouseDown(button, cursor_pos_x_, cursor_pos_y_,
double_click);
}
// Manual camera input.
@ -1349,8 +1304,7 @@ void Input::HandleMouseDown(int button, const Vector2f& position) {
}
void Input::PushMouseUpEvent(int button, const Vector2f& position) {
SafePushLogicCall(
__func__, [this, button, position] { HandleMouseUp(button, position); });
PushLogicCall([this, button, position] { HandleMouseUp(button, position); });
}
void Input::HandleMouseUp(int button, const Vector2f& position) {
@ -1363,8 +1317,6 @@ void Input::HandleMouseUp(int button, const Vector2f& position) {
cursor_pos_y_ = g_base->graphics->PixelToVirtualY(
position.y * g_base->graphics->screen_pixel_height());
bool handled{};
// If we have a touch-input in editing mode, pass along events to it.
// (it usually handles its own events but here we want it to play nice
// with stuff under it by blocking touches, etc)
@ -1373,14 +1325,7 @@ void Input::HandleMouseUp(int button, const Vector2f& position) {
cursor_pos_y_);
}
// ui_v1::Widget* root_widget = g_base->ui->root_widget();
// if (root_widget) {
handled = g_base->ui->SendWidgetMessage(WidgetMessage(
WidgetMessage::Type::kMouseUp, nullptr, cursor_pos_x_, cursor_pos_y_));
// }
Camera* camera = g_base->graphics->camera();
if (!handled && camera) {
if (Camera* camera = g_base->graphics->camera()) {
switch (button) {
case SDL_BUTTON_LEFT:
camera->set_mouse_left_down(false);
@ -1397,11 +1342,11 @@ void Input::HandleMouseUp(int button, const Vector2f& position) {
camera->UpdateManualMode();
}
g_base->ui->HandleLegacyRootUIMouseUp(cursor_pos_x_, cursor_pos_y_);
g_base->ui->HandleMouseUp(button, cursor_pos_x_, cursor_pos_y_);
}
void Input::PushTouchEvent(const TouchEvent& e) {
SafePushLogicCall(__func__, [e, this] { HandleTouchEvent(e); });
PushLogicCall([e, this] { HandleTouchEvent(e); });
}
void Input::HandleTouchEvent(const TouchEvent& e) {
@ -1501,19 +1446,19 @@ void Input::Draw(FrameDef* frame_def) {
}
auto Input::IsCursorVisible() const -> bool {
assert(g_base->InLogicThread());
if (!g_base->ui) {
if (!g_base) {
return false;
}
assert(g_base->InLogicThread());
// Keeps mouse hidden to start with..
// Keeps mouse hidden to start with.
if (mouse_move_count_ < 2) {
return false;
}
bool val;
// Show our cursor if any dialogs/windows are up or else if its been
// moved very recently.
// Show our cursor if any dialogs/windows are up or else if its been moved
// very recently.
if (g_base->ui->MainMenuVisible()) {
val = (g_core->GetAppTimeMillisecs() - last_mouse_move_time_ < 5000);
} else {

View File

@ -11,7 +11,7 @@
#include "ballistica/base/python/base_python.h"
#include "ballistica/base/support/plus_soft.h"
#include "ballistica/base/support/stdio_console.h"
#include "ballistica/base/ui/console.h"
#include "ballistica/base/ui/dev_console.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/shared/foundation/event_loop.h"
#include "ballistica/shared/python/python_sys.h"

View File

@ -63,11 +63,11 @@ void BasePlatformApple::DoOpenURL(const std::string& url) {
#endif
}
void BasePlatformApple::QuitApp() {
void BasePlatformApple::TerminateApp() {
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_HEADLESS_BUILD
core::AppleUtils::Quit(); // will post a cocoa terminate
core::AppleUtils::TerminateApp();
#else
BasePlatform::QuitApp();
BasePlatform::TerminateApp();
#endif
}

View File

@ -16,7 +16,7 @@ class BasePlatformApple : public BasePlatform {
void RestorePurchases() override;
void PurchaseAck(const std::string& purchase,
const std::string& order_id) override;
void QuitApp() override;
void TerminateApp() override;
void DoOpenURL(const std::string& url) override;

View File

@ -321,6 +321,11 @@ void BasePlatform::OnAppShutdown() { assert(g_base->InLogicThread()); }
void BasePlatform::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
void BasePlatform::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
void BasePlatform::QuitApp() { exit(g_base->return_value()); }
void BasePlatform::TerminateApp() { exit(g_base->return_value()); }
auto BasePlatform::CanSoftQuit() -> bool { return false; }
auto BasePlatform::CanBackQuit() -> bool { return false; }
void BasePlatform::DoBackQuit() {}
void BasePlatform::DoSoftQuit() {}
} // namespace ballistica::base

View File

@ -33,8 +33,35 @@ class BasePlatform {
virtual void OnScreenSizeChange();
virtual void DoApplyAppConfig();
/// Quit the app (can be immediate or via posting some high level event).
virtual void QuitApp();
/// Return whether this platform supports soft-quit. A soft quit is
/// when the app is reset/backgrounded/etc. but remains running in case
/// needed again. Generally this is the behavior on mobile apps.
virtual auto CanSoftQuit() -> bool;
/// Implement soft-quit behavior. Will always be called in the logic
/// thread. Make sure to also override CanBackQuit to reflect this being
/// present. Note that when quitting the app yourself, you should use
/// g_base->QuitApp(); do not call this directly.
virtual void DoSoftQuit();
/// Return whether this platform supports back-quit. A back quit is a
/// variation of soft-quit generally triggered by a back button, which may
/// give different results in the OS. For example on Android this may
/// result in jumping back to the previous Android activity instead of
/// just ending the current one and dumping to the home screen as normal
/// soft quit might do.
virtual auto CanBackQuit() -> bool;
/// Implement back-quit behavior. Will always be called in the logic
/// thread. Make sure to also override CanBackQuit to reflect this being
/// present. Note that when quitting the app yourself, you should use
/// g_base->QuitApp(); do not call this directly.
virtual void DoBackQuit();
/// Terminate the app. This can be immediate or by posting some high
/// level event. There should be nothing left to do in the engine at
/// this point.
virtual void TerminateApp();
#pragma mark IN APP PURCHASES --------------------------------------------------

View File

@ -5,6 +5,7 @@
#include "ballistica/base/app_mode/app_mode_empty.h"
#include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/logic/logic.h"
#include "ballistica/base/platform/base_platform.h"
#include "ballistica/base/python/base_python.h"
#include "ballistica/base/python/support/python_context_call_runnable.h"
#include "ballistica/base/support/stress_test.h"
@ -511,45 +512,23 @@ static auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds)
static const char* kwlist[] = {"soft", "back", nullptr};
int soft = 0;
int back = 0;
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|ii",
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|pp",
const_cast<char**>(kwlist), &soft, &back)) {
return nullptr;
}
// Log(LogLevel::kDebug,
// "QUIT soft=" + std::to_string(soft) + " back=" + std::to_string(back));
// FIXME this should all just go through platform and/or app-adapter.
if (g_buildconfig.ostype_ios_tvos()) {
// This should never be called on iOS
Log(LogLevel::kError, "Quit called.");
}
bool handled = false;
// A few types get handled specially on Android.
if (g_buildconfig.ostype_android()) {
if (!handled && back) {
// Back-quit simply synthesizes a back press.
// Note to self: I remember this behaved slightly differently than
// doing a soft quit but I should remind myself how.
g_core->platform->AndroidSynthesizeBackPress();
handled = true;
}
if (!handled && soft) {
// Soft-quit just kills our activity but doesn't run app shutdown.
// Thus we'll be able to spin back up (reset to the main menu)
// if the user re-launches us.
g_core->platform->AndroidQuitActivity();
handled = true;
QuitType quit_type{};
if (back) {
if (!soft) {
Log(LogLevel::kWarning,
"Got soft=False back=True in quit() which is ambiguous.");
}
quit_type = QuitType::kBack;
} else if (soft) {
quit_type = QuitType::kSoft;
} else {
quit_type = QuitType::kHard;
}
// In all other cases, kick off a standard app shutdown.
if (!handled) {
g_base->logic->event_loop()->PushCall([] { g_base->logic->Shutdown(); });
}
g_base->QuitApp(quit_type);
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
@ -559,15 +538,18 @@ static PyMethodDef PyQuitDef = {
(PyCFunction)PyQuit, // method
METH_VARARGS | METH_KEYWORDS, // flags
"quit(soft: bool = False, back: bool = False) -> None\n"
"quit(soft: bool = True, back: bool = False) -> None\n"
"\n"
"Quit the game.\n"
"Quit the app.\n"
"\n"
"Category: **General Utility Functions**\n"
"\n"
"On systems like Android, 'soft' will end the activity but keep the\n"
"app running.",
};
"On platforms such as mobile, a 'soft' quit may background and/or reset\n"
"the app but keep it running. A 'back' quit is a special form of soft\n"
"quit that may trigger different behavior in the OS. On Android, for\n"
"example, a back-quit may simply jump to the previous Android activity,\n"
"while a regular soft quit may just exit the current activity and dump\n"
"the user at their home screen."};
// ----------------------------- apply_config ----------------------------------

View File

@ -218,6 +218,8 @@ void AppConfig::SetupEntries() {
BoolEntry("Always Use Internal Keyboard", false);
bool_entries_[BoolID::kShowFPS] = BoolEntry("Show FPS", false);
bool_entries_[BoolID::kShowPing] = BoolEntry("Show Ping", false);
bool_entries_[BoolID::kShowDevConsoleButton] =
BoolEntry("Show Dev Console Button", false);
bool_entries_[BoolID::kEnableTVBorder] =
BoolEntry("TV Border", g_core->platform->IsRunningOnTV());
bool_entries_[BoolID::kKeyboardP2Enabled] =

View File

@ -64,6 +64,7 @@ class AppConfig {
kAlwaysUseInternalKeyboard,
kShowFPS,
kShowPing,
kShowDevConsoleButton,
kEnableTVBorder,
kKeyboardP2Enabled,
kEnablePackageMods,

View File

@ -6,6 +6,7 @@
#include "ballistica/base/ui/ui.h"
// Predeclare some types we use.
namespace ballistica::ui_v1 {
class RootUI;
class Widget;
@ -13,10 +14,9 @@ class Widget;
namespace ballistica::base {
/// 'Soft' interface to the ui_v1 feature-set, managed by base.
/// Feature-sets listing ui_v1 as a soft requirement must limit their use of
/// it to these methods and should be prepared to handle the not-present
/// case.
/// 'Soft' interface to the ui_v1 feature-set, managed by base. Feature-sets
/// listing ui_v1 as a soft requirement must limit their use of it to these
/// methods and should be prepared to handle the not-present case.
class UIV1SoftInterface {
public:
virtual void DoHandleDeviceMenuPress(base::InputDevice* device) = 0;

View File

@ -1,6 +1,6 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/base/ui/console.h"
#include "ballistica/base/ui/dev_console.h"
#include "ballistica/base/app_mode/app_mode.h"
#include "ballistica/base/audio/audio.h"
@ -19,14 +19,14 @@
namespace ballistica::base {
// How much of the screen the console covers when it is at full size.
const float kConsoleSize = 0.9f;
const float kConsoleZDepth = 0.0f;
const int kConsoleLineLimit = 80;
const int kStringBreakUpSize = 1950;
const int kActivateKey1 = SDLK_BACKQUOTE;
const int kActivateKey2 = SDLK_F2;
const float kDevConsoleSize = 0.9f;
const float kDevConsoleZDepth = 0.0f;
const int kDevConsoleLineLimit = 80;
const int kDevConsoleStringBreakUpSize = 1950;
const int kDevConsoleActivateKey1 = SDLK_BACKQUOTE;
const int kDevConsoleActivateKey2 = SDLK_F2;
Console::Console() {
DevConsole::DevConsole() {
assert(g_base->InLogicThread());
std::string title = std::string("BallisticaKit ") + kEngineVersion + " ("
+ std::to_string(kEngineBuildNumber) + ")";
@ -42,15 +42,15 @@ Console::Console() {
prompt_text_group_.set_text(">");
}
Console::~Console() = default;
DevConsole::~DevConsole() = default;
auto Console::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
auto DevConsole::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
assert(g_base->InLogicThread());
// Handle our toggle buttons no matter whether we're active.
switch (keysym->sym) {
case kActivateKey1:
case kActivateKey2: {
case kDevConsoleActivateKey1:
case kDevConsoleActivateKey2: {
if (!g_buildconfig.demo_build() && !g_buildconfig.arcade_build()) {
// (reset input so characters don't continue walking and stuff)
g_base->input->ResetHoldStates();
@ -111,9 +111,7 @@ auto Console::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
case SDLK_KP_ENTER:
case SDLK_RETURN: {
if (!input_enabled_) {
Log(LogLevel::kWarning,
"Console input is not allowed until the app reaches the 'running' "
"state.");
Log(LogLevel::kWarning, "Console input is not allowed yet.");
break;
}
input_history_position_ = 0;
@ -121,7 +119,7 @@ auto Console::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
last_line_.clear();
lines_.clear();
} else {
PushCommand(input_string_);
SubmitCommand_(input_string_);
}
input_history_.push_front(input_string_);
if (input_history_.size() > 100) {
@ -151,7 +149,7 @@ auto Console::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
return true;
}
void Console::PushCommand(const std::string& command) {
void DevConsole::SubmitCommand_(const std::string& command) {
assert(g_base);
g_base->logic->event_loop()->PushCall([command] {
// These are always run in whichever context is 'visible'.
@ -172,13 +170,25 @@ void Console::PushCommand(const std::string& command) {
});
}
void Console::EnableInput() {
void DevConsole::EnableInput() {
assert(g_base->InLogicThread());
input_enabled_ = true;
}
void Console::ToggleState() {
void DevConsole::Dismiss() {
assert(g_base->InLogicThread());
if (state_ == State::kInactive) {
return;
}
state_prev_ = state_;
state_ = State::kInactive;
transition_start_ = g_core->GetAppTimeMillisecs();
}
void DevConsole::ToggleState() {
assert(g_base->InLogicThread());
state_prev_ = state_;
switch (state_) {
case State::kInactive:
state_ = State::kMini;
@ -194,7 +204,7 @@ void Console::ToggleState() {
transition_start_ = g_core->GetAppTimeMillisecs();
}
auto Console::HandleTextEditing(const std::string& text) -> bool {
auto DevConsole::HandleTextEditing(const std::string& text) -> bool {
assert(g_base->InLogicThread());
if (state_ == State::kInactive) {
return false;
@ -209,31 +219,32 @@ auto Console::HandleTextEditing(const std::string& text) -> bool {
return true;
}
auto Console::HandleKeyRelease(const SDL_Keysym* keysym) -> bool {
auto DevConsole::HandleKeyRelease(const SDL_Keysym* keysym) -> bool {
// Always absorb our activate keys.
if (keysym->sym == kActivateKey1 || keysym->sym == kActivateKey2) {
if (keysym->sym == kDevConsoleActivateKey1
|| keysym->sym == kDevConsoleActivateKey2) {
return true;
}
// Otherwise simply absorb all key-ups if we're active.
// Otherwise absorb *all* key-ups when we're active.
return state_ != State::kInactive;
}
#pragma clang diagnostic push
#pragma ide diagnostic ignored "LocalValueEscapesScope"
void Console::Print(const std::string& s_in) {
void DevConsole::Print(const std::string& s_in) {
assert(g_base->InLogicThread());
std::string s = Utils::GetValidUTF8(s_in.c_str(), "cspr");
last_line_ += s;
std::vector<std::string> broken_up;
g_base->text_graphics->BreakUpString(last_line_.c_str(), kStringBreakUpSize,
&broken_up);
g_base->text_graphics->BreakUpString(
last_line_.c_str(), kDevConsoleStringBreakUpSize, &broken_up);
// Spit out all completed lines and keep the last one as lastline.
for (size_t i = 0; i < broken_up.size() - 1; i++) {
lines_.emplace_back(broken_up[i], g_core->GetAppTimeMillisecs());
if (lines_.size() > kConsoleLineLimit) {
if (lines_.size() > kDevConsoleLineLimit) {
lines_.pop_front();
}
}
@ -243,7 +254,7 @@ void Console::Print(const std::string& s_in) {
#pragma clang diagnostic pop
void Console::Draw(RenderPass* pass) {
void DevConsole::Draw(RenderPass* pass) {
millisecs_t transition_ticks = 100;
if ((transition_start_ != 0)
&& (state_ != State::kInactive
@ -257,27 +268,37 @@ void Console::Draw(RenderPass* pass) {
if (state_ == State::kMini) {
bottom = pass->virtual_height() - mini_size;
} else {
bottom = pass->virtual_height() - pass->virtual_height() * kConsoleSize;
bottom =
pass->virtual_height() - pass->virtual_height() * kDevConsoleSize;
}
if (g_core->GetAppTimeMillisecs() - transition_start_ < transition_ticks) {
if (state_ == State::kMini) {
bottom = pass->virtual_height() * (1.0f - ratio) + bottom * (ratio);
} else if (state_ == State::kFull) {
bottom =
(pass->virtual_height() - pass->virtual_height() * kConsoleSize)
* (ratio)
+ (pass->virtual_height() - mini_size) * (1.0f - ratio);
float from_height;
if (state_prev_ == State::kMini) {
from_height = pass->virtual_height() - mini_size;
} else if (state_prev_ == State::kFull) {
from_height =
pass->virtual_height() - pass->virtual_height() * kDevConsoleSize;
} else {
bottom = pass->virtual_height() * ratio + bottom * (1.0f - ratio);
from_height = pass->virtual_height();
}
float to_height;
if (state_ == State::kMini) {
to_height = pass->virtual_height() - mini_size;
} else if (state_ == State::kFull) {
to_height =
pass->virtual_height() - pass->virtual_height() * kDevConsoleSize;
} else {
to_height = pass->virtual_height();
}
bottom = to_height * ratio + from_height * (1.0 - ratio);
}
{
bg_mesh_.SetPositionAndSize(0, bottom, kConsoleZDepth,
bg_mesh_.SetPositionAndSize(0, bottom, kDevConsoleZDepth,
pass->virtual_width(),
(pass->virtual_height() - bottom));
stripe_mesh_.SetPositionAndSize(0, bottom + 15, kConsoleZDepth,
stripe_mesh_.SetPositionAndSize(0, bottom + 15, kDevConsoleZDepth,
pass->virtual_width(), 15);
shadow_mesh_.SetPositionAndSize(0, bottom - 7, kConsoleZDepth,
shadow_mesh_.SetPositionAndSize(0, bottom - 7, kDevConsoleZDepth,
pass->virtual_width(), 7);
SimpleComponent c(pass);
c.SetTransparent(true);
@ -304,7 +325,8 @@ void Console::Draw(RenderPass* pass) {
for (int e = 0; e < elem_count; e++) {
c.SetTexture(built_text_group_.GetElementTexture(e));
c.PushTransform();
c.Translate(pass->virtual_width() - 175.0f, bottom + 0, kConsoleZDepth);
c.Translate(pass->virtual_width() - 175.0f, bottom + 0,
kDevConsoleZDepth);
c.Scale(0.5f, 0.5f, 0.5f);
c.DrawMesh(built_text_group_.GetElementMesh(e));
c.PopTransform();
@ -313,7 +335,7 @@ void Console::Draw(RenderPass* pass) {
for (int e = 0; e < elem_count; e++) {
c.SetTexture(title_text_group_.GetElementTexture(e));
c.PushTransform();
c.Translate(20.0f, bottom + 0, kConsoleZDepth);
c.Translate(20.0f, bottom + 0, kDevConsoleZDepth);
c.Scale(0.5f, 0.5f, 0.5f);
c.DrawMesh(title_text_group_.GetElementMesh(e));
c.PopTransform();
@ -323,7 +345,7 @@ void Console::Draw(RenderPass* pass) {
c.SetTexture(prompt_text_group_.GetElementTexture(e));
c.SetColor(1, 1, 1, 1);
c.PushTransform();
c.Translate(5.0f, bottom + 15.0f, kConsoleZDepth);
c.Translate(5.0f, bottom + 15.0f, kDevConsoleZDepth);
c.Scale(0.5f, 0.5f, 0.5f);
c.DrawMesh(prompt_text_group_.GetElementMesh(e));
c.PopTransform();
@ -332,7 +354,7 @@ void Console::Draw(RenderPass* pass) {
for (int e = 0; e < elem_count; e++) {
c.SetTexture(input_text_group_.GetElementTexture(e));
c.PushTransform();
c.Translate(15.0f, bottom + 15.0f, kConsoleZDepth);
c.Translate(15.0f, bottom + 15.0f, kDevConsoleZDepth);
c.Scale(0.5f, 0.5f, 0.5f);
c.DrawMesh(input_text_group_.GetElementMesh(e));
c.PopTransform();
@ -350,7 +372,7 @@ void Console::Draw(RenderPass* pass) {
c.PushTransform();
c.Translate(
19.0f + g_base->text_graphics->GetStringWidth(input_string_) * 0.5f,
bottom + 23.0f, kConsoleZDepth);
bottom + 23.0f, kDevConsoleZDepth);
c.Scale(5, 11, 1.0f);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kImage1x1));
c.PopTransform();
@ -365,7 +387,7 @@ void Console::Draw(RenderPass* pass) {
c.SetColor(1, 1, 1, 1);
float h = 0.5f
* (g_base->graphics->screen_virtual_width()
- (kStringBreakUpSize * draw_scale));
- (kDevConsoleStringBreakUpSize * draw_scale));
float v = bottom + 32.0f;
if (!last_line_.empty()) {
if (last_line_mesh_dirty_) {
@ -379,7 +401,7 @@ void Console::Draw(RenderPass* pass) {
for (int e = 0; e < elem_count; e++) {
c.SetTexture(last_line_mesh_group_->GetElementTexture(e));
c.PushTransform();
c.Translate(h, v + 2, kConsoleZDepth);
c.Translate(h, v + 2, kDevConsoleZDepth);
c.Scale(draw_scale, draw_scale);
c.DrawMesh(last_line_mesh_group_->GetElementMesh(e));
c.PopTransform();
@ -391,7 +413,7 @@ void Console::Draw(RenderPass* pass) {
for (int e = 0; e < elem_count; e++) {
c.SetTexture(i->GetText().GetElementTexture(e));
c.PushTransform();
c.Translate(h, v + 2, kConsoleZDepth);
c.Translate(h, v + 2, kDevConsoleZDepth);
c.Scale(draw_scale, draw_scale);
c.DrawMesh(i->GetText().GetElementMesh(e));
c.PopTransform();

View File

@ -1,7 +1,7 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_UI_CONSOLE_H_
#define BALLISTICA_BASE_UI_CONSOLE_H_
#ifndef BALLISTICA_BASE_UI_DEV_CONSOLE_H_
#define BALLISTICA_BASE_UI_DEV_CONSOLE_H_
#include <list>
#include <string>
@ -12,22 +12,31 @@
namespace ballistica::base {
class Console {
class DevConsole {
public:
Console();
~Console();
auto active() const -> bool { return (state_ != State::kInactive); }
auto transition_start() const -> millisecs_t { return transition_start_; }
DevConsole();
~DevConsole();
auto IsActive() const -> bool { return (state_ != State::kInactive); }
auto HandleTextEditing(const std::string& text) -> bool;
auto HandleKeyPress(const SDL_Keysym* keysym) -> bool;
auto HandleKeyRelease(const SDL_Keysym* keysym) -> bool;
auto transition_start() const -> millisecs_t { return transition_start_; }
/// Toggle between mini, fullscreen, and inactive.
void ToggleState();
/// Tell the console to quietly go away no matter what state it is in.
void Dismiss();
/// Print text to the console.
void Print(const std::string& s_in);
void Draw(RenderPass* pass);
/// Called when the console should start accepting Python command input.
void EnableInput();
private:
void PushCommand(const std::string& command);
void SubmitCommand_(const std::string& command);
enum class State { kInactive, kMini, kFull };
ImageMesh bg_mesh_;
ImageMesh stripe_mesh_;
@ -40,6 +49,7 @@ class Console {
bool input_text_dirty_{true};
millisecs_t transition_start_{};
State state_{State::kInactive};
State state_prev_{State::kInactive};
class Message {
public:
@ -71,4 +81,4 @@ class Console {
} // namespace ballistica::base
#endif // BALLISTICA_BASE_UI_CONSOLE_H_
#endif // BALLISTICA_BASE_UI_DEV_CONSOLE_H_

View File

@ -3,13 +3,16 @@
#include "ballistica/base/ui/ui.h"
#include "ballistica/base/audio/audio.h"
#include "ballistica/base/graphics/component/simple_component.h"
#include "ballistica/base/input/device/keyboard_input.h"
#include "ballistica/base/input/input.h"
#include "ballistica/base/logic/logic.h"
#include "ballistica/base/python/base_python.h"
#include "ballistica/base/support/app_config.h"
#include "ballistica/base/support/ui_v1_soft.h"
#include "ballistica/base/ui/console.h"
#include "ballistica/base/ui/dev_console.h"
#include "ballistica/shared/foundation/event_loop.h"
#include "ballistica/shared/foundation/inline.h"
#include "ballistica/shared/generic/utils.h"
namespace ballistica::base {
@ -17,9 +20,10 @@ namespace ballistica::base {
static const int kUIOwnerTimeoutSeconds = 30;
UI::UI() {
// Figure out our interface type.
assert(g_core);
// Figure out our interface scale.
// Allow overriding via an environment variable.
auto* ui_override = getenv("BA_UI_SCALE");
if (ui_override) {
@ -37,7 +41,7 @@ UI::UI() {
if (!force_scale_) {
// Use automatic val.
if (g_core->IsVRMode() || g_core->platform->IsRunningOnTV()) {
// VR and tv builds always use medium.
// VR and TV modes always use medium.
scale_ = UIScale::kMedium;
} else {
scale_ = g_core->platform->GetUIScale();
@ -85,6 +89,8 @@ void UI::DoApplyAppConfig() {
if (g_base->HaveUIV1()) {
g_base->ui_v1()->DoApplyAppConfig();
}
show_dev_console_button_ =
g_base->app_config->Resolve(AppConfig::BoolID::kShowDevConsoleButton);
}
auto UI::MainMenuVisible() const -> bool {
@ -114,36 +120,69 @@ auto UI::PartyWindowOpen() -> bool {
return false;
}
void UI::HandleLegacyRootUIMouseMotion(float x, float y) {
if (g_base->HaveUIV1()) {
g_base->ui_v1()->HandleLegacyRootUIMouseMotion(x, y);
auto UI::HandleMouseDown(int button, float x, float y, bool double_click)
-> bool {
bool handled{};
if (show_dev_console_button_ && button == 1) {
float vx = g_base->graphics->screen_virtual_width();
float vy = g_base->graphics->screen_virtual_height();
if (InDevConsoleButton_(x, y)) {
dev_console_button_pressed_ = true;
}
}
if (!handled && g_base->HaveUIV1()) {
handled = g_base->ui_v1()->HandleLegacyRootUIMouseDown(x, y);
}
if (!handled) {
handled = SendWidgetMessage(WidgetMessage(
WidgetMessage::Type::kMouseDown, nullptr, x, y, double_click ? 2 : 1));
}
return handled;
}
auto UI::HandleLegacyRootUIMouseDown(float x, float y) -> bool {
if (g_base->HaveUIV1()) {
return g_base->ui_v1()->HandleLegacyRootUIMouseDown(x, y);
}
return false;
}
void UI::HandleMouseUp(int button, float x, float y) {
assert(g_base->InLogicThread());
SendWidgetMessage(
WidgetMessage(WidgetMessage::Type::kMouseUp, nullptr, x, y));
if (dev_console_button_pressed_) {
if (InDevConsoleButton_(x, y)) {
if (auto* console = g_base->console()) {
console->ToggleState();
}
}
dev_console_button_pressed_ = false;
}
void UI::HandleLegacyRootUIMouseUp(float x, float y) {
if (g_base->HaveUIV1()) {
g_base->ui_v1()->HandleLegacyRootUIMouseUp(x, y);
}
}
void UI::HandleMouseMotion(float x, float y) {
SendWidgetMessage(
WidgetMessage(WidgetMessage::Type::kMouseMove, nullptr, x, y));
if (g_base->HaveUIV1()) {
g_base->ui_v1()->HandleLegacyRootUIMouseMotion(x, y);
}
}
void UI::PushBackButtonCall(InputDevice* input_device) {
g_base->logic->event_loop()->PushCall([this, input_device] {
assert(g_base->InLogicThread());
// If there's a UI up, send along a cancel message.
if (g_base->ui->MainMenuVisible()) {
g_base->ui->SendWidgetMessage(
WidgetMessage(WidgetMessage::Type::kCancel));
if (MainMenuVisible()) {
SendWidgetMessage(WidgetMessage(WidgetMessage::Type::kCancel));
} else {
// If there's no main screen or overlay windows, ask for a menu owned by
// this device.
// If there's no main screen or overlay windows, ask for a menu owned
// by this device.
MainMenuPress_(input_device);
}
});
@ -173,24 +212,6 @@ void UI::SetUIInputDevice(InputDevice* input_device) {
last_input_device_use_time_ = g_core->GetAppTimeMillisecs();
}
UI::UILock::UILock(bool write) {
assert(g_base->ui);
assert(g_base->InLogicThread());
if (write && g_base->ui->ui_lock_count_ != 0) {
BA_LOG_ERROR_TRACE_ONCE("Illegal operation: UI is locked");
}
g_base->ui->ui_lock_count_++;
}
UI::UILock::~UILock() {
g_base->ui->ui_lock_count_--;
if (g_base->ui->ui_lock_count_ < 0) {
BA_LOG_ERROR_TRACE_ONCE("ui_lock_count_ < 0");
g_base->ui->ui_lock_count_ = 0;
}
}
void UI::Reset() {
if (g_base->HaveUIV1()) {
g_base->ui_v1()->Reset();
@ -198,9 +219,9 @@ void UI::Reset() {
}
auto UI::ShouldHighlightWidgets() const -> bool {
// Show selection highlights only if we've got controllers connected and only
// when the main UI is visible (dont want a selection highlight for toolbar
// buttons during a game).
// Show selection highlights only if we've got controllers connected and
// only when the main UI is visible (dont want a selection highlight for
// toolbar buttons during a game).
return g_base->input->have_non_touch_inputs() && MainMenuVisible();
}
@ -236,22 +257,24 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* {
assert(input_device);
assert(g_base->InLogicThread());
// We only allow input-devices to control the UI when there's a window/dialog
// on the screen (even though our top/bottom bars still exist).
// We only allow input-devices to control the UI when there's a
// window/dialog on the screen (even though our top/bottom bars still
// exist).
if (!MainMenuVisible()) {
return nullptr;
}
millisecs_t time = g_core->GetAppTimeMillisecs();
bool print_menu_owner = false;
bool print_menu_owner{};
ui_v1::Widget* ret_val;
// Ok here's the deal:
// Because having 10 controllers attached to the UI is pure chaos,
// we only allow one input device at a time to control the menu.
// However, if no events are received by that device for a long time,
// it is up for grabs to the next device that requests it.
//
// Because having 10 controllers attached to the UI is pure chaos, we only
// allow one input device at a time to control the menu. However, if no
// events are received by that device for a long time, it is up for grabs
// to the next device that requests it.
if (!g_base->HaveUIV1()) {
ret_val = nullptr;
@ -265,7 +288,6 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* {
// seconds ago to automatically own a newly created widget).
last_input_device_use_time_ = time;
ui_input_device_ = input_device;
// ret_val = screen_root_widget_.Get();
ret_val = g_base->ui_v1()->GetRootWidget();
} else {
// For rejected input devices, play error sounds sometimes so they know
@ -301,8 +323,8 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* {
} else if (input->GetDeviceName() == "TouchScreen") {
name = g_base->assets->GetResourceString("touchScreenText");
} else {
// We used to use player names here, but that's kinda sloppy and random;
// lets just go with device names/numbers.
// We used to use player names here, but that's kinda sloppy and
// random; lets just go with device names/numbers.
auto devicesWithName =
g_base->input->GetInputDevicesWithName(input->GetDeviceName());
if (devicesWithName.size() == 1) {
@ -328,6 +350,82 @@ void UI::Draw(FrameDef* frame_def) {
}
}
void UI::DrawDev(FrameDef* frame_def) {
// Draw dev console.
if (g_base->console()) {
g_base->console()->Draw(frame_def->overlay_pass());
}
// Draw dev console button.
if (show_dev_console_button_) {
DrawDevConsoleButton_(frame_def);
}
}
auto UI::DevConsoleButtonSize_() const -> float {
if (scale_ == UIScale::kLarge) {
return 25.0f;
} else if (scale_ == UIScale::kMedium) {
return 40.0f;
}
return 60.0f;
}
auto UI::InDevConsoleButton_(float x, float y) const -> bool {
float vwidth = g_base->graphics->screen_virtual_width();
float vheight = g_base->graphics->screen_virtual_height();
float bsz = DevConsoleButtonSize_();
float bszh = bsz * 0.5f;
float centerx = vwidth - bsz * 0.5f;
float centery = vheight * 0.5f - bsz * 0.5f;
float diffx = ::std::abs(centerx - x);
float diffy = ::std::abs(centery - y);
return diffx <= bszh && diffy <= bszh;
}
void UI::DrawDevConsoleButton_(FrameDef* frame_def) {
if (!dev_console_button_txt_.Exists()) {
dev_console_button_txt_ = Object::New<TextGroup>();
dev_console_button_txt_->set_text("dev");
}
auto& grp(*dev_console_button_txt_);
float vwidth = g_base->graphics->screen_virtual_width();
float vheight = g_base->graphics->screen_virtual_height();
float bsz = DevConsoleButtonSize_();
SimpleComponent c(frame_def->overlay_pass());
c.SetTransparent(true);
if (dev_console_button_pressed_) {
c.SetColor(1.0f, 1.0f, 1.0f, 0.8f);
} else {
c.SetColor(0.5f, 0.5f, 0.5f, 0.8f);
}
{
auto xf = c.ScopedTransform();
c.Translate(vwidth - bsz * 0.5f, vheight * 0.5f - bsz * 0.5f,
kCursorZDepth - 0.01f);
c.Scale(bsz, bsz, 1.0f);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kImage1x1));
{
auto xf = c.ScopedTransform();
c.Scale(0.02f, 0.02f, 1.0f);
c.Translate(-20.0f, -15.0f, 0.0f);
int text_elem_count = grp.GetElementCount();
if (dev_console_button_pressed_) {
c.SetColor(1.0f, 1.0f, 1.0f, 1.0f);
} else {
c.SetColor(0.0f, 0.0f, 0.0f, 1.0f);
}
for (int e = 0; e < text_elem_count; e++) {
c.SetTexture(grp.GetElementTexture(e));
c.SetFlatness(1.0f);
c.DrawMesh(grp.GetElementMesh(e));
}
}
}
c.Submit();
}
void UI::ShowURL(const std::string& url) {
if (g_base->HaveUIV1()) {
g_base->ui_v1()->DoShowURL(url);
@ -339,15 +437,17 @@ void UI::ShowURL(const std::string& url) {
void UI::ConfirmQuit() {
g_base->logic->event_loop()->PushCall([] {
// If the in-app console is active, dismiss it.
if (g_base->console() != nullptr && g_base->console()->IsActive()) {
g_base->console()->Dismiss();
}
assert(g_base->InLogicThread());
// If we're headless or input is locked or the in-app-console is up or
// we don't have ui-v1, just quit immediately; a confirm screen
// wouldn't work anyway.
// If we're headless or we don't have ui-v1, just quit immediately; a
// confirm screen wouldn't work anyway.
if (g_core->HeadlessMode() || g_base->input->IsInputLocked()
|| !g_base->HaveUIV1()
|| (g_base->console() != nullptr && g_base->console()->active())) {
g_base->logic->Shutdown();
// g_base->python->objs().Get(BasePython::ObjID::kQuitCall).Call();
|| !g_base->HaveUIV1()) {
g_base->QuitApp();
return;
} else {
ScopedSetContext ssc(nullptr);

View File

@ -10,19 +10,6 @@
#include "ballistica/base/ui/widget_message.h"
#include "ballistica/shared/generic/timer_list.h"
// UI-Locks: make sure widget-lists don't change under you. Use a read-lock
// if you just need to ensure lists remain intact but won't be changing
// anything. Use a write-lock whenever modifying a list.
#if BA_DEBUG_BUILD
#define BA_DEBUG_UI_READ_LOCK ::ballistica::base::UI::UILock ui_lock(false)
#define BA_DEBUG_UI_WRITE_LOCK ::ballistica::base::UI::UILock ui_lock(true)
#else
#define BA_DEBUG_UI_READ_LOCK
#define BA_DEBUG_UI_WRITE_LOCK
#endif
#define BA_UI_READ_LOCK UI::UILock ui_lock(false)
#define BA_UI_WRITE_LOCK UI::UILock ui_lock(true)
// Predeclare a few things from ui_v1.
namespace ballistica::ui_v1 {
class Widget;
@ -46,14 +33,17 @@ class UI {
void LanguageChanged();
/// Reset all UI to a default state. Generally should be called when
/// switching app-modes or when resetting things within an app mode.
void Reset();
/// Pop up an in-app window to show a url (NOT in a browser). Can be
/// called from any thread.
/// Pop up an in-app window to display a URL (NOT to open the URL in a
/// browser). Can be called from any thread.
void ShowURL(const std::string& url);
/// High level call to request a quit ui. When a UI can't be shown,
/// triggers an immediate shutdown. This can be called from any thread.
/// High level call to request a quit; ideally with a confirmation ui.
/// When a UI can't be shown, triggers an immediate shutdown. This can be
/// called from any thread.
void ConfirmQuit();
/// Return whether there is UI present in either the main or overlay
@ -61,66 +51,65 @@ class UI {
auto MainMenuVisible() const -> bool;
auto PartyIconVisible() -> bool;
void ActivatePartyIcon();
void HandleLegacyRootUIMouseMotion(float x, float y);
auto HandleLegacyRootUIMouseDown(float x, float y) -> bool;
void HandleLegacyRootUIMouseUp(float x, float y);
auto PartyWindowOpen() -> bool;
void ActivatePartyIcon();
auto HandleMouseDown(int button, float x, float y, bool double_click) -> bool;
void HandleMouseUp(int button, float x, float y);
void HandleMouseMotion(float x, float y);
/// Draw regular UI.
void Draw(FrameDef* frame_def);
// Returns the widget an input should send commands to, if any. Also
// potentially locks other inputs out of controlling the UI, so only call
// this if you intend on sending a message to that widget.
/// Draw dev UI on top.
void DrawDev(FrameDef* frame_def);
/// Return the widget an input-device should send commands to, if any.
/// Potentially assigns UI control to the provide device, so only call
/// this if you intend on actually sending a message to that widget.
auto GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget*;
// Send message to the active widget.
/// Send a message to the active widget.
auto SendWidgetMessage(const WidgetMessage& msg) -> int;
/// Set the device controlling the UI.
void SetUIInputDevice(InputDevice* input_device);
// Returns the input-device that currently owns the menu; otherwise
// nullptr.
/// Return the input-device that currently owns the UI; otherwise nullptr.
auto GetUIInputDevice() const -> InputDevice*;
/// Schedule a back button press. Can be called from any thread.
void PushBackButtonCall(InputDevice* input_device);
// Returns whether currently selected widgets should flash. This will be
// false in some situations such as when only touch screen control is
// active.
/// Return whether currently selected widgets should flash. This will be
/// false in some situations such as when only touch screen control is
/// present.
auto ShouldHighlightWidgets() const -> bool;
// Same except for button shortcuts; these generally only get shown if a
// joystick of some form is present.
/// Return whether currently selected widget should show button shortcuts.
/// These generally only get shown if a joystick of some form is present.
auto ShouldShowButtonShortcuts() const -> bool;
// Used to ensure widgets are not created or destroyed at certain times
// (while traversing widget hierarchy, etc).
class UILock {
public:
explicit UILock(bool write);
~UILock();
private:
BA_DISALLOW_CLASS_COPIES(UILock);
};
/// Overall ui scale for the app.
auto scale() const { return scale_; }
/// Push a generic 'menu press' event, optionally associated with an
/// input device (nullptr to specify none). Note: caller must ensure
/// a RemoveInputDevice() call does not arrive at the logic thread
/// before this one.
/// Push a generic 'menu press' event, optionally associated with an input
/// device (nullptr to specify none). Can be called from any thread.
void PushMainMenuPressCall(InputDevice* device);
private:
void MainMenuPress_(InputDevice* device);
auto DevConsoleButtonSize_() const -> float;
auto InDevConsoleButton_(float x, float y) const -> bool;
void DrawDevConsoleButton_(FrameDef* frame_def);
Object::WeakRef<InputDevice> ui_input_device_;
millisecs_t last_input_device_use_time_{};
millisecs_t last_widget_input_reject_err_sound_time_{};
int ui_lock_count_{};
UIScale scale_{UIScale::kLarge};
bool force_scale_{};
bool show_dev_console_button_{};
bool dev_console_button_pressed_{};
Object::Ref<TextGroup> dev_console_button_txt_;
};
} // namespace ballistica::base

View File

@ -682,14 +682,6 @@ void CorePlatform::AndroidSetResString(const std::string& res) {
throw Exception();
}
void CorePlatform::AndroidSynthesizeBackPress() {
Log(LogLevel::kError, "AndroidSynthesizeBackPress() unimplemented");
}
void CorePlatform::AndroidQuitActivity() {
Log(LogLevel::kError, "AndroidQuitActivity() unimplemented");
}
auto CorePlatform::GetDeviceV1AccountID() -> std::string {
if (g_core->HeadlessMode()) {
return "S-" + GetLegacyDeviceUUID();
@ -758,12 +750,6 @@ void CorePlatform::MusicPlayerSetVolume(float volume) {
auto CorePlatform::IsOSPlayingMusic() -> bool { return false; }
void CorePlatform::AndroidShowAppInvite(const std::string& title,
const std::string& message,
const std::string& code) {
Log(LogLevel::kError, "AndroidShowAppInvite() unimplemented");
}
void CorePlatform::IncrementAnalyticsCount(const std::string& name,
int increment) {}

View File

@ -217,11 +217,6 @@ class CorePlatform {
virtual auto GetAndroidExecArg() -> std::string;
virtual void AndroidSetResString(const std::string& res);
virtual void AndroidSynthesizeBackPress();
virtual void AndroidQuitActivity();
virtual void AndroidShowAppInvite(const std::string& title,
const std::string& message,
const std::string& code);
virtual auto AndroidGetExternalFilesDir() -> std::string;
#pragma mark PERMISSIONS -------------------------------------------------------

View File

@ -2,3 +2,8 @@
Gameplay code for classic BombSquad, as well as app-modes and other support
classes required to use it.
Note that, ideally, gameplay code (scene version) should live in its own feature
set and app-mode code should live in another. That way a single app-mode can
wrangle multiple scene versions. But for historical reasons they're all mixed up
in this case.

View File

@ -71,7 +71,9 @@ auto ImageNode::InitType() -> NodeType* {
ImageNode::ImageNode(Scene* scene) : Node(scene, node_type) {}
ImageNode::~ImageNode() {
if (fill_screen_) scene()->decrement_bg_cover_count();
if (fill_screen_) {
scene()->decrement_bg_cover_count();
}
}
auto ImageNode::GetAttach() const -> std::string {
@ -209,11 +211,11 @@ void ImageNode::Draw(base::FrameDef* frame_def) {
if (host_only_ && !context_ref().GetHostSession()) {
return;
}
bool vr = (g_core->IsVRMode());
bool vr = g_core->IsVRMode();
// In vr mode we use the fixed overlay position if our scene
// is set for that.
bool vr_use_fixed = (scene()->use_fixed_vr_overlay());
bool vr_use_fixed = scene()->use_fixed_vr_overlay();
// Currently front and vr-fixed are mutually-exclusive.. need to fix.
if (front_) {

View File

@ -39,11 +39,10 @@ const int kKickVoteFailRetryDelay = 60000;
/// Extra delay for the initiator of a failed vote.
const int kKickVoteFailRetryDelayInitiatorExtra = 120000;
// Minimum clients that must be present for a kick vote to count.
// (for non-headless builds we require more votes since the host doesn't count
// but may be playing (in a 2on2 with 3 clients, don't want 2 clients able to
// kick).
// NOLINTNEXTLINE(cert-err58-cpp)
// Minimum clients that must be present for a kick vote to count. (for
// non-headless builds we require more votes since the host doesn't count
// but may be playing (in a 2on2 with 3 clients, don't want 2 clients able
// to kick).
const int kKickVoteMinimumClients = (g_buildconfig.headless_build() ? 3 : 4);
struct SceneV1AppMode::ScanResultsEntryPriv {
@ -68,7 +67,7 @@ base::InputDeviceDelegate* SceneV1AppMode::CreateInputDeviceDelegate(
// Go with 5 minute ban.
const int kKickBanSeconds = 5 * 60;
bool SceneV1AppMode::InMainMenu() const {
bool SceneV1AppMode::InClassicMainMenuSession() const {
HostSession* hostsession =
ContextRefSceneV1::FromAppForegroundContext().GetHostSession();
return (hostsession && hostsession->is_main_menu());

View File

@ -150,15 +150,15 @@ class SceneV1AppMode : public base::AppMode {
void OnAppStart() override;
void OnAppPause() override;
void OnAppResume() override;
auto InMainMenu() const -> bool override;
auto InClassicMainMenuSession() const -> bool override;
auto CreateInputDeviceDelegate(base::InputDevice* device)
-> base::InputDeviceDelegate* override;
void SetInternalMusic(base::SoundAsset* music, float volume = 1.0,
bool loop = true);
// Run a cycle of host scanning (basically sending out a broadcast packet to
// see who's out there).
// Run a cycle of host scanning (basically sending out a broadcast packet
// to see who's out there).
void HostScanCycle();
void EndHostScanning();

View File

@ -8,7 +8,7 @@
namespace ballistica::scene_v1 {
/// Wraps a weak-ref to a context_ref with functionality specific to scene_v1.
/// A context-ref specific to SceneV1.
class ContextRefSceneV1 : public base::ContextRef {
public:
ContextRefSceneV1() : ContextRef() {}
@ -23,8 +23,8 @@ class ContextRefSceneV1 : public base::ContextRef {
static auto FromAppForegroundContext() -> ContextRefSceneV1;
// If the current Context is (or is part of) a HostSession, return it;
// otherwise return nullptr. be aware that this will return a session if the
// context is *either* a host-activity or a host-session
// otherwise return nullptr. be aware that this will return a session if
// the context is *either* a host-activity or a host-session
auto GetHostSession() const -> HostSession*;
// Return the current context as an HostActivity if it is one; otherwise
@ -32,15 +32,14 @@ class ContextRefSceneV1 : public base::ContextRef {
auto GetHostActivity() const -> HostActivity*;
// If the current context contains a scene that can be manipulated by
// standard commands, this returns it. This includes host-sessions,
// standard commands, this returns it. This includes host-sessions,
// host-activities, and the UI context.
auto GetMutableScene() const -> Scene*;
};
/// Object containing some sort of context_ref.
/// App-modes can subclass this to provide the actual context_ref they desire,
/// and then code can use GetTyped() to safely retrieve context_ref as that
/// type.
/// Object containing some sort of context_ref. App-modes can subclass this
/// to provide the actual context_ref they desire, and then code can use
/// GetTyped() to safely retrieve context_ref as that type.
class SceneV1Context : public base::Context {
public:
static auto Current() -> SceneV1Context& {

View File

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

View File

@ -2472,45 +2472,6 @@ static PyMethodDef PyShowAd2Def = {
"(internal)",
};
// --------------------------- show_app_invite ---------------------------------
static auto PyShowAppInvite(PyObject* self, PyObject* args, PyObject* keywds)
-> PyObject* {
BA_PYTHON_TRY;
std::string title;
std::string message;
std::string code;
PyObject* title_obj;
PyObject* message_obj;
PyObject* code_obj;
static const char* kwlist[] = {"title", "message", "code", nullptr};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "OOO",
const_cast<char**>(kwlist), &title_obj,
&message_obj, &code_obj)) {
return nullptr;
}
title = g_base->python->GetPyLString(title_obj);
message = g_base->python->GetPyLString(message_obj);
code = g_base->python->GetPyLString(code_obj);
g_core->platform->AndroidShowAppInvite(title, message, code);
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyShowAppInviteDef = {
"show_app_invite", // name
(PyCFunction)PyShowAppInvite, // method
METH_VARARGS | METH_KEYWORDS, // flags
"show_app_invite(title: str | bauiv1.Lstr,\n"
" message: str | bauiv1.Lstr,\n"
" code: str) -> None\n"
"\n"
"(internal)\n"
"\n"
"Category: **General Utility Functions**",
};
// --------------------- set_party_icon_always_visible -------------------------
static auto PySetPartyIconAlwaysVisible(PyObject* self, PyObject* args,
@ -2882,7 +2843,6 @@ auto PythonMethodsUIV1::GetMethods() -> std::vector<PyMethodDef> {
PyGetSpecialWidgetDef,
PySetPartyWindowOpenDef,
PySetPartyIconAlwaysVisibleDef,
PyShowAppInviteDef,
PyShowAdDef,
PyShowAd2Def,
PyShowOnlineScoreUIDef,

View File

@ -223,7 +223,7 @@ void RootUI::Draw(base::FrameDef* frame_def) {
// Flash and show a message if we're in the main menu instructing the
// player to start a game.
bool flash = false;
bool in_main_menu = g_base->app_mode()->InMainMenu();
bool in_main_menu = g_base->app_mode()->InClassicMainMenuSession();
if (in_main_menu && party_size > 0 && show_client_joined) flash = true;
@ -255,8 +255,8 @@ void RootUI::Draw(base::FrameDef* frame_def) {
c.PopTransform();
c.Submit();
// Based on who has menu control, we may show a key/button below the party
// icon.
// Based on who has menu control, we may show a key/button below the
// party icon.
if (!active) {
if (base::InputDevice* uiid = g_base->ui->GetUIInputDevice()) {
std::string party_button_name = uiid->GetPartyButtonName();
@ -304,8 +304,8 @@ void RootUI::Draw(base::FrameDef* frame_def) {
party_size_text_group_->set_text(
std::to_string(party_size_text_group_num_));
// ..we also may want to update our 'someone joined' message if we're
// host
// ..we also may want to update our 'someone joined' message if
// we're host
if (is_host) {
if (!start_a_game_text_group_.Exists()) {
start_a_game_text_group_ = Object::New<base::TextGroup>();

View File

@ -134,9 +134,8 @@ void UIV1FeatureSet::Draw(base::FrameDef* frame_def) {
auto* root_widget = root_widget_.Get();
if (root_widget && root_widget->HasChildren()) {
// Draw our opaque and transparent parts separately.
// This way we can draw front-to-back for opaque and back-to-front for
// transparent.
// Draw our opaque and transparent parts separately. This way we can
// draw front-to-back for opaque and back-to-front for transparent.
g_base->graphics->set_drawing_opaque_only(true);
@ -281,4 +280,22 @@ void UIV1FeatureSet::DoApplyAppConfig() {
base::AppConfig::BoolID::kAlwaysUseInternalKeyboard));
}
UIV1FeatureSet::UILock::UILock(bool write) {
assert(g_base->ui);
assert(g_base->InLogicThread());
if (write && g_ui_v1->ui_lock_count_ != 0) {
BA_LOG_ERROR_TRACE_ONCE("Illegal operation: UI is locked");
}
g_ui_v1->ui_lock_count_++;
}
UIV1FeatureSet::UILock::~UILock() {
g_ui_v1->ui_lock_count_--;
if (g_ui_v1->ui_lock_count_ < 0) {
BA_LOG_ERROR_TRACE_ONCE("ui_lock_count_ < 0");
g_ui_v1->ui_lock_count_ = 0;
}
}
} // namespace ballistica::ui_v1

View File

@ -15,6 +15,19 @@
// BA 2.0 UI testing.
#define BA_UI_V1_TOOLBAR_TEST 0
// UI-Locks: make sure widget-lists don't change under you. Use a read-lock
// if you just need to ensure lists remain intact but won't be changing
// anything. Use a write-lock whenever modifying a list.
#if BA_DEBUG_BUILD
#define BA_DEBUG_UI_READ_LOCK \
::ballistica::ui_v1::UIV1FeatureSet::UILock ui_lock(false)
#define BA_DEBUG_UI_WRITE_LOCK \
::ballistica::ui_v1::UIV1FeatureSet::UILock ui_lock(true)
#else
#define BA_DEBUG_UI_READ_LOCK
#define BA_DEBUG_UI_WRITE_LOCK
#endif
// Predeclared types from other feature sets that we use.
namespace ballistica::core {
class CoreFeatureSet;
@ -57,6 +70,17 @@ class UIV1FeatureSet : public FeatureSetNativeComponent,
/// it. Basically a Python import statement.
static auto Import() -> UIV1FeatureSet*;
/// Used to ensure widgets are not created or destroyed at certain times
/// (while traversing widget hierarchy, etc).
class UILock {
public:
explicit UILock(bool write);
~UILock();
private:
BA_DISALLOW_CLASS_COPIES(UILock);
};
/// Called when our associated Python module is instantiated.
static void OnModuleExec(PyObject* module);
void DoHandleDeviceMenuPress(base::InputDevice* device) override;
@ -113,6 +137,7 @@ class UIV1FeatureSet : public FeatureSetNativeComponent,
Object::Ref<ContainerWidget> screen_root_widget_;
Object::Ref<ContainerWidget> overlay_root_widget_;
Object::Ref<RootWidget> root_widget_;
int ui_lock_count_{};
};
} // namespace ballistica::ui_v1

View File

@ -98,9 +98,9 @@ struct RootWidget::Text {
};
RootWidget::RootWidget() {
// we enable a special 'single-depth-root' mode
// in which we use most of our depth range for our first child
// (our screen stack) and the small remaining bit for the rest
// We enable a special 'single-depth-root' mode in which we use most of
// our depth range for our first child (our screen stack) and the small
// remaining bit for the rest.
set_single_depth(true);
set_single_depth_root(true);
set_background(false);
@ -110,7 +110,7 @@ RootWidget::~RootWidget() = default;
auto RootWidget::AddCover(float h_align, VAlign v_align, float x, float y,
float w, float h, float o) -> RootWidget::Button* {
// currently just not doing these in vr mode
// Currently just not doing these in vr mode.
if (g_core->IsVRMode()) {
return nullptr;
}
@ -132,9 +132,9 @@ auto RootWidget::AddCover(float h_align, VAlign v_align, float x, float y,
bd.visibility_mask =
static_cast<uint32_t>(Widget::ToolbarVisibility::kMenuFullRoot);
// when the user specifies no backing it means they intend to cover the screen
// with a flat-ish window texture.. however this only applies to phone-size;
// for other sizes we always draw a backing.
// When the user specifies no backing it means they intend to cover the
// screen with a flat-ish window texture.. however this only applies to
// phone-size; for other sizes we always draw a backing.
if (g_base->ui->scale() != UIScale::kSmall) {
bd.visibility_mask |=
static_cast<uint32_t>(Widget::ToolbarVisibility::kMenuFull);
@ -352,7 +352,7 @@ void RootWidget::Setup() {
}
}
// widen this a bit in small mode so it just covers most of the top
// Widen this a bit in small mode so it just covers most of the top
// - that looks funny in medium/large mode though
// if (g_ui->scale() == UIScale::kSmall) {
// AddCover(0.5f, VAlign::kTop, 0.0f, 320.0f,
@ -838,8 +838,8 @@ auto RootWidget::AddButton(const ButtonDef& def) -> RootWidget::Button* {
} else {
b.widget->set_up_widget(screen_stack_widget_);
}
// We wanna prevent anyone from redirecting these to point to outside widgets
// since we'll probably outlive those outside widgets.
// We wanna prevent anyone from redirecting these to point to outside
// widgets since we'll probably outlive those outside widgets.
b.widget->set_neighbors_locked(true);
if (!def.img.empty()) {
@ -894,7 +894,7 @@ void RootWidget::UpdateForFocusedWindow() {
void RootWidget::UpdateForFocusedWindow(Widget* widget) {
// Take note if the current session is the main menu; we do a few things
// differently there.
in_main_menu_ = g_base->app_mode()->InMainMenu();
in_main_menu_ = g_base->app_mode()->InClassicMainMenuSession();
if (widget == nullptr) {
toolbar_visibility_ = ToolbarVisibility::kInGame;
@ -929,8 +929,8 @@ void RootWidget::StepPositions(float dt) {
static_cast<bool>(static_cast<uint32_t>(toolbar_visibility_)
& static_cast<uint32_t>(b.visibility_mask));
// When we're in the main menu, always disable the menu button
// and shift the party button a bit to the right
// When we're in the main menu, always disable the menu button and shift
// the party button a bit to the right
if (in_main_menu_) {
if (&b == menu_button_) {
enable_button = false;
@ -940,13 +940,13 @@ void RootWidget::StepPositions(float dt) {
}
}
if (&b == back_button_) {
// back button is always disabled in medium/large UI
// Back button is always disabled in medium/large UI.
if (g_base->ui->scale() != UIScale::kSmall) {
enable_button = false;
}
// whenever back button is enabled, left on account button should go to
// it; otherwise it goes nowhere.
// Whenever back button is enabled, left on account button should go
// to it; otherwise it goes nowhere.
Widget* ab = account_button_->widget.Get();
ab->set_neighbors_locked(false);
ab->set_left_widget(enable_button ? back_button_->widget.Get() : ab);
@ -957,9 +957,10 @@ void RootWidget::StepPositions(float dt) {
b.y_target += disable_offset;
}
// special case: we shift buttons on the top right to the right if the menu
// button is hidden (and also if the button is hidden; otherwise things come
// in diagonally)
// special case: we shift buttons on the top right to the right if the
// menu button is hidden (and also if the button is hidden; otherwise
// things come in diagonally)
//
// if (b.h_align == HAlign::kRight and b.v_align == VAlign::kTop
// if (b.h_align >= 1.0f and b.v_align == VAlign::kTop
// and (toolbar_visibility_ != ToolbarVisibility::kInGame or not
@ -971,15 +972,15 @@ void RootWidget::StepPositions(float dt) {
b.x_smoothed += (b.x_target - b.x_smoothed) * 0.015f * dt;
b.y_smoothed += (b.y_target - b.y_smoothed) * 0.015f * dt;
// Snap in place once we reach the target; otherwise note
// that we need to keep going.
// Snap in place once we reach the target; otherwise note that we need
// to keep going.
if (std::abs(b.x_target - b.x_smoothed) < 0.1f
&& std::abs(b.y_target - b.y_smoothed) < 0.1f) {
b.x_smoothed = b.x_target;
b.y_smoothed = b.y_target;
// Also flip off visibility if we're moving offscreen and have reached our
// target.
// Also flip off visibility if we're moving offscreen and have reached
// our target.
if (!enable_button) {
b.widget->set_visible_in_container(false);
}
@ -989,7 +990,8 @@ void RootWidget::StepPositions(float dt) {
b.widget->set_visible_in_container(true);
}
// Now calc final abs x and y based on screen size, smoothed positions, etc.
// Now calc final abs x and y based on screen size, smoothed positions,
// etc.
float x, y;
x = width() * b.h_align
+ base_scale_ * (b.x_smoothed - b.width * b.scale * 0.5f);

View File

@ -531,9 +531,9 @@ auto TextWidget::ShouldUseStringEditDialog() const -> bool {
return true;
}
// On most platforms we always want to do this.
// on mac/pc, however, we use inline editing if the current UI input-device
// is the mouse or keyboard
// On most platforms we always want to do this. On desktop, however, we
// use inline editing if the current UI input-device is the mouse or
// keyboard.
if (g_buildconfig.ostype_macos() || g_buildconfig.ostype_windows()
|| g_buildconfig.ostype_linux()) {
base::InputDevice* ui_input_device = g_base->ui->GetUIInputDevice();

View File

@ -15,7 +15,7 @@ if TYPE_CHECKING:
def generate_app_module(
feature_sets: dict[str, FeatureSet], existing_data: str
projroot: str, feature_sets: dict[str, FeatureSet], existing_data: str
) -> str:
"""Generate babase._app.py based on its existing version."""
@ -24,7 +24,7 @@ def generate_app_module(
# pylint: disable=too-many-statements
import textwrap
from efrotools import replace_section
from efrotools import replace_section, getprojectconfig
out = ''
@ -156,31 +156,50 @@ def generate_app_module(
# Generate default app-mode-selection logic.
# TODO - make this customizable via project settings or whatnot.
default_app_modes: list[str] | None = getprojectconfig(projroot).get(
'default_app_modes'
)
if not isinstance(default_app_modes, list) or not all(
isinstance(x, str) for x in default_app_modes
):
raise RuntimeError(
'Could not load default_app_modes from projectconfig'
)
def _module_for_app_mode(amode: str) -> str:
return '.'.join(amode.split('.')[:-1])
def _is_valid_app_mode(amode: str) -> bool:
# Consider the app mode valid if it comes from a Python
# package provided by one of our feature-sets.
module = _module_for_app_mode(amode)
for featureset in feature_sets.values():
if featureset.name_python_package == module:
return True
return False
default_app_modes = [m for m in default_app_modes if _is_valid_app_mode(m)]
contents = (
'# Ask our default app modes to handle it.\n'
"# (based on 'default_app_modes' in projectconfig).\n"
"# (generated from 'default_app_modes' in projectconfig).\n"
)
imports: list[str] = []
if 'scene_v1' in fsets:
imports.append('bascenev1')
if 'base' in fsets:
imports.append('babase')
for imp in imports:
contents += f'import {imp}\n'
if not default_app_modes:
raise RuntimeError('No valid default_app_modes specified.')
for mode in default_app_modes:
contents += f'import {_module_for_app_mode(mode)}\n'
contents += '\n'
if 'scene_v1' in fsets:
contents += (
'if bascenev1.SceneV1AppMode.can_handle_intent(intent):\n'
' return bascenev1.SceneV1AppMode\n\n'
)
if 'base' in fsets:
contents += (
'if babase.EmptyAppMode.can_handle_intent(intent):\n'
' return babase.EmptyAppMode\n\n'
)
contents += 'for appmode in [\n'
for mode in default_app_modes:
contents += f' {mode},\n'
contents += (
']:\n'
' if appmode.can_handle_intent(intent):\n'
' return appmode\n'
'\n'
)
contents += 'return None\n'
indent = ' '

View File

@ -699,7 +699,7 @@ class ProjectUpdater:
from batools.appmodule import generate_app_module
self._generated_files[path] = generate_app_module(
self.feature_sets, existing_data
self.projroot, self.feature_sets, existing_data
)
def _update_meta_makefile(self) -> None:

View File

@ -28,6 +28,10 @@ PYVER = '3.11'
# Update; just using the same executable used to launch us.
PYTHON_BIN = sys.executable
# Cache these since we may repeatedly fetch these in batch mode.
_g_project_configs: dict[str, dict[str, Any]] = {}
_g_local_configs: dict[str, dict[str, Any]] = {}
def explicit_bool(value: bool) -> bool:
"""Simply return input value; can avoid unreachable-code type warnings."""
@ -36,36 +40,45 @@ def explicit_bool(value: bool) -> bool:
def getlocalconfig(projroot: Path | str) -> dict[str, Any]:
"""Return a project's localconfig contents (or default if missing)."""
localconfig: dict[str, Any]
projrootstr = str(projroot)
if projrootstr not in _g_local_configs:
localconfig: dict[str, Any]
# Allow overriding path via env var.
path = os.environ.get('EFRO_LOCALCONFIG_PATH')
if path is None:
path = 'config/localconfig.json'
# Allow overriding path via env var.
path = os.environ.get('EFRO_LOCALCONFIG_PATH')
if path is None:
path = 'config/localconfig.json'
try:
with open(Path(projroot, path), encoding='utf-8') as infile:
localconfig = json.loads(infile.read())
except FileNotFoundError:
localconfig = {}
return localconfig
try:
with open(Path(projroot, path), encoding='utf-8') as infile:
localconfig = json.loads(infile.read())
except FileNotFoundError:
localconfig = {}
_g_local_configs[projrootstr] = localconfig
return _g_local_configs[projrootstr]
def getprojectconfig(projroot: Path | str) -> dict[str, Any]:
"""Return a project's projectconfig contents (or default if missing)."""
config: dict[str, Any]
try:
with open(
Path(projroot, 'config/projectconfig.json'), encoding='utf-8'
) as infile:
config = json.loads(infile.read())
except FileNotFoundError:
config = {}
return config
projrootstr = str(projroot)
if projrootstr not in _g_project_configs:
config: dict[str, Any]
try:
with open(
Path(projroot, 'config/projectconfig.json'), encoding='utf-8'
) as infile:
config = json.loads(infile.read())
except FileNotFoundError:
config = {}
_g_project_configs[projrootstr] = config
return _g_project_configs[projrootstr]
def setprojectconfig(projroot: Path | str, config: dict[str, Any]) -> None:
"""Set the project config contents."""
projrootstr = str(projroot)
_g_project_configs[projrootstr] = config
os.makedirs(Path(projroot, 'config'), exist_ok=True)
with Path(projroot, 'config/projectconfig.json').open(
'w', encoding='utf-8'