Skip to content

Commit 176577a

Browse files
authored
[0.81] Cherry-pick TouchableWin32 fix, and centering TextInput fix. (#15756)
* Center single line textinputs (#15754) * Center single line textinputs * Change files * snapshot * Add textinput italic, underline and strikethrough support * Update require's from TouchableWin32 to be imports * Change files
1 parent 6bdad28 commit 176577a

7 files changed

Lines changed: 130 additions & 76 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "Update require's from TouchableWin32 to be imports",
4+
"packageName": "@office-iss/react-native-win32",
5+
"email": "30809111+acoates-ms@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "Center single line textinputs",
4+
"packageName": "react-native-windows",
5+
"email": "30809111+acoates-ms@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

packages/@office-iss/react-native-win32/src-win/Libraries/Components/Touchable/TouchableWin32.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@ import {
2727
} from './TouchableWin32.Types';
2828
import { IKeyboardEvent } from '../View/ViewPropTypes';
2929

30-
const BoundingDimensions = require('./BoundingDimensions');
31-
const Position = require('./Position');
30+
// @ts-ignore
31+
import BoundingDimensions from './BoundingDimensions';
32+
// @ts-ignore
33+
import Position from './Position';
3234

33-
const {findNodeHandle} = require('../../ReactNative/RendererProxy');
35+
// @ts-ignore
36+
import {findNodeHandle} from '../../ReactNative/RendererProxy';
3437

3538
/**
3639
* Extracts a single touch, generally this is the active touch or touch that

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,9 @@ exports[`TextInput Tests Text have cursorColor 1`] = `
381381
"Brush Type": "ColorBrush",
382382
"Color": "rgba(0, 128, 0, 255)",
383383
},
384-
"Offset": "83, 5, 0",
384+
"Offset": "89, 6, 0",
385385
"Opacity": 0,
386-
"Size": "1, 19",
386+
"Size": "1, 21",
387387
"Visual Type": "SpriteVisual",
388388
},
389389
],
@@ -1884,9 +1884,9 @@ exports[`TextInput Tests TextInputs can clear on submit 1`] = `
18841884
"Brush Type": "ColorBrush",
18851885
"Color": "rgba(0, 0, 0, 255)",
18861886
},
1887-
"Offset": "5, 5, 0",
1887+
"Offset": "5, 6, 0",
18881888
"Opacity": 0,
1889-
"Size": "1, 19",
1889+
"Size": "1, 21",
18901890
"Visual Type": "SpriteVisual",
18911891
},
18921892
],
@@ -2805,9 +2805,9 @@ exports[`TextInput Tests TextInputs can have caretHidden 1`] = `
28052805
"Brush Type": "ColorBrush",
28062806
"Color": "rgba(0, 0, 0, 255)",
28072807
},
2808-
"Offset": "83, 5, 0",
2808+
"Offset": "89, 6, 0",
28092809
"Opacity": 0,
2810-
"Size": "1, 19",
2810+
"Size": "1, 21",
28112811
"Visual Type": "SpriteVisual",
28122812
},
28132813
],
@@ -4993,9 +4993,9 @@ exports[`TextInput Tests TextInputs can select text on focus 1`] = `
49934993
"Brush Type": "ColorBrush",
49944994
"Color": "rgba(0, 0, 0, 255)",
49954995
},
4996-
"Offset": "83, 5, 0",
4996+
"Offset": "89, 6, 0",
49974997
"Opacity": 0,
4998-
"Size": "1, 19",
4998+
"Size": "1, 21",
49994999
"Visual Type": "SpriteVisual",
50005000
},
50015001
],
@@ -5226,7 +5226,7 @@ exports[`TextInput Tests TextInputs can submit with custom key, multilined and s
52265226
},
52275227
"Offset": "5, 5, 0",
52285228
"Opacity": 0,
5229-
"Size": "1, 19",
5229+
"Size": "1, 21",
52305230
"Visual Type": "SpriteVisual",
52315231
},
52325232
],

vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp

Lines changed: 90 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
246246
//@cmember Converts screen coordinates of a specified point to the client coordinates
247247
BOOL TxScreenToClient(LPPOINT lppt) override {
248248
winrt::Windows::Foundation::Point pt{static_cast<float>(lppt->x), static_cast<float>(lppt->y)};
249+
pt.X -= m_outer->m_contentOffsetPx.x;
250+
pt.Y -= m_outer->m_contentOffsetPx.y;
249251
auto localpt = m_outer->ScreenToLocal(pt);
250252
lppt->x = static_cast<LONG>(localpt.X);
251253
lppt->y = static_cast<LONG>(localpt.Y);
@@ -255,9 +257,14 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
255257
//@cmember Converts the client coordinates of a specified point to screen coordinates
256258
BOOL TxClientToScreen(LPPOINT lppt) override {
257259
winrt::Windows::Foundation::Point pt{static_cast<float>(lppt->x), static_cast<float>(lppt->y)};
260+
261+
if (!m_outer->m_parent) {
262+
return false;
263+
}
264+
258265
auto screenpt = m_outer->LocalToScreen(pt);
259-
lppt->x = static_cast<LONG>(screenpt.X);
260-
lppt->y = static_cast<LONG>(screenpt.Y);
266+
lppt->x = static_cast<LONG>(screenpt.X) + m_outer->m_contentOffsetPx.x;
267+
lppt->y = static_cast<LONG>(screenpt.Y) + m_outer->m_contentOffsetPx.y;
261268
return true;
262269
}
263270

@@ -276,20 +283,25 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
276283
//@cmember Retrieves the coordinates of a window's client area
277284
HRESULT TxGetClientRect(LPRECT prc) override {
278285
*prc = m_outer->getClientRect();
286+
287+
prc->top += m_outer->m_contentOffsetPx.y;
288+
prc->bottom += m_outer->m_contentOffsetPx.y -
289+
static_cast<LONG>(m_outer->m_layoutMetrics.contentInsets.bottom * m_outer->m_layoutMetrics.pointScaleFactor);
290+
prc->left += m_outer->m_contentOffsetPx.x;
291+
prc->right += m_outer->m_contentOffsetPx.x -
292+
static_cast<LONG>(m_outer->m_layoutMetrics.contentInsets.right * m_outer->m_layoutMetrics.pointScaleFactor);
293+
279294
return S_OK;
280295
}
281296

282297
//@cmember Get the view rectangle relative to the inset
283298
HRESULT TxGetViewInset(LPRECT prc) override {
284299
// Inset is in HIMETRIC
285-
constexpr float HmPerInchF = 2540.0f;
286-
constexpr float PointsPerInch = 96.0f;
287-
constexpr float dipToHm = HmPerInchF / PointsPerInch;
288300

289-
prc->left = static_cast<LONG>(m_outer->m_layoutMetrics.contentInsets.left * dipToHm);
290-
prc->top = static_cast<LONG>(m_outer->m_layoutMetrics.contentInsets.top * dipToHm);
291-
prc->bottom = static_cast<LONG>(m_outer->m_layoutMetrics.contentInsets.bottom * dipToHm);
292-
prc->right = static_cast<LONG>(m_outer->m_layoutMetrics.contentInsets.right * dipToHm);
301+
prc->left = 0;
302+
prc->top = 0;
303+
prc->bottom = 0;
304+
prc->right = 0;
293305

294306
return NOERROR;
295307
}
@@ -492,11 +504,6 @@ AutoCorrectOffCallback(LANGID langid, const WCHAR *pszBefore, WCHAR *pszAfter, L
492504
facebook::react::AttributedString WindowsTextInputComponentView::getAttributedString() const {
493505
// Use BaseTextShadowNode to get attributed string from children
494506

495-
auto childTextAttributes = facebook::react::TextAttributes::defaultTextAttributes();
496-
childTextAttributes.fontSizeMultiplier = m_fontSizeMultiplier;
497-
498-
childTextAttributes.apply(windowsTextInputProps().textAttributes);
499-
500507
auto attributedString = facebook::react::AttributedString{};
501508
// auto attachments = facebook::react::BaseTextShadowNode::Attachments{};
502509

@@ -1114,6 +1121,9 @@ void WindowsTextInputComponentView::updateProps(
11141121
!facebook::react::floatEquality(
11151122
oldTextInputProps.textAttributes.letterSpacing, newTextInputProps.textAttributes.letterSpacing) ||
11161123
oldTextInputProps.textAttributes.fontFamily != newTextInputProps.textAttributes.fontFamily ||
1124+
oldTextInputProps.textAttributes.fontStyle != newTextInputProps.textAttributes.fontStyle ||
1125+
oldTextInputProps.textAttributes.textDecorationLineType !=
1126+
newTextInputProps.textAttributes.textDecorationLineType ||
11171127
!facebook::react::floatEquality(
11181128
oldTextInputProps.textAttributes.maxFontSizeMultiplier,
11191129
newTextInputProps.textAttributes.maxFontSizeMultiplier)) {
@@ -1129,6 +1139,7 @@ void WindowsTextInputComponentView::updateProps(
11291139
}
11301140

11311141
if (oldTextInputProps.multiline != newTextInputProps.multiline) {
1142+
m_recalculateContentVerticalOffset = true;
11321143
m_multiline = newTextInputProps.multiline;
11331144
m_propBitsMask |= TXTBIT_MULTILINE | TXTBIT_WORDWRAP;
11341145
if (newTextInputProps.multiline) {
@@ -1278,6 +1289,10 @@ void WindowsTextInputComponentView::updateLayoutMetrics(
12781289
unsigned int newWidth = static_cast<unsigned int>(layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor);
12791290
unsigned int newHeight = static_cast<unsigned int>(layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor);
12801291

1292+
if (newHeight != m_imgHeight || oldLayoutMetrics.pointScaleFactor != layoutMetrics.pointScaleFactor) {
1293+
m_recalculateContentVerticalOffset = true;
1294+
}
1295+
12811296
if (newWidth != m_imgWidth || newHeight != m_imgHeight) {
12821297
m_drawingSurface = nullptr; // Invalidate surface if we get a size change
12831298
}
@@ -1416,6 +1431,8 @@ void WindowsTextInputComponentView::FinalizeUpdates(
14161431

14171432
void WindowsTextInputComponentView::UpdatePropertyBits() noexcept {
14181433
if (m_propBitsMask != 0) {
1434+
if ((m_propBits & TXTBIT_CHARFORMATCHANGE) == TXTBIT_CHARFORMATCHANGE)
1435+
m_recalculateContentVerticalOffset = true;
14191436
DrawBlock db(*this);
14201437
winrt::check_hresult(m_textServices->OnTxPropertyBitsChange(m_propBitsMask, m_propBits));
14211438
m_propBitsMask = 0;
@@ -1427,6 +1444,10 @@ void WindowsTextInputComponentView::InternalFinalize() noexcept {
14271444
if (m_mounted) {
14281445
UpdatePropertyBits();
14291446

1447+
if (m_recalculateContentVerticalOffset) {
1448+
calculateContentVerticalOffset();
1449+
}
1450+
14301451
ensureDrawingSurface();
14311452
if (m_needsRedraw) {
14321453
DrawText();
@@ -1488,12 +1509,6 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
14881509
// m_crText = RemoveAlpha(fontDetails.FontColor);
14891510
// }
14901511

1491-
// set font face
1492-
// cfNew.dwMask |= CFM_FACE;
1493-
// NetUIWzCchCopy(cfNew.szFaceName, _countof(cfNew.szFaceName), fontDetails.FontName.c_str());
1494-
// cfNew.bPitchAndFamily = FF_DONTCARE;
1495-
1496-
// set font size -- 15 to convert twips to pt
14971512
const auto &props = windowsTextInputProps();
14981513
float fontSize =
14991514
(std::isnan(props.textAttributes.fontSize) ? facebook::react::TextAttributes::defaultTextAttributes().fontSize
@@ -1504,35 +1519,47 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
15041519
fontSize *=
15051520
(maxFontSizeMultiplier >= 1.0f) ? std::min(maxFontSizeMultiplier, m_fontSizeMultiplier) : m_fontSizeMultiplier;
15061521

1507-
// TODO get fontSize from props.textAttributes, or defaultTextAttributes, or fragment?
15081522
cfNew.dwMask |= CFM_SIZE;
1523+
// set font size -- 15 to convert twips to pt
15091524
cfNew.yHeight = static_cast<LONG>(fontSize * 15);
15101525

15111526
// set bold
15121527
cfNew.dwMask |= CFM_WEIGHT;
15131528
cfNew.wWeight =
15141529
props.textAttributes.fontWeight ? static_cast<WORD>(*props.textAttributes.fontWeight) : DWRITE_FONT_WEIGHT_NORMAL;
15151530

1516-
// set font style
1517-
// cfNew.dwMask |= (CFM_ITALIC | CFM_STRIKEOUT | CFM_UNDERLINE);
1518-
// int dFontStyle = fontDetails.FontStyle;
1519-
// if (dFontStyle & FS_Italic) {
1520-
// cfNew.dwEffects |= CFE_ITALIC;
1521-
// }
1522-
// if (dFontStyle & FS_StrikeOut) {
1523-
// cfNew.dwEffects |= CFE_STRIKEOUT;
1524-
//}
1525-
// if (dFontStyle & FS_Underline) {
1526-
// cfNew.dwEffects |= CFE_UNDERLINE;
1527-
// }
1531+
// set font style (italic)
1532+
cfNew.dwMask |= CFM_ITALIC;
1533+
if (props.textAttributes.fontStyle == facebook::react::FontStyle::Italic ||
1534+
props.textAttributes.fontStyle == facebook::react::FontStyle::Oblique) {
1535+
cfNew.dwEffects |= CFE_ITALIC;
1536+
}
1537+
1538+
// set text decoration (underline and strikethrough)
1539+
cfNew.dwMask |= (CFM_UNDERLINE | CFM_STRIKEOUT);
1540+
if (props.textAttributes.textDecorationLineType.has_value()) {
1541+
auto decorationType = *props.textAttributes.textDecorationLineType;
1542+
if (decorationType == facebook::react::TextDecorationLineType::Underline ||
1543+
decorationType == facebook::react::TextDecorationLineType::UnderlineStrikethrough) {
1544+
cfNew.dwEffects |= CFE_UNDERLINE;
1545+
}
1546+
if (decorationType == facebook::react::TextDecorationLineType::Strikethrough ||
1547+
decorationType == facebook::react::TextDecorationLineType::UnderlineStrikethrough) {
1548+
cfNew.dwEffects |= CFE_STRIKEOUT;
1549+
}
1550+
}
15281551

15291552
// set font family
15301553
if (!props.textAttributes.fontFamily.empty()) {
15311554
cfNew.dwMask |= CFM_FACE;
15321555
std::wstring fontFamily =
15331556
std::wstring(props.textAttributes.fontFamily.begin(), props.textAttributes.fontFamily.end());
15341557
wcsncpy_s(cfNew.szFaceName, fontFamily.c_str(), LF_FACESIZE);
1558+
} else {
1559+
cfNew.dwMask |= CFM_FACE;
1560+
wcsncpy_s(cfNew.szFaceName, L"Segoe UI\0", LF_FACESIZE);
15351561
}
1562+
cfNew.bPitchAndFamily = FF_DONTCARE;
15361563

15371564
// set char offset
15381565
cfNew.dwMask |= CFM_OFFSET;
@@ -1541,7 +1568,8 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
15411568
// set letter spacing
15421569
float letterSpacing = props.textAttributes.letterSpacing;
15431570
if (!std::isnan(letterSpacing)) {
1544-
updateLetterSpacing(letterSpacing);
1571+
cfNew.dwMask |= CFM_SPACING;
1572+
cfNew.sSpacing = static_cast<SHORT>(letterSpacing * 20); // Convert to TWIPS
15451573
}
15461574

15471575
// set charset
@@ -1657,7 +1685,7 @@ winrt::com_ptr<::IDWriteTextLayout> WindowsTextInputComponentView::CreatePlaceho
16571685
const auto &props = windowsTextInputProps();
16581686
facebook::react::TextAttributes textAttributes = props.textAttributes;
16591687
if (std::isnan(props.textAttributes.fontSize)) {
1660-
textAttributes.fontSize = 12.0f;
1688+
facebook::react::TextAttributes::defaultTextAttributes().fontSize;
16611689
}
16621690
textAttributes.fontSizeMultiplier = m_fontSizeMultiplier;
16631691
fragment1.string = props.placeholder;
@@ -1674,6 +1702,26 @@ winrt::com_ptr<::IDWriteTextLayout> WindowsTextInputComponentView::CreatePlaceho
16741702
return textLayout;
16751703
}
16761704

1705+
void WindowsTextInputComponentView::calculateContentVerticalOffset() noexcept {
1706+
m_recalculateContentVerticalOffset = false;
1707+
1708+
const auto &props = windowsTextInputProps();
1709+
1710+
m_contentOffsetPx = {
1711+
static_cast<LONG>(m_layoutMetrics.contentInsets.left * m_layoutMetrics.pointScaleFactor),
1712+
static_cast<LONG>(m_layoutMetrics.contentInsets.top * m_layoutMetrics.pointScaleFactor)};
1713+
1714+
if (props.multiline) {
1715+
// Align to the top for multiline
1716+
return;
1717+
}
1718+
1719+
auto [contentWidth, contentHeight] = GetContentSize();
1720+
1721+
m_contentOffsetPx.y += static_cast<LONG>(std::round(
1722+
((m_layoutMetrics.getContentFrame().size.height - contentHeight) / 2) * m_layoutMetrics.pointScaleFactor));
1723+
}
1724+
16771725
void WindowsTextInputComponentView::DrawText() noexcept {
16781726
m_needsRedraw = true;
16791727
if (m_cDrawBlock || theme()->IsEmpty() || !m_textServices) {
@@ -1699,16 +1747,13 @@ void WindowsTextInputComponentView::DrawText() noexcept {
16991747
assert(d2dDeviceContext->GetUnitMode() == D2D1_UNIT_MODE_DIPS);
17001748

17011749
RECTL rc{
1702-
static_cast<LONG>(offset.x),
1703-
static_cast<LONG>(offset.y),
1704-
static_cast<LONG>(offset.x) + static_cast<LONG>(m_imgWidth),
1705-
static_cast<LONG>(offset.y) + static_cast<LONG>(m_imgHeight)};
1750+
offset.x + m_contentOffsetPx.x,
1751+
offset.y + m_contentOffsetPx.y,
1752+
offset.x + m_contentOffsetPx.x + static_cast<LONG>(m_imgWidth),
1753+
offset.y + m_contentOffsetPx.y + static_cast<LONG>(m_imgHeight)};
17061754

17071755
RECT rcClient{
1708-
static_cast<LONG>(offset.x),
1709-
static_cast<LONG>(offset.y),
1710-
static_cast<LONG>(offset.x) + static_cast<LONG>(m_imgWidth),
1711-
static_cast<LONG>(offset.y) + static_cast<LONG>(m_imgHeight)};
1756+
offset.x, offset.y, offset.x + static_cast<LONG>(m_imgWidth), offset.y + static_cast<LONG>(m_imgHeight)};
17121757

17131758
{
17141759
m_cDrawBlock++; // Dont use AutoDrawBlock as we are already in draw, and dont need to draw again.
@@ -1763,8 +1808,8 @@ void WindowsTextInputComponentView::DrawText() noexcept {
17631808
// draw text
17641809
d2dDeviceContext->DrawTextLayout(
17651810
D2D1::Point2F(
1766-
static_cast<FLOAT>((offset.x + m_layoutMetrics.contentInsets.left) / m_layoutMetrics.pointScaleFactor),
1767-
static_cast<FLOAT>((offset.y + m_layoutMetrics.contentInsets.top) / m_layoutMetrics.pointScaleFactor)),
1811+
static_cast<FLOAT>(offset.x + m_contentOffsetPx.x) / m_layoutMetrics.pointScaleFactor,
1812+
static_cast<FLOAT>(offset.y + m_contentOffsetPx.y) / m_layoutMetrics.pointScaleFactor),
17681813
textLayout.get(),
17691814
brush.get(),
17701815
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
@@ -1840,22 +1885,6 @@ void WindowsTextInputComponentView::autoCapitalizeOnUpdateProps(
18401885
}
18411886
}
18421887

1843-
void WindowsTextInputComponentView::updateLetterSpacing(float letterSpacing) noexcept {
1844-
CHARFORMAT2W cf = {};
1845-
cf.cbSize = sizeof(CHARFORMAT2W);
1846-
cf.dwMask = CFM_SPACING;
1847-
cf.sSpacing = static_cast<SHORT>(letterSpacing * 20); // Convert to TWIPS
1848-
1849-
LRESULT res;
1850-
1851-
// Apply to all existing text like placeholder
1852-
winrt::check_hresult(m_textServices->TxSendMessage(EM_SETCHARFORMAT, SCF_ALL, reinterpret_cast<LPARAM>(&cf), &res));
1853-
1854-
// Apply to future text input
1855-
winrt::check_hresult(
1856-
m_textServices->TxSendMessage(EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&cf), &res));
1857-
}
1858-
18591888
void WindowsTextInputComponentView::updateAutoCorrect(bool enable) noexcept {
18601889
LRESULT lresult;
18611890
winrt::check_hresult(m_textServices->TxSendMessage(

0 commit comments

Comments
 (0)