react-native-windows 0.82.0-preview.1 → 0.82.0-preview.11
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/Animated/nodes/AnimatedValue.js +0 -8
- package/Libraries/BatchedBridge/BatchedBridge.js +1 -0
- package/Libraries/BatchedBridge/MessageQueue.js +1 -0
- package/Libraries/Components/Switch/Switch.js +1 -1
- package/Libraries/Components/Switch/Switch.windows.js +1 -1
- package/Libraries/Core/ReactNativeVersion.js +2 -2
- package/Libraries/Core/Timers/JSTimers.js +1 -0
- package/Libraries/Core/Timers/NativeTiming.js +1 -0
- package/Libraries/Core/Timers/immediateShim.js +1 -0
- package/Libraries/Core/setUpPerformance.js +3 -5
- package/Libraries/Interaction/PanResponder.js +6 -51
- package/Microsoft.ReactNative/ComponentView.idl +2 -0
- package/Microsoft.ReactNative/Composition.Input.idl +7 -0
- package/Microsoft.ReactNative/CompositionComponentView.idl +3 -0
- package/Microsoft.ReactNative/Fabric/ComponentView.cpp +18 -0
- package/Microsoft.ReactNative/Fabric/ComponentView.h +9 -0
- package/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp +12 -0
- package/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h +15 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +75 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +1 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +84 -17
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +4 -0
- package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp +56 -82
- package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h +7 -4
- package/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +82 -14
- package/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h +11 -4
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +33 -0
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h +17 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +59 -31
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +3 -0
- package/Microsoft.ReactNative/Modules/ImageViewManagerModule.cpp +42 -15
- package/Microsoft.ReactNative.Cxx/ReactCommon/react/timing/primitives.h +12 -0
- package/PropertySheets/Generated/PackageVersion.g.props +2 -2
- package/PropertySheets/Warnings.props +1 -2
- package/PropertySheets/WinUI.props +1 -1
- package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/text/BaseParagraphProps.cpp +174 -0
- package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/text/BaseParagraphProps.h +69 -0
- package/Scripts/NuGetRestoreForceEvaluateAllSolutions.ps1 +5 -11
- package/Scripts/rnw-dependencies.ps1 +15 -1
- package/Shared/Shared.vcxitems +1 -0
- package/Shared/Shared.vcxitems.filters +1 -3
- package/codegen/NativePerformanceSpec.g.h +41 -35
- package/codegen/NativeReactNativeFeatureFlagsSpec.g.h +55 -49
- package/codegen/rnwcoreJSI-generated.cpp +434 -422
- package/codegen/rnwcoreJSI.h +18 -0
- package/index.js +6 -0
- package/index.windows.js +6 -0
- package/package.json +15 -14
- package/src/private/featureflags/ReactNativeFeatureFlags.js +6 -1
- package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +2 -1
- package/src/private/setup/{setUpPerformanceObserver.js → setUpPerformanceModern.js} +43 -18
- package/src/private/specs_DEPRECATED/components/SwitchNativeComponent.js +1 -0
- package/src/private/specs_DEPRECATED/modules/NativeTiming.js +1 -0
- package/src/private/webapis/performance/EventTiming.js +34 -15
- package/src/private/webapis/performance/LongTasks.js +35 -2
- package/src/private/webapis/performance/Performance.js +49 -13
- package/src/private/webapis/performance/PerformanceEntry.js +21 -8
- package/src/private/webapis/performance/PerformanceObserver.js +30 -1
- package/src/private/webapis/performance/ReactNativeStartupTiming.js +3 -24
- package/src/private/webapis/performance/ResourceTiming.js +29 -18
- package/src/private/webapis/performance/UserTiming.js +33 -28
- package/src/private/webapis/performance/internals/RawPerformanceEntry.js +3 -4
- package/src/private/webapis/performance/specs/NativePerformance.js +2 -0
|
@@ -233,11 +233,15 @@ struct ViewComponentView : public ViewComponentViewT<
|
|
|
233
233
|
|
|
234
234
|
protected:
|
|
235
235
|
virtual winrt::Microsoft::ReactNative::ViewProps ViewPropsInner() noexcept;
|
|
236
|
+
virtual void updateChildrenClippingPath(
|
|
237
|
+
facebook::react::LayoutMetrics const &layoutMetrics,
|
|
238
|
+
const facebook::react::ViewProps &viewProps) noexcept;
|
|
236
239
|
|
|
237
240
|
private:
|
|
238
241
|
bool m_hasNonVisualChildren{false};
|
|
239
242
|
facebook::react::SharedViewProps m_props;
|
|
240
243
|
winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_visual{nullptr};
|
|
244
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_childrenContainer{nullptr};
|
|
241
245
|
winrt::Microsoft::ReactNative::Composition::Experimental::CreateInternalVisualDelegate m_createInternalVisualHandler{
|
|
242
246
|
nullptr};
|
|
243
247
|
};
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <winrt/Windows.UI.Composition.h>
|
|
15
15
|
#include "CompositionContextHelper.h"
|
|
16
16
|
#include "RootComponentView.h"
|
|
17
|
+
#include "ScrollViewComponentView.h"
|
|
17
18
|
|
|
18
19
|
#include "Composition.ContentIslandComponentView.g.cpp"
|
|
19
20
|
|
|
@@ -49,6 +50,14 @@ void ContentIslandComponentView::OnMounted() noexcept {
|
|
|
49
50
|
.as<winrt::Microsoft::UI::Composition::ContainerVisual>());
|
|
50
51
|
m_childSiteLink.ActualSize({m_layoutMetrics.frame.size.width, m_layoutMetrics.frame.size.height});
|
|
51
52
|
|
|
53
|
+
// Issue #15557: Set initial LocalToParentTransformMatrix synchronously before Connect.
|
|
54
|
+
// This fixes popup position being wrong even without scrolling.
|
|
55
|
+
// Note: getClientRect() returns physical pixels, but LocalToParentTransformMatrix expects DIPs.
|
|
56
|
+
auto clientRect = getClientRect();
|
|
57
|
+
float scaleFactor = m_layoutMetrics.pointScaleFactor;
|
|
58
|
+
m_childSiteLink.LocalToParentTransformMatrix(winrt::Windows::Foundation::Numerics::make_float4x4_translation(
|
|
59
|
+
static_cast<float>(clientRect.left) / scaleFactor, static_cast<float>(clientRect.top) / scaleFactor, 0.0f));
|
|
60
|
+
|
|
52
61
|
m_navigationHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteLink(m_childSiteLink);
|
|
53
62
|
|
|
54
63
|
m_navigationHostDepartFocusRequestedToken =
|
|
@@ -80,12 +89,34 @@ void ContentIslandComponentView::OnMounted() noexcept {
|
|
|
80
89
|
strongThis->ParentLayoutChanged();
|
|
81
90
|
}
|
|
82
91
|
}));
|
|
92
|
+
|
|
93
|
+
// Issue #15557: Register for ViewChanged on parent ScrollViews to update transform
|
|
94
|
+
// when scroll position changes, ensuring correct XAML popup positioning.
|
|
95
|
+
if (auto scrollView = view.try_as<winrt::Microsoft::ReactNative::Composition::ScrollViewComponentView>()) {
|
|
96
|
+
auto token =
|
|
97
|
+
scrollView.ViewChanged([wkThis = get_weak()](const winrt::IInspectable &, const winrt::IInspectable &) {
|
|
98
|
+
if (auto strongThis = wkThis.get()) {
|
|
99
|
+
strongThis->ParentLayoutChanged();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
m_viewChangedSubscriptions.push_back({scrollView, token});
|
|
103
|
+
}
|
|
104
|
+
|
|
83
105
|
view = view.Parent();
|
|
84
106
|
}
|
|
85
107
|
}
|
|
86
108
|
|
|
87
109
|
void ContentIslandComponentView::OnUnmounted() noexcept {
|
|
88
110
|
m_layoutMetricChangedRevokers.clear();
|
|
111
|
+
|
|
112
|
+
// Issue #15557: Unsubscribe from parent ScrollView events
|
|
113
|
+
for (auto &subscription : m_viewChangedSubscriptions) {
|
|
114
|
+
if (auto scrollView = subscription.scrollView.get()) {
|
|
115
|
+
scrollView.ViewChanged(subscription.token);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
m_viewChangedSubscriptions.clear();
|
|
119
|
+
|
|
89
120
|
if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) {
|
|
90
121
|
m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken);
|
|
91
122
|
m_navigationHostDepartFocusRequestedToken = {};
|
|
@@ -93,21 +124,25 @@ void ContentIslandComponentView::OnUnmounted() noexcept {
|
|
|
93
124
|
}
|
|
94
125
|
|
|
95
126
|
void ContentIslandComponentView::ParentLayoutChanged() noexcept {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
127
|
+
// Issue #15557: Update transform synchronously to ensure correct popup position
|
|
128
|
+
// when user clicks. Async updates via UIDispatcher().Post() were causing the
|
|
129
|
+
// popup to open with stale transform values.
|
|
130
|
+
//
|
|
131
|
+
// Note: The original async approach was for batching notifications during layout passes.
|
|
132
|
+
// However, LocalToParentTransformMatrix is a cheap call (just sets a matrix), and
|
|
133
|
+
// synchronous updates are required to ensure correct popup position when clicked.
|
|
134
|
+
//
|
|
135
|
+
// getClientRect() returns values in physical pixels (scaled by pointScaleFactor),
|
|
136
|
+
// but LocalToParentTransformMatrix expects logical pixels (DIPs). We need to divide
|
|
137
|
+
// by the scale factor to convert.
|
|
138
|
+
auto clientRect = getClientRect();
|
|
139
|
+
float scaleFactor = m_layoutMetrics.pointScaleFactor;
|
|
140
|
+
|
|
141
|
+
float x = static_cast<float>(clientRect.left) / scaleFactor;
|
|
142
|
+
float y = static_cast<float>(clientRect.top) / scaleFactor;
|
|
143
|
+
|
|
144
|
+
m_childSiteLink.LocalToParentTransformMatrix(
|
|
145
|
+
winrt::Windows::Foundation::Numerics::make_float4x4_translation(x, y, 0.0f));
|
|
111
146
|
}
|
|
112
147
|
|
|
113
148
|
winrt::Windows::Foundation::IInspectable ContentIslandComponentView::CreateAutomationProvider() noexcept {
|
|
@@ -171,24 +206,6 @@ ContentIslandComponentView::~ContentIslandComponentView() noexcept {
|
|
|
171
206
|
m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken);
|
|
172
207
|
m_navigationHostDepartFocusRequestedToken = {};
|
|
173
208
|
}
|
|
174
|
-
if (m_childSiteLink) {
|
|
175
|
-
if (m_fragmentRootAutomationProviderRequestedToken) {
|
|
176
|
-
m_childSiteLink.FragmentRootAutomationProviderRequested(m_fragmentRootAutomationProviderRequestedToken);
|
|
177
|
-
m_fragmentRootAutomationProviderRequestedToken = {};
|
|
178
|
-
}
|
|
179
|
-
if (m_parentAutomationProviderRequestedToken) {
|
|
180
|
-
m_childSiteLink.ParentAutomationProviderRequested(m_parentAutomationProviderRequestedToken);
|
|
181
|
-
m_parentAutomationProviderRequestedToken = {};
|
|
182
|
-
}
|
|
183
|
-
if (m_nextSiblingAutomationProviderRequestedToken) {
|
|
184
|
-
m_childSiteLink.NextSiblingAutomationProviderRequested(m_nextSiblingAutomationProviderRequestedToken);
|
|
185
|
-
m_nextSiblingAutomationProviderRequestedToken = {};
|
|
186
|
-
}
|
|
187
|
-
if (m_previousSiblingAutomationProviderRequestedToken) {
|
|
188
|
-
m_childSiteLink.PreviousSiblingAutomationProviderRequested(m_previousSiblingAutomationProviderRequestedToken);
|
|
189
|
-
m_previousSiblingAutomationProviderRequestedToken = {};
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
209
|
if (m_islandToConnect) {
|
|
193
210
|
m_islandToConnect.Close();
|
|
194
211
|
}
|
|
@@ -230,56 +247,13 @@ void ContentIslandComponentView::prepareForRecycle() noexcept {
|
|
|
230
247
|
}
|
|
231
248
|
|
|
232
249
|
void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
m_childSiteLink.AutomationOption(winrt::Microsoft::UI::Content::ContentAutomationOptions::
|
|
237
|
-
|
|
238
|
-
// These events are raised in response to the child ContentIsland asking for providers.
|
|
239
|
-
// For example, the ContentIsland.FragmentRootAutomationProvider property will return
|
|
240
|
-
// the provider we provide here in FragmentRootAutomationProviderRequested.
|
|
241
|
-
|
|
242
|
-
// We capture "this" as a raw pointer because ContentIslandComponentView doesn't currently support weak ptrs.
|
|
243
|
-
// It's safe because we disconnect these events in the destructor.
|
|
244
|
-
|
|
245
|
-
m_fragmentRootAutomationProviderRequestedToken = m_childSiteLink.FragmentRootAutomationProviderRequested(
|
|
246
|
-
[this](
|
|
247
|
-
const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
|
|
248
|
-
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
|
|
249
|
-
// The child island's fragment tree doesn't have its own fragment root.
|
|
250
|
-
// Here's how we can provide the correct fragment root to the child's UIA logic.
|
|
251
|
-
winrt::com_ptr<IRawElementProviderFragmentRoot> fragmentRoot{nullptr};
|
|
252
|
-
auto uiaProvider = this->EnsureUiaProvider();
|
|
253
|
-
uiaProvider.as<IRawElementProviderFragment>()->get_FragmentRoot(fragmentRoot.put());
|
|
254
|
-
args.AutomationProvider(fragmentRoot.as<IInspectable>());
|
|
255
|
-
args.Handled(true);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
m_parentAutomationProviderRequestedToken = m_childSiteLink.ParentAutomationProviderRequested(
|
|
259
|
-
[this](
|
|
260
|
-
const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
|
|
261
|
-
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
|
|
262
|
-
auto uiaProvider = this->EnsureUiaProvider();
|
|
263
|
-
args.AutomationProvider(uiaProvider);
|
|
264
|
-
args.Handled(true);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
m_nextSiblingAutomationProviderRequestedToken = m_childSiteLink.NextSiblingAutomationProviderRequested(
|
|
268
|
-
[](const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
|
|
269
|
-
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
|
|
270
|
-
// The ContentIsland will always be the one and only child of this node, so it won't have siblings.
|
|
271
|
-
args.AutomationProvider(nullptr);
|
|
272
|
-
args.Handled(true);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
m_previousSiblingAutomationProviderRequestedToken = m_childSiteLink.PreviousSiblingAutomationProviderRequested(
|
|
276
|
-
[](const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
|
|
277
|
-
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
|
|
278
|
-
// The ContentIsland will always be the one and only child of this node, so it won't have siblings.
|
|
279
|
-
args.AutomationProvider(nullptr);
|
|
280
|
-
args.Handled(true);
|
|
281
|
-
});
|
|
250
|
+
// Use FrameworkBased to let the XamlIsland manage its own framework-level accessibility tree
|
|
251
|
+
// and raise focus events naturally. This tells the system that the child island has its own
|
|
252
|
+
// framework (WinUI/XAML) that manages automation.
|
|
253
|
+
m_childSiteLink.AutomationOption(winrt::Microsoft::UI::Content::ContentAutomationOptions::FrameworkBased);
|
|
282
254
|
|
|
255
|
+
// When using FrameworkBased mode, we don't register automation callbacks - let the XamlIsland handle its own UIA
|
|
256
|
+
// tree.
|
|
283
257
|
if (m_innerAutomationProvider) {
|
|
284
258
|
m_innerAutomationProvider->SetChildSiteLink(m_childSiteLink);
|
|
285
259
|
}
|
|
@@ -68,12 +68,15 @@ struct ContentIslandComponentView : ContentIslandComponentViewT<ContentIslandCom
|
|
|
68
68
|
winrt::Microsoft::UI::Input::InputFocusNavigationHost m_navigationHost{nullptr};
|
|
69
69
|
winrt::event_token m_navigationHostDepartFocusRequestedToken{};
|
|
70
70
|
|
|
71
|
+
// Issue #15557: Store ViewChanged subscriptions to parent ScrollViews for transform updates
|
|
72
|
+
struct ViewChangedSubscription {
|
|
73
|
+
winrt::weak_ref<winrt::Microsoft::ReactNative::Composition::ScrollViewComponentView> scrollView;
|
|
74
|
+
winrt::event_token token;
|
|
75
|
+
};
|
|
76
|
+
std::vector<ViewChangedSubscription> m_viewChangedSubscriptions;
|
|
77
|
+
|
|
71
78
|
// Automation
|
|
72
79
|
void ConfigureChildSiteLinkAutomation() noexcept;
|
|
73
|
-
winrt::event_token m_fragmentRootAutomationProviderRequestedToken{};
|
|
74
|
-
winrt::event_token m_parentAutomationProviderRequestedToken{};
|
|
75
|
-
winrt::event_token m_nextSiblingAutomationProviderRequestedToken{};
|
|
76
|
-
winrt::event_token m_previousSiblingAutomationProviderRequestedToken{};
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
|
|
@@ -107,6 +107,10 @@ void ParagraphComponentView::updateProps(
|
|
|
107
107
|
m_requireRedraw = true;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
if (oldViewProps.selectionColor != newViewProps.selectionColor) {
|
|
111
|
+
m_requireRedraw = true;
|
|
112
|
+
}
|
|
113
|
+
|
|
110
114
|
Super::updateProps(props, oldProps);
|
|
111
115
|
}
|
|
112
116
|
|
|
@@ -167,6 +171,26 @@ void ParagraphComponentView::updateTextAlignment(
|
|
|
167
171
|
m_textLayout = nullptr;
|
|
168
172
|
}
|
|
169
173
|
|
|
174
|
+
facebook::react::Tag ParagraphComponentView::hitTest(
|
|
175
|
+
facebook::react::Point pt,
|
|
176
|
+
facebook::react::Point &localPt,
|
|
177
|
+
bool ignorePointerEvents) const noexcept {
|
|
178
|
+
facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y};
|
|
179
|
+
const auto &props = paragraphProps();
|
|
180
|
+
const auto &vProps = *viewProps();
|
|
181
|
+
|
|
182
|
+
if (props.isSelectable && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 &&
|
|
183
|
+
ptLocal.y <= m_layoutMetrics.frame.size.height) {
|
|
184
|
+
// claims if pointer events are enabled for this component
|
|
185
|
+
if (ignorePointerEvents || vProps.pointerEvents == facebook::react::PointerEventsMode::Auto ||
|
|
186
|
+
vProps.pointerEvents == facebook::react::PointerEventsMode::BoxOnly) {
|
|
187
|
+
localPt = ptLocal;
|
|
188
|
+
return Tag();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return Super::hitTest(pt, localPt, ignorePointerEvents);
|
|
192
|
+
}
|
|
193
|
+
|
|
170
194
|
bool ParagraphComponentView::IsTextSelectableAtPoint(facebook::react::Point pt) noexcept {
|
|
171
195
|
// paragraph-level selectable prop is enabled
|
|
172
196
|
const auto &props = paragraphProps();
|
|
@@ -454,9 +478,14 @@ void ParagraphComponentView::DrawSelectionHighlight(
|
|
|
454
478
|
return;
|
|
455
479
|
}
|
|
456
480
|
|
|
457
|
-
// TODO: use prop selectionColor if provided
|
|
458
481
|
winrt::com_ptr<ID2D1SolidColorBrush> selectionBrush;
|
|
459
|
-
|
|
482
|
+
D2D1_COLOR_F selectionColor;
|
|
483
|
+
const auto &props = paragraphProps();
|
|
484
|
+
if (props.selectionColor) {
|
|
485
|
+
selectionColor = theme()->D2DColor(**props.selectionColor);
|
|
486
|
+
} else {
|
|
487
|
+
selectionColor = theme()->D2DPlatformColor("Highlight@40");
|
|
488
|
+
}
|
|
460
489
|
hr = renderTarget.CreateSolidColorBrush(selectionColor, selectionBrush.put());
|
|
461
490
|
|
|
462
491
|
if (FAILED(hr)) {
|
|
@@ -519,6 +548,7 @@ void ParagraphComponentView::ClearSelection() noexcept {
|
|
|
519
548
|
m_selectionStart = std::nullopt;
|
|
520
549
|
m_selectionEnd = std::nullopt;
|
|
521
550
|
m_isSelecting = false;
|
|
551
|
+
m_isWordSelecting = false;
|
|
522
552
|
if (hadSelection) {
|
|
523
553
|
// Clears selection highlight
|
|
524
554
|
DrawText();
|
|
@@ -534,7 +564,8 @@ void ParagraphComponentView::OnPointerPressed(
|
|
|
534
564
|
return;
|
|
535
565
|
}
|
|
536
566
|
|
|
537
|
-
|
|
567
|
+
// Use Tag() to get coordinates in component's local space
|
|
568
|
+
auto pp = args.GetCurrentPoint(static_cast<int32_t>(Tag()));
|
|
538
569
|
|
|
539
570
|
// Ignores right-click
|
|
540
571
|
if (pp.Properties().PointerUpdateKind() ==
|
|
@@ -545,8 +576,8 @@ void ParagraphComponentView::OnPointerPressed(
|
|
|
545
576
|
|
|
546
577
|
auto position = pp.Position();
|
|
547
578
|
|
|
548
|
-
|
|
549
|
-
|
|
579
|
+
// GetCurrentPoint(Tag()) returns position relative to component origin
|
|
580
|
+
facebook::react::Point localPt{position.X, position.Y};
|
|
550
581
|
|
|
551
582
|
std::optional<int32_t> charPosition = GetTextPositionAtPoint(localPt);
|
|
552
583
|
|
|
@@ -568,7 +599,13 @@ void ParagraphComponentView::OnPointerPressed(
|
|
|
568
599
|
|
|
569
600
|
if (isDoubleClick) {
|
|
570
601
|
SelectWordAtPosition(*charPosition);
|
|
571
|
-
|
|
602
|
+
if (m_selectionStart && m_selectionEnd) {
|
|
603
|
+
m_isWordSelecting = true;
|
|
604
|
+
m_wordAnchorStart = *m_selectionStart;
|
|
605
|
+
m_wordAnchorEnd = *m_selectionEnd;
|
|
606
|
+
m_isSelecting = true;
|
|
607
|
+
CapturePointer(args.Pointer());
|
|
608
|
+
}
|
|
572
609
|
} else {
|
|
573
610
|
// Single-click: start drag selection
|
|
574
611
|
m_selectionStart = charPosition;
|
|
@@ -610,17 +647,35 @@ void ParagraphComponentView::OnPointerMoved(
|
|
|
610
647
|
facebook::react::Point localPt{position.X, position.Y};
|
|
611
648
|
std::optional<int32_t> charPosition = GetClampedTextPosition(localPt);
|
|
612
649
|
|
|
613
|
-
if (charPosition
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
650
|
+
if (charPosition) {
|
|
651
|
+
if (m_isWordSelecting) {
|
|
652
|
+
// Extend selection by whole words
|
|
653
|
+
auto [wordStart, wordEnd] = GetWordBoundariesAtPosition(*charPosition);
|
|
654
|
+
|
|
655
|
+
if (*charPosition < m_wordAnchorStart) {
|
|
656
|
+
m_selectionStart = wordStart;
|
|
657
|
+
m_selectionEnd = m_wordAnchorEnd;
|
|
658
|
+
} else if (*charPosition >= m_wordAnchorEnd) {
|
|
659
|
+
m_selectionStart = m_wordAnchorStart;
|
|
660
|
+
m_selectionEnd = wordEnd;
|
|
661
|
+
} else {
|
|
662
|
+
m_selectionStart = m_wordAnchorStart;
|
|
663
|
+
m_selectionEnd = m_wordAnchorEnd;
|
|
664
|
+
}
|
|
665
|
+
DrawText();
|
|
666
|
+
args.Handled(true);
|
|
667
|
+
} else if (charPosition != m_selectionEnd) {
|
|
668
|
+
m_selectionEnd = charPosition;
|
|
669
|
+
DrawText();
|
|
670
|
+
args.Handled(true);
|
|
671
|
+
}
|
|
617
672
|
}
|
|
618
673
|
}
|
|
619
674
|
|
|
620
675
|
void ParagraphComponentView::OnPointerReleased(
|
|
621
676
|
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {
|
|
622
677
|
// Check for right-click to show context menu
|
|
623
|
-
auto pp = args.GetCurrentPoint(
|
|
678
|
+
auto pp = args.GetCurrentPoint(static_cast<int32_t>(Tag()));
|
|
624
679
|
if (pp.Properties().PointerUpdateKind() ==
|
|
625
680
|
winrt::Microsoft::ReactNative::Composition::Input::PointerUpdateKind::RightButtonReleased) {
|
|
626
681
|
const auto &props = paragraphProps();
|
|
@@ -637,6 +692,7 @@ void ParagraphComponentView::OnPointerReleased(
|
|
|
637
692
|
}
|
|
638
693
|
|
|
639
694
|
m_isSelecting = false;
|
|
695
|
+
m_isWordSelecting = false;
|
|
640
696
|
|
|
641
697
|
ReleasePointerCapture(args.Pointer());
|
|
642
698
|
|
|
@@ -661,6 +717,7 @@ void ParagraphComponentView::OnPointerCaptureLost() noexcept {
|
|
|
661
717
|
// Pointer capture was lost stop any active selection drag
|
|
662
718
|
if (m_isSelecting) {
|
|
663
719
|
m_isSelecting = false;
|
|
720
|
+
m_isWordSelecting = false;
|
|
664
721
|
|
|
665
722
|
if (!m_selectionStart || !m_selectionEnd || *m_selectionStart == *m_selectionEnd) {
|
|
666
723
|
m_selectionStart = std::nullopt;
|
|
@@ -711,12 +768,17 @@ void ParagraphComponentView::CopySelectionToClipboard() noexcept {
|
|
|
711
768
|
winrt::Windows::ApplicationModel::DataTransfer::Clipboard::SetContent(dataPackage);
|
|
712
769
|
}
|
|
713
770
|
|
|
714
|
-
|
|
771
|
+
std::pair<int32_t, int32_t> ParagraphComponentView::GetWordBoundariesAtPosition(int32_t charPosition) noexcept {
|
|
715
772
|
const std::wstring utf16Text{facebook::react::WindowsTextLayoutManager::GetTransformedText(m_attributedStringBox)};
|
|
716
773
|
const int32_t textLength = static_cast<int32_t>(utf16Text.length());
|
|
717
774
|
|
|
718
|
-
if (utf16Text.empty() || charPosition < 0
|
|
719
|
-
return;
|
|
775
|
+
if (utf16Text.empty() || charPosition < 0) {
|
|
776
|
+
return {0, 0};
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
charPosition = std::min(charPosition, textLength - 1);
|
|
780
|
+
if (charPosition < 0) {
|
|
781
|
+
return {0, 0};
|
|
720
782
|
}
|
|
721
783
|
|
|
722
784
|
int32_t wordStart = charPosition;
|
|
@@ -749,6 +811,12 @@ void ParagraphComponentView::SelectWordAtPosition(int32_t charPosition) noexcept
|
|
|
749
811
|
}
|
|
750
812
|
}
|
|
751
813
|
|
|
814
|
+
return {wordStart, wordEnd};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
void ParagraphComponentView::SelectWordAtPosition(int32_t charPosition) noexcept {
|
|
818
|
+
auto [wordStart, wordEnd] = GetWordBoundariesAtPosition(charPosition);
|
|
819
|
+
|
|
752
820
|
if (wordEnd > wordStart) {
|
|
753
821
|
SetSelection(wordStart, wordEnd);
|
|
754
822
|
DrawText();
|
|
@@ -49,6 +49,11 @@ struct ParagraphComponentView : ParagraphComponentViewT<ParagraphComponentView,
|
|
|
49
49
|
static facebook::react::SharedViewProps defaultProps() noexcept;
|
|
50
50
|
const facebook::react::ParagraphProps ¶graphProps() const noexcept;
|
|
51
51
|
|
|
52
|
+
facebook::react::Tag hitTest(
|
|
53
|
+
facebook::react::Point pt,
|
|
54
|
+
facebook::react::Point &localPt,
|
|
55
|
+
bool ignorePointerEvents = false) const noexcept override;
|
|
56
|
+
|
|
52
57
|
// Returns true when text is selectable
|
|
53
58
|
bool focusable() const noexcept override;
|
|
54
59
|
|
|
@@ -90,16 +95,13 @@ struct ParagraphComponentView : ParagraphComponentViewT<ParagraphComponentView,
|
|
|
90
95
|
std::optional<int32_t> GetClampedTextPosition(facebook::react::Point pt) noexcept;
|
|
91
96
|
std::string GetSelectedText() const noexcept;
|
|
92
97
|
|
|
93
|
-
// Copies currently selected text to the system clipboard
|
|
94
98
|
void CopySelectionToClipboard() noexcept;
|
|
95
99
|
|
|
96
|
-
// Selects the word at the given character position
|
|
97
100
|
void SelectWordAtPosition(int32_t charPosition) noexcept;
|
|
101
|
+
std::pair<int32_t, int32_t> GetWordBoundariesAtPosition(int32_t charPosition) noexcept;
|
|
98
102
|
|
|
99
|
-
// Shows a context menu with Copy/Select All options on right-click
|
|
100
103
|
void ShowContextMenu() noexcept;
|
|
101
104
|
|
|
102
|
-
// m_selectionStart <= m_selectionEnd
|
|
103
105
|
void SetSelection(int32_t start, int32_t end) noexcept;
|
|
104
106
|
|
|
105
107
|
winrt::com_ptr<::IDWriteTextLayout> m_textLayout;
|
|
@@ -113,6 +115,11 @@ struct ParagraphComponentView : ParagraphComponentViewT<ParagraphComponentView,
|
|
|
113
115
|
std::optional<int32_t> m_selectionEnd;
|
|
114
116
|
bool m_isSelecting{false};
|
|
115
117
|
|
|
118
|
+
// Double click + drag selection
|
|
119
|
+
bool m_isWordSelecting{false};
|
|
120
|
+
int32_t m_wordAnchorStart{0};
|
|
121
|
+
int32_t m_wordAnchorEnd{0};
|
|
122
|
+
|
|
116
123
|
// Double-click detection
|
|
117
124
|
std::chrono::steady_clock::time_point m_lastClickTime{};
|
|
118
125
|
std::optional<int32_t> m_lastClickPosition;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
#include "ScrollViewComponentView.h"
|
|
8
8
|
|
|
9
|
+
#include <Fabric/ComponentView.h>
|
|
9
10
|
#include <Utils/ValueUtils.h>
|
|
10
11
|
|
|
11
12
|
#pragma warning(push)
|
|
@@ -19,6 +20,8 @@
|
|
|
19
20
|
#include <AutoDraw.h>
|
|
20
21
|
#include <Fabric/DWriteHelpers.h>
|
|
21
22
|
#include <unicode.h>
|
|
23
|
+
#include <functional>
|
|
24
|
+
#include "ContentIslandComponentView.h"
|
|
22
25
|
#include "JSValueReader.h"
|
|
23
26
|
#include "RootComponentView.h"
|
|
24
27
|
|
|
@@ -884,6 +887,13 @@ void ScrollViewComponentView::updateContentVisualSize() noexcept {
|
|
|
884
887
|
|
|
885
888
|
void ScrollViewComponentView::prepareForRecycle() noexcept {}
|
|
886
889
|
|
|
890
|
+
void ScrollViewComponentView::updateChildrenClippingPath(
|
|
891
|
+
facebook::react::LayoutMetrics const & /*layoutMetrics*/,
|
|
892
|
+
const facebook::react::ViewProps & /*viewProps*/) noexcept {
|
|
893
|
+
// No-op: ScrollView mounts children into m_scrollVisual (not Visual()),
|
|
894
|
+
// and scroll visuals inherently clip their content.
|
|
895
|
+
}
|
|
896
|
+
|
|
887
897
|
/*
|
|
888
898
|
ScrollViewComponentView::ScrollInteractionTrackerOwner::ScrollInteractionTrackerOwner(
|
|
889
899
|
ScrollViewComponentView *outer)
|
|
@@ -1325,6 +1335,10 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
|
|
|
1325
1335
|
m_allowNextScrollNoMatterWhat = false;
|
|
1326
1336
|
}
|
|
1327
1337
|
}
|
|
1338
|
+
|
|
1339
|
+
// Issue #15557: Notify listeners that scroll position has changed,
|
|
1340
|
+
// so ContentIslandComponentView can update LocalToParentTransformMatrix
|
|
1341
|
+
FireViewChanged();
|
|
1328
1342
|
});
|
|
1329
1343
|
|
|
1330
1344
|
m_scrollBeginDragRevoker = m_scrollVisual.ScrollBeginDrag(
|
|
@@ -1332,6 +1346,9 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
|
|
|
1332
1346
|
[this](
|
|
1333
1347
|
winrt::IInspectable const & /*sender*/,
|
|
1334
1348
|
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
|
|
1349
|
+
// Issue #15557: Notify listeners that scroll position has changed
|
|
1350
|
+
FireViewChanged();
|
|
1351
|
+
|
|
1335
1352
|
m_allowNextScrollNoMatterWhat = true; // Ensure next scroll event is recorded, regardless of throttle
|
|
1336
1353
|
updateStateWithContentOffset();
|
|
1337
1354
|
auto eventEmitter = GetEventEmitter();
|
|
@@ -1478,4 +1495,20 @@ void ScrollViewComponentView::updateShowsVerticalScrollIndicator(bool value) noe
|
|
|
1478
1495
|
void ScrollViewComponentView::updateDecelerationRate(float value) noexcept {
|
|
1479
1496
|
m_scrollVisual.SetDecelerationRate({value, value, value});
|
|
1480
1497
|
}
|
|
1498
|
+
|
|
1499
|
+
// Issue #15557: Notify listeners that scroll position has changed.
|
|
1500
|
+
// ContentIslandComponentView subscribes to this to update LocalToParentTransformMatrix.
|
|
1501
|
+
void ScrollViewComponentView::FireViewChanged() noexcept {
|
|
1502
|
+
m_viewChangedEvent(*this, nullptr);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// Issue #15557: Event accessors for ViewChanged
|
|
1506
|
+
winrt::event_token ScrollViewComponentView::ViewChanged(
|
|
1507
|
+
winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable> const &handler) noexcept {
|
|
1508
|
+
return m_viewChangedEvent.add(handler);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
void ScrollViewComponentView::ViewChanged(winrt::event_token const &token) noexcept {
|
|
1512
|
+
m_viewChangedEvent.remove(token);
|
|
1513
|
+
}
|
|
1481
1514
|
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
|
|
@@ -118,6 +118,18 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
|
|
|
118
118
|
double getVerticalSize() noexcept;
|
|
119
119
|
double getHorizontalSize() noexcept;
|
|
120
120
|
|
|
121
|
+
// Issue #15557: Event accessors for ViewChanged (used by ContentIslandComponentView for transform update)
|
|
122
|
+
winrt::event_token ViewChanged(
|
|
123
|
+
winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable> const &handler) noexcept;
|
|
124
|
+
void ViewChanged(winrt::event_token const &token) noexcept;
|
|
125
|
+
|
|
126
|
+
protected:
|
|
127
|
+
// ScrollView mounts children into m_scrollVisual (not Visual()), and scroll visuals
|
|
128
|
+
// inherently clip their content, so we skip the children container clipping logic.
|
|
129
|
+
void updateChildrenClippingPath(
|
|
130
|
+
facebook::react::LayoutMetrics const &layoutMetrics,
|
|
131
|
+
const facebook::react::ViewProps &viewProps) noexcept override;
|
|
132
|
+
|
|
121
133
|
private:
|
|
122
134
|
void updateDecelerationRate(float value) noexcept;
|
|
123
135
|
void updateContentVisualSize() noexcept;
|
|
@@ -129,6 +141,8 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
|
|
|
129
141
|
bool scrollRight(float delta, bool animate) noexcept;
|
|
130
142
|
void updateBackgroundColor(const facebook::react::SharedColor &color) noexcept;
|
|
131
143
|
void updateStateWithContentOffset() noexcept;
|
|
144
|
+
// Issue #15557: Notify listeners that scroll position has changed
|
|
145
|
+
void FireViewChanged() noexcept;
|
|
132
146
|
facebook::react::ScrollViewEventEmitter::Metrics getScrollMetrics(
|
|
133
147
|
facebook::react::SharedViewEventEmitter const &eventEmitter,
|
|
134
148
|
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) noexcept;
|
|
@@ -160,6 +174,9 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
|
|
|
160
174
|
bool m_allowNextScrollNoMatterWhat{false};
|
|
161
175
|
std::chrono::steady_clock::time_point m_lastScrollEventTime{};
|
|
162
176
|
std::shared_ptr<facebook::react::ScrollViewShadowNode::ConcreteState const> m_state;
|
|
177
|
+
|
|
178
|
+
// Issue #15557: Event for notifying listeners when scroll position changes
|
|
179
|
+
winrt::event<winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>> m_viewChangedEvent;
|
|
163
180
|
};
|
|
164
181
|
|
|
165
182
|
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
|