|
| 1 | +#include "pch.h" |
| 2 | + |
| 3 | +#include "DevMenu.h" |
| 4 | + |
| 5 | +#include <type_traits> |
| 6 | + |
| 7 | +#include "ReactInstance.h" |
| 8 | +#include "Session.h" |
| 9 | + |
| 10 | +using ReactApp::Component; |
| 11 | +using ReactApp::DevMenu; |
| 12 | +using ReactApp::DevMenuCommand; |
| 13 | +using ReactTestApp::JSBundleSource; |
| 14 | +using ReactTestApp::ReactInstance; |
| 15 | +using ReactTestApp::Session; |
| 16 | + |
| 17 | +namespace |
| 18 | +{ |
| 19 | + constexpr wchar_t kLabelLoadFromJSBundle[] = L"Load from &JS bundle"; |
| 20 | + constexpr wchar_t kLabelLoadFromDevServer[] = L"Load from &dev server"; |
| 21 | + constexpr wchar_t kLabelRememberLastComponent[] = L"&Remember last opened component"; |
| 22 | + constexpr wchar_t kLabelReloadJS[] = L"&Reload JavaScript"; |
| 23 | + |
| 24 | + constexpr wchar_t kLabelEnableDirectDebugger[] = L"Enable &direct debugging"; |
| 25 | + constexpr wchar_t kLabelDisableDirectDebugger[] = L"Disable &direct debugging"; |
| 26 | + |
| 27 | + constexpr wchar_t kLabelEnableBreakOnFirstLine[] = L"Enable &break on first line"; |
| 28 | + constexpr wchar_t kLabelDisableBreakOnFirstLine[] = L"Disable &break on first line"; |
| 29 | + |
| 30 | + constexpr wchar_t kLabelEnableFastRefresh[] = L"Enable &Fast Refresh"; |
| 31 | + constexpr wchar_t kLabelDisableFastRefresh[] = L"Disable &Fast Refresh"; |
| 32 | + |
| 33 | + constexpr wchar_t kLabelToggleInspector[] = L"Toggle &inspector"; |
| 34 | + |
| 35 | + constexpr UINT ItemID(DevMenuCommand cmd) |
| 36 | + { |
| 37 | + return static_cast<UINT>(cmd); |
| 38 | + } |
| 39 | + |
| 40 | + HMENU CreateReactMenu(std::vector<Component> const &components) |
| 41 | + { |
| 42 | + auto hReactMenu = CreatePopupMenu(); |
| 43 | + AppendMenuW(hReactMenu, // |
| 44 | + MF_STRING, |
| 45 | + ItemID(DevMenuCommand::LoadFromBundle), |
| 46 | + kLabelLoadFromJSBundle); |
| 47 | + AppendMenuW(hReactMenu, |
| 48 | + MF_STRING, |
| 49 | + ItemID(DevMenuCommand::LoadFromDevServer), |
| 50 | + kLabelLoadFromDevServer); |
| 51 | + |
| 52 | + // TODO: We can't easily change components without `ReactNativeWindow` |
| 53 | + // https://github.com/microsoft/react-native-windows/commit/1d80287100b4ebc559cd4d6bce62b7cd03c3cf04 |
| 54 | + if constexpr (REACT_NATIVE_VERSION >= 84000) { |
| 55 | + auto rememberLastComponent = |
| 56 | + Session::ShouldRememberLastComponent() ? MF_CHECKED : MF_UNCHECKED; |
| 57 | + AppendMenuW(hReactMenu, |
| 58 | + MF_DISABLED | MF_STRING | rememberLastComponent, |
| 59 | + ItemID(DevMenuCommand::RememberLastComponent), |
| 60 | + kLabelRememberLastComponent); |
| 61 | + |
| 62 | + if (!components.empty()) { |
| 63 | + AppendMenuW(hReactMenu, MF_SEPARATOR, 0, nullptr); |
| 64 | + constexpr auto offset = ItemID(DevMenuCommand::ComponentsStart); |
| 65 | + std::remove_const_t<decltype(offset)> index = 0; |
| 66 | + for (auto const &component : components) { |
| 67 | + auto const &title = component.displayName.value_or(component.appKey); |
| 68 | + // Add keyboard accelerator for the first nine (1-9) components |
| 69 | + auto label = index < 8 ? winrt::to_hstring(title) + L"\tCtrl+Shift+" + |
| 70 | + std::to_wstring(index + 1) |
| 71 | + : winrt::to_hstring(title); |
| 72 | + AppendMenuW( |
| 73 | + hReactMenu, MF_DISABLED | MF_STRING, offset + (++index), label.c_str()); |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + return hReactMenu; |
| 79 | + } |
| 80 | + |
| 81 | + HMENU CreateDebugMenu(ReactInstance const &instance) |
| 82 | + { |
| 83 | + auto hDebugMenu = CreatePopupMenu(); |
| 84 | + AppendMenuW(hDebugMenu, // |
| 85 | + MF_STRING, |
| 86 | + ItemID(DevMenuCommand::ReloadJS), |
| 87 | + kLabelReloadJS); |
| 88 | + AppendMenuW(hDebugMenu, |
| 89 | + MF_STRING, |
| 90 | + ItemID(DevMenuCommand::DirectDebugger), |
| 91 | + instance.UseDirectDebugger() ? kLabelDisableDirectDebugger |
| 92 | + : kLabelEnableDirectDebugger); |
| 93 | + AppendMenuW(hDebugMenu, |
| 94 | + MF_STRING, |
| 95 | + ItemID(DevMenuCommand::BreakOnFirstLine), |
| 96 | + instance.BreakOnFirstLine() ? kLabelDisableBreakOnFirstLine |
| 97 | + : kLabelEnableBreakOnFirstLine); |
| 98 | + AppendMenuW(hDebugMenu, |
| 99 | + MF_STRING, |
| 100 | + ItemID(DevMenuCommand::FastRefresh), |
| 101 | + instance.UseFastRefresh() ? kLabelDisableFastRefresh : kLabelEnableFastRefresh); |
| 102 | + AppendMenuW(hDebugMenu, // |
| 103 | + MF_STRING, |
| 104 | + ItemID(DevMenuCommand::ToggleInspector), |
| 105 | + kLabelToggleInspector); |
| 106 | + return hDebugMenu; |
| 107 | + } |
| 108 | + |
| 109 | + HMENU CreateDevMenu(ReactInstance const &instance, std::vector<Component> const &components) |
| 110 | + { |
| 111 | + auto hReactMenu = CreateReactMenu(components); |
| 112 | + auto hDebugMenu = CreateDebugMenu(instance); |
| 113 | + |
| 114 | + auto hMenuBar = CreateMenu(); |
| 115 | + AppendMenuW(hMenuBar, MF_POPUP, reinterpret_cast<UINT_PTR>(hReactMenu), L"&React"); |
| 116 | + AppendMenuW(hMenuBar, MF_POPUP, reinterpret_cast<UINT_PTR>(hDebugMenu), L"&Debug"); |
| 117 | + |
| 118 | + return hMenuBar; |
| 119 | + } |
| 120 | + |
| 121 | + void SetMenuItemLabel(HMENU hMenu, DevMenuCommand cmd, LPCWSTR label) |
| 122 | + { |
| 123 | + MENUITEMINFOW info{.cbSize = sizeof(MENUITEMINFO), |
| 124 | + .fMask = MIIM_TYPE, |
| 125 | + .dwTypeData = const_cast<LPWSTR>(label)}; |
| 126 | + SetMenuItemInfoW(hMenu, ItemID(cmd), false, &info); |
| 127 | + } |
| 128 | +} // namespace |
| 129 | + |
| 130 | +DevMenu::DevMenu(ReactInstance &instance, std::vector<Component> const &components) |
| 131 | + : instance_(instance), hMenu_(CreateDevMenu(instance, components)) |
| 132 | +{ |
| 133 | +} |
| 134 | + |
| 135 | +void DevMenu::OnCommand(DevMenuCommand cmd) const |
| 136 | +{ |
| 137 | + switch (cmd) { |
| 138 | + case DevMenuCommand::LoadFromBundle: { |
| 139 | + instance_.LoadJSBundleFrom(JSBundleSource::Embedded); |
| 140 | + break; |
| 141 | + } |
| 142 | + case DevMenuCommand::LoadFromDevServer: { |
| 143 | + instance_.LoadJSBundleFrom(JSBundleSource::DevServer); |
| 144 | + break; |
| 145 | + } |
| 146 | + case DevMenuCommand::RememberLastComponent: { |
| 147 | + auto rememberLastComponent = !Session::ShouldRememberLastComponent(); |
| 148 | + Session::ShouldRememberLastComponent(rememberLastComponent); |
| 149 | + CheckMenuItem(hMenu_, ItemID(cmd), rememberLastComponent ? MF_CHECKED : MF_UNCHECKED); |
| 150 | + break; |
| 151 | + } |
| 152 | + case DevMenuCommand::ReloadJS: { |
| 153 | + instance_.Reload(); |
| 154 | + break; |
| 155 | + } |
| 156 | + case DevMenuCommand::DirectDebugger: { |
| 157 | + auto useDirectDebugger = !instance_.UseDirectDebugger(); |
| 158 | + instance_.UseDirectDebugger(useDirectDebugger); |
| 159 | + SetMenuItemLabel(hMenu_, |
| 160 | + cmd, |
| 161 | + useDirectDebugger ? kLabelDisableDirectDebugger |
| 162 | + : kLabelEnableDirectDebugger); |
| 163 | + break; |
| 164 | + } |
| 165 | + case DevMenuCommand::BreakOnFirstLine: { |
| 166 | + auto breakOnFirstLine = !instance_.BreakOnFirstLine(); |
| 167 | + instance_.BreakOnFirstLine(breakOnFirstLine); |
| 168 | + SetMenuItemLabel(hMenu_, |
| 169 | + cmd, |
| 170 | + breakOnFirstLine ? kLabelDisableBreakOnFirstLine |
| 171 | + : kLabelEnableBreakOnFirstLine); |
| 172 | + break; |
| 173 | + } |
| 174 | + case DevMenuCommand::FastRefresh: { |
| 175 | + auto useFastRefresh = !instance_.UseFastRefresh(); |
| 176 | + instance_.UseFastRefresh(useFastRefresh); |
| 177 | + SetMenuItemLabel(hMenu_, // |
| 178 | + cmd, |
| 179 | + useFastRefresh ? kLabelDisableFastRefresh : kLabelEnableFastRefresh); |
| 180 | + break; |
| 181 | + } |
| 182 | + case DevMenuCommand::ToggleInspector: { |
| 183 | + instance_.ToggleElementInspector(); |
| 184 | + break; |
| 185 | + } |
| 186 | + default: { |
| 187 | + if (cmd > DevMenuCommand::ComponentsStart) { |
| 188 | + // TODO |
| 189 | + } |
| 190 | + break; |
| 191 | + } |
| 192 | + } |
| 193 | +} |
0 commit comments