Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 48 additions & 53 deletions packages/app/windows/Shared/ReactInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,18 @@ namespace
winrt::hstring const kUseWebDebugger = L"useWebDebugger";
#endif // USE_WEB_DEBUGGER

std::optional<winrt::hstring> GetBundleName(std::optional<winrt::hstring> 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<winrt::hstring> GetBundleName(std::filesystem::path bundlePath,
std::optional<winrt::hstring> 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""}) {
Expand Down Expand Up @@ -104,74 +111,31 @@ std::vector<std::wstring_view> const ReactTestApp::JSBundleNames = {
L"main",
};

ReactInstance::ReactInstance()
{
reactNativeHost_.PackageProviders().Append(winrt::make<ReactPackageProvider>());
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(<JSI/JsiApiContext.h>)
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(<JSI/JsiApiContext.h>)
});
}

#if __has_include(<winrt/Microsoft.UI.Composition.h>)
ReactInstance::ReactInstance(HWND hwnd,
winrt::Microsoft::UI::Composition::Compositor const &compositor)
: ReactInstance()
{
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
reactNativeHost_.InstanceSettings().Properties(), reinterpret_cast<uint64_t>(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(<winrt/Microsoft.UI.Composition.h>)

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");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should just set DebugBundlePath and JavaScriptBundleFile always. The only think you need to change for lading from DevServer vs Embedded is UseFastRefresh.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that wrong? UseFastRefresh can be disabled in dev mode if you wish to manually reload. Using it to determine whether to use the embedded JS bundle seems wrong.

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;
}
instanceSettings.JavaScriptBundleFile(bundleName.value());
break;
}

Reload();
Reload(reloadHost);
return true;
}

void ReactInstance::Reload()
void ReactInstance::Reload(bool reloadHost)
{
auto instanceSettings = reactNativeHost_.InstanceSettings();

Expand All @@ -196,7 +160,9 @@ void ReactInstance::Reload()
instanceSettings.SourceBundleHost(host);
instanceSettings.SourceBundlePort(static_cast<uint16_t>(port));

reactNativeHost_.ReloadInstance();
if (reloadHost) {
reactNativeHost_.ReloadInstance();
}
}

bool ReactInstance::BreakOnFirstLine() const
Expand Down Expand Up @@ -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<ReactPackageProvider>());
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(<JSI/JsiApiContext.h>)
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(<JSI/JsiApiContext.h>)
});
}

winrt::IAsyncOperation<bool> ReactTestApp::IsDevServerRunning()
{
winrt::Uri uri(L"http://localhost:8081/status");
Expand Down
19 changes: 13 additions & 6 deletions packages/app/windows/Shared/ReactInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,24 @@ namespace ReactTestApp
public:
static constexpr uint32_t Version = REACT_NATIVE_VERSION;

ReactInstance();
ReactInstance()
{
InitializeHost(reactNativeHost_);
}

#if __has_include(<winrt/Microsoft.UI.Composition.h>)
ReactInstance(HWND hwnd, winrt::Microsoft::UI::Composition::Compositor const &);
#endif // __has_include(<winrt/Microsoft.UI.Composition.h>)
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);
Expand Down Expand Up @@ -100,6 +105,8 @@ namespace ReactTestApp
std::optional<winrt::hstring> bundleRoot_;
JSBundleSource source_ = JSBundleSource::DevServer;
OnComponentsRegistered onComponentsRegistered_;

void InitializeHost(winrt::Microsoft::ReactNative::ReactNativeHost);
};

winrt::Windows::Foundation::IAsyncOperation<bool> IsDevServerRunning();
Expand Down
126 changes: 13 additions & 113 deletions packages/app/windows/Win32/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,30 +21,9 @@ namespace
#endif
constexpr bool kSingleAppMode = static_cast<bool>(ENABLE_SINGLE_APP_MODE);

float ScaleFactor(HWND hwnd) noexcept
void ConfigureReactViewOptions(winrt::ReactViewOptions viewOptions,
ReactApp::Component const &component)
{
return GetDpiForWindow(hwnd) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
}

void UpdateRootViewSizeToAppWindow(winrt::ReactNativeIsland const &rootView,
winrt::AppWindow const &window)
{
// Do not relayout when minimized
auto windowState = window.Presenter().as<winrt::OverlappedPresenter>().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<ReactApp::JSONObject>({});
Expand All @@ -71,8 +37,6 @@ namespace
}
writer.WriteObjectEnd();
});

return viewOptions;
}
} // namespace

Expand All @@ -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)));
Expand All @@ -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();
}
Loading