Skip to content

Commit efcfebf

Browse files
[Fabric] implement tooltip property (#13941)
* [Fabric] implement view tooltip property * format * Change files * update * Fix lingering tooltip if component is unmounted while tooltip showing * snapshot --------- Co-authored-by: Jon Thysell <jthysell@microsoft.com>
1 parent 6b9130f commit efcfebf

24 files changed

+552
-18
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] implement view tooltip property",
4+
"packageName": "react-native-windows",
5+
"email": "30809111+acoates-ms@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

packages/e2e-test-app-fabric/test/__snapshots__/ViewComponentTest.test.ts.snap

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6085,12 +6085,24 @@ exports[`View Tests Views can have tooltips 1`] = `
60856085
"_Props": {},
60866086
},
60876087
{
6088-
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
6088+
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
60896089
"_Props": {},
6090+
"__Children": [
6091+
{
6092+
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
6093+
"_Props": {},
6094+
},
6095+
],
60906096
},
60916097
{
6092-
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
6098+
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
60936099
"_Props": {},
6100+
"__Children": [
6101+
{
6102+
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
6103+
"_Props": {},
6104+
},
6105+
],
60946106
},
60956107
],
60966108
},
@@ -6126,6 +6138,25 @@ exports[`View Tests Views can have tooltips 1`] = `
61266138
"Offset": "0, 0, 0",
61276139
"Size": "916, 15",
61286140
"Visual Type": "SpriteVisual",
6141+
"__Children": [
6142+
{
6143+
"Offset": "0, 0, 0",
6144+
"Size": "916, 15",
6145+
"Visual Type": "SpriteVisual",
6146+
"__Children": [
6147+
{
6148+
"Offset": "0, 0, 0",
6149+
"Size": "916, 15",
6150+
"Visual Type": "SpriteVisual",
6151+
},
6152+
{
6153+
"Offset": "0, 0, 0",
6154+
"Size": "0, 0",
6155+
"Visual Type": "SpriteVisual",
6156+
},
6157+
],
6158+
},
6159+
],
61296160
},
61306161
{
61316162
"Offset": "0, 0, 0",
@@ -6136,13 +6167,32 @@ exports[`View Tests Views can have tooltips 1`] = `
61366167
},
61376168
{
61386169
"Offset": "0, 29, 0",
6139-
"Size": "916, 16",
6170+
"Size": "916, 14",
61406171
"Visual Type": "SpriteVisual",
61416172
"__Children": [
61426173
{
61436174
"Offset": "0, 0, 0",
6144-
"Size": "916, 16",
6175+
"Size": "916, 14",
61456176
"Visual Type": "SpriteVisual",
6177+
"__Children": [
6178+
{
6179+
"Offset": "0, 0, 0",
6180+
"Size": "916, 16",
6181+
"Visual Type": "SpriteVisual",
6182+
"__Children": [
6183+
{
6184+
"Offset": "0, 0, 0",
6185+
"Size": "916, 16",
6186+
"Visual Type": "SpriteVisual",
6187+
},
6188+
{
6189+
"Offset": "0, 0, 0",
6190+
"Size": "0, 0",
6191+
"Visual Type": "SpriteVisual",
6192+
},
6193+
],
6194+
},
6195+
],
61466196
},
61476197
{
61486198
"Offset": "0, 0, 0",

vnext/Desktop.DLL/React.Windows.Desktop.DLL.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
comsuppw.lib;
104104
Shlwapi.lib;
105105
Version.lib;
106+
Dwmapi.lib;
106107
WindowsApp_downlevel.lib;
107108
%(AdditionalDependencies)
108109
</AdditionalDependencies>

vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <Fabric/Composition/RootComponentView.h>
1515
#include "AbiEventEmitter.h"
1616
#include "AbiShadowNode.h"
17+
#include "ReactCoreInjection.h"
1718

1819
namespace winrt::Microsoft::ReactNative::Composition::implementation {
1920
struct RootComponentView;
@@ -262,6 +263,17 @@ void ComponentView::HandleCommand(const winrt::Microsoft::ReactNative::HandleCom
262263
}
263264
}
264265

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+
265277
winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *ComponentView::rootComponentView()
266278
const noexcept {
267279
if (m_rootView)

vnext/Microsoft.ReactNative/Fabric/ComponentView.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ struct ComponentView : public ComponentViewT<ComponentView> {
209209
// Notify up the tree to bring the rect into view by scrolling as needed
210210
virtual void StartBringIntoView(BringIntoViewOptions &&args) noexcept;
211211

212+
// Eventually PopupContentLink and similar APIs will remove the need for this.
213+
virtual HWND GetHwndForParenting() noexcept;
214+
212215
virtual const winrt::Microsoft::ReactNative::IComponentProps userProps(
213216
facebook::react::Props::Shared const &props) noexcept;
214217

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "CompositionHelpers.h"
2424
#include "RootComponentView.h"
2525
#include "Theme.h"
26+
#include "TooltipService.h"
2627
#include "UiaHelpers.h"
2728
#include "d2d1helper.h"
2829

@@ -43,6 +44,13 @@ ComponentView::ComponentView(
4344
m_outerVisual.InsertAt(m_focusVisual.InnerVisual(), 0);
4445
}
4546

47+
ComponentView::~ComponentView() {
48+
if (m_tooltipTracked) {
49+
TooltipService::GetCurrent(m_reactContext.Properties())->StopTracking(*this);
50+
m_tooltipTracked = false;
51+
}
52+
}
53+
4654
facebook::react::Tag ComponentView::Tag() const noexcept {
4755
return m_tag;
4856
}
@@ -130,6 +138,16 @@ void ComponentView::updateProps(
130138
updateShadowProps(oldViewProps, newViewProps);
131139
}
132140

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+
133151
base_type::updateProps(props, oldProps);
134152
}
135153

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct ComponentView : public ComponentViewT<
3030
facebook::react::Tag tag,
3131
winrt::Microsoft::ReactNative::ReactContext const &reactContext,
3232
ComponentViewFeatures flags);
33+
virtual ~ComponentView();
3334

3435
virtual winrt::Microsoft::ReactNative::Composition::Experimental::IVisual Visual() const noexcept {
3536
return nullptr;
@@ -151,6 +152,7 @@ struct ComponentView : public ComponentViewT<
151152
const facebook::react::ViewProps &viewProps) noexcept;
152153

153154
bool m_FinalizeTransform{false};
155+
bool m_tooltipTracked{false};
154156
ComponentViewFeatures m_flags;
155157
void showFocusVisual(bool show) noexcept;
156158
winrt::Microsoft::ReactNative::Composition::Experimental::IFocusVisual m_focusVisual{nullptr};

vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ winrt::IInspectable ReactNativeIsland::GetUiaProvider() noexcept {
332332
if (m_uiaProvider == nullptr) {
333333
m_uiaProvider =
334334
winrt::make<winrt::Microsoft::ReactNative::implementation::CompositionRootAutomationProvider>(*this);
335-
if (m_hwnd) {
335+
if (m_hwnd && !m_island) {
336336
auto pRootProvider =
337337
static_cast<winrt::Microsoft::ReactNative::implementation::CompositionRootAutomationProvider *>(
338338
m_uiaProvider.as<IRawElementProviderSimple>().get());
@@ -348,6 +348,10 @@ void ReactNativeIsland::SetWindow(uint64_t hwnd) noexcept {
348348
m_hwnd = reinterpret_cast<HWND>(hwnd);
349349
}
350350

351+
HWND ReactNativeIsland::GetHwndForParenting() noexcept {
352+
return m_hwnd;
353+
}
354+
351355
int64_t ReactNativeIsland::SendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept {
352356
if (m_rootTag == -1)
353357
return 0;
@@ -367,7 +371,7 @@ int64_t ReactNativeIsland::SendMessage(uint32_t msg, uint64_t wParam, int64_t lP
367371
bool ReactNativeIsland::CapturePointer(
368372
const winrt::Microsoft::ReactNative::Composition::Input::Pointer &pointer,
369373
facebook::react::Tag tag) noexcept {
370-
if (m_hwnd) {
374+
if (m_hwnd && !m_island) {
371375
SetCapture(m_hwnd);
372376
}
373377
return m_CompositionEventHandler->CapturePointer(pointer, tag);
@@ -377,7 +381,7 @@ void ReactNativeIsland::ReleasePointerCapture(
377381
const winrt::Microsoft::ReactNative::Composition::Input::Pointer &pointer,
378382
facebook::react::Tag tag) noexcept {
379383
if (m_CompositionEventHandler->ReleasePointerCapture(pointer, tag)) {
380-
if (m_hwnd) {
384+
if (m_hwnd && !m_island) {
381385
if (m_hwnd == GetCapture()) {
382386
ReleaseCapture();
383387
}

vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ struct ReactNativeIsland
8282
void AddRenderedVisual(const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept;
8383
void RemoveRenderedVisual(const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept;
8484
bool TrySetFocus() noexcept;
85+
HWND GetHwndForParenting() noexcept;
8586

8687
winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader Resources() noexcept;
8788
void Resources(const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept;

vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,15 @@ winrt::Microsoft::ReactNative::implementation::ClipState RootComponentView::getC
231231
return winrt::Microsoft::ReactNative::implementation::ClipState::NoClip;
232232
}
233233

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+
234245
} // namespace winrt::Microsoft::ReactNative::Composition::implementation

0 commit comments

Comments
 (0)