diff --git a/.efrocachemap b/.efrocachemap index 72b8f52d..ad1d8695 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -432,7 +432,7 @@ "build/assets/ba_data/audio/zoeOw.ogg": "b2d705c31c9dcc1efdc71394764c3beb", "build/assets/ba_data/audio/zoePickup01.ogg": "e9366dc2d2b8ab8b0c4e2c14c02d0789", "build/assets/ba_data/audio/zoeScream01.ogg": "903e0e45ee9b3373e9d9ce20c814374e", - "build/assets/ba_data/data/langdata.json": "cd059a1e20ac384f763c6386038837d8", + "build/assets/ba_data/data/langdata.json": "bf0ed9fa002ad530d64ba801b617a527", "build/assets/ba_data/data/languages/arabic.json": "32b9849fb8389b8c7798f0b744620318", "build/assets/ba_data/data/languages/belarussian.json": "009b452aa308bf2b2f7e92d9b78ba5ff", "build/assets/ba_data/data/languages/chinese.json": "5363a79f843e6be7ef47a840f47cc17d", @@ -441,17 +441,17 @@ "build/assets/ba_data/data/languages/czech.json": "3418bee44e69be13b7f72996abe96921", "build/assets/ba_data/data/languages/danish.json": "8e57db30c5250df2abff14a822f83ea7", "build/assets/ba_data/data/languages/dutch.json": "4ba5bbcc0fecddd0aac6ee2c165d1e40", - "build/assets/ba_data/data/languages/english.json": "5506f0629e42604eb62c92bed4551d3b", + "build/assets/ba_data/data/languages/english.json": "856ba013c0680d69038988df7f04caa1", "build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880", "build/assets/ba_data/data/languages/filipino.json": "1894fc331dcad7ce9cf4c180843f548f", "build/assets/ba_data/data/languages/french.json": "6d20655730b1017ef187fd828b91d43c", "build/assets/ba_data/data/languages/german.json": "bc656f1ada467161c23546f48d0dacc5", - "build/assets/ba_data/data/languages/gibberish.json": "85b93e21e4969e2e3eceb26f1f66237c", + "build/assets/ba_data/data/languages/gibberish.json": "e6c107495d2788f8a2f876986ca1af35", "build/assets/ba_data/data/languages/greek.json": "d28d1092fbb00ed857cbd53124c0dc78", "build/assets/ba_data/data/languages/hindi.json": "567e6976b3c72f891431ad7fcc62ab16", "build/assets/ba_data/data/languages/hungarian.json": "af801baffb2c06460635dfb04c34bb3e", "build/assets/ba_data/data/languages/indonesian.json": "607ba358179185f032096ea1978e4448", - "build/assets/ba_data/data/languages/italian.json": "254d4d3962fda17fe127636fa6221851", + "build/assets/ba_data/data/languages/italian.json": "8ed7e5b3a277ed1576af100fc944ef7e", "build/assets/ba_data/data/languages/korean.json": "360760d72832863e1a3451b0a514cb08", "build/assets/ba_data/data/languages/malay.json": "0212e18e54efa202c17505376e5b82fb", "build/assets/ba_data/data/languages/persian.json": "517217e679c768fff4ffec7f8000ab77", @@ -462,7 +462,7 @@ "build/assets/ba_data/data/languages/russian.json": "70cd57440e4cbb49f2fa0c79dedcafbe", "build/assets/ba_data/data/languages/serbian.json": "623fa4129a1154c2f32ed7867e56ff6a", "build/assets/ba_data/data/languages/slovak.json": "c11c29708b3742cdc2a92b4fa0d6d29f", - "build/assets/ba_data/data/languages/spanish.json": "8c319f6e50275fb0a22d5f1bd4b7f1e0", + "build/assets/ba_data/data/languages/spanish.json": "5bc22761e8a37cbdf38d4bced753b6ba", "build/assets/ba_data/data/languages/swedish.json": "3b179e7333183c70adb0811246b09959", "build/assets/ba_data/data/languages/tamil.json": "ead39b864228696a9b0d19344bc4b5ec", "build/assets/ba_data/data/languages/thai.json": "383540a1e9c7c131ac579f51afc87471", @@ -4174,46 +4174,46 @@ "build/assets/windows/Win32/ucrtbased.dll": "bfd1180c269d3950b76f35a63655e9e1", "build/assets/windows/Win32/vc_redist.x86.exe": "15a5f1f876503885adbdf5b3989b3718", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", - "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "490075b1f0f409dac071e27dc3a5396a", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "60efb54d98253d7d97021efc3dbcbd59", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "9c6eb8e03faf95e6902adb8bfde24a8b", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "bd670699053a1e0d474ee345f4edd656", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "3741d31a0f0dd0422a85f04e0ff0fa3f", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "32c46e38a916c8176322a910efcbcb3b", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "fd8708e164382c48edfbf9c7544a6e38", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "bc05c6a771e4defb28cdb7ca0561a689", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "99ec5b6f6cf2d0f925cb06ce7f8ab805", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "f4f245a47dc0279051dadd007959b689", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "28245a834bb409b6f11e90458c1624e2", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "1aa5230ba881011366f1d9fb9a895d3d", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "1e72ce51c2c2d2f1f2550a52ac3fc344", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "ecc96fe59adc13ea67e1d2e50b253b7a", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "aa0a5767a0eea6d4723ff54cc43cd81c", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "44091a64667a2c06f548a29805890321", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "7497894d2943b75b6ac830cdba8481af", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "273a6f64cac5a233694223b85cd0f22b", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "7497894d2943b75b6ac830cdba8481af", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "273a6f64cac5a233694223b85cd0f22b", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "6f4d0f923e186f013e20d894ce3cf5f0", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "f9866ec5b496b5565cad36eebb695507", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "6f4d0f923e186f013e20d894ce3cf5f0", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "f9866ec5b496b5565cad36eebb695507", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "55a2b0c2d1db4d70627c901471d05fc7", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "7e071962108a8d1727525fb331b70ee4", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "55a2b0c2d1db4d70627c901471d05fc7", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "7e071962108a8d1727525fb331b70ee4", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "ed2d35d12c3abb5113b4725c25076532", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "bfa9e7ac4d012d9d3556fd1401055a14", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "a4dd8c160b5cde72d93b7627950ea755", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "5a730a358d15ddb2f18cd58505941409", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "5db366eeb98b8366ef8f1878f22c0e95", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "b368c2712eb5b1296cac5a35b2ef910e", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "37677230e1b27a733c90552eaa227691", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "efffad67c4056754c29d88010c239175", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "677778a5f7114170c50602335fc3ddf1", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "8887729af3137c9709ee10b70f353786", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "2f9c170609a70d52d3bca2a1fcfe31d6", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "97220018e9d2fb42421d3fb2d7d98d47", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "22e38fabd263a4e639c69c901408e56d", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "9ce287d7714226daa851880f79c38d05", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "a53442bb694a34052bc5de9cc8a27f59", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "69b314b2d8528d226ef1b40f5b02e711", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "251b40efe83b7dfa917be97f86c73f48", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "008d0196de947e99620005d158880336", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "f0f93e610bb29895fd523899269add39", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "36a30c52a024f465d9c40f5fc42323e1", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "d7ebdc77b3ebd9e4f8a03bce5937202c", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "acb0ab12e1883cd54a6384a8ef6aacb5", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "fe4557d5dc57a3bb3281db2ab745fa5b", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "9c2829fc79d58705520f8d73d411afb4", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "4d70703c4eba1a402fbc2d997d84aa93", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "bb64e2182b1a533679f93c5997f14584", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "4d70703c4eba1a402fbc2d997d84aa93", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "bb64e2182b1a533679f93c5997f14584", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "1d25993b7cd4676d3884781b002edf53", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ab6f04d0fb73d88ccbd3c65e73ead01f", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "1d25993b7cd4676d3884781b002edf53", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ab6f04d0fb73d88ccbd3c65e73ead01f", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "e3e9d9c7a94f8cd22939839fd8fbc178", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "116b500c2d57caf06619408b2176e83b", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "e3e9d9c7a94f8cd22939839fd8fbc178", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "116b500c2d57caf06619408b2176e83b", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "654d386245b885f38515f9f9bf7eb501", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "b06edc326b9d47403f2aae52ff26f86c", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "8c6ec7bf96e5139b6252b2a36d63f504", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "4c9c1cc618d134b7da3391562195c034", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "f6debb77214ad953cdb673353cbfd546", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "ff57a7ec1f61492e3af985c49641d4b2", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "c06d91baebd97c6182f0d8ac9c09eacc", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "03a045a22e9eb41e9d6cd6f029aa99b6", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "794d258d59fd17a61752843a9a0551ad", "src/ballistica/base/mgen/pyembed/binding_base.inc": "06042d31df0ff9af96b99477162e2a91", - "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "2d228e7c5578261d394f9c407f4becb1", + "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "152bd413bf5823d163aa6ca931971f10", "src/ballistica/classic/mgen/pyembed/binding_classic.inc": "8ab156122cde23d9718923abe1b4ae5b", "src/ballistica/core/mgen/pyembed/binding_core.inc": "217c84a30f866aaca3a4373e82af7db2", "src/ballistica/core/mgen/pyembed/env.inc": "f015d726b44d2922112fc14d9f146d8b", diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d00522..398b2a0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.37 (build 22222, api 9, 2025-01-20) +### 1.7.37 (build 22226, api 9, 2025-01-21) - Bumping api version to 9. As you'll see below, there's some UI changes that will require a bit of work for any UI mods to adapt to. If your mods don't touch UI stuff at all you can simply bump your api version and call it a day. @@ -191,6 +191,12 @@ in various places. - Tournament now award chests instead of tickets. - Tournaments are now free to enter if you are running this build or newer. +- (build 22225) Added `babase.get_virtual_screen_size()` to get the current + virtual screen size, and added a `refresh_on_screen_size_changes` arg to the + `MainWindow` class. This can be used to fit windows to the exact screen size, + which is especially useful at the small ui-scale. Generally medium and large + ui-scale windows don't fill the entire screen and can simply stay within the + virtual safe area and don't need to refresh. ### 1.7.36 (build 21944, api 8, 2024-07-26) - Wired up Tokens, BombSquad's new purchasable currency. The first thing these diff --git a/config/requirements.txt b/config/requirements.txt index 5ea4a2a6..a1487add 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -1,6 +1,6 @@ cpplint==2.0.0 dmgbuild==1.6.4 -filelock==3.16.1 +filelock==3.17.0 furo==2024.8.6 mypy==1.14.1 pbxproj==4.2.1 diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py index b2d08ac4..6aa4d02f 100644 --- a/src/assets/ba_data/python/babase/_app.py +++ b/src/assets/ba_data/python/babase/_app.py @@ -604,8 +604,8 @@ class App: def set_ui_scale(self, scale: babase.UIScale) -> None: """Change ui-scale on the fly. - Currently this is mainly for debugging and will not - be called as part of normal app operation. + Currently this is mainly for debugging and will not be called as + part of normal app operation. """ assert _babase.in_logic_thread() @@ -618,10 +618,25 @@ class App: assert self._subsystem_registration_ended for subsystem in self._subsystems: try: - subsystem.on_screen_change() + subsystem.on_ui_scale_change() except Exception: logging.exception( - 'Error in on_screen_change() for subsystem %s.', subsystem + 'Error in on_ui_scale_change() for subsystem %s.', subsystem + ) + + def on_screen_size_change(self) -> None: + """Screen size has changed.""" + + # Inform all app subsystems in the same order they were inited. + # Operate on a copy of the list here because this can be called + # while subsystems are still being added. + for subsystem in self._subsystems.copy(): + try: + subsystem.on_screen_size_change() + except Exception: + logging.exception( + 'Error in on_screen_size_change() for subsystem %s.', + subsystem, ) def _set_intent(self, intent: AppIntent) -> None: diff --git a/src/assets/ba_data/python/babase/_appsubsystem.py b/src/assets/ba_data/python/babase/_appsubsystem.py index c22f40a8..8f086ff0 100644 --- a/src/assets/ba_data/python/babase/_appsubsystem.py +++ b/src/assets/ba_data/python/babase/_appsubsystem.py @@ -54,8 +54,17 @@ class AppSubsystem: def do_apply_app_config(self) -> None: """Called when the app config should be applied.""" - def on_screen_change(self) -> None: - """Called when screen dimensions or ui-scale changes.""" + def on_ui_scale_change(self) -> None: + """Called when screen ui-scale changes. + + Will not be called for the initial ui scale. + """ + + def on_screen_size_change(self) -> None: + """Called when the screen size changes. + + Will not be called for the initial screen size. + """ def reset(self) -> None: """Reset the subsystem to a default state. diff --git a/src/assets/ba_data/python/babase/_devconsoletabs.py b/src/assets/ba_data/python/babase/_devconsoletabs.py index 1ea6fba2..9e60b0a6 100644 --- a/src/assets/ba_data/python/babase/_devconsoletabs.py +++ b/src/assets/ba_data/python/babase/_devconsoletabs.py @@ -123,24 +123,19 @@ class DevConsoleTabUI(DevConsoleTab): xoffs = -375 self.text( - 'Make sure all interactive UI fits in the' - ' virtual bounds at all UI-scales (not counting things' - ' that follow screen edges).\n' - 'Note that some elements may not reflect UI-scale changes' - ' until recreated.', + 'Make sure all UIs either fit in the virtual safe area' + ' or dynamically respond to screen size changes.', scale=0.6, pos=(xoffs + 15, 70), - # h_anchor='left', h_align='left', v_align='center', ) ui_overlay = _babase.get_draw_ui_bounds() self.button( - 'Virtual Bounds ON' if ui_overlay else 'Virtual Bounds OFF', + 'Virtual Safe Area ON' if ui_overlay else 'Virtual Safe Area OFF', pos=(xoffs + 10, 10), size=(200, 30), - # h_anchor='left', label_scale=0.6, call=self.toggle_ui_overlay, style='bright' if ui_overlay else 'normal', @@ -149,7 +144,6 @@ class DevConsoleTabUI(DevConsoleTab): self.text( 'UI-Scale', pos=(xoffs + x - 5, 15), - # h_anchor='left', h_align='right', v_align='none', scale=0.6, @@ -161,7 +155,6 @@ class DevConsoleTabUI(DevConsoleTab): scale.name.capitalize(), pos=(xoffs + x, 10), size=(bwidth, 30), - # h_anchor='left', label_scale=0.6, call=partial(_babase.app.set_ui_scale, scale), style=( diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index aff17745..fe7cefbb 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -53,7 +53,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 22222 +TARGET_BALLISTICA_BUILD = 22226 TARGET_BALLISTICA_VERSION = '1.7.37' diff --git a/src/assets/ba_data/python/bauiv1/_appsubsystem.py b/src/assets/ba_data/python/bauiv1/_appsubsystem.py index 5e0aae08..8e7e743d 100644 --- a/src/assets/ba_data/python/bauiv1/_appsubsystem.py +++ b/src/assets/ba_data/python/bauiv1/_appsubsystem.py @@ -4,6 +4,7 @@ from __future__ import annotations +import time import logging import inspect import weakref @@ -85,6 +86,10 @@ class UIV1AppSubsystem(babase.AppSubsystem): self.heading_color = (0.72, 0.7, 0.75) self.infotextcolor = (0.7, 0.9, 0.7) + self._last_win_recreate_size: tuple[float, float] | None = None + self._last_screen_size_win_recreate_time: float | None = None + self._screen_size_win_recreate_timer: babase.AppTimer | None = None + # Elements in our root UI will call anything here when # activated. self.root_ui_calls: dict[ @@ -394,12 +399,12 @@ class UIV1AppSubsystem(babase.AppSubsystem): ) @override - def on_screen_change(self) -> None: + def on_ui_scale_change(self) -> None: # Update our stored UIScale. self._update_ui_scale() # Update native bits (allow root widget to rebuild itself/etc.) - _bauiv1.on_screen_change() + _bauiv1.on_ui_scale_change() # Lastly, if we have a main window, recreate it to pick up the # new UIScale/etc. @@ -408,3 +413,57 @@ class UIV1AppSubsystem(babase.AppSubsystem): winstate = self.save_main_window_state(mainwindow) self.clear_main_window(transition='instant') self.restore_main_window_state(winstate) + + # Store the size we created this for to avoid redundant + # future recreates. + self._last_win_recreate_size = babase.get_virtual_screen_size() + + @override + def on_screen_size_change(self) -> None: + + # Recreating a MainWindow is a kinda heavy thing and it doesn't + # seem like we should be doing it at 120hz during a live window + # resize, so let's limit the max rate we do it. + now = time.monotonic() + + # 4 refreshes per second seems reasonable. + interval = 0.25 + + # If there is a timer set already, do nothing. + if self._screen_size_win_recreate_timer is not None: + return + + # Ok; there's no timer. Schedule one. + till_update = ( + 0.0 + if self._last_screen_size_win_recreate_time is None + else max( + 0.0, self._last_screen_size_win_recreate_time + interval - now + ) + ) + self._screen_size_win_recreate_timer = babase.AppTimer( + till_update, self._do_screen_size_win_recreate + ) + + def _do_screen_size_win_recreate(self) -> None: + self._last_screen_size_win_recreate_time = time.monotonic() + self._screen_size_win_recreate_timer = None + + # Avoid recreating if we're already at this size. This prevents + # a redundant recreate when ui scale changes. + virtual_screen_size = babase.get_virtual_screen_size() + if virtual_screen_size == self._last_win_recreate_size: + return + + mainwindow = self.get_main_window() + if ( + mainwindow is not None + and mainwindow.refreshes_on_screen_size_changes + ): + winstate = self.save_main_window_state(mainwindow) + self.clear_main_window(transition='instant') + self.restore_main_window_state(winstate) + + # Store the size we created this for to avoid redundant + # future recreates. + self._last_win_recreate_size = virtual_screen_size diff --git a/src/assets/ba_data/python/bauiv1/_uitypes.py b/src/assets/ba_data/python/bauiv1/_uitypes.py index e8e8e6c0..3dd8d897 100644 --- a/src/assets/ba_data/python/bauiv1/_uitypes.py +++ b/src/assets/ba_data/python/bauiv1/_uitypes.py @@ -58,9 +58,11 @@ class MainWindow(Window): def __init__( self, root_widget: bauiv1.Widget, + *, transition: str | None, origin_widget: bauiv1.Widget | None, cleanupcheck: bool = True, + refresh_on_screen_size_changes: bool = False, ): """Create a MainWindow given a root widget and transition info. @@ -72,6 +74,12 @@ class MainWindow(Window): self.main_window_is_top_level: bool = False + # Windows that size tailor themselves to exact screen dimensions + # can pass True for this. Generally this only applies to small + # ui scale and at larger scales windows simply fit in the + # virtual safe area. + self.refreshes_on_screen_size_changes = refresh_on_screen_size_changes + # Windows can be flagged as auxiliary when not related to the # main UI task at hand. UI code may choose to handle auxiliary # windows in special ways, such as by implicitly replacing diff --git a/src/assets/ba_data/python/bauiv1lib/achievements.py b/src/assets/ba_data/python/bauiv1lib/achievements.py index a4b9ed19..1e239650 100644 --- a/src/assets/ba_data/python/bauiv1lib/achievements.py +++ b/src/assets/ba_data/python/bauiv1lib/achievements.py @@ -36,11 +36,8 @@ class AchievementsWindow(bui.MainWindow): ) # Do some fancy math to fill all available screen area up to the - # size of our backing container. - # - # TODO: We need an auto-refresh mechanism for cases where screen - # size changes under us. Currently one must navigate out and - # back in to properly reflect such changes. + # size of our backing container. This lets us fit to the exact + # screen shape at small ui scale. screensize = bui.get_virtual_screen_size() scale = ( 2.3 @@ -72,6 +69,8 @@ class AchievementsWindow(bui.MainWindow): ), transition=transition, origin_widget=origin_widget, + # We're affected by screen size only at small ui-scale. + refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, ) if uiscale is bui.UIScale.SMALL: diff --git a/src/assets/ba_data/python/bauiv1lib/inbox.py b/src/assets/ba_data/python/bauiv1lib/inbox.py index 1ca72d60..29247a7d 100644 --- a/src/assets/ba_data/python/bauiv1lib/inbox.py +++ b/src/assets/ba_data/python/bauiv1lib/inbox.py @@ -327,11 +327,8 @@ class InboxWindow(bui.MainWindow): ) # Do some fancy math to fill all available screen area up to the - # size of our backing container. - # - # TODO: We need an auto-refresh mechanism for cases where screen - # size changes under us. Currently one must navigate out and - # back in to properly reflect such changes. + # size of our backing container. This lets us fit to the exact + # screen shape at small ui scale. screensize = bui.get_virtual_screen_size() scale = ( 1.74 @@ -361,6 +358,8 @@ class InboxWindow(bui.MainWindow): ), transition=transition, origin_widget=origin_widget, + # We're affected by screen size only at small ui-scale. + refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, ) if uiscale is bui.UIScale.SMALL: diff --git a/src/assets/ba_data/python/bauiv1lib/playlist/browser.py b/src/assets/ba_data/python/bauiv1lib/playlist/browser.py index 12b9da79..6bd66fc0 100644 --- a/src/assets/ba_data/python/bauiv1lib/playlist/browser.py +++ b/src/assets/ba_data/python/bauiv1lib/playlist/browser.py @@ -65,11 +65,8 @@ class PlaylistBrowserWindow(bui.MainWindow): ) # Do some fancy math to fill all available screen area up to the - # size of our backing container. - # - # TODO: We need an auto-refresh mechanism for cases where screen - # size changes under us. Currently one must navigate out and - # back in to properly reflect such changes. + # size of our backing container. This lets us fit to the exact + # screen shape at small ui scale. screensize = bui.get_virtual_screen_size() scale = ( 1.85 @@ -104,6 +101,8 @@ class PlaylistBrowserWindow(bui.MainWindow): ), transition=transition, origin_widget=origin_widget, + # We're affected by screen size only at small ui-scale. + refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, ) self._back_button: bui.Widget | None diff --git a/src/assets/ba_data/python/bauiv1lib/settings/advanced.py b/src/assets/ba_data/python/bauiv1lib/settings/advanced.py index f9b01431..e7eb2b07 100644 --- a/src/assets/ba_data/python/bauiv1lib/settings/advanced.py +++ b/src/assets/ba_data/python/bauiv1lib/settings/advanced.py @@ -50,11 +50,8 @@ class AdvancedSettingsWindow(bui.MainWindow): self._menu_open = False # Do some fancy math to fill all available screen area up to the - # size of our backing container. - # - # TODO: We need an auto-refresh mechanism for cases where screen - # size changes under us. Currently one must navigate out and - # back in to properly reflect such changes. + # size of our backing container. This lets us fit to the exact + # screen shape at small ui scale. screensize = bui.get_virtual_screen_size() scale = ( 2.04 @@ -87,6 +84,8 @@ class AdvancedSettingsWindow(bui.MainWindow): ), transition=transition, origin_widget=origin_widget, + # We're affected by screen size only at small ui-scale. + refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, ) self._prev_lang = '' diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc index 049810e8..160fff56 100644 --- a/src/ballistica/base/base.cc +++ b/src/ballistica/base/base.cc @@ -507,13 +507,13 @@ auto BaseFeatureSet::HavePlus() -> bool { return plus_soft_ != nullptr; } -void BaseFeatureSet::set_plus(PlusSoftInterface* plus) { +void BaseFeatureSet::SetPlus(PlusSoftInterface* plus) { assert(plus_soft_ == nullptr); plus_soft_ = plus; } /// Access the plus feature-set. Will throw an exception if not present. -auto BaseFeatureSet::plus() -> PlusSoftInterface* { +auto BaseFeatureSet::Plus() -> PlusSoftInterface* { if (!plus_soft_ && !tried_importing_plus_) { python->SoftImportPlus(); // Important to set this *after* import attempt, or a second import @@ -615,7 +615,7 @@ auto BaseFeatureSet::FeatureSetFromData(PyObject* obj) auto BaseFeatureSet::IsUnmodifiedBlessedBuild() -> bool { // If we've got plus present, ask them. Otherwise assume no. if (HavePlus()) { - return plus()->IsUnmodifiedBlessedBuild(); + return Plus()->IsUnmodifiedBlessedBuild(); } return false; } @@ -731,7 +731,7 @@ void BaseFeatureSet::DoV1CloudLog(const std::string& msg) { if (g_core == nullptr) { logsuffix = msg; } - plus()->DirectSendV1CloudLogs(logprefix, logsuffix, false, nullptr); + Plus()->DirectSendV1CloudLogs(logprefix, logsuffix, false, nullptr); } void BaseFeatureSet::PushDevConsolePrintCall(const std::string& msg, diff --git a/src/ballistica/base/base.h b/src/ballistica/base/base.h index e22d9717..af8d771d 100644 --- a/src/ballistica/base/base.h +++ b/src/ballistica/base/base.h @@ -686,9 +686,9 @@ class BaseFeatureSet : public FeatureSetNativeComponent, auto HavePlus() -> bool; /// Access the plus feature-set. Will throw an exception if not present. - auto plus() -> PlusSoftInterface*; + auto Plus() -> PlusSoftInterface*; - void set_plus(PlusSoftInterface* plus); + void SetPlus(PlusSoftInterface* plus); /// Try to load the classic feature-set and return whether it is available. auto HaveClassic() -> bool; diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index 7b4ddc3f..7e79f450 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -70,7 +70,7 @@ void Logic::OnAppStart() { g_base->ui->OnAppStart(); g_base->app_mode()->OnAppStart(); if (g_base->HavePlus()) { - g_base->plus()->OnAppStart(); + g_base->Plus()->OnAppStart(); } g_base->python->OnAppStart(); @@ -191,7 +191,7 @@ void Logic::OnAppSuspend() { // Note: keep these in opposite order of OnAppStart. g_base->python->OnAppSuspend(); if (g_base->HavePlus()) { - g_base->plus()->OnAppSuspend(); + g_base->Plus()->OnAppSuspend(); } g_base->app_mode()->OnAppSuspend(); g_base->ui->OnAppSuspend(); @@ -215,7 +215,7 @@ void Logic::OnAppUnsuspend() { g_base->ui->OnAppUnsuspend(); g_base->app_mode()->OnAppUnsuspend(); if (g_base->HavePlus()) { - g_base->plus()->OnAppUnsuspend(); + g_base->Plus()->OnAppUnsuspend(); } g_base->python->OnAppUnsuspend(); } @@ -247,7 +247,7 @@ void Logic::OnAppShutdown() { // should be registered as shutdown-tasks g_base->python->OnAppShutdown(); if (g_base->HavePlus()) { - g_base->plus()->OnAppShutdown(); + g_base->Plus()->OnAppShutdown(); } g_base->app_mode()->OnAppShutdown(); g_base->ui->OnAppShutdown(); @@ -280,7 +280,7 @@ void Logic::OnAppShutdownComplete() { // should be registered as shutdown-tasks. g_base->python->OnAppShutdownComplete(); if (g_base->HavePlus()) { - g_base->plus()->OnAppShutdownComplete(); + g_base->Plus()->OnAppShutdownComplete(); } g_base->app_mode()->OnAppShutdownComplete(); g_base->ui->OnAppShutdownComplete(); @@ -307,7 +307,7 @@ void Logic::DoApplyAppConfig() { g_base->ui->DoApplyAppConfig(); g_base->app_mode()->DoApplyAppConfig(); if (g_base->HavePlus()) { - g_base->plus()->DoApplyAppConfig(); + g_base->Plus()->DoApplyAppConfig(); } g_base->python->DoApplyAppConfig(); @@ -323,6 +323,7 @@ void Logic::OnScreenSizeChange(float virtual_width, float virtual_height, assert(g_base->InLogicThread()); // Inform all subsystems. + // // Note: keep these in the same order as OnAppStart. g_base->app_adapter->OnScreenSizeChange(); g_base->platform->OnScreenSizeChange(); @@ -333,7 +334,7 @@ void Logic::OnScreenSizeChange(float virtual_width, float virtual_height, g_core->platform->OnScreenSizeChange(); g_base->app_mode()->OnScreenSizeChange(); if (g_base->HavePlus()) { - g_base->plus()->OnScreenSizeChange(); + g_base->Plus()->OnScreenSizeChange(); } g_base->python->OnScreenSizeChange(); } @@ -363,7 +364,7 @@ void Logic::StepDisplayTime_() { g_core->platform->StepDisplayTime(); g_base->app_mode()->StepDisplayTime(); if (g_base->HavePlus()) { - g_base->plus()->StepDisplayTime(); + g_base->Plus()->StepDisplayTime(); } g_base->python->StepDisplayTime(); diff --git a/src/ballistica/base/python/base_python.cc b/src/ballistica/base/python/base_python.cc index fd1252b6..92cefbf0 100644 --- a/src/ballistica/base/python/base_python.cc +++ b/src/ballistica/base/python/base_python.cc @@ -155,7 +155,35 @@ void BasePython::OnAppShutdownComplete() { void BasePython::DoApplyAppConfig() { assert(g_base->InLogicThread()); } -void BasePython::OnScreenSizeChange() { assert(g_base->InLogicThread()); } +void BasePython::OnScreenSizeChange() { + assert(g_base->InLogicThread()); + + float screen_res_x{g_base->graphics->screen_virtual_width()}; + float screen_res_y{g_base->graphics->screen_virtual_height()}; + + // This call runs for all screen sizes including the initial one. However + // we only want to inform the Python layer of *changes*, so we only store + // the initial one and don't pass it on. + if (last_screen_res_x_ < 0.0) { + last_screen_res_x_ = screen_res_x; + last_screen_res_y_ = g_base->graphics->screen_virtual_height(); + return; + } + + // Ignore any redundant values that might come through. + if (last_screen_res_x_ == screen_res_x + && last_screen_res_y_ == screen_res_y) { + return; + } + + // Aight; we got a fresh, non-initial value. Store it and inform Python. + last_screen_res_x_ = screen_res_x; + last_screen_res_y_ = screen_res_y; + + g_base->python->objs() + .Get(BasePython::ObjID::kAppOnScreenSizeChangeCall) + .Call(); +} void BasePython::StepDisplayTime() { assert(g_base->InLogicThread()); } diff --git a/src/ballistica/base/python/base_python.h b/src/ballistica/base/python/base_python.h index cbcd252e..3e80f4c0 100644 --- a/src/ballistica/base/python/base_python.h +++ b/src/ballistica/base/python/base_python.h @@ -118,6 +118,7 @@ class BasePython { kGetV2AccountIdCall, kAppOnNativeActiveChangedCall, kCopyDevConsoleHistoryCall, + kAppOnScreenSizeChangeCall, kLast // Sentinel; must be at end. }; @@ -183,6 +184,8 @@ class BasePython { private: std::set do_once_locations_; PythonObjectSet objs_; + float last_screen_res_x_{-1.0f}; + float last_screen_res_y_{-1.0f}; }; } // namespace ballistica::base diff --git a/src/ballistica/classic/support/classic_app_mode.cc b/src/ballistica/classic/support/classic_app_mode.cc index 8c50e5ee..34771a11 100644 --- a/src/ballistica/classic/support/classic_app_mode.cc +++ b/src/ballistica/classic/support/classic_app_mode.cc @@ -1307,7 +1307,7 @@ void ClassicAppMode::SetPublicPartyEnabled(bool val) { return; } public_party_enabled_ = val; - g_base->plus()->PushPublicPartyState(); + g_base->Plus()->PushPublicPartyState(); } void ClassicAppMode::SetPublicPartySize(int count) { @@ -1320,7 +1320,7 @@ void ClassicAppMode::SetPublicPartySize(int count) { // Push our new state to the server *ONLY* if public-party is turned on // (wasteful otherwise). if (public_party_enabled_) { - g_base->plus()->PushPublicPartyState(); + g_base->Plus()->PushPublicPartyState(); } } @@ -1334,7 +1334,7 @@ void ClassicAppMode::SetPublicPartyQueueEnabled(bool enabled) { // Push our new state to the server *ONLY* if public-party is turned on // (wasteful otherwise). if (public_party_enabled_) { - g_base->plus()->PushPublicPartyState(); + g_base->Plus()->PushPublicPartyState(); } } @@ -1348,7 +1348,7 @@ void ClassicAppMode::SetPublicPartyMaxSize(int count) { // Push our new state to the server *ONLY* if public-party is turned on // (wasteful otherwise). if (public_party_enabled_) { - g_base->plus()->PushPublicPartyState(); + g_base->Plus()->PushPublicPartyState(); } } @@ -1362,7 +1362,7 @@ void ClassicAppMode::SetPublicPartyName(const std::string& name) { // Push our new state to the server *ONLY* if public-party is turned on // (wasteful otherwise). if (public_party_enabled_) { - g_base->plus()->PushPublicPartyState(); + g_base->Plus()->PushPublicPartyState(); } } @@ -1376,7 +1376,7 @@ void ClassicAppMode::SetPublicPartyStatsURL(const std::string& url) { // Push our new state to the server *ONLY* if public-party is turned on // (wasteful otherwise). if (public_party_enabled_) { - g_base->plus()->PushPublicPartyState(); + g_base->Plus()->PushPublicPartyState(); } } @@ -1390,7 +1390,7 @@ void ClassicAppMode::SetPublicPartyPlayerCount(int count) { // Push our new state to the server *ONLY* if public-party is turned on // (wasteful otherwise). if (public_party_enabled_) { - g_base->plus()->PushPublicPartyState(); + g_base->Plus()->PushPublicPartyState(); } } diff --git a/src/ballistica/classic/support/v1_account.cc b/src/ballistica/classic/support/v1_account.cc index 5da691fc..1d0f2065 100644 --- a/src/ballistica/classic/support/v1_account.cc +++ b/src/ballistica/classic/support/v1_account.cc @@ -195,7 +195,7 @@ void V1Account::SetLogin(V1AccountType account_type, V1LoginState login_state, } if (call_login_did_change) { // Inform a few subsystems of the change. - g_base->plus()->V1LoginDidChange(); + g_base->Plus()->V1LoginDidChange(); g_core->platform->V1LoginDidChange(); } } diff --git a/src/ballistica/scene_v1/connection/connection_to_client.cc b/src/ballistica/scene_v1/connection/connection_to_client.cc index 85dfda96..a5b957a3 100644 --- a/src/ballistica/scene_v1/connection/connection_to_client.cc +++ b/src/ballistica/scene_v1/connection/connection_to_client.cc @@ -444,7 +444,7 @@ void ConnectionToClient::HandleMessagePacket( if (!token_.empty()) { // Kick off a query to the master-server for this client's info. // FIXME: we need to add retries for this in case of failure. - g_base->plus()->ClientInfoQuery( + g_base->Plus()->ClientInfoQuery( token_, our_handshake_player_spec_str_ + our_handshake_salt_, peer_hash_, build_number_); } diff --git a/src/ballistica/scene_v1/connection/connection_to_host.cc b/src/ballistica/scene_v1/connection/connection_to_host.cc index 462e981d..33e0f39d 100644 --- a/src/ballistica/scene_v1/connection/connection_to_host.cc +++ b/src/ballistica/scene_v1/connection/connection_to_host.cc @@ -202,7 +202,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { set_peer_spec(PlayerSpec(string_buffer.data())); } - peer_hash_ = g_base->plus()->CalcV1PeerHash(peer_hash_input_); + peer_hash_ = g_base->Plus()->CalcV1PeerHash(peer_hash_input_); set_can_communicate(true); appmode->LaunchClientSession(); @@ -227,7 +227,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { JsonDict dict; dict.AddNumber("b", kEngineBuildNumber); - g_base->plus()->V1SetClientInfo(&dict); + g_base->Plus()->V1SetClientInfo(&dict); // Pass the hash we generated from their handshake; they can use // this to make sure we're who we say we are. diff --git a/src/ballistica/scene_v1/python/methods/python_methods_scene.cc b/src/ballistica/scene_v1/python/methods/python_methods_scene.cc index ebac53d2..24003660 100644 --- a/src/ballistica/scene_v1/python/methods/python_methods_scene.cc +++ b/src/ballistica/scene_v1/python/methods/python_methods_scene.cc @@ -1367,7 +1367,7 @@ static auto PyGetGameRoster(PyObject* self, PyObject* args, PyObject* keywds) // Let's also include a public account-id if we have one. std::string account_id; if (clientid == -1) { - account_id = g_base->plus()->GetPublicV1AccountID(); + account_id = g_base->Plus()->GetPublicV1AccountID(); } else { if (auto* appmode = classic::ClassicAppMode::GetActiveOrWarn()) { auto client2 = diff --git a/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc b/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc index 3d16d34f..92e96ec0 100644 --- a/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc +++ b/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc @@ -244,10 +244,10 @@ void SceneV1InputDeviceDelegate::InvalidateConnectionToHost() { auto SceneV1InputDeviceDelegate::GetPublicV1AccountID() const -> std::string { assert(g_base->InLogicThread()); - // This default implementation assumes the device is local - // so just returns the locally signed in account's public id. + // This default implementation assumes the device is local so just returns + // the locally signed in account's public id. - return g_base->plus()->GetPublicV1AccountID(); + return g_base->Plus()->GetPublicV1AccountID(); } auto SceneV1InputDeviceDelegate::GetPlayerProfiles() const -> PyObject* { diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index d5ae676e..0151dea8 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -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 = 22222; +const int kEngineBuildNumber = 22226; const char* kEngineVersion = "1.7.37"; const int kEngineApiVersion = 9; diff --git a/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc b/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc index fcb99046..c83f3f49 100644 --- a/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc +++ b/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc @@ -2752,23 +2752,23 @@ static PyMethodDef PyIsAvailableDef = { "(internal)", }; -// --------------------------- on_screen_change -------------------------------- +// --------------------------- on_ui_scale_change ------------------------------ -static auto PyOnScreenChange(PyObject* self) -> PyObject* { +static auto PyOnUIScaleChange(PyObject* self) -> PyObject* { BA_PYTHON_TRY; BA_PRECONDITION(g_base->InLogicThread()); - g_ui_v1->OnScreenChange(); + g_ui_v1->OnUIScaleChange(); Py_RETURN_NONE; BA_PYTHON_CATCH; } -static PyMethodDef PyOnScreenChangeDef = { - "on_screen_change", // name - (PyCFunction)PyOnScreenChange, // method - METH_NOARGS, // flags +static PyMethodDef PyOnUIScaleChangeDef = { + "on_ui_scale_change", // name + (PyCFunction)PyOnUIScaleChange, // method + METH_NOARGS, // flags - "on_screen_change() -> None\n" + "on_ui_scale_change() -> None\n" "\n" "(internal)", }; @@ -2844,7 +2844,7 @@ auto PythonMethodsUIV1::GetMethods() -> std::vector { PyGetQRCodeTextureDef, PyGetMeshDef, PyIsAvailableDef, - PyOnScreenChangeDef, + PyOnUIScaleChangeDef, PyRootUIPauseUpdatesDef, PyRootUIResumeUpdatesDef}; } diff --git a/src/ballistica/ui_v1/ui_v1.cc b/src/ballistica/ui_v1/ui_v1.cc index c1a61545..c37bdb19 100644 --- a/src/ballistica/ui_v1/ui_v1.cc +++ b/src/ballistica/ui_v1/ui_v1.cc @@ -223,7 +223,7 @@ void UIV1FeatureSet::OnScreenSizeChange() { } } -void UIV1FeatureSet::OnScreenChange() { +void UIV1FeatureSet::OnUIScaleChange() { // This gets called by the Python layer when UIScale or window size // changes. assert(g_base->InLogicThread()); diff --git a/src/ballistica/ui_v1/ui_v1.h b/src/ballistica/ui_v1/ui_v1.h index 73d7b354..5b3319d6 100644 --- a/src/ballistica/ui_v1/ui_v1.h +++ b/src/ballistica/ui_v1/ui_v1.h @@ -121,7 +121,7 @@ class UIV1FeatureSet : public FeatureSetNativeComponent, void DeleteWidget(Widget* widget); void OnScreenSizeChange() override; - void OnScreenChange(); + void OnUIScaleChange(); void OnLanguageChange() override; auto GetRootWidget() -> ui_v1::Widget* override; diff --git a/src/meta/babasemeta/pyembed/binding_base_app.py b/src/meta/babasemeta/pyembed/binding_base_app.py index a29fafdf..008b9a27 100644 --- a/src/meta/babasemeta/pyembed/binding_base_app.py +++ b/src/meta/babasemeta/pyembed/binding_base_app.py @@ -20,4 +20,5 @@ values = [ app.on_native_shutdown_complete, # kAppOnNativeShutdownCompleteCall app.on_native_active_changed, # kAppOnNativeActiveChangedCall app.devconsole.do_refresh_tab, # kAppDevConsoleDoRefreshTabCall + app.on_screen_size_change, # kAppOnScreenSizeChangeCall ]