diff --git a/.efrocachemap b/.efrocachemap index 2622af6f..972ee88e 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4072,26 +4072,26 @@ "build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b", "build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877", "build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde", - "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/58/ee/1ae7013ce43f284e1037e4d6d919", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/ab/c7/667fbfbddf1633be04f3216e0643", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/26/27/837e81d734fd600e258c02b9dccf", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/6a/b5/0f025731ba6077489aeb2fd177d0", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/27/38/e8add26cb151502314c1b63f17a9", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/85/49/10aa68593cab2a1e8157d08f01b7", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/86/3a/497b582b67614ee0dbca984ec355", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/32/a6/dbd29464c7b8a0939db43f50f05f", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/7a/39/e06e47cbe35b9191a8d01d634690", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/dc/03/292c24cc1576db602d5abef25021", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/7f/d1/ac412a5e2c13c1ff65ba745fe71a", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/b4/ad/78a018863d2aa19c2c75bbc0507a", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/d4/b4/6e407209943bfcbe595bb646d0ec", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/33/56/7ba169a200619cbd84c7f0ce2d05", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d2/02/4e009574cce072d6a20a2ab29a70", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/af/71/1979797270010b09d88fcb4cbf38", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/bd/ed/1c0eccb11e438a2b1e9106464187", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/81/0c/1f834b3e710f788748bdeb0f7e21", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/43/c1/fb11c149f9a8b34cdaad76eec2c3", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/30/f5/3964913ed145b96629055623e1f1", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/4f/46/80f41faa8a51075c2472d7429b77", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/30/cb/93ac10daaf9475d01f5d1064d358", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/34/4e/e2bf216edcd7d6a9a1bee3b55b69", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/5e/2b/c22b17ffa90f65bc906ae9e477d6", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/cf/ac/1485911ed48dac0c430b18687917", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/0d/99/2b9a15fa962b5ea354f8de27fe6c", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/03/e1/e795c72de227173711da1a162632", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/80/26/b82964ce5a5bb8509ca6d7516b71", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/2e/c8/67482dedb186c9dbca2dc66f7699", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/18/fa/c42a603705d7453279689706d2ee", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/bc/d2/89526b59dee86888c628028a406c", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/1d/e5/7425978a2c0112a46192a76563ce", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/ff/cd/21d36a06bbc2322d8e944f392ef0", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/97/ee/e2c3b4a6ee9abb170bd98f38c7de", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/be/13/229e4c027d0ca4b037dbf67134bd", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/53/bb/acf5f4b58a156c53e6ff8f0b58ad", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/f3/d7/426c5cd3ec71438a98474f39ba2c", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/90/79/74d6111711d655963a1e9a2728c5", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/c1/f7/95ff199637a86fe138309e3a5275", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/61/f0/9447fe7637ed96ce1840aa277271", "build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/d4/6a/dd303a200b98a56ba3b100277057", "build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/fc/2c/2996c558fb408a548fdd37398c9a", "build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/ed/28/b7a72be7ae1bd2b58dda4b6902a0", diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b085f6..9d93a0e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.20 (build 21040, api 8, 2023-06-02) +### 1.7.20 (build 21041, api 8, 2023-06-02) - This seems like a good time for a `refactoring` release in anticipation of changes coming in 1.8. Basically this means that a lot of things will be diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index 09c08d6d..dcee55c0 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21040 +TARGET_BALLISTICA_BUILD = 21041 TARGET_BALLISTICA_VERSION = '1.7.20' _g_env_config: EnvConfig | None = None diff --git a/src/ballistica/base/graphics/graphics.cc b/src/ballistica/base/graphics/graphics.cc index ccbda395..a4dfac85 100644 --- a/src/ballistica/base/graphics/graphics.cc +++ b/src/ballistica/base/graphics/graphics.cc @@ -1180,7 +1180,7 @@ void Graphics::BuildAndPushFrameDef() { // drawing/blitting the 2d UI buffer during gameplay for efficiency). if (g_core->IsVRMode()) { if (frame_def->GetOverlayFlatPass()->HasDrawCommands()) { - if (!g_base->ui->IsWindowPresent()) { + if (!g_base->ui->MainMenuVisible()) { BA_LOG_ONCE(LogLevel::kError, "Drawing in overlay pass in VR mode with no UI present; " "shouldn't happen!"); diff --git a/src/ballistica/base/input/device/touch_input.cc b/src/ballistica/base/input/device/touch_input.cc index 341749ed..033b471d 100644 --- a/src/ballistica/base/input/device/touch_input.cc +++ b/src/ballistica/base/input/device/touch_input.cc @@ -350,7 +350,7 @@ void TouchInput::UpdateDPad() { void TouchInput::Draw(FrameDef* frame_def) { assert(g_base->InLogicThread()); - bool active = (!g_base->ui->IsWindowPresent()); + bool active = (!g_base->ui->MainMenuVisible()); millisecs_t real_time = frame_def->real_time(); // Update our action center whenever possible in case screen is resized. @@ -915,7 +915,7 @@ auto TouchInput::HandleTouchDown(void* touch, float x, float y) -> bool { // Normal in-game operation: // Normal operation is disabled while a UI is up. - if (g_base->ui->IsWindowPresent()) { + if (g_base->ui->MainMenuVisible()) { return false; } @@ -1038,7 +1038,7 @@ auto TouchInput::HandleTouchMoved(void* touch, float x, float y) -> bool { } // Ignore button/pad touches while gui is up. - if (g_base->ui->IsWindowPresent()) { + if (g_base->ui->MainMenuVisible()) { return false; } if (touch == buttons_touch_) { diff --git a/src/ballistica/base/input/input.cc b/src/ballistica/base/input/input.cc index 69d2eaf5..b7b11f48 100644 --- a/src/ballistica/base/input/input.cc +++ b/src/ballistica/base/input/input.cc @@ -16,7 +16,6 @@ #include "ballistica/base/ui/ui.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/generic/utils.h" -#include "ballistica/ui_v1/widget/root_widget.h" namespace ballistica::base { @@ -1038,7 +1037,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { g_base->ui->PushMainMenuPressCall(keyboard_input_); } else { // Ok there *is* a main menu up. Send it a cancel message. - g_base->ui->root_widget()->HandleMessage( + g_base->ui->SendWidgetMessage( WidgetMessage(WidgetMessage::Type::kCancel)); } handled = true; @@ -1144,16 +1143,15 @@ void Input::HandleMouseScroll(const Vector2f& amount) { } mark_input_active(); - ui_v1::Widget* root_widget = g_base->ui->root_widget(); - if (std::abs(amount.y) > 0.0001f && root_widget) { - root_widget->HandleMessage(WidgetMessage(WidgetMessage::Type::kMouseWheel, - nullptr, cursor_pos_x_, - cursor_pos_y_, amount.y)); + if (std::abs(amount.y) > 0.0001f) { + g_base->ui->SendWidgetMessage( + WidgetMessage(WidgetMessage::Type::kMouseWheel, nullptr, cursor_pos_x_, + cursor_pos_y_, amount.y)); } - if (std::abs(amount.x) > 0.0001f && root_widget) { - root_widget->HandleMessage(WidgetMessage(WidgetMessage::Type::kMouseWheelH, - nullptr, cursor_pos_x_, - cursor_pos_y_, amount.x)); + if (std::abs(amount.x) > 0.0001f) { + g_base->ui->SendWidgetMessage( + WidgetMessage(WidgetMessage::Type::kMouseWheelH, nullptr, cursor_pos_x_, + cursor_pos_y_, amount.x)); } mouse_move_count_++; @@ -1180,15 +1178,13 @@ void Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum) { mark_input_active(); bool handled = false; - ui_v1::Widget* root_widget = g_base->ui->root_widget(); - if (root_widget) { - handled = root_widget->HandleMessage( - WidgetMessage(WidgetMessage::Type::kMouseWheelVelocity, nullptr, - cursor_pos_x_, cursor_pos_y_, velocity.y, momentum)); - root_widget->HandleMessage( - WidgetMessage(WidgetMessage::Type::kMouseWheelVelocityH, nullptr, - cursor_pos_x_, cursor_pos_y_, velocity.x, momentum)); - } + handled = g_base->ui->SendWidgetMessage( + WidgetMessage(WidgetMessage::Type::kMouseWheelVelocity, nullptr, + cursor_pos_x_, cursor_pos_y_, velocity.y, momentum)); + g_base->ui->SendWidgetMessage( + WidgetMessage(WidgetMessage::Type::kMouseWheelVelocityH, nullptr, + cursor_pos_x_, cursor_pos_y_, velocity.x, momentum)); + last_mouse_move_time_ = g_core->GetAppTimeMillisecs(); mouse_move_count_++; @@ -1233,11 +1229,11 @@ void Input::HandleMouseMotion(const Vector2f& position) { } // UI interaction. - ui_v1::Widget* root_widget = g_base->ui->root_widget(); - if (root_widget && !IsInputLocked()) - handled = root_widget->HandleMessage( + if (!IsInputLocked()) { + handled = g_base->ui->SendWidgetMessage( WidgetMessage(WidgetMessage::Type::kMouseMove, nullptr, cursor_pos_x_, cursor_pos_y_)); + } // Manual camera motion. Camera* camera = g_base->graphics->camera(); @@ -1259,6 +1255,7 @@ void Input::PushMouseDownEvent(int button, const Vector2f& position) { } void Input::HandleMouseDown(int button, const Vector2f& position) { + assert(g_base); assert(g_base->graphics); assert(g_base->InLogicThread()); @@ -1266,7 +1263,7 @@ void Input::HandleMouseDown(int button, const Vector2f& position) { return; } - if (g_base == nullptr || g_base->ui->screen_root_widget() == nullptr) { + if (!g_base->ui->MainMenuVisible()) { return; } @@ -1286,7 +1283,7 @@ void Input::HandleMouseDown(int button, const Vector2f& position) { last_click_time_ = click_time; bool handled{}; - auto* root_widget = g_base->ui->root_widget(); + // 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 @@ -1302,8 +1299,8 @@ void Input::HandleMouseDown(int button, const Vector2f& position) { } } - if (root_widget && !handled) { - handled = root_widget->HandleMessage( + if (!handled) { + handled = g_base->ui->SendWidgetMessage( WidgetMessage(WidgetMessage::Type::kMouseDown, nullptr, cursor_pos_x_, cursor_pos_y_, double_click ? 2 : 1)); } @@ -1353,11 +1350,11 @@ 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 = root_widget->HandleMessage(WidgetMessage( - WidgetMessage::Type::kMouseUp, nullptr, cursor_pos_x_, 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) { @@ -1484,7 +1481,6 @@ auto Input::IsCursorVisible() const -> bool { if (!g_base->ui) { return false; } - ui_v1::ContainerWidget* screen_root_widget = g_base->ui->screen_root_widget(); // Keeps mouse hidden to start with.. if (mouse_move_count_ < 2) { @@ -1494,7 +1490,7 @@ auto Input::IsCursorVisible() const -> bool { // Show our cursor if any dialogs/windows are up or else if its been // moved very recently. - if (screen_root_widget && screen_root_widget->GetChildCount() > 0) { + if (g_base->ui->MainMenuVisible()) { val = (g_core->GetAppTimeMillisecs() - last_mouse_move_time_ < 5000); } else { val = (g_core->GetAppTimeMillisecs() - last_mouse_move_time_ < 1000); diff --git a/src/ballistica/base/python/methods/python_methods_app.cc b/src/ballistica/base/python/methods/python_methods_app.cc index 4b07d0a6..48585d0b 100644 --- a/src/ballistica/base/python/methods/python_methods_app.cc +++ b/src/ballistica/base/python/methods/python_methods_app.cc @@ -5,15 +5,15 @@ #include "ballistica/base/app/app.h" #include "ballistica/base/app/app_mode.h" #include "ballistica/base/graphics/graphics_server.h" +#include "ballistica/base/logic/logic.h" #include "ballistica/base/python/base_python.h" #include "ballistica/base/python/support/python_context_call_runnable.h" #include "ballistica/base/ui/ui.h" -#include "ballistica/scene_v1/assets/scene_texture.h" -#include "ballistica/scene_v1/python/class/python_class_activity_data.h" -#include "ballistica/scene_v1/support/scene.h" +#include "ballistica/core/platform/core_platform.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/foundation/logging.h" #include "ballistica/shared/python/python.h" +#include "ballistica/shared/python/python_sys.h" namespace ballistica::base { diff --git a/src/ballistica/base/support/ui_v1_soft.h b/src/ballistica/base/support/ui_v1_soft.h index ccf6d0df..0a0d8a53 100644 --- a/src/ballistica/base/support/ui_v1_soft.h +++ b/src/ballistica/base/support/ui_v1_soft.h @@ -3,10 +3,13 @@ #ifndef BALLISTICA_BASE_SUPPORT_UI_V1_SOFT_H_ #define BALLISTICA_BASE_SUPPORT_UI_V1_SOFT_H_ +#include "ballistica/base/ui/ui.h" + // Predeclare some types we use. namespace ballistica::ui_v1 { class RootUI; -} +class Widget; +} // namespace ballistica::ui_v1 namespace ballistica::base { @@ -26,6 +29,14 @@ class UIV1SoftInterface { virtual void HandleLegacyRootUIMouseMotion(float x, float y) = 0; virtual auto HandleLegacyRootUIMouseDown(float x, float y) -> bool = 0; virtual void HandleLegacyRootUIMouseUp(float x, float y) = 0; + virtual void Draw(FrameDef* frame_def) = 0; + virtual void OnAppStart() = 0; + virtual auto PartyWindowOpen() -> bool = 0; + virtual void Reset() = 0; + virtual void OnScreenSizeChange() = 0; + virtual void OnLanguageChange() = 0; + virtual auto GetRootWidget() -> ui_v1::Widget* = 0; + virtual auto SendWidgetMessage(const WidgetMessage& m) -> int = 0; }; } // namespace ballistica::base diff --git a/src/ballistica/base/ui/ui.cc b/src/ballistica/base/ui/ui.cc index 4983703a..6604d553 100644 --- a/src/ballistica/base/ui/ui.cc +++ b/src/ballistica/base/ui/ui.cc @@ -4,7 +4,6 @@ #include "ballistica/base/app/app_config.h" #include "ballistica/base/audio/audio.h" -#include "ballistica/base/graphics/component/empty_component.h" #include "ballistica/base/input/device/keyboard_input.h" #include "ballistica/base/input/input.h" #include "ballistica/base/logic/logic.h" @@ -12,8 +11,6 @@ #include "ballistica/base/ui/console.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/generic/utils.h" -#include "ballistica/ui_v1/python/ui_v1_python.h" -#include "ballistica/ui_v1/support/root_ui.h" #include "ballistica/ui_v1/widget/root_widget.h" #include "ballistica/ui_v1/widget/stack_widget.h" #include "ballistica/ui_v1/widget/text_widget.h" @@ -58,9 +55,11 @@ void UI::StepDisplayTime() { assert(g_base->InLogicThread()); } void UI::OnAppStart() { assert(g_base->InLogicThread()); - root_ui_ = g_base->ui_v1()->NewRootUI(); + if (g_base->HaveUIV1()) { + g_base->ui_v1()->OnAppStart(); + } - // Make sure we know when forced-ui-scale is enabled. + // Make sure user knows when forced-ui-scale is enabled. if (force_scale_) { if (scale_ == UIScale::kSmall) { ScreenMessage("FORCING SMALL UI FOR TESTING", Vector3f(1, 0, 0)); @@ -93,13 +92,20 @@ void UI::ApplyAppConfig() { AppConfig::BoolID::kAlwaysUseInternalKeyboard)); } -auto UI::MainMenuVisible() -> bool { +auto UI::MainMenuVisible() const -> bool { if (g_base->HaveUIV1()) { return g_base->ui_v1()->MainMenuVisible(); } return false; } +// FIXME should be same as MainMenuVisible. +// auto UI::IsWindowPresent() const -> bool { +// return ((screen_root_widget_.Exists() && screen_root_widget_->HasChildren()) +// || (overlay_root_widget_.Exists() +// && overlay_root_widget_->HasChildren())); +//} + auto UI::PartyIconVisible() -> bool { if (g_base->HaveUIV1()) { return g_base->ui_v1()->PartyIconVisible(); @@ -113,6 +119,13 @@ void UI::ActivatePartyIcon() { } } +auto UI::PartyWindowOpen() -> bool { + if (g_base->HaveUIV1()) { + g_base->ui_v1()->PartyWindowOpen(); + } + return false; +} + void UI::HandleLegacyRootUIMouseMotion(float x, float y) { if (g_base->HaveUIV1()) { g_base->ui_v1()->HandleLegacyRootUIMouseMotion(x, y); @@ -136,15 +149,10 @@ void UI::PushBackButtonCall(InputDevice* input_device) { g_base->logic->event_loop()->PushCall([this, input_device] { assert(g_base->InLogicThread()); - // Ignore if UI isn't up yet. - if (!overlay_root_widget() || !screen_root_widget()) { - return; - } - // If there's a UI up, send along a cancel message. - if (overlay_root_widget()->GetChildCount() != 0 - || screen_root_widget()->GetChildCount() != 0) { - root_widget()->HandleMessage(WidgetMessage(WidgetMessage::Type::kCancel)); + if (g_base->ui->MainMenuVisible()) { + g_base->ui->SendWidgetMessage( + WidgetMessage(WidgetMessage::Type::kCancel)); } else { // If there's no main screen or overlay windows, ask for a menu owned by // this device. @@ -168,12 +176,6 @@ void UI::MainMenuPress(InputDevice* device) { } } -auto UI::IsWindowPresent() const -> bool { - return ((screen_root_widget_.Exists() && screen_root_widget_->HasChildren()) - || (overlay_root_widget_.Exists() - && overlay_root_widget_->HasChildren())); -} - void UI::SetUIInputDevice(InputDevice* input_device) { assert(g_base->InLogicThread()); @@ -202,82 +204,27 @@ UI::UILock::~UILock() { } void UI::Reset() { - // Hmm; technically we don't need to recreate these each time we reset. - root_widget_.Clear(); - - // Kill our screen-root widget. - screen_root_widget_.Clear(); - - // (Re)create our screen-root widget. - auto sw(Object::New()); - sw->set_is_main_window_stack(true); - sw->SetWidth(g_base->graphics->screen_virtual_width()); - sw->SetHeight(g_base->graphics->screen_virtual_height()); - sw->set_translate(0, 0); - screen_root_widget_ = sw; - - // (Re)create our screen-overlay widget. - auto ow(Object::New()); - ow->set_is_overlay_window_stack(true); - ow->SetWidth(g_base->graphics->screen_virtual_width()); - ow->SetHeight(g_base->graphics->screen_virtual_height()); - ow->set_translate(0, 0); - overlay_root_widget_ = ow; - - // (Re)create our abs-root widget. - auto rw(Object::New()); - root_widget_ = rw; - rw->SetWidth(g_base->graphics->screen_virtual_width()); - rw->SetHeight(g_base->graphics->screen_virtual_height()); - rw->SetScreenWidget(sw.Get()); - rw->Setup(); - rw->SetOverlayWidget(ow.Get()); - - sw->GlobalSelect(); + if (g_base->HaveUIV1()) { + g_base->ui_v1()->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). - return ( - g_base->input->have_non_touch_inputs() - && ((screen_root_widget_.Exists() && screen_root_widget_->HasChildren()) - || (overlay_root_widget_.Exists() - && overlay_root_widget_->HasChildren()))); + return (g_base->input->have_non_touch_inputs() && MainMenuVisible()); } auto UI::ShouldShowButtonShortcuts() const -> bool { return g_base->input->have_non_touch_inputs(); } -void UI::AddWidget(ui_v1::Widget* w, ui_v1::ContainerWidget* parent) { - assert(g_base->InLogicThread()); - - BA_PRECONDITION(parent != nullptr); - - // If they're adding an initial window/dialog to our screen-stack - // or overlay stack, send a reset-local-input message so that characters - // who have lost focus will not get stuck running or whatnot. - // We should come up with a more generalized way to track this sort of - // focus as this is a bit hacky, but it works for now. - auto* screen_root_widget = screen_root_widget_.Get(); - auto* overlay_root_widget = overlay_root_widget_.Get(); - if ((screen_root_widget && !screen_root_widget->HasChildren() - && parent == screen_root_widget) - || (overlay_root_widget && !overlay_root_widget->HasChildren() - && parent == overlay_root_widget)) { - g_base->input->ResetHoldStates(); - } - - parent->AddWidget(w); -} - auto UI::SendWidgetMessage(const WidgetMessage& m) -> int { - if (!root_widget_.Exists()) { - return false; + if (g_base->HaveUIV1()) { + return g_base->ui_v1()->SendWidgetMessage(m); } - return root_widget_->HandleMessage(m); + return false; } void UI::DeleteWidget(ui_v1::Widget* widget) { @@ -291,16 +238,14 @@ void UI::DeleteWidget(ui_v1::Widget* widget) { } void UI::OnScreenSizeChange() { - if (root_widget_.Exists()) { - root_widget_->SetWidth(g_base->graphics->screen_virtual_width()); - root_widget_->SetHeight(g_base->graphics->screen_virtual_height()); + if (g_base->HaveUIV1()) { + g_base->ui_v1()->OnScreenSizeChange(); } } void UI::LanguageChanged() { - // As well as existing UI stuff. - if (ui_v1::Widget* root_widget = g_base->ui->root_widget()) { - root_widget->OnLanguageChange(); + if (g_base->HaveUIV1()) { + g_base->ui_v1()->OnLanguageChange(); } } @@ -315,9 +260,7 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* { // 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 ((!screen_root_widget_.Exists() || (!screen_root_widget_->HasChildren())) - && (!overlay_root_widget_.Exists() - || (!overlay_root_widget_->HasChildren()))) { + if (!MainMenuVisible()) { return nullptr; } @@ -332,15 +275,20 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* { // 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 ((GetUIInputDevice() == nullptr) || (input_device == GetUIInputDevice()) - || (time - last_input_device_use_time_ > (1000 * kUIOwnerTimeoutSeconds)) - || !g_base->input->HaveManyLocalActiveInputDevices()) { + if (!g_base->HaveUIV1()) { + ret_val = nullptr; + } else if ((GetUIInputDevice() == nullptr) + || (input_device == GetUIInputDevice()) + || (time - last_input_device_use_time_ + > (1000 * kUIOwnerTimeoutSeconds)) + || !g_base->input->HaveManyLocalActiveInputDevices()) { // Don't actually assign yet; only update times and owners if there's a // widget to be had (we don't want some guy who moved his character 3 // 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 = screen_root_widget_.Get(); + ret_val = g_base->ui_v1()->GetRootWidget(); } else { // For rejected input devices, play error sounds sometimes so they know // they're not the chosen one. @@ -397,55 +345,8 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* { } void UI::Draw(FrameDef* frame_def) { - RenderPass* overlay_flat_pass = frame_def->GetOverlayFlatPass(); - - // Draw interface elements. - 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. - - g_base->graphics->set_drawing_opaque_only(true); - - // Do a wee bit of shifting based on tilt just for fun. - Vector3f tilt = 0.1f * g_base->graphics->tilt(); - { - EmptyComponent c(overlay_flat_pass); - c.SetTransparent(false); - c.PushTransform(); - c.Translate(-tilt.y, tilt.x, -0.5f); - - // We want our widgets to cover 0.1f in z space. - c.Scale(1.0f, 1.0f, 0.1f); - c.Submit(); - root_widget->Draw(overlay_flat_pass, false); - c.PopTransform(); - c.Submit(); - } - - g_base->graphics->set_drawing_opaque_only(false); - g_base->graphics->set_drawing_transparent_only(true); - - { - EmptyComponent c(overlay_flat_pass); - c.SetTransparent(true); - c.PushTransform(); - c.Translate(-tilt.y, tilt.x, -0.5f); - - // We want our widgets to cover 0.1f in z space. - c.Scale(1.0f, 1.0f, 0.1f); - c.Submit(); - root_widget->Draw(overlay_flat_pass, true); - c.PopTransform(); - c.Submit(); - } - - g_base->graphics->set_drawing_transparent_only(false); - } - if (root_ui_) { - root_ui_->Draw(frame_def); + if (g_base->HaveUIV1()) { + g_base->ui_v1()->Draw(frame_def); } } diff --git a/src/ballistica/base/ui/ui.h b/src/ballistica/base/ui/ui.h index 2f02da17..d7c9b109 100644 --- a/src/ballistica/base/ui/ui.h +++ b/src/ballistica/base/ui/ui.h @@ -60,30 +60,19 @@ class UI { /// This can be called from any thread. void ConfirmQuit(); - auto MainMenuVisible() -> bool; + /// Return whether there is UI present in either the main or overlay + /// stacks. Generally this implies the focus should be on the 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); - - // Return the root widget containing all windows & dialogs - // Whenever this contains children, the UI is considered to be in focus - auto screen_root_widget() -> ui_v1::ContainerWidget* { - return screen_root_widget_.Get(); - } - - auto overlay_root_widget() -> ui_v1::ContainerWidget* { - return overlay_root_widget_.Get(); - } + auto PartyWindowOpen() -> bool; /// Return whether there is UI present in either the main or overlay /// stacks. Generally this implies the focus should be on the UI. - auto IsWindowPresent() const -> bool; - - // Return the absolute root widget; this includes persistent UI - // bits such as the top/bottom bars - auto root_widget() -> ui_v1::RootWidget* { return root_widget_.Get(); } + // auto IsWindowPresent() const -> bool; void Draw(FrameDef* frame_def); @@ -92,11 +81,6 @@ class UI { // so only call this if you intend on sending a message to that widget. auto GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget*; - // Add a widget to a container. - // If a parent is provided, the widget is added to it; otherwise it is added - // to the root widget. - void AddWidget(ui_v1::Widget* w, ui_v1::ContainerWidget* to); - // Send message to the active widget. auto SendWidgetMessage(const WidgetMessage& msg) -> int; @@ -130,11 +114,6 @@ class UI { BA_DISALLOW_CLASS_COPIES(UILock); }; - auto root_ui() const -> ui_v1::RootUI* { - assert(root_ui_); - return root_ui_; - } - auto scale() const { return scale_; } /// Push a generic 'menu press' event, optionally associated with an @@ -145,13 +124,9 @@ class UI { private: void MainMenuPress(InputDevice* device); - ui_v1::RootUI* root_ui_{}; Object::WeakRef ui_input_device_; millisecs_t last_input_device_use_time_{}; millisecs_t last_widget_input_reject_err_sound_time_{}; - Object::Ref screen_root_widget_; - Object::Ref overlay_root_widget_; - Object::Ref root_widget_; int ui_lock_count_{}; UIScale scale_{UIScale::kLarge}; bool force_scale_{}; diff --git a/src/ballistica/core/core.cc b/src/ballistica/core/core.cc index ec6cf889..8edfda7c 100644 --- a/src/ballistica/core/core.cc +++ b/src/ballistica/core/core.cc @@ -42,8 +42,8 @@ void CoreFeatureSet::DoImport(const CoreConfig& config) { g_core = new CoreFeatureSet(config); g_core->PostInit(); - // Slightly hacky: have to report our begin time after the fact since - // core didn't exist before. Can at least add an offset to give an accurate + // Slightly hacky: have to report our begin time after the fact since core + // didn't exist before. Can at least add an offset to give an accurate // time though. auto seconds_since_actual_start = static_cast(CorePlatform::GetCurrentMillisecs() - start_millisecs) @@ -67,14 +67,14 @@ void CoreFeatureSet::PostInit() { // Some of this stuff accesses g_core so we need to run it *after* // assigning our singleton. - // Test our static-type-name functionality. - // This code runs at compile time and extracts human readable type names using - // __PRETTY_FUNCTION__ type functionality. However, it is dependent on - // specific compiler output and so could break easily if anything changes. - // Here we add some compile-time checks to alert us if that happens. + // Test our static-type-name functionality. This code runs at compile time + // and extracts human readable type names using __PRETTY_FUNCTION__ type + // functionality. However, it is dependent on specific compiler output and + // so could break easily if anything changes. Here we add some + // compile-time checks to alert us if that happens. - // Remember that results can vary per compiler; make sure we match - // any one of the expected formats. + // Remember that results can vary per compiler; make sure we match any one + // of the expected formats. static_assert(static_type_name_constexpr() == "ballistica::core::CoreFeatureSet *" || static_type_name_constexpr() @@ -94,13 +94,10 @@ void CoreFeatureSet::PostInit() { || static_type_name_constexpr() == "Object::Ref"); - // int testint{}; - // static_assert(static_type_name_constexpr() == "int"); - - // If anything above breaks, enable this code to debug/fix it. - // This will print a calculated type name as well as the full string - // it was parsed from. Use this to adjust the filtering as necessary so - // the resulting type name matches what is expected. + // If anything above breaks, enable this code to debug/fix it. This will + // print a calculated type name as well as the full string it was parsed + // from. Use this to adjust the filtering as necessary so the resulting + // type name matches what is expected. if (explicit_bool(false)) { Log(LogLevel::kError, "static_type_name check; name is '" + static_type_name() diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc index 5e64ceb1..ba450bc4 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc @@ -1117,7 +1117,7 @@ void SceneV1AppMode::LocalDisplayChatMessage( // Show it on the screen if they don't have their chat window open // (and don't have chat muted). - if (!g_base->ui->root_ui()->party_window_open()) { + if (!g_base->ui->PartyWindowOpen()) { if (!chat_muted_) { ScreenMessage(final_message, {0.7f, 1.0f, 0.7f}); } diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index c30e1fb3..0334ca80 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 = 21040; +const int kEngineBuildNumber = 21041; const char* kEngineVersion = "1.7.20"; auto MonolithicMain(const core::CoreConfig& core_config) -> int { 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 c24554ac..aba4e9c3 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 @@ -263,7 +263,7 @@ static auto PyButtonWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (parent_widget == nullptr) { @@ -445,7 +445,7 @@ static auto PyButtonWidget(PyObject* self, PyObject* args, PyObject* keywds) // If making a new widget add it at the end. if (edit_obj == Py_None) { - g_base->ui->AddWidget(b.Get(), parent_widget); + g_ui_v1->AddWidget(b.Get(), parent_widget); } return b->NewPyRef(); @@ -566,7 +566,7 @@ static auto PyCheckBoxWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (parent_widget == nullptr) { @@ -634,7 +634,7 @@ static auto PyCheckBoxWidget(PyObject* self, PyObject* args, PyObject* keywds) // if making a new widget add it at the end if (edit_obj == Py_None) { - g_base->ui->AddWidget(widget.Get(), parent_widget); + g_ui_v1->AddWidget(widget.Get(), parent_widget); } return widget->NewPyRef(); @@ -738,7 +738,7 @@ static auto PyImageWidget(PyObject* self, PyObject* args, PyObject* keywds) PyExcType::kWidgetNotFound); } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (parent_widget == nullptr) { @@ -822,7 +822,7 @@ static auto PyImageWidget(PyObject* self, PyObject* args, PyObject* keywds) // if making a new widget add it at the end if (edit_obj == Py_None) { - g_base->ui->AddWidget(b.Get(), parent_widget); + g_ui_v1->AddWidget(b.Get(), parent_widget); } return b->NewPyRef(); @@ -928,7 +928,7 @@ static auto PyColumnWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (!parent_widget) { @@ -992,7 +992,7 @@ static auto PyColumnWidget(PyObject* self, PyObject* args, PyObject* keywds) // if making a new widget add it at the end if (edit_obj == Py_None) { - g_base->ui->AddWidget(widget.Get(), parent_widget); + g_ui_v1->AddWidget(widget.Get(), parent_widget); } return widget->NewPyRef(); @@ -1134,11 +1134,10 @@ static auto PyContainerWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { if (parent_obj == Py_None) { - BA_PRECONDITION(g_base && g_base->ui - && g_base->ui->screen_root_widget() != nullptr); + BA_PRECONDITION(g_ui_v1 && g_ui_v1->screen_root_widget() != nullptr); } parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (!parent_widget) { @@ -1146,7 +1145,7 @@ static auto PyContainerWidget(PyObject* self, PyObject* args, PyObject* keywds) PyExcType::kWidgetNotFound); } widget = Object::New(); - g_base->ui->AddWidget(widget.Get(), parent_widget); + g_ui_v1->AddWidget(widget.Get(), parent_widget); } // Set applicable values. @@ -1402,7 +1401,7 @@ static auto PyRowWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (!parent_widget) { @@ -1445,7 +1444,7 @@ static auto PyRowWidget(PyObject* self, PyObject* args, PyObject* keywds) // If making a new widget, add it to the parent. if (edit_obj == Py_None) { - g_base->ui->AddWidget(widget.Get(), parent_widget); + g_ui_v1->AddWidget(widget.Get(), parent_widget); } return widget->NewPyRef(); @@ -1547,7 +1546,7 @@ static auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (!parent_widget) { @@ -1618,7 +1617,7 @@ static auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds) // If making a new widget add it at the end. if (edit_obj == Py_None) { - g_base->ui->AddWidget(widget.Get(), parent_widget); + g_ui_v1->AddWidget(widget.Get(), parent_widget); } return widget->NewPyRef(); @@ -1725,7 +1724,7 @@ static auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (!parent_widget) { @@ -1791,7 +1790,7 @@ static auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds) // if making a new widget add it at the end if (edit_obj == Py_None) { - g_base->ui->AddWidget(widget.Get(), parent_widget); + g_ui_v1->AddWidget(widget.Get(), parent_widget); } return widget->NewPyRef(); @@ -1943,7 +1942,7 @@ static auto PyTextWidget(PyObject* self, PyObject* args, PyObject* keywds) } } else { parent_widget = parent_obj == Py_None - ? g_base->ui->screen_root_widget() + ? g_ui_v1->screen_root_widget() : dynamic_cast( UIV1Python::GetPyWidget(parent_obj)); if (!parent_widget) { @@ -2099,7 +2098,7 @@ static auto PyTextWidget(PyObject* self, PyObject* args, PyObject* keywds) // if making a new widget add it at the end if (edit_obj == Py_None) { - g_base->ui->AddWidget(widget.Get(), parent_widget); + g_ui_v1->AddWidget(widget.Get(), parent_widget); } return widget->NewPyRef(); @@ -2545,7 +2544,7 @@ static auto PySetPartyIconAlwaysVisible(PyObject* self, PyObject* args, return nullptr; } assert(g_base->input); - g_base->ui->root_ui()->set_always_draw_party_icon(static_cast(value)); + g_ui_v1->root_ui()->set_always_draw_party_icon(static_cast(value)); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -2572,7 +2571,7 @@ static auto PySetPartyWindowOpen(PyObject* self, PyObject* args, return nullptr; } assert(g_base->input); - g_base->ui->root_ui()->set_party_window_open(static_cast(value)); + g_ui_v1->root_ui()->set_party_window_open(static_cast(value)); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -2600,7 +2599,7 @@ static auto PyGetSpecialWidget(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } BA_PRECONDITION(g_base->InLogicThread()); - RootWidget* root_widget = g_base->ui->root_widget(); + RootWidget* root_widget = g_ui_v1->root_widget(); BA_PRECONDITION(root_widget); Widget* w = root_widget->GetSpecialWidget(name); if (w == nullptr) { @@ -2872,10 +2871,9 @@ static PyMethodDef PyConsolePrintDef = { static auto PyIsPartyIconVisible(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; - bool party_button_active = - (g_base->app_mode()->HasConnectionToClients() - || g_base->app_mode()->HasConnectionToHost() - || g_base->ui->root_ui()->always_draw_party_icon()); + bool party_button_active = (g_base->app_mode()->HasConnectionToClients() + || g_base->app_mode()->HasConnectionToHost() + || g_ui_v1->root_ui()->always_draw_party_icon()); if (party_button_active) { Py_RETURN_TRUE; } else { diff --git a/src/ballistica/ui_v1/support/root_ui.cc b/src/ballistica/ui_v1/support/root_ui.cc index 70b84d08..7f9be83c 100644 --- a/src/ballistica/ui_v1/support/root_ui.cc +++ b/src/ballistica/ui_v1/support/root_ui.cc @@ -59,8 +59,8 @@ void RootUI::ActivatePartyIcon() const { - menu_button_size_ * 0.5f; float icon_pos_v = g_base->graphics->screen_virtual_height() * 0.5f - menu_button_size_ * 0.5f; - bool menu_active = !(g_base->ui && g_base->ui->screen_root_widget() - && g_base->ui->screen_root_widget()->HasChildren()); + bool menu_active = !(g_ui_v1 && g_ui_v1->screen_root_widget() + && g_ui_v1->screen_root_widget()->HasChildren()); if (menu_active) { icon_pos_h -= menu_button_size_; } @@ -71,8 +71,8 @@ void RootUI::ActivatePartyIcon() const { auto RootUI::HandleMouseButtonDown(float x, float y) -> bool { // Whether the menu button is visible/active. - bool menu_active = !(g_base->ui && g_base->ui->screen_root_widget() - && g_base->ui->screen_root_widget()->HasChildren()); + bool menu_active = !(g_ui_v1 && g_ui_v1->screen_root_widget() + && g_ui_v1->screen_root_widget()->HasChildren()); // Handle party button presses (need to do this before UI since it // floats over the top). Party button is to the left of menu button. @@ -150,8 +150,8 @@ void RootUI::Draw(base::FrameDef* frame_def) { // Menu button. // Update time-dependent stuff to this point. - bool active = !(g_base->ui && g_base->ui->screen_root_widget() - && g_base->ui->screen_root_widget()->HasChildren()); + bool active = !(g_ui_v1 && g_ui_v1->screen_root_widget() + && g_ui_v1->screen_root_widget()->HasChildren()); if (real_time - menu_update_time_ > 500) { menu_update_time_ = real_time - 500; } diff --git a/src/ballistica/ui_v1/ui_v1.cc b/src/ballistica/ui_v1/ui_v1.cc index d7ce10ab..e1e289fa 100644 --- a/src/ballistica/ui_v1/ui_v1.cc +++ b/src/ballistica/ui_v1/ui_v1.cc @@ -3,10 +3,15 @@ #include "ballistica/ui_v1/ui_v1.h" #include "ballistica/base/app/app_mode.h" +#include "ballistica/base/graphics/component/empty_component.h" +#include "ballistica/base/graphics/graphics.h" +#include "ballistica/base/input/input.h" #include "ballistica/base/ui/ui.h" #include "ballistica/ui_v1/python/ui_v1_python.h" #include "ballistica/ui_v1/support/root_ui.h" #include "ballistica/ui_v1/widget/container_widget.h" +#include "ballistica/ui_v1/widget/root_widget.h" +#include "ballistica/ui_v1/widget/stack_widget.h" namespace ballistica::ui_v1 { @@ -77,8 +82,8 @@ RootUI* UIV1FeatureSet::NewRootUI() { return new RootUI(); } bool UIV1FeatureSet::MainMenuVisible() { // We consider anything on our screen or overlay stacks to be a 'main menu'. // Probably need a better name than 'main menu' though. - auto* screen_root = g_base->ui->screen_root_widget(); - auto* overlay_root = g_base->ui->overlay_root_widget(); + auto* screen_root = screen_root_widget(); + auto* overlay_root = overlay_root_widget(); return ((screen_root && screen_root->HasChildren()) || (overlay_root && overlay_root->HasChildren())); } @@ -86,35 +91,180 @@ bool UIV1FeatureSet::MainMenuVisible() { bool UIV1FeatureSet::PartyIconVisible() { int party_size = g_base->app_mode()->GetPartySize(); if (party_size > 1 || g_base->app_mode()->HasConnectionToHost() - || g_base->ui->root_ui()->always_draw_party_icon()) { + || root_ui()->always_draw_party_icon()) { return true; } return false; } void UIV1FeatureSet::ActivatePartyIcon() { - if (auto* root_ui = g_base->ui->root_ui()) { - root_ui->ActivatePartyIcon(); + if (auto* r = root_ui()) { + r->ActivatePartyIcon(); } } +bool UIV1FeatureSet::PartyWindowOpen() { + if (auto* r = root_ui()) { + return r->party_window_open(); + } + return false; +} void UIV1FeatureSet::HandleLegacyRootUIMouseMotion(float x, float y) { - if (auto* root_ui = g_base->ui->root_ui()) { - root_ui->HandleMouseMotion(x, y); + if (auto* r = root_ui()) { + r->HandleMouseMotion(x, y); } } auto UIV1FeatureSet::HandleLegacyRootUIMouseDown(float x, float y) -> bool { - if (auto* root_ui = g_base->ui->root_ui()) { - return root_ui->HandleMouseButtonDown(x, y); + if (auto* r = root_ui()) { + return r->HandleMouseButtonDown(x, y); } return false; } void UIV1FeatureSet::HandleLegacyRootUIMouseUp(float x, float y) { - if (auto* root_ui = g_base->ui->root_ui()) { - root_ui->HandleMouseButtonUp(x, y); + if (auto* r = root_ui()) { + r->HandleMouseButtonUp(x, y); } } +void UIV1FeatureSet::Draw(base::FrameDef* frame_def) { + base::RenderPass* overlay_flat_pass = frame_def->GetOverlayFlatPass(); + + // Draw interface elements. + 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. + + g_base->graphics->set_drawing_opaque_only(true); + + // Do a wee bit of shifting based on tilt just for fun. + Vector3f tilt = 0.1f * g_base->graphics->tilt(); + { + base::EmptyComponent c(overlay_flat_pass); + c.SetTransparent(false); + c.PushTransform(); + c.Translate(-tilt.y, tilt.x, -0.5f); + + // We want our widgets to cover 0.1f in z space. + c.Scale(1.0f, 1.0f, 0.1f); + c.Submit(); + root_widget->Draw(overlay_flat_pass, false); + c.PopTransform(); + c.Submit(); + } + + g_base->graphics->set_drawing_opaque_only(false); + g_base->graphics->set_drawing_transparent_only(true); + + { + base::EmptyComponent c(overlay_flat_pass); + c.SetTransparent(true); + c.PushTransform(); + c.Translate(-tilt.y, tilt.x, -0.5f); + + // We want our widgets to cover 0.1f in z space. + c.Scale(1.0f, 1.0f, 0.1f); + c.Submit(); + root_widget->Draw(overlay_flat_pass, true); + c.PopTransform(); + c.Submit(); + } + + g_base->graphics->set_drawing_transparent_only(false); + } + + if (auto* r = root_ui()) { + r->Draw(frame_def); + } +} + +void UIV1FeatureSet::OnAppStart() { + assert(g_base->InLogicThread()); + root_ui_ = g_base->ui_v1()->NewRootUI(); +} + +void UIV1FeatureSet::Reset() { + // Hmm; technically we don't need to recreate these each time we reset. + root_widget_.Clear(); + + // Kill our screen-root widget. + screen_root_widget_.Clear(); + + // (Re)create our screen-root widget. + auto sw(Object::New()); + sw->set_is_main_window_stack(true); + sw->SetWidth(g_base->graphics->screen_virtual_width()); + sw->SetHeight(g_base->graphics->screen_virtual_height()); + sw->set_translate(0, 0); + screen_root_widget_ = sw; + + // (Re)create our screen-overlay widget. + auto ow(Object::New()); + ow->set_is_overlay_window_stack(true); + ow->SetWidth(g_base->graphics->screen_virtual_width()); + ow->SetHeight(g_base->graphics->screen_virtual_height()); + ow->set_translate(0, 0); + overlay_root_widget_ = ow; + + // (Re)create our abs-root widget. + auto rw(Object::New()); + root_widget_ = rw; + rw->SetWidth(g_base->graphics->screen_virtual_width()); + rw->SetHeight(g_base->graphics->screen_virtual_height()); + rw->SetScreenWidget(sw.Get()); + rw->Setup(); + rw->SetOverlayWidget(ow.Get()); + + sw->GlobalSelect(); +} + +void UIV1FeatureSet::AddWidget(Widget* w, ContainerWidget* parent) { + assert(g_base->InLogicThread()); + + BA_PRECONDITION(parent != nullptr); + + // If they're adding an initial window/dialog to our screen-stack + // or overlay stack, send a reset-local-input message so that characters + // who have lost focus will not get stuck running or whatnot. + // We should come up with a more generalized way to track this sort of + // focus as this is a bit hacky, but it works for now. + auto* screen_root_widget = screen_root_widget_.Get(); + auto* overlay_root_widget = overlay_root_widget_.Get(); + if ((screen_root_widget && !screen_root_widget->HasChildren() + && parent == screen_root_widget) + || (overlay_root_widget && !overlay_root_widget->HasChildren() + && parent == overlay_root_widget)) { + g_base->input->ResetHoldStates(); + } + + parent->AddWidget(w); +} + +void UIV1FeatureSet::OnScreenSizeChange() { + if (root_widget_.Exists()) { + root_widget_->SetWidth(g_base->graphics->screen_virtual_width()); + root_widget_->SetHeight(g_base->graphics->screen_virtual_height()); + } +} + +void UIV1FeatureSet::OnLanguageChange() { + // As well as existing UI stuff. + if (auto* r = root_widget()) { + r->OnLanguageChange(); + } +} + +Widget* UIV1FeatureSet::GetRootWidget() { return root_widget(); } + +auto UIV1FeatureSet::SendWidgetMessage(const base::WidgetMessage& m) -> int { + if (!root_widget_.Exists()) { + return false; + } + return root_widget_->HandleMessage(m); +} + } // namespace ballistica::ui_v1 diff --git a/src/ballistica/ui_v1/ui_v1.h b/src/ballistica/ui_v1/ui_v1.h index f2826af1..f33fabe1 100644 --- a/src/ballistica/ui_v1/ui_v1.h +++ b/src/ballistica/ui_v1/ui_v1.h @@ -18,7 +18,8 @@ class CoreFeatureSet; } namespace ballistica::base { class BaseFeatureSet; -} +class WidgetMessage; +} // namespace ballistica::base namespace ballistica::ui_v1 { @@ -64,11 +65,48 @@ class UIV1FeatureSet : public FeatureSetNativeComponent, void HandleLegacyRootUIMouseMotion(float x, float y) override; auto HandleLegacyRootUIMouseDown(float x, float y) -> bool override; void HandleLegacyRootUIMouseUp(float x, float y) override; + void Draw(base::FrameDef* frame_def) override; UIV1Python* const python; + auto root_ui() const -> ui_v1::RootUI* { + assert(root_ui_); + return root_ui_; + } + void OnAppStart() override; + auto PartyWindowOpen() -> bool override; + + // Return the root widget containing all windows & dialogs + // Whenever this contains children, the UI is considered to be in focus + auto screen_root_widget() -> ui_v1::ContainerWidget* { + return screen_root_widget_.Get(); + } + + auto overlay_root_widget() -> ui_v1::ContainerWidget* { + return overlay_root_widget_.Get(); + } + + // Return the absolute root widget; this includes persistent UI + // bits such as the top/bottom bars + auto root_widget() -> ui_v1::RootWidget* { return root_widget_.Get(); } + void Reset() override; + + // Add a widget to a container. + // If a parent is provided, the widget is added to it; otherwise it is added + // to the root widget. + void AddWidget(Widget* w, ContainerWidget* to); + void OnScreenSizeChange() override; + void OnLanguageChange() override; + auto GetRootWidget() -> ui_v1::Widget* override; + auto SendWidgetMessage(const base::WidgetMessage& m) -> int override; + private: UIV1FeatureSet(); + + RootUI* root_ui_{}; + Object::Ref screen_root_widget_; + Object::Ref overlay_root_widget_; + Object::Ref root_widget_; }; } // namespace ballistica::ui_v1 diff --git a/src/ballistica/ui_v1/widget/container_widget.cc b/src/ballistica/ui_v1/widget/container_widget.cc index 2ab1cd83..03b61057 100644 --- a/src/ballistica/ui_v1/widget/container_widget.cc +++ b/src/ballistica/ui_v1/widget/container_widget.cc @@ -1086,7 +1086,7 @@ void ContainerWidget::AddWidget(Widget* w) { // direct selected child (which may not affect the global selection). if (is_window_stack_ && (is_overlay_window_stack_ - || !g_base->ui->root_widget() + || !g_ui_v1->root_widget() ->overlay_window_stack() ->HasChildren())) { w->GlobalSelect(); @@ -1094,7 +1094,7 @@ void ContainerWidget::AddWidget(Widget* w) { // Special case for the main window stack; whenever a window is added, // update the toolbar state for the topmost living container. if (is_main_window_stack_) { - g_base->ui->root_widget()->UpdateForFocusedWindow(); + g_ui_v1->root_widget()->UpdateForFocusedWindow(); } } else { SelectWidget(w); @@ -1232,7 +1232,7 @@ void ContainerWidget::SetTransition(TransitionType t) { // *immediately* (otherwise we'd have to wait for our transition to complete // before the toolbar switches). if (transitioning_ && transitioning_out_ && parent->is_main_window_stack_) { - g_base->ui->root_widget()->UpdateForFocusedWindow(); + g_ui_v1->root_widget()->UpdateForFocusedWindow(); } } @@ -1275,7 +1275,7 @@ void ContainerWidget::DeleteWidget(Widget* w) { if (is_overlay_window_stack_) { if (widgets_.empty()) { // Eww this logic should be in some sort of controller. - g_base->ui->root_widget()->ReselectLastSelectedWidget(); + g_ui_v1->root_widget()->ReselectLastSelectedWidget(); return; } } @@ -1293,7 +1293,7 @@ void ContainerWidget::DeleteWidget(Widget* w) { // direct selected child (which may not affect the global selection). if (is_window_stack_ && (is_overlay_window_stack_ - || !g_base->ui->root_widget() + || !g_ui_v1->root_widget() ->overlay_window_stack() ->HasChildren())) { (**i).GlobalSelect(); @@ -1308,7 +1308,7 @@ void ContainerWidget::DeleteWidget(Widget* w) { // Special case: if we're the main window stack, // update the active toolbar/etc. if (is_main_window_stack_) { - g_base->ui->root_widget()->UpdateForFocusedWindow(); + g_ui_v1->root_widget()->UpdateForFocusedWindow(); } } @@ -1559,8 +1559,7 @@ auto ContainerWidget::GetClosestDownWidget(float our_x, float our_y, void ContainerWidget::SelectDownWidget() { BA_DEBUG_UI_READ_LOCK; - if (!g_base->ui || !g_base->ui->root_widget() - || !g_base->ui->screen_root_widget()) { + if (!g_ui_v1 || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { BA_LOG_ONCE(LogLevel::kError, "SelectDownWidget called before UI init."); return; } @@ -1581,9 +1580,9 @@ void ContainerWidget::SelectDownWidget() { float x = our_x; float y = our_y; WidgetPointToScreen(&x, &y); - g_base->ui->root_widget()->ScreenPointToWidget(&x, &y); - w = g_base->ui->root_widget()->GetClosestDownWidget( - x, y, g_base->ui->screen_root_widget()); + g_ui_v1->root_widget()->ScreenPointToWidget(&x, &y); + w = g_ui_v1->root_widget()->GetClosestDownWidget( + x, y, g_ui_v1->screen_root_widget()); } // When we find no viable targets for an autoselect widget we do // nothing. @@ -1625,8 +1624,7 @@ void ContainerWidget::SelectDownWidget() { void ContainerWidget::SelectUpWidget() { BA_DEBUG_UI_READ_LOCK; - if (!g_base->ui || !g_base->ui->root_widget() - || !g_base->ui->screen_root_widget()) { + if (!g_ui_v1 || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { BA_LOG_ONCE(LogLevel::kError, "SelectUpWidget called before UI init."); return; } @@ -1647,9 +1645,9 @@ void ContainerWidget::SelectUpWidget() { float x = our_x; float y = our_y; WidgetPointToScreen(&x, &y); - g_base->ui->root_widget()->ScreenPointToWidget(&x, &y); - w = g_base->ui->root_widget()->GetClosestUpWidget( - x, y, g_base->ui->screen_root_widget()); + g_ui_v1->root_widget()->ScreenPointToWidget(&x, &y); + w = g_ui_v1->root_widget()->GetClosestUpWidget( + x, y, g_ui_v1->screen_root_widget()); } // When we find no viable targets for an autoselect widget we do // nothing. @@ -1691,8 +1689,7 @@ void ContainerWidget::SelectUpWidget() { void ContainerWidget::SelectLeftWidget() { BA_DEBUG_UI_READ_LOCK; - if (!g_base->ui || !g_base->ui->root_widget() - || !g_base->ui->screen_root_widget()) { + if (!g_ui_v1 || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { BA_LOG_ONCE(LogLevel::kError, "SelectLeftWidget called before UI init."); return; } @@ -1743,8 +1740,8 @@ void ContainerWidget::SelectLeftWidget() { void ContainerWidget::SelectRightWidget() { BA_DEBUG_UI_READ_LOCK; - if (!g_base->ui || !g_base->ui->root_widget() - || !g_base->ui->screen_root_widget()) { + if (!g_base->ui || !g_ui_v1->root_widget() + || !g_ui_v1->screen_root_widget()) { BA_LOG_ONCE(LogLevel::kError, "SelectRightWidget called before UI init."); return; } @@ -1797,8 +1794,8 @@ void ContainerWidget::SelectRightWidget() { void ContainerWidget::SelectNextWidget() { BA_DEBUG_UI_READ_LOCK; - if (!g_base->ui || !g_base->ui->root_widget() - || !g_base->ui->screen_root_widget()) { + if (!g_base->ui || !g_ui_v1->root_widget() + || !g_ui_v1->screen_root_widget()) { BA_LOG_ONCE(LogLevel::kError, "SelectNextWidget called before UI init."); return; } diff --git a/src/ballistica/ui_v1/widget/widget.cc b/src/ballistica/ui_v1/widget/widget.cc index 1d646dd6..6b8464fc 100644 --- a/src/ballistica/ui_v1/widget/widget.cc +++ b/src/ballistica/ui_v1/widget/widget.cc @@ -33,7 +33,7 @@ void Widget::SetToolbarVisibility(ToolbarVisibility v) { // Most widgets can never influence the global toolbar so we can // do a quick out. if (parent_widget_ != nullptr && parent_widget_->is_window_stack()) { - g_base->ui->root_widget()->UpdateForFocusedWindow(); + g_ui_v1->root_widget()->UpdateForFocusedWindow(); } } @@ -52,7 +52,7 @@ auto Widget::IsInMainStack() const -> bool { } // Navigate up to the top of the hierarchy and see if the // screen-root widget is in there somewhere. - ContainerWidget* screen_root = g_base->ui->screen_root_widget(); + ContainerWidget* screen_root = g_ui_v1->screen_root_widget(); assert(screen_root); if (!screen_root) { return false; @@ -70,7 +70,7 @@ auto Widget::IsInMainStack() const -> bool { auto Widget::IsInOverlayStack() const -> bool { // Navigate up to the top of the hierarchy and see if the overlay-root widget // is in there somewhere. - ContainerWidget* overlay_root = g_base->ui->overlay_root_widget(); + ContainerWidget* overlay_root = g_ui_v1->overlay_root_widget(); assert(overlay_root); ContainerWidget* parent = parent_widget_; while (parent != nullptr) { @@ -99,7 +99,7 @@ auto Widget::IsHierarchySelected() const -> bool { return false; } p = p->GetOwnerWidget(); - if (!p || p == g_base->ui->root_widget()) { + if (!p || p == g_ui_v1->root_widget()) { break; } }