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
2 changes: 2 additions & 0 deletions packages/app/test/pack.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ describe("npm pack", () => {
"windows/UWP/pch.h",
"windows/Win32/AutolinkedNativeModules.g.cpp",
"windows/Win32/AutolinkedNativeModules.g.h",
"windows/Win32/DevMenu.cpp",
"windows/Win32/DevMenu.h",
"windows/Win32/Images/SplashScreen.scale-100.png",
"windows/Win32/Images/SplashScreen.scale-200.png",
"windows/Win32/Images/SplashScreen.scale-400.png",
Expand Down
188 changes: 188 additions & 0 deletions packages/app/windows/Win32/DevMenu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#include "pch.h"

#include "DevMenu.h"

#include <type_traits>

#include "ReactInstance.h"
#include "Session.h"

using ReactApp::Component;
using ReactApp::DevMenu;
using ReactApp::DevMenuCommand;
using ReactTestApp::JSBundleSource;
using ReactTestApp::ReactInstance;
using ReactTestApp::Session;

namespace
{
constexpr wchar_t kLabelLoadFromJSBundle[] = L"Load from &JS bundle";
constexpr wchar_t kLabelLoadFromDevServer[] = L"Load from &dev server";
constexpr wchar_t kLabelRememberLastComponent[] = L"&Remember last opened component";
constexpr wchar_t kLabelReloadJS[] = L"&Reload JavaScript";

constexpr wchar_t kLabelEnableDirectDebugger[] = L"Enable &direct debugging";
constexpr wchar_t kLabelDisableDirectDebugger[] = L"Disable &direct debugging";

constexpr wchar_t kLabelEnableBreakOnFirstLine[] = L"Enable &break on first line";
constexpr wchar_t kLabelDisableBreakOnFirstLine[] = L"Disable &break on first line";

constexpr wchar_t kLabelEnableFastRefresh[] = L"Enable &Fast Refresh";
constexpr wchar_t kLabelDisableFastRefresh[] = L"Disable &Fast Refresh";

constexpr wchar_t kLabelToggleInspector[] = L"Toggle &inspector";

constexpr UINT ItemID(DevMenuCommand cmd)
{
return static_cast<UINT>(cmd);
}

HMENU CreateReactMenu(std::vector<Component> const &components)
{
auto hReactMenu = CreatePopupMenu();
AppendMenuW(hReactMenu, //
MF_STRING,
ItemID(DevMenuCommand::LoadFromBundle),
kLabelLoadFromJSBundle);
AppendMenuW(hReactMenu,
MF_STRING,
ItemID(DevMenuCommand::LoadFromDevServer),
kLabelLoadFromDevServer);

auto rememberLastComponent =
Session::ShouldRememberLastComponent() ? MF_CHECKED : MF_UNCHECKED;
AppendMenuW(hReactMenu,
MF_DISABLED | MF_STRING | rememberLastComponent,
ItemID(DevMenuCommand::RememberLastComponent),
kLabelRememberLastComponent);

if (!components.empty()) {
AppendMenuW(hReactMenu, MF_SEPARATOR, 0, nullptr);
constexpr auto offset = ItemID(DevMenuCommand::ComponentsStart);
std::remove_const_t<decltype(offset)> index = 0;
for (auto const &component : components) {
auto const &title = component.displayName.value_or(component.appKey);
// Add keyboard accelerator for the first nine (1-9) components
auto label = index < 8 ? winrt::to_hstring(title) + L"\tCtrl+Shift+" +
std::to_wstring(index + 1)
: winrt::to_hstring(title);
AppendMenuW(hReactMenu, MF_DISABLED | MF_STRING, offset + (++index), label.c_str());
}
}

return hReactMenu;
}

HMENU CreateDebugMenu(ReactInstance const &instance)
{
auto hDebugMenu = CreatePopupMenu();
AppendMenuW(hDebugMenu, //
MF_STRING,
ItemID(DevMenuCommand::ReloadJS),
kLabelReloadJS);
AppendMenuW(hDebugMenu,
MF_STRING,
ItemID(DevMenuCommand::DirectDebugger),
instance.UseDirectDebugger() ? kLabelDisableDirectDebugger
: kLabelEnableDirectDebugger);
AppendMenuW(hDebugMenu,
MF_STRING,
ItemID(DevMenuCommand::BreakOnFirstLine),
instance.BreakOnFirstLine() ? kLabelDisableBreakOnFirstLine
: kLabelEnableBreakOnFirstLine);
AppendMenuW(hDebugMenu,
MF_STRING,
ItemID(DevMenuCommand::FastRefresh),
instance.UseFastRefresh() ? kLabelDisableFastRefresh : kLabelEnableFastRefresh);
AppendMenuW(hDebugMenu, //
MF_STRING,
ItemID(DevMenuCommand::ToggleInspector),
kLabelToggleInspector);
return hDebugMenu;
}

HMENU CreateDevMenu(ReactInstance const &instance, std::vector<Component> const &components)
{
auto hReactMenu = CreateReactMenu(components);
auto hDebugMenu = CreateDebugMenu(instance);

auto hMenuBar = CreateMenu();
AppendMenuW(hMenuBar, MF_POPUP, reinterpret_cast<UINT_PTR>(hReactMenu), L"&React");
AppendMenuW(hMenuBar, MF_POPUP, reinterpret_cast<UINT_PTR>(hDebugMenu), L"&Debug");

return hMenuBar;
}

void SetMenuItemLabel(HMENU hMenu, DevMenuCommand cmd, LPCWSTR label)
{
MENUITEMINFOW info{.cbSize = sizeof(MENUITEMINFO),
.fMask = MIIM_TYPE,
.dwTypeData = const_cast<LPWSTR>(label)};
SetMenuItemInfoW(hMenu, ItemID(cmd), false, &info);
}
} // namespace

DevMenu::DevMenu(ReactInstance &instance, std::vector<Component> const &components)
: instance_(instance), hMenu_(CreateDevMenu(instance, components))
{
}

void DevMenu::OnCommand(DevMenuCommand cmd) const
{
switch (cmd) {
case DevMenuCommand::LoadFromBundle: {
instance_.LoadJSBundleFrom(JSBundleSource::Embedded);
break;
}
case DevMenuCommand::LoadFromDevServer: {
instance_.LoadJSBundleFrom(JSBundleSource::DevServer);
break;
}
case DevMenuCommand::RememberLastComponent: {
auto rememberLastComponent = !Session::ShouldRememberLastComponent();
Session::ShouldRememberLastComponent(rememberLastComponent);
CheckMenuItem(hMenu_, ItemID(cmd), rememberLastComponent ? MF_CHECKED : MF_UNCHECKED);
break;
}
case DevMenuCommand::ReloadJS: {
instance_.Reload();
break;
}
case DevMenuCommand::DirectDebugger: {
auto useDirectDebugger = !instance_.UseDirectDebugger();
instance_.UseDirectDebugger(useDirectDebugger);
SetMenuItemLabel(hMenu_,
cmd,
useDirectDebugger ? kLabelDisableDirectDebugger
: kLabelEnableDirectDebugger);
break;
}
case DevMenuCommand::BreakOnFirstLine: {
auto breakOnFirstLine = !instance_.BreakOnFirstLine();
instance_.BreakOnFirstLine(breakOnFirstLine);
SetMenuItemLabel(hMenu_,
cmd,
breakOnFirstLine ? kLabelDisableBreakOnFirstLine
: kLabelEnableBreakOnFirstLine);
break;
}
case DevMenuCommand::FastRefresh: {
auto useFastRefresh = !instance_.UseFastRefresh();
instance_.UseFastRefresh(useFastRefresh);
SetMenuItemLabel(hMenu_, //
cmd,
useFastRefresh ? kLabelDisableFastRefresh : kLabelEnableFastRefresh);
break;
}
case DevMenuCommand::ToggleInspector: {
instance_.ToggleElementInspector();
break;
}
default: {
if (cmd > DevMenuCommand::ComponentsStart) {
// TODO
}
break;
}
}
}
51 changes: 51 additions & 0 deletions packages/app/windows/Win32/DevMenu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include <vector>

#include "Manifest.h"

namespace ReactTestApp
{
class ReactInstance;
}

namespace ReactApp
{
enum class DevMenuCommand {
// Custom functions
LoadFromBundle = 1001,
LoadFromDevServer,
RememberLastComponent,

// React Native core functions
ReloadJS = 2001,
DirectDebugger,
BreakOnFirstLine,
FastRefresh,
ToggleInspector,

// User defined components
ComponentsStart = 3000,
};

class DevMenu
{
public:
DevMenu(ReactTestApp::ReactInstance &, std::vector<Component> const &);

HMENU Handle() const
{
return hMenu_;
}

void OnCommand(DevMenuCommand) const;

// DevMenu is non-copyable
DevMenu(DevMenu const &) = delete;
DevMenu &operator=(DevMenu const &) = delete;

private:
ReactTestApp::ReactInstance &instance_;
HMENU hMenu_;
};
} // namespace ReactApp
44 changes: 39 additions & 5 deletions packages/app/windows/Win32/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#include "Main.h"

#include <commctrl.h>

#include "DevMenu.h"
#include "JSValueWriterHelper.h"
#include "Manifest.g.cpp"
#include "ReactInstance.h"
Expand Down Expand Up @@ -40,12 +43,15 @@ namespace
}
} // namespace

_Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE /* instance */,
HINSTANCE,
PSTR /* commandLine */,
int /* showCmd */)
LRESULT APIENTRY SubclassProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

_Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE /* hInstance */,
HINSTANCE /* hPrevInstance */,
PSTR /* lpCmdLine */,
int /* nShowCmd */)
{
auto manifest = ::ReactApp::GetManifest();
auto manifest = ReactApp::GetManifest();
assert(manifest.components.has_value() && (*manifest.components).size() > 0 &&
"At least one component must be declared");

Expand Down Expand Up @@ -91,5 +97,33 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE /* instance */,
window.Title(winrt::to_hstring(manifest.displayName));
window.Resize({600, 800});

#if _DEBUG
ReactApp::DevMenu devMenu{instance, manifest.components.value_or({})};
auto hWnd = GetWindowFromWindowId(window.Id());
SetMenu(hWnd, devMenu.Handle());
SetWindowSubclass(hWnd,
SubclassProc,
reinterpret_cast<UINT_PTR>(&devMenu),
reinterpret_cast<DWORD_PTR>(&devMenu));
#endif // _DEBUG

app.Start();
return 0;
}

LRESULT APIENTRY SubclassProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg) {
case WM_COMMAND: {
auto const &devMenu = *reinterpret_cast<ReactApp::DevMenu *>(dwRefData);
devMenu.OnCommand(static_cast<ReactApp::DevMenuCommand>(LOWORD(wParam)));
return TRUE;
}
case WM_NCDESTROY: {
RemoveWindowSubclass(hWnd, SubclassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
4 changes: 3 additions & 1 deletion packages/app/windows/Win32/ReactApp.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;user32.lib;windowsapp.lib;%(AdditionalDependenices)</AdditionalDependencies>
<AdditionalDependencies>comctl32.lib;shell32.lib;user32.lib;windowsapp.lib;%(AdditionalDependenices)</AdditionalDependencies>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
Expand All @@ -106,6 +106,7 @@
</ItemDefinitionGroup>
<PropertyGroup Label="UserMacros" />
<ItemGroup>
<ClInclude Include="$(ReactAppWin32Dir)\DevMenu.h" />
<ClInclude Include="$(ReactAppSharedDir)\JSValueWriterHelper.h" />
<ClInclude Include="$(ReactAppWin32Dir)\Main.h" />
<ClInclude Include="$(ReactAppSharedDir)\Manifest.h" />
Expand All @@ -117,6 +118,7 @@
<ClInclude Include="$(ReactAppWin32Dir)\targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(ReactAppWin32Dir)\DevMenu.cpp" />
<ClCompile Include="$(ReactAppWin32Dir)\Main.cpp" />
<ClCompile Include="$(ReactAppSharedDir)\ReactInstance.cpp" />
<ClCompile Include="AutolinkedNativeModules.g.cpp" />
Expand Down
Loading