diff --git a/packages/app/windows/Shared/ReactInstance.cpp b/packages/app/windows/Shared/ReactInstance.cpp index bf5fa74ae..bd0e80425 100644 --- a/packages/app/windows/Shared/ReactInstance.cpp +++ b/packages/app/windows/Shared/ReactInstance.cpp @@ -46,11 +46,18 @@ namespace winrt::hstring const kUseWebDebugger = L"useWebDebugger"; #endif // USE_WEB_DEBUGGER - std::optional GetBundleName(std::optional const &bundleRoot) + std::filesystem::path GetBundleRootPath() + { + WCHAR modulePath[MAX_PATH]; + GetModuleFileNameW(NULL, modulePath, MAX_PATH); + return std::filesystem::path{modulePath}.replace_filename(L"Bundle") / L""; + } + + std::optional GetBundleName(std::filesystem::path bundlePath, + std::optional const &bundleRoot) { constexpr std::wstring_view const bundleExtension = L".bundle"; - std::filesystem::path bundlePath{L"Bundle\\"}; if (bundleRoot.has_value()) { std::wstring_view root = bundleRoot.value(); for (auto &&ext : {L".windows", L".native", L""}) { @@ -104,62 +111,19 @@ std::vector const ReactTestApp::JSBundleNames = { L"main", }; -ReactInstance::ReactInstance() -{ - reactNativeHost_.PackageProviders().Append(winrt::make()); - winrt::Microsoft::ReactNative::RegisterAutolinkedNativeModulePackages( - reactNativeHost_.PackageProviders()); - - reactNativeHost_.InstanceSettings().InstanceLoaded( - [this](winrt::IInspectable const & /*sender*/, winrt::InstanceLoadedEventArgs const &args) { - context_ = args.Context(); - -#if __has_include("AppRegistry.h") && __has_include() - if (!onComponentsRegistered_) { - return; - } - - winrt::Microsoft::ReactNative::ExecuteJsi(context_, [this](Runtime &runtime) noexcept { - try { - onComponentsRegistered_(ReactTestApp::GetAppKeys(runtime)); - } catch ([[maybe_unused]] std::exception const &e) { -#if defined(_DEBUG) && !defined(DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION) - if (IsDebuggerPresent()) { - __debugbreak(); - } -#endif // defined(_DEBUG) && !defined(DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION) - } - }); -#endif // __has_include("AppRegistry.h") && __has_include() - }); -} - -#if __has_include() -ReactInstance::ReactInstance(HWND hwnd, - winrt::Microsoft::UI::Composition::Compositor const &compositor) - : ReactInstance() -{ - winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId( - reactNativeHost_.InstanceSettings().Properties(), reinterpret_cast(hwnd)); - - // By using the MicrosoftCompositionContextHelper here, React Native Windows - // will use Lifted Visuals for its tree. - winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositor( - reactNativeHost_.InstanceSettings(), compositor); -} -#endif // __has_include() - -bool ReactInstance::LoadJSBundleFrom(JSBundleSource source) +bool ReactInstance::LoadJSBundleFrom(JSBundleSource source, bool reloadHost) { source_ = source; auto instanceSettings = reactNativeHost_.InstanceSettings(); switch (source) { case JSBundleSource::DevServer: - instanceSettings.JavaScriptBundleFile(L"index"); + instanceSettings.DebugBundlePath(L"index"); break; case JSBundleSource::Embedded: - auto const &bundleName = GetBundleName(bundleRoot_); + auto const bundleRootPath = GetBundleRootPath(); + instanceSettings.BundleRootPath(L"file://" + bundleRootPath.wstring()); + auto const &bundleName = GetBundleName(bundleRootPath, bundleRoot_); if (!bundleName.has_value()) { return false; } @@ -167,11 +131,11 @@ bool ReactInstance::LoadJSBundleFrom(JSBundleSource source) break; } - Reload(); + Reload(reloadHost); return true; } -void ReactInstance::Reload() +void ReactInstance::Reload(bool reloadHost) { auto instanceSettings = reactNativeHost_.InstanceSettings(); @@ -196,7 +160,9 @@ void ReactInstance::Reload() instanceSettings.SourceBundleHost(host); instanceSettings.SourceBundlePort(static_cast(port)); - reactNativeHost_.ReloadInstance(); + if (reloadHost) { + reactNativeHost_.ReloadInstance(); + } } bool ReactInstance::BreakOnFirstLine() const @@ -297,6 +263,35 @@ void ReactInstance::UseWebDebugger(bool useWebDebugger) #endif // USE_WEB_DEBUGGER } +void ReactInstance::InitializeHost(winrt::Microsoft::ReactNative::ReactNativeHost host) +{ + host.PackageProviders().Append(winrt::make()); + winrt::Microsoft::ReactNative::RegisterAutolinkedNativeModulePackages(host.PackageProviders()); + + host.InstanceSettings().InstanceLoaded( + [this](winrt::IInspectable const & /*sender*/, winrt::InstanceLoadedEventArgs const &args) { + context_ = args.Context(); + +#if __has_include("AppRegistry.h") && __has_include() + if (!onComponentsRegistered_) { + return; + } + + winrt::Microsoft::ReactNative::ExecuteJsi(context_, [this](Runtime &runtime) noexcept { + try { + onComponentsRegistered_(ReactTestApp::GetAppKeys(runtime)); + } catch ([[maybe_unused]] std::exception const &e) { +#if defined(_DEBUG) && !defined(DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION) + if (IsDebuggerPresent()) { + __debugbreak(); + } +#endif // defined(_DEBUG) && !defined(DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION) + } + }); +#endif // __has_include("AppRegistry.h") && __has_include() + }); +} + winrt::IAsyncOperation ReactTestApp::IsDevServerRunning() { winrt::Uri uri(L"http://localhost:8081/status"); diff --git a/packages/app/windows/Shared/ReactInstance.h b/packages/app/windows/Shared/ReactInstance.h index 2353d4759..5f880b925 100644 --- a/packages/app/windows/Shared/ReactInstance.h +++ b/packages/app/windows/Shared/ReactInstance.h @@ -32,19 +32,24 @@ namespace ReactTestApp public: static constexpr uint32_t Version = REACT_NATIVE_VERSION; - ReactInstance(); + ReactInstance() + { + InitializeHost(reactNativeHost_); + } -#if __has_include() - ReactInstance(HWND hwnd, winrt::Microsoft::UI::Composition::Compositor const &); -#endif // __has_include() + ReactInstance(winrt::Microsoft::ReactNative::ReactNativeHost reactNativeHost) + : reactNativeHost_(reactNativeHost) + { + InitializeHost(reactNativeHost); + } auto const &ReactHost() const { return reactNativeHost_; } - bool LoadJSBundleFrom(JSBundleSource); - void Reload(); + bool LoadJSBundleFrom(JSBundleSource, bool reloadHost = true); + void Reload(bool reloadHost = true); bool BreakOnFirstLine() const; void BreakOnFirstLine(bool); @@ -100,6 +105,8 @@ namespace ReactTestApp std::optional bundleRoot_; JSBundleSource source_ = JSBundleSource::DevServer; OnComponentsRegistered onComponentsRegistered_; + + void InitializeHost(winrt::Microsoft::ReactNative::ReactNativeHost); }; winrt::Windows::Foundation::IAsyncOperation IsDevServerRunning(); diff --git a/packages/app/windows/Win32/Main.cpp b/packages/app/windows/Win32/Main.cpp index 91da5f7ea..102d6826c 100644 --- a/packages/app/windows/Win32/Main.cpp +++ b/packages/app/windows/Win32/Main.cpp @@ -9,20 +9,7 @@ namespace winrt { using winrt::Microsoft::ReactNative::IJSValueWriter; - using winrt::Microsoft::ReactNative::LayoutDirection; - using winrt::Microsoft::ReactNative::ReactCoreInjection; - using winrt::Microsoft::ReactNative::ReactNativeIsland; using winrt::Microsoft::ReactNative::ReactViewOptions; - using winrt::Microsoft::UI::Composition::Compositor; - using winrt::Microsoft::UI::Content::ContentSizePolicy; - using winrt::Microsoft::UI::Content::DesktopChildSiteBridge; - using winrt::Microsoft::UI::Dispatching::DispatcherQueueController; - using winrt::Microsoft::UI::Windowing::AppWindow; - using winrt::Microsoft::UI::Windowing::AppWindowChangedEventArgs; - using winrt::Microsoft::UI::Windowing::OverlappedPresenter; - using winrt::Microsoft::UI::Windowing::OverlappedPresenterState; - using winrt::Windows::Foundation::AsyncStatus; - using winrt::Windows::Foundation::Size; } // namespace winrt namespace @@ -34,30 +21,9 @@ namespace #endif constexpr bool kSingleAppMode = static_cast(ENABLE_SINGLE_APP_MODE); - float ScaleFactor(HWND hwnd) noexcept + void ConfigureReactViewOptions(winrt::ReactViewOptions viewOptions, + ReactApp::Component const &component) { - return GetDpiForWindow(hwnd) / static_cast(USER_DEFAULT_SCREEN_DPI); - } - - void UpdateRootViewSizeToAppWindow(winrt::ReactNativeIsland const &rootView, - winrt::AppWindow const &window) - { - // Do not relayout when minimized - auto windowState = window.Presenter().as().State(); - if (windowState == winrt::OverlappedPresenterState::Minimized) { - return; - } - - auto hwnd = winrt::Microsoft::UI::GetWindowFromWindowId(window.Id()); - auto scaleFactor = ScaleFactor(hwnd); - winrt::Size size{window.ClientSize().Width / scaleFactor, - window.ClientSize().Height / scaleFactor}; - rootView.Arrange({size, size, winrt::LayoutDirection::Undefined}, {0, 0}); - } - - winrt::ReactViewOptions MakeReactViewOptions(ReactApp::Component const &component) - { - winrt::ReactViewOptions viewOptions; viewOptions.ComponentName(winrt::to_hstring(component.appKey)); auto initialProps = component.initialProperties.value_or({}); @@ -71,8 +37,6 @@ namespace } writer.WriteObjectEnd(); }); - - return viewOptions; } } // namespace @@ -91,22 +55,8 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE /* instance */, // Enable per monitor DPI scaling SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - // Create a DispatcherQueue for this thread. This is needed for Composition, Content, and - // Input APIs. - auto dispatcherQueueController = winrt::DispatcherQueueController::CreateOnCurrentThread(); - - // Create a Compositor for all Content on this thread. - auto compositor = winrt::Compositor{}; - - // Create a top-level window. - auto window = winrt::AppWindow::Create(); - window.Title(winrt::to_hstring(manifest.displayName)); - window.Resize({600, 800}); - window.Show(); - auto hwnd = winrt::Microsoft::UI::GetWindowFromWindowId(window.Id()); - auto scaleFactor = ScaleFactor(hwnd); - - auto instance = ReactTestApp::ReactInstance{hwnd, compositor}; + auto app = winrt::Microsoft::ReactNative::ReactNativeAppBuilder().Build(); + auto instance = ReactTestApp::ReactInstance{app.ReactNativeHost()}; if (manifest.bundleRoot.has_value()) { auto &bundleRoot = *manifest.bundleRoot; instance.BundleRoot(std::make_optional(winrt::to_hstring(bundleRoot))); @@ -115,81 +65,31 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE /* instance */, // Start the react-native instance, which will create a JavaScript runtime and load the // applications bundle if constexpr (kDebug) { - instance.LoadJSBundleFrom(ReactTestApp::JSBundleSource::DevServer); + instance.LoadJSBundleFrom(ReactTestApp::JSBundleSource::DevServer, false); } else { - instance.LoadJSBundleFrom(ReactTestApp::JSBundleSource::Embedded); + instance.LoadJSBundleFrom(ReactTestApp::JSBundleSource::Embedded, false); } - // Create a RootView which will present a react-native component - winrt::ReactViewOptions viewOptions; + // Configure ReactViewOptions to load the initial component if constexpr (kSingleAppMode) { assert(manifest.singleApp.has_value() || !"`ENABLE_SINGLE_APP_MODE` shouldn't have been true"); for (auto &component : *manifest.components) { if (component.slug == *manifest.singleApp) { - viewOptions = MakeReactViewOptions(component); + ConfigureReactViewOptions(app.ReactViewOptions(), component); break; } } } else { // TODO: Implement session restoration auto &component = (*manifest.components)[0]; - viewOptions = MakeReactViewOptions(component); + ConfigureReactViewOptions(app.ReactViewOptions(), component); } - auto rootView = winrt::ReactNativeIsland{compositor}; - rootView.ReactViewHost( - winrt::ReactCoreInjection::MakeViewHost(instance.ReactHost(), viewOptions)); - - // Update the size of the RootView when the AppWindow changes size - window.Changed( - [wkRootView = winrt::make_weak(rootView)](winrt::AppWindow const &window, - winrt::AppWindowChangedEventArgs const &args) { - if (args.DidSizeChange() || args.DidVisibilityChange()) { - if (auto rootView = wkRootView.get()) { - UpdateRootViewSizeToAppWindow(rootView, window); - } - } - }); - - // Quit application when main window is closed - window.Destroying([&host = instance.ReactHost()](winrt::AppWindow const & /* window */, - winrt::IInspectable const & /* args */) { - // Before we shutdown the application - unload the ReactNativeHost to give the javascript a - // chance to save any state - auto async = host.UnloadInstance(); - async.Completed([host](auto asyncInfo, winrt::AsyncStatus asyncStatus) { - assert(asyncStatus == winrt::AsyncStatus::Completed); - host.InstanceSettings().UIDispatcher().Post([]() { PostQuitMessage(0); }); - }); - }); - - // DesktopChildSiteBridge create a ContentSite that can host the RootView ContentIsland - auto bridge = winrt::DesktopChildSiteBridge::Create(compositor, window.Id()); - bridge.Connect(rootView.Island()); - bridge.ResizePolicy(winrt::ContentSizePolicy::ResizeContentToParentWindow); - - auto invScale = 1.0f / scaleFactor; - rootView.RootVisual().Scale({invScale, invScale, invScale}); - rootView.ScaleFactor(scaleFactor); - - // Set the intialSize of the root view - UpdateRootViewSizeToAppWindow(rootView, window); - - bridge.Show(); - - // Run the main application event loop - dispatcherQueueController.DispatcherQueue().RunEventLoop(); - - // Rundown the DispatcherQueue. This drains the queue and raises events to let components - // know the message loop has finished. - dispatcherQueueController.ShutdownQueue(); - - bridge.Close(); - bridge = nullptr; + auto window = app.AppWindow(); + window.Title(winrt::to_hstring(manifest.displayName)); + window.Resize({600, 800}); - // Destroy all Composition objects - compositor.Close(); - compositor = nullptr; + app.Start(); }