react-native-windows 0.81.0-preview.1 → 0.81.0-preview.4
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/Microsoft.ReactNative/CompositionSwitcher.idl +163 -162
- package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +104 -4
- package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +41 -33
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +19 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +102 -18
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +1 -0
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp +21 -2
- package/Microsoft.ReactNative/ReactHost/ReactHost.cpp +39 -6
- package/PropertySheets/Generated/PackageVersion.g.props +2 -2
- package/package.json +1 -1
- package/template/metro.config.js +2 -4
- package/templates/cpp-app/metro.config.js +2 -4
- package/templates/cpp-lib/example/metro.config.js +2 -3
|
@@ -1,178 +1,179 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
|
|
4
|
-
#include "NamespaceRedirect.h"
|
|
5
4
|
#include "DocString.h"
|
|
5
|
+
#include "NamespaceRedirect.h"
|
|
6
6
|
import "Composition.Input.idl";
|
|
7
7
|
|
|
8
|
-
namespace Microsoft.ReactNative.Composition.Experimental
|
|
8
|
+
namespace Microsoft.ReactNative.Composition.Experimental {
|
|
9
|
+
enum CompositionStretch {
|
|
10
|
+
None,
|
|
11
|
+
Fill,
|
|
12
|
+
Uniform,
|
|
13
|
+
UniformToFill,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
enum BackfaceVisibility { Inherit, Visible, Hidden };
|
|
17
|
+
|
|
18
|
+
enum AnimationClass {
|
|
19
|
+
None = 0,
|
|
20
|
+
ScrollBar,
|
|
21
|
+
ScrollBarThumbHorizontal,
|
|
22
|
+
ScrollBarThumbVertical,
|
|
23
|
+
SwitchThumb,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
enum SnapPointsAlignment {
|
|
27
|
+
Near = 0, // Start alignment (left/top)
|
|
28
|
+
Center = 1, // Center alignment
|
|
29
|
+
Far = 2, // End alignment (right/bottom)
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
[webhosthidden][uuid("172def51-9e1a-4e3c-841a-e5a470065acc")] // uuid needed for empty interfaces
|
|
33
|
+
[version(0)][experimental] interface IBrush {}
|
|
34
|
+
|
|
35
|
+
[webhosthidden][experimental] interface IDrawingSurfaceBrush
|
|
36
|
+
requires IBrush
|
|
37
|
+
{
|
|
38
|
+
void HorizontalAlignmentRatio(Single value);
|
|
39
|
+
void VerticalAlignmentRatio(Single value);
|
|
40
|
+
void Stretch(CompositionStretch value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[webhosthidden][experimental] interface IDropShadow {
|
|
44
|
+
void Offset(Windows.Foundation.Numerics.Vector3 value);
|
|
45
|
+
void Opacity(Single value);
|
|
46
|
+
void BlurRadius(Single value);
|
|
47
|
+
void Color(Windows.UI.Color value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[webhosthidden][experimental] interface IVisual {
|
|
51
|
+
void InsertAt(IVisual visual, Int32 index);
|
|
52
|
+
void Remove(IVisual visual);
|
|
53
|
+
IVisual GetAt(UInt32 index);
|
|
54
|
+
void Opacity(Single opacity);
|
|
55
|
+
void Scale(Windows.Foundation.Numerics.Vector3 scale);
|
|
56
|
+
void TransformMatrix(Windows.Foundation.Numerics.Matrix4x4 transform);
|
|
57
|
+
void RotationAngle(Single angle);
|
|
58
|
+
void IsVisible(Boolean isVisible);
|
|
59
|
+
void Size(Windows.Foundation.Numerics.Vector2 size);
|
|
60
|
+
void Offset(Windows.Foundation.Numerics.Vector3 offset);
|
|
61
|
+
void Offset(Windows.Foundation.Numerics.Vector3 offset, Windows.Foundation.Numerics.Vector3 relativeAdjustment);
|
|
62
|
+
void RelativeSizeWithOffset(
|
|
63
|
+
Windows.Foundation.Numerics.Vector2 size, Windows.Foundation.Numerics.Vector2 relativeSizeAdjustment);
|
|
64
|
+
BackfaceVisibility BackfaceVisibility {
|
|
65
|
+
get;
|
|
66
|
+
set;
|
|
67
|
+
};
|
|
68
|
+
String Comment {
|
|
69
|
+
get;
|
|
70
|
+
set;
|
|
71
|
+
};
|
|
72
|
+
void AnimationClass(AnimationClass value);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
[webhosthidden][experimental] interface ISpriteVisual
|
|
76
|
+
requires IVisual
|
|
77
|
+
{
|
|
78
|
+
void Brush(IBrush brush);
|
|
79
|
+
void Shadow(IDropShadow shadow);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
[webhosthidden][experimental] interface IRoundedRectangleVisual
|
|
83
|
+
requires IVisual
|
|
9
84
|
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
85
|
+
void Brush(IBrush brush);
|
|
86
|
+
void CornerRadius(Windows.Foundation.Numerics.Vector2 value);
|
|
87
|
+
void StrokeBrush(IBrush brush);
|
|
88
|
+
void StrokeThickness(Single value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
[webhosthidden][experimental] interface IScrollPositionChangedArgs {
|
|
92
|
+
Windows.Foundation.Numerics.Vector2 Position {
|
|
93
|
+
get;
|
|
16
94
|
};
|
|
95
|
+
}
|
|
17
96
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
97
|
+
[webhosthidden][experimental] interface IScrollVisual
|
|
98
|
+
requires IVisual
|
|
99
|
+
{
|
|
100
|
+
void Brush(IBrush brush);
|
|
101
|
+
void ScrollEnabled(Boolean isScrollEnabled);
|
|
102
|
+
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollPositionChanged;
|
|
103
|
+
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollBeginDrag;
|
|
104
|
+
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollEndDrag;
|
|
105
|
+
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumBegin;
|
|
106
|
+
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumEnd;
|
|
107
|
+
void ContentSize(Windows.Foundation.Numerics.Vector2 size);
|
|
108
|
+
Windows.Foundation.Numerics.Vector3 ScrollPosition {
|
|
109
|
+
get;
|
|
110
|
+
};
|
|
111
|
+
void ScrollBy(Windows.Foundation.Numerics.Vector3 offset, Boolean animate);
|
|
112
|
+
void TryUpdatePosition(Windows.Foundation.Numerics.Vector3 position, Boolean animate);
|
|
113
|
+
void OnPointerPressed(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args);
|
|
114
|
+
void SetDecelerationRate(Windows.Foundation.Numerics.Vector3 decelerationRate);
|
|
115
|
+
void SetMaximumZoomScale(Single maximumZoomScale);
|
|
116
|
+
void SetMinimumZoomScale(Single minimumZoomScale);
|
|
117
|
+
Boolean Horizontal;
|
|
118
|
+
void SetSnapPoints(
|
|
119
|
+
Boolean snapToStart, Boolean snapToEnd, Windows.Foundation.Collections.IVectorView<Single> offsets);
|
|
120
|
+
void PagingEnabled(Boolean pagingEnabled);
|
|
121
|
+
void SnapToInterval(Single interval);
|
|
122
|
+
void SnapToAlignment(SnapPointsAlignment alignment);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
[webhosthidden][experimental] interface IActivityVisual
|
|
126
|
+
requires IVisual
|
|
127
|
+
{
|
|
128
|
+
void Size(Single value);
|
|
129
|
+
void Brush(IBrush brush);
|
|
130
|
+
void StartAnimation();
|
|
131
|
+
void StopAnimation();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
[webhosthidden][experimental] interface ICaretVisual {
|
|
135
|
+
IVisual InnerVisual {
|
|
136
|
+
get;
|
|
23
137
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
ScrollBarThumbHorizontal,
|
|
30
|
-
ScrollBarThumbVertical,
|
|
31
|
-
SwitchThumb,
|
|
138
|
+
void Size(Windows.Foundation.Numerics.Vector2 size);
|
|
139
|
+
void Position(Windows.Foundation.Numerics.Vector2 position);
|
|
140
|
+
Boolean IsVisible {
|
|
141
|
+
get;
|
|
142
|
+
set;
|
|
32
143
|
};
|
|
144
|
+
void Brush(IBrush brush);
|
|
145
|
+
}
|
|
33
146
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
[webhosthidden]
|
|
60
|
-
[experimental]
|
|
61
|
-
interface IVisual
|
|
62
|
-
{
|
|
63
|
-
void InsertAt(IVisual visual, Int32 index);
|
|
64
|
-
void Remove(IVisual visual);
|
|
65
|
-
IVisual GetAt(UInt32 index);
|
|
66
|
-
void Opacity(Single opacity);
|
|
67
|
-
void Scale(Windows.Foundation.Numerics.Vector3 scale);
|
|
68
|
-
void TransformMatrix(Windows.Foundation.Numerics.Matrix4x4 transform);
|
|
69
|
-
void RotationAngle(Single angle);
|
|
70
|
-
void IsVisible(Boolean isVisible);
|
|
71
|
-
void Size(Windows.Foundation.Numerics.Vector2 size);
|
|
72
|
-
void Offset(Windows.Foundation.Numerics.Vector3 offset);
|
|
73
|
-
void Offset(Windows.Foundation.Numerics.Vector3 offset, Windows.Foundation.Numerics.Vector3 relativeAdjustment);
|
|
74
|
-
void RelativeSizeWithOffset(Windows.Foundation.Numerics.Vector2 size, Windows.Foundation.Numerics.Vector2 relativeSizeAdjustment);
|
|
75
|
-
BackfaceVisibility BackfaceVisibility{ get; set; };
|
|
76
|
-
String Comment { get; set; };
|
|
77
|
-
void AnimationClass(AnimationClass value);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
[webhosthidden]
|
|
81
|
-
[experimental]
|
|
82
|
-
interface ISpriteVisual requires IVisual
|
|
83
|
-
{
|
|
84
|
-
void Brush(IBrush brush);
|
|
85
|
-
void Shadow(IDropShadow shadow);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
[webhosthidden]
|
|
89
|
-
[experimental]
|
|
90
|
-
interface IRoundedRectangleVisual requires IVisual
|
|
91
|
-
{
|
|
92
|
-
void Brush(IBrush brush);
|
|
93
|
-
void CornerRadius(Windows.Foundation.Numerics.Vector2 value);
|
|
94
|
-
void StrokeBrush(IBrush brush);
|
|
95
|
-
void StrokeThickness(Single value);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
[webhosthidden]
|
|
99
|
-
[experimental]
|
|
100
|
-
interface IScrollPositionChangedArgs
|
|
101
|
-
{
|
|
102
|
-
Windows.Foundation.Numerics.Vector2 Position { get; };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
[webhosthidden]
|
|
106
|
-
[experimental]
|
|
107
|
-
interface IScrollVisual requires IVisual
|
|
108
|
-
{
|
|
109
|
-
void Brush(IBrush brush);
|
|
110
|
-
void ScrollEnabled(Boolean isScrollEnabled);
|
|
111
|
-
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollPositionChanged;
|
|
112
|
-
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollBeginDrag;
|
|
113
|
-
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollEndDrag;
|
|
114
|
-
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumBegin;
|
|
115
|
-
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumEnd;
|
|
116
|
-
void ContentSize(Windows.Foundation.Numerics.Vector2 size);
|
|
117
|
-
Windows.Foundation.Numerics.Vector3 ScrollPosition { get; };
|
|
118
|
-
void ScrollBy(Windows.Foundation.Numerics.Vector3 offset, Boolean animate);
|
|
119
|
-
void TryUpdatePosition(Windows.Foundation.Numerics.Vector3 position, Boolean animate);
|
|
120
|
-
void OnPointerPressed(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args);
|
|
121
|
-
void SetDecelerationRate(Windows.Foundation.Numerics.Vector3 decelerationRate);
|
|
122
|
-
void SetMaximumZoomScale(Single maximumZoomScale);
|
|
123
|
-
void SetMinimumZoomScale(Single minimumZoomScale);
|
|
124
|
-
Boolean Horizontal;
|
|
125
|
-
void SetSnapPoints(Boolean snapToStart, Boolean snapToEnd, Windows.Foundation.Collections.IVectorView<Single> offsets);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
[webhosthidden]
|
|
129
|
-
[experimental]
|
|
130
|
-
interface IActivityVisual requires IVisual
|
|
131
|
-
{
|
|
132
|
-
void Size(Single value);
|
|
133
|
-
void Brush(IBrush brush);
|
|
134
|
-
void StartAnimation();
|
|
135
|
-
void StopAnimation();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
[webhosthidden]
|
|
139
|
-
[experimental]
|
|
140
|
-
interface ICaretVisual
|
|
141
|
-
{
|
|
142
|
-
IVisual InnerVisual { get; };
|
|
143
|
-
void Size(Windows.Foundation.Numerics.Vector2 size);
|
|
144
|
-
void Position(Windows.Foundation.Numerics.Vector2 position);
|
|
145
|
-
Boolean IsVisible { get; set; };
|
|
146
|
-
void Brush(IBrush brush);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
[webhosthidden]
|
|
150
|
-
[experimental]
|
|
151
|
-
interface IFocusVisual
|
|
152
|
-
{
|
|
153
|
-
IVisual InnerVisual { get; };
|
|
154
|
-
Boolean IsFocused { get; set; };
|
|
155
|
-
Single ScaleFactor { get; set; };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
[webhosthidden]
|
|
159
|
-
[experimental]
|
|
160
|
-
interface ICompositionContext
|
|
161
|
-
{
|
|
162
|
-
ISpriteVisual CreateSpriteVisual();
|
|
163
|
-
IScrollVisual CreateScrollerVisual();
|
|
164
|
-
IRoundedRectangleVisual CreateRoundedRectangleVisual();
|
|
165
|
-
IActivityVisual CreateActivityVisual();
|
|
166
|
-
ICaretVisual CreateCaretVisual();
|
|
167
|
-
IFocusVisual CreateFocusVisual();
|
|
168
|
-
IDropShadow CreateDropShadow();
|
|
169
|
-
IBrush CreateColorBrush(Windows.UI.Color color);
|
|
170
|
-
IDrawingSurfaceBrush CreateDrawingSurfaceBrush(Windows.Foundation.Size surfaceSize,
|
|
147
|
+
[webhosthidden][experimental] interface IFocusVisual {
|
|
148
|
+
IVisual InnerVisual {
|
|
149
|
+
get;
|
|
150
|
+
};
|
|
151
|
+
Boolean IsFocused {
|
|
152
|
+
get;
|
|
153
|
+
set;
|
|
154
|
+
};
|
|
155
|
+
Single ScaleFactor {
|
|
156
|
+
get;
|
|
157
|
+
set;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
[webhosthidden][experimental] interface ICompositionContext {
|
|
162
|
+
ISpriteVisual CreateSpriteVisual();
|
|
163
|
+
IScrollVisual CreateScrollerVisual();
|
|
164
|
+
IRoundedRectangleVisual CreateRoundedRectangleVisual();
|
|
165
|
+
IActivityVisual CreateActivityVisual();
|
|
166
|
+
ICaretVisual CreateCaretVisual();
|
|
167
|
+
IFocusVisual CreateFocusVisual();
|
|
168
|
+
IDropShadow CreateDropShadow();
|
|
169
|
+
IBrush CreateColorBrush(Windows.UI.Color color);
|
|
170
|
+
IDrawingSurfaceBrush CreateDrawingSurfaceBrush(
|
|
171
|
+
Windows.Foundation.Size surfaceSize,
|
|
171
172
|
Windows.Graphics.DirectX.DirectXPixelFormat pixelFormat,
|
|
172
173
|
Windows.Graphics.DirectX.DirectXAlphaMode alphaMode);
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
// TODO Add and hook up to rootnode - to notify the tree
|
|
176
|
+
// event Windows.Foundation.EventHandler<RenderingDeviceReplacedArgs> RenderingDeviceReplaced;
|
|
177
|
+
}
|
|
177
178
|
|
|
178
|
-
} // namespace Microsoft.ReactNative.Composition.Experimental
|
|
179
|
+
} // namespace Microsoft.ReactNative.Composition. Experimental
|
|
@@ -910,6 +910,31 @@ struct CompScrollerVisual : winrt::implements<
|
|
|
910
910
|
m_snapToOffsets.push_back(offset);
|
|
911
911
|
}
|
|
912
912
|
}
|
|
913
|
+
// Match Paper behavior: snapToOffsets disables pagingEnabled and snapToInterval
|
|
914
|
+
if (!m_snapToOffsets.empty()) {
|
|
915
|
+
m_pagingEnabled = false;
|
|
916
|
+
m_snapToInterval = 0.0f;
|
|
917
|
+
}
|
|
918
|
+
ConfigureSnapInertiaModifiers();
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
void PagingEnabled(bool pagingEnabled) noexcept {
|
|
922
|
+
m_pagingEnabled = pagingEnabled;
|
|
923
|
+
ConfigureSnapInertiaModifiers();
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
void SnapToInterval(float interval) noexcept {
|
|
927
|
+
m_snapToInterval = interval;
|
|
928
|
+
// Match Paper behavior: snapToOffsets disables snapToInterval
|
|
929
|
+
if (!m_snapToOffsets.empty()) {
|
|
930
|
+
m_snapToInterval = 0.0f;
|
|
931
|
+
}
|
|
932
|
+
ConfigureSnapInertiaModifiers();
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
void SnapToAlignment(
|
|
936
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment alignment) noexcept {
|
|
937
|
+
m_snapToAlignment = alignment;
|
|
913
938
|
ConfigureSnapInertiaModifiers();
|
|
914
939
|
}
|
|
915
940
|
|
|
@@ -934,9 +959,13 @@ struct CompScrollerVisual : winrt::implements<
|
|
|
934
959
|
}
|
|
935
960
|
|
|
936
961
|
void Size(winrt::Windows::Foundation::Numerics::float2 const &size) noexcept {
|
|
962
|
+
bool sizeChanged = (m_visualSize.x != size.x || m_visualSize.y != size.y);
|
|
937
963
|
m_visualSize = size;
|
|
938
964
|
m_visual.Size(size);
|
|
939
965
|
UpdateMaxPosition();
|
|
966
|
+
if (sizeChanged && m_pagingEnabled) {
|
|
967
|
+
ConfigureSnapInertiaModifiers();
|
|
968
|
+
}
|
|
940
969
|
}
|
|
941
970
|
|
|
942
971
|
void Offset(winrt::Windows::Foundation::Numerics::float3 const &offset) noexcept {
|
|
@@ -1046,9 +1075,13 @@ struct CompScrollerVisual : winrt::implements<
|
|
|
1046
1075
|
}
|
|
1047
1076
|
|
|
1048
1077
|
void ContentSize(winrt::Windows::Foundation::Numerics::float2 const &size) noexcept {
|
|
1078
|
+
bool sizeChanged = (m_contentSize.x != size.x || m_contentSize.y != size.y);
|
|
1049
1079
|
m_contentSize = size;
|
|
1050
1080
|
m_contentVisual.Size(size);
|
|
1051
1081
|
UpdateMaxPosition();
|
|
1082
|
+
if (sizeChanged && m_pagingEnabled) {
|
|
1083
|
+
ConfigureSnapInertiaModifiers();
|
|
1084
|
+
}
|
|
1052
1085
|
}
|
|
1053
1086
|
|
|
1054
1087
|
winrt::Windows::Foundation::Numerics::float3 ScrollPosition() const noexcept {
|
|
@@ -1155,11 +1188,73 @@ struct CompScrollerVisual : winrt::implements<
|
|
|
1155
1188
|
// Collect and deduplicate all snap positions
|
|
1156
1189
|
std::vector<float> snapPositions;
|
|
1157
1190
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1191
|
+
// Priority: snapToOffsets > snapToInterval > pagingEnabled
|
|
1192
|
+
if (!m_snapToOffsets.empty()) {
|
|
1193
|
+
// Use explicit snap points when snapToOffsets is set (highest priority)
|
|
1194
|
+
if (m_snapToStart) {
|
|
1195
|
+
snapPositions.push_back(0.0f);
|
|
1196
|
+
}
|
|
1197
|
+
snapPositions.insert(snapPositions.end(), m_snapToOffsets.begin(), m_snapToOffsets.end());
|
|
1198
|
+
} else if (m_snapToInterval > 0.0f) {
|
|
1199
|
+
// Generate snap points at interval positions
|
|
1200
|
+
float viewportSize = m_horizontal ? visualSize.x : visualSize.y;
|
|
1201
|
+
float maxScroll =
|
|
1202
|
+
m_horizontal ? std::max(contentSize.x - visualSize.x, 0.0f) : std::max(contentSize.y - visualSize.y, 0.0f);
|
|
1203
|
+
|
|
1204
|
+
// Calculate alignment offset based on snapToAlignment
|
|
1205
|
+
float alignmentOffset = 0.0f;
|
|
1206
|
+
using SnapPointsAlignment = winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment;
|
|
1207
|
+
if (m_snapToAlignment == SnapPointsAlignment::Center) {
|
|
1208
|
+
alignmentOffset = -viewportSize / 2.0f;
|
|
1209
|
+
} else if (m_snapToAlignment == SnapPointsAlignment::Far) {
|
|
1210
|
+
alignmentOffset = -viewportSize;
|
|
1211
|
+
}
|
|
1212
|
+
// Near (start) alignment has no offset (alignmentOffset = 0)
|
|
1213
|
+
|
|
1214
|
+
// Generate snap points at interval positions with alignment offset
|
|
1215
|
+
for (float position = alignmentOffset; position <= maxScroll; position += m_snapToInterval) {
|
|
1216
|
+
if (position >= 0.0f) { // Only include positions >= 0
|
|
1217
|
+
snapPositions.push_back(position);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// Ensure we have at least the start position
|
|
1222
|
+
if (snapPositions.empty() || snapPositions.front() > 0.0f) {
|
|
1223
|
+
snapPositions.insert(snapPositions.begin(), 0.0f);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Ensure the end position is included if not already
|
|
1227
|
+
if (!snapPositions.empty() && snapPositions.back() < maxScroll) {
|
|
1228
|
+
snapPositions.push_back(maxScroll);
|
|
1229
|
+
}
|
|
1230
|
+
} else if (m_pagingEnabled) {
|
|
1231
|
+
// Generate snap points at viewport intervals (paging)
|
|
1232
|
+
float viewportSize = m_horizontal ? visualSize.x : visualSize.y;
|
|
1233
|
+
float maxScroll =
|
|
1234
|
+
m_horizontal ? std::max(contentSize.x - visualSize.x, 0.0f) : std::max(contentSize.y - visualSize.y, 0.0f);
|
|
1235
|
+
|
|
1236
|
+
// Only generate paging snap points if viewport size is valid
|
|
1237
|
+
if (viewportSize > 0 && maxScroll > 0) {
|
|
1238
|
+
// Add snap points at each page (viewport size) interval
|
|
1239
|
+
for (float position = 0.0f; position <= maxScroll; position += viewportSize) {
|
|
1240
|
+
snapPositions.push_back(position);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// Ensure the end position is included if not already
|
|
1244
|
+
if (!snapPositions.empty() && snapPositions.back() < maxScroll) {
|
|
1245
|
+
snapPositions.push_back(maxScroll);
|
|
1246
|
+
}
|
|
1247
|
+
} else {
|
|
1248
|
+
// If content fits in viewport or invalid size, just snap to start
|
|
1249
|
+
snapPositions.push_back(0.0f);
|
|
1250
|
+
}
|
|
1251
|
+
} else {
|
|
1252
|
+
// No interval or paging - use explicit snap points only
|
|
1253
|
+
if (m_snapToStart) {
|
|
1254
|
+
snapPositions.push_back(0.0f);
|
|
1255
|
+
}
|
|
1160
1256
|
}
|
|
1161
1257
|
|
|
1162
|
-
snapPositions.insert(snapPositions.end(), m_snapToOffsets.begin(), m_snapToOffsets.end());
|
|
1163
1258
|
std::sort(snapPositions.begin(), snapPositions.end());
|
|
1164
1259
|
snapPositions.erase(std::unique(snapPositions.begin(), snapPositions.end()), snapPositions.end());
|
|
1165
1260
|
|
|
@@ -1222,7 +1317,8 @@ struct CompScrollerVisual : winrt::implements<
|
|
|
1222
1317
|
restingValues.push_back(restingValue);
|
|
1223
1318
|
}
|
|
1224
1319
|
|
|
1225
|
-
|
|
1320
|
+
// Only add snapToEnd handling when NOT using pagingEnabled or snapToInterval (they already include end position)
|
|
1321
|
+
if (m_snapToEnd && !m_pagingEnabled && m_snapToInterval <= 0.0f) {
|
|
1226
1322
|
auto endRestingValue = TTypeRedirects::InteractionTrackerInertiaRestingValue::Create(compositor);
|
|
1227
1323
|
|
|
1228
1324
|
// Create property sets to dynamically compute content - visual size
|
|
@@ -1286,6 +1382,10 @@ struct CompScrollerVisual : winrt::implements<
|
|
|
1286
1382
|
bool m_horizontal{false};
|
|
1287
1383
|
bool m_snapToStart{true};
|
|
1288
1384
|
bool m_snapToEnd{true};
|
|
1385
|
+
bool m_pagingEnabled{false};
|
|
1386
|
+
float m_snapToInterval{0.0f};
|
|
1387
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment m_snapToAlignment{
|
|
1388
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment::Near};
|
|
1289
1389
|
std::vector<float> m_snapToOffsets;
|
|
1290
1390
|
bool m_inertia{false};
|
|
1291
1391
|
bool m_custom{false};
|
package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
CHANGED
|
@@ -35,26 +35,38 @@ struct ModalHostState
|
|
|
35
35
|
struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::Foundation::IInspectable>,
|
|
36
36
|
::Microsoft::ReactNativeSpecs::BaseModalHostView<ModalHostView> {
|
|
37
37
|
~ModalHostView() {
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (m_popUp) {
|
|
39
|
+
// Unregister closing event handler
|
|
40
|
+
if (m_appWindow && m_appWindowClosingToken) {
|
|
41
|
+
m_appWindow.Closing(m_appWindowClosingToken);
|
|
42
|
+
m_appWindowClosingToken.value = 0;
|
|
43
|
+
}
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// Reset topWindowID before destroying
|
|
46
|
+
if (m_prevWindowID) {
|
|
47
|
+
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
|
|
48
|
+
m_reactContext.Properties().Handle(), m_prevWindowID);
|
|
49
|
+
m_prevWindowID = 0;
|
|
50
|
+
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
// Close island
|
|
53
|
+
if (m_reactNativeIsland) {
|
|
54
|
+
m_reactNativeIsland.Island().Close();
|
|
55
|
+
m_reactNativeIsland = nullptr;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Hide popup
|
|
59
|
+
if (m_popUp.IsVisible()) {
|
|
60
|
+
m_popUp.Hide();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Destroy AppWindow this automatically resumes parent window to receive inputs
|
|
64
|
+
if (m_appWindow) {
|
|
65
|
+
m_appWindow.Destroy();
|
|
66
|
+
m_appWindow = nullptr;
|
|
57
67
|
}
|
|
68
|
+
|
|
69
|
+
// Close bridge
|
|
58
70
|
m_popUp.Close();
|
|
59
71
|
m_popUp = nullptr;
|
|
60
72
|
}
|
|
@@ -88,7 +100,7 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
|
|
|
88
100
|
QueueShow(view);
|
|
89
101
|
} else {
|
|
90
102
|
m_visible = false;
|
|
91
|
-
|
|
103
|
+
HideWindow();
|
|
92
104
|
}
|
|
93
105
|
}
|
|
94
106
|
|
|
@@ -219,31 +231,27 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
|
|
|
219
231
|
}
|
|
220
232
|
}
|
|
221
233
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
234
|
+
/*
|
|
235
|
+
HideWindow called on visible=false
|
|
236
|
+
unmounts the modal window using onDismiss event
|
|
237
|
+
*/
|
|
238
|
+
void HideWindow() noexcept {
|
|
239
|
+
// Hide popup
|
|
225
240
|
if (m_popUp) {
|
|
226
241
|
m_popUp.Hide();
|
|
227
242
|
}
|
|
228
243
|
|
|
229
|
-
//
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
// Restore message routing to parent
|
|
245
|
+
if (m_prevWindowID) {
|
|
246
|
+
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
|
|
247
|
+
m_reactContext.Properties().Handle(), m_prevWindowID);
|
|
233
248
|
}
|
|
234
249
|
|
|
235
|
-
//
|
|
250
|
+
// Dispatch onDismiss event
|
|
236
251
|
if (auto eventEmitter = EventEmitter()) {
|
|
237
252
|
::Microsoft::ReactNativeSpecs::ModalHostViewEventEmitter::OnDismiss eventArgs;
|
|
238
253
|
eventEmitter->onDismiss(eventArgs);
|
|
239
254
|
}
|
|
240
|
-
|
|
241
|
-
// reset the topWindowID
|
|
242
|
-
if (m_prevWindowID) {
|
|
243
|
-
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
|
|
244
|
-
m_reactContext.Properties().Handle(), m_prevWindowID);
|
|
245
|
-
m_prevWindowID = 0;
|
|
246
|
-
}
|
|
247
255
|
}
|
|
248
256
|
|
|
249
257
|
// creates a new modal window
|
|
@@ -814,6 +814,25 @@ void ScrollViewComponentView::updateProps(
|
|
|
814
814
|
}
|
|
815
815
|
m_scrollVisual.SetSnapPoints(newViewProps.snapToStart, newViewProps.snapToEnd, snapToOffsets.GetView());
|
|
816
816
|
}
|
|
817
|
+
|
|
818
|
+
if (!oldProps || oldViewProps.pagingEnabled != newViewProps.pagingEnabled) {
|
|
819
|
+
m_scrollVisual.PagingEnabled(newViewProps.pagingEnabled);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (!oldProps || oldViewProps.snapToInterval != newViewProps.snapToInterval) {
|
|
823
|
+
m_scrollVisual.SnapToInterval(static_cast<float>(newViewProps.snapToInterval));
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (!oldProps || oldViewProps.snapToAlignment != newViewProps.snapToAlignment) {
|
|
827
|
+
using SnapPointsAlignment = winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment;
|
|
828
|
+
SnapPointsAlignment alignment = SnapPointsAlignment::Near; // default is "start"
|
|
829
|
+
if (newViewProps.snapToAlignment == facebook::react::ScrollViewSnapToAlignment::Center) {
|
|
830
|
+
alignment = SnapPointsAlignment::Center;
|
|
831
|
+
} else if (newViewProps.snapToAlignment == facebook::react::ScrollViewSnapToAlignment::End) {
|
|
832
|
+
alignment = SnapPointsAlignment::Far;
|
|
833
|
+
}
|
|
834
|
+
m_scrollVisual.SnapToAlignment(alignment);
|
|
835
|
+
}
|
|
817
836
|
}
|
|
818
837
|
|
|
819
838
|
void ScrollViewComponentView::updateState(
|
package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp
CHANGED
|
@@ -697,10 +697,17 @@ void WindowsTextInputComponentView::OnPointerPressed(
|
|
|
697
697
|
}
|
|
698
698
|
|
|
699
699
|
if (m_textServices && msg) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
700
|
+
if (msg == WM_RBUTTONUP && !windowsTextInputProps().contextMenuHidden) {
|
|
701
|
+
ShowContextMenu(position);
|
|
702
|
+
args.Handled(true);
|
|
703
|
+
} else if (msg == WM_RBUTTONUP && windowsTextInputProps().contextMenuHidden) {
|
|
704
|
+
args.Handled(true);
|
|
705
|
+
} else {
|
|
706
|
+
LRESULT lresult;
|
|
707
|
+
DrawBlock db(*this);
|
|
708
|
+
auto hr = m_textServices->TxSendMessage(msg, static_cast<WPARAM>(wParam), static_cast<LPARAM>(lParam), &lresult);
|
|
709
|
+
args.Handled(hr != S_FALSE);
|
|
710
|
+
}
|
|
704
711
|
}
|
|
705
712
|
|
|
706
713
|
// Emits the OnPressIn event
|
|
@@ -844,8 +851,8 @@ void WindowsTextInputComponentView::OnPointerWheelChanged(
|
|
|
844
851
|
}
|
|
845
852
|
void WindowsTextInputComponentView::OnKeyDown(
|
|
846
853
|
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
|
|
847
|
-
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
848
|
-
// behavior We do forward Ctrl+Tab to the textinput.
|
|
854
|
+
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
855
|
+
// WinUI behavior We do forward Ctrl+Tab to the textinput.
|
|
849
856
|
if (args.Key() != winrt::Windows::System::VirtualKey::Tab ||
|
|
850
857
|
(args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
|
|
851
858
|
winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down) {
|
|
@@ -872,8 +879,8 @@ void WindowsTextInputComponentView::OnKeyDown(
|
|
|
872
879
|
|
|
873
880
|
void WindowsTextInputComponentView::OnKeyUp(
|
|
874
881
|
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
|
|
875
|
-
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
876
|
-
// behavior We do forward Ctrl+Tab to the textinput.
|
|
882
|
+
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
883
|
+
// WinUI behavior We do forward Ctrl+Tab to the textinput.
|
|
877
884
|
if (args.Key() != winrt::Windows::System::VirtualKey::Tab ||
|
|
878
885
|
(args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
|
|
879
886
|
winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down) {
|
|
@@ -943,8 +950,8 @@ bool WindowsTextInputComponentView::ShouldSubmit(
|
|
|
943
950
|
|
|
944
951
|
void WindowsTextInputComponentView::OnCharacterReceived(
|
|
945
952
|
const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept {
|
|
946
|
-
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
947
|
-
// behavior We do forward Ctrl+Tab to the textinput.
|
|
953
|
+
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
954
|
+
// WinUI behavior We do forward Ctrl+Tab to the textinput.
|
|
948
955
|
if ((args.KeyCode() == '\t') &&
|
|
949
956
|
((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
|
|
950
957
|
winrt::Microsoft::UI::Input::VirtualKeyStates::Down) != winrt::Microsoft::UI::Input::VirtualKeyStates::Down)) {
|
|
@@ -1547,25 +1554,59 @@ void WindowsTextInputComponentView::UpdateParaFormat() noexcept {
|
|
|
1547
1554
|
m_pf.dwMask = PFM_ALL;
|
|
1548
1555
|
|
|
1549
1556
|
auto &textAlign = windowsTextInputProps().textAlign;
|
|
1557
|
+
auto &baseWritingDirection = windowsTextInputProps().textAttributes.baseWritingDirection;
|
|
1558
|
+
|
|
1559
|
+
// Handle writingDirection (baseWritingDirection)
|
|
1560
|
+
// For WritingDirection::Natural, use the computed layout direction from the layout tree
|
|
1561
|
+
// since direction can be overridden at any point in the tree
|
|
1562
|
+
bool isRTL = false;
|
|
1563
|
+
if (baseWritingDirection.has_value()) {
|
|
1564
|
+
if (*baseWritingDirection == facebook::react::WritingDirection::RightToLeft) {
|
|
1565
|
+
isRTL = true;
|
|
1566
|
+
m_pf.dwMask |= PFM_RTLPARA;
|
|
1567
|
+
m_pf.wEffects |= PFE_RTLPARA;
|
|
1568
|
+
} else if (*baseWritingDirection == facebook::react::WritingDirection::LeftToRight) {
|
|
1569
|
+
isRTL = false;
|
|
1570
|
+
// Ensure RTL flag is not set
|
|
1571
|
+
m_pf.wEffects &= ~PFE_RTLPARA;
|
|
1572
|
+
} else if (*baseWritingDirection == facebook::react::WritingDirection::Natural) {
|
|
1573
|
+
// Natural uses the layout direction computed from the tree
|
|
1574
|
+
isRTL = (layoutMetrics().layoutDirection == facebook::react::LayoutDirection::RightToLeft);
|
|
1575
|
+
if (isRTL) {
|
|
1576
|
+
m_pf.dwMask |= PFM_RTLPARA;
|
|
1577
|
+
m_pf.wEffects |= PFE_RTLPARA;
|
|
1578
|
+
} else {
|
|
1579
|
+
m_pf.wEffects &= ~PFE_RTLPARA;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
} else {
|
|
1583
|
+
// No explicit writing direction set - use layout direction from tree
|
|
1584
|
+
isRTL = (layoutMetrics().layoutDirection == facebook::react::LayoutDirection::RightToLeft);
|
|
1585
|
+
if (isRTL) {
|
|
1586
|
+
m_pf.dwMask |= PFM_RTLPARA;
|
|
1587
|
+
m_pf.wEffects |= PFE_RTLPARA;
|
|
1588
|
+
} else {
|
|
1589
|
+
m_pf.wEffects &= ~PFE_RTLPARA;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1550
1592
|
|
|
1593
|
+
// Handle textAlign
|
|
1551
1594
|
if (textAlign == facebook::react::TextAlignment::Center) {
|
|
1552
1595
|
m_pf.wAlignment = PFA_CENTER;
|
|
1553
1596
|
} else if (textAlign == facebook::react::TextAlignment::Right) {
|
|
1554
1597
|
m_pf.wAlignment = PFA_RIGHT;
|
|
1598
|
+
} else if (textAlign == facebook::react::TextAlignment::Justified) {
|
|
1599
|
+
m_pf.wAlignment = PFA_JUSTIFY;
|
|
1600
|
+
} else if (textAlign == facebook::react::TextAlignment::Natural) {
|
|
1601
|
+
// Natural alignment respects writing direction
|
|
1602
|
+
m_pf.wAlignment = isRTL ? PFA_RIGHT : PFA_LEFT;
|
|
1555
1603
|
} else {
|
|
1604
|
+
// Default to left alignment
|
|
1556
1605
|
m_pf.wAlignment = PFA_LEFT;
|
|
1557
1606
|
}
|
|
1558
1607
|
|
|
1559
1608
|
m_pf.cTabCount = 1;
|
|
1560
1609
|
m_pf.rgxTabs[0] = lDefaultTab;
|
|
1561
|
-
|
|
1562
|
-
/*
|
|
1563
|
-
if (m_spcontroller->IsCurrentReadingOrderRTL())
|
|
1564
|
-
{
|
|
1565
|
-
m_pf.dwMask |= PFM_RTLPARA;
|
|
1566
|
-
m_pf.wEffects |= PFE_RTLPARA;
|
|
1567
|
-
}
|
|
1568
|
-
*/
|
|
1569
1610
|
}
|
|
1570
1611
|
|
|
1571
1612
|
void WindowsTextInputComponentView::OnRenderingDeviceLost() noexcept {
|
|
@@ -1826,4 +1867,47 @@ void WindowsTextInputComponentView::updateSpellCheck(bool enable) noexcept {
|
|
|
1826
1867
|
winrt::check_hresult(
|
|
1827
1868
|
m_textServices->TxSendMessage(EM_SETLANGOPTIONS, IMF_SPELLCHECKING, enable ? newLangOptions : 0, &lresult));
|
|
1828
1869
|
}
|
|
1870
|
+
|
|
1871
|
+
void WindowsTextInputComponentView::ShowContextMenu(const winrt::Windows::Foundation::Point &position) noexcept {
|
|
1872
|
+
HMENU menu = CreatePopupMenu();
|
|
1873
|
+
if (!menu)
|
|
1874
|
+
return;
|
|
1875
|
+
|
|
1876
|
+
CHARRANGE selection;
|
|
1877
|
+
LRESULT res;
|
|
1878
|
+
m_textServices->TxSendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&selection), &res);
|
|
1879
|
+
|
|
1880
|
+
bool hasSelection = selection.cpMin != selection.cpMax;
|
|
1881
|
+
bool isEmpty = GetTextFromRichEdit().empty();
|
|
1882
|
+
bool isReadOnly = windowsTextInputProps().editable == false;
|
|
1883
|
+
bool canPaste = !isReadOnly && IsClipboardFormatAvailable(CF_UNICODETEXT);
|
|
1884
|
+
|
|
1885
|
+
AppendMenuW(menu, MF_STRING | (hasSelection && !isReadOnly ? 0 : MF_GRAYED), 1, L"Cut");
|
|
1886
|
+
AppendMenuW(menu, MF_STRING | (hasSelection ? 0 : MF_GRAYED), 2, L"Copy");
|
|
1887
|
+
AppendMenuW(menu, MF_STRING | (canPaste ? 0 : MF_GRAYED), 3, L"Paste");
|
|
1888
|
+
AppendMenuW(menu, MF_STRING | (!isEmpty && !isReadOnly ? 0 : MF_GRAYED), 4, L"Select All");
|
|
1889
|
+
|
|
1890
|
+
POINT cursorPos;
|
|
1891
|
+
GetCursorPos(&cursorPos);
|
|
1892
|
+
|
|
1893
|
+
HWND hwnd = GetActiveWindow();
|
|
1894
|
+
|
|
1895
|
+
int cmd = TrackPopupMenu(
|
|
1896
|
+
menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_NONOTIFY, cursorPos.x, cursorPos.y, 0, hwnd, NULL);
|
|
1897
|
+
|
|
1898
|
+
if (cmd == 1) { // Cut
|
|
1899
|
+
m_textServices->TxSendMessage(WM_CUT, 0, 0, &res);
|
|
1900
|
+
OnTextUpdated();
|
|
1901
|
+
} else if (cmd == 2) { // Copy
|
|
1902
|
+
m_textServices->TxSendMessage(WM_COPY, 0, 0, &res);
|
|
1903
|
+
} else if (cmd == 3) { // Paste
|
|
1904
|
+
m_textServices->TxSendMessage(WM_PASTE, 0, 0, &res);
|
|
1905
|
+
OnTextUpdated();
|
|
1906
|
+
} else if (cmd == 4) { // Select All
|
|
1907
|
+
m_textServices->TxSendMessage(EM_SETSEL, 0, -1, &res);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
DestroyMenu(menu);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1829
1913
|
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
|
|
@@ -118,6 +118,7 @@ struct WindowsTextInputComponentView
|
|
|
118
118
|
void updateLetterSpacing(float letterSpacing) noexcept;
|
|
119
119
|
void updateAutoCorrect(bool value) noexcept;
|
|
120
120
|
void updateSpellCheck(bool value) noexcept;
|
|
121
|
+
void ShowContextMenu(const winrt::Windows::Foundation::Point &position) noexcept;
|
|
121
122
|
|
|
122
123
|
winrt::Windows::UI::Composition::CompositionSurfaceBrush m_brush{nullptr};
|
|
123
124
|
winrt::Microsoft::ReactNative::Composition::Experimental::ICaretVisual m_caretVisual{nullptr};
|
|
@@ -136,6 +136,25 @@ void WindowsTextLayoutManager::GetTextLayout(
|
|
|
136
136
|
outerFragment.textAttributes.lineHeight * 0.8f));
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
// Set reading direction (RTL/LTR) based on baseWritingDirection
|
|
140
|
+
// Only set reading direction if explicitly specified to avoid breaking existing layouts
|
|
141
|
+
bool isRTL = false;
|
|
142
|
+
if (outerFragment.textAttributes.baseWritingDirection.has_value()) {
|
|
143
|
+
DWRITE_READING_DIRECTION readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
144
|
+
if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::RightToLeft) {
|
|
145
|
+
readingDirection = DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
146
|
+
isRTL = true;
|
|
147
|
+
} else if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::LeftToRight) {
|
|
148
|
+
readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
149
|
+
isRTL = false;
|
|
150
|
+
} else if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::Natural) {
|
|
151
|
+
// Natural uses the layout direction from textAttributes
|
|
152
|
+
isRTL = (outerFragment.textAttributes.layoutDirection == facebook::react::LayoutDirection::RightToLeft);
|
|
153
|
+
readingDirection = isRTL ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
154
|
+
}
|
|
155
|
+
winrt::check_hresult(spTextFormat->SetReadingDirection(readingDirection));
|
|
156
|
+
}
|
|
157
|
+
|
|
139
158
|
// Set text alignment
|
|
140
159
|
DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
141
160
|
if (outerFragment.textAttributes.alignment) {
|
|
@@ -152,9 +171,9 @@ void WindowsTextLayoutManager::GetTextLayout(
|
|
|
152
171
|
case facebook::react::TextAlignment::Right:
|
|
153
172
|
alignment = DWRITE_TEXT_ALIGNMENT_TRAILING;
|
|
154
173
|
break;
|
|
155
|
-
// TODO use LTR values
|
|
156
174
|
case facebook::react::TextAlignment::Natural:
|
|
157
|
-
alignment
|
|
175
|
+
// Natural alignment respects reading direction if baseWritingDirection was set
|
|
176
|
+
alignment = isRTL ? DWRITE_TEXT_ALIGNMENT_TRAILING : DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
158
177
|
break;
|
|
159
178
|
default:
|
|
160
179
|
assert(false);
|
|
@@ -326,10 +326,32 @@ class ReactInspectorHostTargetDelegate : public jsinspector_modern::HostTargetDe
|
|
|
326
326
|
ReactInspectorHostTargetDelegate(Mso::WeakPtr<ReactHost> &&reactHost) noexcept : m_reactHost(std::move(reactHost)) {}
|
|
327
327
|
|
|
328
328
|
jsinspector_modern::HostTargetMetadata getMetadata() override {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
329
|
+
jsinspector_modern::HostTargetMetadata metadata{};
|
|
330
|
+
metadata.integrationName = "React Native Windows (Host)";
|
|
331
|
+
metadata.platform = "windows";
|
|
332
|
+
|
|
333
|
+
if (Mso::CntPtr<ReactHost> reactHost = m_reactHost.GetStrongPtr()) {
|
|
334
|
+
const ReactOptions &options = reactHost->Options();
|
|
335
|
+
if (!options.Identity.empty()) {
|
|
336
|
+
std::string identity = options.Identity;
|
|
337
|
+
// Replace illegal characters with underscore
|
|
338
|
+
for (char &c : identity) {
|
|
339
|
+
if (c == '\\' || c == '/' || c == ':' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' ||
|
|
340
|
+
c == '|') {
|
|
341
|
+
c = '_';
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
metadata.appDisplayName = identity;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
349
|
+
DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
|
|
350
|
+
if (GetComputerNameW(computerName, &size)) {
|
|
351
|
+
metadata.deviceName = winrt::to_string(computerName);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return metadata;
|
|
333
355
|
}
|
|
334
356
|
|
|
335
357
|
void onReload(jsinspector_modern::HostTargetDelegate::PageReloadRequest const &request) override {
|
|
@@ -630,9 +652,20 @@ void ReactHost::AddInspectorPage() noexcept {
|
|
|
630
652
|
jsinspector_modern::InspectorTargetCapabilities capabilities;
|
|
631
653
|
capabilities.nativePageReloads = true;
|
|
632
654
|
capabilities.prefersFuseboxFrontend = true;
|
|
633
|
-
|
|
655
|
+
|
|
656
|
+
auto metadata = m_inspectorHostTargetDelegate->getMetadata();
|
|
657
|
+
std::string pageName;
|
|
658
|
+
if (metadata.appDisplayName.has_value() && !metadata.appDisplayName.value().empty()) {
|
|
659
|
+
pageName = metadata.appDisplayName.value();
|
|
660
|
+
} else {
|
|
661
|
+
pageName = "React Native Windows (Experimental)";
|
|
662
|
+
}
|
|
663
|
+
if (metadata.deviceName.has_value() && !metadata.deviceName.value().empty()) {
|
|
664
|
+
pageName += " (" + metadata.deviceName.value() + ")";
|
|
665
|
+
}
|
|
666
|
+
|
|
634
667
|
inspectorPageId = jsinspector_modern::getInspectorInstance().addPage(
|
|
635
|
-
|
|
668
|
+
pageName,
|
|
636
669
|
"Hermes",
|
|
637
670
|
[weakInspectorHostTarget =
|
|
638
671
|
std::weak_ptr(m_inspectorHostTarget)](std::unique_ptr<jsinspector_modern::IRemoteConnection> remote)
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
-->
|
|
11
11
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
12
12
|
<PropertyGroup>
|
|
13
|
-
<ReactNativeWindowsVersion>0.81.0-preview.
|
|
13
|
+
<ReactNativeWindowsVersion>0.81.0-preview.4</ReactNativeWindowsVersion>
|
|
14
14
|
<ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
|
|
15
15
|
<ReactNativeWindowsMinor>81</ReactNativeWindowsMinor>
|
|
16
16
|
<ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
|
|
17
17
|
<ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
|
|
18
|
-
<ReactNativeWindowsCommitId>
|
|
18
|
+
<ReactNativeWindowsCommitId>a999c767d29d998a9b7792f8dd1b28242f1395dc</ReactNativeWindowsCommitId>
|
|
19
19
|
</PropertyGroup>
|
|
20
20
|
</Project>
|
package/package.json
CHANGED
package/template/metro.config.js
CHANGED
|
@@ -2,8 +2,6 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
|
6
|
-
|
|
7
5
|
const rnwPath = fs.realpathSync(
|
|
8
6
|
path.resolve(require.resolve('react-native-windows/package.json'), '..'),
|
|
9
7
|
);
|
|
@@ -25,7 +23,7 @@ const config = {
|
|
|
25
23
|
watchFolders: [rnwPath, rnwRootNodeModules, rnwPackages],
|
|
26
24
|
// devMode]{{/devMode}}
|
|
27
25
|
resolver: {
|
|
28
|
-
blockList:
|
|
26
|
+
blockList: [
|
|
29
27
|
// This stops "npx @react-native-community/cli run-windows" from causing the metro server to crash if its already running
|
|
30
28
|
new RegExp(
|
|
31
29
|
`${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
|
|
@@ -34,7 +32,7 @@ const config = {
|
|
|
34
32
|
new RegExp(`${rnwPath}/build/.*`),
|
|
35
33
|
new RegExp(`${rnwPath}/target/.*`),
|
|
36
34
|
/.*\.ProjectImports\.zip/,
|
|
37
|
-
]
|
|
35
|
+
],
|
|
38
36
|
//{{#devMode}} [devMode
|
|
39
37
|
extraNodeModules: {
|
|
40
38
|
'react-native-windows': rnwPath,
|
|
@@ -2,8 +2,6 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
|
6
|
-
|
|
7
5
|
const rnwPath = fs.realpathSync(
|
|
8
6
|
path.resolve(require.resolve('react-native-windows/package.json'), '..'),
|
|
9
7
|
);
|
|
@@ -25,7 +23,7 @@ const config = {
|
|
|
25
23
|
watchFolders: [rnwPath, rnwRootNodeModules, rnwPackages],
|
|
26
24
|
// devMode]{{/devMode}}
|
|
27
25
|
resolver: {
|
|
28
|
-
blockList:
|
|
26
|
+
blockList: [
|
|
29
27
|
// This stops "npx @react-native-community/cli run-windows" from causing the metro server to crash if its already running
|
|
30
28
|
new RegExp(
|
|
31
29
|
`${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
|
|
@@ -34,7 +32,7 @@ const config = {
|
|
|
34
32
|
new RegExp(`${rnwPath}/build/.*`),
|
|
35
33
|
new RegExp(`${rnwPath}/target/.*`),
|
|
36
34
|
/.*\.ProjectImports\.zip/,
|
|
37
|
-
]
|
|
35
|
+
],
|
|
38
36
|
//{{#devMode}} [devMode
|
|
39
37
|
extraNodeModules: {
|
|
40
38
|
'react-native-windows': rnwPath,
|
|
@@ -2,7 +2,6 @@ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const escape = require('escape-string-regexp');
|
|
5
|
-
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
|
6
5
|
const pack = require('../package.json');
|
|
7
6
|
|
|
8
7
|
const root = path.resolve(__dirname, '..');
|
|
@@ -33,7 +32,7 @@ const config = {
|
|
|
33
32
|
// We need to make sure that only one version is loaded for peerDependencies
|
|
34
33
|
// So we block them at the root, and alias them to the versions in example's node_modules
|
|
35
34
|
resolver: {
|
|
36
|
-
blockList:
|
|
35
|
+
blockList:
|
|
37
36
|
modules.map(
|
|
38
37
|
(m) =>
|
|
39
38
|
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
|
|
@@ -47,7 +46,7 @@ const config = {
|
|
|
47
46
|
new RegExp(`${rnwPath}/target/.*`),
|
|
48
47
|
/.*\.ProjectImports\.zip/,
|
|
49
48
|
])
|
|
50
|
-
|
|
49
|
+
,
|
|
51
50
|
|
|
52
51
|
extraNodeModules: modules.reduce((acc, name) => {
|
|
53
52
|
acc[name] = path.join(__dirname, 'node_modules', name);
|