react-native-windows 0.0.0-canary.489 → 0.0.0-canary.490
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/Components/Button.windows.js +1 -0
- package/Libraries/Pressability/Pressability.windows.js +13 -2
- package/Microsoft.ReactNative/Fabric/DWriteHelpers.cpp +19 -0
- package/Microsoft.ReactNative/Fabric/DWriteHelpers.h +13 -0
- package/Microsoft.ReactNative/Fabric/ParagraphComponentView.cpp +36 -10
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp +107 -57
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.h +8 -2
- package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +1 -0
- package/Microsoft.ReactNative/Views/ControlViewManager.cpp +32 -0
- package/Microsoft.ReactNative/Views/ControlViewManager.h +11 -0
- package/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp +58 -3
- package/Microsoft.ReactNative/Views/ViewViewManager.cpp +21 -0
- package/PropertySheets/Generated/PackageVersion.g.props +1 -1
- package/package.json +1 -1
|
@@ -342,6 +342,7 @@ class Button extends React.Component<
|
|
|
342
342
|
if (Platform.OS === 'windows') {
|
|
343
343
|
return (
|
|
344
344
|
<Touchable
|
|
345
|
+
accessible={accessible}
|
|
345
346
|
accessibilityLabel={accessibilityLabel}
|
|
346
347
|
accessibilityHint={accessibilityHint}
|
|
347
348
|
accessibilityLanguage={accessibilityLanguage}
|
|
@@ -721,6 +721,11 @@ export default class Pressability {
|
|
|
721
721
|
}
|
|
722
722
|
}
|
|
723
723
|
|
|
724
|
+
// [Windows]
|
|
725
|
+
_isDefaultPressButton(button) {
|
|
726
|
+
return !button; // Treat 0 or undefined as default press
|
|
727
|
+
}
|
|
728
|
+
|
|
724
729
|
/**
|
|
725
730
|
* Performs a transition between touchable states and identify any activations
|
|
726
731
|
* or deactivations (and callback invocations).
|
|
@@ -771,7 +776,10 @@ export default class Pressability {
|
|
|
771
776
|
}
|
|
772
777
|
const {onLongPress, onPress, android_disableSound} = this._config;
|
|
773
778
|
|
|
774
|
-
if (
|
|
779
|
+
if (
|
|
780
|
+
onPress != null &&
|
|
781
|
+
this._isDefaultPressButton(getTouchFromPressEvent(event).button)
|
|
782
|
+
) {
|
|
775
783
|
const isPressCanceledByLongPress =
|
|
776
784
|
onLongPress != null &&
|
|
777
785
|
prevState === 'RESPONDER_ACTIVE_LONG_PRESS_IN' &&
|
|
@@ -800,7 +808,10 @@ export default class Pressability {
|
|
|
800
808
|
|
|
801
809
|
_deactivate(event: PressEvent): void {
|
|
802
810
|
const {onPressOut} = this._config;
|
|
803
|
-
if (
|
|
811
|
+
if (
|
|
812
|
+
onPressOut != null &&
|
|
813
|
+
this._isDefaultPressButton(getTouchFromPressEvent(event).button)
|
|
814
|
+
) {
|
|
804
815
|
const minPressDuration = normalizeDelay(
|
|
805
816
|
this._config.minPressDuration,
|
|
806
817
|
0,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
#pragma once
|
|
5
|
+
|
|
6
|
+
#include "DWriteHelpers.h"
|
|
7
|
+
|
|
8
|
+
namespace Microsoft::ReactNative {
|
|
9
|
+
|
|
10
|
+
winrt::com_ptr<::IDWriteFactory> DWriteFactory() noexcept {
|
|
11
|
+
static winrt::com_ptr<::IDWriteFactory> s_dwriteFactory;
|
|
12
|
+
if (!s_dwriteFactory) {
|
|
13
|
+
winrt::check_hresult(::DWriteCreateFactory(
|
|
14
|
+
DWRITE_FACTORY_TYPE_SHARED, __uuidof(s_dwriteFactory), reinterpret_cast<::IUnknown **>(s_dwriteFactory.put())));
|
|
15
|
+
}
|
|
16
|
+
return s_dwriteFactory;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
} // namespace Microsoft::ReactNative
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
#include <dwrite.h>
|
|
8
|
+
|
|
9
|
+
namespace Microsoft::ReactNative {
|
|
10
|
+
|
|
11
|
+
winrt::com_ptr<::IDWriteFactory> DWriteFactory() noexcept;
|
|
12
|
+
|
|
13
|
+
} // namespace Microsoft::ReactNative
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include "ParagraphComponentView.h"
|
|
8
8
|
|
|
9
9
|
#include <UI.Xaml.Controls.h>
|
|
10
|
+
#include <UI.Xaml.Documents.h>
|
|
10
11
|
#include <Utils/ValueUtils.h>
|
|
11
12
|
#include <dwrite.h>
|
|
12
13
|
#include <react/renderer/components/text/ParagraphShadowNode.h>
|
|
@@ -30,14 +31,12 @@ void ParagraphComponentView::mountChildComponentView(
|
|
|
30
31
|
uint32_t index) noexcept {
|
|
31
32
|
auto v = static_cast<const ParagraphComponentView &>(childComponentView);
|
|
32
33
|
assert(false);
|
|
33
|
-
// m_element.Children().InsertAt(index, v.Element());
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
void ParagraphComponentView::unmountChildComponentView(
|
|
37
37
|
const IComponentView &childComponentView,
|
|
38
38
|
uint32_t index) noexcept {
|
|
39
39
|
assert(false);
|
|
40
|
-
// m_element.Children().RemoveAt(index);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
void ParagraphComponentView::updateProps(
|
|
@@ -99,11 +98,40 @@ void ParagraphComponentView::updateState(
|
|
|
99
98
|
facebook::react::State::Shared const &oldState) noexcept {
|
|
100
99
|
const auto &newState = *std::static_pointer_cast<facebook::react::ParagraphShadowNode::ConcreteState const>(state);
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
m_element.Inlines().Clear();
|
|
102
|
+
|
|
103
|
+
for (const auto &fragment : newState.getData().attributedString.getFragments()) {
|
|
104
|
+
auto inlines = m_element.Inlines();
|
|
105
|
+
|
|
106
|
+
if (auto tdlt = fragment.textAttributes.textDecorationLineType; tdlt &&
|
|
107
|
+
(*tdlt == facebook::react::TextDecorationLineType::Underline ||
|
|
108
|
+
*tdlt == facebook::react::TextDecorationLineType::UnderlineStrikethrough)) {
|
|
109
|
+
auto underline = xaml::Documents::Underline();
|
|
110
|
+
inlines.Append(underline);
|
|
111
|
+
inlines = underline.Inlines();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (fragment.textAttributes.fontStyle == facebook::react::FontStyle::Italic ||
|
|
115
|
+
fragment.textAttributes.fontStyle == facebook::react::FontStyle::Oblique) {
|
|
116
|
+
auto italic = xaml::Documents::Italic();
|
|
117
|
+
inlines.Append(italic);
|
|
118
|
+
inlines = italic.Inlines();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
auto run = xaml::Documents::Run();
|
|
122
|
+
run.Text(winrt::to_hstring(fragment.string));
|
|
123
|
+
run.FontFamily(xaml::Media::FontFamily(
|
|
124
|
+
fragment.textAttributes.fontFamily.empty() ? L"Segoe UI"
|
|
125
|
+
: winrt::to_hstring(fragment.textAttributes.fontFamily)));
|
|
126
|
+
|
|
127
|
+
run.FontWeight(
|
|
128
|
+
winrt::Windows::UI::Text::FontWeight{static_cast<uint16_t>(fragment.textAttributes.fontWeight.value_or(
|
|
129
|
+
static_cast<facebook::react::FontWeight>(DWRITE_FONT_WEIGHT_REGULAR)))});
|
|
130
|
+
|
|
131
|
+
run.FontSize(fragment.textAttributes.fontSize);
|
|
132
|
+
run.Foreground(fragment.textAttributes.foregroundColor.AsWindowsBrush());
|
|
133
|
+
inlines.Append(run);
|
|
134
|
+
}
|
|
107
135
|
}
|
|
108
136
|
void ParagraphComponentView::updateLayoutMetrics(
|
|
109
137
|
facebook::react::LayoutMetrics const &layoutMetrics,
|
|
@@ -118,9 +146,7 @@ void ParagraphComponentView::updateLayoutMetrics(
|
|
|
118
146
|
m_element.Width(layoutMetrics.frame.size.width);
|
|
119
147
|
m_element.Height(layoutMetrics.frame.size.height);
|
|
120
148
|
}
|
|
121
|
-
void ParagraphComponentView::finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept {
|
|
122
|
-
// m_element.FinalizeProperties();
|
|
123
|
-
}
|
|
149
|
+
void ParagraphComponentView::finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept {}
|
|
124
150
|
void ParagraphComponentView::prepareForRecycle() noexcept {}
|
|
125
151
|
facebook::react::SharedProps ParagraphComponentView::props() noexcept {
|
|
126
152
|
assert(false);
|
package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp
CHANGED
|
@@ -5,72 +5,125 @@
|
|
|
5
5
|
|
|
6
6
|
#include "pch.h"
|
|
7
7
|
|
|
8
|
+
#include <Fabric/DWriteHelpers.h>
|
|
9
|
+
#include <dwrite.h>
|
|
8
10
|
#include "TextLayoutManager.h"
|
|
9
11
|
|
|
10
12
|
#include <unicode.h>
|
|
11
13
|
|
|
12
14
|
namespace facebook::react {
|
|
13
15
|
|
|
14
|
-
TextLayoutManager
|
|
16
|
+
void TextLayoutManager::GetTextLayout(
|
|
17
|
+
AttributedStringBox attributedStringBox,
|
|
18
|
+
ParagraphAttributes paragraphAttributes,
|
|
19
|
+
LayoutConstraints layoutConstraints,
|
|
20
|
+
const std::optional<TextAlignment> &textAlignment,
|
|
21
|
+
winrt::com_ptr<IDWriteTextLayout> &spTextLayout) noexcept {
|
|
22
|
+
if (attributedStringBox.getValue().isEmpty())
|
|
23
|
+
return;
|
|
24
|
+
|
|
25
|
+
auto fragments = attributedStringBox.getValue().getFragments();
|
|
26
|
+
auto outerFragment = fragments[0];
|
|
27
|
+
|
|
28
|
+
DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL;
|
|
29
|
+
if (outerFragment.textAttributes.fontStyle == facebook::react::FontStyle::Italic)
|
|
30
|
+
style = DWRITE_FONT_STYLE_ITALIC;
|
|
31
|
+
else if (outerFragment.textAttributes.fontStyle == facebook::react::FontStyle::Oblique)
|
|
32
|
+
style = DWRITE_FONT_STYLE_OBLIQUE;
|
|
33
|
+
|
|
34
|
+
winrt::com_ptr<IDWriteTextFormat> spTextFormat;
|
|
35
|
+
winrt::check_hresult(Microsoft::ReactNative::DWriteFactory()->CreateTextFormat(
|
|
36
|
+
outerFragment.textAttributes.fontFamily.empty()
|
|
37
|
+
? L"Segoe UI"
|
|
38
|
+
: Microsoft::Common::Unicode::Utf8ToUtf16(outerFragment.textAttributes.fontFamily).c_str(),
|
|
39
|
+
nullptr, // Font collection (nullptr sets it to use the system font collection).
|
|
40
|
+
static_cast<DWRITE_FONT_WEIGHT>(outerFragment.textAttributes.fontWeight.value_or(
|
|
41
|
+
static_cast<facebook::react::FontWeight>(DWRITE_FONT_WEIGHT_REGULAR))),
|
|
42
|
+
style,
|
|
43
|
+
DWRITE_FONT_STRETCH_NORMAL,
|
|
44
|
+
outerFragment.textAttributes.fontSize,
|
|
45
|
+
L"",
|
|
46
|
+
spTextFormat.put()));
|
|
47
|
+
|
|
48
|
+
DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
49
|
+
if (textAlignment) {
|
|
50
|
+
switch (*textAlignment) {
|
|
51
|
+
case facebook::react::TextAlignment::Center:
|
|
52
|
+
alignment = DWRITE_TEXT_ALIGNMENT_CENTER;
|
|
53
|
+
break;
|
|
54
|
+
case facebook::react::TextAlignment::Justified:
|
|
55
|
+
alignment = DWRITE_TEXT_ALIGNMENT_JUSTIFIED;
|
|
56
|
+
break;
|
|
57
|
+
case facebook::react::TextAlignment::Left:
|
|
58
|
+
alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
59
|
+
break;
|
|
60
|
+
case facebook::react::TextAlignment::Right:
|
|
61
|
+
alignment = DWRITE_TEXT_ALIGNMENT_TRAILING;
|
|
62
|
+
break;
|
|
63
|
+
// TODO use LTR values
|
|
64
|
+
case facebook::react::TextAlignment::Natural:
|
|
65
|
+
alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
66
|
+
break;
|
|
67
|
+
default:
|
|
68
|
+
assert(false);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
winrt::check_hresult(spTextFormat->SetTextAlignment(alignment));
|
|
72
|
+
|
|
73
|
+
auto str = Microsoft::Common::Unicode::Utf8ToUtf16(attributedStringBox.getValue().getString());
|
|
74
|
+
|
|
75
|
+
winrt::check_hresult(Microsoft::ReactNative::DWriteFactory()->CreateTextLayout(
|
|
76
|
+
str.c_str(), // The string to be laid out and formatted.
|
|
77
|
+
static_cast<UINT32>(str.length()), // The length of the string.
|
|
78
|
+
spTextFormat.get(), // The text format to apply to the string (contains font information, etc).
|
|
79
|
+
layoutConstraints.maximumSize.width, // The width of the layout box.
|
|
80
|
+
layoutConstraints.maximumSize.height, // The height of the layout box.
|
|
81
|
+
spTextLayout.put() // The IDWriteTextLayout interface pointer.
|
|
82
|
+
));
|
|
83
|
+
|
|
84
|
+
unsigned int position = 0;
|
|
85
|
+
unsigned int length = 0;
|
|
86
|
+
for (const auto &fragment : fragments) {
|
|
87
|
+
length = static_cast<UINT32>(fragment.string.length());
|
|
88
|
+
DWRITE_TEXT_RANGE range = {position, length};
|
|
89
|
+
TextAttributes attributes = fragment.textAttributes;
|
|
90
|
+
DWRITE_FONT_STYLE fragmentStyle = DWRITE_FONT_STYLE_NORMAL;
|
|
91
|
+
if (attributes.fontStyle == facebook::react::FontStyle::Italic)
|
|
92
|
+
fragmentStyle = DWRITE_FONT_STYLE_ITALIC;
|
|
93
|
+
else if (attributes.fontStyle == facebook::react::FontStyle::Oblique)
|
|
94
|
+
fragmentStyle = DWRITE_FONT_STYLE_OBLIQUE;
|
|
95
|
+
|
|
96
|
+
winrt::check_hresult(spTextLayout->SetFontFamilyName(
|
|
97
|
+
attributes.fontFamily.empty() ? L"Segoe UI"
|
|
98
|
+
: Microsoft::Common::Unicode::Utf8ToUtf16(attributes.fontFamily).c_str(),
|
|
99
|
+
range));
|
|
100
|
+
winrt::check_hresult(spTextLayout->SetFontWeight(
|
|
101
|
+
static_cast<DWRITE_FONT_WEIGHT>(
|
|
102
|
+
attributes.fontWeight.value_or(static_cast<facebook::react::FontWeight>(DWRITE_FONT_WEIGHT_REGULAR))),
|
|
103
|
+
range));
|
|
104
|
+
winrt::check_hresult(spTextLayout->SetFontStyle(fragmentStyle, range));
|
|
105
|
+
winrt::check_hresult(spTextLayout->SetFontSize(attributes.fontSize, range));
|
|
106
|
+
|
|
107
|
+
position += length;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
15
110
|
|
|
16
111
|
TextMeasurement TextLayoutManager::measure(
|
|
17
112
|
AttributedStringBox attributedStringBox,
|
|
18
113
|
ParagraphAttributes paragraphAttributes,
|
|
19
114
|
LayoutConstraints layoutConstraints) const {
|
|
20
|
-
winrt::com_ptr<
|
|
115
|
+
winrt::com_ptr<IDWriteTextLayout> spTextLayout;
|
|
21
116
|
|
|
22
|
-
|
|
23
|
-
DWriteCreateFactory(
|
|
24
|
-
DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(m_spDWriteFactory.put()));
|
|
25
|
-
}
|
|
117
|
+
GetTextLayout(attributedStringBox, paragraphAttributes, layoutConstraints, TextAlignment::Left, spTextLayout);
|
|
26
118
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
style = DWRITE_FONT_STYLE_OBLIQUE;
|
|
33
|
-
|
|
34
|
-
winrt::com_ptr<IDWriteTextFormat> spTextFormat;
|
|
35
|
-
m_spDWriteFactory->CreateTextFormat(
|
|
36
|
-
fragment.textAttributes.fontFamily.empty()
|
|
37
|
-
? L"Segoe UI"
|
|
38
|
-
: Microsoft::Common::Unicode::Utf8ToUtf16(fragment.textAttributes.fontFamily).c_str(),
|
|
39
|
-
nullptr, // Font collection (nullptr sets it to use the system font collection).
|
|
40
|
-
static_cast<DWRITE_FONT_WEIGHT>(fragment.textAttributes.fontWeight.value_or(
|
|
41
|
-
static_cast<facebook::react::FontWeight>(DWRITE_FONT_WEIGHT_REGULAR))),
|
|
42
|
-
style,
|
|
43
|
-
DWRITE_FONT_STRETCH_NORMAL,
|
|
44
|
-
fragment.textAttributes.fontSize * fragment.textAttributes.fontSizeMultiplier,
|
|
45
|
-
L"en-us",
|
|
46
|
-
spTextFormat.put());
|
|
47
|
-
|
|
48
|
-
auto str = Microsoft::Common::Unicode::Utf8ToUtf16(fragment.string);
|
|
49
|
-
|
|
50
|
-
winrt::com_ptr<IDWriteTextLayout> spTextLayout;
|
|
51
|
-
// TODO - For now assuming fragment.textAttributes.fontSizeMultiplier is the same as the pointScaleFactor
|
|
52
|
-
m_spDWriteFactory->CreateTextLayout(
|
|
53
|
-
str.c_str(), // The string to be laid out and formatted.
|
|
54
|
-
static_cast<UINT32>(str.length()), // The length of the string.
|
|
55
|
-
spTextFormat.get(), // The text format to apply to the string (contains font information, etc).
|
|
56
|
-
layoutConstraints.maximumSize.width *
|
|
57
|
-
fragment.textAttributes.fontSizeMultiplier, // The width of the layout box.
|
|
58
|
-
layoutConstraints.maximumSize.height *
|
|
59
|
-
fragment.textAttributes.fontSizeMultiplier, // The height of the layout box.
|
|
60
|
-
spTextLayout.put() // The IDWriteTextLayout interface pointer.
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
TextMeasurement tm;
|
|
64
|
-
DWRITE_TEXT_METRICS dtm;
|
|
65
|
-
spTextLayout->GetMetrics(&dtm);
|
|
66
|
-
tm.size = {
|
|
67
|
-
dtm.width / fragment.textAttributes.fontSizeMultiplier,
|
|
68
|
-
dtm.height / fragment.textAttributes.fontSizeMultiplier};
|
|
69
|
-
return tm;
|
|
119
|
+
TextMeasurement tm{};
|
|
120
|
+
if (spTextLayout) {
|
|
121
|
+
DWRITE_TEXT_METRICS dtm{};
|
|
122
|
+
winrt::check_hresult(spTextLayout->GetMetrics(&dtm));
|
|
123
|
+
tm.size = {dtm.width, dtm.height};
|
|
70
124
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
125
|
+
return tm;
|
|
126
|
+
}
|
|
74
127
|
|
|
75
128
|
/**
|
|
76
129
|
* Measures an AttributedString on the platform, as identified by some
|
|
@@ -81,8 +134,7 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById(
|
|
|
81
134
|
ParagraphAttributes const ¶graphAttributes,
|
|
82
135
|
LayoutConstraints layoutConstraints) const {
|
|
83
136
|
assert(false);
|
|
84
|
-
|
|
85
|
-
return tm;
|
|
137
|
+
return {};
|
|
86
138
|
}
|
|
87
139
|
|
|
88
140
|
LinesMeasurements TextLayoutManager::measureLines(
|
|
@@ -90,9 +142,7 @@ LinesMeasurements TextLayoutManager::measureLines(
|
|
|
90
142
|
ParagraphAttributes paragraphAttributes,
|
|
91
143
|
Size size) const {
|
|
92
144
|
assert(false);
|
|
93
|
-
|
|
94
|
-
std::vector<LineMeasurement> paragraphLines{};
|
|
95
|
-
return paragraphLines;
|
|
145
|
+
return {};
|
|
96
146
|
}
|
|
97
147
|
|
|
98
148
|
void *TextLayoutManager::getNativeTextLayoutManager() const {
|
package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.h
CHANGED
|
@@ -28,7 +28,7 @@ class TextLayoutManager {
|
|
|
28
28
|
|
|
29
29
|
#pragma region required interface from core cxx cross platform impl
|
|
30
30
|
TextLayoutManager(const ContextContainer::Shared &contextContainer) : m_contextContainer(contextContainer){};
|
|
31
|
-
~TextLayoutManager();
|
|
31
|
+
~TextLayoutManager() = default;
|
|
32
32
|
|
|
33
33
|
/*
|
|
34
34
|
* Measures `attributedStringBox` using native text rendering infrastructure.
|
|
@@ -60,11 +60,17 @@ class TextLayoutManager {
|
|
|
60
60
|
*/
|
|
61
61
|
void *getNativeTextLayoutManager() const;
|
|
62
62
|
|
|
63
|
+
static void GetTextLayout(
|
|
64
|
+
AttributedStringBox attributedStringBox,
|
|
65
|
+
ParagraphAttributes paragraphAttributes,
|
|
66
|
+
LayoutConstraints layoutConstraints,
|
|
67
|
+
const std::optional<TextAlignment> &textAlignment,
|
|
68
|
+
winrt::com_ptr<IDWriteTextLayout> &spTextLayout) noexcept;
|
|
69
|
+
|
|
63
70
|
#pragma endregion
|
|
64
71
|
|
|
65
72
|
private:
|
|
66
73
|
ContextContainer::Shared m_contextContainer;
|
|
67
|
-
mutable winrt::com_ptr<IDWriteFactory> m_spDWriteFactory;
|
|
68
74
|
};
|
|
69
75
|
|
|
70
76
|
} // namespace react
|
|
@@ -507,6 +507,7 @@
|
|
|
507
507
|
<ClCompile Include="$(ReactNativeWindowsDir)codegen\react\components\rnwcore\ShadowNodes.cpp" />
|
|
508
508
|
<ClCompile Include="Fabric\ActivityIndicatorComponentView.cpp" />
|
|
509
509
|
<ClCompile Include="Fabric\ComponentViewRegistry.cpp" />
|
|
510
|
+
<ClCompile Include="Fabric\DWriteHelpers.cpp" />
|
|
510
511
|
<ClCompile Include="Fabric\FabricUIManagerModule.cpp" />
|
|
511
512
|
<ClCompile Include="Fabric\ImageComponentView.cpp" />
|
|
512
513
|
<ClCompile Include="Fabric\ImageManager.cpp" />
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
#include <JSValueWriter.h>
|
|
13
13
|
#include <Utils/PropertyUtils.h>
|
|
14
14
|
|
|
15
|
+
#include <UI.Xaml.Automation.Peers.h>
|
|
16
|
+
|
|
15
17
|
#ifdef USE_WINUI3
|
|
16
18
|
#define TAB_INDEX_PROPERTY xaml::UIElement::TabIndexProperty
|
|
17
19
|
#define TAB_STOP_PROPERTY xaml::UIElement::IsTabStopProperty
|
|
@@ -80,11 +82,18 @@ bool ControlViewManager::UpdateProperty(
|
|
|
80
82
|
}
|
|
81
83
|
} else if (propertyName == "focusable") {
|
|
82
84
|
if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean) {
|
|
85
|
+
IsFocusable(propertyValue.AsBoolean());
|
|
83
86
|
control.IsTabStop(propertyValue.AsBoolean());
|
|
84
87
|
} else if (propertyValue.IsNull()) {
|
|
85
88
|
control.ClearValue(TAB_STOP_PROPERTY());
|
|
89
|
+
IsFocusable(false);
|
|
86
90
|
}
|
|
87
91
|
} else {
|
|
92
|
+
if (propertyName == "accessible") {
|
|
93
|
+
if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean) {
|
|
94
|
+
IsAccessible(propertyValue.AsBoolean());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
88
97
|
ret = Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue);
|
|
89
98
|
}
|
|
90
99
|
}
|
|
@@ -96,6 +105,16 @@ bool ControlViewManager::UpdateProperty(
|
|
|
96
105
|
return ret;
|
|
97
106
|
}
|
|
98
107
|
|
|
108
|
+
void ControlViewManager::OnPropertiesUpdated(ShadowNodeBase *node) {
|
|
109
|
+
auto control(node->GetView().as<xaml::Controls::Control>());
|
|
110
|
+
|
|
111
|
+
if (IsAccessible() != IsFocusable()) {
|
|
112
|
+
control.IsTabStop(false);
|
|
113
|
+
xaml::Automation::AutomationProperties::SetAccessibilityView(
|
|
114
|
+
control, xaml::Automation::Peers::AccessibilityView::Raw);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
99
118
|
void ControlViewManager::OnViewCreated(XamlView view) {
|
|
100
119
|
// Set the default cornerRadius to 0 for Control: WinUI usually default cornerRadius to 2
|
|
101
120
|
// Only works on >= RS5 because Control.CornerRadius is only supported >= RS5
|
|
@@ -104,4 +123,17 @@ void ControlViewManager::OnViewCreated(XamlView view) {
|
|
|
104
123
|
}
|
|
105
124
|
}
|
|
106
125
|
|
|
126
|
+
void ControlViewManager::IsAccessible(bool accessible) {
|
|
127
|
+
m_isAccessible = accessible;
|
|
128
|
+
}
|
|
129
|
+
bool ControlViewManager::IsAccessible() {
|
|
130
|
+
return m_isAccessible;
|
|
131
|
+
}
|
|
132
|
+
void ControlViewManager::IsFocusable(bool focusable) {
|
|
133
|
+
m_isFocusable = focusable;
|
|
134
|
+
}
|
|
135
|
+
bool ControlViewManager::IsFocusable() {
|
|
136
|
+
return m_isFocusable;
|
|
137
|
+
}
|
|
138
|
+
|
|
107
139
|
} // namespace Microsoft::ReactNative
|
|
@@ -24,6 +24,17 @@ class REACTWINDOWS_EXPORT ControlViewManager : public FrameworkElementViewManage
|
|
|
24
24
|
|
|
25
25
|
protected:
|
|
26
26
|
void OnViewCreated(XamlView view) override;
|
|
27
|
+
|
|
28
|
+
void OnPropertiesUpdated(ShadowNodeBase *node) override;
|
|
29
|
+
|
|
30
|
+
private:
|
|
31
|
+
void IsAccessible(bool accessible);
|
|
32
|
+
bool IsAccessible();
|
|
33
|
+
void IsFocusable(bool focusable);
|
|
34
|
+
bool IsFocusable();
|
|
35
|
+
|
|
36
|
+
bool m_isAccessible = true;
|
|
37
|
+
bool m_isFocusable = true;
|
|
27
38
|
};
|
|
28
39
|
|
|
29
40
|
} // namespace Microsoft::ReactNative
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
#include <WindowsNumerics.h>
|
|
18
18
|
#include <winrt/Windows.Foundation.h>
|
|
19
19
|
|
|
20
|
+
#include <UI.Xaml.Automation.Peers.h>
|
|
20
21
|
#include <UI.Xaml.Automation.h>
|
|
21
22
|
#include <UI.Xaml.Controls.h>
|
|
22
23
|
#include "Utils/PropertyHandlerUtils.h"
|
|
@@ -416,13 +417,30 @@ bool FrameworkElementViewManager::UpdateProperty(
|
|
|
416
417
|
const std::string &innerName = pair.first;
|
|
417
418
|
const auto &innerValue = pair.second;
|
|
418
419
|
|
|
419
|
-
|
|
420
|
+
auto peer = xaml::Automation::Peers::FrameworkElementAutomationPeer::FromElement(element);
|
|
421
|
+
|
|
422
|
+
if (innerName == "selected") {
|
|
420
423
|
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Selected)] =
|
|
421
424
|
innerValue.AsBoolean();
|
|
422
|
-
|
|
425
|
+
const auto prevSelectedState = DynamicAutomationProperties::GetAccessibilityStateSelected(element);
|
|
426
|
+
if (peer != nullptr && prevSelectedState != innerValue.AsBoolean()) {
|
|
427
|
+
peer.RaisePropertyChangedEvent(
|
|
428
|
+
winrt::SelectionItemPatternIdentifiers::IsSelectedProperty(),
|
|
429
|
+
winrt::box_value(prevSelectedState),
|
|
430
|
+
winrt::box_value(innerValue.AsBoolean()));
|
|
431
|
+
}
|
|
432
|
+
} else if (innerName == "disabled") {
|
|
423
433
|
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Disabled)] =
|
|
424
434
|
innerValue.AsBoolean();
|
|
425
|
-
|
|
435
|
+
const auto prevDisabledState = DynamicAutomationProperties::GetAccessibilityStateDisabled(element);
|
|
436
|
+
|
|
437
|
+
if (peer != nullptr && prevDisabledState != innerValue.AsBoolean()) {
|
|
438
|
+
peer.RaisePropertyChangedEvent(
|
|
439
|
+
winrt::AutomationElementIdentifiers::IsEnabledProperty(),
|
|
440
|
+
winrt::box_value(!prevDisabledState),
|
|
441
|
+
winrt::box_value(!innerValue.AsBoolean()));
|
|
442
|
+
}
|
|
443
|
+
} else if (innerName == "checked") {
|
|
426
444
|
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Checked)] =
|
|
427
445
|
innerValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean && innerValue.AsBoolean();
|
|
428
446
|
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Unchecked)] =
|
|
@@ -430,6 +448,30 @@ bool FrameworkElementViewManager::UpdateProperty(
|
|
|
430
448
|
// If the state is "mixed" we'll just set both Checked and Unchecked to false,
|
|
431
449
|
// then later in the IToggleProvider implementation it will return the Intermediate state
|
|
432
450
|
// due to both being set to false (see DynamicAutomationPeer::ToggleState()).
|
|
451
|
+
const auto prevCheckedState = DynamicAutomationProperties::GetAccessibilityStateChecked(element);
|
|
452
|
+
const auto prevUncheckedState = DynamicAutomationProperties::GetAccessibilityStateUnchecked(element);
|
|
453
|
+
|
|
454
|
+
if (peer != nullptr) {
|
|
455
|
+
if (prevCheckedState !=
|
|
456
|
+
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Checked)] ||
|
|
457
|
+
prevUncheckedState !=
|
|
458
|
+
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Unchecked)]) {
|
|
459
|
+
// Checking if either state has changed here to catch changes involving "mixed" state.
|
|
460
|
+
const auto oldValue = prevCheckedState ? winrt::ToggleState::On : winrt::ToggleState::Off;
|
|
461
|
+
if (innerValue.Type() != winrt::Microsoft::ReactNative::JSValueType::Boolean) {
|
|
462
|
+
peer.RaisePropertyChangedEvent(
|
|
463
|
+
winrt::TogglePatternIdentifiers::ToggleStateProperty(),
|
|
464
|
+
winrt::box_value(oldValue),
|
|
465
|
+
winrt::box_value(winrt::ToggleState::Indeterminate));
|
|
466
|
+
} else {
|
|
467
|
+
const auto newValue = innerValue.AsBoolean() ? winrt::ToggleState::On : winrt::ToggleState::Off;
|
|
468
|
+
peer.RaisePropertyChangedEvent(
|
|
469
|
+
winrt::TogglePatternIdentifiers::ToggleStateProperty(),
|
|
470
|
+
winrt::box_value(oldValue),
|
|
471
|
+
winrt::box_value(newValue));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
433
475
|
} else if (innerName == "busy")
|
|
434
476
|
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Busy)] =
|
|
435
477
|
!innerValue.IsNull() && innerValue.AsBoolean();
|
|
@@ -438,6 +480,19 @@ bool FrameworkElementViewManager::UpdateProperty(
|
|
|
438
480
|
!innerValue.IsNull() && innerValue.AsBoolean();
|
|
439
481
|
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Collapsed)] =
|
|
440
482
|
innerValue.IsNull() || !innerValue.AsBoolean();
|
|
483
|
+
|
|
484
|
+
const auto prevExpandedState = DynamicAutomationProperties::GetAccessibilityStateExpanded(element);
|
|
485
|
+
|
|
486
|
+
if (peer != nullptr && prevExpandedState != innerValue.AsBoolean()) {
|
|
487
|
+
const auto newValue =
|
|
488
|
+
innerValue.AsBoolean() ? winrt::ExpandCollapseState::Expanded : winrt::ExpandCollapseState::Collapsed;
|
|
489
|
+
const auto oldValue =
|
|
490
|
+
prevExpandedState ? winrt::ExpandCollapseState::Expanded : winrt::ExpandCollapseState::Collapsed;
|
|
491
|
+
peer.RaisePropertyChangedEvent(
|
|
492
|
+
winrt::ExpandCollapsePatternIdentifiers::ExpandCollapseStateProperty(),
|
|
493
|
+
winrt::box_value(oldValue),
|
|
494
|
+
winrt::box_value(newValue));
|
|
495
|
+
}
|
|
441
496
|
}
|
|
442
497
|
}
|
|
443
498
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#include "ViewControl.h"
|
|
9
9
|
|
|
10
|
+
#include <UI.Xaml.Automation.Peers.h>
|
|
10
11
|
#include "DynamicAutomationProperties.h"
|
|
11
12
|
|
|
12
13
|
#include <JSValueWriter.h>
|
|
@@ -127,6 +128,14 @@ class ViewShadowNode : public ShadowNodeBase {
|
|
|
127
128
|
GetControl().IsTabStop(m_isFocusable);
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
bool IsAccessible() const {
|
|
132
|
+
return m_isAccessible;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void IsAccessible(bool isAccessible) {
|
|
136
|
+
m_isAccessible = isAccessible;
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
bool IsHitTestBrushRequired() const {
|
|
131
140
|
return IsRegisteredForMouseEvents();
|
|
132
141
|
}
|
|
@@ -251,6 +260,7 @@ class ViewShadowNode : public ShadowNodeBase {
|
|
|
251
260
|
bool m_enableFocusRing = true;
|
|
252
261
|
bool m_onClick = false;
|
|
253
262
|
bool m_isFocusable = false;
|
|
263
|
+
bool m_isAccessible = false;
|
|
254
264
|
int32_t m_tabIndex = std::numeric_limits<std::int32_t>::max();
|
|
255
265
|
|
|
256
266
|
xaml::Controls::ContentControl::GotFocus_revoker m_contentControlGotFocusRevoker{};
|
|
@@ -420,6 +430,11 @@ bool ViewViewManager::UpdateProperty(
|
|
|
420
430
|
pViewShadowNode->TabIndex(std::numeric_limits<std::int32_t>::max());
|
|
421
431
|
}
|
|
422
432
|
} else {
|
|
433
|
+
if (propertyName == "accessible") {
|
|
434
|
+
if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean) {
|
|
435
|
+
pViewShadowNode->IsAccessible(propertyValue.AsBoolean());
|
|
436
|
+
}
|
|
437
|
+
}
|
|
423
438
|
ret = Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue);
|
|
424
439
|
}
|
|
425
440
|
}
|
|
@@ -563,6 +578,12 @@ void ViewViewManager::TryUpdateView(
|
|
|
563
578
|
|
|
564
579
|
if (useControl)
|
|
565
580
|
pViewShadowNode->GetControl().Content(visualRoot);
|
|
581
|
+
|
|
582
|
+
if (useControl && pViewShadowNode->IsAccessible() != pViewShadowNode->IsFocusable()) {
|
|
583
|
+
pViewShadowNode->GetControl().IsTabStop(false);
|
|
584
|
+
xaml::Automation::AutomationProperties::SetAccessibilityView(
|
|
585
|
+
pViewShadowNode->GetControl(), xaml::Automation::Peers::AccessibilityView::Raw);
|
|
586
|
+
}
|
|
566
587
|
}
|
|
567
588
|
|
|
568
589
|
void ViewViewManager::SetLayoutProps(
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
-->
|
|
11
11
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
12
12
|
<PropertyGroup>
|
|
13
|
-
<ReactNativeWindowsVersion>0.0.0-canary.
|
|
13
|
+
<ReactNativeWindowsVersion>0.0.0-canary.490</ReactNativeWindowsVersion>
|
|
14
14
|
<ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
|
|
15
15
|
<ReactNativeWindowsMinor>0</ReactNativeWindowsMinor>
|
|
16
16
|
<ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
|