react-native-windows 0.76.0-preview.2 → 0.76.0-preview.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Microsoft.ReactNative/Fabric/ComponentView.cpp +12 -0
- package/Microsoft.ReactNative/Fabric/ComponentView.h +3 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +66 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h +8 -2
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +24 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +2 -0
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +8 -3
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +1 -0
- package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +11 -0
- package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +2 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextDrawing.cpp +1 -2
- package/Microsoft.ReactNative/Fabric/Composition/TextDrawing.h +1 -1
- package/Microsoft.ReactNative/Fabric/Composition/Theme.cpp +12 -4
- package/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp +338 -0
- package/Microsoft.ReactNative/Fabric/Composition/TooltipService.h +66 -0
- package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp +8 -0
- package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h +1 -0
- package/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +3 -0
- package/Microsoft.ReactNative/Fabric/ReactTaggedView.h +4 -0
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.cpp +5 -0
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h +1 -1
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewTraitsInitializer.h +1 -1
- package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +1 -1
- package/Microsoft.ReactNative/ReactCoreInjection.h +0 -1
- package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +15 -4
- package/PropertySheets/Generated/PackageVersion.g.props +2 -2
- package/Shared/Shared.vcxitems +3 -0
- package/Shared/Shared.vcxitems.filters +1 -0
- package/package.json +13 -13
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <Fabric/Composition/RootComponentView.h>
|
|
15
15
|
#include "AbiEventEmitter.h"
|
|
16
16
|
#include "AbiShadowNode.h"
|
|
17
|
+
#include "ReactCoreInjection.h"
|
|
17
18
|
|
|
18
19
|
namespace winrt::Microsoft::ReactNative::Composition::implementation {
|
|
19
20
|
struct RootComponentView;
|
|
@@ -262,6 +263,17 @@ void ComponentView::HandleCommand(const winrt::Microsoft::ReactNative::HandleCom
|
|
|
262
263
|
}
|
|
263
264
|
}
|
|
264
265
|
|
|
266
|
+
HWND ComponentView::GetHwndForParenting() noexcept {
|
|
267
|
+
if (m_parent) {
|
|
268
|
+
return winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_parent)
|
|
269
|
+
->GetHwndForParenting();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Fallback if we do not know any more specific HWND
|
|
273
|
+
return reinterpret_cast<HWND>(winrt::Microsoft::ReactNative::implementation::ReactCoreInjection::GetTopLevelWindowId(
|
|
274
|
+
m_reactContext.Properties().Handle()));
|
|
275
|
+
}
|
|
276
|
+
|
|
265
277
|
winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *ComponentView::rootComponentView()
|
|
266
278
|
const noexcept {
|
|
267
279
|
if (m_rootView)
|
|
@@ -209,6 +209,9 @@ struct ComponentView : public ComponentViewT<ComponentView> {
|
|
|
209
209
|
// Notify up the tree to bring the rect into view by scrolling as needed
|
|
210
210
|
virtual void StartBringIntoView(BringIntoViewOptions &&args) noexcept;
|
|
211
211
|
|
|
212
|
+
// Eventually PopupContentLink and similar APIs will remove the need for this.
|
|
213
|
+
virtual HWND GetHwndForParenting() noexcept;
|
|
214
|
+
|
|
212
215
|
virtual const winrt::Microsoft::ReactNative::IComponentProps userProps(
|
|
213
216
|
facebook::react::Props::Shared const &props) noexcept;
|
|
214
217
|
|
|
@@ -129,6 +129,18 @@ bool accessibilityValueHasValue(const facebook::react::AccessibilityValue &value
|
|
|
129
129
|
return (value.min.has_value() && value.max.has_value()) || value.now.has_value() || value.text.has_value();
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
bool expandableControl(const facebook::react::SharedViewProps props) {
|
|
133
|
+
if (props->accessibilityState.has_value() && props->accessibilityState->expanded.has_value())
|
|
134
|
+
return true;
|
|
135
|
+
auto accessibilityActions = props->accessibilityActions;
|
|
136
|
+
for (size_t i = 0; i < accessibilityActions.size(); i++) {
|
|
137
|
+
if (accessibilityActions[i].name == "expand" || accessibilityActions[i].name == "collapse") {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
132
144
|
HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal) {
|
|
133
145
|
if (pRetVal == nullptr)
|
|
134
146
|
return E_POINTER;
|
|
@@ -181,6 +193,15 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE
|
|
|
181
193
|
AddRef();
|
|
182
194
|
}
|
|
183
195
|
|
|
196
|
+
if (patternId == UIA_ExpandCollapsePatternId &&
|
|
197
|
+
(accessibilityRole == "combobox" || accessibilityRole == "splitbutton" || accessibilityRole == "treeitem" ||
|
|
198
|
+
(expandableControl(props) &&
|
|
199
|
+
(accessibilityRole == "toolbar" || accessibilityRole == "menuitem" || accessibilityRole == "menubar" ||
|
|
200
|
+
accessibilityRole == "listitem" || accessibilityRole == "group" || accessibilityRole == "button")))) {
|
|
201
|
+
*pRetVal = static_cast<IExpandCollapseProvider *>(this);
|
|
202
|
+
AddRef();
|
|
203
|
+
}
|
|
204
|
+
|
|
184
205
|
return S_OK;
|
|
185
206
|
}
|
|
186
207
|
|
|
@@ -349,6 +370,13 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERT
|
|
|
349
370
|
pRetVal->lVal = GetLiveSetting(props->accessibilityLiveRegion);
|
|
350
371
|
break;
|
|
351
372
|
}
|
|
373
|
+
case UIA_ItemStatusPropertyId: {
|
|
374
|
+
pRetVal->vt = VT_BSTR;
|
|
375
|
+
pRetVal->bstrVal = (props->accessibilityState.has_value() && props->accessibilityState->busy)
|
|
376
|
+
? SysAllocString(L"Busy")
|
|
377
|
+
: SysAllocString(L"");
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
352
380
|
}
|
|
353
381
|
|
|
354
382
|
return hr;
|
|
@@ -496,4 +524,42 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::Toggle() {
|
|
|
496
524
|
return S_OK;
|
|
497
525
|
}
|
|
498
526
|
|
|
527
|
+
HRESULT __stdcall CompositionDynamicAutomationProvider::get_ExpandCollapseState(ExpandCollapseState *pRetVal) {
|
|
528
|
+
if (pRetVal == nullptr)
|
|
529
|
+
return E_POINTER;
|
|
530
|
+
auto strongView = m_view.view();
|
|
531
|
+
|
|
532
|
+
if (!strongView)
|
|
533
|
+
return UIA_E_ELEMENTNOTAVAILABLE;
|
|
534
|
+
|
|
535
|
+
auto props = std::static_pointer_cast<const facebook::react::ViewProps>(
|
|
536
|
+
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(strongView)->props());
|
|
537
|
+
|
|
538
|
+
if (props == nullptr)
|
|
539
|
+
return UIA_E_ELEMENTNOTAVAILABLE;
|
|
540
|
+
|
|
541
|
+
*pRetVal = props->accessibilityState->expanded.has_value()
|
|
542
|
+
? GetExpandCollapseState(props->accessibilityState->expanded.value())
|
|
543
|
+
: ExpandCollapseState_Collapsed;
|
|
544
|
+
return S_OK;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
HRESULT __stdcall CompositionDynamicAutomationProvider::Expand() {
|
|
548
|
+
auto strongView = m_view.view();
|
|
549
|
+
|
|
550
|
+
if (!strongView)
|
|
551
|
+
return UIA_E_ELEMENTNOTAVAILABLE;
|
|
552
|
+
DispatchAccessibilityAction(m_view, "expand");
|
|
553
|
+
return S_OK;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
HRESULT __stdcall CompositionDynamicAutomationProvider::Collapse() {
|
|
557
|
+
auto strongView = m_view.view();
|
|
558
|
+
|
|
559
|
+
if (!strongView)
|
|
560
|
+
return UIA_E_ELEMENTNOTAVAILABLE;
|
|
561
|
+
DispatchAccessibilityAction(m_view, "collapse");
|
|
562
|
+
return S_OK;
|
|
563
|
+
}
|
|
564
|
+
|
|
499
565
|
} // namespace winrt::Microsoft::ReactNative::implementation
|
|
@@ -16,7 +16,8 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
|
|
|
16
16
|
IInvokeProvider,
|
|
17
17
|
IScrollItemProvider,
|
|
18
18
|
IValueProvider,
|
|
19
|
-
IToggleProvider
|
|
19
|
+
IToggleProvider,
|
|
20
|
+
IExpandCollapseProvider> {
|
|
20
21
|
public:
|
|
21
22
|
CompositionDynamicAutomationProvider(
|
|
22
23
|
const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView) noexcept;
|
|
@@ -47,10 +48,15 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
|
|
|
47
48
|
virtual HRESULT __stdcall get_Value(BSTR *pRetVal) override;
|
|
48
49
|
virtual HRESULT __stdcall get_IsReadOnly(BOOL *pRetVal) override;
|
|
49
50
|
|
|
50
|
-
// inherited via
|
|
51
|
+
// inherited via IToggleProvider
|
|
51
52
|
virtual HRESULT __stdcall get_ToggleState(ToggleState *pRetVal) override;
|
|
52
53
|
virtual HRESULT __stdcall Toggle() override;
|
|
53
54
|
|
|
55
|
+
// inherited via IExpandCollapseProvider
|
|
56
|
+
virtual HRESULT __stdcall get_ExpandCollapseState(ExpandCollapseState *pRetVal) override;
|
|
57
|
+
virtual HRESULT __stdcall Expand() override;
|
|
58
|
+
virtual HRESULT __stdcall Collapse() override;
|
|
59
|
+
|
|
54
60
|
private:
|
|
55
61
|
::Microsoft::ReactNative::ReactTaggedView m_view;
|
|
56
62
|
};
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
#include "CompositionHelpers.h"
|
|
24
24
|
#include "RootComponentView.h"
|
|
25
25
|
#include "Theme.h"
|
|
26
|
+
#include "TooltipService.h"
|
|
26
27
|
#include "UiaHelpers.h"
|
|
27
28
|
#include "d2d1helper.h"
|
|
28
29
|
|
|
@@ -43,6 +44,13 @@ ComponentView::ComponentView(
|
|
|
43
44
|
m_outerVisual.InsertAt(m_focusVisual.InnerVisual(), 0);
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
ComponentView::~ComponentView() {
|
|
48
|
+
if (m_tooltipTracked) {
|
|
49
|
+
TooltipService::GetCurrent(m_reactContext.Properties())->StopTracking(*this);
|
|
50
|
+
m_tooltipTracked = false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
facebook::react::Tag ComponentView::Tag() const noexcept {
|
|
47
55
|
return m_tag;
|
|
48
56
|
}
|
|
@@ -130,6 +138,16 @@ void ComponentView::updateProps(
|
|
|
130
138
|
updateShadowProps(oldViewProps, newViewProps);
|
|
131
139
|
}
|
|
132
140
|
|
|
141
|
+
if (oldViewProps.tooltip != newViewProps.tooltip) {
|
|
142
|
+
if (!m_tooltipTracked && newViewProps.tooltip) {
|
|
143
|
+
TooltipService::GetCurrent(m_reactContext.Properties())->StartTracking(*this);
|
|
144
|
+
m_tooltipTracked = true;
|
|
145
|
+
} else if (m_tooltipTracked && !newViewProps.tooltip) {
|
|
146
|
+
TooltipService::GetCurrent(m_reactContext.Properties())->StopTracking(*this);
|
|
147
|
+
m_tooltipTracked = false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
base_type::updateProps(props, oldProps);
|
|
134
152
|
}
|
|
135
153
|
|
|
@@ -1303,6 +1321,12 @@ void ComponentView::updateAccessibilityProps(
|
|
|
1303
1321
|
!(oldViewProps.accessibilityState && oldViewProps.accessibilityState->disabled),
|
|
1304
1322
|
!(newViewProps.accessibilityState && newViewProps.accessibilityState->disabled));
|
|
1305
1323
|
|
|
1324
|
+
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
|
|
1325
|
+
m_uiaProvider,
|
|
1326
|
+
UIA_IsEnabledPropertyId,
|
|
1327
|
+
!(oldViewProps.accessibilityState && oldViewProps.accessibilityState->busy),
|
|
1328
|
+
!(newViewProps.accessibilityState && newViewProps.accessibilityState->busy));
|
|
1329
|
+
|
|
1306
1330
|
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
|
|
1307
1331
|
m_uiaProvider, UIA_ControlTypePropertyId, oldViewProps.accessibilityRole, newViewProps.accessibilityRole);
|
|
1308
1332
|
|
|
@@ -30,6 +30,7 @@ struct ComponentView : public ComponentViewT<
|
|
|
30
30
|
facebook::react::Tag tag,
|
|
31
31
|
winrt::Microsoft::ReactNative::ReactContext const &reactContext,
|
|
32
32
|
ComponentViewFeatures flags);
|
|
33
|
+
virtual ~ComponentView();
|
|
33
34
|
|
|
34
35
|
virtual winrt::Microsoft::ReactNative::Composition::Experimental::IVisual Visual() const noexcept {
|
|
35
36
|
return nullptr;
|
|
@@ -151,6 +152,7 @@ struct ComponentView : public ComponentViewT<
|
|
|
151
152
|
const facebook::react::ViewProps &viewProps) noexcept;
|
|
152
153
|
|
|
153
154
|
bool m_FinalizeTransform{false};
|
|
155
|
+
bool m_tooltipTracked{false};
|
|
154
156
|
ComponentViewFeatures m_flags;
|
|
155
157
|
void showFocusVisual(bool show) noexcept;
|
|
156
158
|
winrt::Microsoft::ReactNative::Composition::Experimental::IFocusVisual m_focusVisual{nullptr};
|
|
@@ -157,6 +157,7 @@ void ReactNativeIsland::ReactViewHost(winrt::Microsoft::ReactNative::IReactViewH
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
if (m_reactViewHost) {
|
|
160
|
+
UninitRootView();
|
|
160
161
|
m_reactViewHost.DetachViewInstance();
|
|
161
162
|
}
|
|
162
163
|
|
|
@@ -332,7 +333,7 @@ winrt::IInspectable ReactNativeIsland::GetUiaProvider() noexcept {
|
|
|
332
333
|
if (m_uiaProvider == nullptr) {
|
|
333
334
|
m_uiaProvider =
|
|
334
335
|
winrt::make<winrt::Microsoft::ReactNative::implementation::CompositionRootAutomationProvider>(*this);
|
|
335
|
-
if (m_hwnd) {
|
|
336
|
+
if (m_hwnd && !m_island) {
|
|
336
337
|
auto pRootProvider =
|
|
337
338
|
static_cast<winrt::Microsoft::ReactNative::implementation::CompositionRootAutomationProvider *>(
|
|
338
339
|
m_uiaProvider.as<IRawElementProviderSimple>().get());
|
|
@@ -348,6 +349,10 @@ void ReactNativeIsland::SetWindow(uint64_t hwnd) noexcept {
|
|
|
348
349
|
m_hwnd = reinterpret_cast<HWND>(hwnd);
|
|
349
350
|
}
|
|
350
351
|
|
|
352
|
+
HWND ReactNativeIsland::GetHwndForParenting() noexcept {
|
|
353
|
+
return m_hwnd;
|
|
354
|
+
}
|
|
355
|
+
|
|
351
356
|
int64_t ReactNativeIsland::SendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept {
|
|
352
357
|
if (m_rootTag == -1)
|
|
353
358
|
return 0;
|
|
@@ -367,7 +372,7 @@ int64_t ReactNativeIsland::SendMessage(uint32_t msg, uint64_t wParam, int64_t lP
|
|
|
367
372
|
bool ReactNativeIsland::CapturePointer(
|
|
368
373
|
const winrt::Microsoft::ReactNative::Composition::Input::Pointer &pointer,
|
|
369
374
|
facebook::react::Tag tag) noexcept {
|
|
370
|
-
if (m_hwnd) {
|
|
375
|
+
if (m_hwnd && !m_island) {
|
|
371
376
|
SetCapture(m_hwnd);
|
|
372
377
|
}
|
|
373
378
|
return m_CompositionEventHandler->CapturePointer(pointer, tag);
|
|
@@ -377,7 +382,7 @@ void ReactNativeIsland::ReleasePointerCapture(
|
|
|
377
382
|
const winrt::Microsoft::ReactNative::Composition::Input::Pointer &pointer,
|
|
378
383
|
facebook::react::Tag tag) noexcept {
|
|
379
384
|
if (m_CompositionEventHandler->ReleasePointerCapture(pointer, tag)) {
|
|
380
|
-
if (m_hwnd) {
|
|
385
|
+
if (m_hwnd && !m_island) {
|
|
381
386
|
if (m_hwnd == GetCapture()) {
|
|
382
387
|
ReleaseCapture();
|
|
383
388
|
}
|
|
@@ -82,6 +82,7 @@ struct ReactNativeIsland
|
|
|
82
82
|
void AddRenderedVisual(const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept;
|
|
83
83
|
void RemoveRenderedVisual(const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept;
|
|
84
84
|
bool TrySetFocus() noexcept;
|
|
85
|
+
HWND GetHwndForParenting() noexcept;
|
|
85
86
|
|
|
86
87
|
winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader Resources() noexcept;
|
|
87
88
|
void Resources(const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept;
|
|
@@ -231,4 +231,15 @@ winrt::Microsoft::ReactNative::implementation::ClipState RootComponentView::getC
|
|
|
231
231
|
return winrt::Microsoft::ReactNative::implementation::ClipState::NoClip;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
HWND RootComponentView::GetHwndForParenting() noexcept {
|
|
235
|
+
if (auto rootView = m_wkRootView.get()) {
|
|
236
|
+
auto hwnd = winrt::get_self<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>(rootView)
|
|
237
|
+
->GetHwndForParenting();
|
|
238
|
+
if (hwnd)
|
|
239
|
+
return hwnd;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return base_type::GetHwndForParenting();
|
|
243
|
+
}
|
|
244
|
+
|
|
234
245
|
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
|
|
@@ -64,6 +64,8 @@ struct RootComponentView : RootComponentViewT<RootComponentView, ViewComponentVi
|
|
|
64
64
|
winrt::Microsoft::ReactNative::ComponentView FindFirstFocusableElement() noexcept;
|
|
65
65
|
winrt::Microsoft::ReactNative::ComponentView FindLastFocusableElement() noexcept;
|
|
66
66
|
|
|
67
|
+
HWND GetHwndForParenting() noexcept override;
|
|
68
|
+
|
|
67
69
|
private:
|
|
68
70
|
// should this be a ReactTaggedView? - It shouldn't actually matter since if the view is going away it should always
|
|
69
71
|
// be clearing its focus But being a reactTaggedView might make it easier to identify cases where that isn't
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
namespace winrt::Microsoft::ReactNative::Composition {
|
|
17
17
|
|
|
18
18
|
void RenderText(
|
|
19
|
-
|
|
19
|
+
ID2D1RenderTarget &deviceContext,
|
|
20
20
|
::IDWriteTextLayout &textLayout,
|
|
21
21
|
const facebook::react::AttributedString &attributedString,
|
|
22
22
|
const facebook::react::TextAttributes &textAttributes,
|
|
@@ -26,7 +26,6 @@ void RenderText(
|
|
|
26
26
|
float offsetX = offset.x / pointScaleFactor;
|
|
27
27
|
float offsetY = offset.y / pointScaleFactor;
|
|
28
28
|
|
|
29
|
-
assert(deviceContext.GetUnitMode() == D2D1_UNIT_MODE_DIPS);
|
|
30
29
|
const auto dpi = pointScaleFactor * 96.0f;
|
|
31
30
|
float oldDpiX, oldDpiY;
|
|
32
31
|
deviceContext.GetDpi(&oldDpiX, &oldDpiY);
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
namespace winrt::Microsoft::ReactNative::Composition {
|
|
15
15
|
|
|
16
16
|
void RenderText(
|
|
17
|
-
|
|
17
|
+
ID2D1RenderTarget &deviceContext,
|
|
18
18
|
::IDWriteTextLayout &textLayout,
|
|
19
19
|
const facebook::react::AttributedString &attributedString,
|
|
20
20
|
const facebook::react::TextAttributes &textAttributes,
|
|
@@ -296,7 +296,9 @@ bool Theme::TryGetPlatformColor(const std::string &platformColor, winrt::Windows
|
|
|
296
296
|
{"ScrollBarThumbFillDisabled", "ControlStrongFillColorDisabled"},
|
|
297
297
|
{"ScrollBarTrackFill",
|
|
298
298
|
"AcrylicInAppFillColorDefault"}, // TODO make AcrylicInAppFillColorDefault a real acrylic brush
|
|
299
|
-
|
|
299
|
+
{"ToolTipBackground", "SystemChromeMediumLowColor"},
|
|
300
|
+
{"ToolTipForeground", "SystemControlForegroundBaseHighColor"},
|
|
301
|
+
{"ToolTipBorderBrush", "SystemControlTransientBorderColor"}};
|
|
300
302
|
|
|
301
303
|
static std::unordered_map<std::string, winrt::Windows::UI::Color, std::hash<std::string_view>, std::equal_to<>>
|
|
302
304
|
s_lightColors = {
|
|
@@ -326,7 +328,9 @@ bool Theme::TryGetPlatformColor(const std::string &platformColor, winrt::Windows
|
|
|
326
328
|
{"ControlStrongFillColorDefault", {0x72, 0x00, 0x00, 0x00}},
|
|
327
329
|
{"ControlStrongFillColorDisabled", {0x51, 0x00, 0x00, 0x00}},
|
|
328
330
|
{"AcrylicInAppFillColorDefault", {0x9E, 0xFF, 0xFF, 0xFF}},
|
|
329
|
-
|
|
331
|
+
{"SystemChromeMediumLowColor", {0xFF, 0xF2, 0xF2, 0xF2}},
|
|
332
|
+
{"SystemControlForegroundBaseHighColor", {0xFF, 0x00, 0x00, 0x00}},
|
|
333
|
+
{"SystemControlTransientBorderColor", {0x24, 0x00, 0x00, 0x00}}};
|
|
330
334
|
|
|
331
335
|
static std::unordered_map<std::string, winrt::Windows::UI::Color, std::hash<std::string_view>, std::equal_to<>>
|
|
332
336
|
s_darkColors = {
|
|
@@ -356,7 +360,9 @@ bool Theme::TryGetPlatformColor(const std::string &platformColor, winrt::Windows
|
|
|
356
360
|
{"ControlStrongFillColorDefault", {0x8B, 0xFF, 0xFF, 0xFF}},
|
|
357
361
|
{"ControlStrongFillColorDisabled", {0x3F, 0xFF, 0xFF, 0xFF}},
|
|
358
362
|
{"AcrylicInAppFillColorDefault", {0x9E, 0x00, 0x00, 0x00}},
|
|
359
|
-
|
|
363
|
+
{"SystemChromeMediumLowColor", {0xFF, 0x2B, 0x2B, 0x2B}},
|
|
364
|
+
{"SystemControlForegroundBaseHighColor", {0xFF, 0xFF, 0xFF, 0xFF}},
|
|
365
|
+
{"SystemControlTransientBorderColor", {0x5C, 0x00, 0x00, 0x00}}};
|
|
360
366
|
|
|
361
367
|
static std::unordered_map<
|
|
362
368
|
std::string,
|
|
@@ -391,7 +397,9 @@ bool Theme::TryGetPlatformColor(const std::string &platformColor, winrt::Windows
|
|
|
391
397
|
{"SubtleFillColorSecondary", {winrt::Windows::UI::ViewManagement::UIElementType::ButtonFace, {}}},
|
|
392
398
|
{"ControlStrongFillColorDefault", {winrt::Windows::UI::ViewManagement::UIElementType::ButtonFace, {}}},
|
|
393
399
|
{"ControlStrongFillColorDisabled", {winrt::Windows::UI::ViewManagement::UIElementType::ButtonFace, {}}},
|
|
394
|
-
|
|
400
|
+
{"SystemChromeMediumLowColor", {winrt::Windows::UI::ViewManagement::UIElementType::ButtonFace, {}}},
|
|
401
|
+
{"SystemControlForegroundBaseHighColor", {winrt::Windows::UI::ViewManagement::UIElementType::ButtonText, {}}},
|
|
402
|
+
{"SystemControlTransientBorderColor", {winrt::Windows::UI::ViewManagement::UIElementType::ButtonText, {}}}};
|
|
395
403
|
|
|
396
404
|
auto alias = s_xamlAliasedColors.find(platformColor);
|
|
397
405
|
if (alias != s_xamlAliasedColors.end()) {
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
|
|
5
|
+
#include "TooltipService.h"
|
|
6
|
+
|
|
7
|
+
#include <CompositionSwitcher.Experimental.interop.h>
|
|
8
|
+
#include <Fabric/Composition/CompositionViewComponentView.h>
|
|
9
|
+
#include <react/renderer/attributedstring/AttributedStringBox.h>
|
|
10
|
+
#include <react/renderer/core/LayoutConstraints.h>
|
|
11
|
+
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
|
|
12
|
+
#include <winrt/Microsoft.ReactNative.Composition.h>
|
|
13
|
+
#include "TextDrawing.h"
|
|
14
|
+
#include "dwmapi.h"
|
|
15
|
+
|
|
16
|
+
namespace winrt::Microsoft::ReactNative {
|
|
17
|
+
|
|
18
|
+
constexpr PCWSTR c_tooltipWindowClassName = L"RN_TOOLTIP";
|
|
19
|
+
constexpr auto TooltipDataProperty = L"TooltipData";
|
|
20
|
+
constexpr float tooltipFontSize = 12;
|
|
21
|
+
constexpr float tooltipMaxHeight = 1000;
|
|
22
|
+
constexpr float tooltipMaxWidth = 320;
|
|
23
|
+
constexpr float tooltipHorizontalPadding = 8;
|
|
24
|
+
constexpr float tooltipTopPadding = 5;
|
|
25
|
+
constexpr float tooltipBottomPadding = 7;
|
|
26
|
+
constexpr float toolTipBorderThickness = 1;
|
|
27
|
+
constexpr int toolTipPlacementMargin = 12;
|
|
28
|
+
constexpr int toolTipAnimationTimeMs = 200;
|
|
29
|
+
constexpr int toolTipTimeToShowMs = 1000;
|
|
30
|
+
|
|
31
|
+
struct TooltipData {
|
|
32
|
+
TooltipData(const winrt::Microsoft::ReactNative::ComponentView &view) : view(view) {}
|
|
33
|
+
|
|
34
|
+
static TooltipData *GetFromWindow(HWND hwnd) {
|
|
35
|
+
auto data = reinterpret_cast<TooltipData *>(GetProp(hwnd, TooltipDataProperty));
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
int width;
|
|
40
|
+
int height;
|
|
41
|
+
::Microsoft::ReactNative::ReactTaggedView view;
|
|
42
|
+
winrt::com_ptr<::IDWriteTextLayout> textLayout;
|
|
43
|
+
facebook::react::AttributedStringBox attributedString;
|
|
44
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext compositionContext;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
facebook::react::AttributedStringBox CreateTooltipAttributedString(const std::string &tooltip) noexcept {
|
|
48
|
+
auto attributedString = facebook::react::AttributedString{};
|
|
49
|
+
auto fragment = facebook::react::AttributedString::Fragment{};
|
|
50
|
+
fragment.string = tooltip;
|
|
51
|
+
fragment.textAttributes.fontSize = tooltipFontSize;
|
|
52
|
+
attributedString.appendFragment(fragment);
|
|
53
|
+
return facebook::react::AttributedStringBox{attributedString};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
LRESULT CALLBACK TooltipWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept {
|
|
57
|
+
switch (message) {
|
|
58
|
+
case WM_DESTROY: {
|
|
59
|
+
delete TooltipData::GetFromWindow(hwnd);
|
|
60
|
+
SetProp(hwnd, TooltipDataProperty, 0);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
case WM_PRINTCLIENT:
|
|
64
|
+
case WM_PAINT: {
|
|
65
|
+
HDC hdc;
|
|
66
|
+
PAINTSTRUCT ps;
|
|
67
|
+
|
|
68
|
+
if (message != WM_PRINTCLIENT)
|
|
69
|
+
hdc = BeginPaint(hwnd, &ps);
|
|
70
|
+
else
|
|
71
|
+
hdc = (HDC)wparam;
|
|
72
|
+
auto data = TooltipData::GetFromWindow(hwnd);
|
|
73
|
+
|
|
74
|
+
if (auto view = data->view.view()) {
|
|
75
|
+
auto scaleFactor = view.LayoutMetrics().PointScaleFactor;
|
|
76
|
+
|
|
77
|
+
auto ccInterop = data->compositionContext
|
|
78
|
+
.as<::Microsoft::ReactNative::Composition::Experimental::ICompositionContextInterop>();
|
|
79
|
+
winrt::com_ptr<ID2D1Factory1> factory;
|
|
80
|
+
ccInterop->D2DFactory(factory.put());
|
|
81
|
+
|
|
82
|
+
winrt::com_ptr<ID2D1DCRenderTarget> renderTarget;
|
|
83
|
+
|
|
84
|
+
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
|
|
85
|
+
D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
|
86
|
+
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
|
|
87
|
+
0,
|
|
88
|
+
0,
|
|
89
|
+
D2D1_RENDER_TARGET_USAGE_NONE,
|
|
90
|
+
D2D1_FEATURE_LEVEL_DEFAULT);
|
|
91
|
+
winrt::check_hresult(factory->CreateDCRenderTarget(&props, renderTarget.put()));
|
|
92
|
+
RECT rc;
|
|
93
|
+
GetClientRect(hwnd, &rc);
|
|
94
|
+
winrt::check_hresult(renderTarget->BindDC(hdc, &rc));
|
|
95
|
+
auto theme = view.as<winrt::Microsoft::ReactNative::Composition::ComponentView>().Theme();
|
|
96
|
+
auto selfTheme = winrt::get_self<winrt::Microsoft::ReactNative::Composition::implementation::Theme>(theme);
|
|
97
|
+
|
|
98
|
+
renderTarget->BeginDraw();
|
|
99
|
+
renderTarget->Clear(selfTheme->D2DPlatformColor("ToolTipBackground"));
|
|
100
|
+
|
|
101
|
+
auto textAttributes = facebook::react::TextAttributes{};
|
|
102
|
+
facebook::react::Color fgColor;
|
|
103
|
+
fgColor.m_platformColor.push_back("ToolTipForeground");
|
|
104
|
+
textAttributes.foregroundColor = fgColor;
|
|
105
|
+
|
|
106
|
+
winrt::Microsoft::ReactNative::Composition::RenderText(
|
|
107
|
+
*renderTarget,
|
|
108
|
+
*data->textLayout,
|
|
109
|
+
data->attributedString.getValue(),
|
|
110
|
+
textAttributes,
|
|
111
|
+
{std::round(tooltipHorizontalPadding * scaleFactor), std::round(tooltipTopPadding * scaleFactor)},
|
|
112
|
+
scaleFactor,
|
|
113
|
+
*selfTheme);
|
|
114
|
+
|
|
115
|
+
auto hr = renderTarget->EndDraw();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (message != WM_PRINTCLIENT)
|
|
119
|
+
EndPaint(hwnd, &ps);
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
case WM_NCCREATE: {
|
|
123
|
+
auto cs = reinterpret_cast<CREATESTRUCT *>(lparam);
|
|
124
|
+
auto data = static_cast<TooltipData *>(cs->lpCreateParams);
|
|
125
|
+
WINRT_ASSERT(data);
|
|
126
|
+
SetProp(hwnd, TooltipDataProperty, reinterpret_cast<HANDLE>(data));
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return DefWindowProc(hwnd, message, wparam, lparam);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
void RegisterTooltipWndClass() noexcept {
|
|
135
|
+
static bool registered = false;
|
|
136
|
+
if (registered) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
HINSTANCE hInstance =
|
|
141
|
+
GetModuleHandle(NULL); // returns a handle to the file used to create the calling process (.exe file)
|
|
142
|
+
|
|
143
|
+
WNDCLASSEX wcex = {}; // contains window class information
|
|
144
|
+
wcex.cbSize = sizeof(wcex); // size of windows class (bytes)
|
|
145
|
+
wcex.style = CS_HREDRAW | CS_VREDRAW; // class style (redraw window on size adjustment)
|
|
146
|
+
wcex.lpfnWndProc = &TooltipWndProc; // pointer to windows procedure
|
|
147
|
+
wcex.cbClsExtra = DLGWINDOWEXTRA; // extra bytes to allocate
|
|
148
|
+
wcex.cbWndExtra = sizeof(TooltipData *); // extra bytes to allocate
|
|
149
|
+
wcex.hInstance = hInstance;
|
|
150
|
+
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); // handle to class cursor
|
|
151
|
+
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // background color
|
|
152
|
+
wcex.lpszClassName = c_tooltipWindowClassName; // specify resource name
|
|
153
|
+
ATOM classId = RegisterClassEx(&wcex); // register new windows class
|
|
154
|
+
WINRT_VERIFY(classId); // 0 = fail
|
|
155
|
+
winrt::check_win32(!classId);
|
|
156
|
+
|
|
157
|
+
registered = true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
TooltipTracker::TooltipTracker(
|
|
161
|
+
const winrt::Microsoft::ReactNative::ComponentView &view,
|
|
162
|
+
const winrt::Microsoft::ReactNative::ReactPropertyBag &properties,
|
|
163
|
+
TooltipService *outer)
|
|
164
|
+
: m_view(view), m_properties(properties), m_outer(outer) {
|
|
165
|
+
view.PointerEntered({this, &TooltipTracker::OnPointerEntered});
|
|
166
|
+
view.PointerExited({this, &TooltipTracker::OnPointerExited});
|
|
167
|
+
view.PointerMoved({this, &TooltipTracker::OnPointerMoved});
|
|
168
|
+
view.Unmounted({this, &TooltipTracker::OnUnmounted});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
TooltipTracker::~TooltipTracker() {
|
|
172
|
+
DestroyTimer();
|
|
173
|
+
DestroyTooltip();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
facebook::react::Tag TooltipTracker::Tag() const noexcept {
|
|
177
|
+
return m_view.Tag();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
void TooltipTracker::OnPointerEntered(
|
|
181
|
+
const winrt::Windows::Foundation::IInspectable &sender,
|
|
182
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {
|
|
183
|
+
if (args.Pointer().PointerDeviceType() !=
|
|
184
|
+
winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse &&
|
|
185
|
+
args.Pointer().PointerDeviceType() != winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Pen)
|
|
186
|
+
return;
|
|
187
|
+
|
|
188
|
+
auto pp = args.GetCurrentPoint(-1);
|
|
189
|
+
m_pos = pp.Position();
|
|
190
|
+
|
|
191
|
+
m_timer = winrt::Microsoft::ReactNative::Timer::Create(m_properties.Handle());
|
|
192
|
+
m_timer.Interval(std::chrono::milliseconds(toolTipTimeToShowMs));
|
|
193
|
+
m_timer.Tick({this, &TooltipTracker::OnTick});
|
|
194
|
+
m_timer.Start();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
void TooltipTracker::OnPointerMoved(
|
|
198
|
+
const winrt::Windows::Foundation::IInspectable &sender,
|
|
199
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {
|
|
200
|
+
if (args.Pointer().PointerDeviceType() !=
|
|
201
|
+
winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse &&
|
|
202
|
+
args.Pointer().PointerDeviceType() != winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Pen)
|
|
203
|
+
return;
|
|
204
|
+
|
|
205
|
+
auto pp = args.GetCurrentPoint(-1);
|
|
206
|
+
m_pos = pp.Position();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
void TooltipTracker::OnTick(
|
|
210
|
+
const winrt::Windows::Foundation::IInspectable &,
|
|
211
|
+
const winrt::Windows::Foundation::IInspectable &) noexcept {
|
|
212
|
+
ShowTooltip(m_view.view());
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
void TooltipTracker::OnPointerExited(
|
|
216
|
+
const winrt::Windows::Foundation::IInspectable &sender,
|
|
217
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {
|
|
218
|
+
if (args.Pointer().PointerDeviceType() !=
|
|
219
|
+
winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse &&
|
|
220
|
+
args.Pointer().PointerDeviceType() != winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Pen)
|
|
221
|
+
return;
|
|
222
|
+
DestroyTimer();
|
|
223
|
+
DestroyTooltip();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
void TooltipTracker::OnUnmounted(
|
|
227
|
+
const winrt::Windows::Foundation::IInspectable &,
|
|
228
|
+
const winrt::Microsoft::ReactNative::ComponentView &) noexcept {
|
|
229
|
+
DestroyTimer();
|
|
230
|
+
DestroyTooltip();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
void TooltipTracker::ShowTooltip(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
|
|
234
|
+
auto viewCompView = view.as<winrt::Microsoft::ReactNative::Composition::ViewComponentView>();
|
|
235
|
+
|
|
236
|
+
auto selfView =
|
|
237
|
+
winrt::get_self<winrt::Microsoft::ReactNative::Composition::implementation::ViewComponentView>(viewCompView);
|
|
238
|
+
auto parentHwnd = selfView->GetHwndForParenting();
|
|
239
|
+
DestroyTimer();
|
|
240
|
+
|
|
241
|
+
if (!m_hwndTip) {
|
|
242
|
+
auto tooltipData = std::make_unique<TooltipData>(view);
|
|
243
|
+
tooltipData->attributedString = CreateTooltipAttributedString(*selfView->viewProps()->tooltip);
|
|
244
|
+
|
|
245
|
+
tooltipData->compositionContext = selfView->CompositionContext();
|
|
246
|
+
tooltipData->view = view;
|
|
247
|
+
|
|
248
|
+
auto scaleFactor = view.LayoutMetrics().PointScaleFactor;
|
|
249
|
+
facebook::react::LayoutConstraints layoutConstraints;
|
|
250
|
+
layoutConstraints.layoutDirection = facebook::react::LayoutDirection::Undefined;
|
|
251
|
+
layoutConstraints.maximumSize.height = tooltipMaxHeight * scaleFactor;
|
|
252
|
+
layoutConstraints.maximumSize.width = tooltipMaxWidth * scaleFactor;
|
|
253
|
+
layoutConstraints.minimumSize.height = 0;
|
|
254
|
+
layoutConstraints.minimumSize.width = 0;
|
|
255
|
+
|
|
256
|
+
facebook::react::TextLayoutManager::GetTextLayout(
|
|
257
|
+
tooltipData->attributedString, {} /*paragraphAttributes*/, layoutConstraints, tooltipData->textLayout);
|
|
258
|
+
|
|
259
|
+
DWRITE_TEXT_METRICS tm;
|
|
260
|
+
winrt::check_hresult(tooltipData->textLayout->GetMetrics(&tm));
|
|
261
|
+
|
|
262
|
+
tooltipData->width =
|
|
263
|
+
static_cast<int>(tm.width + ((tooltipHorizontalPadding + tooltipHorizontalPadding) * scaleFactor));
|
|
264
|
+
tooltipData->height = static_cast<int>(tm.height + ((tooltipTopPadding + tooltipBottomPadding) * scaleFactor));
|
|
265
|
+
|
|
266
|
+
POINT pt = {static_cast<LONG>(m_pos.X), static_cast<LONG>(m_pos.Y)};
|
|
267
|
+
ClientToScreen(parentHwnd, &pt);
|
|
268
|
+
|
|
269
|
+
RegisterTooltipWndClass();
|
|
270
|
+
HINSTANCE hInstance = GetModuleHandle(NULL);
|
|
271
|
+
m_hwndTip = CreateWindow(
|
|
272
|
+
c_tooltipWindowClassName,
|
|
273
|
+
L"Tooltip",
|
|
274
|
+
WS_POPUP,
|
|
275
|
+
pt.x - tooltipData->width / 2,
|
|
276
|
+
static_cast<int>(pt.y - tooltipData->height - (toolTipPlacementMargin * scaleFactor)),
|
|
277
|
+
tooltipData->width,
|
|
278
|
+
tooltipData->height,
|
|
279
|
+
parentHwnd,
|
|
280
|
+
NULL,
|
|
281
|
+
hInstance,
|
|
282
|
+
tooltipData.get());
|
|
283
|
+
|
|
284
|
+
DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_ROUNDSMALL;
|
|
285
|
+
UINT borderThickness = 0;
|
|
286
|
+
DwmSetWindowAttribute(m_hwndTip, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference));
|
|
287
|
+
|
|
288
|
+
tooltipData.release();
|
|
289
|
+
AnimateWindow(m_hwndTip, toolTipAnimationTimeMs, AW_BLEND);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
void TooltipTracker::DestroyTooltip() noexcept {
|
|
294
|
+
if (m_hwndTip) {
|
|
295
|
+
AnimateWindow(m_hwndTip, toolTipAnimationTimeMs, AW_BLEND | AW_HIDE);
|
|
296
|
+
DestroyWindow(m_hwndTip);
|
|
297
|
+
m_hwndTip = nullptr;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
void TooltipTracker::DestroyTimer() noexcept {
|
|
302
|
+
if (m_timer) {
|
|
303
|
+
m_timer.Stop();
|
|
304
|
+
m_timer = nullptr;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
TooltipService::TooltipService(const winrt::Microsoft::ReactNative::ReactPropertyBag &properties)
|
|
309
|
+
: m_properties(properties) {}
|
|
310
|
+
|
|
311
|
+
void TooltipService::StartTracking(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
|
|
312
|
+
m_trackers.push_back(std::make_shared<TooltipTracker>(view, m_properties, this));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
void TooltipService::StopTracking(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
|
|
316
|
+
for (auto it = m_trackers.begin(); it != m_trackers.end();) {
|
|
317
|
+
if ((*it)->Tag() == view.Tag())
|
|
318
|
+
it = m_trackers.erase(it);
|
|
319
|
+
else
|
|
320
|
+
++it;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
static const ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<std::shared_ptr<TooltipService>>>
|
|
325
|
+
&TooltipServicePropertyId() noexcept {
|
|
326
|
+
static const ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<std::shared_ptr<TooltipService>>> prop{
|
|
327
|
+
L"ReactNative", L"TooltipService"};
|
|
328
|
+
return prop;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
std::shared_ptr<TooltipService> TooltipService::GetCurrent(
|
|
332
|
+
const winrt::Microsoft::ReactNative::ReactPropertyBag &properties) noexcept {
|
|
333
|
+
return *properties.GetOrCreate(TooltipServicePropertyId(), [properties]() -> std::shared_ptr<TooltipService> {
|
|
334
|
+
return std::make_shared<TooltipService>(properties);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
} // namespace winrt::Microsoft::ReactNative
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
#include <Fabric/ReactTaggedView.h>
|
|
8
|
+
#include <winrt/Microsoft.ReactNative.h>
|
|
9
|
+
|
|
10
|
+
namespace winrt::Microsoft::ReactNative {
|
|
11
|
+
|
|
12
|
+
struct TooltipService;
|
|
13
|
+
|
|
14
|
+
struct TooltipTracker {
|
|
15
|
+
TooltipTracker(
|
|
16
|
+
const winrt::Microsoft::ReactNative::ComponentView &view,
|
|
17
|
+
const winrt::Microsoft::ReactNative::ReactPropertyBag &properties,
|
|
18
|
+
TooltipService *outer);
|
|
19
|
+
~TooltipTracker();
|
|
20
|
+
|
|
21
|
+
void OnPointerEntered(
|
|
22
|
+
const winrt::Windows::Foundation::IInspectable &sender,
|
|
23
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept;
|
|
24
|
+
void OnPointerMoved(
|
|
25
|
+
const winrt::Windows::Foundation::IInspectable &sender,
|
|
26
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept;
|
|
27
|
+
void OnPointerExited(
|
|
28
|
+
const winrt::Windows::Foundation::IInspectable &sender,
|
|
29
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept;
|
|
30
|
+
void OnTick(
|
|
31
|
+
const winrt::Windows::Foundation::IInspectable &,
|
|
32
|
+
const winrt::Windows::Foundation::IInspectable &) noexcept;
|
|
33
|
+
void OnUnmounted(
|
|
34
|
+
const winrt::Windows::Foundation::IInspectable &,
|
|
35
|
+
const winrt::Microsoft::ReactNative::ComponentView &) noexcept;
|
|
36
|
+
|
|
37
|
+
facebook::react::Tag Tag() const noexcept;
|
|
38
|
+
|
|
39
|
+
private:
|
|
40
|
+
void ShowTooltip(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept;
|
|
41
|
+
void DestroyTimer() noexcept;
|
|
42
|
+
void DestroyTooltip() noexcept;
|
|
43
|
+
|
|
44
|
+
TooltipService *m_outer;
|
|
45
|
+
winrt::Windows::Foundation::Point m_pos;
|
|
46
|
+
::Microsoft::ReactNative::ReactTaggedView m_view;
|
|
47
|
+
winrt::Microsoft::ReactNative::ITimer m_timer;
|
|
48
|
+
HWND m_hwndTip{nullptr};
|
|
49
|
+
winrt::Microsoft::ReactNative::ReactPropertyBag m_properties;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
struct TooltipService {
|
|
53
|
+
TooltipService(const winrt::Microsoft::ReactNative::ReactPropertyBag &properties);
|
|
54
|
+
void StartTracking(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept;
|
|
55
|
+
void StopTracking(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept;
|
|
56
|
+
|
|
57
|
+
static std::shared_ptr<TooltipService> GetCurrent(
|
|
58
|
+
const winrt::Microsoft::ReactNative::ReactPropertyBag &properties) noexcept;
|
|
59
|
+
|
|
60
|
+
private:
|
|
61
|
+
std::vector<std::shared_ptr<TooltipTracker>> m_enteredTrackers;
|
|
62
|
+
std::vector<std::shared_ptr<TooltipTracker>> m_trackers;
|
|
63
|
+
winrt::Microsoft::ReactNative::ReactPropertyBag m_properties;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
} // namespace winrt::Microsoft::ReactNative
|
|
@@ -206,4 +206,12 @@ void DispatchAccessibilityAction(::Microsoft::ReactNative::ReactTaggedView &view
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
ExpandCollapseState GetExpandCollapseState(const bool &expanded) noexcept {
|
|
210
|
+
if (expanded) {
|
|
211
|
+
return ExpandCollapseState_Expanded;
|
|
212
|
+
} else {
|
|
213
|
+
return ExpandCollapseState_Collapsed;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
209
217
|
} // namespace winrt::Microsoft::ReactNative::implementation
|
|
@@ -35,4 +35,5 @@ std::string extractAccessibilityValue(const facebook::react::AccessibilityValue
|
|
|
35
35
|
|
|
36
36
|
void DispatchAccessibilityAction(::Microsoft::ReactNative::ReactTaggedView &view, const std::string &action) noexcept;
|
|
37
37
|
|
|
38
|
+
ExpandCollapseState GetExpandCollapseState(const bool &expanded) noexcept;
|
|
38
39
|
} // namespace winrt::Microsoft::ReactNative::implementation
|
|
@@ -155,6 +155,9 @@ void FabricUIManager::startSurface(
|
|
|
155
155
|
|
|
156
156
|
void FabricUIManager::stopSurface(facebook::react::SurfaceId surfaceId) noexcept {
|
|
157
157
|
m_surfaceManager->stopSurface(surfaceId);
|
|
158
|
+
auto &rootDescriptor = m_registry.componentViewDescriptorWithTag(surfaceId);
|
|
159
|
+
m_registry.enqueueComponentViewWithComponentHandle(
|
|
160
|
+
facebook::react::RootShadowNode::Handle(), surfaceId, rootDescriptor);
|
|
158
161
|
}
|
|
159
162
|
|
|
160
163
|
winrt::Microsoft::ReactNative::ReactNativeIsland FabricUIManager::GetReactNativeIsland(
|
|
@@ -27,6 +27,10 @@ HostPlatformViewProps::HostPlatformViewProps(
|
|
|
27
27
|
CoreFeatures::enablePropIteratorSetter
|
|
28
28
|
? sourceProps.focusable
|
|
29
29
|
: convertRawProp(context, rawProps, "focusable", sourceProps.focusable, {})),
|
|
30
|
+
tooltip(
|
|
31
|
+
CoreFeatures::enablePropIteratorSetter
|
|
32
|
+
? sourceProps.tooltip
|
|
33
|
+
: convertRawProp(context, rawProps, "tooltip", sourceProps.tooltip, {})),
|
|
30
34
|
accessibilityPosInSet(
|
|
31
35
|
CoreFeatures::enablePropIteratorSetter
|
|
32
36
|
? sourceProps.accessibilityPosInSet
|
|
@@ -82,6 +86,7 @@ void HostPlatformViewProps::setProp(
|
|
|
82
86
|
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLiveRegion);
|
|
83
87
|
RAW_SET_PROP_SWITCH_CASE_BASIC(keyDownEvents);
|
|
84
88
|
RAW_SET_PROP_SWITCH_CASE_BASIC(keyUpEvents);
|
|
89
|
+
RAW_SET_PROP_SWITCH_CASE_BASIC(tooltip);
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
|
package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h
CHANGED
|
@@ -29,7 +29,7 @@ class HostPlatformViewProps : public BaseViewProps {
|
|
|
29
29
|
std::string accessibilityLiveRegion{"none"};
|
|
30
30
|
|
|
31
31
|
// std::optional<std::string> overflowAnchor{};
|
|
32
|
-
|
|
32
|
+
std::optional<std::string> tooltip{};
|
|
33
33
|
std::vector<HandledKeyEvent> keyDownEvents{};
|
|
34
34
|
std::vector<HandledKeyEvent> keyUpEvents{};
|
|
35
35
|
};
|
|
@@ -13,7 +13,7 @@ inline bool formsStackingContext(ViewProps const &viewProps) {
|
|
|
13
13
|
// Only Views which are marked as focusable can actually trigger the events, which will already avoid being collapsed.
|
|
14
14
|
constexpr decltype(WindowsViewEvents::bits) focusEventsMask = {
|
|
15
15
|
(1 << (int)WindowsViewEvents::Offset::Focus) & (1 << (int)WindowsViewEvents::Offset::Blur)};
|
|
16
|
-
return (viewProps.windowsEvents.bits & focusEventsMask).any();
|
|
16
|
+
return (viewProps.windowsEvents.bits & focusEventsMask).any() || viewProps.tooltip;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
inline bool formsView(ViewProps const &viewProps) {
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
|
145
145
|
</ClCompile>
|
|
146
146
|
<Link>
|
|
147
|
-
<AdditionalDependencies>winsqlite3.lib;ChakraRT.lib;dxguid.lib;dloadhelper.lib;OneCoreUap_apiset.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
147
|
+
<AdditionalDependencies>winsqlite3.lib;ChakraRT.lib;dxguid.lib;dloadhelper.lib;OneCoreUap_apiset.lib;Dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
148
148
|
<DelayLoadDLLs>
|
|
149
149
|
api-ms-win-core-file-l1-2-0.dll;
|
|
150
150
|
api-ms-win-core-windowserrorreporting-l1-1-0.dll;
|
|
@@ -53,7 +53,6 @@ struct ReactCoreInjection : ReactCoreInjectionT<ReactCoreInjection> {
|
|
|
53
53
|
static uint64_t GetTopLevelWindowId(const IReactPropertyBag &properties) noexcept;
|
|
54
54
|
static void SetTopLevelWindowId(const IReactPropertyBag &properties, uint64_t windowId) noexcept;
|
|
55
55
|
|
|
56
|
-
static ITimer CreateTimer(const IReactPropertyBag &properties);
|
|
57
56
|
static TimerFactory GetTimerFactory(const IReactPropertyBag &properties) noexcept;
|
|
58
57
|
static void SetTimerFactory(const IReactPropertyBag &properties, const TimerFactory &timerFactory) noexcept;
|
|
59
58
|
};
|
|
@@ -400,9 +400,12 @@ void ReactInstanceWin::LoadModules(
|
|
|
400
400
|
}
|
|
401
401
|
#endif
|
|
402
402
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
403
|
+
if (!m_options.UseWebDebugger()) {
|
|
404
|
+
turboModulesProvider->AddModuleProvider(
|
|
405
|
+
L"SampleTurboModule",
|
|
406
|
+
winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::SampleTurboModule>(),
|
|
407
|
+
false);
|
|
408
|
+
}
|
|
406
409
|
|
|
407
410
|
if (devSettings->useTurboModulesOnly) {
|
|
408
411
|
::Microsoft::ReactNative::ExceptionsManager::SetRedBoxHander(
|
|
@@ -680,8 +683,16 @@ void ReactInstanceWin::InitializeBridgeless() noexcept {
|
|
|
680
683
|
return turboModuleManager->getModule(name);
|
|
681
684
|
};
|
|
682
685
|
|
|
686
|
+
// Use a legacy native module binding that always returns null
|
|
687
|
+
// This means that calls to NativeModules.XXX will always return null, rather than crashing on access
|
|
688
|
+
auto legacyNativeModuleBinding =
|
|
689
|
+
[](const std::string & /*name*/) -> std::shared_ptr<facebook::react::TurboModule> { return nullptr; };
|
|
690
|
+
|
|
683
691
|
facebook::react::TurboModuleBinding::install(
|
|
684
|
-
runtime,
|
|
692
|
+
runtime,
|
|
693
|
+
std::function(binding),
|
|
694
|
+
std::function(legacyNativeModuleBinding),
|
|
695
|
+
m_options.TurboModuleProvider->LongLivedObjectCollection());
|
|
685
696
|
|
|
686
697
|
auto componentDescriptorRegistry =
|
|
687
698
|
Microsoft::ReactNative::WindowsComponentDescriptorRegistry::FromProperties(
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
-->
|
|
11
11
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
12
12
|
<PropertyGroup>
|
|
13
|
-
<ReactNativeWindowsVersion>0.76.0-preview.
|
|
13
|
+
<ReactNativeWindowsVersion>0.76.0-preview.3</ReactNativeWindowsVersion>
|
|
14
14
|
<ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
|
|
15
15
|
<ReactNativeWindowsMinor>76</ReactNativeWindowsMinor>
|
|
16
16
|
<ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
|
|
17
17
|
<ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
|
|
18
|
-
<ReactNativeWindowsCommitId>
|
|
18
|
+
<ReactNativeWindowsCommitId>c147dcb404ad758a03db0a8e1733cf30ef60ff4b</ReactNativeWindowsCommitId>
|
|
19
19
|
</PropertyGroup>
|
|
20
20
|
</Project>
|
package/Shared/Shared.vcxitems
CHANGED
|
@@ -150,6 +150,9 @@
|
|
|
150
150
|
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\Composition\TextDrawing.cpp">
|
|
151
151
|
<ExcludedFromBuild Condition="'$(UseFabric)' != 'true'">true</ExcludedFromBuild>
|
|
152
152
|
</ClCompile>
|
|
153
|
+
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\Composition\TooltipService.cpp">
|
|
154
|
+
<ExcludedFromBuild Condition="'$(UseFabric)' != 'true'">true</ExcludedFromBuild>
|
|
155
|
+
</ClCompile>
|
|
153
156
|
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\Composition\UiaHelpers.cpp">
|
|
154
157
|
<ExcludedFromBuild Condition="'$(UseFabric)' != 'true'">true</ExcludedFromBuild>
|
|
155
158
|
</ClCompile>
|
|
@@ -333,6 +333,7 @@
|
|
|
333
333
|
<ClCompile Include="$(ReactNativeDir)\ReactCommon\jsinspector-modern\RuntimeTargetConsole.cpp" />
|
|
334
334
|
<ClCompile Include="$(MSBuildThisFileDirectory)Networking\NetworkPropertyIds.cpp" />
|
|
335
335
|
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\AbiEventEmitter.cpp" />
|
|
336
|
+
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\Composition\TooltipService.cpp" />
|
|
336
337
|
</ItemGroup>
|
|
337
338
|
<ItemGroup>
|
|
338
339
|
<Filter Include="Source Files">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-windows",
|
|
3
|
-
"version": "0.76.0-preview.
|
|
3
|
+
"version": "0.76.0-preview.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
"@react-native-community/cli": "15.0.0-alpha.2",
|
|
27
27
|
"@react-native-community/cli-platform-android": "15.0.0-alpha.2",
|
|
28
28
|
"@react-native-community/cli-platform-ios": "15.0.0-alpha.2",
|
|
29
|
-
"@react-native-windows/cli": "0.76.0-preview.
|
|
29
|
+
"@react-native-windows/cli": "0.76.0-preview.3",
|
|
30
30
|
"@react-native/assets": "1.0.0",
|
|
31
|
-
"@react-native/assets-registry": "0.76.0-rc.
|
|
32
|
-
"@react-native/codegen": "0.76.0-rc.
|
|
33
|
-
"@react-native/community-cli-plugin": "0.76.0-rc.
|
|
34
|
-
"@react-native/gradle-plugin": "0.76.0-rc.
|
|
35
|
-
"@react-native/js-polyfills": "0.76.0-rc.
|
|
36
|
-
"@react-native/normalize-colors": "0.76.0-rc.
|
|
37
|
-
"@react-native/virtualized-lists": "0.76.0-rc.
|
|
31
|
+
"@react-native/assets-registry": "0.76.0-rc.6",
|
|
32
|
+
"@react-native/codegen": "0.76.0-rc.6",
|
|
33
|
+
"@react-native/community-cli-plugin": "0.76.0-rc.6",
|
|
34
|
+
"@react-native/gradle-plugin": "0.76.0-rc.6",
|
|
35
|
+
"@react-native/js-polyfills": "0.76.0-rc.6",
|
|
36
|
+
"@react-native/normalize-colors": "0.76.0-rc.6",
|
|
37
|
+
"@react-native/virtualized-lists": "0.76.0-rc.6",
|
|
38
38
|
"abort-controller": "^3.0.0",
|
|
39
39
|
"anser": "^1.4.9",
|
|
40
40
|
"ansi-regex": "^5.0.0",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"jest-environment-node": "^29.6.3",
|
|
51
51
|
"jsc-android": "^250231.0.0",
|
|
52
52
|
"memoize-one": "^5.0.0",
|
|
53
|
-
"metro-runtime": "^0.81.0
|
|
54
|
-
"metro-source-map": "^0.81.0
|
|
53
|
+
"metro-runtime": "^0.81.0",
|
|
54
|
+
"metro-source-map": "^0.81.0",
|
|
55
55
|
"mkdirp": "^0.5.1",
|
|
56
56
|
"nullthrows": "^1.1.1",
|
|
57
57
|
"pretty-format": "^29.7.0",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"just-scripts": "^1.3.3",
|
|
87
87
|
"prettier": "2.8.8",
|
|
88
88
|
"react": "18.3.1",
|
|
89
|
-
"react-native": "0.76.0-rc.
|
|
89
|
+
"react-native": "0.76.0-rc.6",
|
|
90
90
|
"react-native-platform-override": "^1.9.46",
|
|
91
91
|
"react-refresh": "^0.14.0",
|
|
92
92
|
"typescript": "5.0.4"
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"peerDependencies": {
|
|
95
95
|
"@types/react": "^18.2.6",
|
|
96
96
|
"react": "^18.2.0",
|
|
97
|
-
"react-native": "0.76.0-rc.
|
|
97
|
+
"react-native": "0.76.0-rc.6"
|
|
98
98
|
},
|
|
99
99
|
"beachball": {
|
|
100
100
|
"defaultNpmTag": "preview",
|