in vnext/Microsoft.ReactNative/Views/DevMenu.cpp [98:300]
void DevMenuManager::CreateAndShowUI() noexcept {
if (!Mso::React::ReactOptions::UseDeveloperSupport(m_context->Properties()))
return;
winrt::Microsoft::ReactNative::DevMenuControl devMenu{};
devMenu.RemoteDebugText().Text(
Mso::React::ReactOptions::UseWebDebugger(m_context->Properties()) ? L"Disable Remote JS Debugging"
: L"Enable Remote JS Debugging");
devMenu.RemoteDebugDesc().Text(
L"When enabled runs the JS remotely in VSCode or Chrome based on what you attach to the packager. This means that the JS may run with a different JS engine than it runs in on in the real application, in addition synchronous native module calls, and JSI native modules will not work.");
devMenu.FastRefreshText().Text(
Mso::React::ReactOptions::UseFastRefresh(m_context->Properties()) ? L"Disable Fast Refresh"
: L"Enable Fast Refresh");
if (Mso::React::ReactOptions::JsiEngine(m_context->Properties()) == Mso::React::JSIEngine::Hermes) {
devMenu.SamplingProfilerText().Text(
!Microsoft::ReactNative::HermesSamplingProfiler::IsStarted() ? L"Start Hermes sampling profiler"
: L"Stop and copy trace path to clipboard");
devMenu.SamplingProfilerIcon().Glyph(
!Microsoft::ReactNative::HermesSamplingProfiler::IsStarted() ? L"\ue1e5" : L"\ue15b");
std::ostringstream os;
if (Microsoft::ReactNative::HermesSamplingProfiler::IsStarted()) {
os << "Hermes Sampling profiler is running.. !";
} else {
os << "Click to start.";
}
auto lastTraceFilePath = Microsoft::ReactNative::HermesSamplingProfiler::GetLastTraceFilePath();
if (!lastTraceFilePath.empty()) {
os << std::endl
<< "Samples from last invocation are stored at " << lastTraceFilePath.c_str()
<< " (path copied to clipboard).";
os << std::endl << "Navigate to \"edge:\\tracing\" and load the trace file.";
}
devMenu.SamplingProfilerDescText().Text(winrt::to_hstring(os.str()));
}
devMenu.DirectDebugText().Text(
Mso::React::ReactOptions::UseDirectDebugger(m_context->Properties()) ? L"Disable Direct Debugging"
: L"Enable Direct Debugging");
devMenu.DirectDebugDesc().Text(
L"If using Chakra, this will allow Visual Studio to be attached directly to the application using \"Script Debugging\" to debug the JS running directly in this app.\nIf using V8/Hermes, this will enable standard JS debugging tools such as VSCode to attach to the application.");
devMenu.BreakOnNextLineText().Text(
Mso::React::ReactOptions::DebuggerBreakOnNextLine(m_context->Properties()) ? L"Disable Break on First Line"
: L"Enable Break on First Line");
m_reloadJSRevoker = devMenu.Reload().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
DevSettings::Reload(React::ReactPropertyBag(strongThis->m_context->Properties()));
}
});
m_remoteDebugJSRevoker = devMenu.RemoteDebug().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
Mso::React::ReactOptions::SetUseWebDebugger(
strongThis->m_context->Properties(),
!Mso::React::ReactOptions::UseWebDebugger(strongThis->m_context->Properties()));
DevSettings::Reload(React::ReactPropertyBag(strongThis->m_context->Properties()));
}
});
m_directDebuggingRevoker = devMenu.DirectDebug().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
Mso::React::ReactOptions::SetUseDirectDebugger(
strongThis->m_context->Properties(),
!Mso::React::ReactOptions::UseDirectDebugger(strongThis->m_context->Properties()));
DevSettings::Reload(React::ReactPropertyBag(strongThis->m_context->Properties()));
}
});
m_breakOnNextLineRevoker = devMenu.BreakOnNextLine().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
Mso::React::ReactOptions::SetDebuggerBreakOnNextLine(
strongThis->m_context->Properties(),
!Mso::React::ReactOptions::DebuggerBreakOnNextLine(strongThis->m_context->Properties()));
DevSettings::Reload(React::ReactPropertyBag(strongThis->m_context->Properties()));
}
});
m_fastRefreshRevoker = devMenu.FastRefresh().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](auto & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
Mso::React::ReactOptions::SetUseFastRefresh(
strongThis->m_context->Properties(),
!Mso::React::ReactOptions::UseFastRefresh(strongThis->m_context->Properties()));
DevSettings::Reload(React::ReactPropertyBag(strongThis->m_context->Properties()));
}
});
if (Mso::React::ReactOptions::JsiEngine(m_context->Properties()) == Mso::React::JSIEngine::Hermes) {
m_samplingProfilerRevoker = devMenu.SamplingProfiler().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](
auto & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept -> winrt::fire_and_forget {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
if (!Microsoft::ReactNative::HermesSamplingProfiler::IsStarted()) {
Microsoft::ReactNative::HermesSamplingProfiler::Start();
} else {
auto traceFilePath = co_await Microsoft::ReactNative::HermesSamplingProfiler::Stop();
auto uiDispatcher =
React::implementation::ReactDispatcher::GetUIDispatcher(strongThis->m_context->Properties());
uiDispatcher.Post([traceFilePath]() {
DataTransfer::DataPackage data;
data.SetText(winrt::to_hstring(traceFilePath));
DataTransfer::Clipboard::SetContentWithOptions(data, nullptr);
});
}
}
});
} else {
devMenu.SamplingProfiler().Visibility(xaml::Visibility::Collapsed);
}
m_toggleInspectorRevoker = devMenu.Inspector().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
DevSettings::ToggleElementInspector(*(strongThis->m_context));
}
});
m_configBundlerRevoker = devMenu.ConfigBundler().Click(
winrt::auto_revoke,
[wkThis = weak_from_this()](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
React::ReactPropertyBag(strongThis->m_context->Properties()).Get(ConfigureBundlerProperty())();
}
});
// Only show Configure Bundler when connected to a bundler
devMenu.ConfigBundler().Visibility(
(Mso::React::ReactOptions::UseFastRefresh(m_context->Properties()) ||
Mso::React::ReactOptions::UseWebDebugger(m_context->Properties()))
? xaml::Visibility::Visible
: xaml::Visibility::Collapsed);
m_cancelRevoker = devMenu.Cancel().Click(
winrt::auto_revoke, [wkThis = weak_from_this()](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
}
});
m_flyout = xaml::Controls::Flyout{};
m_flyout.Content(devMenu);
if (Is19H1OrHigher()) {
// ShouldConstrainToRootBounds added in 19H1
m_flyout.ShouldConstrainToRootBounds(false);
}
xaml::UIElement root{nullptr};
auto xamlRoot = React::XamlUIService::GetXamlRoot(m_context->Properties());
if (xamlRoot) {
m_flyout.XamlRoot(xamlRoot);
root = xamlRoot.Content();
} else {
auto window = xaml::Window::Current();
root = window.Content();
}
m_flyout.LightDismissOverlayMode(xaml::Controls::LightDismissOverlayMode::On);
devMenu.XYFocusKeyboardNavigation(xaml::Input::XYFocusKeyboardNavigationMode::Enabled);
auto style = xaml::Style(winrt::xaml_typename<xaml::Controls::FlyoutPresenter>());
style.Setters().Append(xaml::Setter(
xaml::Controls::ScrollViewer::HorizontalScrollBarVisibilityProperty(),
winrt::box_value(xaml::Controls::ScrollBarVisibility::Disabled)));
style.Setters().Append(xaml::Setter(
xaml::Controls::ScrollViewer::VerticalScrollBarVisibilityProperty(),
winrt::box_value(xaml::Controls::ScrollBarVisibility::Disabled)));
style.Setters().Append(xaml::Setter(
xaml::Controls::ScrollViewer::HorizontalScrollModeProperty(),
winrt::box_value(xaml::Controls::ScrollMode::Disabled)));
style.Setters().Append(xaml::Setter(
xaml::Controls::ScrollViewer::VerticalScrollModeProperty(),
winrt::box_value(xaml::Controls::ScrollMode::Disabled)));
m_flyout.FlyoutPresenterStyle(style);
m_flyout.ShowAt(root.as<xaml::FrameworkElement>());
// Notify instance that dev menu is shown -- This is used to trigger a connection to dev tools
m_context->CallJSFunction("RCTNativeAppEventEmitter", "emit", folly::dynamic::array("RCTDevMenuShown"));
}