react-native-windows 0.81.37 → 0.81.40
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/Fabric/AbiComponentDescriptor.cpp +6 -2
- package/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp +0 -47
- package/Microsoft.ReactNative/Fabric/AbiShadowNode.h +40 -8
- package/Microsoft.ReactNative/Fabric/AbiState.h +26 -8
- package/Microsoft.ReactNative/Fabric/AbiTextInputProps.h +51 -0
- package/Microsoft.ReactNative/Fabric/AbiTextInputShadowNode.cpp +88 -0
- package/Microsoft.ReactNative/Fabric/AbiTextInputShadowNode.h +65 -0
- package/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.h +52 -18
- package/Microsoft.ReactNative/Fabric/AbiViewShadowNode.cpp +0 -18
- package/Microsoft.ReactNative/Fabric/AbiViewShadowNode.h +16 -4
- package/Microsoft.ReactNative/Fabric/ComponentView.cpp +2 -1
- package/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp +1 -2
- package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +1 -1
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +3 -2
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +6 -4
- package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +6 -4
- package/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +2 -1
- package/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp +22 -0
- package/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h +2 -1
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/AbiTextInputState.h +38 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/ConcreteTextInputShadowNode.h +272 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentDescriptor.h +21 -14
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +345 -90
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +24 -4
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.h +1 -1
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp +0 -205
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.h +22 -65
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.h +2 -1
- package/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp +0 -1
- package/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl +5 -0
- package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +0 -1
- package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +60 -5
- package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h +3 -0
- package/Microsoft.ReactNative/ViewProps.idl +11 -0
- package/Microsoft.ReactNative/Views/ViewViewManager.cpp +2 -2
- package/Microsoft.ReactNative.Cxx/ComponentView.Experimental.interop.h +12 -0
- package/PropertySheets/Generated/PackageVersion.g.props +3 -3
- package/PropertySheets/JSEngine.props +1 -1
- package/PropertySheets/React.Cpp.props +0 -1
- package/PropertySheets/WinUI.props +2 -1
- package/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/Instance.cpp +386 -0
- package/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/NativeToJsBridge.h +148 -0
- package/Scripts/EnableInternalWinAppSDKFeed.ps1 +12 -5
- package/Scripts/NuGetRestoreForceEvaluateAllSolutions.ps1 +2 -2
- package/Shared/Networking/WinRTWebSocketResource.h +1 -5
- package/Shared/OInstance.cpp +13 -8
- package/Shared/Shared.vcxitems +3 -3
- package/Shared/Shared.vcxitems.filters +1 -1
- package/external/folly/folly.vcxitems +10 -0
- package/index.windows.js +5 -0
- package/package.json +4 -4
- package/Microsoft.ReactNative/Fabric/AbiState.cpp +0 -29
package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include "WindowsTextInputComponentView.h"
|
|
7
7
|
|
|
8
8
|
#include <AutoDraw.h>
|
|
9
|
+
#include <Fabric/AbiTextInputProps.h>
|
|
9
10
|
#include <Fabric/Composition/UiaHelpers.h>
|
|
10
11
|
#include <Fabric/platform/react/renderer/graphics/PlatformColorUtils.h>
|
|
11
12
|
#include <Utils/ThemeUtils.h>
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
#include <winrt/Windows.System.h>
|
|
20
21
|
#include <winrt/Windows.UI.h>
|
|
21
22
|
#include <mutex>
|
|
23
|
+
#include <string_view>
|
|
22
24
|
#include "../Composition.Input.h"
|
|
23
25
|
#include "../CompositionHelpers.h"
|
|
24
26
|
#include "../RootComponentView.h"
|
|
@@ -83,7 +85,7 @@ static PCreateTextServices g_pfnCreateTextServices;
|
|
|
83
85
|
HRESULT HrEnsureRichEd20Loaded() noexcept {
|
|
84
86
|
HRESULT hr = S_OK;
|
|
85
87
|
std::call_once(g_richEditLoadedFlag, [&hr]() {
|
|
86
|
-
g_hInstRichEdit =
|
|
88
|
+
g_hInstRichEdit = LoadLibraryExW(L"Msftedit.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
87
89
|
if (!g_hInstRichEdit) {
|
|
88
90
|
hr = E_FAIL;
|
|
89
91
|
return;
|
|
@@ -543,28 +545,118 @@ AutoCorrectOffCallback(LANGID langid, const WCHAR *pszBefore, WCHAR *pszAfter, L
|
|
|
543
545
|
}
|
|
544
546
|
|
|
545
547
|
facebook::react::AttributedString WindowsTextInputComponentView::getAttributedString() const {
|
|
546
|
-
// Use BaseTextShadowNode to get attributed string from children
|
|
547
|
-
|
|
548
548
|
auto attributedString = facebook::react::AttributedString{};
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
549
|
+
const auto &props = windowsTextInputProps();
|
|
550
|
+
auto baseTextAttributes = facebook::react::TextAttributes::defaultTextAttributes();
|
|
551
|
+
baseTextAttributes.fontSizeMultiplier = m_fontSizeMultiplier;
|
|
552
|
+
baseTextAttributes.apply(props.textAttributes);
|
|
553
|
+
baseTextAttributes.allowFontScaling = baseTextAttributes.allowFontScaling.value_or(props.allowFontScaling);
|
|
554
|
+
baseTextAttributes.foregroundColor = props.textAttributes.foregroundColor;
|
|
555
|
+
baseTextAttributes.backgroundColor = facebook::react::clearColor();
|
|
556
|
+
attributedString.setBaseTextAttributes(baseTextAttributes);
|
|
557
|
+
|
|
558
|
+
if (!m_textServices) {
|
|
559
|
+
return attributedString;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
winrt::com_ptr<::IUnknown> richEditOle;
|
|
563
|
+
LRESULT result;
|
|
564
|
+
winrt::check_hresult(
|
|
565
|
+
m_textServices->TxSendMessage(EM_GETOLEINTERFACE, 0, reinterpret_cast<LPARAM>(richEditOle.put()), &result));
|
|
566
|
+
winrt::check_bool(richEditOle != nullptr);
|
|
567
|
+
auto document = richEditOle.as<ITextDocument>();
|
|
568
|
+
winrt::com_ptr<ITextRange> range;
|
|
569
|
+
winrt::check_hresult(document->Range(0, 0, range.put()));
|
|
570
|
+
LONG storyLength;
|
|
571
|
+
winrt::check_hresult(range->GetStoryLength(&storyLength));
|
|
572
|
+
LRESULT textMode{};
|
|
573
|
+
winrt::check_hresult(m_textServices->TxSendMessage(EM_GETTEXTMODE, 0, 0, &textMode));
|
|
574
|
+
// Only rich-text stories include a final paragraph mark outside the TextInput's value.
|
|
575
|
+
const LONG textLength = storyLength - ((textMode & TM_RICHTEXT) ? 1 : 0);
|
|
576
|
+
|
|
577
|
+
float fontSizeMultiplier = 1.0f;
|
|
578
|
+
if (*baseTextAttributes.allowFontScaling) {
|
|
579
|
+
auto maxFontSizeMultiplier = baseTextAttributes.maxFontSizeMultiplier;
|
|
580
|
+
fontSizeMultiplier = (maxFontSizeMultiplier >= 1.0f)
|
|
581
|
+
? std::min(maxFontSizeMultiplier, baseTextAttributes.fontSizeMultiplier)
|
|
582
|
+
: baseTextAttributes.fontSizeMultiplier;
|
|
583
|
+
}
|
|
584
|
+
winrt::check_bool(fontSizeMultiplier > 0);
|
|
585
|
+
|
|
586
|
+
for (LONG position = 0; position < textLength;) {
|
|
587
|
+
winrt::check_hresult(range->SetRange(position, position));
|
|
588
|
+
LONG delta;
|
|
589
|
+
winrt::check_hresult(range->EndOf(tomCharFormat, tomExtend, &delta));
|
|
590
|
+
LONG end;
|
|
591
|
+
winrt::check_hresult(range->GetEnd(&end));
|
|
592
|
+
end = std::min(end, textLength);
|
|
593
|
+
winrt::check_bool(end > position);
|
|
594
|
+
winrt::check_hresult(range->SetRange(position, end));
|
|
595
|
+
|
|
558
596
|
auto fragment = facebook::react::AttributedString::Fragment{};
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
597
|
+
BSTR text = nullptr;
|
|
598
|
+
winrt::check_hresult(range->GetText(&text));
|
|
599
|
+
auto bstrToString = [](BSTR value) {
|
|
600
|
+
auto converted = value ? winrt::to_string(std::wstring_view{value, SysStringLen(value)}) : std::string{};
|
|
601
|
+
SysFreeString(value);
|
|
602
|
+
return converted;
|
|
603
|
+
};
|
|
604
|
+
fragment.string = bstrToString(text);
|
|
605
|
+
auto &textAttributes = fragment.textAttributes;
|
|
606
|
+
textAttributes = baseTextAttributes;
|
|
607
|
+
|
|
608
|
+
winrt::com_ptr<ITextFont> font;
|
|
609
|
+
winrt::check_hresult(range->GetFont(font.put()));
|
|
610
|
+
BSTR fontName = nullptr;
|
|
611
|
+
winrt::check_hresult(font->GetName(&fontName));
|
|
612
|
+
textAttributes.fontFamily = bstrToString(fontName);
|
|
613
|
+
float fontSize;
|
|
614
|
+
winrt::check_hresult(font->GetSize(&fontSize));
|
|
615
|
+
// TOM reports rendered points; store unscaled DIPs so UpdateText does not scale them twice.
|
|
616
|
+
textAttributes.fontSize = fontSize * (4.0f / 3.0f) / fontSizeMultiplier;
|
|
617
|
+
LONG weight;
|
|
618
|
+
winrt::check_hresult(font->GetWeight(&weight));
|
|
619
|
+
textAttributes.fontWeight = static_cast<facebook::react::FontWeight>(weight);
|
|
620
|
+
LONG italic;
|
|
621
|
+
winrt::check_hresult(font->GetItalic(&italic));
|
|
622
|
+
textAttributes.fontStyle =
|
|
623
|
+
italic == tomTrue ? facebook::react::FontStyle::Italic : facebook::react::FontStyle::Normal;
|
|
624
|
+
float spacing;
|
|
625
|
+
winrt::check_hresult(font->GetSpacing(&spacing));
|
|
626
|
+
textAttributes.letterSpacing = spacing;
|
|
627
|
+
|
|
628
|
+
LONG underline;
|
|
629
|
+
LONG strikethrough;
|
|
630
|
+
winrt::check_hresult(font->GetUnderline(&underline));
|
|
631
|
+
winrt::check_hresult(font->GetStrikeThrough(&strikethrough));
|
|
632
|
+
const bool hasUnderline = underline != tomNone;
|
|
633
|
+
const bool hasStrikethrough = strikethrough == tomTrue;
|
|
634
|
+
textAttributes.textDecorationLineType = hasUnderline
|
|
635
|
+
? (hasStrikethrough ? facebook::react::TextDecorationLineType::UnderlineStrikethrough
|
|
636
|
+
: facebook::react::TextDecorationLineType::Underline)
|
|
637
|
+
: (hasStrikethrough ? facebook::react::TextDecorationLineType::Strikethrough
|
|
638
|
+
: facebook::react::TextDecorationLineType::None);
|
|
639
|
+
|
|
640
|
+
LONG foreground;
|
|
641
|
+
LONG background;
|
|
642
|
+
winrt::check_hresult(font->GetForeColor(&foreground));
|
|
643
|
+
winrt::check_hresult(font->GetBackColor(&background));
|
|
644
|
+
if (foreground != tomAutoColor) {
|
|
645
|
+
textAttributes.foregroundColor =
|
|
646
|
+
facebook::react::colorFromRGBA(GetRValue(foreground), GetGValue(foreground), GetBValue(foreground), 255);
|
|
647
|
+
}
|
|
648
|
+
if (background != tomAutoColor) {
|
|
649
|
+
textAttributes.backgroundColor =
|
|
650
|
+
facebook::react::colorFromRGBA(GetRValue(background), GetGValue(background), GetBValue(background), 255);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
auto &fragments = attributedString.getFragments();
|
|
654
|
+
if (!fragments.empty() && fragments.back().textAttributes == textAttributes) {
|
|
655
|
+
fragments.back().string += fragment.string;
|
|
656
|
+
} else {
|
|
657
|
+
attributedString.appendFragment(std::move(fragment));
|
|
658
|
+
}
|
|
659
|
+
position = end;
|
|
568
660
|
}
|
|
569
661
|
|
|
570
662
|
return attributedString;
|
|
@@ -573,13 +665,15 @@ facebook::react::AttributedString WindowsTextInputComponentView::getAttributedSt
|
|
|
573
665
|
WindowsTextInputComponentView::WindowsTextInputComponentView(
|
|
574
666
|
const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
575
667
|
facebook::react::Tag tag,
|
|
576
|
-
winrt::Microsoft::ReactNative::ReactContext const &reactContext
|
|
668
|
+
winrt::Microsoft::ReactNative::ReactContext const &reactContext,
|
|
669
|
+
winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder *builder)
|
|
577
670
|
: Super(
|
|
578
671
|
WindowsTextInputComponentView::defaultProps(),
|
|
579
672
|
compContext,
|
|
580
673
|
tag,
|
|
581
674
|
reactContext,
|
|
582
|
-
ComponentViewFeatures::Default & ~ComponentViewFeatures::Background
|
|
675
|
+
ComponentViewFeatures::Default & ~ComponentViewFeatures::Background,
|
|
676
|
+
builder) {}
|
|
583
677
|
|
|
584
678
|
WindowsTextInputComponentView::~WindowsTextInputComponentView() {
|
|
585
679
|
// Release text services first to prevent further callbacks into CompTextHost.
|
|
@@ -591,6 +685,17 @@ WindowsTextInputComponentView::~WindowsTextInputComponentView() {
|
|
|
591
685
|
}
|
|
592
686
|
}
|
|
593
687
|
|
|
688
|
+
winrt::Microsoft::ReactNative::ViewProps WindowsTextInputComponentView::ViewPropsInner() noexcept {
|
|
689
|
+
// During initialization, even custom text inputs still have native default props.
|
|
690
|
+
if (auto abiProps = dynamic_cast<const ::Microsoft::ReactNative::AbiTextInputProps *>(viewProps().get())) {
|
|
691
|
+
if (auto props = abiProps->ViewProps()) {
|
|
692
|
+
return props;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return winrt::make<winrt::Microsoft::ReactNative::implementation::TextInputProps>(viewProps());
|
|
697
|
+
}
|
|
698
|
+
|
|
594
699
|
void WindowsTextInputComponentView::HandleCommand(
|
|
595
700
|
const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept {
|
|
596
701
|
Super::HandleCommand(args);
|
|
@@ -608,7 +713,9 @@ void WindowsTextInputComponentView::HandleCommand(
|
|
|
608
713
|
{
|
|
609
714
|
if (text.has_value()) {
|
|
610
715
|
DrawBlock db(*this);
|
|
611
|
-
|
|
716
|
+
facebook::react::AttributedString attributedString;
|
|
717
|
+
attributedString.appendFragment({winrt::to_string(text.value()), windowsTextInputProps().textAttributes, {}});
|
|
718
|
+
UpdateText(attributedString);
|
|
612
719
|
}
|
|
613
720
|
|
|
614
721
|
SELCHANGE sc;
|
|
@@ -1039,21 +1146,23 @@ void WindowsTextInputComponentView::OnCharacterReceived(
|
|
|
1039
1146
|
return;
|
|
1040
1147
|
}
|
|
1041
1148
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1149
|
+
if (m_eventEmitter) {
|
|
1150
|
+
// convert keyCode to std::string
|
|
1151
|
+
wchar_t key[2] = L" ";
|
|
1152
|
+
key[0] = static_cast<wchar_t>(args.KeyCode());
|
|
1153
|
+
std::string keyString = ::Microsoft::Common::Unicode::Utf16ToUtf8(key, 1);
|
|
1154
|
+
// Call onKeyPress event
|
|
1155
|
+
auto emitter = std::static_pointer_cast<const facebook::react::WindowsTextInputEventEmitter>(m_eventEmitter);
|
|
1156
|
+
facebook::react::WindowsTextInputEventEmitter::OnKeyPress onKeyPressArgs;
|
|
1157
|
+
if (keyString.compare("\r") == 0) {
|
|
1158
|
+
onKeyPressArgs.key = "Enter";
|
|
1159
|
+
} else if (keyString.compare("\b") == 0) {
|
|
1160
|
+
onKeyPressArgs.key = "Backspace";
|
|
1161
|
+
} else {
|
|
1162
|
+
onKeyPressArgs.key = keyString;
|
|
1163
|
+
}
|
|
1164
|
+
emitter->onKeyPress(onKeyPressArgs);
|
|
1055
1165
|
}
|
|
1056
|
-
emitter->onKeyPress(onKeyPressArgs);
|
|
1057
1166
|
|
|
1058
1167
|
WPARAM wParam = static_cast<WPARAM>(args.KeyCode());
|
|
1059
1168
|
|
|
@@ -1160,6 +1269,12 @@ void WindowsTextInputComponentView::updateCursorColor(
|
|
|
1160
1269
|
m_caretVisual.Brush(theme()->Brush(*caretColor));
|
|
1161
1270
|
}
|
|
1162
1271
|
|
|
1272
|
+
const winrt::Microsoft::ReactNative::IComponentProps WindowsTextInputComponentView::userProps(
|
|
1273
|
+
facebook::react::Props::Shared const &props) noexcept {
|
|
1274
|
+
const auto &textInputProps = *std::static_pointer_cast<const ::Microsoft::ReactNative::AbiTextInputProps>(props);
|
|
1275
|
+
return textInputProps.UserProps();
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1163
1278
|
void WindowsTextInputComponentView::updateProps(
|
|
1164
1279
|
facebook::react::Props::Shared const &props,
|
|
1165
1280
|
facebook::react::Props::Shared const &oldProps) noexcept {
|
|
@@ -1270,15 +1385,28 @@ void WindowsTextInputComponentView::updateProps(
|
|
|
1270
1385
|
void WindowsTextInputComponentView::updateState(
|
|
1271
1386
|
facebook::react::State::Shared const &state,
|
|
1272
1387
|
facebook::react::State::Shared const &oldState) noexcept {
|
|
1273
|
-
|
|
1388
|
+
if (m_builder) {
|
|
1389
|
+
m_abistate = std::static_pointer_cast<::Microsoft::ReactNative::AbiTextInputShadowNode::ConcreteState const>(state);
|
|
1390
|
+
} else {
|
|
1391
|
+
m_state = std::static_pointer_cast<facebook::react::WindowsTextInputShadowNode::ConcreteState const>(state);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// Avoid new-ing up a new AbiComponentState on every state change if we are not a custom component
|
|
1395
|
+
if (m_builder && m_builder->UpdateStateHandler()) {
|
|
1396
|
+
m_builder->UpdateStateHandler()(
|
|
1397
|
+
*this,
|
|
1398
|
+
winrt::make<::Microsoft::ReactNative::AbiComponentState<::Microsoft::ReactNative::AbiTextInputStateData>>(
|
|
1399
|
+
state));
|
|
1400
|
+
}
|
|
1274
1401
|
|
|
1275
|
-
if (!m_state) {
|
|
1402
|
+
if (!m_state && !m_abistate) {
|
|
1276
1403
|
assert(false && "State is `null` for <TextInput> component.");
|
|
1277
1404
|
return;
|
|
1278
1405
|
}
|
|
1279
1406
|
|
|
1280
1407
|
if (!oldState) {
|
|
1281
|
-
m_mostRecentEventCount =
|
|
1408
|
+
m_mostRecentEventCount =
|
|
1409
|
+
m_builder ? m_abistate->getData().mostRecentEventCount : m_state->getData().mostRecentEventCount;
|
|
1282
1410
|
}
|
|
1283
1411
|
|
|
1284
1412
|
if (auto root = rootComponentView()) {
|
|
@@ -1290,22 +1418,32 @@ void WindowsTextInputComponentView::updateState(
|
|
|
1290
1418
|
}
|
|
1291
1419
|
}
|
|
1292
1420
|
|
|
1293
|
-
|
|
1421
|
+
// Userdata-only updates must not replay potentially stale native text.
|
|
1422
|
+
const bool textChanged = !m_builder || !oldState ||
|
|
1423
|
+
m_abistate->getData().attributedStringBox !=
|
|
1424
|
+
static_cast<const ::Microsoft::ReactNative::AbiTextInputShadowNode::ConcreteState *>(oldState.get())
|
|
1425
|
+
->getData()
|
|
1426
|
+
.attributedStringBox;
|
|
1427
|
+
if (textChanged &&
|
|
1428
|
+
(m_builder ? m_mostRecentEventCount == m_abistate->getData().mostRecentEventCount
|
|
1429
|
+
: m_mostRecentEventCount == m_state->getData().mostRecentEventCount)) {
|
|
1294
1430
|
m_comingFromState = true;
|
|
1295
|
-
auto &fragments = m_state->getData().attributedStringBox.getValue().getFragments();
|
|
1296
1431
|
{
|
|
1297
1432
|
// DrawBlock defers DrawText() until after UpdateText completes
|
|
1298
1433
|
DrawBlock db(*this);
|
|
1299
|
-
UpdateText(
|
|
1434
|
+
UpdateText(
|
|
1435
|
+
m_builder ? m_abistate->getData().attributedStringBox.getValue()
|
|
1436
|
+
: m_state->getData().attributedStringBox.getValue());
|
|
1300
1437
|
}
|
|
1301
1438
|
m_comingFromState = false;
|
|
1302
1439
|
}
|
|
1303
1440
|
}
|
|
1304
1441
|
|
|
1305
|
-
void WindowsTextInputComponentView::UpdateText(const
|
|
1442
|
+
void WindowsTextInputComponentView::UpdateText(const facebook::react::AttributedString &attributedString) noexcept {
|
|
1443
|
+
DrawBlock db(*this);
|
|
1306
1444
|
SETTEXTEX stt;
|
|
1307
1445
|
memset(&stt, 0, sizeof(stt));
|
|
1308
|
-
stt.flags =
|
|
1446
|
+
stt.flags = 0x20 /*ST_PLAINTEXTONLY*/;
|
|
1309
1447
|
stt.codepage = CP_UTF8;
|
|
1310
1448
|
LRESULT res;
|
|
1311
1449
|
|
|
@@ -1313,8 +1451,51 @@ void WindowsTextInputComponentView::UpdateText(const std::string &str) noexcept
|
|
|
1313
1451
|
cr.cpMin = cr.cpMax = 0;
|
|
1314
1452
|
winrt::check_hresult(m_textServices->TxSendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&cr), &res));
|
|
1315
1453
|
|
|
1454
|
+
// Formatting uses temporary selections and must not publish intermediate text or selection changes.
|
|
1455
|
+
LRESULT eventMask;
|
|
1456
|
+
winrt::check_hresult(m_textServices->TxSendMessage(EM_GETEVENTMASK, 0, 0, &eventMask));
|
|
1457
|
+
winrt::check_hresult(
|
|
1458
|
+
m_textServices->TxSendMessage(EM_SETEVENTMASK, 0, eventMask & ~(ENM_CHANGE | ENM_SELCHANGE), &res));
|
|
1459
|
+
|
|
1460
|
+
auto text = attributedString.getString();
|
|
1316
1461
|
winrt::check_hresult(m_textServices->TxSendMessage(
|
|
1317
|
-
EM_SETTEXTEX, reinterpret_cast<WPARAM>(&stt), reinterpret_cast<LPARAM>(
|
|
1462
|
+
EM_SETTEXTEX, reinterpret_cast<WPARAM>(&stt), reinterpret_cast<LPARAM>(text.c_str()), &res));
|
|
1463
|
+
|
|
1464
|
+
auto baseTextAttributes = windowsTextInputProps().textAttributes;
|
|
1465
|
+
baseTextAttributes.apply(attributedString.getBaseTextAttributes());
|
|
1466
|
+
auto charFormat = GetCharFormat(baseTextAttributes);
|
|
1467
|
+
winrt::check_hresult(
|
|
1468
|
+
m_textServices->TxSendMessage(EM_SETCHARFORMAT, SCF_ALL, reinterpret_cast<LPARAM>(&charFormat), &res));
|
|
1469
|
+
winrt::check_bool(res != 0);
|
|
1470
|
+
|
|
1471
|
+
// Richedit in password mode does not support per-fragment formatting, so we only apply it when not in secureTextEntry
|
|
1472
|
+
// mode.
|
|
1473
|
+
if (!windowsTextInputProps().secureTextEntry) {
|
|
1474
|
+
LONG position = 0;
|
|
1475
|
+
bool previousWasCR = false;
|
|
1476
|
+
for (const auto &fragment : attributedString.getFragments()) {
|
|
1477
|
+
auto fragmentText = ::Microsoft::Common::Unicode::Utf8ToUtf16(fragment.string);
|
|
1478
|
+
LONG end = position;
|
|
1479
|
+
for (auto character : fragmentText) {
|
|
1480
|
+
// RichEdit positions count UTF-16 code units, with CRLF normalized to a single paragraph mark.
|
|
1481
|
+
if (character != L'\n' || !previousWasCR) {
|
|
1482
|
+
++end;
|
|
1483
|
+
}
|
|
1484
|
+
previousWasCR = character == L'\r';
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
if (end != position) {
|
|
1488
|
+
auto textAttributes = baseTextAttributes;
|
|
1489
|
+
textAttributes.apply(fragment.textAttributes);
|
|
1490
|
+
charFormat = GetCharFormat(textAttributes);
|
|
1491
|
+
winrt::check_hresult(m_textServices->TxSendMessage(EM_SETSEL, position, end, &res));
|
|
1492
|
+
winrt::check_hresult(m_textServices->TxSendMessage(
|
|
1493
|
+
EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&charFormat), &res));
|
|
1494
|
+
winrt::check_bool(res != 0);
|
|
1495
|
+
}
|
|
1496
|
+
position = end;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1318
1499
|
|
|
1319
1500
|
winrt::check_hresult(
|
|
1320
1501
|
m_textServices->TxSendMessage(EM_SETSEL, static_cast<WPARAM>(cr.cpMin), static_cast<LPARAM>(cr.cpMax), &res));
|
|
@@ -1322,6 +1503,20 @@ void WindowsTextInputComponentView::UpdateText(const std::string &str) noexcept
|
|
|
1322
1503
|
// enable colored emojis
|
|
1323
1504
|
winrt::check_hresult(
|
|
1324
1505
|
m_textServices->TxSendMessage(EM_SETTYPOGRAPHYOPTIONS, 0x1000 | 0x2000, 0x1000 | 0x2000, nullptr));
|
|
1506
|
+
|
|
1507
|
+
m_recalculateContentVerticalOffset = true;
|
|
1508
|
+
winrt::check_hresult(m_textServices->TxSendMessage(EM_SETEVENTMASK, 0, eventMask, &res));
|
|
1509
|
+
if ((eventMask & ENM_CHANGE) && !m_comingFromState) {
|
|
1510
|
+
OnTextUpdated();
|
|
1511
|
+
}
|
|
1512
|
+
if (eventMask & ENM_SELCHANGE) {
|
|
1513
|
+
CHARRANGE finalSelection;
|
|
1514
|
+
winrt::check_hresult(
|
|
1515
|
+
m_textServices->TxSendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&finalSelection), &res));
|
|
1516
|
+
if (finalSelection.cpMin != cr.cpMin || finalSelection.cpMax != cr.cpMax) {
|
|
1517
|
+
OnSelectionChanged(finalSelection.cpMin, finalSelection.cpMax);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1325
1520
|
}
|
|
1326
1521
|
|
|
1327
1522
|
void WindowsTextInputComponentView::updateLayoutMetrics(
|
|
@@ -1373,7 +1568,10 @@ std::pair<float, float> WindowsTextInputComponentView::GetContentSize() const no
|
|
|
1373
1568
|
|
|
1374
1569
|
SIZE extentHimetric = {
|
|
1375
1570
|
static_cast<LONG>(availableWidth * scale * HIMETRIC_PER_INCH / dpi),
|
|
1376
|
-
|
|
1571
|
+
// -1 measures without zoom, matching how this host draws (TxGetExtent returns E_NOTIMPL).
|
|
1572
|
+
// A positive extent is taken as a zoom reference; LONG_MAX scaled to HIMETRIC saturated to a
|
|
1573
|
+
// huge positive value on ARM64 and shrank the natural height to about 0.
|
|
1574
|
+
-1};
|
|
1377
1575
|
|
|
1378
1576
|
SIZE naturalSize = {0, 0};
|
|
1379
1577
|
|
|
@@ -1401,14 +1599,22 @@ std::pair<float, float> WindowsTextInputComponentView::GetContentSize() const no
|
|
|
1401
1599
|
|
|
1402
1600
|
// When we are notified by RichEdit that the text changed, we need to notify JS
|
|
1403
1601
|
void WindowsTextInputComponentView::OnTextUpdated() noexcept {
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1602
|
+
if (m_builder) {
|
|
1603
|
+
auto attributedStringBox = facebook::react::AttributedStringBox(getAttributedString());
|
|
1604
|
+
const auto eventCount = m_nativeEventCount;
|
|
1605
|
+
m_abistate->updateState([attributedStringBox = std::move(attributedStringBox),
|
|
1606
|
+
eventCount](const ::Microsoft::ReactNative::AbiTextInputStateData &oldData) {
|
|
1607
|
+
auto data = oldData;
|
|
1608
|
+
data.attributedStringBox = attributedStringBox;
|
|
1609
|
+
data.mostRecentEventCount = eventCount;
|
|
1610
|
+
return std::make_shared<const ::Microsoft::ReactNative::AbiTextInputStateData>(std::move(data));
|
|
1611
|
+
});
|
|
1612
|
+
} else {
|
|
1613
|
+
auto data = m_state->getData();
|
|
1614
|
+
data.attributedStringBox = facebook::react::AttributedStringBox(getAttributedString());
|
|
1615
|
+
data.mostRecentEventCount = m_nativeEventCount;
|
|
1616
|
+
m_state->updateState(std::move(data));
|
|
1617
|
+
}
|
|
1412
1618
|
|
|
1413
1619
|
if (m_eventEmitter && !m_comingFromJS) {
|
|
1414
1620
|
// call onChange event
|
|
@@ -1537,7 +1743,9 @@ std::optional<std::string> WindowsTextInputComponentView::getAccessiblityValue()
|
|
|
1537
1743
|
}
|
|
1538
1744
|
|
|
1539
1745
|
void WindowsTextInputComponentView::setAcccessiblityValue(std::string &&value) noexcept {
|
|
1540
|
-
|
|
1746
|
+
facebook::react::AttributedString attributedString;
|
|
1747
|
+
attributedString.appendFragment({std::move(value), windowsTextInputProps().textAttributes, {}});
|
|
1748
|
+
UpdateText(attributedString);
|
|
1541
1749
|
}
|
|
1542
1750
|
|
|
1543
1751
|
bool WindowsTextInputComponentView::getAcccessiblityIsReadOnly() noexcept {
|
|
@@ -1554,6 +1762,11 @@ const facebook::react::WindowsTextInputProps &WindowsTextInputComponentView::win
|
|
|
1554
1762
|
}
|
|
1555
1763
|
|
|
1556
1764
|
void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
|
|
1765
|
+
m_cf = GetCharFormat(windowsTextInputProps().textAttributes);
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
CHARFORMAT2W WindowsTextInputComponentView::GetCharFormat(
|
|
1769
|
+
const facebook::react::TextAttributes &textAttributes) noexcept {
|
|
1557
1770
|
CHARFORMAT2W cfNew = {0};
|
|
1558
1771
|
// Set CHARFORMAT structure
|
|
1559
1772
|
cfNew.cbSize = sizeof(CHARFORMAT2W);
|
|
@@ -1567,15 +1780,18 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
|
|
|
1567
1780
|
// m_crText = RemoveAlpha(fontDetails.FontColor);
|
|
1568
1781
|
// }
|
|
1569
1782
|
|
|
1570
|
-
const auto &props = windowsTextInputProps();
|
|
1571
1783
|
float fontSize =
|
|
1572
|
-
(std::isnan(
|
|
1573
|
-
|
|
1784
|
+
(std::isnan(textAttributes.fontSize) ? facebook::react::TextAttributes::defaultTextAttributes().fontSize
|
|
1785
|
+
: textAttributes.fontSize);
|
|
1574
1786
|
|
|
1575
1787
|
// Apply maxFontSizeMultiplier if specified
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1788
|
+
if (textAttributes.allowFontScaling.value_or(windowsTextInputProps().allowFontScaling)) {
|
|
1789
|
+
auto fontSizeMultiplier =
|
|
1790
|
+
std::isnan(textAttributes.fontSizeMultiplier) ? m_fontSizeMultiplier : textAttributes.fontSizeMultiplier;
|
|
1791
|
+
auto maxFontSizeMultiplier = textAttributes.maxFontSizeMultiplier;
|
|
1792
|
+
fontSize *=
|
|
1793
|
+
(maxFontSizeMultiplier >= 1.0f) ? std::min(maxFontSizeMultiplier, fontSizeMultiplier) : fontSizeMultiplier;
|
|
1794
|
+
}
|
|
1579
1795
|
|
|
1580
1796
|
cfNew.dwMask |= CFM_SIZE;
|
|
1581
1797
|
// set font size -- 15 to convert twips to pt
|
|
@@ -1583,20 +1799,19 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
|
|
|
1583
1799
|
|
|
1584
1800
|
// set bold
|
|
1585
1801
|
cfNew.dwMask |= CFM_WEIGHT;
|
|
1586
|
-
cfNew.wWeight =
|
|
1587
|
-
props.textAttributes.fontWeight ? static_cast<WORD>(*props.textAttributes.fontWeight) : DWRITE_FONT_WEIGHT_NORMAL;
|
|
1802
|
+
cfNew.wWeight = textAttributes.fontWeight ? static_cast<WORD>(*textAttributes.fontWeight) : DWRITE_FONT_WEIGHT_NORMAL;
|
|
1588
1803
|
|
|
1589
1804
|
// set font style (italic)
|
|
1590
1805
|
cfNew.dwMask |= CFM_ITALIC;
|
|
1591
|
-
if (
|
|
1592
|
-
|
|
1806
|
+
if (textAttributes.fontStyle == facebook::react::FontStyle::Italic ||
|
|
1807
|
+
textAttributes.fontStyle == facebook::react::FontStyle::Oblique) {
|
|
1593
1808
|
cfNew.dwEffects |= CFE_ITALIC;
|
|
1594
1809
|
}
|
|
1595
1810
|
|
|
1596
1811
|
// set text decoration (underline and strikethrough)
|
|
1597
1812
|
cfNew.dwMask |= (CFM_UNDERLINE | CFM_STRIKEOUT);
|
|
1598
|
-
if (
|
|
1599
|
-
auto decorationType = *
|
|
1813
|
+
if (textAttributes.textDecorationLineType.has_value()) {
|
|
1814
|
+
auto decorationType = *textAttributes.textDecorationLineType;
|
|
1600
1815
|
if (decorationType == facebook::react::TextDecorationLineType::Underline ||
|
|
1601
1816
|
decorationType == facebook::react::TextDecorationLineType::UnderlineStrikethrough) {
|
|
1602
1817
|
cfNew.dwEffects |= CFE_UNDERLINE;
|
|
@@ -1608,11 +1823,10 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
|
|
|
1608
1823
|
}
|
|
1609
1824
|
|
|
1610
1825
|
// set font family
|
|
1611
|
-
if (!
|
|
1826
|
+
if (!textAttributes.fontFamily.empty()) {
|
|
1612
1827
|
cfNew.dwMask |= CFM_FACE;
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
wcsncpy_s(cfNew.szFaceName, fontFamily.c_str(), LF_FACESIZE);
|
|
1828
|
+
auto fontFamily = ::Microsoft::Common::Unicode::Utf8ToUtf16(textAttributes.fontFamily);
|
|
1829
|
+
wcsncpy_s(cfNew.szFaceName, fontFamily.c_str(), _TRUNCATE);
|
|
1616
1830
|
} else {
|
|
1617
1831
|
cfNew.dwMask |= CFM_FACE;
|
|
1618
1832
|
wcsncpy_s(cfNew.szFaceName, L"Segoe UI\0", LF_FACESIZE);
|
|
@@ -1624,17 +1838,29 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
|
|
|
1624
1838
|
cfNew.yOffset = 0;
|
|
1625
1839
|
|
|
1626
1840
|
// set letter spacing
|
|
1627
|
-
float letterSpacing =
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1841
|
+
float letterSpacing = textAttributes.letterSpacing;
|
|
1842
|
+
cfNew.dwMask |= CFM_SPACING;
|
|
1843
|
+
cfNew.sSpacing = std::isnan(letterSpacing) ? 0 : static_cast<SHORT>(letterSpacing * 20); // Convert to TWIPS
|
|
1844
|
+
|
|
1845
|
+
cfNew.dwMask |= CFM_COLOR | CFM_BACKCOLOR;
|
|
1846
|
+
cfNew.dwEffects |= CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
|
|
1847
|
+
if (textAttributes.foregroundColor &&
|
|
1848
|
+
textAttributes.foregroundColor != windowsTextInputProps().textAttributes.foregroundColor) {
|
|
1849
|
+
auto color = theme()->Color(*textAttributes.foregroundColor);
|
|
1850
|
+
cfNew.crTextColor = RGB(color.R, color.G, color.B);
|
|
1851
|
+
cfNew.dwEffects &= ~CFE_AUTOCOLOR;
|
|
1852
|
+
}
|
|
1853
|
+
if (facebook::react::isColorMeaningful(textAttributes.backgroundColor)) {
|
|
1854
|
+
auto color = theme()->Color(*textAttributes.backgroundColor);
|
|
1855
|
+
cfNew.crBackColor = RGB(color.R, color.G, color.B);
|
|
1856
|
+
cfNew.dwEffects &= ~CFE_AUTOBACKCOLOR;
|
|
1631
1857
|
}
|
|
1632
1858
|
|
|
1633
1859
|
// set charset
|
|
1634
1860
|
cfNew.dwMask |= CFM_CHARSET;
|
|
1635
1861
|
cfNew.bCharSet = DEFAULT_CHARSET;
|
|
1636
1862
|
|
|
1637
|
-
|
|
1863
|
+
return cfNew;
|
|
1638
1864
|
}
|
|
1639
1865
|
|
|
1640
1866
|
void WindowsTextInputComponentView::UpdateParaFormat() noexcept {
|
|
@@ -1885,21 +2111,50 @@ void WindowsTextInputComponentView::DrawText() noexcept {
|
|
|
1885
2111
|
m_needsRedraw = false;
|
|
1886
2112
|
}
|
|
1887
2113
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
winrt::check_bool(m_textServices != nullptr);
|
|
2114
|
+
void WindowsTextInputComponentView::ensureTextHost() noexcept {
|
|
2115
|
+
if (!m_textHost) {
|
|
2116
|
+
winrt::check_hresult(HrEnsureRichEd20Loaded());
|
|
2117
|
+
m_textHost = winrt::make<CompTextHost>(this);
|
|
2118
|
+
winrt::com_ptr<::IUnknown> spUnk;
|
|
2119
|
+
winrt::check_hresult(g_pfnCreateTextServices(nullptr, m_textHost.get(), spUnk.put()));
|
|
2120
|
+
spUnk.as(m_textServices);
|
|
2121
|
+
winrt::check_bool(m_textServices != nullptr);
|
|
1897
2122
|
|
|
2123
|
+
// Enable TSF support
|
|
2124
|
+
winrt::check_hresult(m_textServices->TxSendMessage(EM_SETEDITSTYLE, SES_USECTF, SES_USECTF, nullptr));
|
|
2125
|
+
winrt::check_hresult(m_textServices->TxSendMessage(EM_SETTEXTMODE, TM_RICHTEXT, 0, nullptr));
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
HRESULT WindowsTextInputComponentView::GetTextDocument(ITextDocument2 **textDocument) noexcept {
|
|
2130
|
+
winrt::com_ptr<IUnknown> spUnk;
|
|
2131
|
+
winrt::com_ptr<ITextDocument2> spTextDoc;
|
|
2132
|
+
ensureTextHost();
|
|
2133
|
+
|
|
2134
|
+
// initialize TOM
|
|
1898
2135
|
LRESULT res;
|
|
1899
|
-
|
|
2136
|
+
m_textServices->TxSendMessage(EM_GETOLEINTERFACE, 0, reinterpret_cast<LPARAM>(spUnk.put()), &res);
|
|
2137
|
+
HRESULT hr = spUnk->QueryInterface(__uuidof(ITextDocument2), reinterpret_cast<void **>(spTextDoc.put()));
|
|
2138
|
+
spTextDoc.copy_to(textDocument);
|
|
2139
|
+
return hr;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
HRESULT WindowsTextInputComponentView::GetTextServices(ITextServices2 **textServices) noexcept {
|
|
2143
|
+
ensureTextHost();
|
|
2144
|
+
m_textServices.copy_to(textServices);
|
|
2145
|
+
return S_OK;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
HRESULT WindowsTextInputComponentView::GetTextHost(ITextHost **textHost) noexcept {
|
|
2149
|
+
ensureTextHost();
|
|
2150
|
+
m_textHost.copy_to(textHost);
|
|
2151
|
+
return S_OK;
|
|
2152
|
+
}
|
|
1900
2153
|
|
|
1901
|
-
|
|
1902
|
-
|
|
2154
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::IVisual
|
|
2155
|
+
WindowsTextInputComponentView::createVisual() noexcept {
|
|
2156
|
+
auto visual = m_compContext.CreateSpriteVisual();
|
|
2157
|
+
ensureTextHost();
|
|
1903
2158
|
|
|
1904
2159
|
m_caretVisual = m_compContext.CreateCaretVisual();
|
|
1905
2160
|
visual.InsertAt(m_caretVisual.InnerVisual(), 0);
|